+
@@ -194,14 +202,15 @@ const redirectToResearchEvaluation = async () => {
const result = await response.json();
if (result.code === 0) {
// 跳转到目标网站
- await localStorage.setItem('access_token', result.data.access);
- await localStorage.setItem('refresh_token', result.data.refresh);
- await localStorage.setItem('user_info', JSON.stringify({
- user_id: result.data.user_id,
- username: result.data.username,
- permission: '2'
- }));
- window.location.href = 'http://82.156.236.221:10004/user/file'
+ // await localStorage.setItem('access_token', result.data.access);
+ // await localStorage.setItem('refresh_token', result.data.refresh);
+ // await localStorage.setItem('user_info', JSON.stringify({
+ // user_id: result.data.user_id,
+ // username: result.data.username,
+ // permission: '2'
+ // }));
+ window.open(`http://82.156.236.221:10004/user/file?refresh_token=${result.data.refresh}`), '_blank';
+
} else {
console.error('代理登录失败:', result.error);
}
@@ -218,14 +227,14 @@ const redirectToResearchEvaluation2 = async () => {
const result = await response.json();
if (result.code === 0) {
// 跳转到目标网站
- await localStorage.setItem('access_token', result.data.access);
- await localStorage.setItem('refresh_token', result.data.refresh);
- await localStorage.setItem('user_info', JSON.stringify({
- user_id: result.data.user_id,
- username: result.data.username,
- permission: '1'
- }))
- window.location.href = 'http://82.156.236.221:10004/admin/manage'
+ // await localStorage.setItem('access_token', result.data.access);
+ // await localStorage.setItem('refresh_token', result.data.refresh);
+ // await localStorage.setItem('user_info', JSON.stringify({
+ // user_id: result.data.user_id,
+ // username: result.data.username,
+ // permission: '1'
+ // }))
+ window.open(`http://82.156.236.221:10004/admin/manage?refresh_token=${result.data.refresh}`), '_blank';
} else {
console.error('代理登录失败:', result.error);
}
@@ -366,6 +375,8 @@ const chatMessagesRef = ref(null)
const userInput = ref('')
const isLoading = ref(false)
const chatMessages = ref([])
+let typingInterval = null
+let thinkingInterval = null
// DeepSeek API 调用函数
const sendMessage = async () => {
@@ -373,35 +384,41 @@ const sendMessage = async () => {
// 添加用户消息到对话
const userMessage = userInput.value.trim();
- chatMessages.value.push({ role: 'user', content: userMessage });
+ chatMessages.value.push({
+ role: 'user',
+ content: userMessage,
+ isTyping: false,
+ isThinking: false
+ });
userInput.value = '';
- // 立即滚动到底部(用户消息)
- await nextTick();
- scrollToBottom();
+ // 立即滚动到底部
+ // await nextTick();
+ // scrollToBottom();
// 设置加载状态并添加占位消息
isLoading.value = true;
- chatMessages.value.push({ role: 'assistant', content: '思考中...' });
+ chatMessages.value.push({
+ role: 'assistant',
+ content: '',
+ isTyping: false,
+ isThinking: true,
+ isHidden: true // 添加一个标志表示消息暂时隐藏
+ });
const aiMessageIndex = chatMessages.value.length - 1;
try {
- // 只保留最近5条消息以优化性能
- const apiMessages = chatMessages.value
- .slice(-5)
- .map(msg => ({ role: msg.role, content: msg.content }));
-
// 调用API(启用流式传输)
- const response = await fetch('https://api.deepseek.com/chat/completions', {
+ const response = await fetch(`${getChatApiUrl()}/chat/chat`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
- 'Authorization': 'Bearer sk-06801499dd52426fa7cf3b0670931e3a'
},
body: JSON.stringify({
- model: 'deepseek-chat',
- messages: apiMessages,
- stream: true // 关键优化:启用流式响应
+ model: "deepseek-r1:7b",
+ messages: [{ role: "user", content: userMessage }],
+ options: { temperature: 0.6 },
+ keep_thinking: false
})
});
@@ -411,37 +428,75 @@ const sendMessage = async () => {
// 流式处理数据
const reader = response.body.getReader();
- let fullResponse = '';
const decoder = new TextDecoder();
-
+ let buffer = "";
+ let fullResponse = "";
+
+ // 替换思考中消息为实际响应消息
+ chatMessages.value[aiMessageIndex] = {
+ role: 'assistant',
+ content: '',
+ isTyping: false,
+ isThinking: false,
+ isHidden: true // 添加一个标志表示消息暂时隐藏
+ };
+ // 立即滚动到底部
+ await nextTick();
+ scrollToBottom();
+ // 等待API响应完成
while (true) {
const { done, value } = await reader.read();
if (done) break;
-
- // 解析流式数据(假设API返回ndjson格式)
- const chunk = decoder.decode(value);
- const lines = chunk.split('\n').filter(line => line.trim());
-
+
+ buffer += decoder.decode(value, { stream: true });
+ const lines = buffer.split("\n");
+ buffer = lines.pop() || "";
+
for (const line of lines) {
+ if (!line.trim()) continue;
+
try {
- const data = JSON.parse(line.replace('data: ', ''));
- if (data.choices?.[0]?.delta?.content) {
- fullResponse += data.choices[0].delta.content;
- // 实时更新UI(逐字显示)
- chatMessages.value[aiMessageIndex].content = md.render(fullResponse);
- scrollToBottom();
+ const obj = JSON.parse(line);
+ if (obj.message?.content) {
+ fullResponse += obj.message.content;
}
} catch (e) {
- console.warn('解析流数据失败:', e);
+ console.warn('解析JSON失败:', e);
}
}
}
+ // API响应完成后,设置isLoading为false
+ setTimeout(() => {
+ isLoading.value = false;
+ // 移除隐藏标志,开始打字机效果
+ chatMessages.value[aiMessageIndex].isHidden = false;
+
+ // 然后开始打字机效果
+ let typingIndex = 0;
+ const typingInterval = setInterval(() => {
+ if (typingIndex < fullResponse.length) {
+ chatMessages.value[aiMessageIndex].content =
+ fullResponse.substring(0, typingIndex + 1);
+ typingIndex++;
+ scrollToBottom();
+ } else {
+ clearInterval(typingInterval);
+ }
+ }, 20);
+ }, 100)
+
+
} catch (error) {
console.error('请求失败:', error);
- chatMessages.value[aiMessageIndex].content = '抱歉,回答时遇到问题,请重试。';
- } finally {
isLoading.value = false;
+ chatMessages.value[aiMessageIndex] = {
+ role: 'assistant',
+ content: '抱歉,回答时遇到问题,请重试。',
+ isTyping: false,
+ isThinking: false,
+ isHidden: false // 确保错误消息显示
+ };
scrollToBottom();
}
};
@@ -450,7 +505,7 @@ const sendMessage = async () => {
const scrollToBottom = () => {
nextTick().then(() => {
if (chatMessagesRef.value) {
- chatMessagesRef.value.scrollTop = chatMessagesRef.value.scrollHeight;
+ chatMessagesRef.value.scrollTop = chatMessagesRef.value.scrollHeight;
}
});
};
@@ -503,7 +558,7 @@ const dashboardData2 = ref(null);
const loading = ref(true);
// 引入API配置
-import { getApiBaseUrl } from '../config';
+import { getApiBaseUrl, getChatApiUrl } from '../config';
// 从API获取仪表盘数据
const fetchDashboardData = async () => {
@@ -1086,14 +1141,27 @@ const initCharts = () => {
'博士': outputLegendStatus.value[2],
'硕士': outputLegendStatus.value[1],
'学士': outputLegendStatus.value[0],
- }
+ },
+ top: 10 // 调整图例位置
+ },
+ grid: {
+ left: '3%',
+ right: '12%',
+ bottom: '3%',
+ top: '20%', // 增加顶部间距给图例
+ containLabel: true
},
- grid: { left: '3%', right: '12%', bottom: '3%', containLabel: true },
yAxis: {
type: 'category',
data: outputData.value.months,
axisLine: { lineStyle: { color: '#fff' } },
- axisLabel: { color: '#fff' }
+ axisLabel: {
+ color: '#fff',
+ margin: 15 // 增加y轴标签边距
+ },
+ axisTick: {
+ alignWithLabel: true
+ }
},
xAxis: {
type: 'value',
@@ -1105,7 +1173,8 @@ const initCharts = () => {
type: 'dashed',
color: 'rgba(255, 255, 255, 0.2)'
}
- }
+ },
+ interval: 1
},
series: [
{
@@ -1113,13 +1182,16 @@ const initCharts = () => {
type: 'bar',
data: outputData.value.doctor,
itemStyle: { color: '#4080ff' },
- barWidth: '20%',
- barGap: '20%',
+ barWidth: '25%', // 减小柱宽度
+ barGap: '20%', // 增加柱子间距
label: {
show: true,
position: 'right',
color: '#fff',
- formatter: '{c}'
+ formatter: function(params) {
+ // 只显示非零值,避免0重叠
+ return params.value > 0 ? params.value : '';
+ }
}
},
{
@@ -1127,13 +1199,16 @@ const initCharts = () => {
type: 'bar',
data: outputData.value.master,
itemStyle: { color: 'rgb(107,187,196)' },
- barWidth: '20%',
+ barWidth: '25%',
barGap: '20%',
label: {
show: true,
position: 'right',
color: '#fff',
- formatter: '{c}'
+ formatter: function(params) {
+ // 只显示非零值,避免0重叠
+ return params.value > 0 ? params.value : '';
+ }
}
},
{
@@ -1141,13 +1216,16 @@ const initCharts = () => {
type: 'bar',
data: outputData.value.bachelor,
itemStyle: { color: 'rgb(107,187,19)' },
- barWidth: '20%',
+ barWidth: '25%',
barGap: '20%',
label: {
show: true,
position: 'right',
color: '#fff',
- formatter: '{c}'
+ formatter: function(params) {
+ // 只显示非零值,避免0重叠
+ return params.value > 0 ? params.value : '';
+ }
}
}
]
@@ -1312,6 +1390,12 @@ const newsData = ref([]);
// 组件卸载时清理定时器
onUnmounted(() => {
+ if (typingInterval) {
+ clearInterval(typingInterval);
+ }
+ if (thinkingInterval) {
+ clearInterval(thinkingInterval);
+ }
window.removeEventListener('resize', () => {});
})
@@ -1765,7 +1849,31 @@ onUnmounted(() => {
.assistant-panel .panel-header {
display: flex;
align-items: center;
- cursor: move; /* 整个头部可拖动 */
- user-select: none; /* 防止拖动时选中文本 */
+ cursor: move;
+ user-select: none;
+}
+
+/* 思考消息样式 */
+.thinking-message {
+ display: flex;
+ align-items: center;
+}
+
+.thinking-dots::after {
+ content: '...';
+ display: inline-block;
+ width: 20px;
+ animation: blinkDots 1.5s infinite steps(4, end);
+
+}
+
+@keyframes blinkDots {
+ 0%, 100% { opacity: 0; }
+ 25% { opacity: 0.5; }
+ 50% { opacity: 1; }
+}
+
+.message {
+ transition: all 0.3s ease;
}
\ No newline at end of file
diff --git a/src/config.js b/src/config.js
index d275462..f7f29f8 100644
--- a/src/config.js
+++ b/src/config.js
@@ -3,16 +3,23 @@ const env = import.meta.env.MODE || 'development';
const config = {
development: {
- apiBaseUrl: 'http://192.168.5.102:48080',
+ // apiBaseUrl: 'http://192.168.5.49:48080',
+ apiBaseUrl: 'http://36.103.199.218:48088',
+ chatApiUrl: 'http://36.103.199.218:8093'
},
production: {
- apiBaseUrl: 'http://221.238.217.216:4162',
- // apiBaseUrl: 'http://36.103.199.218:48088',
+ // apiBaseUrl: 'http://221.238.217.216:4162',
+ // chatApiUrl: 'http://221.238.217.216:8093',
+ apiBaseUrl: 'http://36.103.199.218:48088',
+ chatApiUrl: 'http://36.103.199.218:8093',
}
};
export const getApiBaseUrl = () => {
return config[env].apiBaseUrl;
};
+export const getChatApiUrl = () => {
+ return config[env].chatApiUrl;
+}
export default config;
\ No newline at end of file