diff --git a/modules/m-channel/channel-analytics.js b/modules/m-channel/channel-analytics.js
index f1b43148..df5ed7e3 100644
--- a/modules/m-channel/channel-analytics.js
+++ b/modules/m-channel/channel-analytics.js
@@ -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);
diff --git a/modules/m-channel/channel-router.js b/modules/m-channel/channel-router.js
index 3a51bc8c..08962089 100644
--- a/modules/m-channel/channel-router.js
+++ b/modules/m-channel/channel-router.js
@@ -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 = '
';
diff --git a/modules/m-channel/channel-settings-ui.js b/modules/m-channel/channel-settings-ui.js
new file mode 100644
index 00000000..3d07af7d
--- /dev/null
+++ b/modules/m-channel/channel-settings-ui.js
@@ -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 += '
';
+ });
+ 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('✅ 已恢复默认设置');
+ }
+ }
+ };
+})();
diff --git a/modules/m-channel/channel-settings.css b/modules/m-channel/channel-settings.css
new file mode 100644
index 00000000..577df357
--- /dev/null
+++ b/modules/m-channel/channel-settings.css
@@ -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);
+}
diff --git a/modules/m-channel/channel-settings.js b/modules/m-channel/channel-settings.js
new file mode 100644
index 00000000..a7421d4c
--- /dev/null
+++ b/modules/m-channel/channel-settings.js
@@ -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();
+}
diff --git a/modules/m-channel/channel-transition.css b/modules/m-channel/channel-transition.css
index d705abd0..25628e02 100644
--- a/modules/m-channel/channel-transition.css
+++ b/modules/m-channel/channel-transition.css
@@ -46,3 +46,8 @@
.slide-exit-right {
transform: translateX(100%);
}
+
+/* 临时测试动画 */
+.fade-out, .fade-in {
+ transition: opacity 1s ease !important;
+}
diff --git a/modules/m-channel/index.html b/modules/m-channel/index.html
index 59dc02c1..dd5afd81 100644
--- a/modules/m-channel/index.html
+++ b/modules/m-channel/index.html
@@ -6,7 +6,9 @@
光湖频道 · 动态渲染引擎
-
+
+
+
@@ -16,7 +18,8 @@
-
+
+
@@ -38,5 +41,7 @@
+
+