feat(admin,api,player): 结算预览分页、统计图表与返水限额
完善结算计算与预览 API(含后端分页),加强管理端结算/返水/权限,并优化玩家端投注单与队徽展示。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
123
apps/admin/src/utils/settlement-charts.ts
Normal file
123
apps/admin/src/utils/settlement-charts.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import type { EChartsOption } from 'echarts';
|
||||
import { buildBarChartOption, buildPieChartOption, type PieSegment } from './dashboard-charts';
|
||||
|
||||
const STATUS_COLORS: Record<string, string> = {
|
||||
PENDING: '#e8a040',
|
||||
WON: '#2fb56a',
|
||||
LOST: '#ff453a',
|
||||
PUSH: '#8e8e93',
|
||||
VOID: '#555',
|
||||
};
|
||||
|
||||
function withCompactHeader(option: EChartsOption, text: string, subtext?: string): EChartsOption {
|
||||
const grid = (option.grid ?? {}) as { top?: number; bottom?: number; left?: number; right?: number };
|
||||
const top = subtext ? 36 : 24;
|
||||
return {
|
||||
...option,
|
||||
title: {
|
||||
text,
|
||||
subtext,
|
||||
left: 'center',
|
||||
top: 0,
|
||||
textStyle: { color: '#ccc', fontSize: 11, fontWeight: 600 },
|
||||
subtextStyle: { color: '#777', fontSize: 9 },
|
||||
itemGap: 2,
|
||||
},
|
||||
grid: { ...grid, top: Math.max(top, grid.top ?? 0) },
|
||||
};
|
||||
}
|
||||
|
||||
function compactPie(option: EChartsOption, centerLines?: string[]): EChartsOption {
|
||||
const series = option.series as Array<Record<string, unknown>> | undefined;
|
||||
if (series?.[0]) {
|
||||
series[0].center = ['50%', '55%'];
|
||||
series[0].radius = ['34%', '50%'];
|
||||
series[0].label = { show: false };
|
||||
series[0].labelLine = { show: false };
|
||||
}
|
||||
|
||||
const lines = centerLines?.filter(Boolean) ?? [];
|
||||
const graphic = lines.map((line, i) => ({
|
||||
type: 'text' as const,
|
||||
left: 'center',
|
||||
top: lines.length === 1 ? '52%' : `${48 + i * 12}%`,
|
||||
style: {
|
||||
text: line,
|
||||
textAlign: 'center' as const,
|
||||
fill: i === 0 ? '#eee' : '#888',
|
||||
fontSize: i === 0 ? 15 : 9,
|
||||
fontWeight: i === 0 ? 700 : 400,
|
||||
},
|
||||
}));
|
||||
|
||||
return {
|
||||
...option,
|
||||
legend: {
|
||||
bottom: 0,
|
||||
left: 'center',
|
||||
itemWidth: 8,
|
||||
itemHeight: 8,
|
||||
textStyle: { color: '#999', fontSize: 9 },
|
||||
},
|
||||
graphic,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildBetTypePieOption(input: {
|
||||
title: string;
|
||||
singleBets: number;
|
||||
parlayBets: number;
|
||||
totalBets: number;
|
||||
legCount: number;
|
||||
labels: { single: string; parlay: string };
|
||||
subtext: string;
|
||||
centerLabel: string;
|
||||
}): EChartsOption {
|
||||
const segments: PieSegment[] = [
|
||||
{ label: input.labels.single, value: input.singleBets, color: '#2fb56a' },
|
||||
{ label: input.labels.parlay, value: input.parlayBets, color: '#fb923c' },
|
||||
].filter((s) => s.value > 0);
|
||||
|
||||
const base = buildPieChartOption(input.title, segments, { pieTooltip: '{b}:{c}({d}%)' });
|
||||
const withHeader = withCompactHeader(base, input.title, input.subtext);
|
||||
return compactPie(withHeader, [String(input.totalBets), input.centerLabel]);
|
||||
}
|
||||
|
||||
export function buildStatusPieOption(input: {
|
||||
title: string;
|
||||
statusCounts: Record<string, number>;
|
||||
labelFor: (status: string) => string;
|
||||
subtext: string;
|
||||
}): EChartsOption {
|
||||
const segments: PieSegment[] = Object.entries(input.statusCounts)
|
||||
.filter(([, count]) => count > 0)
|
||||
.map(([status, value]) => ({
|
||||
label: input.labelFor(status),
|
||||
value,
|
||||
color: STATUS_COLORS[status] ?? '#666',
|
||||
}));
|
||||
|
||||
const total = segments.reduce((sum, s) => sum + s.value, 0);
|
||||
const base = buildPieChartOption(input.title, segments, { pieTooltip: '{b}:{c}({d}%)' });
|
||||
const withHeader = withCompactHeader(base, input.title, input.subtext);
|
||||
return compactPie(withHeader, [String(total)]);
|
||||
}
|
||||
|
||||
export function buildSelectionStakeBarOption(input: {
|
||||
title: string;
|
||||
subtext: string;
|
||||
labels: string[];
|
||||
stakes: number[];
|
||||
seriesName: string;
|
||||
}): EChartsOption {
|
||||
const base = buildBarChartOption(
|
||||
input.labels,
|
||||
[{ name: input.seriesName, color: '#2fb56a', values: input.stakes }],
|
||||
{ amountAxis: true },
|
||||
);
|
||||
return withCompactHeader(
|
||||
{ ...base, grid: { left: 40, right: 6, top: 36, bottom: 22 }, legend: { show: false } },
|
||||
input.title,
|
||||
input.subtext,
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user