{"id":888,"date":"2025-11-10T21:38:40","date_gmt":"2025-11-10T13:38:40","guid":{"rendered":"https:\/\/explore2022.com\/?page_id=888"},"modified":"2025-11-11T08:31:26","modified_gmt":"2025-11-11T00:31:26","slug":"%e7%be%8e%e8%82%a1-2","status":"publish","type":"page","link":"https:\/\/www.explore2022.com\/?page_id=888","title":{"rendered":"\u80a1\u5e02\u884c\u60c5"},"content":{"rendered":"\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/chart.js\"><\/script>\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/chartjs-plugin-zoom\"><\/script>\n\n<div id=\"stock-container\" style=\"font-family: Arial; font-size:15px; max-width:900px; margin:auto;\">\n  <h3>\u80a1\u7968\u4e0e\u6307\u6570\u884c\u60c5<\/h3>\n\n  <div style=\"margin-bottom:10px;\">\n    <input id=\"symbolInput\" type=\"text\" placeholder=\"\u8f93\u5165\u80a1\u7968\u4ee3\u7801\uff0c\u5982 TSLA, 00700.HK\" style=\"padding:5px 8px; width:220px;\">\n    <button id=\"searchBtn\" style=\"padding:5px 10px;\">\u641c\u7d22<\/button>\n    <button id=\"resetBtn\" style=\"padding:5px 10px;\">\u6062\u590d\u9ed8\u8ba4<\/button>\n    <select id=\"intervalSelect\" style=\"padding:5px 8px;\">\n      <option value=\"5m\">5\u5206\u949f<\/option>\n      <option value=\"15m\" selected>15\u5206\u949f<\/option>\n      <option value=\"1h\">1\u5c0f\u65f6<\/option>\n      <option value=\"4h\">4\u5c0f\u65f6<\/option>\n      <option value=\"1d\">\u65e5\u7ebf<\/option>\n    <\/select>\n  <\/div>\n\n  <table id=\"stock-table\" border=\"1\" cellspacing=\"0\" cellpadding=\"6\" style=\"border-collapse: collapse; width:100%; text-align:center;\">\n    <thead style=\"background:#f5f5f5;\">\n      <tr>\n        <th>\u4ee3\u7801<\/th>\n        <th>\u540d\u79f0<\/th>\n        <th>\u4ef7\u683c<\/th>\n        <th>\u65e5\u6da8\u8dcc\u989d<\/th>\n        <th>\u65e5\u6da8\u5e45<\/th>\n      <\/tr>\n    <\/thead>\n    <tbody><\/tbody>\n  <\/table>\n\n  <canvas id=\"lineChart\" style=\"margin-top:20px; max-height:500px;\"><\/canvas>\n  <p id=\"updateTime\" style=\"font-size:13px; text-align:right; color:gray;\"><\/p>\n<\/div>\n\n<style>\n  .flash-green { animation: flashGreen 0.6s ease; }\n  .flash-red { animation: flashRed 0.6s ease; }\n  @keyframes flashGreen {\n    from { background-color: rgba(0,255,0,0.15); }\n    to { background-color: transparent; }\n  }\n  @keyframes flashRed {\n    from { background-color: rgba(255,0,0,0.15); }\n    to { background-color: transparent; }\n  }\n<\/style>\n\n<script>\nconst apiBase = \"https:\/\/f.202620260.xyz\";\nconst tbody = document.querySelector(\"#stock-table tbody\");\nconst updateLabel = document.getElementById(\"updateTime\");\nlet currentSymbol = null;\nlet intervalId = null;\nlet chart = null;\nlet lastPrice = {};\n\nconst defaultIndices = [\n  { symbol: \"^IXIC\", name: \"\u7eb3\u65af\u8fbe\u514b\u6307\u6570\" },\n  { symbol: \"^GSPC\", name: \"\u6807\u666e500\u6307\u6570\" },\n  { symbol: \"^DJI\",  name: \"\u9053\u743c\u65af\u6307\u6570\" },\n  { symbol: \"^HSTECH\", name: \"\u6052\u751f\u79d1\u6280\u6307\u6570\" }\n];\n\n\/\/ \u52a0\u8f7d\u5e76\u6e32\u67d3\u884c\u60c5\nasync function fetchAndRender(symbol=null){\n  try{\n    const url = symbol ? `${apiBase}\/search?symbol=${symbol}` : `${apiBase}\/price`;\n    const res = await fetch(url);\n    const data = await res.json();\n\n    let mergedData = {};\n    if(!symbol){\n      for(const idx of defaultIndices){\n        try{\n          const idxRes = await fetch(`${apiBase}\/search?symbol=${encodeURIComponent(idx.symbol)}`);\n          const idxData = await idxRes.json();\n          if(idxData && Object.keys(idxData).length>0){\n            for(const k in idxData){ idxData[k].name = idx.name; }\n            Object.assign(mergedData, idxData);\n          }\n        }catch(e){ console.warn(\"\u6307\u6570\u52a0\u8f7d\u5931\u8d25\", idx.symbol); }\n      }\n      Object.assign(mergedData, data);\n    }else{\n      mergedData = data;\n    }\n\n    const orderedData = Object.values(mergedData).sort((a,b)=>{\n      const idxOrder = defaultIndices.map(i=>i.symbol);\n      const ia = idxOrder.indexOf(a.symbol);\n      const ib = idxOrder.indexOf(b.symbol);\n      if(ia!==-1 && ib!==-1) return ia-ib;\n      if(ia!==-1) return -1;\n      if(ib!==-1) return 1;\n      return a.symbol.localeCompare(b.symbol);\n    });\n\n    orderedData.forEach(item=>{\n      const price = item.price || 0;\n      const change = item.change || 0;\n\n      \/\/ ---- \u667a\u80fd\u4fee\u6b63\u6da8\u8dcc\u5e45 ----\n      let percent = item.percent || item.changePercent || 0;\n      if (Math.abs(percent) < 5) percent *= 100; \/\/ \u81ea\u52a8\u4fee\u6b63\u4e3a\u767e\u5206\u6bd4\n      if (isNaN(percent)) percent = 0;\n      \/\/ -------------------------\n\n      const rowId = `row-${item.symbol.replace(\/\\W\/g,'')}`;\n      let row = document.getElementById(rowId);\n\n      const changeColor = change >= 0 ? \"green\" : \"red\";\n      const arrow = change >= 0 ? \"\u25b2\" : \"\u25bc\";\n\n      if(!row){\n        row = document.createElement(\"tr\");\n        row.id = rowId;\n        row.innerHTML = `\n          <td>${item.symbol}<\/td>\n          <td>${item.name || \"-\"}<\/td>\n          <td class=\"price\" style=\"color:black\">${price.toFixed(2)}<\/td>\n          <td class=\"change\" style=\"color:${changeColor}\">${arrow} ${change.toFixed(2)}<\/td>\n          <td class=\"percent\" style=\"color:${changeColor}\">${percent.toFixed(2)}%<\/td>\n        `;\n        tbody.appendChild(row);\n      } else {\n        const priceCell = row.querySelector(\".price\");\n        const oldPrice = lastPrice[item.symbol];\n        if(oldPrice !== undefined && oldPrice !== price){\n          const diffUp = price > oldPrice;\n          priceCell.classList.remove(\"flash-green\",\"flash-red\");\n          void priceCell.offsetWidth;\n          priceCell.classList.add(diffUp ? \"flash-green\" : \"flash-red\");\n        }\n        priceCell.textContent = price.toFixed(2);\n        row.querySelector(\".change\").innerHTML = `${arrow} ${change.toFixed(2)}`;\n        row.querySelector(\".change\").style.color = changeColor;\n        row.querySelector(\".percent\").textContent = `${percent.toFixed(2)}%`;\n        row.querySelector(\".percent\").style.color = changeColor;\n      }\n\n      lastPrice[item.symbol] = price;\n    });\n\n    updateLabel.textContent = \"\u6700\u540e\u66f4\u65b0\uff1a\" + new Date().toLocaleTimeString();\n  }catch(err){ console.error(\"\u6570\u636e\u52a0\u8f7d\u5931\u8d25\uff1a\", err); }\n}\n\n\/\/ \u7ed8\u5236\u8d70\u52bf\u56fe\nasync function fetchLineChart(symbol, interval){\n  try{\n    if(symbol.includes(\".HK\") && interval!==\"1d\") interval=\"1d\";\n    const res = await fetch(`${apiBase}\/kline?symbol=${symbol}&interval=${interval}`);\n    const data = await res.json();\n    if(!data || data.error || data.length===0){\n      if(chart) chart.destroy();\n      return;\n    }\n\n    const ctx = document.getElementById(\"lineChart\").getContext(\"2d\");\n    const labels = data.map(d=>d.time);\n    const closeData = data.map(d=>d.close);\n\n    const chartData = {\n      labels,\n      datasets:[{\n        label:'\u6536\u76d8\u4ef7',\n        data: closeData,\n        borderColor:'black',\n        fill:false,\n        tension:0.2,\n        pointRadius:0\n      }]\n    };\n\n    const options = {\n      plugins:{\n        tooltip:{\n          mode:'index',\n          intersect:false,\n          callbacks:{\n            label:function(ctx){\n              const i = ctx.dataIndex;\n              const d = data[i];\n              return `O:${d.open} H:${d.high} L:${d.low} C:${d.close}`;\n            }\n          }\n        },\n        zoom:{ pan:{enabled:true,mode:'x'}, zoom:{enabled:true,mode:'x'} },\n        legend:{display:false}\n      },\n      scales:{y:{beginAtZero:false}}\n    };\n\n    if(chart) chart.destroy();\n    chart = new Chart(ctx,{type:'line',data:chartData,options});\n  }catch(err){ console.error(\"\u8d70\u52bf\u56fe\u52a0\u8f7d\u5931\u8d25\",err); }\n}\n\n\/\/ \u9ed8\u8ba4\u52a0\u8f7d\nfunction loadDefault(){\n  currentSymbol = null;\n  fetchAndRender();\n  if(intervalId) clearInterval(intervalId);\n  intervalId = setInterval(()=>{\n    fetchAndRender();\n    if(currentSymbol) fetchLineChart(currentSymbol, document.getElementById(\"intervalSelect\").value);\n  },10000);\n  if(chart) chart.destroy();\n}\n\n\/\/ \u641c\u7d22\u6309\u94ae\ndocument.getElementById(\"searchBtn\").addEventListener(\"click\", ()=>{\n  const symbol = document.getElementById(\"symbolInput\").value.trim();\n  if(!symbol) return alert(\"\u8bf7\u8f93\u5165\u80a1\u7968\u4ee3\u7801\uff01\");\n  currentSymbol = symbol;\n  fetchAndRender(symbol);\n  fetchLineChart(symbol, document.getElementById(\"intervalSelect\").value);\n  if(intervalId) clearInterval(intervalId);\n  intervalId = setInterval(()=>{\n    fetchAndRender(symbol);\n    fetchLineChart(symbol, document.getElementById(\"intervalSelect\").value);\n  },10000);\n});\n\n\/\/ \u6062\u590d\u9ed8\u8ba4\ndocument.getElementById(\"resetBtn\").addEventListener(\"click\", loadDefault);\n\n\/\/ \u65f6\u95f4\u5468\u671f\u5207\u6362\ndocument.getElementById(\"intervalSelect\").addEventListener(\"change\", ()=>{\n  if(currentSymbol) fetchLineChart(currentSymbol, document.getElementById(\"intervalSelect\").value);\n});\n\n\/\/ \u521d\u59cb\u5316\nloadDefault();\n<\/script>\n\n","protected":false},"excerpt":{"rendered":"<p>\u80a1\u7968\u4e0e\u6307\u6570\u884c\u60c5 \u641c\u7d22 \u6062\u590d\u9ed8\u8ba4 5\u5206\u949f15\u5206\u949f1\u5c0f\u65f64\u5c0f\u65f6\u65e5\u7ebf \u4ee3\u7801 \u540d\u79f0 \u4ef7\u683c \u65e5\u6da8\u8dcc\u989d \u65e5\u6da8\u5e45<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"emotion":"","emotion_color":"","title_style":"","license":"","footnotes":""},"_links":{"self":[{"href":"https:\/\/www.explore2022.com\/index.php?rest_route=\/wp\/v2\/pages\/888"}],"collection":[{"href":"https:\/\/www.explore2022.com\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.explore2022.com\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.explore2022.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.explore2022.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=888"}],"version-history":[{"count":9,"href":"https:\/\/www.explore2022.com\/index.php?rest_route=\/wp\/v2\/pages\/888\/revisions"}],"predecessor-version":[{"id":911,"href":"https:\/\/www.explore2022.com\/index.php?rest_route=\/wp\/v2\/pages\/888\/revisions\/911"}],"wp:attachment":[{"href":"https:\/\/www.explore2022.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=888"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}