diff --git a/app/static/index.html b/app/static/index.html index 2c27ad5..b104e83 100644 --- a/app/static/index.html +++ b/app/static/index.html @@ -297,43 +297,74 @@ .badge-info { background: #dbeafe; color: #1e40af; } .badge-gray { background: var(--gray-100); color: var(--gray-600); } - /* Symbol Grid */ - .symbol-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); - gap: 12px; + /* Symbol Table */ + .symbol-table { + width: 100%; + border-collapse: collapse; margin-top: 16px; max-height: 500px; overflow-y: auto; - padding-right: 8px; + display: block; + } + + .symbol-table thead { + position: sticky; + top: 0; + background: var(--gray-50); + z-index: 1; + } + + .symbol-table th { + padding: 12px 16px; + text-align: left; + font-weight: 600; + color: var(--gray-700); + font-size: 13px; + border-bottom: 2px solid var(--gray-200); + } + + .symbol-table td { + padding: 12px 16px; + border-bottom: 1px solid var(--gray-100); + font-size: 14px; } - .symbol-card { + .symbol-table tr:hover { background: var(--gray-50); + } + + .symbol-table .symbol-name { + font-weight: 600; + color: var(--gray-800); + } + + .symbol-table .symbol-code-input { + font-family: 'SF Mono', 'Cascadia Code', monospace; + font-size: 13px; + color: var(--primary); + padding: 6px 10px; border: 1px solid var(--gray-200); - border-radius: var(--radius); - padding: 14px; - text-align: center; + border-radius: 6px; + width: 120px; transition: all 0.2s; - position: relative; } - .symbol-card:hover { border-color: var(--primary-light); box-shadow: var(--shadow-md); } - .symbol-name { font-weight: 600; color: var(--gray-800); font-size: 14px; } - .symbol-code { font-family: 'SF Mono', 'Cascadia Code', monospace; font-size: 13px; color: var(--primary); margin-top: 4px; } - - .symbol-actions { + .symbol-table .symbol-code-input:focus { + outline: none; + border-color: var(--primary-light); + box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1); + } + + .symbol-table .symbol-actions { display: flex; gap: 6px; - margin-top: 10px; - justify-content: center; } - - .symbol-actions .btn-sm { - padding: 4px 8px; + + .symbol-table .symbol-actions .btn-sm { + padding: 4px 10px; font-size: 12px; } - + .add-symbol-form { background: var(--gray-50); border: 2px dashed var(--gray-300); @@ -1451,7 +1482,7 @@ const stockCount = Object.keys(stock).length; document.getElementById('configCount').textContent = `期货 ${futuresCount} 个, 股票 ${stockCount} 个`; - const renderGrid = (items, type) => { + const renderTable = (items, type) => { let filteredItems = items; if (currentSearchTerm) { @@ -1476,20 +1507,31 @@ return text.replace(regex, '$1'); }; - return `
${Object.entries(filteredItems).map(([name, code]) => - `
-
${highlightText(name)}
-
${highlightText(code)}
-
- + const rows = Object.entries(filteredItems).map(([name, code]) => + ` + ${highlightText(name)} + + + -
-
` - ).join('')}
`; + + ` + ).join(''); + + return ` + + + + + + + + ${rows} +
品种名称合约代码操作
`; }; - document.getElementById('tab-futures').innerHTML = renderGrid(futures, 'futures'); - document.getElementById('tab-stock').innerHTML = renderGrid(stock, 'stock'); + document.getElementById('tab-futures').innerHTML = renderTable(futures, 'futures'); + document.getElementById('tab-stock').innerHTML = renderTable(stock, 'stock'); } function filterSymbols(searchTerm) { @@ -1608,6 +1650,52 @@ openEditSymbolModal(type, code, name); } + async function saveSymbolCode(type, name, btn) { + const row = btn.closest('tr'); + const input = row.querySelector('.symbol-code-input'); + const newCode = input.value.trim(); + const originalCode = input.dataset.original; + + if (!newCode) { + return showToast('合约代码不能为空', 'error'); + } + + if (newCode === originalCode) { + return showToast('合约代码未修改', 'info'); + } + + // 更新配置 + const symbols = type === 'futures' ? (currentConfig.futures || {}) : (currentConfig.stock || {}); + symbols[name] = newCode; + + const fullConfig = { + futures: currentConfig.futures || {}, + stock: currentConfig.stock || {} + }; + fullConfig[type] = symbols; + + try { + const res = await fetch(`${API}/config/upload`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(fullConfig) + }); + + if (res.ok) { + showToast('合约代码修改成功', 'success'); + input.dataset.original = newCode; + } else { + const data = await res.json(); + showToast(data.detail || '修改失败', 'error'); + // 恢复原始值 + input.value = originalCode; + } + } catch (e) { + showToast(`修改失败: ${e.message}`, 'error'); + input.value = originalCode; + } + } + async function deleteSymbol(type, code) { if (!confirm('确定删除此品种?')) return; diff --git a/data/buffer.db b/data/buffer.db index 9698de9..1b63c8d 100644 Binary files a/data/buffer.db and b/data/buffer.db differ diff --git a/data/futures_analysis.db b/data/futures_analysis.db index d2b6dac..e842d73 100644 Binary files a/data/futures_analysis.db and b/data/futures_analysis.db differ diff --git a/openspec/changes/symbol-config-list-view/.comet.yaml b/openspec/changes/symbol-config-list-view/.comet.yaml new file mode 100644 index 0000000..a364b91 --- /dev/null +++ b/openspec/changes/symbol-config-list-view/.comet.yaml @@ -0,0 +1,8 @@ +name: symbol-config-list-view +phase: build +workflow: tweak +created_at: "2026-07-07T23:08:00+08:00" +updated_at: "2026-07-07T23:08:00+08:00" +build_mode: direct +verify_result: null +archived: false diff --git a/openspec/changes/symbol-config-list-view/design.md b/openspec/changes/symbol-config-list-view/design.md new file mode 100644 index 0000000..e06bce6 --- /dev/null +++ b/openspec/changes/symbol-config-list-view/design.md @@ -0,0 +1,27 @@ +# 设计文档 + +## 实现方案 + +### 1. 列表视图结构 +使用表格替代卡片网格: +``` +| 品种名称 | 合约代码 | 操作 | +|----------|----------|------| +| 螺纹钢 | rb | [保存] [删除] | +| 铁矿石 | i | [保存] [删除] | +``` + +### 2. 合约代码编辑 +- 合约代码列使用 input 输入框 +- 修改后点击"保存"按钮提交 +- 保存时调用 API 更新配置 + +### 3. CSS 调整 +- 移除 `.symbol-grid` 和 `.symbol-card` 样式 +- 添加 `.symbol-table` 列表样式 +- 保持与现有 UI 风格一致 + +### 4. JavaScript 调整 +- `renderConfig()` 函数改为渲染表格 +- 新增 `saveSymbolCode(type, oldCode, newCode, name)` 函数 +- 保留删除功能 diff --git a/openspec/changes/symbol-config-list-view/proposal.md b/openspec/changes/symbol-config-list-view/proposal.md new file mode 100644 index 0000000..e9797ca --- /dev/null +++ b/openspec/changes/symbol-config-list-view/proposal.md @@ -0,0 +1,16 @@ +# 品种配置列表视图改造 + +## 动机 +当前品种配置使用卡片网格展示,修改配置不方便: +1. 卡片占用空间大,品种多时需要大量滚动 +2. 修改合约代码需要点击"修改"按钮打开弹窗,操作繁琐 + +## 目标 +1. 将品种卡片改为紧凑列表视图 +2. 支持直接在列表中修改合约代码 + +## 范围 +- 修改 `app/static/index.html` 中的品种配置页面 +- 将 `symbol-grid` + `symbol-card` 改为表格列表 +- 在列表中直接显示可编辑的合约代码输入框 +- 保留删除功能 diff --git a/openspec/changes/symbol-config-list-view/tasks.md b/openspec/changes/symbol-config-list-view/tasks.md new file mode 100644 index 0000000..18f25e1 --- /dev/null +++ b/openspec/changes/symbol-config-list-view/tasks.md @@ -0,0 +1,5 @@ +# 任务列表 + +- [x] 1. 修改 CSS:将 `.symbol-grid` + `.symbol-card` 改为 `.symbol-table` 列表样式 +- [x] 2. 修改 JavaScript:将 `renderConfig()` 中的卡片渲染改为表格渲染,合约代码使用 input 输入框 +- [x] 3. 新增 `saveSymbolCode()` 函数,支持直接在列表中保存修改的合约代码