Compare commits

...

7 Commits

10 changed files with 957 additions and 18 deletions

View File

@ -43,6 +43,11 @@ export default defineConfig({
changeOrigin: true,
rewrite: (p) => p.replace(/^\/baidubce/, '')
},
'/parth': {
target: 'https://zwapi.xfyun.cn', // 第三方API的地址
changeOrigin: true, // 改变请求的起源
rewrite: (path) => path.replace(/^\/parth/, '') // 重写路径
},
},
},
plugins: [vue(), WindiCSS()],

View File

@ -10,12 +10,12 @@
<li class="flex" :class="[activeId == menu.path ? 'active-li' : '', menu.disabled ? 'disabled' : '']"
v-for="menu in headerMenus" :key="menu.id" @click="clickMenu(menu)">
<div class="icon-box">
<svg class="icon iconfont" aria-hidden="true">
<use :xlink:href="menu.icon"></use>
</svg>
</div>
<span class="text">{{ menu.name }}</span>
</li>
@ -80,8 +80,8 @@ import { updateUserInfo } from '@/api/system/user'
import logoIco from '@/assets/images/logo.png'
import { listEvaluation } from '@/api/classManage/index'
import { sessionStore } from '@/utils/store'
import Chat from '@/utils/chat' // im
if (!Chat.imChat) Chat.init()
// import Chat from '@/utils/chat' // im
// if (!Chat.imChat) Chat.init()
let homeTitle = ref(import.meta.env.VITE_APP_TITLE)
const { ipcRenderer } = window.electron || {}
@ -145,7 +145,7 @@ function handleCommand(command) {
break
case 'logout':
logout()
Chat?.logout() // im 退
// Chat?.logout() // im 退
break
default:
break
@ -161,7 +161,9 @@ function logout() {
cancelButtonText: '取消',
type: 'warning'
})
.then(() => {
.then(async () => {
const Chat = (await import('@/utils/chat')).default
if (!!Chat.imChat) Chat.logout()
userStore
.logOut()
.then(() => {
@ -172,8 +174,7 @@ function logout() {
// router.replace('/login')
ipcRenderer && ipcRenderer.send('openLoginWindow')
})
})
.catch(() => { })
}).catch(()=>{})
}
const emits = defineEmits(['setLayout'])
@ -183,10 +184,10 @@ function setLayout() {
//
const changeSubject = async (command) =>{
let sessionSubject = {
bookList: null,
curBook: null,
curNode: null,
defaultExpandedKeys: [],
bookList: null,
curBook: null,
curNode: null,
defaultExpandedKeys: [],
subjectTree: []
}
sessionStore.set( 'subject', sessionSubject)

View File

@ -0,0 +1,80 @@
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}&timestamp=${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);
};
}

View File

@ -20,9 +20,9 @@ export class Chat {
}
/**
* 初始化 获取IM签名
* @param {*} isInit : 是否初始化IM
* @param {*} isInit : 是否初始化IM
* @param {*} isLogin : 是否登录IM
* @param {*} callback: 监听消息回调函数
* @param {*} callback: 监听消息回调函数
* @returns Promise<ImChat>
*/
async init(isInit = true, isLogin = true, callback) {
@ -69,7 +69,6 @@ export class Chat {
async logout() {
if (!this.imChat) return
await this.imChat?.logout()
imChat = null
this.imChat = null
}
// 发群消息
@ -95,4 +94,4 @@ export class Chat {
}
}
export default new Chat()
export default new Chat()

View File

@ -0,0 +1,33 @@
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;
}
}

View File

@ -0,0 +1,78 @@
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 };

View File

@ -32,6 +32,8 @@ function getColor(index) {
//
function initChart() {
const myChart = echarts.init(chartRef.value);
const total = dataList.value.reduce((acc, cur) => acc + cur.value, 0); //
const options = {
tooltip: {
trigger: 'axis',
@ -70,7 +72,11 @@ function initChart() {
label: {
show: true,
position: 'top',
formatter: '{c}人',
formatter: params => {
const value = dataList.value[params.dataIndex].value;
const percentage = ((value / total) * 100).toFixed(2); //
return `${percentage}%`; //
},
color: '#333',
fontSize: 12
}

View File

@ -0,0 +1,697 @@
<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/docxpdfmdtxt 格式文档,不超过 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}&timestamp=${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>

View File

@ -0,0 +1,33 @@
<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>

View File

@ -13,6 +13,10 @@
<div class="create-btn-title"><el-icon><Plus /></el-icon><label>PPT</label></div>
<div class="create-btn-info">传统课件</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 class="prepare-center-body">
<kj-list-item
@ -133,6 +137,7 @@
></reserv>
<!-- 上课配置 -->
<class-start ref="calssRef" @close="closeChange"/>
<PptDialog v-model="pptDialog"/>
</template>
<script setup>
import { Check,Plus } from '@element-plus/icons-vue'
@ -150,6 +155,7 @@ import { useToolState } from '@/store/modules/tool'
import MoveFile from '@/components/move-file/index.vue'
import FileListItem from '@/views/prepare/container/file-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 { toTimeText } from '@/utils/date'
import { ElMessage } from 'element-plus'
@ -226,6 +232,7 @@ export default {
isOpenHomework: false,
//
activeClass: null,
pptDialog: false
}
},
computed: {