Browse Source

pblCourse对话

SanHQin 11 tháng trước cách đây
mục cha
commit
93e6791e52

BIN
src/assets/icon/pblCourse/aiTeacherAvatar.png


BIN
src/assets/icon/pblCourse/copyIcon.png


BIN
src/assets/icon/pblCourse/fileIcon.png


BIN
src/assets/icon/pblCourse/loading.gif


BIN
src/assets/icon/pblCourse/recordIcon.png


BIN
src/assets/icon/pblCourse/sendIcon.png


BIN
src/assets/icon/pblCourse/userAvatar.png


+ 527 - 8
src/components/pages/pblCourse/component/chatArea.vue

@@ -1,19 +1,538 @@
 <template>
-	<div class="chat">
-		<h1>对话</h1>
+	<div class="chat" v-loading="loading">
+		<div class="c_chat" ref="chatRef">
+			<div class="c_c_item" v-for="item in chatList" :key="item.uid">
+				<div class="c_c_i_user" v-if="item.content">
+					<div class="c_c_i_u_message">
+						<div class="c_c_i_u_m_top">
+							<span class="chatTime">{{ item.createtime }}</span>
+							<span>科科</span>
+						</div>
+						<div class="c_c_i_u_m_bottom">
+							<span class="coptText" @click="copyText(item.aiContent)"></span>
+							<div
+								class="c_c_i_u_m_content chatContent"
+								v-html="item.content"
+							></div>
+						</div>
+					</div>
+					<div class="c_c_i_u_avatar">
+						<el-avatar
+							style="width: 100%; height: 100%"
+							:src="require('../../../../assets/icon/pblCourse/userAvatar.png')"
+							fit="fit"
+						></el-avatar>
+					</div>
+				</div>
+				<div class="c_c_i_ai">
+					<div class="c_c_i_ai_avatar">
+						<el-avatar
+							style="width: 100%; height: 100%"
+							:src="
+								require('../../../../assets/icon/pblCourse/aiTeacherAvatar.png')
+							"
+							fit="fit"
+						></el-avatar>
+					</div>
+					<div class="c_c_i_ai_message">
+						<div class="c_c_i_ai_m_top">
+							<span>小可老师</span>
+							<span class="chatTime">{{item.createtime}}</span>
+						</div>
+						<div class="c_c_i_ai_m_bottom">
+							<div
+								v-loading="item.loading"
+								class="c_c_i_ai_m_content chatContent"
+								v-html="item.aiContent"
+							></div>
+							<span class="coptText" @click="copyText(item.aiContent)"></span>
+						</div>
+					</div>
+				</div>
+			</div>
+		</div>
+		<!-- <div class="c_controls">
+			<span class="c_controls_item" @click="clearChat">清空对话</span>
+		</div> -->
+		<div class="c_bottom" 	v-loading="chatLoading">
+			<div class="c_b_record">
+				<span></span>
+			</div>
+			<div class="c_b_inputArea">
+				<el-input
+					class="c_b_input"
+					@keyup.enter.native="send()"
+					v-model="textValue"
+				
+				>
+				</el-input>
+				<span></span>
+			</div>
+			<div class="c_b_send" @click="send">
+				<span></span>
+			</div>
+		</div>
 	</div>
 </template>
 
 <script>
