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

chore: update

上级 7649fdcd
...@@ -115,4 +115,7 @@ export interface GroupItem { ...@@ -115,4 +115,7 @@ export interface GroupItem {
student_num: number student_num: number
updated_operator: string updated_operator: string
updated_time: string updated_time: string
experiment_name?: string
course_name?: string
class_name?: string
} }
...@@ -64,10 +64,10 @@ function handleRemoveStudent(row: StudentItem) { ...@@ -64,10 +64,10 @@ function handleRemoveStudent(row: StudentItem) {
<template> <template>
<AppCard title="实验管理"> <AppCard title="实验管理">
<el-descriptions :column="2" v-if="detail"> <el-descriptions :column="2" v-if="detail">
<el-descriptions-item label="实验名称:">{{ detail.name }}</el-descriptions-item> <el-descriptions-item label="实验名称:">{{ detail.experiment_name }}</el-descriptions-item>
<el-descriptions-item label="实验课程:">{{ detail.name }}</el-descriptions-item> <el-descriptions-item label="实验课程:">{{ detail.course_name }}</el-descriptions-item>
<el-descriptions-item label="小组名称:">{{ detail.name }}</el-descriptions-item> <el-descriptions-item label="小组名称:">{{ detail.name }}</el-descriptions-item>
<el-descriptions-item label="班级名称:">{{ detail.name }}</el-descriptions-item> <el-descriptions-item label="班级名称:">{{ detail.class_name }}</el-descriptions-item>
</el-descriptions> </el-descriptions>
<AppList v-bind="listOptions" ref="appList"> <AppList v-bind="listOptions" ref="appList">
<template #header-buttons> <template #header-buttons>
......
import httpRequest from '@/utils/axios'
// 获取课程列表
export function getCourseList() {
return httpRequest.get('/api/student/v1/student/course/all')
}
// 获取实验指导书
export function getExperimentBook(params: { experiment_id: string }) {
return httpRequest.get('/api/student/v1/student/experiment-book/detail', { params })
}
// 获取实验视频
export function getExperimentVideoList(params: { experiment_id: string }) {
return httpRequest.get('/api/student/v1/student/experiment-video/all', { params })
}
// 获取实验视频播放信息
export function getExperimentVideoPlayInfo(params: { source_id: string }) {
return httpRequest.get('/api/student/v1/student/experiment-video/replay-list', { params })
}
// 获取实验讨论交流
export function getExperimentDiscussList(params: {
experiment_id: string
tag: string
page?: number
'per-page'?: number
}) {
return httpRequest.get('/api/student/v1/student/experiment-topic/list', { params })
}
// 发表新话题
export function addExperimentDiscuss(data: { experiment_id: string; title: string; content: string }) {
return httpRequest.post('/api/student/v1/student/experiment-topic/issue', data)
}
// 发表回复
export function addExperimentDiscussComment(data: { discussion_id: string; content: string }) {
return httpRequest.post('/api/student/v1/student/experiment-topic/comment', data)
}
<script setup lang="ts"></script> <script setup lang="ts">
import type { ExperimentBookType } from '../types'
import { getExperimentBook } from '../api'
interface Props {
experiment_id?: string
}
const props = defineProps<Props>()
let detail = $ref<ExperimentBookType>()
function fetchInfo() {
if (!props.experiment_id) return
getExperimentBook({ experiment_id: props.experiment_id }).then(res => {
detail = res.data.detail
})
}
watchEffect(() => {
fetchInfo()
})
const isEmpty = $computed(() => {
return !props.experiment_id || !detail?.id
})
// 文件扩展名
const fileExtensionName = $computed(() => {
return detail?.url?.split('.').pop() || ''
})
// office文件
const officeUrl = $computed(() => {
return ['pptx', 'doc', 'docx', 'xls', 'xlsx'].includes(fileExtensionName)
? `https://view.officeapps.live.com/op/view.aspx?src=${detail.url}`
: ''
})
</script>
<template> <template>
<div>book</div> <el-empty description="暂无数据" v-if="isEmpty" />
<template v-else>
<iframe :src="officeUrl" frameborder="0" style="width: 100%; height: 100%" v-if="officeUrl"></iframe>
<object :data="detail.url" style="width: 100%; height: 100%; object-fit: none" v-else></object>
</template>
</template> </template>
<style lang="scss" scoped></style>
<script setup lang="ts"> <script setup lang="ts">
import DiscussAddDialog from './DiscussAddDialog.vue' import type { ExperimentDiscussType } from '../types'
import DiscussCommentAddDialog from './DiscussCommentAddDialog.vue' import { getExperimentDiscussList } from '../api'
import DiscussItem from './DiscussItem.vue'
const DiscussAddDialog = defineAsyncComponent(() => import('./DiscussAddDialog.vue'))
interface Props {
experiment_id?: string
}
const props = defineProps<Props>()
const discussTag = $ref('1')
let list = $ref<ExperimentDiscussType[]>([])
function fetchInfo() {
if (!props.experiment_id) return
getExperimentDiscussList({ experiment_id: props.experiment_id, tag: discussTag }).then(res => {
list = res.data.list
})
}
watchEffect(() => {
fetchInfo()
})
const isEmpty = $computed(() => {
return !props.experiment_id || !list.length
})
const dialogVisible = $ref(false) const dialogVisible = $ref(false)
const commentDialogVisible = $ref(false)
</script> </script>
<template> <template>
<el-radio-group> <el-radio-group v-model="discussTag">
<el-radio :label="1">我发起的</el-radio> <el-radio :label="1">我发起的</el-radio>
<el-radio :label="2">我回复的</el-radio> <el-radio :label="2">我回复的</el-radio>
<el-radio :label="3">我的小组</el-radio> <el-radio :label="3">我的小组</el-radio>
...@@ -16,12 +37,13 @@ const commentDialogVisible = $ref(false) ...@@ -16,12 +37,13 @@ const commentDialogVisible = $ref(false)
<el-row justify="end"> <el-row justify="end">
<el-button round type="primary" @click="dialogVisible = true">发表新话题</el-button> <el-button round type="primary" @click="dialogVisible = true">发表新话题</el-button>
</el-row> </el-row>
<div>discuss</div> <el-empty description="暂无数据" v-if="isEmpty" />
<el-row justify="end"> <template v-else>
<el-button round type="primary" @click="commentDialogVisible = true">我要评论</el-button> <DiscussItem v-for="item in list" :key="item.id" :data="item"></DiscussItem>
</el-row> </template>
<DiscussAddDialog v-model="dialogVisible"></DiscussAddDialog>
<DiscussCommentAddDialog v-model="commentDialogVisible"></DiscussCommentAddDialog> <!-- 发表新话题 -->
<DiscussAddDialog v-model="dialogVisible" v-if="dialogVisible"></DiscussAddDialog>
</template> </template>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>
<script setup lang="ts">
import type { ExperimentDiscussType } from '../types'
const DiscussCommentAddDialog = defineAsyncComponent(() => import('./DiscussCommentAddDialog.vue'))
interface Props {
data: ExperimentDiscussType
}
defineProps<Props>()
const commentDialogVisible = $ref(false)
</script>
<template>
<div class="discuss-item">
<div class="discuss-item-user">
<img src="" />
<p></p>
</div>
<div class="discuss-item-main"></div>
</div>
<el-row justify="end">
<el-button round type="primary" @click="commentDialogVisible = true">我要评论</el-button>
</el-row>
<!-- 我要评论 -->
<DiscussCommentAddDialog v-model="commentDialogVisible" v-if="commentDialogVisible"></DiscussCommentAddDialog>
</template>
<style lang="scss" scoped>
.video-item {
h2 {
font-size: 16px;
color: #333;
margin-bottom: 10px;
text-align: center;
}
img {
width: 100%;
height: 200px;
object-fit: cover;
}
}
</style>
<script setup lang="ts"></script> <script setup lang="ts">
interface Props {
experiment_id?: string
}
defineProps<Props>()
</script>
<template> <template>
<div>result</div> <div>result</div>
......
<script setup lang="ts"></script> <script setup lang="ts">
import type { ExperimentVideoType } from '../types'
import VideoItem from './VideoItem.vue'
import { getExperimentVideoList } from '../api'
interface Props {
experiment_id?: string
}
const props = defineProps<Props>()
let list = $ref<ExperimentVideoType[]>([])
function fetchInfo() {
if (!props.experiment_id) return
getExperimentVideoList({ experiment_id: props.experiment_id }).then(res => {
list = res.data.list
})
}
watchEffect(() => {
fetchInfo()
})
const isEmpty = $computed(() => {
return !props.experiment_id || !list.length
})
</script>
<template> <template>
<div>video</div> <el-empty description="暂无数据" v-if="isEmpty" />
<template v-else>
<VideoItem v-for="item in list" :key="item.id" :data="item"></VideoItem>
</template>
</template> </template>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>
<script setup lang="ts">
import type { ExperimentVideoType, PlayInfo } from '../types'
import { getExperimentVideoPlayInfo } from '../api'
interface Props {
data: ExperimentVideoType
}
const props = defineProps<Props>()
let playList = $ref<PlayInfo[]>([])
function fetchInfo() {
getExperimentVideoPlayInfo({ source_id: props.data.source_id }).then(res => {
playList = res.data.play_info_list
})
}
console.log(playList)
onMounted(() => {
fetchInfo()
})
</script>
<template>
<div class="video-item">
<h2>{{ data.name }}</h2>
<img :src="data.cover" />
</div>
</template>
<style lang="scss" scoped>
.video-item {
h2 {
font-size: 16px;
color: #333;
margin-bottom: 10px;
text-align: center;
}
img {
width: 100%;
height: 200px;
object-fit: cover;
}
}
</style>
import type { CourseType } from '../types'
import { getCourseList } from '../api'
const courses = ref<CourseType[]>([])
export function useGetCourseList() {
!courses.value.length &&
getCourseList().then((res: any) => {
courses.value = res.data.list
})
return { courses }
}
export interface CourseType {
id: string
name: string
cover: string
experiments: ExperimentType[]
}
export interface ExperimentType {
id: string
name: string
course_id: string
organ_id: string
}
export interface ExperimentBookType {
id: string
name: string
size: string
type: string
url: string
}
export interface ExperimentVideoType {
id: string
name: string
size: string
type: string
source_id: string
length: number
cover: string
}
export interface PlayInfo {
BitDepth: number
Bitrate: string
CreationTime: string
Definition: string
Duration: string
Encrypt: number
Format: string
Fps: string
HDRType: string
Height: number
JobId: string
ModificationTime: string
NarrowBandType: string
PlayURL: string
PreprocessStatus: string
Size: number
Specification: string
Status: string
StreamType: string
Width: number
}
export interface ExperimentDiscussType {
content: string
created_operator: string
created_time: string
id: string
is_reply: string
replies: ExperimentDiscussCommentType[]
reply_count: number
sso_user: UserType
student_id: string
title: string
updated_time: string
}
export interface ExperimentDiscussCommentType {
content: string
created_time: string
discussion_id: string
id: string
role: string
sso_id: string
sso_user: UserType
}
export interface UserType {
avatar: string
id: string
nickname: string
real_name: string
username: string
}
<script setup lang="ts"> <script setup lang="ts">
import type { CourseType } from '../types'
import { HomeFilled, Select, UploadFilled, FullScreen } from '@element-plus/icons-vue' import { HomeFilled, Select, UploadFilled, FullScreen } from '@element-plus/icons-vue'
import type { FormInstance, FormRules } from 'element-plus' import { useGetCourseList } from '../composables/useGetCourseList'
import ReportDialog from '../components/ReportDialog.vue'
const Book = defineAsyncComponent(() => import('../components/Book.vue')) const Book = defineAsyncComponent(() => import('../components/Book.vue'))
const Video = defineAsyncComponent(() => import('../components/Video.vue')) const Video = defineAsyncComponent(() => import('../components/Video.vue'))
const Discuss = defineAsyncComponent(() => import('../components/Discuss.vue')) const Discuss = defineAsyncComponent(() => import('../components/Discuss.vue'))
const Result = defineAsyncComponent(() => import('../components/Result.vue')) const Result = defineAsyncComponent(() => import('../components/Result.vue'))
const ReportDialog = defineAsyncComponent(() => import('../components/ReportDialog.vue'))
// 左侧 // 左侧
const formRef = $ref<FormInstance>() const form = reactive<{ course?: CourseType; experiment_id: string }>({
const form = reactive({ course_id: '', e_id: 1 }) course: undefined,
const rules = ref<FormRules>({ experiment_id: '6965149866569760768'
course_id: [{ required: true, message: '请选择课程', trigger: 'change' }], })
e_id: [{ required: true, message: '请选择实验', trigger: 'change' }] // 课程列表
const { courses } = useGetCourseList()
// 实验列表
const experimentList = $computed(() => {
return form.course?.experiments || []
}) })
// 右侧 // 右侧
const LAB_URL = import.meta.env.VITE_LAB_URL const LAB_URL = import.meta.env.VITE_LAB_URL
...@@ -61,33 +66,30 @@ onUnmounted(() => { ...@@ -61,33 +66,30 @@ onUnmounted(() => {
<template> <template>
<section class="lab"> <section class="lab">
<div class="lab-left"> <div class="lab-left">
<el-form <el-form :model="form" label-suffix=":" hide-required-asterisk>
:model="form" <el-form-item label="请选择课程">
:rules="rules" <el-select value-key="id" v-model="form.course" style="width: 100%">
label-suffix=":" <el-option v-for="item in courses" :key="item.id" :label="item.name" :value="item"></el-option>
hide-required-asterisk </el-select>
ref="formRef"
style="--el-text-color-regular: #fff"
>
<el-form-item label="请选择课程" prop="course_id">
<el-select style="width: 100%"></el-select>
</el-form-item> </el-form-item>
<el-form-item label="请选择实验" prop="e_id"> <el-form-item label="请选择实验" prop="experiment_id">
<el-select style="width: 100%"></el-select> <el-select v-model="form.experiment_id" 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-item>
</el-form> </el-form>
<el-tabs type="border-card"> <el-tabs type="border-card" stretch>
<el-tab-pane label="实训指导" name="first"> <el-tab-pane label="实训指导" lazy>
<Book></Book> <Book :experiment_id="form.experiment_id"></Book>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="操作视频" name="second"> <el-tab-pane label="操作视频" lazy>
<Video></Video> <Video :experiment_id="form.experiment_id"></Video>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="讨论交流" name="third"> <el-tab-pane label="讨论交流" lazy>
<Discuss></Discuss> <Discuss :experiment_id="form.experiment_id"></Discuss>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="过程与结果" name="fourth"> <el-tab-pane label="过程与结果" lazy>
<Result></Result> <Result :experiment_id="form.experiment_id"></Result>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
</div> </div>
...@@ -119,8 +121,8 @@ onUnmounted(() => { ...@@ -119,8 +121,8 @@ onUnmounted(() => {
</div> </div>
</div> </div>
</section> </section>
<!-- 上传报告 -->
<ReportDialog v-model="reportDialogVisible"></ReportDialog> <ReportDialog v-model="reportDialogVisible" v-if="reportDialogVisible"></ReportDialog>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
...@@ -129,11 +131,23 @@ onUnmounted(() => { ...@@ -129,11 +131,23 @@ onUnmounted(() => {
height: 100%; height: 100%;
} }
.lab-left { .lab-left {
display: flex;
flex-direction: column;
width: 500px; width: 500px;
padding: 20px; padding: 20px;
color: #fff;
background-color: rgba(45, 48, 55, 1); background-color: rgba(45, 48, 55, 1);
border-radius: 6px; border-radius: 6px;
.el-tabs {
flex: 1;
}
:deep(.el-tabs__content) {
height: calc(100% - 40px);
box-sizing: border-box;
}
:deep(.el-tab-pane) {
height: 100%;
overflow-y: auto;
}
} }
.lab-right { .lab-right {
margin-left: 20px; margin-left: 20px;
......
...@@ -26,8 +26,12 @@ export default defineConfig(({ mode }) => ({ ...@@ -26,8 +26,12 @@ export default defineConfig(({ mode }) => ({
cert: fs.readFileSync(path.join(__dirname, './https/dev.ezijing.com.pem')) cert: fs.readFileSync(path.join(__dirname, './https/dev.ezijing.com.pem'))
}, },
proxy: { proxy: {
'/api': 'https://resource-center.ezijing.com', '/api/student': {
'/lab': 'https://dev.ezijing.com' target: 'http://test-resource-api.ezijing.com:8001',
changeOrigin: true,
rewrite: path => path.replace(/^\/api\/student/, '')
},
'/api': 'https://resource-center.ezijing.com'
} }
}, },
resolve: { resolve: {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论