// ==================== 辅助函数(格式化) ==================== function formatTimestamp(ms) { if (!ms || ms === 0) return '—'; try { return new Date(parseInt(ms)).toLocaleString('zh-CN', { hour12: false }); } catch(e) { return ms; } } /** * 格式化价格/金额数值 * 规则: * - 绝对值 > 1e6 → 保留2位小数(适用于大额成交额) * - 绝对值 > 1 → 保留4位小数(适用于常规币价) * - 其他情况 → 保留8位小数(适用于小额币种或极小值) */ function formatPrice(v) { if (v === undefined || v === null) return '—'; let n = parseFloat(v); if (isNaN(n)) return v; if (Math.abs(n) > 1e6) return n.toFixed(2); if (Math.abs(n) > 1) return n.toFixed(4); return n.toFixed(8); } function copyToClipboard(text, successMsg) { navigator.clipboard.writeText(text).then(() => { alert(successMsg || '已复制到剪贴板'); }).catch(err => { console.error('复制失败:', err); alert('复制失败,请手动复制'); }); } // ==================== 公共排序函数 ==================== /** * 对币种符号数组进行排序(按预设顺序:BTC, ETH, SOL, LINK, AVAX, BNB, LTC) * @param {Array} symbols 币种符号数组 * @returns {Array} 排序后的数组 */ function sortSymbols(symbols) { const orderHint = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'LINKUSDT', 'AVAXUSDT', 'BNBUSDT', 'LTCUSDT']; return symbols.sort((a, b) => { let ia = orderHint.indexOf(a), ib = orderHint.indexOf(b); if (ia !== -1 && ib !== -1) return ia - ib; if (ia !== -1) return -1; if (ib !== -1) return 1; return a.localeCompare(b); }); } // ==================== 动态指标字段加载 ==================== window.indicatorFields = null; async function loadIndicatorFields() { if (window.indicatorFields) return window.indicatorFields; try { const response = await fetch('06.04_IndicatorFields_JSON.php'); if (!response.ok) throw new Error(`HTTP ${response.status}`); const data = await response.json(); window.indicatorFields = data; return data; } catch (err) { console.error('加载指标字段定义失败:', err); // 降级:返回空数组,避免页面崩溃 window.indicatorFields = { MACRO_ITEMS: [], CURRENT_ITEMS: [] }; return window.indicatorFields; } } // ==================== 参数格式化公共函数 ==================== /** * 将参数键值对格式化为可读字符串 * @param {string} key 参数名 * @param {*} value 参数值 * @returns {string} 格式化后的字符串 */ function formatParamValue(key, value) { // 特殊处理:成交额阈值对象 if (key === 'TURNOVER_THRESHOLDS' && typeof value === 'object') { let str = ''; for (let sym in value) { if (value.hasOwnProperty(sym)) { str += `${sym}=${(value[sym] / 1e6).toFixed(1)}M `; } } return str.trim(); } // 特殊处理:EMA_PERIODS 数组 if (key === 'EMA_PERIODS' && Array.isArray(value)) { return value.join(', '); } // 特殊处理:币种列表数组 if ((key === 'BINANCE_COINS' || key === 'MEXC_COINS') && Array.isArray(value)) { return value.join(', '); } // 数值类型处理(添加单位) if (typeof value === 'number') { let unit = ''; if (key.includes('PCT') || (key.includes('THRESHOLD') && value < 10)) { unit = ' %'; } else if (key.includes('SEC')) { unit = ' 秒'; } else if (key.includes('MULTIPLIER')) { unit = ' 倍'; } return value + unit; } // 其他类型直接返回 return value; } // ==================== 看板渲染通用辅助函数 ==================== /** * 安全格式化数值(保留小数,处理 null/undefined) */ function safeNum(val, digits = 2) { // ✅ 纯 JavaScript 参数 if (val === undefined || val === null) return '0.00'; let n = parseFloat(val); return isNaN(n) ? '0.00' : n.toFixed(digits); } /** * 图标:✅ 或 ❌ */ function icon(ok) { return ok ? '✅' : '❌'; } function formatCooldown(sec) { if (!sec || sec <= 0) return '0分00秒'; let totalSecs = Math.round(sec); let mins = Math.floor(totalSecs / 60); let secs = totalSecs % 60; return mins.toString().padStart(2, '0') + '分' + secs.toString().padStart(2, '0') + '秒'; }