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

Merge branch 'master' into gdrtvu

{ {
"globals": { "globals": {
"$": true,
"$$": true,
"$computed": true,
"$customRef": true,
"$ref": true,
"$shallowRef": true,
"$toRef": true,
"Component": true, "Component": true,
"ComponentPublicInstance": true, "ComponentPublicInstance": true,
"ComputedRef": true, "ComputedRef": true,
"EffectScope": true, "EffectScope": true,
"ExtractDefaultPropTypes": true,
"ExtractPropTypes": true,
"ExtractPublicPropTypes": true,
"InjectionKey": true, "InjectionKey": true,
"PropType": true, "PropType": true,
"Ref": true, "Ref": true,
"VNode": true, "VNode": true,
"WritableComputedRef": true,
"asyncComputed": true, "asyncComputed": true,
"autoResetRef": true, "autoResetRef": true,
"computed": true, "computed": true,
...@@ -106,6 +103,7 @@ ...@@ -106,6 +103,7 @@
"toReactive": true, "toReactive": true,
"toRef": true, "toRef": true,
"toRefs": true, "toRefs": true,
"toValue": true,
"triggerRef": true, "triggerRef": true,
"tryOnBeforeMount": true, "tryOnBeforeMount": true,
"tryOnBeforeUnmount": true, "tryOnBeforeUnmount": true,
...@@ -297,11 +295,6 @@ ...@@ -297,11 +295,6 @@
"watchThrottled": true, "watchThrottled": true,
"watchTriggerable": true, "watchTriggerable": true,
"watchWithFilter": true, "watchWithFilter": true,
"whenever": true, "whenever": true
"toValue": true,
"ExtractDefaultPropTypes": true,
"ExtractPropTypes": true,
"ExtractPublicPropTypes": true,
"WritableComputedRef": true
} }
} }
/* eslint-disable */ /* eslint-disable */
/* prettier-ignore */ /* prettier-ignore */
// @ts-nocheck // @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import // Generated by unplugin-auto-import
export {} export {}
declare global { declare global {
const $: typeof import('vue/macros')['$']
const $$: typeof import('vue/macros')['$$']
const $computed: typeof import('vue/macros')['$computed']
const $customRef: typeof import('vue/macros')['$customRef']
const $ref: typeof import('vue/macros')['$ref']
const $shallowRef: typeof import('vue/macros')['$shallowRef']
const $toRef: typeof import('vue/macros')['$toRef']
const EffectScope: typeof import('vue')['EffectScope'] const EffectScope: typeof import('vue')['EffectScope']
const asyncComputed: typeof import('@vueuse/core')['asyncComputed'] const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
const autoResetRef: typeof import('@vueuse/core')['autoResetRef'] const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
...@@ -300,5 +294,6 @@ declare global { ...@@ -300,5 +294,6 @@ declare global {
// for type re-export // for type re-export
declare global { declare global {
// @ts-ignore // @ts-ignore
export type { Component, ComponentPublicInstance, ComputedRef, InjectionKey, PropType, Ref, VNode } from 'vue' export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
import('vue')
} }
...@@ -10,5 +10,6 @@ ...@@ -10,5 +10,6 @@
<body> <body>
<div id="app"></div> <div id="app"></div>
<script type="module" src="/src/main.ts"></script> <script type="module" src="/src/main.ts"></script>
<script src="https://webapp-pub.ezijing.com/plugins/sky-agents/sky-agent.umd.cjs?v=1"></script>
</body> </body>
</html> </html>
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -14,9 +14,9 @@ ...@@ -14,9 +14,9 @@
"cert": "node ./cert.js" "cert": "node ./cert.js"
}, },
"dependencies": { "dependencies": {
"@element-plus/icons-vue": "^2.0.10", "@element-plus/icons-vue": "^2.3.1",
"@tinymce/tinymce-vue": "^5.0.0", "@tinymce/tinymce-vue": "^5.0.0",
"@vant/area-data": "^1.4.0", "@vant/area-data": "^1.5.1",
"@vueuse/core": "^9.13.0", "@vueuse/core": "^9.13.0",
"@vueuse/head": "^1.0.26", "@vueuse/head": "^1.0.26",
"@vueuse/integrations": "^9.13.0", "@vueuse/integrations": "^9.13.0",
...@@ -26,23 +26,24 @@ ...@@ -26,23 +26,24 @@
"blueimp-md5": "^2.19.0", "blueimp-md5": "^2.19.0",
"countup.js": "^2.6.2", "countup.js": "^2.6.2",
"dayjs": "^1.11.7", "dayjs": "^1.11.7",
"element-plus": "^2.2.32", "element-plus": "^2.4.4",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"filesize": "^10.0.7", "filesize": "^10.0.7",
"html-to-image": "^1.11.11", "html-to-image": "^1.11.11",
"html2pdf.js": "^0.10.1", "html2pdf.js": "^0.10.1",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"pinia": "^2.1.4", "pinia": "^2.1.7",
"print-js": "^1.6.0", "print-js": "^1.6.0",
"ua-parser-js": "^1.0.33", "ua-parser-js": "^1.0.33",
"universal-cookie": "^4.0.4", "universal-cookie": "^4.0.4",
"video.js": "^7.21.1", "video.js": "^7.21.1",
"vue": "^3.3.4", "vue": "^3.4.25",
"vue-router": "^4.2.2", "vue-router": "^4.3.2",
"vuedraggable": "^4.1.0" "vuedraggable": "^4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@rushstack/eslint-patch": "^1.2.0", "@rushstack/eslint-patch": "^1.2.0",
"@tsconfig/node20": "^20.1.4",
"@types/ali-oss": "^6.16.8", "@types/ali-oss": "^6.16.8",
"@types/blueimp-md5": "^2.18.0", "@types/blueimp-md5": "^2.18.0",
"@types/file-saver": "^2.0.5", "@types/file-saver": "^2.0.5",
...@@ -50,17 +51,18 @@ ...@@ -50,17 +51,18 @@
"@types/node": "^20.3.1", "@types/node": "^20.3.1",
"@types/ua-parser-js": "^0.7.36", "@types/ua-parser-js": "^0.7.36",
"@types/video.js": "^7.3.52", "@types/video.js": "^7.3.52",
"@vitejs/plugin-vue": "^4.2.3", "@vitejs/plugin-vue": "^5.0.4",
"@vue/eslint-config-typescript": "^11.0.3", "@vue-macros/reactivity-transform": "^0.4.4",
"@vue/tsconfig": "^0.1.3", "@vue/eslint-config-typescript": "^12.0.0",
"@vue/tsconfig": "^0.5.1",
"chalk": "^5.2.0", "chalk": "^5.2.0",
"eslint": "^8.34.0", "eslint": "^8.57.0",
"eslint-plugin-vue": "^9.9.0", "eslint-plugin-vue": "^9.25.0",
"sass": "^1.58.3", "sass": "^1.58.3",
"typescript": "~4.9.5", "typescript": "~5.4.5",
"unplugin-auto-import": "^0.16.4", "unplugin-auto-import": "^0.17.5",
"vite": "^4.3.9", "vite": "^5.2.10",
"vite-plugin-checker": "^0.6.0", "vite-plugin-checker": "^0.6.4",
"vue-tsc": "^1.6.5" "vue-tsc": "^1.8.27"
} }
} }
src/assets/images/x_home_student_bg.png

971.1 KB | W: | H:

src/assets/images/x_home_student_bg.png

962.9 KB | W: | H:

src/assets/images/x_home_student_bg.png
src/assets/images/x_home_student_bg.png
src/assets/images/x_home_student_bg.png
src/assets/images/x_home_student_bg.png
  • 2-up
  • Swipe
  • Onion skin
