Files
zydi-roundtable/history.html
T
2025-01-12 08:13:40 +00:00

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>