fix: 推荐品种下拉改用缓存 main_code,避免加载卡住。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+35
-13
@@ -1,4 +1,35 @@
|
|||||||
(function () {
|
(function () {
|
||||||
|
var recommendedGroupsCache = null;
|
||||||
|
var recommendedGroupsPromise = null;
|
||||||
|
|
||||||
|
function loadRecommendedGroups() {
|
||||||
|
if (recommendedGroupsCache) {
|
||||||
|
return Promise.resolve(recommendedGroupsCache);
|
||||||
|
}
|
||||||
|
if (recommendedGroupsPromise) {
|
||||||
|
return recommendedGroupsPromise;
|
||||||
|
}
|
||||||
|
recommendedGroupsPromise = fetch('/api/symbols/recommended')
|
||||||
|
.then(function (r) {
|
||||||
|
if (!r.ok) {
|
||||||
|
throw new Error('HTTP ' + r.status);
|
||||||
|
}
|
||||||
|
return r.json();
|
||||||
|
})
|
||||||
|
.then(function (groups) {
|
||||||
|
recommendedGroupsCache = Array.isArray(groups) ? groups : [];
|
||||||
|
return recommendedGroupsCache;
|
||||||
|
})
|
||||||
|
.catch(function () {
|
||||||
|
recommendedGroupsCache = null;
|
||||||
|
throw new Error('load failed');
|
||||||
|
})
|
||||||
|
.finally(function () {
|
||||||
|
recommendedGroupsPromise = null;
|
||||||
|
});
|
||||||
|
return recommendedGroupsPromise;
|
||||||
|
}
|
||||||
|
|
||||||
function formatSub(item) {
|
function formatSub(item) {
|
||||||
var sub = '同花顺 ' + item.ths_code +
|
var sub = '同花顺 ' + item.ths_code +
|
||||||
(item.market_code ? ' · ' + item.market_code : '') +
|
(item.market_code ? ' · ' + item.market_code : '') +
|
||||||
@@ -47,7 +78,6 @@
|
|||||||
let abortCtrl = null;
|
let abortCtrl = null;
|
||||||
const cache = new Map();
|
const cache = new Map();
|
||||||
let mainsCache = null;
|
let mainsCache = null;
|
||||||
let mainsLoading = false;
|
|
||||||
|
|
||||||
function hideDropdown() {
|
function hideDropdown() {
|
||||||
dropdown.classList.remove('show');
|
dropdown.classList.remove('show');
|
||||||
@@ -135,16 +165,9 @@
|
|||||||
renderGrouped(mainsCache, q);
|
renderGrouped(mainsCache, q);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (mainsLoading) {
|
|
||||||
dropdown.innerHTML = '<div class="symbol-option">正在加载推荐品种…</div>';
|
|
||||||
dropdown.classList.add('show');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mainsLoading = true;
|
|
||||||
dropdown.innerHTML = '<div class="symbol-option">正在加载推荐品种…</div>';
|
dropdown.innerHTML = '<div class="symbol-option">正在加载推荐品种…</div>';
|
||||||
dropdown.classList.add('show');
|
dropdown.classList.add('show');
|
||||||
fetch('/api/symbols/recommended')
|
loadRecommendedGroups()
|
||||||
.then(function (r) { return r.json(); })
|
|
||||||
.then(function (groups) {
|
.then(function (groups) {
|
||||||
mainsCache = groups;
|
mainsCache = groups;
|
||||||
if (!groups.length) {
|
if (!groups.length) {
|
||||||
@@ -156,10 +179,9 @@
|
|||||||
showMarketMains(filterQ, onEmpty);
|
showMarketMains(filterQ, onEmpty);
|
||||||
})
|
})
|
||||||
.catch(function () {
|
.catch(function () {
|
||||||
hideDropdown();
|
dropdown.innerHTML =
|
||||||
})
|
'<div class="symbol-option">推荐品种加载失败,请刷新页面或输入合约代码搜索</div>';
|
||||||
.finally(function () {
|
dropdown.classList.add('show');
|
||||||
mainsLoading = false;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+38
-15
@@ -431,15 +431,42 @@ def _product_for_ths(ths: str) -> Optional[dict]:
|
|||||||
return _THS_TO_PRODUCT.get(key) or _THS_TO_PRODUCT.get(key.lower())
|
return _THS_TO_PRODUCT.get(key) or _THS_TO_PRODUCT.get(key.lower())
|
||||||
|
|
||||||
|
|
||||||
def _main_for_product(product: dict) -> Optional[dict]:
|
def _item_from_recommend_row(row: dict, product: dict) -> Optional[dict]:
|
||||||
|
"""由推荐缓存行快速构造下拉项(不在 HTTP 请求中解析主力)。"""
|
||||||
|
name = row.get("name") or product["name"]
|
||||||
|
main_code = (row.get("main_code") or "").strip()
|
||||||
|
max_lots = row.get("max_lots")
|
||||||
|
|
||||||
|
if main_code:
|
||||||
|
codes = ths_to_codes(main_code)
|
||||||
|
if codes:
|
||||||
|
ths = codes["ths_code"]
|
||||||
|
item = {
|
||||||
|
"name": name,
|
||||||
|
"ths_code": ths,
|
||||||
|
"market_code": codes.get("market_code") or "",
|
||||||
|
"sina_code": codes.get("sina_code") or "",
|
||||||
|
"exchange": product["exchange"],
|
||||||
|
"contract": f"主力 {ths}",
|
||||||
|
"display": f"{name} 主力 {ths}",
|
||||||
|
"input_label": f"{name} {ths}",
|
||||||
|
}
|
||||||
|
if max_lots is not None:
|
||||||
|
item["max_lots"] = max_lots
|
||||||
|
return _enrich_item(item)
|
||||||
|
|
||||||
with _main_index_lock:
|
with _main_index_lock:
|
||||||
index = dict(_main_index)
|
main = _main_index.get(product["sina"])
|
||||||
main = index.get(product["sina"])
|
if main:
|
||||||
if not main:
|
item = dict(main)
|
||||||
resolved = resolve_main_contract(product)
|
if max_lots is not None:
|
||||||
if resolved:
|
item["max_lots"] = max_lots
|
||||||
main = _enrich_item(resolved)
|
return _enrich_item(item)
|
||||||
return main
|
|
||||||
|
item = _stub_main_contract(product)
|
||||||
|
if max_lots is not None:
|
||||||
|
item["max_lots"] = max_lots
|
||||||
|
return item
|
||||||
|
|
||||||
|
|
||||||
def list_recommended_symbols_grouped(recommend_rows: list[dict]) -> list[dict]:
|
def list_recommended_symbols_grouped(recommend_rows: list[dict]) -> list[dict]:
|
||||||
@@ -459,14 +486,10 @@ def list_recommended_symbols_grouped(recommend_rows: list[dict]) -> list[dict]:
|
|||||||
if not product:
|
if not product:
|
||||||
continue
|
continue
|
||||||
seen.add(ths_key)
|
seen.add(ths_key)
|
||||||
main = _main_for_product(product)
|
item = _item_from_recommend_row(row, product)
|
||||||
if not main:
|
if not item:
|
||||||
continue
|
continue
|
||||||
item = dict(main)
|
buckets[product["exchange"]].append(item)
|
||||||
max_lots = row.get("max_lots")
|
|
||||||
if max_lots is not None:
|
|
||||||
item["max_lots"] = max_lots
|
|
||||||
buckets[product["exchange"]].append(_enrich_item(item))
|
|
||||||
|
|
||||||
groups: list[dict] = []
|
groups: list[dict] = []
|
||||||
for cat in EXCHANGE_ORDER:
|
for cat in EXCHANGE_ORDER:
|
||||||
|
|||||||
Reference in New Issue
Block a user