Compare commits

..

No commits in common. "bc83efc14ee9e922310573e4ac4608225452fc9f" and "3bad5b423d5c18b4a59a7f115337ea002c313743" have entirely different histories.

2 changed files with 70 additions and 185 deletions

View File

@ -46,8 +46,8 @@
</div> </div>
<!-- 学术产出 --> <!-- 学术产出 -->
<div class="dashboard-panel" style="flex: 1.4;"> <div class="dashboard-panel" style="flex: 1 1 0;">
<h2 style="margin-bottom: 0;">学术产出</h2> <h2>学术产出</h2>
<div class="output-content"> <div class="output-content">
<div ref="outputChartRef" class="chart-container-65"></div> <div ref="outputChartRef" class="chart-container-65"></div>
<!-- <div class="international-impact"> <!-- <div class="international-impact">
@ -138,18 +138,10 @@
<div v-html="dashboardData2?.prompt"></div> <div v-html="dashboardData2?.prompt"></div>
</div> </div>
<div v-for="(message, index) in chatMessages" :key="index" <div v-for="(message, index) in chatMessages" :key="index"
:class="['message', message.role === 'user' ? 'user-message' : 'assistant-message']" v-show="!message.isHidden"> :class="['message', message.role === 'user' ? 'user-message' : 'assistant-message']">
<!-- 修改这里的显示逻辑 --> <div v-html="message.content"></div>
<div v-if="message.isThinking" class="thinking-message">
<span class="loading-dot"></span>
<span class="loading-dot"></span>
<span class="loading-dot"></span>
</div>
<div v-else-if="message.isTyping" v-html="message.content"></div>
<div v-else v-html="message.content"></div>
</div> </div>
<div v-if="isLoading && (!chatMessages.length || !chatMessages[chatMessages.length-1].isThinking)" <div v-if="isLoading" class="assistant-message loading-message">
class="assistant-message loading-message">
<span class="loading-dot"></span> <span class="loading-dot"></span>
<span class="loading-dot"></span> <span class="loading-dot"></span>
<span class="loading-dot"></span> <span class="loading-dot"></span>
@ -202,15 +194,14 @@ const redirectToResearchEvaluation = async () => {
const result = await response.json(); const result = await response.json();
if (result.code === 0) { if (result.code === 0) {
// //
// await localStorage.setItem('access_token', result.data.access); await localStorage.setItem('access_token', result.data.access);
// await localStorage.setItem('refresh_token', result.data.refresh); await localStorage.setItem('refresh_token', result.data.refresh);
// await localStorage.setItem('user_info', JSON.stringify({ await localStorage.setItem('user_info', JSON.stringify({
// user_id: result.data.user_id, user_id: result.data.user_id,
// username: result.data.username, username: result.data.username,
// permission: '2' permission: '2'
// })); }));
window.open(`http://82.156.236.221:10004/user/file?refresh_token=${result.data.refresh}`), '_blank'; window.location.href = 'http://82.156.236.221:10004/user/file'
} else { } else {
console.error('代理登录失败:', result.error); console.error('代理登录失败:', result.error);
} }
@ -227,14 +218,14 @@ const redirectToResearchEvaluation2 = async () => {
const result = await response.json(); const result = await response.json();
if (result.code === 0) { if (result.code === 0) {
// //
// await localStorage.setItem('access_token', result.data.access); await localStorage.setItem('access_token', result.data.access);
// await localStorage.setItem('refresh_token', result.data.refresh); await localStorage.setItem('refresh_token', result.data.refresh);
// await localStorage.setItem('user_info', JSON.stringify({ await localStorage.setItem('user_info', JSON.stringify({
// user_id: result.data.user_id, user_id: result.data.user_id,
// username: result.data.username, username: result.data.username,
// permission: '1' permission: '1'
// })) }))
window.open(`http://82.156.236.221:10004/admin/manage?refresh_token=${result.data.refresh}`), '_blank'; window.location.href = 'http://82.156.236.221:10004/admin/manage'
} else { } else {
console.error('代理登录失败:', result.error); console.error('代理登录失败:', result.error);
} }
@ -375,8 +366,6 @@ const chatMessagesRef = ref(null)
const userInput = ref('') const userInput = ref('')
const isLoading = ref(false) const isLoading = ref(false)
const chatMessages = ref([]) const chatMessages = ref([])
let typingInterval = null
let thinkingInterval = null
// DeepSeek API // DeepSeek API
const sendMessage = async () => { const sendMessage = async () => {
@ -384,41 +373,35 @@ const sendMessage = async () => {
// //
const userMessage = userInput.value.trim(); const userMessage = userInput.value.trim();
chatMessages.value.push({ chatMessages.value.push({ role: 'user', content: userMessage });
role: 'user',
content: userMessage,
isTyping: false,
isThinking: false
});
userInput.value = ''; userInput.value = '';
// //
// await nextTick(); await nextTick();
// scrollToBottom(); scrollToBottom();
// //
isLoading.value = true; isLoading.value = true;
chatMessages.value.push({ chatMessages.value.push({ role: 'assistant', content: '思考中...' });
role: 'assistant',
content: '',
isTyping: false,
isThinking: true,
isHidden: true //
});
const aiMessageIndex = chatMessages.value.length - 1; const aiMessageIndex = chatMessages.value.length - 1;
try { try {
// 5
const apiMessages = chatMessages.value
.slice(-5)
.map(msg => ({ role: msg.role, content: msg.content }));
// API // API
const response = await fetch(`${getChatApiUrl()}/chat/chat`, { const response = await fetch('https://api.deepseek.com/chat/completions', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Authorization': 'Bearer sk-06801499dd52426fa7cf3b0670931e3a'
}, },
body: JSON.stringify({ body: JSON.stringify({
model: "deepseek-r1:7b", model: 'deepseek-chat',
messages: [{ role: "user", content: userMessage }], messages: apiMessages,
options: { temperature: 0.6 }, stream: true //
keep_thinking: false
}) })
}); });
@ -428,75 +411,37 @@ const sendMessage = async () => {
// //
const reader = response.body.getReader(); const reader = response.body.getReader();
let fullResponse = '';
const decoder = new TextDecoder(); 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) { 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 }); // APIndjson
const lines = buffer.split("\n"); const chunk = decoder.decode(value);
buffer = lines.pop() || ""; const lines = chunk.split('\n').filter(line => line.trim());
for (const line of lines) { for (const line of lines) {
if (!line.trim()) continue;
try { try {
const obj = JSON.parse(line); const data = JSON.parse(line.replace('data: ', ''));
if (obj.message?.content) { if (data.choices?.[0]?.delta?.content) {
fullResponse += obj.message.content; fullResponse += data.choices[0].delta.content;
// UI
chatMessages.value[aiMessageIndex].content = md.render(fullResponse);
scrollToBottom();
} }
} catch (e) { } catch (e) {
console.warn('解析JSON失败:', e); console.warn('解析流数据失败:', e);
} }
} }
} }
// APIisLoadingfalse
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) { } catch (error) {
console.error('请求失败:', error); console.error('请求失败:', error);
chatMessages.value[aiMessageIndex].content = '抱歉,回答时遇到问题,请重试。';
} finally {
isLoading.value = false; isLoading.value = false;
chatMessages.value[aiMessageIndex] = {
role: 'assistant',
content: '抱歉,回答时遇到问题,请重试。',
isTyping: false,
isThinking: false,
isHidden: false //
};
scrollToBottom(); scrollToBottom();
} }
}; };
@ -505,7 +450,7 @@ const sendMessage = async () => {
const scrollToBottom = () => { const scrollToBottom = () => {
nextTick().then(() => { nextTick().then(() => {
if (chatMessagesRef.value) { if (chatMessagesRef.value) {
chatMessagesRef.value.scrollTop = chatMessagesRef.value.scrollHeight; chatMessagesRef.value.scrollTop = chatMessagesRef.value.scrollHeight;
} }
}); });
}; };
@ -558,7 +503,7 @@ const dashboardData2 = ref(null);
const loading = ref(true); const loading = ref(true);
// API // API
import { getApiBaseUrl, getChatApiUrl } from '../config'; import { getApiBaseUrl } from '../config';
// API // API
const fetchDashboardData = async () => { const fetchDashboardData = async () => {
@ -1141,27 +1086,14 @@ const initCharts = () => {
'博士': outputLegendStatus.value[2], '博士': outputLegendStatus.value[2],
'硕士': outputLegendStatus.value[1], '硕士': outputLegendStatus.value[1],
'学士': outputLegendStatus.value[0], '学士': 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: { yAxis: {
type: 'category', type: 'category',
data: outputData.value.months, data: outputData.value.months,
axisLine: { lineStyle: { color: '#fff' } }, axisLine: { lineStyle: { color: '#fff' } },
axisLabel: { axisLabel: { color: '#fff' }
color: '#fff',
margin: 15 // y
},
axisTick: {
alignWithLabel: true
}
}, },
xAxis: { xAxis: {
type: 'value', type: 'value',
@ -1173,8 +1105,7 @@ const initCharts = () => {
type: 'dashed', type: 'dashed',
color: 'rgba(255, 255, 255, 0.2)' color: 'rgba(255, 255, 255, 0.2)'
} }
}, }
interval: 1
}, },
series: [ series: [
{ {
@ -1182,16 +1113,13 @@ const initCharts = () => {
type: 'bar', type: 'bar',
data: outputData.value.doctor, data: outputData.value.doctor,
itemStyle: { color: '#4080ff' }, itemStyle: { color: '#4080ff' },
barWidth: '25%', // barWidth: '20%',
barGap: '20%', // barGap: '20%',
label: { label: {
show: true, show: true,
position: 'right', position: 'right',
color: '#fff', color: '#fff',
formatter: function(params) { formatter: '{c}'
// 0
return params.value > 0 ? params.value : '';
}
} }
}, },
{ {
@ -1199,16 +1127,13 @@ const initCharts = () => {
type: 'bar', type: 'bar',
data: outputData.value.master, data: outputData.value.master,
itemStyle: { color: 'rgb(107,187,196)' }, itemStyle: { color: 'rgb(107,187,196)' },
barWidth: '25%', barWidth: '20%',
barGap: '20%', barGap: '20%',
label: { label: {
show: true, show: true,
position: 'right', position: 'right',
color: '#fff', color: '#fff',
formatter: function(params) { formatter: '{c}'
// 0
return params.value > 0 ? params.value : '';
}
} }
}, },
{ {
@ -1216,16 +1141,13 @@ const initCharts = () => {
type: 'bar', type: 'bar',
data: outputData.value.bachelor, data: outputData.value.bachelor,
itemStyle: { color: 'rgb(107,187,19)' }, itemStyle: { color: 'rgb(107,187,19)' },
barWidth: '25%', barWidth: '20%',
barGap: '20%', barGap: '20%',
label: { label: {
show: true, show: true,
position: 'right', position: 'right',
color: '#fff', color: '#fff',
formatter: function(params) { formatter: '{c}'
// 0
return params.value > 0 ? params.value : '';
}
} }
} }
] ]
@ -1390,12 +1312,6 @@ const newsData = ref([]);
// //
onUnmounted(() => { onUnmounted(() => {
if (typingInterval) {
clearInterval(typingInterval);
}
if (thinkingInterval) {
clearInterval(thinkingInterval);
}
window.removeEventListener('resize', () => {}); window.removeEventListener('resize', () => {});
}) })
</script> </script>
@ -1849,31 +1765,7 @@ onUnmounted(() => {
.assistant-panel .panel-header { .assistant-panel .panel-header {
display: flex; display: flex;
align-items: center; align-items: center;
cursor: move; cursor: move; /* 整个头部可拖动 */
user-select: none; 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;
} }
</style> </style>

View File

@ -3,23 +3,16 @@ const env = import.meta.env.MODE || 'development';
const config = { const config = {
development: { development: {
// apiBaseUrl: 'http://192.168.5.49:48080', apiBaseUrl: 'http://192.168.5.102:48080',
apiBaseUrl: 'http://36.103.199.218:48088',
chatApiUrl: 'http://36.103.199.218:8093'
}, },
production: { production: {
// apiBaseUrl: 'http://221.238.217.216:4162', apiBaseUrl: 'http://221.238.217.216:4162',
// chatApiUrl: 'http://221.238.217.216:8093', // apiBaseUrl: 'http://36.103.199.218:48088',
apiBaseUrl: 'http://36.103.199.218:48088',
chatApiUrl: 'http://36.103.199.218:8093',
} }
}; };
export const getApiBaseUrl = () => { export const getApiBaseUrl = () => {
return config[env].apiBaseUrl; return config[env].apiBaseUrl;
}; };
export const getChatApiUrl = () => {
return config[env].chatApiUrl;
}
export default config; export default config;