/** * SmartUI AutoLoader - 智能 UI 组件自动加载系统 * * 核心功能: * 1. 自动识别后端功能模块 * 2. 根据模块类型智能匹配 UI 组件 * 3. 动态加载对应的后端 API 接口 * 4. 自动注入 AI 对话组件 * 5. 支持热插拔式模块扩展 * * 使用方式: * * */ (function(global) { 'use strict'; // 配置选项 - 支持从全局配置读取或自动检测当前域名 const defaultConfig = { apiBaseUrl: '', // ✅ 修改为相对路径(通过同域名 nginx 转发到后端 8001 端口) // ✅ 关键修复:删除硬编码的 cdnBaseUrl,改为 null,在 initialize() 中动态设置 cdnBaseUrl: null, // 将在 initialize() 中根据当前域名自动推断 enableAIChat: true, autoLoadComponents: true, debug: false, timeout: 10000 }; // 模块类型与 UI 组件映射表 const MODULE_COMPONENT_MAP = { // AI 能力类 'content_generation': { component: 'content-generation-workbench', icon: '✍️', category: 'ai_creation', apiPrefix: '/api/content' }, 'digital_human_live': { component: 'digital-human-live-component', icon: '🎭', category: 'ai_creation', apiPrefix: '/api/digital-human' }, 'workflow_engine': { component: 'workflow-editor', icon: '⚙️', category: 'automation', apiPrefix: '/api/workflow' }, 'ai_army': { component: 'ai-army-commander', icon: '🤖', category: 'automation', apiPrefix: '/api/ai-army' }, 'knowledge': { component: 'knowledge-manager', icon: '📚', category: 'data_management', apiPrefix: '/api/knowledge' }, // 商业化类 'tuike': { component: 'tuike-dashboard', icon: '💰', category: 'business', apiPrefix: '/api/tuike' }, 'billing': { component: 'billing-center', icon: '💳', category: 'business', apiPrefix: '/api/billing' }, 'advertising': { component: 'ad-manager', icon: '📢', category: 'business', apiPrefix: '/api/advertising' }, // 系统工具类 'cloud_phone_system': { component: 'cloud-phone-manager', icon: '📱', category: 'system', apiPrefix: '/api/cloud-phone' }, 'desktop_automation': { component: 'desktop-automation', icon: '🖥️', category: 'automation', apiPrefix: '/api/desktop' }, 'smart_home': { component: 'smart-home-controller', icon: '🏠', category: 'iot', apiPrefix: '/api/smart-home' }, 'smart_agriculture': { component: 'agriculture-monitor', icon: '🌾', category: 'iot', apiPrefix: '/api/agriculture' } }; // 通用 UI 组件库 (所有模块共享) const SHARED_COMPONENTS = [ { name: 'toast', path: 'components/toast.js', loaded: false, required: true }, { name: 'loading', path: 'components/loading.js', loaded: false, required: true }, { name: 'dialog', path: 'components/dialog.js', loaded: false, required: true }, { name: 'data-table', path: 'components/table.js', loaded: false, required: true }, { name: 'stat-card', path: 'components/stat-card.js', loaded: false, required: true }, // { name: 'wechat-bridge', path: 'wechat-bridge.js', loaded: false, required: false }, // 暂时禁用,功能未实现 { name: 'ai-chat', path: 'components/chat-client.js', loaded: false } // AI 对话组件 ]; // 主加载器对象 const SmartUILoader = { /** * ✅ 从modules.json动态构建MODULE_COMPONENT_MAP * @param {Array} modules - 从modules.json加载的模块数组 */ buildModuleComponentMap(modules) { const dynamicMap = {}; modules.forEach(mod => { dynamicMap[mod.name] = { component: mod.component, icon: mod.icon || '📦', category: mod.category || 'general', apiPrefix: mod.apiPrefix || `/api/${mod.name}`, path: mod.path || null, // ✅ 从modules.json读取path title: mod.title || mod.name, description: mod.description || '' }; }); // 合并到现有的MODULE_COMPONENT_MAP Object.assign(MODULE_COMPONENT_MAP, dynamicMap); console.log('[SmartUI] ✅ 动态构建MODULE_COMPONENT_MAP完成,共', Object.keys(dynamicMap).length, '个模块'); console.log('[SmartUI] 模块列表:', Object.keys(dynamicMap).join(', ')); }, config: null, loadedModules: new Set(), loadedComponents: new Set(), currentModule: null, /** * 初始化加载器 */ initialize(userConfig = {}) { // ✅ 关键修复:根据当前访问域名自动推断 CDN 路径 const currentHost = window.location.hostname; let autoCdnBaseUrl = null; // 域名与 CDN 路径映射表 const domainCdnMap = { 'console.duoweiying.cn': 'https://console.duoweiying.cn/smart-control-center', 'admin.duoweiying.cn': 'https://admin.duoweiying.cn/smart-control-center', 'h5.duoweiying.cn': 'https://h5.duoweiying.cn/smart-control-center', 'code.duoweiying.cn': 'https://code.duoweiying.cn/smart-control-center', 'console-test.duoweiying.cn': 'https://console-test.duoweiying.cn/smart-control-center', 'code-test.duoweiying.cn': 'https://code-test.duoweiying.cn/smart-control-center' }; // 优先使用用户配置,其次使用自动检测,最后使用默认值 if (userConfig.cdnBaseUrl) { autoCdnBaseUrl = userConfig.cdnBaseUrl; console.log('[SmartUI] 使用用户配置的 CDN 路径:', autoCdnBaseUrl); } else if (domainCdnMap[currentHost]) { autoCdnBaseUrl = domainCdnMap[currentHost]; console.log('[SmartUI] 根据域名自动设置 CDN 路径:', autoCdnBaseUrl); } else { // 回退到默认配置或测试域名 autoCdnBaseUrl = defaultConfig.cdnBaseUrl || 'https://console-test.duoweiying.cn/smart-control-center'; console.warn('[SmartUI] 未识别的域名,使用默认 CDN 路径:', autoCdnBaseUrl); } this.config = { ...defaultConfig, cdnBaseUrl: autoCdnBaseUrl, ...userConfig }; // ✅ 新增:保存模块过滤配置 this.enabledModules = userConfig.enabledModules || []; this.disabledModules = userConfig.disabledModules || []; if (this.config.debug) { console.log('[SmartUI] 初始化配置:', this.config); console.log('[SmartUI] 启用的模块:', this.enabledModules); console.log('[SmartUI] 禁用的模块:', this.disabledModules); } // 加载共享组件 this.loadSharedComponents(); // 监听 DOM 就绪 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => this.onDOMReady()); } else { this.onDOMReady(); } return this; }, /** * DOM 就绪后的处理 */ async onDOMReady() { if (this.config.debug) { console.log('[SmartUI] DOM 已就绪,开始自动加载...'); } // 1. 检测当前页面所在的模块 await this.detectCurrentModule(); // 2. 如果启用了自动加载,加载对应模块的组件 if (this.config.autoLoadComponents && this.currentModule) { await this.loadModuleComponents(this.currentModule); } // 3. ⭐ 新增:注入 AI 对话组件 (在所有页面) if (this.config.enableAIChat) { this.injectAIChat(); console.log('[SmartUI] ✅ AI 对话框已注入到所有页面'); } // 4. 设置全局导航 this.setupGlobalNavigation(); // 5. ⭐ 新增:从后端加载启用的模块列表 (后端控制前端开关) if (this.config.autoLoadComponents) { this.syncEnabledModulesFromBackend().catch(err => { console.warn('[SmartUI] 同步后端模块列表失败:', err); }); } }, /** * ⭐ 新增:从后端同步启用的模块列表 * 后端可通过 MODULE_REGISTRY 控制前端显示/隐藏 */ async syncEnabledModulesFromBackend() { try { const response = await fetch(`${this.config.apiBaseUrl}/api/modules/list`, { method: 'GET', headers: { 'Accept': 'application/json' } }); if (!response.ok) { throw new Error(`HTTP ${response.status}`); } const data = await response.json(); // 更新 modules.json 的内容 if (data.modules && Array.isArray(data.modules)) { console.log('[SmartUI] 从后端同步了', data.modules.length, '个启用的模块'); // 可以在这里动态更新 UI const moduleGrid = document.getElementById('module-grid'); if (moduleGrid) { // 重新渲染模块网格 window.renderModuleGrid && window.renderModuleGrid(data.modules); } } } catch (error) { console.warn('[SmartUI] 后端模块同步不可用,使用本地配置'); } }, /** * 检测当前模块 */ async detectCurrentModule() { // 方法 1: 从 URL 路径识别 const pathParts = window.location.pathname.split('/').filter(Boolean); const moduleName = pathParts[pathParts.length - 1]; if (moduleName && MODULE_COMPONENT_MAP[moduleName]) { this.currentModule = moduleName; if (this.config.debug) { console.log(`[SmartUI] 从 URL 检测到模块:${moduleName}`); } return; } // 方法 2: 从页面 data 属性读取 const moduleData = document.querySelector('[data-module]'); if (moduleData) { const moduleName = moduleData.getAttribute('data-module'); if (MODULE_COMPONENT_MAP[moduleName]) { this.currentModule = moduleName; if (this.config.debug) { console.log(`[SmartUI] 从 data-module 检测到模块:${moduleName}`); } return; } } // 方法 3: 从后端 API 获取模块列表并匹配 try { const modules = await this.fetchModules(); // 尝试匹配当前页面标题或其他特征 const pageTitle = document.title.toLowerCase(); for (const [name, info] of Object.entries(modules)) { if (pageTitle.includes(info.display_name?.toLowerCase())) { this.currentModule = name; if (this.config.debug) { console.log(`[SmartUI] 从标题匹配到模块:${name}`); } break; } } } catch (error) { console.warn('[SmartUI] 无法从 API 获取模块列表:', error); } }, /** * 从后端获取模块列表 */ async fetchModules() { try { // ✅ 关键修复:优先从 modules.json 加载,而不是 API const modulesUrl = `${this.config.cdnBaseUrl}/modules.json`; console.log('[SmartUI] 加载模块配置:', modulesUrl); const response = await fetch(modulesUrl, { cache: 'no-cache' }); if (!response.ok) { throw new Error(`HTTP ${response.status} - ${modulesUrl}`); } const data = await response.json(); console.log('[SmartUI] 模块配置加载成功:', data); // 转换为模块映射表 const modules = {}; if (data.modules && Array.isArray(data.modules)) { // ✅ 新增:根据配置过滤模块 const filteredModules = data.modules.filter(mod => { // 如果指定了启用列表,只显示启用的 if (this.enabledModules.length > 0 && !this.enabledModules.includes(mod.name)) { return false; } // 排除禁用的模块 if (this.disabledModules.includes(mod.name)) { return false; } return true; }); console.log('[SmartUI] 过滤后的模块数量:', filteredModules.length, '/', data.modules.length); filteredModules.forEach(mod => { modules[mod.name] = { name: mod.name, title: mod.title, icon: mod.icon, category: mod.category, description: mod.description, component: mod.component, apiPrefix: mod.apiPrefix, path: mod.path || `/app/${mod.name}`, // ✅ 新增 path 字段 display_name: mod.title }; }); } // ✅ 动态构建MODULE_COMPONENT_MAP this.buildModuleComponentMap(Object.values(modules)); return modules; } catch (error) { console.error('[SmartUI] 获取模块列表失败:', error); // 回退到本地 MODULE_COMPONENT_MAP return MODULE_COMPONENT_MAP; } }, /** * ✅ 核心功能:导航跳转到指定模块 * @param {string} moduleName - 模块名称 * @param {Object} options - 可选参数 */ /** * ✅ 新增:显示场景容器并隐藏模块选择界面 */ showSceneContainer(moduleName, moduleConfig) { const moduleSelectionView = document.getElementById('module-selection-view') || document.getElementById('module-grid'); const sceneContainer = document.getElementById('scene-container'); const sceneTitle = document.getElementById('scene-title'); if (!sceneContainer) { console.error('[SmartUI] ❌ 场景容器不存在!'); return false; } if (moduleSelectionView) { moduleSelectionView.style.display = 'none'; } sceneContainer.style.display = 'block'; if (sceneTitle) { sceneTitle.textContent = moduleConfig.title || moduleConfig.display_name || moduleName; } console.log(`[SmartUI] ✅ 已切换到场景: ${moduleName}`); return true; }, /** * ✅ 新增:返回模块列表 */ backToModules() { const moduleSelectionView = document.getElementById('module-selection-view') || document.getElementById('module-grid'); const sceneContainer = document.getElementById('scene-container'); const sceneContent = document.getElementById('scene-content'); if (moduleSelectionView) { moduleSelectionView.style.display = 'grid'; } if (sceneContainer) { sceneContainer.style.display = 'none'; } if (sceneContent) { sceneContent.innerHTML = '
请选择一个模块开始工作