-	export default {
-		
-	}
+import { v4 as uuidv4 } from "uuid";
+import MarkdownIt from "markdown-it";
+export default {
+	data() {
+		return {
+			loading: false,
+			chatLoading:false,
+			userId:this.$route.query['userid'],
+			chatList: [],
+			textValue: "",
+		};
+	},
+	methods: {
+		// 发送消息
+		send() {
+			if(this.chatLoading || this.loading)return this.$message.info("请稍等...")
+			if(!this.textValue.trim())return this.$message.info("请输入内容");
+			const _uuid = uuidv4();
+			this.chatLoading = true;
+			this.chatList.push({
+				role: "user",
+				content: `${this.textValue}`,
+				uid: _uuid,
+				AI: "AI",
+				aiContent: "",
+				oldContent: "",
+				filename: "",
+				index: this.chatList.length,
+				createtime:new Date().toLocaleString().replaceAll("/",'-'),
+				loading: true,
+			})
+			
+			let params = {
+				model: "gpt-3.5-turbo",
+					temperature: 0,
+					max_tokens: 4096,
+					top_p: 1,
+					frequency_penalty: 0,
+					presence_penalty: 0,
+					messages: [{role:"user",content:this.textValue}],
+					uid: _uuid,
+					mind_map_question: "",
+			}
+			this.scrollBottom();
+			this.textValue = "";
+			this.ajax
+					.post("https://gpt4.cocorobo.cn/chat", params)
+					.then((res) => {
+						if (res.data.FunctionResponse.result == "发送成功") {
+						} else {
+							this.chatLoading = false;
+							this.$message.warning(res.data.FunctionResponse.result);
+						}
+					})
+					.catch((e) => {
+						this.chatLoading = false;
+						console.log(e);
+					});
+			// this.$message.info(`发送:${this.textValue}`);
+			// this.textValue = "";
+			this.getAiContent(_uuid)
+		},
+		getAiContent(_uid) {
+			let _source = new EventSource(`https://gpt4.cocorobo.cn/stream/${_uid}`); //http://gpt4.cocorobo.cn:8011/stream/     https://gpt4.cocorobo.cn/stream/
+			let _allText = "";
+			let _mdText = "";
+			const md = new MarkdownIt();
+			_source.onmessage = (_e) => {
+				if (_e.data.replace("'", "").replace("'", "") == "[DONE]") {
+					//对话已经完成
+					_mdText = _mdText.replace("_", "");
+					_source.close();
+					this.chatList.find((i) => i.uid == _uid).aiContent = _mdText;
+					this.chatList.find((i) => i.uid == _uid).isalltext = true;
+					this.chatList.find((i) => i.uid == _uid).isShowSynchronization = true;
+					this.chatList.find((i) => i.uid == _uid).loading = false;
+					this.chatLoading = false;
+					this.insertChat(_uid);
+					return;
+				} else {
+					//对话还在继续
+					let _text = "";
+					_text = _e.data.replaceAll("'", "");
+					if (_allText == "") {
+						_allText = _text.replace(/^\n+/, ""); //去掉回复消息中偶尔开头就存在的连续换行符
+					} else {
+						_allText += _text;
+					}
+					_mdText = _allText + "_";
+					_mdText = _mdText.replace(/\\n/g, "\n");
+					_mdText = _mdText.replace(/\\/g, "");
+					if (_allText.split("```").length % 2 == 0) _mdText += "\n```\n";
+					//转化返回的回复流数据
+					_mdText = md.render(_mdText);
+					this.chatList.find((i) => i.uid == _uid).aiContent = _mdText;
+					this.chatList.find((i) => i.uid == _uid).loading = false;
+					this.scrollBottom();
+					// 处理流数据
+				}
+			};
+		},
+		// 复制
+		copyText(_html) {
+			const div = document.createElement("div")
+			div.innerHTML = _html
+			const _text = div.innerText
+			navigator.clipboard.writeText(_text).then(_=>{
+			  this.$message.success("复制成功")
+			}, function(err) {
+				this.$message.error(`无法复制:${err}`)
+			});
+		},
+		// 保存对话
+		//保存消息
+		insertChat(_uid) {
+			let _data = this.chatList.find((i) => i.uid == _uid);
+			if (!_data) return;
+			let params = {
+				userId: this.userId,
+				userName: "qgt",
+				groupId: "602def61-005d-11ee-91d8-005056b8q12w",
+				answer: _data.aiContent,
+				problem: _data.content,
+				file_id: "",
+				alltext: _data.aiContent,
+				type: "chat",
+				filename: _data.filename,
+				session_name: `${this.userId}-pblCourse`,
+			};
+			this.ajax
+				.post("https://gpt4.cocorobo.cn/insert_chat", params)
+				.then((res) => {
+					console.log("保存对话")
+				});
+		},
+		// 获取对话记录
+		getChatList() {
+			return new Promise((resolve, reject) => {
+				this.chatList = [];
+				this.loading = true;
+				this.chatLoading = true;
+				let params = {
+					userid: this.userId,
+					groupid: "602def61-005d-11ee-91d8-005056b8q12w",
+					// session_name:``
+					session_name: `${this.userId}-pblCourse`,
+				};
+				this.ajax
+					.post("https://gpt4.cocorobo.cn/get_agent_park_chat", params)
+					.then((res) => {
+						let _data = JSON.parse(res.data.FunctionResponse);
+						if (_data.length > 0) {
+							console.log(_data)
+							let _chatList = [];
+							for (let i = 0; i < _data.length; i++) {
+								_chatList.push({
+									loading: false,
+									role: "user",
+									content: _data[i].problem,
+									uid: _data[i].id,
+									AI: "AI",
+									aiContent: _data[i].answer,
+									oldContent: _data[i].answer,
+									isShowSynchronization: false,
+									filename: _data[i].filename,
+									index: i,
+									is_mind_map: false,
+									fileid: _data[i].fileid,
+									createtime:_data[i].createtime
+								});
+							}
+							this.chatList = _chatList;
+							this.chatLoading = false;
+							this.loading = false;
+						} else {
+							//没有对话记录
+							this.loading = false;
+							this.chatLoading = false;
+						}
+						resolve();
+						this.scrollBottom();
+					})
+					.catch((err) => {
+						console.log(err);
+						this.$message.error("获取对话记录失败");
+						this.chatLoading = false;
+						this.scrollBottom();
+						resolve();
+					});
+			});
+		},
+		//对话触底
+		scrollBottom(){
+			this.$nextTick(()=>{
+				this.$refs.chatRef.scrollTop = this.$refs.chatRef.scrollHeight;
+			})
+		},
+		//清除对话
+		clearChat(){
+			this.chatList = [];
+			this.scrollBottom();
+		},
+	},
+
+	mounted() {
+		this.getChatList().then(_=>{
+			this.scrollBottom();
+		})
+	},
+};
 </script>
 
 <style scoped>
