feat: 新增免费演出雷达工具与定投评估功能,优化周报提醒逻辑
1. 新增免费演出雷达工具:含秀动/猫眼/大麦平台抓取、本地代理鉴权配置 2. 新增建行定投评估模块:四大指数PE-TTM估值与定投档位建议 3. 新增推送告警白名单过滤功能,限制推送指定指数告警 4. 重构周报首页提醒逻辑,移至服务端处理并减少前端请求 5. 新增多套调试探针脚本与单元测试用例
This commit is contained in:
@@ -27,6 +27,7 @@
|
||||
<button id="m-tab-wisdom" class="subtab">理论与实践</button>
|
||||
<button id="m-tab-zhizi" class="subtab">知资札记</button>
|
||||
<button id="m-tab-tools" class="subtab">工具箱</button>
|
||||
<button id="m-tab-dip" class="subtab">建行定投评估</button>
|
||||
<div id="m-status" class="status status-inline" style="display:none"></div>
|
||||
</div>
|
||||
|
||||
@@ -189,6 +190,79 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 建行定投评估:场外基金定投/赎回参考,点击按钮按需抓取(无定时任务) -->
|
||||
<section id="m-panel-dip" class="panel" style="display:none">
|
||||
<div class="bar">
|
||||
<button id="dip-btn-run" class="btn">开始评估</button>
|
||||
<span id="dip-status" class="status status-inline" style="display:none"></span>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="data-table" id="dip-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:90px">指数名称</th>
|
||||
<th style="width:70px">代码</th>
|
||||
<th style="width:60px">市场</th>
|
||||
<th style="width:80px">PE-TTM</th>
|
||||
<th style="width:110px">近10年PE分位</th>
|
||||
<th style="width:95px">数据日期</th>
|
||||
<th style="width:130px">数据源</th>
|
||||
<th>定投建议</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="dip-list">
|
||||
<tr><td colspan="8" class="muted" style="text-align:center;padding:30px">请点击「开始评估」获取四大指数最新 PE 估值与定投档位建议</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="dip-errors" style="display:none;margin-top:10px"></div>
|
||||
<div class="dip-thresholds" style="margin-top:14px;padding:12px 14px;border:1px solid #e2e8f0;border-radius:10px;background:#f8fafc;font-size:13px;line-height:1.9;color:#334155">
|
||||
<div style="font-weight:700;color:#111827;margin-bottom:4px">我的定投档位阈值(定投/止盈依据)</div>
|
||||
<div><b>沪深300</b>:PE<12(分位<30%)定投200元/日 | 12~14.5 定投150元/日 | 14.5~16 定投100元/日·可止盈50% | PE>16 暂停定投+止盈清仓</div>
|
||||
<div><b>中证500</b>:PE<25 定投200元/日 | 25~32 定投150元/日 | 32~36 定投100元/日·可止盈50% | PE>36 暂停定投+止盈清仓</div>
|
||||
<div><b>纳指100</b>:PE<24 定投200元/日 | 24~30 定投150元/日 | 30~36 定投100元/日·可止盈50% | PE>36 暂停定投+止盈清仓</div>
|
||||
<div><b>标普500</b>:PE<16 定投200元/日 | 16~21 定投150元/日 | 21~25 定投100元/日·可止盈50% | PE>25 暂停定投+止盈清仓</div>
|
||||
<div style="margin-top:6px;color:#64748b;font-size:12px">数据源:A股为中证指数官网(近10年日频自算分位),美股为蛋卷基金(分位窗口自2016年,约10年);标普500备源 multpl.com。点击按钮按需抓取,非实时行情,仅供参考。</div>
|
||||
</div>
|
||||
<!-- 富国中证红利备忘卡片:纯静态提醒,无数据源,实际盈亏以建行APP为准 -->
|
||||
<div style="margin-top:14px;padding:12px 14px;border:1px solid #fde68a;border-radius:10px;background:#fffbeb;font-size:13px;line-height:1.9;color:#334155">
|
||||
<div style="font-weight:700;color:#92400e;margin-bottom:4px">富国中证红利(100033)持仓提醒(备忘 · 无数据源 · 以建行APP实际盈亏为准)</div>
|
||||
<div><b>收益率提醒</b>:降至 <span style="color:#15803d;font-weight:700">-6%</span> → 大跌后分批加仓信号 | 升至 <span style="color:#b91c1c;font-weight:700">+7%</span> → 维持1000元定投</div>
|
||||
<div><b>净值精准提醒</b>(参考锚点净值 <b>3.282</b>):降至 <span style="color:#15803d;font-weight:700">3.05</span>(跌幅约7%)→ 提醒加仓 | 升至 <span style="color:#b91c1c;font-weight:700">3.52</span>(涨幅约7%)→ 维持1000元定投</div>
|
||||
</div>
|
||||
<!-- 投资纪律座右铭:红字黄底圆角标签 -->
|
||||
<div style="margin-top:14px;text-align:center">
|
||||
<span style="display:inline-block;padding:8px 18px;border-radius:999px;background:#fde047;color:#b91c1c;font-weight:700;font-size:14px;letter-spacing:0.5px">现金是未来的期权,不要把弹药消耗在普通波动里。一年真正的大机会没有几次。</span>
|
||||
</div>
|
||||
<!-- 六种盈利投资者自我提醒:黑底绿环编号样式,两栏布局,行高=字体120% -->
|
||||
<div style="margin-top:14px;padding:12px 14px;border-radius:12px;background:#0b0f0a;border:1px solid #24301a;display:grid;grid-template-columns:1fr 1fr;gap:0 22px">
|
||||
<div style="display:flex;align-items:center;gap:10px;padding:8px 4px;border-bottom:1px solid rgba(163,230,53,0.3)">
|
||||
<div style="flex:0 0 32px;width:32px;height:32px;border-radius:50%;border:3px solid #a3e635;background:#131a10;color:#fff;font-weight:800;font-size:14px;display:flex;align-items:center;justify-content:center">1.</div>
|
||||
<div style="color:#d9f99d;font-size:13px;line-height:1.2"><b style="color:#fff">猎人型:</b>一年出手没几次,80%的时间都在等。不开枪则已,<b style="color:#bef264">一开枪就带着利润离场。</b></div>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:10px;padding:8px 4px;border-bottom:1px solid rgba(163,230,53,0.3)">
|
||||
<div style="flex:0 0 32px;width:32px;height:32px;border-radius:50%;border:3px solid #a3e635;background:#131a10;color:#fff;font-weight:800;font-size:14px;display:flex;align-items:center;justify-content:center">2.</div>
|
||||
<div style="color:#d9f99d;font-size:13px;line-height:1.2"><b style="color:#fff">守株型:</b>认准一只好股票,拿三五年。眼里没有分时,<b style="color:#bef264">只有趋势。</b></div>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:10px;padding:8px 4px;border-bottom:1px solid rgba(163,230,53,0.3)">
|
||||
<div style="flex:0 0 32px;width:32px;height:32px;border-radius:50%;border:3px solid #a3e635;background:#131a10;color:#fff;font-weight:800;font-size:14px;display:flex;align-items:center;justify-content:center">3.</div>
|
||||
<div style="color:#d9f99d;font-size:13px;line-height:1.2"><b style="color:#fff">工匠型:</b>反复做同一只票,高抛低吸、不断做T,<b style="color:#bef264">把成本一点点磨到接近零。</b></div>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:10px;padding:8px 4px;border-bottom:1px solid rgba(163,230,53,0.3)">
|
||||
<div style="flex:0 0 32px;width:32px;height:32px;border-radius:50%;border:3px solid #a3e635;background:#131a10;color:#fff;font-weight:800;font-size:14px;display:flex;align-items:center;justify-content:center">4.</div>
|
||||
<div style="color:#d9f99d;font-size:13px;line-height:1.2"><b style="color:#fff">龙头型:</b>只做主线,只追最强,不碰杂毛。<b style="color:#bef264">纪律比胆子更大,复盘比睡觉还认真。</b></div>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:10px;padding:8px 4px">
|
||||
<div style="flex:0 0 32px;width:32px;height:32px;border-radius:50%;border:3px solid #a3e635;background:#131a10;color:#fff;font-weight:800;font-size:14px;display:flex;align-items:center;justify-content:center">5.</div>
|
||||
<div style="color:#d9f99d;font-size:13px;line-height:1.2"><b style="color:#fff">系统型:</b>只信交易系统,不信消息,不猜新闻。<b style="color:#bef264">信号一到,机械执行,毫不犹豫。</b></div>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:10px;padding:8px 4px">
|
||||
<div style="flex:0 0 32px;width:32px;height:32px;border-radius:50%;border:3px solid #a3e635;background:#131a10;color:#fff;font-weight:800;font-size:14px;display:flex;align-items:center;justify-content:center">6.</div>
|
||||
<div style="color:#d9f99d;font-size:13px;line-height:1.2"><b style="color:#fff">清醒型:</b>懂得见好就收,<b style="color:#bef264">从不幻想卖在最高点。</b></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -343,6 +417,7 @@
|
||||
<script src="/common/common.js"></script>
|
||||
<script src="/tools/markets/markets.js?v=3"></script>
|
||||
<script src="/tools/markets/markets_tools.js?v=1"></script>
|
||||
<script src="/tools/markets/markets_dip_eval.js?v=1"></script>
|
||||
<script>
|
||||
(async function(){
|
||||
try {
|
||||
|
||||
@@ -1712,6 +1712,7 @@ const switchPanel = which => {
|
||||
const wisdomTab = el('m-tab-wisdom')
|
||||
const zhiziTab = el('m-tab-zhizi')
|
||||
const toolsTab = el('m-tab-tools')
|
||||
const dipTab = el('m-tab-dip')
|
||||
const rt = el('m-panel-realtime')
|
||||
const ar = el('m-panel-archive')
|
||||
const cal = el('m-panel-calendar')
|
||||
@@ -1719,25 +1720,27 @@ const switchPanel = which => {
|
||||
const news = el('m-panel-news')
|
||||
const wisdom = el('m-panel-wisdom')
|
||||
const tools = el('m-panel-tools')
|
||||
const dip = el('m-panel-dip')
|
||||
const hideDip = () => { if (dipTab) dipTab.classList.remove('active'); if (dip) dip.style.display = 'none' }
|
||||
if (!rtTab || !arTab || !calTab || !wkTab || !rt || !ar || !cal || !wk) return
|
||||
if (which === 'archive') {
|
||||
rtTab.classList.remove('active'); arTab.classList.add('active'); calTab.classList.remove('active'); wkTab.classList.remove('active'); if (newsTab) newsTab.classList.remove('active'); if (wisdomTab) wisdomTab.classList.remove('active'); if (zhiziTab) zhiziTab.classList.remove('active'); if (toolsTab) toolsTab.classList.remove('active')
|
||||
rt.style.display = 'none'; ar.style.display = ''; cal.style.display = 'none'; wk.style.display = 'none'; if (news) news.style.display = 'none'; if (wisdom) wisdom.style.display = 'none'; if (tools) tools.style.display = 'none'
|
||||
rt.style.display = 'none'; ar.style.display = ''; cal.style.display = 'none'; wk.style.display = 'none'; if (news) news.style.display = 'none'; if (wisdom) wisdom.style.display = 'none'; if (tools) tools.style.display = 'none'; hideDip()
|
||||
loadArchive()
|
||||
} else if (which === 'calendar') {
|
||||
rtTab.classList.remove('active'); arTab.classList.remove('active'); calTab.classList.add('active'); wkTab.classList.remove('active'); if (newsTab) newsTab.classList.remove('active'); if (wisdomTab) wisdomTab.classList.remove('active'); if (zhiziTab) zhiziTab.classList.remove('active'); if (toolsTab) toolsTab.classList.remove('active')
|
||||
rt.style.display = 'none'; ar.style.display = 'none'; cal.style.display = ''; wk.style.display = 'none'; if (news) news.style.display = 'none'; if (wisdom) wisdom.style.display = 'none'; if (tools) tools.style.display = 'none'
|
||||
rt.style.display = 'none'; ar.style.display = 'none'; cal.style.display = ''; wk.style.display = 'none'; if (news) news.style.display = 'none'; if (wisdom) wisdom.style.display = 'none'; if (tools) tools.style.display = 'none'; hideDip()
|
||||
if (!calendarState.month || !Array.isArray(calendarState.days) || calendarState.days.length === 0) loadSecurityCalendar(false)
|
||||
} else if (which === 'weekly') {
|
||||
rtTab.classList.remove('active'); arTab.classList.remove('active'); calTab.classList.remove('active'); wkTab.classList.add('active'); if (newsTab) newsTab.classList.remove('active'); if (wisdomTab) wisdomTab.classList.remove('active'); if (zhiziTab) zhiziTab.classList.remove('active'); if (toolsTab) toolsTab.classList.remove('active')
|
||||
rt.style.display = 'none'; ar.style.display = 'none'; cal.style.display = 'none'; wk.style.display = ''; if (news) news.style.display = 'none'; if (wisdom) wisdom.style.display = 'none'; if (tools) tools.style.display = 'none'
|
||||
rt.style.display = 'none'; ar.style.display = 'none'; cal.style.display = 'none'; wk.style.display = ''; if (news) news.style.display = 'none'; if (wisdom) wisdom.style.display = 'none'; if (tools) tools.style.display = 'none'; hideDip()
|
||||
if (!weeklyState.weekStart || !Array.isArray(weeklyState.rows) || weeklyState.rows.length === 0) {
|
||||
setWeeklyIdleState('请选择周后点击“加载”读取历史,或点“刷新并补存”主动更新。', { clearData: false })
|
||||
}
|
||||
} else if (which === 'news') {
|
||||
rtTab.classList.remove('active'); arTab.classList.remove('active'); calTab.classList.remove('active'); wkTab.classList.remove('active'); if (wisdomTab) wisdomTab.classList.remove('active'); if (zhiziTab) zhiziTab.classList.remove('active'); if (toolsTab) toolsTab.classList.remove('active')
|
||||
if (newsTab) newsTab.classList.add('active')
|
||||
rt.style.display = 'none'; ar.style.display = 'none'; cal.style.display = 'none'; wk.style.display = 'none'; if (news) news.style.display = ''; if (wisdom) wisdom.style.display = 'none'; if (tools) tools.style.display = 'none'
|
||||
rt.style.display = 'none'; ar.style.display = 'none'; cal.style.display = 'none'; wk.style.display = 'none'; if (news) news.style.display = ''; if (wisdom) wisdom.style.display = 'none'; if (tools) tools.style.display = 'none'; hideDip()
|
||||
loadGlobalNews(false)
|
||||
} else if (which === 'wisdom') {
|
||||
window.open('https://traesite.umersoft.com:8975/tools/markets/wallstreet_wisdom.html', '_blank')
|
||||
@@ -1755,6 +1758,19 @@ const switchPanel = which => {
|
||||
if (news) news.style.display = 'none'
|
||||
if (wisdom) wisdom.style.display = 'none'
|
||||
if (tools) tools.style.display = ''
|
||||
hideDip()
|
||||
} else if (which === 'dip') {
|
||||
rtTab.classList.remove('active'); arTab.classList.remove('active'); calTab.classList.remove('active'); wkTab.classList.remove('active')
|
||||
if (newsTab) newsTab.classList.remove('active')
|
||||
if (wisdomTab) wisdomTab.classList.remove('active')
|
||||
if (zhiziTab) zhiziTab.classList.remove('active')
|
||||
if (toolsTab) toolsTab.classList.remove('active')
|
||||
if (dipTab) dipTab.classList.add('active')
|
||||
rt.style.display = 'none'; ar.style.display = 'none'; cal.style.display = 'none'; wk.style.display = 'none'
|
||||
if (news) news.style.display = 'none'
|
||||
if (wisdom) wisdom.style.display = 'none'
|
||||
if (tools) tools.style.display = 'none'
|
||||
if (dip) dip.style.display = ''
|
||||
} else {
|
||||
rtTab.classList.add('active'); arTab.classList.remove('active'); calTab.classList.remove('active'); wkTab.classList.remove('active'); if (newsTab) newsTab.classList.remove('active')
|
||||
if (wisdomTab) wisdomTab.classList.remove('active')
|
||||
@@ -1763,6 +1779,7 @@ const switchPanel = which => {
|
||||
rt.style.display = ''; ar.style.display = 'none'; cal.style.display = 'none'; wk.style.display = 'none'; if (news) news.style.display = 'none'
|
||||
if (wisdom) wisdom.style.display = 'none'
|
||||
if (tools) tools.style.display = 'none'
|
||||
hideDip()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1793,6 +1810,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const stWisdom = el('m-tab-wisdom'); if (stWisdom) stWisdom.addEventListener('click', () => switchPanel('wisdom'))
|
||||
const stZhizi = el('m-tab-zhizi'); if (stZhizi) stZhizi.addEventListener('click', () => switchPanel('zhizi'))
|
||||
const stTools = el('m-tab-tools'); if (stTools) stTools.addEventListener('click', () => switchPanel('tools'))
|
||||
const stDip = el('m-tab-dip'); if (stDip) stDip.addEventListener('click', () => switchPanel('dip'))
|
||||
const cbp = el('cal-btn-prev'); if (cbp) cbp.addEventListener('click', () => navigateMonthCalendar(-1))
|
||||
const cbc = el('cal-btn-current'); if (cbc) cbc.addEventListener('click', () => { const i = el('cal-month'); if (i) i.value = todayMonthLocal(); loadSecurityCalendar(false) })
|
||||
const cbn = el('cal-btn-next'); if (cbn) cbn.addEventListener('click', () => navigateMonthCalendar(1))
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* ===== 建行定投评估(前端逻辑) =====
|
||||
* 银行场外基金定投/赎回参考:点击「开始评估」按需抓取四大指数 PE-TTM 与近10年分位,无定时任务。
|
||||
*
|
||||
* 档位阈值与后端 src/server/markets_dip_eval.js 保持一致:
|
||||
* 沪深300:PE<12 定投200元/日 | 12~14.5 定投150元/日 | 14.5~16 定投100元/日·可止盈50% | PE>16 暂停定投+止盈清仓
|
||||
* 中证500:PE<25 定投200元/日 | 25~32 定投150元/日 | 32~36 定投100元/日·可止盈50% | PE>36 暂停定投+止盈清仓
|
||||
* 纳指100:PE<24 定投200元/日 | 24~30 定投150元/日 | 30~36 定投100元/日·可止盈50% | PE>36 暂停定投+止盈清仓
|
||||
* 标普500:PE<16 定投200元/日 | 16~21 定投150元/日 | 21~25 定投100元/日·可止盈50% | PE>25 暂停定投+止盈清仓
|
||||
*
|
||||
* 依赖 markets.js 提供的全局函数:el / runTrackedInteraction / setButtonBusy
|
||||
*/
|
||||
|
||||
// 档位 → 徽章样式(绿=定投加仓、蓝=轻定投、黄=可止盈一半、红=暂停+清仓)
|
||||
const DIP_LEVEL_STYLE = {
|
||||
buy_heavy: { bg: '#dcfce7', color: '#15803d', label: '低估档' },
|
||||
buy: { bg: '#dcfce7', color: '#16a34a', label: '定投档' },
|
||||
buy_light: { bg: '#e0f2fe', color: '#0369a1', label: '轻定投档' },
|
||||
take_half: { bg: '#fef3c7', color: '#b45309', label: '止盈档' },
|
||||
exit: { bg: '#fee2e2', color: '#b91c1c', label: '清仓档' },
|
||||
unknown: { bg: '#f1f5f9', color: '#64748b', label: '未知' }
|
||||
}
|
||||
|
||||
// HTML 转义,防止数据源返回内容注入
|
||||
const dipEscapeHtml = s => String(s == null ? '' : s).replace(/[&<>"']/g, c => ({
|
||||
'&': '&', '<': '<', '>': '>', '"': '"', "'": '''
|
||||
}[c]))
|
||||
|
||||
// 渲染评估结果表格
|
||||
const renderDipReport = data => {
|
||||
const list = el('dip-list')
|
||||
const status = el('dip-status')
|
||||
const errorsBox = el('dip-errors')
|
||||
if (!list) return
|
||||
const rows = Array.isArray(data.rows) ? data.rows : []
|
||||
|
||||
if (!rows.length) {
|
||||
list.innerHTML = '<tr><td colspan="8" class="muted" style="text-align:center;padding:30px">未获取到有效数据,请查看下方异常提示</td></tr>'
|
||||
} else {
|
||||
list.innerHTML = rows.map(r => {
|
||||
const st = DIP_LEVEL_STYLE[r.level] || DIP_LEVEL_STYLE.unknown
|
||||
const pe = Number.isFinite(r.pe_ttm) ? r.pe_ttm.toFixed(2) : '—'
|
||||
const pct = Number.isFinite(r.percentile) ? `${r.percentile.toFixed(1)}%` : '—'
|
||||
const note = r.note ? `<div style="margin-top:6px;font-size:12px;color:#b45309;line-height:1.6">⚠ ${dipEscapeHtml(r.note)}</div>` : ''
|
||||
return `<tr>
|
||||
<td><b>${dipEscapeHtml(r.name)}</b></td>
|
||||
<td>${dipEscapeHtml(r.code)}</td>
|
||||
<td>${dipEscapeHtml(r.market || '—')}</td>
|
||||
<td style="font-weight:700">${pe}</td>
|
||||
<td title="${dipEscapeHtml(r.percentile_note || '')}">${pct}</td>
|
||||
<td>${dipEscapeHtml(r.pe_date || '—')}</td>
|
||||
<td>${dipEscapeHtml(r.source || '—')}</td>
|
||||
<td>
|
||||
<span style="display:inline-block;padding:3px 10px;border-radius:999px;background:${st.bg};color:${st.color};font-weight:700;font-size:13px">${st.label}:${dipEscapeHtml(r.advice || '—')}</span>
|
||||
${note}
|
||||
</td>
|
||||
</tr>`
|
||||
}).join('')
|
||||
}
|
||||
|
||||
if (status) {
|
||||
status.style.display = ''
|
||||
status.textContent = `评估时间 ${data.captured_at || '—'} | 指数 ${rows.length} 个 | 异常 ${(Array.isArray(data.errors) ? data.errors.length : 0)} 条`
|
||||
}
|
||||
|
||||
// 异常提示区:单指数失败不影响其他指数展示
|
||||
if (errorsBox) {
|
||||
const errs = Array.isArray(data.errors) ? data.errors : []
|
||||
if (errs.length) {
|
||||
errorsBox.style.display = ''
|
||||
errorsBox.innerHTML = errs.map(e => `<div style="font-size:13px;color:#b91c1c;line-height:1.9">⚠ ${dipEscapeHtml(e)}</div>`).join('')
|
||||
} else {
|
||||
errorsBox.style.display = 'none'
|
||||
errorsBox.innerHTML = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 点击「开始评估」:走 markets 统一交互详情弹窗(双模式由 runTrackedInteraction 内部处理)
|
||||
const runDipEval = async () => {
|
||||
const btn = el('dip-btn-run')
|
||||
try {
|
||||
const data = await runTrackedInteraction({
|
||||
title: '建行定投评估',
|
||||
action: '抓取四大指数 PE-TTM 与近10年分位并判档',
|
||||
summary: '按需抓取:中证指数官网 / 蛋卷基金(备源 multpl.com)...',
|
||||
button: btn,
|
||||
loadingText: '评估中…',
|
||||
request: { url: '/api/markets/dip_eval', method: 'GET' },
|
||||
successText: d => `评估完成:${(d && Array.isArray(d.rows)) ? d.rows.length : 0} 个指数,${((d && d.errors) || []).length} 条异常`
|
||||
}, async ctx => {
|
||||
return await ctx.fetchJson('/api/markets/dip_eval', {}, { 说明: '按需抓取,无定时任务' })
|
||||
})
|
||||
renderDipReport(data || {})
|
||||
} catch (e) {
|
||||
// 交互弹窗已展示错误详情(异常时窗口停留),这里给表格区一个兜底提示
|
||||
const list = el('dip-list')
|
||||
if (list) {
|
||||
list.innerHTML = `<tr><td colspan="8" style="text-align:center;padding:30px;color:#b91c1c">评估失败:${dipEscapeHtml(String((e && e.message) || e))}(网络波动或数据源不可用,可稍后重试)</td></tr>`
|
||||
}
|
||||
const status = el('dip-status')
|
||||
if (status) status.style.display = 'none'
|
||||
const errorsBox = el('dip-errors')
|
||||
if (errorsBox) { errorsBox.style.display = 'none'; errorsBox.innerHTML = '' }
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const btn = el('dip-btn-run')
|
||||
if (btn) btn.addEventListener('click', runDipEval)
|
||||
})
|
||||
Reference in New Issue
Block a user