Compare commits
No commits in common. "1c9a3762ef11ab85e116d39a6543e6b864c24653" and "1ef4615587b3b5707112135d8b5d2841bcf20295" have entirely different histories.
1c9a3762ef
...
1ef4615587
|
@ -43,11 +43,6 @@ export default defineConfig({
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (p) => p.replace(/^\/baidubce/, '')
|
rewrite: (p) => p.replace(/^\/baidubce/, '')
|
||||||
},
|
},
|
||||||
'/parth': {
|
|
||||||
target: 'https://zwapi.xfyun.cn', // 第三方API的地址
|
|
||||||
changeOrigin: true, // 改变请求的起源
|
|
||||||
rewrite: (path) => path.replace(/^\/parth/, '') // 重写路径
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [vue(), WindiCSS()],
|
plugins: [vue(), WindiCSS()],
|
||||||
|
|
|
@ -80,8 +80,6 @@ import { updateUserInfo } from '@/api/system/user'
|
||||||
import logoIco from '@/assets/images/logo.png'
|
import logoIco from '@/assets/images/logo.png'
|
||||||
import { listEvaluation } from '@/api/classManage/index'
|
import { listEvaluation } from '@/api/classManage/index'
|
||||||
import { sessionStore } from '@/utils/store'
|
import { sessionStore } from '@/utils/store'
|
||||||
import Chat from '@/utils/chat' // im 登录初始化
|
|
||||||
if (!Chat.imChat) Chat.init()
|
|
||||||
|
|
||||||
let homeTitle = ref(import.meta.env.VITE_APP_TITLE)
|
let homeTitle = ref(import.meta.env.VITE_APP_TITLE)
|
||||||
const { ipcRenderer } = window.electron || {}
|
const { ipcRenderer } = window.electron || {}
|
||||||
|
@ -145,7 +143,6 @@ function handleCommand(command) {
|
||||||
break
|
break
|
||||||
case 'logout':
|
case 'logout':
|
||||||
logout()
|
logout()
|
||||||
Chat?.logout() // im 退出登录
|
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
|
|
|
@ -19,11 +19,8 @@ import AppMain from './components/AppMain.vue'
|
||||||
import Uploader from './components/Uploader.vue'
|
import Uploader from './components/Uploader.vue'
|
||||||
import AiChart from '@/components/ai-chart/index.vue'
|
import AiChart from '@/components/ai-chart/index.vue'
|
||||||
import uploaderState from '@/store/modules/uploader'
|
import uploaderState from '@/store/modules/uploader'
|
||||||
// import Chat from '@/utils/chat'
|
|
||||||
|
|
||||||
let uploaderStore = ref(uploaderState())
|
let uploaderStore = ref(uploaderState())
|
||||||
// window.test = Chat
|
|
||||||
// Chat.init()
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -70,10 +70,7 @@ export class ImChat {
|
||||||
// 日志监听
|
// 日志监听
|
||||||
this.timChat.TIMSetLogCallback({
|
this.timChat.TIMSetLogCallback({
|
||||||
callback: data => {
|
callback: data => {
|
||||||
const [type, log] = data
|
this.setConsole('%cchat-log ', data[1])
|
||||||
if (type == log_level) { // 打印对应日志
|
|
||||||
this.setConsole('%cchat-log ', log)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
user_data: ''
|
user_data: ''
|
||||||
})
|
})
|
||||||
|
@ -89,7 +86,7 @@ export class ImChat {
|
||||||
if (code == 0) { // 初始化成功
|
if (code == 0) { // 初始化成功
|
||||||
this.setConsole('%cim-chat: init', '初始化成功')
|
this.setConsole('%cim-chat: init', '初始化成功')
|
||||||
this.status.isConnect = true
|
this.status.isConnect = true
|
||||||
// this.setConfig() // 设置日志级别
|
this.setConfig() // 设置日志级别
|
||||||
resolve(this)
|
resolve(this)
|
||||||
} else { // 失败:具体请看code
|
} else { // 失败:具体请看code
|
||||||
console.error('[im-chat]:初始化失败', code)
|
console.error('[im-chat]:初始化失败', code)
|
||||||
|
@ -230,11 +227,10 @@ export class ImChat {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// 删除群组
|
// 删除群组
|
||||||
deleteGroup(timGroupId) {
|
deleteGroup() {
|
||||||
const groupId = timGroupId || this.timGroupId
|
if (!this.timGroupId) return
|
||||||
if (!groupId) return
|
|
||||||
return this.timChat.TIMGroupDelete({
|
return this.timChat.TIMGroupDelete({
|
||||||
groupId,
|
groupId: this.timGroupId,
|
||||||
data: '', // 用户自定义数据
|
data: '', // 用户自定义数据
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -253,7 +249,7 @@ export class ImChat {
|
||||||
}
|
}
|
||||||
// 获取群组列表
|
// 获取群组列表
|
||||||
getGroupList() {
|
getGroupList() {
|
||||||
return this.timChat.TIMGroupGetJoinedGroupList().then(res => {
|
return this.timChat.getGroupList().then(res => {
|
||||||
console.log('获取群组列表', res)
|
console.log('获取群组列表', res)
|
||||||
return res
|
return res
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
import axios from "axios";
|
|
||||||
import { getSignature } from "./index";
|
|
||||||
|
|
||||||
let appId = "01ec9aa3";
|
|
||||||
let secret = "M2QxMDAxMjYyYTEzODMwMGRkZTQ4NmUy";
|
|
||||||
let timestamp = Math.floor(Date.now() / 1000);
|
|
||||||
let signature = getSignature(appId, secret, timestamp);
|
|
||||||
|
|
||||||
export function text2Text(data) {
|
|
||||||
return axios({
|
|
||||||
url: "v1/chat/completions",
|
|
||||||
method: "post",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
Authorization: "Bearer yjCwlZCeUtBYvjHAQZdk:FtWNmJSWcZMCmTBQZfoH",
|
|
||||||
Accept: "*/*",
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
model: "4.0Ultra",
|
|
||||||
messages: data,
|
|
||||||
stream: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
export function uploadDoc(data) {
|
|
||||||
return axios({
|
|
||||||
url: "openapi/v1/file/upload",
|
|
||||||
method: "post",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "multipart/form-data",
|
|
||||||
appId: appId,
|
|
||||||
timestamp: timestamp,
|
|
||||||
signature: signature,
|
|
||||||
},
|
|
||||||
data: data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
export function queryDocStatus(data) {
|
|
||||||
return axios({
|
|
||||||
url: "openapi/v1/file/status",
|
|
||||||
method: "post",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/form-data",
|
|
||||||
appId: appId,
|
|
||||||
timestamp: timestamp,
|
|
||||||
signature: signature,
|
|
||||||
},
|
|
||||||
data: data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
export function chatByDoc(fileId, data) {
|
|
||||||
const wsUrl = `wss://chatdoc.xfyun.cn/openapi/chat?fileId=${fileId}&appId=${appId}×tamp=${timestamp}&signature=${signature}`;
|
|
||||||
|
|
||||||
const ws = new WebSocket(wsUrl);
|
|
||||||
|
|
||||||
ws.onopen = () => {
|
|
||||||
const messageBody = {
|
|
||||||
fileIds: [fileId],
|
|
||||||
messages: data,
|
|
||||||
chatExtends: {
|
|
||||||
wikiPromptTpl:
|
|
||||||
"请将以下内容作为已知信息:\n<wikicontent>\n请根据以上内容回答用户的问题。\n问题:<wikiquestion>\n回答:",
|
|
||||||
wikiFilterScore: 0.82,
|
|
||||||
temperature: 0.5,
|
|
||||||
sparkWhenWithoutEmbedding: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
ws.send(JSON.stringify(messageBody));
|
|
||||||
};
|
|
||||||
|
|
||||||
ws.onmessage = (event) => {
|
|
||||||
const response = JSON.parse(event.data);
|
|
||||||
console.log("WebSocket 消息:", response);
|
|
||||||
};
|
|
||||||
|
|
||||||
ws.onerror = (error) => {
|
|
||||||
console.error("WebSocket 错误:", error);
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,98 +0,0 @@
|
||||||
/**
|
|
||||||
* 实现单例模式
|
|
||||||
*/
|
|
||||||
import useUserStore from '@/store/modules/user'
|
|
||||||
import { ImChat } from '@/plugins/imChat'
|
|
||||||
import * as http from '@/api/apiService' // 自定义api service
|
|
||||||
|
|
||||||
export class Chat {
|
|
||||||
instance = null;
|
|
||||||
sdkAppId = 0; // 应用id
|
|
||||||
sign = ''; // 签名
|
|
||||||
imUserId = ''; // 用户id
|
|
||||||
imChat = null; // IM实例
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
if (!Chat.instance) { // 存在的时候
|
|
||||||
Chat.instance = this;
|
|
||||||
}
|
|
||||||
return Chat.instance;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 初始化 获取IM签名
|
|
||||||
* @param {*} isInit : 是否初始化IM
|
|
||||||
* @param {*} isLogin : 是否登录IM
|
|
||||||
* @param {*} callback: 监听消息回调函数
|
|
||||||
* @returns Promise<ImChat>
|
|
||||||
*/
|
|
||||||
async init(isInit = true, isLogin = true, callback) {
|
|
||||||
// 特殊处理,只传1个参数且为函数,则默认为callback,isInit和isLogin默认为true
|
|
||||||
if (typeof isInit == 'function'){
|
|
||||||
callback = isInit
|
|
||||||
isInit = true
|
|
||||||
isLogin = true
|
|
||||||
}
|
|
||||||
const userStore = useUserStore()
|
|
||||||
const { timuserid: imUserId } = userStore.user
|
|
||||||
// 获取腾讯云签名
|
|
||||||
const res = await http.imChat.getTxCloudSign({imUserId})
|
|
||||||
if (res && res.code == 200) {
|
|
||||||
const { sdkAppId, sign } = res.data
|
|
||||||
this.sdkAppId = sdkAppId
|
|
||||||
this.sign = sign
|
|
||||||
this.imUserId = imUserId
|
|
||||||
// 初始化IM
|
|
||||||
if (isInit) return await this.initIM(isLogin, callback)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 初始化IM
|
|
||||||
async initIM(isLogin, callback) {
|
|
||||||
const imChat = new ImChat(this.sdkAppId, this.sign, this.imUserId)
|
|
||||||
this.imChat = imChat
|
|
||||||
await imChat.init() // 初始化IM
|
|
||||||
callback && this.listenMsg(callback) // 监听消息
|
|
||||||
if(isLogin) await imChat.login() // 登录IM
|
|
||||||
return imChat
|
|
||||||
}
|
|
||||||
// 监听消息
|
|
||||||
async listenMsg(callback) {
|
|
||||||
if (!callback) return
|
|
||||||
if (!this.imChat) return
|
|
||||||
await this.imChat?.watch(msg => callback(msg))
|
|
||||||
}
|
|
||||||
// 解散群
|
|
||||||
async dismissGroup(groupId) {
|
|
||||||
if (!this.imChat) return
|
|
||||||
await this.imChat?.deleteGroup(groupId)
|
|
||||||
}
|
|
||||||
// 退出登录
|
|
||||||
async logout() {
|
|
||||||
if (!this.imChat) return
|
|
||||||
await this.imChat?.logout()
|
|
||||||
imChat = null
|
|
||||||
this.imChat = null
|
|
||||||
}
|
|
||||||
// 发群消息
|
|
||||||
async sendMsg(conv_id, msg) {
|
|
||||||
if (!this.imChat) return
|
|
||||||
await this.imChat?.sendMsg(conv_id, msg)
|
|
||||||
}
|
|
||||||
// 发群消息
|
|
||||||
async sendMsgGroup(msg, head, type) {
|
|
||||||
if (!this.imChat) return
|
|
||||||
this.imChat?.sendMsgGroup(msg, head, type)
|
|
||||||
}
|
|
||||||
// 发群消息
|
|
||||||
async sendMsgGroupId(groupId, msg, head, type) {
|
|
||||||
if (!this.imChat) return
|
|
||||||
const msgObj = this.imChat?.getMsgObj(head, msg, type)
|
|
||||||
this.imChat?.sendMsg(groupId, msgObj)
|
|
||||||
}
|
|
||||||
// 获取群列表
|
|
||||||
async getGroupList() {
|
|
||||||
if (!this.imChat) return
|
|
||||||
return await this.imChat?.getGroupList()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default new Chat()
|
|
|
@ -1,33 +0,0 @@
|
||||||
import CryptoJS from 'crypto-js';
|
|
||||||
|
|
||||||
// 生成签名的主方法
|
|
||||||
export function getSignature(appId, secret, ts) {
|
|
||||||
try {
|
|
||||||
const auth = md5(appId + ts);
|
|
||||||
return hmacSHA1Encrypt(auth, secret);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error generating signature:', error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MD5 加密
|
|
||||||
function md5(cipherText) {
|
|
||||||
try {
|
|
||||||
return CryptoJS.MD5(cipherText).toString(); // 使用 CryptoJS 进行 MD5 加密
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error in MD5 hashing:', error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// HMAC-SHA1 加密
|
|
||||||
function hmacSHA1Encrypt(encryptText, encryptKey) {
|
|
||||||
try {
|
|
||||||
// 使用 CryptoJS 进行 HMAC-SHA1 加密,并转换为 Base64 格式
|
|
||||||
return CryptoJS.HmacSHA1(encryptText, encryptKey).toString(CryptoJS.enc.Base64);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error in HMAC-SHA1 encryption:', error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,78 +0,0 @@
|
||||||
import axios from "axios";
|
|
||||||
import { getSignature } from "./index";
|
|
||||||
import { ElMessage } from "element-plus";
|
|
||||||
|
|
||||||
let appId = "01ec9aa3";
|
|
||||||
let secret = "M2QxMDAxMjYyYTEzODMwMGRkZTQ4NmUy";
|
|
||||||
let timestamp = Math.floor(Date.now() / 1000);
|
|
||||||
let signature = getSignature(appId, secret, timestamp);
|
|
||||||
|
|
||||||
const instance = axios.create({
|
|
||||||
baseURL: "",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
appId: appId,
|
|
||||||
timestamp: timestamp,
|
|
||||||
signature: signature,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const createOutline = async (data) => {
|
|
||||||
console.log("createOutline data:", data);
|
|
||||||
try {
|
|
||||||
const response = await instance.post(
|
|
||||||
"/parth/api/aippt/createOutline",
|
|
||||||
data
|
|
||||||
);
|
|
||||||
console.log("createOutline response:", response);
|
|
||||||
if (response.code == 81002) {
|
|
||||||
ElMessage.error("并发数量超过限制");
|
|
||||||
}
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
console.error("请求失败:", error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const getBackGround = async () => {
|
|
||||||
try {
|
|
||||||
const response = await instance.get("/parth/api/aippt/themeList");
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
console.error("请求失败:", error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const createPPT = async (data) => {
|
|
||||||
try {
|
|
||||||
const response = await instance.post("/parth/api/aippt/create", data);
|
|
||||||
console.log("createOutline response:", response);
|
|
||||||
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
console.error("请求失败:", error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const createByOutline = async (data) => {
|
|
||||||
try {
|
|
||||||
const response = await instance.post("/parth/api/aippt/createByOutline", data);
|
|
||||||
console.log("createByOutline response:", response);
|
|
||||||
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
console.error("请求失败:", error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const getProgress = async (id) => {
|
|
||||||
try {
|
|
||||||
const response = await instance.get(`/parth/api/aippt/progress?sid=${id}`);
|
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
console.error("请求失败:", error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export { instance, createOutline, getBackGround, createPPT, getProgress, createByOutline };
|
|
|
@ -11,7 +11,6 @@
|
||||||
v-if="item.bookImg"
|
v-if="item.bookImg"
|
||||||
@open-edit="reservDialog.openDialog(item)"
|
@open-edit="reservDialog.openDialog(item)"
|
||||||
@delete-reserv="deleteReserv(item)"
|
@delete-reserv="deleteReserv(item)"
|
||||||
@change="(...o) => emit('change', ...o)"
|
|
||||||
></reserv-item>
|
></reserv-item>
|
||||||
<reserv-item-apt
|
<reserv-item-apt
|
||||||
v-if="!item.bookImg"
|
v-if="!item.bookImg"
|
||||||
|
@ -19,7 +18,6 @@
|
||||||
:item="item"
|
:item="item"
|
||||||
@open-edit="reservDialog.openDialog(item)"
|
@open-edit="reservDialog.openDialog(item)"
|
||||||
@delete-reserv="deleteReserv(item)"
|
@delete-reserv="deleteReserv(item)"
|
||||||
@change="(...o) => emit('change', ...o)"
|
|
||||||
></reserv-item-apt>
|
></reserv-item-apt>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
@ -36,10 +34,6 @@ import Reserv from '@/views/prepare/container/reserv.vue'
|
||||||
import { useToolState } from '@/store/modules/tool'
|
import { useToolState } from '@/store/modules/tool'
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
import ReservItemApt from '@/views/classManage/reserv-item-apt.vue'
|
import ReservItemApt from '@/views/classManage/reserv-item-apt.vue'
|
||||||
// import Chat from '@/utils/chat' // im 登录初始化
|
|
||||||
// if (!Chat.imChat) Chat.init()
|
|
||||||
|
|
||||||
const emit = defineEmits(['change'])
|
|
||||||
const reservDialog = ref(null)
|
const reservDialog = ref(null)
|
||||||
const tabOptions = ref(['进行中', '已结束'])
|
const tabOptions = ref(['进行中', '已结束'])
|
||||||
const tabActive = ref('进行中')
|
const tabActive = ref('进行中')
|
||||||
|
|
|
@ -7,14 +7,14 @@
|
||||||
<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 === 'close'" style="margin-right: 5px" type="success">已结束</el-tag>
|
||||||
<el-tag v-if="item.status === 'open'" style="margin-right: 5px" type="danger">上课中</el-tag>
|
<el-tag v-if="item.status === 'open'" style="margin-right: 5px" type="danger">上课中</el-tag>
|
||||||
<el-button v-if="item.status === 'open'" :disabled="toolStore.isToolWin" size="small" type="primary" @click="startClassR(item)"
|
<el-button v-if="item.status === 'open'" :disabled="toolStore.isToolWin" size="small" type="primary" @click="startClassR(item)"
|
||||||
>继续上课</el-button
|
>继续上课</el-button
|
||||||
>
|
>
|
||||||
<!-- <el-button v-if="item.status === '未开始'" @click="openEdit">编辑</el-button>-->
|
<!-- <el-button v-if="item.status === '未开始'" @click="openEdit">编辑</el-button>-->
|
||||||
<el-button v-if="item.status === 'open'" :loading="loading" size="small" type="info" @click="endClassR(item)"
|
<el-button v-if="item.status === 'open'" size="small" type="info" @click="endClassR(item)"
|
||||||
>下课{{ loading?'中...':'' }}</el-button
|
>下课</el-button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="class-reserv-item-tool" style="width: 50px;">
|
<div class="class-reserv-item-tool" style="width: 50px;">
|
||||||
|
@ -25,14 +25,13 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue'
|
|
||||||
import { useToolState } from '@/store/modules/tool'
|
import { useToolState } from '@/store/modules/tool'
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
import { createWindow } from '@/utils/tool'
|
import { createWindow } from '@/utils/tool'
|
||||||
import { deleteSmartReserv, startClass, endClass } from '@/api/classManage'
|
import { deleteSmartReserv, startClass, endClass } from '@/api/classManage'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { listEntpcourse } from '@/api/teaching/classwork'
|
import { listEntpcourse } from '@/api/teaching/classwork'
|
||||||
const emit = defineEmits(['openEdit', 'deleteReserv', 'change'])
|
const emit = defineEmits(['openEdit', 'deleteReserv'])
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
item: {
|
item: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -41,7 +40,6 @@ 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 openEdit = () => {
|
const openEdit = () => {
|
||||||
emit('openEdit', props.item)
|
emit('openEdit', props.item)
|
||||||
}
|
}
|
||||||
|
@ -56,15 +54,42 @@ const deleteReserv = () => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// 继续上课
|
|
||||||
const startClassR = (item) => {
|
const startClassR = (item) => {
|
||||||
emit('change', 'continue', item)
|
// startClass(item.id).then((res) => {
|
||||||
|
// if (res.data === true) {
|
||||||
|
// item.status = '上课中'
|
||||||
|
// openLesson()
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// item.status = '上课中'
|
||||||
|
openLesson()
|
||||||
}
|
}
|
||||||
// 下课
|
|
||||||
const endClassR = (item) => {
|
const endClassR = (item) => {
|
||||||
emit('change', 'close', item, { type: 2, loading })
|
/*endClass(item.id).then((res) => {
|
||||||
|
if (res.data === true) {
|
||||||
|
ElMessage({
|
||||||
|
message: '下课成功',
|
||||||
|
type: 'success'
|
||||||
|
})
|
||||||
|
item.status = '已结束'
|
||||||
|
}
|
||||||
|
})*/
|
||||||
|
}
|
||||||
|
// const toolStore = useToolState()
|
||||||
|
let wins = null;
|
||||||
|
// 上课-工具类悬浮
|
||||||
|
const openLesson = () => {
|
||||||
|
// startClass(props.item.id)
|
||||||
|
/*listEntpcourse({
|
||||||
|
evalid: props.item.ex2,
|
||||||
|
edituserid: useUserStore().user.userId,
|
||||||
|
pageSize: 500
|
||||||
|
}).then(async res=>{
|
||||||
|
if (res.rows[0].id) {
|
||||||
|
wins = await createWindow('tool-sphere', { url: '/tool/sphere?entpcourseid=' + res.rows[0].id + "&reservId=" + props.item.id })
|
||||||
|
}
|
||||||
|
})*/
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.class-reserv-item {
|
.class-reserv-item {
|
||||||
|
|
|
@ -13,8 +13,8 @@
|
||||||
>继续上课</el-button
|
>继续上课</el-button
|
||||||
>
|
>
|
||||||
<!-- <el-button v-if="item.status === '未开始'" @click="openEdit">编辑</el-button>-->
|
<!-- <el-button v-if="item.status === '未开始'" @click="openEdit">编辑</el-button>-->
|
||||||
<el-button v-if="item.status === '上课中'" :loading="loading" size="small" type="info" @click="endClassR(item)"
|
<el-button v-if="item.status === '上课中'" size="small" type="info" @click="endClassR(item)"
|
||||||
>下课{{ loading?'中...':'' }}</el-button
|
>下课</el-button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="class-reserv-item-tool" style="width: 50px;">
|
<div class="class-reserv-item-tool" style="width: 50px;">
|
||||||
|
@ -25,14 +25,13 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue'
|
|
||||||
import { useToolState } from '@/store/modules/tool'
|
import { useToolState } from '@/store/modules/tool'
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
import { createWindow } from '@/utils/tool'
|
import { createWindow } from '@/utils/tool'
|
||||||
import { deleteSmartReserv, startClass, endClass } from '@/api/classManage'
|
import { deleteSmartReserv, startClass, endClass } from '@/api/classManage'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { listEntpcourse } from '@/api/teaching/classwork'
|
import { listEntpcourse } from '@/api/teaching/classwork'
|
||||||
const emit = defineEmits(['openEdit', 'deleteReserv','change'])
|
const emit = defineEmits(['openEdit', 'deleteReserv'])
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
item: {
|
item: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -41,7 +40,6 @@ 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 openEdit = () => {
|
const openEdit = () => {
|
||||||
emit('openEdit', props.item)
|
emit('openEdit', props.item)
|
||||||
}
|
}
|
||||||
|
@ -82,7 +80,15 @@ const openLesson = () => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const endClassR = (item) => {
|
const endClassR = (item) => {
|
||||||
emit('change', 'close', item, { type: 2, loading })
|
endClass(item.id).then((res) => {
|
||||||
|
if (res.data === true) {
|
||||||
|
ElMessage({
|
||||||
|
message: '下课成功',
|
||||||
|
type: 'success'
|
||||||
|
})
|
||||||
|
item.status = '已结束'
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|
|
@ -32,8 +32,6 @@ function getColor(index) {
|
||||||
// 初始化图表
|
// 初始化图表
|
||||||
function initChart() {
|
function initChart() {
|
||||||
const myChart = echarts.init(chartRef.value);
|
const myChart = echarts.init(chartRef.value);
|
||||||
const total = dataList.value.reduce((acc, cur) => acc + cur.value, 0); // 计算总数
|
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
|
@ -72,11 +70,7 @@ function initChart() {
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
position: 'top',
|
position: 'top',
|
||||||
formatter: params => {
|
formatter: '{c}人',
|
||||||
const value = dataList.value[params.dataIndex].value;
|
|
||||||
const percentage = ((value / total) * 100).toFixed(2); // 计算百分比并保留两位小数
|
|
||||||
return `${percentage}%`; // 显示为百分比形式
|
|
||||||
},
|
|
||||||
color: '#333',
|
color: '#333',
|
||||||
fontSize: 12
|
fontSize: 12
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,697 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="ai-container">
|
|
||||||
<el-steps style="max-width:100% " :active="activeStep" align-center>
|
|
||||||
<el-step title="开始创作" />
|
|
||||||
<el-step title="输入主题" />
|
|
||||||
<el-step title="编辑大纲" />
|
|
||||||
<el-step title="选择模板" />
|
|
||||||
<el-step title="制作PPT" />
|
|
||||||
</el-steps>
|
|
||||||
<div class="card-box">
|
|
||||||
<el-card class="card1" v-if="activeStep == 0">
|
|
||||||
<el-tabs v-model="activeName" type="card" class="demo-tabs">
|
|
||||||
<el-tab-pane label="输入主题与要求" name="first">
|
|
||||||
<div style="padding: 20px;">输入主题</div>
|
|
||||||
<el-input type="textarea" v-model="inputTheme" :rows="3" placeholder="在此输入您的PPT主题..."
|
|
||||||
@keydown.enter.exact.prevent="addMessage" @keydown.enter.shift.exact.prevent="inputTheme += '\n'" />
|
|
||||||
<div style="padding: 20px;">具体生成要求</div>
|
|
||||||
<el-input type="textarea" v-model="inputRequire" :rows="3" placeholder="请输入对生成大纲的具体要求,比如要包含那些内容"
|
|
||||||
@keydown.enter.exact.prevent="addMessage" @keydown.enter.shift.exact.prevent="inputRequire += '\n'" />
|
|
||||||
<div>
|
|
||||||
<el-button style="margin:15px 0" type="primary" @click="addMessage">生成大纲→</el-button>
|
|
||||||
</div>
|
|
||||||
</el-tab-pane>
|
|
||||||
<!-- <el-tab-pane label="上传文件并解析" name="second">
|
|
||||||
<el-upload action="#" :on-change="onFileChange" :before-upload="beforeUpload" :show-file-list="false">
|
|
||||||
<el-button type="primary">点击上传并解析文件</el-button>
|
|
||||||
<text>(支持 doc/docx、pdf、md、txt 格式文档,不超过 20M,不超过 100W 字符)</text>
|
|
||||||
</el-upload>
|
|
||||||
<br/>
|
|
||||||
<div v-if="enableButton">{{docName}}文档已解析完毕</div>
|
|
||||||
<br/>
|
|
||||||
<div style="padding: 20px;">输入主题</div>
|
|
||||||
<textarea style="width:50vw" v-model="inputTheme" :rows="3" placeholder="在此输入您的PPT主题..."
|
|
||||||
@keydown.enter.shift.exact.prevent="inputTheme += '\n'">
|
|
||||||
</textarea>
|
|
||||||
<div style="padding: 20px;">具体生成要求</div>
|
|
||||||
<textarea style="width:50vw; margin:20px" v-model="docRequire" :rows="3"
|
|
||||||
placeholder="请输入对生成大纲的具体要求,比如要包含那些内容" @keydown.enter.shift.exact.prevent="inputRequire += '\n'">
|
|
||||||
</textarea>
|
|
||||||
<div>
|
|
||||||
<el-button style="padding:15px" type="primary" :disabled="!enableButton"
|
|
||||||
@click="chatDoc">生成大纲→</el-button>
|
|
||||||
</div>
|
|
||||||
</el-tab-pane> -->
|
|
||||||
</el-tabs>
|
|
||||||
</el-card>
|
|
||||||
<el-card class="card2" v-if="activeStep == 1">
|
|
||||||
<div class="paragraphs">
|
|
||||||
{{ outputText }}
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
<el-card class="card3" v-if="activeStep == 2">
|
|
||||||
<div class="outline">
|
|
||||||
<el-scrollbar height="250px">
|
|
||||||
<el-row :gutter="20" class="outline-row">
|
|
||||||
<el-col :span="8">
|
|
||||||
<div v-for="item in firstArray" :key="item" class="item-with-dash">{{ item }}</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="16">
|
|
||||||
<div v-for="(item, index) in secondArray" :key="index">
|
|
||||||
<el-input v-model="item.value"></el-input>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</el-scrollbar>
|
|
||||||
</div>
|
|
||||||
<el-input type="textarea" v-model="fixRequire" :rows="3" placeholder="与AI对话,告诉AI您想如何修改"
|
|
||||||
@keydown.enter.exact.prevent="fixOutline" @keydown.enter.shift.exact.prevent="inputRequire += '\n'" />
|
|
||||||
<br />
|
|
||||||
<el-button v-if="fixRequire.length > 0" style="padding:15px" type="primary"
|
|
||||||
@click="fixOutline()">发送修改</el-button>
|
|
||||||
<el-button style="margin-top:15px" type="primary" @click="combineOutline()">下一步→</el-button>
|
|
||||||
</el-card>
|
|
||||||
<el-card v-if="activeStep == 3">
|
|
||||||
<div style="padding-bottom: 10px">ppt模板选择</div>
|
|
||||||
<div class="themes">
|
|
||||||
<div v-for="item in backGroundList" :key="item.key" :style="{
|
|
||||||
padding: '20px',
|
|
||||||
paddingRight: '30px',
|
|
||||||
paddingLeft: '30px',
|
|
||||||
margin: '10px',
|
|
||||||
backgroundColor: getBackgroundColor(item.key),
|
|
||||||
borderRadius: '10px',
|
|
||||||
borderBlock: '10px solid #e6e6e6'
|
|
||||||
}" @click="chooseBackground(item.key)" @mouseenter="changeCursor('pointer')" @mouseleave="changeCursor('default')">
|
|
||||||
{{ item.name }}
|
|
||||||
<br />
|
|
||||||
<img style="width: 150px; height: auto" :src="item.thumbnail" alt="" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<el-row class="el-row">
|
|
||||||
<el-col :span="6" class="el-col">
|
|
||||||
<div class="grid-content-1">
|
|
||||||
<div>演讲备注</div>
|
|
||||||
<el-switch v-model="outlineData.is_card_note" />
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="6" class="el-col">
|
|
||||||
<div class="grid-content-2">
|
|
||||||
<div>生成封面</div>
|
|
||||||
<el-switch v-model="outlineData.is_cover_img" />
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="6" class="el-col">
|
|
||||||
<div class="grid-content-1">
|
|
||||||
<div>自动配图</div>
|
|
||||||
<el-switch v-model="outlineData.is_figure" />
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="6" class="el-col">
|
|
||||||
<div class="grid-content-2">
|
|
||||||
<div>PPT作者名:</div>
|
|
||||||
<el-input v-model="outlineData.author" style="width: 50%" />
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<div>
|
|
||||||
<el-button style="margin-bottom: 5px;" type="primary" @click="outlineCreatePPT()">生成PPT</el-button>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
<el-card v-if="activeStep == 4">
|
|
||||||
<el-progress :percentage="30" type="circle" v-if="percentage == 30"></el-progress>
|
|
||||||
<el-progress :percentage="70" type="circle" v-if="percentage == 70"></el-progress>
|
|
||||||
<el-progress :percentage="100" type="circle" v-if="percentage == 100"></el-progress>
|
|
||||||
</el-card>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, onMounted } from "vue";
|
|
||||||
import { ElMessage } from 'element-plus'
|
|
||||||
import {
|
|
||||||
getBackGround,
|
|
||||||
createPPT,
|
|
||||||
getProgress,
|
|
||||||
} from "@/utils/ppt-request.js";
|
|
||||||
import { uploadDoc, queryDocStatus } from "@/utils/aichat.js";
|
|
||||||
import CryptoJS from "crypto-js"
|
|
||||||
|
|
||||||
import { getSignature } from "@/utils/index.js";
|
|
||||||
|
|
||||||
let appId = "01ec9aa3";
|
|
||||||
let secret = "M2QxMDAxMjYyYTEzODMwMGRkZTQ4NmUy";
|
|
||||||
let apikey = "39d05b269fa229f431a56c21794a8ea5"
|
|
||||||
let timestamp = Math.floor(Date.now() / 1000);
|
|
||||||
let signature = getSignature(appId, secret, timestamp);
|
|
||||||
const { ipcRenderer } = window.electron || {}
|
|
||||||
|
|
||||||
|
|
||||||
const outputText = ref(""); // 用于展示的大纲数据
|
|
||||||
const stagingData = ref([]); //储存的对话数据,用于多轮对话
|
|
||||||
const stagOutputText = ref(""); // 暂存大纲用于拆分
|
|
||||||
let extractedParts = ref([]) // 初步拆分
|
|
||||||
|
|
||||||
let firstArray = ref([]); //大纲的大纲等级数字部分
|
|
||||||
let secondArray = ref([]); //大纲的文字部分
|
|
||||||
|
|
||||||
|
|
||||||
const backGroundList = ref([]);
|
|
||||||
|
|
||||||
const inputTheme = ref(""); // 输入的主题
|
|
||||||
const inputRequire = ref("") // 输入的需求
|
|
||||||
const activeStep = ref(0); // 上方进度条
|
|
||||||
const fixRequire = ref(""); // 对话修改大纲
|
|
||||||
const combined = ref('') // 修改完毕的大纲数据,准备传入ppt生成模型
|
|
||||||
|
|
||||||
const treeData = ref([]);
|
|
||||||
const status = ref("init");
|
|
||||||
|
|
||||||
const percentage = ref(0);
|
|
||||||
|
|
||||||
const activeName = ref("first");
|
|
||||||
|
|
||||||
const getBackground = () => {
|
|
||||||
treeData.value = [];
|
|
||||||
getBackGround().then((res) => {
|
|
||||||
console.log(res);
|
|
||||||
backGroundList.value = res.data;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const getBackgroundColor = (key) => {
|
|
||||||
return outlineData.value.theme === key ? '#83e2b6' : '#f5f5f5';
|
|
||||||
};
|
|
||||||
|
|
||||||
const outlineData = ref({
|
|
||||||
query: '', // 用户要求(最多8000字)
|
|
||||||
theme: 'auto', // ppt生成主题
|
|
||||||
author: 'AIX平台',
|
|
||||||
is_card_note: false, // 是否自动生成ppt演讲备注
|
|
||||||
is_cover_img: false, // 是否自动生成封面
|
|
||||||
is_figure: false, // 是否自动配图
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// 将输入数据或返回数据存入记忆中
|
|
||||||
function updateStagingData(role, newData) {
|
|
||||||
stagingData.value.push({ role: role, content: newData });
|
|
||||||
}
|
|
||||||
//大纲直接生成ppt
|
|
||||||
const outlineCreatePPT = () => {
|
|
||||||
const newOutlineData = { ...outlineData.value, };
|
|
||||||
newOutlineData.query = combined.value;
|
|
||||||
|
|
||||||
createPPT(newOutlineData).then((res) => {
|
|
||||||
console.log(res, "正在生成中");
|
|
||||||
activeStep.value = 4
|
|
||||||
|
|
||||||
const checkProgress = () => {
|
|
||||||
getProgress(res.data.sid).then((response) => {
|
|
||||||
percentage.value = response.data.process;
|
|
||||||
if (response.data && response.data.pptUrl && response.data.pptUrl.length > 4) {
|
|
||||||
console.log('PPT',response)
|
|
||||||
//TODO window.location.href = response.data.pptUrl;
|
|
||||||
ElMessage.success("生成成功");
|
|
||||||
} else {
|
|
||||||
const sleepTime = 2000;
|
|
||||||
let remainingTime = sleepTime;
|
|
||||||
const intervalId = setInterval(() => {
|
|
||||||
remainingTime -= 100;
|
|
||||||
if (remainingTime <= 0) {
|
|
||||||
clearInterval(intervalId);
|
|
||||||
checkProgress();
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
checkProgress();
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
//初次对话
|
|
||||||
const addMessage = () => {
|
|
||||||
const themeValue = inputTheme.value;
|
|
||||||
const requireValue = inputRequire.value;
|
|
||||||
firstArray.value = []
|
|
||||||
secondArray.value = []
|
|
||||||
extractedParts.value = []
|
|
||||||
stagOutputText.value = ''
|
|
||||||
const combinedString = `请帮我生成一个ppt大纲,主题为:${themeValue}。具体内容要求为:${requireValue}。注意,用三个等级大纲展示,如1. 1.1 1.1.2 2. 2.1这种类型,且按照这种顺序,不要有完全相同数字等级的大纲,不要有目录`
|
|
||||||
updateStagingData("user", combinedString);
|
|
||||||
connectWebSocket(stagingData.value);
|
|
||||||
activeStep.value = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
//修改大纲时和ai对话
|
|
||||||
const fixOutline = () => {
|
|
||||||
outputText.value = '';
|
|
||||||
firstArray.value = [];
|
|
||||||
secondArray.value = [];
|
|
||||||
extractedParts.value = []
|
|
||||||
stagOutputText.value = ''
|
|
||||||
|
|
||||||
const fixValue = fixRequire.value;
|
|
||||||
updateStagingData('user', fixValue)
|
|
||||||
activeStep.value = 1
|
|
||||||
if (enableButton.value) {
|
|
||||||
uploadAndAskMainContent(stagingData.value)
|
|
||||||
} else { connectWebSocket(stagingData.value); }
|
|
||||||
fixRequire.value = ''
|
|
||||||
};
|
|
||||||
|
|
||||||
// 大纲的拆分
|
|
||||||
function extractAndRemove() {
|
|
||||||
let startIndex = -1;
|
|
||||||
let endIndex = -1;
|
|
||||||
|
|
||||||
for (let i = 0; i < stagOutputText.value.length; i++) {
|
|
||||||
const char = stagOutputText.value[i];
|
|
||||||
if (!isNaN(parseInt(char))) {
|
|
||||||
startIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (startIndex !== -1) {
|
|
||||||
for (let j = startIndex; j < stagOutputText.value.length; j++) {
|
|
||||||
const char2 = stagOutputText.value[j];
|
|
||||||
if (char2 === '\n') {
|
|
||||||
endIndex = j;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let extractedPart = '';
|
|
||||||
if (startIndex !== -1 && endIndex !== -1) {
|
|
||||||
extractedPart = stagOutputText.value.slice(startIndex, endIndex).replace(/\n/g, '');
|
|
||||||
extractedParts.value.push(extractedPart);
|
|
||||||
stagOutputText.value = stagOutputText.value.replace(extractedPart, '');
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 开始大纲拆分
|
|
||||||
function startExtraction() {
|
|
||||||
stagOutputText.value = outputText.value
|
|
||||||
while (extractAndRemove()) { }
|
|
||||||
|
|
||||||
//将拆分好的大纲的大纲等级和文字部分拆分到两个数组中
|
|
||||||
extractedParts.value.forEach(item => {
|
|
||||||
const parts = item.split(' ');
|
|
||||||
if (parts.length === 2) {
|
|
||||||
firstArray.value.push(parts[0]);
|
|
||||||
secondArray.value.push({ value: parts[1] });
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 拼接修改完毕的大纲
|
|
||||||
function combineOutline() {
|
|
||||||
let tempCombined = '';
|
|
||||||
for (let i = 0; i < Math.max(firstArray.value.length, secondArray.value.length); i++) {
|
|
||||||
tempCombined += firstArray.value[i] || '';
|
|
||||||
tempCombined += secondArray.value[i] ? secondArray.value[i].value : '';
|
|
||||||
tempCombined += i < Math.max(firstArray.value.length, secondArray.value.length) - 1 ? ',' : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
combined.value = tempCombined;
|
|
||||||
fixRequire.value = ''
|
|
||||||
activeStep.value = 3
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let ttsWS
|
|
||||||
function connectWebSocket(data) {
|
|
||||||
outputText.value = ""; //清楚展示部分内容
|
|
||||||
status.value = "ttsing";
|
|
||||||
return getWebsocketUrl().then((url) => {
|
|
||||||
ttsWS = new WebSocket(url);
|
|
||||||
ttsWS.onopen = () => {
|
|
||||||
webSocketSend(ttsWS, data);
|
|
||||||
};
|
|
||||||
ttsWS.onmessage = (e) => {
|
|
||||||
result1(e.data);
|
|
||||||
};
|
|
||||||
ttsWS.onerror = (e) => {
|
|
||||||
status.value = "error";
|
|
||||||
console.log("WebSocket error:", e);
|
|
||||||
};
|
|
||||||
ttsWS.onclose = () => {
|
|
||||||
status.value = "init";
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getWebsocketUrl() {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
var apiKey = apikey;
|
|
||||||
var apiSecret = secret;
|
|
||||||
var url = "wss://spark-api.xf-yun.com/v4.0/chat";
|
|
||||||
|
|
||||||
var host = "spark-api.xf-yun.com";
|
|
||||||
var date = new Date().toGMTString();
|
|
||||||
var algorithm = "hmac-sha256";
|
|
||||||
var headers = "host date request-line";
|
|
||||||
var signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v4.0/chat HTTP/1.1`;
|
|
||||||
var signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret);
|
|
||||||
var signature = CryptoJS.enc.Base64.stringify(signatureSha);
|
|
||||||
|
|
||||||
var authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`;
|
|
||||||
var authorization = CryptoJS.enc.Base64.stringify(
|
|
||||||
CryptoJS.enc.Utf8.parse(authorizationOrigin)
|
|
||||||
);
|
|
||||||
|
|
||||||
url = `${url}?authorization=${authorization}&date=${date}&host=${host}`;
|
|
||||||
console.log(url);
|
|
||||||
resolve(url);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function webSocketSend(ws, data) {
|
|
||||||
const params = {
|
|
||||||
header: {
|
|
||||||
app_id: appId,
|
|
||||||
},
|
|
||||||
parameter: {
|
|
||||||
chat: {
|
|
||||||
domain: "4.0Ultra",
|
|
||||||
temperature: 0.5,
|
|
||||||
max_tokens: 1024,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
payload: {
|
|
||||||
message: {
|
|
||||||
text: data,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
ws.send(JSON.stringify(params));
|
|
||||||
}
|
|
||||||
|
|
||||||
function result1(resultData) {
|
|
||||||
let jsonData = JSON.parse(resultData);
|
|
||||||
outputText.value += jsonData.payload.choices.text[0].content;
|
|
||||||
const div = document.querySelector('.paragraphs');
|
|
||||||
if (div) {
|
|
||||||
div.scrollTop = div.scrollHeight;
|
|
||||||
}
|
|
||||||
if (jsonData.payload && jsonData.payload.usage) {
|
|
||||||
startExtraction() // 返回完毕后开始拆分大纲
|
|
||||||
console.log(firstArray.value, secondArray.value)
|
|
||||||
activeStep.value = 2
|
|
||||||
updateStagingData("assistant", outputText.value) //返回数据存入记忆池
|
|
||||||
}
|
|
||||||
if (jsonData.header.code !== 0) {
|
|
||||||
alert(`提问失败: ${jsonData.header.code}:${jsonData.header.message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const selectedFile = ref(null);
|
|
||||||
|
|
||||||
const maxSize = 1024 * 1024 * 20;
|
|
||||||
const allowedTypes = [
|
|
||||||
"application/pdf",
|
|
||||||
"application/msword",
|
|
||||||
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
||||||
"text/markdown",
|
|
||||||
"text/plain",
|
|
||||||
];
|
|
||||||
const fileType = "wiki";
|
|
||||||
const parseType = "AUTO";
|
|
||||||
const upfileId = ref('');
|
|
||||||
const docName = ref('')
|
|
||||||
|
|
||||||
const docTheme = ref('') // 文档生成ppt的主题
|
|
||||||
const docRequire = ref('') // 文档生成要求
|
|
||||||
|
|
||||||
const onFileChange = (file) => {
|
|
||||||
console.log(file);
|
|
||||||
if (!allowedTypes.includes(file.raw.type)) {
|
|
||||||
console.error("不支持的文件类型");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (file.size > maxSize) {
|
|
||||||
console.error("文件过大");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
docName.value = file.raw.name
|
|
||||||
selectedFile.value = file.raw;
|
|
||||||
uploadFile();
|
|
||||||
};
|
|
||||||
const beforeUpload = (file) => {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
const uploadFile = async () => {
|
|
||||||
if (!selectedFile.value) {
|
|
||||||
console.error("请先选择文件");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append("file", selectedFile.value);
|
|
||||||
formData.append("fileType", fileType);
|
|
||||||
formData.append("parseType", parseType);
|
|
||||||
|
|
||||||
for (const pair of formData.entries()) {
|
|
||||||
console.log(pair[0], pair[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await uploadDoc(formData);
|
|
||||||
const upfileData = new FormData();
|
|
||||||
upfileId.value = response.data.data.fileId;
|
|
||||||
upfileData.append("fileIds", upfileId.value);
|
|
||||||
askdoc(upfileData);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//文档状态查询
|
|
||||||
const askdoc = async (data) => {
|
|
||||||
const response = await queryDocStatus(data);
|
|
||||||
if (response.data.data && response.data.data.length > 0) {
|
|
||||||
let foundVectored = false;
|
|
||||||
for (const item of response.data.data) {
|
|
||||||
if (item.fileStatus === 'vectored') {
|
|
||||||
foundVectored = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (foundVectored) {
|
|
||||||
enableButton.value = true
|
|
||||||
} else {
|
|
||||||
const sleepTime = 2000;
|
|
||||||
let remainingTime = sleepTime;
|
|
||||||
const intervalId = setInterval(() => {
|
|
||||||
remainingTime -= 100;
|
|
||||||
if (remainingTime <= 0) {
|
|
||||||
clearInterval(intervalId);
|
|
||||||
askdoc(data);
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//文档对话
|
|
||||||
function chatDoc() {
|
|
||||||
const docthemeValue = docTheme.value;
|
|
||||||
const docrequireValue = docRequire.value;
|
|
||||||
firstArray.value = []
|
|
||||||
secondArray.value = []
|
|
||||||
extractedParts.value = []
|
|
||||||
stagOutputText.value = ''
|
|
||||||
const combinedString = `请帮我生成一个ppt大纲,主题为:${docthemeValue}。具体内容要求为:${docrequireValue}。注意,用三个等级大纲展示,如1. 1.1 1.1.2 2. 2.1这种类型,且按照这种顺序,不要有完全相同数字等级的大纲,不要有目录`
|
|
||||||
updateStagingData("user", combinedString);
|
|
||||||
activeStep.value = 1
|
|
||||||
uploadAndAskMainContent(stagingData.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function chatByDoc(fileId, data) {
|
|
||||||
const wsUrl = `wss://chatdoc.xfyun.cn/openapi/chat?fileId=${fileId}&appId=${appId}×tamp=${timestamp}&signature=${signature}`;
|
|
||||||
|
|
||||||
const ws = new WebSocket(wsUrl);
|
|
||||||
|
|
||||||
ws.onopen = () => {
|
|
||||||
const messageBody = {
|
|
||||||
fileIds: [fileId],
|
|
||||||
messages: data,
|
|
||||||
chatExtends: {
|
|
||||||
wikiPromptTpl:
|
|
||||||
"请将以下内容作为已知信息:\n<wikicontent>\n请根据以上内容回答用户的问题。\n问题:<wikiquestion>\n回答:",
|
|
||||||
wikiFilterScore: 0.82,
|
|
||||||
temperature: 0.5,
|
|
||||||
sparkWhenWithoutEmbedding: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
ws.send(JSON.stringify(messageBody));
|
|
||||||
};
|
|
||||||
|
|
||||||
ws.onmessage = (event) => {
|
|
||||||
const response = JSON.parse(event.data);
|
|
||||||
result2(response)
|
|
||||||
console.log("WebSocket 消息:", response);
|
|
||||||
};
|
|
||||||
|
|
||||||
ws.onerror = (error) => {
|
|
||||||
console.error("WebSocket 错误:", error);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function result2(resultData) {
|
|
||||||
const div = document.querySelector('.paragraphs');
|
|
||||||
if (div) {
|
|
||||||
div.scrollTop = div.scrollHeight;
|
|
||||||
}
|
|
||||||
if (resultData.status == 1) {
|
|
||||||
outputText.value += resultData.content
|
|
||||||
}
|
|
||||||
if (resultData.status == 2) {
|
|
||||||
startExtraction() // 返回完毕后开始拆分大纲
|
|
||||||
activeStep.value = 2
|
|
||||||
updateStagingData("assistant", outputText.value) //返回数据存入记忆池
|
|
||||||
}
|
|
||||||
if (resultData.code !== 0) {
|
|
||||||
alert(`提问失败: ${jsonData.header.code}:${jsonData.header.message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//文档上传问题查询
|
|
||||||
const uploadAndAskMainContent = async (data) => {
|
|
||||||
try {
|
|
||||||
// const formData = new FormData();
|
|
||||||
// formData.append(
|
|
||||||
// "url",
|
|
||||||
// "https://bjcdn.openstorage.cn/xinghuo/chatdocs/2024-09-13/dad35a4f-e7c1-4efc-b6df-cae763cb984b/39052b09-d154-419f-9832-20884adeb2f41726226211177.pdf"
|
|
||||||
// );
|
|
||||||
// formData.append("fileName", "test.pdf");
|
|
||||||
// formData.append("appId", "2ff2cc26");
|
|
||||||
// formData.append("secret", "YTMyZWFiOGVlYTc5ZGM5NGIwOTU3NWMx");
|
|
||||||
// // formData.append("fileType", "wiki");
|
|
||||||
// // formData.append("parseType", "AUTO");
|
|
||||||
|
|
||||||
// const uploadResp = await uploadDoc(formData);
|
|
||||||
// const fileId = uploadResp.data.data.fileId;
|
|
||||||
const fileId = upfileId.value;
|
|
||||||
chatByDoc(fileId, data);
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error("上传或提问过程中发生错误:", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const enableButton = ref(false);
|
|
||||||
|
|
||||||
const chooseBackground = (data) => {
|
|
||||||
outlineData.value.theme = data
|
|
||||||
}
|
|
||||||
|
|
||||||
const changeCursor = (cursorStyle) => {
|
|
||||||
document.documentElement.style.cursor = cursorStyle;
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
connectWebSocket("");
|
|
||||||
getBackground();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.ai-container {
|
|
||||||
width: 100%;
|
|
||||||
background-color: #f5f7f6;
|
|
||||||
padding: 20px
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-box {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card1 {
|
|
||||||
padding: 0;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.paragraphs {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
text-align: left;
|
|
||||||
max-height: 60vh;
|
|
||||||
overflow-y: auto;
|
|
||||||
border: 1px solid #409EFF;
|
|
||||||
padding: 10px;
|
|
||||||
margin: 5px
|
|
||||||
}
|
|
||||||
|
|
||||||
.themes {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
height: 250px;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.outline {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
text-align: left;
|
|
||||||
|
|
||||||
border: 1px solid #409EFF;
|
|
||||||
padding: 10px;
|
|
||||||
outline-style: none;
|
|
||||||
/* margin: 5px */
|
|
||||||
}
|
|
||||||
|
|
||||||
.outline-row {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.outline-row>.el-col {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column
|
|
||||||
}
|
|
||||||
|
|
||||||
.outline-row>.el-col>div,
|
|
||||||
.outline-row>.el-col>div>.el-input {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-with-dash {
|
|
||||||
margin-left: 100px
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-with-dash::after {
|
|
||||||
content: "";
|
|
||||||
border-bottom: 1px dashed #000;
|
|
||||||
flex-grow: 1;
|
|
||||||
margin-left: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-content-1 {
|
|
||||||
border-radius: 4px;
|
|
||||||
background-color: #c2dbf3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-content-2 {
|
|
||||||
border-radius: 4px;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-row {
|
|
||||||
padding: 20px
|
|
||||||
}
|
|
||||||
:deep(.el-card__body){
|
|
||||||
padding: 10px 15px;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -24,10 +24,9 @@
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<c-form v-bind="classForm">
|
<c-form v-bind="classForm">
|
||||||
<template #item_classid="{prop, form}">
|
<template #item_classid="{prop, form}">
|
||||||
<span v-if="dt.ctCourse">{{ dt.ctCourse?.caption }}</span>
|
<el-select v-model="form[prop]" placeholder="请选择班级">
|
||||||
<el-select v-else v-model="form[prop]" placeholder="请选择班级">
|
|
||||||
<el-option v-for="item in listData.classList" :value="item.id"
|
<el-option v-for="item in listData.classList" :value="item.id"
|
||||||
:label="`${item.caption} (${item.classstudentcount}人)`" />
|
:label="`${item.caption} (${item.classstudentcount}人)`" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</template>
|
</template>
|
||||||
</c-form>
|
</c-form>
|
||||||
|
@ -119,9 +118,7 @@ const dt = reactive({ // 其他数据
|
||||||
isHistory: false, // 是否显示-历史记录
|
isHistory: false, // 是否显示-历史记录
|
||||||
loading: false, // 加载-loading
|
loading: false, // 加载-loading
|
||||||
loadingDel: false, // 删除-loading
|
loadingDel: false, // 删除-loading
|
||||||
atClass: {}, // 当前班级
|
|
||||||
atCourse: {}, // 当前课程
|
atCourse: {}, // 当前课程
|
||||||
ctCourse: null, // 继续课程
|
|
||||||
})
|
})
|
||||||
let chat = null // im-chat 对象
|
let chat = null // im-chat 对象
|
||||||
|
|
||||||
|
@ -131,10 +128,9 @@ onMounted(() => {
|
||||||
})
|
})
|
||||||
/**
|
/**
|
||||||
* @description 暴露方法-打开对话框
|
* @description 暴露方法-打开对话框
|
||||||
* @param id 课件id
|
* @param row 课件对象
|
||||||
* @param classObj 课程对象-用于继续上课
|
|
||||||
*/
|
*/
|
||||||
const open = async (id, classObj) => {
|
const open = async (id) => {
|
||||||
visible.value = true
|
visible.value = true
|
||||||
if (id) {
|
if (id) {
|
||||||
// 重置数据
|
// 重置数据
|
||||||
|
@ -143,11 +139,6 @@ const open = async (id, classObj) => {
|
||||||
await getAptInfo(id)
|
await getAptInfo(id)
|
||||||
// 获取班级列表
|
// 获取班级列表
|
||||||
getClassList()
|
getClassList()
|
||||||
// 继续上课
|
|
||||||
if (!!classObj) {
|
|
||||||
dt.ctCourse = classObj
|
|
||||||
teacherForm.form.classcourseid = classObj.id
|
|
||||||
}
|
|
||||||
// 初始化im-chat
|
// 初始化im-chat
|
||||||
nextTick(async() => {
|
nextTick(async() => {
|
||||||
chat = await imChatRef.value?.initImChat()
|
chat = await imChatRef.value?.initImChat()
|
||||||
|
@ -157,9 +148,8 @@ const open = async (id, classObj) => {
|
||||||
// 关闭弹窗
|
// 关闭弹窗
|
||||||
const handleClose = async () => {
|
const handleClose = async () => {
|
||||||
reset() // 重置数据
|
reset() // 重置数据
|
||||||
// await chat?.logout()
|
await chat?.logout()
|
||||||
// chat = null
|
chat = null
|
||||||
dt.ctCourse = null
|
|
||||||
emit('close')
|
emit('close')
|
||||||
}
|
}
|
||||||
// 初始化-数据
|
// 初始化-数据
|
||||||
|
@ -187,10 +177,8 @@ const reset = () => {
|
||||||
teacherForm.form = { classcourseid: 0 }
|
teacherForm.form = { classcourseid: 0 }
|
||||||
dt.isCreate = false
|
dt.isCreate = false
|
||||||
dt.isHistory = false
|
dt.isHistory = false
|
||||||
dt.atClass = {}
|
|
||||||
dt.atCourse = {}
|
dt.atCourse = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取课件APT
|
// 获取课件APT
|
||||||
const getAptInfo = async (id) => {
|
const getAptInfo = async (id) => {
|
||||||
const res = await Http_Entpcoursefile.getEntpcoursefile(id)
|
const res = await Http_Entpcoursefile.getEntpcoursefile(id)
|
||||||
|
@ -350,9 +338,9 @@ const chatChange = (type, data, ...args) => {
|
||||||
// 监听-班级id
|
// 监听-班级id
|
||||||
watch(() => classForm.form.classid, (val)=> {
|
watch(() => classForm.form.classid, (val)=> {
|
||||||
// 获取选中课程
|
// 获取选中课程
|
||||||
dt.atClass = listData.classList.find(o => o.id === val) || {}
|
dt.atCourse = listData.classList.find(o => o.id === val) || {}
|
||||||
// 获取选中课程-学生列表
|
// 获取选中课程-学生列表
|
||||||
// listData.activeStudentList = dt.atClass?.classstudentlist || []
|
listData.activeStudentList = dt.atCourse?.classstudentlist || []
|
||||||
// 清空课程列表
|
// 清空课程列表
|
||||||
listData.classcourseList = []
|
listData.classcourseList = []
|
||||||
// 如果当前显示历史, 就从新获取
|
// 如果当前显示历史, 就从新获取
|
||||||
|
|
|
@ -102,7 +102,7 @@ import { deleteSmarttalk, updateSmarttalk, getPrepareById } from '@/api/file'
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
import outLink from '@/utils/linkConfig'
|
import outLink from '@/utils/linkConfig'
|
||||||
import { sessionStore } from '@/utils/store'
|
import { sessionStore } from '@/utils/store'
|
||||||
import { listClasscourseNew, updateClasscourse } from '@/api/teaching/classcourse'
|
import { listClasscourseNew } from '@/api/teaching/classcourse'
|
||||||
import { endClass, getSelfReserv } from '@/api/classManage'
|
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'
|
||||||
|
@ -137,7 +137,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
expose: ['openFileWin'],
|
expose: ['openFileWin'],
|
||||||
emits: { 'on-start-class': null, 'on-delete': null, 'on-set': null, 'on-delhomework': null,'on-filearg': null, 'change': null },
|
emits: { 'on-start-class': null, 'on-delete': null, 'on-set': null, 'on-delhomework': null,'on-filearg': null },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
listenList: [],
|
listenList: [],
|
||||||
|
@ -148,24 +148,27 @@ export default {
|
||||||
this.userInfo = useUserStore().user
|
this.userInfo = useUserStore().user
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// 获取当前上课的课程列表
|
getOpenCourse() {
|
||||||
getOpenCourse(isApt) {
|
return Promise.all([listClasscourseNew({teacherid: this.userInfo.userId,status:"open",evalid: this.curNode.id,pageSize:1000}), getSelfReserv({ex2:this.curNode.id})]).then(([res1,res2])=>{
|
||||||
const curNodeId = this.curNode.id
|
let list2 = res1.rows || []
|
||||||
if (isApt) { // APT课程
|
let list = res2.data || []
|
||||||
const params = {teacherid: this.userInfo.userId,status:"open",evalid: curNodeId,pageSize:1000}
|
let one = list.find(item1 => {
|
||||||
return listClasscourseNew(params).then(res => {
|
if (item1.status === "上课中") {
|
||||||
return (res.rows || [])
|
return true
|
||||||
|
}
|
||||||
})
|
})
|
||||||
} else { // 普通课程PPT
|
if (one) {
|
||||||
return getSelfReserv({ex2: curNodeId}).then(res => {
|
return one
|
||||||
return (res.data || []).filter(o => o.status === "上课中")
|
}
|
||||||
})
|
if (list2.length>0) {
|
||||||
}
|
one = list2[0]
|
||||||
|
}
|
||||||
|
return one
|
||||||
|
})
|
||||||
},
|
},
|
||||||
clickStartClass(item) {
|
clickStartClass(item) {
|
||||||
const isApt = item.fileFlag === 'apt'
|
this.getOpenCourse().then(res=>{
|
||||||
this.getOpenCourse(isApt).then(res => {
|
if(!res){
|
||||||
if(!res || res.length === 0){
|
|
||||||
this.$emit('on-start-class', item)
|
this.$emit('on-start-class', item)
|
||||||
}else{
|
}else{
|
||||||
ElMessageBox.alert('<strong>上次课程尚未结束,是否继续上课?</strong>', '', {
|
ElMessageBox.alert('<strong>上次课程尚未结束,是否继续上课?</strong>', '', {
|
||||||
|
@ -182,80 +185,51 @@ export default {
|
||||||
confirmButtonClass: "el-button--danger",
|
confirmButtonClass: "el-button--danger",
|
||||||
center: true,
|
center: true,
|
||||||
beforeClose: (action, instance, done) => {
|
beforeClose: (action, instance, done) => {
|
||||||
const obj = res[0]
|
|
||||||
// console.log(action, obj, item)
|
|
||||||
if (action === 'confirm'){
|
if (action === 'confirm'){
|
||||||
// 下课
|
// 下课
|
||||||
this.$emit('change', 'close', obj, { type: 1, instance, done })
|
if (res.bookImg) {
|
||||||
// if (obj.bookImg) {
|
|
||||||
// // //PPT
|
|
||||||
// // endClass(obj.id).then((res1) => {
|
|
||||||
// // if (res1.data === true) {
|
|
||||||
// // ElMessage({
|
|
||||||
// // message: '下课成功',
|
|
||||||
// // type: 'success'
|
|
||||||
// // })
|
|
||||||
// // obj.status = '已结束'
|
|
||||||
// // done()
|
|
||||||
// // }
|
|
||||||
// // })
|
|
||||||
// }else {
|
|
||||||
// //APT 结束课程-父组件触发
|
|
||||||
// // this.$emit('change', 'close', obj, { type: 1, instance, done })
|
|
||||||
// // this.closeCourse(obj, instance, done)
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
if (action === 'cancel'){
|
|
||||||
// 继续上课
|
|
||||||
if (obj.bookImg) {
|
|
||||||
console.log('PPT')
|
|
||||||
//PPT
|
//PPT
|
||||||
listEntpcourse({
|
endClass(res.id).then((res1) => {
|
||||||
evalid: obj.ex2,
|
if (res1.data === true) {
|
||||||
edituserid: useUserStore().user.userId,
|
ElMessage({
|
||||||
pageSize: 500
|
message: '下课成功',
|
||||||
}).then(async res1=>{
|
type: 'success'
|
||||||
if (res1.rows[0].id) {
|
})
|
||||||
createWindow('tool-sphere', { url: '/tool/sphere?entpcourseid=' + res1.rows[0].id + "&reservId=" + obj.id })
|
res.status = '已结束'
|
||||||
|
done()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}else {
|
||||||
|
//APT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (action === 'cancel'){
|
||||||
|
// 继续上课
|
||||||
|
if (res.bookImg) {
|
||||||
|
//PPT
|
||||||
|
listEntpcourse({
|
||||||
|
evalid: res.ex2,
|
||||||
|
edituserid: useUserStore().user.userId,
|
||||||
|
pageSize: 500
|
||||||
|
}).then(async res1=>{
|
||||||
|
if (res1.rows[0].id) {
|
||||||
|
createWindow('tool-sphere', { url: '/tool/sphere?entpcourseid=' + res1.rows[0].id + "&reservId=" + res.id })
|
||||||
done()
|
done()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}else {
|
}else {
|
||||||
//APT
|
//APT
|
||||||
this.$emit('on-start-class', item, obj)
|
|
||||||
done()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (action === 'close') {
|
if (action === 'close') {
|
||||||
done()
|
done()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}).catch(() => {})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// this.$emit('on-start-class', item)
|
// this.$emit('on-start-class', item)
|
||||||
},
|
},
|
||||||
// 结束课程(下课)
|
|
||||||
// async closeCourse(row, instance, done) {
|
|
||||||
// instance.confirmButtonLoading = true
|
|
||||||
// instance.confirmButtonText = '下课中...'
|
|
||||||
// // 发送-下课消息
|
|
||||||
// if (!!row.timgroupid) {
|
|
||||||
// const msg = { msgKey: 'closed', actor: 'teacher', classcourseid: row.id }
|
|
||||||
// Chat.sendMsg(row.timgroupid, msg)
|
|
||||||
// }
|
|
||||||
// // 接口-修改状态
|
|
||||||
// const params = { id: row.id, status: 'closed', timgroupid: '' }
|
|
||||||
// await updateClasscourse(params)
|
|
||||||
// // 解散群
|
|
||||||
// setTimeout(async() => {
|
|
||||||
// if (!!row.timgroupid) await Chat.dismissGroup(row.timgroupid)
|
|
||||||
// instance.confirmButtonLoading = false
|
|
||||||
// instance.confirmButtonText = '下课'
|
|
||||||
// done()
|
|
||||||
// ElMessage({ type: 'success', message: `下课成功!` })
|
|
||||||
// }, 1000)
|
|
||||||
// },
|
|
||||||
editTalk(item) {
|
editTalk(item) {
|
||||||
ElMessageBox.prompt('请输入新的标签', '添加标签', {
|
ElMessageBox.prompt('请输入新的标签', '添加标签', {
|
||||||
confirmButtonText: '确认',
|
confirmButtonText: '确认',
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<el-dialog class="ppt-dialog" v-model="model" :show-close="false" width="900" destroy-on-close :top="'3vh'">
|
|
||||||
<template #header="{ close, titleId, titleClass }">
|
|
||||||
<div class="dialog-header">
|
|
||||||
<h4 :id="titleId" :class="titleClass">生成PPT(试验版)</h4>
|
|
||||||
<i class="iconfont icon-guanbi" @click="close"></i>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<AiPpt/>
|
|
||||||
</el-dialog>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import AiPpt from './ai-ppt.vue';
|
|
||||||
const model = defineModel()
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
:deep(.ppt-dialog){
|
|
||||||
-webkit-app-region: no-drag;
|
|
||||||
}
|
|
||||||
.dialog-header{
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
.icon-guanbi {
|
|
||||||
font-size: 20px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -13,10 +13,6 @@
|
||||||
<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 class="center-create-btn" style="background-color: #909399" @click="pptDialog = true">
|
|
||||||
<div class="create-btn-title"><el-icon><Plus /></el-icon><label>生成PPT</label></div>
|
|
||||||
<div class="create-btn-info">试验版</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="prepare-center-body">
|
<div class="prepare-center-body">
|
||||||
<kj-list-item
|
<kj-list-item
|
||||||
|
@ -28,14 +24,12 @@
|
||||||
:curNode="currentNode"
|
:curNode="currentNode"
|
||||||
@on-delete="deleteTalk"
|
@on-delete="deleteTalk"
|
||||||
@on-start-class="startClass"
|
@on-start-class="startClass"
|
||||||
@change="changeClass"
|
|
||||||
>
|
>
|
||||||
</kj-list-item>
|
</kj-list-item>
|
||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="教学实录" name="教学实录" class="prepare-center-jxsl">
|
<el-tab-pane label="教学实录" name="教学实录" class="prepare-center-jxsl">
|
||||||
<class-reserv v-if="activeAptTab==='教学实录'" :curNode="currentNode"
|
<class-reserv v-if="activeAptTab==='教学实录'" :curNode="currentNode"></class-reserv>
|
||||||
@change="changeClass"></class-reserv>
|
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
</div>
|
</div>
|
||||||
|
@ -137,7 +131,6 @@
|
||||||
></reserv>
|
></reserv>
|
||||||
<!-- 上课配置 -->
|
<!-- 上课配置 -->
|
||||||
<class-start ref="calssRef" @close="closeChange"/>
|
<class-start ref="calssRef" @close="closeChange"/>
|
||||||
<PptDialog v-model="pptDialog"/>
|
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { Check,Plus } from '@element-plus/icons-vue'
|
import { Check,Plus } from '@element-plus/icons-vue'
|
||||||
|
@ -155,7 +148,6 @@ import { useToolState } from '@/store/modules/tool'
|
||||||
import MoveFile from '@/components/move-file/index.vue'
|
import MoveFile from '@/components/move-file/index.vue'
|
||||||
import FileListItem from '@/views/prepare/container/file-list-item.vue'
|
import FileListItem from '@/views/prepare/container/file-list-item.vue'
|
||||||
import KjListItem from '@/views/prepare/container/kj-list-item.vue'
|
import KjListItem from '@/views/prepare/container/kj-list-item.vue'
|
||||||
import PptDialog from './container/ppt-dialog.vue'
|
|
||||||
import { getSmarttalkPage, moveSmarttalk, creatAPT } from '@/api/file'
|
import { getSmarttalkPage, moveSmarttalk, creatAPT } from '@/api/file'
|
||||||
import { toTimeText } from '@/utils/date'
|
import { toTimeText } from '@/utils/date'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
|
@ -163,21 +155,16 @@ import { parseCataByNode, creatPPT, asyncLocalFile } from '@/utils/talkFile'
|
||||||
import FileOperBatch from '@/views/prepare/container/file-oper-batch.vue'
|
import FileOperBatch from '@/views/prepare/container/file-oper-batch.vue'
|
||||||
import SetHomework from '@/components/set-homework/index.vue'
|
import SetHomework from '@/components/set-homework/index.vue'
|
||||||
import outLink from '@/utils/linkConfig'
|
import outLink from '@/utils/linkConfig'
|
||||||
import { createWindow, sessionStore, getAppInstallUrl, ipcMsgSend } from '@/utils/tool'
|
import { createWindow, sessionStore, getAppInstallUrl } from '@/utils/tool'
|
||||||
import { cloneDeep } from 'lodash'
|
import { cloneDeep } from 'lodash'
|
||||||
import { delClasswork, listEntpcourse } from '@/api/teaching/classwork'
|
import { delClasswork, listEntpcourse } from '@/api/teaching/classwork'
|
||||||
import { updateClasscourse } from '@/api/teaching/classcourse'
|
import { getClassInfo, getSelfReserv } from '@/api/classManage'
|
||||||
import { getClassInfo, getSelfReserv, endClass } from '@/api/classManage'
|
|
||||||
import { useGetHomework } from '@/hooks/useGetHomework'
|
import { useGetHomework } from '@/hooks/useGetHomework'
|
||||||
import { addEntpcoursefileReturnId } from '@/api/education/entpcoursefile'
|
import { addEntpcoursefileReturnId } from '@/api/education/entpcoursefile'
|
||||||
import ClassReserv from '@/views/classManage/classReserv.vue'
|
import ClassReserv from '@/views/classManage/classReserv.vue'
|
||||||
import classStart from './container/class-start.vue' // 预备上课
|
import classStart from './container/class-start.vue' // 预备上课
|
||||||
import MsgEnum from '@/plugins/imChat/msgEnum' // im 消息枚举
|
|
||||||
import Chat from '@/utils/chat' // im 登录初始化
|
|
||||||
import msgEnum from '@/plugins/imChat/msgEnum'
|
|
||||||
if (!Chat.imChat) Chat.init()
|
|
||||||
|
|
||||||
const toolStore = useToolState()
|
const toolStore = useToolState()
|
||||||
|
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const { ipcRenderer } = window.electron || {}
|
const { ipcRenderer } = window.electron || {}
|
||||||
|
|
||||||
|
@ -232,7 +219,6 @@ export default {
|
||||||
isOpenHomework: false,
|
isOpenHomework: false,
|
||||||
// 当前上课课程
|
// 当前上课课程
|
||||||
activeClass: null,
|
activeClass: null,
|
||||||
pptDialog: false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -286,8 +272,7 @@ export default {
|
||||||
// }
|
// }
|
||||||
// },
|
// },
|
||||||
methods: {
|
methods: {
|
||||||
// 开始上课
|
startClass(item) {
|
||||||
startClass(item, classObj) {
|
|
||||||
// 关闭状态,打开上课相关功能(已打开,忽略)
|
// 关闭状态,打开上课相关功能(已打开,忽略)
|
||||||
const id = sessionStore.has('activeClass.id') ? sessionStore.get('activeClass.id') : null
|
const id = sessionStore.has('activeClass.id') ? sessionStore.get('activeClass.id') : null
|
||||||
if (id && id == item.id) return ElMessage.warning('当前正在上课,请勿重复操作')
|
if (id && id == item.id) return ElMessage.warning('当前正在上课,请勿重复操作')
|
||||||
|
@ -298,68 +283,11 @@ export default {
|
||||||
this.openReserv()
|
this.openReserv()
|
||||||
}
|
}
|
||||||
if(item.fileFlag === 'apt') {
|
if(item.fileFlag === 'apt') {
|
||||||
this.$refs.calssRef.open(item.fileId, classObj)
|
this.$refs.calssRef.open(item.fileId)
|
||||||
}
|
|
||||||
},
|
|
||||||
// 继续上课-apt
|
|
||||||
async changeClass(type, row, other) {
|
|
||||||
switch(type) {
|
|
||||||
case 'continue': { // 继续上课
|
|
||||||
const aptFileId = row.entpcoursefileid
|
|
||||||
this.$refs.calssRef.open(aptFileId, row)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case 'close': { // 关闭上课
|
|
||||||
const head = MsgEnum.HEADS.MSG_closed // closed
|
|
||||||
const msgT = msgEnum.TYPES.TEACHER // teacher
|
|
||||||
const isApt = !row.bookImg // bookImg ppt 否则Apt
|
|
||||||
row.ex3 == 'undefined' && (row.ex3 = null)
|
|
||||||
const timgroupid = isApt ? row.timgroupid : row.ex3 // ex3 ppt 否则Apt
|
|
||||||
if (other.type == 1) { // 弹窗-下课
|
|
||||||
other.instance.confirmButtonLoading = true
|
|
||||||
other.instance.confirmButtonText = '下课中...'
|
|
||||||
} else { // 列表-下课
|
|
||||||
other.loading.value = true
|
|
||||||
}
|
|
||||||
// 发送-下课消息
|
|
||||||
if (!!timgroupid) {
|
|
||||||
const msg = { msgKey: head, actor: msgT, classcourseid: row.id }
|
|
||||||
Chat.sendMsg(timgroupid, msg)
|
|
||||||
}
|
|
||||||
if (isApt) { // Apt
|
|
||||||
// 接口-修改状态
|
|
||||||
await updateClasscourse({ id: row.id, status: head, timgroupid: '' })
|
|
||||||
} else { // PPT
|
|
||||||
const toolStore = useToolState()
|
|
||||||
// 窗口已打开,直接关闭
|
|
||||||
if (toolStore.isToolWin) {
|
|
||||||
toolStore.resetDef() // 重置状态
|
|
||||||
ipcMsgSend('tool-sphere:close') // 关闭窗口
|
|
||||||
}
|
|
||||||
// 接口-修改状态
|
|
||||||
await endClass(row.id)
|
|
||||||
}
|
|
||||||
// 解散群
|
|
||||||
setTimeout(async() => {
|
|
||||||
if (!!timgroupid) await Chat.dismissGroup(timgroupid)
|
|
||||||
if (other.type == 1) { // 弹窗-下课
|
|
||||||
other.instance.confirmButtonLoading = false
|
|
||||||
other.instance.confirmButtonText = '下课'
|
|
||||||
other.done()
|
|
||||||
} else {
|
|
||||||
other.loading.value = false
|
|
||||||
row.status = isApt ? head : '已结束'
|
|
||||||
}
|
|
||||||
ElMessage({ type: 'success', message: `下课成功!` })
|
|
||||||
}, 1000)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
closeChange() { // 上课弹窗被关闭-触发
|
closeChange() { // 上课弹窗被关闭-触发
|
||||||
// console.log('关闭上课弹窗')
|
console.log('关闭上课弹窗')
|
||||||
// this.activeClass = null
|
// this.activeClass = null
|
||||||
sessionStore.delete('activeClass')
|
sessionStore.delete('activeClass')
|
||||||
},
|
},
|
||||||
|
|
|
@ -239,7 +239,7 @@ const sideChange = async o => {
|
||||||
// 延迟2秒后关闭窗口,如果马上解散群,会导致群组不存在
|
// 延迟2秒后关闭窗口,如果马上解散群,会导致群组不存在
|
||||||
setTimeout(async() => {
|
setTimeout(async() => {
|
||||||
await imChatRef.value?.deleteGroup() // 解散群
|
await imChatRef.value?.deleteGroup() // 解散群
|
||||||
// await imChatRef.value?.logout() // 退出im
|
await imChatRef.value?.logout() // 退出im
|
||||||
elMsg.close()
|
elMsg.close()
|
||||||
ipcMsgSend('tool-sphere:close') // 关闭窗口
|
ipcMsgSend('tool-sphere:close') // 关闭窗口
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|
Loading…
Reference in New Issue