1.关闭eslintrc.js校验
2.完成AI辅导员弹窗 3.降低了md插件版本到v13
This commit is contained in:
@@ -48,6 +48,7 @@
|
|||||||
"clipboard": "2.0.8",
|
"clipboard": "2.0.8",
|
||||||
"core-js": "3.25.3",
|
"core-js": "3.25.3",
|
||||||
"dayjs": "^1.11.8",
|
"dayjs": "^1.11.8",
|
||||||
|
"dompurify": "^3.2.6",
|
||||||
"echarts": "5.4.0",
|
"echarts": "5.4.0",
|
||||||
"echarts-gl": "^2.0.9",
|
"echarts-gl": "^2.0.9",
|
||||||
"element-china-area-data": "^6.1.0",
|
"element-china-area-data": "^6.1.0",
|
||||||
@@ -63,6 +64,7 @@
|
|||||||
"jspdf": "^2.5.2",
|
"jspdf": "^2.5.2",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mapv-three": "^1.0.18",
|
"mapv-three": "^1.0.18",
|
||||||
|
"markdown-it": "^13.0.2",
|
||||||
"marked": "^4.3.0",
|
"marked": "^4.3.0",
|
||||||
"nprogress": "0.2.0",
|
"nprogress": "0.2.0",
|
||||||
"print-js": "^1.6.0",
|
"print-js": "^1.6.0",
|
||||||
|
@@ -21,8 +21,9 @@ export const getHistory = ({
|
|||||||
limit
|
limit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果有beforeId参数,添加到请求中(后端参数名为firstId)
|
||||||
if (beforeId) {
|
if (beforeId) {
|
||||||
params.beforeId = beforeId
|
params.firstId = beforeId
|
||||||
}
|
}
|
||||||
|
|
||||||
return request({
|
return request({
|
||||||
|
BIN
src/assets/ai/AI.png
Normal file
BIN
src/assets/ai/AI.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 MiB |
1
src/assets/ai/good.svg
Normal file
1
src/assets/ai/good.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1753693561642" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11887" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M64 483.04V872c0 37.216 30.144 67.36 67.36 67.36H192V416.32l-60.64-0.64A67.36 67.36 0 0 0 64 483.04zM857.28 344.992l-267.808 1.696c12.576-44.256 18.944-83.584 18.944-118.208 0-78.56-68.832-155.488-137.568-145.504-60.608 8.8-67.264 61.184-67.264 126.816v59.264c0 76.064-63.84 140.864-137.856 148L256 416.96v522.4h527.552a102.72 102.72 0 0 0 100.928-83.584l73.728-388.96a102.72 102.72 0 0 0-100.928-121.824z" p-id="11888" fill="#4F46E5"></path></svg>
|
After Width: | Height: | Size: 782 B |
1
src/assets/ai/tread.svg
Normal file
1
src/assets/ai/tread.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1753693581732" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="13840" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M611.188364 651.962182h226.56a93.090909 93.090909 0 0 0 91.834181-108.334546l-61.905454-372.689454A93.090909 93.090909 0 0 0 775.889455 93.090909H372.968727v558.871273c82.152727 81.338182 72.866909 210.571636 88.832 242.338909 15.941818 31.767273 47.616 36.119273 55.621818 36.608 39.703273 0 179.665455-32.395636 93.789091-278.946909zM313.832727 651.636364V93.090909H202.891636a93.090909 93.090909 0 0 0-92.997818 88.901818l-16.709818 372.363637A93.090909 93.090909 0 0 0 186.181818 651.636364h127.650909z" fill="#4F46E5" p-id="13841"></path></svg>
|
After Width: | Height: | Size: 883 B |
BIN
src/assets/ai/yonghu.png
Normal file
BIN
src/assets/ai/yonghu.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.3 KiB |
File diff suppressed because it is too large
Load Diff
@@ -21,10 +21,11 @@
|
|||||||
<!-- 其他页面内容 -->
|
<!-- 其他页面内容 -->
|
||||||
<!-- 触发按钮,控制弹窗显示隐藏 -->
|
<!-- 触发按钮,控制弹窗显示隐藏 -->
|
||||||
<div class="ai-hover" @click="showAI = !showAI">
|
<div class="ai-hover" @click="showAI = !showAI">
|
||||||
<i class="el-icon-question" style="font-size: 24px;"></i>
|
<span v-if="!showAI" style="font-size: 14px; font-weight: bold;">AI</span>
|
||||||
|
<i v-else class="el-icon-close" style="font-size: 20px;"></i>
|
||||||
</div>
|
</div>
|
||||||
<!-- 聊天弹窗,通过 v-if 控制显隐 -->
|
<!-- 聊天弹窗,通过 v-if 控制显隐 -->
|
||||||
<ChatPopup v-if="showAI" />
|
<ChatPopup v-if="showAI" @close="showAI = false" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { getToken } from './auth'
|
import { getTokenKeySessionStorage } from './auth'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { showToast } from '@/utils/toast' // 请替换为你的Toast组件
|
import { showToast } from '@/utils/toast' // 请替换为你的Toast组件
|
||||||
|
|
||||||
// 创建axios实例
|
// 创建axios实例
|
||||||
const service = axios.create({
|
const service = axios.create({
|
||||||
baseURL: process.env.VUE_APP_API_BASE_URL || 'http://localhost:8088/aitutor/aichat',
|
baseURL: process.env.VUE_APP_API_BASE_URL || 'http://localhost:8088',
|
||||||
timeout: 15000,
|
timeout: 15000,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
@@ -16,7 +16,7 @@ const service = axios.create({
|
|||||||
service.interceptors.request.use(
|
service.interceptors.request.use(
|
||||||
config => {
|
config => {
|
||||||
// 从本地存储获取token
|
// 从本地存储获取token
|
||||||
const token = getToken()
|
const token = getTokenKeySessionStorage()
|
||||||
if (token) {
|
if (token) {
|
||||||
config.headers.Authorization = `Bearer ${token}`
|
config.headers.Authorization = `Bearer ${token}`
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { getToken } from '@/utils/auth'
|
import { getTokenKeySessionStorage } from "@/utils/auth";
|
||||||
|
|
||||||
// 使用环境变量配置基础URL
|
// 使用环境变量配置基础URL
|
||||||
const BASE_URL = process.env.VUE_APP_API_BASE_URL || 'http://localhost:8088'
|
const BASE_URL = process.env.VUE_APP_API_BASE_URL || "http://localhost:8088";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建聊天流式连接
|
* 创建聊天流式连接
|
||||||
@@ -13,54 +13,55 @@ const BASE_URL = process.env.VUE_APP_API_BASE_URL || 'http://localhost:8088'
|
|||||||
* @returns {Object} 包含stream和cancel方法的对象
|
* @returns {Object} 包含stream和cancel方法的对象
|
||||||
*/
|
*/
|
||||||
export function createChatStream(params) {
|
export function createChatStream(params) {
|
||||||
const requestId = `req-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
const requestId = `req-${Date.now()}-${Math.random()
|
||||||
const url = `${BASE_URL}/aitutor/aichat/stream`
|
.toString(36)
|
||||||
const token = getToken()
|
.slice(2, 8)}`;
|
||||||
|
const url = `${BASE_URL}/aitutor/aichat/stream`;
|
||||||
|
const token = getTokenKeySessionStorage();
|
||||||
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
throw new Error('请先登录')
|
throw new Error("请先登录");
|
||||||
}
|
}
|
||||||
|
|
||||||
const controller = new AbortController()
|
const controller = new AbortController();
|
||||||
|
|
||||||
const fetchPromise = fetch(url, {
|
const fetchPromise = fetch(url, {
|
||||||
method: 'POST',
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
"Content-Type": "application/json",
|
||||||
'Authorization': `Bearer ${token}`,
|
Authorization: `Bearer ${token}`,
|
||||||
'X-Request-ID': requestId
|
"X-Request-ID": requestId,
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
query: params.prompt,
|
query: params.prompt,
|
||||||
user_id: params.userId,
|
user_id: params.userId,
|
||||||
user_name: params.userName,
|
user_name: params.userName,
|
||||||
user_token: params.user_token || '123',
|
user_token: params.user_token || "123",
|
||||||
user_role: 'student',
|
user_role: "student",
|
||||||
conversation_id: params.conversationId || null,
|
conversation_id: params.conversationId || null,
|
||||||
}),
|
}),
|
||||||
signal: controller.signal
|
signal: controller.signal,
|
||||||
})
|
}).then((response) => {
|
||||||
.then(response => {
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`HTTP ${response.status}`)
|
throw new Error(`HTTP ${response.status}`);
|
||||||
}
|
}
|
||||||
if (!response.body) {
|
if (!response.body) {
|
||||||
throw new Error('Response body is null')
|
throw new Error("Response body is null");
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
reader: response.body.getReader(),
|
reader: response.body.getReader(),
|
||||||
decoder: new TextDecoder('utf-8')
|
decoder: new TextDecoder("utf-8"),
|
||||||
}
|
};
|
||||||
})
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
stream: fetchPromise,
|
stream: fetchPromise,
|
||||||
cancel: (reason) => {
|
cancel: (reason) => {
|
||||||
if (!controller.signal.aborted) {
|
if (!controller.signal.aborted) {
|
||||||
controller.abort(reason)
|
controller.abort(reason);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,40 +72,44 @@ export function createChatStream(params) {
|
|||||||
* @param {Function} onError 错误回调
|
* @param {Function} onError 错误回调
|
||||||
* @param {Function} onComplete 完成回调
|
* @param {Function} onComplete 完成回调
|
||||||
*/
|
*/
|
||||||
export async function processStream(reader, decoder, { onMessage, onError, onComplete }) {
|
export async function processStream(
|
||||||
let buffer = ''
|
reader,
|
||||||
|
decoder,
|
||||||
|
{ onMessage, onError, onComplete }
|
||||||
|
) {
|
||||||
|
let buffer = "";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
while (true) {
|
while (true) {
|
||||||
const { done, value } = await reader.read()
|
const { done, value } = await reader.read();
|
||||||
if (done) break
|
if (done) break;
|
||||||
|
|
||||||
buffer += decoder.decode(value, { stream: true })
|
buffer += decoder.decode(value, { stream: true });
|
||||||
const lines = buffer.split('\n')
|
const lines = buffer.split("\n");
|
||||||
buffer = lines.pop() || ''
|
buffer = lines.pop() || "";
|
||||||
|
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
if (!line.trim()) continue
|
if (!line.trim()) continue;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(line)
|
const data = JSON.parse(line);
|
||||||
if (typeof onMessage === 'function') {
|
if (typeof onMessage === "function") {
|
||||||
onMessage(data)
|
onMessage(data);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('解析消息失败:', line, e)
|
console.warn("解析消息失败:", line, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof onComplete === 'function') {
|
if (typeof onComplete === "function") {
|
||||||
onComplete()
|
onComplete();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.name !== 'AbortError' && typeof onError === 'function') {
|
if (error.name !== "AbortError" && typeof onError === "function") {
|
||||||
onError(error)
|
onError(error);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
reader.releaseLock()
|
reader.releaseLock();
|
||||||
}
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user