# 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 = `