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

Merge branch 'master' into gdrtvu

{
"globals": {
"$": true,
"$$": true,
"$computed": true,
"$customRef": true,
"$ref": true,
"$shallowRef": true,
"$toRef": true,
"Component": true,
"ComponentPublicInstance": true,
"ComputedRef": true,
"EffectScope": true,
"ExtractDefaultPropTypes": true,
"ExtractPropTypes": true,
"ExtractPublicPropTypes": true,
"InjectionKey": true,
"PropType": true,
"Ref": true,
"VNode": true,
"WritableComputedRef": true,
"asyncComputed": true,
"autoResetRef": true,
"computed": true,
......@@ -106,6 +103,7 @@
"toReactive": true,
"toRef": true,
"toRefs": true,
"toValue": true,
"triggerRef": true,
"tryOnBeforeMount": true,
"tryOnBeforeUnmount": true,
......@@ -297,11 +295,6 @@
"watchThrottled": true,
"watchTriggerable": true,
"watchWithFilter": true,
"whenever": true,
"toValue": true,
"ExtractDefaultPropTypes": true,
"ExtractPropTypes": true,
"ExtractPublicPropTypes": true,
"WritableComputedRef": true
"whenever": true
}
}
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
export {}
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 asyncComputed: typeof import('@vueuse/core')['asyncComputed']
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
......@@ -300,5 +294,6 @@ declare global {
// for type re-export
declare global {
// @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 @@
<body>
<div id="app"></div>
<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>
</html>
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -14,9 +14,9 @@
"cert": "node ./cert.js"
},
"dependencies": {
"@element-plus/icons-vue": "^2.0.10",
"@element-plus/icons-vue": "^2.3.1",
"@tinymce/tinymce-vue": "^5.0.0",
"@vant/area-data": "^1.4.0",
"@vant/area-data": "^1.5.1",
"@vueuse/core": "^9.13.0",
"@vueuse/head": "^1.0.26",
"@vueuse/integrations": "^9.13.0",
......@@ -26,23 +26,24 @@
"blueimp-md5": "^2.19.0",
"countup.js": "^2.6.2",
"dayjs": "^1.11.7",
"element-plus": "^2.2.32",
"element-plus": "^2.4.4",
"file-saver": "^2.0.5",
"filesize": "^10.0.7",
"html-to-image": "^1.11.11",
"html2pdf.js": "^0.10.1",
"lodash-es": "^4.17.21",
"pinia": "^2.1.4",
"pinia": "^2.1.7",
"print-js": "^1.6.0",
"ua-parser-js": "^1.0.33",
"universal-cookie": "^4.0.4",
"video.js": "^7.21.1",
"vue": "^3.3.4",
"vue-router": "^4.2.2",
"vue": "^3.4.25",
"vue-router": "^4.3.2",
"vuedraggable": "^4.1.0"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.2.0",
"@tsconfig/node20": "^20.1.4",
"@types/ali-oss": "^6.16.8",
"@types/blueimp-md5": "^2.18.0",
"@types/file-saver": "^2.0.5",
......@@ -50,17 +51,18 @@
"@types/node": "^20.3.1",
"@types/ua-parser-js": "^0.7.36",
"@types/video.js": "^7.3.52",
"@vitejs/plugin-vue": "^4.2.3",
"@vue/eslint-config-typescript": "^11.0.3",
"@vue/tsconfig": "^0.1.3",
"@vitejs/plugin-vue": "^5.0.4",
"@vue-macros/reactivity-transform": "^0.4.4",
"@vue/eslint-config-typescript": "^12.0.0",
"@vue/tsconfig": "^0.5.1",
"chalk": "^5.2.0",
"eslint": "^8.34.0",
"eslint-plugin-vue": "^9.9.0",
"eslint": "^8.57.0",
"eslint-plugin-vue": "^9.25.0",
"sass": "^1.58.3",
"typescript": "~4.9.5",
"unplugin-auto-import": "^0.16.4",
"vite": "^4.3.9",
"vite-plugin-checker": "^0.6.0",
"vue-tsc": "^1.6.5"
"typescript": "~5.4.5",
"unplugin-auto-import": "^0.17.5",
"vite": "^5.2.10",
"vite-plugin-checker": "^0.6.4",
"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<{
(e: 'resize'): void
}>()
const props = defineProps<{ isLeftShow?: number }>()
const leftPanelVisible = $ref<boolean>(true)
const leftPanelWidth = useStorage('leftPanelWidth', 400)
const leftPanelWidthText = $computed(() => {
......@@ -40,7 +42,7 @@ onMounted(() => {
<template>
<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>
<slot name="left"></slot>
<div class="panel-resize" id="panel-resize"></div>
......@@ -50,12 +52,14 @@ onMounted(() => {
<path
class="path-wapper"
d="M0 0l14.12 8.825A4 4 0 0116 12.217v61.566a4 4 0 01-1.88 3.392L0 86V0z"
fill="#e1e4eb"></path>
fill="#e1e4eb"
></path>
<path
class="path-arrow"
d="M10.758 48.766a.778.778 0 000-1.127L6.996 43l3.762-4.639a.778.778 0 000-1.127.85.85 0 00-1.172 0l-4.344 5.202a.78.78 0 000 1.128l4.344 5.202a.85.85 0 001.172 0z"
fill="#8D9EA7"
fill-rule="nonzero"></path>
fill-rule="nonzero"
></path>
</g>
</svg>
</div>
......
......@@ -132,29 +132,14 @@ defineExpose({ refetch, tableRef })
</template>
<template v-else>
<!-- input -->
<el-input
v-model="params[item.prop]"
v-bind="item"
clearable
@change="search"
style="width: 200px"
v-if="item.type === 'input'"
/>
<el-input v-model="params[item.prop]" v-bind="item" clearable @change="search" style="width: 200px" v-if="item.type === 'input'" />
<!-- select -->
<el-select
v-model="params[item.prop]"
v-bind="item"
filterable
clearable
@change="search"
v-if="item.type === 'select'"
>
<el-select v-model="params[item.prop]" v-bind="item" filterable clearable @change="search" v-if="item.type === 'select'">
<el-option
:label="option[item.labelKey] || option.label"
:value="option[item.valueKey] || option.value"
v-for="(option, index) in item.options"
:key="index"
/>
:key="index" />
</el-select>
</template>
</el-form-item>
......@@ -172,14 +157,7 @@ defineExpose({ refetch, tableRef })
<!-- 主体 -->
<div class="table-list-bd">
<slot name="body" v-bind="{ data: dataList }">
<el-table
stripe
:header-cell-style="{ background: '#ededed' }"
:data="dataList"
v-loading="loading"
v-bind="$attrs"
ref="tableRef"
>
<el-table 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">
<template #default="scope" v-if="item.slots || item.computed">
<slot :name="item.slots" v-bind="scope" v-if="item.slots"></slot>
......@@ -198,15 +176,14 @@ defineExpose({ refetch, tableRef })
class="table-list-pagination"
background
layout="total, sizes, prev, pager, next, jumper"
:page-sizes="[10, 20, 30, 50, 100]"
:page-sizes="[10, 20, 30, 50]"
:page-size="page.size"
:total="page.total"
v-model:currentPage="page.currentPage"
@size-change="pageSizeChange"
@current-change="fetchList()"
:hide-on-single-page="true"
v-if="hasPagination"
>
v-if="hasPagination">
</el-pagination>
</div>
</div>
......
......@@ -164,14 +164,14 @@ function handleClick(path: string) {
font-weight: 500;
border-bottom: 0;
> .el-menu-item {
margin-left: 40px;
margin-right: 44px;
padding: 0;
background-color: unset !important;
border-bottom: 0 !important;
}
}
.el-sub-menu__title {
margin-left: 40px;
// margin-left: 40px;
padding: 0;
border-bottom: 0 !important;
}
......
......@@ -13,7 +13,7 @@ const appConfigList = [
},
{
system: 'x',
title: '1+X实训平台(中级)',
title: '1+X实训平台',
logo: 'https://zws-imgs-pub.ezijing.com/pc/base/ezijing-logo.svg',
hosts: ['saas-x'],
studentMenus: [
......
<script setup lang="ts"></script>
<template>
<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"
frameborder="0"
class="iframe"></iframe>
......
......@@ -56,7 +56,7 @@ const qaURL = import.meta.env.VITE_QA_CENTER_URL
</script>
<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-item label="赛项名称">{{ detail.competition_id_name }}</el-form-item>
<el-form-item label="选手姓名">{{ detail.student_name }}</el-form-item>
......
......@@ -110,7 +110,7 @@ function handleResize() {
</el-row>
</AppCard>
<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>
</template>
</DragPanel>
......
......@@ -118,7 +118,7 @@ function handleUpdate() {
</script>
<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-item label="报名赛项">
<el-input v-model="form.competition_name" :disabled="isUpdate" />
......
......@@ -78,7 +78,7 @@ function handleSubmit() {
</script>
<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">
<DiscussItem :info="detail.info" :list="detail.list" v-if="detail.info"></DiscussItem>
<div class="tips" v-if="isLoading">
......@@ -90,13 +90,7 @@ function handleSubmit() {
<div class="tips" v-if="!hasMore">没有更多了</div>
</div>
<el-form
ref="formRef"
:rules="rules"
:model="form"
style="margin-top: 40px"
v-permission="'v1-teacher-discussion-comment'"
>
<el-form ref="formRef" :rules="rules" :model="form" style="margin-top: 40px" v-permission="'v1-teacher-discussion-comment'">
<el-form-item prop="content">
<el-input type="textarea" v-model="form.content" :autosize="{ minRows: 4, maxRows: 6 }" :maxlength="200" />
</el-form-item>
......
......@@ -97,7 +97,7 @@ function handleUpdate() {
</script>
<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-item label="所属部门/学校" prop="organization_id">
<el-select v-model="form.organization_id" style="width: 100%" :disabled="isUpdate">
......
......@@ -247,7 +247,7 @@ function handleDateRangeChange(value: any) {
</script>
<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-item label="赛项名称" prop="name">
<el-input v-model="form.name" :disabled="isUpdate" />
......
......@@ -53,7 +53,7 @@ function handleSubmit() {
</script>
<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-item label="赛项名称">{{ detail.name }}</el-form-item>
<el-form-item label="角色" prop="role">
......
......@@ -100,7 +100,7 @@ function handleRemoveClass(index: number) {
</script>
<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">
<AppList v-bind="listOptions" ref="appList">
<template #header-buttons>
......
......@@ -106,11 +106,7 @@ function handleUpdate(params: ContestBookUpdateParams) {
</script>
<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" :rules="rules" label-width="124px" :disabled="disabled">
<el-form-item label="评分细则文件" prop="files">
<AppUpload
......
......@@ -149,7 +149,7 @@ function handleRatioChange(row: any, index: number) {
</script>
<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-item label="赛项名称">
<el-input v-model="form.name" disabled />
......
......@@ -119,7 +119,7 @@ getPlatformKeysList()
</script>
<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-item label="训练指导书文件" prop="files">
<AppUpload
......@@ -127,8 +127,7 @@ getPlatformKeysList()
:limit="1"
: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"
@success="handleUploadSuccess"
>
@success="handleUploadSuccess">
<template #tip>训练指导书文件支持格式包含:doc docx xls xlsx pdf ppt pptx,大小不超过50M</template>
</AppUpload>
</el-form-item>
......@@ -141,9 +140,7 @@ getPlatformKeysList()
</el-form-item>
<el-form-item label="关联实验">
<el-radio-group v-model="form.platform_key">
<el-radio v-for="item in platformKeys" :key="item.platform_key" :label="item.platform_key">{{
item.name
}}</el-radio>
<el-radio v-for="item in platformKeys" :key="item.platform_key" :label="item.platform_key">{{ item.name }}</el-radio>
<!-- <el-radio :key="1" label="1">商业数据分析实验</el-radio>
<el-radio :key="1" label="2">数字营销实操</el-radio> -->
</el-radio-group>
......
......@@ -65,7 +65,7 @@ function handleUpdate(params: any) {
</script>
<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-item label="考试类型" prop="type">
<el-radio-group v-model="form.type" :disabled="isUpdate">
......
......@@ -77,7 +77,7 @@ function handleUpdate(params: any) {
</script>
<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-item label="试卷类型" prop="type">
<el-radio-group v-model="form.type">
......
......@@ -119,7 +119,7 @@ getPlatformKeysList()
</script>
<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-item label="大赛试题文件" prop="files">
<AppUpload
......@@ -140,9 +140,7 @@ getPlatformKeysList()
</el-form-item>
<el-form-item label="关联实验">
<el-radio-group v-model="form.platform_key">
<el-radio v-for="item in platformKeys" :key="item.platform_key" :label="item.platform_key">{{
item.name
}}</el-radio>
<el-radio v-for="item in platformKeys" :key="item.platform_key" :label="item.platform_key">{{ item.name }}</el-radio>
<!-- <el-radio :key="1" label="1">商业数据分析实验</el-radio>
<el-radio :key="1" label="2">数字营销实操</el-radio> -->
</el-radio-group>
......
......@@ -60,10 +60,7 @@ const title = $computed(() => {
function handleBeforeUpload() {
if (form.source_id) {
return ElMessageBox.confirm(
'系统仅支持1个训练操作视频,此操作将覆盖原有训练操作视频文件,确认上传新文件吗?',
'提示'
)
return ElMessageBox.confirm('系统仅支持1个训练操作视频,此操作将覆盖原有训练操作视频文件,确认上传新文件吗?', '提示')
}
return true
}
......@@ -98,7 +95,7 @@ function handleUpdate(params: ContestVideoUpdateParams) {
</script>
<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-item label="训练操作视频文件" prop="source_id">
<UploadVideo :beforeUpload="handleBeforeUpload" @success="handleUploadSuccess">
......
<script setup lang="ts">
import type { ContestScoreItem } from '../types'
import { ElMessage } from 'element-plus'
import { publishScore } from '../api'
interface Props {
data: ContestScoreItem
import type { ContestScoreItem } from '../types'
import { ElMessage } from 'element-plus'
import { publishScore } from '../api'
interface Props {
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>()
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)
})
b {
color: var(--main-color);
}
</script>
<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;
}
span + span {
margin-left: 40px;
}
</style>
\ No newline at end of file
}
</style>
......@@ -105,7 +105,7 @@ function handleUpdate(params: BookCreateItem) {
</script>
<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-item label="实验指导书文件" prop="files">
<AppUpload
......
......@@ -112,7 +112,7 @@ function handleUpdate(params: CaseUpdateItem) {
</script>
<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-item label="案例原文文件" prop="files">
<AppUpload
......
......@@ -78,7 +78,7 @@ function handleSubmit() {
</script>
<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">
<DiscussItem :info="detail.info" :list="detail.list" v-if="detail.info"></DiscussItem>
<div class="tips" v-if="isLoading">
......@@ -90,13 +90,7 @@ function handleSubmit() {
<div class="tips" v-if="!hasMore">没有更多了</div>
</div>
<el-form
ref="formRef"
:rules="rules"
:model="form"
style="margin-top: 40px"
v-permission="'v1-teacher-discussion-comment'"
>
<el-form ref="formRef" :rules="rules" :model="form" style="margin-top: 40px" v-permission="'v1-teacher-discussion-comment'">
<el-form-item prop="content">
<el-input type="textarea" v-model="form.content" :autosize="{ minRows: 4, maxRows: 6 }" :maxlength="200" />
</el-form-item>
......
......@@ -86,11 +86,7 @@ export function getExperimentReportRule(params: { experiment_id: string }) {
return httpRequest.get('/api/resource/v1/backend/experiment-report/detail', { params })
}
// 更新实验报告规则
export function updateExperimentReportRule(data: {
experiment_id: string
report_upload_way: number
detail_list: string
}) {
export function updateExperimentReportRule(data: { experiment_id: string; report_upload_way: number; detail_list: string }) {
return httpRequest.post('/api/resource/v1/backend/experiment-report/save', data)
}
......@@ -153,4 +149,35 @@ export function getQuestionTags(params: { experiment_id: string }) {
// 获取群组
export function getQuestionGroup(params: { experiment_id: string }) {
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({
user_attr_config: { is_all: true, items: [] },
event_config: { is_all: true, items: [] },
is_use_common: 0,
is_use_common_tags: 0,
is_use_common_groups: 0,
is_use_common_materials: 0,
ids: ['教师维护的用户和事件数据'],
tag_ids: [],
group_ids: [],
......@@ -73,6 +76,9 @@ function fetchInfo() {
user_attr_config,
event_config,
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,
group_ids,
material_ids
......@@ -118,7 +124,12 @@ function handleSubmit() {
</script>
<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-row justify="space-between">
<el-form-item label="实验名称">{{ data.name }}</el-form-item>
......@@ -141,7 +152,13 @@ function handleSubmit() {
</el-form-item>
<el-form-item label="连接" label-width="82" prop="connect_ids">
<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-form-item>
</el-tab-pane>
......@@ -151,7 +168,12 @@ function handleSubmit() {
<el-radio :label="true">全部</el-radio>
<el-radio :label="false">部分</el-radio>
</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-select>
</el-form-item>
......@@ -160,12 +182,69 @@ function handleSubmit() {
<el-radio :label="true">全部</el-radio>
<el-radio :label="false">部分</el-radio>
</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-select>
</el-form-item>
</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-radio-group v-model="form.is_use_common">
<el-radio :label="1">否</el-radio>
......@@ -195,7 +274,7 @@ function handleSubmit() {
</el-select>
</el-form-item>
</template>
</el-tab-pane>
</el-tab-pane> -->
</el-tabs>
</el-form>
<template #footer>
......
......@@ -8,6 +8,8 @@ import { useGetProjectList } from '@/composables/useGetProjectList'
import { useGetCourseList } from '../composables/useGetCourseList'
import { useGetTeacherList } from '../composables/useGetTeacherList'
import { useMapStore } from '@/stores/map'
import { useAppConfig } from '@/composables/useAppConfig'
const appConfig = useAppConfig()
interface Props {
data?: ExperimentItem | null
......@@ -41,7 +43,8 @@ const form = reactive<ExperimentCreateItem>({
purpose: '',
requirements: '',
content: '',
procedure: ''
procedure: '',
exam_status: '0'
})
watchEffect(() => {
if (!props.data) return
......@@ -111,7 +114,12 @@ function handleUpdate(params: ExperimentCreateItem) {
</script>
<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-item label="实验所属部门/学校" prop="organ_id">
<el-select v-model="form.organ_id" style="width: 100%" :disabled="isUpdate" @change="handleOrgChange">
......@@ -124,7 +132,7 @@ function handleUpdate(params: ExperimentCreateItem) {
</el-select>
</el-form-item>
<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 label="实验学时" prop="length">
<el-input-number v-model="form.length" :min="1" :max="20" step-strictly style="width: 100%" />
......@@ -142,6 +150,13 @@ function handleUpdate(params: ExperimentCreateItem) {
<el-form-item label="实验总成绩" prop="score">
<el-input-number v-model="form.score" :min="1" :max="150" step-strictly style="width: 100%" />
</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-input
v-model="form.purpose"
......@@ -149,7 +164,8 @@ function handleUpdate(params: ExperimentCreateItem) {
type="textarea"
maxlength="200"
show-word-limit
placeholder="请输入实验目的" />
placeholder="请输入实验目的"
/>
</el-form-item>
<el-form-item label="实验要求" prop="requirements">
<el-input
......@@ -158,7 +174,8 @@ function handleUpdate(params: ExperimentCreateItem) {
type="textarea"
maxlength="200"
show-word-limit
placeholder="请输入实验要求" />
placeholder="请输入实验要求"
/>
</el-form-item>
<el-form-item label="实验内容及原理" prop="content">
<el-input
......@@ -167,7 +184,8 @@ function handleUpdate(params: ExperimentCreateItem) {
type="textarea"
maxlength="200"
show-word-limit
placeholder="请输入实验内容及原理" />
placeholder="请输入实验内容及原理"
/>
</el-form-item>
<el-form-item label="实验步骤及过程" prop="procedure">
<el-input
......@@ -176,7 +194,8 @@ function handleUpdate(params: ExperimentCreateItem) {
type="textarea"
maxlength="200"
show-word-limit
placeholder="请输入实验步骤及过程" />
placeholder="请输入实验步骤及过程"
/>
</el-form-item>
<el-form-item label="有效状态" prop="status">
<el-radio-group v-model="form.status">
......
......@@ -4,7 +4,9 @@ import type { ExperimentItem } from '../types'
import { Plus } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
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 {
data: ExperimentItem
......@@ -21,7 +23,9 @@ const formRef = $ref<FormInstance>()
const form = reactive<any>({
experiment_id: props.data.id,
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() {
......@@ -39,15 +43,31 @@ function fetchInfo() {
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(() => {
fetchInfo()
fetchExamList()
})
// 合计
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)
}, 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) {
}
// 提交
function handleSubmit(call?: any) {
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}行规则配置错误,请检查后重试`)
for (let i = 0; i < form.exam_rules.length; i++) {
const item = form.exam_rules[i]
if (!item.exam_id) {
ElMessage.error(`理论考试${i + 1}行规则配置错误,请检查后重试`)
return
}
if (!item.percent) {
ElMessage.error(`${item.name}权重不能为0!`)
ElMessage.error(`理论考试权重不能为0!`)
return
}
if (item.type === 5 && /实验报告|实验准备|实验结果|课堂活跃度/.test(item.name)) {
ElMessage.error(`第${i + 1}行规则配置错误,自定义的名称不能包含“实验报告”、“实验准备”、“实验结果”和“课堂活跃度”`)
return
}
if (appConfig?.system !== 'x') {
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) {
......@@ -116,16 +149,22 @@ function handleTypeChange(row: any) {
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) => {
if (index !== i) {
if (index !== i || type !== 1) {
result += item.percent || 0
}
return result
}, 0)
// 最大可输入占比
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) {
)
})
}
// 添加
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>
<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" label-suffix=":">
<el-form-item label="实验名称">{{ data?.name }}</el-form-item>
<el-row>
......@@ -166,30 +210,58 @@ const handleEdit = function (type: number) {
<el-form-item label="实验总成绩">{{ data?.score }}</el-form-item>
</el-col>
</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-row justify="space-between" style="width: 100%">
<p>验成绩规则</p>
<p>操考试</p>
<el-button type="primary" :icon="Plus" @click="handleAdd"></el-button>
</el-row>
<el-table :data="form.rule_list" row-key="id">
<el-table-column prop="name" width="170">
<template #default="{ row }">
<el-input v-model="row.name" :maxlength="20" style="width: 100%" v-if="row.type === 5" />
<el-select
v-model="row.type"
:disabled="row.type === 1"
style="width: 100%"
@change="handleTypeChange(row)"
v-else
>
<el-select 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-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)" /> %
</template>
<template #default="{ row, $index }"> <el-input-number v-model="row.percent" :min="0" @change="handlePercentChange(row, $index, 1)" /> % </template>
</el-table-column>
<el-table-column prop="rule_mode" width="200">
<template #default="{ row }">
......@@ -207,18 +279,10 @@ const handleEdit = function (type: number) {
<template #default="{ $index, row }">
<div class="btn-box">
<!-- || row.type === 8 -->
<el-button
:disabled="row.type === 1"
style="padding: 0"
text
type="primary"
@click="handleEdit(row.type)"
v-if="row.type !== 1"
<el-button :disabled="row.type === 1" style="padding: 0" text type="primary" @click="handleEdit(row.type)" v-if="row.type !== 1"
>编辑</el-button
>
<el-button style="padding: 0" text type="primary" @click="handleRemove($index)" v-if="row.type !== 1"
>删除</el-button
>
<el-button style="padding: 0" text type="primary" @click="handleRemove($index)">删除</el-button>
</div>
</template>
</el-table-column>
......@@ -238,7 +302,7 @@ const handleEdit = function (type: number) {
<style lang="scss" scoped>
.total {
width: 100%;
padding: 10px 200px;
padding: 10px 170px;
box-sizing: border-box;
p {
margin-left: 16px;
......
......@@ -19,12 +19,7 @@ const selectAnswer = ['302', '202']
</script>
<template>
<el-dialog
title="2023商业数据分析大赛决赛试题"
:close-on-click-modal="false"
width="50%"
@update:modelValue="$emit('update:modelValue')"
>
<el-dialog title="2023商业数据分析大赛决赛试题" :close-on-click-modal="false" width="50%" @update:modelValue="value => $emit('update:modelValue', value)">
<el-card class="box-card" v-for="(item, index) in list" :key="item.id">
<template #header>
<div class="card-header">
......@@ -32,9 +27,7 @@ const selectAnswer = ['302', '202']
<el-form-item label="本题分值" style="margin: 0"> {{ item.score }} </el-form-item>
</div>
</template>
<div class="text item">
正确答案:{{ ['302', '202'].includes(item.type) ? item.answer_info?.name : item?.answer || '上传成功' }}
</div>
<div class="text item">正确答案:{{ ['302', '202'].includes(item.type) ? item.answer_info?.name : item?.answer || '上传成功' }}</div>
</el-card>
</el-dialog>
</template>
......
......@@ -42,13 +42,11 @@ function handleSubmit() {
}
</script>
<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>
<AppList v-bind="listOptions" @selection-change="handleSelectionChange"></AppList>
<el-row justify="center">
<el-button type="primary" round :disabled="!multipleSelection.length" @click="handleSubmit"
>关联选择班级</el-button
>
<el-button type="primary" round :disabled="!multipleSelection.length" @click="handleSubmit">关联选择班级</el-button>
<el-button round auto-insert-space @click="$emit('update:modelValue', false)">取消</el-button>
</el-row>
</el-dialog>
......
......@@ -42,12 +42,10 @@ function handleSubmit() {
}
</script>
<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>
<el-row justify="center">
<el-button type="primary" round :disabled="!multipleSelection.length" @click="handleSubmit"
>添加选择学生</el-button
>
<el-button type="primary" round :disabled="!multipleSelection.length" @click="handleSubmit">添加选择学生</el-button>
<el-button round auto-insert-space @click="$emit('update:modelValue', false)">取消</el-button>
</el-row>
</el-dialog>
......
......@@ -38,11 +38,7 @@ const handleCreate = () => {
</script>
<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" :rules="rules" label-width="110px">
<el-form-item label="实验名称">{{ detail.name }}</el-form-item>
<el-form-item label="实验小组名称" prop="name">
......
......@@ -43,6 +43,7 @@ const columns = computed(() => {
{ label: '序号', type: 'index', width: 60 },
{ label: '学号', prop: 'id_number' },
{ label: '姓名', prop: 'name' },
{ label: '手机号', prop: 'mobile' },
{ label: '性别', prop: 'gender_name' },
{ label: '所属部门/学校', prop: 'organ_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 {
content: string
procedure: string
stu_commit_count: number
exam_status?: string
}
export interface ExperimentCreateItem {
......@@ -48,6 +49,7 @@ export interface ExperimentCreateItem {
requirements: string
content: string
procedure: string
exam_status: string
}
export interface ClassItem {
......
<script setup lang="ts">
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 { getExperimentList } from '../api'
import { getExperimentList, deleteExperiment } from '../api'
import { useGetCourseList } from '../composables/useGetCourseList'
import { useGetCourseExperimentList } from '../composables/useGetCourseExperimentList'
import { useMapStore } from '@/stores/map'
import { ElMessage, ElMessageBox } from 'element-plus'
const FormDialog = defineAsyncComponent(() => import('../components/FormDialog.vue'))
const GradeRulesDialog = defineAsyncComponent(() => import('../components/GradeRulesDialog.vue'))
const DMLFormDialog = defineAsyncComponent(() => import('../components/DMLFormDialog.vue'))
const CopyDialog = defineAsyncComponent(() => import('../components/CopyDialog.vue'))
// 数据状态
const status = useMapStore().getMapValuesByKey('system_status')
......@@ -93,7 +95,7 @@ const listOptions = $computed(() => {
{ label: '生效状态', prop: 'status_name' },
{ label: '更新人', prop: 'updated_operator_name' },
{ label: '更新时间', prop: 'updated_time' },
{ label: '操作', slots: 'table-x', width: 200 }
{ label: '操作', slots: 'table-x', width: 220 }
]
}
})
......@@ -128,19 +130,56 @@ function handleUpdateDML(row: ExperimentItem) {
rowData.value = row
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>
<template>
<AppCard title="实验管理">
<AppList v-bind="listOptions" ref="appList">
<template #header-buttons>
<el-button type="primary" :icon="CirclePlus" v-permission="'v1-backend-experiment-create'" @click="handleAdd"
>新增实验</el-button
>
<el-button type="primary" :icon="CirclePlus" v-permission="'v1-backend-experiment-create'" @click="handleAdd">新增实验</el-button>
</template>
<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
type="primary"
round
......@@ -159,11 +198,7 @@ function handleUpdateDML(row: ExperimentItem) {
v-permission="'v1-backend-experiment-scorerule-detail'"
>编辑成绩规则</el-button
><br />
<el-button
type="info"
round
style="width: 132px; margin-bottom: 12px"
v-permission="'v1-backend-experiment-report-detail'">
<el-button 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>
</el-button>
<br />
......@@ -171,22 +206,18 @@ function handleUpdateDML(row: ExperimentItem) {
<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 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-button type="primary" round @click="handleUpdate(row)" v-permission="'v1-backend-experiment-update'">编辑</el-button>
<div style="margin-top: 12px">
<el-button type="primary" round @click="handleCopy(row)">复制</el-button>
<el-button type="primary" round @click="handleDelete(row)">删除</el-button>
</div> -->
</template>
</AppList>
</AppCard>
<FormDialog v-model="dialogVisible" :data="rowData" @update="onUpdateSuccess" v-if="dialogVisible"></FormDialog>
<!-- 编辑实验成绩规则 -->
<GradeRulesDialog
v-model="gradeRulesDialogVisible"
:data="rowData"
@update="onUpdateSuccess"
v-if="gradeRulesDialogVisible && rowData"></GradeRulesDialog>
<GradeRulesDialog v-model="gradeRulesDialogVisible" :data="rowData" @update="onUpdateSuccess" v-if="gradeRulesDialogVisible && rowData"></GradeRulesDialog>
<!-- 配置数字营销实验 -->
<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>
......@@ -13,6 +13,7 @@ const StudentGroupDialog = defineAsyncComponent(() => import('../components/Stud
const StudentListDialog = defineAsyncComponent(() => import('../components/StudentListDialog.vue'))
const ViewGradeRules = defineAsyncComponent(() => import('../components/ViewGradeRules.vue'))
const ViewReportRules = defineAsyncComponent(() => import('../components/ViewReportRules.vue'))
const ViewExam = defineAsyncComponent(() => import('../components/ViewExam.vue'))
interface Props {
id: string
......@@ -113,21 +114,53 @@ const dmlURL = computed(() => {
<el-divider />
<AppList v-bind="listOptions" ref="appList">
<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 #table-x="{ row }">
<el-button type="primary" 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>
<el-button
type="primary"
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>
</AppList>
</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>
<!-- 学生分组 -->
<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>
<ViewReportRules v-model="reportRulesVisible" :experiment_id="id" v-if="reportRulesVisible"></ViewReportRules>
</template>
......@@ -24,13 +24,7 @@ export function getExperimentRecord(params: { experiment_id: string; student_id:
}
// 实验记录评分
export function checkExperimentRecord(data: {
experiment_id: string
student_id: string
operate: number
result: number
file: number
}) {
export function checkExperimentRecord(data: { experiment_id: string; student_id: string; operate: number; result: number; file: number }) {
return httpRequest.post('/api/lab/v1/teacher/record/check', data)
}
......@@ -56,12 +50,7 @@ export function getExperimentReport(params: { experiment_id: string; student_id:
return httpRequest.get('/api/lab/v1/teacher/experiment/report-achievement', { params })
}
// 批改学员实验报告成绩
export function updateExperimentReport(data: {
experiment_id: string
student_id: string
score_detail: string
comment: string
}) {
export function updateExperimentReport(data: { experiment_id: string; student_id: string; score_detail: string; comment: string }) {
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
}
// 同步学生分数
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)
}
export function getStudentTicket(params: { student_id: string }) {
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 () {
) {
setData('4', '资料管理')
}
if (b.type === '501') {
setData('5', '用户旅程')
}
function setData(type: string, name: string) {
const index = a.findIndex((item: { type: string }) => item.type === type)
b.order = i + 1
......
......@@ -149,7 +149,7 @@ function getOperationUrl(type: number) {
</script>
<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-row>
<el-col :span="12">
......@@ -176,6 +176,7 @@ function getOperationUrl(type: number) {
</el-col>
</el-row>
<el-divider />
<el-form-item label="理论成绩">{{ data.theory_score }}</el-form-item>
<el-form-item label="实验成绩">
<el-row justify="end" style="width: 100%">
<el-button type="primary" @click="prepareVisible = true">查看实验准备</el-button>
......@@ -212,7 +213,7 @@ function getOperationUrl(type: number) {
<el-table-column label="操作" prop="commit_score" align="center">
<template #default="{ row }">
<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>
</el-button>
<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 {
student_id: string
student_name: string
score_log: ScoreLog[]
theory_score: string
}
export interface FileItem {
......
<script setup lang="ts">
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 { getExperimentRecordList } from '../api'
import { getExperimentRecordList, recordReject } from '../api'
import { useFilterList } from '../composables/useFilterList'
import { useCookies } from '@vueuse/integrations/useCookies'
import { ElMessage } from 'element-plus'
const ScoreDialog = defineAsyncComponent(() => import('../components/ScoreDialog.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 { courses, experiments, specialties, classes } = useFilterList()
......@@ -26,9 +29,9 @@ const params = reactive({
class_id: ''
})
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) {
return classes.value.filter(item => item.specialty_id === specialty.id)
return classes.value.filter((item) => item.specialty_id === specialty.id)
}
return classes.value
})
......@@ -95,7 +98,7 @@ const listOptions = $computed(() => {
{ label: '学号', prop: 'sno_number' },
{ label: '实验名称', prop: 'experiment_name' },
{ label: '提交状态', prop: 'status_name', slots: 'table-status' },
{ label: '实验成绩', prop: 'score', slots: 'table-score' },
{ label: '考试成绩', prop: 'score', slots: 'table-score' },
{ label: '更新时间', prop: 'commit_time' },
{ label: '操作', slots: 'table-x', width: 210 }
]
......@@ -118,6 +121,15 @@ function handleScoreLog(row: RecordItem) {
function onUpdateSuccess() {
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>
<template>
......@@ -125,7 +137,13 @@ function onUpdateSuccess() {
<AppList v-bind="listOptions" ref="appList">
<template #header-buttons>
<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">
<el-button type="primary" round :icon="Promotion">进入实验室</el-button>
......@@ -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
>
<<<<<<< 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>
</template>
</AppList>
......@@ -153,6 +174,10 @@ function onUpdateSuccess() {
<ScoreLogDialog v-model="scoreLogVisible" :data="rowData" v-if="scoreLogVisible && rowData"></ScoreLogDialog>
<!-- 批量导入 -->
<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>
<style lang="scss" scoped>
......
......@@ -56,7 +56,7 @@ const getIframeUrl = function () {
</template>
<template #right>
<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>
</template>
</DragPanel>
......
......@@ -66,10 +66,7 @@ const title = $computed(() => {
function handleBeforeUpload() {
if (form.source_id) {
return ElMessageBox.confirm(
'系统仅支持1个实验操作视频,此操作将覆盖原有实验操作视频文件,确认上传新文件吗?',
'提示'
)
return ElMessageBox.confirm('系统仅支持1个实验操作视频,此操作将覆盖原有实验操作视频文件,确认上传新文件吗?', '提示')
}
return true
}
......@@ -104,7 +101,7 @@ function handleUpdate(params: VideoCreateItem) {
</script>
<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-item label="实验操作视频文件" prop="source_id">
<UploadVideo :beforeUpload="handleBeforeUpload" @success="handleUploadSuccess">
......
......@@ -20,6 +20,7 @@ const currentExam = computed(() => {
// 考试平台 URL
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 `https://dev.ezijing.com:5173/exam/7003551966412406784?has_time=0&has_submit=0&has_save=1&show_answer=1`
})
......@@ -36,6 +37,6 @@ onMounted(() => {
</script>
<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 />
</template>
......@@ -56,11 +56,7 @@ const update = () => {
</script>
<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" :rules="rules">
<el-form-item label="实验报告文件" prop="files">
<AppUpload
......
......@@ -183,7 +183,7 @@ const reportDialogVisible = $ref(false)
</g>
</svg>
</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>
</template>
</DragPanel>
......
......@@ -30,12 +30,7 @@ export function getExperimentVideoPlayInfo(params: { source_id: string }) {
}
// 获取实验讨论交流
export function getExperimentDiscussList(params: {
experiment_id: string
tag: number
page?: number
'per-page'?: number
}) {
export function getExperimentDiscussList(params: { experiment_id: string; tag: number; page?: number; 'per-page'?: number }) {
return httpRequest.get('/api/lab/v1/student/experiment-topic/list', { params })
}
// 发表新话题
......@@ -90,21 +85,11 @@ export function getExperimentReportTemplate(params: { experiment_id: string }) {
return httpRequest.get('/api/lab/v1/student/experiment/report-template', { params })
}
// 更新实验在线报告
export function updateExperimentReport(data: {
experiment_id: string
experiment_address: string
experiment_date: string
detail: string
}) {
export function updateExperimentReport(data: { experiment_id: string; experiment_address: string; experiment_date: string; detail: string }) {
return httpRequest.post('/api/lab/v1/student/experiment/upload-online-report', data)
}
// 缓存实验在线报告
export function cacheExperimentReport(data: {
experiment_id: string
experiment_address: string
experiment_date: string
detail: string
}) {
export function cacheExperimentReport(data: { experiment_id: string; experiment_address: string; experiment_date: string; detail: string }) {
return httpRequest.post('/api/lab/v1/student/experiment/cache-online-report', data)
}
// 获取实验在线报告缓存
......@@ -130,3 +115,8 @@ export function getExperimentQuestionList(params: { experiment_id: string }) {
export function getExperimentQuestion(params: { experiment_id: string; id: string }) {
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) {
</script>
<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-column prop="name">
<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 = () => {
</script>
<template>
<el-dialog
append-to-body
title="实验准备"
:close-on-click-modal="false"
width="800px"
@update:modelValue="$emit('update:modelValue')">
<el-dialog append-to-body title="实验准备" :close-on-click-modal="false" width="800px" @update:modelValue="value => $emit('update:modelValue', value)">
<el-form ref="formRef" :model="form" :rules="rules" label-suffix=":">
<el-form-item label="实验名称">{{ data.name }} </el-form-item>
<el-form-item prop="detail">
......
......@@ -4,7 +4,8 @@ import { Document } from '@element-plus/icons-vue'
import { filesize } from 'filesize'
interface Props {
experiment_id: string
experiment_id: string,
exam_status?: number
}
interface QuestionListItem {
experiment_id: string
......@@ -110,7 +111,7 @@ function customFloor(num: number) {
<el-card shadow="never" class="question-item" v-if="questionDetail">
<h3 class="question-item__title">{{ getQuestionTypeName(questionDetail.type) }}</h3>
<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 class="question-item__content">{{ questionDetail.content }}</p>
<ul class="question-item__files">
......
......@@ -45,11 +45,7 @@ const update = () => {
</script>
<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" :rules="rules">
<el-form-item label="实验报告文件" prop="files">
<AppUpload
......
......@@ -48,12 +48,7 @@ const update = () => {
</script>
<template>
<el-dialog
append-to-body
title="实验结果"
:close-on-click-modal="false"
width="800px"
@update:modelValue="$emit('update:modelValue')">
<el-dialog append-to-body title="实验结果" :close-on-click-modal="false" width="800px" @update:modelValue="value => $emit('update:modelValue', value)">
<el-form ref="formRef" :model="form" :rules="rules" label-suffix=":">
<el-form-item label="实验名称">{{ data.name }} </el-form-item>
<el-form-item prop="detail">
......
......@@ -140,6 +140,7 @@ export interface ExperimentInfo {
student: ExperimentStudent
is_commit_report: boolean
is_commit: boolean
exam_status: number
}
interface IdName {
......
......@@ -25,6 +25,7 @@ const ReportFilePreview = defineAsyncComponent(() => import('../components/Repor
const PrepareDialog = defineAsyncComponent(() => import('../components/PrepareDialog.vue'))
const ResultDialog = defineAsyncComponent(() => import('../components/ResultDialog.vue'))
const ReportPreview = defineAsyncComponent(() => import('../components/ReportPreview.vue'))
const Exam = defineAsyncComponent(() => import('../components/Exam.vue'))
const route = useRoute()
......@@ -73,13 +74,13 @@ provide('info', $$(experimentInfo))
function fetchInfo() {
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
})
}
function fetchExperimentRecord() {
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)) {
detail = undefined
} else {
......@@ -93,13 +94,18 @@ watchEffect(() => {
fetchExperimentRecord()
})
const tabActive = ref('qa')
const examURL = ref('')
// 右侧
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
? `${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}`
})
let iframeKey = $ref(Date.now())
// 返回首页
function handleBackHome() {
......@@ -139,7 +145,7 @@ function handleCapture() {
function handleCaptureCallback(event: MessageEvent) {
const { data } = event
if (data.action === 'screenshot' && data.timestamp === screenshotTimestamp) {
upload(data.blob).then(url => {
upload(data.blob).then((url) => {
uploadPicture(url)
})
}
......@@ -218,7 +224,7 @@ function handleReportPreviewReady() {
</script>
<template>
<DragPanel @resize="handleResize">
<DragPanel @resize="handleResize" v-if="appConfig.system !== 'x'">
<template #left>
<div class="lab-left">
<el-form :model="form" label-suffix=":" hide-required-asterisk>
......@@ -233,10 +239,13 @@ function handleReportPreviewReady() {
</el-select>
</el-form-item>
</el-form>
<el-tabs type="border-card" :key="form.experiment_id">
<el-tab-pane label="实验试题" lazy>
<el-tabs type="border-card" :key="form.experiment_id" v-model="tabActive">
<el-tab-pane label="实验试题" name="qa" lazy>
<Question :experiment_id="form.experiment_id"></Question>
</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>
<Info :data="experimentInfo"></Info>
</el-tab-pane>
......@@ -282,7 +291,90 @@ function handleReportPreviewReady() {
</AppCard>
<div class="lab-box">
<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>
</template>
</DragPanel>
......@@ -340,6 +432,7 @@ function handleReportPreviewReady() {
flex: 1;
width: 100%;
margin-top: 20px;
display: flex;
}
.iframe {
width: 100%;
......
{
"extends": "@vue/tsconfig/tsconfig.node.json",
"include": ["vite.config.*", "vitest.config.*", "cypress.config.*"],
"extends": "@tsconfig/node20/tsconfig.json",
"include": ["vite.config.*", "vitest.config.*", "cypress.config.*", "playwright.config.*"],
"compilerOptions": {
"composite": true,
"types": ["node"]
......
{
"extends": "@vue/tsconfig/tsconfig.web.json",
"extends": "@vue/tsconfig/tsconfig.json",
"include": ["auto-imports.d.ts", "env.d.ts", "src/**/*", "src/**/*.vue"],
"compilerOptions": {
"allowJs": true,
......@@ -7,7 +7,7 @@
"paths": {
"@/*": ["./src/*"]
},
"types": ["element-plus/global"]
"types": ["element-plus/global", "@vue-macros/reactivity-transform/macros-global"]
},
"references": [
......
......@@ -6,16 +6,18 @@ import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// import checker from 'vite-plugin-checker'
import AutoImport from 'unplugin-auto-import/vite'
import ReactivityTransform from '@vue-macros/reactivity-transform/vite'
export default defineConfig(() => ({
// base: mode === 'prod' ? 'https://webapp-pub.ezijing.com/website/prod/saas-lab/' : '/',
plugins: [
vue({ reactivityTransform: true, script: { defineModel: true } }),
vue(),
AutoImport({
imports: ['vue', 'vue/macros', 'vue-router', '@vueuse/core', '@vueuse/math'],
imports: ['vue', 'vue-router', '@vueuse/core', '@vueuse/math'],
dts: true,
eslintrc: { enabled: true }
})
}),
ReactivityTransform()
// checker({ vueTsc: true, eslint: { lintCommand: 'eslint "./src/**/*.{vue,js,jsx,ts,tsx}"' } })
],
server: {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论