Compare commits
34 Commits
9603406a0b
...
f2faa882a8
Author | SHA1 | Date |
---|---|---|
zhangxuelin | f2faa882a8 | |
zhangxuelin | e4afb26e4e | |
zhangxuelin | d60d8822a6 | |
朱浩 | 90137bc3a2 | |
朱浩 | 82c61a3fa7 | |
朱浩 | 48d631a17c | |
zhangxuelin | 1835f76e56 | |
zhangxuelin | 432c1ff71d | |
朱浩 | 7f595c09a9 | |
朱浩 | c8e10d4fe1 | |
朱浩 | f916660156 | |
zhengdegang | e0107814ea | |
zdg | bb0aa82e82 | |
zdg | 8ed13a3146 | |
zhangxuelin | 82b70558c2 | |
zhangxuelin | bace41e12e | |
zhangxuelin | 04e204928d | |
zdg | 9e5609fbdd | |
lyc | 50aff2f158 | |
lyc | a1a4e11de7 | |
lyc | 20fe5be502 | |
lyc | 7e9ac2bf74 | |
lyc | 013669dd35 | |
yangws | 3ca20cd327 | |
小杨 | c159d6be1a | |
yangws | cc44a86437 | |
小杨 | e7c6ab9e8d | |
小杨 | 85d06f306a | |
朱浩 | 656a58693f | |
zouyf | 34cfcf5a6f | |
朱浩 | 772b196b31 | |
朱浩 | 757edb0f67 | |
zdg | 4f68ae27b3 | |
zdg | 38c041465e |
|
@ -17,8 +17,8 @@ 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 地址
|
# websocket 地址
|
||||||
# VITE_APP_WS_URL = 'wss://file.ysaix.com:7868'
|
VITE_APP_WS_URL = 'wss://file.ysaix.com:7868'
|
||||||
VITE_APP_WS_URL = 'ws://192.168.2.16:7865'
|
# VITE_APP_WS_URL = 'ws://192.168.2.16:7865'
|
||||||
|
|
||||||
# 是否显示开发工具
|
# 是否显示开发工具
|
||||||
VITE_SHOW_DEV_TOOLS = 'true'
|
VITE_SHOW_DEV_TOOLS = 'true'
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import 'animate.css'
|
||||||
import { ref, onMounted, watch, onBeforeMount } from 'vue'
|
import { ref, onMounted, watch, onBeforeMount } from 'vue'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { useScreenStore, useMainStore, useSnapshotStore, useSlidesStore } from './store'
|
import { useScreenStore, useMainStore, useSnapshotStore, useSlidesStore } from './store'
|
||||||
|
@ -28,7 +29,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()
|
||||||
|
|
|
@ -2,21 +2,51 @@
|
||||||
* @author zdg
|
* @author zdg
|
||||||
* @description 上课相关内容
|
* @description 上课相关内容
|
||||||
*/
|
*/
|
||||||
import type { Classcourse } from './types'
|
// import type { Classcourse } from './types'
|
||||||
import { sessionStore } from '@/utils/store' // electron-store 状态管理
|
import { sessionStore } from '@/utils/store' // electron-store 状态管理
|
||||||
import * as useStore from '../store' // pptist-状态管理
|
import * as useStore from '../store' // pptist-状态管理
|
||||||
import { ChatWs } from '@/plugins/socket' // 聊天socket
|
import ChatWs from '@/plugins/socket' // 聊天socket
|
||||||
|
import msgUtils from '@/plugins/modal' // 消息工具
|
||||||
|
|
||||||
const screenStore = useStore.useScreenStore() // 全屏-状态管理
|
const screenStore = useStore.useScreenStore() // 全屏-状态管理
|
||||||
const classcourseStore = useStore.useClasscourseStore() // 课堂信息-状态管理
|
const classcourseStore = useStore.useClasscourseStore() // 课堂信息-状态管理
|
||||||
const classcourse: Classcourse = sessionStore.get('curr.classcourse') // 课堂信息
|
const classcourse = sessionStore.get('curr.classcourse') // 课堂信息
|
||||||
|
|
||||||
// 如果课堂信息有值,则连接socket
|
export class Classcourse {
|
||||||
if (!!classcourse) {
|
msgObj:ElMessageBox = null // 提示消息对象
|
||||||
// 连接socket
|
|
||||||
const ws = new ChatWs()
|
constructor() {
|
||||||
console.log('ws- ',ws)
|
this.load()
|
||||||
// ChatWs.connect(classcourse.id)
|
}
|
||||||
classcourseStore.setClasscourse(classcourse)
|
/**
|
||||||
|
* @description 加载
|
||||||
|
*/
|
||||||
|
load() {
|
||||||
|
// 打开全屏
|
||||||
|
screenStore.setScreening(!!classcourse)
|
||||||
|
// 如果课堂信息有值,则连接socket
|
||||||
|
if (!!classcourse) {
|
||||||
|
// 连接socket
|
||||||
|
if (!ChatWs.ws) ChatWs.init()
|
||||||
|
ChatWs.id = classcourse.timgroupid // 群组id
|
||||||
|
console.log('ws- ', classcourse)
|
||||||
|
classcourseStore.setClasscourse(classcourse)
|
||||||
|
// 待上课提示
|
||||||
|
if (!classcourse.status) {
|
||||||
|
this.msgObj = {
|
||||||
|
type: 'success',
|
||||||
|
title: '系统提示',
|
||||||
|
message: '公屏课堂已准备完毕,请等待老师开启课堂!',
|
||||||
|
center: true,
|
||||||
|
showClose: false,
|
||||||
|
showCancelButton: false,
|
||||||
|
showConfirmButton: false,
|
||||||
|
beforeClose: () => {}
|
||||||
|
}
|
||||||
|
msgUtils.ElMessageBox(this.msgObj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 打开全屏
|
|
||||||
screenStore.setScreening(!!classcourse)
|
export default new Classcourse()
|
|
@ -3,22 +3,22 @@
|
||||||
* @author zdg
|
* @author zdg
|
||||||
* @date 2024-11-26
|
* @date 2024-11-26
|
||||||
*/
|
*/
|
||||||
import { toRaw } from 'vue'
|
import { toRaw, nextTick } from 'vue'
|
||||||
import type { Result } from './types' // 接口类型
|
import type { Result } from './types' // 接口类型
|
||||||
import msgUtils from '@/plugins/modal' // 消息工具
|
import msgUtils from '@/plugins/modal' // 消息工具
|
||||||
import * as API_entpcoursefile from '@/api/education/entpcoursefile' // 相关api
|
import * as API_entpcoursefile from '@/api/education/entpcoursefile' // 相关api
|
||||||
import * as API_smarttalk from '@/api/file' // 相关api
|
import * as API_smarttalk from '@/api/file' // 相关api
|
||||||
|
import * as Api_server from '@/api/apiService' // 相关api
|
||||||
|
import * as CreateHomework from '@/views/tool/createHomework' // 统计相关
|
||||||
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 { toPng, toJpeg } from 'html-to-image' // 引入html-to-image库
|
||||||
import * as commUtils from '@/utils/comm.js'
|
// import * as commUtils from '@/utils/comm.js'
|
||||||
|
import { createWindow } from '@/utils/tool'
|
||||||
|
import { useToolState } from '@/store/modules/tool'
|
||||||
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()
|
const toolStore = useToolState()
|
||||||
/** 工具类 */
|
/** 工具类 */
|
||||||
export class Utils {
|
export class Utils {
|
||||||
|
@ -60,7 +60,6 @@ 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) {
|
||||||
|
@ -74,7 +73,8 @@ 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]
|
||||||
slidesStore.updateSlideIndex(0) // 下标0 为第一页
|
// 加入活动后刷新ppt数据内容,不跟换为第一页
|
||||||
|
// slidesStore.updateSlideIndex(0) // 下标0 为第一页
|
||||||
slidesStore.setSlides(slides) // 写入数据
|
slidesStore.setSlides(slides) // 写入数据
|
||||||
// 写入作业列表数据
|
// 写入作业列表数据
|
||||||
slidesStore.setWorkList(workList)
|
slidesStore.setWorkList(workList)
|
||||||
|
@ -147,6 +147,8 @@ export class PPTApi {
|
||||||
// 更新幻灯片
|
// 更新幻灯片
|
||||||
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 thumUrl = await this.getSlideThumUrl()
|
||||||
|
data.base64Code = thumUrl // 更新缩略图
|
||||||
const res: Result = await API_entpcoursefile.updateEntpcoursefileNew(data)
|
const res: Result = await API_entpcoursefile.updateEntpcoursefileNew(data)
|
||||||
console.log(data,'data');
|
console.log(data,'data');
|
||||||
console.log(res,'dresata');
|
console.log(res,'dresata');
|
||||||
|
@ -191,30 +193,40 @@ export class PPTApi {
|
||||||
else msgUtils.msgError(res.msg || '更新失败');return false
|
else msgUtils.msgError(res.msg || '更新失败');return false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// thumbnail-slide thumbnail
|
||||||
|
static getSlideThumUrl(): Promise<Boolean> {
|
||||||
|
return nextTick().then(async() => {
|
||||||
|
const slideIndex = slidesStore.slideIndex
|
||||||
|
const elements = document.querySelectorAll('.thumbnail-slide')
|
||||||
|
if (elements.length && slideIndex >= 0) {
|
||||||
|
const element = elements[slideIndex]
|
||||||
|
return await toPng(element)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 图片|音频|视频 转换为在线地址
|
// 图片|音频|视频 转换为在线地址
|
||||||
static toRousrceUrl =async (o:any) => {
|
static toRousrceUrl =async (file: File|any) => {
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
formData.append('file', o)
|
formData.append('file', file)
|
||||||
const res = await Api_server.Other.uploadFile(formData)
|
const res = await Api_server.Other.uploadFile(formData)
|
||||||
if (res && res.code == 200){
|
if (res && res.code == 200){
|
||||||
const url = res?.url
|
const url = res?.url
|
||||||
url &&(o.src = url)
|
url &&(o.src = url)
|
||||||
return url
|
return url
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Homework{
|
export class Homework{
|
||||||
// 作业弹窗
|
// 作业弹窗
|
||||||
static async showHomework(id: any) {
|
static async showHomework(id: any) {
|
||||||
let result = await getClassWorkList(id)
|
let result = await CreateHomework.getClassWorkList(id)
|
||||||
result = await getStudentClassWorkData()
|
result = await CreateHomework.getStudentClassWorkData()
|
||||||
localStorage.setItem('teachClassWorkItem', JSON.stringify(result[0]));
|
localStorage.setItem('teachClassWorkItem', JSON.stringify(result[0]));
|
||||||
toolStore.isTaskWin=true; // 设置打开批改窗口
|
toolStore.isTaskWin=true; // 设置打开批改窗口
|
||||||
// emit('closeActive')
|
createWindow('open-taskwin',{url:'/teachClassTask'});
|
||||||
createWindow('open-taskwin',{url:'/teachClassTask'});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default PPTApi
|
export default PPTApi
|
|
@ -19,6 +19,147 @@ export interface Classcourse {
|
||||||
entpcoursefileid?: number|string, // 课程文件id
|
entpcoursefileid?: number|string, // 课程文件id
|
||||||
classid?: number|string, // 班级id
|
classid?: number|string, // 班级id
|
||||||
entpcourseid?: number|string, // 章节中间表id
|
entpcourseid?: number|string, // 章节中间表id
|
||||||
|
timgroupid?: number|string, // ws 群组id
|
||||||
plandate?: string, // 计划时间
|
plandate?: string, // 计划时间
|
||||||
opendate?: string, // 开课时间
|
opendate?: string, // 开课时间
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 消息枚举
|
||||||
|
* @author zdg
|
||||||
|
* @date 2021-07-05 14:07:01
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class MsgEnum {
|
||||||
|
/**
|
||||||
|
* @description: 消息类型
|
||||||
|
*
|
||||||
|
* | 名称 | 含义 | 值(enum) |
|
||||||
|
* | ---- | ---- | ---- |
|
||||||
|
* | SYSTEM | 系统消息 | system |
|
||||||
|
* | TEACHER | 老师消息 | teacher |
|
||||||
|
* | STUDENT | 学生消息 | student |
|
||||||
|
* | NOTICE | 通知消息 | notice |
|
||||||
|
*/
|
||||||
|
static TYPES = {
|
||||||
|
/** @desc: 系统消息 */
|
||||||
|
SYSTEM: 'system',
|
||||||
|
/** @desc: 老师消息 */
|
||||||
|
TEACHER: 'teacher',
|
||||||
|
/** @desc: 学生消息 */
|
||||||
|
STUDENT: 'student',
|
||||||
|
/** @desc: 通知消息 */
|
||||||
|
NOTICE: 'notice'
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @description: 消息头-类型
|
||||||
|
*
|
||||||
|
* | 名称 | 含义 | 值(enum) |
|
||||||
|
* | ---- | ---- | ---- |
|
||||||
|
* | --- | 以下为旧定义-消息头 | --- |
|
||||||
|
* | MSG_closed | 结束课程(下课) | closed |
|
||||||
|
* | MSG_onlineStatus | 在线状态 | onlineStatus |
|
||||||
|
* | MSG_pushQuizOfClassWorkdata2Public | 老师端:把选中的学生习题作业,推到大屏 | pushQuizOfClassWorkdata2Public |
|
||||||
|
* | MSG_pushClassWorkdata2Public | 老师端:把选中的学生作业,推到大屏 | pushClassWorkdata2Public |
|
||||||
|
* | MSG_shareStudentPresentdata2All | 把某个学生的展示成果数据推给全班所有学生 | shareStudentPresentdata2All |
|
||||||
|
* | MSG_pushStudentPresentdata2Public | 老师端:课堂展示活动,把选中的学生展示数据,推到大屏 | pushStudentPresentdata2Public |
|
||||||
|
* | MSG_pushClassWorkPresentList2Public | 老师端:课堂展示活动,任务列表,推到大屏 | pushClassWorkPresentList2Public |
|
||||||
|
* | MSG_activePageType | 课标研读-分页切换 | activePageType |
|
||||||
|
* | MSG_slideFlapping | 幻灯片-切换 | slideFlapping |
|
||||||
|
* | MSG_anmationclick | 幻灯片-动画切换 | anmationclick |
|
||||||
|
* | MSG_classcourseopen | 群组创建成功 | classcourseopen |
|
||||||
|
* | MSG_classquizfeedback | 学生的测练结果反馈 | classquizfeedback |
|
||||||
|
* | MSG_classtaskfeedback | 老师端:接收到学生反馈消息-课堂测练中的其他任务 | classtaskfeedback |
|
||||||
|
* | MSG_studentfeedback | 老师端:学生反馈的消息,具体要看其中的feedbackkey,类别较繁杂 | studentfeedback |
|
||||||
|
* | MSG_studentfeedbackcancel | 老师端:学生反馈的消息取消,如取消学会了,取消困惑 | studentfeedbackcancel |
|
||||||
|
* | MSG_classshowdata | 学生提交的课堂展示数据-要在老师端显示,再由老师选择推送到公屏上 | classshowdata |
|
||||||
|
* | MSG_classWorkOfPresentDataUpdate | 学生在公屏上展示并完善后,保存后,老师端要更新 | classWorkOfPresentDataUpdate |
|
||||||
|
* | MSG_classlecturePagesrc | 课堂讲授活动,选择不同的内容 | classlecturePagesrc |
|
||||||
|
* | --- | 以下为新定义-消息头 | --- |
|
||||||
|
* | MSG_0001 | 点赞 | 0x0001 |
|
||||||
|
* | MSG_0002 | xx | 0x0002 |
|
||||||
|
* | MSG_0003 | xx | 0x0003 |
|
||||||
|
*/
|
||||||
|
static HEADS = {
|
||||||
|
// === 旧定义-消息头(兼容以前) ===
|
||||||
|
/** @desc: 开课 */
|
||||||
|
MSG_open : 'open',
|
||||||
|
/** @desc: 结束课程(下课) */
|
||||||
|
MSG_closed : 'closed',
|
||||||
|
/** @desc: 在线状态 */
|
||||||
|
MSG_onlineStatus : 'onlineStatus',
|
||||||
|
/** @desc: 老师端:把选中的学生习题作业,推到大屏 */
|
||||||
|
MSG_pushQuizOfClassWorkdata2Public : 'pushQuizOfClassWorkdata2Public',
|
||||||
|
/** @desc: 老师端:把选中的学生作业,推到大屏 */
|
||||||
|
MSG_pushClassWorkdata2Public : 'pushClassWorkdata2Public',
|
||||||
|
/** @desc: 把某个学生的展示成果数据推给全班所有学生 */
|
||||||
|
MSG_shareStudentPresentdata2All : 'shareStudentPresentdata2All',
|
||||||
|
/** @desc: 老师端:课堂展示活动,把选中的学生展示数据,推到大屏 */
|
||||||
|
MSG_pushStudentPresentdata2Public : 'pushStudentPresentdata2Public',
|
||||||
|
/** @desc: 老师端:课堂展示活动,任务列表,推到大屏 */
|
||||||
|
MSG_pushClassWorkPresentList2Public : 'pushClassWorkPresentList2Public',
|
||||||
|
/** @desc: 课标研读-分页切换 */
|
||||||
|
MSG_activePageType : 'activePageType',
|
||||||
|
/** @desc: 幻灯片-切换 */
|
||||||
|
MSG_slideFlapping : 'slideFlapping',
|
||||||
|
/** @desc: 幻灯片-动画切换 */
|
||||||
|
MSG_anmationclick : 'anmationclick',
|
||||||
|
/** @desc: 群组创建成功 */
|
||||||
|
MSG_classcourseopen : 'classcourseopen',
|
||||||
|
/** @desc: 学生的测练结果反馈 */
|
||||||
|
MSG_classquizfeedback : 'classquizfeedback',
|
||||||
|
/** @desc: 老师端:接收到学生反馈消息-课堂测练中的其他任务 */
|
||||||
|
MSG_classtaskfeedback : 'classtaskfeedback',
|
||||||
|
/** @desc: 老师端:学生反馈的消息,具体要看其中的feedbackkey,类别较繁杂 */
|
||||||
|
MSG_studentfeedback : 'studentfeedback',
|
||||||
|
/** @desc: 老师端:学生反馈的消息取消,如取消学会了,取消困惑 */
|
||||||
|
MSG_studentfeedbackcancel : 'studentfeedbackcancel',
|
||||||
|
/** @desc: 学生提交的课堂展示数据-要在老师端显示,再由老师选择推送到公屏上 */
|
||||||
|
MSG_classshowdata : 'classshowdata',
|
||||||
|
/** @desc: 学生在公屏上展示并完善后,保存后,老师端要更新 */
|
||||||
|
MSG_classWorkOfPresentDataUpdate : 'classWorkOfPresentDataUpdate',
|
||||||
|
/** @desc: 课堂讲授活动,选择不同的内容 */
|
||||||
|
MSG_classlecturePagesrc : 'classlecturePagesrc',
|
||||||
|
// === 新定义-消息头 ===
|
||||||
|
/** @desc: 课程创建-待开课 */
|
||||||
|
MSG_0000: 0x0000,
|
||||||
|
/** @desc: 点赞 */
|
||||||
|
MSG_0001: 0x0001,
|
||||||
|
/** @desc: 疑惑 */
|
||||||
|
MSG_0002: 0x0002,
|
||||||
|
MSG_0003: 0x0003,
|
||||||
|
MSG_0004: 0x0004,
|
||||||
|
MSG_0005: 0x0005,
|
||||||
|
MSG_0006: 0x0006,
|
||||||
|
MSG_0007: 0x0007,
|
||||||
|
MSG_0008: 0x0008,
|
||||||
|
MSG_0009: 0x0009,
|
||||||
|
MSG_0010: 0x000a,
|
||||||
|
MSG_0011: 0x000b,
|
||||||
|
MSG_0012: 0x000c,
|
||||||
|
MSG_0013: 0x000d,
|
||||||
|
MSG_0014: 0x000e,
|
||||||
|
MSG_0015: 0x000f,
|
||||||
|
/** @desc: 作业推送 */
|
||||||
|
MSG_0016: 0x0010,
|
||||||
|
MSG_0017: 0x0011,
|
||||||
|
MSG_0018: 0x0012,
|
||||||
|
MSG_0019: 0x0013,
|
||||||
|
MSG_0020: 0x0014,
|
||||||
|
MSG_0021: 0x0015,
|
||||||
|
MSG_0022: 0x0016,
|
||||||
|
MSG_0023: 0x0017,
|
||||||
|
MSG_0024: 0x0018,
|
||||||
|
MSG_0025: 0x0019,
|
||||||
|
MSG_0026: 0x001a,
|
||||||
|
MSG_0027: 0x001b,
|
||||||
|
MSG_0028: 0x001c,
|
||||||
|
MSG_0029: 0x001d,
|
||||||
|
MSG_0030: 0x001e,
|
||||||
|
MSG_0031: 0x001f,
|
||||||
|
MSG_0032: 0x0020,
|
||||||
|
MSG_0033: 0x0021,
|
||||||
|
MSG_0034: 0x0022,
|
||||||
|
MSG_0035: 0x0023,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,13 +6,17 @@ import { watch } from 'vue'
|
||||||
import { PPTApi } from './index'
|
import { PPTApi } from './index'
|
||||||
import * as store from '../store'
|
import * as store from '../store'
|
||||||
import { sessionStore } from '@/utils/store' // electron-store 状态管理
|
import { sessionStore } from '@/utils/store' // electron-store 状态管理
|
||||||
|
import { MsgEnum } from './types' // 消息枚举
|
||||||
|
import ChatWs from '@/plugins/socket' // 聊天socket
|
||||||
|
import Classcourse from './classcourse' // 课程相关
|
||||||
|
import msgUtils from '@/plugins/modal' // 消息工具
|
||||||
const slidesStore = store.useSlidesStore()
|
const slidesStore = store.useSlidesStore()
|
||||||
|
const classcourseStore = store.useClasscourseStore() // 课堂信息-状态管理
|
||||||
const resource = sessionStore.get('curr.resource') // apt 资源
|
const resource = sessionStore.get('curr.resource') // apt 资源
|
||||||
const smarttalk = sessionStore.get('curr.smarttalk') // 备课资源
|
const smarttalk = sessionStore.get('curr.smarttalk') // 备课资源
|
||||||
/**
|
/**
|
||||||
* @description 监听器
|
* @description 监听器
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// 监听幻灯片内容变化
|
// 监听幻灯片内容变化
|
||||||
watch(() => slidesStore.slides, (newVal, oldVal) => {
|
watch(() => slidesStore.slides, (newVal, oldVal) => {
|
||||||
PPTApi.updateSlides(newVal, oldVal) // 更新幻灯片内容
|
PPTApi.updateSlides(newVal, oldVal) // 更新幻灯片内容
|
||||||
|
@ -24,6 +28,20 @@ watch(() => slidesStore.title, (newVal, oldVal) => {
|
||||||
updatePPT({title: newVal})
|
updatePPT({title: newVal})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 消息监听ws
|
||||||
|
console.log('监听器已开启', ChatWs)
|
||||||
|
if (ChatWs.ws) {
|
||||||
|
ChatWs.watch((msg, e) => {
|
||||||
|
try {
|
||||||
|
handleMessage(JSON.parse(msg))
|
||||||
|
} catch (error) {
|
||||||
|
console.error('socket 解析异常 ', error, e)
|
||||||
|
handleMessage(msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新ppt内容
|
||||||
const updatePPT = async (data) => {
|
const updatePPT = async (data) => {
|
||||||
if (!resource) return
|
if (!resource) return
|
||||||
data.id = resource.id
|
data.id = resource.id
|
||||||
|
@ -37,3 +55,40 @@ const updatePPT = async (data) => {
|
||||||
sessionStore.set('curr.smarttalk.fileShowName', params.fileShowName)
|
sessionStore.set('curr.smarttalk.fileShowName', params.fileShowName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ws消息处理
|
||||||
|
const handleMessage = (msg) => {
|
||||||
|
if (typeof msg === 'object'){
|
||||||
|
const { head, content, ...other } = msg
|
||||||
|
switch (head) {
|
||||||
|
case MsgEnum.HEADS.MSG_open: // 开课
|
||||||
|
// 课堂信息不一致
|
||||||
|
if (Classcourse.id !== content.id) {
|
||||||
|
msgUtils.alertError('老师开课信息异常,请重新进入公屏!')
|
||||||
|
.then(() => { // 点击确定按钮,关闭窗口
|
||||||
|
window.close()
|
||||||
|
})
|
||||||
|
} else { // 正常更新数据
|
||||||
|
classcourseStore.classcourse.status = 'open'
|
||||||
|
// 更新课堂信息-关闭警告框
|
||||||
|
Classcourse?.msgObj?.onVanish()
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case MsgEnum.HEADS.MSG_slideFlapping: // 幻灯片翻页
|
||||||
|
const slideIndex = content.current
|
||||||
|
slidesStore.updateSlideIndex(slideIndex) // 更新幻灯片下标
|
||||||
|
break
|
||||||
|
case MsgEnum.HEADS.MSG_closed: // 下课:
|
||||||
|
window.close() // 关闭窗口
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// console.log('监听器已开启', Classcourse)
|
||||||
|
// setTimeout(() => {
|
||||||
|
// console.log('关闭弹窗')
|
||||||
|
// // Classcourse.msgObj?.close()
|
||||||
|
// Classcourse?.msgObj?.onVanish()
|
||||||
|
// }, 10 * 1000)
|
|
@ -33,6 +33,48 @@ const { theme } = storeToRefs(useSlidesStore())
|
||||||
|
|
||||||
const { addSlidesFromData } = useAddSlidesOrElements()
|
const { addSlidesFromData } = useAddSlidesOrElements()
|
||||||
const { isEmptySlide } = useSlideHandler()
|
const { isEmptySlide } = useSlideHandler()
|
||||||
|
const parseLineElement = (el: Shape) => {
|
||||||
|
let start: [number, number] = [0, 0]
|
||||||
|
let end: [number, number] = [0, 0]
|
||||||
|
|
||||||
|
if (!el.isFlipV && !el.isFlipH) { // 右下
|
||||||
|
start = [0, 0]
|
||||||
|
end = [el.width, el.height]
|
||||||
|
}
|
||||||
|
else if (el.isFlipV && el.isFlipH) { // 左上
|
||||||
|
start = [el.width, el.height]
|
||||||
|
end = [0, 0]
|
||||||
|
}
|
||||||
|
else if (el.isFlipV && !el.isFlipH) { // 右上
|
||||||
|
start = [0, el.height]
|
||||||
|
end = [el.width, 0]
|
||||||
|
}
|
||||||
|
else { // 左下
|
||||||
|
start = [el.width, 0]
|
||||||
|
end = [0, el.height]
|
||||||
|
}
|
||||||
|
|
||||||
|
const data: PPTLineElement = {
|
||||||
|
type: 'line',
|
||||||
|
id: nanoid(10),
|
||||||
|
width: el.borderWidth || 1,
|
||||||
|
left: el.left,
|
||||||
|
top: el.top,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
style: el.borderType,
|
||||||
|
color: el.borderColor,
|
||||||
|
points: ['', /straightConnector/.test(el.shapType) ? 'arrow' : '']
|
||||||
|
}
|
||||||
|
if (/bentConnector/.test(el.shapType)) {
|
||||||
|
data.broken2 = [
|
||||||
|
Math.abs(start[0] - end[0]) / 2,
|
||||||
|
Math.abs(start[1] - end[1]) / 2,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
export default () => {
|
export default () => {
|
||||||
|
|
||||||
const exporting = ref(false)
|
const exporting = ref(false)
|
||||||
|
@ -59,48 +101,7 @@ export default () => {
|
||||||
reader.readAsText(file)
|
reader.readAsText(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
const parseLineElement = (el: Shape) => {
|
|
||||||
let start: [number, number] = [0, 0]
|
|
||||||
let end: [number, number] = [0, 0]
|
|
||||||
|
|
||||||
if (!el.isFlipV && !el.isFlipH) { // 右下
|
|
||||||
start = [0, 0]
|
|
||||||
end = [el.width, el.height]
|
|
||||||
}
|
|
||||||
else if (el.isFlipV && el.isFlipH) { // 左上
|
|
||||||
start = [el.width, el.height]
|
|
||||||
end = [0, 0]
|
|
||||||
}
|
|
||||||
else if (el.isFlipV && !el.isFlipH) { // 右上
|
|
||||||
start = [0, el.height]
|
|
||||||
end = [el.width, 0]
|
|
||||||
}
|
|
||||||
else { // 左下
|
|
||||||
start = [el.width, 0]
|
|
||||||
end = [0, el.height]
|
|
||||||
}
|
|
||||||
|
|
||||||
const data: PPTLineElement = {
|
|
||||||
type: 'line',
|
|
||||||
id: nanoid(10),
|
|
||||||
width: el.borderWidth || 1,
|
|
||||||
left: el.left,
|
|
||||||
top: el.top,
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
style: el.borderType,
|
|
||||||
color: el.borderColor,
|
|
||||||
points: ['', /straightConnector/.test(el.shapType) ? 'arrow' : '']
|
|
||||||
}
|
|
||||||
if (/bentConnector/.test(el.shapType)) {
|
|
||||||
data.broken2 = [
|
|
||||||
Math.abs(start[0] - end[0]) / 2,
|
|
||||||
Math.abs(start[1] - end[1]) / 2,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
// 导入PPTX文件
|
// 导入PPTX文件
|
||||||
const importPPTXFile = (files: FileList) => {
|
const importPPTXFile = (files: FileList) => {
|
||||||
|
@ -493,6 +494,7 @@ export default () => {
|
||||||
importPPTXFile,
|
importPPTXFile,
|
||||||
PPTXFileToJson,
|
PPTXFileToJson,
|
||||||
exporting,
|
exporting,
|
||||||
|
parseLineElement
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -664,6 +666,7 @@ export const PPTXFileToJson = (data: File|ArrayBuffer) => {
|
||||||
}
|
}
|
||||||
else if (el.type === 'shape') {
|
else if (el.type === 'shape') {
|
||||||
if (el.shapType === 'line' || /Connector/.test(el.shapType)) {
|
if (el.shapType === 'line' || /Connector/.test(el.shapType)) {
|
||||||
|
// 从返回对象中解构出 xx 函数并调用
|
||||||
const lineElement = parseLineElement(el)
|
const lineElement = parseLineElement(el)
|
||||||
slide.elements.push(lineElement)
|
slide.elements.push(lineElement)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { defineStore } from 'pinia'
|
||||||
import type { Classcourse } from '../api/types'
|
import type { Classcourse } from '../api/types'
|
||||||
|
|
||||||
export interface ClasscourseState {
|
export interface ClasscourseState {
|
||||||
classcourse: Classcourse
|
classcourse: Classcourse | any, // 课堂信息
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useClasscourseStore = defineStore('classcourse', {
|
export const useClasscourseStore = defineStore('classcourse', {
|
||||||
|
|
|
@ -30,7 +30,7 @@ let params = {
|
||||||
levelSecondId: null,
|
levelSecondId: null,
|
||||||
fileSource: '个人',
|
fileSource: '个人',
|
||||||
fileRoot: '备课',
|
fileRoot: '备课',
|
||||||
orderByColumn: 'uploadTime',
|
orderByColumn: 'createTime',
|
||||||
isAsc: 'desc',
|
isAsc: 'desc',
|
||||||
pageSize: 500
|
pageSize: 500
|
||||||
}
|
}
|
||||||
|
|
|
@ -245,7 +245,6 @@ const runAnimation = (elId: string, effect: string, duration: number) => {
|
||||||
const animationName = `${ANIMATION_CLASS_PREFIX}${effect}`
|
const animationName = `${ANIMATION_CLASS_PREFIX}${effect}`
|
||||||
document.documentElement.style.setProperty('--animate-duration', `${duration}ms`)
|
document.documentElement.style.setProperty('--animate-duration', `${duration}ms`)
|
||||||
elRef.classList.add(`${ANIMATION_CLASS_PREFIX}animated`, animationName)
|
elRef.classList.add(`${ANIMATION_CLASS_PREFIX}animated`, animationName)
|
||||||
|
|
||||||
const handleAnimationEnd = () => {
|
const handleAnimationEnd = () => {
|
||||||
document.documentElement.style.removeProperty('--animate-duration')
|
document.documentElement.style.removeProperty('--animate-duration')
|
||||||
elRef.classList.remove(`${ANIMATION_CLASS_PREFIX}animated`, animationName)
|
elRef.classList.remove(`${ANIMATION_CLASS_PREFIX}animated`, animationName)
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<!-- 作业列表 -->
|
<!-- 作业列表 -->
|
||||||
<div class="c-apt-r">
|
<div class="c-apt-r" v-loading='loadingActive'>
|
||||||
<!-- 显示-作业内容 -->
|
<!-- 显示-作业内容 -->
|
||||||
<template v-for="(item, index) in workList" :key="index">
|
<template v-for="(item, index) in workList" :key="index">
|
||||||
<div class="item">
|
<div class="item">
|
||||||
|
@ -45,9 +45,11 @@
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<!-- // 推送作业 -->
|
<!-- // 推送作业 -->
|
||||||
<el-dialog v-model="dialogVisible" append-to-body :show-close="false" width="80%" height="500">
|
<el-dialog v-model="dialogVisible" append-to-body :show-close="false" width="90%" height="500">
|
||||||
<el-scrollbar height="500">
|
<el-scrollbar height="550">
|
||||||
<NewClassTsakAssign :currentCourse='currentCourse' @getData="getData" />
|
<div style="height: 550px;">
|
||||||
|
<NewClassTsakAssign :currentCourse='currentCourse' @getData="getData" />
|
||||||
|
</div>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
<!-- 活动引用 -->
|
<!-- 活动引用 -->
|
||||||
|
@ -142,7 +144,6 @@ const currentCourse = reactive<CurrentCourse>({
|
||||||
worktype: '',
|
worktype: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
const dataList = ref<WorkItem[]>([])
|
|
||||||
const dialogVisible = ref<boolean>(false)
|
const dialogVisible = ref<boolean>(false)
|
||||||
const tasklist_loading = ref<boolean>(false)
|
const tasklist_loading = ref<boolean>(false)
|
||||||
|
|
||||||
|
@ -152,11 +153,6 @@ const taskList = ref<WorkItem[]>([])
|
||||||
// 活动引用的弹窗
|
// 活动引用的弹窗
|
||||||
const activeVisible = ref<boolean>(false)
|
const activeVisible = ref<boolean>(false)
|
||||||
|
|
||||||
const params = reactive<Params>({
|
|
||||||
parentid: 14766,
|
|
||||||
pageSize: 500,
|
|
||||||
orderby: 'fileidx'
|
|
||||||
})
|
|
||||||
|
|
||||||
const type = ref<WorkType[]>([
|
const type = ref<WorkType[]>([
|
||||||
{
|
{
|
||||||
|
@ -179,6 +175,8 @@ const workList = ref<WorkItem[]>([])
|
||||||
|
|
||||||
// 获取所选择的作业列表
|
// 获取所选择的作业列表
|
||||||
const selectedWorkList = ref<WorkItem[]>([])
|
const selectedWorkList = ref<WorkItem[]>([])
|
||||||
|
// 活动页面的loading框
|
||||||
|
const loadingActive = ref<boolean>(false)
|
||||||
|
|
||||||
|
|
||||||
const paramData = ref<{ id: number, activityContent: string }>({} as { id: number, activityContent: string })
|
const paramData = ref<{ id: number, activityContent: string }>({} as { id: number, activityContent: string })
|
||||||
|
@ -196,7 +194,6 @@ const formatClassWorkFile = async (postData: WorkItem[]): Promise<void> => {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case '习题训练': {
|
case '习题训练': {
|
||||||
console.log(item,'item');
|
|
||||||
// let workIds = item.quizlist!.map(items => items.id).join(',');
|
// let workIds = item.quizlist!.map(items => items.id).join(',');
|
||||||
// let ress = await listEntpcoursework({ ids: workIds });
|
// let ress = await listEntpcoursework({ ids: workIds });
|
||||||
// const arr = ress.rows.map((item:{id:number}) => {
|
// const arr = ress.rows.map((item:{id:number}) => {
|
||||||
|
@ -214,22 +211,19 @@ const formatClassWorkFile = async (postData: WorkItem[]): Promise<void> => {
|
||||||
// item.prevData = JSON.parse(item.workcodes);
|
// item.prevData = JSON.parse(item.workcodes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const arr = paramData.value.activityContent.split(',')
|
workList.value.push(item)
|
||||||
arr.push(item.id.toString())
|
loadingActive.value = false
|
||||||
await PPTApi.updateSlide(paramData.value)
|
|
||||||
addWorkList(item)
|
|
||||||
}
|
}
|
||||||
await nextTick();
|
await nextTick();
|
||||||
}
|
}// 删除作业
|
||||||
// 添加的活动回显到页面上面去
|
|
||||||
const addWorkList = (item: WorkItem) => {
|
|
||||||
workList.value.push(item)
|
|
||||||
}
|
|
||||||
// 删除作业
|
|
||||||
const handleRemoveDemoActivityClassWork = (item: WorkItem) => {
|
const handleRemoveDemoActivityClassWork = (item: WorkItem) => {
|
||||||
ElMessageBox.confirm('是否确认删除?')
|
ElMessageBox.confirm('是否确认删除?')
|
||||||
.then(() => {
|
.then(() => {
|
||||||
workList.value.splice(workList.value.indexOf(item), 1);
|
workList.value = []
|
||||||
|
const arr = paramData.value.activityContent.split(',')
|
||||||
|
const filterArr = arr.filter(itemId => itemId!== item.id.toString())
|
||||||
|
paramData.value.activityContent = filterArr.join(',')
|
||||||
|
upDateData()
|
||||||
})
|
})
|
||||||
.catch(() => { });
|
.catch(() => { });
|
||||||
}
|
}
|
||||||
|
@ -269,13 +263,51 @@ const savePPtData = async () => {
|
||||||
ElMessage.warning('请选择活动')
|
ElMessage.warning('请选择活动')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
workList.value = []
|
||||||
const arr = selectedWorkList.value.map(item => item.id)
|
const arr = selectedWorkList.value.map(item => item.id)
|
||||||
// 应该是新加而不是覆盖
|
// 应该是新加而不是覆盖
|
||||||
paramData.value.activityContent = arr.join(',')
|
const existingIds = paramData.value.activityContent ? paramData.value.activityContent.split(',') : []
|
||||||
await PPTApi.updateSlide(paramData.value)
|
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
|
const curNode = sessionStore.get('subject.curNode') as CourseNode
|
||||||
currentCourse.textbookId = curNode.rootid
|
currentCourse.textbookId = curNode.rootid
|
||||||
|
@ -283,33 +315,12 @@ onMounted(() => {
|
||||||
currentCourse.levelSecondId = curNode.id
|
currentCourse.levelSecondId = curNode.id
|
||||||
currentCourse.coursetitle = curNode.itemtitle
|
currentCourse.coursetitle = curNode.itemtitle
|
||||||
currentCourse.node = curNode
|
currentCourse.node = curNode
|
||||||
listEntpcoursefile(params).then((res: { rows: WorkItem[] }) => {
|
|
||||||
dataList.value = [...res.rows]
|
|
||||||
})
|
|
||||||
objItem.value = workItem.value[slideIndex.value]
|
objItem.value = workItem.value[slideIndex.value]
|
||||||
getCurrentPPtData()
|
getCurrentPPtData()
|
||||||
})
|
})
|
||||||
watch(() => slideIndex.value, () => {
|
watch(() => slideIndex.value, () => {
|
||||||
getCurrentPPtData()
|
getCurrentPPtData()
|
||||||
})
|
})
|
||||||
// 获取当前ppt页的数据
|
|
||||||
const getCurrentPPtData = async () => {
|
|
||||||
workList.value = []
|
|
||||||
objItem.value = workItem.value[slideIndex.value]
|
|
||||||
paramData.value.id = objItem.value.id
|
|
||||||
if (objItem.value?.activityContent) {
|
|
||||||
paramData.value.activityContent = objItem.value?.activityContent
|
|
||||||
const res = await homeworklist({ ids: objItem.value?.activityContent, pageSize: 100 })
|
|
||||||
await formatClassWorkFile(res.rows)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 接收习题训练的值
|
|
||||||
const getData = async (data: WorkItem) => {
|
|
||||||
console.log(data, 'data')
|
|
||||||
await formatClassWorkFile([data])
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.buttonDiv{
|
.buttonDiv{
|
||||||
|
@ -375,5 +386,8 @@ const getData = async (data: WorkItem) => {
|
||||||
--el-border-color: var(--current-color);
|
--el-border-color: var(--current-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.page .page-resource{
|
||||||
|
height: 500px !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
|
@ -34,7 +34,10 @@
|
||||||
<IconLeftTwo class="tool-btn" theme="two-tone" :fill="['#111', '#fff']" @click="execPrev()" />
|
<IconLeftTwo class="tool-btn" theme="two-tone" :fill="['#111', '#fff']" @click="execPrev()" />
|
||||||
<IconRightTwo class="tool-btn" theme="two-tone" :fill="['#111', '#fff']" @click="execNext()" />
|
<IconRightTwo class="tool-btn" theme="two-tone" :fill="['#111', '#fff']" @click="execNext()" />
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 点赞组件 -->
|
||||||
|
<div style="z-index: 999;position: absolute;top:10px">
|
||||||
|
<upvote-vue ref="upvoteRef" :test='true' type="2"></upvote-vue>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
class="tools-right" :class="{ 'visible': rightToolsVisible }"
|
class="tools-right" :class="{ 'visible': rightToolsVisible }"
|
||||||
@mouseleave="rightToolsVisible = false"
|
@mouseleave="rightToolsVisible = false"
|
||||||
|
@ -69,7 +72,7 @@ import ScreenSlideList from './ScreenSlideList.vue'
|
||||||
import SlideThumbnails from './SlideThumbnails.vue'
|
import SlideThumbnails from './SlideThumbnails.vue'
|
||||||
import WritingBoardTool from './WritingBoardTool.vue'
|
import WritingBoardTool from './WritingBoardTool.vue'
|
||||||
import CountdownTimer from './CountdownTimer.vue'
|
import CountdownTimer from './CountdownTimer.vue'
|
||||||
|
import upvoteVue from '@/views/tool/components/upvote.vue' // 点赞-子组件
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
changeViewMode: (mode: 'base' | 'presenter') => void
|
changeViewMode: (mode: 'base' | 'presenter') => void
|
||||||
}>()
|
}>()
|
||||||
|
|
|
@ -56,4 +56,5 @@ export class Other {
|
||||||
static baseUrl = "/common/upload"
|
static baseUrl = "/common/upload"
|
||||||
// 测试
|
// 测试
|
||||||
static uploadFile = data => ApiService.publicHttp(this.baseUrl, data, 'post', null, 'file')
|
static uploadFile = data => ApiService.publicHttp(this.baseUrl, data, 'post', null, 'file')
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "iconfont"; /* Project id 4723712 */
|
font-family: "iconfont"; /* Project id 4723712 */
|
||||||
src: url('iconfont.woff2?t=1732240267757') format('woff2'),
|
src: url('iconfont.woff2?t=1733880548695') format('woff2'),
|
||||||
url('iconfont.woff?t=1732240267757') format('woff'),
|
url('iconfont.woff?t=1733880548695') format('woff'),
|
||||||
url('iconfont.ttf?t=1732240267757') format('truetype');
|
url('iconfont.ttf?t=1733880548695') format('truetype');
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconfont {
|
.iconfont {
|
||||||
|
@ -13,6 +13,26 @@
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-yuyin:before {
|
||||||
|
content: "\e648";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-dianying:before {
|
||||||
|
content: "\e693";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-jiqirenfushi:before {
|
||||||
|
content: "\e624";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-xiangmuicon_maobishufa:before {
|
||||||
|
content: "\e651";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-meishu-F:before {
|
||||||
|
content: "\e638";
|
||||||
|
}
|
||||||
|
|
||||||
.icon-shangchuan:before {
|
.icon-shangchuan:before {
|
||||||
content: "\e61b";
|
content: "\e61b";
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -5,6 +5,41 @@
|
||||||
"css_prefix_text": "icon-",
|
"css_prefix_text": "icon-",
|
||||||
"description": "",
|
"description": "",
|
||||||
"glyphs": [
|
"glyphs": [
|
||||||
|
{
|
||||||
|
"icon_id": "6338162",
|
||||||
|
"name": "语音生成",
|
||||||
|
"font_class": "yuyin",
|
||||||
|
"unicode": "e648",
|
||||||
|
"unicode_decimal": 58952
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "6880941",
|
||||||
|
"name": "视频生成",
|
||||||
|
"font_class": "dianying",
|
||||||
|
"unicode": "e693",
|
||||||
|
"unicode_decimal": 59027
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "11532042",
|
||||||
|
"name": "数字人生成",
|
||||||
|
"font_class": "jiqirenfushi",
|
||||||
|
"unicode": "e624",
|
||||||
|
"unicode_decimal": 58916
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "13522843",
|
||||||
|
"name": "文生图片",
|
||||||
|
"font_class": "xiangmuicon_maobishufa",
|
||||||
|
"unicode": "e651",
|
||||||
|
"unicode_decimal": 58961
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "37635062",
|
||||||
|
"name": "文生连环画",
|
||||||
|
"font_class": "meishu-F",
|
||||||
|
"unicode": "e638",
|
||||||
|
"unicode_decimal": 58936
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"icon_id": "4942656",
|
"icon_id": "4942656",
|
||||||
"name": "上传",
|
"name": "上传",
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -35,7 +35,12 @@ const getFileTypeIcon = () => {
|
||||||
txt: 'icon-txt',
|
txt: 'icon-txt',
|
||||||
rar: 'icon-rar',
|
rar: 'icon-rar',
|
||||||
apt: 'icon-A',
|
apt: 'icon-A',
|
||||||
aippt: 'icon-A',
|
aippt: 'icon-a-ziyuan91',
|
||||||
|
aiyuyin: 'icon-yuyin', // 语音生成
|
||||||
|
aivideo: 'icon-dianying', // 视频生成
|
||||||
|
airobot: 'icon-jiqirenfushi', // 数字人生成
|
||||||
|
aiimg: 'icon-xiangmuicon_maobishufa', // 文生图片
|
||||||
|
aidraw: 'icon-meishu-F', // 文生连环画
|
||||||
}
|
}
|
||||||
if (iconObj[name]) {
|
if (iconObj[name]) {
|
||||||
return '#' + iconObj[name]
|
return '#' + iconObj[name]
|
||||||
|
|
|
@ -29,6 +29,20 @@
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
|
<div class="file-list">
|
||||||
|
<el-dropdown @command="changeFile">
|
||||||
|
<span class="el-dropdown-link">
|
||||||
|
{{ curFile.fileName }}
|
||||||
|
<i class="iconfont icon-xiangxia"></i>
|
||||||
|
</span>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item v-for="item in fileList" :command="item" :key="item.id">{{ item.fileName
|
||||||
|
}}</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</div>
|
||||||
<div class="input-box flex">
|
<div class="input-box flex">
|
||||||
<el-input v-model="textarea" @keyup.enter="send" :disabled="loaded"/>
|
<el-input v-model="textarea" @keyup.enter="send" :disabled="loaded"/>
|
||||||
<div class="ipt-icon" @click="send">
|
<div class="ipt-icon" @click="send">
|
||||||
|
@ -40,13 +54,15 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, onMounted, watch } from 'vue'
|
import { ref, reactive, onMounted, onUnmounted, watch } from 'vue'
|
||||||
import { completion } from '@/api/mode/index'
|
import { completion, docList } from '@/api/mode/index'
|
||||||
import { sessionStore } from '@/utils/store'
|
import { sessionStore } from '@/utils/store'
|
||||||
import { ElMessage } from 'element-plus'
|
|
||||||
import { dataSetJson } from '@/utils/comm.js'
|
import { dataSetJson } from '@/utils/comm.js'
|
||||||
|
import useUserStore from '@/store/modules/user'
|
||||||
import emitter from '@/utils/mitt';
|
import emitter from '@/utils/mitt';
|
||||||
|
|
||||||
|
const userInfo = useUserStore().user
|
||||||
|
|
||||||
const textarea = ref('')
|
const textarea = ref('')
|
||||||
|
|
||||||
const isDialog = defineModel()
|
const isDialog = defineModel()
|
||||||
|
@ -106,7 +122,6 @@ const getCompletion = async (val) => {
|
||||||
|
|
||||||
const saveAdjust = (item) =>{
|
const saveAdjust = (item) =>{
|
||||||
isDialog.value = false
|
isDialog.value = false
|
||||||
ElMessage.success('操作成功')
|
|
||||||
emitter.emit('onSaveAdjust', item.msg)
|
emitter.emit('onSaveAdjust', item.msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,6 +139,27 @@ watch(() => props.type, (newVal) => {
|
||||||
|
|
||||||
}, { immediate: false })
|
}, { immediate: false })
|
||||||
|
|
||||||
|
const curFile = reactive({})
|
||||||
|
const dataset_id = ref('')
|
||||||
|
const fileList = ref([])
|
||||||
|
const getList = () =>{
|
||||||
|
docList({
|
||||||
|
userId: userInfo.userId,
|
||||||
|
dataset_id: dataset_id.value
|
||||||
|
}).then( res =>{
|
||||||
|
fileList.value = res.rows
|
||||||
|
Object.assign(curFile, fileList.value[0])
|
||||||
|
params.document_ids = fileList.value[0].docId
|
||||||
|
})
|
||||||
|
}
|
||||||
|
emitter.on('changeCurFile', (item) =>{
|
||||||
|
changeFile(item)
|
||||||
|
})
|
||||||
|
const changeFile = (val) =>{
|
||||||
|
Object.assign(curFile, val);
|
||||||
|
params.document_ids = val.docId
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
let data = sessionStore.get('subject.curNode')
|
let data = sessionStore.get('subject.curNode')
|
||||||
|
@ -131,6 +167,15 @@ onMounted(() => {
|
||||||
Object.assign(curNode, data);
|
Object.assign(curNode, data);
|
||||||
let jsonKey = `${modeType.value}-${data.edustage}-${data.edusubject}`
|
let jsonKey = `${modeType.value}-${data.edustage}-${data.edusubject}`
|
||||||
params.dataset_id = dataSetJson[jsonKey]
|
params.dataset_id = dataSetJson[jsonKey]
|
||||||
|
if(props.type == 3){
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
// 解绑
|
||||||
|
onUnmounted(() => {
|
||||||
|
emitter.off('changeCurFile');
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -267,4 +312,9 @@ onMounted(() => {
|
||||||
transform: scale(0.01);
|
transform: scale(0.01);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.file-list{
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<el-dialog v-model="mode" :show-close="false" width="600" append-to-body destroy-on-close>
|
<el-dialog v-model="mode" :show-close="false" width="600" append-to-body destroy-on-close>
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="custom-header flex">
|
<div class="custom-header flex">
|
||||||
<span>{{ item.ex3 == '1' ? '请输入新的模板名称' : isAdd ? '添加提示词' : '编辑提示词' }}</span>
|
<span>{{ item.ex3 == '1' ? '请输入新的模板名称' : item.isAdd ? '添加提示词' : '编辑提示词' }}</span>
|
||||||
<i class="iconfont icon-guanbi" @click="mode = false"></i>
|
<i class="iconfont icon-guanbi" @click="mode = false"></i>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -40,10 +40,6 @@ const props = defineProps({
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 1
|
default: 1
|
||||||
},
|
},
|
||||||
isAdd: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
item: { // 子模板
|
item: { // 子模板
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => {
|
default: () => {
|
||||||
|
@ -58,21 +54,27 @@ const form = reactive({
|
||||||
prompt: '',
|
prompt: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(() => props.isAdd, (newVal) => {
|
|
||||||
if (!newVal) {
|
watch(() => mode.value, (newVal) => {
|
||||||
form.name = props.item?.name
|
if(newVal){
|
||||||
form.prompt = props.item?.prompt
|
if (props.item.isAdd) {
|
||||||
|
form.name = ''
|
||||||
|
form.prompt = ''
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
form.name = props.item?.name
|
||||||
|
form.prompt = props.item?.prompt
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
},{ deep: true})
|
||||||
}, { immediate: false })
|
|
||||||
|
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const saveAdd = async () => {
|
const saveAdd = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
if (props.item.ex3 == '1') {
|
if (props.item.ex3 == '1') {
|
||||||
|
|
||||||
if (props.isAdd) {
|
if (props.item.isAdd) {
|
||||||
try {
|
try {
|
||||||
// 系统预设模板 copy一份
|
// 系统预设模板 copy一份
|
||||||
const { msg } = await addKeyWords({ name: form.name, id: props.item.id })
|
const { msg } = await addKeyWords({ name: form.name, id: props.item.id })
|
||||||
|
@ -88,7 +90,7 @@ const saveAdd = async () => {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if (props.isAdd) {
|
if (props.item.isAdd) {
|
||||||
onAddChildTemp(props.item.id)
|
onAddChildTemp(props.item.id)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<el-dialog v-model="isDialog" :show-close="false" width="900" destroy-on-close>
|
<el-dialog v-model="isDialog" :show-close="false" width="900" append-to-body destroy-on-close>
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="custom-header flex">
|
<div class="custom-header flex">
|
||||||
<span>选择{{ title }}</span>
|
<span>选择{{ title }}</span>
|
||||||
|
@ -7,25 +7,54 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="dialog-content">
|
<div class="dialog-content">
|
||||||
<div class="flex">
|
|
||||||
<el-radio-group v-model="radio" @change="changeRadio">
|
|
||||||
<el-radio :value="item.value" v-for="item in radioList">{{ item.label }}</el-radio>
|
|
||||||
</el-radio-group>
|
|
||||||
</div>
|
|
||||||
<div class="content-list">
|
<div class="content-list">
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="(item, index) in list" :class="activeIndex == index ? 'li-active' : ''" @click="clickItem(index)">
|
<li v-for="(item, index) in fileList" :class="activeIndex == index ? 'li-active' : ''"
|
||||||
<el-image class="img" :src="item.url" />
|
@click="clickItem(index, item)">
|
||||||
<span>{{ item.name }}</span>
|
<el-image class="img" :src="url" />
|
||||||
|
<el-button type="primary" class="prev-btn" @click.stop="onPrevItem(item)">预览</el-button>
|
||||||
|
<el-text truncated>{{ item.fileName }}</el-text>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="dialog-footer">
|
<div class="dialog-footer">
|
||||||
<el-button @click="isDialog = false">取消</el-button>
|
<el-upload class="upload-demo" :action="uploadFileUrl" :limit="1" :show-file-list="false" :headers="headers"
|
||||||
<el-button type="primary" @click="isDialog = false">
|
:on-success="onSuccess">
|
||||||
确定
|
<el-button type="primary">上传</el-button>
|
||||||
|
</el-upload>
|
||||||
|
<div>
|
||||||
|
<el-button @click="isDialog = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="isDialog = false">
|
||||||
|
确定
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
<el-dialog v-model="prevVisible" fullscreen :show-close="false" class="prev-dialog">
|
||||||
|
<template #header>
|
||||||
|
<div class="custom-header flex">
|
||||||
|
<span>预览</span>
|
||||||
|
<i class="iconfont icon-guanbi" @click="prevVisible = false"></i>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div style="height: calc(100vh - 120px);">
|
||||||
|
<template v-if="getFileSuffix(prevItem.fileUrl) == 'pdf'">
|
||||||
|
<iframe :src="prevItem.fileUrl"
|
||||||
|
frameborder="0" width="100%" height="100%"></iframe>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<el-image :src="prevItem.fileUrl" style="height:100%"/>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<div></div>
|
||||||
|
<el-button type="primary" @click="prevVisible = false">
|
||||||
|
关闭
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -33,11 +62,24 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed, onMounted, reactive } from 'vue'
|
||||||
|
import { completion, addDoc, docList } from '@/api/mode/index.js'
|
||||||
|
import { getToken } from "@/utils/auth";
|
||||||
|
import { sessionStore } from '@/utils/store'
|
||||||
|
import { dataSetJson } from '@/utils/comm.js'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import { getFileSuffix } from '@/utils/ruoyi.js'
|
||||||
|
import emitter from '@/utils/mitt';
|
||||||
|
|
||||||
|
const userInfo = useUserStore().user
|
||||||
|
const uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload");
|
||||||
|
const headers = ref({ Authorization: "Bearer " + getToken() });
|
||||||
|
|
||||||
const url = 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2F11044b08-04c1-41a0-a453-1fd20b58a614%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1732953359&t=7ab1d1b3a903db85b1149914407aea35'
|
const url = 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2F11044b08-04c1-41a0-a453-1fd20b58a614%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1732953359&t=7ab1d1b3a903db85b1149914407aea35'
|
||||||
|
|
||||||
const isDialog = defineModel()
|
const isDialog = defineModel()
|
||||||
|
const prevVisible = ref(false)
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modeType: {
|
modeType: {
|
||||||
|
@ -52,14 +94,13 @@ const title = computed(() => {
|
||||||
if (props.modeType == 3) return '考试';
|
if (props.modeType == 3) return '考试';
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
const radio = ref(1)
|
const radio = ref(1)
|
||||||
const radioList = ref([
|
const radioList = ref([
|
||||||
{ label: '浏览研读', value: 1 },
|
{ label: '浏览研读', value: 1 },
|
||||||
// { label: '跨学科研读', value: 2 },
|
{ label: '跨学科研读', value: 2 },
|
||||||
// { label: '跨学段研读', value: 3 },
|
{ label: '跨学段研读', value: 3 },
|
||||||
// { label: '课标修订研读', value: 4 },
|
{ label: '课标修订研读', value: 4 },
|
||||||
// { label: '自由研读', value: 5 },
|
{ label: '自由研读', value: 5 },
|
||||||
])
|
])
|
||||||
const list = ref([
|
const list = ref([
|
||||||
{
|
{
|
||||||
|
@ -76,15 +117,70 @@ const changeRadio = () => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const activeIndex = ref(0)
|
||||||
|
|
||||||
const activeIndex = ref(-1)
|
const dataset_id = ref('')
|
||||||
|
|
||||||
const clickItem = (index) => {
|
// 上传成功
|
||||||
activeIndex.value = index
|
const onSuccess = async (response) => {
|
||||||
|
let data = {
|
||||||
|
url: response.url,
|
||||||
|
dataset_id: dataset_id.value
|
||||||
|
}
|
||||||
|
const res = await completion(data)
|
||||||
|
|
||||||
|
if (res.data.code != 200) return
|
||||||
|
let docData = {
|
||||||
|
fileUrl: response.url,
|
||||||
|
fileId: response.file.id,
|
||||||
|
fileName: response.file.fileName,
|
||||||
|
filesize: response.file.fileSize,
|
||||||
|
datasetId: dataset_id.value,
|
||||||
|
docId: res.data.document_id,
|
||||||
|
edustage: curNode.edustage,
|
||||||
|
edusubject: curNode.edusubject
|
||||||
|
}
|
||||||
|
const { msg } = await addDoc(docData)
|
||||||
|
ElMessage.success(msg)
|
||||||
|
getList()
|
||||||
|
|
||||||
|
}
|
||||||
|
const curNode = reactive({})
|
||||||
|
|
||||||
|
const fileList = ref([])
|
||||||
|
const curFile = reactive({})
|
||||||
|
const getList = () => {
|
||||||
|
docList({
|
||||||
|
userId: userInfo.userId,
|
||||||
|
dataset_id: dataset_id.value
|
||||||
|
}).then(res => {
|
||||||
|
fileList.value = [...res.rows]
|
||||||
|
Object.assign(curFile, fileList.value[0])
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
const clickItem = (index, item) => {
|
||||||
|
activeIndex.value = index
|
||||||
|
Object.assign(curFile, item)
|
||||||
|
emitter.emit('changeCurFile', item)
|
||||||
|
}
|
||||||
|
|
||||||
|
const prevItem = reactive({})
|
||||||
|
const onPrevItem = (item) => {
|
||||||
|
Object.assign(prevItem, item)
|
||||||
|
prevVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
let data = sessionStore.get('subject.curNode')
|
||||||
|
Object.assign(curNode, data);
|
||||||
|
// 暂时写死"考试-" 目前只有考试分析才会弹出来
|
||||||
|
let jsonKey = `考试-${curNode.edustage}-${curNode.edusubject}`
|
||||||
|
dataset_id.value = dataSetJson[jsonKey]
|
||||||
|
getList()
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.custom-header {
|
.custom-header {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
@ -98,13 +194,16 @@ const clickItem = (index) => {
|
||||||
|
|
||||||
.dialog-content {
|
.dialog-content {
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
|
|
||||||
.content-list {
|
.content-list {
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
||||||
li {
|
li {
|
||||||
|
width: 130px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
@ -114,9 +213,11 @@ const clickItem = (index) => {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
.img {
|
.img {
|
||||||
width: 100px;
|
width: 100%;
|
||||||
height: 130px;
|
height: 130px;
|
||||||
border: solid #ccc 1px;
|
border: solid #ccc 1px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
@ -125,6 +226,10 @@ const clickItem = (index) => {
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #E0EAFF;
|
background: #E0EAFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover .prev-btn {
|
||||||
|
transform: translate(-50%, -40px)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.li-active {
|
.li-active {
|
||||||
|
@ -134,4 +239,20 @@ const clickItem = (index) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dialog-footer {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prev-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%) translateY(-110px);
|
||||||
|
/* 按钮初始位置在容器外 */
|
||||||
|
transition: transform 0.3s ease-in-out;
|
||||||
|
/* 设置过渡效果 */
|
||||||
|
}
|
||||||
</style>
|
</style>
|
|
@ -2,12 +2,13 @@
|
||||||
<div class="container-left-page flex">
|
<div class="container-left-page flex">
|
||||||
<div class="container-left-header flex">
|
<div class="container-left-header flex">
|
||||||
<el-button link @click="onClick">
|
<el-button link @click="onClick">
|
||||||
{{ curNode.edustage }}{{ curNode.edusubject }}{{ type == 1 ? '课标研读' : '教材分析' }}<i
|
{{ curNode.edustage }}{{ curNode.edusubject }}{{ type == 1 ? '课标研读' : type == 2 ? '教材分析' : '考试分析' }}<i
|
||||||
class="iconfont icon-xiangxia"></i>
|
class="iconfont icon-xiangxia"></i>
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="container-left-pdf">
|
<div class="container-left-pdf">
|
||||||
<PDF :url="pdfUrl" :showCatalog="false" v-if="pdfUrl" />
|
<PDF :url="pdfUrl" :showCatalog="false" v-if="pdfUrl" />
|
||||||
|
<el-empty v-else description="暂无数据" />
|
||||||
</div>
|
</div>
|
||||||
<!--弹窗-->
|
<!--弹窗-->
|
||||||
<LeftDialog v-model="showDialog" :modeType="type" />
|
<LeftDialog v-model="showDialog" :modeType="type" />
|
||||||
|
@ -24,7 +25,7 @@ const props = defineProps(['curNode', 'type'])
|
||||||
|
|
||||||
const showDialog = ref(false)
|
const showDialog = ref(false)
|
||||||
const onClick = () => {
|
const onClick = () => {
|
||||||
if (props.type == 1) return
|
if (props.type != 3) return
|
||||||
showDialog.value = true
|
showDialog.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +38,9 @@ onMounted(async () => {
|
||||||
if(props.type == 1){
|
if(props.type == 1){
|
||||||
fileurl = `${data.edustage}-${data.edusubject}-课标.txt`
|
fileurl = `${data.edustage}-${data.edusubject}-课标.txt`
|
||||||
}
|
}
|
||||||
|
if(fileurl == '') return
|
||||||
pdfUrl.value = import.meta.env.VITE_APP_RES_FILE_PATH + fileurl.replace('.txt', '.pdf')
|
pdfUrl.value = import.meta.env.VITE_APP_RES_FILE_PATH + fileurl.replace('.txt', '.pdf')
|
||||||
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
<i class="iconfont icon-shenglvehao"></i></el-button>
|
<i class="iconfont icon-shenglvehao"></i></el-button>
|
||||||
</template>
|
</template>
|
||||||
<template #default>
|
<template #default>
|
||||||
<el-button type="primary" link @click="editKeyWord(item)">编辑</el-button>
|
<el-button type="primary" link @click="editKeyWord(item, false)">编辑</el-button>
|
||||||
<el-button type="primary" link @click="removeItem(item, true)">移除</el-button>
|
<el-button type="primary" link @click="removeItem(item, true)">移除</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-popover>
|
</el-popover>
|
||||||
|
@ -79,7 +79,7 @@
|
||||||
<!--AI 对话调整-->
|
<!--AI 对话调整-->
|
||||||
<AdjustDialog v-model="isAdjust" :type="type" :item="editItem" />
|
<AdjustDialog v-model="isAdjust" :type="type" :item="editItem" />
|
||||||
<!--添加、编辑提示词-->
|
<!--添加、编辑提示词-->
|
||||||
<keywordDialog v-model="isWordDialog" :isAdd="isAdd" :item="editItem" />
|
<keywordDialog v-model="isWordDialog" :item="editItem" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
@ -105,21 +105,22 @@ const props = defineProps(['curNode', 'type'])
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const isWordDialog = ref(false)
|
const isWordDialog = ref(false)
|
||||||
const isAdd = ref(false)
|
|
||||||
const editItem = reactive({})
|
const editItem = reactive({})
|
||||||
const onAdd = () => {
|
const onAdd = () => {
|
||||||
isAdd.value = true
|
|
||||||
Object.assign(editItem, curTemplate)
|
Object.assign(editItem, curTemplate)
|
||||||
|
editItem.isAdd = true
|
||||||
isWordDialog.value = true
|
isWordDialog.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
const editKeyWord = (item, val) => {
|
const editKeyWord = (item, val) => {
|
||||||
/**
|
/**
|
||||||
* isAdd: 字模板中的移除 为编辑 头部删除 添加提示词为新增
|
* isAdd: 子模板中的移除 为编辑false 头部删除 添加提示词为新增 true
|
||||||
*/
|
*/
|
||||||
isAdd.value = val
|
|
||||||
Object.assign(editItem, item)
|
Object.assign(editItem, item)
|
||||||
|
editItem.isAdd = val
|
||||||
isWordDialog.value = true
|
isWordDialog.value = true
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************模板相关**********************/
|
/*******************模板相关**********************/
|
||||||
|
@ -361,8 +362,8 @@ emitter.on('onSaveAdjust', (item) => {
|
||||||
|
|
||||||
// 保存 重新研读后的结果
|
// 保存 重新研读后的结果
|
||||||
const onEditSave = async (item) => {
|
const onEditSave = async (item) => {
|
||||||
const { res } = await editTempResult({ id: item.resultId, content: item.answer })
|
const { msg } = await editTempResult({ id: item.resultId, content: item.answer })
|
||||||
ElMessage.success(res)
|
ElMessage.success(msg)
|
||||||
getChildTemplate()
|
getChildTemplate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
readonly
|
readonly
|
||||||
resize="none"
|
resize="none"
|
||||||
style="width: 100%;"
|
style="width: 100%;"
|
||||||
input-style="border:none;outline: none;box-shadow:none;color:000;fontSize:15px"
|
input-style="border:none;outline: none;box-shadow:none;color:000;fontSize:14px"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -71,9 +71,6 @@ watch([() => props.text, () => props.delay], resetAndType);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.typing-effect {
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
:deep(.el-textarea__inner:hover){
|
:deep(.el-textarea__inner:hover){
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,6 +93,12 @@ const headerMenus = [
|
||||||
icon: 'icon-jiaoxueshijian',
|
icon: 'icon-jiaoxueshijian',
|
||||||
path: '/prepare'
|
path: '/prepare'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: '教学活动',
|
||||||
|
id: 5,
|
||||||
|
icon: 'icon-zuoyepigai',
|
||||||
|
path: '/classTask'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: '资源中心',
|
name: '资源中心',
|
||||||
id: 3,
|
id: 3,
|
||||||
|
|
|
@ -56,6 +56,8 @@ export class MsgEnum {
|
||||||
*/
|
*/
|
||||||
static HEADS = {
|
static HEADS = {
|
||||||
// === 旧定义-消息头(兼容以前) ===
|
// === 旧定义-消息头(兼容以前) ===
|
||||||
|
/** @desc: 开课 */
|
||||||
|
MSG_open : 'open',
|
||||||
/** @desc: 结束课程(下课) */
|
/** @desc: 结束课程(下课) */
|
||||||
MSG_closed : 'closed',
|
MSG_closed : 'closed',
|
||||||
/** @desc: 在线状态 */
|
/** @desc: 在线状态 */
|
||||||
|
@ -93,6 +95,8 @@ export class MsgEnum {
|
||||||
/** @desc: 课堂讲授活动,选择不同的内容 */
|
/** @desc: 课堂讲授活动,选择不同的内容 */
|
||||||
MSG_classlecturePagesrc : 'classlecturePagesrc',
|
MSG_classlecturePagesrc : 'classlecturePagesrc',
|
||||||
// === 新定义-消息头 ===
|
// === 新定义-消息头 ===
|
||||||
|
/** @desc: 课程创建-待开课 */
|
||||||
|
MSG_0000: 0x0000,
|
||||||
/** @desc: 点赞 */
|
/** @desc: 点赞 */
|
||||||
MSG_0001: 0x0001,
|
MSG_0001: 0x0001,
|
||||||
/** @desc: 疑惑 */
|
/** @desc: 疑惑 */
|
||||||
|
|
|
@ -5,51 +5,51 @@ let loadingInstance;
|
||||||
export default {
|
export default {
|
||||||
// 消息提示
|
// 消息提示
|
||||||
msg(content) {
|
msg(content) {
|
||||||
ElMessage.info(content)
|
return ElMessage.info(content)
|
||||||
},
|
},
|
||||||
// 错误消息
|
// 错误消息
|
||||||
msgError(content) {
|
msgError(content) {
|
||||||
ElMessage.error(content)
|
return ElMessage.error(content)
|
||||||
},
|
},
|
||||||
// 成功消息
|
// 成功消息
|
||||||
msgSuccess(content) {
|
msgSuccess(content) {
|
||||||
ElMessage.success(content)
|
return ElMessage.success(content)
|
||||||
},
|
},
|
||||||
// 警告消息
|
// 警告消息
|
||||||
msgWarning(content) {
|
msgWarning(content) {
|
||||||
ElMessage.warning(content)
|
return ElMessage.warning(content)
|
||||||
},
|
},
|
||||||
// 弹出提示
|
// 弹出提示
|
||||||
alert(content) {
|
alert(content, option = {}) {
|
||||||
ElMessageBox.alert(content, "系统提示")
|
return ElMessageBox.alert(content, "系统提示", option)
|
||||||
},
|
},
|
||||||
// 错误提示
|
// 错误提示
|
||||||
alertError(content) {
|
alertError(content, option = {}) {
|
||||||
ElMessageBox.alert(content, "系统提示", { type: 'error' })
|
return ElMessageBox.alert(content, "系统提示", { type: 'error', ...option })
|
||||||
},
|
},
|
||||||
// 成功提示
|
// 成功提示
|
||||||
alertSuccess(content) {
|
alertSuccess(content, option = {}) {
|
||||||
ElMessageBox.alert(content, "系统提示", { type: 'success' })
|
return ElMessageBox.alert(content, "系统提示", { type: 'success', ...option })
|
||||||
},
|
},
|
||||||
// 警告提示
|
// 警告提示
|
||||||
alertWarning(content) {
|
alertWarning(content, option = {}) {
|
||||||
ElMessageBox.alert(content, "系统提示", { type: 'warning' })
|
return ElMessageBox.alert(content, "系统提示", { type: 'warning', ...option })
|
||||||
},
|
},
|
||||||
// 通知提示
|
// 通知提示
|
||||||
notify(content) {
|
notify(content) {
|
||||||
ElNotification.info(content)
|
return ElNotification.info(content)
|
||||||
},
|
},
|
||||||
// 错误通知
|
// 错误通知
|
||||||
notifyError(content) {
|
notifyError(content) {
|
||||||
ElNotification.error(content);
|
return ElNotification.error(content);
|
||||||
},
|
},
|
||||||
// 成功通知
|
// 成功通知
|
||||||
notifySuccess(content) {
|
notifySuccess(content) {
|
||||||
ElNotification.success(content)
|
return ElNotification.success(content)
|
||||||
},
|
},
|
||||||
// 警告通知
|
// 警告通知
|
||||||
notifyWarning(content) {
|
notifyWarning(content) {
|
||||||
ElNotification.warning(content)
|
return ElNotification.warning(content)
|
||||||
},
|
},
|
||||||
// 确认窗体
|
// 确认窗体
|
||||||
confirm(content) {
|
confirm(content) {
|
||||||
|
@ -78,5 +78,8 @@ export default {
|
||||||
// 关闭遮罩层
|
// 关闭遮罩层
|
||||||
closeLoading() {
|
closeLoading() {
|
||||||
loadingInstance.close();
|
loadingInstance.close();
|
||||||
}
|
},
|
||||||
|
// messageBox: opt => ElMessageBox(opt),
|
||||||
|
// 其他实例放出去,方便调用
|
||||||
|
ElMessage, ElMessageBox, ElNotification, ElLoading
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import useUserStore from '@/store/modules/user' // 用户信息
|
||||||
export class ChatWs {
|
export class ChatWs {
|
||||||
instance = null; // 实例
|
instance = null; // 实例
|
||||||
id = null; // 群聊id || 单聊id-用户id(userId)
|
id = null; // 群聊id || 单聊id-用户id(userId)
|
||||||
|
url = null; // ws地址
|
||||||
closed = false; // 关闭状态
|
closed = false; // 关闭状态
|
||||||
onmessage = null; // 自定义处理
|
onmessage = null; // 自定义处理
|
||||||
errCount = 5; // 重连次数 (ms) 暂时不使用
|
errCount = 5; // 重连次数 (ms) 暂时不使用
|
||||||
|
@ -19,12 +20,16 @@ export class ChatWs {
|
||||||
beat: 'heart_beat', // 心跳
|
beat: 'heart_beat', // 心跳
|
||||||
}
|
}
|
||||||
static base = 'wss://file.ysaix.com:7868'
|
static base = 'wss://file.ysaix.com:7868'
|
||||||
constructor() {
|
|
||||||
|
// 构造函数 是否自动连接
|
||||||
|
constructor(bool = true) {
|
||||||
if (!ChatWs.instance) {
|
if (!ChatWs.instance) {
|
||||||
const userStore = useUserStore() // 用户信息
|
if (bool) { // 是否自动连接
|
||||||
const wsBase = import.meta.env.VITE_APP_WS_URL; // ws地址
|
const userStore = useUserStore() // 用户信息
|
||||||
const url = `${wsBase||ChatWs.base}/ws/websocket/${userStore.id}`;
|
const wsBase = import.meta.env.VITE_APP_WS_URL; // ws地址
|
||||||
this.init(url);
|
this.url = `${wsBase||ChatWs.base}/ws/websocket/${userStore.id}`;
|
||||||
|
// this.init(url);
|
||||||
|
}
|
||||||
ChatWs.instance = this;
|
ChatWs.instance = this;
|
||||||
}
|
}
|
||||||
return ChatWs.instance;
|
return ChatWs.instance;
|
||||||
|
@ -32,11 +37,11 @@ export class ChatWs {
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
init(url) {
|
init(url) {
|
||||||
this.url = url;
|
!!url && (this.url = url);
|
||||||
this.ws = null;
|
this.ws = null;
|
||||||
const _this = this
|
const _this = this
|
||||||
this.heartCheck = {
|
this.heartCheck = {
|
||||||
timeout: 1000 * 10, // 60s
|
timeout: 1000 * 60, // 60s
|
||||||
timeoutObj: null,
|
timeoutObj: null,
|
||||||
serverTimeoutObj: null,
|
serverTimeoutObj: null,
|
||||||
reset() {
|
reset() {
|
||||||
|
@ -78,10 +83,9 @@ export class ChatWs {
|
||||||
// 拿到任何消息都说明当前连接是正常的
|
// 拿到任何消息都说明当前连接是正常的
|
||||||
const isBeat = e.data == 'pong'
|
const isBeat = e.data == 'pong'
|
||||||
isBeat && self.heartCheck.reset().start();
|
isBeat && self.heartCheck.reset().start();
|
||||||
const exts = ['sessionId', 'pong'] // 不处理的消息头
|
|
||||||
const isEmpty = !e.data
|
const isEmpty = !e.data
|
||||||
const isExts = exts.some(item => e.data.includes(item))
|
const isExts = e.data.includes('sessionId') || e.data == ('pong')
|
||||||
if (isEmpty && isExts) return;
|
if (isEmpty || isExts) return;
|
||||||
// 自定义处理
|
// 自定义处理
|
||||||
self.onmessage && self.onmessage(e.data, e);
|
self.onmessage && self.onmessage(e.data, e);
|
||||||
};
|
};
|
||||||
|
@ -118,12 +122,12 @@ export class ChatWs {
|
||||||
this.ws.send(msg)
|
this.ws.send(msg)
|
||||||
}
|
}
|
||||||
// 发送消息-带消息头(key)
|
// 发送消息-带消息头(key)
|
||||||
sendMsg(head, content, option = {}) {
|
sendMsg(head, content, option = {}, ...arg) {
|
||||||
if (!head) throw new Error("head is not null")
|
if (!head && head!==0) throw new Error("head is not null")
|
||||||
if (!content) throw new Error("content is not null")
|
if (!content && content!==0) throw new Error("content is not null")
|
||||||
let msg = { head, content, ...option }
|
let msg = { head, content, ...option }
|
||||||
// 发送消息
|
// 发送消息
|
||||||
this.send(this.getMsgObj(msg))
|
this.send(this.getMsgObj(msg, ...arg))
|
||||||
}
|
}
|
||||||
// 发送心跳
|
// 发送心跳
|
||||||
sendMsgBeat() {
|
sendMsgBeat() {
|
||||||
|
@ -137,7 +141,7 @@ export class ChatWs {
|
||||||
* @param {*} id 群聊id || 单聊id-用户id(userId)
|
* @param {*} id 群聊id || 单聊id-用户id(userId)
|
||||||
*/
|
*/
|
||||||
getMsgObj(msg, chatType = 'group', id) {
|
getMsgObj(msg, chatType = 'group', id) {
|
||||||
if (typeof msg === "object") msg = JSON.stringify(msg)
|
// if (typeof msg === "object") msg = JSON.stringify(msg)
|
||||||
const res = {msg, chatType}
|
const res = {msg, chatType}
|
||||||
// if (!id) throw new Error(`${type=='group'?'群ID':'用户ID'} is not null`)
|
// if (!id) throw new Error(`${type=='group'?'群ID':'用户ID'} is not null`)
|
||||||
if (chatType == 'group') res.groupId = id || this.id || ''
|
if (chatType == 'group') res.groupId = id || this.id || ''
|
||||||
|
@ -160,5 +164,6 @@ export class ChatWs {
|
||||||
}
|
}
|
||||||
// 连接socket
|
// 连接socket
|
||||||
export const connect = () => new ChatWs()
|
export const connect = () => new ChatWs()
|
||||||
|
export const getInstance = () => new ChatWs(false)
|
||||||
// 默认实例
|
// 默认实例
|
||||||
export default new ChatWs()
|
export default new ChatWs()
|
|
@ -7,15 +7,20 @@
|
||||||
<span>{{item.caption}}</span>
|
<span>{{item.caption}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="class-reserv-item-tool" style="width: 200px;max-width: 300px">
|
<div class="class-reserv-item-tool" style="width: 200px;max-width: 300px">
|
||||||
<el-tag v-if="item.status === 'closed'" style="margin-right: 5px" type="success">已结束</el-tag>
|
<el-tag v-if="item.status === ''" style="margin-right: 5px" type="warning">待开课</el-tag>
|
||||||
<el-tag v-if="item.status === 'open'" style="margin-right: 5px" type="danger">上课中</el-tag>
|
<el-tag v-else-if="item.status === 'closed'" style="margin-right: 5px" type="success">已结束</el-tag>
|
||||||
<el-button v-if="item.status === 'open'" :disabled="toolStore.isToolWin" size="small" type="primary" @click="startClassR(item)"
|
<el-tag v-else-if="item.status === 'open'" style="margin-right: 5px" type="danger">上课中</el-tag>
|
||||||
>继续上课</el-button
|
<template v-if="!item.status">
|
||||||
>
|
<el-button size="small" type="primary" :icon="ChatDotRound" @click="chatSend()">上课(APP)</el-button>
|
||||||
<!-- <el-button v-if="item.status === '未开始'" @click="openEdit">编辑</el-button>-->
|
</template>
|
||||||
<el-button v-if="item.status === 'open'" :loading="loading" size="small" type="info" @click="endClassR(item)"
|
<template v-else-if="item.status === 'open'">
|
||||||
>下课{{ loading?'中...':'' }}</el-button
|
<el-button :disabled="toolStore.isToolWin" size="small" type="primary" @click="startClassR(item)"
|
||||||
>
|
>继续上课</el-button
|
||||||
|
>
|
||||||
|
<!--<el-button v-if="item.status === '未开始'" @click="openEdit">编辑</el-button>-->
|
||||||
|
<el-button :loading="loading" size="small" type="info" @click="endClassR(item)"
|
||||||
|
>下课{{ loading?'中...':'' }}</el-button>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class="class-reserv-item-tool" style="width: 50px;">
|
<div class="class-reserv-item-tool" style="width: 50px;">
|
||||||
<!-- <el-button v-if="item.status!='open'" size="small" type="danger" @click="deleteReserv">删除</el-button>-->
|
<!-- <el-button v-if="item.status!='open'" size="small" type="danger" @click="deleteReserv">删除</el-button>-->
|
||||||
|
@ -25,10 +30,12 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue'
|
import { ref, reactive } from 'vue'
|
||||||
import { useToolState } from '@/store/modules/tool'
|
import { useToolState } from '@/store/modules/tool'
|
||||||
import { deleteSmartReserv } from '@/api/classManage'
|
import { deleteSmartReserv } from '@/api/classManage'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { ChatDotRound } from '@element-plus/icons-vue'
|
||||||
|
|
||||||
const emit = defineEmits(['openEdit', 'deleteReserv', 'change'])
|
const emit = defineEmits(['openEdit', 'deleteReserv', 'change'])
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
item: {
|
item: {
|
||||||
|
@ -39,6 +46,7 @@ const props = defineProps({
|
||||||
const basePath = import.meta.env.VITE_APP_BUILD_BASE_PATH
|
const basePath = import.meta.env.VITE_APP_BUILD_BASE_PATH
|
||||||
const toolStore = useToolState() // 获取状态管理-tool
|
const toolStore = useToolState() // 获取状态管理-tool
|
||||||
const loading = ref(false) // 下课中的loading
|
const loading = ref(false) // 下课中的loading
|
||||||
|
const msg = reactive({ id: null, time: null }) // 发送消息的对象
|
||||||
const openEdit = () => {
|
const openEdit = () => {
|
||||||
emit('openEdit', props.item)
|
emit('openEdit', props.item)
|
||||||
}
|
}
|
||||||
|
@ -61,6 +69,18 @@ const startClassR = (item) => {
|
||||||
const endClassR = (item) => {
|
const endClassR = (item) => {
|
||||||
emit('change', 'close', item, { type: 2, loading })
|
emit('change', 'close', item, { type: 2, loading })
|
||||||
}
|
}
|
||||||
|
// 发送上课消息
|
||||||
|
const chatSend = () => {
|
||||||
|
const time = Date.now()
|
||||||
|
if(msg.id) { // 同一个人30秒内不能重复发送
|
||||||
|
const isEq = msg.id == props.item.id && (time - msg.time) < 30 * 1000
|
||||||
|
if(isEq) return ElMessage.warning('请勿重复发送(30秒内)!')
|
||||||
|
} else { // 记录发送信息
|
||||||
|
msg.id = props.item.id
|
||||||
|
msg.time = time
|
||||||
|
}
|
||||||
|
emit('change', 'wsApp', props.item)
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|
|
@ -157,6 +157,7 @@ const props = defineProps({
|
||||||
currentCourse: Object,
|
currentCourse: Object,
|
||||||
})
|
})
|
||||||
const emits = defineEmits(['getData'])
|
const emits = defineEmits(['getData'])
|
||||||
|
// 这个代表的是是否从ppt里面添加的作业
|
||||||
const isShow = ref(false)
|
const isShow = ref(false)
|
||||||
|
|
||||||
const propsQueryCourseObj = route.query.courseObj;//作业布置的内容对象
|
const propsQueryCourseObj = route.query.courseObj;//作业布置的内容对象
|
||||||
|
@ -490,12 +491,14 @@ const handleClassWorkFormQuizRemove = (index) =>{
|
||||||
|
|
||||||
|
|
||||||
// 当前为[编辑]状态下点进来得处理 newWorkSpaceEdit true 为编辑状态
|
// 当前为[编辑]状态下点进来得处理 newWorkSpaceEdit true 为编辑状态
|
||||||
if(classWorkForm.id != '' ) {// 编辑状态 有id
|
if(isShow.value === false){
|
||||||
|
if(classWorkForm.id != '' ) {// 编辑状态 有id
|
||||||
editWork(cform); // 编辑作业
|
editWork(cform); // 编辑作业
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (classWorkForm.worktype === "课堂展示") {
|
if (classWorkForm.worktype === "课堂展示") {
|
||||||
boardLoading.value = true
|
boardLoading.value = true
|
||||||
let canvasJson = proxy.$refs.boardref.getCanvasJson()
|
let canvasJson = proxy.$refs.boardref.getCanvasJson()
|
||||||
|
@ -596,7 +599,11 @@ const handleClassWorkFormQuizRemove = (index) =>{
|
||||||
}
|
}
|
||||||
console.log('该清空左侧列表数据了');
|
console.log('该清空左侧列表数据了');
|
||||||
// 清空左侧 选中的布置列表 并刷新列表
|
// 清空左侧 选中的布置列表 并刷新列表
|
||||||
currentRow.value = {id:0};
|
if(isShow.value){
|
||||||
|
currentRow.value = {id:1};
|
||||||
|
}else{
|
||||||
|
currentRow.value = {id:0};
|
||||||
|
}
|
||||||
initHomeWork();
|
initHomeWork();
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,306 +0,0 @@
|
||||||
<template>
|
|
||||||
<el-dialog v-model="isDialog" :show-close="false" width="800" destroy-on-close>
|
|
||||||
<template #header>
|
|
||||||
<div class="custom-header flex">
|
|
||||||
<span>{{ item.name }}</span>
|
|
||||||
<i class="iconfont icon-guanbi" @click="isDialog = false"></i>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<div class="dialog-content">
|
|
||||||
<el-scrollbar height="400px">
|
|
||||||
<div class="chart-con flex">
|
|
||||||
<template v-for="item in msgList">
|
|
||||||
<div class="flex-end flex" v-if="item.type == 'user'">
|
|
||||||
<div class="chart-item user">{{ item.msg }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex-start flex" v-else>
|
|
||||||
<div class="flex" v-loading="!item.msg">
|
|
||||||
<div class="chart-item robot">{{ item.msg }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-end replace-item">
|
|
||||||
<span @click="saveAdjust(item)"><i class="iconfont icon-tihuan"></i>替换分析结果</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="loaded" class="chart-loading">
|
|
||||||
<div></div>
|
|
||||||
<div></div>
|
|
||||||
<div></div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</el-scrollbar>
|
|
||||||
<div class="file-list">
|
|
||||||
<el-dropdown @command="changeFile">
|
|
||||||
<span class="el-dropdown-link">
|
|
||||||
{{ curFile.fileName }}
|
|
||||||
<i class="iconfont icon-xiangxia"></i>
|
|
||||||
</span>
|
|
||||||
<template #dropdown>
|
|
||||||
<el-dropdown-menu>
|
|
||||||
<el-dropdown-item v-for="item in fileList" :command="item" :key="item.id">{{ item.fileName
|
|
||||||
}}</el-dropdown-item>
|
|
||||||
</el-dropdown-menu>
|
|
||||||
</template>
|
|
||||||
</el-dropdown>
|
|
||||||
</div>
|
|
||||||
<div class="input-box flex">
|
|
||||||
<el-input v-model="textarea" @keyup.enter="send" :disabled="loaded"/>
|
|
||||||
<div class="ipt-icon" @click="send">
|
|
||||||
<i class="iconfont icon-fasong"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-dialog>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, reactive, onMounted } from 'vue'
|
|
||||||
import { conversation, completion, docList } from '@/api/mode/index'
|
|
||||||
import { sessionStore } from '@/utils/store'
|
|
||||||
import { ElMessage } from 'element-plus'
|
|
||||||
import { dataSetJson } from '@/utils/comm.js'
|
|
||||||
import useUserStore from '@/store/modules/user'
|
|
||||||
import emitter from '@/utils/mitt';
|
|
||||||
|
|
||||||
const userInfo = useUserStore().user
|
|
||||||
const textarea = ref('')
|
|
||||||
|
|
||||||
const isDialog = defineModel()
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
item: {
|
|
||||||
type: Object,
|
|
||||||
default: () => {
|
|
||||||
return { name: '11' }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
modeType: {
|
|
||||||
type: Number,
|
|
||||||
default: 1
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const emit = defineEmits(['saveEdit'])
|
|
||||||
|
|
||||||
const loaded = ref(false)
|
|
||||||
|
|
||||||
const msgList = ref([])
|
|
||||||
|
|
||||||
const send = () =>{
|
|
||||||
if(loaded.value) return
|
|
||||||
msgList.value.push({
|
|
||||||
type: 'user',
|
|
||||||
msg: textarea.value
|
|
||||||
})
|
|
||||||
loaded.value = true
|
|
||||||
getConversation(textarea.value)
|
|
||||||
textarea.value = ''
|
|
||||||
}
|
|
||||||
const curNode = reactive({})
|
|
||||||
const params = reactive(
|
|
||||||
{
|
|
||||||
prompt: '',
|
|
||||||
dataset_id: '',
|
|
||||||
document_ids: '',
|
|
||||||
}
|
|
||||||
)
|
|
||||||
// 获取会话ID
|
|
||||||
const getConversation = (val) => {
|
|
||||||
|
|
||||||
getCompletion(val)
|
|
||||||
}
|
|
||||||
// 大模型对话
|
|
||||||
const getCompletion = async (val) => {
|
|
||||||
try {
|
|
||||||
|
|
||||||
params.prompt = `按照${val}的要求,针对${curNode.edustage}${curNode.edusubject}考试 对${curNode.itemtitle}进行教学分析`
|
|
||||||
const { data } = await completion(params)
|
|
||||||
let answer = data.answer
|
|
||||||
msgList.value.push({
|
|
||||||
type: 'robot',
|
|
||||||
msg: answer,
|
|
||||||
})
|
|
||||||
|
|
||||||
} finally {
|
|
||||||
loaded.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const saveAdjust = (item) =>{
|
|
||||||
emit('saveAdjust', item.msg)
|
|
||||||
isDialog.value = false
|
|
||||||
ElMessage.success('操作成功')
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const curFile = reactive({})
|
|
||||||
const dataset_id = ref('')
|
|
||||||
const fileList = ref([])
|
|
||||||
const getList = () =>{
|
|
||||||
docList({
|
|
||||||
userId: userInfo.userId,
|
|
||||||
dataset_id: dataset_id.value
|
|
||||||
}).then( res =>{
|
|
||||||
fileList.value = res.rows
|
|
||||||
Object.assign(curFile, fileList.value[0])
|
|
||||||
params.document_ids = fileList.value[0].docId
|
|
||||||
})
|
|
||||||
}
|
|
||||||
emitter.on('curFile', (item) =>{
|
|
||||||
changeFile(item)
|
|
||||||
})
|
|
||||||
const changeFile = (val) =>{
|
|
||||||
Object.assign(curFile, val);
|
|
||||||
params.document_ids = val.docId
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
let data = sessionStore.get('subject.curNode')
|
|
||||||
Object.assign(curNode, data);
|
|
||||||
let text = props.modeType == 1 ||props.modeType == 2 ? '课标' : '考试'
|
|
||||||
|
|
||||||
let jsonKey = `${text}-${data.edustage}-${data.edusubject}`
|
|
||||||
params.dataset_id = dataSetJson[jsonKey]
|
|
||||||
dataset_id.value = dataSetJson[jsonKey]
|
|
||||||
getList()
|
|
||||||
})
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.custom-header {
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.icon-guanbi {
|
|
||||||
cursor: pointer;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog-content {
|
|
||||||
padding-top: 10px;
|
|
||||||
padding-bottom: 20px;
|
|
||||||
|
|
||||||
.chart-con {
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
.flex-end{
|
|
||||||
width: 100%;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
.flex-start{
|
|
||||||
width: 100%;
|
|
||||||
justify-content: flex-start;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
}
|
|
||||||
.chart-item {
|
|
||||||
border-radius: 5px;
|
|
||||||
padding: 5px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user {
|
|
||||||
background: #F2F2F2;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.robot {
|
|
||||||
background: #409EFF;
|
|
||||||
color: #FFF;
|
|
||||||
}
|
|
||||||
.replace-item{
|
|
||||||
font-size: 12px;
|
|
||||||
color: #409EFF;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-box {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.ipt-icon {
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 0 5px;
|
|
||||||
.icon-fasong {
|
|
||||||
font-size: 26px;
|
|
||||||
color: #409EFF;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.chart-loading,
|
|
||||||
.chart-loading>div {
|
|
||||||
position: relative;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-loading {
|
|
||||||
display: block;
|
|
||||||
font-size: 0;
|
|
||||||
color: #66b1ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-loading.la-dark {
|
|
||||||
color: #66b1ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-loading>div {
|
|
||||||
display: inline-block;
|
|
||||||
float: none;
|
|
||||||
background-color: currentColor;
|
|
||||||
border: 0 solid currentColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-loading {
|
|
||||||
width: 54px;
|
|
||||||
height: 18px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-loading>div:nth-child(1) {
|
|
||||||
animation-delay: -200ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-loading>div:nth-child(2) {
|
|
||||||
animation-delay: -100ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-loading>div:nth-child(3) {
|
|
||||||
animation-delay: 0ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-loading>div {
|
|
||||||
width: 8px;
|
|
||||||
height: 8px;
|
|
||||||
border-radius: 100%;
|
|
||||||
margin-right: 4px;
|
|
||||||
animation: ball-pulse 1s ease infinite;
|
|
||||||
}
|
|
||||||
@keyframes ball-pulse {
|
|
||||||
|
|
||||||
0%,
|
|
||||||
60%,
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
30% {
|
|
||||||
opacity: 0.1;
|
|
||||||
transform: scale(0.01);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-list{
|
|
||||||
display: flex;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,268 +0,0 @@
|
||||||
<template>
|
|
||||||
<el-dialog v-model="isDialog" :show-close="false" width="900" append-to-body destroy-on-close>
|
|
||||||
<template #header>
|
|
||||||
<div class="custom-header flex">
|
|
||||||
<span>选择{{ title }}</span>
|
|
||||||
<i class="iconfont icon-guanbi" @click="isDialog = false"></i>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<div class="dialog-content">
|
|
||||||
<div class="flex dialog-top">
|
|
||||||
<!-- <el-radio-group v-model="radio" @change="changeRadio">
|
|
||||||
<el-radio :value="item.value" v-for="item in radioList">{{ item.label }}</el-radio>
|
|
||||||
</el-radio-group> -->
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="content-list">
|
|
||||||
<ul>
|
|
||||||
<li v-for="(item, index) in fileList" :class="activeIndex == index ? 'li-active' : ''"
|
|
||||||
@click="clickItem(index, item)">
|
|
||||||
<el-image class="img" :src="url" />
|
|
||||||
<el-button type="primary" class="prev-btn" @click.stop="onPrevItem(item)">预览</el-button>
|
|
||||||
<el-text truncated>{{ item.fileName }}</el-text>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<template #footer>
|
|
||||||
<div class="dialog-footer">
|
|
||||||
<el-upload class="upload-demo" :action="uploadFileUrl" :limit="1" :show-file-list="false" :headers="headers"
|
|
||||||
:on-success="onSuccess">
|
|
||||||
<el-button type="primary">上传</el-button>
|
|
||||||
</el-upload>
|
|
||||||
<div>
|
|
||||||
<el-button @click="isDialog = false">取消</el-button>
|
|
||||||
<el-button type="primary" @click="isDialog = false">
|
|
||||||
确定
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
<el-dialog v-model="prevVisible" fullscreen :show-close="false" class="prev-dialog">
|
|
||||||
<template #header>
|
|
||||||
<div class="custom-header flex">
|
|
||||||
<span>预览</span>
|
|
||||||
<i class="iconfont icon-guanbi" @click="prevVisible = false"></i>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<div style="height: calc(100vh - 120px);">
|
|
||||||
<template v-if="getFileSuffix(prevItem.fileUrl) == 'pdf'">
|
|
||||||
<iframe :src="prevItem.fileUrl"
|
|
||||||
frameborder="0" width="100%" height="100%"></iframe>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<el-image :src="prevItem.fileUrl" style="height:100%"/>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<template #footer>
|
|
||||||
<div class="dialog-footer">
|
|
||||||
<div></div>
|
|
||||||
<el-button type="primary" @click="prevVisible = false">
|
|
||||||
关闭
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, computed, onMounted, reactive } from 'vue'
|
|
||||||
import { completion, addDoc, docList } from '@/api/mode/index.js'
|
|
||||||
import { getToken } from "@/utils/auth";
|
|
||||||
import { sessionStore } from '@/utils/store'
|
|
||||||
import { dataSetJson } from '@/utils/comm.js'
|
|
||||||
import { ElMessage } from 'element-plus'
|
|
||||||
import useUserStore from '@/store/modules/user'
|
|
||||||
import { getFileSuffix } from '@/utils/ruoyi.js'
|
|
||||||
import emitter from '@/utils/mitt';
|
|
||||||
|
|
||||||
const userInfo = useUserStore().user
|
|
||||||
const uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload");
|
|
||||||
const headers = ref({ Authorization: "Bearer " + getToken() });
|
|
||||||
|
|
||||||
const url = 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2F11044b08-04c1-41a0-a453-1fd20b58a614%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1732953359&t=7ab1d1b3a903db85b1149914407aea35'
|
|
||||||
|
|
||||||
const isDialog = defineModel()
|
|
||||||
const prevVisible = ref(false)
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
modeType: {
|
|
||||||
type: Number,
|
|
||||||
default: 1
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const title = computed(() => {
|
|
||||||
if (props.modeType == 1) return '课标';
|
|
||||||
if (props.modeType == 2) return '教材';
|
|
||||||
if (props.modeType == 3) return '考试';
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
const radio = ref(1)
|
|
||||||
const radioList = ref([
|
|
||||||
{ label: '浏览研读', value: 1 },
|
|
||||||
{ label: '跨学科研读', value: 2 },
|
|
||||||
{ label: '跨学段研读', value: 3 },
|
|
||||||
{ label: '课标修订研读', value: 4 },
|
|
||||||
{ label: '自由研读', value: 5 },
|
|
||||||
])
|
|
||||||
const list = ref([
|
|
||||||
{
|
|
||||||
name: '高中语文课程标准',
|
|
||||||
url
|
|
||||||
}
|
|
||||||
])
|
|
||||||
const changeRadio = () => {
|
|
||||||
list.value = []
|
|
||||||
for (let i = 0; i < Math.floor(Math.random() * 5) + 1; i++) {
|
|
||||||
list.value.push({
|
|
||||||
name: '高中语文课程标准',
|
|
||||||
url
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const activeIndex = ref(0)
|
|
||||||
|
|
||||||
const dataset_id = ref('')
|
|
||||||
|
|
||||||
// 上传成功
|
|
||||||
const onSuccess = async (response) => {
|
|
||||||
let data = {
|
|
||||||
url: response.url,
|
|
||||||
dataset_id: dataset_id.value
|
|
||||||
}
|
|
||||||
const res = await completion(data)
|
|
||||||
|
|
||||||
if (res.data.code != 200) return
|
|
||||||
let docData = {
|
|
||||||
fileUrl: response.url,
|
|
||||||
fileId: response.file.id,
|
|
||||||
fileName: response.file.fileName,
|
|
||||||
filesize: response.file.fileSize,
|
|
||||||
datasetId: dataset_id.value,
|
|
||||||
docId: res.data.document_id,
|
|
||||||
edustage: curNode.edustage,
|
|
||||||
edusubject: curNode.edusubject
|
|
||||||
}
|
|
||||||
const { msg } = await addDoc(docData)
|
|
||||||
ElMessage.success(msg)
|
|
||||||
getList()
|
|
||||||
|
|
||||||
}
|
|
||||||
const curNode = reactive({})
|
|
||||||
|
|
||||||
const fileList = ref([])
|
|
||||||
const curFile = reactive({})
|
|
||||||
const getList = () => {
|
|
||||||
docList({
|
|
||||||
userId: userInfo.userId,
|
|
||||||
dataset_id: dataset_id.value
|
|
||||||
}).then(res => {
|
|
||||||
fileList.value = [...res.rows]
|
|
||||||
Object.assign(curFile, fileList.value[0])
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const clickItem = (index, item) => {
|
|
||||||
activeIndex.value = index
|
|
||||||
Object.assign(curFile, item)
|
|
||||||
emitter.emit('curFile', item)
|
|
||||||
}
|
|
||||||
|
|
||||||
const prevItem = reactive({})
|
|
||||||
const onPrevItem = (item) => {
|
|
||||||
Object.assign(prevItem, item)
|
|
||||||
prevVisible.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
let data = sessionStore.get('subject.curNode')
|
|
||||||
Object.assign(curNode, data);
|
|
||||||
|
|
||||||
let jsonKey = `考试-${curNode.edustage}-${curNode.edusubject}`
|
|
||||||
dataset_id.value = dataSetJson[jsonKey]
|
|
||||||
getList()
|
|
||||||
})
|
|
||||||
|
|
||||||
</script>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.custom-header {
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.icon-guanbi {
|
|
||||||
cursor: pointer;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog-content {
|
|
||||||
padding-top: 10px;
|
|
||||||
|
|
||||||
.dialog-top {
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-list {
|
|
||||||
padding-top: 10px;
|
|
||||||
|
|
||||||
ul {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
|
|
||||||
li {
|
|
||||||
width: 130px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
font-size: 13px;
|
|
||||||
padding: 10px;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 5px;
|
|
||||||
overflow: hidden;
|
|
||||||
margin-right: 20px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.img {
|
|
||||||
width: 100%;
|
|
||||||
height: 130px;
|
|
||||||
border: solid #ccc 1px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: #E0EAFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover .prev-btn {
|
|
||||||
transform: translate(-50%, -50%)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.li-active {
|
|
||||||
background: #E0EAFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog-footer {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.prev-btn {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%) translateY(-110px);
|
|
||||||
/* 按钮初始位置在容器外 */
|
|
||||||
transition: transform 0.3s ease-in-out;
|
|
||||||
/* 设置过渡效果 */
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,78 +0,0 @@
|
||||||
<template>
|
|
||||||
<el-dialog v-model="isDialog" :show-close="false" width="900">
|
|
||||||
<template #header>
|
|
||||||
<div class="custom-header flex">
|
|
||||||
<span>{{ item.name }}</span>
|
|
||||||
<i class="iconfont icon-guanbi" @click="isDialog = false"></i>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<div class="dialog-content">
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="24">
|
|
||||||
<el-input v-model="textarea" :autosize="{ minRows: 5, maxRows: 15 }" type="textarea" />
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<template #footer>
|
|
||||||
<div class="dialog-footer">
|
|
||||||
<el-button @click="isDialog = false">取消</el-button>
|
|
||||||
<el-button type="primary" @click="onSave">
|
|
||||||
确定
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, watch} from 'vue'
|
|
||||||
import { ElMessage } from 'element-plus'
|
|
||||||
import { editTempResult } from '@/api/mode/index.js'
|
|
||||||
|
|
||||||
const textarea = ref('')
|
|
||||||
|
|
||||||
const isDialog = defineModel()
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
item: {
|
|
||||||
type: Object,
|
|
||||||
default: () => {
|
|
||||||
return { name: '11' }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(() => props.item.answer, (newVal) => {
|
|
||||||
if (newVal) {
|
|
||||||
textarea.value = newVal
|
|
||||||
}
|
|
||||||
},{ deep: true })
|
|
||||||
|
|
||||||
const emit = defineEmits(['saveEdit'])
|
|
||||||
const onSave = () =>{
|
|
||||||
editTempResult({id: props.item.resultId, content: textarea.value}).then( res =>{
|
|
||||||
isDialog.value = false
|
|
||||||
ElMessage.success('操作成功')
|
|
||||||
emit('saveEdit', textarea.value)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.custom-header {
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.icon-guanbi {
|
|
||||||
cursor: pointer;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog-content {
|
|
||||||
padding-top: 10px;
|
|
||||||
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,143 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="container-header flex">
|
|
||||||
<div class="header-left flex">
|
|
||||||
<el-button link @click="showDialog = true">
|
|
||||||
{{ curNode.edustage}}{{ curNode.edusubject }}考试分析<i class="iconfont icon-xiangxia"></i>
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="header-right flex">
|
|
||||||
<el-dropdown @command="changeTemplate" :hide-on-click="false">
|
|
||||||
<span class="el-dropdown-link">
|
|
||||||
{{ curTemplate.name }}
|
|
||||||
<i class="iconfont icon-xiangxia"></i>
|
|
||||||
</span>
|
|
||||||
<template #dropdown>
|
|
||||||
<el-dropdown-menu>
|
|
||||||
<el-dropdown-item v-for="item in templateList" :command="item" :key="item.id">{{ item.name
|
|
||||||
}}</el-dropdown-item>
|
|
||||||
</el-dropdown-menu>
|
|
||||||
</template>
|
|
||||||
</el-dropdown>
|
|
||||||
<div>
|
|
||||||
<el-button type="primary" link @click="onAdd">
|
|
||||||
<el-icon>
|
|
||||||
<Plus />
|
|
||||||
</el-icon>
|
|
||||||
添加提示词
|
|
||||||
</el-button>
|
|
||||||
<el-button type="primary" link>保存模板</el-button>
|
|
||||||
<el-button type="primary" @click="aiRead">一键研读</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!--添加提示词-->
|
|
||||||
<keywordDialog v-model="wordDialog" :modeType="type" :isAdd="wordDialog" :item="curTemplate" />
|
|
||||||
<Dialog v-model="showDialog" :modeType="type" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, reactive, onMounted, onUnmounted, watch } from 'vue'
|
|
||||||
import { Plus } from '@element-plus/icons-vue'
|
|
||||||
import { ElMessageBox } from 'element-plus'
|
|
||||||
import { modelList } from '@/api/mode/index'
|
|
||||||
import Dialog from './dialog.vue'
|
|
||||||
import keywordDialog from './keyword-dialog.vue'
|
|
||||||
import emitter from '@/utils/mitt';
|
|
||||||
import { sessionStore } from '@/utils/store'
|
|
||||||
|
|
||||||
|
|
||||||
const wordDialog = ref(false)
|
|
||||||
const props = defineProps({
|
|
||||||
type: {
|
|
||||||
type: Number,
|
|
||||||
default: 1
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
const emit = defineEmits(['changeTemp', 'onRead'])
|
|
||||||
|
|
||||||
const showDialog = ref(false)
|
|
||||||
|
|
||||||
const aiRead = () => {
|
|
||||||
emit('onRead')
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 当前模板名称
|
|
||||||
const curTemplate = reactive({ name: '', id: '' })
|
|
||||||
// 模板列表
|
|
||||||
const templateList = ref([])
|
|
||||||
// 获取模板列表
|
|
||||||
const getTemplateList = () => {
|
|
||||||
modelList({ model: props.type, type: 1, pageNum: 1, pageSize: 10000 }).then(res => {
|
|
||||||
templateList.value = res.rows
|
|
||||||
Object.assign(curTemplate, res.rows[0]);
|
|
||||||
emit('changeTemp', res.rows[0].id)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// 模板切换
|
|
||||||
const changeTemplate = (val) => {
|
|
||||||
|
|
||||||
ElMessageBox.confirm(
|
|
||||||
'切换模板将清除当前研读结果?',
|
|
||||||
'提示',
|
|
||||||
{
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
cancelButtonText: '取消',
|
|
||||||
type: 'warning',
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(() => {
|
|
||||||
Object.assign(curTemplate, val);
|
|
||||||
emit('changeTemp', curTemplate.id)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
emitter.on('onGetMain', () =>{
|
|
||||||
getTemplateList()
|
|
||||||
})
|
|
||||||
|
|
||||||
const onAdd = () => {
|
|
||||||
wordDialog.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
emitter.off('onGetMain')
|
|
||||||
})
|
|
||||||
const curNode = reactive({})
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
let data = sessionStore.get('subject.curNode')
|
|
||||||
Object.assign(curNode, data);
|
|
||||||
getTemplateList()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.container-header {
|
|
||||||
height: 45px;
|
|
||||||
background: #fff;
|
|
||||||
border-radius: 5px 5px 0 0;
|
|
||||||
box-shadow: 0px 0px 20px 0px rgba(99, 99, 99, 0.06);
|
|
||||||
|
|
||||||
.header-left {
|
|
||||||
width: 50%;
|
|
||||||
align-items: center;
|
|
||||||
padding-left: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-right {
|
|
||||||
width: 50%;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-xiangxia {
|
|
||||||
margin-left: 5px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,161 +0,0 @@
|
||||||
<template>
|
|
||||||
<el-dialog v-model="mode" :show-close="false" width="600" destroy-on-close>
|
|
||||||
<template #header>
|
|
||||||
<div class="custom-header flex">
|
|
||||||
<span>{{ item.ex3 == '1' ? '请输入新的模板名称' : isAdd ? '添加提示词' : '编辑提示词' }}</span>
|
|
||||||
<i class="iconfont icon-guanbi" @click="mode = false"></i>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<div class="dialog-content" v-loading="loading">
|
|
||||||
<p class="small-tip" v-if="item && item.ex3 == '1'">*当前模板为系统预设,不支持直接操作。需要复制一份为自己的然后再操作</p>
|
|
||||||
<el-form :model="form" label-width="auto">
|
|
||||||
<el-form-item label="名称">
|
|
||||||
<el-input v-model="form.name" />
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="提示词" v-if="item.ex3 == '1' ? false : true">
|
|
||||||
<el-input v-model="form.prompt" type="textarea" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<template #footer>
|
|
||||||
<div class="dialog-footer">
|
|
||||||
<el-button @click="mode = false">取消</el-button>
|
|
||||||
<el-button type="primary" @click="saveAdd">
|
|
||||||
确定
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, reactive, watch } from 'vue'
|
|
||||||
import { ElMessage } from 'element-plus'
|
|
||||||
import emitter from '@/utils/mitt';
|
|
||||||
import { addKeyWords, addChildTemp, editChildTemp } from '@/api/mode/index'
|
|
||||||
|
|
||||||
const mode = defineModel()
|
|
||||||
const props = defineProps({
|
|
||||||
modeType: {
|
|
||||||
type: Number,
|
|
||||||
default: 1
|
|
||||||
},
|
|
||||||
isAdd: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
item: { // 子模板
|
|
||||||
type: Object,
|
|
||||||
default: () => {
|
|
||||||
return { ex3: '' }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tempId: {
|
|
||||||
type: [String, Number],
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const form = reactive({
|
|
||||||
name: '',
|
|
||||||
prompt: '',
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(() => props.isAdd, (newVal) => {
|
|
||||||
if (!newVal) {
|
|
||||||
form.name = props.item?.name
|
|
||||||
form.prompt = props.item?.prompt
|
|
||||||
}
|
|
||||||
|
|
||||||
}, { immediate: false })
|
|
||||||
|
|
||||||
const loading = ref(false)
|
|
||||||
const saveAdd = async () => {
|
|
||||||
|
|
||||||
loading.value = true
|
|
||||||
if (props.item.ex3 == '1') {
|
|
||||||
if (props.isAdd) {
|
|
||||||
try {
|
|
||||||
// 系统预设模板 copy一份
|
|
||||||
const { msg } = await addKeyWords({ name: form.name, id: props.item.id })
|
|
||||||
emitter.emit('onGetChild')
|
|
||||||
ElMessage.success(msg)
|
|
||||||
mode.value = false
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
onAddChildTemp(props.item.parentId)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
|
|
||||||
if (props.isAdd) {
|
|
||||||
onAddChildTemp(props.item.id)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
try {
|
|
||||||
let data = JSON.parse(JSON.stringify(props.item))
|
|
||||||
data.name = form.name;
|
|
||||||
data.prompt = form.prompt
|
|
||||||
const { msg } = await editChildTemp(data)
|
|
||||||
emitter.emit('onGetChild')
|
|
||||||
ElMessage.success(msg)
|
|
||||||
mode.value = false
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加子模板
|
|
||||||
const onAddChildTemp = async (parentId) => {
|
|
||||||
// 添加子模板
|
|
||||||
let obj = {
|
|
||||||
name: form.name,
|
|
||||||
type: 2, // 子模板 固定值为2
|
|
||||||
sortNum: 1,
|
|
||||||
parentId,
|
|
||||||
lmType: 1,
|
|
||||||
model: props.modeType,
|
|
||||||
prompt: form.prompt,
|
|
||||||
ex1: props.item.ex1, //学段
|
|
||||||
ex2: props.item.ex2, // 学科
|
|
||||||
ex3: '', //是否系统预设 这里默认空
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
var { msg } = await addChildTemp(obj)
|
|
||||||
emitter.emit('onGetChild')
|
|
||||||
ElMessage.success(msg)
|
|
||||||
mode.value = false
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.custom-header {
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.icon-guanbi {
|
|
||||||
cursor: pointer;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.small-tip {
|
|
||||||
text-align: left;
|
|
||||||
font-size: 12px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
color: #F56C6C;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,32 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="container-pdf">
|
|
||||||
<PDF :url="pdfUrl" :showCatalog="false" v-if="pdfUrl" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, onMounted, nextTick } from 'vue';
|
|
||||||
import PDF from '@/components/PdfJs/index.vue'
|
|
||||||
import { sessionStore } from '@/utils/store'
|
|
||||||
|
|
||||||
const pdfUrl = ref('')
|
|
||||||
|
|
||||||
onMounted(async () =>{
|
|
||||||
await nextTick()
|
|
||||||
|
|
||||||
let data = sessionStore.get('subject.curBook')
|
|
||||||
let fileurl = data.fileurl
|
|
||||||
|
|
||||||
if(fileurl == ''){
|
|
||||||
fileurl = `${data.edustage}-${data.edusubject}-课标.txt`
|
|
||||||
}
|
|
||||||
pdfUrl.value = import.meta.env.VITE_APP_RES_FILE_PATH + fileurl.replace('.txt', '.pdf')
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.container-pdf{
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,513 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="read-container">
|
|
||||||
|
|
||||||
<el-scrollbar height="100%">
|
|
||||||
<div class="template-list">
|
|
||||||
<el-row v-for="(item, index) in childTempList">
|
|
||||||
<el-col :span="24">
|
|
||||||
<div class="template-item" v-loading="item.loading">
|
|
||||||
<div class="item-header">
|
|
||||||
<div>
|
|
||||||
<span class="blue">#</span>{{ item.name }}
|
|
||||||
</div>
|
|
||||||
<el-popover placement="bottom-end" trigger="hover" popper-class="template-custom-popover">
|
|
||||||
<template #reference>
|
|
||||||
<el-button link type="primary">
|
|
||||||
<i class="iconfont icon-shenglvehao"></i></el-button>
|
|
||||||
</template>
|
|
||||||
<template #default>
|
|
||||||
<el-button type="primary" link @click="editKeyWord(item)">编辑</el-button>
|
|
||||||
<el-button type="primary" link @click="removeItem(item)">移除</el-button>
|
|
||||||
</template>
|
|
||||||
</el-popover>
|
|
||||||
</div>
|
|
||||||
<div class="item-text" >
|
|
||||||
{{ item.prompt }}
|
|
||||||
</div>
|
|
||||||
<div class="item-text text-answer" v-if="item.answer">
|
|
||||||
<div class="item-icon">
|
|
||||||
<i class="iconfont icon-ai"></i>
|
|
||||||
</div>
|
|
||||||
<div class="item-answer" v-html="item.answer"></div>
|
|
||||||
</div>
|
|
||||||
<div class="ai-btn" v-if="item.answer">
|
|
||||||
<el-button type="primary" link @click="againResult(index, item)">
|
|
||||||
<i class="iconfont icon-ai1"></i>
|
|
||||||
重新研读
|
|
||||||
</el-button>
|
|
||||||
<el-button type="primary" link @click="onAdjust(index, item)">
|
|
||||||
<i class="iconfont icon-duihua"></i>
|
|
||||||
AI对话调整
|
|
||||||
</el-button>
|
|
||||||
<el-button type="primary" link @click="onEdit(index, item)">
|
|
||||||
<i class="iconfont icon-bianji1"></i>
|
|
||||||
手动编辑结果
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-empty v-if="!childTempList.length" description="暂无模板数据" />
|
|
||||||
</div>
|
|
||||||
</el-scrollbar>
|
|
||||||
<!--编辑结果-->
|
|
||||||
<EditDialog v-model="isEdit" :item="editItem" @saveEdit="saveEdit" />
|
|
||||||
<!--AI 对话调整-->
|
|
||||||
<AdjustDialog v-model="isAdjust" :modeType="modeType" :item="editItem" @saveAdjust="saveAdjust" />
|
|
||||||
<!--编辑提示词-->
|
|
||||||
<keywordDialog v-model="isEditKeyWord" :isAdd="isAdd" :item="keywordItem" :tempId="tempId" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, reactive, onMounted, watch, onUnmounted } from 'vue';
|
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
||||||
import EditDialog from './edit-dialog.vue'
|
|
||||||
import AdjustDialog from './adjust-dialog.vue'
|
|
||||||
import keywordDialog from './keyword-dialog.vue';
|
|
||||||
import { sessionStore } from '@/utils/store'
|
|
||||||
import useUserStore from '@/store/modules/user'
|
|
||||||
import { tempSave, completion, modelList, removeChildTemp, tempResult, editTempResult } from '@/api/mode/index'
|
|
||||||
import { dataSetJson } from '@/utils/comm.js'
|
|
||||||
import emitter from '@/utils/mitt';
|
|
||||||
|
|
||||||
const userStore = useUserStore()
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
tempId: {
|
|
||||||
type: [String, Number],
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
modeType: {
|
|
||||||
type: Number,
|
|
||||||
default: 1
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
emitter.on('onGetChild', () =>{
|
|
||||||
getChildTemplate()
|
|
||||||
})
|
|
||||||
|
|
||||||
// 获取子模板
|
|
||||||
const tempLoading = ref(false)
|
|
||||||
const childTempList = ref([])
|
|
||||||
const getChildTemplate = () => {
|
|
||||||
|
|
||||||
tempLoading.value = true
|
|
||||||
modelList({ model: props.modeType, type: 2, parentId: props.tempId }).then(res => {
|
|
||||||
childTempList.value = res.rows
|
|
||||||
getTempResult()
|
|
||||||
}).finally(() => {
|
|
||||||
tempLoading.value = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// 查询模板结果
|
|
||||||
const getTempResult = () => {
|
|
||||||
tempResult({ mainModelId: props.tempId }).then(res => {
|
|
||||||
let rows = res.rows
|
|
||||||
childTempList.value.forEach(item => {
|
|
||||||
rows.forEach(el => {
|
|
||||||
if (item.id == el.modelId) {
|
|
||||||
item.answer = el.content
|
|
||||||
item.resultId = el.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const isEdit = ref(false)
|
|
||||||
watch(() => props.tempId, (newVal) => {
|
|
||||||
if (newVal) {
|
|
||||||
// isEdit.value = true
|
|
||||||
getChildTemplate()
|
|
||||||
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
// 获取会话ID
|
|
||||||
const params = reactive(
|
|
||||||
{
|
|
||||||
prompt: '',
|
|
||||||
dataset_id: ''
|
|
||||||
}
|
|
||||||
)
|
|
||||||
const curNode = reactive({})
|
|
||||||
const getConversation = () => {
|
|
||||||
|
|
||||||
getCompletion()
|
|
||||||
}
|
|
||||||
// 大模型对话
|
|
||||||
const getCompletion = async () => {
|
|
||||||
for (let item of childTempList.value) {
|
|
||||||
try {
|
|
||||||
item.loading = true
|
|
||||||
params.prompt = `按照${item.prompt}的要求,针对${curNode.edustage}${curNode.edusubject}考试 对${curNode.itemtitle}进行教学分析`
|
|
||||||
const { data } = await completion(params)
|
|
||||||
let answer = data.answer
|
|
||||||
item.oldAnswer = answer
|
|
||||||
item.answer = getResult(answer);
|
|
||||||
onSaveTemp(item)
|
|
||||||
} finally {
|
|
||||||
item.loading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存模板
|
|
||||||
const onSaveTemp = (item) => {
|
|
||||||
const data = {
|
|
||||||
mainModelId: props.tempId,
|
|
||||||
modelId: item.id,
|
|
||||||
examDocld: '',
|
|
||||||
content: item.oldAnswer
|
|
||||||
}
|
|
||||||
tempSave(data).then(res => {
|
|
||||||
console.log(res)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 重新研读
|
|
||||||
const againResult = async (index, item) => {
|
|
||||||
try {
|
|
||||||
childTempList.value[index].loading = true
|
|
||||||
params.prompt = `按照${item.name}的要求,针对${curNode.edustage}${curNode.edusubject}考试 对${curNode.itemtitle}进行教学分析`
|
|
||||||
const { data } = await completion(params)
|
|
||||||
let answer = data.answer
|
|
||||||
childTempList.value[index].oldAnswer = answer
|
|
||||||
childTempList.value[index].answer = getResult(answer);
|
|
||||||
onEditSave(childTempList.value[index])
|
|
||||||
} finally {
|
|
||||||
childTempList.value[index].loading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存 重新研读后的结果
|
|
||||||
const onEditSave = async (item) =>{
|
|
||||||
const { res } = await editTempResult({id: item.resultId, content: item.oldAnswer})
|
|
||||||
ElMessage.success(res)
|
|
||||||
getChildTemplate()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 分析获取课标对话结果
|
|
||||||
let getResult = (text) => {
|
|
||||||
text = text.replace(/^\n\n(.*?)\n\n$/s, '<div>$1</div>');
|
|
||||||
text = text.replace(/^\n(.*?)\n$/s, '<p>$1</p>');
|
|
||||||
text = text.replace(/\*\*(.*?)\*\*/g, "<div class='text-tit'>$1</div>");
|
|
||||||
text = text.replace(/(\d+\..*?)\n/g, "<div class='text-num'>$1</div>\n");
|
|
||||||
return text
|
|
||||||
}
|
|
||||||
|
|
||||||
// 编辑
|
|
||||||
const curIndex = ref(-1)
|
|
||||||
const editItem = reactive({})
|
|
||||||
const onEdit = (index, item) => {
|
|
||||||
curIndex.value = index
|
|
||||||
Object.assign(editItem, item)
|
|
||||||
isEdit.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存编辑
|
|
||||||
const saveEdit = (data) => {
|
|
||||||
// childTempList.value[curIndex.value].oldAnswer = data
|
|
||||||
// let answer = getResult(data);
|
|
||||||
// childTempList.value[curIndex.value].answer = answer
|
|
||||||
getChildTemplate()
|
|
||||||
}
|
|
||||||
|
|
||||||
const isAdjust = ref(false)
|
|
||||||
const onAdjust = (index, item) => {
|
|
||||||
curIndex.value = index
|
|
||||||
Object.assign(editItem, item)
|
|
||||||
isAdjust.value = true
|
|
||||||
}
|
|
||||||
const saveAdjust = (item) => {
|
|
||||||
childTempList.value[curIndex.value].oldAnswer = item
|
|
||||||
let answer = getResult(item);
|
|
||||||
childTempList.value[curIndex.value].answer = answer
|
|
||||||
}
|
|
||||||
|
|
||||||
// 编辑提示词
|
|
||||||
const keywordItem = reactive({})
|
|
||||||
const isEditKeyWord = ref(false)
|
|
||||||
const isAdd = ref(true)
|
|
||||||
const editKeyWord = (item) => {
|
|
||||||
console.log(item)
|
|
||||||
isAdd.value = false
|
|
||||||
isEditKeyWord.value = true
|
|
||||||
Object.assign(keywordItem, item)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 移除子模板
|
|
||||||
const removeItem = async (item) => {
|
|
||||||
if (item.ex3 != '1') {
|
|
||||||
ElMessageBox.confirm(
|
|
||||||
'确认是否移除?',
|
|
||||||
'提示',
|
|
||||||
{
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
cancelButtonText: '取消',
|
|
||||||
type: 'warning',
|
|
||||||
}
|
|
||||||
).then(() => {
|
|
||||||
console.log(item)
|
|
||||||
removeChildTemp(item.id).then(res => {
|
|
||||||
ElMessage.success('操作成功')
|
|
||||||
getChildTemplate()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
isAdd.value = false
|
|
||||||
Object.assign(keywordItem, item)
|
|
||||||
isEditKeyWord.value = true
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// const { msg } = await removeChildTemp(item.id)
|
|
||||||
// ElMessage.success(msg)
|
|
||||||
// getChildTemplate()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
let data = sessionStore.get('subject.curNode')
|
|
||||||
Object.assign(curNode, data);
|
|
||||||
let text = props.modeType == 1 || props.modeType == 2? '课标' : '考试'
|
|
||||||
let jsonKey = `${text}-${data.edustage}-${data.edusubject}`
|
|
||||||
params.dataset_id = dataSetJson[jsonKey]
|
|
||||||
|
|
||||||
})
|
|
||||||
// 解绑
|
|
||||||
onUnmounted(() => {
|
|
||||||
emitter.off('onGetChild')
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
getConversation
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.read-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100%;
|
|
||||||
padding: 15px 0;
|
|
||||||
height: 100%;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.el-scrollbar {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-dropdown-link {
|
|
||||||
font-weight: bold;
|
|
||||||
|
|
||||||
.el-icon--right {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.read-header {
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.add-btn {
|
|
||||||
font-size: 13px;
|
|
||||||
|
|
||||||
.icon-jiahao {
|
|
||||||
margin-right: 3px;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-con {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.template-list {
|
|
||||||
|
|
||||||
.template-item {
|
|
||||||
background: #fff;
|
|
||||||
padding: 10px;
|
|
||||||
margin-top: 10px;
|
|
||||||
border-radius: 5px;
|
|
||||||
|
|
||||||
.item-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #000;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
.blue {
|
|
||||||
font-size: 22px;
|
|
||||||
color: #409eff;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-text {
|
|
||||||
display: flex;
|
|
||||||
margin-top: 10px;
|
|
||||||
font-size: 14px;
|
|
||||||
text-align: left;
|
|
||||||
color: #606266;
|
|
||||||
|
|
||||||
.item-icon {
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
line-height: 30px;
|
|
||||||
text-align: center;
|
|
||||||
background: #F6F6F6;
|
|
||||||
border-radius: 50%;
|
|
||||||
margin-right: 10px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-answer {
|
|
||||||
flex-direction: column;
|
|
||||||
padding-top: 5px;
|
|
||||||
|
|
||||||
:deep(.text-tit) {
|
|
||||||
font-weight: bold;
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.text-num) {
|
|
||||||
padding-left: 2em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-answer {
|
|
||||||
color: #409eff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ai-btn {
|
|
||||||
margin-top: 10px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
|
|
||||||
.iconfont {
|
|
||||||
margin-right: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.el-button) {
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-ai1 {
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.template-item-result {
|
|
||||||
background: #DDEAFD !important;
|
|
||||||
|
|
||||||
.result-item-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
text-align: left;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #3D3D3D;
|
|
||||||
|
|
||||||
.icon-xiaoxi {
|
|
||||||
color: #5881D5;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 20px;
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.result-icon-btn {
|
|
||||||
justify-content: space-between;
|
|
||||||
font-size: 13px;
|
|
||||||
margin-top: 5px;
|
|
||||||
|
|
||||||
span {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
margin-right: 10px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: #cfe0fa
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.iconfont {
|
|
||||||
margin-right: 3px;
|
|
||||||
color: #3498fc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.line {
|
|
||||||
height: 1px;
|
|
||||||
background: #D8D8D8;
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.other-msg {
|
|
||||||
font-size: 13px;
|
|
||||||
|
|
||||||
.other-user {
|
|
||||||
align-items: center;
|
|
||||||
color: #BA4B0F;
|
|
||||||
font-size: 12px;
|
|
||||||
|
|
||||||
.icon-touxiang {
|
|
||||||
color: #BA4B0F;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 16px;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.other-text {
|
|
||||||
color: #191919;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-shenglvehao {
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.el-popover) {
|
|
||||||
min-width: 50px;
|
|
||||||
width: 50px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pl-25 {
|
|
||||||
padding-left: 25px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<style>
|
|
||||||
.template-custom-popover {
|
|
||||||
width: 110px !important;
|
|
||||||
min-width: 110px !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,52 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="page-template flex">
|
<TemplateStudy :type="3"/>
|
||||||
<!--头部-->
|
|
||||||
<Header :type="type" @changeTemp="changeTemp" @onRead="onRead"/>
|
|
||||||
<el-row :gutter="20" class="tempalte-main">
|
|
||||||
<el-col :span="12">
|
|
||||||
<!--左侧pdf-->
|
|
||||||
<Pdf />
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<!--右侧模板研读-->
|
|
||||||
<Result ref="resultRef" :modeType="type" :tempId="tempId"/>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue'
|
import TemplateStudy from '@/components/template-study/index.vue'
|
||||||
import Header from './container/header.vue'
|
|
||||||
import Pdf from './container/pdf.vue'
|
|
||||||
import Result from './container/result.vue'
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
type: {
|
|
||||||
type: Number,
|
|
||||||
default: 3
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
const resultRef = ref()
|
|
||||||
const tempId = ref('')
|
|
||||||
const changeTemp = (id) =>{
|
|
||||||
tempId.value = id
|
|
||||||
}
|
|
||||||
|
|
||||||
const onRead = () =>{
|
|
||||||
resultRef.value.getConversation()
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.page-template {
|
|
||||||
flex-direction: column;
|
|
||||||
height: 100%;
|
|
||||||
.tempalte-main {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
|
@ -1,388 +1,461 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="page-resource flex mb-4">
|
<el-row class="model-wrap">
|
||||||
<!-- 左侧 教材 目录 -->
|
|
||||||
<!-- <ChooseTextbook @change-book="getData" @node-click="getData" /> -->
|
|
||||||
|
|
||||||
<div class="page-right">
|
|
||||||
<div class="button-container">
|
|
||||||
<el-button type="primary" @click="onchange('/model/curriculum')">课标研读</el-button>
|
|
||||||
<!-- <el-button type="primary" @click="onchange('/model/management')">作业管理1</el-button> -->
|
|
||||||
<el-button type="primary" @click="onchange('/model/newClassTaskAssign')">作业管理</el-button>
|
|
||||||
<el-button type="success" @click="onchange('/model/teaching')">教材研读</el-button>
|
|
||||||
<el-button type="info" @click="onchange('/model/design')">教学框架设计</el-button>
|
|
||||||
<!-- <el-button type="success" @click="openPPTist">打开PPTist</el-button>-->
|
|
||||||
<el-button type="info" @click="onchange('/model/examination')">考试分析</el-button>
|
|
||||||
<el-button type="primary" v-menus="dt.menus">测试</el-button>
|
|
||||||
<el-button type="success" @click="onchange('/model/aiKolors')">文生图片</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<el-row class="container">
|
|
||||||
<!-- 左侧 选择教材 目录 -->
|
<!-- 左侧 选择教材 目录 -->
|
||||||
<ChooseTextbook @change-book="changeBook" @node-click="changeBook" />
|
<ChooseTextbook @change-book="changeBook" @node-click="changeBook" />
|
||||||
<!-- 中间 展示内容 -->
|
<!-- 右侧 展示内容 -->
|
||||||
<el-col :span="10">
|
<div class="right-content">
|
||||||
<div class="c-item mb-4 mx-4">
|
<div class="content-header-wrap">
|
||||||
<div class="flex justify-between pb-2">
|
<div v-for="(item,index) in tags" :key="index" :style="{'background-color':item.bgcolor}" @click="onchange(item)">
|
||||||
<h3>教师资源</h3>
|
<el-icon class="item-icon"><Flag /></el-icon>
|
||||||
<span class="c-btns">
|
<div class="content-header-title">{{item.name}}</div>
|
||||||
<template v-for="item in resourBtns">
|
<div class="content-header-body">
|
||||||
<el-button :size="item.size" text :icon="item.icon" @click="handleAll(item.prop)">{{ item.name }}</el-button>
|
<div class="content-header-num">6</div>
|
||||||
</template>
|
<div class="content-header-text">分析结果</div>
|
||||||
</span>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<c-table ref="resourRef" v-bind="sourceOpt" t-class="rounded">
|
|
||||||
<template #title="{row,value}">
|
|
||||||
<el-link :underline="false" @click="handleAll('open', row)">
|
|
||||||
<svg class="icon svg-icon" aria-hidden="true">
|
|
||||||
<use :xlink:href="`#icon-${getIcon(row)}`"></use>
|
|
||||||
</svg>
|
|
||||||
<b class="ml-1">{{ value }}</b>
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</c-table>
|
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
<div class="content-body-wrap">
|
||||||
|
<div class="content-body-left">
|
||||||
|
<div class="content-body-left-title">
|
||||||
|
文枢课件
|
||||||
|
<el-button class="add-btn" size="small" type="primary" @click="createAIPPT">新建</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="content-body-left-body">
|
||||||
|
<kj-list-item
|
||||||
|
v-for="(item, index) in currentFileList"
|
||||||
|
:key="index"
|
||||||
|
:ref="collectRef('kjItemRef'+item.id)"
|
||||||
|
:item="item"
|
||||||
|
:show-tool="false"
|
||||||
|
:index="index"
|
||||||
|
:curNode="currentNode"
|
||||||
|
@on-delete="deleteTalk"
|
||||||
|
@change="changeClass"
|
||||||
|
>
|
||||||
|
</kj-list-item>
|
||||||
|
<!-- <div class="content-body-left-item" v-for="item in 5">
|
||||||
|
<div class="content-body-left-item-img">
|
||||||
|
<FileImage :size="50" :file-name="'aaa.aippt'" />
|
||||||
|
</div>
|
||||||
|
<div class="content-body-left-item-text">
|
||||||
|
<div class="content-body-left-item-title">沁园春*长沙</div>
|
||||||
|
<div class="content-body-left-item-info">21页 访问 100 引用 50 点赞 20 更新时间 2022-01-01 15:20</div>
|
||||||
|
</div>
|
||||||
|
<div></div>
|
||||||
|
</div>-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content-body-right">
|
||||||
|
<div class="content-body-right-title">模型辅助工具</div>
|
||||||
|
<div class="content-body-right-body">
|
||||||
|
<div class="content-body-right-item" v-for="(item,index) in tools" :key="index" @click="gotoRoute(item)">
|
||||||
|
<div class="content-body-right-item-img">
|
||||||
|
<FileImage :fileName="item.img"/>
|
||||||
|
</div>
|
||||||
|
<div class="content-body-right-item-text">{{item.name}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</el-row>
|
</el-row>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref, watch, reactive } from 'vue'
|
import { onMounted, reactive, ref, nextTick } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { Plus, Refresh, Upload, Files, UploadFilled } from '@element-plus/icons-vue'
|
|
||||||
import useUserStore from '@/store/modules/user' // 用户信息
|
import useUserStore from '@/store/modules/user' // 用户信息
|
||||||
import msgUtils from '@/plugins/modal' // 消息工具
|
|
||||||
import { createWindow } from '@/utils/tool' // 相关工具
|
|
||||||
import * as API_smarttalk from '@/api/file' // 文件相关api
|
|
||||||
import * as API_entpcourse from '@/api/education/entpcourse' // 相关api
|
|
||||||
import * as API_entpcoursefile from '@/api/education/entpcoursefile' // 相关api
|
|
||||||
import { dataSetJson } from '@/utils/comm' // 数据集id文生图
|
|
||||||
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 KjListItem from '@/views/prepare/container/kj-list-item.vue'
|
||||||
|
import FileImage from '@/components/file-image/index.vue'
|
||||||
|
import {creatAPT, getSmarttalkPage} from '@/api/file'
|
||||||
|
import {Flag, Position} from '@element-plus/icons-vue'
|
||||||
|
import {asyncLocalFile, parseCataByNode} from "@/utils/talkFile";
|
||||||
|
import { dataSetJson } from '@/utils/comm' // 数据集id文生图
|
||||||
|
import { sessionStore } from '@/utils/store'
|
||||||
|
import {listEntpcourse} from "@/api/teaching/classwork";
|
||||||
|
import {addEntpcoursefileReturnId, getEntpcoursefile} from "@/api/education/entpcoursefile";
|
||||||
|
import {createWindow, ipcMsgSend} from "@/utils/tool";
|
||||||
|
import {ElMessage} from "element-plus"; // 学科名字文生图
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const userStore = useUserStore() // 用户信息
|
const userStore = useUserStore().user // 用户信息
|
||||||
|
const currentNode = ref({})
|
||||||
|
const refs = ref([]);
|
||||||
|
|
||||||
const courseObj = reactive({
|
const collectRef = (key) => {
|
||||||
// 课程相关参数: 教材id,单元id,章节id,课程名称
|
return (el) => {
|
||||||
textbookId: '',
|
refs.value[key] = el;
|
||||||
levelFirstId: '',
|
};
|
||||||
levelSecondId: '',
|
};
|
||||||
coursetitle: '',
|
|
||||||
node: null, // 选择的课程节点
|
|
||||||
entp: null, // 当前课程
|
|
||||||
})
|
|
||||||
const dt = reactive({
|
|
||||||
curRow: null, // 当前行数据
|
|
||||||
menus: [ // 右键菜单
|
|
||||||
{ label: '打开', click: (_, args) => handleAll('open', args) },
|
|
||||||
{ label: '删除', click: (_, args) => handleAll('delete', args) },
|
|
||||||
],
|
|
||||||
})
|
|
||||||
// ref定义
|
|
||||||
const resourRef = ref() // 资源ref
|
|
||||||
// 资源按钮配置
|
|
||||||
const resourBtns = [
|
|
||||||
{ name: '刷新', prop: 'refresh', size: 'small', icon: Refresh },
|
|
||||||
{ name: '资源库', prop:'resource', size:'small', icon: Files },
|
|
||||||
{ name: '上传', prop:'upload', size:'small', icon: UploadFilled },
|
|
||||||
{ name: '添加', prop:'add', size:'small', icon: Plus },
|
|
||||||
]
|
|
||||||
// 资源相关配置-cTable
|
|
||||||
const sourceOpt = reactive({
|
|
||||||
data: [], // 数据
|
|
||||||
option: [ // 列配置
|
|
||||||
{ label: '名称', prop: 'title', align: 'left' },
|
|
||||||
{ label: '类型', prop: 'filetype', width: 80, },
|
|
||||||
{ label: '时间', prop: 'timestamp', width: 160, sortable: true },
|
|
||||||
],
|
|
||||||
noPage: true, // 不显示分页
|
|
||||||
isMain: false, // 主界面
|
|
||||||
highlightCurrentRow: true, // 高亮当前行
|
|
||||||
rowClick: (r, c, e) => { // 行点击事件-处理高亮(再次点击取消选中)
|
|
||||||
if (dt.curRow == r) { // 重复点击-取消选中
|
|
||||||
resourRef.value.$refs.table.setCurrentRow()
|
|
||||||
dt.curRow = null
|
|
||||||
} else dt.curRow = r
|
|
||||||
},
|
|
||||||
rowContextmenu: (r, c, e) => { // 行—右键菜单事件
|
|
||||||
dt.menus.forEach(item => {
|
|
||||||
if(item.label == '打开') item.icon = getIcon(r, 'svg')
|
|
||||||
else if(item.label == '删除') item.icon = getIcon('icon-shanchu', 'class')
|
|
||||||
})
|
|
||||||
menusEvent(e, dt.menus, r)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
|
const tags = reactive([{
|
||||||
|
name: '课标分析',
|
||||||
|
path: '/model/curriculum',
|
||||||
|
bgcolor: 'rgb(241,65,108)'
|
||||||
|
},{
|
||||||
|
name: '教材分析',
|
||||||
|
path: '/model/teaching',
|
||||||
|
bgcolor: 'rgb(114,57,234)'
|
||||||
|
},{
|
||||||
|
name: '考试分析',
|
||||||
|
path: '/model/examination',
|
||||||
|
bgcolor: 'rgb(251,132,4)'
|
||||||
|
},{
|
||||||
|
name: '素材设计',
|
||||||
|
path: '/model/aiKolors',
|
||||||
|
bgcolor: 'rgb(25,123,237)'
|
||||||
|
},{
|
||||||
|
name: '作业设计',
|
||||||
|
path: '/model/newClassTaskAssign',
|
||||||
|
bgcolor: 'rgb(23,198,83)'
|
||||||
|
},{
|
||||||
|
name: '框架设计',
|
||||||
|
path: '/model/design',
|
||||||
|
bgcolor: 'rgb(34,35,43)'
|
||||||
|
}])
|
||||||
|
|
||||||
|
const tools = reactive([{
|
||||||
|
name: '数字人生成',
|
||||||
|
path: '',
|
||||||
|
img: 'airobot'
|
||||||
|
},{
|
||||||
|
name: '语音生成',
|
||||||
|
path: '',
|
||||||
|
img: 'aiyuyin'
|
||||||
|
},{
|
||||||
|
name: '文生图片',
|
||||||
|
path: '/model/aiKolors',
|
||||||
|
img: 'aiimg'
|
||||||
|
},{
|
||||||
|
name: '文生连环画',
|
||||||
|
path: '',
|
||||||
|
img: 'aidraw'
|
||||||
|
},{
|
||||||
|
name: '视频生成',
|
||||||
|
path: '',
|
||||||
|
img: 'aivideo'
|
||||||
|
}])
|
||||||
|
|
||||||
|
const uploadData = ref({
|
||||||
|
textbookId: null,
|
||||||
|
levelFirstId: null,
|
||||||
|
levelSecondId: null,
|
||||||
|
fileSource: '个人',
|
||||||
|
fileRoot: '备课'
|
||||||
|
})
|
||||||
|
const currentFileList = ref([])
|
||||||
// 页面加载
|
// 页面加载
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
})
|
})
|
||||||
|
|
||||||
// 相关方法-methods
|
const gotoRoute = (item) => {
|
||||||
|
if (item.path) {
|
||||||
|
if (item.path === '/model/aiKolors') {
|
||||||
|
gotoAiKolors(item.path)
|
||||||
|
}else {
|
||||||
|
router.push(item.path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const createAIPPT = () => {
|
||||||
|
console.log(userStore)
|
||||||
|
listEntpcourse({
|
||||||
|
evalid: currentNode.value.id,
|
||||||
|
edituserid: userStore.userId,
|
||||||
|
pageSize: 500
|
||||||
|
}).then((response) => {
|
||||||
|
if (response.rows.length <= 0) return
|
||||||
|
let resCourse = response.rows[0]
|
||||||
|
// 添加
|
||||||
|
let form = {
|
||||||
|
parentid: 0,
|
||||||
|
entpid: userStore.deptId,
|
||||||
|
entpcourseid: resCourse.id,
|
||||||
|
ppttype: 'file',
|
||||||
|
title: resCourse.coursetitle,
|
||||||
|
fileurl: '',
|
||||||
|
filetype: 'aippt',
|
||||||
|
datacontent: '',
|
||||||
|
filekey: '',
|
||||||
|
filetag: '',
|
||||||
|
fileidx: 0,
|
||||||
|
dflag: 0,
|
||||||
|
status: '',
|
||||||
|
edituserid: userStore.userId
|
||||||
|
}
|
||||||
|
addEntpcoursefileReturnId(form).then((slideid) => {
|
||||||
|
// 添加
|
||||||
|
var form = {
|
||||||
|
parentid: slideid,
|
||||||
|
entpid: resCourse.entpid,
|
||||||
|
entpcourseid: resCourse.id,
|
||||||
|
title: '第一页',
|
||||||
|
filetype: 'slide',
|
||||||
|
datacontent: '{"elements":[],"background":{"type":"solid","color":"#fff"}}',
|
||||||
|
edituserid: userStore.userId
|
||||||
|
}
|
||||||
|
addEntpcoursefileReturnId(form).then((res) => {
|
||||||
|
creatAPT({
|
||||||
|
...uploadData.value,
|
||||||
|
fileId: slideid,
|
||||||
|
fileFlag: 'aippt',
|
||||||
|
fileShowName: currentNode.value.itemtitle + '.aippt'
|
||||||
|
}).then(async (res) => {
|
||||||
|
currentFileList.value.unshift(res.resData)
|
||||||
|
await nextTick();
|
||||||
|
refs.value['kjItemRef'+res.resData.id].openFileWin(res.resData);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteTalk = (item) => {
|
||||||
|
let index = currentFileList.value.indexOf(item)
|
||||||
|
currentFileList.value.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
// 教材选中
|
// 教材选中
|
||||||
const changeBook = async(data) => {
|
const changeBook = async(data) => {
|
||||||
// console.log(data)
|
console.log(data)
|
||||||
const { textBook, node } = data
|
let cata = parseCataByNode(data.node)
|
||||||
let textbookId = textBook.curBookId
|
currentNode.value = data.node
|
||||||
let levelSecondId = node.id
|
uploadData.value.levelFirstId = cata[0]
|
||||||
let levelFirstId
|
uploadData.value.levelSecondId = cata[1]
|
||||||
if (node.parentNode) {
|
uploadData.value.levelThirdId = cata[2]
|
||||||
levelFirstId = node.parentNode.id
|
uploadData.value.textbookId = data.textBook.curBookId
|
||||||
} else {
|
getSmarttalkPage({
|
||||||
levelFirstId = node.id
|
...uploadData.value,
|
||||||
levelSecondId = ''
|
orderByColumn: 'createTime',
|
||||||
}
|
fileFlags: "'aippt'",
|
||||||
|
isAsc: 'desc',
|
||||||
courseObj.textbookId = textbookId // 版本
|
pageSize: 500
|
||||||
courseObj.levelFirstId = levelFirstId // 单元
|
}).then((res) => {
|
||||||
courseObj.levelSecondId = levelSecondId // 章节
|
currentFileList.value = [...res.rows]
|
||||||
courseObj.coursetitle = node.itemtitle // (单元/章节) 名称
|
})
|
||||||
courseObj.node = node; // 保存当前节点
|
|
||||||
|
|
||||||
// 头部 教材分析打开外部链接需要当前章节ID
|
|
||||||
localStorage.setItem('unitId', JSON.stringify({ levelFirstId, levelSecondId }))
|
|
||||||
// 获取当前章节对应的课程信息
|
|
||||||
const params = { evalid: node.id, edituserid: userStore.id, pageSize: 1 }
|
|
||||||
const res = await HTTP_SERVER_API('getCourseList', params)
|
|
||||||
courseObj.entp = res?.rows?.[0] || null
|
|
||||||
sessionStore.set('curr.entp', courseObj.entp) // 缓存当前课程信息
|
|
||||||
// 更新-资源列表
|
|
||||||
getResourceList()
|
|
||||||
}
|
}
|
||||||
|
const onchange = (item) => {
|
||||||
const openPPTist = () => {
|
let path = item.path
|
||||||
createWindow('open-win', { url: '/pptist' })
|
|
||||||
}
|
|
||||||
|
|
||||||
const onchange = (path) => {
|
|
||||||
if (path == '/model/newClassTaskAssign') {
|
if (path == '/model/newClassTaskAssign') {
|
||||||
|
uploadData.value.coursetitle = currentNode.value.itemtitle // (单元/章节) 名称
|
||||||
|
uploadData.value.node = currentNode.value; // 保存当前节点
|
||||||
// 作业管理
|
// 作业管理
|
||||||
router.push({ path, query: { courseObj: JSON.stringify(courseObj) } })
|
router.push({ path, query: { courseObj: JSON.stringify(uploadData.value) } })
|
||||||
} else if (path == '/model/aiKolors') {
|
} else if (path == '/model/aiKolors') {
|
||||||
// ai生图
|
gotoAiKolors(path)
|
||||||
let subjectdata = sessionStore.get('subject.curNode')
|
|
||||||
let datasubject = `课标-${subjectdata.edustage}-${subjectdata.edusubject}`
|
|
||||||
console.log(subjectdata)
|
|
||||||
router.push({
|
|
||||||
path,
|
|
||||||
query: {
|
|
||||||
datasetId: dataSetJson[datasubject],
|
|
||||||
coursetitle: courseObj.coursetitle,
|
|
||||||
levelFirstId: subjectdata.parentid,
|
|
||||||
levelSecondId: subjectdata.id,
|
|
||||||
textbookId: subjectdata.rootid,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
router.push(path)
|
router.push(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 获取资源列表
|
|
||||||
const getResourceList = async () => {
|
const gotoAiKolors = (path)=> {
|
||||||
const entpcourseidarray = courseObj?.entp?.id
|
// ai生图
|
||||||
if (!entpcourseidarray) return msgUtils.msgWarning('请选择章节?')
|
let subjectdata = sessionStore.get('subject.curNode')
|
||||||
const params = {
|
let datasubject = `课标-${subjectdata.edustage}-${subjectdata.edusubject}`
|
||||||
pageSize: 100, parentid: 0, entpcourseidarray,
|
console.log(subjectdata)
|
||||||
orderByColumn: 'timestamp', isAsc: 'desc',
|
router.push({
|
||||||
}
|
path,
|
||||||
const res = await HTTP_SERVER_API('getCourseFileList', params)
|
query: {
|
||||||
if (res?.code == 200) {
|
datasetId: dataSetJson[datasubject],
|
||||||
sourceOpt.data = res?.rows || []
|
coursetitle: currentNode.value.itemtitle,
|
||||||
} else {
|
levelFirstId: subjectdata.parentid,
|
||||||
msgUtils.msgWarning('获取资源列表, 请重试')
|
levelSecondId: subjectdata.id,
|
||||||
}
|
textbookId: subjectdata.rootid,
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
// 统一HTTP处理
|
|
||||||
const HTTP_SERVER_API = (type, params = {}) => {
|
const changeClass = async (type, row, other) => {
|
||||||
switch (type) {
|
switch(type) {
|
||||||
case 'addSmarttalk': { // 获取课程
|
case 'click': { // 点击-打开课件-aippt
|
||||||
const def = {
|
if (row.fileFlag === 'aippt' && !!row.fileId) {
|
||||||
fileId: '', // 文件id - Entpcoursefile 对应id
|
const res = await getEntpcoursefile(row.fileId)
|
||||||
fileFlag: 'aptist',
|
if (res && res.code === 200) {
|
||||||
fileShowName: courseObj.coursetitle + '.aptist',
|
sessionStore.set('curr.resource', res.data) // 缓存当前资源信息
|
||||||
textbookId: courseObj.textbookId,
|
sessionStore.set('curr.smarttalk', row) // 缓存当前文件smarttalk
|
||||||
levelFirstId: courseObj.levelFirstId,
|
createWindow('open-win', {
|
||||||
levelSecondId: courseObj.levelSecondId,
|
url: '/pptist', // 窗口关闭时,清除缓存
|
||||||
fileSource: '个人',
|
close: () => {
|
||||||
fileRoot: '备课'
|
sessionStore.set('curr.resource', null) // 清除缓存
|
||||||
}
|
sessionStore.set('curr.smarttalk', null) // 清除缓存
|
||||||
return API_smarttalk.creatAPT({...def, ...params})
|
getSmarttalkPage({
|
||||||
}
|
...uploadData.value,
|
||||||
case 'addEntpcourse': { // 添加课程
|
orderByColumn: 'createTime',
|
||||||
const node = courseObj.node || {}
|
fileFlags: "'aippt'",
|
||||||
if (!node) return msgUtils.msgWarning('请选择章节?')
|
isAsc: 'desc',
|
||||||
const def = { // 默认参数
|
pageSize: 500
|
||||||
entpid: userStore.user.deptId, // 部门id
|
}).then((res) => {
|
||||||
level: 1, // 层级
|
currentFileList.value = [...res.rows]
|
||||||
parentid: 0, // 父级id
|
})
|
||||||
dictid: 0, // 字典id
|
}
|
||||||
evalid: node.id, // 章节id
|
})
|
||||||
evalparentid: node.parentid, // 单元id(父级id)
|
} else {
|
||||||
edusubject: node.edusubject, // 学科
|
ElMessage.warning(res.msg||'文件获取异常!')
|
||||||
edudegree: node.edudegree, // 年级
|
|
||||||
edustage: node.edustage, // 阶段
|
|
||||||
coursetype: '课标学科', // 课程类型
|
|
||||||
coursetitle: node.itemtitle, // 课程名称
|
|
||||||
coursedesc: '', // 课程描述
|
|
||||||
status: '', // 状态
|
|
||||||
dflag: 0, // 状态
|
|
||||||
edituserid: userStore.id, // 编辑人id
|
|
||||||
createblankfile: 'no', // 创建空白文件
|
|
||||||
}
|
|
||||||
courseObj.entp = def
|
|
||||||
return API_entpcourse.addEntpcourse(def)
|
|
||||||
}
|
|
||||||
case 'addEntpcoursefile': { // 添加课程文件
|
|
||||||
const enpt = courseObj.entp
|
|
||||||
const def = {
|
|
||||||
parentid: 0,
|
|
||||||
entpid: userStore.user.deptId,
|
|
||||||
entpcourseid: enpt.id,
|
|
||||||
ppttype: 'file',
|
|
||||||
title: enpt.coursetitle,
|
|
||||||
fileurl: '',
|
|
||||||
filetype: 'aippt',
|
|
||||||
datacontent: '',
|
|
||||||
filekey: '',
|
|
||||||
filetag: '',
|
|
||||||
fileidx: 0,
|
|
||||||
dflag: 0,
|
|
||||||
status: '',
|
|
||||||
edituserid: userStore.id
|
|
||||||
}
|
|
||||||
// return Promise.resolve(1)
|
|
||||||
return API_entpcoursefile.addEntpcoursefileReturnId({...def,...params})
|
|
||||||
}
|
|
||||||
case 'getCourseList': { // 获取课程列表
|
|
||||||
return API_entpcourse.listEntpcourse(params)
|
|
||||||
}
|
|
||||||
case 'getCourseFileList':{ // 获取课程文件列表
|
|
||||||
return API_entpcoursefile.listEntpcoursefileNew(params)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 事件回调
|
|
||||||
const handleAll = async(type, row) =>{
|
|
||||||
// console.log(type)
|
|
||||||
switch (type) {
|
|
||||||
case 'refresh': // 刷新
|
|
||||||
getResourceList()
|
|
||||||
break;
|
|
||||||
case 'resource': // 资源库
|
|
||||||
break;
|
|
||||||
case 'upload': // 上传
|
|
||||||
break;
|
|
||||||
case 'add':{ // 添加PPT-list - 课程文件
|
|
||||||
const enpt = courseObj.entp // 获取课程信息
|
|
||||||
if (!enpt) { // 如果没有,就新增课程
|
|
||||||
const resid = await HTTP_SERVER_API('addEntpcourse')
|
|
||||||
courseObj.entp.id = resid
|
|
||||||
}
|
|
||||||
// 生成ppt课件-父级
|
|
||||||
const p_params = {parentContent: '{"width":1000,"ratio":0.5625}'}
|
|
||||||
const id = await HTTP_SERVER_API('addEntpcoursefile', p_params)
|
|
||||||
if (!!id??null) { // 生成第一个幻灯片
|
|
||||||
const params = {
|
|
||||||
parentid: id,
|
|
||||||
title: '第一页',
|
|
||||||
filetype: 'slide',
|
|
||||||
datacontent: '{"elements":[],"background":{"type":"solid","color":"#fff"}}' // json内容
|
|
||||||
}
|
}
|
||||||
// 生成ppt课件-子级(slide)
|
return
|
||||||
await HTTP_SERVER_API('addEntpcoursefile', params)
|
|
||||||
// 生成备课资源-Smarttalk
|
|
||||||
await HTTP_SERVER_API('addSmarttalk',{fileId: id})
|
|
||||||
// 刷新资源列表
|
|
||||||
await getResourceList()
|
|
||||||
} else {
|
|
||||||
msgUtils.msgWarning('添加失败!')
|
|
||||||
}
|
}
|
||||||
break;
|
ElMessage.warning('该功能暂未开放!')
|
||||||
}
|
|
||||||
case 'open': { // 打开资源-pptist
|
|
||||||
if (row.filetype != 'aippt') return msgUtils.msgWarning('暂不支持该类型文件操作!')
|
|
||||||
sessionStore.set('curr.resource', row) // 缓存当前资源信息
|
|
||||||
createWindow('open-win', {
|
|
||||||
url: '/pptist', // 窗口关闭时,清除缓存
|
|
||||||
close: () => {
|
|
||||||
sessionStore.set('curr.resource', null) // 清除缓存
|
|
||||||
getResourceList() // 刷新资源列表
|
|
||||||
}
|
|
||||||
})
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'delete':{ // 删除资源
|
default:
|
||||||
if (!(row && row.id)) return msgUtils.msgWarning('请选择要删除的资源!')
|
break
|
||||||
await msgUtils.confirm(`是否确认删除【${row.title}】课程课件?`)
|
|
||||||
await API_entpcoursefile.delEntpcoursefileNew(row.id)
|
|
||||||
msgUtils.msgSuccess('删除成功!')
|
|
||||||
// 刷新资源列表
|
|
||||||
await getResourceList()
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// icons 处理 type 代表传递svg
|
|
||||||
const getIcon = (o, type) => {
|
|
||||||
let icon = typeof o == 'string' ? o : o?.filetype
|
|
||||||
if (['aippt'].includes(o?.filetype)) icon = 'pptx'
|
|
||||||
if (!!type) { // 其他格式icon
|
|
||||||
switch(type) {
|
|
||||||
case 'svg': // 返回svg格式
|
|
||||||
return `<svg class="icon svg-icon" aria-hidden="true">
|
|
||||||
<use xlink:href="#icon-${icon}"></use>
|
|
||||||
</svg>`
|
|
||||||
case 'class': // class
|
|
||||||
return `<span class="icon iconfont ${icon}"></span>`
|
|
||||||
case 'unicode': // unicode
|
|
||||||
return `<span class="icon iconfont">${icon}</span>`
|
|
||||||
default: // 返回icon-class
|
|
||||||
return `icon-${icon}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return icon
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.page-resource {
|
.model-wrap{
|
||||||
// height: 100%;
|
width: 100%;
|
||||||
// padding: 10px 15px 0;
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
.page-right {
|
flex-direction: row;
|
||||||
min-width: 0;
|
.right-content{
|
||||||
|
flex: 1;
|
||||||
|
margin-left: 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex: 1;
|
|
||||||
// margin-left: 20px;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: #ffffff;
|
.content-header-wrap{
|
||||||
border-radius: 10px;
|
display: flex;
|
||||||
box-shadow: 0px 0px 20px 0px rgba(99, 99, 99, 0.06);
|
flex-direction: row;
|
||||||
}
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
.button-container {
|
width: 100%;
|
||||||
display: flex;
|
//height: 215px;
|
||||||
flex-wrap: wrap;
|
&>div{
|
||||||
gap: 10px;
|
&:hover{
|
||||||
margin: 1rem;
|
cursor: pointer;
|
||||||
justify-content: flex-start;
|
}
|
||||||
|
width: calc(100%/6 - 10px);
|
||||||
.el-button {
|
aspect-ratio: 0.718 / 1;
|
||||||
flex: 1 1 15%;
|
border-radius: 10px;
|
||||||
max-width: 15%;
|
color: white;
|
||||||
min-width: 15%;
|
text-align: left;
|
||||||
box-sizing: border-box;
|
background-color: #999999;
|
||||||
|
position: relative;
|
||||||
|
.item-icon{
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
left: 20px;
|
||||||
|
font-size: calc(1.8vw);
|
||||||
|
}
|
||||||
|
.content-header-title{
|
||||||
|
position: absolute;
|
||||||
|
bottom: 50%;
|
||||||
|
width: 100%;
|
||||||
|
font-size: calc(1.8vw);
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
.content-header-body{
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0.27);
|
||||||
|
width: 100%;
|
||||||
|
height: 25%;
|
||||||
|
padding-left: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: calc(1vw);
|
||||||
|
border-radius: 0 0 10px 10px;
|
||||||
|
.content-header-num{
|
||||||
|
}
|
||||||
|
.content-header-text{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
.content-body-wrap{
|
||||||
}
|
display: flex;
|
||||||
.container{
|
flex-direction: row;
|
||||||
height: calc(100% - 32px - 3rem);
|
justify-content: space-between;
|
||||||
.c-item{
|
margin-top: 20px;
|
||||||
.c-btns{
|
flex: 1;
|
||||||
.el-button{margin: 0;}
|
overflow: auto;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 10px;
|
||||||
|
.content-body-left{
|
||||||
|
width: calc(50% - 7px);
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
.content-body-left-title{
|
||||||
|
text-align: left;
|
||||||
|
padding: 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
position: relative;
|
||||||
|
height: 40px;
|
||||||
|
.add-btn{
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.content-body-left-body{
|
||||||
|
flex: 1;
|
||||||
|
overflow: auto;
|
||||||
|
.content-body-left-item{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
border-bottom: 1px solid rgba(153, 153, 153, 0.29);
|
||||||
|
padding: 10px 0;
|
||||||
|
.content-body-left-item-img{
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
.content-body-left-item-text{
|
||||||
|
text-align: left;
|
||||||
|
.content-body-left-item-title{
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.content-body-left-item-info{
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.content-body-right{
|
||||||
|
width: calc(50% - 8px);
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 10px;
|
||||||
|
.content-body-right-title{
|
||||||
|
text-align: left;
|
||||||
|
padding: 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.content-body-right-body{
|
||||||
|
display: flex;
|
||||||
|
.content-body-right-item{
|
||||||
|
&:hover{
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
height: 80px;
|
||||||
|
width: 100px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
.content-body-right-item-img{
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
border-radius: 25px;
|
||||||
|
border: 1px solid rgba(153, 153, 153, 0.18);
|
||||||
|
img{
|
||||||
|
border-radius: 25px;
|
||||||
|
width: 25px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.content-body-right-item-text{
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,7 +87,8 @@ import { onMounted, reactive, ref, watchEffect, watch, nextTick, toRaw } from 'v
|
||||||
import { Refresh } from '@element-plus/icons-vue'
|
import { Refresh } from '@element-plus/icons-vue'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus' // ui框架: 消息提示
|
import { ElMessage, ElMessageBox } from 'element-plus' // ui框架: 消息提示
|
||||||
import vueQr from 'vue-qr/src/packages/vue-qr.vue' // 插件: 二维码
|
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 ChatWs from '@/plugins/socket' // 聊天socket
|
||||||
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, createWindow, getStaticUrl, sessionStore } from '@/utils/tool' // 工具类-主进程相关
|
||||||
|
@ -151,9 +152,11 @@ const open = async (id, classObj) => {
|
||||||
teacherForm.form.classcourseid = classObj.id
|
teacherForm.form.classcourseid = classObj.id
|
||||||
}
|
}
|
||||||
// 初始化im-chat
|
// 初始化im-chat
|
||||||
// nextTick(async() => {
|
nextTick(async() => {
|
||||||
// chat = await imChatRef.value?.initImChat()
|
// chat = await imChatRef.value?.initImChat()
|
||||||
// })
|
// 连接socket
|
||||||
|
if (!ChatWs.ws) ChatWs.init()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 关闭弹窗
|
// 关闭弹窗
|
||||||
|
@ -267,10 +270,10 @@ const createClasscourse = async () => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
msgEl.close()
|
msgEl.close()
|
||||||
msgEl = ElMessage.warning({message:'正在打开公屏,请稍后...',duration: 0})
|
msgEl = ElMessage.warning({message:'正在打开公屏,请稍后...',duration: 0})
|
||||||
setTimeout(() => {
|
setTimeout(async() => {
|
||||||
msgEl.close()
|
msgEl.close()
|
||||||
const classcourse = {...params, id: teacherForm.form.classcourseid}
|
const res = await Http_Classcourse.getClasscourse(teacherForm.form.classcourseid)
|
||||||
openPublicScreen(classcourse)
|
openPublicScreen(res.data)
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
@ -303,9 +306,10 @@ const classTeachingStart = async () => {
|
||||||
// 新版-pptList 打开公屏
|
// 新版-pptList 打开公屏
|
||||||
if (myClassActive.value.filetype == 'aptist') {
|
if (myClassActive.value.filetype == 'aptist') {
|
||||||
const msgEl = ElMessage.warning({message:'正在打开公屏,请稍后...',duration: 0})
|
const msgEl = ElMessage.warning({message:'正在打开公屏,请稍后...',duration: 0})
|
||||||
setTimeout(() => {
|
setTimeout(async () => {
|
||||||
msgEl.close()
|
msgEl.close()
|
||||||
openPublicScreen({id})
|
const res = await Http_Classcourse.getClasscourse(teacherForm.form.classcourseid)
|
||||||
|
openPublicScreen(res.data)
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}else {
|
}else {
|
||||||
const url = `/teaching/classteaching?classcourseid=${id}&actor=classTeachingOnPublicScreen`
|
const url = `/teaching/classteaching?classcourseid=${id}&actor=classTeachingOnPublicScreen`
|
||||||
|
@ -350,6 +354,10 @@ const getQrUrl = async() => {
|
||||||
// 打开公屏
|
// 打开公屏
|
||||||
const openPublicScreen = (classcourse) => {
|
const openPublicScreen = (classcourse) => {
|
||||||
console.log('打开公屏', classcourse)
|
console.log('打开公屏', classcourse)
|
||||||
|
// 发送app端待开课消息
|
||||||
|
const data = { id: classcourse.id }
|
||||||
|
ChatWs.sendMsg(MsgEnum.HEADS.MSG_0000, data, {}, ChatWs.TYPES.single, userStore.id)
|
||||||
|
// 缓存当前资源信息
|
||||||
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) // 缓存当前当前上课
|
||||||
|
@ -367,25 +375,25 @@ const openPublicScreen = (classcourse) => {
|
||||||
|
|
||||||
// ================== 监听 =======================
|
// ================== 监听 =======================
|
||||||
// im-chat: 聊天事件 {type, data}
|
// im-chat: 聊天事件 {type, data}
|
||||||
const chatChange = (type, data, ...args) => {
|
// const chatChange = (type, data, ...args) => {
|
||||||
if (type == 'msg') { // im-chat 消息监听
|
// if (type == 'msg') { // im-chat 消息监听
|
||||||
console.log('msg:===== ',data, args)
|
// console.log('msg:===== ',data, args)
|
||||||
// const msgId = (args||[])[0].message_msg_id
|
// // const msgId = (args||[])[0].message_msg_id
|
||||||
const { msgKey:head, msgcontent:msg, senduserid:sendId, msgType } = data
|
// const { msgKey:head, msgcontent:msg, senduserid:sendId, msgType } = data
|
||||||
switch(head) {
|
// switch(head) {
|
||||||
case MsgEnum.HEADS.MSG_classcourseopen:{ // 开课, 跳转公屏
|
// case MsgEnum.HEADS.MSG_classcourseopen:{ // 开课, 跳转公屏
|
||||||
const { classcourseid:id } = teacherForm.form
|
// const { classcourseid:id } = teacherForm.form
|
||||||
const { classcourseid: imId, status } = data
|
// const { classcourseid: imId, status } = data
|
||||||
if (imId == id && status == 'open') {
|
// if (imId == id && status == 'open') {
|
||||||
classTeachingStart() // 开始跳转
|
// classTeachingStart() // 开始跳转
|
||||||
}
|
// }
|
||||||
break}
|
// break}
|
||||||
default:
|
// default:
|
||||||
console.log('未知消息:', data)
|
// console.log('未知消息:', data)
|
||||||
break
|
// break
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 监听-班级id
|
// 监听-班级id
|
||||||
watch(() => classForm.form.classid, (val)=> {
|
watch(() => classForm.form.classid, (val)=> {
|
||||||
|
|
|
@ -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,9 +49,19 @@
|
||||||
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" v-if="showTool">
|
||||||
<!-- <el-button v-if="activeClassId==item.id" type="success" @click="clickStartClass(item)">上课中</el-button> -->
|
<!-- <el-button v-if="activeClassId==item.id" type="success" @click="clickStartClass(item)">上课中</el-button> -->
|
||||||
<el-button type="primary" @click="clickStartClass(item)">上课</el-button>
|
<el-button type="primary" @click="clickStartClass(item)">上课</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -107,6 +117,7 @@ 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 {
|
||||||
|
@ -134,6 +145,10 @@ export default {
|
||||||
activeClassId: { // 当前上课id
|
activeClassId: { // 当前上课id
|
||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
|
},
|
||||||
|
showTool: { //是否显示上课工具
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
expose: ['openFileWin'],
|
expose: ['openFileWin'],
|
||||||
|
@ -403,6 +418,11 @@ export default {
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.prepare-popper{
|
||||||
|
width: 80px !important;
|
||||||
|
min-width: 80px !important;
|
||||||
|
padding: 5px !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.prepare-body-main-item {
|
.prepare-body-main-item {
|
||||||
|
|
|
@ -8,15 +8,16 @@
|
||||||
</el-button>
|
</el-button>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
<el-dropdown-item @click="createAptFile">新建文枢课件</el-dropdown-item>
|
<el-dropdown-item @click="createAIPPT">新建文枢课件</el-dropdown-item>
|
||||||
<el-dropdown-item>AI一键生成</el-dropdown-item>
|
<el-dropdown-item @click="aiTOPPT">AI一键生成</el-dropdown-item>
|
||||||
<el-dropdown-item>导入PPT</el-dropdown-item>
|
<el-dropdown-item @click="openFilePicker">导入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>
|
||||||
|
@ -29,7 +30,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"
|
||||||
|
@ -103,10 +104,10 @@
|
||||||
</file-list-item>
|
</file-list-item>
|
||||||
</el-checkbox-group>
|
</el-checkbox-group>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="作业" name="作业">
|
<el-tab-pane label="教学活动" name="教学活动">
|
||||||
<div class="prepare-body-header">
|
<div class="prepare-body-header">
|
||||||
<div>
|
<div>
|
||||||
<label style="font-size: 15px">共{{ currentWorkList.length }}个作业</label>
|
<label style="font-size: 15px">共{{ currentWorkList.length }}个教学活动</label>
|
||||||
<!-- <el-button size="small" @click="handleOutLink('homeWork')">作业设计</el-button> -->
|
<!-- <el-button size="small" @click="handleOutLink('homeWork')">作业设计</el-button> -->
|
||||||
<el-button size="small" @click="goNewClassTask()">作业设计</el-button>
|
<el-button size="small" @click="goNewClassTask()">作业设计</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -152,6 +153,7 @@
|
||||||
<!-- 上课配置 -->
|
<!-- 上课配置 -->
|
||||||
<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> -->
|
||||||
|
@ -161,8 +163,16 @@ 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'
|
||||||
|
@ -192,16 +202,18 @@ 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 * 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";
|
||||||
|
import ChatWs from '@/plugins/socket' // 聊天socket
|
||||||
|
if (!ChatWs.ws) ChatWs.init()
|
||||||
// import Chat from '@/utils/chat' // im 登录初始化
|
// import Chat from '@/utils/chat' // im 登录初始化
|
||||||
// if (!Chat.imChat) Chat.init()
|
// 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: {
|
||||||
|
@ -237,7 +249,7 @@ export default {
|
||||||
activeAptTab: "教学课件",
|
activeAptTab: "教学课件",
|
||||||
uploadData: {
|
uploadData: {
|
||||||
textbookId: null,
|
textbookId: null,
|
||||||
levelFirstId: 39103,
|
levelFirstId: null,
|
||||||
levelSecondId: null,
|
levelSecondId: null,
|
||||||
fileSource: '个人',
|
fileSource: '个人',
|
||||||
fileRoot: '备课'
|
fileRoot: '备课'
|
||||||
|
@ -257,7 +269,23 @@ 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: {
|
||||||
|
@ -313,6 +341,8 @@ export default {
|
||||||
// }
|
// }
|
||||||
// },
|
// },
|
||||||
methods: {
|
methods: {
|
||||||
|
// 延时
|
||||||
|
sleep(ms){return new Promise(resolve => setTimeout(resolve, ms))},
|
||||||
addAiPPT(item) {
|
addAiPPT(item) {
|
||||||
this.currentFileList.unshift(item.resData)
|
this.currentFileList.unshift(item.resData)
|
||||||
KjListItem.methods.openFileWin(item.resData);
|
KjListItem.methods.openFileWin(item.resData);
|
||||||
|
@ -415,6 +445,33 @@ export default {
|
||||||
ElMessage.warning('该功能暂未开放!')
|
ElMessage.warning('该功能暂未开放!')
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
case 'wsApp': { // 发送app端待开课消息
|
||||||
|
// console.log('wsApp', row)
|
||||||
|
const head = MsgEnum.HEADS.MSG_0000
|
||||||
|
const data = { id: row.id }
|
||||||
|
const type = ChatWs.TYPES.single
|
||||||
|
const userId = this.userStore.userId
|
||||||
|
ElMessage.success('APP端待开课消息-发送成功!')
|
||||||
|
await this.sleep(1000) // 等待1s
|
||||||
|
// 打开公屏
|
||||||
|
const msgEl = ElMessage.warning({message:'正在打开公屏,请稍后...',duration: 0})
|
||||||
|
const res = await getEntpcoursefile(row.entpcoursefileid)
|
||||||
|
await this.sleep(2000) // 等待2s
|
||||||
|
ChatWs.sendMsg(head, data, null, type, userId)
|
||||||
|
msgEl.close() // 关闭提示
|
||||||
|
const resource = res?.data||{}
|
||||||
|
const classcourse = row
|
||||||
|
sessionStore.set('curr.resource', resource) // 缓存当前资源信息
|
||||||
|
sessionStore.set('curr.classcourse', classcourse) // 缓存当前当前上课
|
||||||
|
createWindow('open-win', {
|
||||||
|
url: '/pptist', // 窗口关闭时,清除缓存
|
||||||
|
close: () => {
|
||||||
|
sessionStore.set('curr.resource', null) // 清除缓存
|
||||||
|
sessionStore.set('curr.classcourse', null) // 清除缓存
|
||||||
|
}
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -476,6 +533,139 @@ 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) { // 视频和音频
|
||||||
|
const res = await fetch(o.src)
|
||||||
|
const blob = await res.blob()
|
||||||
|
const fileName = o.type=='video'? Date.now() + '.mp4':Date.now() + '.mp3'
|
||||||
|
const file = commUtils.blobToFile(blob, fileName)
|
||||||
|
// o.src = fileName
|
||||||
|
// console.log('file', file)
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
const ress = await Api_server.Other.uploadFile(formData)
|
||||||
|
if (ress && ress.code == 200){
|
||||||
|
const url = ress?.url
|
||||||
|
url &&(o.src = url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (o?.background?.image) await this.toRousrceUrl(o.background.image)
|
||||||
|
// if (o?.elements) o.elements.forEach(async o => {await this.toRousrceUrl(o)})
|
||||||
|
if(o?.elements){
|
||||||
|
for (let element of o.elements) {
|
||||||
|
await this.toRousrceUrl(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
console.log('结束', slides)
|
||||||
|
return
|
||||||
|
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))
|
||||||
|
console.log(resSlides)
|
||||||
|
return
|
||||||
|
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,
|
||||||
|
@ -502,57 +692,22 @@ 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: JSON.stringify(pagearray),
|
datacontent: '{"elements":[],"background":{"type":"solid","color":"#fff"}}',
|
||||||
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,
|
||||||
fileShowName: this.currentNode.itemtitle + '.apt'
|
fileFlag: 'aippt',
|
||||||
|
fileShowName: this.currentNode.itemtitle + '.aippt'
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
this.currentFileList.unshift(res.resData)
|
this.currentFileList.unshift(res.resData)
|
||||||
setTimeout(()=>{
|
setTimeout(()=>{
|
||||||
|
@ -742,7 +897,7 @@ export default {
|
||||||
this.isLoading = true
|
this.isLoading = true
|
||||||
return getSmarttalkPage({
|
return getSmarttalkPage({
|
||||||
...this.uploadData,
|
...this.uploadData,
|
||||||
orderByColumn: 'uploadTime',
|
orderByColumn: 'createTime',
|
||||||
isAsc: 'desc',
|
isAsc: 'desc',
|
||||||
pageSize: 500
|
pageSize: 500
|
||||||
})
|
})
|
||||||
|
|
|
@ -53,7 +53,7 @@ export default defineStore('resource', {
|
||||||
fileFlags: resourceType[0].value,
|
fileFlags: resourceType[0].value,
|
||||||
fileRoot: '资源',
|
fileRoot: '资源',
|
||||||
fileName: '',
|
fileName: '',
|
||||||
orderByColumn: 'uploadTime',
|
orderByColumn: 'createTime',
|
||||||
isAsc: 'desc',
|
isAsc: 'desc',
|
||||||
...structQuery
|
...structQuery
|
||||||
},
|
},
|
||||||
|
|
|
@ -330,11 +330,27 @@ const toRousrceUrl = async(o) => {
|
||||||
url &&(o.src = url)
|
url &&(o.src = url)
|
||||||
}
|
}
|
||||||
} else if (isBlobUrl) { // 视频和音频
|
} else if (isBlobUrl) { // 视频和音频
|
||||||
|
const res = await fetch(o.src)
|
||||||
|
const blob = await res.blob()
|
||||||
|
const fileName = o.type=='video'? Date.now() + '.mp4':Date.now() + '.mp3'
|
||||||
|
const file = commUtils.blobToFile(blob, fileName)
|
||||||
|
// o.src = fileName
|
||||||
|
// console.log('file', file)
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
const ress = await Api_server.Other.uploadFile(formData)
|
||||||
|
if (ress && ress.code == 200){
|
||||||
|
const url = ress?.url
|
||||||
|
url &&(o.src = url)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (o?.background?.image) await toRousrceUrl(o.background.image)
|
if (o?.background?.image) await toRousrceUrl(o.background.image)
|
||||||
if (o?.elements) o.elements.forEach(async o => {await toRousrceUrl(o)})
|
if(o?.elements){
|
||||||
|
for (let element of o.elements) {
|
||||||
|
await this.toRousrceUrl(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// ======== zdg end ============
|
// ======== zdg end ============
|
||||||
|
|
||||||
|
|
|
@ -203,7 +203,7 @@ const openFileLink = async (item) =>{
|
||||||
// 获取资源数据
|
// 获取资源数据
|
||||||
const getResource = () => {
|
const getResource = () => {
|
||||||
let querySearch = toRaw(toolStore.curSubjectNode).querySearch
|
let querySearch = toRaw(toolStore.curSubjectNode).querySearch
|
||||||
querySearch.orderByColumn = 'uploadTime'
|
querySearch.orderByColumn = 'createTime'
|
||||||
querySearch.isAsc = 'desc'
|
querySearch.isAsc = 'desc'
|
||||||
querySearch.pageSize = 500
|
querySearch.pageSize = 500
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue