feat(markets): add trading calculator tool and optimize app config

1. 新增工具箱页面和A股交易计算工具
2. 配置expo项目信息与eas构建参数
3. 清理无用的worklets依赖
This commit is contained in:
yangxiangyuan
2026-07-08 14:40:53 +08:00
parent 0540960638
commit 41c1821204
9 changed files with 9440 additions and 2863 deletions
+1
View File
@@ -0,0 +1 @@
legacy-peer-deps=true
+2 -1
View File
@@ -1,7 +1,7 @@
{ {
"expo": { "expo": {
"name": "观空禅音", "name": "观空禅音",
"slug": "zen-box", "slug": "app-zen-box",
"version": "1.0.0", "version": "1.0.0",
"orientation": "portrait", "orientation": "portrait",
"icon": "./assets/icon.png", "icon": "./assets/icon.png",
@@ -23,6 +23,7 @@
} }
}, },
"android": { "android": {
"package": "com.yangdafe.zenbox",
"adaptiveIcon": { "adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png", "foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#060612" "backgroundColor": "#060612"
+27
View File
@@ -0,0 +1,27 @@
{
"cli": {
"version": ">= 15.0.0",
"appVersionSource": "remote",
"promptToConfigurePushNotifications": false
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal"
},
"preview": {
"distribution": "internal",
"android": {
"buildType": "apk"
}
},
"production": {
"android": {
"buildType": "apk"
}
}
},
"submit": {
"production": {}
}
}
+8991 -2850
View File
File diff suppressed because it is too large Load Diff
+2 -3
View File
@@ -25,15 +25,14 @@
"react-native-reanimated": "~4.1.1", "react-native-reanimated": "~4.1.1",
"react-native-safe-area-context": "~5.6.0", "react-native-safe-area-context": "~5.6.0",
"react-native-screens": "~4.16.0", "react-native-screens": "~4.16.0",
"react-native-svg": "15.2.0", "react-native-svg": "15.2.0"
"react-native-worklets": "^0.10.0",
"react-native-worklets-core": "^1.6.3"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.20.0", "@babel/core": "^7.20.0",
"@types/react": "~19.1.10", "@types/react": "~19.1.10",
"@types/react-native": "~0.73.0", "@types/react-native": "~0.73.0",
"babel-preset-expo": "~54.0.10", "babel-preset-expo": "~54.0.10",
"eas-cli": "^20.5.1",
"typescript": "~5.9.2" "typescript": "~5.9.2"
}, },
"private": true "private": true
+63
View File
@@ -25,6 +25,7 @@
<button id="m-tab-weekly" class="subtab">指数周报</button> <button id="m-tab-weekly" class="subtab">指数周报</button>
<button id="m-tab-news" class="subtab">全球新闻</button> <button id="m-tab-news" class="subtab">全球新闻</button>
<button id="m-tab-wisdom" class="subtab">理论与实践</button> <button id="m-tab-wisdom" class="subtab">理论与实践</button>
<button id="m-tab-tools" class="subtab">工具箱</button>
<div id="m-status" class="status status-inline" style="display:none"></div> <div id="m-status" class="status status-inline" style="display:none"></div>
</div> </div>
@@ -128,6 +129,67 @@
<iframe id="wisdom-frame" src="/tools/markets/wallstreet_wisdom.html?v=10" style="width:100%;height:calc(100vh - 140px);border:none;"></iframe> <iframe id="wisdom-frame" src="/tools/markets/wallstreet_wisdom.html?v=10" style="width:100%;height:calc(100vh - 140px);border:none;"></iframe>
</section> </section>
<section id="m-panel-tools" class="panel" style="display:none">
<div class="tools-container">
<!-- 工具一:交易计算 -->
<div class="tool-card" id="tool-calc">
<div class="tool-card-header">
<h3>1、交易计算</h3>
</div>
<div class="tool-card-body">
<div class="calc-input-row">
<label class="calc-label">原始价格</label>
<input type="number" id="calc-price" class="calc-input" placeholder="输入价格" step="0.01" min="0" />
<label class="calc-label">股数</label>
<input type="number" id="calc-shares" class="calc-input" placeholder="输入股数" step="100" min="100" />
<label class="calc-label">幅度</label>
<div class="calc-amount-group">
<select id="calc-amount-select" class="calc-select">
<option value="5">5%</option>
<option value="10">10%</option>
<option value="15">15%</option>
<option value="20">20%</option>
<option value="25">25%</option>
<option value="30">30%</option>
</select>
<input type="number" id="calc-amount-input" class="calc-input calc-input-sm" placeholder="自定义%" step="0.1" min="0" />
</div>
<div class="calc-radio-group">
<label class="calc-radio">
<input type="radio" name="calc-direction" value="up" checked />
<span class="calc-radio-label calc-radio-up">涨</span>
</label>
<label class="calc-radio">
<input type="radio" name="calc-direction" value="down" />
<span class="calc-radio-label calc-radio-down">跌</span>
</label>
<span id="calc-sign-display" class="calc-sign-display">+</span>
</div>
<label class="calc-label">成交金额</label>
<input type="text" id="calc-buy-amount" class="calc-input calc-result-input" readonly />
<button id="btn-calc" class="btn">计算</button>
</div>
<div class="calc-result-row">
<span class="calc-result-label">计算得到,最终价格</span>
<input type="text" id="calc-result-price" class="calc-input calc-result-input" readonly />
<span class="calc-result-label">(含交易费</span>
<input type="text" id="calc-result-fee" class="calc-input calc-result-input" readonly />
<span class="calc-result-label">),股数</span>
<input type="text" id="calc-result-shares" class="calc-input calc-result-input" readonly />
<span class="calc-result-label">最终成交金额</span>
<input type="text" id="calc-sell-amount" class="calc-input calc-result-input" readonly />
<span class="calc-result-label">差额</span>
<input type="text" id="calc-diff" class="calc-input calc-result-input" readonly />
</div>
</div>
</div>
<!-- 未来工具预留区域 -->
<div class="tools-placeholder" id="tools-placeholder">
<p class="tools-placeholder-text">更多工具开发中...</p>
</div>
</div>
</section>
</div> </div>
</main> </main>
@@ -281,6 +343,7 @@
<script src="/common/common.js"></script> <script src="/common/common.js"></script>
<script src="/tools/markets/markets.js?v=3"></script> <script src="/tools/markets/markets.js?v=3"></script>
<script src="/tools/markets/markets_tools.js?v=1"></script>
<script> <script>
(async function(){ (async function(){
try { try {
+206
View File
@@ -210,6 +210,212 @@ input[type="date"]:hover {
.markets-weekly-stat-range { font-size: 15px } .markets-weekly-stat-range { font-size: 15px }
} }
/* ===== 工具箱样式 ===== */
.tools-container {
display: flex;
flex-direction: column;
gap: 20px;
}
.tool-card {
border: 1px solid #e8e8e8;
border-radius: 12px;
background: #ffffff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
overflow: hidden;
}
.tool-card-header {
padding: 14px 16px;
border-bottom: 1px solid #e8e8e8;
background: linear-gradient(135deg, #f8fbff, #ffffff);
}
.tool-card-header h3 {
margin: 0;
font-size: 16px;
font-weight: 700;
color: #111827;
}
.tool-card-body {
padding: 16px;
}
.calc-input-row {
display: flex;
flex-wrap: wrap;
gap: 10px;
align-items: center;
margin-bottom: 16px;
}
.calc-label {
font-size: 14px;
font-weight: 500;
color: #374151;
white-space: nowrap;
}
.calc-input {
width: 100px;
padding: 6px 10px;
border: 1px solid #d9d9d9;
border-radius: 6px;
font-size: 14px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif;
background: #ffffff;
color: #262626;
transition: all 0.2s ease;
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.03);
}
.calc-input:focus {
outline: none;
border-color: #1677ff;
box-shadow: 0 0 0 2px rgba(22, 119, 255, 0.1);
}
.calc-input:hover {
border-color: #4096ff;
}
.calc-input-sm {
width: 70px;
}
.calc-result-input {
background: #f8f9fa;
font-weight: 600;
color: #111827;
}
.calc-amount-group {
display: flex;
gap: 6px;
align-items: center;
}
.calc-select {
padding: 6px 10px;
border: 1px solid #d9d9d9;
border-radius: 6px;
font-size: 14px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif;
background: #ffffff;
color: #262626;
cursor: pointer;
transition: all 0.2s ease;
}
.calc-select:focus {
outline: none;
border-color: #1677ff;
box-shadow: 0 0 0 2px rgba(22, 119, 255, 0.1);
}
.calc-radio-group {
display: flex;
align-items: center;
gap: 8px;
}
.calc-radio {
display: flex;
align-items: center;
cursor: pointer;
}
.calc-radio input[type="radio"] {
margin: 0 4px 0 0;
cursor: pointer;
}
.calc-radio-label {
font-size: 14px;
font-weight: 500;
user-select: none;
}
.calc-radio-up {
color: #f5222d;
}
.calc-radio-down {
color: #52c41a;
}
.calc-sign-display {
display: inline-block;
min-width: 18px;
font-size: 16px;
font-weight: 700;
text-align: center;
margin-left: 4px;
}
.calc-sign-display.is-up {
color: #f5222d;
}
.calc-sign-display.is-down {
color: #52c41a;
}
.calc-result-row {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
padding: 12px 16px;
background: linear-gradient(135deg, #f8fbff, #ffffff);
border: 1px solid #e8e8e8;
border-radius: 8px;
}
.calc-result-label {
font-size: 14px;
color: #64748b;
white-space: nowrap;
}
.tools-placeholder {
border: 1px dashed #cbd5e1;
border-radius: 12px;
padding: 30px;
text-align: center;
background: #f8fafc;
}
.tools-placeholder-text {
margin: 0;
font-size: 14px;
color: #94a3b8;
}
@media (max-width: 768px) {
.calc-input-row {
gap: 8px;
}
.calc-input {
width: 80px;
font-size: 13px;
}
.calc-input-sm {
width: 60px;
}
.calc-label {
font-size: 13px;
}
.calc-result-row {
padding: 10px 12px;
gap: 6px;
}
.calc-result-label {
font-size: 13px;
}
}
#news-table { table-layout: fixed } #news-table { table-layout: fixed }
#news-table td { word-wrap: break-word; overflow-wrap: break-word } #news-table td { word-wrap: break-word; overflow-wrap: break-word }
+24 -9
View File
@@ -1710,31 +1710,33 @@ const switchPanel = which => {
const wkTab = el('m-tab-weekly') const wkTab = el('m-tab-weekly')
const newsTab = el('m-tab-news') const newsTab = el('m-tab-news')
const wisdomTab = el('m-tab-wisdom') const wisdomTab = el('m-tab-wisdom')
const toolsTab = el('m-tab-tools')
const rt = el('m-panel-realtime') const rt = el('m-panel-realtime')
const ar = el('m-panel-archive') const ar = el('m-panel-archive')
const cal = el('m-panel-calendar') const cal = el('m-panel-calendar')
const wk = el('m-panel-weekly') const wk = el('m-panel-weekly')
const news = el('m-panel-news') const news = el('m-panel-news')
const wisdom = el('m-panel-wisdom') const wisdom = el('m-panel-wisdom')
const tools = el('m-panel-tools')
if (!rtTab || !arTab || !calTab || !wkTab || !rt || !ar || !cal || !wk) return if (!rtTab || !arTab || !calTab || !wkTab || !rt || !ar || !cal || !wk) return
if (which === 'archive') { if (which === 'archive') {
rtTab.classList.remove('active'); arTab.classList.add('active'); calTab.classList.remove('active'); wkTab.classList.remove('active'); if (newsTab) newsTab.classList.remove('active'); if (wisdomTab) wisdomTab.classList.remove('active') rtTab.classList.remove('active'); arTab.classList.add('active'); calTab.classList.remove('active'); wkTab.classList.remove('active'); if (newsTab) newsTab.classList.remove('active'); if (wisdomTab) wisdomTab.classList.remove('active'); if (toolsTab) toolsTab.classList.remove('active')
rt.style.display = 'none'; ar.style.display = ''; cal.style.display = 'none'; wk.style.display = 'none'; if (news) news.style.display = 'none'; if (wisdom) wisdom.style.display = 'none' rt.style.display = 'none'; ar.style.display = ''; cal.style.display = 'none'; wk.style.display = 'none'; if (news) news.style.display = 'none'; if (wisdom) wisdom.style.display = 'none'; if (tools) tools.style.display = 'none'
loadArchive() loadArchive()
} else if (which === 'calendar') { } else if (which === 'calendar') {
rtTab.classList.remove('active'); arTab.classList.remove('active'); calTab.classList.add('active'); wkTab.classList.remove('active'); if (newsTab) newsTab.classList.remove('active'); if (wisdomTab) wisdomTab.classList.remove('active') rtTab.classList.remove('active'); arTab.classList.remove('active'); calTab.classList.add('active'); wkTab.classList.remove('active'); if (newsTab) newsTab.classList.remove('active'); if (wisdomTab) wisdomTab.classList.remove('active'); if (toolsTab) toolsTab.classList.remove('active')
rt.style.display = 'none'; ar.style.display = 'none'; cal.style.display = ''; wk.style.display = 'none'; if (news) news.style.display = 'none'; if (wisdom) wisdom.style.display = 'none' rt.style.display = 'none'; ar.style.display = 'none'; cal.style.display = ''; wk.style.display = 'none'; if (news) news.style.display = 'none'; if (wisdom) wisdom.style.display = 'none'; if (tools) tools.style.display = 'none'
if (!calendarState.month || !Array.isArray(calendarState.days) || calendarState.days.length === 0) loadSecurityCalendar(false) if (!calendarState.month || !Array.isArray(calendarState.days) || calendarState.days.length === 0) loadSecurityCalendar(false)
} else if (which === 'weekly') { } else if (which === 'weekly') {
rtTab.classList.remove('active'); arTab.classList.remove('active'); calTab.classList.remove('active'); wkTab.classList.add('active'); if (newsTab) newsTab.classList.remove('active'); if (wisdomTab) wisdomTab.classList.remove('active') rtTab.classList.remove('active'); arTab.classList.remove('active'); calTab.classList.remove('active'); wkTab.classList.add('active'); if (newsTab) newsTab.classList.remove('active'); if (wisdomTab) wisdomTab.classList.remove('active'); if (toolsTab) toolsTab.classList.remove('active')
rt.style.display = 'none'; ar.style.display = 'none'; cal.style.display = 'none'; wk.style.display = ''; if (news) news.style.display = 'none'; if (wisdom) wisdom.style.display = 'none' rt.style.display = 'none'; ar.style.display = 'none'; cal.style.display = 'none'; wk.style.display = ''; if (news) news.style.display = 'none'; if (wisdom) wisdom.style.display = 'none'; if (tools) tools.style.display = 'none'
if (!weeklyState.weekStart || !Array.isArray(weeklyState.rows) || weeklyState.rows.length === 0) { if (!weeklyState.weekStart || !Array.isArray(weeklyState.rows) || weeklyState.rows.length === 0) {
setWeeklyIdleState('请选择周后点击“加载”读取历史,或点“刷新并补存”主动更新。', { clearData: false }) setWeeklyIdleState('请选择周后点击“加载”读取历史,或点“刷新并补存”主动更新。', { clearData: false })
} }
} else if (which === 'news') { } else if (which === 'news') {
rtTab.classList.remove('active'); arTab.classList.remove('active'); calTab.classList.remove('active'); wkTab.classList.remove('active'); if (wisdomTab) wisdomTab.classList.remove('active') rtTab.classList.remove('active'); arTab.classList.remove('active'); calTab.classList.remove('active'); wkTab.classList.remove('active'); if (wisdomTab) wisdomTab.classList.remove('active'); if (toolsTab) toolsTab.classList.remove('active')
if (newsTab) newsTab.classList.add('active') if (newsTab) newsTab.classList.add('active')
rt.style.display = 'none'; ar.style.display = 'none'; cal.style.display = 'none'; wk.style.display = 'none'; if (news) news.style.display = ''; if (wisdom) wisdom.style.display = 'none' rt.style.display = 'none'; ar.style.display = 'none'; cal.style.display = 'none'; wk.style.display = 'none'; if (news) news.style.display = ''; if (wisdom) wisdom.style.display = 'none'; if (tools) tools.style.display = 'none'
loadGlobalNews(false) loadGlobalNews(false)
} else if (which === 'wisdom') { } else if (which === 'wisdom') {
rtTab.classList.remove('active'); arTab.classList.remove('active'); calTab.classList.remove('active'); wkTab.classList.remove('active') rtTab.classList.remove('active'); arTab.classList.remove('active'); calTab.classList.remove('active'); wkTab.classList.remove('active')
@@ -1743,11 +1745,23 @@ const switchPanel = which => {
rt.style.display = 'none'; ar.style.display = 'none'; cal.style.display = 'none'; wk.style.display = 'none' rt.style.display = 'none'; ar.style.display = 'none'; cal.style.display = 'none'; wk.style.display = 'none'
if (news) news.style.display = 'none' if (news) news.style.display = 'none'
if (wisdom) wisdom.style.display = '' if (wisdom) wisdom.style.display = ''
if (tools) tools.style.display = 'none'
} else if (which === 'tools') {
rtTab.classList.remove('active'); arTab.classList.remove('active'); calTab.classList.remove('active'); wkTab.classList.remove('active')
if (newsTab) newsTab.classList.remove('active')
if (wisdomTab) wisdomTab.classList.remove('active')
if (toolsTab) toolsTab.classList.add('active')
rt.style.display = 'none'; ar.style.display = 'none'; cal.style.display = 'none'; wk.style.display = 'none'
if (news) news.style.display = 'none'
if (wisdom) wisdom.style.display = 'none'
if (tools) tools.style.display = ''
} else { } else {
rtTab.classList.add('active'); arTab.classList.remove('active'); calTab.classList.remove('active'); wkTab.classList.remove('active'); if (newsTab) newsTab.classList.remove('active') rtTab.classList.add('active'); arTab.classList.remove('active'); calTab.classList.remove('active'); wkTab.classList.remove('active'); if (newsTab) newsTab.classList.remove('active')
if (wisdomTab) wisdomTab.classList.remove('active') if (wisdomTab) wisdomTab.classList.remove('active')
if (toolsTab) toolsTab.classList.remove('active')
rt.style.display = ''; ar.style.display = 'none'; cal.style.display = 'none'; wk.style.display = 'none'; if (news) news.style.display = 'none' rt.style.display = ''; ar.style.display = 'none'; cal.style.display = 'none'; wk.style.display = 'none'; if (news) news.style.display = 'none'
if (wisdom) wisdom.style.display = 'none' if (wisdom) wisdom.style.display = 'none'
if (tools) tools.style.display = 'none'
} }
} }
@@ -1776,6 +1790,7 @@ document.addEventListener('DOMContentLoaded', () => {
const stWk = el('m-tab-weekly'); if (stWk) stWk.addEventListener('click', () => switchPanel('weekly')) const stWk = el('m-tab-weekly'); if (stWk) stWk.addEventListener('click', () => switchPanel('weekly'))
const stNews = el('m-tab-news'); if (stNews) stNews.addEventListener('click', () => switchPanel('news')) const stNews = el('m-tab-news'); if (stNews) stNews.addEventListener('click', () => switchPanel('news'))
const stWisdom = el('m-tab-wisdom'); if (stWisdom) stWisdom.addEventListener('click', () => switchPanel('wisdom')) const stWisdom = el('m-tab-wisdom'); if (stWisdom) stWisdom.addEventListener('click', () => switchPanel('wisdom'))
const stTools = el('m-tab-tools'); if (stTools) stTools.addEventListener('click', () => switchPanel('tools'))
const cbp = el('cal-btn-prev'); if (cbp) cbp.addEventListener('click', () => navigateMonthCalendar(-1)) const cbp = el('cal-btn-prev'); if (cbp) cbp.addEventListener('click', () => navigateMonthCalendar(-1))
const cbc = el('cal-btn-current'); if (cbc) cbc.addEventListener('click', () => { const i = el('cal-month'); if (i) i.value = todayMonthLocal(); loadSecurityCalendar(false) }) const cbc = el('cal-btn-current'); if (cbc) cbc.addEventListener('click', () => { const i = el('cal-month'); if (i) i.value = todayMonthLocal(); loadSecurityCalendar(false) })
const cbn = el('cal-btn-next'); if (cbn) cbn.addEventListener('click', () => navigateMonthCalendar(1)) const cbn = el('cal-btn-next'); if (cbn) cbn.addEventListener('click', () => navigateMonthCalendar(1))
@@ -1892,7 +1907,7 @@ document.addEventListener('DOMContentLoaded', () => {
}) })
} }
loadLogs() loadLogs()
switchPanel(query.tab === 'calendar' ? 'calendar' : (query.tab === 'weekly' ? 'weekly' : 'realtime')) switchPanel(query.tab === 'calendar' ? 'calendar' : (query.tab === 'weekly' ? 'weekly' : (query.tab === 'tools' ? 'tools' : 'realtime')))
} catch (e) { } catch (e) {
logLocal(`frontend.error ${String(e.message || e)}`) logLocal(`frontend.error ${String(e.message || e)}`)
} }
+124
View File
@@ -0,0 +1,124 @@
// ===== 工具箱:交易计算 =====
const calcState = {
direction: 'up'
}
const updateCalcSign = () => {
const signEl = document.getElementById('calc-sign-display')
if (!signEl) return
const radios = document.querySelectorAll('input[name="calc-direction"]')
radios.forEach(r => {
if (r.checked) calcState.direction = r.value
})
if (calcState.direction === 'up') {
signEl.textContent = '+'
signEl.className = 'calc-sign-display is-up'
} else {
signEl.textContent = '-'
signEl.className = 'calc-sign-display is-down'
}
}
const getCalcAmount = () => {
const inputEl = document.getElementById('calc-amount-input')
const selectEl = document.getElementById('calc-amount-select')
const inputValue = inputEl ? parseFloat(inputEl.value) : NaN
if (Number.isFinite(inputValue) && inputValue > 0) return inputValue
const selectValue = selectEl ? parseFloat(selectEl.value) : NaN
return Number.isFinite(selectValue) ? selectValue : 0
}
// A 股费率:佣金万 1.0,最低 5 元
const calcCommission = amount => {
const rate = 0.0001
const fee = amount * rate
return Math.max(fee, 5)
}
// 过户费:万 0.1
const calcTransferFee = amount => {
return amount * 0.00001
}
// 印花税:卖出 0.1%
const calcStampTax = amount => {
return amount * 0.001
}
const doTradeCalc = () => {
const priceEl = document.getElementById('calc-price')
const sharesEl = document.getElementById('calc-shares')
const resultPriceEl = document.getElementById('calc-result-price')
const resultFeeEl = document.getElementById('calc-result-fee')
const resultSharesEl = document.getElementById('calc-result-shares')
const buyAmountEl = document.getElementById('calc-buy-amount')
const sellAmountEl = document.getElementById('calc-sell-amount')
const diffEl = document.getElementById('calc-diff')
const price = priceEl ? parseFloat(priceEl.value) : NaN
const shares = sharesEl ? parseFloat(sharesEl.value) : NaN
if (!Number.isFinite(price) || price <= 0 || !Number.isFinite(shares) || shares <= 0) {
if (resultPriceEl) resultPriceEl.value = ''
if (resultFeeEl) resultFeeEl.value = ''
if (resultSharesEl) resultSharesEl.value = ''
if (buyAmountEl) buyAmountEl.value = ''
if (sellAmountEl) sellAmountEl.value = ''
if (diffEl) diffEl.value = ''
return
}
const amount = getCalcAmount()
if (amount <= 0) return
const sign = calcState.direction === 'up' ? 1 : -1
const theoreticalPrice = price * (1 + sign * amount / 100)
if (theoreticalPrice <= 0) {
if (resultPriceEl) resultPriceEl.value = ''
if (resultFeeEl) resultFeeEl.value = ''
if (resultSharesEl) resultSharesEl.value = ''
if (buyAmountEl) buyAmountEl.value = ''
if (sellAmountEl) sellAmountEl.value = ''
if (diffEl) diffEl.value = ''
return
}
const buyAmountVal = price * shares
const sellAmountVal = theoreticalPrice * shares
const buyCommission = calcCommission(buyAmountVal)
const buyTransferFee = calcTransferFee(buyAmountVal)
const sellCommission = calcCommission(sellAmountVal)
const sellTransferFee = calcTransferFee(sellAmountVal)
const sellStampTax = calcStampTax(sellAmountVal)
const totalFees = buyCommission + buyTransferFee + sellCommission + sellTransferFee + sellStampTax
// 差额 = 卖出金额 - 买入金额 - 交易费
const diff = sellAmountVal - buyAmountVal - totalFees
if (resultPriceEl) resultPriceEl.value = theoreticalPrice.toFixed(3)
if (resultFeeEl) resultFeeEl.value = totalFees.toFixed(2)
if (resultSharesEl) resultSharesEl.value = shares
if (buyAmountEl) buyAmountEl.value = buyAmountVal.toFixed(2)
if (sellAmountEl) sellAmountEl.value = sellAmountVal.toFixed(2)
if (diffEl) diffEl.value = diff.toFixed(2)
}
// ===== 工具箱初始化 =====
document.addEventListener('DOMContentLoaded', () => {
// 幅度符号更新
const amountSelect = document.getElementById('calc-amount-select')
const amountInput = document.getElementById('calc-amount-input')
if (amountSelect) amountSelect.addEventListener('change', updateCalcSign)
if (amountInput) amountInput.addEventListener('input', updateCalcSign)
const directionRadios = document.querySelectorAll('input[name="calc-direction"]')
directionRadios.forEach(r => r.addEventListener('change', updateCalcSign))
updateCalcSign()
// 计算按钮
const btnCalc = document.getElementById('btn-calc')
if (btnCalc) btnCalc.addEventListener('click', doTradeCalc)
})