Commit 6bf7630a authored by 姜耀祖's avatar 姜耀祖

前端修改

parent 72adaf97
Pipeline #21063 passed with stages
in 3 minutes and 18 seconds
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
......@@ -55,17 +55,32 @@
<div class="chat-area">
<!-- 消息历史区域 -->
<div id="scrollContainer" class="scrollContainer">
<div id="chat-messages" class="chat-messages">
<div class="fist-loading" v-if="fistLoading">
<p>快速查找专家</p>
<div v-for="(item, index) in guides" :key="index" @click="questionClick(item)">
<p>{{ item }}</p>
</div>
</div>
<div id="chat-messages" class="chat-messages" v-if="!fistLoading">
<div v-for="(message, index) in messages"
:key="index"
class="message"
:class="message.role + '-message'">
<div class="avatar">{{ message.role === 'user' ? '我' : 'AI' }}</div>
<div class="content">
<p>{{ message.content }}</p>
<p v-html="message.content"></p><!--v-html可能会有xss攻击,但是数据来源于大模型,是否需要清洗数据然后再显示?-->
<div v-if="message.typing" class="typing-indicator"></div>
</div>
</div>
<div class="question">
<div class="question_left"></div>
<div v-if="questions.length" class="question_right">
<p>您可以继续问我: </p>
<div class="questionContent" v-for="(q, i) in questions" :key="i" @click="questionClick(q)">
<p>{{ q }}</p>
</div>
</div>
</div>
</div>
</div>
......@@ -73,7 +88,7 @@
<div class="input-area">
<textarea id="user-input" v-model="userInput" placeholder="请输入您的问题 shift+enter换行" rows="3"></textarea>
<div class="input-area-content">
<div>
<div style="display: flex;align-items: center;justify-content: flex-start;">
<div class="custom-select" v-click-outside="closeExpertDropdown">
<div class="selected-option" @click.stop="toggleExpertDropdown">
<span>{{ selectedExpert }}</span>
......@@ -90,6 +105,25 @@
</div>
</div>
</div>
<div class="custom-select" v-click-outside="closeOrgDropdown">
<div class="selected-option" @click.stop="toggleOrgDropdown">
<span>{{ selectedOrg }}</span>
<svg class="dropdown-icon" :class="{ 'rotated': showOrgDropdown }" viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none">
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</div>
<div class="dropdown-menu" v-show="showOrgDropdown">
<div class="dropdown-item" @click.stop="selectOrg('全部组织')" :class="{ 'active': selectedOrg === '全部组织' }">
<span>全部</span>
</div>
<div class="dropdown-item" @click.stop="selectOrg('组织内')" :class="{ 'active': selectedOrg === '组织内' }">
<span>组织内</span>
</div>
<div class="dropdown-item" @click.stop="selectOrg('组织外')" :class="{ 'active': selectedOrg === '组织外' }">
<span>组织外</span>
</div>
</div>
</div>
</div>
<div>
<button id="send-btn" class="send-btn" @click="sendMessage" v-show="!isResponding">
......
/**
* AI聊天页面Vue应用
*/
require(['jquery', 'vue', 'utils', 'echarts', 'global'], function ($, Vue, utils, echarts) {
require(['jquery', 'vue', 'utils','marked','markdown', 'global'], function ($, Vue, utils, marked,markdown) {
// 添加点击外部关闭指令
Vue.directive('click-outside', {
bind: function (el, binding, vnode) {
......@@ -22,7 +22,7 @@ require(['jquery', 'vue', 'utils', 'echarts', 'global'], function ($, Vue, utils
data: {
// 用户相关数据
currentLoginUser: {},
fistLoading: true,
// 聊天相关数据
chatHistory: [],
sessionId: "",
......@@ -36,7 +36,11 @@ require(['jquery', 'vue', 'utils', 'echarts', 'global'], function ($, Vue, utils
// 专家选择相关数据
selectedExpert: '内部专家',
showExpertDropdown: false,
// 组织内网选择相关数据
selectedOrg:'全部组织',
showOrgDropdown: false,
questions:[],
guides:['请帮我推荐5位南京地区的区块链外部专家','我需要找既懂通信又擅长软件开发的内部专家','我需要网络安全领域的专家,不确定选内部还是外部'],
// 历史对话分类
historySections: [
{
......@@ -74,7 +78,7 @@ require(['jquery', 'vue', 'utils', 'echarts', 'global'], function ($, Vue, utils
// 消息列表
messages: [
{ role: 'ai', content: '您好!我是您的专家推荐助手,有什么可以帮助您的吗?' }
// { role: 'ai', content: '您好!我是您的专家推荐助手,有什么可以帮助您的吗?' }
]
},
computed: {
......@@ -87,7 +91,6 @@ require(['jquery', 'vue', 'utils', 'echarts', 'global'], function ($, Vue, utils
this.getSessionId();
},
mounted: function () {
this.pageEvent();
// 监听输入框的键盘事件
this.$nextTick(() => {
const textarea = document.getElementById('user-input');
......@@ -102,37 +105,9 @@ require(['jquery', 'vue', 'utils', 'echarts', 'global'], function ($, Vue, utils
this.closeAllMenus();
}
});
// // 获取滚动容器和消息容器
// const scrollContainer = document.getElementById('chat-messages').parentElement;
// const chatMessages = document.getElementById('chat-messages');
// // 创建MutationObserver监听消息变化
// const observer = new MutationObserver(function(mutations) {
// mutations.forEach(mutation => {
// if (mutation.addedNodes.length) {
// // 使用延时确保内容已渲染
// setTimeout(() => {
// scrollContainer.scrollTo({
// top: scrollContainer.scrollHeight,
// behavior: 'smooth' // 可选平滑滚动
// });
// }, 50);
// }
// });
// });
//
// // 开始观察子元素变化
// observer.observe(chatMessages, {
// childList: true
// });
});
},
methods: {
pageEvent:function(){
// 确保在DOM加载完成后执行
document.addEventListener('DOMContentLoaded', function() {
});
},
// 用户相关方法
currentUser: function () {
const that = this;
......@@ -147,7 +122,6 @@ require(['jquery', 'vue', 'utils', 'echarts', 'global'], function ($, Vue, utils
}
});
},
// 主题相关方法
toggleTheme() {
this.theme = this.theme === 'light' ? 'dark' : 'light';
......@@ -174,11 +148,16 @@ require(['jquery', 'vue', 'utils', 'echarts', 'global'], function ($, Vue, utils
this.sendMessage();
}
},
questionClick:function(message){
this.userInput = message;
this.sendMessage();
},
sendMessage() {
const message = this.userInput.trim();
if (!message) return;
if (this.fistLoading)
this.fistLoading = false;
this.questions= [];
this.stopResponse();
// 添加用户消息
this.addMessage('user', message);
......@@ -237,16 +216,30 @@ require(['jquery', 'vue', 'utils', 'echarts', 'global'], function ($, Vue, utils
}
});
// 连接SSE
this.currentEventSource = new EventSource('../../api/langchain/sseIntelligent?chatMessage=' + encodeURIComponent(chatMessage) + "&dialogId=" + this.sessionId);
this.currentEventSource = new EventSource('../../api/langchain/sseBigTwo');
let responseText = '';
var md = new markdown({
html: true, // 允许解析 HTML 标签
linkify: true, // 自动识别链接
typographer: true, // 启用排版优化
breaks: true, // 将单个换行符视为换行
});
this.currentEventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
const content = data.dataToSend[1].data;
if (content !== "stop") {
responseText += content;
this.messages[aiMessageIndex].content = responseText;
const match = content.match(/SUGGEST#\[(.*?)\]#SUGGEST/);
if (match) {
const suggestionsJson = `[${match[1]}]`;
const suggestions = JSON.parse(suggestionsJson);
if (Array.isArray(suggestions)) {
this.questions = suggestions;
}
}else{//提示词不展示
responseText += content;
}
this.messages[aiMessageIndex].content = md.render(responseText);
} else {
// 移除输入指示器
this.messages[aiMessageIndex].typing = false;
......@@ -302,7 +295,7 @@ require(['jquery', 'vue', 'utils', 'echarts', 'global'], function ($, Vue, utils
this.currentEventSource = null;
this.isResponding = false;
}
this.questions = [];
// 获取新的会话ID并清空对话
this.getSessionId().then(() => {
this.clearChat();
......@@ -410,6 +403,18 @@ require(['jquery', 'vue', 'utils', 'echarts', 'global'], function ($, Vue, utils
this.selectedExpert = expert;
// 立即关闭下拉框
this.showExpertDropdown = false;
},
//组织内外选择相关方法
toggleOrgDropdown() {
this.showOrgDropdown = !this.showOrgDropdown;
},
closeOrgDropdown() {
this.showOrgDropdown = false;
},
selectOrg(org) {
this.selectedOrg = org;
// 立即关闭下拉框
this.showOrgDropdown = false;
}
}
});
......
......@@ -27,6 +27,7 @@
--scrollbar-thumb: #c1c1c1;
--scrollbar-hover: #a8a8a8;
--box-shadow: rgba(0, 0, 0, 0.08);
--question-text: #676c90;
}
.dark-theme {
......@@ -50,6 +51,7 @@
--scrollbar-thumb: #555555;
--scrollbar-hover: #777777;
--box-shadow: rgba(0, 0, 0, 0.2);
--question-text: #dbdef8;
}
body {
......@@ -214,6 +216,39 @@ body {
}
/* 右侧聊天区域 */
.fist-loading{
width: 900px;
margin: 8% auto 10px;
}
.fist-loading>p{
color: var(--primary-color);
font-weight: bold;
font-size: 20px;
width: 500px;
margin:0 auto;
}
.fist-loading div{
width: 500px;
max-height: 72px;
line-height: 72px;
border-radius: 16px;
padding: 0 16px;
background-color: var(--sidebar-bg);
border: 1px solid var(--box-shadow);
box-shadow: 0 1px 8px var(--box-shadow);
box-sizing: border-box;
align-items: center;
margin:10px auto;
font-size: 16px;
cursor: pointer;
color: var(--question-text);
}
.fist-loading div:hover{
background-color: #e5eaf6;
color: #475ada;
}
.chat-area {
flex: 1;
display: flex;
......@@ -229,15 +264,14 @@ body {
width: 100%;
overflow-y: auto; /* 让内容继续流动 */
height: calc(100vh - 150px); /* 确保占据足够高度 */
overflow-x: hidden;
padding-top: 20px;
}
/* 聊天消息区域 */
.chat-messages {
width: 900px;
max-width: 95%;
margin: 0 auto;
padding: 20px;
margin-bottom: 10px;
margin: 0 auto 10px;
}
/* 输入区域 */
......@@ -271,6 +305,44 @@ body {
}
/* 用户消息 */
.question{
display: flex;
justify-content: flex-start;
}
.question_left{
width: 40px;
margin: 0 10px;
}
.question_right{
padding:0 6px;
}
.question_right>p{
font-size: 14px;
color: var(--question-text);
}
.questionContent{
font-size: 13px;
color: var(--question-text);
background-color: var(--input-area-bg);
border-radius: 6px;
box-shadow: 0 16px 20px 0 rgba(174, 167, 223, .06);
line-height: 20px;
max-width: 800px;
overflow: hidden;
padding: 8px 16px;
text-overflow: ellipsis;
white-space: nowrap;
width: -moz-fit-content;
width: fit-content;
margin: 10px 0;
cursor: pointer;
}
.questionContent:hover{
background-color: #e5eaf6;
color: #475ada;
}
.user-message {
flex-direction: row-reverse;
}
......@@ -294,7 +366,7 @@ body {
}
.message .content {
max-width: 70%;
max-width: 87%;
padding: 12px 16px;
border-radius: 12px;
box-shadow: 0 1px 2px var(--box-shadow);
......@@ -315,6 +387,7 @@ body {
.content p {
white-space: pre-wrap;
word-break: break-word;
color: var(--text-color);
}
#user-input {
......@@ -396,6 +469,22 @@ body {
66% { content: "..."; }
}
@media(max-width: 1190px){
.chat-messages{
width: 800px;
}
}
@media(max-width: 1090px){
.chat-messages{
width: 700px;
}
}
@media(max-width: 990px){
.chat-messages{
width: 600px;
}
}
/* 响应式设计 */
@media (max-width: 768px) {
.sidebar {
......
......@@ -54,6 +54,8 @@ require.config({
'Popper': [getContextPath() + '/libs/popper/popper-x'],
'component-demo-image': ['./component-demo-image'],
'component-demo-image-new': ['./component-demo-image-new'],
'marked':[getContextPath() + '/libs/marked/marked.umd.min'],
'markdown':[getContextPath() + '/libs/marked/markdown-it.min'],
//流程引擎组件
'process-engine-toolbar-v2': [getContextPath() + '/libs/process-engine/component-process-engine-toolbar-v2'],
'process-engine-toolbar-v3': [getContextPath() + '/libs/process-engine/component-process-engine-toolbar-v3'],
......@@ -188,6 +190,8 @@ define("global",
'XMLHttp-download-file', //下载文件前校验
'component-demo-image', //显示图片
'component-checkbox-list', //复选框
'marked',
'markdown',
//'component-demo-image-new',
// 'directive-ellipsis', //显示更多文字
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment