BC-M-CHANNEL-007-JZ · DEV-010桔子 · 环节9 频道设置中心与用户偏好管理

This commit is contained in:
juzi0412 2026-03-15 02:24:17 +08:00
parent e4b538fef6
commit 91e4247851
9 changed files with 826 additions and 293 deletions

View File

@ -1,7 +1,6 @@
/**
* channel-analytics.js
* 频道数据采集核心·环节8
* 频道数据采集核心·环节8 + 环节9设置联动
* 记录模块访问次数停留时间页面加载速度
*/
@ -58,8 +57,13 @@ const ChannelAnalytics = (function() {
// 公开方法
return {
// 记录模块访问
// 记录模块访问环节9检查数据采集开关
recordVisit: function(moduleId) {
// 数据采集开关检查环节9新增
if (typeof ChannelSettings !== 'undefined' && !ChannelSettings.get('analyticsEnabled')) {
console.log('📊 数据采集已关闭(设置中心)');
return;
}
if (!moduleId) return;
const data = loadData();
const mod = ensureModule(data, moduleId);
@ -95,6 +99,13 @@ const ChannelAnalytics = (function() {
// 结束计时(切换模块或离开时调用)
endSession: function() {
if (!currentModule || !enterTime) return;
// 数据采集开关检查环节9新增
if (typeof ChannelSettings !== 'undefined' && !ChannelSettings.get('analyticsEnabled')) {
// 如果关闭采集,清空当前会话但不记录
currentModule = null;
enterTime = null;
return;
}
const duration = Math.round((performance.now() - enterTime) / 1000); // 秒
const data = loadData();
const mod = ensureModule(data, currentModule);
@ -108,6 +119,10 @@ const ChannelAnalytics = (function() {
// 记录加载性能
recordLoadTime: function(moduleId, loadTimeMs) {
if (!moduleId) return;
// 数据采集开关检查环节9新增
if (typeof ChannelSettings !== 'undefined' && !ChannelSettings.get('analyticsEnabled')) {
return;
}
const data = loadData();
const mod = ensureModule(data, moduleId);
mod.loadTimes.push(loadTimeMs);

View File

@ -69,7 +69,7 @@ window.ChannelRouter = {
// 数据采集钩子:开始加载
if (typeof ChannelAnalytics !== 'undefined') {
ChannelAnalytics.markLoadStart();
if (channel !== 'home' && channel !== 'debug' && channel !== 'dashboard') {
if (channel !== 'home' && channel !== 'debug' && channel !== 'dashboard' && channel !== 'settings') {
ChannelAnalytics.recordVisit(channel);
}
}
@ -114,6 +114,27 @@ window.ChannelRouter = {
return;
}
// 设置中心路由环节9新增
if (channel === 'settings') {
fetch('views/channel-settings.html')
.then(r => r.text())
.then(html => {
this.contentEl.innerHTML = html;
if (typeof SettingsUI !== 'undefined') {
SettingsUI.init();
}
// 记录加载完成
if (typeof ChannelAnalytics !== 'undefined') {
ChannelAnalytics.markLoadEnd('settings');
}
})
.catch(err => {
this.contentEl.innerHTML = '<h2>设置面板加载失败</h2>';
console.error(err);
});
return;
}
// 内置频道
if (channel === 'home') {
this.renderModuleCards();
@ -124,7 +145,7 @@ window.ChannelRouter = {
}
// 对于内置频道也记录加载完成
if (typeof ChannelAnalytics !== 'undefined' && channel !== 'dashboard') {
if (typeof ChannelAnalytics !== 'undefined' && channel !== 'dashboard' && channel !== 'settings') {
ChannelAnalytics.markLoadEnd(channel);
}
},

View File

@ -1,4 +1,4 @@
// 频道路由(支持真实模块适配器 + 卡片列表首页
// 频道路由(支持真实模块适配器 + 卡片列表首页 + 数据采集钩子
window.ChannelRouter = {
currentChannel: 'home',
contentEl: null,
@ -24,7 +24,7 @@ window.ChannelRouter = {
});
}
// 【新增】页面关闭时结束计时
// 页面关闭时结束计时
window.addEventListener('beforeunload', function() {
if (typeof ChannelAnalytics !== 'undefined') {
ChannelAnalytics.endSession();
@ -66,36 +66,89 @@ window.ChannelRouter = {
loadChannel: function(channel) {
if (!this.contentEl) return;
// 如果是真实模块m06/m08/m11
if (channel === 'm06' || channel === 'm08' || channel === 'm11') {
// 【新增】数据采集钩子:标记加载开始并记录访问
if (typeof ChannelAnalytics !== 'undefined') {
ChannelAnalytics.markLoadStart();
// 数据采集钩子:开始加载
if (typeof ChannelAnalytics !== 'undefined') {
ChannelAnalytics.markLoadStart();
if (channel !== 'home' && channel !== 'debug' && channel !== 'dashboard') {
ChannelAnalytics.recordVisit(channel);
}
}
// 如果是真实模块m06/m08/m11
if (channel === 'm06' || channel === 'm08' || channel === 'm11') {
if (window.ModuleAdapter) {
ModuleAdapter.loadModule(channel, 'channel-content');
// 模块加载完成后记录加载时间
setTimeout(() => {
if (typeof ChannelAnalytics !== 'undefined') {
ChannelAnalytics.markLoadEnd(channel);
}
}, 100);
} else {
this.contentEl.innerHTML = '<div class="error-boundary">适配器未加载</div>';
}
ModuleLifecycle.onLoad(channel);
// 【新增】数据采集钩子:标记加载完成
if (typeof ChannelAnalytics !== 'undefined') {
ChannelAnalytics.markLoadEnd(channel);
}
return;
.catch(err => {
document.getElementById('module-content').innerHTML = '<h2>设置面板加载失败</h2>';
console.error(err);
});
return;
}
}
// 数据面板路由环节8新增
if (channel === 'dashboard') {
fetch('views/channel-dashboard.html')
.then(response => response.text())
.then(html => {
this.contentEl.innerHTML = html;
// 渲染图表
if (typeof ChannelDashboard !== 'undefined') {
ChannelDashboard.render();
}
ModuleLifecycle.onLoad('dashboard');
// 记录加载完成
if (typeof ChannelAnalytics !== 'undefined') {
ChannelAnalytics.markLoadEnd('dashboard');
}
})
.catch(err => {
this.contentEl.innerHTML = '<h2>数据面板加载失败</h2>';
console.error(err);
});
return;
// 设置中心路由环节9新增
if (channel === 'settings') {
fetch('views/channel-settings.html')
.then(r => r.text())
.then(html => {
document.getElementById('module-content').innerHTML = html;
if (typeof SettingsUI !== 'undefined') {
SettingsUI.init();
}
})
.catch(err => {
document.getElementById('module-content').innerHTML = '<h2>设置面板加载失败</h2>';
console.error(err);
});
return;
}
}
// 内置频道
if (channel === 'home') {
this.renderModuleCards(); // 显示模块卡片列表
this.renderModuleCards();
} else if (channel === 'debug') {
this.loadDebugPanel();
} else {
this.contentEl.innerHTML = '<h2>未知频道</h2>';
}
// 对于内置频道也记录加载完成
if (typeof ChannelAnalytics !== 'undefined' && channel !== 'dashboard') {
ChannelAnalytics.markLoadEnd(channel);
}
},
// 渲染所有模块卡片(用于首页)
@ -104,7 +157,8 @@ window.ChannelRouter = {
{ id: 'm06', name: '工单管理', icon: '📋', desc: '管理任务和工单' },
{ id: 'm08', name: '数据统计', icon: '📊', desc: '查看数据统计和报表' },
{ id: 'm11', name: '组件库', icon: '🧩', desc: '系统组件展示' },
{ id: 'debug', name: '调试面板', icon: '🐞', desc: '查看事件和模块状态' }
{ id: 'debug', name: '调试面板', icon: '🐞', desc: '查看事件和模块状态' },
{ id: 'dashboard', name: '数据面板', icon: '📈', desc: '查看使用统计和性能' }
];
let html = '<div class="module-grid">';

View File

@ -0,0 +1,123 @@
/**
* channel-settings-ui.js
* 设置面板UI控制器·环节9
*/
var SettingsUI = (function() {
// 渲染主题卡片
function renderThemeCards() {
var container = document.getElementById('themeCards');
if (!container) return;
var current = ChannelSettings.get('theme');
var themes = ChannelSettings.THEMES;
var html = '';
Object.keys(themes).forEach(function(id) {
var t = themes[id];
var active = (id === current) ? 'active' : '';
html += '<div class="theme-card ' + active + '" ' +
'style="background:' + t.cardBg + '; color:' + t.text + ';" ' +
'onclick="SettingsUI.selectTheme(\'' + id + '\')">' +
'<div class="theme-preview" style="background:' + t.bg + ';"></div>' +
t.name + '</div>';
});
container.innerHTML = html;
}
// 同步UI状态
function syncUI() {
var s = ChannelSettings.get();
// 下拉框
var fontSize = document.getElementById('settingFontSize');
if (fontSize) fontSize.value = s.fontSize;
var interval = document.getElementById('settingRefreshInterval');
if (interval) interval.value = s.refreshInterval;
// 开关
var toggleMap = {
settingAnimation: 'animationEnabled',
settingSidebar: 'sidebarCollapsed',
settingNotifyModule: 'notifyModuleUpdate',
settingNotifySystem: 'notifySystem',
settingNotifySound: 'notifySound',
settingAnalytics: 'analyticsEnabled',
settingAutoRefresh: 'autoRefresh'
};
Object.keys(toggleMap).forEach(function(elId) {
var el = document.getElementById(elId);
if (el) el.checked = !!s[toggleMap[elId]];
});
renderThemeCards();
}
return {
// 初始化面板
init: function() {
syncUI();
console.log('⚙️ 设置面板已加载');
},
// 选择主题
selectTheme: function(themeId) {
ChannelSettings.set('theme', themeId);
renderThemeCards();
},
// 开关变更
onToggle: function(key, value) {
ChannelSettings.set(key, value);
console.log('⚙️ ' + key + ' = ' + value);
},
// 下拉变更
onChange: function(key, value) {
ChannelSettings.set(key, value);
console.log('⚙️ ' + key + ' = ' + value);
},
// 导出
exportSettings: function() {
var json = ChannelSettings.exportJSON();
if (navigator.clipboard) {
navigator.clipboard.writeText(json).then(function() {
alert('✅ 设置已复制到剪贴板!\n\n可以保存为文件或发给其他设备。');
});
} else {
// 降级:显示在弹窗
prompt('复制下面的内容:', json);
}
console.log('⚙️ 设置已导出');
},
// 显示导入弹窗
showImport: function() {
var modal = document.getElementById('importModal');
if (modal) modal.classList.add('show');
},
// 隐藏导入弹窗
hideImport: function() {
var modal = document.getElementById('importModal');
if (modal) modal.classList.remove('show');
},
// 执行导入
doImport: function() {
var textarea = document.getElementById('importTextarea');
if (!textarea || !textarea.value.trim()) {
alert('请粘贴JSON内容');
return;
}
var ok = ChannelSettings.importJSON(textarea.value.trim());
if (ok) {
alert('✅ 设置导入成功!');
this.hideImport();
syncUI();
} else {
alert('❌ JSON格式错误请检查内容');
}
},
// 恢复默认
resetAll: function() {
if (confirm('确定恢复所有设置为默认值吗?')) {
ChannelSettings.reset();
syncUI();
alert('✅ 已恢复默认设置');
}
}
};
})();

View File

@ -0,0 +1,258 @@
/**
* channel-settings.css
* 频道设置面板样式 · 环节9
*/
.settings-container {
padding: 20px;
max-width: 800px;
margin: 0 auto;
color: var(--text-color, #e0e0e0);
}
.settings-header {
text-align: center;
margin-bottom: 30px;
}
.settings-header h2 {
font-size: 24px;
color: var(--accent-color, #4fc3f7);
margin-bottom: 8px;
}
/* 设置分组 */
.settings-group {
background: var(--card-bg, #1a1a2e);
border-radius: 12px;
padding: 20px;
margin-bottom: 20px;
border: 1px solid var(--border-color, #2a2a4a);
}
.settings-group h3 {
font-size: 16px;
color: var(--accent-color, #4fc3f7);
margin-bottom: 15px;
padding-bottom: 8px;
border-bottom: 1px solid var(--border-color, #2a2a4a);
}
/* 设置行 */
.setting-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 0;
border-bottom: 1px solid rgba(255,255,255,0.05);
}
.setting-row:last-child {
border-bottom: none;
}
.setting-label {
display: flex;
flex-direction: column;
flex: 1;
}
.setting-label .title {
font-size: 14px;
font-weight: 500;
}
.setting-label .desc {
font-size: 12px;
color: #888;
margin-top: 2px;
}
/* 开关组件 */
.toggle-switch {
position: relative;
display: inline-block;
width: 44px;
height: 24px;
margin-left: 16px;
}
.toggle-switch input {
opacity: 0;
width: 0;
height: 0;
}
.toggle-slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #555;
transition: .3s;
border-radius: 24px;
}
.toggle-slider:before {
position: absolute;
content: "";
height: 18px;
width: 18px;
left: 3px;
bottom: 3px;
background: white;
transition: .3s;
border-radius: 50%;
}
input:checked + .toggle-slider {
background: var(--accent-color, #4fc3f7);
}
input:checked + .toggle-slider::before {
transform: translateX(20px);
}
/* 下拉选择 */
.setting-select {
background: var(--card-bg, #1a1a2e);
color: var(--text-color, #e0e0e0);
border: 1px solid var(--border-color, #2a2a4a);
border-radius: 8px;
padding: 6px 12px;
font-size: 14px;
cursor: pointer;
margin-left: 16px;
}
/* 主题预览卡片 */
.theme-cards {
display: flex;
gap: 12px;
flex-wrap: wrap;
margin-top: 10px;
}
.theme-card {
width: 120px;
padding: 12px;
border-radius: 10px;
cursor: pointer;
text-align: center;
font-size: 13px;
border: 2px solid transparent;
transition: border-color 0.3s, transform 0.2s;
}
.theme-card:hover {
transform: translateY(-2px);
}
.theme-card.active {
border-color: var(--accent-color, #4fc3f7);
}
.theme-preview {
height: 40px;
border-radius: 6px;
margin-bottom: 8px;
}
/* 操作按钮组 */
.settings-actions {
display: flex;
gap: 12px;
justify-content: center;
margin-top: 24px;
flex-wrap: wrap;
}
.btn-settings {
padding: 10px 24px;
border-radius: 8px;
font-size: 14px;
cursor: pointer;
border: none;
transition: background 0.3s, transform 0.2s;
}
.btn-settings:hover {
transform: translateY(-1px);
}
.btn-export {
background: #66bb6a;
color: white;
}
.btn-import {
background: #42a5f5;
color: white;
}
.btn-reset {
background: #ff7043;
color: white;
}
/* 导入弹窗 */
.import-modal {
display: none;
position: fixed;
top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.6);
align-items: center;
justify-content: center;
z-index: 1000;
}
.import-modal.show {
display: flex;
}
.import-dialog {
background: var(--card-bg, #1a1a2e);
border-radius: 16px;
padding: 24px;
max-width: 500px;
width: 90%;
border: 1px solid var(--border-color);
}
.import-textarea {
width: 100%;
height: 150px;
background: #111;
color: #e0e0e0;
border: 1px solid #333;
border-radius: 8px;
padding: 10px;
font-family: monospace;
font-size: 13px;
resize: vertical;
margin: 12px 0;
}
/* 响应式 */
@media (max-width: 600px) {
.theme-cards {
justify-content: center;
}
.settings-actions {
flex-direction: column;
align-items: stretch;
}
}
/* 无动画模式 */
.no-animation * {
transition: none !important;
animation: none !important;
}
/* 字体大小全局应用 */
body, .channel-btn, .module-card, .channel-content, .nav-item,
p, span, div, h1, h2, h3, h4, a, button, .setting-row, .setting-label {
font-size: var(--base-font-size, 15px);
}

View File

@ -0,0 +1,180 @@
/**
* channel-settings.js
* 频道设置数据管理核心·环节9
* 统一管理所有用户偏好主题布局通知显示
*/
const ChannelSettings = (function(){
const STORAGE_KEY = 'channel-user-settings';
// 默认设置
const DEFAULTS = {
theme: 'dark', // dark / light / ocean
accentColor: '#4fc3f7', // 主题强调色
layout: 'grid', // grid / list
fontSize: 'medium', // small / medium / large
showWelcome: true, // 显示欢迎横幅
notifyModuleUpdate: true, // 模块更新通知
notifySystem: true, // 系统通知
notifySound: false, // 通知声音
autoRefresh: true, // 自动刷新数据
refreshInterval: 30, // 刷新间隔(秒)
sidebarCollapsed: false,// 侧边栏收起
animationEnabled: true, // 过渡动画
analyticsEnabled: true // 数据采集
};
// 主题预设
const THEMES = {
dark: {
name: '深色模式',
bg: '#0d1117',
cardBg: '#1a1a2e',
text: '#e0e0e0',
accent: '#4fc3f7',
border: '#2a2a4a'
},
light: {
name: '浅色模式',
bg: '#f5f5f5',
cardBg: '#ffffff',
text: '#333333',
accent: '#4fc3f7',
border: '#dddddd'
},
ocean: {
name: '海洋模式',
bg: '#1a2f3f',
cardBg: '#1e3a4a',
text: '#e0f0fa',
accent: '#7bc8e0',
border: '#2d5a6e'
}
};
// 加载设置
function load() {
try {
var stored = localStorage.getItem(STORAGE_KEY);
if (stored) {
var parsed = JSON.parse(stored);
// 合并默认值,保证新字段存在
var merged = {};
Object.keys(DEFAULTS).forEach(function(k) {
merged[k] = parsed.hasOwnProperty(k) ? parsed[k] : DEFAULTS[k];
});
return merged;
}
} catch(e) {
console.warn('读取设置失败,使用默认值', e);
}
return JSON.parse(JSON.stringify(DEFAULTS));
}
function save(settings) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
}
// ── 应用主题到页面 ──
function applyTheme(themeName) {
var theme = THEMES[themeName] || THEMES.dark;
var root = document.documentElement;
root.style.setProperty('--bg-color', theme.bg);
root.style.setProperty('--card-bg', theme.cardBg);
root.style.setProperty('--text-color', theme.text);
root.style.setProperty('--accent-color', theme.accent);
root.style.setProperty('--border-color', theme.border);
document.body.style.background = theme.bg;
document.body.style.color = theme.text;
console.log('⚙️ 主题已切换:' + theme.name);
}
// ── 应用字体大小 ──
function applyFontSize(size) {
var map = { small: '13px', medium: '15px', large: '17px' };
var fontSize = map[size] || '15px';
document.documentElement.style.setProperty('--base-font-size', fontSize);
document.body.style.fontSize = fontSize;
console.log('⚙️ 字体大小已应用:' + size + ' (' + fontSize + ')');
}
// ── 公开方法 ──
return {
DEFAULTS: DEFAULTS,
THEMES: THEMES,
get: function(key) {
var s = load();
return key ? s[key] : s;
},
set: function(key, value) {
var s = load();
s[key] = value;
save(s);
// 即时应用
if (key === 'theme') applyTheme(value);
if (key === 'fontSize') applyFontSize(value);
if (key === 'animationEnabled') {
document.body.classList.toggle('no-animation', !value);
}
},
setMultiple: function(obj) {
var s = load();
Object.keys(obj).forEach(function(k) { s[k] = obj[k]; });
save(s);
if (obj.theme) applyTheme(obj.theme);
if (obj.fontSize) applyFontSize(obj.fontSize);
},
reset: function() {
save(JSON.parse(JSON.stringify(DEFAULTS)));
applyTheme(DEFAULTS.theme);
applyFontSize(DEFAULTS.fontSize);
console.log('⚙️ 所有设置已恢复默认');
},
// 导出为JSON字符串
exportJSON: function() {
var s = load();
return JSON.stringify(s, null, 2);
},
// 从JSON字符串导入
importJSON: function(jsonStr) {
try {
var imported = JSON.parse(jsonStr);
var merged = {};
Object.keys(DEFAULTS).forEach(function(k) {
merged[k] = imported.hasOwnProperty(k) ? imported[k] : DEFAULTS[k];
});
save(merged);
applyTheme(merged.theme);
applyFontSize(merged.fontSize);
console.log('✅ 设置导入成功');
return true;
} catch(e) {
console.error('❌ 导入失败: JSON格式错误');
return false;
}
},
// 初始化(页面加载时调用)
init: function() {
var s = load();
applyTheme(s.theme);
applyFontSize(s.fontSize);
if (!s.animationEnabled) {
document.body.classList.add('no-animation');
}
console.log('⚙️ 频道设置已初始化');
},
getThemeList: function() {
return Object.keys(THEMES).map(function(k) {
return { id: k, name: THEMES[k].name };
});
}
};
})();
// 页面加载时自动初始化
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function() {
ChannelSettings.init();
});
} else {
ChannelSettings.init();
}

View File

@ -46,3 +46,8 @@
.slide-exit-right {
transform: translateX(100%);
}
/* 临时测试动画 */
.fade-out, .fade-in {
transition: opacity 1s ease !important;
}

View File

@ -6,7 +6,9 @@
<title>光湖频道 · 动态渲染引擎</title>
<link rel="stylesheet" href="channel-style.css">
<link rel="stylesheet" href="channel-transition.css">
<link rel="stylesheet" href="channel-dashboard.css"> <!-- 新增:面板样式 -->
<link rel="stylesheet" href="channel-dashboard.css">
<link rel="stylesheet" href="channel-settings.css">
<!-- 新增:面板样式 -->
</head>
<body>
<div class="channel-container">
@ -16,7 +18,8 @@
<button class="channel-btn" data-channel="module-b">📦 模块B</button>
<button class="channel-btn" data-channel="module-c">📦 模块C</button>
<button class="channel-btn" data-channel="dashboard">📊 数据面板</button> <!-- 新增入口 -->
<button class="channel-btn" data-channel="debug">🐞 调试面板</button>
<button class="channel-btn" data-channel="settings">⚙️ 设置</button>
<button class="channel-btn" data-channel="debug">🐞 调试面板</button>
</nav>
<main id="channel-content" class="channel-content">
<!-- 动态内容会加载到这里 -->
@ -38,5 +41,7 @@
<!-- 新增:数据采集与面板逻辑 -->
<script src="channel-analytics.js"></script>
<script src="channel-dashboard.js"></script>
<script src="channel-settings.js"></script>
<script src="channel-settings-ui.js"></script>
</body>
</html>

View File

@ -1,272 +1,144 @@
<!DOCTYPE html>
<html>
<head>
<style>
.settings-panel {
padding: 24px;
background: white;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
max-width: 600px;
margin: 0 auto;
}
.settings-section {
margin-bottom: 32px;
padding-bottom: 24px;
border-bottom: 1px solid #e5e7eb;
}
.settings-section:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
.settings-title {
font-size: 18px;
font-weight: 600;
color: #1f2937;
margin: 0 0 16px 0;
display: flex;
align-items: center;
gap: 8px;
}
.layout-options {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.layout-btn {
padding: 8px 16px;
background: #f3f4f6;
border: 2px solid transparent;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
transition: all 0.2s;
}
.layout-btn:hover {
background: #e5e7eb;
}
.layout-btn.active {
background: #3b82f6;
color: white;
border-color: #2563eb;
}
.theme-options {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.theme-btn {
width: 40px;
height: 40px;
border-radius: 50%;
border: 3px solid transparent;
cursor: pointer;
transition: transform 0.2s;
}
.theme-btn:hover {
transform: scale(1.1);
}
.theme-btn.active {
border-color: #000;
box-shadow: 0 0 0 2px white, 0 0 0 4px currentColor;
}
.theme-default { background: #3b82f6; }
.theme-ocean { background: #0891b2; }
.theme-forest { background: #059669; }
.theme-sunset { background: #d97706; }
.theme-lavender { background: #8b5cf6; }
.reset-btn {
padding: 10px 20px;
background: #ef4444;
color: white;
border: none;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
transition: background 0.2s;
}
.reset-btn:hover {
background: #dc2626;
}
.preview-area {
margin-top: 24px;
padding: 16px;
background: #f9fafb;
border-radius: 8px;
border: 1px dashed #9ca3af;
}
.preview-title {
font-size: 14px;
color: #6b7280;
margin-bottom: 8px;
}
.preview-cards {
display: flex;
gap: 12px;
}
.preview-card {
flex: 1;
padding: 12px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
font-size: 12px;
}
.preview-card .card-title {
font-weight: 600;
margin-bottom: 8px;
}
.close-btn {
position: absolute;
top: 16px;
right: 16px;
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: #9ca3af;
}
.close-btn:hover {
color: #4b5563;
}
</style>
</head>
<body>
<div class="settings-panel">
<button class="close-btn" onclick="parent.ChannelSettings?.closePanel()">&times;</button>
<h2>⚙️ 频道设置</h2>
<!-- 布局切换 -->
<div class="settings-section">
<div class="settings-title">📐 布局模式</div>
<div class="layout-options" id="layout-options">
<button class="layout-btn" data-layout="grid">网格视图</button>
<button class="layout-btn" data-layout="list">列表视图</button>
<button class="layout-btn" data-layout="compact">紧凑视图</button>
</div>
</div>
<!-- 主题色切换 -->
<div class="settings-section">
<div class="settings-title">🎨 主题颜色</div>
<div class="theme-options" id="theme-options">
<button class="theme-btn theme-default" data-theme="default" title="默认蓝"></button>
<button class="theme-btn theme-ocean" data-theme="ocean" title="海洋"></button>
<button class="theme-btn theme-forest" data-theme="forest" title="森林"></button>
<button class="theme-btn theme-sunset" data-theme="sunset" title="日落"></button>
<button class="theme-btn theme-lavender" data-theme="lavender" title="薰衣草"></button>
</div>
</div>
<!-- 预览区域(实时显示效果) -->
<div class="preview-area">
<div class="preview-title">实时预览</div>
<div class="preview-cards">
<div class="preview-card">
<div class="card-title">工单管理</div>
<div>待处理: 3</div>
</div>
<div class="preview-card">
<div class="card-title">数据统计</div>
<div>完成率: 75%</div>
</div>
<div class="preview-card">
<div class="card-title">组件库</div>
<div>24个组件</div>
</div>
</div>
</div>
<!-- 恢复默认 -->
<div class="settings-section">
<button class="reset-btn" id="reset-defaults">恢复默认设置</button>
</div>
<div class="settings-container" id="settingsPanel">
<div class="settings-header">
<h2>频道设置中心</h2>
<p>管理你的频道偏好 · 主题 · 通知 · 显示</p>
</div>
<!-- 外观设置 -->
<div class="settings-group">
<h3>🎨 外观设置</h3>
<div class="setting-row">
<div class="setting-label">
<span class="title">主题模式</span>
<span class="desc">选择你喜欢的界面风格</span>
</div>
</div>
<script>
// 这个脚本在 iframe 内运行,通过 postMessage 与父页面通信
(function() {
// 发送消息到父页面
function sendMessage(type, data) {
window.parent.postMessage({ type, data }, '*');
}
// 请求当前设置
sendMessage('settings:request', {});
// 监听来自父页面的设置更新
window.addEventListener('message', function(event) {
const data = event.data;
if (!data || !data.type) return;
if (data.type === 'settings:update') {
applySettings(data.settings);
} else if (data.type === 'settings:active') {
// 标记当前激活的选项
markActive(data.settings);
}
});
// 应用设置(更新预览区样式)
function applySettings(settings) {
// 这里可以通过 CSS 变量影响预览区颜色
if (settings.theme && window.ChannelTheme) {
// 实际上我们无法直接调用父页面的 ChannelTheme但可以通过 postMessage 通知父页面
sendMessage('theme:change', { theme: settings.theme });
}
// 更新预览区布局样式(通过给预览卡片添加类)
const previewCards = document.querySelector('.preview-cards');
if (previewCards) {
previewCards.className = 'preview-cards';
if (settings.layout) {
previewCards.classList.add(`preview-${settings.layout}`);
}
}
}
// 标记当前激活的按钮
function markActive(settings) {
// 布局按钮
document.querySelectorAll('.layout-btn').forEach(btn => {
btn.classList.remove('active');
if (btn.dataset.layout === settings.layout) {
btn.classList.add('active');
}
});
// 主题按钮
document.querySelectorAll('.theme-btn').forEach(btn => {
btn.classList.remove('active');
if (btn.dataset.theme === settings.theme) {
btn.classList.add('active');
}
});
}
// 绑定点击事件
document.querySelectorAll('.layout-btn').forEach(btn => {
btn.addEventListener('click', function() {
const layout = this.dataset.layout;
sendMessage('layout:change', { layout });
});
});
document.querySelectorAll('.theme-btn').forEach(btn => {
btn.addEventListener('click', function() {
const theme = this.dataset.theme;
sendMessage('theme:change', { theme });
});
});
document.getElementById('reset-defaults').addEventListener('click', function() {
if (confirm('确定要恢复所有默认设置吗?')) {
sendMessage('settings:reset', {});
}
});
})();
</script>
</body>
</html>
<div class="theme-cards" id="themeCards"></div>
<div class="setting-row">
<div class="setting-label">
<span class="title">字体大小</span>
<span class="desc">调整界面文字大小</span>
</div>
<select class="setting-select" id="settingFontSize" onchange="SettingsUI.onChange('fontSize', this.value)">
<option value="small"></option>
<option value="medium"></option>
<option value="large"></option>
</select>
</div>
<div class="setting-row">
<div class="setting-label">
<span class="title">过渡动画</span>
<span class="desc">开启页面切换动画效果</span>
</div>
<label class="toggle-switch">
<input type="checkbox" id="settingAnimation" onchange="SettingsUI.onToggle('animationEnabled', this.checked)">
<span class="toggle-slider"></span>
</label>
</div>
<div class="setting-row">
<div class="setting-label">
<span class="title">侧边栏默认收起</span>
<span class="desc">页面加载时侧边栏是否收起</span>
</div>
<label class="toggle-switch">
<input type="checkbox" id="settingSidebar" onchange="SettingsUI.onToggle('sidebarCollapsed', this.checked)">
<span class="toggle-slider"></span>
</label>
</div>
</div>
<!-- 🔔 通知设置 -->
<div class="settings-group">
<h3>🔔 通知设置</h3>
<div class="setting-row">
<div class="setting-label">
<span class="title">模块更新通知</span>
<span class="desc">有模块内容更新时提醒</span>
</div>
<label class="toggle-switch">
<input type="checkbox" id="settingNotifyModule" onchange="SettingsUI.onToggle('notifyModuleUpdate', this.checked)">
<span class="toggle-slider"></span>
</label>
</div>
<div class="setting-row">
<div class="setting-label">
<span class="title">系统通知</span>
<span class="desc">系统公告和维护提醒</span>
</div>
<label class="toggle-switch">
<input type="checkbox" id="settingNotifySystem" onchange="SettingsUI.onToggle('notifySystem', this.checked)">
<span class="toggle-slider"></span>
</label>
</div>
<div class="setting-row">
<div class="setting-label">
<span class="title">通知声音</span>
<span class="desc">收到通知时播放提示音</span>
</div>
<label class="toggle-switch">
<input type="checkbox" id="settingNotifySound" onchange="SettingsUI.onToggle('notifySound', this.checked)">
<span class="toggle-slider"></span>
</label>
</div>
</div>
<!-- 数据设置 -->
<div class="settings-group">
<h3>📊 数据设置</h3>
<div class="setting-row">
<div class="setting-label">
<span class="title">数据采集</span>
<span class="desc">允许采集模块访问和性能数据(用于数据面板)</span>
</div>
<label class="toggle-switch">
<input type="checkbox" id="settingAnalytics" onchange="SettingsUI.onToggle('analyticsEnabled', this.checked)">
<span class="toggle-slider"></span>
</label>
</div>
<div class="setting-row">
<div class="setting-label">
<span class="title">自动刷新</span>
<span class="desc">定时自动刷新频道数据</span>
</div>
<label class="toggle-switch">
<input type="checkbox" id="settingAutoRefresh" onchange="SettingsUI.onToggle('autoRefresh', this.checked)">
<span class="toggle-slider"></span>
</label>
</div>
<div class="setting-row">
<div class="setting-label">
<span class="title">刷新间隔</span>
<span class="desc">自动刷新的时间间隔</span>
</div>
<select class="setting-select" id="settingRefreshInterval" onchange="SettingsUI.onChange('refreshInterval', parseInt(this.value))">
<option value="15">15秒</option>
<option value="30">30秒</option>
<option value="60">1分钟</option>
<option value="300">5分钟</option>
</select>
</div>
</div>
<!-- 操作按钮 -->
<div class="settings-actions">
<button class="btn-settings btn-export" onclick="SettingsUI.exportSettings()">📤 导出设置</button>
<button class="btn-settings btn-import" onclick="SettingsUI.showImport()">📥 导入设置</button>
<button class="btn-settings btn-reset" onclick="SettingsUI.resetAll()">🔄 恢复默认</button>
</div>
</div>
<!-- 导入弹窗 -->
<div class="import-modal" id="importModal">
<div class="import-dialog">
<h3 style="color:var(--accent-color); margin-bottom:8px;">📥 导入设置</h3>
<p style="font-size:13px; margin-bottom:12px;">粘贴之前导出的JSON内容</p>
<textarea class="import-textarea" id="importTextarea" placeholder='{"theme":"dark",...}'></textarea>
<div style="display:flex; gap:10px; justify-content:flex-end;">
<button class="btn-settings" style="background:#555; color:#fff;" onclick="SettingsUI.hideImport()">取消</button>
<button class="btn-settings btn-import" onclick="SettingsUI.doImport()">确认导入</button>
</div>
</div>
</div>