-.chat{
+.chat {
 	width: 100%;
 	height: 100%;
-	background-color: pink;
+	background-color: #ffffff;
+	border-radius: 12px;
+	box-sizing: border-box;
+	border: solid 1px #f3f7fd;
+	box-shadow: 0 4px 10px 0 #1d388321;
+}
+
+.c_chat {
+	width: 100%;
+	height: calc(100% - 56px);
+	box-sizing: border-box;
+	padding: 15px;
+	overflow: auto;
+}
+
+.c_c_item {
+	background-color: none;
+}
+
+.c_c_i_user {
+	width: 100%;
+	height: auto;
+	display: flex;
+	justify-content: flex-end;
+	align-items: flex-start;
+	margin-bottom: 10px;
+}
+
+.c_c_i_u_message {
+	height: auto;
+	width: auto;
+	max-width: calc(100% - 50px - 40px);
+	display: flex;
+	flex-direction: column;
+	align-items: flex-end;
+	margin-right: 5px;
+}
+.c_c_i_u_m_top {
+	margin-bottom: 10px;
+	display: flex;
+	align-items: flex-end;
+}
+
+.c_c_i_u_m_top > span {
+	margin-left: 5px;
+}
+
+.c_c_i_u_m_bottom {
+	display: flex;
+	align-items: flex-end;
+}
+
+.coptText {
+	min-width: 15px;
+	min-height: 15px;
+	display: block;
+	background: url("../../../../assets/icon/pblCourse/copyIcon.png") no-repeat;
+	background-size: 100% 100%;
+	margin: 0px 10px 6px 10px;
+	cursor: pointer;
+}
+
+.c_c_i_u_m_content {
+	width: auto;
+	height: auto;
+	padding: 10px;
+	background-color: #e2eeff;
+	border-radius: 8px 2px 8px 8px;
+	border: solid 1px #000000e5;
+}
+
+.c_c_i_u_avatar {
+	width: 45px;
+	height: 45px;
+	margin: 0px 0 0 10px;
+}
+
+.c_c_i_ai {
+	margin-top: 10px;
+	width: 100%;
+	height: auto;
+	display: flex;
+	align-items: flex-start;
+	margin-bottom: 10px;
+}
+
+.c_c_i_ai_avatar {
+	width: 45px;
+	height: 45px;
+	margin: 0px 0 0 10px;
+}
+
+.c_c_i_ai_message {
+	height: auto;
+	width: auto;
+	max-width: calc(100% - 50px - 40px);
+	display: flex;
+	flex-direction: column;
+	align-items: flex-start;
+	margin-left: 5px;
+}
+.c_c_i_ai_m_top {
+	display: flex;
+	align-items: flex-end;
+	margin-bottom: 10px;
+}
+
+.c_c_i_ai_m_top > span {
+	margin-left: 5px;
+}
+
+.c_c_i_ai_m_bottom {
+	display: flex;
+	align-items: flex-end;
+}
+
+.c_c_i_ai_m_content {
+	min-width: 40px;
+	min-height: 25px;
+	width: auto;
+	height: auto;
+	padding: 10px;
+	background-color: #ffffff;
+	border-radius: 2px 8px 8px 8px;
+	border: solid 1px #000000e5;
+}
+
+.chatContent >>> ol {
+	margin-left: 25px;
+}
+
+.chatContent >>> ul {
+	margin-left: 25px;
+}
+
+.chatTime{
+	font-size: 14px;
+	margin: 0 10px;
+	color: #2c2f3b;
+}
+
+.c_controls{
+	width: 100%;
+	height: 30px;
+}
+
+.c_controls>span{
+	max-height: 30px;
+	width: auto;
+	padding: 5px 10px;
+	font-size: 14px;
+	border-radius: 15px;
+	box-shadow: 0 0 2px 0px gray;
+	margin-left: 10px;
+	cursor: pointer;
+}
+
+.c_controls>span:hover{
+	background-color: #f3f7fd;
+}
+
+.c_bottom {
+	width: 100%;
+	height: 56px;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	box-sizing: border-box;
+	border-top: solid 0.5px #e7e7e7;
+}
+
+.c_b_record {
+	width: 30px;
+	height: 30px;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+}
+
+.c_b_record > span {
+	width: 24px;
+	height: 24px;
+	background-image: url("../../../../assets/icon/pblCourse/recordIcon.png");
+	background-size: 100% 100%;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+}
+
+.c_b_inputArea {
+	width: calc(100% - 140px);
+	background-color: #f3f3f3;
+	border-radius: 50px;
+	height: 80%;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	margin: 0 15px;
+}
+
+.c_b_input {
+	width: calc(100% - 40px);
+	margin-right: 10px;
+}
+
+.c_b_input >>> .el-input__inner {
+	border: none;
+	background-color: #f3f3f3;
+	outline: none;
+	border-radius: 50px 0 0 50px;
+	font-size: 1.2em;
+}
+
+.c_b_inputArea > span {
+	width: 24px;
+	height: 24px;
+	background-image: url("../../../../assets/icon/pblCourse/fileIcon.png");
+	background-size: 100% 100%;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	margin-right: 10px;
+	cursor: pointer;
+}
+
+.c_b_send {
+	width: 40px;
+	height: 40px;
+	border-radius: 50%;
+	background-color: #3681fc;
+	cursor: pointer;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	cursor: pointer;
+}
+
+.c_b_send > span {
+	width: 60%;
+	height: 60%;
+	background-image: url("../../../../assets/icon/pblCourse/sendIcon.png");
+	background-size: 100% 100%;
 }
-</style>
+</style>