ADD: 支持ETF、 LOF基金!
This commit is contained in:
@@ -301,29 +301,28 @@
|
||||
<option value="A">A股</option>
|
||||
<option value="HK">港股</option>
|
||||
<option value="US">美股</option>
|
||||
<option value="ETF">ETF基金</option>
|
||||
<option value="LOF">LOF基金</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- 美股搜索框 -->
|
||||
<div id="usStockSearch" class="mb-4 hidden">
|
||||
<!-- 搜索框 -->
|
||||
<div id="searchContainer" class="mb-4 hidden">
|
||||
<div class="relative">
|
||||
<input type="text"
|
||||
id="searchInput"
|
||||
class="w-full p-2 border rounded bg-white focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
placeholder="输入美股名称搜索(中文和英文都试下)"
|
||||
oninput="handleSearchInput(event)">
|
||||
<!-- 添加搜索 loading 图标 -->
|
||||
placeholder="输入名称回车搜索"
|
||||
onkeydown="handleKeyDown(event)">
|
||||
<div id="searchLoading" class="absolute right-3 top-2.5 hidden">
|
||||
<svg class="animate-spin h-5 w-5 text-blue-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- 搜索结果下拉框 -->
|
||||
<div id="searchResults"
|
||||
class="absolute z-10 w-full mt-1 bg-white border rounded-md shadow-lg hidden">
|
||||
<div id="searchResults" class="absolute z-10 w-full mt-1 bg-white border rounded-md shadow-lg hidden max-h-80 overflow-y-auto">
|
||||
</div>
|
||||
</div>
|
||||
<!-- 添加错误提示 -->
|
||||
<div id="searchError" class="hidden absolute z-10 w-full mt-1 p-3 bg-red-50 text-red-600 rounded-md border border-red-200">
|
||||
</div>
|
||||
</div>
|
||||
@@ -342,6 +341,17 @@
|
||||
};
|
||||
}
|
||||
|
||||
function handleKeyDown(event) {
|
||||
// 检查是否按下回车键且不在输入法编辑状态
|
||||
if (event.key === 'Enter' && !event.isComposing) {
|
||||
event.preventDefault(); // 阻止默认行为
|
||||
const keyword = event.target.value.trim();
|
||||
if (keyword) {
|
||||
debouncedSearch(keyword);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 使用防抖包装搜索函数
|
||||
const debouncedSearch = debounce(async (keyword) => {
|
||||
if (!keyword) {
|
||||
@@ -350,20 +360,26 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示 loading
|
||||
const marketType = document.getElementById('marketType').value;
|
||||
document.getElementById('searchLoading').classList.remove('hidden');
|
||||
// 隐藏之前的错误信息
|
||||
document.getElementById('searchError').classList.add('hidden');
|
||||
|
||||
try {
|
||||
const response = await fetch(`/search_us_stocks?keyword=${encodeURIComponent(keyword)}`);
|
||||
let endpoint = '';
|
||||
if (marketType === 'US') {
|
||||
endpoint = '/search_us_stocks';
|
||||
} else if (['ETF', 'LOF'].includes(marketType)) {
|
||||
endpoint = '/search_funds';
|
||||
}
|
||||
|
||||
const response = await fetch(`${endpoint}?market_type=${marketType}&keyword=${encodeURIComponent(keyword)}`);
|
||||
const data = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error || '搜索失败');
|
||||
}
|
||||
|
||||
displaySearchResults(data.results);
|
||||
displaySearchResults(data.results, marketType);
|
||||
} catch (error) {
|
||||
console.error('搜索出错:', error);
|
||||
const errorDiv = document.getElementById('searchError');
|
||||
@@ -373,12 +389,8 @@
|
||||
} finally {
|
||||
document.getElementById('searchLoading').classList.add('hidden');
|
||||
}
|
||||
}, 500); // 设置500ms的防抖延迟
|
||||
}, 500);
|
||||
|
||||
// 修改输入事件处理函数
|
||||
function handleSearchInput(event) {
|
||||
debouncedSearch(event.target.value);
|
||||
}
|
||||
</script>
|
||||
<div class="mb-4">
|
||||
<label for="batchStocks" class="block text-sm font-medium text-gray-700 mb-2">
|
||||
@@ -395,12 +407,28 @@
|
||||
|
||||
function handleMarketTypeChange() {
|
||||
const marketType = document.getElementById('marketType').value;
|
||||
const searchDiv = document.getElementById('usStockSearch');
|
||||
searchDiv.classList.toggle('hidden', marketType !== 'US');
|
||||
const searchContainer = document.getElementById('searchContainer');
|
||||
const searchInput = document.getElementById('searchInput');
|
||||
const batchStocks = document.getElementById('batchStocks');
|
||||
|
||||
// 清空搜索结果
|
||||
// 显示/隐藏搜索框
|
||||
searchContainer.classList.toggle('hidden', !['US', 'ETF', 'LOF'].includes(marketType));
|
||||
|
||||
// 更新搜索框提示文本
|
||||
if (marketType === 'US') {
|
||||
searchInput.placeholder = '输入美股名称回车搜索(中文和英文都试下)';
|
||||
} else if (marketType === 'ETF') {
|
||||
searchInput.placeholder = '输入ETF基金名称回车搜索';
|
||||
} else if (marketType === 'LOF') {
|
||||
searchInput.placeholder = '输入LOF基金名称回车搜索';
|
||||
}
|
||||
|
||||
// 清空搜索结果和输入
|
||||
document.getElementById('searchResults').innerHTML = '';
|
||||
document.getElementById('searchInput').value = '';
|
||||
searchInput.value = '';
|
||||
|
||||
// 清空代码输入框
|
||||
batchStocks.value = '';
|
||||
}
|
||||
|
||||
function debounceSearch(event) {
|
||||
@@ -410,7 +438,7 @@
|
||||
}, 300);
|
||||
}
|
||||
|
||||
function displaySearchResults(results) {
|
||||
function displaySearchResults(results, marketType) {
|
||||
const resultsDiv = document.getElementById('searchResults');
|
||||
|
||||
if (!results || results.length === 0) {
|
||||
@@ -418,18 +446,32 @@
|
||||
return;
|
||||
}
|
||||
|
||||
let html = '<div class="py-1">';
|
||||
results.forEach(stock => {
|
||||
let html = '<div class="divide-y divide-gray-100">'; // 添加分割线
|
||||
results.forEach(item => {
|
||||
let rightContent = '';
|
||||
if (marketType === 'US') {
|
||||
rightContent = `
|
||||
<div class="font-medium">$${item.price}</div>
|
||||
<div class="text-sm text-gray-500">市值: ${formatMarketValue(item.market_value)}</div>
|
||||
`;
|
||||
} else {
|
||||
rightContent = `
|
||||
<div class="font-medium">¥${item.price}</div>
|
||||
<div class="text-sm text-gray-500">
|
||||
${item.discount_rate ? `折价率: ${item.discount_rate}%` : ''}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
html += `
|
||||
<div class="px-4 py-2 hover:bg-gray-100 cursor-pointer flex justify-between items-center"
|
||||
onclick="selectStock('${stock.symbol}')">
|
||||
onclick="selectStock('${item.symbol}')">
|
||||
<div>
|
||||
<div class="font-medium">${stock.name}</div>
|
||||
<div class="text-sm text-gray-500">${stock.symbol}</div>
|
||||
<div class="font-medium">${item.name}</div>
|
||||
<div class="text-sm text-gray-500">${item.symbol}</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div class="font-medium">$${stock.price}</div>
|
||||
<div class="text-sm text-gray-500">市值: ${formatMarketValue(stock.market_value)}</div>
|
||||
${rightContent}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -686,10 +728,10 @@
|
||||
errorCard = document.createElement('div');
|
||||
errorCard.id = `error-${stockCode}`;
|
||||
errorCard.className = 'bg-red-50 p-4 rounded-lg text-red-600';
|
||||
errorCard.innerHTML = `分析股票 ${stockCode} 出错: ${chunk.error}`;
|
||||
errorCard.innerHTML = `分析 ${stockCode} 出错: ${chunk.error}`;
|
||||
container.appendChild(errorCard);
|
||||
} else {
|
||||
errorCard.innerHTML = `分析股票 ${stockCode} 出错: ${chunk.error}`;
|
||||
errorCard.innerHTML = `分析 ${stockCode} 出错: ${chunk.error}`;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user