/** * 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-workbench', icon: '✍️', category: 'ai_creation', apiPrefix: '/api/content' }, 'digital_human_live': { component: 'digital-human-console', 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: '/smart-control-center/shared/components/feedback/toast.js', loaded: false, required: true }, { name: 'wechat-bridge', path: '/smart-control-center/shared/wechat-bridge.js', loaded: false, required: false }, // 以下组件当前不存在于 OBS,暂时注释,待后续补充 // { name: 'loading', path: '/smart-control-center/shared/components/feedback/loading.js', loaded: false }, // { name: 'dialog', path: '/smart-control-center/shared/components/feedback/dialog.js', loaded: false }, // { name: 'data-table', path: '/smart-control-center/shared/components/data/table.js', loaded: false }, // { name: 'stat-card', path: '/smart-control-center/shared/components/display/stat-card.js', loaded: false }, { name: 'ai-chat', path: '/components/chat-client.js', loaded: false } // AI 对话组件 ]; // 主加载器对象 const SmartUILoader = { 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 }; }); } return modules; } catch (error) { console.error('[SmartUI] 获取模块列表失败:', error); // 回退到本地 MODULE_COMPONENT_MAP return MODULE_COMPONENT_MAP; } }, /** * ✅ 核心功能:导航跳转到指定模块 * @param {string} moduleName - 模块名称 * @param {Object} options - 可选参数 */ navigateToModule(moduleName, options = {}) { const moduleInfo = MODULE_COMPONENT_MAP[moduleName]; if (!moduleInfo) { console.error(`[SmartUI] 未找到模块:${moduleName}`); alert(`模块不存在:${moduleName}`); return; } // 1. 如果有 path 字段,直接跳转 if (moduleInfo.path) { const targetUrl = moduleInfo.path; console.log(`[SmartUI] 跳转到模块:${moduleName} → ${targetUrl}`); // ✅ 使用 HTML5 History API 进行 SPA 跳转 if (options.useHistory !== false) { window.history.pushState({ module: moduleName }, '', targetUrl); window.dispatchEvent(new PopStateEvent('popstate')); } else { window.location.href = targetUrl; } return; } // 2. 如果没有 path,动态加载组件 console.log(`[SmartUI] 模块 ${moduleName} 没有配置 path,动态加载组件`); this.loadModuleComponents(moduleName); }, /** * ✅ 全局暴露:让所有页面都能调用导航 */ setupGlobalNavigation() { // 暴露到全局 window 对象 window.navigateToModule = this.navigateToModule.bind(this); // 监听浏览器的后退/前进按钮 window.addEventListener('popstate', (event) => { if (event.state && event.state.module) { console.log(`[SmartUI] 浏览器导航:返回到模块 ${event.state.module}`); this.loadModuleComponents(event.state.module); } }); console.log('[SmartUI] 全局导航已设置'); }, /** * 加载模块特定的 UI 组件 */ async loadModuleComponents(moduleName) { if (this.loadedModules.has(moduleName)) { if (this.config.debug) { console.log(`[SmartUI] 模块 ${moduleName} 已加载,跳过`); } return; } const moduleInfo = MODULE_COMPONENT_MAP[moduleName]; if (!moduleInfo) { console.warn(`[SmartUI] 未找到模块 ${moduleName} 的组件配置`); return; } if (this.config.debug) { console.log(`[SmartUI] 开始加载模块 ${moduleName} 的组件:`, moduleInfo); } // 1. 加载业务组件 await this.loadBusinessComponent(moduleInfo.component); // 2. 加载 API 适配器 await this.loadAPIAdapter(moduleName, moduleInfo.apiPrefix); // 3. 标记为已加载 this.loadedModules.add(moduleName); if (this.config.debug) { console.log(`[SmartUI] 模块 ${moduleName} 加载完成`); } }, /** * 加载业务组件 */ async loadBusinessComponent(componentName) { const componentPath = `${this.config.cdnBaseUrl}/frontend/components/${componentName}.js`; return new Promise((resolve, reject) => { if (this.loadedComponents.has(componentName)) { resolve(); return; } const script = document.createElement('script'); script.src = componentPath; script.async = true; script.onload = () => { this.loadedComponents.add(componentName); if (this.config.debug) { console.log(`[SmartUI] 组件加载成功:${componentName}`); } resolve(); }; script.onerror = () => { console.error(`[SmartUI] 组件加载失败:${componentName}`, componentPath); reject(new Error(`Failed to load component: ${componentName}`)); }; document.head.appendChild(script); }); }, /** * 加载 API 适配器 */ async loadAPIAdapter(moduleName, apiPrefix) { // 动态创建 API 适配器 const adapterName = `${moduleName}APIAdapter`; global[adapterName] = { baseURL: `${this.config.apiBaseUrl}${apiPrefix}`, async get(endpoint, params = {}) { const url = new URL(endpoint, this.baseURL); Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v)); const response = await fetch(url.toString(), { headers: { 'Authorization': `Bearer ${localStorage.getItem('authToken') || ''}` } }); if (!response.ok) throw new Error(`HTTP ${response.status}`); return await response.json(); }, async post(endpoint, data = {}) { const url = new URL(endpoint, this.baseURL); const response = await fetch(url.toString(), { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('authToken') || ''}` }, body: JSON.stringify(data) }); if (!response.ok) throw new Error(`HTTP ${response.status}`); return await response.json(); }, async put(endpoint, data = {}) { const url = new URL(endpoint, this.baseURL); const response = await fetch(url.toString(), { method: 'PUT', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('authToken') || ''}` }, body: JSON.stringify(data) }); if (!response.ok) throw new Error(`HTTP ${response.status}`); return await response.json(); }, async delete(endpoint) { const url = new URL(endpoint, this.baseURL); const response = await fetch(url.toString(), { method: 'DELETE', headers: { 'Authorization': `Bearer ${localStorage.getItem('authToken') || ''}` } }); if (!response.ok) throw new Error(`HTTP ${response.status}`); return await response.json(); } }; if (this.config.debug) { console.log(`[SmartUI] API 适配器已创建:${adapterName}`); } }, /** * 加载共享组件 */ async loadSharedComponents() { const loadPromises = SHARED_COMPONENTS.map(comp => { if (comp.loaded) return Promise.resolve(); const componentPath = `${this.config.cdnBaseUrl}${comp.path}`; return new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = componentPath; script.async = true; script.onload = () => { comp.loaded = true; if (this.config.debug) { console.log(`[SmartUI] 共享组件加载成功:${comp.name}`); } resolve(); }; script.onerror = () => { console.warn(`[SmartUI] 共享组件加载失败:${comp.name}`, componentPath); resolve(); // 不阻止后续加载 }; document.head.appendChild(script); }); }); await Promise.all(loadPromises); }, /** * 注入 AI 对话组件 */ injectAIChat() { // 检查是否已有 chat 容器 let chatContainer = document.getElementById('ai-chat-container'); if (!chatContainer) { // 创建浮动聊天窗口 chatContainer = document.createElement('div'); chatContainer.id = 'ai-chat-container'; chatContainer.innerHTML = `
`; document.body.appendChild(chatContainer); // 初始化 ChatClientComponent setTimeout(() => { if (typeof ChatClientComponent !== 'undefined') { const chat = new ChatClientComponent({ containerId: 'ai-chat-messages', mode: 'assistant', enableSuggestions: true, placeholder: '输入问题...' }); chat.initialize(); // 欢迎消息 chat.appendMessage('ai', '您好!我是 AI 智能助手,有什么可以帮助您的吗?'); } }, 500); } }, /** * 发送 AI 消息 */ async sendAIMessage() { const inputField = document.getElementById('ai-chat-input-field'); const message = inputField.value.trim(); if (!message) return; // 添加用户消息 const messagesContainer = document.getElementById('ai-chat-messages'); const userMsg = document.createElement('div'); userMsg.style.cssText = 'text-align: right; margin-bottom: 15px;'; userMsg.innerHTML = `${message}`; messagesContainer.appendChild(userMsg); inputField.value = ''; // TODO: 调用后端 AI API showToast.info('AI 回复功能开发中...', 3000); }, /** * 手动加载指定模块 */ async loadModule(moduleName) { if (this.config.debug) { console.log(`[SmartUI] 手动加载模块:${moduleName}`); } await this.loadModuleComponents(moduleName); }, /** * 获取当前模块信息 */ getCurrentModuleInfo() { if (!this.currentModule) return null; return MODULE_COMPONENT_MAP[this.currentModule]; }, /** * 列出所有支持的模块 */ listSupportedModules() { return Object.keys(MODULE_COMPONENT_MAP); } }; // 导出到全局 global.SmartUILoader = SmartUILoader; // 自动初始化 (如果配置了) if (global.SmartUIConfig && global.SmartUIConfig.autoInit !== false) { SmartUILoader.initialize(global.SmartUIConfig || {}); } })(typeof window !== 'undefined' ? window : this);