+
{{ formatMoney(tx.amount, locale) }}
›
@@ -362,6 +364,7 @@ const pullIndicatorStyle = () => ({
.tx-type { font-weight: 600; color: var(--text); letter-spacing: -0.01em; }
.pos { color: var(--success); font-weight: 600; font-size: 14px; font-variant-numeric: tabular-nums; }
.neg { color: var(--text); font-weight: 600; font-size: 14px; font-variant-numeric: tabular-nums; }
+.zero { color: var(--text-muted); font-weight: 600; font-size: 14px; font-variant-numeric: tabular-nums; }
.tx-time { font-size: 11px; color: var(--text-dim); }
.tx-arrow { font-size: 15px; color: var(--text-dim); font-weight: 400; line-height: 1; }
diff --git a/apps/player/src/views/WalletTransactionDetailView.vue b/apps/player/src/views/WalletTransactionDetailView.vue
index cbf3810..c2c5eb1 100644
--- a/apps/player/src/views/WalletTransactionDetailView.vue
+++ b/apps/player/src/views/WalletTransactionDetailView.vue
@@ -4,7 +4,7 @@ import { useRoute, useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import api from '../api';
import { formatMoney } from '../utils/localeDisplay';
-import { txTypeKey, isCashbackType, txDisplayType, txSummaryLabel, txRemarkLabel, isDepositReversalType } from '../utils/walletTx';
+import { txTypeKey, isCashbackType, txDisplayType, txAmountClass, txSummaryLabel, txRemarkLabel, isDepositReversalType } from '../utils/walletTx';
import GoldSpinner from '../components/GoldSpinner.vue';
import { usePullToRefresh } from '../composables/usePullToRefresh';
@@ -81,10 +81,7 @@ const summaryText = computed(() => {
return txSummaryLabel(tx.value, t);
});
-const amountClass = computed(() => {
- if (!tx.value) return '';
- return parseFloat(tx.value.amount) >= 0 ? 'pos' : 'neg';
-});
+const amountClass = computed(() => (tx.value ? txAmountClass(tx.value.amount) : 'zero'));
const formattedTime = computed(() => {
if (!tx.value) return '';
@@ -279,6 +276,11 @@ function goCashbackDetail() {
background: linear-gradient(135deg, rgba(255, 255, 255, 0.16), rgba(255, 255, 255, 0.05));
}
+.hero.zero {
+ border-color: var(--border);
+ background: var(--bg-elevated);
+}
+
.hero-type {
font-size: 12px;
font-weight: 800;
@@ -303,6 +305,7 @@ function goCashbackDetail() {
.hero.pos .hero-amount { color: var(--success); }
.hero.neg .hero-amount { color: var(--primary); }
+.hero.zero .hero-amount { color: var(--text-muted); }
.hero-time {
font-size: 11px;
@@ -350,6 +353,7 @@ function goCashbackDetail() {
.pos { color: var(--success) !important; }
.neg { color: var(--primary) !important; }
+.zero { color: var(--text-muted) !important; }
.mono {
font-family: ui-monospace, monospace;
diff --git a/apps/player/src/views/WalletView.vue b/apps/player/src/views/WalletView.vue
index 2ff1245..8df0075 100644
--- a/apps/player/src/views/WalletView.vue
+++ b/apps/player/src/views/WalletView.vue
@@ -10,7 +10,7 @@ import api from '../api';
import { formatMoney } from '../utils/localeDisplay';
-import { txTypeKey, txDisplayType, txSummaryLabel } from '../utils/walletTx';
+import { txTypeKey, txDisplayType, txAmountClass, txSummaryLabel } from '../utils/walletTx';
import GoldSpinner from '../components/GoldSpinner.vue';
@@ -38,6 +38,10 @@ type Transaction = {
amount: string;
+ frozenBefore?: string;
+
+ frozenAfter?: string;
+
createdAt: string;
transactionId: string;
@@ -236,7 +240,7 @@ const pullIndicatorStyle = () => ({
-
+
{{ formatMoney(tx.amount, locale) }}
@@ -530,6 +534,20 @@ const pullIndicatorStyle = () => ({
+.zero {
+
+ color: var(--text-muted);
+
+ font-weight: 600;
+
+ font-size: 14px;
+
+ font-variant-numeric: tabular-nums;
+
+}
+
+
+
.tx-time {
font-size: 11px;
diff --git a/packages/shared/src/api-errors.js b/packages/shared/src/api-errors.js
index 6060efa..53b3f70 100644
--- a/packages/shared/src/api-errors.js
+++ b/packages/shared/src/api-errors.js
@@ -112,6 +112,11 @@ export const API_ERROR_MESSAGES = {
'en-US': 'Cannot unpublish league after outright market is settled',
'ms-MY': 'Liga tidak boleh ditarik selepas pasaran juara diselesaikan',
},
+ LEAGUE_OUTRIGHT_SETTLED: {
+ 'zh-CN': '优胜赛已结算,不可再新增单场',
+ 'en-US': 'Outright market is settled; new fixtures cannot be added',
+ 'ms-MY': 'Pasaran juara telah diselesaikan; perlawanan baharu tidak boleh ditambah',
+ },
MATCH_UNPUBLISH_FORBIDDEN: {
'zh-CN': '当前状态不可下架',
'en-US': 'Match cannot be unpublished in current status',
@@ -747,6 +752,26 @@ export const API_ERROR_MESSAGES = {
'en-US': 'Batch already confirmed',
'ms-MY': 'Batch sudah disahkan',
},
+ SETTLEMENT_BATCH_STALE: {
+ 'zh-CN': '结算批次已过期,请使用最新预览批次',
+ 'en-US': 'Settlement batch is stale; use the latest preview batch',
+ 'ms-MY': 'Batch penyelesaian lapuk; guna batch pratonton terkini',
+ },
+ SETTLEMENT_BET_UPDATE_FAILED: {
+ 'zh-CN': '注单 {betNo} 结算状态更新失败,请重试',
+ 'en-US': 'Failed to update bet {betNo} for settlement',
+ 'ms-MY': 'Gagal mengemas kini pertaruhan {betNo} untuk penyelesaian',
+ },
+ SETTLEMENT_SCORE_INVALID: {
+ 'zh-CN': '比分无效:半场比分不能大于全场比分',
+ 'en-US': 'Invalid score: half-time cannot exceed full-time',
+ 'ms-MY': 'Skor tidak sah: separuh masa tidak boleh melebihi masa penuh',
+ },
+ SETTLEMENT_MARKET_UNSUPPORTED: {
+ 'zh-CN': '盘口 {marketType} 暂不支持结算',
+ 'en-US': 'Market type {marketType} is not supported for settlement',
+ 'ms-MY': 'Jenis pasaran {marketType} belum disokong untuk penyelesaian',
+ },
SCORE_NOT_FOUND: {
'zh-CN': '比分不存在',
'en-US': 'Score not found',
diff --git a/packages/shared/src/api-errors.ts b/packages/shared/src/api-errors.ts
index 65ea1b7..e58b4aa 100644
--- a/packages/shared/src/api-errors.ts
+++ b/packages/shared/src/api-errors.ts
@@ -114,6 +114,11 @@ export const API_ERROR_MESSAGES = {
'en-US': 'Cannot unpublish league after outright market is settled',
'ms-MY': 'Liga tidak boleh ditarik selepas pasaran juara diselesaikan',
},
+ LEAGUE_OUTRIGHT_SETTLED: {
+ 'zh-CN': '优胜赛已结算,不可再新增单场',
+ 'en-US': 'Outright market is settled; new fixtures cannot be added',
+ 'ms-MY': 'Pasaran juara telah diselesaikan; perlawanan baharu tidak boleh ditambah',
+ },
MATCH_UNPUBLISH_FORBIDDEN: {
'zh-CN': '当前状态不可下架',
'en-US': 'Match cannot be unpublished in current status',
@@ -749,6 +754,26 @@ export const API_ERROR_MESSAGES = {
'en-US': 'Batch already confirmed',
'ms-MY': 'Batch sudah disahkan',
},
+ SETTLEMENT_BATCH_STALE: {
+ 'zh-CN': '结算批次已过期,请使用最新预览批次',
+ 'en-US': 'Settlement batch is stale; use the latest preview batch',
+ 'ms-MY': 'Batch penyelesaian lapuk; guna batch pratonton terkini',
+ },
+ SETTLEMENT_BET_UPDATE_FAILED: {
+ 'zh-CN': '注单 {betNo} 结算状态更新失败,请重试',
+ 'en-US': 'Failed to update bet {betNo} for settlement',
+ 'ms-MY': 'Gagal mengemas kini pertaruhan {betNo} untuk penyelesaian',
+ },
+ SETTLEMENT_SCORE_INVALID: {
+ 'zh-CN': '比分无效:半场比分不能大于全场比分',
+ 'en-US': 'Invalid score: half-time cannot exceed full-time',
+ 'ms-MY': 'Skor tidak sah: separuh masa tidak boleh melebihi masa penuh',
+ },
+ SETTLEMENT_MARKET_UNSUPPORTED: {
+ 'zh-CN': '盘口 {marketType} 暂不支持结算',
+ 'en-US': 'Market type {marketType} is not supported for settlement',
+ 'ms-MY': 'Jenis pasaran {marketType} belum disokong untuk penyelesaian',
+ },
SCORE_NOT_FOUND: {
'zh-CN': '比分不存在',
'en-US': 'Score not found',
diff --git a/packages/shared/src/index.js b/packages/shared/src/index.js
index 3cbc3b6..faac469 100644
--- a/packages/shared/src/index.js
+++ b/packages/shared/src/index.js
@@ -126,4 +126,5 @@ export * from './playerUsername';
export * from './initial-depositRemark';
export * from './phone-countries';
export * from './match-time';
+export * from './walletTx';
export * from './api-errors';
diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts
index 5c36d7d..e853092 100644
--- a/packages/shared/src/index.ts
+++ b/packages/shared/src/index.ts
@@ -130,6 +130,7 @@ export * from './playerUsername';
export * from './initial-depositRemark';
export * from './phone-countries';
export * from './match-time';
+export * from './walletTx';
export interface ApiResponse {
success: boolean;
diff --git a/packages/shared/src/walletTx.js b/packages/shared/src/walletTx.js
new file mode 100644
index 0000000..ef5d354
--- /dev/null
+++ b/packages/shared/src/walletTx.js
@@ -0,0 +1,11 @@
+/** 钱包流水展示用金额:输单结算 amount 为 0(可用余额未变),用冻结差额表示亏损 */
+export function txDisplayAmount(tx) {
+ const type = tx.transactionType.toUpperCase();
+ const amt = parseFloat(tx.amount);
+ if (type === 'BET_SETTLE_LOSE' && amt === 0 && tx.frozenBefore != null && tx.frozenAfter != null) {
+ const frozenDelta = parseFloat(tx.frozenBefore) - parseFloat(tx.frozenAfter);
+ if (frozenDelta > 0)
+ return (-frozenDelta).toString();
+ }
+ return tx.amount;
+}
diff --git a/packages/shared/src/walletTx.ts b/packages/shared/src/walletTx.ts
new file mode 100644
index 0000000..90a70ed
--- /dev/null
+++ b/packages/shared/src/walletTx.ts
@@ -0,0 +1,15 @@
+/** 钱包流水展示用金额:输单结算 amount 为 0(可用余额未变),用冻结差额表示亏损 */
+export function txDisplayAmount(tx: {
+ transactionType: string;
+ amount: string;
+ frozenBefore?: string;
+ frozenAfter?: string;
+}): string {
+ const type = tx.transactionType.toUpperCase();
+ const amt = parseFloat(tx.amount);
+ if (type === 'BET_SETTLE_LOSE' && amt === 0 && tx.frozenBefore != null && tx.frozenAfter != null) {
+ const frozenDelta = parseFloat(tx.frozenBefore) - parseFloat(tx.frozenAfter);
+ if (frozenDelta > 0) return (-frozenDelta).toString();
+ }
+ return tx.amount;
+}