665 lines
23 KiB
HTML
665 lines
23 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<script src="lang-1.8.js"></script>
|
|
<title data-i18n="historytitle">讨论内容</title>
|
|
<style>
|
|
body {
|
|
font-family: 'Helvetica Neue', Arial, sans-serif;
|
|
max-width: 1000px;
|
|
margin: 0 auto;
|
|
padding: 50px 20px;
|
|
line-height: 1.8;
|
|
background-color: #f5f5f5;
|
|
color: #333;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
}
|
|
|
|
h1 {
|
|
font-size: 3.2em;
|
|
color: #1a1a1a;
|
|
margin-bottom: 40px;
|
|
text-align: center;
|
|
letter-spacing: 5px;
|
|
font-weight: 700;
|
|
width: 100%;
|
|
}
|
|
|
|
#content-display {
|
|
width: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
}
|
|
|
|
.message-container {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
background-color: white;
|
|
border-radius: 15px;
|
|
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
|
|
margin-bottom: 25px;
|
|
padding: 25px;
|
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
border-left: 4px solid #1a1a1a;
|
|
max-width: 900px;
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.message-container:hover {
|
|
transform: translateY(-3px);
|
|
box-shadow: 0 6px 12px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
.message-role {
|
|
font-weight: 600;
|
|
margin-bottom: 15px;
|
|
color: #1a1a1a;
|
|
font-size: 1.1em;
|
|
text-transform: uppercase;
|
|
letter-spacing: 1px;
|
|
}
|
|
|
|
.message-text {
|
|
white-space: pre-wrap;
|
|
margin-bottom: 20px;
|
|
line-height: 1.8;
|
|
color: #333;
|
|
}
|
|
|
|
.audio-player {
|
|
width: 100%;
|
|
margin-top: 20px;
|
|
border-radius: 25px;
|
|
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
#back-to-square {
|
|
/*position: fixed;*/
|
|
top: 20px;
|
|
left: 20px;
|
|
padding: 10px 20px;
|
|
background-color: #1a1a1a;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 25px;
|
|
cursor: pointer;
|
|
font-size: 16px;
|
|
transition: background-color 0.3s ease, transform 0.2s ease;
|
|
text-transform: uppercase;
|
|
letter-spacing: 1px;
|
|
z-index: 1000;
|
|
}
|
|
|
|
#back-to-square:hover {
|
|
background-color: #333;
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.topic-message {
|
|
background-color: #000000;
|
|
border: 2px solid #ffffff;
|
|
border-radius: 10px;
|
|
padding: 15px 25px;
|
|
margin: 20px 0;
|
|
text-align: center;
|
|
box-shadow: 0 4px 15px rgba(255, 255, 255, 0.1);
|
|
max-width: 900px;
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
|
|
.topic-message .message-role {
|
|
font-size: 1.2em;
|
|
color: #ffffff;
|
|
margin-bottom: 5px;
|
|
text-transform: uppercase;
|
|
letter-spacing: 1.5px;
|
|
font-weight: bold;
|
|
margin-bottom: 0;
|
|
font-weight: bold;
|
|
text-align: center;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
body {
|
|
padding: 30px 15px;
|
|
}
|
|
|
|
#back-to-square {
|
|
top: 10px;
|
|
left: 10px;
|
|
font-size: 14px;
|
|
padding: 8px 16px;
|
|
}
|
|
|
|
.message-container, .topic-message {
|
|
padding: 20px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
h1 {
|
|
font-size: 2.5em;
|
|
letter-spacing: 3px;
|
|
}
|
|
}
|
|
|
|
#download-audio-btn {
|
|
background-color: #000000;
|
|
color: #ffffff;
|
|
border: 2px solid #ffffff;
|
|
border-radius: 10px;
|
|
padding: 15px 25px;
|
|
margin: 1px auto 40px; /* 修改这里:使用 auto 左右外边距来居中 */
|
|
cursor: pointer;
|
|
font-size: 1em;
|
|
text-transform: uppercase;
|
|
letter-spacing: 1.5px;
|
|
/*font-weight: bold;*/
|
|
transition: background-color 0.3s ease, transform 0.2s ease, box-shadow 0.3s ease;
|
|
max-width: 900px;
|
|
width: calc(100% - 40px); /* 修改这里:减去左右各20px的内边距 */
|
|
box-sizing: border-box;
|
|
text-align: center;
|
|
display: block; /* 添加这行:确保按钮是块级元素 */
|
|
}
|
|
|
|
#download-audio-btn:hover {
|
|
background-color: #333333;
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 15px rgba(255, 255, 255, 0.2);
|
|
}
|
|
|
|
/* 添加媒体查询以确保在小屏幕上的响式布局 */
|
|
@media (max-width: 768px) {
|
|
#download-audio-btn {
|
|
width: calc(100% - 30px); /* 在小屏幕上减少一些宽度 */
|
|
font-size: 0.9em; /* 在小屏幕上稍微减小字体大小 */
|
|
padding: 12px 20px; /* 在小屏幕上稍微减小内边距 */
|
|
}
|
|
}
|
|
|
|
/* 添加语言选择器样式 */
|
|
.container {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 20px 0;
|
|
width: 100%;
|
|
max-width: 1000px;
|
|
box-sizing: border-box;
|
|
margin: 0 auto;
|
|
}
|
|
.right-section {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
#back-to-square {
|
|
padding: 10px 20px;
|
|
background-color: #1a1a1a;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 25px;
|
|
cursor: pointer;
|
|
font-size: 16px;
|
|
transition: background-color 0.3s ease, transform 0.2s ease;
|
|
text-transform: uppercase;
|
|
letter-spacing: 1px;
|
|
width: auto; /* Allow the width to adjust to content */
|
|
min-width: 40px; /* Set a minimum width */
|
|
}
|
|
|
|
#back-to-square:hover {
|
|
background-color: #333;
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
#language-selector {
|
|
margin: 10px;
|
|
}
|
|
|
|
#language-select {
|
|
padding: 10px 20px;
|
|
font-size: 16px;
|
|
border: none;
|
|
border-radius: 25px;
|
|
background-color: #1a1a1a;
|
|
color: white;
|
|
cursor: pointer;
|
|
transition: background-color 0.3s ease;
|
|
width: auto; /* Allow the width to adjust to content */
|
|
min-width: 40px; /* Set a minimum width */
|
|
}
|
|
|
|
#language-select:hover {
|
|
background-color: #333;
|
|
}
|
|
|
|
#language-select option {
|
|
background-color: white;
|
|
color: black;
|
|
padding: 10px;
|
|
}
|
|
|
|
#language-select:focus {
|
|
outline: none;
|
|
box-shadow: 0 0 0 2px rgba(255,255,255,0.5);
|
|
}
|
|
.avatar {
|
|
width: 60px;
|
|
height: 60px;
|
|
border-radius: 50%;
|
|
margin-right: 20px;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.message-content {
|
|
flex: 1;
|
|
}
|
|
|
|
.user-info {
|
|
display: flex;
|
|
align-items: center;
|
|
position: relative;
|
|
cursor: pointer;
|
|
margin-left: auto; /* 将用户信息推到右侧 */
|
|
}
|
|
|
|
.user-avatar {
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: 50%;
|
|
background-color: #1a1a1a;
|
|
color: white;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
font-weight: bold;
|
|
font-size: 20px;
|
|
margin-right: 10px;
|
|
}
|
|
|
|
.user-dropdown {
|
|
display: none;
|
|
position: absolute;
|
|
top: 100%;
|
|
right: 0;
|
|
background-color: white;
|
|
border: 1px solid #ccc;
|
|
border-radius: 5px;
|
|
padding: 10px;
|
|
z-index: 1000;
|
|
}
|
|
|
|
.user-dropdown.show {
|
|
display: block;
|
|
}
|
|
|
|
.logout-btn {
|
|
background-color: #1a1a1a;
|
|
color: white;
|
|
border: none;
|
|
padding: 8px 10px; /* Increased padding */
|
|
border-radius: 5px;
|
|
cursor: pointer;
|
|
width: 100%; /* Make the button full width within its container */
|
|
min-width: 60px; /* Set a minimum width */
|
|
font-size: 13px; /* Increased font size */
|
|
transition: background-color 0.3s ease; /* Smooth transition for hover effect */
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.container {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 10px;
|
|
width: calc(100% - 30px);
|
|
}
|
|
|
|
#back-to-square {
|
|
font-size: 14px;
|
|
padding: 8px 16px;
|
|
white-space: nowrap;
|
|
min-width: 100px;
|
|
}
|
|
|
|
.right-section {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-left: auto; /* 将右侧部分推到最右边 */
|
|
}
|
|
|
|
#language-select {
|
|
font-size: 14px;
|
|
padding: 8px 16px;
|
|
white-space: nowrap;
|
|
width: 100px;
|
|
min-width: 100px;
|
|
margin-right: 10px;
|
|
}
|
|
|
|
.user-info {
|
|
display: flex;
|
|
align-items: center;
|
|
font-size: 14px;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.user-dropdown {
|
|
right: 0;
|
|
left: auto;
|
|
transform: none;
|
|
}
|
|
.logout-btn {
|
|
background-color: #1a1a1a;
|
|
color: white;
|
|
border: none;
|
|
padding: 8px 8px;
|
|
border-radius: 5px;
|
|
cursor: pointer;
|
|
width: auto; /* 调整为自动宽度 */
|
|
min-width: 60px;
|
|
font-size: 13px;
|
|
transition: background-color 0.3s ease;
|
|
white-space: nowrap;
|
|
}
|
|
}
|
|
|
|
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<button id="back-to-square" data-i18n="backToSquare">返回广场</button>
|
|
<div class="right-section">
|
|
<div id="language-selector">
|
|
<select id="language-select">
|
|
<option value="zh">中文</option>
|
|
<option value="en">English</option>
|
|
<option value="ko">한국어</option>
|
|
</select>
|
|
</div>
|
|
<div class="user-info" id="userInfo">
|
|
<div class="user-avatar" id="userAvatar"></div>
|
|
<span id="username"></span>
|
|
<div class="user-dropdown" id="userDropdown">
|
|
<button class="logout-btn" id="logoutBtn" data-i18n="logout">登出</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<h1 id="discussion-title" data-i18n="historytitle">讨论内容</h1>
|
|
|
|
<div id="content-display"></div>
|
|
<button id="download-audio-btn" onclick="downloadAudio()" data-i18n="downloadAudio">点击下载讨论记录</button>
|
|
|
|
<script>
|
|
const BASE_URL = 'https://dev.obscura.work/user';
|
|
const contentDisplay = document.getElementById('content-display');
|
|
const discussionTitle = document.getElementById('discussion-title');
|
|
|
|
// 从 URL 获取参数
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const sessionId = urlParams.get('session_id');
|
|
const discussionNumber = urlParams.get('discussion_number');
|
|
let currentLanguage = urlParams.get('lang') || localStorage.getItem('preferredLanguage') || 'zh';
|
|
|
|
function updateLanguage(lang) {
|
|
currentLanguage = lang;
|
|
localStorage.setItem('preferredLanguage', lang);
|
|
document.documentElement.lang = lang;
|
|
document.querySelectorAll('[data-i18n]').forEach(element => {
|
|
const key = element.getAttribute('data-i18n');
|
|
if (translations[lang][key]) {
|
|
if (key === 'historytitle' && discussionNumber) {
|
|
element.textContent = translations[lang][key].replace('{0}', discussionNumber);
|
|
} else {
|
|
element.textContent = translations[lang][key];
|
|
}
|
|
}
|
|
});
|
|
|
|
// 更新讨论标题和文档标题
|
|
if (discussionNumber) {
|
|
discussionTitle.textContent = translations[lang].historytitle.replace('{0}', discussionNumber);
|
|
document.title = translations[lang].historytitle.replace('{0}', discussionNumber);
|
|
}
|
|
|
|
// 重新获取并显示讨论内容
|
|
fetchDiscussion();
|
|
}
|
|
|
|
// 获取讨论内容
|
|
async function fetchDiscussion() {
|
|
try {
|
|
const response = await fetch(`${BASE_URL}/api/get-discussion/${sessionId}?language=${currentLanguage}`);
|
|
if (!response.ok) {
|
|
throw new Error('Network response was not ok');
|
|
}
|
|
const data = await response.json();
|
|
console.log('Server response:', data);
|
|
displayContent(data);
|
|
} catch (error) {
|
|
console.error('Error:', error);
|
|
contentDisplay.innerHTML = `<p>${translations[currentLanguage].errorOccurred}</p>`;
|
|
}
|
|
}
|
|
// 显示内容
|
|
function displayContent(data) {
|
|
contentDisplay.innerHTML = '';
|
|
if (data && data.topic && data.content) {
|
|
// 显示主题
|
|
const topicContainer = document.createElement('div');
|
|
topicContainer.className = 'message-container topic-message';
|
|
topicContainer.innerHTML = `
|
|
<div class="message-role">${translations[currentLanguage].discussionTopic} ${data.topic}</div>
|
|
`;
|
|
contentDisplay.appendChild(topicContainer);
|
|
// 显示内容
|
|
data.content.forEach(message => {
|
|
const messageContainer = document.createElement('div');
|
|
messageContainer.className = 'message-container';
|
|
|
|
const avatarImg = document.createElement('img');
|
|
avatarImg.className = 'avatar';
|
|
avatarImg.src = `${BASE_URL}/avatar/${message.avatar}`; // 更新头像路径
|
|
avatarImg.alt = message.post;
|
|
|
|
const messageContent = document.createElement('div');
|
|
messageContent.className = 'message-content';
|
|
messageContent.innerHTML = `
|
|
<div class="message-role">${translations[currentLanguage].roles[message.post] || message.post}</div>
|
|
<div class="message-text">${message.chunk}</div>
|
|
`;
|
|
|
|
messageContainer.appendChild(avatarImg);
|
|
messageContainer.appendChild(messageContent);
|
|
contentDisplay.appendChild(messageContainer);
|
|
|
|
if (message.audio_task_id) {
|
|
handleAudio(message.audio_task_id, messageContent);
|
|
}
|
|
});
|
|
} else {
|
|
contentDisplay.innerHTML = `<p>${translations[currentLanguage].invalidDataFormat}</p>`;
|
|
}
|
|
}
|
|
async function handleAudio(taskId, messageContainer) {
|
|
console.log('Handling audio for task ID:', taskId);
|
|
let status = 'queued';
|
|
while (status === 'queued' || status === 'processing') {
|
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
try {
|
|
const response = await fetch(`${BASE_URL}/tts_result/${taskId}`);
|
|
const data = await response.json();
|
|
status = data.status;
|
|
console.log('Audio status:', status);
|
|
} catch (error) {
|
|
console.error('Error checking audio status:', error);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (status === 'completed') {
|
|
try {
|
|
console.log('Audio completed, fetching audio file');
|
|
const audioResponse = await fetch(`${BASE_URL}/tts_audio/${taskId}`);
|
|
const audioBlob = await audioResponse.blob();
|
|
const audioUrl = URL.createObjectURL(audioBlob);
|
|
|
|
const audioPlayer = document.createElement('audio');
|
|
audioPlayer.controls = true;
|
|
audioPlayer.src = audioUrl;
|
|
audioPlayer.className = 'audio-player';
|
|
|
|
messageContainer.appendChild(audioPlayer);
|
|
console.log('Audio player added to message container');
|
|
} catch (error) {
|
|
console.error('Error fetching or creating audio:', error);
|
|
}
|
|
} else {
|
|
console.error('Audio generation failed');
|
|
}
|
|
}
|
|
// 下载音频函数
|
|
async function downloadAudio() {
|
|
try {
|
|
const response = await fetch(`${BASE_URL}/api/get-combined-audio/${sessionId}?language=${currentLanguage}`);
|
|
|
|
if (response.ok) {
|
|
const blob = await response.blob();
|
|
const url = window.URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.style.display = 'none';
|
|
a.href = url;
|
|
a.download = `${sessionId}_combined.wav`;
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
window.URL.revokeObjectURL(url);
|
|
} else if (response.status === 404) {
|
|
const data = await response.json();
|
|
if (data.detail === "Combined audio not found") {
|
|
alert(translations[currentLanguage].audioGenerating);
|
|
} else {
|
|
alert(translations[currentLanguage].audioNotExist);
|
|
}
|
|
} else {
|
|
throw new Error(translations[currentLanguage].downloadError);
|
|
}
|
|
} catch (error) {
|
|
console.error('下载音频时发生错误:', error);
|
|
alert(translations[currentLanguage].downloadError);
|
|
}
|
|
}
|
|
|
|
document.getElementById('language-select').addEventListener('change', function() {
|
|
updateLanguage(this.value);
|
|
localStorage.setItem('preferredLanguage', this.value);
|
|
});
|
|
|
|
// 在页面加载时初始化语言和获取讨论内容
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
document.getElementById('language-select').value = currentLanguage;
|
|
updateLanguage(currentLanguage);
|
|
fetchDiscussion();
|
|
});
|
|
|
|
document.getElementById('back-to-square').addEventListener('click', function(e) {
|
|
e.preventDefault();
|
|
const lang = document.getElementById('language-select').value;
|
|
window.location.href = `https://beta.obscura.work/space.html?lang=${lang}`;
|
|
});
|
|
|
|
// 检查登录状态的函数
|
|
async function checkLoginStatus() {
|
|
const token = localStorage.getItem('access_token');
|
|
if (!token) {
|
|
window.location.href = '/login.html';
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch(`${BASE_URL}/me`, {
|
|
headers: {
|
|
'Authorization': `Bearer ${token}`
|
|
}
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error('未登录或会话已过期');
|
|
}
|
|
|
|
// 如果成功,用户已登录
|
|
console.log('用户已登录');
|
|
} catch (error) {
|
|
console.error('检查登录状态时出错:', error);
|
|
localStorage.removeItem('access_token');
|
|
window.location.href = '/login.html';
|
|
}
|
|
}
|
|
|
|
// 在页面加载时检查登录状态
|
|
document.addEventListener('DOMContentLoaded', checkLoginStatus);
|
|
|
|
// 添加用户信息和登出功能
|
|
const userInfo = document.getElementById('userInfo');
|
|
const userDropdown = document.getElementById('userDropdown');
|
|
const logoutBtn = document.getElementById('logoutBtn');
|
|
const usernameSpan = document.getElementById('username');
|
|
const userAvatar = document.getElementById('userAvatar');
|
|
|
|
async function fetchUserInfo() {
|
|
try {
|
|
const response = await fetch(`${BASE_URL}/user-info`, {
|
|
headers: {
|
|
'Authorization': `Bearer ${localStorage.getItem('access_token')}`
|
|
}
|
|
});
|
|
if (response.ok) {
|
|
const userData = await response.json();
|
|
usernameSpan.textContent = userData.username;
|
|
userAvatar.textContent = userData.avatar;
|
|
}
|
|
} catch (error) {
|
|
console.error('Error fetching user info:', error);
|
|
}
|
|
}
|
|
|
|
userInfo.addEventListener('click', () => {
|
|
userDropdown.classList.toggle('show');
|
|
});
|
|
|
|
logoutBtn.addEventListener('click', async () => {
|
|
try {
|
|
const response = await fetch(`${BASE_URL}/logout`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Authorization': `Bearer ${localStorage.getItem('access_token')}`
|
|
}
|
|
});
|
|
if (response.ok) {
|
|
localStorage.removeItem('access_token');
|
|
window.location.href = '/login.html';
|
|
}
|
|
} catch (error) {
|
|
console.error('Error logging out:', error);
|
|
}
|
|
});
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
// 现有的初始化代码
|
|
fetchUserInfo();
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|