feat: Refactor settings UI and fix commission fee recording

Settings improvements:
- Reorganize config groups with logical ordering (server, auth, ai, trading, etc.)
- Add description/tooltip for each config item with question mark icon
- Add icon to each group header
- Support i18n for descriptions (zh-CN, zh-TW, en-US)
- Move order execution config (ORDER_MODE, MAKER_WAIT_SEC, MAKER_OFFSET_BPS) to env

Commission fee fixes:
- Fix fee extraction in exchange clients: Bybit, Coinbase, Kraken, Gate, Kucoin, Bitfinex
- Properly accumulate and record commission fees in pending_order_worker
- Add fee/fee_ccy fields to wait_for_fill returns

Frontend updates:
- Remove order_mode config from trading-assistant frontend (now uses env config)
- Add sorted schema display by order field
- Add tooltip with description on hover
This commit is contained in:
TIANHE
2026-01-12 00:15:52 +08:00
parent e7cb9c6493
commit 5a4c770279
16 changed files with 1121 additions and 233 deletions
+94 -26
View File
@@ -28,15 +28,13 @@
<a-spin :spinning="loading">
<div class="settings-content">
<a-collapse v-model="activeKeys" :bordered="false" class="settings-collapse">
<a-collapse-panel v-for="(group, groupKey) in schema" :key="groupKey">
<a-collapse-panel v-for="(group, groupKey) in sortedSchema" :key="groupKey">
<template slot="header">
<span class="panel-header">
<a-icon :type="group.icon || getGroupIcon(groupKey)" class="panel-icon-left" />
<span class="panel-title">{{ getGroupTitle(groupKey, group.title) }}</span>
</span>
</template>
<template slot="extra">
<a-icon :type="getGroupIcon(groupKey)" class="panel-icon" />
</template>
<a-form :form="form" layout="vertical" class="settings-form">
<a-row :gutter="24">
@@ -49,8 +47,14 @@
:key="item.key">
<a-form-item>
<template slot="label">
<span class="form-label-with-link">
<span>{{ getItemLabel(groupKey, item) }}</span>
<span class="form-label-with-tooltip">
<span class="label-text">{{ getItemLabel(groupKey, item) }}</span>
<a-tooltip v-if="item.description" placement="top">
<template slot="title">
{{ getItemDescription(groupKey, item) }}
</template>
<a-icon type="question-circle" class="help-icon" />
</a-tooltip>
<a
v-if="item.link"
:href="item.link"
@@ -158,7 +162,7 @@ export default {
saving: false,
schema: {},
values: {},
activeKeys: ['ai', 'data_source', 'app', 'auth'],
activeKeys: ['server', 'auth', 'ai', 'trading'],
passwordVisible: {},
showRestartTip: false
}
@@ -166,6 +170,20 @@ export default {
computed: {
isDarkTheme () {
return this.navTheme === 'dark' || this.navTheme === 'realdark'
},
// 按 order 排序的 schema
sortedSchema () {
const entries = Object.entries(this.schema)
entries.sort((a, b) => {
const orderA = a[1].order || 999
const orderB = b[1].order || 999
return orderA - orderB
})
const sorted = {}
for (const [key, value] of entries) {
sorted[key] = value
}
return sorted
}
},
beforeCreate () {
@@ -203,15 +221,16 @@ export default {
server: 'cloud-server',
worker: 'schedule',
notification: 'notification',
smtp: 'mail',
twilio: 'phone',
email: 'mail',
sms: 'phone',
strategy: 'fund',
proxy: 'global',
network: 'global',
app: 'appstore',
ai: 'robot',
market: 'stock',
trading: 'stock',
data_source: 'database',
search: 'search'
search: 'search',
agent: 'experiment'
}
return icons[groupKey] || 'setting'
},
@@ -228,6 +247,17 @@ export default {
return translated !== key ? translated : item.label
},
getItemDescription (groupKey, item) {
// 先尝试从多语言获取描述
const key = `settings.desc.${item.key}`
const translated = this.$t(key)
if (translated !== key) {
return translated
}
// 回退到后端返回的描述
return item.description || ''
},
getLinkText (linkText) {
if (!linkText) return this.$t('settings.getApi')
// 如果是翻译键(以 settings.link. 开头),则翻译
@@ -400,17 +430,18 @@ export default {
.panel-header {
display: inline-flex;
align-items: center;
gap: 10px;
flex: 1;
.panel-icon-left {
font-size: 18px;
color: @primary-color;
}
.panel-title {
font-size: 16px;
}
}
.panel-icon {
font-size: 18px;
color: @primary-color;
}
}
.ant-collapse-content {
@@ -432,10 +463,27 @@ export default {
font-weight: 500;
}
.form-label-with-link {
.form-label-with-tooltip {
display: flex;
align-items: center;
gap: 8px;
gap: 6px;
flex-wrap: wrap;
.label-text {
color: #475569;
font-weight: 500;
}
.help-icon {
font-size: 14px;
color: #94a3b8;
cursor: help;
transition: color 0.2s;
&:hover {
color: @primary-color;
}
}
.api-link {
font-size: 12px;
@@ -449,6 +497,7 @@ export default {
background: rgba(24, 144, 255, 0.08);
border-radius: 4px;
transition: all 0.2s;
margin-left: 4px;
&:hover {
background: rgba(24, 144, 255, 0.15);
@@ -540,8 +589,13 @@ export default {
color: #e0e6ed;
border-bottom-color: rgba(255, 255, 255, 0.06);
.panel-header .panel-title {
color: #e0e6ed;
.panel-header {
.panel-icon-left {
color: #58a6ff;
}
.panel-title {
color: #e0e6ed;
}
}
}
@@ -561,12 +615,26 @@ export default {
color: #c9d1d9;
}
.form-label-with-link .api-link {
background: rgba(24, 144, 255, 0.15);
color: #58a6ff;
.form-label-with-tooltip {
.label-text {
color: #c9d1d9;
}
&:hover {
background: rgba(24, 144, 255, 0.25);
.help-icon {
color: #6e7681;
&:hover {
color: #58a6ff;
}
}
.api-link {
background: rgba(24, 144, 255, 0.15);
color: #58a6ff;
&:hover {
background: rgba(24, 144, 255, 0.25);
}
}
}
}
@@ -2570,8 +2570,7 @@ export default {
trade_direction: tradeDirection,
timeframe: values.timeframe,
market_type: marketType,
// Preset order params (advanced settings removed from UI)
order_mode: 'maker',
// Order execution settings moved to backend env config (ORDER_MODE, MAKER_WAIT_SEC, MAKER_OFFSET_BPS)
margin_mode: 'cross',
signal_mode: 'confirmed',
// Backtest-like configs