1<!DOCTYPE html> 2<html lang="zh-CN"> 3<head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <meta name="description" content="æµæ¾ä¹è·¯2åºç¡ç©åä»·æ ¼æ¥è¯¢ï¼å æ¬æ¦å¨ãæ¤ç²ã饰åçåºç¡è£ å¤ç宿¶ä»·æ ¼ä¿¡æ¯"> 7 <meta name="keywords" content="POE2,æµæ¾ä¹è·¯2,åºç¡ç©å,è£ å¤ä»·æ ¼,æ¦å¨,æ¤ç²"> 8 <title>åºåºç©åä»·æ ¼ - POE2 Base Items | poe2lens</title> 9 <link rel="icon" type="image/x-icon" href="/favicon.ico"> 10 <link rel="canonical" href="https://poe2lens.com/poe2_base_price.html" /> 11 <link rel="stylesheet" href="/static/css/page_helper.css"> 12 13
13<script src="/js/deferred_analytics.js" defer></script>
13 14
14<script src="/js/deferred_baidu_push.js" defer></script>
14 15 <!-- ç»æåæ°æ®ï¼Schema.orgï¼- WebSite --> 16
16<script type="application/ld+json"> 17 { 18 "@context": "https://schema.org", 19 "@type": "WebSite", 20 "name": "poe2lens", 21 "description": "æµæ¾ä¹è·¯2å ¨è½å·¥å ·ç®±ï¼æä¾ç©ä»·æ¥è¯¢ãå货计ç®ãå®ç³å©æ¶¦åæçåè½", 22 "url": "https://poe2lens.com/", 23 "potentialAction": { 24 "@type": "SearchAction", 25 "target": "https://poe2lens.com/poe2_base_price.html?search={search_term_string}", 26 "query-input": "required name=search_term_string" 27 } 28 } 29 </script>
29 30 31 <!-- ç»æåæ°æ®ï¼Schema.orgï¼- BreadcrumbList --> 32
32<script type="application/ld+json"> 33 { 34 "@context": "https://schema.org", 35 "@type": "BreadcrumbList", 36 "itemListElement": [ 37 { 38 "@type": "ListItem", 39 "position": 1, 40 "name": "é¦é¡µ", 41 "item": "https://poe2lens.com/" 42 }, 43 { 44 "@type": "ListItem", 45 "position": 2, 46 "name": "åºåºç©åä»·æ ¼", 47 "item": "https://poe2lens.com/poe2_base_price.html" 48 } 49 ] 50 } 51 </script>
51 52 53 <link rel="stylesheet" href="/static/vendor/font-awesome/css/all.min.css"> 54 <!-- ä½¿ç¨æ¬å°axiosæä»¶ --> 55
55<script src="/static/js/axios.min.js" onerror="console.error('axioså 载失败');"></script>
55 56 57 <!-- Chart.js for price history charts --> 58
58<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.min.js"></script>
58 59 60 <style> 61 * { box-sizing: border-box; } 62 body { 63 margin: 0; 64 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; 65 background: #111827; 66 color: #e5e7eb; 67 } 68 .page-wrap { 69 max-width: var(--page-max-width, 1440px); 70 margin: 0 auto; 71 padding: var(--page-pad-top, 5.25rem) var(--page-pad-x, 1rem) 2rem; 72 } 73 .page-header h1 { margin: 0 0 0.35rem; font-size: 1.5rem; color: #fff; } 74 .page-header p { margin: 0; color: #9ca3af; font-size: 0.85rem; line-height: 1.45; } 75 .meta-bar { 76 display: flex; flex-wrap: wrap; gap: 0.65rem 1.25rem; align-items: center; 77 margin: 0.85rem 0 0.9rem; color: #9ca3af; font-size: 0.8rem; 78 } 79 .unit-toggle { 80 display: inline-flex; border: 1px solid #374151; border-radius: 0.35rem; 81 overflow: hidden; background: #111827; 82 } 83 .unit-toggle button { 84 background: transparent; border: none; color: #9ca3af; 85 padding: 0.28rem 0.7rem; font-size: 0.78rem; font-weight: 600; 86 cursor: pointer; line-height: 1.2; 87 } 88 .unit-toggle button + button { border-left: 1px solid #374151; } 89 .unit-toggle button:hover { color: #fff; background: #1f2937; } 90 .unit-toggle button.active { 91 background: #1e3a5f; color: #93c5fd; 92 } 93 .rates-hint { 94 color: #9ca3af; font-size: 0.8rem; 95 display: inline-flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; 96 } 97 .rate-pill { 98 display: inline-flex; align-items: baseline; gap: 0.25rem; 99 padding: 0.15rem 0.5rem; border-radius: 4px; 100 background: #1f2937; border: 1px solid #374151; 101 font-variant-numeric: tabular-nums; 102 } 103 .rate-pill strong { color: #fbbf24; font-weight: 600; } 104 .rate-pill .rate-sub { color: #6b7280; font-size: 0.72rem; } 105 .league-badge { 106 display: inline-flex; align-items: center; 107 background: #1f2937; border: 1px solid #4b5563; 108 color: #93c5fd; border-radius: 0.35rem; 109 padding: 0.2rem 0.55rem; font-weight: 600; 110 } 111 .meta-count { color: #6b7280; margin-left: 0.35rem; } 112 .toolbar { 113 display: flex; flex-wrap: wrap; gap: 0.65rem; align-items: center; 114 margin-bottom: 0.85rem; 115 } 116 .toolbar input[type="search"], 117 .toolbar input[type="number"] { 118 background: #1f2937; border: 1px solid #374151; color: #fff; 119 border-radius: 0.375rem; padding: 0.5rem 0.7rem; font-size: 0.88rem; 120 } 121 .toolbar input[type="search"] { flex: 1 1 200px; min-width: 150px; } 122 .toolbar input[type="number"] { width: 5.5rem; } 123 .toolbar input:focus { 124 outline: none; border-color: #60a5fa; 125 box-shadow: 0 0 0 1px #60a5fa; 126 } 127 .toolbar label { 128 color: #9ca3af; font-size: 0.82rem; 129 display: inline-flex; align-items: center; gap: 0.35rem; 130 } 131 .toolbar-btn { 132 background: #1f2937; border: 1px solid #374151; color: #d1d5db; 133 border-radius: 0.375rem; padding: 0.45rem 0.8rem; font-size: 0.84rem; 134 cursor: pointer; line-height: 1.2; 135 } 136 .toolbar-btn:hover { border-color: #4b5563; color: #fff; background: #374151; } 137 .toolbar-btn.primary { 138 background: #1d4ed8; border-color: #2563eb; color: #fff; 139 } 140 .toolbar-btn.primary:hover { filter: brightness(1.08); } 141 .search-wrap { position: relative; flex: 1 1 200px; min-width: 150px; } 142 .search-wrap input { width: 100%; padding-left: 2.2rem; padding-right: 2rem; } 143 .search-wrap .search-ico { 144 position: absolute; left: 0.7rem; top: 50%; transform: translateY(-50%); 145 color: #6b7280; pointer-events: none; font-size: 0.8rem; 146 } 147 .search-wrap .clear-btn { 148 position: absolute; right: 0.45rem; top: 50%; transform: translateY(-50%); 149 background: transparent; border: none; color: #9ca3af; cursor: pointer; 150 padding: 0.2rem 0.35rem; display: none; 151 } 152 .search-wrap .clear-btn:hover { color: #fff; } 153 .search-wrap .clear-btn.show { display: inline-block; } 154 .legend-bar { 155 display: flex; flex-wrap: wrap; gap: 0.5rem 0.85rem; align-items: center; 156 margin-bottom: 0.75rem; color: #9ca3af; font-size: 0.78rem; 157 } 158 .base-layout { 159 display: grid; 160 grid-template-columns: 230px minmax(0, 1fr); 161 gap: 1rem; 162 align-items: stretch; 163 height: calc(100vh - 13.5rem); 164 min-height: 360px;
165 overflow-anchor: none; 166 } 167 .panel { 168 background: #1f2937; border: 1px solid #374151; border-radius: 0.5rem; 169 } 170 .panel-side { 171 position: relative; 172 height: 100%; 173 overflow-y: auto; 174 overflow-x: hidden; 175 padding: 0.55rem 0.45rem; 176 overscroll-behavior: contain; 177 } 178 .panel-title { 179 margin: 0 0 0.45rem; padding: 0 0.4rem; 180 font-size: 0.78rem; font-weight: 600; color: #9ca3af; 181 } 182 .sidebar-category { 183 cursor: pointer; 184 padding: 0.32rem 0.4rem; 185 border-radius: 0.35rem; 186 display: flex; 187 align-items: center; 188 gap: 0.35rem; 189 margin-bottom: 1px; 190 color: #d1d5db; 191 font-size: 0.82rem; 192 border: 1px solid transparent; 193 } 194 .sidebar-category.subcategory { 195 padding-left: 0.45rem; 196 margin-left: 0.55rem; 197 border-left: 2px solid #374151; 198 font-size: 0.78rem; 199 color: #9ca3af; 200 } 201 .sidebar-category:hover { 202 background: #374151; 203 color: #fff; 204 } 205 .sidebar-category.active { 206 background: #1e3a5f; 207 border-color: #2563eb; 208 color: #fff; 209 font-weight: 600; 210 } 211 .sidebar-category.active.subcategory { 212 border-left-color: #2563eb; 213 } 214 .cat-chevron { 215 flex: 0 0 1.1rem; width: 1.1rem; height: 1.1rem; 216 padding: 0; margin: 0; border: none; background: transparent; 217 color: #9ca3af; cursor: pointer; display: inline-flex; 218 align-items: center; justify-content: center; font-size: 0.72rem; 219 } 220 .cat-chevron:hover { color: #fff; } 221 .cat-chevron-spacer { flex: 0 0 1.1rem; width: 1.1rem; } 222 .cat-count { 223 margin-left: auto; color: #6b7280; font-size: 0.72rem; flex-shrink: 0; 224 } 225 .category-name { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } 226 .category-icon { 227 display: flex; align-items: center; justify-content: center; 228 width: 18px; height: 18px; flex-shrink: 0; 229 } 230 .category-icon img { width: 18px; height: 18px; object-fit: contain; } 231 .category-divider { 232 height: 1px; margin: 0.45rem 0.35rem; 233 background: #374151; 234 } 235 .table-wrap { 236 background: #1f2937; border: 1px solid #374151; border-radius: 0.5rem; 237 overflow: auto; height: 100%; max-height: none; 238 overscroll-behavior: contain; overflow-anchor: none; 239 } 240 table.base-table { 241 width: 100%; border-collapse: separate; border-spacing: 0; 242 min-width: 980px; 243 } 244 table.base-table th, table.base-table td { 245 padding: 0.55rem 0.6rem; text-align: left; border-bottom: 1px solid #374151; 246 vertical-align: middle; font-size: 0.85rem; 247 background: #1f2937; 248 } 249 table.base-table th { 250 position: sticky; top: 0; background: #111827; color: #9ca3af; 251 font-weight: 600; user-select: none; white-space: nowrap; 252 z-index: 3; box-shadow: 0 1px 0 #374151; 253 } 254 table.base-table th.sortable { cursor: pointer; } 255 table.base-table th.sortable:hover { color: #fff; } 256 table.base-table tbody tr:hover td { background: #374151; } 257 table.base-table .col-type { color: #d1d5db; font-size: 0.8rem; } 258 table.base-table .col-muted { color: #6b7280; } 259 table.base-table .col-premium { color: #6ee7b7; } 260 table.base-table .text-center { text-align: center; } 261 .sort-indicator { margin-left: 0.2rem; font-size: 0.72rem; color: #93c5fd; } 262 .ladder-cell { font-variant-numeric: tabular-nums; font-size: 0.82rem; white-space: nowrap; } 263 .ladder-price { color: #e5e7eb; font-weight: 600; } 264 .ladder-no-listing { color: #fbbf24; font-size: 0.74rem; } 265 .ladder-not-queried { color: #6b7280; font-size: 0.74rem; } 266 .ladder-legend { 267 display: inline-block; padding: 0.12rem 0.4rem; border-radius: 0.25rem; 268 border: 1px solid #374151; 269 } 270 .ladder-legend.ladder-price { background: #1f2937; color: #e5e7eb; font-weight: 600; } 271 .ladder-legend.ladder-no-listing { background: #422006; border-color: #854d0e; color: #fbbf24; } 272 .ladder-legend.ladder-not-queried { background: #111827; color: #9ca3af; } 273 .action-btn { 274 background: transparent; border: 1px solid #374151; color: #93c5fd; 275 border-radius: 0.3rem; padding: 0.2rem 0.45rem; font-size: 0.78rem; 276 cursor: pointer; 277 } 278 .action-btn:hover { background: #1e3a5f; border-color: #2563eb; color: #fff; } 279 .hidden { display: none !important; }
280 .modal-overlay { 281 position: fixed; inset: 0; background: rgba(0,0,0,0.75); 282 z-index: 9999; display: flex; align-items: center; justify-content: center; 283 } 284 .modal-card { 285 background: #1f2937; border: 1px solid #374151; border-radius: 0.5rem; 286 padding: 1rem 1.1rem; max-width: 90%; max-height: 90%; 287 width: 800px; display: flex; flex-direction: column; 288 } 289 .modal-card h3 { margin: 0; color: #fff; font-size: 1.05rem; } 290 .modal-close { 291 background: transparent; border: none; color: #9ca3af; 292 font-size: 1.35rem; cursor: pointer; line-height: 1; 293 } 294 .modal-close:hover { color: #fff; } 295 @media (max-width: 900px) { 296 .base-layout { 297 grid-template-columns: 1fr; 298 height: auto; 299 min-height: 0; 300 } 301 .panel-side { position: static; height: auto; max-height: 40vh; } 302 .table-wrap { height: 65vh; } 303 } 304 </style> 305 306 307 <!-- AdSense å¹¿åæ ·å¼ --> 308 <link rel="stylesheet" href="/static/css/adsense.css"> 309 <link rel="stylesheet" href="/static/css/styles.css?v=2.3"> 310 311 <!-- AdSense é ç½®åèæ¬ --> 312
312<script src="/js/adsense_config.js"></script>
312 313
313<script src="/js/adsense.js"></script>
313 314 315 <!-- ç¾åº¦ç»è®¡ --> 316
316<script> 317 var _hmt = _hmt || []; 318 (function() { 319 var hm = document.createElement("script"); 320 hm.src = "https://hm.baidu.com/hm.js?5a085d195477c2675243b896093d6d8c"; 321 var s = document.getElementsByTagName("script")[0]; 322 s.parentNode.insertBefore(hm, s); 323 })(); 324 </script>
324 325</head> 326<body data-site-nav data-current-game="poe2" data-nav-page="nav-base-price"> 327 <noscript> 328 <div style="padding:20px; background:#1f2937; color:#fff;"> 329 <h1>poe2lens - æµæ¾ä¹è·¯2å·¥å ·ç®±</h1> 330 <p>æ¬ç½ç«éè¦ JavaScript æ¯æä»¥è·å宿´åè½ã请å¯ç¨å访é®ã</p> 331 </div> 332 </noscript> 333 334 <div class="page-wrap"> 335 <div class="page-header"> 336 <h1>åºåºç©åä»·æ ¼ <span id="itemCount" class="meta-count" style="font-size:1rem;font-weight:400;">0</span></h1> 337 <p>é«çº§åºåå¨ä¸å ilvl ä¸çä»·æ ¼é¶æ¢¯ã䏿¹ <b>c/e</b>=1 chaos å å¤å° exaltedï¼<b>d/e</b>=1 divine å å¤å° exaltedï¼ä¸åä»·æ¢ç®åä¸å¥æ±çï¼ã</p> 338 </div> 339 340 <div class="meta-bar" id="baseMetaBar"> 341 <span>èèµï¼<span class="league-badge" id="baseLeagueLabel">â</span></span> 342 <span id="baseUpdatedWrap">æ°æ®æ´æ°ï¼â</span> 343 <span class="rates-hint" id="baseRatesHint" title="页颿¢ç®/å价使ç¨çæ±çï¼X/e = 1 åä½ X çäºå¤å° exalted"> 344 <span class="rate-pill" id="baseCeRatePill">c/eï¼â</span> 345 <span class="rate-pill" id="baseDeRatePill">d/eï¼â</span> 346 </span> 347 <span class="unit-toggle" id="baseUnitToggle" title="æ¾ç¤ºè´§å¸åä½ï¼æä¸æ¹ c/eãd/e æ¢ç®ï¼"> 348 <button type="button" data-unit="exalted" class="active">Exalt</button> 349 <button type="button" data-unit="chaos">Chaos</button> 350 <button type="button" data-unit="divine">Divine</button> 351 </span> 352 </div> 353 354 <div class="toolbar"> 355 <div class="search-wrap"> 356 <i class="fas fa-search search-ico"></i> 357 <input type="search" id="categorySearchInput" placeholder="æç´¢åºåºåï¼ä¸/è±ï¼â¦" autocomplete="off" /> 358 <button type="button" id="clearCategorySearchBtn" class="clear-btn" title="æ¸ é¤">Ã</button> 359 </div> 360 <label><span id="filter-min-price-label">æä½ä»·(e)</span> 361 <input type="number" id="filter-min-price" placeholder="0" min="0" step="0.1" /> 362 </label> 363 <label>æå°åå®¶ 364 <input type="number" id="filter-min-sellers" placeholder="0" min="0" step="1" /> 365 </label> 366 <button type="button" id="clear-filters-btn" class="toolbar-btn">æ¸ é¤è¿æ»¤</button> 367 <button type="button" id="generate-filter-code-btn" class="toolbar-btn primary">çæè¿æ»¤ä»£ç </button> 368 </div> 369 370 <div class="legend-bar"> 371 <span>å¾ä¾ï¼</span> 372 <span id="level-tabs"></span> 373 </div> 374 375 <div class="base-layout"> 376 <aside class="panel panel-side" aria-label="ç±»å«"> 377 <div class="panel-title">ç±»å«</div> 378 <div id="base-categories"></div> 379 </aside> 380 381 <div class="table-wrap"> 382 <table class="base-table"> 383 <thead> 384 <tr> 385 <th class="sortable" data-sort-field="name" style="width:22%;"> 386 é«çº§åºå <span class="sort-indicator" data-sort-indicator="name"></span> 387 </th> 388 <th class="sortable" data-sort-field="sub_category" style="width:9%;"> 389 ç±»å <span class="sort-indicator" data-sort-indicator="sub_category"></span> 390 </th> 391 <th class="sortable" data-sort-field="price_82" style="width:8%;text-align:center;"> 392 82 <span class="sort-indicator" data-sort-indicator="price_82"></span> 393 </th> 394 <th class="sortable" data-sort-field="price_81" style="width:8%;text-align:center;"> 395 81 <span class="sort-indicator" data-sort-indicator="price_81"></span> 396 </th> 397 <th class="sortable" data-sort-field="price_80" style="width:8%;text-align:center;"> 398 80 <span class="sort-indicator" data-sort-indicator="price_80"></span> 399 </th> 400 <th class="sortable" data-sort-field="price_79" style="width:8%;text-align:center;"> 401 79 <span class="sort-indicator" data-sort-indicator="price_79"></span> 402 </th> 403 <th class="sortable" data-sort-field="min_effective_ilvl" style="width:9%;text-align:center;"> 404 æä½ææ <span class="sort-indicator" data-sort-indicator="min_effective_ilvl"></span> 405 </th> 406 <th class="sortable" data-sort-field="premium_82" style="width:9%;text-align:center;"> 407 82溢价 <span class="sort-indicator" data-sort-indicator="premium_82"></span> 408 </th> 409 <th style="width:7%;text-align:center;">åå²</th> 410 <th style="width:7%;text-align:center;">交æ</th> 411 </tr> 412 </thead> 413 <tbody id="base-table-body"></tbody> 414 </table> 415 </div> 416 </div> 417 </div> 418 419 <!-- ä»·æ ¼å岿¨¡ææ¡ --> 420 <div id="basePriceHistoryModal" class="modal-overlay hidden"> 421 <div class="modal-card"> 422 <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:0.75rem;"> 423 <h3 id="baseModalItemName">ä»·æ ¼åå²è¶å¿</h3> 424 <button type="button" class="modal-close" onclick="closeBasePriceHistoryModal()">×</button> 425 </div> 426 <div style="flex:1;min-height:400px;position:relative;">
427 <canvas id="basePriceHistoryChart"></canvas> 428 </div> 429 </div> 430 </div> 431 <!-- 页è - å®å ¨ç± footer.js ç»ä¸ç®¡ç --> 432 <footer id="site-footer"></footer> 433
433<script src="/static/js/footer.js"></script>
433 434 435 <!-- èæ¬ --> 436
436<script src="/js/nav_config.js" onerror="console.error('nav_config.jså 载失败');"></script>
436 437
437<script src="/js/nav_bar.js" onerror="console.error('nav_bar.jså 载失败');"></script>
437 438
438<script src="/js/nav_render.js" onerror="console.error('nav_render.jså 载失败');"></script>
438 439
439<script src="/js/nav_filter.js" onerror="console.error('nav_filter.jså 载失败');"></script>
439 440
440<script src="/js/site_navigation.js"></script>
440 441 <!-- 认è¯èæ¬ç± nav_bar.js ç»ä¸å è½½ --> 442
442<script src="/js/page_helper.js" onerror="console.error('page_helper.jså 载失败');"></script>
442 443 <!-- ç»ä¸å¾æ åæ¾ç¤ºå·¥å · --> 444
444<script src="/js/iconUtils.js" onerror="console.error('iconUtils.jså 载失败');"></script>
444 445
445<script src="/js/assetNameUtils.js"></script>
445 446
446<script src="/js/itemDisplayUtils.js" onerror="console.error('itemDisplayUtils.jså 载失败');"></script>
446 447
447<script src="/js/priceUpdateUtils.js"></script>
447 448
448<script> 449 // ç¡®ä¿é¡µé¢è³å°è½æ¾ç¤ºï¼å³ä½¿èæ¬å¤±è´¥ 450 console.log('poe2_base_price 页é¢èæ¬å¼å§æ§è¡'); 451 452 // å ¨å±é误å¤ç 453 window.addEventListener('error', function(e) { 454 console.error('å ¨å±é误:', e.error, e.message, e.filename, e.lineno); 455 }); 456 457 window.addEventListener('unhandledrejection', function(e) { 458 console.error('æªå¤ççPromiseæç»:', e.reason); 459 }); 460 461document.addEventListener('DOMContentLoaded', function() { 462 console.log('DOMContentLoadedäºä»¶è§¦å'); 463 try { 464 // ææºç«¯èå忢 465 const mobileMenuToggle = document.getElementById('mobile-menu-toggle'); 466 const mobileNavMenu = document.getElementById('mobile-nav-menu'); 467 if (mobileMenuToggle && mobileNavMenu) { 468 mobileMenuToggle.addEventListener('click', function() { 469 mobileNavMenu.classList.toggle('mobile-nav-open'); 470 }); 471 472 // ç¹å»å¯¼èªé¾æ¥åå ³éèå 473 mobileNavMenu.addEventListener('click', function(e) { 474 if (e.target.closest('a')) { 475 mobileNavMenu.classList.remove('mobile-nav-open'); 476 } 477 }); 478 } 479 480} catch (error) { 481 console.error('DOMContentLoadedå¤ç失败:', error); 482 } 483 }); 484 485 // Base Price 页é¢åè½èæ¬ 486 // ä¸ unique åºç±»ä¼¼ç主类å«ä¸ææ å°ï¼åèä»·æ ¼é¡µé¢çæéé¨åï¼ 487 const BASE_MAIN_CATEGORY_NAME_MAP = { 488 'Off-hand': '坿', 489 'Weapons': 'æ¦å¨', 490 'Armour': 'æ¤ç²', 491 'Jewellery': '饰å', 492 'Flasks': 'è¯å', 493 'Jewels': 'ç å®', 494 'Misc': 'ç¢ç/éç©', 495 '': 'æªåç±»' 496 }; 497 window.BASE_MAIN_CATEGORY_NAME_MAP = BASE_MAIN_CATEGORY_NAME_MAP; 498 499 // åç±»å«åç§°æ å° 500 const SUBCATEGORY_NAME_MAP = { 501 'Shields': 'ç¾ç', 502 'Bucklers': 'è½»ç¾', 503 'Quivers': 'ç®è¢', 504 'Foci': 'æ³å¨', 505 'OneHanded': 'åæ', 506 'TwoHanded': 'åæ', 507 'Bows': 'å¼', 508 'Crossbows': 'ååå¼', 509 'Wands': 'éæ', 510 'Sceptres': 'ææ', 511 'Staves': 'é¿æ', 512 'Quarterstaves': 'é¿æ£', 513 'Spears': 'é¿ç', 514 'Flails': 'è¿æ·', 515 'OneHandMaces': 'åæé¤', 516 'TwoHandMaces': 'åæé¤', 517 'Daggers': 'åé¦', 518 'Claws': 'çª', 519 'BodyArmours': 'è¸ç²', 520 'Boots': 'é´å', 521 'Gloves': 'æå¥', 522 'Helmets': '头ç', 523 'Rings': 'ææ', 524 'Amulets': '项é¾', 525 'Belts': 'è °å¸¦', 526 'Life': 'çå½è¯å', 527 'Charms': 'æ¤ç¬¦', 528 'Jewels': 'ç å®', 529 'Weapons': 'æ¦å¨' 530 }; 531 window.SUBCATEGORY_NAME_MAP = SUBCATEGORY_NAME_MAP; 532 533 // 主类å«å¾æ æ å°ï¼ä½¿ç¨ data/poe2/icons/stackable ä¸ç徿 ï¼ä¸åç±»å«ä¸åï¼ 534 const MAIN_CATEGORY_ICON_MAP = { 535 'Armour': 'Adherents_Raiment.png', // æ¤ç² - 使ç¨ä¸åçè¸ç²å¾æ ï¼ä¸BodyArmoursåç±»å«ä¸åï¼ 536 'Jewellery': 'Amber_Amulet.png', // 饰å - 使ç¨ä¸åç项é¾å¾æ ï¼ä¸Amuletsåç±»å«ä¸åï¼ 537 'Off-hand': 'Ancient_Buckler.png', // 坿 - 使ç¨ä¸åçè½»ç¾å¾æ ï¼ä¸Bucklersåç±»å«ä¸åï¼ 538 'Weapons': 'Anvil_Maul.png' // æ¦å¨ - 使ç¨ä¸åçæ¦å¨å¾æ ï¼ä¸TwoHandedåç±»å«ä¸åï¼ 539 }; 540 541 // åç±»å«å¾æ æ å°ï¼ä½¿ç¨ data/poe2/icons/stackable ä¸ç徿 ï¼ 542 const SUBCATEGORY_ICON_MAP = { 543 // æ¤ç²åç±»å« 544 'BodyArmours': 'Abyssal_Cuirass.png', // è¸ç² 545 'Boots': 'Bangled_Sandals.png', // é´å 546 'Gloves': 'Adorned_Gloves.png', // æå¥ 547 'Helmets': 'Archon_Crown.png', // 头ç 548 // 饰ååç±»å« 549 'Rings': 'Amethyst_Ring.png', // ææ 550 'Amulets': 'Gold_Amulet.png', // é¡¹é¾ 551 'Belts': 'Heavy_Belt.png', // è °å¸¦ 552 // 坿åç±»å« 553 'Shields': 'Aegis_Buckler.png', // ç¾ç 554 'Bucklers': 'Aegis_Buckler.png', // è½»ç¾ 555 'Quivers': 'Blunt_Quiver.png', // ç®è¢ 556 'Foci': 'Crystal_Focus.png', // æ³å¨ 557 // æ¦å¨åç±»å« 558 'OneHanded': 'Akoyan_Club.png', // åæ 559 'TwoHanded': 'Anvil_Maul.png', // åæ 560 'Bows': 'Adherent_Bow.png', // å¼ 561 'Crossbows': 'Alloy_Crossbow.png', // ååå¼ 562 'Wands': 'Acrid_Wand.png', // éæ 563 'Sceptres': 'Aromatic_Sceptre.png', // ææ 564 'Staves': 'Aegis_Quarterstaff.png', // é¿æ 565 'Quarterstaves': 'Aegis_Quarterstaff.png', // é¿æ£ 566 'Spears': 'Akoyan_Spear.png', // é¿ç 567 'OneHandMaces': 'Akoyan_Club.png', // åæé¤ 568 'TwoHandMaces': 'Anvil_Maul.png', // åæé¤ 569 'Daggers': 'Akoyan_Club.png', // åé¦ï¼æç¨æ¿ä»£å¾æ ï¼ 570 'Claws': 'Akoyan_Club.png', // çªï¼æç¨æ¿ä»£å¾æ ï¼ 571 'Flails': 'Anvil_Maul.png' // è¿æ·ï¼æç¨æ¿ä»£å¾æ ï¼ 572 }; 573 574 // è·åç±»å«å¾æ è·¯å¾ 575 function getCategoryIconPath(categoryKey, isSubcategory = false) { 576 if (isSubcategory) { 577 const iconFile = SUBCATEGORY_ICON_MAP[categoryKey]; 578 if (iconFile) { 579 return `/data/poe2/icons/stackable/${iconFile}`; 580 } 581 } else { 582 const iconFile = MAIN_CATEGORY_ICON_MAP[categoryKey]; 583 if (iconFile) { 584 return `/data/poe2/icons/stackable/${iconFile}`; 585 } 586 } 587 // é»è®¤å¾æ 588 return '/data/poe2/icons/stackable/Chaos_Orb.png'; 589 } 590 591 // å¿ é¡»ç¨ varï¼æå° windowï¼ï¼poe2_base_price.js ç ladder 渲æè¯»çæ¯ window.* 592 var baseData = null; // å端è¿åç宿´æ°æ® 593 var selectedLevels = new Set(); // å½åéä¸çç©åç级ï¼å¤éï¼ï¼ç©ºè¡¨ç¤ºå ¨é¨ 594 var selectedMainCategory = null; // å½åéä¸ç主类å«ï¼nullè¡¨ç¤ºå ¨é¨ï¼ 595 var selectedSubCategory = null; // å½åéä¸çåç±»å«ï¼nullè¡¨ç¤ºå ¨é¨ï¼ 596 // var expandedCategories = new Set(); // å·²å±å¼çä¸»ç±»å« - 已移é¤å±å¼åè½ï¼åç±»å«å§ç»æ¾ç¤º 597 var currentSortField = 'avg_price'; 598 var currentSortOrder = 'desc'; 599 var basePriceHistoryChart = null; // ä»·æ ¼åå²å¾è¡¨ 600 var divineRate = 0.0; // divine æ±ç 601 var categorySearchTerm = ''; // ç±»å«å æç´¢å ³é®è¯ 602 var minPriceFilter = 10;
602 // æä½ä»·ï¼å½åå±ç¤ºåä½ï¼ï¼æå ilvl æé«ä»·è¿æ»¤ 603 var minSellersFilter = 0; // æå°åå®¶ 604 var basePriceUnit = 'exalted'; 605 var expandedCategories = new Set(); 606 607 function mapCategoryToMain(categoryFull) { 608 if (!categoryFull) return ''; 609 const parts = categoryFull.split('.'); 610 if (!parts.length) return ''; 611 return parts[0]; // ç´æ¥è¿åä¸»ç±»å« 612 } 613 614 function mapCategoryToSub(categoryFull) { 615 if (!categoryFull) return ''; 616 const parts = categoryFull.split('.'); 617 if (parts.length >= 2) { 618 return parts[1]; // è¿ååç±»å« 619 } 620 return ''; 621 } 622 623 function formatCategoryLabel(mainCategory) { 624 const cn = BASE_MAIN_CATEGORY_NAME_MAP[mainCategory] || mainCategory || 'æªåç±»'; 625 if (!mainCategory) return cn; 626 return cn + ` (${mainCategory})`; 627 } 628 629 function buildCategories() { 630 const container = document.getElementById('base-categories'); 631 container.innerHTML = ''; 632 if (!baseData || !baseData.levels) return; 633 634 const allItems = []; 635 Object.values(baseData.levels).forEach(items => { 636 items.forEach(it => allItems.push(it)); 637 }); 638 639 // ç»è®¡ä¸»ç±»å«ååç±»å«çæ°éï¼åèä»·æ ¼é¡µé¢çæéç©åï¼ 640 const mainCategoryCounts = {}; 641 const subCategoryCounts = {}; // key: "main.sub" -> count 642 643 allItems.forEach(item => { 644 // ä¼å ä½¿ç¨æ°æ®åºä¸çmain_categoryåsub_category 645 // å¦ææ²¡æï¼ä»categoryåæ®µè§£æï¼æ ¼å¼ï¼MainCategory.SubCategoryï¼ 646 let mainCat = item.main_category || ''; 647 let subCat = item.sub_category || ''; 648 649 if (!mainCat && item.category) { 650 mainCat = mapCategoryToMain(item.category); 651 } 652 if (!subCat && item.category) { 653 subCat = mapCategoryToSub(item.category); 654 } 655 656 mainCat = mainCat || ''; 657 658 // ç»è®¡ä¸»ç±»å« 659 if (!mainCategoryCounts[mainCat]) mainCategoryCounts[mainCat] = 0; 660 mainCategoryCounts[mainCat]++; 661 662 // ç»è®¡åç±»å« 663 if (subCat) { 664 const key = `${mainCat}.${subCat}`; 665 if (!subCategoryCounts[key]) subCategoryCounts[key] = 0; 666 subCategoryCounts[key]++; 667 } 668 }); 669 670 // "å ¨é¨"æé® 671 const allDiv = document.createElement('div'); 672 allDiv.className = 'sidebar-category'; 673 allDiv.setAttribute('data-category', 'ALL'); 674 allDiv.onclick = () => { 675 selectedMainCategory = null; 676 selectedSubCategory = null; 677 // æ¸ ç©ºæç´¢è¯ 678 categorySearchTerm = ''; 679 const searchInput = document.getElementById('categorySearchInput'); 680 const clearBtn = document.getElementById('clearCategorySearchBtn'); 681 if (searchInput) searchInput.value = ''; 682 if (clearBtn) clearBtn.classList.remove('show'); 683 updateCategoryActiveState(); 684 renderTableBody(); 685 }; 686 allDiv.innerHTML = ` 687 <div class="category-icon"> 688 <i class="fas fa-folder-open text-gray-400"></i> 689 </div> 690 <span class="category-name">å ¨é¨ç±»å«</span> 691 <span class="text-xs text-gray-400" style="margin-left:auto;">${allItems.length}</span> 692 `; 693 container.appendChild(allDiv); 694 695 // åéæ¡ 696 const divider = document.createElement('div'); 697 divider.className = 'category-divider'; 698 container.appendChild(divider); 699 700 // æä¸»ç±»å«åç»æ¾ç¤º
701 Object.keys(mainCategoryCounts).sort().forEach(mainCat => { 702 const mainCount = mainCategoryCounts[mainCat]; 703 const mainKey = mainCat || ''; 704 705 // ä¸»ç±»å«æé® 706 const mainDiv = document.createElement('div'); 707 mainDiv.className = 'sidebar-category'; 708 mainDiv.setAttribute('data-category', `main_${mainKey}`); 709 710 const mainIconPath = getCategoryIconPath(mainKey, false); 711 mainDiv.innerHTML = ` 712 <div class="category-icon"> 713 <img src="${mainIconPath}" alt="${mainKey}" style="width: 20px; height: 20px; object-fit: contain;" 714 onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';"> 715 <i class="fas fa-folder text-gray-400" style="display: none;"></i> 716 </div> 717 <span class="category-name">${formatCategoryLabel(mainKey)}</span> 718 <span class="text-xs text-gray-400" style="margin-left:auto;">${mainCount}</span> 719 `; 720 container.appendChild(mainDiv); 721 722 // å廿´ä¸ªåºåéæ©ç±»å« 723 mainDiv.onclick = (e) => { 724 e.stopPropagation(); 725 // å§ç»éä¸è¯¥ä¸»ç±»å«ï¼åæ¶å次ç¹å»åæ¶éæ©çåè½ï¼ 726 selectedMainCategory = mainKey; 727 selectedSubCategory = null; // éæ©ä¸»ç±»å«æ¶ï¼æ¸ 空åç±»å«éæ© 728 // æ¸ ç©ºæç´¢è¯ 729 categorySearchTerm = ''; 730 const searchInput = document.getElementById('categorySearchInput'); 731 const clearBtn = document.getElementById('clearCategorySearchBtn'); 732 if (searchInput) searchInput.value = ''; 733 if (clearBtn) clearBtn.classList.remove('show'); 734 updateCategoryActiveState(); 735 renderTableBody(); 736 }; 737 738 // å§ç»æ¾ç¤ºåç±»å«ï¼ä¸åéè¦å±å¼/æ¶èµ·ï¼ 739 // è·å该主类å«çææåç±»å« 740 const subCats = {}; 741 Object.keys(subCategoryCounts).forEach(key => { 742 if (key.startsWith(mainKey + '.')) { 743 const subCat = key.substring(mainKey.length + 1); 744 subCats[subCat] = subCategoryCounts[key]; 745 } 746 }); 747 748 Object.keys(subCats).sort().forEach(subCat => { 749 const subCount = subCats[subCat]; 750 const subDiv = document.createElement('div'); 751 subDiv.className = 'sidebar-category subcategory'; // æ·»å subcategoryç±»æ¥æ¾ç¤ºå±çº§å ³ç³» 752 subDiv.setAttribute('data-category', `sub_${mainKey}_${subCat}`); 753 subDiv.onclick = (e) => { 754 e.stopPropagation(); 755 selectedMainCategory = mainKey; 756 selectedSubCategory = subCat; 757 // æ¸ ç©ºæç´¢è¯ 758 categorySearchTerm = ''; 759 const searchInput = document.getElementById('categorySearchInput'); 760 const clearBtn = document.getElementById('clearCategorySearchBtn'); 761 if (searchInput) searchInput.value = ''; 762 if (clearBtn) clearBtn.classList.remove('show'); 763 updateCategoryActiveState(); 764 renderTableBody(); 765 }; 766 const subIconPath = getCategoryIconPath(subCat, true); 767 subDiv.innerHTML = ` 768 <div class="category-icon"> 769 <img src="${subIconPath}" alt="${subCat}" style="width: 20px; height: 20px; object-fit: contain;" 770 onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';"> 771 <i class="fas fa-tag text-gray-400" style="display: none;"></i> 772 </div>
773 <span class="category-name">${SUBCATEGORY_NAME_MAP[subCat] || subCat}</span> 774 <span class="text-xs text-gray-400" style="margin-left:auto;">${subCount}</span> 775 `; 776 container.appendChild(subDiv); 777 }); 778 }); 779 780 updateCategoryActiveState(); 781 } 782 783 function updateCategoryActiveState() { 784 document.querySelectorAll('#base-categories .sidebar-category').forEach(el => { 785 el.classList.remove('active'); 786 const cat = el.getAttribute('data-category'); 787 788 if (cat === 'ALL' && selectedMainCategory === null && selectedSubCategory === null) { 789 el.classList.add('active'); 790 } else if (cat && cat.startsWith('sub_') && selectedSubCategory) { 791 const parts = cat.split('_'); 792 if (parts.length >= 3 && parts[1] === selectedMainCategory && parts[2] === selectedSubCategory) { 793 el.classList.add('active'); 794 } 795 } else if (cat && cat.startsWith('main_') && selectedMainCategory && !selectedSubCategory) { 796 const mainKey = cat.substring(5); 797 if (mainKey === selectedMainCategory) { 798 el.classList.add('active'); 799 } 800 } 801 }); 802 } 803 804 function buildLevelTabs() { 805 const container = document.getElementById('level-tabs'); 806 container.innerHTML = ''; 807 if (!baseData || !baseData.levels) return; 808 809 // "å ¨é¨"æé® 810 const allBtn = document.createElement('button'); 811 allBtn.className = 'level-tab'; 812 allBtn.setAttribute('data-level', 'ALL'); 813 allBtn.textContent = 'å ¨é¨'; 814 allBtn.onclick = () => { 815 selectedLevels.clear(); // 空éåè¡¨ç¤ºå ¨é¨ 816 updateLevelTabActiveState(); 817 renderTableBody(); 818 }; 819 container.appendChild(allBtn); 820 821 const levels = Object.keys(baseData.levels).sort((a, b) => Number(b) - Number(a)); 822 levels.forEach(lvl => { 823 const btn = document.createElement('button'); 824 btn.className = 'level-tab'; 825 btn.setAttribute('data-level', lvl); 826 btn.textContent = `ilvl ${lvl}`; 827 btn.onclick = () => { 828 // å¤éé»è¾ 829 if (selectedLevels.has(lvl)) { 830 selectedLevels.delete(lvl); 831 } else { 832 selectedLevels.add(lvl); 833 } 834 updateLevelTabActiveState(); 835 renderTableBody(); 836 }; 837 container.appendChild(btn); 838 }); 839 840 updateLevelTabActiveState(); 841 } 842 843 function updateLevelTabActiveState() { 844 document.querySelectorAll('#level-tabs .level-tab').forEach(el => { 845 const lvl = el.getAttribute('data-level'); 846 if (lvl === 'ALL') { 847 // "å ¨é¨"æé®ï¼å½ selectedLevels ä¸ºç©ºæ¶æ¿æ´» 848 if (selectedLevels.size === 0) { 849 el.classList.add('active'); 850 } else { 851 el.classList.remove('active'); 852 } 853 } else { 854 // æ®é ilvl æé® 855 if (selectedLevels.has(lvl)) { 856 el.classList.add('active'); 857 } else { 858 el.classList.remove('active'); 859 } 860 } 861 }); 862 } 863 864 function getCurrentItems() { 865 if (!baseData || !baseData.levels) return []; 866 const allItems = []; 867 868 // 仿æéä¸çç级ä¸è·åç©åï¼å¦æ selectedLevels 为空ï¼åè·åææççº§ï¼ 869 if (selectedLevels.size === 0) { 870 // éæ©"å ¨é¨"ï¼è·åææç级
871 Object.values(baseData.levels).forEach(items => { 872 allItems.push(...items); 873 }); 874 } else { 875 // ä»éä¸çç级ä¸è·å 876 selectedLevels.forEach(level => { 877 const items = baseData.levels[String(level)] || []; 878 allItems.push(...items); 879 }); 880 } 881 882 return allItems.filter(item => { 883 // ç±»å«è¿æ»¤ 884 if (selectedMainCategory !== null || selectedSubCategory !== null) { 885 // è·å主类å«ååç±»å«ï¼ä¼å ä½¿ç¨æ°æ®åºå段ï¼å¦åä»categoryè§£æï¼ 886 let mainCat = item.main_category || ''; 887 let subCat = item.sub_category || ''; 888 889 if (!mainCat && item.category) { 890 mainCat = mapCategoryToMain(item.category); 891 } 892 if (!subCat && item.category) { 893 subCat = mapCategoryToSub(item.category); 894 } 895 896 if (selectedSubCategory) { 897 // éæ©äºåç±»å«ï¼å¿ 须忶å¹é 主类å«ååç±»å« 898 if (mainCat !== selectedMainCategory || subCat !== selectedSubCategory) { 899 return false; 900 } 901 } else if (selectedMainCategory) { 902 // åªéæ©äºä¸»ç±»å« 903 if (mainCat !== selectedMainCategory) { 904 return false; 905 } 906 } 907 } 908 909 // æä½ä»·è¿æ»¤ï¼åä½ï¼eï¼ 910 if (minPriceFilter > 0) { 911 const priceValue = item.avg_price || 0; 912 if (priceValue < minPriceFilter) { 913 return false; 914 } 915 } 916 917 // æå°åå®¶æ°è¿æ»¤ 918 if (minSellersFilter > 0) { 919 const sellerCount = item.total_search_results || 0; 920 if (sellerCount < minSellersFilter) { 921 return false; 922 } 923 } 924 925 // ç±»å«å æç´¢è¿æ»¤ 926 if (categorySearchTerm) { 927 const nameTW = (item.name_tw || '').toLowerCase(); 928 const nameEN = (item.name || '').toLowerCase(); 929 const searchTerm = categorySearchTerm.toLowerCase(); 930 if (!nameTW.includes(searchTerm) && !nameEN.includes(searchTerm)) { 931 return false; 932 } 933 } 934 935 return true; 936 }); 937 } 938 939 function renderTableBody() { 940 const tbody = document.getElementById('base-table-body'); 941 if (!tbody) { 942 console.error('æ¾ä¸å°è¡¨æ ¼ä½å ç´ : base-table-body'); 943 return; 944 } 945 tbody.innerHTML = ''; 946 947 let items = getCurrentItems().slice(); 948 949 // æåº 950 if (currentSortField) { 951 items.sort((a, b) => { 952 let va = a[currentSortField]; 953 let vb = b[currentSortField]; 954 955 // å¤ç null/undefined å¼ï¼æåºæ¶æ¾å¨æå 956 if (va === null || va === undefined) va = currentSortOrder === 'asc' ? Infinity : -Infinity; 957 if (vb === null || vb === undefined) vb = currentSortOrder === 'asc' ? Infinity : -Infinity; 958 959 // å¯¹äºæ°ååæ®µï¼ç¡®ä¿ææ°åæ¯è¾ 960 if (currentSortField === 'total_search_results' || currentSortField === 'avg_price' || currentSortField === 'ilvl') { 961 va = Number(va) || 0; 962 vb = Number(vb) || 0; 963 } 964 965 if (va === vb) return 0; 966 if (currentSortOrder === 'asc') { 967 return va > vb ? 1 : -1; 968 } 969 return va < vb ? 1 : -1; 970 }); 971 } 972 973 // ç§»é¤"å½åç级"ä¿¡æ¯æ¾ç¤º 974 const itemCountEl = document.getElementById('itemCount'); 975 if (itemCountEl) { 976 itemCountEl.textContent = items.length; 977 } 978
979 items.forEach(item => { 980 const row = document.createElement('tr'); 981 row.className = 'border-b border-gray-700 hover:bg-gray-800'; 982 983 // è·ååç±»åæ¾ç¤ºåç§°ï¼åèä»·æ ¼é¡µé¢çæéç©åï¼ 984 let subCategoryDisplay = ''; 985 // ä¼å ä½¿ç¨æ°æ®åºä¸çsub_category 986 if (item.sub_category) { 987 subCategoryDisplay = SUBCATEGORY_NAME_MAP[item.sub_category] || item.sub_category; 988 } 989 // å¦ææ²¡æsub_categoryï¼å°è¯ä»categoryåæ®µè§£æ 990 else if (item.category) { 991 const subCat = mapCategoryToSub(item.category); 992 if (subCat) { 993 subCategoryDisplay = SUBCATEGORY_NAME_MAP[subCat] || subCat; 994 } else { 995 // 妿category䏿²¡æåç±»å«ï¼æ ¹æ®ä¸»ç±»å«æ¾ç¤º 996 const mainCat = item.main_category || mapCategoryToMain(item.category); 997 if (mainCat === 'Weapons') { 998 subCategoryDisplay = 'æ¦å¨'; 999 } else if (mainCat) { 1000 subCategoryDisplay = BASE_MAIN_CATEGORY_NAME_MAP[mainCat] || mainCat; 1001 } 1002 } 1003 } 1004 // æååéï¼ä½¿ç¨main_category 1005 else if (item.main_category) { 1006 if (item.main_category === 'Weapons') { 1007 subCategoryDisplay = 'æ¦å¨'; 1008 } else { 1009 subCategoryDisplay = BASE_MAIN_CATEGORY_NAME_MAP[item.main_category] || item.main_category; 1010 } 1011 } 1012 1013 // è´ä¹°æé®ï¼åèæéç©åçå®ç°ï¼ 1014 const buyButton = document.createElement('button'); 1015 buyButton.className = 'text-blue-400 hover:text-blue-300 text-sm px-2 py-1 rounded hover:bg-gray-700'; 1016 buyButton.textContent = 'è´ä¹°'; 1017 buyButton.addEventListener('click', () => { 1018 // è·åèèµåç§° 1019 const league = baseData?.league || 'Forbidden Rites'; 1020 const leagueLower = league.toLowerCase(); 1021 1022 // è·åç©ååç§°ï¼baseç©å使ç¨è±æåç§°ï¼ 1023 const itemName = item.name || ''; 1024 1025 // æå»º POE2 äº¤ææ¥è¯¢åæ°ï¼åèæåèæ¬ä¸çæ¡ä»¶ï¼ 1026 // baseç©åä½¿ç¨ type è䏿¯ nameï¼å¹¶ä¸éè¦è®¾ç½® rarity=normal, ilvlçæ¡ä»¶ 1027 const tradeQuery = { 1028 query: { 1029 status: { option: 'securable' }, // securable = buyoutçé 1030 type: itemName.trim(), // baseç©å使ç¨typeåæ®µ 1031 stats: [{ type: 'and', filters: [] }], 1032 filters: { 1033 type_filters: { 1034 filters: { 1035 rarity: { 1036 option: 'normal' // æ®éåè´¨ 1037 }, 1038 ilvl: { 1039 min: item.ilvl || 79 // ç©åç级æå°å¼ 1040 } 1041 } 1042 }, 1043 misc_filters: { 1044 filters: { 1045 corrupted: { 1046 option: 'false' // æªè å 1047 } 1048 } 1049 } 1050 } 1051 }, 1052 sort: { price: 'asc' } 1053 }; 1054 1055 // POE2 交æé¾æ¥æ ¼å¼ï¼https://www.pathofexile.com/trade2/search/{league}?q={query} 1056 const encodedLeague = encodeURIComponent(leagueLower); 1057 const jsonString = JSON.stringify(tradeQuery); 1058 const encodedQuery = encodeURIComponent(jsonString); 1059 const tradeUrl = `https://www.pathofexile.com/trade2/search/${encodedLeague}?q=${encodedQuery}`; 1060 window.open(tradeUrl, '_blank'); 1061 }); 1062 1063 // åå²è¶å¿æé® 1064 const historyButton = document.createElement('button'); 1065 historyButton.className = 'text-blue-400 hover:text-blue-300 text-sm px-2 py-1 rounded hover:bg-gray-700'; 1066 historyButton.textContent = 'æ¥çè¶å¿'; 1067 historyButton.onclick = () => showBasePriceHistory(item.name, item.ilvl); 1068 1069 // æ ¼å¼åä»·æ ¼ï¼ä½¿ç¨å¾æ æ¾ç¤ºï¼å¤§äºçäº1dç¨divine徿 ï¼å°äºç¨exalted徿 1070 let priceDisplay = ''; 1071 let priceValue = item.avg_price || 0; 1072 1073 if (divineRate > 0 && priceValue >= divineRate) {
1074 // 大äºçäº1dï¼æ¾ç¤ºä¸ºdivine徿 1075 const divineValue = priceValue / divineRate; 1076 const divineIconHtml = CurrencyUtils.getCurrencyIcon('divine', { size: 20, style: 'margin-left: 4px;' }); 1077 priceDisplay = `<span class="text-yellow-400 font-medium">${divineValue.toFixed(2)}</span>${divineIconHtml}`; 1078 } else { 1079 // å°äº1dï¼æ¾ç¤ºä¸ºexalted徿 1080 const exaltedIconHtml = CurrencyUtils.getCurrencyIcon('exalted', { size: 20, style: 'margin-left: 4px;' }); 1081 priceDisplay = `<span class="text-yellow-400 font-medium">${priceValue.toFixed(2)}</span>${exaltedIconHtml}`; 1082 } 1083 1084 // è·å徿 è·¯å¾ï¼ä¼å 使ç¨APIè¿åçicon_pathï¼ 1085 let iconPath = item.icon_path || '/data/poe2/icons/stackable/Chaos_Orb.png'; 1086 // ç¡®ä¿ä½¿ç¨.pngæ©å±å 1087 if (iconPath.endsWith('.webp')) { 1088 iconPath = iconPath.replace('.webp', '.png'); 1089 } 1090 1091 // ç©ååç§°ï¼ä¸æåè±æåä¸ä¸ä¸¤è¡ï¼ä»CSV第ä¸åName_TWè·åï¼ï¼å¸¦å¾æ 1092 const nameTW = item.name_tw || ''; 1093 const nameText = nameTW 1094 ? `<div style="line-height: 1.4;"><div class="text-white">${nameTW}</div><div style="font-size: 0.75rem; color: #9ca3af;">${item.name}</div></div>` 1095 : `<div class="text-white">${item.name}</div>`; 1096 1097 const nameDisplay = ` 1098 <div class="flex items-center"> 1099 <div class="icon-container mr-3" style="width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; flex-shrink: 0;"> 1100 <img src="${iconPath}" alt="${item.name}" 1101 style="max-width: 32px; max-height: 32px; width: auto; height: auto; object-fit: contain; display: block;" 1102 onerror="this.src='/data/poe2/icons/stackable/Chaos_Orb.png';"> 1103 </div> 1104 <div class="flex-1"> 1105 ${nameText} 1106 </div> 1107 </div> 1108 `; 1109 1110 // åå®¶æ°éæ¾ç¤º 1111 const sellerCount = item.total_search_results !== null && item.total_search_results !== undefined 1112 ? item.total_search_results 1113 : '-'; 1114 1115 row.innerHTML = ` 1116 <td class="px-4 py-2"> 1117 ${nameDisplay} 1118 </td> 1119 <td class="px-4 py-2 text-center"> 1120 ${item.ilvl || '-'} 1121 </td> 1122 <td class="px-4 py-2"> 1123 ${subCategoryDisplay || '-'} 1124 </td> 1125 <td class="px-4 py-2 text-right"> 1126 ${priceDisplay} 1127 </td> 1128 <td class="px-4 py-2 text-center"> 1129 ${sellerCount} 1130 </td> 1131 <td class="px-4 py-2 text-center"> 1132 </td> 1133 <td class="px-4 py-2 text-center"> 1134 </td> 1135 `; 1136 1137 // 尿鮿·»å å°å¯¹åºçåå æ ¼ 1138 const cells = row.querySelectorAll('td'); 1139 const historyCell = cells[cells.length - 2]; // åæ°ç¬¬äºä¸ªæ¯åå²è¶å¿å 1140 const tradeCell = cells[cells.length - 1]; // æåä¸ä¸ªæ¯äº¤æå 1141 historyCell.appendChild(historyButton); 1142 tradeCell.appendChild(buyButton); 1143 1144 tbody.appendChild(row); 1145 }); 1146 1147 // æ´æ°æåºæç¤º 1148 document.querySelectorAll('.sort-indicator').forEach(el => { 1149 const field = el.getAttribute('data-sort-indicator'); 1150 if (field === currentSortField) { 1151 el.textContent = currentSortOrder === 'asc' ? 'â²' : 'â¼'; 1152 } else { 1153 el.textContent = ''; 1154 } 1155 }); 1156 } 1157 1158 async function loadBaseData() { 1159 const CACHE_KEY = 'poe2_base_items_v7_rates'; 1160 const CACHE_TTL_MS = 180000; 1161 1162 function applyBaseData(data) { 1163 baseData = data; 1164 window.baseData = data; 1165 if (baseData && baseData.divine_rate) { 1166 divineRate = baseData.divine_rate; 1167 window.divineRate = divineRate; 1168 } 1169 updateBaseMetaBar(baseData); 1170 buildCategories(); 1171 if (typeof window.setBasePriceUnit === 'function') { 1172 window.setBasePriceUnit(window.basePriceUnit || basePriceUnit || 'exalted'); 1173 } else { 1174 buildLevelTabs(); 1175 renderTableBody(); 1176 } 1177 } 1178 1179 function updateBaseMetaBar(data) { 1180 const leagueEl = document.getElementById('baseLeagueLabel'); 1181 const updatedWrap = document.getElementById('baseUpdatedWrap'); 1182 if (leagueEl) { 1183 leagueEl.textContent = (data && data.league) ? data.league : 'â'; 1184 } 1185 // c/eãd/eï¼ä¸å¾ã1 è¯¥å¸ = N exaltedãï¼ä¸ API rates / åä»·æ¢ç®ç¸åï¼ 1186 const rates = (data && data.rates) || {}; 1187 const ce = Number(rates.chaos || (data && data.chaos_rate) || 0) || 0; 1188 const de = Number(rates.divine || (data && data.divine_rate) || 0) || 0; 1189 const ceEl = document.getElementById('baseCeRatePill'); 1190 const deEl = document.getElementById('baseDeRatePill'); 1191 if (ceEl) { 1192 ceEl.innerHTML = ce > 0 1193 ? 'c/eï¼<strong>' + ce.toFixed(2) + '</strong><span class="rate-sub">ï¼1c=' + ce.toFixed(2) + 'eï¼</span>' 1194 : 'c/eï¼â'; 1195 } 1196 if (deEl) { 1197 deEl.innerHTML = de > 0 1198 ? 'd/eï¼<strong>' + de.toFixed(1) + '</strong><span class="rate-sub">ï¼1d=' + de.toFixed(1) + 'eï¼</span>' 1199 : 'd/eï¼â'; 1200 } 1201 if (typeof window.Poe2BaseLadder !== 'undefined' && window.Poe2BaseLadder.syncRatesHint) {
1202 window.Poe2BaseLadder.syncRatesHint(); 1203 } 1204 if (!updatedWrap) return; 1205 const at = data && (data.updated_at || data.db_mtime); 1206 if (window.PriceUpdate && at != null) { 1207 updatedWrap.innerHTML = PriceUpdate.renderPageBadge( 1208 [{ label: 'Base æ°æ®åº', at: at }], 1209 { label: 'æ°æ®æ´æ°' } 1210 ); 1211 } else if (at != null) { 1212 updatedWrap.textContent = 'æ°æ®æ´æ°ï¼' + String(at); 1213 } else { 1214 updatedWrap.textContent = 'æ°æ®æ´æ°ï¼â'; 1215 } 1216 } 1217 1218 try { 1219 const cachedRaw = sessionStorage.getItem(CACHE_KEY); 1220 if (cachedRaw) { 1221 try { 1222 const cached = JSON.parse(cachedRaw); 1223 if (cached.data && Date.now() - cached.ts < CACHE_TTL_MS) { 1224 applyBaseData(cached.data); 1225 return; 1226 } 1227 } catch (e) { 1228 sessionStorage.removeItem(CACHE_KEY); 1229 } 1230 } 1231 1232 const resp = await axios.get('/api/v1/prices/base-items'); 1233 if (typeof resp.data === 'string') { 1234 try { 1235 baseData = JSON.parse(resp.data); 1236 } catch (e) { 1237 console.error('base_price JSON.parse 失败:', e, resp.data); 1238 baseData = null; 1239 } 1240 } else { 1241 baseData = resp.data; 1242 } 1243 1244 if (baseData) { 1245 sessionStorage.setItem(CACHE_KEY, JSON.stringify({ 1246 ts: Date.now(), 1247 data: baseData 1248 })); 1249 applyBaseData(baseData); 1250 } 1251 } catch (error) { 1252 console.error('å è½½ base items æ°æ®å¤±è´¥:', error); 1253 } 1254 } 1255 1256 function setupSortHandlers() { 1257 document.querySelectorAll('.sortable').forEach(el => { 1258 el.addEventListener('click', () => { 1259 const field = el.getAttribute('data-sort-field'); 1260 if (!field) return; 1261 console.log('poe2_base_price ç¹å»è¡¨å¤´æåº:', field); 1262 if (currentSortField === field) { 1263 currentSortOrder = currentSortOrder === 'asc' ? 'desc' : 'asc'; 1264 } else { 1265 currentSortField = field; 1266 currentSortOrder = field === 'name' ? 'asc' : 'desc'; 1267 } 1268 renderTableBody(); 1269 }); 1270 }); 1271 } 1272 1273 // æ¾ç¤ºåºç¡ç©åä»·æ ¼åå² 1274 async function showBasePriceHistory(itemName, ilvl) { 1275 try { 1276 const params = new URLSearchParams({ 1277 league_name: baseData?.league || 'Forbidden Rites', 1278 ilvl: ilvl, 1279 days: 30 1280 }); 1281 1282 const response = await axios.get(`/api/v1/prices/base-items/history/${encodeURIComponent(itemName)}?${params}`); 1283 1284 console.log('ð [base_price] åå§response:', response); 1285 console.log('ð [base_price] response.dataç±»å:', typeof response.data); 1286 console.log('ð [base_price] response.data:', response.data); 1287 1288 // å¤çååºæ°æ®ï¼å ¼å®¹å符串åå¯¹è±¡æ ¼å¼ï¼ 1289 let historyData = response.data; 1290 if (typeof historyData === 'string') { 1291 try { 1292 historyData = JSON.parse(historyData); 1293 console.log('ð [base_price] è§£æåçhistoryData:', historyData); 1294 } catch (e) { 1295 console.error('â [base_price] JSONè§£æå¤±è´¥:', e, historyData); 1296 alert(`è§£æå岿°æ®å¤±è´¥: ${e.message}`); 1297 return; 1298 } 1299 } 1300 1301 console.log('ð [base_price] æç»historyData:', historyData); 1302 console.log('ð [base_price] historyDataç±»å:', typeof historyData); 1303 console.log('ð [base_price] historyDataçææé®:', Object.keys(historyData || {})); 1304 console.log('ð [base_price] historyåæ®µ:', historyData.history); 1305 console.log('ð [base_price] historyæ°ç»é¿åº¦:', historyData.history ? historyData.history.length : 0); 1306 console.log('ð [base_price] historyæ°ç»å 容:', historyData.history); 1307 1308 if (!historyData || !historyData.history || historyData.history.length === 0) { 1309 console.warn('â ï¸ [base_price] å岿°æ®ä¸ºç©
1309º:', historyData); 1310 alert(`ç©å "${itemName}" ææ å岿°æ®`); 1311 return; 1312 } 1313 1314 // æ¾ç¤ºæ¨¡ææ¡ 1315 const modal = document.getElementById('basePriceHistoryModal'); 1316 const modalItemName = document.getElementById('baseModalItemName'); 1317 if (!modal) { 1318 console.error('â æ¾ä¸å°æ¨¡ææ¡å ç´ : basePriceHistoryModal'); 1319 alert('æ æ³æ¾ç¤ºåå²è¶å¿ï¼æ¨¡ææ¡å ç´ ä¸åå¨'); 1320 return; 1321 } 1322 1323 if (modalItemName) { 1324 modalItemName.textContent = `${itemName} (ilvl ${ilvl}) - ä»·æ ¼åå²è¶å¿`; 1325 } 1326 1327 // ç§»é¤ hidden 类并æ¾ç¤ºæ¨¡ææ¡ 1328 modal.classList.remove('hidden'); 1329 modal.style.display = 'flex'; 1330 1331 // æè´§å¸åç»æ°æ® 1332 const dataByCurrency = {};
1333 historyData.history.forEach(point => { 1334 console.log('ð [base_price] å¤çå岿°æ®ç¹:', point); 1335 const currency = point.currency || 'exalted'; 1336 if (!dataByCurrency[currency]) { 1337 dataByCurrency[currency] = []; 1338 } 1339 dataByCurrency[currency].push(point); 1340 }); 1341 console.log('ð [base_price] æè´§å¸åç»åçæ°æ®:', dataByCurrency); 1342 1343 // åå¤å¾è¡¨æ°æ® 1344 const labels = []; 1345 const datasets = []; 1346 1347 // ææ¶é´æåº 1348 const sortedHistory = [...historyData.history].sort((a, b) => a.time - b.time); 1349 1350 // çææ¶é´æ ç¾ 1351 sortedHistory.forEach(point => { 1352 const date = new Date(point.time * 1000); 1353 const label = date.toLocaleString('zh-CN', { 1354 month: 'short', 1355 day: 'numeric', 1356 hour: '2-digit', 1357 minute: '2-digit' 1358 }); 1359 if (!labels.includes(label)) { 1360 labels.push(label); 1361 } 1362 }); 1363 1364 // 为æ¯ç§è´§å¸åå»ºæ°æ®é 1365 const colors = [ 1366 { border: 'rgb(59, 130, 246)', fill: 'rgba(59, 130, 246, 0.1)' }, // blue 1367 { border: 'rgb(34, 197, 94)', fill: 'rgba(34, 197, 94, 0.1)' }, // green 1368 { border: 'rgb(251, 146, 60)', fill: 'rgba(251, 146, 60, 0.1)' }, // orange 1369 { border: 'rgb(168, 85, 247)', fill: 'rgba(168, 85, 247, 0.1)' } // purple 1370 ]; 1371 1372 let colorIndex = 0;
1373 Object.keys(dataByCurrency).forEach(currency => { 1374 const currencyData = dataByCurrency[currency].sort((a, b) => a.time - b.time); 1375 const prices = []; 1376 const currencyLabels = []; 1377 1378 currencyData.forEach(point => { 1379 console.log('ð [base_price] å¤çè´§å¸æ°æ®ç¹:', point, 'price_value:', point.price_value); 1380 const date = new Date(point.time * 1000); 1381 const label = date.toLocaleString('zh-CN', { 1382 month: 'short', 1383 day: 'numeric', 1384 hour: '2-digit', 1385 minute: '2-digit' 1386 }); 1387 currencyLabels.push(label); 1388 // ç¡®ä¿ price_value ä¸ä¸º null æ undefined 1389 const priceValue = point.price_value !== null && point.price_value !== undefined ? point.price_value : 0; 1390 prices.push(priceValue); 1391 }); 1392 1393 // å°ä»·æ ¼æ°æ®æ å°å°ä¸»æ ç¾æ°ç» 1394 const mappedPrices = labels.map(label => { 1395 const index = currencyLabels.indexOf(label); 1396 return index >= 0 ? prices[index] : null; 1397 }); 1398 1399 const color = colors[colorIndex % colors.length]; 1400 datasets.push({ 1401 label: currency === 'exalted' ? 'Exalted' : currency === 'divine' ? 'Divine' : currency === 'chaos' ? 'Chaos' : currency, 1402 data: mappedPrices, 1403 borderColor: color.border, 1404 backgroundColor: color.fill, 1405 tension: 0.1, 1406 fill: true, 1407 spanGaps: true 1408 }); 1409 colorIndex++; 1410 }); 1411 1412 // 鿝æ§å¾è¡¨ 1413 if (basePriceHistoryChart) { 1414 basePriceHistoryChart.destroy(); 1415 } 1416 1417 // å建æ°å¾è¡¨ 1418 const ctx = document.getElementById('basePriceHistoryChart'); 1419 if (!ctx) { 1420 console.error('â æ¾ä¸å°å¾è¡¨canvaså ç´ : basePriceHistoryChart'); 1421 alert('æ æ³å建å¾è¡¨ï¼canvaså ç´ ä¸åå¨'); 1422 return; 1423 } 1424 1425 // æ£æ¥ Chart æ¯å¦å·²å è½½ 1426 if (typeof Chart === 'undefined') { 1427 console.error('â Chart.js æªå è½½'); 1428 alert('æ æ³å建å¾è¡¨ï¼Chart.js åºæªå è½½ï¼è¯·å·æ°é¡µé¢éè¯'); 1429 return; 1430 } 1431 1432 basePriceHistoryChart = new Chart(ctx, { 1433 type: 'line', 1434 data: { 1435 labels: labels, 1436 datasets: datasets 1437 }, 1438 options: { 1439 responsive: true, 1440 maintainAspectRatio: false, 1441 plugins: { 1442 legend: { 1443 display: true, 1444 labels: { 1445 color: '#d1d5db' 1446 } 1447 }, 1448 tooltip: { 1449 mode: 'index', 1450 intersect: false, 1451 callbacks: { 1452 label: function(context) { 1453 let label = context.dataset.label || ''; 1454 if (label) { 1455 label += ': '; 1456 } 1457 if (context.parsed.y !== null) { 1458 label += context.parsed.y.toFixed(2); 1459 } else { 1460 label += 'æ æ°æ®'; 1461 } 1462 return label; 1463 } 1464 } 1465 } 1466 }, 1467 scales: { 1468 x: { 1469 ticks: { 1470 color: '#9ca3af', 1471 maxRotation: 45, 1472 minRotation: 45 1473 }, 1474 grid: { 1475 color: '#374151' 1476 } 1477 }, 1478 y: { 1479 ticks: { 1480 color: '#9ca3af' 1481 }, 1482 grid: { 1483 color: '#374151' 1484 }, 1485 title: { 1486 display: true, 1487 text: 'ä»·æ ¼', 1488 color: '#9ca3af' 1489 } 1490 } 1491 } 1492 } 1493 }); 1494 } catch (error) { 1495 console.error('â æ¾ç¤ºä»·æ ¼åå²å¤±è´¥:', error); 1496 alert('æ¾ç¤ºä»·æ ¼åå²å¤±è´¥: ' + error.message); 1497 } 1498 } 1499 1500 // å ³éä»·æ ¼å岿¨¡ææ¡ 1501 function closeBasePriceHistoryModal() { 1502 const modal = document.getElementById('basePriceHistoryModal'); 1503 if (modal) { 1504 modal.style.display = 'none'; 1505 modal.classList.add('hidden'); 1506 } 1507 if (basePriceHistoryChart) { 1508 basePriceHistoryChart.destroy(); 1509 basePriceHistoryChart = null; 1510 } 1511 } 1512 1513 // åå§åè¿æ»¤å¨äºä»¶çå¬ 1514 function setupFilters() { 1515 const minPriceInput = document.getElementById('filter-min-price'); 1516 const minSellersInput = document.getElementById('filter-min-sellers'); 1517 const clearFiltersBtn = document.getElementById('clear-filters-btn'); 1518 1519 // 设置é»è®¤å¼ 1520 if (minPriceInput) { 1521 minPriceInput.value = minPriceFilter; // é»è®¤10e 1522 minPriceInput.addEventListener('input', (e) => { 1523 const value = parseFloat(e.target.value) || 0; 1524 minPriceFilter = value; 1525 renderTableBody(); 1526 }); 1527 } 1528 1529 if (minSellersInput) { 1530 minSellersInput.value = minSellersFilter; 1531 minSellersInput.addEventListener('input', (e) => { 1532 const value = parseInt(e.target.value) || 0; 1533 minSellersFilter = value; 1534 renderTableBody(); 1535 }); 1536 } 1537 1538 if (clearFiltersBtn) { 1539 clearFiltersBtn.addEventListener('click', () => { 1540 minPriceFilter = 0; 1541 minSellersFilter = 0; 1542 if (minPriceInput) minPriceInput.value = ''; 1543 if (minSellersInput) minSellersInput.value = ''; 1544 renderTableBody(); 1545 }); 1546 } 1547 1548 // ç±»å«å æç´¢æ¡ 1549 const categorySearchInput = document.getElementById('categorySearchInput'); 1550 const clearCategorySearchBtn = document.getElementById('clearCategorySearchBtn'); 1551 1552 if (categorySearchInput) { 1553 categorySearchInput.addEventListener('input', (e) => { 1554 categorySearchTerm = e.target.value.trim(); 1555 renderTableBody(); 1556 1557 // æ¾ç¤º/éèæ¸ é¤æé® 1558 if (clearCategorySearchBtn) { 1559 if (e.target.value.length > 0) { 1560 clearCategorySearchBtn.classList.add('show'); 1561 } else { 1562 clearCategorySearchBtn.classList.remove('show'); 1563 } 1564 } 1565 }); 1566 } 1567 1568 if (clearCategorySearchBtn) { 1569 clearCategorySearchBtn.addEventListener('click', () => { 1570 if (categorySearchInput) { 1571 categorySearchInput.value = ''; 1572 categorySearchTerm = ''; 1573 clearCategorySearchBtn.classList.remove('show'); 1574 renderTableBody(); 1575 } 1576 }); 1577 } 1578 } 1579 1580 // çæè¿æ»¤ä»£ç ï¼åºäºå½åçéåçç»æï¼ 1581 const BASE_FILTER_MARKER_START = '# ====以䏿¯æ£±éæ¨èçåºåºè¿æ»¤ä»£ç ======'; 1582 const BASE_FILTER_MARKER_END = '# ====以䏿¯æ£±éæ¨èçåºåºè¿æ»¤ä»£ç ï¼å¦æ´æ°è¯·å é¤ä¸è¿°======'; 1583 1584 function generateFilterCode() { 1585 const minPriceDisplay = parseFloat(document.getElementById('filter-min-price').value) || 0; 1586 const minSellers = parseInt(document.getElementById('filter-min-sellers').value, 10) || 0; 1587 // è¿æ»¤ä»£ç éå¼ä¸å¾ç¨ exaltedï¼ä¸æ¸¸æ filter / qualification ä¸è´ï¼ 1588 const minPrice = 1589 window.Poe2BaseLadder && typeof window.Poe2BaseLadder.displayToExalted === 'function' 1590 ? window.Poe2BaseLadder.displayToExalted(minPriceDisplay) 1591 : minPriceDisplay; 1592
1593 // å¯ä¸äºå®æºï¼API.qualified_setsï¼S79âS80âS81ï¼ï¼ç¦æ¢ç¨ levels å彿¡¶ 1594 const contract = (baseData && baseData.prune_contract) || {}; 1595 if (contract.ok === false) { 1596 alert('åªæå¥çº¦ç ´åï¼æç»çæè¿æ»¤ä»£ç ï¼\n' + (contract.error || '')); 1597 return; 1598 } 1599 let qualified = (baseData && baseData.qualified_sets) || null; 1600 if (!qualified || typeof qualified !== 'object') { 1601 alert('ç¼ºå° qualified_setsï¼è¯·å·æ°é¡µé¢ï¼ï¼æç»ç¨ levels åééç®'); 1602 return; 1603 } 1604 1605 // å¯éåå®¶é¨ï¼ä¿æé¾å å«ï¼ä» 81 å䏿¶ç´§ï¼ 1606 const ladderByName = {}; 1607 ((baseData && baseData.ladder) || []).forEach(function (it) { 1608 if (it && it.name) ladderByName[it.name] = it; 1609 }); 1610 function sellersOk(lv, name) { 1611 if (minSellers <= 0) return true; 1612 const it = ladderByName[name]; 1613 const t = it && it.total_search_results ? it.total_search_results[String(lv)] : 0; 1614 return Number(t || 0) >= minSellers; 1615 } 1616 function priceOk(lv, name) { 1617 const it = ladderByName[name];
1618 const p = it && it.prices ? it.prices[String(lv)] : null; 1619 if (p == null) return false; 1620 return Number(p) >= minPrice; 1621 } 1622 1623 let s81 = (qualified['81'] || []).filter(function (n) { 1624 return sellersOk(81, n) && priceOk(81, n); 1625 }); 1626 const s81set = new Set(s81); 1627 let s80 = (qualified['80'] || []).filter(function (n) { 1628 return s81set.has(n) && sellersOk(80, n) && priceOk(80, n); 1629 }); 1630 const s80set = new Set(s80); 1631 let s79 = (qualified['79'] || []).filter(function (n) { 1632 return s80set.has(n) && sellersOk(79, n) && priceOk(79, n); 1633 }); 1634 let s82 = (qualified['82'] || []).filter(function (n) { 1635 return sellersOk(82, n) && priceOk(82, n); 1636 }); 1637 1638 if (s79.length > s80.length || s80.length > s81.length) { 1639 alert('åªæå¥çº¦ç ´åï¼|79|â¤|80|â¤|81| 䏿ç«ï¼æç»çæè¿æ»¤ä»£ç '); 1640 return; 1641 } 1642 1643 const itemsByLevel = { 1644 82: s82, 1645 81: s81, 1646 80: s80, 1647 79: s79, 1648 }; 1649 1650 if (!s82.length && !s81.length && !s80.length && !s79.length) { 1651 alert('没æç¬¦åæ¡ä»¶çç©åï¼æ æ³çæè¿æ»¤ä»£ç '); 1652 return; 1653 } 1654 1655 // çæè¿æ»¤ä»£ç 1656 const filterCodeParts = []; 1657 const generatedAt = new Date().toLocaleString('zh-CN', { hour12: false }); 1658 filterCodeParts.push(BASE_FILTER_MARKER_START); 1659 filterCodeParts.push(`# çææ¶é´: ${generatedAt} æä½ä»·>=${minPrice}e æå°åå®¶>=${minSellers}`); 1660 filterCodeParts.push('# å¥çº¦: ç´æ¥æ¶è´¹ qualified_*ï¼S79âS80âS81ï¼ç¦æ¢åæ¡£ç¬ç«çå ¨é'); 1661 filterCodeParts.push(`# æ°é: 81=${s81.length} 80=${s80.length} 79=${s79.length} 82=${s82.length}`); 1662 filterCodeParts.push(''); 1663 const levels = [82, 81, 80, 79]; 1664 1665 for (const ilvl of levels) { 1666 const itemNames = itemsByLevel[ilvl]; 1667 if (!itemNames || itemNames.length === 0) { 1668 continue; 1669 } 1670 1671 const uniqueNames = [...new Set(itemNames)].sort(); 1672 if (uniqueNames.length === 0) { 1673 continue; 1674 } 1675 1676 filterCodeParts.push(`# ========== ilvl ${ilvl} (${uniqueNames.length}) ==========`); 1677 filterCodeParts.push(`Show # $type->value material> average_price->${Math.floor(minPrice)}`); 1678 filterCodeParts.push(' Mirrored False'); 1679 filterCodeParts.push(' Corrupted False'); 1680 filterCodeParts.push(' Rarity Normal'); 1681 filterCodeParts.push(` ItemLevel >= ${ilvl}`); 1682 1683 const formattedNames = uniqueNames.map(name => `"${name}"`).join(' '); 1684 filterCodeParts.push(` BaseType == ${formattedNames}`); 1685 1686 filterCodeParts.push(' SetFontSize 45'); 1687 filterCodeParts.push(' SetTextColor 125 255 89 255'); 1688 filterCodeParts.push(' SetBorderColor 125 255 89 255'); 1689 filterCodeParts.push(' SetBackgroundColor 0 50 0'); 1690 filterCodeParts.push(' PlayAlertSound 1 300'); 1691 filterCodeParts.push(' PlayEffect Blue'); 1692 filterCodeParts.push(' MinimapIcon 0 Blue Star'); 1693 filterCodeParts.push(''); 1694 } 1695 1696 filterCodeParts.push(BASE_FILTER_MARKER_END); 1697 const finalFilterCode = filterCodeParts.join('\n'); 1698 showFilterCodeModal(finalFilterCode); 1699 } 1700 1701 // æ¾ç¤ºè¿æ»¤ä»£ç æ¨¡ææ¡ 1702 function showFilterCodeModal(code) { 1703 const modal = document.getElementById('filterCodeModal'); 1704 const textarea = document.getElementById('generatedFilterCode'); 1705 if (modal && textarea) { 1706 textarea.value = code; 1707 modal.classList.remove('hidden'); 1708 modal.style.display = 'flex'; 1709 } 1710 } 1711 1712 // å ³éè¿æ»¤ä»£ç æ¨¡ææ¡ 1713 function closeFilterCodeModal() { 1714 const modal = document.getElementById('filterCodeModal'); 1715 if (modal) { 1716 modal.classList.add('hidden'); 1717 modal.style.display = 'none'; 1718 } 1719 } 1720 1721 // å¤å¶è¿æ»¤ä»£ç 1722 function copyFilterCode() { 1723 const textarea = document.getElementById('generatedFilterCode'); 1724 if (textarea) { 1725 textarea.select(); 1726 document.execCommand('copy'); 1727 showCopySuccessToast(); 1728 } 1729 } 1730 1731 // æ¾ç¤ºå¤å¶æåæç¤ºæ¡ï¼2ç§åèªå¨æ¶å¤±ï¼ 1732 function showCopySuccessToast() { 1733 const toast = document.getElementById('copySuccessToast'); 1734 if (!toast) { 1735 // 妿æç¤ºæ¡ä¸åå¨ï¼åéå°alert 1736 alert('å·²å¤å¶å°åªè´´æ¿'); 1737 return; 1738 } 1739 1740 // æ¸ é¤ä¹åç宿¶å¨ï¼å¦æåå¨ï¼ 1741 if (toast._autoCloseTimer) { 1742 clearTimeout(toast._autoCloseTimer); 1743 } 1744 1745 toast.classList.remove('hidden'); 1746 toast.style.display = 'flex'; 1747 1748 // 2ç§åèªå¨å ³é 1749 toast._autoCloseTimer = setTimeout(() => { 1750 toast.classList.add('hidden'); 1751 toast.style.display = 'none'; 1752 toast._autoCloseTimer = null; 1753 }, 2000); 1754 } 1755 1756 // åå§å 1757 document.addEventListener('DOMContentLoaded', () => { 1758 if (typeof window.Poe2BaseLadderInstall === 'function') { 1759 window.Poe2BaseLadderInstall(); 1760 } 1761 setupSortHandlers(); 1762 setupFilters(); 1763 loadBaseData(); 1764 1765 // ç»å®çæè¿æ»¤ä»£ç æé® 1766 const generateBtn = document.getElementById('generate-filter-code-btn'); 1767 if (generateBtn) { 1768 generateBtn.addEventListener('click', generateFilterCode); 1769 } 1770 1771 // ç»å®æ¨¡ææ¡å ³éæé® 1772 const closeBtn = document.getElementById('closeFilterCodeModalBtn'); 1773 if (closeBtn) { 1774 closeBtn.addEventListener('click', closeFilterCodeModal); 1775 } 1776 1777 // ç»å®å¤å¶æé® 1778 const copyBtn = document.getElementById('copyFilterCodeBtn'); 1779 if (copyBtn) { 1780 copyBtn.addEventListener('click', copyFilterCode); 1781 } 1782 1783 // å¤å¶æåæç¤ºæ¡çç¡®å®æé® 1784 const copySuccessToastBtn = document.getElementById('copySuccessToastBtn'); 1785 if (copySuccessToastBtn) { 1786 copySuccessToastBtn.addEventListener('click', () => { 1787 const toast = document.getElementById('copySuccessToast'); 1788 if (toast) { 1789 toast.classList.add('hidden'); 1790 // æ¸ é¤å®æ¶å¨ 1791 if (toast._autoCloseTimer) { 1792 clearTimeout(toast._autoCloseTimer); 1793 toast._autoCloseTimer = null; 1794 } 1795 } 1796 }); 1797 } 1798 1799 // ç¹å»æ¨¡ææ¡å¤é¨å ³é 1800 const modal = document.getElementById('filterCodeModal'); 1801 if (modal) { 1802 modal.addEventListener('click', (e) => { 1803 if (e.target === modal) { 1804 closeFilterCodeModal(); 1805 } 1806 }); 1807 } 1808 }); 1809 1810 console.log('base_price 页é¢èæ¬æ§è¡å®æ'); 1811 </script>
1811 1812
1812<script src="/js/poe2_base_price.js?v=20260910e"></script>
1812 1813 1814 <!-- è¿æ»¤ä»£ç æ¨¡ææ¡ --> 1815 <div id="filterCodeModal" class="modal-overlay hidden"> 1816 <div class="modal-card" style="width:min(640px,92vw);height:min(60vh,520px);"> 1817 <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:0.65rem;"> 1818 <h3>è¿æ»¤ä»£ç </h3> 1819 <button type="button" id="closeFilterCodeModalBtn" class="modal-close">×</button> 1820 </div> 1821 <textarea id="generatedFilterCode" readonly style="flex:1;width:100%;background:#111827;border:1px solid #374151;border-radius:0.35rem;color:#e5e7eb;padding:0.75rem;font-family:ui-monospace,monospace;font-size:0.8rem;resize:none;"></textarea> 1822 <div style="display:flex;justify-content:flex-end;margin-top:0.75rem;"> 1823 <button type="button" id="copyFilterCodeBtn" class="toolbar-btn primary">å¤å¶</button> 1824 </div> 1825 </div> 1826 </div> 1827 1828 <div id="copySuccessToast" class="modal-overlay hidden"> 1829 <div class="modal-card" style="width:min(320px,90vw);text-align:center;"> 1830 <h3 style="margin-bottom:0.5rem;">å·²å¤å¶</h3> 1831 <p style="color:#9ca3af;margin:0 0 0.85rem;">è¿æ»¤ä»£ç å·²å¤å¶å°åªè´´æ¿</p> 1832 <button type="button" id="copySuccessToastBtn" class="toolbar-btn primary">ç¡®å®</button> 1833 </div> 1834 </div> 1835</body> 1836</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.