# DataTools Pro 前端模块化设计文档 ## 1. 前端架构概述 ### 1.1 设计理念 DataTools Pro前端采用现代化的模块化架构,基于ES6模块系统构建,追求高内聚、低耦合的设计原则,提供可维护、可扩展的前端解决方案。 ### 1.2 技术栈 ``` 前端技术栈: ├── 核心技术 │ ├── 原生JavaScript (ES6+) │ ├── ES6 Modules (import/export) │ └── 现代浏览器API (Fetch, Promise, async/await) ├── UI框架 │ ├── Bootstrap 5.1.3 (响应式UI框架) │ ├── Font Awesome 6.0.0 (图标库) │ └── 原生CSS3 (自定义样式和动画) └── 开发模式 ├── 模块化开发 ├── 组件化设计 └── 事件驱动架构 ``` ### 1.3 架构优势 - **无构建依赖**: 直接在浏览器中运行,无需复杂的构建工具 - **模块化设计**: 清晰的职责分离,便于维护和测试 - **轻量级**: 没有框架包袱,性能优异 - **现代化**: 使用最新的JavaScript特性和API - **兼容性**: 支持现代浏览器(Chrome 61+, Firefox 60+, Safari 10.1+) ## 2. 模块架构图 ``` DataTools Pro 前端模块架构 ┌─────────────────────────────────────────────────────────────┐ │ 应用层 (App Layer) │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ app-main.js │ │ │ │ 主应用入口和协调器 │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ │ │ │ │ 应用初始化 │ │ 生命周期 │ │ 全局事件管理 │ │ │ │ │ │ 管理 │ │ 管理 │ │ │ │ │ │ │ └─────────────┘ └─────────────┘ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────┘ │ ├─────────────────────────────────────────────────────────────┤ │ 核心模块层 (Core Modules) │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │ │ │ config.js │ │ utils.js │ │ api.js │ │ │ │ 配置管理 │ │ 工具函数库 │ │ HTTP客户端 │ │ │ │ ·APP_INFO │ │ ·escapeHtml │ │ ·CassandraAPI │ │ │ │ ·API端点 │ │ ·formatDate │ │ ·RedisAPI │ │ │ │ ·UI配置 │ │ ·debounce │ │ ·统一错误处理 │ │ │ └─────────────┘ └─────────────┘ └─────────────────────────┘ │ │ ┌─────────────┐ ┌─────────────────────────────────────────┐ │ │ │ ui.js │ │ navigation.js │ │ │ │ UI组件库 │ │ 导航管理模块 │ │ │ │ ·AlertMgr │ │ ·面包屑导航 ·键盘快捷键 │ │ │ │ ·ModalMgr │ │ ·活跃状态管理 ·程序化导航 │ │ │ │ ·Pagination │ │ ·页面标题管理 │ │ │ └─────────────┘ └─────────────────────────────────────────┘ │ ├─────────────────────────────────────────────────────────────┤ │ 页面层 (Page Layer) │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │ │ │ index.html │ │db_compare. │ │ redis_compare.html │ │ │ │ 首页模块 │ │html │ │ Redis比对模块 │ │ │ │ ·工具展示 │ │Cassandra │ │ ·集群配置管理 │ │ │ │ ·统计信息 │ │比对模块 │ │ ·随机采样查询 │ │ │ │ ·导航入口 │ │·查询配置 │ │ ·指定Key查询 │ │ │ └─────────────┘ │·分表查询 │ │ ·数据类型比对 │ │ │ │·多主键查询 │ └─────────────────────────┘ │ │ └─────────────┘ │ └─────────────────────────────────────────────────────────────┘ ``` ## 3. 核心模块详解 ### 3.1 主应用模块 (app-main.js) #### 3.1.1 模块职责 - **应用初始化**: 统一管理各模块启动和配置 - **生命周期管理**: 处理应用启动、运行、销毁周期 - **全局事件管理**: 统一处理全局事件和错误 - **模块协调**: 协调各个功能模块之间的交互 #### 3.1.2 核心类设计 ```javascript /** * 主应用程序类 */ class DataToolsApp { constructor() { this.config = config; // 配置管理 this.utils = utils; // 工具函数 this.api = apiManager; // API管理 this.navigation = navigationManager; // 导航管理 this.ui = { // UI组件集合 alert: alertManager, modal: modalManager, loading: loadingIndicator, confirm: confirmDialog, prompt: promptDialog }; this.isInitialized = false; this.currentTool = null; } // 核心方法 async init() // 应用初始化 checkDependencies() // 依赖检查 initUI() // UI初始化 bindGlobalEvents() // 全局事件绑定 detectCurrentTool() // 当前工具检测 destroy() // 应用销毁 } ``` #### 3.1.3 初始化流程 ```javascript // 应用初始化流程 async function initApplication() { try { console.log('DataTools Pro 正在初始化...'); // 1. 检查浏览器兼容性和依赖 checkDependencies(); // 2. 初始化UI组件 initUI(); // 3. 启用导航和快捷键 navigation.enableKeyboardShortcuts(); // 4. 绑定全局事件 bindGlobalEvents(); // 5. 检测当前工具类型 detectCurrentTool(); console.log('应用程序初始化完成'); // 6. 显示欢迎消息 showWelcomeMessage(); } catch (error) { console.error('应用程序初始化失败:', error); ui.alert.error('应用程序初始化失败,请刷新页面重试'); } } ``` ### 3.2 配置管理模块 (config.js) #### 3.2.1 模块结构 ```javascript // 应用基础信息 export const APP_INFO = { name: 'DataTools Pro', version: '2.0.0', description: '企业级数据处理与比对工具平台' }; // API端点配置 export const API_ENDPOINTS = { // Cassandra相关API CASSANDRA: { DEFAULT_CONFIG: '/api/default-config', QUERY: '/api/query', SHARDING_QUERY: '/api/sharding-query' }, // Redis相关API REDIS: { COMPARE: '/api/redis/compare', CONFIG: '/api/redis/config' }, // 配置管理API CONFIG_GROUPS: { LIST: '/api/config-groups', CREATE: '/api/config-groups', GET: (id) => `/api/config-groups/${id}`, DELETE: (id) => `/api/config-groups/${id}` }, // 历史记录API QUERY_HISTORY: { LIST: '/api/query-history', CREATE: '/api/query-history', GET: (id) => `/api/query-history/${id}`, RESULTS: (id) => `/api/query-history/${id}/results`, DELETE: (id) => `/api/query-history/${id}` }, // 日志管理API QUERY_LOGS: { LIST: '/api/query-logs', HISTORY: (id) => `/api/query-logs/history/${id}`, CLEAR: '/api/query-logs' } }; // UI配置 export const UI_CONFIG = { PAGINATION: { DEFAULT_PAGE_SIZE: 10, MAX_PAGE_SIZE: 100, SHOW_SIZE_CHANGER: true }, ALERT: { AUTO_HIDE_DELAY: 3000, MAX_ALERTS: 5 }, LOADING: { MIN_DISPLAY_TIME: 500, TIMEOUT: 30000 }, ANIMATION: { DURATION: 300, EASING: 'ease-in-out' } }; // 工具配置 export const TOOLS_CONFIG = { CASSANDRA: { name: 'Cassandra数据比对工具', url: '/cassandra-compare', icon: 'fas fa-database', description: '企业级Cassandra数据库比对分析' }, REDIS: { name: 'Redis集群比对工具', url: '/redis-compare', icon: 'fab fa-redis', description: '专业的Redis集群数据比对分析' } }; ``` #### 3.2.2 配置管理类 ```javascript class ConfigManager { constructor() { this.cache = new Map(); this.listeners = new Map(); } // 获取配置值 get(key, defaultValue = null) { const keys = key.split('.'); let value = this; for (const k of keys) { value = value[k]; if (value === undefined) { return defaultValue; } } return value; } // 设置配置值 set(key, value) { this.cache.set(key, value); this.notifyListeners(key, value); } // 监听配置变更 onChange(key, callback) { if (!this.listeners.has(key)) { this.listeners.set(key, new Set()); } this.listeners.get(key).add(callback); } // 通知监听器 notifyListeners(key, value) { if (this.listeners.has(key)) { this.listeners.get(key).forEach(callback => { callback(value, key); }); } } } ``` ### 3.3 工具函数模块 (utils.js) #### 3.3.1 核心工具函数 ```javascript /** * HTML转义,防止XSS攻击 */ export function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } /** * 日期格式化 */ export function formatDate(date, format = 'YYYY-MM-DD HH:mm:ss') { const d = new Date(date); const formatMap = { 'YYYY': d.getFullYear(), 'MM': String(d.getMonth() + 1).padStart(2, '0'), 'DD': String(d.getDate()).padStart(2, '0'), 'HH': String(d.getHours()).padStart(2, '0'), 'mm': String(d.getMinutes()).padStart(2, '0'), 'ss': String(d.getSeconds()).padStart(2, '0') }; return format.replace(/YYYY|MM|DD|HH|mm|ss/g, match => formatMap[match]); } /** * 防抖函数 */ export function debounce(func, wait, immediate = false) { let timeout; return function executedFunction(...args) { const later = () => { timeout = null; if (!immediate) func.apply(this, args); }; const callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) func.apply(this, args); }; } /** * 节流函数 */ export function throttle(func, limit) { let inThrottle; return function(...args) { if (!inThrottle) { func.apply(this, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; } /** * 深度克隆对象 */ export function deepClone(obj) { if (obj === null || typeof obj !== "object") { return obj; } if (obj instanceof Date) { return new Date(obj.getTime()); } if (obj instanceof Array) { return obj.map(item => deepClone(item)); } if (typeof obj === "object") { const clonedObj = {}; for (let key in obj) { if (obj.hasOwnProperty(key)) { clonedObj[key] = deepClone(obj[key]); } } return clonedObj; } } /** * 剪贴板操作 */ export const clipboard = { async copy(text) { try { await navigator.clipboard.writeText(text); return true; } catch (err) { // 降级方案 const textArea = document.createElement('textarea'); textArea.value = text; document.body.appendChild(textArea); textArea.select(); document.execCommand('copy'); document.body.removeChild(textArea); return true; } }, async paste() { try { return await navigator.clipboard.readText(); } catch (err) { console.warn('无法访问剪贴板'); return ''; } } }; /** * 文件操作工具 */ export const fileUtils = { // 下载文件 download(data, filename, type = 'text/plain') { const blob = new Blob([data], { type }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = filename; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); }, // 读取文件 async readFile(file) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = e => resolve(e.target.result); reader.onerror = reject; reader.readAsText(file); }); } }; ``` #### 3.3.2 数据处理工具 ```javascript /** * 数据验证工具 */ export const validator = { // 非空验证 required(value, message = '此字段不能为空') { if (!value || value.toString().trim() === '') { throw new Error(message); } return true; }, // 邮箱验证 email(value, message = '请输入有效的邮箱地址') { const pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!pattern.test(value)) { throw new Error(message); } return true; }, // 数字验证 number(value, min = null, max = null, message = '请输入有效的数字') { const num = Number(value); if (isNaN(num)) { throw new Error(message); } if (min !== null && num < min) { throw new Error(`数值不能小于${min}`); } if (max !== null && num > max) { throw new Error(`数值不能大于${max}`); } return true; }, // JSON验证 json(value, message = '请输入有效的JSON格式') { try { JSON.parse(value); return true; } catch (e) { throw new Error(message); } } }; /** * 字符串处理工具 */ export const stringUtils = { // 首字母大写 capitalize(str) { return str.charAt(0).toUpperCase() + str.slice(1); }, // 驼峰命名转换 camelCase(str) { return str.replace(/-([a-z])/g, (match, letter) => letter.toUpperCase()); }, // 截断字符串 truncate(str, maxLength, suffix = '...') { if (str.length <= maxLength) return str; return str.substring(0, maxLength - suffix.length) + suffix; }, // 格式化文件大小 formatBytes(bytes, decimals = 2) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(decimals)) + ' ' + sizes[i]; } }; ``` ### 3.4 API管理模块 (api.js) #### 3.4.1 HTTP客户端封装 ```javascript /** * HTTP客户端类 */ class HttpClient { constructor(baseURL = '', options = {}) { this.baseURL = baseURL; this.defaultOptions = { headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, credentials: 'same-origin', ...options }; this.interceptors = { request: [], response: [] }; } // 添加请求拦截器 addRequestInterceptor(interceptor) { this.interceptors.request.push(interceptor); } // 添加响应拦截器 addResponseInterceptor(interceptor) { this.interceptors.response.push(interceptor); } // 执行请求 async request(url, options = {}) { const fullUrl = this.baseURL + url; let config = { ...this.defaultOptions, ...options }; // 执行请求拦截器 for (const interceptor of this.interceptors.request) { config = await interceptor(config); } try { let response = await fetch(fullUrl, config); // 执行响应拦截器 for (const interceptor of this.interceptors.response) { response = await interceptor(response); } // 处理HTTP错误状态 if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } // 解析JSON响应 const data = await response.json(); return data; } catch (error) { console.error('HTTP请求失败:', error); throw error; } } // HTTP方法快捷方式 get(url, params = {}) { const queryString = new URLSearchParams(params).toString(); const fullUrl = queryString ? `${url}?${queryString}` : url; return this.request(fullUrl, { method: 'GET' }); } post(url, data = {}) { return this.request(url, { method: 'POST', body: JSON.stringify(data) }); } put(url, data = {}) { return this.request(url, { method: 'PUT', body: JSON.stringify(data) }); } delete(url) { return this.request(url, { method: 'DELETE' }); } } ``` #### 3.4.2 API管理器 ```javascript /** * API管理器类 */ class APIManager { constructor() { this.httpClient = new HttpClient(); this.setupInterceptors(); } // 设置拦截器 setupInterceptors() { // 请求拦截器 - 添加时间戳和请求ID this.httpClient.addRequestInterceptor(async (config) => { config.headers['X-Request-ID'] = this.generateRequestId(); config.headers['X-Timestamp'] = Date.now(); return config; }); // 响应拦截器 - 统一错误处理 this.httpClient.addResponseInterceptor(async (response) => { const data = await response.json(); if (!data.success) { throw new APIError(data.error?.message || '请求失败', data.error?.code); } return data; }); } // 生成请求ID generateRequestId() { return 'req_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); } // Cassandra API get cassandra() { return { getDefaultConfig: () => this.httpClient.get(API_ENDPOINTS.CASSANDRA.DEFAULT_CONFIG), executeQuery: (config) => this.httpClient.post(API_ENDPOINTS.CASSANDRA.QUERY, config), executeShardingQuery: (config) => this.httpClient.post(API_ENDPOINTS.CASSANDRA.SHARDING_QUERY, config) }; } // Redis API get redis() { return { compareData: (config) => this.httpClient.post(API_ENDPOINTS.REDIS.COMPARE, config) }; } // 配置组API get configGroups() { return { list: () => this.httpClient.get(API_ENDPOINTS.CONFIG_GROUPS.LIST), create: (config) => this.httpClient.post(API_ENDPOINTS.CONFIG_GROUPS.CREATE, config), get: (id) => this.httpClient.get(API_ENDPOINTS.CONFIG_GROUPS.GET(id)), delete: (id) => this.httpClient.delete(API_ENDPOINTS.CONFIG_GROUPS.DELETE(id)) }; } // 查询历史API get queryHistory() { return { list: (params = {}) => this.httpClient.get(API_ENDPOINTS.QUERY_HISTORY.LIST, params), create: (history) => this.httpClient.post(API_ENDPOINTS.QUERY_HISTORY.CREATE, history), get: (id) => this.httpClient.get(API_ENDPOINTS.QUERY_HISTORY.GET(id)), getResults: (id) => this.httpClient.get(API_ENDPOINTS.QUERY_HISTORY.RESULTS(id)), delete: (id) => this.httpClient.delete(API_ENDPOINTS.QUERY_HISTORY.DELETE(id)) }; } // 日志API get queryLogs() { return { list: (params = {}) => this.httpClient.get(API_ENDPOINTS.QUERY_LOGS.LIST, params), getHistory: (id) => this.httpClient.get(API_ENDPOINTS.QUERY_LOGS.HISTORY(id)), clear: () => this.httpClient.delete(API_ENDPOINTS.QUERY_LOGS.CLEAR) }; } } /** * API错误类 */ class APIError extends Error { constructor(message, code = 'UNKNOWN_ERROR') { super(message); this.name = 'APIError'; this.code = code; } } ``` ### 3.5 UI组件模块 (ui.js) #### 3.5.1 提示管理器 ```javascript /** * 提示信息管理器 */ class AlertManager { constructor() { this.alerts = []; this.container = null; this.init(); } init() { // 创建提示容器 this.container = document.createElement('div'); this.container.className = 'alert-container'; this.container.style.cssText = ` position: fixed; top: 20px; right: 20px; z-index: 9999; max-width: 400px; `; document.body.appendChild(this.container); } show(type, message, autoHide = UI_CONFIG.ALERT.AUTO_HIDE_DELAY) { const alertId = 'alert_' + Date.now(); const alertElement = this.createAlertElement(alertId, type, message); this.container.appendChild(alertElement); this.alerts.push({ id: alertId, element: alertElement }); // 限制提示数量 if (this.alerts.length > UI_CONFIG.ALERT.MAX_ALERTS) { this.removeOldest(); } // 自动隐藏 if (autoHide > 0) { setTimeout(() => this.hide(alertId), autoHide); } return alertId; } createAlertElement(id, type, message) { const alert = document.createElement('div'); alert.id = id; alert.className = `alert alert-${type} alert-dismissible fade show`; alert.innerHTML = ` ${escapeHtml(message)} `; // 绑定关闭事件 alert.querySelector('.btn-close').addEventListener('click', () => { this.hide(id); }); return alert; } getIcon(type) { const icons = { success: 'check-circle', error: 'exclamation-circle', warning: 'exclamation-triangle', info: 'info-circle' }; return icons[type] || 'info-circle'; } hide(alertId) { const alertIndex = this.alerts.findIndex(alert => alert.id === alertId); if (alertIndex !== -1) { const alert = this.alerts[alertIndex]; alert.element.remove(); this.alerts.splice(alertIndex, 1); } } // 便捷方法 success(message, autoHide) { return this.show('success', message, autoHide); } error(message, autoHide) { return this.show('error', message, autoHide); } warning(message, autoHide) { return this.show('warning', message, autoHide); } info(message, autoHide) { return this.show('info', message, autoHide); } } ``` #### 3.5.2 分页组件 ```javascript /** * 分页组件 */ class PaginationComponent { constructor(container, options = {}) { this.container = typeof container === 'string' ? document.querySelector(container) : container; this.options = { pageSize: UI_CONFIG.PAGINATION.DEFAULT_PAGE_SIZE, showSizeChanger: UI_CONFIG.PAGINATION.SHOW_SIZE_CHANGER, showTotal: true, showQuickJumper: true, ...options }; this.currentPage = 1; this.totalItems = 0; this.totalPages = 0; this.onPageChange = options.onPageChange || (() => {}); this.onSizeChange = options.onSizeChange || (() => {}); } // 更新分页数据 update(totalItems, currentPage = 1) { this.totalItems = totalItems; this.currentPage = currentPage; this.totalPages = Math.ceil(totalItems / this.options.pageSize); this.render(); } // 渲染分页组件 render() { if (!this.container) return; const pagination = this.createPaginationElement(); this.container.innerHTML = ''; this.container.appendChild(pagination); } createPaginationElement() { const nav = document.createElement('nav'); nav.innerHTML = `
${this.renderTotalInfo()}
${this.renderSizeChanger()} ${this.renderQuickJumper()}
`; this.bindEvents(nav); return nav; } renderTotalInfo() { if (!this.options.showTotal) return ''; const start = (this.currentPage - 1) * this.options.pageSize + 1; const end = Math.min(this.currentPage * this.options.pageSize, this.totalItems); return ` 显示 ${start}-${end} 项,共 ${this.totalItems} 项 `; } renderPaginationItems() { let items = []; // 上一页 items.push(`
  • `); // 页码 const pageNumbers = this.generatePageNumbers(); pageNumbers.forEach(page => { if (page === '...') { items.push('
  • ...
  • '); } else { items.push(`
  • ${page}
  • `); } }); // 下一页 items.push(`
  • `); return items.join(''); } generatePageNumbers() { const total = this.totalPages; const current = this.currentPage; const pages = []; if (total <= 7) { for (let i = 1; i <= total; i++) { pages.push(i); } } else { if (current <= 4) { pages.push(1, 2, 3, 4, 5, '...', total); } else if (current >= total - 3) { pages.push(1, '...', total - 4, total - 3, total - 2, total - 1, total); } else { pages.push(1, '...', current - 1, current, current + 1, '...', total); } } return pages; } bindEvents(nav) { // 分页点击事件 nav.addEventListener('click', (e) => { if (e.target.matches('.page-link[data-page]')) { e.preventDefault(); const page = parseInt(e.target.dataset.page); if (page > 0 && page <= this.totalPages) { this.goToPage(page); } } }); // 页面大小变更事件 const sizeSelect = nav.querySelector('.page-size-select'); if (sizeSelect) { sizeSelect.addEventListener('change', (e) => { this.changePageSize(parseInt(e.target.value)); }); } // 快速跳转事件 const jumpInput = nav.querySelector('.page-jump-input'); if (jumpInput) { jumpInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { const page = parseInt(e.target.value); if (page > 0 && page <= this.totalPages) { this.goToPage(page); } } }); } } goToPage(page) { if (page !== this.currentPage) { this.currentPage = page; this.render(); this.onPageChange(page, this.options.pageSize); } } changePageSize(pageSize) { this.options.pageSize = pageSize; this.currentPage = 1; this.totalPages = Math.ceil(this.totalItems / pageSize); this.render(); this.onSizeChange(pageSize, this.currentPage); } } ``` ### 3.6 导航管理模块 (navigation.js) #### 3.6.1 导航管理器 ```javascript /** * 导航管理器类 */ class NavigationManager { constructor() { this.currentPath = window.location.pathname; this.breadcrumbContainer = null; this.shortcuts = new Map(); this.init(); } init() { this.updateActiveNavigation(); this.createBreadcrumb(); this.bindNavigationEvents(); } // 更新活跃导航状态 updateActiveNavigation() { document.querySelectorAll('.nav-link').forEach(link => { link.classList.remove('active'); }); // 根据当前路径设置活跃状态 const currentNavLink = this.findActiveNavLink(); if (currentNavLink) { currentNavLink.classList.add('active'); } } findActiveNavLink() { // 直接匹配 let link = document.querySelector(`a[href="${this.currentPath}"]`); if (link && link.classList.contains('nav-link')) { return link; } // 路由别名匹配 if (this.currentPath.includes('db-compare') || this.currentPath.includes('cassandra-compare')) { return document.querySelector('a[href="/cassandra-compare"]'); } if (this.currentPath.includes('redis-compare')) { return document.querySelector('a[href="/redis-compare"]'); } if (this.currentPath === '/') { return document.querySelector('a[href="/"]'); } return null; } // 创建面包屑导航 createBreadcrumb() { const breadcrumbItems = this.getBreadcrumbItems(); if (breadcrumbItems.length <= 1) return; this.breadcrumbContainer = this.findOrCreateBreadcrumbContainer(); this.renderBreadcrumb(breadcrumbItems); } getBreadcrumbItems() { const items = [ { title: '首页', url: '/', icon: 'fas fa-home' } ]; if (this.currentPath.includes('cassandra-compare') || this.currentPath.includes('db-compare')) { items.push({ title: 'Cassandra数据比对', url: '/cassandra-compare', icon: 'fas fa-database' }); } else if (this.currentPath.includes('redis-compare')) { items.push({ title: 'Redis集群比对', url: '/redis-compare', icon: 'fab fa-redis' }); } return items; } // 启用键盘快捷键 enableKeyboardShortcuts() { // 定义快捷键 this.shortcuts.set('ctrl+h', { action: () => this.navigateTo('/'), description: '返回首页' }); this.shortcuts.set('ctrl+1', { action: () => this.navigateTo('/cassandra-compare'), description: 'Cassandra工具' }); this.shortcuts.set('ctrl+2', { action: () => this.navigateTo('/redis-compare'), description: 'Redis工具' }); // 绑定键盘事件 document.addEventListener('keydown', (e) => { const key = this.getKeyString(e); const shortcut = this.shortcuts.get(key); if (shortcut) { e.preventDefault(); shortcut.action(); } }); // 显示快捷键提示 this.createShortcutsTooltip(); } getKeyString(event) { const keys = []; if (event.ctrlKey || event.metaKey) keys.push('ctrl'); if (event.altKey) keys.push('alt'); if (event.shiftKey) keys.push('shift'); keys.push(event.key.toLowerCase()); return keys.join('+'); } createShortcutsTooltip() { const tooltip = document.createElement('div'); tooltip.className = 'keyboard-shortcuts-tooltip'; tooltip.style.cssText = ` position: fixed; bottom: 100px; right: 30px; z-index: 1000; background: rgba(0, 0, 0, 0.8); color: white; padding: 10px; border-radius: 5px; font-size: 12px; display: none; `; let html = '
    键盘快捷键:
    '; this.shortcuts.forEach((shortcut, key) => { const displayKey = key.replace('ctrl', 'Ctrl').replace('+', ' + '); html += `
    ${displayKey} ${shortcut.description}
    `; }); tooltip.innerHTML = html; document.body.appendChild(tooltip); // 按?键显示/隐藏 document.addEventListener('keydown', (e) => { if (e.key === '?' && !e.ctrlKey && !e.metaKey && !e.altKey) { e.preventDefault(); tooltip.style.display = tooltip.style.display === 'none' ? 'block' : 'none'; } }); } // 程序化导航 navigateTo(url, replace = false) { if (replace) { window.history.replaceState(null, '', url); } else { window.location.href = url; } } // 更新页面标题 updatePageTitle(title) { const toolInfo = this.getCurrentToolInfo(); if (toolInfo) { document.title = `${title} - ${toolInfo.name} - DataTools Pro`; } else { document.title = `${title} - DataTools Pro`; } } } ``` ## 4. 模块间通信 ### 4.1 事件驱动架构 ```javascript /** * 事件总线 - 模块间通信 */ class EventBus { constructor() { this.events = new Map(); } // 监听事件 on(eventName, callback) { if (!this.events.has(eventName)) { this.events.set(eventName, new Set()); } this.events.get(eventName).add(callback); } // 移除监听 off(eventName, callback) { if (this.events.has(eventName)) { this.events.get(eventName).delete(callback); } } // 触发事件 emit(eventName, ...args) { if (this.events.has(eventName)) { this.events.get(eventName).forEach(callback => { try { callback(...args); } catch (error) { console.error(`事件处理错误 [${eventName}]:`, error); } }); } } // 一次性监听 once(eventName, callback) { const onceCallback = (...args) => { callback(...args); this.off(eventName, onceCallback); }; this.on(eventName, onceCallback); } } // 全局事件总线实例 const eventBus = new EventBus(); ``` ### 4.2 模块依赖注入 ```javascript /** * 依赖注入容器 */ class DIContainer { constructor() { this.services = new Map(); this.instances = new Map(); } // 注册服务 register(name, factory, singleton = true) { this.services.set(name, { factory, singleton }); } // 获取服务实例 get(name) { if (!this.services.has(name)) { throw new Error(`Service '${name}' not found`); } const service = this.services.get(name); if (service.singleton) { if (!this.instances.has(name)) { this.instances.set(name, service.factory()); } return this.instances.get(name); } else { return service.factory(); } } // 清除实例缓存 clear(name) { if (name) { this.instances.delete(name); } else { this.instances.clear(); } } } // 全局DI容器 const container = new DIContainer(); // 注册核心服务 container.register('config', () => config); container.register('utils', () => utils); container.register('api', () => apiManager); container.register('navigation', () => navigationManager); container.register('eventBus', () => eventBus); ``` ## 5. 性能优化 ### 5.1 模块懒加载 ```javascript /** * 模块懒加载管理器 */ class LazyLoader { constructor() { this.loadedModules = new Set(); this.loadingPromises = new Map(); } async loadModule(modulePath) { if (this.loadedModules.has(modulePath)) { return Promise.resolve(); } if (this.loadingPromises.has(modulePath)) { return this.loadingPromises.get(modulePath); } const loadPromise = import(modulePath).then(module => { this.loadedModules.add(modulePath); this.loadingPromises.delete(modulePath); return module; }); this.loadingPromises.set(modulePath, loadPromise); return loadPromise; } async loadModules(modulePaths) { return Promise.all(modulePaths.map(path => this.loadModule(path))); } } ``` ### 5.2 缓存策略 ```javascript /** * 缓存管理器 */ class CacheManager { constructor(maxSize = 100) { this.cache = new Map(); this.maxSize = maxSize; this.accessOrder = []; } set(key, value, ttl = 0) { const item = { value, timestamp: Date.now(), ttl }; if (this.cache.has(key)) { this.updateAccessOrder(key); } else { if (this.cache.size >= this.maxSize) { this.evictLRU(); } this.accessOrder.push(key); } this.cache.set(key, item); } get(key) { const item = this.cache.get(key); if (!item) return null; // 检查过期时间 if (item.ttl > 0 && Date.now() - item.timestamp > item.ttl) { this.delete(key); return null; } this.updateAccessOrder(key); return item.value; } delete(key) { this.cache.delete(key); const index = this.accessOrder.indexOf(key); if (index > -1) { this.accessOrder.splice(index, 1); } } updateAccessOrder(key) { const index = this.accessOrder.indexOf(key); if (index > -1) { this.accessOrder.splice(index, 1); } this.accessOrder.push(key); } evictLRU() { const oldestKey = this.accessOrder.shift(); this.cache.delete(oldestKey); } clear() { this.cache.clear(); this.accessOrder = []; } } ``` ## 6. 错误处理和调试 ### 6.1 全局错误处理 ```javascript /** * 全局错误处理器 */ class ErrorHandler { constructor() { this.setupGlobalHandlers(); this.errorReporters = []; } setupGlobalHandlers() { // JavaScript错误 window.addEventListener('error', (e) => { this.handleError(e.error, 'javascript'); }); // Promise拒绝错误 window.addEventListener('unhandledrejection', (e) => { this.handleError(e.reason, 'promise'); e.preventDefault(); }); // 资源加载错误 window.addEventListener('error', (e) => { if (e.target !== window) { this.handleError(new Error(`资源加载失败: ${e.target.src || e.target.href}`), 'resource'); } }, true); } handleError(error, type = 'unknown') { const errorInfo = { message: error.message || error.toString(), stack: error.stack, type, timestamp: new Date().toISOString(), url: window.location.href, userAgent: navigator.userAgent }; console.error('Global Error:', errorInfo); // 通知错误报告器 this.errorReporters.forEach(reporter => { try { reporter(errorInfo); } catch (reportError) { console.error('Error reporter failed:', reportError); } }); // 显示用户友好的错误信息 if (type !== 'resource') { this.showUserError(error); } } showUserError(error) { if (window.DataToolsApp && window.DataToolsApp.ui) { window.DataToolsApp.ui.alert.error( '系统出现错误,请刷新页面后重试' ); } } addErrorReporter(reporter) { this.errorReporters.push(reporter); } } ``` ### 6.2 调试工具 ```javascript /** * 开发调试工具 */ class DebugTool { constructor() { this.isEnabled = this.checkDebugMode(); this.logs = []; this.maxLogs = 1000; if (this.isEnabled) { this.setupDebugConsole(); } } checkDebugMode() { return window.location.search.includes('debug=true') || localStorage.getItem('datatools_debug') === 'true'; } log(message, data = null) { if (!this.isEnabled) return; const logEntry = { timestamp: Date.now(), message, data, stack: new Error().stack }; this.logs.push(logEntry); if (this.logs.length > this.maxLogs) { this.logs.shift(); } console.log(`[DEBUG] ${message}`, data); } setupDebugConsole() { // 添加全局调试命令 window.DataToolsDebug = { getLogs: () => this.logs, clearLogs: () => { this.logs = []; }, exportLogs: () => { const data = JSON.stringify(this.logs, null, 2); fileUtils.download(data, 'datatools-debug.json', 'application/json'); }, getAppState: () => window.DataToolsApp, getModules: () => ({ config, utils, apiManager, navigationManager }) }; console.log('DataTools Pro Debug Mode Enabled'); console.log('Use DataToolsDebug object for debugging'); } } ``` ## 7. 测试策略 ### 7.1 单元测试框架 ```javascript /** * 轻量级测试框架 */ class TestFramework { constructor() { this.tests = []; this.results = { passed: 0, failed: 0, errors: [] }; } describe(description, testFn) { console.group(`📋 ${description}`); testFn(); console.groupEnd(); } it(description, testFn) { try { testFn(); this.results.passed++; console.log(`✅ ${description}`); } catch (error) { this.results.failed++; this.results.errors.push({ description, error }); console.error(`❌ ${description}:`, error); } } expect(actual) { return { toBe: (expected) => { if (actual !== expected) { throw new Error(`Expected ${expected}, got ${actual}`); } }, toEqual: (expected) => { if (JSON.stringify(actual) !== JSON.stringify(expected)) { throw new Error(`Expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`); } }, toBeTruthy: () => { if (!actual) { throw new Error(`Expected truthy value, got ${actual}`); } }, toBeFalsy: () => { if (actual) { throw new Error(`Expected falsy value, got ${actual}`); } } }; } async runTests() { console.log('🚀 Running DataTools Pro Tests...'); // 运行所有测试 this.testUtils(); this.testAPI(); this.testUI(); // 输出结果 console.log(`\n📊 Test Results:`); console.log(`✅ Passed: ${this.results.passed}`); console.log(`❌ Failed: ${this.results.failed}`); if (this.results.errors.length > 0) { console.log('\n❌ Errors:'); this.results.errors.forEach(({ description, error }) => { console.error(` - ${description}: ${error.message}`); }); } } testUtils() { this.describe('Utils Module', () => { this.it('should escape HTML correctly', () => { const result = utils.escapeHtml(''); this.expect(result).toBe('<script>alert("xss")</script>'); }); this.it('should format date correctly', () => { const date = new Date('2024-08-05T10:30:00Z'); const result = utils.formatDate(date, 'YYYY-MM-DD'); this.expect(result).toBe('2024-08-05'); }); this.it('should debounce function calls', (done) => { let callCount = 0; const debouncedFn = utils.debounce(() => callCount++, 100); debouncedFn(); debouncedFn(); debouncedFn(); setTimeout(() => { this.expect(callCount).toBe(1); done(); }, 150); }); }); } } ``` ## 8. 构建和部署 ### 8.1 开发环境 ```javascript /** * 开发环境配置 */ const devConfig = { // 热重载 enableHotReload: true, // 调试模式 debugMode: true, // 本地服务器配置 devServer: { port: 3000, host: 'localhost', open: true }, // 代理配置 proxy: { '/api': { target: 'http://localhost:5000', changeOrigin: true } } }; ``` ### 8.2 生产环境优化 ```javascript /** * 生产环境优化 */ const prodOptimizations = { // 代码压缩 minification: true, // 资源合并 bundling: false, // 保持模块化 // 缓存策略 caching: { staticAssets: '1y', api: '5m' }, // CDN配置 cdn: { bootstrap: 'https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css', fontawesome: 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css' } }; ``` --- **版本**: v2.0 **更新日期**: 2024-08-05 **维护者**: DataTools Pro Team