...@@ -5,6 +5,8 @@ const emit = defineEmits<{ ...@@ -5,6 +5,8 @@ const emit = defineEmits<{
(e: 'resize'): void (e: 'resize'): void
}>() }>()
const props = defineProps<{ isLeftShow?: number }>()
const leftPanelVisible = $ref<boolean>(true) const leftPanelVisible = $ref<boolean>(true)
const leftPanelWidth = useStorage('leftPanelWidth', 400) const leftPanelWidth = useStorage('leftPanelWidth', 400)
const leftPanelWidthText = $computed(() => { const leftPanelWidthText = $computed(() => {
...@@ -40,7 +42,7 @@ onMounted(() => { ...@@ -40,7 +42,7 @@ onMounted(() => {
<template> <template>
<section class="drag-panel"> <section class="drag-panel">
<div class="drag-panel-left" :class="{ 'is-hidden': !leftPanelVisible }"> <div v-show="isLeftShow !== 1" class="drag-panel-left" :class="{ 'is-hidden': !leftPanelVisible }">
<div class="drag-cover" v-if="dragFlag"></div> <div class="drag-cover" v-if="dragFlag"></div>
<slot name="left"></slot> <slot name="left"></slot>
<div class="panel-resize" id="panel-resize"></div> <div class="panel-resize" id="panel-resize"></div>
...@@ -50,12 +52,14 @@ onMounted(() => { ...@@ -50,12 +52,14 @@ onMounted(() => {
<path <path
class="path-wapper" class="path-wapper"
d="M0 0l14.12 8.825A4 4 0 0116 12.217v61.566a4 4 0 01-1.88 3.392L0 86V0z" 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 <path
class="path-arrow" 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" 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="#8D9EA7"
fill-rule="nonzero"></path> fill-rule="nonzero"
></path>
</g> </g>
</svg> </svg>
</div> </div>
......
...@@ -132,29 +132,14 @@ defineExpose({ refetch, tableRef }) ...@@ -132,29 +132,14 @@ defineExpose({ refetch, tableRef })
</template> </template>
<template v-else> <template v-else>
<!-- input --> <!-- input -->
<el-input <el-input v-model="params[item.prop]" v-bind="item" clearable @change="search" style="width: 200px" v-if="item.type === 'input'" />
v-model="params[item.prop]"
v-bind="item"
clearable
@change="search"
style="width: 200px"
v-if="item.type === 'input'"
/>
<!-- select --> <!-- select -->
<el-select <el-select v-model="params[item.prop]" v-bind="item" filterable clearable @change="search" v-if="item.type === 'select'">
v-model="params[item.prop]"
v-bind="item"
filterable
clearable
@change="search"
v-if="item.type === 'select'"
>
<el-option <el-option
:label="option[item.labelKey] || option.label" :label="option[item.labelKey] || option.label"
:value="option[item.valueKey] || option.value" :value="option[item.valueKey] || option.value"
v-for="(option, index) in item.options" v-for="(option, index) in item.options"
:key="index" :key="index" />
/>
</el-select> </el-select>
</template> </template>
</el-form-item> </el-form-item>
...@@ -172,14 +157,7 @@ defineExpose({ refetch, tableRef }) ...@@ -172,14 +157,7 @@ defineExpose({ refetch, tableRef })
<!-- 主体 --> <!-- 主体 -->
<div class="table-list-bd"> <div class="table-list-bd">
<slot name="body" v-bind="{ data: dataList }"> <slot name="body" v-bind="{ data: dataList }">
<el-table <el-table stripe :header-cell-style="{ background: '#ededed' }" :data="dataList" v-loading="loading" v-bind="$attrs" ref="tableRef">
stripe
:header-cell-style="{ background: '#ededed' }"
:data="dataList"
v-loading="loading"
v-bind="$attrs"
ref="tableRef"
>
<el-table-column align="center" v-bind="item || {}" v-for="item in columns" :key="item.prop"> <el-table-column align="center" v-bind="item || {}" v-for="item in columns" :key="item.prop">
<template #default="scope" v-if="item.slots || item.computed"> <template #default="scope" v-if="item.slots || item.computed">
<slot :name="item.slots" v-bind="scope" v-if="item.slots"></slot> <slot :name="item.slots" v-bind="scope" v-if="item.slots"></slot>
...@@ -198,15 +176,14 @@ defineExpose({ refetch, tableRef }) ...@@ -198,15 +176,14 @@ defineExpose({ refetch, tableRef })
class="table-list-pagination" class="table-list-pagination"
background background
layout="total, sizes, prev, pager, next, jumper" layout="total, sizes, prev, pager, next, jumper"
:page-sizes="[10, 20, 30, 50, 100]" :page-sizes="[10, 20, 30, 50]"
:page-size="page.size" :page-size="page.size"
:total="page.total" :total="page.total"
v-model:currentPage="page.currentPage" v-model:currentPage="page.currentPage"
@size-change="pageSizeChange" @size-change="pageSizeChange"
@current-change="fetchList()" @current-change="fetchList()"
:hide-on-single-page="true" :hide-on-single-page="true"
v-if="hasPagination" v-if="hasPagination">
>
</el-pagination> </el-pagination>
</div> </div>
</div> </div>
......
...@@ -164,14 +164,14 @@ function handleClick(path: string) { ...@@ -164,14 +164,14 @@ function handleClick(path: string) {
font-weight: 500; font-weight: 500;
border-bottom: 0; border-bottom: 0;
> .el-menu-item { > .el-menu-item {
margin-left: 40px; margin-right: 44px;
padding: 0; padding: 0;
background-color: unset !important; background-color: unset !important;
border-bottom: 0 !important; border-bottom: 0 !important;
} }
} }
.el-sub-menu__title { .el-sub-menu__title {
margin-left: 40px; // margin-left: 40px;
padding: 0; padding: 0;
border-bottom: 0 !important; border-bottom: 0 !important;
} }
......
...@@ -13,7 +13,7 @@ const appConfigList = [ ...@@ -13,7 +13,7 @@ const appConfigList = [
}, },
{ {
system: 'x', system: 'x',
title: '1+X实训平台(中级)', title: '1+X实训平台',
logo: 'https://zws-imgs-pub.ezijing.com/pc/base/ezijing-logo.svg', logo: 'https://zws-imgs-pub.ezijing.com/pc/base/ezijing-logo.svg',
hosts: ['saas-x'], hosts: ['saas-x'],
studentMenus: [ studentMenus: [
......
<script setup lang="ts"></script> <script setup lang="ts"></script>
<template> <template>
<iframe <iframe
allowfullscreen
src="https://bi.ezijing.com/bi//?proc=1&action=viewer&hback=true&isInPreview=true&db=!7d2b!!8346!!6559!!80b2!e-SaaS!2f!!5927!!8d5b!!6210!!7ee9!!7efc!!5408!!5206!!6790!.db&platform=PC&browserType=chrome" src="https://bi.ezijing.com/bi//?proc=1&action=viewer&hback=true&isInPreview=true&db=!7d2b!!8346!!6559!!80b2!e-SaaS!2f!!5927!!8d5b!!6210!!7ee9!!7efc!!5408!!5206!!6790!.db&platform=PC&browserType=chrome"
frameborder="0" frameborder="0"
class="iframe"></iframe> class="iframe"></iframe>
......
...@@ -56,7 +56,7 @@ const qaURL = import.meta.env.VITE_QA_CENTER_URL ...@@ -56,7 +56,7 @@ const qaURL = import.meta.env.VITE_QA_CENTER_URL
</script> </script>
<template> <template>
<el-dialog title="评分" :close-on-click-modal="false" width="700px" @update:modelValue="$emit('update:modelValue')"> <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 label-suffix=":" v-if="detail">
<el-form-item label="赛项名称">{{ detail.competition_id_name }}</el-form-item> <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="选手姓名">{{ detail.student_name }}</el-form-item>
......
...@@ -110,7 +110,7 @@ function handleResize() { ...@@ -110,7 +110,7 @@ function handleResize() {
</el-row> </el-row>
</AppCard> </AppCard>
<div class="lab-box"> <div class="lab-box">
<iframe :src="currentPlatformUrl" frameborder="0" class="iframe" ref="iframeRef"></iframe> <iframe allowfullscreen :src="currentPlatformUrl" frameborder="0" class="iframe" ref="iframeRef"></iframe>
</div> </div>
</template> </template>
</DragPanel> </DragPanel>
......
...@@ -118,7 +118,7 @@ function handleUpdate() { ...@@ -118,7 +118,7 @@ function handleUpdate() {
</script> </script>
<template> <template>
<el-dialog :title="title" :close-on-click-modal="false" width="600px" @update:modelValue="$emit('update:modelValue')"> <el-dialog :title="title" :close-on-click-modal="false" width="600px" @update:modelValue="value => $emit('update:modelValue', value)">
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px"> <el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="报名赛项"> <el-form-item label="报名赛项">
<el-input v-model="form.competition_name" :disabled="isUpdate" /> <el-input v-model="form.competition_name" :disabled="isUpdate" />
......
...@@ -78,7 +78,7 @@ function handleSubmit() { ...@@ -78,7 +78,7 @@ function handleSubmit() {
</script> </script>
<template> <template>
<el-dialog title="大赛训练答疑" :close-on-click-modal="false" @update:modelValue="$emit('update:modelValue')"> <el-dialog title="大赛训练答疑" :close-on-click-modal="false" @update:modelValue="value => $emit('update:modelValue', value)">
<div class="discuss-scroll" ref="scrollRef"> <div class="discuss-scroll" ref="scrollRef">
<DiscussItem :info="detail.info" :list="detail.list" v-if="detail.info"></DiscussItem> <DiscussItem :info="detail.info" :list="detail.list" v-if="detail.info"></DiscussItem>
<div class="tips" v-if="isLoading"> <div class="tips" v-if="isLoading">
...@@ -90,13 +90,7 @@ function handleSubmit() { ...@@ -90,13 +90,7 @@ function handleSubmit() {
<div class="tips" v-if="!hasMore">没有更多了</div> <div class="tips" v-if="!hasMore">没有更多了</div>
</div> </div>
<el-form <el-form ref="formRef" :rules="rules" :model="form" style="margin-top: 40px" v-permission="'v1-teacher-discussion-comment'">
ref="formRef"
:rules="rules"
:model="form"
style="margin-top: 40px"
v-permission="'v1-teacher-discussion-comment'"
>
<el-form-item prop="content"> <el-form-item prop="content">
<el-input type="textarea" v-model="form.content" :autosize="{ minRows: 4, maxRows: 6 }" :maxlength="200" /> <el-input type="textarea" v-model="form.content" :autosize="{ minRows: 4, maxRows: 6 }" :maxlength="200" />
</el-form-item> </el-form-item>
......
...@@ -97,7 +97,7 @@ function handleUpdate() { ...@@ -97,7 +97,7 @@ function handleUpdate() {
</script> </script>
<template> <template>
<el-dialog :title="title" :close-on-click-modal="false" width="600px" @update:modelValue="$emit('update:modelValue')"> <el-dialog :title="title" :close-on-click-modal="false" width="600px" @update:modelValue="value => $emit('update:modelValue', value)">
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px"> <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
<el-form-item label="所属部门/学校" prop="organization_id"> <el-form-item label="所属部门/学校" prop="organization_id">
<el-select v-model="form.organization_id" style="width: 100%" :disabled="isUpdate"> <el-select v-model="form.organization_id" style="width: 100%" :disabled="isUpdate">
......
...@@ -247,7 +247,7 @@ function handleDateRangeChange(value: any) { ...@@ -247,7 +247,7 @@ function handleDateRangeChange(value: any) {
</script> </script>
<template> <template>
<el-dialog :title="title" :close-on-click-modal="false" align-center width="600px" @update:modelValue="$emit('update:modelValue')"> <el-dialog :title="title" :close-on-click-modal="false" align-center width="600px" @update:modelValue="value => $emit('update:modelValue', value)">
<el-form ref="formRef" :model="form" :rules="rules" label-width="110px"> <el-form ref="formRef" :model="form" :rules="rules" label-width="110px">
<el-form-item label="赛项名称" prop="name"> <el-form-item label="赛项名称" prop="name">
<el-input v-model="form.name" :disabled="isUpdate" /> <el-input v-model="form.name" :disabled="isUpdate" />
......
...@@ -53,7 +53,7 @@ function handleSubmit() { ...@@ -53,7 +53,7 @@ function handleSubmit() {
</script> </script>
<template> <template>
<el-dialog title="添加专家" width="500px" @update:modelValue="$emit('update:modelValue')"> <el-dialog title="添加专家" width="500px" @update:modelValue="value => $emit('update:modelValue', value)">
<el-form ref="formRef" :model="form" :rules="rules" label-position="right" label-width="100px" label-suffix=":"> <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="赛项名称">{{ detail.name }}</el-form-item>
<el-form-item label="角色" prop="role"> <el-form-item label="角色" prop="role">
......
...@@ -100,7 +100,7 @@ function handleRemoveClass(index: number) { ...@@ -100,7 +100,7 @@ function handleRemoveClass(index: number) {
</script> </script>
<template> <template>
<el-dialog title="评分专家列表" :close-on-click-modal="false" @update:modelValue="$emit('update:modelValue')"> <el-dialog title="评分专家列表" :close-on-click-modal="false" @update:modelValue="value => $emit('update:modelValue', value)">
<el-form :disabled="disabled"> <el-form :disabled="disabled">
<AppList v-bind="listOptions" ref="appList"> <AppList v-bind="listOptions" ref="appList">
<template #header-buttons> <template #header-buttons>
......
...@@ -106,11 +106,7 @@ function handleUpdate(params: ContestBookUpdateParams) { ...@@ -106,11 +106,7 @@ function handleUpdate(params: ContestBookUpdateParams) {
</script> </script>
<template> <template>
<el-dialog <el-dialog title="编辑评分细则" :close-on-click-modal="false" width="600px" @update:modelValue="value => $emit('update:modelValue', value)">
title="编辑评分细则"
:close-on-click-modal="false"
width="600px"
@update:modelValue="$emit('update:modelValue')">
<el-form ref="formRef" :model="form" :rules="rules" label-width="124px" :disabled="disabled"> <el-form ref="formRef" :model="form" :rules="rules" label-width="124px" :disabled="disabled">
<el-form-item label="评分细则文件" prop="files"> <el-form-item label="评分细则文件" prop="files">
<AppUpload <AppUpload
......
...@@ -149,7 +149,7 @@ function handleRatioChange(row: any, index: number) { ...@@ -149,7 +149,7 @@ function handleRatioChange(row: any, index: number) {
</script> </script>
<template> <template>
<el-dialog title="评分规则" :close-on-click-modal="false" width="800px" @update:modelValue="$emit('update:modelValue')"> <el-dialog title="评分规则" :close-on-click-modal="false" width="800px" @update:modelValue="value => $emit('update:modelValue', value)">
<el-form ref="formRef" :model="form" :rules="rules" label-width="170px" :disabled="disabled"> <el-form ref="formRef" :model="form" :rules="rules" label-width="170px" :disabled="disabled">
<el-form-item label="赛项名称"> <el-form-item label="赛项名称">
<el-input v-model="form.name" disabled /> <el-input v-model="form.name" disabled />
......
...@@ -119,7 +119,7 @@ getPlatformKeysList() ...@@ -119,7 +119,7 @@ getPlatformKeysList()
</script> </script>
<template> <template>
<el-dialog :title="title" :close-on-click-modal="false" width="600px" @update:modelValue="$emit('update:modelValue')"> <el-dialog :title="title" :close-on-click-modal="false" width="600px" @update:modelValue="value => $emit('update:modelValue', value)">
<el-form ref="formRef" :model="form" :rules="rules" label-width="124px"> <el-form ref="formRef" :model="form" :rules="rules" label-width="124px">
<el-form-item label="训练指导书文件" prop="files"> <el-form-item label="训练指导书文件" prop="files">
<AppUpload <AppUpload
...@@ -127,8 +127,7 @@ getPlatformKeysList() ...@@ -127,8 +127,7 @@ getPlatformKeysList()
:limit="1" :limit="1"
:beforeUpload="handleBeforeUpload" :beforeUpload="handleBeforeUpload"
accept=".doc,.docx,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,.pdf,application/pdf,.ppt,.pptx,application/vnd.ms-powerpoint,.csv,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel" accept=".doc,.docx,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,.pdf,application/pdf,.ppt,.pptx,application/vnd.ms-powerpoint,.csv,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"
@success="handleUploadSuccess" @success="handleUploadSuccess">
>
<template #tip>训练指导书文件支持格式包含:doc docx xls xlsx pdf ppt pptx,大小不超过50M</template> <template #tip>训练指导书文件支持格式包含:doc docx xls xlsx pdf ppt pptx,大小不超过50M</template>
</AppUpload> </AppUpload>
</el-form-item> </el-form-item>
...@@ -141,9 +140,7 @@ getPlatformKeysList() ...@@ -141,9 +140,7 @@ getPlatformKeysList()
</el-form-item> </el-form-item>
<el-form-item label="关联实验"> <el-form-item label="关联实验">
<el-radio-group v-model="form.platform_key"> <el-radio-group v-model="form.platform_key">
<el-radio v-for="item in platformKeys" :key="item.platform_key" :label="item.platform_key">{{ <el-radio v-for="item in platformKeys" :key="item.platform_key" :label="item.platform_key">{{ item.name }}</el-radio>
item.name
}}</el-radio>
<!-- <el-radio :key="1" label="1">商业数据分析实验</el-radio> <!-- <el-radio :key="1" label="1">商业数据分析实验</el-radio>
<el-radio :key="1" label="2">数字营销实操</el-radio> --> <el-radio :key="1" label="2">数字营销实操</el-radio> -->
</el-radio-group> </el-radio-group>
......
...@@ -65,7 +65,7 @@ function handleUpdate(params: any) { ...@@ -65,7 +65,7 @@ function handleUpdate(params: any) {
</script> </script>
<template> <template>
<el-dialog :title="title" :close-on-click-modal="false" width="600px" @update:modelValue="$emit('update:modelValue')"> <el-dialog :title="title" :close-on-click-modal="false" width="600px" @update:modelValue="value => $emit('update:modelValue', value)">
<el-form ref="formRef" :model="form" :rules="rules" label-width="90px"> <el-form ref="formRef" :model="form" :rules="rules" label-width="90px">
<el-form-item label="考试类型" prop="type"> <el-form-item label="考试类型" prop="type">
<el-radio-group v-model="form.type" :disabled="isUpdate"> <el-radio-group v-model="form.type" :disabled="isUpdate">
......
...@@ -77,7 +77,7 @@ function handleUpdate(params: any) { ...@@ -77,7 +77,7 @@ function handleUpdate(params: any) {
</script> </script>
<template> <template>
<el-dialog :title="title" :close-on-click-modal="false" width="600px" @update:modelValue="$emit('update:modelValue')"> <el-dialog :title="title" :close-on-click-modal="false" width="600px" @update:modelValue="value => $emit('update:modelValue', value)">
<el-form ref="formRef" :model="form" :rules="rules" label-width="90px"> <el-form ref="formRef" :model="form" :rules="rules" label-width="90px">
<el-form-item label="试卷类型" prop="type"> <el-form-item label="试卷类型" prop="type">
<el-radio-group v-model="form.type"> <el-radio-group v-model="form.type">
......
...@@ -119,7 +119,7 @@ getPlatformKeysList() ...@@ -119,7 +119,7 @@ getPlatformKeysList()
</script> </script>
<template> <template>
<el-dialog :title="title" :close-on-click-modal="false" width="600px" @update:modelValue="$emit('update:modelValue')"> <el-dialog :title="title" :close-on-click-modal="false" width="600px" @update:modelValue="value => $emit('update:modelValue', value)">
<el-form ref="formRef" :model="form" :rules="rules" label-width="124px"> <el-form ref="formRef" :model="form" :rules="rules" label-width="124px">
<el-form-item label="大赛试题文件" prop="files"> <el-form-item label="大赛试题文件" prop="files">
<AppUpload <AppUpload
...@@ -140,9 +140,7 @@ getPlatformKeysList() ...@@ -140,9 +140,7 @@ getPlatformKeysList()
</el-form-item> </el-form-item>
<el-form-item label="关联实验"> <el-form-item label="关联实验">
<el-radio-group v-model="form.platform_key"> <el-radio-group v-model="form.platform_key">
<el-radio v-for="item in platformKeys" :key="item.platform_key" :label="item.platform_key">{{ <el-radio v-for="item in platformKeys" :key="item.platform_key" :label="item.platform_key">{{ item.name }}</el-radio>
item.name
}}</el-radio>
<!-- <el-radio :key="1" label="1">商业数据分析实验</el-radio> <!-- <el-radio :key="1" label="1">商业数据分析实验</el-radio>
<el-radio :key="1" label="2">数字营销实操</el-radio> --> <el-radio :key="1" label="2">数字营销实操</el-radio> -->
</el-radio-group> </el-radio-group>
......
...@@ -60,10 +60,7 @@ const title = $computed(() => { ...@@ -60,10 +60,7 @@ const title = $computed(() => {
function handleBeforeUpload() { function handleBeforeUpload() {
if (form.source_id) { if (form.source_id) {
return ElMessageBox.confirm( return ElMessageBox.confirm('系统仅支持1个训练操作视频,此操作将覆盖原有训练操作视频文件,确认上传新文件吗?', '提示')
'系统仅支持1个训练操作视频,此操作将覆盖原有训练操作视频文件,确认上传新文件吗?',
'提示'
)
} }
return true return true
} }
...@@ -98,7 +95,7 @@ function handleUpdate(params: ContestVideoUpdateParams) { ...@@ -98,7 +95,7 @@ function handleUpdate(params: ContestVideoUpdateParams) {
</script> </script>
<template> <template>
<el-dialog :title="title" :close-on-click-modal="false" width="600px" @update:modelValue="$emit('update:modelValue')"> <el-dialog :title="title" :close-on-click-modal="false" width="600px" @update:modelValue="value => $emit('update:modelValue', value)">
<el-form ref="formRef" :model="form" :rules="rules" label-width="140px"> <el-form ref="formRef" :model="form" :rules="rules" label-width="140px">
<el-form-item label="训练操作视频文件" prop="source_id"> <el-form-item label="训练操作视频文件" prop="source_id">
<UploadVideo :beforeUpload="handleBeforeUpload" @success="handleUploadSuccess"> <UploadVideo :beforeUpload="handleBeforeUpload" @success="handleUploadSuccess">
......
<script setup lang="ts"> <script setup lang="ts">
import type { ContestScoreItem } from '../types' import type { ContestScoreItem } from '../types'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { publishScore } from '../api' import { publishScore } from '../api'
interface Props { interface Props {
data: ContestScoreItem data: ContestScoreItem
}
const props = defineProps<Props>()
const emit = defineEmits<{
(e: 'update'): void
(e: 'update:modelValue', visible: boolean): void
}>()
// 发布
function handleSubmit() {
publishScore({ id: props.data.id }).then(() => {
ElMessage.success('发布成功')
emit('update')
emit('update:modelValue', false)
})
}
</script>
<template>
<el-dialog title="发布成绩" width="500px" :close-on-click-modal="false" @update:modelValue="value => $emit('update:modelValue', value)">
<div class="content">
<p>请确认赛项成绩之后再进行发布成绩!</p>
<p>
赛项名称:<b>{{ data.name }}</b>
</p>
<br />
<p>
<span>报名人数:{{ data.all_competitor_count }}</span>
<span>完赛人数:{{ data.complete_competitor_count }}</span>
<span>已评分人数:{{ data.checked_competitor_count }}</span>
</p>
<br />
<p>请确认是否发布成绩!</p>
</div>
<el-row justify="center" style="margin-top: 40px">
<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>
<style lang="scss" scoped>
.content {
text-align: center;
p {
margin: 10px 0;
} }
const props = defineProps<Props>() b {
color: var(--main-color);
const emit = defineEmits<{
(e: 'update'): void
(e: 'update:modelValue', visible: boolean): void
}>()
// 发布
function handleSubmit() {
publishScore({ id: props.data.id }).then(() => {
ElMessage.success('发布成功')
emit('update')
emit('update:modelValue', false)
})
} }
</script> span + span {
margin-left: 40px;
<template>
<el-dialog
title="发布成绩"
width="500px"
:close-on-click-modal="false"
@update:modelValue="$emit('update:modelValue')">
<div class="content">
<p>请确认赛项成绩之后再进行发布成绩!</p>
<p>
赛项名称:<b>{{ data.name }}</b>
</p>
<br />
<p>
<span>报名人数:{{ data.all_competitor_count }}</span>
<span>完赛人数:{{ data.complete_competitor_count }}</span>
<span>已评分人数:{{ data.checked_competitor_count }}</span>
</p>
<br />
<p>请确认是否发布成绩!</p>
</div>
<el-row justify="center" style="margin-top: 40px">
<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>
<style lang="scss" scoped>
.content {
text-align: center;
p {
margin: 10px 0;
}
b {
color: var(--main-color);
}
span + span {
margin-left: 40px;
}
} }
</style> }
</style>
\ No newline at end of file
...@@ -105,7 +105,7 @@ function handleUpdate(params: BookCreateItem) { ...@@ -105,7 +105,7 @@ function handleUpdate(params: BookCreateItem) {
</script> </script>
<template> <template>
<el-dialog :title="title" :close-on-click-modal="false" width="600px" @update:modelValue="$emit('update:modelValue')"> <el-dialog :title="title" :close-on-click-modal="false" width="600px" @update:modelValue="value => $emit('update:modelValue', value)">
<el-form ref="formRef" :model="form" :rules="rules" label-width="124px"> <el-form ref="formRef" :model="form" :rules="rules" label-width="124px">
<el-form-item label="实验指导书文件" prop="files"> <el-form-item label="实验指导书文件" prop="files">
<AppUpload <AppUpload
......
...@@ -112,7 +112,7 @@ function handleUpdate(params: CaseUpdateItem) { ...@@ -112,7 +112,7 @@ function handleUpdate(params: CaseUpdateItem) {
</script> </script>
<template> <template>
<el-dialog :title="title" :close-on-click-modal="false" width="600px" @update:modelValue="$emit('update:modelValue')"> <el-dialog :title="title" :close-on-click-modal="false" width="600px" @update:modelValue="value => $emit('update:modelValue', value)">
<el-form ref="formRef" :model="form" :rules="rules" label-width="124px"> <el-form ref="formRef" :model="form" :rules="rules" label-width="124px">
<el-form-item label="案例原文文件" prop="files"> <el-form-item label="案例原文文件" prop="files">
<AppUpload <AppUpload
......
...@@ -78,7 +78,7 @@ function handleSubmit() { ...@@ -78,7 +78,7 @@ function handleSubmit() {
</script> </script>
<template> <template>
<el-dialog title="实验话题讨论" :close-on-click-modal="false" @update:modelValue="$emit('update:modelValue')"> <el-dialog title="实验话题讨论" :close-on-click-modal="false" @update:modelValue="value => $emit('update:modelValue', value)">
<div class="discuss-scroll" ref="scrollRef"> <div class="discuss-scroll" ref="scrollRef">
<DiscussItem :info="detail.info" :list="detail.list" v-if="detail.info"></DiscussItem> <DiscussItem :info="detail.info" :list="detail.list" v-if="detail.info"></DiscussItem>
<div class="tips" v-if="isLoading"> <div class="tips" v-if="isLoading">
...@@ -90,13 +90,7 @@ function handleSubmit() { ...@@ -90,13 +90,7 @@ function handleSubmit() {
<div class="tips" v-if="!hasMore">没有更多了</div> <div class="tips" v-if="!hasMore">没有更多了</div>
</div> </div>
<el-form <el-form ref="formRef" :rules="rules" :model="form" style="margin-top: 40px" v-permission="'v1-teacher-discussion-comment'">
ref="formRef"
:rules="rules"
:model="form"
style="margin-top: 40px"
v-permission="'v1-teacher-discussion-comment'"
>
<el-form-item prop="content"> <el-form-item prop="content">
<el-input type="textarea" v-model="form.content" :autosize="{ minRows: 4, maxRows: 6 }" :maxlength="200" /> <el-input type="textarea" v-model="form.content" :autosize="{ minRows: 4, maxRows: 6 }" :maxlength="200" />
</el-form-item> </el-form-item>
......
...@@ -86,11 +86,7 @@ export function getExperimentReportRule(params: { experiment_id: string }) { ...@@ -86,11 +86,7 @@ export function getExperimentReportRule(params: { experiment_id: string }) {
return httpRequest.get('/api/resource/v1/backend/experiment-report/detail', { params }) return httpRequest.get('/api/resource/v1/backend/experiment-report/detail', { params })
} }
// 更新实验报告规则 // 更新实验报告规则
export function updateExperimentReportRule(data: { export function updateExperimentReportRule(data: { experiment_id: string; report_upload_way: number; detail_list: string }) {
experiment_id: string
report_upload_way: number
detail_list: string
}) {
return httpRequest.post('/api/resource/v1/backend/experiment-report/save', data) return httpRequest.post('/api/resource/v1/backend/experiment-report/save', data)
} }
...@@ -153,4 +149,35 @@ export function getQuestionTags(params: { experiment_id: string }) { ...@@ -153,4 +149,35 @@ export function getQuestionTags(params: { experiment_id: string }) {
// 获取群组 // 获取群组
export function getQuestionGroup(params: { experiment_id: string }) { export function getQuestionGroup(params: { experiment_id: string }) {
return httpRequest.get('/api/resource/v1/backend/experiment-question/groups', { params }) return httpRequest.get('/api/resource/v1/backend/experiment-question/groups', { params })
} }
\ No newline at end of file
// 获取实验下的所有理论考试
export function getAllExamList(params: { project: string; q?: string; name?: string; page?: number; 'per-page'?: number }) {
return httpRequest.get('/api/resource/v1/backend/exam/search-all-exam', { params })
}
// 获取实验的理论考试列表
export function getExamList(params: { experiment_id: string, type: string }) {
return httpRequest.get('/api/resource/v1/backend/experiment/exam-list', { params })
}
// 更新实验的理论考试
export function updateExam(data: { experiment_id: string; exam_id: string }) {
return httpRequest.post('/api/resource/v1/backend/experiment/exam-save', data)
}
// 删除实验的理论考试
export function deleteExam(data: { id: string }) {
return httpRequest.post('/api/resource/v1/backend/experiment/exam-delete', data)
}
// 获取实验下的所有理论考试
export function getScoreExamList(params: { experiment_id: string }) {
return httpRequest.get('/api/resource/v1/backend/experiment-score-rule/exams', { params })
}
// 复制实验
export function copyExperiment(data: { experiment_id: string }) {
return httpRequest.post('/api/resource/v1/backend/experiment/copy', data)
}
// 删除实验
export function deleteExperiment(data: { experiment_id: string }) {
return httpRequest.post('/api/resource/v1/backend/experiment/delete', data)
}
<script setup lang="ts">
import type { FormInstance } from 'element-plus'
import type { ExperimentItem } from '../types'
import { ElMessage } from 'element-plus'
import { copyExperiment } from '../api'
import { useGetProjectList } from '@/composables/useGetProjectList'
import { useGetTeacherList } from '../composables/useGetTeacherList'
import { pickBy } from 'lodash-es'
const props = defineProps<{
data: ExperimentItem
}>()
const emit = defineEmits<{
(e: 'update'): void
(e: 'update:modelValue', visible: boolean): void
}>()
const formRef = ref<FormInstance>()
const form = reactive({
experiment_name: '',
organ_id: '',
sso_id: ''
})
onMounted(() => {
form.organ_id = props.data.organ_id
form.experiment_name = props.data.name + '(copy)'
const [teacher] = props.data.teacher
form.sso_id = teacher.id
})
// 机构列表
const { organizations } = useGetProjectList()
// 指导教师列表
const { teachers, updateTeachers } = useGetTeacherList()
watchEffect(() => {
updateTeachers(form.organ_id)
})
function handleOrgChange() {
form.sso_id = ''
}
async function handleSubmit() {
formRef.value?.validate().then(async () => {
const params = { ...pickBy(form, item => item !== ''), experiment_id: props.data.id }
await copyExperiment(params)
ElMessage.success('复制成功')
emit('update')
emit('update:modelValue', false)
})
}
</script>
<template>
<el-dialog title="复制实验" width="600px" @update:modelValue="value => $emit('update:modelValue', value)">
<el-form ref="formRef" :model="form" label-width="130px">
<el-form-item label="实验名称" prop="experiment_name">
<el-input v-model="form.experiment_name" />
</el-form-item>
<el-form-item label="实验所属部门/学校" prop="organ_id">
<el-select v-model="form.organ_id" style="width: 100%" @change="handleOrgChange" clearable>
<el-option v-for="item in organizations" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="指导老师" prop="sso_id">
<el-select v-model="form.sso_id" style="width: 100%" clearable>
<el-option v-for="item in teachers" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-row justify="center">
<el-button round auto-insert-space @click="$emit('update:modelValue', false)">关闭</el-button>
<el-button type="primary" round auto-insert-space @click="handleSubmit">复制</el-button>
</el-row>
</template></el-dialog
>
</template>
...@@ -37,6 +37,9 @@ const form = reactive({ ...@@ -37,6 +37,9 @@ const form = reactive({
user_attr_config: { is_all: true, items: [] }, user_attr_config: { is_all: true, items: [] },
event_config: { is_all: true, items: [] }, event_config: { is_all: true, items: [] },
is_use_common: 0, is_use_common: 0,
is_use_common_tags: 0,
is_use_common_groups: 0,
is_use_common_materials: 0,
ids: ['教师维护的用户和事件数据'], ids: ['教师维护的用户和事件数据'],
tag_ids: [], tag_ids: [],
group_ids: [], group_ids: [],
...@@ -73,6 +76,9 @@ function fetchInfo() { ...@@ -73,6 +76,9 @@ function fetchInfo() {
user_attr_config, user_attr_config,
event_config, event_config,
is_use_common: data.is_use_common, is_use_common: data.is_use_common,
is_use_common_tags: data.is_use_common_tags,
is_use_common_groups: data.is_use_common_groups,
is_use_common_materials: data.is_use_common_materials,
tag_ids, tag_ids,
group_ids, group_ids,
material_ids material_ids
...@@ -118,7 +124,12 @@ function handleSubmit() { ...@@ -118,7 +124,12 @@ function handleSubmit() {
</script> </script>
<template> <template>
<el-dialog title="配置数字营销实验" :close-on-click-modal="false" width="600px" @update:modelValue="$emit('update:modelValue')"> <el-dialog
title="配置数字营销实验"
:close-on-click-modal="false"
width="600px"
@update:modelValue="value => $emit('update:modelValue', value)"
>
<el-form ref="formRef" :model="form" label-suffix=":"> <el-form ref="formRef" :model="form" label-suffix=":">
<el-row justify="space-between"> <el-row justify="space-between">
<el-form-item label="实验名称">{{ data.name }}</el-form-item> <el-form-item label="实验名称">{{ data.name }}</el-form-item>
...@@ -141,7 +152,13 @@ function handleSubmit() { ...@@ -141,7 +152,13 @@ function handleSubmit() {
</el-form-item> </el-form-item>
<el-form-item label="连接" label-width="82" prop="connect_ids"> <el-form-item label="连接" label-width="82" prop="connect_ids">
<el-select v-model="form.connect_ids" multiple style="width: 100%"> <el-select v-model="form.connect_ids" multiple style="width: 100%">
<el-option v-for="item in connectionList" :label="item.name" :value="item.id" :key="item.id" disabled></el-option> <el-option
v-for="item in connectionList"
:label="item.name"
:value="item.id"
:key="item.id"
disabled
></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-tab-pane> </el-tab-pane>
...@@ -151,7 +168,12 @@ function handleSubmit() { ...@@ -151,7 +168,12 @@ function handleSubmit() {
<el-radio :label="true">全部</el-radio> <el-radio :label="true">全部</el-radio>
<el-radio :label="false">部分</el-radio> <el-radio :label="false">部分</el-radio>
</el-radio-group> </el-radio-group>
<el-select v-model="form.user_attr_config.items" multiple style="margin-left: 40px" v-if="!form.user_attr_config.is_all"> <el-select
v-model="form.user_attr_config.items"
multiple
style="margin-left: 40px"
v-if="!form.user_attr_config.is_all"
>
<el-option v-for="item in userAttrList" :label="item.name" :value="item.id" :key="item.id"></el-option> <el-option v-for="item in userAttrList" :label="item.name" :value="item.id" :key="item.id"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
...@@ -160,12 +182,69 @@ function handleSubmit() { ...@@ -160,12 +182,69 @@ function handleSubmit() {
<el-radio :label="true">全部</el-radio> <el-radio :label="true">全部</el-radio>
<el-radio :label="false">部分</el-radio> <el-radio :label="false">部分</el-radio>
</el-radio-group> </el-radio-group>
<el-select v-model="form.event_config.items" multiple style="margin-left: 40px" v-if="!form.event_config.is_all"> <el-select
v-model="form.event_config.items"
multiple
style="margin-left: 40px"
v-if="!form.event_config.is_all"
>
<el-option v-for="item in metaEventList" :label="item.name" :value="item.id" :key="item.id"></el-option> <el-option v-for="item in metaEventList" :label="item.name" :value="item.id" :key="item.id"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="旅程资源" :name="2"> <el-tab-pane label="用户/事件数据" :name="2">
<el-form-item label="是否允许学生新建如下资源">
<el-radio-group v-model="form.is_use_common">
<el-radio :label="1"></el-radio>
<el-radio :label="0"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="用户/事件数据" label-width="118">
<el-select v-model="form.ids" multiple style="width: 100%" disabled>
<el-option value="教师维护的用户和事件数据"></el-option>
</el-select>
</el-form-item>
</el-tab-pane>
<el-tab-pane label="标签数据" :name="3">
<el-form-item label="是否允许学生新建如下资源">
<el-radio-group v-model="form.is_use_common_tags">
<el-radio :label="1"></el-radio>
<el-radio :label="0"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="标签数据" label-width="118" prop="tag_ids">
<el-select v-model="form.tag_ids" multiple style="width: 100%">
<el-option v-for="item in tagList" :label="item.name" :value="item.id" :key="item.id"></el-option>
</el-select>
</el-form-item>
</el-tab-pane>
<el-tab-pane label="用户群组" :name="4">
<el-form-item label="是否允许学生新建如下资源">
<el-radio-group v-model="form.is_use_common_groups">
<el-radio :label="1"></el-radio>
<el-radio :label="0"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="用户群组" label-width="118" prop="group_ids">
<el-select v-model="form.group_ids" multiple style="width: 100%">
<el-option v-for="item in groupList" :label="item.name" :value="item.id" :key="item.id"></el-option>
</el-select>
</el-form-item>
</el-tab-pane>
<el-tab-pane label="营销资料" :name="5">
<el-form-item label="是否允许学生新建如下资源">
<el-radio-group v-model="form.is_use_common_materials">
<el-radio :label="1"></el-radio>
<el-radio :label="0"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="营销资料" label-width="118" prop="material_ids">
<el-select v-model="form.material_ids" multiple style="width: 100%">
<el-option v-for="item in materialList" :label="item.name" :value="item.id" :key="item.id"></el-option>
</el-select>
</el-form-item>
</el-tab-pane>
<!-- <el-tab-pane label="旅程资源" :name="10">
<el-form-item label="是否允许学生新建如下资源"> <el-form-item label="是否允许学生新建如下资源">
<el-radio-group v-model="form.is_use_common"> <el-radio-group v-model="form.is_use_common">
<el-radio :label="1">否</el-radio> <el-radio :label="1">否</el-radio>
...@@ -195,7 +274,7 @@ function handleSubmit() { ...@@ -195,7 +274,7 @@ function handleSubmit() {
</el-select> </el-select>
</el-form-item> </el-form-item>
</template> </template>
</el-tab-pane> </el-tab-pane> -->
</el-tabs> </el-tabs>
</el-form> </el-form>
<template #footer> <template #footer>
......
...@@ -8,6 +8,8 @@ import { useGetProjectList } from '@/composables/useGetProjectList' ...@@ -8,6 +8,8 @@ import { useGetProjectList } from '@/composables/useGetProjectList'
import { useGetCourseList } from '../composables/useGetCourseList' import { useGetCourseList } from '../composables/useGetCourseList'
import { useGetTeacherList } from '../composables/useGetTeacherList' import { useGetTeacherList } from '../composables/useGetTeacherList'
import { useMapStore } from '@/stores/map' import { useMapStore } from '@/stores/map'
import { useAppConfig } from '@/composables/useAppConfig'
const appConfig = useAppConfig()
interface Props { interface Props {
data?: ExperimentItem | null data?: ExperimentItem | null
...@@ -41,7 +43,8 @@ const form = reactive<ExperimentCreateItem>({ ...@@ -41,7 +43,8 @@ const form = reactive<ExperimentCreateItem>({
purpose: '', purpose: '',
requirements: '', requirements: '',
content: '', content: '',
procedure: '' procedure: '',
exam_status: '0'
}) })
watchEffect(() => { watchEffect(() => {
if (!props.data) return if (!props.data) return
...@@ -111,7 +114,12 @@ function handleUpdate(params: ExperimentCreateItem) { ...@@ -111,7 +114,12 @@ function handleUpdate(params: ExperimentCreateItem) {
</script> </script>
<template> <template>
<el-dialog :title="title" :close-on-click-modal="false" width="600px" @update:modelValue="$emit('update:modelValue')"> <el-dialog
:title="title"
:close-on-click-modal="false"
width="600px"
@update:modelValue="value => $emit('update:modelValue', value)"
>
<el-form ref="formRef" :model="form" :rules="rules" label-width="145px"> <el-form ref="formRef" :model="form" :rules="rules" label-width="145px">
<el-form-item label="实验所属部门/学校" prop="organ_id"> <el-form-item label="实验所属部门/学校" prop="organ_id">
<el-select v-model="form.organ_id" style="width: 100%" :disabled="isUpdate" @change="handleOrgChange"> <el-select v-model="form.organ_id" style="width: 100%" :disabled="isUpdate" @change="handleOrgChange">
...@@ -124,7 +132,7 @@ function handleUpdate(params: ExperimentCreateItem) { ...@@ -124,7 +132,7 @@ function handleUpdate(params: ExperimentCreateItem) {
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="实验名称" prop="name"> <el-form-item label="实验名称" prop="name">
<el-input v-model="form.name" :disabled="isUpdate" /> <el-input v-model="form.name" />
</el-form-item> </el-form-item>
<el-form-item label="实验学时" prop="length"> <el-form-item label="实验学时" prop="length">
<el-input-number v-model="form.length" :min="1" :max="20" step-strictly style="width: 100%" /> <el-input-number v-model="form.length" :min="1" :max="20" step-strictly style="width: 100%" />
...@@ -142,6 +150,13 @@ function handleUpdate(params: ExperimentCreateItem) { ...@@ -142,6 +150,13 @@ function handleUpdate(params: ExperimentCreateItem) {
<el-form-item label="实验总成绩" prop="score"> <el-form-item label="实验总成绩" prop="score">
<el-input-number v-model="form.score" :min="1" :max="150" step-strictly style="width: 100%" /> <el-input-number v-model="form.score" :min="1" :max="150" step-strictly style="width: 100%" />
</el-form-item> </el-form-item>
<el-form-item label="是否有理论考试" v-if="appConfig.system === 'x'">
<el-radio-group v-model="form.exam_status">
<el-radio label="0"></el-radio>
<el-radio label="1"></el-radio>
</el-radio-group>
<!-- <el-input-number v-model="form.score" :min="1" :max="150" step-strictly style="width: 100%" /> -->
</el-form-item>
<el-form-item label="实验目的" prop="purpose"> <el-form-item label="实验目的" prop="purpose">
<el-input <el-input
v-model="form.purpose" v-model="form.purpose"
...@@ -149,7 +164,8 @@ function handleUpdate(params: ExperimentCreateItem) { ...@@ -149,7 +164,8 @@ function handleUpdate(params: ExperimentCreateItem) {
type="textarea" type="textarea"
maxlength="200" maxlength="200"
show-word-limit show-word-limit
placeholder="请输入实验目的" /> placeholder="请输入实验目的"
/>
</el-form-item> </el-form-item>
<el-form-item label="实验要求" prop="requirements"> <el-form-item label="实验要求" prop="requirements">
<el-input <el-input
...@@ -158,7 +174,8 @@ function handleUpdate(params: ExperimentCreateItem) { ...@@ -158,7 +174,8 @@ function handleUpdate(params: ExperimentCreateItem) {
type="textarea" type="textarea"
maxlength="200" maxlength="200"
show-word-limit show-word-limit
placeholder="请输入实验要求" /> placeholder="请输入实验要求"
/>
</el-form-item> </el-form-item>
<el-form-item label="实验内容及原理" prop="content"> <el-form-item label="实验内容及原理" prop="content">
<el-input <el-input
...@@ -167,7 +184,8 @@ function handleUpdate(params: ExperimentCreateItem) { ...@@ -167,7 +184,8 @@ function handleUpdate(params: ExperimentCreateItem) {
type="textarea" type="textarea"
maxlength="200" maxlength="200"
show-word-limit show-word-limit
placeholder="请输入实验内容及原理" /> placeholder="请输入实验内容及原理"
/>
</el-form-item> </el-form-item>
<el-form-item label="实验步骤及过程" prop="procedure"> <el-form-item label="实验步骤及过程" prop="procedure">
<el-input <el-input
...@@ -176,7 +194,8 @@ function handleUpdate(params: ExperimentCreateItem) { ...@@ -176,7 +194,8 @@ function handleUpdate(params: ExperimentCreateItem) {
type="textarea" type="textarea"
maxlength="200" maxlength="200"
show-word-limit show-word-limit
placeholder="请输入实验步骤及过程" /> placeholder="请输入实验步骤及过程"
/>
</el-form-item> </el-form-item>
<el-form-item label="有效状态" prop="status"> <el-form-item label="有效状态" prop="status">
<el-radio-group v-model="form.status"> <el-radio-group v-model="form.status">
......
...@@ -4,7 +4,9 @@ import type { ExperimentItem } from '../types' ...@@ -4,7 +4,9 @@ import type { ExperimentItem } from '../types'
import { Plus } from '@element-plus/icons-vue' import { Plus } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { gradeRule, gradeRuleList } from '@/utils/dictionary' import { gradeRule, gradeRuleList } from '@/utils/dictionary'
import { getExperimentGradeRule, updateExperimentGradeRule } from '../api' import { getExperimentGradeRule, updateExperimentGradeRule, getScoreExamList } from '../api'
import { useAppConfig } from '@/composables/useAppConfig'
const appConfig = useAppConfig()
interface Props { interface Props {
data: ExperimentItem data: ExperimentItem
...@@ -21,7 +23,9 @@ const formRef = $ref<FormInstance>() ...@@ -21,7 +23,9 @@ const formRef = $ref<FormInstance>()
const form = reactive<any>({ const form = reactive<any>({
experiment_id: props.data.id, experiment_id: props.data.id,
is_show: '1', is_show: '1',
rule_list: [{ name: '实验报告', type: 1, percent: 100, rule_mode: 1 }] // rule_list: [{ name: '实验报告', type: 1, percent: 100, rule_mode: 1 }],
rule_list: [],
exam_rules: []
}) })
function fetchInfo() { function fetchInfo() {
...@@ -39,15 +43,31 @@ function fetchInfo() { ...@@ -39,15 +43,31 @@ function fetchInfo() {
Object.assign(form, detail, { rule_list: ruleList }) Object.assign(form, detail, { rule_list: ruleList })
}) })
} }
const examList = ref<any>([])
async function fetchExamList() {
const res = await getScoreExamList({ experiment_id: props.data.id })
const items = res.data.items || []
examList.value = items.map((item: any) => {
return { ...item, ...item.exam_info }
})
}
onMounted(() => { onMounted(() => {
fetchInfo() fetchInfo()
fetchExamList()
}) })
// 合计 // 合计
const total = $computed(() => { const total = $computed(() => {
return form.rule_list.reduce((result: number, item: any) => { const examTotal = form.exam_rules.reduce((result: number, item: any) => {
return result + (item.percent || 0) return result + (item.percent || 0)
}, 0) }, 0)
return (
form.rule_list.reduce((result: number, item: any) => {
return result + (item.percent || 0)
}, 0) + examTotal
)
}) })
// 添加 // 添加
...@@ -60,19 +80,32 @@ function handleRemove(index: number) { ...@@ -60,19 +80,32 @@ function handleRemove(index: number) {
} }
// 提交 // 提交
function handleSubmit(call?: any) { function handleSubmit(call?: any) {
for (let i = 0; i < form.rule_list.length; i++) { for (let i = 0; i < form.exam_rules.length; i++) {
const item = form.rule_list[i] const item = form.exam_rules[i]
if (!item.name || !item.type) { if (!item.exam_id) {
ElMessage.error(`第${i + 1}行规则配置错误,请检查后重试`) ElMessage.error(`理论考试${i + 1}行规则配置错误,请检查后重试`)
return return
} }
if (!item.percent) { if (!item.percent) {
ElMessage.error(`${item.name}权重不能为0!`) ElMessage.error(`理论考试权重不能为0!`)
return return
} }
if (item.type === 5 && /实验报告|实验准备|实验结果|课堂活跃度/.test(item.name)) { }
ElMessage.error(`第${i + 1}行规则配置错误,自定义的名称不能包含“实验报告”、“实验准备”、“实验结果”和“课堂活跃度”`) if (appConfig?.system !== 'x') {
return for (let i = 0; i < form.rule_list.length; i++) {
const item = form.rule_list[i]
if (!item.name || !item.type) {
ElMessage.error(`实操考试第${i + 1}行规则配置错误,请检查后重试`)
return
}
if (!item.percent) {
ElMessage.error(`${item.name}权重不能为0!`)
return
}
if (item.type === 5 && /实验报告|实验准备|实验结果|课堂活跃度/.test(item.name)) {
ElMessage.error(`第${i + 1}行规则配置错误,自定义的名称不能包含“实验报告”、“实验准备”、“实验结果”和“课堂活跃度”`)
return
}
} }
} }
if (total < 100) { if (total < 100) {
...@@ -116,16 +149,22 @@ function handleTypeChange(row: any) { ...@@ -116,16 +149,22 @@ function handleTypeChange(row: any) {
row.name = gradeRule[row.type] row.name = gradeRule[row.type]
} }
// 占比改变 // 占比改变
function handlePercentChange(row: any, index: number) { function handlePercentChange(row: any, index: number, type: number) {
const examTotal = form.exam_rules.reduce((result: number, item: any, i: number) => {
if (index !== i || type !== 2) {
result += item.percent || 0
}
return result
}, 0)
const otherTotal = form.rule_list.reduce((result: number, item: any, i: number) => { const otherTotal = form.rule_list.reduce((result: number, item: any, i: number) => {
if (index !== i) { if (index !== i || type !== 1) {
result += item.percent || 0 result += item.percent || 0
} }
return result return result
}, 0) }, 0)
// 最大可输入占比 // 最大可输入占比
nextTick(() => { nextTick(() => {
row.percent = Math.min(100 - otherTotal, row.percent) row.percent = Math.min(100 - otherTotal - examTotal, row.percent)
}) })
} }
...@@ -142,15 +181,20 @@ const handleEdit = function (type: number) { ...@@ -142,15 +181,20 @@ const handleEdit = function (type: number) {
) )
}) })
} }
// 添加
function handleAddExamRule() {
form.exam_rules.push({ id: '0', exam_id: '', percent: undefined, is_auto_scoring: '1' })
}
// 删除
function handleRemoveExamRule(index: number) {
form.exam_rules.splice(index, 1)
}
</script> </script>
<template> <template>
<el-dialog <el-dialog title="编辑实验成绩规则" :close-on-click-modal="false" width="800px" @update:modelValue="value => $emit('update:modelValue', value)">
title="编辑实验成绩规则"
:close-on-click-modal="false"
width="800px"
@update:modelValue="$emit('update:modelValue')"
>
<el-form ref="formRef" :model="form" label-suffix=":"> <el-form ref="formRef" :model="form" label-suffix=":">
<el-form-item label="实验名称">{{ data?.name }}</el-form-item> <el-form-item label="实验名称">{{ data?.name }}</el-form-item>
<el-row> <el-row>
...@@ -166,30 +210,58 @@ const handleEdit = function (type: number) { ...@@ -166,30 +210,58 @@ const handleEdit = function (type: number) {
<el-form-item label="实验总成绩">{{ data?.score }}</el-form-item> <el-form-item label="实验总成绩">{{ data?.score }}</el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<el-form-item label="实验成绩规则"></el-form-item>
<el-divider></el-divider>
<el-form-item>
<el-row justify="space-between" style="width: 100%">
<p>理论考试:</p>
<el-button type="primary" :icon="Plus" @click="handleAddExamRule" :disabled="form.exam_rules.length >= 1"> </el-button>
</el-row>
<el-table :data="form.exam_rules" row-key="id">
<el-table-column prop="name" width="170">
<template #default="{ row }">
<el-select v-model="row.exam_id" style="width: 100%">
<el-option v-for="item in examList" :key="item.id" :label="item.name" :value="item.exam_id"></el-option>
</el-select>
</template>
</el-table-column>
<el-table-column prop="percent" align="center" width="200">
<template #default="{ row, $index }"> <el-input-number v-model="row.percent" :min="0" @change="handlePercentChange(row, $index, 2)" /> % </template>
</el-table-column>
<el-table-column prop="is_auto_scoring" width="200">
<template #default="{ row }">
<el-radio-group v-model="row.is_auto_scoring" size="small">
<el-radio label="1">自动评分</el-radio>
<!-- <el-radio label="2">人工评分</el-radio> -->
</el-radio-group>
</template>
</el-table-column>
<el-table-column width="90">
<template #default="{ row }">满分:{{ 100 || rowScore(row.percent) }} </template>
</el-table-column>
<el-table-column align="right">
<template #default="{ $index, row }">
<el-button style="padding: 0" text type="primary" @click="handleRemoveExamRule($index)" v-if="row.type !== 1">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-form-item>
<el-form-item> <el-form-item>
<el-row justify="space-between" style="width: 100%"> <el-row justify="space-between" style="width: 100%">
<p>验成绩规则</p> <p>操考试</p>
<el-button type="primary" :icon="Plus" @click="handleAdd"></el-button> <el-button type="primary" :icon="Plus" @click="handleAdd"></el-button>
</el-row> </el-row>
<el-table :data="form.rule_list" row-key="id"> <el-table :data="form.rule_list" row-key="id">
<el-table-column prop="name" width="170"> <el-table-column prop="name" width="170">
<template #default="{ row }"> <template #default="{ row }">
<el-input v-model="row.name" :maxlength="20" style="width: 100%" v-if="row.type === 5" /> <el-input v-model="row.name" :maxlength="20" style="width: 100%" v-if="row.type === 5" />
<el-select <el-select v-model="row.type" :disabled="row.type === 1" style="width: 100%" @change="handleTypeChange(row)" v-else>
v-model="row.type"
:disabled="row.type === 1"
style="width: 100%"
@change="handleTypeChange(row)"
v-else
>
<el-option v-for="item in currentRuleNames(row.type)" :key="item.value" v-bind="item"></el-option> <el-option v-for="item in currentRuleNames(row.type)" :key="item.value" v-bind="item"></el-option>
</el-select> </el-select>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="percent" align="center" width="200"> <el-table-column prop="percent" align="center" width="200">
<template #default="{ row, $index }"> <template #default="{ row, $index }"> <el-input-number v-model="row.percent" :min="0" @change="handlePercentChange(row, $index, 1)" /> % </template>
<el-input-number v-model="row.percent" :min="0" @change="handlePercentChange(row, $index)" /> %
</template>
</el-table-column> </el-table-column>
<el-table-column prop="rule_mode" width="200"> <el-table-column prop="rule_mode" width="200">
<template #default="{ row }"> <template #default="{ row }">
...@@ -207,18 +279,10 @@ const handleEdit = function (type: number) { ...@@ -207,18 +279,10 @@ const handleEdit = function (type: number) {
<template #default="{ $index, row }"> <template #default="{ $index, row }">
<div class="btn-box"> <div class="btn-box">
<!-- || row.type === 8 --> <!-- || row.type === 8 -->
<el-button <el-button :disabled="row.type === 1" style="padding: 0" text type="primary" @click="handleEdit(row.type)" v-if="row.type !== 1"
:disabled="row.type === 1"
style="padding: 0"
text
type="primary"
@click="handleEdit(row.type)"
v-if="row.type !== 1"
>编辑</el-button >编辑</el-button
> >
<el-button style="padding: 0" text type="primary" @click="handleRemove($index)" v-if="row.type !== 1" <el-button style="padding: 0" text type="primary" @click="handleRemove($index)">删除</el-button>
>删除</el-button
>
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
...@@ -238,7 +302,7 @@ const handleEdit = function (type: number) { ...@@ -238,7 +302,7 @@ const handleEdit = function (type: number) {
<style lang="scss" scoped> <style lang="scss" scoped>
.total { .total {
width: 100%; width: 100%;
padding: 10px 200px; padding: 10px 170px;
box-sizing: border-box; box-sizing: border-box;
p { p {
margin-left: 16px; margin-left: 16px;
......
...@@ -19,12 +19,7 @@ const selectAnswer = ['302', '202'] ...@@ -19,12 +19,7 @@ const selectAnswer = ['302', '202']
</script> </script>
<template> <template>
<el-dialog <el-dialog title="2023商业数据分析大赛决赛试题" :close-on-click-modal="false" width="50%" @update:modelValue="value => $emit('update:modelValue', value)">
title="2023商业数据分析大赛决赛试题"
:close-on-click-modal="false"
width="50%"
@update:modelValue="$emit('update:modelValue')"
>
<el-card class="box-card" v-for="(item, index) in list" :key="item.id"> <el-card class="box-card" v-for="(item, index) in list" :key="item.id">
<template #header> <template #header>
<div class="card-header"> <div class="card-header">
...@@ -32,9 +27,7 @@ const selectAnswer = ['302', '202'] ...@@ -32,9 +27,7 @@ const selectAnswer = ['302', '202']
<el-form-item label="本题分值" style="margin: 0"> {{ item.score }} </el-form-item> <el-form-item label="本题分值" style="margin: 0"> {{ item.score }} </el-form-item>
</div> </div>
</template> </template>
<div class="text item"> <div class="text item">正确答案:{{ ['302', '202'].includes(item.type) ? item.answer_info?.name : item?.answer || '上传成功' }}</div>
正确答案:{{ ['302', '202'].includes(item.type) ? item.answer_info?.name : item?.answer || '上传成功' }}
</div>
</el-card> </el-card>
</el-dialog> </el-dialog>
</template> </template>
......
...@@ -42,13 +42,11 @@ function handleSubmit() { ...@@ -42,13 +42,11 @@ function handleSubmit() {
} }
</script> </script>
<template> <template>
<el-dialog title="关联班级" @update:modelValue="$emit('update:modelValue')"> <el-dialog title="关联班级" @update:modelValue="value => $emit('update:modelValue', value)">
<p>所属机构/学校:{{ detail.organ_id_name }}</p> <p>所属机构/学校:{{ detail.organ_id_name }}</p>
<AppList v-bind="listOptions" @selection-change="handleSelectionChange"></AppList> <AppList v-bind="listOptions" @selection-change="handleSelectionChange"></AppList>
<el-row justify="center"> <el-row justify="center">
<el-button type="primary" round :disabled="!multipleSelection.length" @click="handleSubmit" <el-button type="primary" round :disabled="!multipleSelection.length" @click="handleSubmit">关联选择班级</el-button>
>关联选择班级</el-button
>
<el-button round auto-insert-space @click="$emit('update:modelValue', false)">取消</el-button> <el-button round auto-insert-space @click="$emit('update:modelValue', false)">取消</el-button>
</el-row> </el-row>
</el-dialog> </el-dialog>
......
...@@ -42,12 +42,10 @@ function handleSubmit() { ...@@ -42,12 +42,10 @@ function handleSubmit() {
} }
</script> </script>
<template> <template>
<el-dialog title="添加小组成员" @update:modelValue="$emit('update:modelValue')"> <el-dialog title="添加小组成员" @update:modelValue="value => $emit('update:modelValue', value)">
<AppList v-bind="listOptions" @selection-change="handleSelectionChange"></AppList> <AppList v-bind="listOptions" @selection-change="handleSelectionChange"></AppList>
<el-row justify="center"> <el-row justify="center">
<el-button type="primary" round :disabled="!multipleSelection.length" @click="handleSubmit" <el-button type="primary" round :disabled="!multipleSelection.length" @click="handleSubmit">添加选择学生</el-button>
>添加选择学生</el-button
>
<el-button round auto-insert-space @click="$emit('update:modelValue', false)">取消</el-button> <el-button round auto-insert-space @click="$emit('update:modelValue', false)">取消</el-button>
</el-row> </el-row>
</el-dialog> </el-dialog>
......
...@@ -38,11 +38,7 @@ const handleCreate = () => { ...@@ -38,11 +38,7 @@ const handleCreate = () => {
</script> </script>
<template> <template>
<el-dialog <el-dialog title="新增实验分组" :close-on-click-modal="false" width="600px" @update:modelValue="value => $emit('update:modelValue', value)">
title="新增实验分组"
:close-on-click-modal="false"
width="600px"
@update:modelValue="$emit('update:modelValue')">
<el-form ref="formRef" :model="form" :rules="rules" label-width="110px"> <el-form ref="formRef" :model="form" :rules="rules" label-width="110px">
<el-form-item label="实验名称">{{ detail.name }}</el-form-item> <el-form-item label="实验名称">{{ detail.name }}</el-form-item>
<el-form-item label="实验小组名称" prop="name"> <el-form-item label="实验小组名称" prop="name">
......
...@@ -43,6 +43,7 @@ const columns = computed(() => { ...@@ -43,6 +43,7 @@ const columns = computed(() => {
{ label: '序号', type: 'index', width: 60 }, { label: '序号', type: 'index', width: 60 },
{ label: '学号', prop: 'id_number' }, { label: '学号', prop: 'id_number' },
{ label: '姓名', prop: 'name' }, { label: '姓名', prop: 'name' },
{ label: '手机号', prop: 'mobile' },
{ label: '性别', prop: 'gender_name' }, { label: '性别', prop: 'gender_name' },
{ label: '所属部门/学校', prop: 'organ_id_name' }, { label: '所属部门/学校', prop: 'organ_id_name' },
{ label: '专业', prop: 'specialty_id_name' }, { label: '专业', prop: 'specialty_id_name' },
......
<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 { getExamList, deleteExam } from '../api'
const FormDialog = defineAsyncComponent(() => import('./ViewExamFormDialog.vue'))
interface Props {
id: string
type: string
}
const props = defineProps<Props>()
const appList = $ref<InstanceType<typeof AppList> | null>(null)
// 列表配置
const listOptions = {
hasPagination: false,
remote: {
httpRequest: getExamList,
params: { experiment_id: props.id, type: props.type },
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: 220 }
]
}
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 = `${import.meta.env.VITE_QA_CENTER_URL}/paper/detail/${paper.paper_id}`
window.open(qaURL)
}
// 删除
function handleRemove(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
v-if="props.type === '2'"
link
round
type="primary"
@click="handleView(row)"
v-permission="'competition-book-update'"
>查阅</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="handleRemove(row)" v-permission="'competition-book-delete'"
>删除</el-button
>
</template>
</AppList>
<FormDialog
v-model="dialogVisible"
:id="id"
:type="props.type"
:data="rowData"
@update="onUpdateSuccess"
v-if="dialogVisible"
></FormDialog>
</template>
<script setup lang="ts">
import type { FormInstance, FormRules } from 'element-plus'
import { ElMessage } from 'element-plus'
import { pick } from 'lodash-es'
import { updateExam, getAllExamList } from '../api'
interface Props {
id: string
data?: any
type: string
}
const props = defineProps<Props>()
const emit = defineEmits<{
(e: 'update'): void
(e: 'update:modelValue', visible: boolean): void
}>()
const formRef = ref<FormInstance>()
const form = reactive<any>({
experiment_id: props.id,
exam_id: ''
})
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 ? '编辑理论考试' : '新增理论考试'
})
const examList = ref<Record<string, any>[]>([])
// 获取关联考试列表
function fetchExamList() {
getAllExamList({ project: 'x1', 'per-page': 1000 }).then(res => {
examList.value = res.data.list || []
})
}
onMounted(() => {
fetchExamList()
})
// 提交
function handleSubmit() {
formRef.value?.validate().then(() => {
const params = Object.assign({ type: props.type }, pick(form, ['experiment_id', 'exam_id']))
isUpdate ? handleUpdate(params) : handleUpdate(params)
})
}
// 修改
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="value => $emit('update:modelValue', value)">
<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-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>
...@@ -31,6 +31,7 @@ export interface ExperimentItem { ...@@ -31,6 +31,7 @@ export interface ExperimentItem {
content: string content: string
procedure: string procedure: string
stu_commit_count: number stu_commit_count: number
exam_status?: string
} }
export interface ExperimentCreateItem { export interface ExperimentCreateItem {
...@@ -48,6 +49,7 @@ export interface ExperimentCreateItem { ...@@ -48,6 +49,7 @@ export interface ExperimentCreateItem {
requirements: string requirements: string
content: string content: string
procedure: string procedure: string
exam_status: string
} }
export interface ClassItem { export interface ClassItem {
......
<script setup lang="ts"> <script setup lang="ts">
import type { ExperimentItem } from '../types' import type { ExperimentItem } from '../types'
import { CirclePlus } from '@element-plus/icons-vue' import { CirclePlus, MoreFilled, CopyDocument, Delete, Setting, Edit, EditPen } from '@element-plus/icons-vue'
import AppList from '@/components/base/AppList.vue' import AppList from '@/components/base/AppList.vue'
import { getExperimentList } from '../api' import { getExperimentList, deleteExperiment } from '../api'
import { useGetCourseList } from '../composables/useGetCourseList' import { useGetCourseList } from '../composables/useGetCourseList'
import { useGetCourseExperimentList } from '../composables/useGetCourseExperimentList' import { useGetCourseExperimentList } from '../composables/useGetCourseExperimentList'
import { useMapStore } from '@/stores/map' import { useMapStore } from '@/stores/map'
import { ElMessage, ElMessageBox } from 'element-plus'
const FormDialog = defineAsyncComponent(() => import('../components/FormDialog.vue')) const FormDialog = defineAsyncComponent(() => import('../components/FormDialog.vue'))
const GradeRulesDialog = defineAsyncComponent(() => import('../components/GradeRulesDialog.vue')) const GradeRulesDialog = defineAsyncComponent(() => import('../components/GradeRulesDialog.vue'))
const DMLFormDialog = defineAsyncComponent(() => import('../components/DMLFormDialog.vue')) const DMLFormDialog = defineAsyncComponent(() => import('../components/DMLFormDialog.vue'))
const CopyDialog = defineAsyncComponent(() => import('../components/CopyDialog.vue'))
// 数据状态 // 数据状态
const status = useMapStore().getMapValuesByKey('system_status') const status = useMapStore().getMapValuesByKey('system_status')
...@@ -93,7 +95,7 @@ const listOptions = $computed(() => { ...@@ -93,7 +95,7 @@ const listOptions = $computed(() => {
{ label: '生效状态', prop: 'status_name' }, { label: '生效状态', prop: 'status_name' },
{ label: '更新人', prop: 'updated_operator_name' }, { label: '更新人', prop: 'updated_operator_name' },
{ label: '更新时间', prop: 'updated_time' }, { label: '更新时间', prop: 'updated_time' },
{ label: '操作', slots: 'table-x', width: 200 } { label: '操作', slots: 'table-x', width: 220 }
] ]
} }
}) })
...@@ -128,19 +130,56 @@ function handleUpdateDML(row: ExperimentItem) { ...@@ -128,19 +130,56 @@ function handleUpdateDML(row: ExperimentItem) {
rowData.value = row rowData.value = row
dmlDialogVisible = true dmlDialogVisible = true
} }
// 复制
let copyDialogVisible = $ref(false)
async function handleCopy(row: ExperimentItem) {
rowData.value = row
copyDialogVisible = true
}
// 删除
async function handleDelete(row: ExperimentItem) {
ElMessageBox.confirm('确定要删除该实验吗?', '提示').then(async () => {
await deleteExperiment({ experiment_id: row.id })
ElMessage.success('删除成功')
appList?.refetch()
})
}
</script> </script>
<template> <template>
<AppCard title="实验管理"> <AppCard title="实验管理">
<AppList v-bind="listOptions" ref="appList"> <AppList v-bind="listOptions" ref="appList">
<template #header-buttons> <template #header-buttons>
<el-button type="primary" :icon="CirclePlus" v-permission="'v1-backend-experiment-create'" @click="handleAdd" <el-button type="primary" :icon="CirclePlus" v-permission="'v1-backend-experiment-create'" @click="handleAdd">新增实验</el-button>
>新增实验</el-button
>
</template> </template>
<template #table-x="{ row }: { row: ExperimentItem }"> <template #table-x="{ row }: { row: ExperimentItem }">
<template v-if="row.type === '4'"> <el-button type="primary" round v-permission="'v1-backend-experiment-view'">
<router-link :to="`/admin/lab/experiment/${row.id}`" target="_blank">查看</router-link>
</el-button>
<el-button type="primary" round @click="handleUpdate(row)" v-permission="'v1-backend-experiment-update'">编辑</el-button>
<el-dropdown style="margin-left: 12px">
<el-button type="primary" round :icon="MoreFilled"></el-button>
<template #dropdown>
<el-dropdown-menu>
<!-- <el-dropdown-item>
<router-link :to="`/admin/lab/experiment/${row.id}`" target="_blank">查看</router-link>
</el-dropdown-item>
<el-dropdown-item @click="handleUpdate(row)">编辑</el-dropdown-item> -->
<el-dropdown-item :icon="Setting" @click="handleUpdateDML(row)" v-if="row.type === '4'">配置数字营销</el-dropdown-item>
<template v-if="!row.stu_commit_count">
<el-dropdown-item :icon="Edit" @click="handleUpdateGradeRules(row)">编辑成绩规则</el-dropdown-item>
<el-dropdown-item :icon="EditPen">
<router-link :to="`/admin/lab/experiment/report/${row.id}`" target="_blank">编辑报告规则</router-link>
</el-dropdown-item>
</template>
<el-dropdown-item :icon="CopyDocument" @click="handleCopy(row)">复制</el-dropdown-item>
<el-dropdown-item :icon="Delete" @click="handleDelete(row)">删除</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<!-- <template v-if="row.type === '4'">
<el-button <el-button
type="primary" type="primary"
round round
...@@ -159,11 +198,7 @@ function handleUpdateDML(row: ExperimentItem) { ...@@ -159,11 +198,7 @@ function handleUpdateDML(row: ExperimentItem) {
v-permission="'v1-backend-experiment-scorerule-detail'" v-permission="'v1-backend-experiment-scorerule-detail'"
>编辑成绩规则</el-button >编辑成绩规则</el-button
><br /> ><br />
<el-button <el-button type="info" round style="width: 132px; margin-bottom: 12px" v-permission="'v1-backend-experiment-report-detail'">
type="info"
round
style="width: 132px; margin-bottom: 12px"
v-permission="'v1-backend-experiment-report-detail'">
<router-link :to="`/admin/lab/experiment/report/${row.id}`" target="_blank">编辑报告规则</router-link> <router-link :to="`/admin/lab/experiment/report/${row.id}`" target="_blank">编辑报告规则</router-link>
</el-button> </el-button>
<br /> <br />
...@@ -171,22 +206,18 @@ function handleUpdateDML(row: ExperimentItem) { ...@@ -171,22 +206,18 @@ function handleUpdateDML(row: ExperimentItem) {
<el-button type="primary" round v-permission="'v1-backend-experiment-view'"> <el-button type="primary" round v-permission="'v1-backend-experiment-view'">
<router-link :to="`/admin/lab/experiment/${row.id}`" target="_blank">查看</router-link> <router-link :to="`/admin/lab/experiment/${row.id}`" target="_blank">查看</router-link>
</el-button> </el-button>
<!-- <el-button type="primary" round v-permission="'v1-backend-experiment-view'"> <el-button type="primary" round @click="handleUpdate(row)" v-permission="'v1-backend-experiment-update'">编辑</el-button>
<router-link :to="`/admin/lab/experiment/${row.id}`" target="_blank">关联班级与分组</router-link> <div style="margin-top: 12px">
</el-button> --> <el-button type="primary" round @click="handleCopy(row)">复制</el-button>
<el-button type="primary" round @click="handleUpdate(row)" v-permission="'v1-backend-experiment-update'" <el-button type="primary" round @click="handleDelete(row)">删除</el-button>
>编辑</el-button </div> -->
>
</template> </template>
</AppList> </AppList>
</AppCard> </AppCard>
<FormDialog v-model="dialogVisible" :data="rowData" @update="onUpdateSuccess" v-if="dialogVisible"></FormDialog> <FormDialog v-model="dialogVisible" :data="rowData" @update="onUpdateSuccess" v-if="dialogVisible"></FormDialog>
<!-- 编辑实验成绩规则 --> <!-- 编辑实验成绩规则 -->
<GradeRulesDialog <GradeRulesDialog v-model="gradeRulesDialogVisible" :data="rowData" @update="onUpdateSuccess" v-if="gradeRulesDialogVisible && rowData"></GradeRulesDialog>
v-model="gradeRulesDialogVisible"
:data="rowData"
@update="onUpdateSuccess"
v-if="gradeRulesDialogVisible && rowData"></GradeRulesDialog>
<!-- 配置数字营销实验 --> <!-- 配置数字营销实验 -->
<DMLFormDialog v-model="dmlDialogVisible" :data="rowData" v-if="dmlDialogVisible && rowData"></DMLFormDialog> <DMLFormDialog v-model="dmlDialogVisible" :data="rowData" v-if="dmlDialogVisible && rowData"></DMLFormDialog>
<CopyDialog v-model="copyDialogVisible" :data="rowData" @update="onUpdateSuccess" v-if="copyDialogVisible && rowData"></CopyDialog>
</template> </template>
...@@ -13,6 +13,7 @@ const StudentGroupDialog = defineAsyncComponent(() => import('../components/Stud ...@@ -13,6 +13,7 @@ const StudentGroupDialog = defineAsyncComponent(() => import('../components/Stud
const StudentListDialog = defineAsyncComponent(() => import('../components/StudentListDialog.vue')) const StudentListDialog = defineAsyncComponent(() => import('../components/StudentListDialog.vue'))
const ViewGradeRules = defineAsyncComponent(() => import('../components/ViewGradeRules.vue')) const ViewGradeRules = defineAsyncComponent(() => import('../components/ViewGradeRules.vue'))
const ViewReportRules = defineAsyncComponent(() => import('../components/ViewReportRules.vue')) const ViewReportRules = defineAsyncComponent(() => import('../components/ViewReportRules.vue'))
const ViewExam = defineAsyncComponent(() => import('../components/ViewExam.vue'))
interface Props { interface Props {
id: string id: string
...@@ -113,21 +114,53 @@ const dmlURL = computed(() => { ...@@ -113,21 +114,53 @@ const dmlURL = computed(() => {
<el-divider /> <el-divider />
<AppList v-bind="listOptions" ref="appList"> <AppList v-bind="listOptions" ref="appList">
<template #header-buttons> <template #header-buttons>
<el-button type="primary" :icon="CirclePlus" @click="selectClassVisible = true" v-permission="'v1-backend-experiment-class-add'">关联班级</el-button> <el-button
type="primary"
:icon="CirclePlus"
@click="selectClassVisible = true"
v-permission="'v1-backend-experiment-class-add'"
>关联班级</el-button
>
</template> </template>
<template #table-x="{ row }"> <template #table-x="{ row }">
<el-button type="primary" round @click="handleViewStudent(row)" v-permission="'v1-backend-experiment-class-students'">查看学生</el-button> <el-button
<el-button type="primary" round @click="handleStudentGroup(row)" v-permission="'v1-backend-experiment-class-teams'">学生分组</el-button> type="primary"
<el-button type="primary" round @click="handleRemoveClass(row)" v-permission="'v1-backend-experiment-class-add'">移除</el-button> round
@click="handleViewStudent(row)"
v-permission="'v1-backend-experiment-class-students'"
>查看学生</el-button
>
<el-button
type="primary"
round
@click="handleStudentGroup(row)"
v-permission="'v1-backend-experiment-class-teams'"
>学生分组</el-button
>
<el-button type="primary" round @click="handleRemoveClass(row)" v-permission="'v1-backend-experiment-class-add'"
>移除</el-button
>
</template> </template>
</AppList> </AppList>
</AppCard> </AppCard>
<AppCard :title="detail?.exam_status === '0' ? '训练考试' : '理论考试'">
<ViewExam v-if="detail" :id="id" :type="detail?.exam_status === '0' ? '2' : '1'"></ViewExam>
</AppCard>
<!-- 关联班级 --> <!-- 关联班级 -->
<SelectClassDialog v-model="selectClassVisible" @update="handleRefetch" v-if="selectClassVisible"></SelectClassDialog> <SelectClassDialog v-model="selectClassVisible" @update="handleRefetch" v-if="selectClassVisible"></SelectClassDialog>
<!-- 学生分组 --> <!-- 学生分组 -->
<StudentGroupDialog v-model="studentGroupVisible" :data="rowData" v-if="studentGroupVisible && rowData"></StudentGroupDialog> <StudentGroupDialog
v-model="studentGroupVisible"
:data="rowData"
v-if="studentGroupVisible && rowData"
></StudentGroupDialog>
<!-- 查看学生 --> <!-- 查看学生 -->
<StudentListDialog v-model="studentListVisible" :data="rowData" :experimentId="id" v-if="studentListVisible && rowData"></StudentListDialog> <StudentListDialog
v-model="studentListVisible"
:data="rowData"
:experimentId="id"
v-if="studentListVisible && rowData"
></StudentListDialog>
<ViewGradeRules v-model="gradeRulesVisible" :data="detail" v-if="gradeRulesVisible && detail"></ViewGradeRules> <ViewGradeRules v-model="gradeRulesVisible" :data="detail" v-if="gradeRulesVisible && detail"></ViewGradeRules>
<ViewReportRules v-model="reportRulesVisible" :experiment_id="id" v-if="reportRulesVisible"></ViewReportRules> <ViewReportRules v-model="reportRulesVisible" :experiment_id="id" v-if="reportRulesVisible"></ViewReportRules>
</template> </template>
...@@ -24,13 +24,7 @@ export function getExperimentRecord(params: { experiment_id: string; student_id: ...@@ -24,13 +24,7 @@ export function getExperimentRecord(params: { experiment_id: string; student_id:
} }
// 实验记录评分 // 实验记录评分
export function checkExperimentRecord(data: { export function checkExperimentRecord(data: { experiment_id: string; student_id: string; operate: number; result: number; file: number }) {
experiment_id: string
student_id: string
operate: number
result: number
file: number
}) {
return httpRequest.post('/api/lab/v1/teacher/record/check', data) return httpRequest.post('/api/lab/v1/teacher/record/check', data)
} }
...@@ -56,12 +50,7 @@ export function getExperimentReport(params: { experiment_id: string; student_id: ...@@ -56,12 +50,7 @@ export function getExperimentReport(params: { experiment_id: string; student_id:
return httpRequest.get('/api/lab/v1/teacher/experiment/report-achievement', { params }) return httpRequest.get('/api/lab/v1/teacher/experiment/report-achievement', { params })
} }
// 批改学员实验报告成绩 // 批改学员实验报告成绩
export function updateExperimentReport(data: { export function updateExperimentReport(data: { experiment_id: string; student_id: string; score_detail: string; comment: string }) {
experiment_id: string
student_id: string
score_detail: string
comment: string
}) {
return httpRequest.post('/api/lab/v1/teacher/experiment/report-check', data) return httpRequest.post('/api/lab/v1/teacher/experiment/report-check', data)
} }
...@@ -95,10 +84,25 @@ export function updateQuestionScore(data: { question_id: string; student_id: str ...@@ -95,10 +84,25 @@ export function updateQuestionScore(data: { question_id: string; student_id: str
} }
// 同步学生分数 // 同步学生分数
export function asyncStudentResult(data: { experiment_id: string; student_id: string; }) { export function asyncStudentResult(data: { experiment_id: string; student_id: string }) {
return httpRequest.post('/api/lab/v1/teacher/experiment-question/async-student-result', data) return httpRequest.post('/api/lab/v1/teacher/experiment-question/async-student-result', data)
} }
export function getStudentTicket(params: { student_id: string }) { export function getStudentTicket(params: { student_id: string }) {
return httpRequest.get('/api/lab/v1/teacher/system/get-student-ticket', { params }) return httpRequest.get('/api/lab/v1/teacher/system/get-student-ticket', { params })
} }
// 批量导出实验记录评分
export function exportExperimentRecord(params: { experiment_id: string }) {
return httpRequest.get('/api/lab/v1/teacher/record/download', { params })
}
// 同步理论成绩
export function syncExperimentExam(params: { experiment_id: string }) {
return httpRequest.get('/api/lab/v1/teacher/record/sync-theory-score', { params })
}
// 重置
export function recordReject(params: { experiment_id: string; student_id: string }) {
return httpRequest.get('/api/lab/v1/teacher/record/reject', { params })
}
<script setup lang="ts">
import { useFilterList } from '../composables/useFilterList'
const { experiments } = useFilterList()
const form = reactive<any>({
experiment_id: ''
})
const exportUrl = computed(() => {
return `/api/lab/v1/teacher/record/download?experiment_id=${form.experiment_id}`
})
</script>
<template>
<el-dialog title="批量导出" :close-on-click-modal="false" width="400px">
<el-form label-width="70px">
<el-form-item label="实验名称">
<el-select v-model="form.experiment_id">
<el-option v-for="item in experiments" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
&nbsp;&nbsp;
<a :href="exportUrl" target="_blank">
<el-button type="primary" :disabled="!form.experiment_id">导出</el-button>
</a>
</el-form-item>
</el-form>
</el-dialog>
</template>
...@@ -51,6 +51,9 @@ const fetchList = function () { ...@@ -51,6 +51,9 @@ const fetchList = function () {
) { ) {
setData('4', '资料管理') setData('4', '资料管理')
} }
if (b.type === '501') {
setData('5', '用户旅程')
}
function setData(type: string, name: string) { function setData(type: string, name: string) {
const index = a.findIndex((item: { type: string }) => item.type === type) const index = a.findIndex((item: { type: string }) => item.type === type)
b.order = i + 1 b.order = i + 1
......
...@@ -149,7 +149,7 @@ function getOperationUrl(type: number) { ...@@ -149,7 +149,7 @@ function getOperationUrl(type: number) {
</script> </script>
<template> <template>
<el-dialog title="学生实验评分" :close-on-click-modal="false" width="800px" @update:modelValue="$emit('update:modelValue')"> <el-dialog title="学生实验评分" :close-on-click-modal="false" width="800px" @update:modelValue="value => $emit('update:modelValue', value)">
<el-form label-width="120px" label-suffix=":" v-if="detail"> <el-form label-width="120px" label-suffix=":" v-if="detail">
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
...@@ -176,6 +176,7 @@ function getOperationUrl(type: number) { ...@@ -176,6 +176,7 @@ function getOperationUrl(type: number) {
</el-col> </el-col>
</el-row> </el-row>
<el-divider /> <el-divider />
<el-form-item label="理论成绩">{{ data.theory_score }}</el-form-item>
<el-form-item label="实验成绩"> <el-form-item label="实验成绩">
<el-row justify="end" style="width: 100%"> <el-row justify="end" style="width: 100%">
<el-button type="primary" @click="prepareVisible = true">查看实验准备</el-button> <el-button type="primary" @click="prepareVisible = true">查看实验准备</el-button>
...@@ -212,7 +213,7 @@ function getOperationUrl(type: number) { ...@@ -212,7 +213,7 @@ function getOperationUrl(type: number) {
<el-table-column label="操作" prop="commit_score" align="center"> <el-table-column label="操作" prop="commit_score" align="center">
<template #default="{ row }"> <template #default="{ row }">
<template v-if="row.type === 1"> <template v-if="row.type === 1">
<el-button text type="primary" @click="$emit('update:modelValue')" v-if="experiment.report_upload_way === 2"> <el-button text type="primary" v-if="experiment.report_upload_way === 2">
<a :href="getOperationUrl(row.type)" target="_blank">批改</a> <a :href="getOperationUrl(row.type)" target="_blank">批改</a>
</el-button> </el-button>
<template v-if="experiment.report_upload_way === 1"> <template v-if="experiment.report_upload_way === 1">
......
<script setup lang="ts">
import { ElMessage } from 'element-plus'
import { useFilterList } from '../composables/useFilterList'
import { syncExperimentExam } from '../api'
const emit = defineEmits<{
(e: 'update'): void
}>()
const { experiments } = useFilterList()
const form = reactive({ experiment_id: '' })
// 同步
function handleSync() {
syncExperimentExam({ experiment_id: form.experiment_id }).then(() => {
ElMessage.success('同步成功')
emit('update')
})
}
</script>
<template>
<el-dialog title="同步1+X考试成绩" :close-on-click-modal="false" width="440px">
<el-form>
<el-form-item label="实验名称">
<el-select v-model="form.experiment_id" value-key="id">
<el-option v-for="item in experiments" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
&nbsp;&nbsp;
<el-button type="primary" :disabled="!form.experiment_id" @click="handleSync">一键同步成绩</el-button>
</el-form-item>
</el-form>
</el-dialog>
</template>
...@@ -19,6 +19,7 @@ export interface RecordItem { ...@@ -19,6 +19,7 @@ export interface RecordItem {
student_id: string student_id: string
student_name: string student_name: string
score_log: ScoreLog[] score_log: ScoreLog[]
theory_score: string
} }
export interface FileItem { export interface FileItem {
......
<script setup lang="ts"> <script setup lang="ts">
import type { RecordItem } from '../types' import type { RecordItem } from '../types'
import { Upload, Promotion } from '@element-plus/icons-vue' import { Upload, Promotion, Download, Refresh } from '@element-plus/icons-vue'
import AppList from '@/components/base/AppList.vue' import AppList from '@/components/base/AppList.vue'
import { getExperimentRecordList } from '../api' import { getExperimentRecordList, recordReject } from '../api'
import { useFilterList } from '../composables/useFilterList' import { useFilterList } from '../composables/useFilterList'
import { useCookies } from '@vueuse/integrations/useCookies' import { useCookies } from '@vueuse/integrations/useCookies'
import { ElMessage } from 'element-plus'
const ScoreDialog = defineAsyncComponent(() => import('../components/ScoreDialog.vue')) const ScoreDialog = defineAsyncComponent(() => import('../components/ScoreDialog.vue'))
const ImportDialog = defineAsyncComponent(() => import('../components/ImportDialog.vue')) const ImportDialog = defineAsyncComponent(() => import('../components/ImportDialog.vue'))
const ExportDialog = defineAsyncComponent(() => import('../components/ExportDialog.vue'))
const SyncExamDialog = defineAsyncComponent(() => import('../components/SyncExamDialog.vue'))
const ScoreLogDialog = defineAsyncComponent(() => import('../components/ScoreLogDialog.vue')) const ScoreLogDialog = defineAsyncComponent(() => import('../components/ScoreLogDialog.vue'))
const { courses, experiments, specialties, classes } = useFilterList() const { courses, experiments, specialties, classes } = useFilterList()
...@@ -26,9 +29,9 @@ const params = reactive({ ...@@ -26,9 +29,9 @@ const params = reactive({
class_id: '' class_id: ''
}) })
const classList = $computed(() => { const classList = $computed(() => {
const specialty = specialties.value.find(item => item.id === params.specialty_id) const specialty = specialties.value.find((item) => item.id === params.specialty_id)
if (specialty) { if (specialty) {
return classes.value.filter(item => item.specialty_id === specialty.id) return classes.value.filter((item) => item.specialty_id === specialty.id)
} }
return classes.value return classes.value
}) })
...@@ -95,7 +98,7 @@ const listOptions = $computed(() => { ...@@ -95,7 +98,7 @@ const listOptions = $computed(() => {
{ label: '学号', prop: 'sno_number' }, { label: '学号', prop: 'sno_number' },
{ label: '实验名称', prop: 'experiment_name' }, { label: '实验名称', prop: 'experiment_name' },
{ label: '提交状态', prop: 'status_name', slots: 'table-status' }, { label: '提交状态', prop: 'status_name', slots: 'table-status' },
{ label: '实验成绩', prop: 'score', slots: 'table-score' }, { label: '考试成绩', prop: 'score', slots: 'table-score' },
{ label: '更新时间', prop: 'commit_time' }, { label: '更新时间', prop: 'commit_time' },
{ label: '操作', slots: 'table-x', width: 210 } { label: '操作', slots: 'table-x', width: 210 }
] ]
...@@ -118,6 +121,15 @@ function handleScoreLog(row: RecordItem) { ...@@ -118,6 +121,15 @@ function handleScoreLog(row: RecordItem) {
function onUpdateSuccess() { function onUpdateSuccess() {
appList?.refetch() appList?.refetch()
} }
const exportVisible = ref(false)
const syncVisible = ref(false)
async function handleReset(row: RecordItem) {
await recordReject({ experiment_id: row.experiment_id, student_id: row.student_id })
ElMessage.success('重置成功')
appList?.refetch()
}
</script> </script>
<template> <template>
...@@ -125,7 +137,13 @@ function onUpdateSuccess() { ...@@ -125,7 +137,13 @@ function onUpdateSuccess() {
<AppList v-bind="listOptions" ref="appList"> <AppList v-bind="listOptions" ref="appList">
<template #header-buttons> <template #header-buttons>
<el-row justify="space-between"> <el-row justify="space-between">
<el-button type="primary" round :icon="Upload" @click="importVisible = true" v-permission="'v1-teacher-record-upload'">批量导入</el-button> <div>
<el-button type="primary" round :icon="Upload" @click="importVisible = true" v-permission="'v1-teacher-record-upload'">批量导入</el-button>
<el-button type="primary" round :icon="Download" @click="exportVisible = true" v-permission="'v1-teacher-record-download'">批量导出</el-button>
<el-button type="primary" round :icon="Refresh" @click="syncVisible = true" v-permission="'v1-teacher-record-sync-theory-score'"
>同步理论成绩</el-button
>
</div>
<!-- <!--
<a :href="LAB_URL" target="_blank"> <a :href="LAB_URL" target="_blank">
<el-button type="primary" round :icon="Promotion">进入实验室</el-button> <el-button type="primary" round :icon="Promotion">进入实验室</el-button>
...@@ -143,6 +161,9 @@ function onUpdateSuccess() { ...@@ -143,6 +161,9 @@ function onUpdateSuccess() {
<el-button text type="primary" v-if="row.status === 1 || row.status === 2" @click="handleScore(row)" v-permission="'v1-teacher-record-check'" <el-button text type="primary" v-if="row.status === 1 || row.status === 2" @click="handleScore(row)" v-permission="'v1-teacher-record-check'"
>打分</el-button >打分</el-button
> >
<<<<<<< HEAD =======
<el-button text type="primary" v-if="row.status === 1 || row.status === 2" @click="handleReset(row)">重置</el-button>
>>>>>>> master
<el-button text type="primary" v-if="row.has_score_log" @click="handleScoreLog(row)" v-permission="'v1-teacher-record-check'">查看历史成绩</el-button> <el-button text type="primary" v-if="row.has_score_log" @click="handleScoreLog(row)" v-permission="'v1-teacher-record-check'">查看历史成绩</el-button>
</template> </template>
</AppList> </AppList>
...@@ -153,6 +174,10 @@ function onUpdateSuccess() { ...@@ -153,6 +174,10 @@ function onUpdateSuccess() {
<ScoreLogDialog v-model="scoreLogVisible" :data="rowData" v-if="scoreLogVisible && rowData"></ScoreLogDialog> <ScoreLogDialog v-model="scoreLogVisible" :data="rowData" v-if="scoreLogVisible && rowData"></ScoreLogDialog>
<!-- 批量导入 --> <!-- 批量导入 -->
<ImportDialog v-model="importVisible" @update="onUpdateSuccess" v-if="importVisible"></ImportDialog> <ImportDialog v-model="importVisible" @update="onUpdateSuccess" v-if="importVisible"></ImportDialog>
<!-- 批量导出 -->
<ExportDialog v-model="exportVisible" v-if="exportVisible"></ExportDialog>
<!-- 同步理论成绩 -->
<SyncExamDialog v-model="syncVisible" v-if="syncVisible"></SyncExamDialog>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
......
...@@ -56,7 +56,7 @@ const getIframeUrl = function () { ...@@ -56,7 +56,7 @@ const getIframeUrl = function () {
</template> </template>
<template #right> <template #right>
<div class="lab-box"> <div class="lab-box">
<iframe :src="iframeUrl" frameborder="0" class="iframe" ref="iframeRef"></iframe> <iframe allowfullscreen :src="iframeUrl" frameborder="0" class="iframe" ref="iframeRef"></iframe>
</div> </div>
</template> </template>
</DragPanel> </DragPanel>
......
...@@ -66,10 +66,7 @@ const title = $computed(() => { ...@@ -66,10 +66,7 @@ const title = $computed(() => {
function handleBeforeUpload() { function handleBeforeUpload() {
if (form.source_id) { if (form.source_id) {
return ElMessageBox.confirm( return ElMessageBox.confirm('系统仅支持1个实验操作视频,此操作将覆盖原有实验操作视频文件,确认上传新文件吗?', '提示')
'系统仅支持1个实验操作视频,此操作将覆盖原有实验操作视频文件,确认上传新文件吗?',
'提示'
)
} }
return true return true
} }
...@@ -104,7 +101,7 @@ function handleUpdate(params: VideoCreateItem) { ...@@ -104,7 +101,7 @@ function handleUpdate(params: VideoCreateItem) {
</script> </script>
<template> <template>
<el-dialog :title="title" :close-on-click-modal="false" width="600px" @update:modelValue="$emit('update:modelValue')"> <el-dialog :title="title" :close-on-click-modal="false" width="600px" @update:modelValue="value => $emit('update:modelValue', value)">
<el-form ref="formRef" :model="form" :rules="rules" label-width="140px"> <el-form ref="formRef" :model="form" :rules="rules" label-width="140px">
<el-form-item label="实验操作视频文件" prop="source_id"> <el-form-item label="实验操作视频文件" prop="source_id">
<UploadVideo :beforeUpload="handleBeforeUpload" @success="handleUploadSuccess"> <UploadVideo :beforeUpload="handleBeforeUpload" @success="handleUploadSuccess">
......
...@@ -20,6 +20,7 @@ const currentExam = computed(() => { ...@@ -20,6 +20,7 @@ const currentExam = computed(() => {
// 考试平台 URL // 考试平台 URL
const examURL = computed(() => { const examURL = computed(() => {
if (!currentExam.value) return ''
return `${import.meta.env.VITE_EXAM_SHOW_URL}/exam/${currentExam.value?.exam_id}?has_time=0&has_submit=0&has_save=1&show_answer=1` return `${import.meta.env.VITE_EXAM_SHOW_URL}/exam/${currentExam.value?.exam_id}?has_time=0&has_submit=0&has_save=1&show_answer=1`
// return `https://dev.ezijing.com:5173/exam/7003551966412406784?has_time=0&has_submit=0&has_save=1&show_answer=1` // return `https://dev.ezijing.com:5173/exam/7003551966412406784?has_time=0&has_submit=0&has_save=1&show_answer=1`
}) })
...@@ -36,6 +37,6 @@ onMounted(() => { ...@@ -36,6 +37,6 @@ onMounted(() => {
</script> </script>
<template> <template>
<iframe class="iframe" :src="examURL" frameborder="0" v-if="currentExam"></iframe> <iframe allowfullscreen class="iframe" :src="examURL" frameborder="0" v-if="currentExam"></iframe>
<el-empty description="暂无数据" v-else /> <el-empty description="暂无数据" v-else />
</template> </template>
...@@ -56,11 +56,7 @@ const update = () => { ...@@ -56,11 +56,7 @@ const update = () => {
</script> </script>
<template> <template>
<el-dialog <el-dialog title="上传实验报告" :close-on-click-modal="false" width="600px" @update:modelValue="value => $emit('update:modelValue', value)">
title="上传实验报告"
:close-on-click-modal="false"
width="600px"
@update:modelValue="$emit('update:modelValue')">
<el-form ref="formRef" :model="form" :rules="rules"> <el-form ref="formRef" :model="form" :rules="rules">
<el-form-item label="实验报告文件" prop="files"> <el-form-item label="实验报告文件" prop="files">
<AppUpload <AppUpload
......
...@@ -183,7 +183,7 @@ const reportDialogVisible = $ref(false) ...@@ -183,7 +183,7 @@ const reportDialogVisible = $ref(false)
</g> </g>
</svg> </svg>
</div> </div>
<iframe :src="competitionUrl" :key="iframeKey" frameborder="0" class="iframe" ref="iframeRef"></iframe> <iframe allowfullscreen :src="competitionUrl" :key="iframeKey" frameborder="0" class="iframe" ref="iframeRef"></iframe>
</div> </div>
</template> </template>
</DragPanel> </DragPanel>
......
...@@ -30,12 +30,7 @@ export function getExperimentVideoPlayInfo(params: { source_id: string }) { ...@@ -30,12 +30,7 @@ export function getExperimentVideoPlayInfo(params: { source_id: string }) {
} }
// 获取实验讨论交流 // 获取实验讨论交流
export function getExperimentDiscussList(params: { export function getExperimentDiscussList(params: { experiment_id: string; tag: number; page?: number; 'per-page'?: number }) {
experiment_id: string
tag: number
page?: number
'per-page'?: number
}) {
return httpRequest.get('/api/lab/v1/student/experiment-topic/list', { params }) return httpRequest.get('/api/lab/v1/student/experiment-topic/list', { params })
} }
// 发表新话题 // 发表新话题
...@@ -90,21 +85,11 @@ export function getExperimentReportTemplate(params: { experiment_id: string }) { ...@@ -90,21 +85,11 @@ export function getExperimentReportTemplate(params: { experiment_id: string }) {
return httpRequest.get('/api/lab/v1/student/experiment/report-template', { params }) return httpRequest.get('/api/lab/v1/student/experiment/report-template', { params })
} }
// 更新实验在线报告 // 更新实验在线报告
export function updateExperimentReport(data: { export function updateExperimentReport(data: { experiment_id: string; experiment_address: string; experiment_date: string; detail: string }) {
experiment_id: string
experiment_address: string
experiment_date: string
detail: string
}) {
return httpRequest.post('/api/lab/v1/student/experiment/upload-online-report', data) return httpRequest.post('/api/lab/v1/student/experiment/upload-online-report', data)
} }
// 缓存实验在线报告 // 缓存实验在线报告
export function cacheExperimentReport(data: { export function cacheExperimentReport(data: { experiment_id: string; experiment_address: string; experiment_date: string; detail: string }) {
experiment_id: string
experiment_address: string
experiment_date: string
detail: string
}) {
return httpRequest.post('/api/lab/v1/student/experiment/cache-online-report', data) return httpRequest.post('/api/lab/v1/student/experiment/cache-online-report', data)
} }
// 获取实验在线报告缓存 // 获取实验在线报告缓存
...@@ -130,3 +115,8 @@ export function getExperimentQuestionList(params: { experiment_id: string }) { ...@@ -130,3 +115,8 @@ export function getExperimentQuestionList(params: { experiment_id: string }) {
export function getExperimentQuestion(params: { experiment_id: string; id: string }) { export function getExperimentQuestion(params: { experiment_id: string; id: string }) {
return httpRequest.get('/api/lab/v1/student/experiment-question/detail', { params }) return httpRequest.get('/api/lab/v1/student/experiment-question/detail', { params })
} }
// 获取实验理论考试列表
export function getExperimentExamList(params: { experiment_id: string }) {
return httpRequest.get('/api/lab/v1/student/experiment-exam/exams', { params })
}
...@@ -49,7 +49,7 @@ function handleRemove(row) { ...@@ -49,7 +49,7 @@ function handleRemove(row) {
</script> </script>
<template> <template>
<el-dialog append-to-body title="关联截图" width="800px" @update:modelValue="$emit('update:modelValue')"> <el-dialog append-to-body title="关联截图" width="800px" @update:modelValue="value => $emit('update:modelValue', value)">
<el-table :data="currentPictures" stripe> <el-table :data="currentPictures" stripe>
<el-table-column prop="name"> <el-table-column prop="name">
<template #default="{ row }"> <template #default="{ row }">
......
<script setup lang="ts">
import { useCookies } from '@vueuse/integrations/useCookies'
import { getExperimentExamList } from '../api'
import { useAppConfig } from '@/composables/useAppConfig'
const appConfig = useAppConfig()
interface Props {
experiment_id: string
examStatus?: number
}
const props = defineProps<Props>()
const model = defineModel()
const cookies = useCookies()
const list = ref<any[]>([])
// 当前选中的考试
const currentExam = computed(() => {
return list.value[0]
})
// 考试平台 URL
const examURL = computed(() => {
if (!currentExam.value) return ''
return appConfig.system !== 'x' || props.examStatus !== 0
? `${import.meta.env.VITE_EXAM_SHOW_URL}/exam/${currentExam.value?.exam_id}`
: `${import.meta.env.VITE_EXAM_SHOW_URL}/exam/${
currentExam.value?.exam_id
}?has_time=0&has_submit=0&has_save=1&show_answer=1`
// return `https://dev.ezijing.com:5173/exam/7003551966412406784?has_time=0&has_submit=0&has_save=1&show_answer=1`
})
async function fetchInfo() {
const res = await getExperimentExamList({ experiment_id: props.experiment_id })
const resCookies = res.data.cookies
cookies.set(resCookies.key, resCookies.auth_key, { domain: '.ezijing.com', path: '/' })
list.value = res.data.items || []
model.value = examURL.value
}
onMounted(() => {
fetchInfo()
})
</script>
<template>
<template v-if="currentExam">
<el-form label-suffix=":" label-position="top" v-if="appConfig.system !== 'x'">
<el-form-item label="考试名称">{{ currentExam.exam_info.name }}</el-form-item>
<el-form-item label="考试时间"
>{{ currentExam.exam_info.start_time }}{{ currentExam.exam_info.end_time }}</el-form-item
>
</el-form>
<div style="width: 100%; height: 100%" v-if="props.examStatus === 0">
<iframe style="width: 100%; height: 100%" allowfullscreen class="iframe" :src="examURL" frameborder="0"></iframe>
</div>
<!-- <teleport to=".lab-box"> -->
<!-- </teleport> -->
</template>
<el-empty description="暂无数据" v-else />
</template>
...@@ -48,12 +48,7 @@ const update = () => { ...@@ -48,12 +48,7 @@ const update = () => {
</script> </script>
<template> <template>
<el-dialog <el-dialog append-to-body title="实验准备" :close-on-click-modal="false" width="800px" @update:modelValue="value => $emit('update:modelValue', value)">
append-to-body
title="实验准备"
:close-on-click-modal="false"
width="800px"
@update:modelValue="$emit('update:modelValue')">
<el-form ref="formRef" :model="form" :rules="rules" label-suffix=":"> <el-form ref="formRef" :model="form" :rules="rules" label-suffix=":">
<el-form-item label="实验名称">{{ data.name }} </el-form-item> <el-form-item label="实验名称">{{ data.name }} </el-form-item>
<el-form-item prop="detail"> <el-form-item prop="detail">
......
...@@ -4,7 +4,8 @@ import { Document } from '@element-plus/icons-vue' ...@@ -4,7 +4,8 @@ import { Document } from '@element-plus/icons-vue'
import { filesize } from 'filesize' import { filesize } from 'filesize'
interface Props { interface Props {
experiment_id: string experiment_id: string,
exam_status?: number
} }
interface QuestionListItem { interface QuestionListItem {
experiment_id: string experiment_id: string
...@@ -110,7 +111,7 @@ function customFloor(num: number) { ...@@ -110,7 +111,7 @@ function customFloor(num: number) {
<el-card shadow="never" class="question-item" v-if="questionDetail"> <el-card shadow="never" class="question-item" v-if="questionDetail">
<h3 class="question-item__title">{{ getQuestionTypeName(questionDetail.type) }}</h3> <h3 class="question-item__title">{{ getQuestionTypeName(questionDetail.type) }}</h3>
<p class="question-item__stem"> <p class="question-item__stem">
{{ questionIndex }}{{ questionDetail.title }} <span>{{ questionDetail.score }}分)</span> {{ questionIndex }}{{ questionDetail.title }} <span v-if="props?.exam_status !== 1">{{ questionDetail.score }}分)</span>
</p> </p>
<p class="question-item__content">{{ questionDetail.content }}</p> <p class="question-item__content">{{ questionDetail.content }}</p>
<ul class="question-item__files"> <ul class="question-item__files">
......
...@@ -45,11 +45,7 @@ const update = () => { ...@@ -45,11 +45,7 @@ const update = () => {
</script> </script>
<template> <template>
<el-dialog <el-dialog title="上传实验报告" :close-on-click-modal="false" width="600px" @update:modelValue="value => $emit('update:modelValue', value)">
title="上传实验报告"
:close-on-click-modal="false"
width="600px"
@update:modelValue="$emit('update:modelValue')">
<el-form ref="formRef" :model="form" :rules="rules"> <el-form ref="formRef" :model="form" :rules="rules">
<el-form-item label="实验报告文件" prop="files"> <el-form-item label="实验报告文件" prop="files">
<AppUpload <AppUpload
......
...@@ -48,12 +48,7 @@ const update = () => { ...@@ -48,12 +48,7 @@ const update = () => {
</script> </script>
<template> <template>
<el-dialog <el-dialog append-to-body title="实验结果" :close-on-click-modal="false" width="800px" @update:modelValue="value => $emit('update:modelValue', value)">
append-to-body
title="实验结果"
:close-on-click-modal="false"
width="800px"
@update:modelValue="$emit('update:modelValue')">
<el-form ref="formRef" :model="form" :rules="rules" label-suffix=":"> <el-form ref="formRef" :model="form" :rules="rules" label-suffix=":">
<el-form-item label="实验名称">{{ data.name }} </el-form-item> <el-form-item label="实验名称">{{ data.name }} </el-form-item>
<el-form-item prop="detail"> <el-form-item prop="detail">
......
...@@ -140,6 +140,7 @@ export interface ExperimentInfo { ...@@ -140,6 +140,7 @@ export interface ExperimentInfo {
student: ExperimentStudent student: ExperimentStudent
is_commit_report: boolean is_commit_report: boolean
is_commit: boolean is_commit: boolean
exam_status: number
} }
interface IdName { interface IdName {
......
...@@ -25,6 +25,7 @@ const ReportFilePreview = defineAsyncComponent(() => import('../components/Repor ...@@ -25,6 +25,7 @@ const ReportFilePreview = defineAsyncComponent(() => import('../components/Repor
const PrepareDialog = defineAsyncComponent(() => import('../components/PrepareDialog.vue')) const PrepareDialog = defineAsyncComponent(() => import('../components/PrepareDialog.vue'))
const ResultDialog = defineAsyncComponent(() => import('../components/ResultDialog.vue')) const ResultDialog = defineAsyncComponent(() => import('../components/ResultDialog.vue'))
const ReportPreview = defineAsyncComponent(() => import('../components/ReportPreview.vue')) const ReportPreview = defineAsyncComponent(() => import('../components/ReportPreview.vue'))
const Exam = defineAsyncComponent(() => import('../components/Exam.vue'))
const route = useRoute() const route = useRoute()
...@@ -73,13 +74,13 @@ provide('info', $$(experimentInfo)) ...@@ -73,13 +74,13 @@ provide('info', $$(experimentInfo))
function fetchInfo() { function fetchInfo() {
if (!form.experiment_id) return if (!form.experiment_id) return
getExperiment({ experiment_id: form.experiment_id }).then(res => { getExperiment({ experiment_id: form.experiment_id }).then((res) => {
experimentInfo = res.data.detail experimentInfo = res.data.detail
}) })
} }
function fetchExperimentRecord() { function fetchExperimentRecord() {
if (!form.experiment_id) return if (!form.experiment_id) return
getExperimentRecord({ experiment_id: form.experiment_id }).then(res => { getExperimentRecord({ experiment_id: form.experiment_id }).then((res) => {
if (Array.isArray(res.data.data)) { if (Array.isArray(res.data.data)) {
detail = undefined detail = undefined
} else { } else {
...@@ -93,13 +94,18 @@ watchEffect(() => { ...@@ -93,13 +94,18 @@ watchEffect(() => {
fetchExperimentRecord() fetchExperimentRecord()
}) })
const tabActive = ref('qa')
const examURL = ref('')
// 右侧 // 右侧
const cookies = useCookies(['TGC']) const cookies = useCookies(['TGC'])
const LAB_URL = computed(() => { const LAB_URL: any = computed(() => {
if (tabActive.value === 'exam' && examURL.value) return examURL.value
return experimentInfo?.type === 4 return experimentInfo?.type === 4
? `${appConfig.dmlURL || import.meta.env.VITE_DML_URL}/trip/my?experiment_id=${form.experiment_id}` ? `${appConfig.dmlURL || import.meta.env.VITE_DML_URL}/trip/my?experiment_id=${form.experiment_id}`
: `${appConfig.dmlURL || import.meta.env.VITE_LAB_URL}&token=${cookies.get('TGC')}&sysFlag=${import.meta.env.VITE_SYS_FLAG}` : `${appConfig.dmlURL || import.meta.env.VITE_LAB_URL}&token=${cookies.get('TGC')}&sysFlag=${import.meta.env.VITE_SYS_FLAG}`
}) })
let iframeKey = $ref(Date.now()) let iframeKey = $ref(Date.now())
// 返回首页 // 返回首页
function handleBackHome() { function handleBackHome() {
...@@ -139,7 +145,7 @@ function handleCapture() { ...@@ -139,7 +145,7 @@ function handleCapture() {
function handleCaptureCallback(event: MessageEvent) { function handleCaptureCallback(event: MessageEvent) {
const { data } = event const { data } = event
if (data.action === 'screenshot' && data.timestamp === screenshotTimestamp) { if (data.action === 'screenshot' && data.timestamp === screenshotTimestamp) {
upload(data.blob).then(url => { upload(data.blob).then((url) => {
uploadPicture(url) uploadPicture(url)
}) })
} }
...@@ -218,7 +224,7 @@ function handleReportPreviewReady() { ...@@ -218,7 +224,7 @@ function handleReportPreviewReady() {
</script> </script>
<template> <template>
<DragPanel @resize="handleResize"> <DragPanel @resize="handleResize" v-if="appConfig.system !== 'x'">
<template #left> <template #left>
<div class="lab-left"> <div class="lab-left">
<el-form :model="form" label-suffix=":" hide-required-asterisk> <el-form :model="form" label-suffix=":" hide-required-asterisk>
...@@ -233,10 +239,13 @@ function handleReportPreviewReady() { ...@@ -233,10 +239,13 @@ function handleReportPreviewReady() {
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-tabs type="border-card" :key="form.experiment_id"> <el-tabs type="border-card" :key="form.experiment_id" v-model="tabActive">
<el-tab-pane label="实验试题" lazy> <el-tab-pane label="实验试题" name="qa" lazy>
<Question :experiment_id="form.experiment_id"></Question> <Question :experiment_id="form.experiment_id"></Question>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="理论考试" name="exam" lazy>
<Exam :experiment_id="form.experiment_id" v-model="examURL"></Exam>
</el-tab-pane>
<el-tab-pane label="实验信息" lazy> <el-tab-pane label="实验信息" lazy>
<Info :data="experimentInfo"></Info> <Info :data="experimentInfo"></Info>
</el-tab-pane> </el-tab-pane>
...@@ -282,7 +291,90 @@ function handleReportPreviewReady() { ...@@ -282,7 +291,90 @@ function handleReportPreviewReady() {
</AppCard> </AppCard>
<div class="lab-box"> <div class="lab-box">
<el-empty description="您已经提交该实验,不能再进行操作,切换其他实验再做操作吧。" v-if="submitted" /> <el-empty description="您已经提交该实验,不能再进行操作,切换其他实验再做操作吧。" v-if="submitted" />
<iframe :src="LAB_URL" :key="iframeKey" frameborder="0" class="iframe" ref="iframeRef" v-else></iframe> <iframe allowfullscreen :src="LAB_URL" :key="iframeKey" frameborder="0" class="iframe" ref="iframeRef" v-else></iframe>
</div>
</template>
</DragPanel>
<DragPanel v-else @resize="handleResize" :isLeftShow="experimentInfo?.exam_status">
<template #left>
<div class="lab-left">
<el-form :model="form" label-suffix=":" hide-required-asterisk>
<el-form-item label="请选择课程">
<el-select :model-value="form.course_id" @change="handleCourseChange" style="width: 100%">
<el-option v-for="item in courses" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="请选择实验" prop="experiment_id">
<el-select :model-value="form.experiment_id" @change="handleExperimentChange" style="width: 100%">
<el-option v-for="item in experimentList" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</el-form-item>
</el-form>
<el-tabs type="border-card" v-model="tabActive">
<el-tab-pane :label="experimentInfo?.exam_status === 0 ? '实验训练' : '实验试题'" name="qa" lazy>
<Exam
:experiment_id="form.experiment_id"
v-model="examURL"
:examStatus="experimentInfo?.exam_status"
v-if="experimentInfo?.exam_status === 0"></Exam>
<Question :experiment_id="form.experiment_id" v-else></Question>
</el-tab-pane>
<el-tab-pane label="理论考试" name="exam" lazy v-show="experimentInfo?.exam_status === 1 && tabActive === 'exam'">
<Exam :experiment_id="form.experiment_id" v-model="examURL"></Exam>
</el-tab-pane>
<el-tab-pane label="实验信息" lazy>
<Info :data="experimentInfo"></Info>
</el-tab-pane>
<el-tab-pane label="案例原文" lazy>
<Case :course_id="form.course_id" :experiment_id="form.experiment_id" :key="resizeKey"></Case>
</el-tab-pane>
<el-tab-pane label="实训指导" lazy>
<Book :course_id="form.course_id" :experiment_id="form.experiment_id" :key="resizeKey"></Book>
</el-tab-pane>
<el-tab-pane label="操作视频" lazy>
<Video :course_id="form.course_id" :experiment_id="form.experiment_id"></Video>
</el-tab-pane>
<el-tab-pane label="讨论交流" lazy>
<Discuss :experiment_id="form.experiment_id"></Discuss>
</el-tab-pane>
<el-tab-pane label="过程与结果" lazy>
<Result :data="experimentInfo" @update="fetchExperimentRecord"></Result>
</el-tab-pane>
</el-tabs>
</div>
</template>
<template #right>
<div class="exam-status" v-if="appConfig.system === 'x' && experimentInfo?.exam_status === 1">
<el-button type="primary" @click="tabActive = 'qa'">实操系统</el-button>
<el-button type="primary" @click="tabActive = 'exam'">理论试题</el-button>
</div>
<AppCard v-else>
<el-row justify="space-between">
<div>
<el-button type="primary" :icon="HomeFilled" :disabled="submitted" @click="handleBackHome">返回首页</el-button>
<el-button type="primary" :disabled="disabled" @click="handleSubmit">提交实验</el-button>
</div>
<div>
<el-button type="primary" :disabled="disabled" :loading="screenshotLoading" @click="handleCapture">截图</el-button>
<el-button type="primary" :disabled="disabled" @click="prepareDialogVisible = true">实验准备</el-button>
<el-button type="primary" :disabled="disabled" @click="resultDialogVisible = true">实验结果</el-button>
<el-button type="primary" :disabled="disabled" v-if="experimentInfo?.report_upload_way === 2 && !experimentInfo?.is_commit_report">
<router-link :to="`/student/lab/report/${form.experiment_id}`" target="_blank">在线实验报告</router-link>
</el-button>
<el-button type="primary" :disabled="disabled" @click="reportDialogVisible = true" v-if="experimentInfo?.report_upload_way === 1 && !submitted"
>上传实验报告</el-button
>
<el-button type="primary" @click="handleReportView" v-if="experimentInfo?.is_commit_report">查看实验报告</el-button>
<el-button type="primary" @click="handleReportExport" v-if="detail?.status === 2 && experimentInfo?.is_commit_report">导出实验报告</el-button>
</div>
</el-row>
</AppCard>
<div class="lab-box">
<el-empty description="您已经提交该实验,不能再进行操作,切换其他实验再做操作吧。" v-if="submitted" />
<iframe allowfullscreen :src="LAB_URL" :key="iframeKey" frameborder="0" class="iframe" ref="iframeRef" v-else></iframe>
<div style="padding: 10px; background-color: #fff; max-width: 300px; min-width: 300px" v-if="experimentInfo?.exam_status === 1 && tabActive === 'qa'">
<Question :experiment_id="form.experiment_id" :exam_status="experimentInfo?.exam_status"></Question>
</div>
</div> </div>
</template> </template>
</DragPanel> </DragPanel>
...@@ -340,6 +432,7 @@ function handleReportPreviewReady() { ...@@ -340,6 +432,7 @@ function handleReportPreviewReady() {
flex: 1; flex: 1;
width: 100%; width: 100%;
margin-top: 20px; margin-top: 20px;
display: flex;
} }
.iframe { .iframe {
width: 100%; width: 100%;
......
{ {
"extends": "@vue/tsconfig/tsconfig.node.json", "extends": "@tsconfig/node20/tsconfig.json",
"include": ["vite.config.*", "vitest.config.*", "cypress.config.*"], "include": ["vite.config.*", "vitest.config.*", "cypress.config.*", "playwright.config.*"],
"compilerOptions": { "compilerOptions": {
"composite": true, "composite": true,
"types": ["node"] "types": ["node"]
......
{ {
"extends": "@vue/tsconfig/tsconfig.web.json", "extends": "@vue/tsconfig/tsconfig.json",
"include": ["auto-imports.d.ts", "env.d.ts", "src/**/*", "src/**/*.vue"], "include": ["auto-imports.d.ts", "env.d.ts", "src/**/*", "src/**/*.vue"],
"compilerOptions": { "compilerOptions": {
"allowJs": true, "allowJs": true,
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
"paths": { "paths": {
"@/*": ["./src/*"] "@/*": ["./src/*"]
}, },
"types": ["element-plus/global"] "types": ["element-plus/global", "@vue-macros/reactivity-transform/macros-global"]
}, },
"references": [ "references": [
......
...@@ -6,16 +6,18 @@ import { defineConfig } from 'vite' ...@@ -6,16 +6,18 @@ import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue' import vue from '@vitejs/plugin-vue'
// import checker from 'vite-plugin-checker' // import checker from 'vite-plugin-checker'
import AutoImport from 'unplugin-auto-import/vite' import AutoImport from 'unplugin-auto-import/vite'
import ReactivityTransform from '@vue-macros/reactivity-transform/vite'
export default defineConfig(() => ({ export default defineConfig(() => ({
// base: mode === 'prod' ? 'https://webapp-pub.ezijing.com/website/prod/saas-lab/' : '/', // base: mode === 'prod' ? 'https://webapp-pub.ezijing.com/website/prod/saas-lab/' : '/',
plugins: [ plugins: [
vue({ reactivityTransform: true, script: { defineModel: true } }), vue(),
AutoImport({ AutoImport({
imports: ['vue', 'vue/macros', 'vue-router', '@vueuse/core', '@vueuse/math'], imports: ['vue', 'vue-router', '@vueuse/core', '@vueuse/math'],
dts: true, dts: true,
eslintrc: { enabled: true } eslintrc: { enabled: true }
}) }),
ReactivityTransform()
// checker({ vueTsc: true, eslint: { lintCommand: 'eslint "./src/**/*.{vue,js,jsx,ts,tsx}"' } }) // checker({ vueTsc: true, eslint: { lintCommand: 'eslint "./src/**/*.{vue,js,jsx,ts,tsx}"' } })
], ],
server: { server: {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论