BC-M-CHANNEL-007-JZ · DEV-010桔子 · 环节9 频道设置中心与用户偏好管理
This commit is contained in:
parent
e4b538fef6
commit
91e4247851
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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">';
|
||||
|
|
|
|||
|
|
@ -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('✅ 已恢复默认设置');
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -46,3 +46,8 @@
|
|||
.slide-exit-right {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
/* 临时测试动画 */
|
||||
.fade-out, .fade-in {
|
||||
transition: opacity 1s ease !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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()">×</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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue