fix: 优化对话
This commit is contained in:
parent
d0c70c62b3
commit
12a401ec8d
1160
node_modules/.package-lock.json
generated
vendored
1160
node_modules/.package-lock.json
generated
vendored
File diff suppressed because it is too large
Load Diff
1161
package-lock.json
generated
1161
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -10,6 +10,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design-vue/pro-layout": "^1.0.8",
|
"@ant-design-vue/pro-layout": "^1.0.8",
|
||||||
|
"@kangc/v-md-editor": "^1.7.12",
|
||||||
"@tinymce/tinymce-vue": "^3.2.6",
|
"@tinymce/tinymce-vue": "^3.2.6",
|
||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
"ant-design-vue": "^1.7.4",
|
"ant-design-vue": "^1.7.4",
|
||||||
|
@ -334,6 +334,24 @@ export function AnyncChart(data) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 搜索知识库
|
||||||
|
export function searchKnowledgeBase(query) {
|
||||||
|
return request({
|
||||||
|
url: '/api/knowledge/search',
|
||||||
|
method: 'get',
|
||||||
|
params: { query }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取AI响应
|
||||||
|
export function getAIResponse(data) {
|
||||||
|
return request({
|
||||||
|
url: '/api/assistant/chat',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
// 获取所有教师
|
// 获取所有教师
|
||||||
export const getTeacherCount = params => request.get('/user/getTeacherCount', { params });
|
export const getTeacherCount = params => request.get('/user/getTeacherCount', { params });
|
||||||
// 获取所有学生
|
// 获取所有学生
|
||||||
|
@ -204,6 +204,7 @@ export default {
|
|||||||
margin: 24px;
|
margin: 24px;
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
height: 100vh;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
||||||
min-height: calc(100vh - 128px);
|
min-height: calc(100vh - 128px);
|
||||||
|
@ -10,6 +10,14 @@ import './permission'; // permission control
|
|||||||
import '@/plugins';
|
import '@/plugins';
|
||||||
import '@/tool';
|
import '@/tool';
|
||||||
import VueAnimateNumber from 'vue-animate-number'
|
import VueAnimateNumber from 'vue-animate-number'
|
||||||
|
import VMdEditor from '@kangc/v-md-editor';
|
||||||
|
import VMdPreview from '@kangc/v-md-editor/lib/preview';
|
||||||
|
import '@kangc/v-md-editor/lib/style/base-editor.css';
|
||||||
|
import '@kangc/v-md-editor/lib/theme/style/github.css';
|
||||||
|
import githubTheme from '@kangc/v-md-editor/lib/theme/github.js';
|
||||||
|
|
||||||
|
VMdEditor.use(githubTheme);
|
||||||
|
VMdPreview.use(githubTheme);
|
||||||
|
|
||||||
Vue.use(VueAnimateNumber)
|
Vue.use(VueAnimateNumber)
|
||||||
Vue.config.productionTip = false;
|
Vue.config.productionTip = false;
|
||||||
|
@ -132,9 +132,15 @@ export const routes = [
|
|||||||
{
|
{
|
||||||
path: '/account/Agent',
|
path: '/account/Agent',
|
||||||
name: 'AccountAgent',
|
name: 'AccountAgent',
|
||||||
meta: { title: '竞赛助手', auth: 0 },
|
meta: { title: '编程助手', auth: 0 },
|
||||||
component: () => import('@/views/user/Agent.vue'),
|
component: () => import('@/views/user/Agent.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/account/CompetitionAssistant',
|
||||||
|
name: 'CompetitionAssistant',
|
||||||
|
meta: { title: '竞赛助手', auth: 0 },
|
||||||
|
component: () => import('@/views/CompetitionAssistant.vue'),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
219
src/views/CompetitionAssistant.vue
Normal file
219
src/views/CompetitionAssistant.vue
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
<template>
|
||||||
|
<div class="chat-container">
|
||||||
|
<!-- 聊天区域 -->
|
||||||
|
<div class="chat-main">
|
||||||
|
<!-- 聊天记录 -->
|
||||||
|
<div class="chat-messages" ref="messageContainer">
|
||||||
|
<div
|
||||||
|
v-for="(message, index) in chatHistory"
|
||||||
|
:key="index"
|
||||||
|
:class="['message', message.role]"
|
||||||
|
>
|
||||||
|
<div class="message-avatar">
|
||||||
|
<a-avatar :src="message.role === 'user' ? userAvatar : aiAvatar" />
|
||||||
|
</div>
|
||||||
|
<div class="message-content">
|
||||||
|
<v-md-preview :text="message.content" />
|
||||||
|
<div class="message-time">{{ message.time }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 输入区域 -->
|
||||||
|
<div class="chat-input-area">
|
||||||
|
<v-md-editor v-model="inputContent" height="150px" @save="handleSend" />
|
||||||
|
<div class="input-actions">
|
||||||
|
<a-button type="primary" :loading="loading" @click="handleSend"
|
||||||
|
>发送</a-button
|
||||||
|
>
|
||||||
|
<a-button @click="clearChat">清空对话</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ref, onMounted, nextTick } from "vue";
|
||||||
|
import VMdEditor from "@kangc/v-md-editor";
|
||||||
|
import VMdPreview from "@kangc/v-md-editor/lib/preview";
|
||||||
|
import "@kangc/v-md-editor/lib/style/base-editor.css";
|
||||||
|
import "@kangc/v-md-editor/lib/theme/style/github.css";
|
||||||
|
import githubTheme from "@kangc/v-md-editor/lib/theme/github.js";
|
||||||
|
import { searchKnowledgeBase, getAIResponse } from "@/api";
|
||||||
|
|
||||||
|
VMdEditor.use(githubTheme);
|
||||||
|
VMdPreview.use(githubTheme);
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "CompetitionAssistant",
|
||||||
|
components: {
|
||||||
|
VMdEditor,
|
||||||
|
VMdPreview,
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const inputContent = ref("");
|
||||||
|
const loading = ref(false);
|
||||||
|
const chatHistory = ref([]);
|
||||||
|
const messageContainer = ref(null);
|
||||||
|
const searchQuery = ref("");
|
||||||
|
const knowledgeList = ref([]);
|
||||||
|
|
||||||
|
const userAvatar = "path/to/user-avatar.png";
|
||||||
|
const aiAvatar = "path/to/ai-avatar.png";
|
||||||
|
|
||||||
|
// 搜索知识库
|
||||||
|
const searchKnowledge = async (query) => {
|
||||||
|
try {
|
||||||
|
const result = await searchKnowledgeBase(query);
|
||||||
|
knowledgeList.value = result.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("搜索知识库失败:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 选择知识库条目
|
||||||
|
const selectKnowledge = (item) => {
|
||||||
|
inputContent.value = `参考知识:${item.title}\n\n${inputContent.value}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 发送消息
|
||||||
|
const handleSend = async () => {
|
||||||
|
if (!inputContent.value.trim()) return;
|
||||||
|
|
||||||
|
const userMessage = {
|
||||||
|
role: "user",
|
||||||
|
content: inputContent.value,
|
||||||
|
time: new Date().toLocaleTimeString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
chatHistory.value.push(userMessage);
|
||||||
|
loading.value = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await getAIResponse({
|
||||||
|
message: inputContent.value,
|
||||||
|
history: chatHistory.value,
|
||||||
|
});
|
||||||
|
|
||||||
|
chatHistory.value.push({
|
||||||
|
role: "assistant",
|
||||||
|
content: response.data,
|
||||||
|
time: new Date().toLocaleTimeString(),
|
||||||
|
});
|
||||||
|
|
||||||
|
inputContent.value = "";
|
||||||
|
scrollToBottom();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("获取AI响应失败:", error);
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 滚动到底部
|
||||||
|
const scrollToBottom = async () => {
|
||||||
|
await nextTick();
|
||||||
|
if (messageContainer.value) {
|
||||||
|
messageContainer.value.scrollTop = messageContainer.value.scrollHeight;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 清空对话
|
||||||
|
const clearChat = () => {
|
||||||
|
chatHistory.value = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
searchKnowledge("");
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
inputContent,
|
||||||
|
loading,
|
||||||
|
chatHistory,
|
||||||
|
messageContainer,
|
||||||
|
searchQuery,
|
||||||
|
knowledgeList,
|
||||||
|
userAvatar,
|
||||||
|
aiAvatar,
|
||||||
|
handleSend,
|
||||||
|
clearChat,
|
||||||
|
searchKnowledge,
|
||||||
|
selectKnowledge,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.chat-container {
|
||||||
|
display: flex;
|
||||||
|
height: calc(100vh - 64px);
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.knowledge-panel {
|
||||||
|
width: 300px;
|
||||||
|
border-right: 1px solid #e8e8e8;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-header {
|
||||||
|
padding: 16px;
|
||||||
|
border-bottom: 1px solid #e8e8e8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.knowledge-list {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-main {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-messages {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.assistant {
|
||||||
|
background: #f7f7f8;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-content {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-time {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-input-area {
|
||||||
|
border-top: 1px solid #e8e8e8;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-actions {
|
||||||
|
margin-top: 12px;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
x
Reference in New Issue
Block a user