Compare commits

..

No commits in common. "7e9ac2bf740f7434ed7798ae1eb50ffb38ad4d23" and "013669dd35cfd9fd076974c851959895aad950f7" have entirely different histories.

25 changed files with 239 additions and 884 deletions

View File

@ -16,9 +16,4 @@ VITE_APP_RES_FILE_PATH = 'https://file.ysaix.com:7868/src/assets/textbook/booktx
VITE_APP_BUILD_BASE_PATH = 'https://file.ysaix.com:7868/' VITE_APP_BUILD_BASE_PATH = 'https://file.ysaix.com:7868/'
# websocket 地址
# VITE_APP_WS_URL = 'wss://file.ysaix.com:7868'
VITE_APP_WS_URL = 'ws://192.168.2.16:7865'
# 是否显示开发工具
VITE_SHOW_DEV_TOOLS = 'true' VITE_SHOW_DEV_TOOLS = 'true'

View File

@ -18,8 +18,4 @@ VITE_APP_RES_FILE_PATH = 'https://prev.ysaix.com:7868/src/assets/textbook/booktx
VITE_APP_BUILD_BASE_PATH = 'https://prev.ysaix.com:7868/' VITE_APP_BUILD_BASE_PATH = 'https://prev.ysaix.com:7868/'
# websocket 地址
VITE_APP_WS_URL = 'wss://file.ysaix.com:7868'
# 是否显示开发工具
VITE_SHOW_DEV_TOOLS = 'false' VITE_SHOW_DEV_TOOLS = 'false'

View File

@ -28,7 +28,6 @@ import * as API_entpcoursefile from '@/api/education/entpcoursefile' // 相关ap
import { PPTApi } from './api' import { PPTApi } from './api'
import { sessionStore } from '@/utils/store' // electron-store import { sessionStore } from '@/utils/store' // electron-store
import './api/watcher' // import './api/watcher' //
import './api/classcourse' //
const loading = ref(true) const loading = ref(true)
const _isPC = isPC() const _isPC = isPC()

View File

@ -1,22 +0,0 @@
/**
* @author zdg
* @description
*/
import type { Classcourse } from './types'
import { sessionStore } from '@/utils/store' // electron-store 状态管理
import * as useStore from '../store' // pptist-状态管理
import { ChatWs } from '@/plugins/socket' // 聊天socket
const screenStore = useStore.useScreenStore() // 全屏-状态管理
const classcourseStore = useStore.useClasscourseStore() // 课堂信息-状态管理
const classcourse: Classcourse = sessionStore.get('curr.classcourse') // 课堂信息
// 如果课堂信息有值则连接socket
if (!!classcourse) {
// 连接socket
const ws = new ChatWs()
console.log('ws- ',ws)
// ChatWs.connect(classcourse.id)
classcourseStore.setClasscourse(classcourse)
}
// 打开全屏
screenStore.setScreening(!!classcourse)

View File

@ -11,15 +11,10 @@ import * as API_smarttalk from '@/api/file' // 相关api
import * as useStore from '../store' // pptist-状态管理 import * as useStore from '../store' // pptist-状态管理
import { sessionStore } from '@/utils/store' // electron-store 状态管理 import { sessionStore } from '@/utils/store' // electron-store 状态管理
import useUserStore from '@/store/modules/user' // 外部-用户信息 import useUserStore from '@/store/modules/user' // 外部-用户信息
import * as Api_server from '@/api/apiService' // 相关api
import * as commUtils from '@/utils/comm.js'
const slidesStore = useStore.useSlidesStore() const slidesStore = useStore.useSlidesStore()
const userStore = useUserStore() const userStore = useUserStore()
import { getClassWorkList,getStudentClassWorkData } from '@/views/tool/createHomework'
import {createWindow} from '@/utils/tool'
import { useToolState } from '@/store/modules/tool'
const toolStore = useToolState()
/** 工具类 */ /** 工具类 */
export class Utils { export class Utils {
static mxData: any = { static mxData: any = {
@ -60,6 +55,7 @@ export class PPTApi {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
const params: object = { parentid, orderByColumn: 'fileidx', isAsc: 'asc', pageSize: 9999 } const params: object = { parentid, orderByColumn: 'fileidx', isAsc: 'asc', pageSize: 9999 }
const res: Result = await API_entpcoursefile.listEntpcoursefileNew(params) const res: Result = await API_entpcoursefile.listEntpcoursefileNew(params)
console.log(res.rows,'res.rows');
if (res.code === 200) { if (res.code === 200) {
const slides = (res.rows || []).map(o => { const slides = (res.rows || []).map(o => {
if (!!o.datacontent) { if (!!o.datacontent) {
@ -73,8 +69,7 @@ export class PPTApi {
// 活动列表处理 // 活动列表处理
const workList = (res.rows || []).map(o => o.activityContent) const workList = (res.rows || []).map(o => o.activityContent)
const workItem = [...res.rows] const workItem = [...res.rows]
// 加入活动后刷新ppt数据内容不跟换为第一页 slidesStore.updateSlideIndex(0) // 下标0 为第一页
// slidesStore.updateSlideIndex(0) // 下标0 为第一页
slidesStore.setSlides(slides) // 写入数据 slidesStore.setSlides(slides) // 写入数据
// 写入作业列表数据 // 写入作业列表数据
slidesStore.setWorkList(workList) slidesStore.setWorkList(workList)
@ -141,15 +136,13 @@ export class PPTApi {
id: currentSlide.id, id: currentSlide.id,
datacontent: JSON.stringify(currentSlide), datacontent: JSON.stringify(currentSlide),
} }
Utils.mxThrottle(() => {this.updateSlide(params)}, 200, 2) Utils.mxThrottle(() => {this.updateSlide(params)}, 1000, 2)
} }
} }
// 更新幻灯片 // 更新幻灯片
static updateSlide(data: object): Promise<Boolean> { static updateSlide(data: object): Promise<Boolean> {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
const res: Result = await API_entpcoursefile.updateEntpcoursefileNew(data) const res: Result = await API_entpcoursefile.updateEntpcoursefileNew(data)
console.log(data,'data');
console.log(res,'dresata');
if (res.code === 200) { if (res.code === 200) {
resolve(true) resolve(true)
} else msgUtils.msgError(res.msg || '更新失败');resolve(false) } else msgUtils.msgError(res.msg || '更新失败');resolve(false)
@ -191,30 +184,6 @@ export class PPTApi {
else msgUtils.msgError(res.msg || '更新失败');return false else msgUtils.msgError(res.msg || '更新失败');return false
}) })
} }
// 图片|音频|视频 转换为在线地址
static toRousrceUrl =async (o:any) => {
const formData = new FormData()
formData.append('file', o)
const res = await Api_server.Other.uploadFile(formData)
if (res && res.code == 200){
const url = res?.url
url &&(o.src = url)
return url
}
}
} }
export class Homework{
// 作业弹窗
static async showHomework(id: any) {
let result = await getClassWorkList(id)
  result = await getStudentClassWorkData()
  localStorage.setItem('teachClassWorkItem', JSON.stringify(result[0]));
  toolStore.isTaskWin=true; // 设置打开批改窗口
//   emit('closeActive')
  createWindow('open-taskwin',{url:'/teachClassTask'});
}
}
export default PPTApi export default PPTApi

View File

@ -5,20 +5,4 @@ export interface Result {
data?: any data?: any
rows?: Array<any>, rows?: Array<any>,
total?: number total?: number
}
/** 课程信息 */
export interface Classcourse {
id?: number|string, // 课程id
coursetitle?: string, // 课程名称
coursetype?: string, // 课程类型
courseverid?: string, // 课程版本id
coursedesc?: string, // 课程描述
status?: number, // 课程状态
teacherid?: number|string, // 教师id
entpcoursefileid?: number|string, // 课程文件id
classid?: number|string, // 班级id
entpcourseid?: number|string, // 章节中间表id
plandate?: string, // 计划时间
opendate?: string, // 开课时间
} }

View File

@ -1,10 +1,9 @@
import { useScreenStore, useSlidesStore, useClasscourseStore } from '../store' import { useScreenStore, useSlidesStore } from '../store'
import { enterFullscreen, exitFullscreen, isFullscreen } from '../utils/fullscreen' import { enterFullscreen, exitFullscreen, isFullscreen } from '../utils/fullscreen'
export default () => { export default () => {
const screenStore = useScreenStore() const screenStore = useScreenStore()
const slidesStore = useSlidesStore() const slidesStore = useSlidesStore()
const classcourseStore = useClasscourseStore() // 课堂信息
// 进入放映状态(从当前页开始) // 进入放映状态(从当前页开始)
const enterScreening = () => { const enterScreening = () => {
@ -20,11 +19,7 @@ export default () => {
// 退出放映状态 // 退出放映状态
const exitScreening = () => { const exitScreening = () => {
const classcourse = classcourseStore.classcourse screenStore.setScreening(false)
if (!!classcourse) { //DOTO 有课堂,执行退相关操作
console.log('退出放映状态')
window.close()
} else screenStore.setScreening(false)
if (isFullscreen()) exitFullscreen() if (isFullscreen()) exitFullscreen()
} }

View File

@ -1,18 +0,0 @@
import { defineStore } from 'pinia'
import type { Classcourse } from '../api/types'
export interface ClasscourseState {
classcourse: Classcourse
}
export const useClasscourseStore = defineStore('classcourse', {
state: (): ClasscourseState => ({
classcourse: null, // 课堂信息
}),
actions: {
setClasscourse(classcourse: Classcourse) {
this.classcourse = classcourse
},
},
})

View File

@ -3,7 +3,6 @@ import { useSlidesStore } from './slides'
import { useSnapshotStore } from './snapshot' import { useSnapshotStore } from './snapshot'
import { useKeyboardStore } from './keyboard' import { useKeyboardStore } from './keyboard'
import { useScreenStore } from './screen' import { useScreenStore } from './screen'
import { useClasscourseStore } from './classcourse'
export { export {
useMainStore, useMainStore,
@ -11,5 +10,4 @@ export {
useSnapshotStore, useSnapshotStore,
useKeyboardStore, useKeyboardStore,
useScreenStore, useScreenStore,
useClasscourseStore,
} }

View File

@ -7,44 +7,19 @@
/> />
<template v-if="type === 'video'"> <template v-if="type === 'video'">
<el-tabs :tab-position="'left'" class="demo-tabs" v-model="tabvalue"> <Input v-model:value="videoSrc" placeholder="请输入视频地址e.g. https://xxx.mp4"></Input>
<el-tab-pane label="常规" > <div class="btns">
<Input v-model:value="videoSrc" style="width:100%" placeholder="请输入视频地址e.g. https://xxx.mp4"></Input>
</el-tab-pane>
<el-tab-pane label="上传" >
<FileInput accept="video/*" @change="files => insertImageElementvideo(files)">
<div class="updivs">+点击上传视频</div>
</FileInput>
</el-tab-pane>
</el-tabs>
<div class="btns" v-if="tabvalue=='0'">
<Button @click="emit('close')" style="margin-right: 10px;">取消</Button> <Button @click="emit('close')" style="margin-right: 10px;">取消</Button>
<Button type="primary" @click="insertVideo()">确认</Button> <Button type="primary" @click="insertVideo()">确认</Button>
</div> </div>
</template> </template>
<template v-if="type === 'audio'"> <template v-if="type === 'audio'">
<el-tabs :tab-position="'left'" class="demo-tabs" v-model="tabvalue1"> <Input v-model:value="audioSrc" placeholder="请输入音频地址e.g. https://xxx.mp3"></Input>
<el-tab-pane label="常规" > <div class="btns">
<Input v-model:value="audioSrc" style="width:100%" placeholder="请输入音频地址e.g. https://xxx.mp3"></Input>
</el-tab-pane>
<el-tab-pane label="上传" >
<FileInput accept="audio/*" @change="files => insertImageElementaudio(files)">
<div class="updivs">+点击上传音频</div>
</FileInput>
</el-tab-pane>
</el-tabs>
<div class="btns" v-if="tabvalue1=='0'">
<Button @click="emit('close')" style="margin-right: 10px;">取消</Button> <Button @click="emit('close')" style="margin-right: 10px;">取消</Button>
<Button type="primary" @click="insertAudio()">确认</Button> <Button type="primary" @click="insertAudio()">确认</Button>
</div> </div>
</template> </template>
</div> </div>
</template> </template>
@ -55,8 +30,6 @@ import message from '../../../utils/message'
import Tabs from '../../../components/Tabs.vue' import Tabs from '../../../components/Tabs.vue'
import Input from '../../../components/Input.vue' import Input from '../../../components/Input.vue'
import Button from '../../../components/Button.vue' import Button from '../../../components/Button.vue'
import FileInput from '../../../components/FileInput.vue'
import { PPTApi } from '../../../api'
type TypeKey = 'video' | 'audio' type TypeKey = 'video' | 'audio'
interface TabItem { interface TabItem {
@ -72,33 +45,9 @@ const emit = defineEmits<{
const type = ref<TypeKey>('video') const type = ref<TypeKey>('video')
const videoSrc = ref('') const videoSrc = ref('https://mazwai.com/videvo_files/video/free/2019-01/small_watermarked/181004_04_Dolphins-Whale_06_preview.webm')
const audioSrc = ref('') const audioSrc = ref('https://freesound.org/data/previews/614/614107_11861866-lq.mp3')
// https://freesound.org/data/previews/614/614107_11861866-lq.mp3
const tabvalue = ref('0')
const tabvalue1 = ref('0')
const insertImageElementvideo = (files: FileList) => {
console.log('files', files)
const imageFile = files[0]
if (!imageFile) return
PPTApi.toRousrceUrl(imageFile).then(data=>{
videoSrc.value=data
insertVideo()
})
}
const insertImageElementaudio = (files: FileList) => {
console.log('files', files)
const imageFile = files[0]
if (!imageFile) return
PPTApi.toRousrceUrl(imageFile).then(data=>{
videoSrc.value=data
insertAudio()
})
}
const tabs: TabItem[] = [ const tabs: TabItem[] = [
{ key: 'video', label: '视频' }, { key: 'video', label: '视频' },
{ key: 'audio', label: '音频' }, { key: 'audio', label: '音频' },
@ -125,33 +74,4 @@ const insertAudio = () => {
margin-top: 10px; margin-top: 10px;
text-align: right; text-align: right;
} }
.updivs{
width: 100%;
height: 100%;
background: #f5f7fa;
border: 1px dashed #d9d9d9;
border-radius: 6px;
text-align: center;
line-height: 100px;
cursor: pointer;
}
.demo-tabs{
:deep(.el-tabs__content){
display: flex;
align-items: center;
div{
width: 100%;
}
}
:deep( .el-tabs__item.is-active) {
color: #d14424;
}
:deep( .el-tabs__item:hover) {
color: #d14424;
}
:deep(.el-tabs__active-bar) {
background-color: #d14424;
height: 3px;
}
}
</style> </style>

View File

@ -161,9 +161,7 @@ import Popover from '../../../components/Popover.vue'
import PopoverMenuItem from '../../../components/PopoverMenuItem.vue' import PopoverMenuItem from '../../../components/PopoverMenuItem.vue'
import QuestToPPTist from '@/views/classTask/newClassTaskAssign/questToPPTist/index.vue' import QuestToPPTist from '@/views/classTask/newClassTaskAssign/questToPPTist/index.vue'
import MaterialDialog from './MaterialDialog.vue' import MaterialDialog from './MaterialDialog.vue'
import { PPTApi } from '../../../api'
import TextCreateImg from '@/components/ai-kolors/index.vue' import TextCreateImg from '@/components/ai-kolors/index.vue'
import { toPng } from 'html-to-image' // html-to-image
const mainStore = useMainStore() const mainStore = useMainStore()
const { creatingElement, creatingCustomShape, showSelectPanel, showSearchPanel, showNotesPanel } = storeToRefs(mainStore) const { creatingElement, creatingCustomShape, showSelectPanel, showSearchPanel, showNotesPanel } = storeToRefs(mainStore)
@ -196,21 +194,13 @@ const {
} = useCreateElement() } = useCreateElement()
const insertImageElement = (files: FileList) => { const insertImageElement = (files: FileList) => {
console.log('files', files)
const imageFile = files[0] const imageFile = files[0]
if (!imageFile) return if (!imageFile) return
// 线 getImageDataURL(imageFile).then(dataURL => createImageElement(dataURL))
PPTApi.toRousrceUrl(imageFile).then(data=>{
createImageElement(data)
})
// getImageDataURL(imageFile).then(dataURL => {
// createImageElement(dataURL)
// })
} }
const onhtml2canvas = async (html: HTMLElement) => { const onhtml2canvas = (imgbs64: string) => {
const ele = await toPng(html); createImageElement(imgbs64)
createImageElement(ele);
} }
const shapePoolVisible = ref(false) const shapePoolVisible = ref(false)

View File

@ -162,10 +162,6 @@ const setDialogForExport = (type: DialogForExportTypes) => {
.icon { .icon {
font-size: 18px; font-size: 18px;
color: #666; color: #666;
:deep(svg) {
display: block !important;
}
} }
&:hover { &:hover {

View File

@ -67,6 +67,8 @@
<div class="page-number">幻灯片 {{slideIndex + 1}} / {{slides.length}}</div> <div class="page-number">幻灯片 {{slideIndex + 1}} / {{slides.length}}</div>
<!-- 引入活动的列表页面 -->
<Active ref="activeRef" v-show="false"/>
</div> </div>
</template> </template>
@ -86,6 +88,7 @@ import ThumbnailSlide from '../../../views/components/ThumbnailSlide/index.vue'
import LayoutPool from './LayoutPool.vue' import LayoutPool from './LayoutPool.vue'
import Popover from '../../../components/Popover.vue' import Popover from '../../../components/Popover.vue'
import Draggable from 'vuedraggable' import Draggable from 'vuedraggable'
import Active from '../Toolbar/ElementStylePanel/Active/index.vue'
const mainStore = useMainStore() const mainStore = useMainStore()
const slidesStore = useSlidesStore() const slidesStore = useSlidesStore()
@ -124,6 +127,7 @@ const {
updateSectionTitle, updateSectionTitle,
} = useSectionHandler() } = useSectionHandler()
const activeRef = ref()
// //
const thumbnailsRef = ref<InstanceType<typeof Draggable>>() const thumbnailsRef = ref<InstanceType<typeof Draggable>>()
@ -147,6 +151,8 @@ watch(() => slideIndex.value, () => {
// //
const changeSlideIndex = (index: number) => { const changeSlideIndex = (index: number) => {
console.log(workItem.value[index],'hasSection');
activeRef.value.clickPPTList(workItem.value[index])
mainStore.setActiveElementIdList([]) mainStore.setActiveElementIdList([])
@ -396,17 +402,12 @@ const contextmenusThumbnailItem = (): ContextmenuItem[] => {
.icon { .icon {
margin-right: 3px; margin-right: 3px;
font-size: 14px; font-size: 14px;
:deep(svg) {
display: block !important;
}
} }
} }
.thumbnail-list { .thumbnail-list {
padding: 5px 0; padding: 5px 0;
flex: 1; flex: 1;
overflow: auto; overflow: auto;
border-bottom: 1px solid $borderColor;
} }
.thumbnail-item { .thumbnail-item {
display: flex; display: flex;
@ -485,6 +486,7 @@ const contextmenusThumbnailItem = (): ContextmenuItem[] => {
.page-number { .page-number {
height: 40px; height: 40px;
font-size: 12px; font-size: 12px;
border-top: 1px solid $borderColor;
line-height: 40px; line-height: 40px;
text-align: center; text-align: center;
color: #666; color: #666;

View File

@ -30,14 +30,14 @@
<Divider /> <Divider />
<!-- 作业列表 --> <!-- 作业列表 -->
<div class="c-apt-r" v-loading='loadingActive'> <div class="c-apt-r">
<!-- 显示-作业内容 --> <!-- 显示-作业内容 -->
<template v-for="(item, index) in workList" :key="index"> <template v-for="(item, index) in workList">
<div class="item"> <div class="item">
<div class="item-title"> <div class="item-title">
<el-tag :type="getTagType(item.worktype) || 'primary'">{{item.worktype}}</el-tag> <el-tag :type="getTagType(item.worktype) || 'primary'">{{item.worktype}}</el-tag>
<el-tooltip :content="item.evaltitle" placement="top"> <el-tooltip :content="item.title||item.uniquekey" placement="top">
<div class="tt">{{item.evaltitle}}</div> <div class="tt">{{item.title||item.uniquekey}}</div>
</el-tooltip> </el-tooltip>
<el-button class="btn-del" type="danger" link @click="handleRemoveDemoActivityClassWork(item)">删除</el-button> <el-button class="btn-del" type="danger" link @click="handleRemoveDemoActivityClassWork(item)">删除</el-button>
</div> </div>
@ -45,12 +45,8 @@
</template> </template>
</div> </div>
<!-- // --> <!-- // -->
<el-dialog v-model="dialogVisible" append-to-body :show-close="false" width="90%" height="500"> <el-dialog v-model="dialogVisible" append-to-body :show-close="false" width="80%">
<el-scrollbar height="550"> <NewClassTsakAssign :currentCourse='currentCourse'/>
<div style="height: 550px;">
<NewClassTsakAssign :currentCourse='currentCourse' @getData="getData" />
</div>
</el-scrollbar>
</el-dialog> </el-dialog>
<!-- 活动引用 --> <!-- 活动引用 -->
<el-dialog <el-dialog
@ -59,9 +55,9 @@
append-to-body append-to-body
:show-close="false" :show-close="false"
width="40%" width="40%"
> @selection-change="handleSelectionChange">
<el-table :data="taskList" style="width: 100%" height="500" @selection-change="handleSelectionChange"> <el-table :data="taskList" style="width: 100%" height="500">
<el-table-column type="selection" width="55" :selectable="selectable"/> <el-table-column type="selection" :selectable="selectable" width="55" />
<el-table-column prop="evaltitle" label="活动名称" width="150" /> <el-table-column prop="evaltitle" label="活动名称" width="150" />
<el-table-column prop="worktype" label="活动类型" width="120" sortable> <el-table-column prop="worktype" label="活动类型" width="120" sortable>
<template #default="scope"> <template #default="scope">
@ -72,254 +68,159 @@
</el-table> </el-table>
<template #footer> <template #footer>
<el-button @click="activeVisible = false"> </el-button> <el-button @click="activeVisible = false"> </el-button>
<el-button type="primary" @click="savePPtData"> </el-button> <el-button type="primary" @click="save"> </el-button>
</template> </template>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup>
import { ref, reactive, onMounted, nextTick, watch } from 'vue' import { ref, reactive, onMounted, onBeforeMount, defineExpose } from 'vue'
import Divider from '../../../../../components/Divider.vue' import Divider from '../../../../../components/Divider.vue'
import { listEntpcoursefile } from '@/api/education/entpcoursefile' import {listEntpcoursefile} from '@/api/education/entpcoursefile'
import { homeworklist } from '@/api/teaching/classwork' import {homeworklist} from '@/api/teaching/classwork'
import { processList } from "@/hooks/useProcessList"; import { processList } from "@/hooks/useProcessList";
import { listEntpcoursework } from "@/api/classTask/index"; import { listEntpcoursework } from "@/api/classTask/index";
import { ElMessageBox,ElMessage } from 'element-plus' import { ElMessageBox } from 'element-plus'
import NewClassTsakAssign from '@/views/classTask/newClassTaskAssign/index.vue' import NewClassTsakAssign from '@/views/classTask/newClassTaskAssign/index.vue'
import { sessionStore } from '@/utils/store' import { sessionStore } from '@/utils/store'
import { useGetHomework } from '@/hooks/useGetHomework' import { useGetHomework } from '@/hooks/useGetHomework'
import { PPTApi } from '../../../../../api/index' const currentCourse = reactive({
import { storeToRefs } from 'pinia' textbookId:0,
import {useSlidesStore} from '../../../../../store' levelFirstId:0,
const slidesStore = useSlidesStore() levelSecondId:0,
const { slides, slideIndex, currentSlide, workItem } = storeToRefs(slidesStore) coursetitle:'',
node:{},
interface CourseNode { id:1,
rootid: number; worktype:'',
parentNode: { id: number };
id: number;
itemtitle: string;
}
interface CurrentCourse {
textbookId: number;
levelFirstId: number;
levelSecondId: number;
coursetitle: string;
node: CourseNode;
id: number;
worktype: string;
}
interface Params {
parentid: number;
pageSize: number;
orderby: string;
}
interface WorkType {
label: string;
value: string;
}
interface WorkItem {
status: string;
activityContent?: string;
worktype: string;
quizlist?: { id: number }[];
workcodes: string;
base64?: string;
prevData?: any;
id: number;
evaltitle?: string; // evaltitle
}
const currentCourse = reactive<CurrentCourse>({
textbookId: 0,
levelFirstId: 0,
levelSecondId: 0,
coursetitle: '',
node: {} as CourseNode,
id: 1,
worktype: '',
}) })
const dataList = ref([])
const dialogVisible = ref<boolean>(false) const dialogVisible = ref(false)
const tasklist_loading = ref<boolean>(false) const tasklist_loading = ref(false)
// //
const taskList = ref<WorkItem[]>([]) const taskList = ref([])
// //
const activeVisible = ref<boolean>(false) const activeVisible = ref(false)
const params = reactive({
parentid:14766,
const type = ref<WorkType[]>([ pageSize:500,
orderby:'fileidx'
})
const type = ref([
{ {
label: '习题训练', label:'习题训练',
value: 'danger' value:'danger'
}, },
{ {
label: '课堂展示', label:'课堂展示',
value: 'success' value:'success'
}, },
{ {
label: '常规作业', label:'常规作业',
value: 'primary' value:'primary'
}, },
]) ])
const objItem = ref<any>({})
// //
const workList = ref<WorkItem[]>([]) const workList = ref([])
const selectable = (row,index) => {
console.log(row,index,'row,index');
return true
}
// const clickPPTList = (item) => {
const selectedWorkList = ref<WorkItem[]>([]) console.log(item,'点击了')
// loading workList.value = []
const loadingActive = ref<boolean>(false) let datacontent = item.datacontent;
let pptJson = "";
if(typeof datacontent === 'string') pptJson = JSON.parse(datacontent)
const paramData = ref<{ id: number, activityContent: string }>({} as { id: number, activityContent: string }) if(pptJson&&pptJson[0]&&pptJson[0].classworkList) {
homeworklist({ids:pptJson[0].classworkList, pageSize: 100}).then( async res => {
const selectable = (row: WorkItem, index: number): boolean => { await formatClassWorkFile(res.rows)
return row.status === '10';
};
const formatClassWorkFile = async (postData: WorkItem[]): Promise<void> => {
for (let i = 0; i < postData.length; i++) {
let item = postData[i];
switch (item.worktype) {
case '框架梳理': {
}
break;
case '习题训练': {
// let workIds = item.quizlist!.map(items => items.id).join(',');
// let ress = await listEntpcoursework({ ids: workIds });
// const arr = ress.rows.map((item:{id:number}) => {
// return item.id
// })
// processList(ress.rows);
}
break;
case '课堂展示': {
// item.base64 = JSON.parse(item.workcodes).base64;
}
break;
case '常规作业': {
// item.prevData = JSON.parse(item.workcodes);
}
}
workList.value.push(item)
loadingActive.value = false
}
await nextTick();
}//
const handleRemoveDemoActivityClassWork = (item: WorkItem) => {
ElMessageBox.confirm('是否确认删除?')
.then(() => {
workList.value = []
const arr = paramData.value.activityContent.split(',')
const filterArr = arr.filter(itemId => itemId!== item.id.toString())
paramData.value.activityContent = filterArr.join(',')
upDateData()
}) })
.catch(() => { }); }
}
const formatClassWorkFile = async (postData) => {
return new Promise(async (resolve, reject)=>{
for (let i = 0; i < postData.length; i++) {
let item = postData[i];
switch (item.worktype) {
case '框架梳理': {
}
break;
case '习题训练': {
item.entpcourseworklistarray = item.entpcourseworklist?JSON.parse('['+item.entpcourseworklist+']'):[];
let workIds = item.entpcourseworklistarray.map(items=>items.id).join(',')
let ress = await listEntpcoursework({ids:workIds})
processList(ress.rows)
item.workShowList = ress.rows
}
break;
case '课堂展示': {
item.base64 = JSON.parse(item.workcodes).base64
}
break;
case '常规作业': {
item.prevData = JSON.parse(item.workcodes)
}
}
workList.value.push(item)
}
resolve()
})
}
//
const handleRemoveDemoActivityClassWork = (item) => {
ElMessageBox.confirm('是否确认删除?')
.then(function () {
workList.value.splice(workList.value.indexOf(item), 1);
})
.catch(() => {});
} }
// tag // tag
const getTagType = (worktype: string): string => { const getTagType = (worktype) => {
return type.value.find(item => item.label === worktype)!.value return type.value.find(item => item.label == worktype).value
} }
// //
const initHomeWork = async () => { const initHomeWork = async()=> {
tasklist_loading.value = true; tasklist_loading.value = true;
const { res, chapterId } = await useGetHomework(sessionStore.get('subject.curNode')); const { res, chapterId } = await useGetHomework(sessionStore.get('subject.curNode'));
taskList.value = res; taskList.value = res;
tasklist_loading.value = false; tasklist_loading.value = false;
} }
// //
const handleSelectionChange = (val: WorkItem[]) => { const handleSelectionChange = (val) => {
selectedWorkList.value = [...val] console.log(val,'多选')
} }
// //
const showDialog = (item: string) => { const showDialog = (item) => {
currentCourse.worktype = item currentCourse.worktype = item
dialogVisible.value = true dialogVisible.value = true
} }
const openList = () => { const openList = () => {
activeVisible.value = true activeVisible.value = true
initHomeWork() initHomeWork()
} }
// //
const savePPtData = async () => { const save = () => {
if (selectedWorkList.value.length === 0) { console.log('添加了')
ElMessage.warning('请选择活动')
return
}
workList.value = []
const arr = selectedWorkList.value.map(item => item.id)
//
const existingIds = paramData.value.activityContent ? paramData.value.activityContent.split(',') : []
paramData.value.activityContent = Array.from(new Set([...existingIds, ...arr])).join(',')
upDateData()
activeVisible.value = false activeVisible.value = false
} }
// ppt
const getCurrentPPtData = async () => {
workList.value = []
objItem.value = workItem.value[slideIndex.value]
paramData.value.id = objItem.value.id
paramData.value.activityContent = objItem.value?.activityContent
if (objItem.value?.activityContent) {
loadingActive.value = true
const res = await homeworklist({ ids: objItem.value?.activityContent, pageSize: 100 })
await formatClassWorkFile(res.rows)
}
}
//
const getData = async (data: WorkItem) => {
workList.value = []
if(paramData.value.activityContent){
const arr = paramData.value.activityContent.split(',')
arr.push(data.id.toString())
const unitArr = Array.from(new Set(arr))
paramData.value.activityContent = unitArr.join(',')
}else{
paramData.value.activityContent = data.id.toString()
}
upDateData()
dialogVisible.value = false
}
const upDateData = async () => {
await PPTApi.updateSlide(paramData.value)
loadingActive.value = true
const res = await homeworklist({ ids: paramData.value.activityContent, pageSize: 100 })
await formatClassWorkFile(res.rows)
const resource = sessionStore.get('curr.resource')
await PPTApi.getSlideList(resource.id)
}
onMounted(() => { onMounted(() => {
const curNode = sessionStore.get('subject.curNode') as CourseNode // console.log(sessionStore.get('subject.curBook'),'curBook');
// console.log(sessionStore.get('subject.subjectTree'),'subjectTree');
// console.log(sessionStore.get('subject.bookList'),'bookList');
console.log(sessionStore.get('subject.curNode'),'curNode');
const curNode = sessionStore.get('subject.curNode')
currentCourse.textbookId = curNode.rootid currentCourse.textbookId = curNode.rootid
currentCourse.levelFirstId = curNode.parentNode.id currentCourse.levelFirstId = curNode.parentNode.id
currentCourse.levelSecondId = curNode.id currentCourse.levelSecondId = curNode.id
currentCourse.coursetitle = curNode.itemtitle currentCourse.coursetitle = curNode.itemtitle,
currentCourse.node = curNode currentCourse.node = curNode
objItem.value = workItem.value[slideIndex.value] listEntpcoursefile(params).then((res) => {
getCurrentPPtData() dataList.value = [...res.rows]
})
}) })
watch(() => slideIndex.value, () => { defineExpose({
getCurrentPPtData() clickPPTList
}) })
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@ -386,8 +287,5 @@ watch(() => slideIndex.value, () => {
--el-border-color: var(--current-color); --el-border-color: var(--current-color);
} }
} }
.page .page-resource{
height: 500px !important;
}
} }
</style> </style>

View File

@ -1,164 +0,0 @@
/**
* websocket 工具类(im 自己实现)
* 单例模式: 保证一个类仅有一个实例并提供一个访问它的全局访问点
* 实现的方法为先判断实例存在与否如果存在则直接返回不存在就创建了再返回这就确保了一个类只有一个实例对象
*/
import useUserStore from '@/store/modules/user' // 用户信息
export class ChatWs {
instance = null; // 实例
id = null; // 群聊id || 单聊id-用户id(userId)
closed = false; // 关闭状态
onmessage = null; // 自定义处理
errCount = 5; // 重连次数 (ms) 暂时不使用
errTime = null; // 重连时间 (ms) 1秒内zhi间内不重连
// 类型定义
TYPES = {
group: 'group', // 群发
single: 'single', // 单发
beat: 'heart_beat', // 心跳
}
static base = 'wss://file.ysaix.com:7868'
constructor() {
if (!ChatWs.instance) {
const userStore = useUserStore() // 用户信息
const wsBase = import.meta.env.VITE_APP_WS_URL; // ws地址
const url = `${wsBase||ChatWs.base}/ws/websocket/${userStore.id}`;
this.init(url);
ChatWs.instance = this;
}
return ChatWs.instance;
}
// 初始化
init(url) {
this.url = url;
this.ws = null;
const _this = this
this.heartCheck = {
timeout: 1000 * 10, // 60s
timeoutObj: null,
serverTimeoutObj: null,
reset() {
clearTimeout(this.timeoutObj);
clearTimeout(this.serverTimeoutObj);
return this;
},
start() {
const self = this;
this.timeoutObj = setTimeout(function () {
// 这里发送一个心跳,后端收到后,返回一个心跳消息,
// onmessage拿到返回的心跳就说明连接正常
console.log("websocket-发送心跳")
_this.sendMsgBeat();
self.serverTimeoutObj = setTimeout(function () {
console.log("websocket-心跳响应超时")
// 如果超过一定时间还没重置,说明后端主动断开了
_this.ws.close(); // 如果onclose会执行reconnect我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次
}, self.timeout);
}, this.timeout);
},
};
this.reconnect();
}
// 重连
reconnect() {
const self = this;
if (!!this.ws) { // 关闭之前的链接
this.ws.close()
this.ws = null
}
this.ws = new WebSocket(this.url);
this.ws.onopen = function () {
console.log("websocket-连接成功")
self.heartCheck.reset().start();
};
this.ws.onmessage = function (e) {
// console.log("websocket-收到消息", e)
// 拿到任何消息都说明当前连接是正常的
const isBeat = e.data == 'pong'
isBeat && self.heartCheck.reset().start();
const exts = ['sessionId', 'pong'] // 不处理的消息头
const isEmpty = !e.data
const isExts = exts.some(item => e.data.includes(item))
if (isEmpty && isExts) return;
// 自定义处理
self.onmessage && self.onmessage(e.data, e);
};
this.ws.onerror = function (e) {
console.log("websocket-连接异常", e)
self.connectSocket() // 重连
};
this.ws.onclose = function (e) {
console.log("websocket-连接断开", e)
self.connectSocket() // 重连
};
}
connectSocket() {
this.heartCheck.reset() // 重置心跳
if (self.closed) return; // 关闭状态不重连
// if(self.errCount <= 0) return; // 超过重连次数
// self.errCount--; // 重连次数减1
if (this.errTime) {
const nowTime = Date.now();
const bool = nowTime - this.errTime < 1000 // 1s内zhi间内不重连
if (bool) return; // 1s内不重连
}
this.errTime = Date.now();
// 延时5s 后重连
console.log('重连中...')
this.sleep(5000).then(_ => {this.reconnect()})
}
// 发送消息
send(msg) {
if (!msg) throw new Error("msg is not null")
if (!this.ws) throw new Error("ws is not null")
if (typeof msg === "object") msg = JSON.stringify(msg)
if (!msg.includes('"msg":')) throw new Error("msg 格式错误请重试")
this.ws.send(msg)
}
// 发送消息-带消息头(key)
sendMsg(head, content, option = {}) {
if (!head) throw new Error("head is not null")
if (!content) throw new Error("content is not null")
let msg = { head, content, ...option }
// 发送消息
this.send(this.getMsgObj(msg))
}
// 发送心跳
sendMsgBeat() {
// this.send(this.getMsgObj('ping', this.TYPES.beat))
this.ws.send('ping')
}
/**
* @description 获取消息对象
* @param {*} msg 消息内容
* @param {*} chatType 群发 group| 单发 single| 心态 heart_beat
* @param {*} id 群聊id || 单聊id-用户id(userId)
*/
getMsgObj(msg, chatType = 'group', id) {
if (typeof msg === "object") msg = JSON.stringify(msg)
const res = {msg, chatType}
// if (!id) throw new Error(`${type=='group'?'群ID':'用户ID'} is not null`)
if (chatType == 'group') res.groupId = id || this.id || ''
else if (chatType == 'single') res.to = id || this.id || ''
return res
}
// 监听
watch(callback) {
callback && (this.onmessage = callback);
}
// 关闭链接
close() {
this.closed = true;
this.ws.close();
}
// 延时 ms 毫秒
sleep(ms){
return new Promise(resolve => setTimeout(resolve, ms))
}
}
// 连接socket
export const connect = () => new ChatWs()
// 默认实例
export default new ChatWs()

View File

@ -208,10 +208,6 @@ export const createWindow = async (type, data) => {
autoHideMenuBar: true, autoHideMenuBar: true,
maximizable: false, maximizable: false,
} }
// pptlist的时候可以选择最大化
if (data.url == '/pptist'){
defOption.maximizable = true;
}
data.isConsole = true // 是否开启控制台 data.isConsole = true // 是否开启控制台
data.option = {...defOption, ...option} data.option = {...defOption, ...option}
const win = await toolWindow(type, data) const win = await toolWindow(type, data)

View File

@ -949,11 +949,11 @@ const showExamAnalyseDrawer = (row) => {
} }
const tableRef = ref(); const tableRef = ref();
const getPaginationList = async ( page, limit ) => { const getPaginationList = ( page, limit ) => {
paginationParams.pageNum = page; paginationParams.pageNum = page;
paginationParams.pageSize = limit; paginationParams.pageSize = limit;
//console.log(page, limit) //console.log(page, limit)
await handleQueryFromEntpCourseWork(0); handleQueryFromEntpCourseWork(0);
// //
tableRef.value.setScrollTop(0); tableRef.value.setScrollTop(0);
} }

View File

@ -126,7 +126,7 @@
</div> </div>
</template> </template>
<script setup> <script setup>
import { onMounted, ref, watch, reactive, getCurrentInstance, nextTick, defineEmits } from 'vue' import { onMounted, ref, watch, reactive, getCurrentInstance, nextTick } from 'vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { cloneDeep } from 'lodash' import { cloneDeep } from 'lodash'
import { Plus, Delete } from '@element-plus/icons-vue' import { Plus, Delete } from '@element-plus/icons-vue'
@ -156,8 +156,6 @@ const { proxy } = getCurrentInstance()
const props = defineProps({ const props = defineProps({
currentCourse: Object, currentCourse: Object,
}) })
const emits = defineEmits(['getData'])
// ppt
const isShow = ref(false) const isShow = ref(false)
const propsQueryCourseObj = route.query.courseObj;// const propsQueryCourseObj = route.query.courseObj;//
@ -462,8 +460,10 @@ const handleClassWorkFormQuizRemove = (index) =>{
await nextTick(); // DOM await nextTick(); // DOM
proxy.$refs["classWorkFormRef"].validate(async valid => { proxy.$refs["classWorkFormRef"].validate(async valid => {
if (valid) { if (valid) {
//
// const { chapterId } = await useGetHomework(courseObj.node) // const { chapterId } = await useGetHomework(courseObj.node)
// this.entpcourseid = chapterId // this.entpcourseid = chapterId
const cform = { const cform = {
id: 0, id: 0,
workdate: classWorkForm.workdate, // //web workdate: classWorkForm.workdate, // //web
@ -491,13 +491,11 @@ const handleClassWorkFormQuizRemove = (index) =>{
// [] newWorkSpaceEdit true // [] newWorkSpaceEdit true
if(isShow.value === false){ if(classWorkForm.id != '' ) {// id
if(classWorkForm.id != '' ) {// id
editWork(cform); // editWork(cform); //
return; return;
}
} }
if (classWorkForm.worktype === "课堂展示") { if (classWorkForm.worktype === "课堂展示") {
boardLoading.value = true boardLoading.value = true
@ -509,7 +507,7 @@ const handleClassWorkFormQuizRemove = (index) =>{
cform.workcodes = JSON.stringify({json: canvasJson, base64: canvasBase64}); cform.workcodes = JSON.stringify({json: canvasJson, base64: canvasBase64});
cform.entpcourseworklist = JSON.stringify([{'id':-1, 'score': '10'}]); cform.entpcourseworklist = JSON.stringify([{'id':-1, 'score': '10'}]);
try { try {
addClassworkReturnId(cform).then((res) => { addClassworkReturnId(cform).then(() => {
ElMessage({ type: 'success', message: '作业设计成功!'}); ElMessage({ type: 'success', message: '作业设计成功!'});
// //
classWorkForm.worktype = "课堂展示"; classWorkForm.worktype = "课堂展示";
@ -521,8 +519,7 @@ const handleClassWorkFormQuizRemove = (index) =>{
// //
classWorkForm.chooseWorkLists = []; // list classWorkForm.chooseWorkLists = []; // list
classWorkForm.whiteboardObj = ''; // ? // classWorkForm.whiteboardObj = ''; // ? //
classWorkForm.id = res
emits('getData',classWorkForm)
boardLoading.value = false boardLoading.value = false
}) })
} finally { } finally {
@ -534,7 +531,7 @@ const handleClassWorkFormQuizRemove = (index) =>{
cform.workcodes = JSON.stringify(classWorkForm.fileHomeworkList); cform.workcodes = JSON.stringify(classWorkForm.fileHomeworkList);
cform.entpcourseworklist = JSON.stringify([{'id':-2, 'score': '10'}]); cform.entpcourseworklist = JSON.stringify([{'id':-2, 'score': '10'}]);
try { try {
addClassworkReturnId(cform).then((res) => { addClassworkReturnId(cform).then(() => {
ElMessage({ type: 'success', message: '作业设计成功!'}); ElMessage({ type: 'success', message: '作业设计成功!'});
// //
classWorkForm.worktype = "常规作业"; classWorkForm.worktype = "常规作业";
@ -546,8 +543,7 @@ const handleClassWorkFormQuizRemove = (index) =>{
classWorkForm.chooseWorkLists = []; // list classWorkForm.chooseWorkLists = []; // list
classWorkForm.whiteboardObj = ''; // ? // classWorkForm.whiteboardObj = ''; // ? //
classWorkForm.fileHomeworkList = []; // list classWorkForm.fileHomeworkList = []; // list
classWorkForm.id = res
emits('getData',classWorkForm)
fileLoading.value = false fileLoading.value = false
}) })
} finally { } finally {
@ -578,7 +574,7 @@ const handleClassWorkFormQuizRemove = (index) =>{
console.log(cform,'提交的数据'); console.log(cform,'提交的数据');
if(cform.entpcourseworklist == '') return ElMessage({ type: 'warning', message: '请先添加作业资源!'}); if(cform.entpcourseworklist == '') return ElMessage({ type: 'warning', message: '请先添加作业资源!'});
addClassworkReturnId(cform).then(res => { addClassworkReturnId(cform).then(workres => {
ElMessage({ type: 'success', message: '作业设计成功!'}); ElMessage({ type: 'success', message: '作业设计成功!'});
// //
classWorkForm.worktype = "习题训练"; classWorkForm.worktype = "习题训练";
@ -589,8 +585,6 @@ const handleClassWorkFormQuizRemove = (index) =>{
// //
classWorkForm.chooseWorkLists = []; classWorkForm.chooseWorkLists = [];
classWorkForm.whiteboardObj = ''; // ? // classWorkForm.whiteboardObj = ''; // ? //
classWorkForm.id = res
emits('getData',classWorkForm)
// refresh the list // refresh the list
// //
// this.getClassWorkAllList(); // this.getClassWorkAllList();
@ -599,11 +593,7 @@ const handleClassWorkFormQuizRemove = (index) =>{
} }
console.log('该清空左侧列表数据了'); console.log('该清空左侧列表数据了');
// //
if(isShow.value){ currentRow.value = {id:0};
currentRow.value = {id:1};
}else{
currentRow.value = {id:0};
}
initHomeWork(); initHomeWork();
@ -614,7 +604,6 @@ const handleClassWorkFormQuizRemove = (index) =>{
// // // //
// router.push({ path: '/classTaskAssign' }); // router.push({ path: '/classTaskAssign' });
// } // }
} }
}); });
}; };

View File

@ -49,7 +49,6 @@
:data="workResource.entpCourseWorkList" :data="workResource.entpCourseWorkList"
style="width: 100%; height: calc(100% - 55px);" style="width: 100%; height: calc(100% - 55px);"
v-loading="pageParams.loading" v-loading="pageParams.loading"
ref="tableRef"
> >
<el-table-column type="index" width="60" /> <el-table-column type="index" width="60" />
<el-table-column align="left" > <el-table-column align="left" >
@ -119,7 +118,7 @@
import { Search } from '@element-plus/icons-vue' import { Search } from '@element-plus/icons-vue'
import { onMounted, ref,watch, reactive, getCurrentInstance,nextTick } from 'vue' import { onMounted, ref,watch, reactive, getCurrentInstance,nextTick } from 'vue'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
// import html2canvas from 'html2canvas'; import html2canvas from 'html2canvas';
import { listEntpcoursework, listEntpcourseworkLocal } from '@/api/education/entpCourseWork' import { listEntpcoursework, listEntpcourseworkLocal } from '@/api/education/entpCourseWork'
import { listEvaluationclue } from '@/api/classTask' import { listEvaluationclue } from '@/api/classTask'
@ -365,15 +364,11 @@ const showExamAnalyseDrawer = (row) => {
proxy.$refs.examDetailsDrawerRef.acceptParams(activeParams); proxy.$refs.examDetailsDrawerRef.acceptParams(activeParams);
}) })
} }
const getPaginationList = ( page, limit ) => {
const tableRef = ref();
const getPaginationList = async ( page, limit ) => {
paginationParams.pageNum = page; paginationParams.pageNum = page;
paginationParams.pageSize = limit; paginationParams.pageSize = limit;
console.log(page, limit) console.log(page, limit)
await handleQueryFromEntpCourseWork(0); handleQueryFromEntpCourseWork(0);
//
tableRef.value.setScrollTop(0);
} }
/** 单题上传弹出框----纠错修改框 */ /** 单题上传弹出框----纠错修改框 */
@ -458,16 +453,13 @@ const handleDelete = async(item, index) => {
*/ */
const captureScreenshot = (id) => { const captureScreenshot = (id) => {
const targetElement = document.getElementById('screenshot-target-' + id); const targetElement = document.getElementById('screenshot-target-' + id);
if (targetElement) { html2canvas(targetElement).then(canvas => {
emit('addQuizImgBs64', targetElement); // canvasURL
} const screenshotUrl = canvas.toDataURL('image/png');
// html2canvas(targetElement).then(canvas => { //
// // canvasURL // console.log(screenshotUrl);
// const screenshotUrl = canvas.toDataURL('image/png'); emit('addQuizImgBs64', screenshotUrl);
// // });
// // console.log(screenshotUrl);
// emit('addQuizImgBs64', screenshotUrl);
// });
} }
// //

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="page"> <div class="page">
<!-- 习题筛选1 --> <!-- 习题筛选1 -->
<el-row style="width: 100%; height: 50px;"> <el-row style="width: 100%; height: 50px;">
<el-col :span="7"> <el-col :span="7">
@ -60,9 +60,8 @@
<div class="page-table" > <div class="page-table" >
<el-table <el-table
:data="workResource.entpCourseWorkList" :data="workResource.entpCourseWorkList"
style="width: 100%; height: calc(100% - 50px);" style="width: 100%; height: calc(100% - 55px);"
v-loading="pageParams.loading" v-loading="pageParams.loading"
ref="tableRef"
> >
<el-table-column type="index" width="60" /> <el-table-column type="index" width="60" />
<el-table-column align="left" > <el-table-column align="left" >
@ -90,13 +89,13 @@
</el-table-column> </el-table-column>
</el-table> </el-table>
<!-- 分页--> <!-- 分页-->
<div style="height: 50px;"> <div style="height: 55px;">
<el-pagination <el-pagination
v-show="pageParams.total > 0" v-show="pageParams.total > 0"
v-model:page="paginationParams.pageNum" v-model:page="paginationParams.pageNum"
v-model:limit="paginationParams.pageSize" v-model:limit="paginationParams.pageSize"
:total="pageParams.total" :total="pageParams.total"
:style="{ position: 'relative', 'padding-top': '10px' }" :style="{ position: 'relative', 'margin-top': '5px' }"
@change="getPaginationList" /> @change="getPaginationList" />
</div> </div>
</div> </div>
@ -106,7 +105,7 @@
</template> </template>
<script setup> <script setup>
import { Search } from '@element-plus/icons-vue' import { Search } from '@element-plus/icons-vue'
//import html2canvas from 'html2canvas'; import html2canvas from 'html2canvas';
import { onMounted, ref,watch, reactive, getCurrentInstance,nextTick } from 'vue' import { onMounted, ref,watch, reactive, getCurrentInstance,nextTick } from 'vue'
import {listEntpcoursework, listEntpcourseworkNew, getEntpcoursework} from '@/api/education/entpCourseWork' import {listEntpcoursework, listEntpcourseworkNew, getEntpcoursework} from '@/api/education/entpCourseWork'
@ -189,7 +188,6 @@ const workResource = reactive({
}); // }); //
onMounted(() => { onMounted(() => {
console.log('entpCourseWorkTypeList', entpCourseWorkTypeList);
debounceQueryData(); // debounceQueryData(); //
}) })
@ -397,15 +395,11 @@ const showExamAnalyseDrawer = (row) => {
proxy.$refs.examDetailsDrawerRef.acceptParams(activeParams); proxy.$refs.examDetailsDrawerRef.acceptParams(activeParams);
}) })
} }
const getPaginationList = ( page, limit ) => {
const tableRef = ref();
const getPaginationList = async ( page, limit ) => {
paginationParams.pageNum = page; paginationParams.pageNum = page;
paginationParams.pageSize = limit; paginationParams.pageSize = limit;
console.log(page, limit) console.log(page, limit)
await handleQueryFromEntpCourseWork(0); handleQueryFromEntpCourseWork(0);
//
tableRef.value.setScrollTop(0);
} }
@ -444,16 +438,13 @@ const getPaginationList = async ( page, limit ) => {
*/ */
const captureScreenshot = (id) => { const captureScreenshot = (id) => {
const targetElement = document.getElementById('screenshot-target-' + id); const targetElement = document.getElementById('screenshot-target-' + id);
if (targetElement) { html2canvas(targetElement).then(canvas => {
emit('addQuizImgBs64', targetElement); // canvasURL
} const screenshotUrl = canvas.toDataURL('image/png');
// html2canvas(targetElement).then(canvas => { //
// // canvasURL // console.log(screenshotUrl);
// const screenshotUrl = canvas.toDataURL('image/png'); emit('addQuizImgBs64', screenshotUrl);
// // });
// // console.log(screenshotUrl);
// emit('addQuizImgBs64', screenshotUrl);
// });
} }

View File

@ -61,6 +61,7 @@ import { sessionStore } from '@/utils/store' // 学科名字文生图
// //
import ChooseTextbook from '@/components/choose-textbook/index.vue' import ChooseTextbook from '@/components/choose-textbook/index.vue'
import { menusEvent } from '@/plugins/vue3-menus' // import { menusEvent } from '@/plugins/vue3-menus' //
const router = useRouter() const router = useRouter()
const userStore = useUserStore() // const userStore = useUserStore() //
@ -196,19 +197,6 @@ const getResourceList = async () => {
// HTTP // HTTP
const HTTP_SERVER_API = (type, params = {}) => { const HTTP_SERVER_API = (type, params = {}) => {
switch (type) { switch (type) {
case 'addSmarttalk': { //
const def = {
fileId: '', // id - Entpcoursefile id
fileFlag: 'aptist',
fileShowName: courseObj.coursetitle + '.aptist',
textbookId: courseObj.textbookId,
levelFirstId: courseObj.levelFirstId,
levelSecondId: courseObj.levelSecondId,
fileSource: '个人',
fileRoot: '备课'
}
return API_smarttalk.creatAPT({...def, ...params})
}
case 'addEntpcourse': { // case 'addEntpcourse': { //
const node = courseObj.node || {} const node = courseObj.node || {}
if (!node) return msgUtils.msgWarning('请选择章节?') if (!node) return msgUtils.msgWarning('请选择章节?')
@ -291,8 +279,6 @@ const handleAll = async(type, row) =>{
} }
// ppt-(slide) // ppt-(slide)
await HTTP_SERVER_API('addEntpcoursefile', params) await HTTP_SERVER_API('addEntpcoursefile', params)
// -Smarttalk
await HTTP_SERVER_API('addSmarttalk',{fileId: id})
// //
await getResourceList() await getResourceList()
} else { } else {

View File

@ -78,7 +78,7 @@
</div> </div>
</el-dialog> </el-dialog>
<!-- im-chat 聊天组件 --> <!-- im-chat 聊天组件 -->
<!-- <im-chat ref="imChatRef" v-if="visible" @change="chatChange" /> --> <im-chat ref="imChatRef" v-if="visible" @change="chatChange" />
</template> </template>
<script setup> <script setup>
@ -90,7 +90,7 @@ import vueQr from 'vue-qr/src/packages/vue-qr.vue' // 插件: 二维码
import imChat from '@/views/tool/components/imChat.vue' // im-chat- import imChat from '@/views/tool/components/imChat.vue' // im-chat-
import MsgEnum from '@/plugins/imChat/msgEnum' // -(nuem) import MsgEnum from '@/plugins/imChat/msgEnum' // -(nuem)
import * as commUtil from '@/utils/comm' // - import * as commUtil from '@/utils/comm' // -
import { toLinkWeb, createWindow, getStaticUrl, sessionStore } from '@/utils/tool' // - import { toLinkWeb, getStaticUrl } from '@/utils/tool' // -
import * as Http_ClassManage from '@/api/classManage' // api import * as Http_ClassManage from '@/api/classManage' // api
import * as Http_Classcourse from '@/api/teaching/classcourse' // api import * as Http_Classcourse from '@/api/teaching/classcourse' // api
@ -103,7 +103,7 @@ let baseUrl = import.meta.env.VITE_APP_BUILD_BASE_PATH
const userStore = useUserStore() const userStore = useUserStore()
const visible = ref(false) // const visible = ref(false) //
const myClassActive = ref({}) // APT const myClassActive = ref({}) // APT
// const imChatRef = ref(null) // im-chat ref const imChatRef = ref(null) // im-chat ref
const emit = defineEmits(['close']) const emit = defineEmits(['close'])
const classForm = reactive({ // () const classForm = reactive({ // ()
form: {}, itemOption: [], option: {} form: {}, itemOption: [], option: {}
@ -177,7 +177,7 @@ const initData = () => {
teacherForm.itemOption = [ teacherForm.itemOption = [
// { label: '', prop: 'classid' }, // { label: '', prop: 'classid' },
// { label: '', prop: 'classcourseid' }, // { label: '', prop: 'classcourseid' },
// { label: '', prop: 'qrUrl', show: false }, { label: '老师扫码', prop: 'qrUrl', show: false },
{ label: '手机登录', prop: 'mobile', show: false }, { label: '手机登录', prop: 'mobile', show: false },
{ label: '故障备用', prop: 'backup', show: false }, { label: '故障备用', prop: 'backup', show: false },
] ]
@ -257,22 +257,18 @@ const createClasscourse = async () => {
entpcourseid, evalid, coursetitle, entpcourseid, evalid, coursetitle,
plandate: curDate, opendate: curDate plandate: curDate, opendate: curDate
} }
// teacherForm.form.classcourseid = 100
teacherForm.form.classcourseid = await Http_Classcourse.addClasscourseReturnId(params) teacherForm.form.classcourseid = await Http_Classcourse.addClasscourseReturnId(params)
dt.loading = false dt.loading = false
// getClasscourseList('update') // // getClasscourseList('update') //
let msgEl = ElMessage.success('创建课程-成功') ElMessage.success('创建课程-成功')
// -pptList // -pptList
if (myClassActive.value.filetype == 'aippt') { if (myClassActive.value.filetype == 'aippt') {
const msgEl = ElMessage.warning({message:'正在打开公屏,请稍后...',duration: 0})
setTimeout(() => { setTimeout(() => {
msgEl.close() msgEl.close()
msgEl = ElMessage.warning({message:'正在打开公屏,请稍后...',duration: 0}) const classcourse = {...params, id: teacherForm.form.classcourseid}
setTimeout(() => { openPublicScreen(classcourse)
msgEl.close() }, 1500);
const classcourse = {...params, id: teacherForm.form.classcourseid}
openPublicScreen(classcourse)
}, 2000);
}, 1000);
} }
} }
// //
@ -300,23 +296,14 @@ const removeClasscourse = async () => {
const classTeachingStart = async () => { const classTeachingStart = async () => {
const { classcourseid:id } = teacherForm.form const { classcourseid:id } = teacherForm.form
if (id) { // if (id) { //
// -pptList const url = `/teaching/classteaching?classcourseid=${id}&actor=classTeachingOnPublicScreen`
if (myClassActive.value.filetype == 'aptist') { toLinkWeb(url) // web-
const msgEl = ElMessage.warning({message:'正在打开公屏,请稍后...',duration: 0}) visible.value = false //
setTimeout(() => { handleClose() // im-chat
msgEl.close()
openPublicScreen({id})
}, 2000);
}else {
const url = `/teaching/classteaching?classcourseid=${id}&actor=classTeachingOnPublicScreen`
toLinkWeb(url) // web-
visible.value = false //
}
} }
} }
// //
const getQrUrl = async() => { const getQrUrl = async() => {
// console.log('')
const { classcourseid:id } = teacherForm.form const { classcourseid:id } = teacherForm.form
const { userName, userId } = userStore.user const { userName, userId } = userStore.user
if (!id||!userName) return if (!id||!userName) return
@ -349,7 +336,6 @@ const getQrUrl = async() => {
// //
const openPublicScreen = (classcourse) => { const openPublicScreen = (classcourse) => {
console.log('打开公屏', classcourse)
const resource = toRaw(myClassActive.value) const resource = toRaw(myClassActive.value)
sessionStore.set('curr.resource', resource) // sessionStore.set('curr.resource', resource) //
sessionStore.set('curr.classcourse', classcourse) // sessionStore.set('curr.classcourse', classcourse) //
@ -360,7 +346,6 @@ const openPublicScreen = (classcourse) => {
sessionStore.set('curr.classcourse', null) // sessionStore.set('curr.classcourse', null) //
} }
}) })
visible.value = false //
} }
// //
@ -401,14 +386,12 @@ watch(() => classForm.form.classid, (val)=> {
// -id // -id
watch(() => teacherForm.form.classcourseid, (val) => { watch(() => teacherForm.form.classcourseid, (val) => {
const bool = !!val const bool = !!val
const isApt = myClassActive.filetype=='apt'
// - // -
isApt && bool && getQrUrl() bool && getQrUrl()
// id // id
teacherForm.itemOption.forEach(o => { teacherForm.itemOption.forEach(o => {
// id // id
const arr = isApt ? ['qrUrl','backup'] : ['backup'] if (['qrUrl','backup'].includes(o.prop)) o.show = bool
if (arr.includes(o.prop)) o.show = bool
// id // id
if (['mobile'].includes(o.prop)) o.show = !bool if (['mobile'].includes(o.prop)) o.show = !bool
}) })

View File

@ -36,7 +36,7 @@
{{ item.async === 'on' ? '同步中' : '' }} {{ item.async === 'on' ? '同步中' : '' }}
</div> </div>
<template v-if="item.fileFlag ==='课件'">|</template> <template v-if="item.fileFlag ==='课件'">|</template>
<!-- <div style="width: 70px">访问 100</div> <div style="width: 70px">访问 100</div>
| |
<div style="width: 70px">引用 50</div> <div style="width: 70px">引用 50</div>
| |
@ -49,16 +49,6 @@
white-space: nowrap; white-space: nowrap;
width: 200px; width: 200px;
">点赞 20</div> ">点赞 20</div>
|-->
<div
style="
flex: 1;
text-align: left;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 200px;
">{{formatDate(item.createTime, 'yyyy-MM-dd hh:mm:ss')}}</div>
</div> </div>
</div> </div>
<div class="prepare-body-main-item-btn"> <div class="prepare-body-main-item-btn">
@ -117,7 +107,6 @@ import { endClass, getSelfReserv } from '@/api/classManage'
import { listEntpcourse } from '@/api/teaching/classwork' import { listEntpcourse } from '@/api/teaching/classwork'
import { createWindow } from '@/utils/tool' import { createWindow } from '@/utils/tool'
import { defineExpose } from 'vue' import { defineExpose } from 'vue'
import {formatDate} from '@/utils/comm'
const { ipcRenderer } = window.electron || {} const { ipcRenderer } = window.electron || {}
export default { export default {

View File

@ -8,16 +8,15 @@
</el-button> </el-button>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item @click="createAIPPT">新建文枢课件</el-dropdown-item> <el-dropdown-item @click="createAptFile">新建文枢课件</el-dropdown-item>
<el-dropdown-item @click="aiTOPPT">AI一键生成</el-dropdown-item> <el-dropdown-item>AI一键生成</el-dropdown-item>
<el-dropdown-item @click="openFilePicker">导入PPT</el-dropdown-item> <el-dropdown-item>导入PPT</el-dropdown-item>
<input type="file" ref="fileInput" style="display: none;" @change="handleFileChange" accept="application/vnd.ms-powerpoint,application/vnd.openxmlformats-officedocument.presentationml.presentation">
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
</el-dropdown> </el-dropdown>
<el-tabs v-model="activeAptTab" style="height: 100%;"> <el-tabs v-model="activeAptTab" style="height: 100%;">
<el-tab-pane label="文枢课件" name="教学课件" class="prepare-center-jxkj"> <el-tab-pane label="文枢课件" name="教学课件" class="prepare-center-jxkj">
<!-- <div class="prepare-center-header"> <div class="prepare-center-header">
<div class="center-create-btn" style="background-color: rgb(64,158,255)" @click="createAptFile"> <div class="center-create-btn" style="background-color: rgb(64,158,255)" @click="createAptFile">
<div class="create-btn-title"><el-icon><Plus /></el-icon><label>APT</label></div> <div class="create-btn-title"><el-icon><Plus /></el-icon><label>APT</label></div>
<div class="create-btn-info">智能交互课件</div> <div class="create-btn-info">智能交互课件</div>
@ -30,7 +29,7 @@
<div class="create-btn-title"><el-icon><Plus /></el-icon><label>PPT</label></div> <div class="create-btn-title"><el-icon><Plus /></el-icon><label>PPT</label></div>
<div class="create-btn-info">试验版</div> <div class="create-btn-info">试验版</div>
</div> </div>
</div>--> </div>
<div class="prepare-center-body"> <div class="prepare-center-body">
<kj-list-item <kj-list-item
v-for="(item, index) in currentKJFileList" v-for="(item, index) in currentKJFileList"
@ -153,7 +152,6 @@
<!-- 上课配置 --> <!-- 上课配置 -->
<class-start ref="calssRef" @close="closeChange"/> <class-start ref="calssRef" @close="closeChange"/>
<PptDialog @add-success="addAiPPT" :currentNode="currentNode" :uploadData="uploadData" v-model="pptDialog"/> <PptDialog @add-success="addAiPPT" :currentNode="currentNode" :uploadData="uploadData" v-model="pptDialog"/>
<progress-dialog v-model:visible="pgDialog.visible" v-bind="pgDialog" />
<!-- 章节弹窗 --> <!-- 章节弹窗 -->
<TreeLog ref="treelogRef"/> <TreeLog ref="treelogRef"/>
<!-- <button @click="test">test</button> --> <!-- <button @click="test">test</button> -->
@ -163,16 +161,8 @@ import {Check, Plus, Position} from '@element-plus/icons-vue'
import Reserv from '@/views/prepare/container/reserv.vue' import Reserv from '@/views/prepare/container/reserv.vue'
import { ArrowDown } from '@element-plus/icons-vue' import { ArrowDown } from '@element-plus/icons-vue'
import PptDialog from '@/views/prepare/container/ppt-dialog.vue' import PptDialog from '@/views/prepare/container/ppt-dialog.vue'
import progressDialog from '@/views/teachingDesign/container/progress-dialog.vue'
import {useRouter} from "vue-router";
const router = useRouter()
const aiTOPPT = ()=> {
router.push({path: '/model/design'})
}
</script> </script>
<script> <script>
import {PPTXFileToJson} from "@/AixPPTist/src/hooks/useImport";
const Remote = require('@electron/remote') const Remote = require('@electron/remote')
import ChooseTextbook from '@/components/choose-textbook/index.vue' import ChooseTextbook from '@/components/choose-textbook/index.vue'
import uploadDialog from '@/components/upload-dialog/index.vue' import uploadDialog from '@/components/upload-dialog/index.vue'
@ -202,21 +192,13 @@ import ClassReserv from '@/views/classManage/classReserv.vue'
import TreeLog from '@/views/prepare/components/treeLog.vue' import TreeLog from '@/views/prepare/components/treeLog.vue'
import classStart from './container/class-start.vue' // import classStart from './container/class-start.vue' //
import MsgEnum from '@/plugins/imChat/msgEnum' // im import MsgEnum from '@/plugins/imChat/msgEnum' // im
import Chat from '@/utils/chat' import Chat from '@/utils/chat' // im
import * as commUtils from "@/utils/comm";
import * as Api_server from "@/api/apiService";
import msgUtils from "@/plugins/modal";
import * as API_entpcoursefile from "@/api/education/entpcoursefile"; // im
if (!Chat.imChat) Chat.init() if (!Chat.imChat) Chat.init()
// import Chat from '@/utils/chat' // im
// if (!Chat.imChat) Chat.init()
// import ChatWs from '@/plugins/socket'
// console.log('xxxx',ChatWs)
// ChatWs.watch((data,e) => console.log('ws', data, e))
const toolStore = useToolState() const toolStore = useToolState()
const fs = require('fs') const fs = require('fs')
const { ipcRenderer } = window.electron || {} const { ipcRenderer } = window.electron || {}
export default { export default {
name: 'Prepare', name: 'Prepare',
components: { components: {
@ -272,23 +254,7 @@ export default {
// //
treelogRef:null, treelogRef:null,
// Entpcourse // Entpcourse
entp: null, entp: null
pgDialog: { // -
visible: false,
title: 'PPT解析中...',
width: 300,
showClose: false,
draggable: true,
beforeClose: done => {}, // -
pg: { // -
percentage: 0, //
color: [
{ color: '#1989fa', percentage: 50 }, //
{ color: '#e6a23c', percentage: 80 }, //
{ color: '#5cb87a', percentage: 100 }, // 绿
]
}
}
} }
}, },
computed: { computed: {
@ -507,117 +473,6 @@ export default {
},500) },500)
}) })
}, },
openFilePicker(){
this.$refs.fileInput.click();
},
handleFileChange(){
const file = event.target.files[0];
if (file) {
console.log(file);
console.log('文件名:', file.name);
console.log('文件类型:', file.type);
console.log('文件大小:', file.size);
this.createAIPPTByFile(file)
}
},
async toRousrceUrl(o) {
if (!!o.src) { // src
const isBase64 = /^data:image\/(\w+);base64,/.test(o.src)
const isBlobUrl = /^blob:/.test(o.src)
console.log('isBase64', o, isBase64)
if (isBase64) {
const bolb = commUtils.base64ToBlob(o.src)
const fileName = Date.now() + '.png'
const file = commUtils.blobToFile(bolb, fileName)
// o.src = fileName
// console.log('file', file)
const formData = new FormData()
formData.append('file', file)
const res = await Api_server.Other.uploadFile(formData)
if (res && res.code == 200){
const url = res?.url
url &&(o.src = url)
}
} else if (isBlobUrl) { //
}
}
if (o?.background?.image) await this.toRousrceUrl(o.background.image)
if (o?.elements) o.elements.forEach(async o => {await this.toRousrceUrl(o)})
},
async createAIPPTByFile(file) {
this.pgDialog.visible = true
this.pgDialog.pg.percentage = 0
const resPptJson = await PPTXFileToJson(file)
const { def, slides, ...content } = resPptJson
// || 线
let completed = 0
const total = slides.length
for( let o of slides ) {
completed++
await this.toRousrceUrl(o)
//
this.pgDialog.pg.percentage = Math.floor(completed / total * 100)
}
this.pgDialog.pg.percentage = 0
this.pgDialog.visible = false
listEntpcourse({
evalid: this.currentNode.id,
edituserid: this.userStore.userId,
pageSize: 500
}).then((response) => {
if (response.rows.length <= 0) return
let resCourse = response.rows[0]
//
let form = {
parentid: 0,
entpid: this.userStore.deptId,
entpcourseid: resCourse.id,
ppttype: 'file',
title: resCourse.coursetitle,
fileurl: '',
filetype: 'aippt',
datacontent: '',
parentContent: JSON.stringify(content),
filekey: '',
filetag: '',
fileidx: 0,
dflag: 0,
status: '',
edituserid: this.userStore.userId
}
addEntpcoursefileReturnId(form).then((slideid) => {
creatAPT({
...this.uploadData,
fileId: slideid,
fileFlag: 'aippt',
fileShowName: this.currentNode.itemtitle + '.aippt'
}).then(async (res) => {
const resSlides = slides.map(({id, ...slide}) => JSON.stringify(slide))
let params = {
parentid: slideid,
entpid: resCourse.entpid,
entpcourseid: resCourse.id,
title: '',
filetype: 'slide',
slides: resSlides,
edituserid: this.userStore.userId
}
const res_3 = await API_entpcoursefile.batchAddNew(params)
if (res_3 && res_3.code == 200) {
msgUtils.msgSuccess('导入PPT课件成功')
this.currentFileList.unshift(res.resData)
setTimeout(()=>{
this.$refs['kjItemRef'+res.resData.id][0].openFileWin(res.resData);
},500)
} else {
msgUtils.msgWarning('导入PPT课件失败')
}
})
})
})
},
createAIPPT() { createAIPPT() {
listEntpcourse({ listEntpcourse({
evalid: this.currentNode.id, evalid: this.currentNode.id,
@ -644,22 +499,57 @@ export default {
edituserid: this.userStore.userId edituserid: this.userStore.userId
} }
addEntpcoursefileReturnId(form).then((slideid) => { addEntpcoursefileReturnId(form).then((slideid) => {
let pagearray = []
//
pagearray.push({
key: '公屏',
title: '公屏页',
slidedata: {
attrs: { width: 1333, height: 749.8125 },
className: 'Stage',
children: [
{
attrs: {},
className: 'Layer',
children: [
{
attrs: {
width: 1333,
height: 749.8125,
fill: 'white',
name: 'fixedbackground',
listening: true
},
className: 'Rect'
}
]
}
]
}
})
// //
var form = { var form = {
parentid: slideid, parentid: slideid,
entpid: resCourse.entpid, entpid: resCourse.entpid,
entpcourseid: resCourse.id, entpcourseid: resCourse.id,
ppttype: 'file',
title: '第一页', title: '第一页',
fileurl: '',
filetype: 'slide', filetype: 'slide',
datacontent: '{"elements":[],"background":{"type":"solid","color":"#fff"}}', datacontent: JSON.stringify(pagearray),
filekey: '',
filetag: '',
fileidx: 0,
dflag: 0,
status: '',
edituserid: this.userStore.userId edituserid: this.userStore.userId
} }
addEntpcoursefileReturnId(form).then((res) => { addEntpcoursefileReturnId(form).then((res) => {
creatAPT({ creatAPT({
...this.uploadData, ...this.uploadData,
fileId: slideid, fileId: slideid,
fileFlag: 'aippt', fileShowName: this.currentNode.itemtitle + '.apt'
fileShowName: this.currentNode.itemtitle + '.aippt'
}).then((res) => { }).then((res) => {
this.currentFileList.unshift(res.resData) this.currentFileList.unshift(res.resData)
setTimeout(()=>{ setTimeout(()=>{

View File

@ -34,6 +34,7 @@ import {getDept } from '@/api/login'
import { listEvaluation } from '@/api/subject/index' import { listEvaluation } from '@/api/subject/index'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import {ElMessage} from 'element-plus' import {ElMessage} from 'element-plus'
import { clearBookInfo } from '@/utils/ruoyi'
import { sessionStore } from '@/utils/store' import { sessionStore } from '@/utils/store'
import {listClassmain} from '@/api/classManage/index' import {listClassmain} from '@/api/classManage/index'
// //