设置项改成设置栏
This commit is contained in:
1 parent
742e2fd66d
commit
3f1093aee3
7 files changed
+220
-118
No files matched your search
@@ -49,3 +49,4 @@ client/build/
|
||||
# Ignore coverage
|
||||
coverage/
|
||||
dist/
|
||||
新增功能.md
|
||||
+19
-7
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
Reference in new issue
Block a user