feat(admin,api,player): 结算预览分页、统计图表与返水限额

完善结算计算与预览 API(含后端分页),加强管理端结算/返水/权限,并优化玩家端投注单与队徽展示。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-05 13:54:33 +08:00
parent 6264b8806c
commit efff7c27e6
40 changed files with 3560 additions and 578 deletions

View File

@@ -34,7 +34,7 @@ export const useBetSlipStore = defineStore('betSlip', () => {
return Number.isFinite(n) ? n : null;
}
/** 球赛/详情页:仅单关,且投注单同时只能有 1 项 */
/** 球赛/详情页:同场可多选,分笔单关;再点同一项可取消 */
function addItem(item: SlipItem) {
if (mode.value === 'parlay') items.value = [];
mode.value = 'single';
@@ -44,13 +44,13 @@ export const useBetSlipStore = defineStore('betSlip', () => {
(i) => i.selectionId === item.selectionId,
);
if (existing >= 0) {
items.value = [];
items.value.splice(existing, 1);
return;
}
items.value = [item];
items.value.push(item);
}
/** 串关页专用:跨场组合,同场只保留一个选项 */
/** 串关页:同场/跨场均可多选,合成一张串关单 */
function addParlayLeg(item: SlipItem): ParlayRejectReason | 'MAX_LEGS' | null {
if (mode.value === 'single') items.value = [];
mode.value = 'parlay';
@@ -77,7 +77,6 @@ export const useBetSlipStore = defineStore('betSlip', () => {
return 'MAX_LEGS';
}
items.value = items.value.filter((i) => i.matchId !== item.matchId);
items.value.push(item);
lastParlayError.value = null;
return null;
@@ -101,10 +100,10 @@ export const useBetSlipStore = defineStore('betSlip', () => {
const potentialReturn = computed(() => {
if (!items.value.length) return 0;
if (mode.value === 'parlay' && items.value.length >= PARLAY_MIN_LEGS) {
if (mode.value === 'parlay' && canPlaceParlay.value) {
return stake.value * totalOdds.value;
}
return stake.value * items.value[0].odds;
return items.value.reduce((acc, i) => acc + stake.value * i.odds, 0);
});
const hasSameMatch = computed(() => {
@@ -116,8 +115,16 @@ export const useBetSlipStore = defineStore('betSlip', () => {
() =>
mode.value === 'parlay' &&
items.value.length >= PARLAY_MIN_LEGS &&
items.value.length <= PARLAY_MAX_LEGS &&
!hasSameMatch.value,
items.value.length <= PARLAY_MAX_LEGS,
);
/** 详情页等同场多笔单关(串关模式不走此路径) */
const canPlaceBatchSingles = computed(
() => mode.value === 'single' && items.value.length >= 1,
);
const canSubmit = computed(
() => canPlaceParlay.value || canPlaceBatchSingles.value,
);
const drawerOpen = ref(false);
@@ -140,6 +147,8 @@ export const useBetSlipStore = defineStore('betSlip', () => {
potentialReturn,
hasSameMatch,
canPlaceParlay,
canPlaceBatchSingles,
canSubmit,
lastParlayError,
drawerOpen,
addItem,