设置项改成设置栏

This commit is contained in:
shuaiplus committed 2025-05-30 23:13:44 +08:00
1 parent 742e2fd66d
commit 3f1093aee3
7 files changed
+220 -118

No files matched your search

+1
View File
@@ -49,3 +49,4 @@ client/build/
# Ignore coverage
coverage/
dist/
新增功能.md
+19 -7
View File
@@ -70,10 +70,9 @@
* {
touch-action: pan-y;
}
.sidebar,
.rightbar {
} .sidebar,
.rightbar,
.settings-sidebar {
position: fixed;
top: 0;
bottom: 0;
@@ -95,6 +94,19 @@
transform: translateX(0);
}
.settings-sidebar {
left: 0;
max-width: 80vw;
width: 80vw !important;
transform: translateX(-100%);
border-right: 1px solid #e0e0e0;
z-index: 1001; /* Higher than normal sidebar */
}
.settings-sidebar.mobile-open {
transform: translateX(0);
}
.rightbar {
right: 0;
max-width: 70vw;
@@ -143,11 +155,11 @@
.mobile-info-btn:active {
background: #e0e0e0;
transform: scale(0.93);
}
.settings-panel-card {
} .settings-panel-card {
left: 2vw !important;
}
/* Settings sidebar mobile styles - removed duplicate rules since they are above */
}
.member.private-chat-active {
+74 -46
View File
@@ -52,74 +52,102 @@ body {
display: block;
}
.settings-panel {
display: none;
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
z-index: 9999;
pointer-events: none;
background: none;
}
.settings-panel-card {
/* New settings sidebar styles */
.settings-sidebar {
width: 250px;
background: #fff;
border-radius: 16px;
box-shadow: 0 2px 24px rgba(60, 80, 100, 0.18);
padding: 15px 15px 15px 15px;
min-width: 210px;
position: absolute;
border-right: 1px solid #e0e0e0;
display: flex;
flex-direction: column;
gap: 10px;
pointer-events: auto;
opacity: 0;
transform: translateY(-10px) scale(0.95);
transition: opacity 0.3s ease, transform 0.3s ease;
box-shadow: 2px 0 8px #e0e0e0a0;
transition: width 0.3s ease;
position: relative;
}
.settings-panel-card.open-anim {
opacity: 1;
transform: translateY(0) scale(1);
.settings-header {
display: flex;
align-items: center;
padding: 17.2px 18px 10px 17px;
background: #fff;
border-bottom: 1px solid #dcdcdc;
position: relative;
}
.settings-panel-card.close-anim {
opacity: 0;
transform: translateY(-10px) scale(0.95);
.settings-back-btn {
background: none;
border: none;
cursor: pointer;
padding: 4px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
transition: background-color 0.2s ease;
margin-right: 12px;
}
.settings-back-btn:hover {
background: #f5f5f5;
}
.settings-title {
font-size: 1.3rem;
font-weight: 600;
margin-bottom: 10px;
margin: 0;
color: #30a8f7;
text-align: left;
}
.settings-content {
flex: 1;
padding: 20px;
overflow-y: auto;
background: #fff;
}
.settings-section {
margin-bottom: 24px;
}
.settings-section-title {
font-size: 1.1rem;
font-weight: 600;
margin-bottom: 12px;
color: #222;
border-bottom: 1px solid #f0f0f0;
padding-bottom: 8px;
}
.settings-item {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 1.08rem;
color: #222;
margin-bottom: 8px;
}
.settings-item select {
font-size: 1rem;
border-radius: 8px;
border: 1px solid #e0e0e0;
padding: 4px 10px;
background: #fafbfc;
outline: none;
transition: border-color 0.2s ease, background-color 0.2s ease;
color: #222;
margin-bottom: 12px;
padding: 8px 0;
}
.settings-item select:focus {
border-color: #30a8f7;
background: #fff;
.settings-item-label {
flex: 1;
margin-right: 12px;
}
.settings-item-description {
font-size: 0.9rem;
color: #666;
margin-top: 4px;
}
/* Old settings panel - hide */
.settings-panel {
display: none !important;
}
/* Old settings panel styles - remove */
.settings-panel-card,
.settings-panel-card.open-anim,
.settings-panel-card.close-anim {
display: none !important;
}
.switch {
+15 -2
View File
@@ -15,10 +15,23 @@
<!-- 登录表单内容将由JavaScript动态生成 -->
<!-- Login form content will be dynamically generated by JavaScript -->
</form>
</div>
<div class="container" id="chat-container" style="display:none;">
</div> <div class="container" id="chat-container" style="display:none;">
<div class="mobile-sidebar-mask" id="mobile-sidebar-mask"></div>
<div class="mobile-rightbar-mask" id="mobile-rightbar-mask"></div>
<!-- Settings panel as a sidebar replacement -->
<div class="settings-sidebar" id="settings-sidebar" style="display:none;">
<div class="settings-header">
<button class="settings-back-btn" id="settings-back-btn" title="返回" aria-label="返回">
<svg width="24px" height="24px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15 18L9 12L15 6" stroke="#808080" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
<h2 class="settings-title">设置</h2>
</div>
<div class="settings-content" id="settings-content">
<!-- Settings content will be populated by JavaScript -->
</div>
</div>
<div class="sidebar" id="sidebar">
<div class="sidebar-user"> <span class="avatar" id="sidebar-user-avatar"></span> <span class="sidebar-username" id="sidebar-username">My Name</span> <button class="settings-btn" id="settings-btn" title="设置" aria-label="设置"> <svg width="30px" height="30px" viewBox="0 0 24 24" fill="none"
xmlns="http://www.w3.org/2000/svg">
+9 -11
View File
@@ -112,7 +112,6 @@ window.addEventListener('DOMContentLoaded', () => {
setupImagePreview();
setupEmojiPicker();
initSettings();
const settingsBtn = $id('settings-btn'); // 设置按钮 / Settings button
if (settingsBtn) {
settingsBtn.onclick = (e) => {
@@ -121,17 +120,16 @@ window.addEventListener('DOMContentLoaded', () => {
}
}
// 点击其他地方时关闭设置面板
// Close settings panel when clicking outside
document.addEventListener('click', (ev) => {
const panel = $id('settings-panel');
if (panel && panel.style.display === 'block') {
const card = panel.querySelector('.settings-panel-card');
if (card && !card.contains(ev.target) && ev.target.id !== 'settings-btn') {
closeSettingsPanel();
}
// 设置返回按钮事件处理 / Settings back button event handler
const settingsBackBtn = $id('settings-back-btn');
if (settingsBackBtn) {
settingsBackBtn.onclick = (e) => {
e.stopPropagation();
closeSettingsPanel(); // 关闭设置面板 / Close settings panel
}
});
}
// 点击其他地方时关闭设置面板 (已移除,因为现在使用侧边栏形式)
// Close settings panel when clicking outside (removed since we now use sidebar format)
const input = document.querySelector('.input-message-input'); // 消息输入框 / Message input box
if (input) {
+18 -6
View File
@@ -16,6 +16,9 @@ import {
import {
$id
} from './util.dom.js';
import {
closeSettingsPanel
} from './util.settings.js';
// Render the main header
// 渲染主标题栏
@@ -37,6 +40,7 @@ export function renderMainHeader() {
export function setupMobileUIHandlers() {
const sidebar = document.getElementById('sidebar');
const rightbar = document.getElementById('rightbar');
const settingsSidebar = document.getElementById('settings-sidebar');
const mobileMenuBtn = document.getElementById('mobile-menu-btn');
const mobileInfoBtn = document.getElementById('mobile-info-btn');
const sidebarMask = document.getElementById('mobile-sidebar-mask');
@@ -66,10 +70,14 @@ export function setupMobileUIHandlers() {
e.stopPropagation();
sidebar.classList.add('mobile-open');
sidebarMask.classList.add('active')
};
sidebarMask.onclick = function() {
sidebar.classList.remove('mobile-open');
sidebarMask.classList.remove('active')
}; sidebarMask.onclick = function() {
// Check if settings sidebar is open
if (settingsSidebar && settingsSidebar.classList.contains('mobile-open')) {
closeSettingsPanel();
} else {
sidebar.classList.remove('mobile-open');
sidebarMask.classList.remove('active');
}
}
}
if (mobileInfoBtn && rightbar && rightbarMask) {
@@ -82,8 +90,7 @@ export function setupMobileUIHandlers() {
rightbar.classList.remove('mobile-open');
rightbarMask.classList.remove('active')
}
}
document.addEventListener('click', function(ev) {
} document.addEventListener('click', function(ev) {
if (!isMobile()) return;
if (sidebar && sidebar.classList.contains('mobile-open')) {
if (!sidebar.contains(ev.target) && ev.target !== mobileMenuBtn) {
@@ -91,6 +98,11 @@ export function setupMobileUIHandlers() {
if (sidebarMask) sidebarMask.classList.remove('active')
}
}
if (settingsSidebar && settingsSidebar.classList.contains('mobile-open')) {
if (!settingsSidebar.contains(ev.target)) {
closeSettingsPanel();
}
}
if (rightbar && rightbar.classList.contains('mobile-open')) {
if (!rightbar.contains(ev.target) && ev.target !== mobileInfoBtn) {
rightbar.classList.remove('mobile-open');
+84 -46
View File
@@ -64,19 +64,38 @@ function askNotificationPermission(callback) {
// Setup the settings panel UI
// 设置设置面板 UI
function setupSettingsPanel() {
let panel = $id('settings-panel');
if (!panel) {
panel = createElement('div', {
id: 'settings-panel',
class: 'settings-panel'
}, `<div class="settings-panel-card"><div class="settings-title">Settings</div><div class="settings-item"><span>Desktop Notification</span><label class="switch"><input type="checkbox"id="settings-notify"><span class="slider"></span></label></div><div class="settings-item"><span>Sound Notification</span><label class="switch"><input type="checkbox"id="settings-sound"><span class="slider"></span></label></div></div>`);
document.body.appendChild(panel)
}
const settingsSidebar = $id('settings-sidebar');
const settingsContent = $id('settings-content');
if (!settingsSidebar || !settingsContent) return;
const settings = loadSettings();
const notifyCheckbox = $('#settings-notify', panel);
const soundCheckbox = $('#settings-sound', panel);
if (notifyCheckbox) notifyCheckbox.checked = !!settings.notify;
if (soundCheckbox) soundCheckbox.checked = !!settings.sound;
// Create settings content HTML
settingsContent.innerHTML = `
<div class="settings-section">
<div class="settings-section-title">通知设置</div>
<div class="settings-item">
<div class="settings-item-label">
<div>桌面通知</div>
</div>
<label class="switch">
<input type="checkbox" id="settings-notify" ${settings.notify ? 'checked' : ''}>
<span class="slider"></span>
</label>
</div>
<div class="settings-item">
<div class="settings-item-label">
<div>声音通知</div>
</div>
<label class="switch">
<input type="checkbox" id="settings-sound" ${settings.sound ? 'checked' : ''}>
<span class="slider"></span>
</label>
</div>
</div>
`;
const notifyCheckbox = $('#settings-notify', settingsContent);
const soundCheckbox = $('#settings-sound', settingsContent);
on(notifyCheckbox, 'change', e => {
const checked = e.target.checked;
if (checked) {
@@ -91,13 +110,12 @@ function setupSettingsPanel() {
settings.sound = false;
if (soundCheckbox) soundCheckbox.checked = false;
saveSettings(settings);
applySettings(settings);
// 防止重复通知,添加一个标志位
if (!panel._notificationShown) {
applySettings(settings); // 防止重复通知,添加一个标志位
if (!settingsSidebar._notificationShown) {
new Notification('Notifications enabled', {
body: 'You will receive alerts here.'
});
panel._notificationShown = true; // 设置标志位
settingsSidebar._notificationShown = true; // 设置标志位
}
} else {
settings.notify = false;
@@ -110,10 +128,9 @@ function setupSettingsPanel() {
} else {
settings.notify = false;
saveSettings(settings);
applySettings(settings);
// 重置标志位
if (panel._notificationShown) {
panel._notificationShown = false;
applySettings(settings); // 重置标志位
if (settingsSidebar._notificationShown) {
settingsSidebar._notificationShown = false;
}
}
});
@@ -128,41 +145,62 @@ function setupSettingsPanel() {
});
}
// Check if device is mobile
function isMobile() {
return window.innerWidth <= 768;
}
// Open the settings panel
// 打开设置面板
function openSettingsPanel() {
const settingsSidebar = $id('settings-sidebar');
const sidebar = $id('sidebar');
const sidebarMask = $id('mobile-sidebar-mask');
if (!settingsSidebar || !sidebar) return;
if (isMobile()) {
// Mobile: hide main sidebar and show settings sidebar with mask
sidebar.classList.remove('mobile-open');
settingsSidebar.style.display = 'flex';
settingsSidebar.classList.add('mobile-open');
if (sidebarMask) {
sidebarMask.classList.add('active');
}
} else {
// Desktop: hide main sidebar and show settings sidebar
sidebar.style.display = 'none';
settingsSidebar.style.display = 'flex';
}
// Setup settings content
setupSettingsPanel();
const panel = $id('settings-panel');
const btn = $id('settings-btn');
if (!btn || !panel) return;
if (panel.style.display === 'block') return;
const btnRect = btn.getBoundingClientRect();
panel.style.display = 'block';
const card = $('.settings-panel-card', panel);
if (!card) return;
card.style.position = 'fixed';
card.style.left = btnRect.left + 'px';
card.style.top = (btnRect.bottom + 8) + 'px';
card.style.transform = 'translateX(0)';
removeClass(card, 'close-anim');
// 强制触发重绘,然后添加打开动画
card.offsetHeight; // 强制重绘
addClass(card, 'open-anim');
}
// Close the settings panel
// 关闭设置面板
function closeSettingsPanel() {
const panel = $id('settings-panel');
if (!panel) return;
const card = $('.settings-panel-card', panel);
if (!card) return;
removeClass(card, 'open-anim');
addClass(card, 'close-anim');
setTimeout(() => {
panel.style.display = 'none';
removeClass(card, 'close-anim')
}, 300)
const settingsSidebar = $id('settings-sidebar');
const sidebar = $id('sidebar');
const sidebarMask = $id('mobile-sidebar-mask');
if (!settingsSidebar || !sidebar) return;
if (isMobile()) {
// Mobile: hide settings sidebar and mask, show main sidebar if needed
settingsSidebar.classList.remove('mobile-open');
if (sidebarMask) {
sidebarMask.classList.remove('active');
}
// Don't auto-show main sidebar on mobile - let user click menu button
setTimeout(() => {
settingsSidebar.style.display = 'none';
}, 300); // Wait for animation to complete
} else {
// Desktop: hide settings sidebar and show main sidebar
settingsSidebar.style.display = 'none';
sidebar.style.display = 'flex';
}
}
// Initialize settings on page load