whx 14 hours ago
parent
commit
bfac58ea49

+ 3 - 0
virgo.wzfrontend/aiChat/src/assets/scss/variables.scss

@@ -4,6 +4,9 @@
     'primary': (
       'base': #7a6dff,
     ),
+	'success': (
+	  'base': #0bbd87,
+	)
   )
 );
 

+ 174 - 25
virgo.wzfrontend/aiChat/src/components/AIFlowChat.vue

@@ -15,7 +15,10 @@
 	} from '@/api/ai'
 	import {
 		Top,
-		Plus
+		Plus,
+		Check,
+		Loading,
+		Monitor
 	} from '@element-plus/icons-vue'
 	import {
 		useUserStore
@@ -99,7 +102,7 @@
 		if (flowData.state) {
 			versionIndex.value = 1;
 			chatHistory.value = flowData.data.data.map(node => {
-				node['AIoutputs'] = renderMarkdown(node.answer);
+				node['AIoutputs'] = returnAIoutputs(node.answer);
 				return node
 			});
 			hljs.highlightAll();
@@ -114,6 +117,23 @@
 		if (!prompt.value.trim() || loading.value) return;
 		AIMessage(prompt.value)
 	}
+	const returnAIoutputs = (data) => {
+		let str = '';
+		if (data.indexOf('```markdown') > -1) {
+			str = data.replaceAll('\n', '</n>').replaceAll('```markdown', '').replaceAll('```', '');
+		} else {
+			str = data;
+		}
+		let obj = str.split('<workark>');
+		let newData = obj.map(node => {
+			try {
+				return JSON.parse(node)
+			} catch {
+				return {}
+			}
+		});
+		return deduplicateByTypeAndId(newData);
+	}
 	const AIMessage = async (userMessage) => {
 		try {
 			// 1. 添加用户消息到历史记录
@@ -127,6 +147,7 @@
 			currentAIResponse.value = ''
 			prompt.value = '' // 清空输入框
 			let result = '';
+			let setTime = null;
 			fetchEventSource(`${config.baseURL}/api/ai/chat/run/7`, {
 				method: 'POST',
 				headers: {
@@ -146,11 +167,21 @@
 				}),
 				onmessage(event) {
 					// 处理接收到的消息
-					if (!isStrictJSONString(event.data)) {
-						result += event.data;
-						console.log(result);
-						chatHistory.value[chatHistory.value.length - 1].AIoutputs = renderMarkdown(result);
-						scrollToBottom();
+					result += event.data;
+					if (!setTime) {
+						setTime = setTimeout(() => {
+							console.log(result);
+							if (result.indexOf('markdown') > -1) {
+								initChatList();
+							} else {
+								chatHistory.value[chatHistory.value.length - 1].AIoutputs =
+									returnAIoutputs(result);
+								scrollToBottom();
+								clearTimeout(setTime);
+								loading.value = false;
+								setTime = null;
+							}
+						}, 1000)
 					}
 				},
 				onopen(response) {
@@ -162,6 +193,7 @@
 					console.error('Error:', error);
 				},
 				onclose() {
+					console.log('close');
 					// 连接关闭时的回调
 					loading.value = false;
 				}
@@ -170,16 +202,6 @@
 			console.error("请求失败:", error);
 		}
 	}
-	const isStrictJSONString = str => {
-		console.log(str);
-		try {
-			const parsed = JSON.parse(str);
-			return parsed !== null && typeof parsed === 'object';
-		} catch (e) {
-			return false;
-		}
-	}
-
 	const scrollToBottom = () => {
 		nextTick(() => {
 			if (chatBody.value) {
@@ -191,6 +213,16 @@
 		if (!data) return '';
 		return JSON.parse(data).query;
 	}
+	const deduplicateByTypeAndId = (arr) => {
+		const map = new Map();
+
+		arr.forEach(item => {
+			const key = item.type === 'step' && item.id ? `${item.type}-${item.id}` : item.type;
+			map.set(key, item); // 后面覆盖前面的
+		});
+
+		return Array.from(map.values());
+	}
 	/**
 	 * 使用 marked 解析 Markdown
 	 * @param markdown 解析的文本
@@ -207,7 +239,7 @@
 			const highlighted = hljs.highlight(text, {
 				language
 			}).value;
-			return `<div class="hljs-box"><div class="hljs-title">${language.toLocaleUpperCase()}</div><pre><code class="hljs language-${language}">${highlighted}</code></pre></div>`;
+			return `<div class="hljs-box"><div class="hljs-title">${language.toLocaleUpperCase()}</div><pre class="hljs-pre"><code class="hljs language-${language}">${highlighted}</code></pre></div>`;
 		};
 		renderer.link = (href) => {
 			let divBox = document.createElement('div');
@@ -220,7 +252,7 @@
 			previewUrl.value = href.href;
 			return divBox.innerHTML;
 		};
-		return marked(markdown, {
+		return marked('```markdown' + markdown.replaceAll('</n>', '\n') + '```', {
 			renderer
 		});
 	};
@@ -241,6 +273,36 @@
 	onUnmounted(() => {
 		stopTimer()
 	})
+	const activities = ref([]);
+	// {
+	// 	title: '现在我将开始创建项目',
+	// 	type: 'success',
+	// 	icon: Check,
+	// 	content: '项目创建成功',
+	// 	describe: '项目名:text-project01'
+	// }, {
+	// 	title: '开始创建首页',
+	// 	type: 'success',
+	// 	icon: Check,
+	// 	content: '页面创建成功',
+	// 	describe: '/app/index/page.jsx'
+	// }, {
+	// 	title: '开始创建关于我们',
+	// 	type: 'success',
+	// 	icon: Check,
+	// 	content: '页面创建成功',
+	// 	describe: '/app/about/page.jsx'
+	// }, {
+	// 	title: '开始创建联系我们',
+	// 	type: 'success',
+	// 	icon: Check,
+	// 	content: '页面创建成功',
+	// 	describe: '/app/tell/page.jsx'
+	// }, {
+	// 	title: '创建项目',
+	// 	type: 'warning',
+	// 	icon: Loading,
+	// }
 </script>
 <template>
 	<div class="ai-chat-container">
@@ -259,7 +321,34 @@
 						<div class="message-header-label" style="width: 100px;">AI助手</div>
 					</div>
 					<div class="message-content">
-						<div class="ai-website-boxs" @click="AIClick" v-html="message.AIoutputs"></div>
+						<div class="ai-website-boxs">
+							<div v-for="(item,index) in message.AIoutputs">
+								<div class="wk-query" v-if="item.type === 'query'">{{item.label}}</div>
+								<div class="wk-markdown wk-query" v-if="item.type === 'markdown'"
+									v-html="renderMarkdown(item.label)">
+								</div>
+								<div class="wk-step" v-if="item.type === 'step'">
+									<el-timeline class="wk-timeline">
+										<el-timeline-item :class="'timeline-'+item.type"
+											:icon="item.status === 'success' ? Check:Loading" :type="item.status"
+											size="large">
+											<div class="wk-content">
+												<div class="title">{{ item.label }}</div>
+												<div class="content">
+													<el-icon>
+														<Monitor />
+													</el-icon>
+													<span class="content-label">{{ item.label }}</span>
+													<span class="content-describe">{{ item.describe }}</span>
+												</div>
+											</div>
+										</el-timeline-item>
+									</el-timeline>
+								</div>
+							</div>
+						</div>
+						<!-- <div class="ai-website-boxs" @click="AIClick" v-html="message.AIoutputs"></div> -->
+
 						<el-button size="small" @click="reloadMessage(message.query)" :disabled="loading"
 							style="margin-top: 10px;" v-if="message.AIoutputs.indexOf('系统繁忙,请重试')>-1">
 							点击重试
@@ -318,15 +407,16 @@
 			padding: 5px 10px;
 			color: #354052;
 			font-weight: bold;
-			border-bottom: 1px solid #999999;
+			border-bottom: 1px solid #d0d3d9;
 		}
 
-		.language-html {
+		.language-html,
+		.language-markdown {
 			background: #f1f2f5;
 		}
 
-		pre {
-			margin-top: 0;
+		.hljs-pre {
+			margin: 0;
 		}
 	}
 
@@ -338,6 +428,65 @@
 		display: inline-block;
 		margin-top: 5px;
 	}
+
+	.timeline-warning {
+		.el-timeline-item__icon {
+			animation: rotating 2s linear infinite;
+		}
+	}
+
+	.wk-query {
+		padding: 12px 0;
+		font-size: 15px;
+
+		&:last-child {
+			padding-bottom: 0;
+		}
+	}
+
+	.wk-step {
+		padding-top: 15px;
+	}
+
+	.wk-timeline {
+		.el-timeline-item__icon {
+			font-size: 10px;
+		}
+
+		.el-timeline-item {
+			padding-bottom: 10px;
+		}
+	}
+
+	.wk-content {
+		.title {
+			font-size: 15px;
+			color: #222;
+		}
+
+		.content {
+			background: rgb(246, 247, 249);
+			border: 1px solid rgb(234, 234, 234);
+			height: 30px;
+			line-height: 28px;
+			border-radius: 30px;
+			padding: 0 15px;
+			font-size: 13px;
+			box-sizing: border-box;
+			color: rgb(120, 121, 121);
+			margin-top: 10px;
+			display: flex;
+			align-items: center;
+		}
+
+		.content-label {
+			margin: 0 10px 0 5px;
+		}
+
+		.content-describe {
+			font-size: 12px;
+		}
+	}
 </style>
 <style scoped lang="scss">
 	.ai-chat-container {
@@ -389,12 +538,12 @@
 		border: 1px solid #e9ecef;
 		margin-right: auto;
 		border-bottom-left-radius: 4px;
+		width: 100%;
 	}
 
 	.message-header {
 		display: flex;
 		align-items: center;
-		margin-bottom: 5px;
 		font-weight: 500;
 	}
 

+ 1 - 1
virgo.wzfrontend/aiChat/src/views/AIChat.vue

@@ -175,7 +175,7 @@
 		}
 
 		.website-form {
-			width: 400px;
+			width: 600px;
 			border-right: 1px solid var(--el-border-color);
 			margin-right: 10px;
 		}

+ 1 - 1
virgo.wzfrontend/aiChat/src/views/Home.vue

@@ -297,7 +297,7 @@
 					alt="logo.png" />
 				<span class="title">Workark.AI</span>
 				<ul class="header-ul">
-					<li class="header-li" @click="linkTo('https://www.workark.com/workark/index.html')">Ark</li>
+					<li class="header-li" @click="linTo('https://www.workark.com/workark/index.html')">Ark</li>
 					<li class="header-li">AI</li>
 					<li class="header-li" @click="linkTo()">定价</li>
 					<li class="header-li" @click="linkTo()">学习</li>