{"id":34,"date":"2026-03-25T01:41:13","date_gmt":"2026-03-24T17:41:13","guid":{"rendered":"https:\/\/radar.neofabmoto.com\/?page_id=34"},"modified":"2026-03-25T16:13:55","modified_gmt":"2026-03-25T08:13:55","slug":"live%e5%80%92%e7%88%b7%e6%83%85%e6%8a%a5%e9%9b%b7%e8%be%be","status":"publish","type":"page","link":"https:\/\/radar.neofabmoto.com\/index.php\/live%e5%80%92%e7%88%b7%e6%83%85%e6%8a%a5%e9%9b%b7%e8%be%be\/","title":{"rendered":"live\u5012\u7237\u60c5\u62a5\u96f7\u8fbe"},"content":{"rendered":"\n<style>\n    html, body { margin: 0 !important; padding: 0 !important; width: 100vw !important; height: 100vh !important; background: #000 !important; overflow: hidden !important; }\n    .wp-site-blocks, header, footer, nav, .site-header, .site-footer, .entry-header { display: none !important; }\n    #live-radar-app { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: #000; z-index: 2147483647; display: flex; flex-direction: column; }\n    .radar-header { height: 60px; display: flex; justify-content: space-between; align-items: center; padding: 0 20px; background: #050505; border-bottom: 1px solid #333; }\n    .ai-search-bar { display: flex; gap: 10px; align-items: center;}\n    .ai-search-bar input { width: 300px; padding: 8px 15px; background: #111; border: 1px solid #444; color: #fff; border-radius: 6px; outline: none; transition: 0.3s; }\n    .ai-search-bar input:focus { border-color: #eab308; }\n    .ai-search-bar button { background: #eab308; color: #000; border: none; padding: 8px 20px; border-radius: 6px; font-weight: bold; cursor: pointer; transition: 0.2s;}\n    #chart-main { flex: 1; width: 100vw !important; background: #000; position: relative; }\n    #error-alert { position: absolute; top: 20%; left: 50%; transform: translateX(-50%); color: #ef4444; text-align: center; display: none; z-index: 99; width: 80%; background: rgba(20,0,0,0.8); padding: 30px; border-radius: 12px; border: 1px solid #ef4444;}\n<\/style>\n\n<div id=\"live-radar-app-container\">\n    <div id=\"live-radar-app\">\n        <script src=\"https:\/\/cdn.tailwindcss.com\"><\/script>\n        <script src=\"https:\/\/cdn.jsdelivr.net\/npm\/echarts@5.5.0\/dist\/echarts.min.js\"><\/script>\n\n        <div class=\"radar-header\">\n            <h1 style=\"font-size: 1.2rem; font-weight: 900; font-style: italic; color: #eab308; margin:0;\">\n                LOCAL<span style=\"color: white;\">RADAR<\/span> \n                <span style=\"font-size: 0.8rem; font-weight: normal; color: #666; margin-left: 10px;\">V8.4 \u5168\u57df\u91cf\u5316\u60c5\u62a5\u7ec8\u7aef<\/span>\n            <\/h1>\n            <div class=\"ai-search-bar\">\n                <input id=\"ai-search-input\" type=\"text\" placeholder=\"\u26a1 \u6295\u5582\u7a81\u53d1\u4e8b\u4ef6...\">\n                <button id=\"ai-search-btn\">AI \u6ce8\u5165<\/button>\n            <\/div>\n            <div id=\"radar-status\" style=\"font-size: 0.8rem; color: #6b7280; font-family: monospace; margin:0;\">SYNCING&#8230;<\/div>\n        <\/div>\n        \n        <div id=\"error-alert\"><\/div>\n        <div id=\"chart-main\"><\/div>\n\n        <script>\n            \/\/ \ud83d\udea8 \u8fd9\u4e00\u6b65\u53bb\u6389\u4e86\u9650\u5236\uff0c\u7d22\u8981\u5b8c\u6574\u6570\u636e\n            const WP_API_URL = window.location.origin + \"\/wp-json\/wp\/v2\/asset?per_page=100\";\n            const chart = echarts.init(document.getElementById('chart-main'), 'dark');\n\n            function getHeatColor(change) {\n                if (change > 0) {\n                    let intensity = Math.min(change \/ 20, 1); \n                    return `rgb(${Math.round(80 + intensity * 175)}, 25, 25)`; \n                } else if (change < 0) {\n                    let intensity = Math.min(Math.abs(change) \/ 20, 1);\n                    return `rgb(25, ${Math.round(80 + intensity * 175)}, 25)`; \n                }\n                return '#444444'; \n            }\n\n            async function fetchRadarData() {\n                try {\n                    const res = await fetch(WP_API_URL);\n                    const posts = await res.json();\n                    \n                    \/\/ \ud83d\udea8 \u8d85\u7ea7\u4fa6\u5bdf\u5175\uff1a\u5f3a\u884c\u5728\u6d4f\u89c8\u5668\u7684\u63a7\u5236\u53f0\u6253\u5370 WordPress \u7ed9\u6211\u4eec\u7684\u771f\u5b9e\u6570\u636e\uff01\n                    console.log(\"\ud83d\udd25 \u4ece\u6570\u636e\u5e93\u6293\u5230\u7684\u6240\u6709\u539f\u59cb\u6570\u636e\uff1a\", posts);\n\n                    let treemapData = [];\n                    let debugLog = \"\"; \n\n                    posts.forEach(post => {\n                        if (!post.title || !post.title.rendered.includes('\u5b9e\u76d8')) return;\n                        \n                        let catName = post.title.rendered.replace(\/\\[\u5b9e\u76d8\\]|&#91;\u5b9e\u76d8&#93;|\u5b9e\u76d8|\\[|\\]\/g, '').trim();\n                        let acf = post.acf || {};\n                        \n                        let items = [];\n                        let rawData = acf.history_data || \"\";\n                        \n                        try { \n                            if(rawData) items = JSON.parse(rawData); \n                        } catch(e) {\n                            debugLog += `<div style=\"text-align:left; margin-top:10px; font-size:14px; color:#fff; font-family:monospace;\">\u274c \u677f\u5757 <b>[${catName}]<\/b> \u6570\u636e\u89e3\u6790\u5d29\u6e83\uff01<br><span style=\"color:#888;\">\u6b8b\u9ab8\u4ee3\u7801\uff1a<\/span><br><span style=\"color:#eab308;\">${rawData.substring(0, 150)}...<\/span><\/div>`;\n                        }\n\n                        if(!items || items.length === 0) return;\n\n                        let childrenNode = items.map(item => {\n                            return {\n                                name: item.product_name,\n                                value: item.volatility_abs, \n                                itemStyle: { color: getHeatColor(item.price_change) },\n                                label: { \n                                    show: true, position: 'insideTopLeft',\n                                    formatter: `{title|{b}}\\n{change|${item.price_change > 0 ? '+' : ''}${item.price_change}%}`,\n                                    rich: { title: { fontSize: 13, color: '#fff', padding: [5,0,2,0] }, change: { fontSize: 16, fontWeight: 'bold', color: '#fff' } }\n                                },\n                                meta: { ...item, catCause: acf.cause }\n                            };\n                        });\n\n                        treemapData.push({\n                            name: catName,\n                            value: parseFloat(acf.heat) || 10000, \n                            children: childrenNode,\n                            itemStyle: { color: 'transparent', borderColor: '#333', borderWidth: 2 },\n                            meta: { catName: catName, heat: acf.heat, heatChange: acf.change, cause: acf.cause }\n                        });\n                    });\n\n                    let errAlert = document.getElementById('error-alert');\n                    if(treemapData.length === 0) {\n                        if(debugLog !== \"\") {\n                            errAlert.innerHTML = `<h2 style=\"font-size: 28px; font-weight: bold; margin-bottom: 15px;\">\ud83d\udea8 \u6570\u636e\u88ab WordPress ACF \u622a\u65ad\uff01<\/h2><div style=\"background:#000; padding:15px; border-radius:8px;\">${debugLog}<\/div><p style=\"margin-top:20px; color:#22c55e;\">\u8bf7\u53bb ACF \u628a history_data \u6539\u6210\u3010\u591a\u884c\u6587\u672c (Text Area)\u3011\u518d\u8dd1 Python\uff01<\/p>`;\n                        } else {\n                            errAlert.innerHTML = `<h2 style=\"font-size: 30px; font-weight: bold; margin-bottom: 10px;\">\ud83d\udea8 \u5927\u76d8\u7a7a\u7a7a\u5982\u4e5f\uff01<\/h2><p style=\"font-size: 16px; color: #ccc;\">\u8bf7\u6309 F12 \u67e5\u770b Console \u63a7\u5236\u53f0\u6253\u5370\u7684 \ud83d\udd25 \u65e5\u5fd7\uff0c\u770b\u770b WordPress \u5230\u5e95\u8fd4\u56de\u4e86\u4ec0\u4e48\uff01<\/p>`;\n                        }\n                        errAlert.style.display = 'block';\n                        chart.clear();\n                        return;\n                    } else {\n                        errAlert.style.display = 'none';\n                    }\n\n                    chart.setOption({\n                        backgroundColor: '#050505',\n                        tooltip: {\n                            formatter: function (info) {\n                                if(info.treePathInfo.length === 2) { \n                                    let d = info.data.meta;\n                                    let heatSign = d.heatChange > 0 ? '+' : '';\n                                    return `<div style=\"padding:5px;\"><b>${info.name}<\/b><br>\u5168\u7f51\u70ed\u5ea6\u503c: ${d.heat}<br>\u8bdd\u9898\u5ea6\u8d8b\u52bf: <span style=\"color:${d.heatChange>0?'#ef4444':'#22c55e'}\">${heatSign}${d.heatChange}%<\/span><\/div>`;\n                                } else if(info.treePathInfo.length === 3) { \n                                    let d = info.data.meta;\n                                    let changeSign = d.price_change > 0 ? '+' : '';\n                                    let profit = (d.resale - d.retail).toFixed(2);\n                                    return `<div style=\"padding:5px;\"><b>${d.product_name}<\/b><br>\u62ff\u8d27\u6210\u672c: \uffe5${d.retail}<br>\u5012\u5356\u884c\u60c5: <span style=\"color:#eab308\">\uffe5${d.resale}<\/span><br>\u6ea2\u4ef7\u5229\u6da6: ${changeSign}\uffe5${profit} (${changeSign}${d.price_change}%)<\/div>`;\n                                }\n                            }\n                        },\n                        series: [{\n                            type: 'treemap', width: '100%', height: '100%', roam: true, nodeClick: false, breadcrumb: { show: false },\n                            levels: [\n                                { itemStyle: { borderColor: '#000', borderWidth: 0, gapWidth: 2 } }, \n                                { itemStyle: { borderColor: '#444', borderWidth: 4, gapWidth: 6 }, upperLabel: { show: true, height: 32, color: '#eab308', fontWeight: 'bold', fontSize: 15, backgroundColor: '#111' } }, \n                                { itemStyle: { borderColor: '#111', borderWidth: 1, gapWidth: 1 } } \n                            ],\n                            data: treemapData\n                        }]\n                    });\n                    document.getElementById('radar-status').innerText = \"DATA SYNCED \u2022 \" + new Date().toLocaleTimeString();\n                } catch (e) { console.error(e); }\n            }\n\n            fetchRadarData();\n            setInterval(fetchRadarData, 60000);\n            window.onresize = () => chart.resize();\n            setTimeout(() => { window.dispatchEvent(new Event('resize')); }, 600);\n\n            \/\/ ================= \u5f39\u7a97\u7cfb\u7edf =================\n            let modal = document.createElement('div');\n            modal.innerHTML = `\n                <div id=\"radar-modal\" style=\"position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.85); z-index:9999999; display:none; justify-content:center; align-items:center; backdrop-filter: blur(8px);\">\n                    <div style=\"background:#111; border:1px solid #333; border-radius:12px; padding:25px; width:90%; max-width:850px; position:relative; box-shadow: 0 20px 50px rgba(0,0,0,0.9);\">\n                        <button onclick=\"document.getElementById('radar-modal').style.display='none'\" style=\"position:absolute; top:15px; right:20px; color:#888; font-size:32px; background:none; border:none; cursor:pointer; line-height:1;\">&times;<\/button>\n                        <h2 id=\"m-title\" style=\"color:#fff; font-size:26px; font-weight:900; margin:0 0 15px 0;\"><\/h2>\n                        \n                        <div style=\"display:flex; gap: 20px; margin-bottom:20px;\">\n                            <div style=\"flex: 2; background: rgba(234, 179, 8, 0.05); border-left: 4px solid #eab308; padding: 15px; border-radius: 4px;\">\n                                <span style=\"color:#eab308; font-weight:bold; font-size:14px;\">\u26a1 AI \u4e8b\u4ef6\u5185\u9a71\u5206\u6790\uff1a<\/span>\n                                <div id=\"m-cause\" style=\"color:#d1d5db; font-size:14px; margin-top:8px; line-height: 1.6;\"><\/div>\n                            <\/div>\n                            <div style=\"flex: 1; text-align:right; background: #1a1a1a; padding: 15px; border-radius: 8px; display:flex; flex-direction:column; justify-content:center;\">\n                                <span style=\"color:#888; font-size:12px;\">\u5b9e\u65f6\u4e8c\u7ea7\u5e02\u573a\u5012\u5356\u4ef7<\/span>\n                                <div id=\"m-resale\" style=\"font-size:32px; font-family:monospace; font-weight:bold; color:#eab308;\"><\/div>\n                                <div style=\"color:#888; font-size:12px; margin-top:5px;\">\u8fdb\u8d27\u6210\u672c: <span id=\"m-retail\" style=\"color:#fff;\"><\/span><\/div>\n                            <\/div>\n                        <\/div>\n\n                        <div style=\"margin-bottom:10px;\"><span style=\"color:#888; font-size:13px;\">\u8fd1 7 \u65e5\u4e8c\u7ea7\u5e02\u573a\u5012\u5356\u4ef7\u8d8b\u52bf (\uffe5)<\/span><\/div>\n                        <div id=\"m-chart\" style=\"width:100%; height:260px; background:#0a0a0a; border-radius:8px; border:1px solid #222;\"><\/div>\n                    <\/div>\n                <\/div>\n            `;\n            document.body.appendChild(modal);\n\n            chart.on('click', function (params) {\n                if(params.treePathInfo.length !== 3) return;\n                \n                const d = params.data.meta;\n                const isUp = d.price_change >= 0;\n                const sign = isUp ? '+' : '';\n\n                document.getElementById('m-title').innerText = d.product_name;\n                document.getElementById('m-cause').innerHTML = `<b>\u3010\u5927\u76d8\u903b\u8f91\u3011<\/b> ${d.catCause}<br><br><b>\u3010\u5355\u54c1\u903b\u8f91\u3011<\/b> ${d.product_cause}`;\n                document.getElementById('m-retail').innerText = `\uffe5${d.retail}`;\n                document.getElementById('m-resale').innerText = `\uffe5${d.resale}`;\n\n                document.getElementById('radar-modal').style.display = 'flex';\n                \n                let lineChart = echarts.init(document.getElementById('m-chart'), 'dark');\n                let xAxisData = [];\n                for(let i = d.history.length - 1; i >= 0; i--) { xAxisData.push(i === 0 ? '\u76ee\u524d\u6700\u65b0' : `T-${i}`); }\n\n                lineChart.setOption({\n                    backgroundColor: 'transparent', tooltip: { trigger: 'axis', axisPointer: { type: 'cross' } },\n                    grid: { left: '4%', right: '4%', bottom: '5%', top: '10%', containLabel: true },\n                    xAxis: { type: 'category', data: xAxisData, axisLine: { lineStyle: { color: '#444' } } },\n                    yAxis: { type: 'value', scale: true, splitLine: { lineStyle: { color: '#222', type: 'dashed' } } },\n                    series: [{\n                        data: d.history, type: 'line', smooth: true, symbolSize: 6,\n                        itemStyle: { color: isUp ? '#ef4444' : '#22c55e' },\n                        lineStyle: { width: 3, color: isUp ? '#ef4444' : '#22c55e' },\n                        areaStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{offset: 0, color: isUp ? 'rgba(239,68,68,0.2)' : 'rgba(34,197,94,0.2)'}, {offset: 1, color: 'rgba(0,0,0,0)'}]) }\n                    }]\n                });\n            });\n        <\/script>\n    <\/div>\n<\/div>\n<script>document.body.insertBefore(document.getElementById('live-radar-app-container'), document.body.firstChild);<\/script>\n","protected":false},"excerpt":{"rendered":"<p>LOCALRADAR V8.4 \u5168\u57df\u91cf\u5316\u60c5\u62a5\u7ec8\u7aef AI \u6ce8\u5165 SYNCING&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_acf_changed":false,"footnotes":""},"class_list":["post-34","page","type-page","status-publish","hentry"],"acf":[],"_links":{"self":[{"href":"https:\/\/radar.neofabmoto.com\/index.php\/wp-json\/wp\/v2\/pages\/34","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/radar.neofabmoto.com\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/radar.neofabmoto.com\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/radar.neofabmoto.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/radar.neofabmoto.com\/index.php\/wp-json\/wp\/v2\/comments?post=34"}],"version-history":[{"count":16,"href":"https:\/\/radar.neofabmoto.com\/index.php\/wp-json\/wp\/v2\/pages\/34\/revisions"}],"predecessor-version":[{"id":98,"href":"https:\/\/radar.neofabmoto.com\/index.php\/wp-json\/wp\/v2\/pages\/34\/revisions\/98"}],"wp:attachment":[{"href":"https:\/\/radar.neofabmoto.com\/index.php\/wp-json\/wp\/v2\/media?parent=34"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}