feat: 品种推荐与下单显示主力合约

推荐列表展示当前主力代码;下单品种支持中文/代码搜索并按交易所分组选择主力合约。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-24 12:56:06 +08:00
parent 3ba3be6035
commit aea9aca472
7 changed files with 94 additions and 41 deletions
+51 -23
View File
@@ -19,15 +19,26 @@
return hay.indexOf(qLower) >= 0;
}
function groupedHasMatch(groups, qLower) {
if (!qLower) return true;
return groups.some(function (group) {
return group.items.some(function (item) {
return itemMatchesQuery(item, qLower);
});
});
}
function initSymbolInput(wrapper) {
const input = wrapper.querySelector('.symbol-input');
const hiddenThs = wrapper.querySelector('input[name="symbol"]');
const hiddenThs = wrapper.querySelector('input[name="symbol"]')
|| wrapper.querySelector('.symbol-ths-code');
const hiddenName = wrapper.querySelector('input[name="symbol_name"]');
const hiddenMarket = wrapper.querySelector('input[name="market_code"]');
const hiddenSina = wrapper.querySelector('input[name="sina_code"]');
const dropdown = wrapper.querySelector('.symbol-dropdown');
const selectedEl = wrapper.querySelector('.symbol-selected');
const isMarketPicker = wrapper.classList.contains('market-symbol-wrap');
const useMainsPicker = isMarketPicker || wrapper.classList.contains('symbol-mains');
let timer = null;
let abortCtrl = null;
const cache = new Map();
@@ -41,15 +52,13 @@
function selectItem(item) {
const label = formatInputLabel(item);
input.value = label;
hiddenThs.value = item.ths_code;
hiddenName.value = item.name;
if (hiddenThs) hiddenThs.value = item.ths_code;
if (hiddenName) hiddenName.value = item.name;
if (hiddenMarket) hiddenMarket.value = item.market_code || '';
if (hiddenSina) hiddenSina.value = item.sina_code || '';
selectedEl.textContent = formatSub(item);
if (selectedEl) selectedEl.textContent = formatSub(item);
hideDropdown();
if (isMarketPicker) {
input.dispatchEvent(new CustomEvent('symbol-selected', { detail: item }));
}
input.dispatchEvent(new CustomEvent('symbol-selected', { detail: item, bubbles: true }));
}
function buildOptionEl(item) {
@@ -107,9 +116,19 @@
dropdown.classList.add('show');
}
function showMarketMains(filterQ) {
function showMarketMains(filterQ, onEmpty) {
const q = (filterQ || '').trim();
const qLower = q.toLowerCase();
if (mainsCache) {
renderGrouped(mainsCache, filterQ);
if (!q || groupedHasMatch(mainsCache, qLower)) {
renderGrouped(mainsCache, q);
return;
}
if (typeof onEmpty === 'function') {
onEmpty(q);
return;
}
renderGrouped(mainsCache, q);
return;
}
if (mainsLoading) {
@@ -124,7 +143,7 @@
.then(function (r) { return r.json(); })
.then(function (groups) {
mainsCache = groups;
renderGrouped(groups, filterQ);
showMarketMains(filterQ, onEmpty);
})
.catch(function () {
hideDropdown();
@@ -157,15 +176,25 @@
});
}
function handleQuery(q) {
if (useMainsPicker) {
showMarketMains(q, function (query) {
search(query);
});
} else {
search(q);
}
}
input.addEventListener('input', function () {
hiddenThs.value = '';
hiddenName.value = '';
if (hiddenThs) hiddenThs.value = '';
if (hiddenName) hiddenName.value = '';
if (hiddenMarket) hiddenMarket.value = '';
if (hiddenSina) hiddenSina.value = '';
selectedEl.textContent = '';
if (selectedEl) selectedEl.textContent = '';
const q = input.value.trim();
if (!q) {
if (isMarketPicker) {
if (useMainsPicker) {
showMarketMains('');
} else {
hideDropdown();
@@ -174,11 +203,7 @@
}
clearTimeout(timer);
timer = setTimeout(function () {
if (isMarketPicker) {
showMarketMains(q);
} else {
search(q);
}
handleQuery(q);
}, 120);
});
@@ -188,11 +213,13 @@
input.addEventListener('focus', function () {
const q = input.value.trim();
if (isMarketPicker) {
showMarketMains(q);
if (useMainsPicker) {
showMarketMains(q, function (query) {
if (query) search(query);
});
return;
}
if (q && !hiddenThs.value) {
if (q && hiddenThs && !hiddenThs.value) {
search(q);
}
});
@@ -205,7 +232,8 @@
if (!form.querySelector('.symbol-wrap')) return;
if (form.id === 'market-form') return;
form.addEventListener('submit', function (e) {
const ths = form.querySelector('input[name="symbol"]');
const ths = form.querySelector('input[name="symbol"]')
|| form.querySelector('.symbol-ths-code');
const market = form.querySelector('input[name="market_code"]');
if (ths && !ths.value.trim()) {
e.preventDefault();