123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453 |
- <template>
- <div class="right">
- <!-- 智能助手侧边栏 -->
- <div class="assistant-sidebar">
- <div class="assistant-header">
- <!-- <div class="assistant-avatar">
- <i class="fas fa-robot"></i>
- </div> -->
- <div class="assistant-title">
- <h2>教学助手</h2>
- <p>随时为您提供帮助</p>
- </div>
- </div>
- <div class="assistant-chat" ref="codeRef">
- <!-- <div class="message assistant-message">
- 您好!我是AI教学助手,有什么可以帮您的吗?
- </div>
- <div class="message user-message">
- 如何启动沙盘的交通模拟?
- </div>
- <div class="message assistant-message">
- 您可以在沙盘控制面板点击"交通模拟"按钮。系统会自动启动预设的交通场景,包括车辆运行、信号灯控制等。
- </div> -->
- <div v-if="datas.length" style="display: flex;flex-direction:column;gap: 20px;" v-for="item in datas"
- :key="item.problem">
- <div class="message user-message">
- {{ item.problem }}
- </div>
- <div class="message assistant-message">
- <span v-if="item.codeData" v-html="item.codeData"></span>
- <svg v-else width="50" height="50" viewBox="0 0 50 50" fill="none"
- xmlns="http://www.w3.org/2000/svg" style="margin: 0 auto;">
- <circle cx="25" cy="25" r="20" fill="none" stroke="#00d2ff" stroke-width="4"
- stroke-linecap="round" stroke-dasharray="31.4 31.4" transform="rotate(-90 25 25)">
- <animateTransform attributeName="transform" type="rotate" from="0 25 25" to="360 25 25"
- dur="1s" repeatCount="indefinite" />
- </circle>
- </svg>
- </div>
- </div>
- </div>
- <div class="chat-input">
- <textarea v-model="problem" type="text" placeholder="输入您的问题..." @keyup.enter="onCodeSubmit" ></textarea>
- <button @click="onCodeSubmit">
- <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 32 32" fill="none">
- <rect width="32" height="32" rx="16" fill="#68A1FD" />
- <path
- d="M13.188 23.2586C12.9758 23.2586 12.7723 23.1954 12.6223 23.0829C12.4723 22.9703 12.388 22.8177 12.388 22.6586V17.1806C12.388 17.0006 12.496 16.8296 12.684 16.7156L19.164 12.7766C19.3289 12.6804 19.5376 12.6365 19.7452 12.6543C19.9527 12.6721 20.1428 12.7502 20.2746 12.8718C20.4064 12.9934 20.4694 13.1488 20.4503 13.3049C20.4311 13.4609 20.3312 13.6051 20.172 13.7066L13.988 17.4656V20.8556L15.932 18.9116C16.168 18.6746 16.604 18.6026 16.952 18.7436L20.176 20.0396L24 9.74359C24.076 9.54259 23.92 9.41659 23.848 9.37159C23.776 9.32659 23.588 9.23359 23.336 9.32059L8.83598 14.4236L10.716 15.2336C11.1 15.3986 11.232 15.7646 11.012 16.0526C10.792 16.3406 10.304 16.4396 9.91998 16.2746L6.70798 14.8916C6.58077 14.837 6.47612 14.7571 6.40555 14.6608C6.33497 14.5645 6.30121 14.4554 6.30798 14.3456C6.31998 14.1206 6.49598 13.9226 6.76798 13.8266L22.656 8.23459C23.4 7.97359 24.252 8.05759 24.88 8.45659C25.1856 8.64798 25.4107 8.90178 25.5279 9.1872C25.645 9.47263 25.6492 9.77741 25.54 10.0646L21.444 21.0836C21.4117 21.1701 21.3539 21.2501 21.2748 21.3176C21.1958 21.385 21.0975 21.4383 20.9872 21.4735C20.877 21.5086 20.7576 21.5247 20.638 21.5206C20.5183 21.5165 20.4014 21.4922 20.296 21.4496L16.8 20.0426L13.828 23.0186C13.672 23.1716 13.432 23.2586 13.188 23.2586Z"
- fill="white" />
- </svg>
- </button>
- </div>
- </div>
- <!-- <div class="chat-container">
- <div class="chat-messages">
- <div v-if="datas.length" class="chat-message ai" v-for="item in datas" :key="item.problem">
- <div class="chat-message user" style="text-align: right;">
- {{ item.problem }}<span>:你</span>
- </div>
- <div class="chat-message ai">
- <span>AI:</span>
- <span v-html="item.codeData"></span>
- </div>
- </div>
- </div>
- <el-input v-model="problem" placeholder="请输入你的问题..." @keyup.enter="onCodeSubmit" clearable
- class="chat-input" />
- </div>
- <br /><br />
- <div class="code-show" v-html="codeData" ref="codeRef"></div>
- <el-button type="primary" @click="onCodeSubmit">提交</el-button> -->
- </div>
- </template>
- <script setup lang="ts">
- import { ref, onMounted, watch } from 'vue';
- import { v4 as uuidv4 } from "uuid";
- import { fetchEventSource } from '@microsoft/fetch-event-source';
- import MarkdownIt from 'markdown-it';
- import useSockets from "../../../../hooks/useSockets.ts";
- import sendBtnImg from '../../../../assets/images/send_btn.svg'
- const { sockets, connectionStatus, connected } = useSockets();
- const problem = ref('')
- const codeData = ref('')
- const generatedUuid = ref('')
- const codeRef = ref<any>(null)
- const datas = ref<{ problem: string, codeData: string }[]>([])
- const userId = ref('')
- onMounted(() => {
- userId.value = localStorage.getItem('userId') || uuidv4()
- getAiAgentChat(userId.value)
- })
- const getAiAgentChat = async (userId: any) => {
- fetch('https://gpt4.cocorobo.cn/get_agent_chat', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({
- "userid": "ce601631-50e4-11ed-8c78-005056b86db5",
- "groupid": "d2e3c73a-7d8e-4da9-a95e-c6207782bab7"
- }),
- }).then((res: any) => res.json()).then((data: any) => {
- datas.value = JSON.parse(data.FunctionResponse).map((r: any) => {
- return {
- problem: decodeURIComponent(r.problem),
- codeData: decodeURIComponent(r.answer)
- }
- })
- }).catch((error) => {
- console.error('Error:', error);
- });
- }
- const onCodeSubmit = async () => {
- generatedUuid.value = uuidv4()
- localStorage.setItem('userId', userId.value)
- codeData.value = "123123";
- let datasLenth = datas.value.length
- const problemContent = problem.value
- datas.value[datasLenth] = { problem: problemContent, codeData: "" }
- let params = {
- "id": "d2e3c73a-7d8e-4da9-a95e-c6207782bab7",
- "message": problem.value,
- "userId": "ce601631-50e4-11ed-8c78-005056b86db5",
- "model": "open-gpt-4.1-mini",
- "file_ids": [],
- "sound_url": "",
- "temperature": 0.2,
- "top_p": 1,
- "max_completion_tokens": 4096,
- "stream": true,
- "uid": generatedUuid.value,
- }
-
- let alltext = ''
- let newalltext = '';
- problem.value = ''
- const md = new MarkdownIt({
- html: true,
- });
- const abortController = new AbortController();
- await fetchEventSource('https://appapi.cocorobo.cn/api/agentchats/ai_agent_chat', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(params),
- signal: abortController.signal,
- onmessage: (msg) => {
- try {
- const data = JSON.parse(msg.data);
- if (data.content == '[DONE]') {
- const finalText = md.render(alltext)
- console.log(finalText)
- codeData.value = finalText
- console.log(datas.value[datasLenth])
- const insertData = {
- userId: "ce601631-50e4-11ed-8c78-005056b86db5",
- groupId: "d2e3c73a-7d8e-4da9-a95e-c6207782bab7",
- type: "chat",
- problem: encodeURIComponent(datas.value[datasLenth].problem),
- answer: encodeURIComponent(finalText),
- userName: "jidechao@cocorobo.cc",
- file_id: "",
- alltext,
- }
- insert_operating_record(insertData)
- return
- }
- if (data.content) {
- alltext += data.content;
- newalltext = alltext;
- newalltext = newalltext.replace(/\\n/g, '\n');
- newalltext = newalltext.replace(/\\/g, '');
- // 处理代码块
- if (alltext.split('```').length % 2 === 0) {
- newalltext += '\n```\n';
- }
- // 渲染 markdown
- newalltext = md.render(newalltext);
- codeRef.value.scrollTop = codeRef.value.scrollHeight;
- datas.value[datasLenth] = { problem: problemContent, codeData: newalltext }
- }
- } catch (e) {
- console.log(e)
- }
- },
- onclose() {
- console.log("close")
- },
- onerror(error) {
- console.log(error)
- },
- })
- }
- const insert_operating_record = async (data: any) => {
- fetch('https://gpt4.cocorobo.cn/insert_chat', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(data),
- }).then((res: any) => res.json()).then((data: any) => {
- console.log(data)
- }).catch((error) => {
- console.error('Error:', error);
- })
- }
- watch(() => datas.value, (newValue, oldValue) => {
- if (newValue) {
- setTimeout(() => {
- console.log(document.getElementsByClassName("assistant-chat")[0].scrollHeight);
- codeRef.value.scrollTop = codeRef.value.scrollHeight;
- }, 1000);
- }
- })
- </script>
- <style>
- .chat-messages {
- border: 1px solid #ccc;
- border-radius: 8px;
- margin: 15px auto;
- }
- .chat-message {
- padding: 5px;
- }
- /* 智能助手侧边栏 */
- .assistant-sidebar {
- width: 100%;
- /* background: #f2f3f5; */
- /* border-left: 1px solid rgba(0, 210, 255, 0.2); */
- display: flex;
- flex-direction: column;
- /* box-shadow: -5px 0 15px rgba(0, 0, 0, 0.3); */
- height: 100%;
- overflow-y: auto;
- border-radius: 10px;
- color: #fff;
- }
- .assistant-header {
- padding: 20px;
- background: rgba(0, 0, 0, 0.2);
- border-bottom: 1px solid rgba(255, 255, 255, 0.1);
- display: flex;
- align-items: center;
- gap: 15px;
- }
- .assistant-avatar {
- width: 50px;
- height: 50px;
- background: linear-gradient(135deg, #00d2ff, #3a7bd5);
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 1.5rem;
- }
- .assistant-title h2 {
- font-size: 1.5rem;
- margin-bottom: 5px;
- }
- .assistant-title p {
- opacity: 0.7;
- font-size: 0.9rem;
- }
- .assistant-chat {
- flex: 1;
- padding: 20px;
- overflow-y: auto;
- overflow-x: hidden;
- display: flex;
- flex-direction: column;
- gap: 20px;
- min-height: calc(100% - 200px);
- }
- .message {
- padding: 15px;
- border-radius: 15px;
- position: relative;
- animation: fadeIn 0.3s ease-out;
- color: #fff;
- }
- .message code {
- white-space: pre-wrap;
- }
- .user-message {
- background: rgba(0, 210, 255, 0.15);
- align-self: flex-end;
- border-bottom-right-radius: 5px;
- }
- .assistant-message {
- background: rgba(255, 255, 255, 0.1);
- align-self: flex-start;
- border-bottom-left-radius: 5px;
- }
- .suggested-questions {
- padding: 15px;
- border-top: 1px solid rgba(255, 255, 255, 0.1);
- border-bottom: 1px solid rgba(255, 255, 255, 0.1);
- background: rgba(0, 0, 0, 0.1);
- }
- .suggested-title {
- margin-bottom: 10px;
- font-size: 1.1rem;
- display: flex;
- align-items: center;
- gap: 10px;
- }
- .suggested-list {
- display: flex;
- flex-direction: column;
- gap: 10px;
- }
- .suggested-item {
- padding: 10px 15px;
- background: rgba(255, 255, 255, 0.05);
- border-radius: 8px;
- cursor: pointer;
- transition: all 0.2s ease;
- font-size: 0.95rem;
- }
- .suggested-item:hover {
- background: rgba(0, 210, 255, 0.15);
- transform: translateX(5px);
- }
- .keyword-tabs {
- display: flex;
- flex-wrap: wrap;
- gap: 10px;
- padding: 15px;
- }
- .keyword-tab {
- padding: 8px 15px;
- background: rgba(58, 123, 213, 0.2);
- border-radius: 20px;
- font-size: 0.9rem;
- cursor: pointer;
- transition: all 0.2s ease;
- }
- .keyword-tab:hover {
- background: rgba(58, 123, 213, 0.3);
- transform: translateY(-2px);
- }
- .chat-input {
- padding: 15px;
- background: rgba(0, 0, 0, 0.2);
- display: flex;
- gap: 10px;
- }
- .chat-input textarea {
- flex: 1;
- padding: 12px 15px;
- background: rgba(255, 255, 255, 0.05);
- border: 1px solid rgba(255, 255, 255, 0.1);
- border-radius: 25px;
- width: 100%;
- min-height: 48px !important;
- height: 48px;
- padding: 10px 10px !important;
- resize: none;
- font-size: 16px !important;
- outline: none;
- line-height: 24px;
- }
- .chat-input button {
- width: 50px;
- height: 50px;
- border-radius: 50%;
- background: linear-gradient(135deg, #00d2ff, #3a7bd5);
- border: none;
- color: white;
- font-size: 1.2rem;
- cursor: pointer;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- /* 动画 */
- @keyframes fadeIn {
- from {
- opacity: 0;
- transform: translateY(10px);
- }
- to {
- opacity: 1;
- transform: translateY(0);
- }
- }
- @media (max-width: 768px) {
- .dashboard {
- grid-template-columns: 1fr;
- }
- .full-width,
- .half-width {
- grid-column: span 1;
- }
- .header {
- flex-direction: column;
- gap: 20px;
- }
- .assistant-sidebar {
- display: none;
- }
- }
- </style>
|