@@ -16,87 +16,133 @@
> 刷新数据 < / el-button >
< / div >
<!-- 统计卡片网格 -- >
< div class = "stat-grid " >
< div
v-for = "card in statCards"
:key = "card.key"
class = "stat-card"
: class = "{ clickable: !!card.route }"
@click ="card.route && handto(card.route)"
>
< div class = "stat-icon" : class = "'theme-' + card.theme" >
< i :class = "card.icon" > < / i >
< / div >
< div class = "stat-info" >
< div class = "stat-value" > { { card . value } } < / div >
< div class = "stat-label" >
{ { card . label } }
< el-tooltip v-if = "card.note" :content="card.note" placement="top" >
< i class = "el-icon-info stat-note-icon" > < / i >
< / el-tooltip >
< / div >
< / div >
< i v-if = "card.route" class="el-icon-arrow-right stat-arrow" > < / i >
< / div >
< / div >
<!-- 主体 : 左窄轨 + 右侧主区 -- >
< div class = "home-body " >
<!-- 左窄轨 : 核心指标 + 待办 -- >
< div class = "rail" >
< div class = "rail-title" > 核心指标 < / div >
< ! - - 实时 RTP - - >
< div class = "section-card" >
< div class = "section-header" >
< div class = "section-title" >
< span class = "title-bar" > < / span >
< span > 实时 RTP < / span >
< / div >
< span class = "section-sub" > 每 30 秒自动刷新 < / span >
< / div >
< div class = "rtp-grid" >
< div
v-for = "(item, idx) in visibleRtpList"
: key = "item.name || idx"
class = "rtp-card"
: class = "'rtp-theme-' + (idx % 5)"
>
< div class = "rtp-name" >
< span class = "rtp-dot" > < / span >
{ { item . name } }
< / div >
< div class = "rtp-value" >
{ { item . rtp !== null && item . rtp !== undefined ? item . rtp . toFixed ( 2 ) + '%' : '--' } }
< / div >
< div class = "rtp-progress" >
< div class = "rtp-progress-inner" : style = "{ width: Math.min(Number(item.rtp) || 0, 100) + '%' }" > < / div >
< / div >
< div class = "rtp-rows" >
< div class = "rtp-row" >
< span > 投注 < / span >
< span > ¥ { { item . bet !== null && item . bet !== undefined ? Number ( item . bet ) . toFixed ( 2 ) : '--' } } < / span >
< div class = "rail-metrics" >
< div
v-for = "m in railMetrics"
:key = "m.key"
class = "rail-metric"
: class = "{ clickable: !!m.route }"
@click ="m.route && handto(m.route)"
>
< div class = "rail-metric-label" >
< span > { { m . label } } < / span >
< el-tooltip v-if = "m.note" :content="m.note" placement="top" >
< i class = "el-icon-info rail-note" > < / i >
< / el-tooltip >
< / div >
< div class = "rtp-row" >
< span > 奖励 < / span >
< span > ¥ { { item . reward !== null && item . reward !== undefined ? Number ( item . reward ) . toFixed ( 2 ) : '--' } } < / span >
< div class = "rail-metric-value" > { { m . value } } < / div >
< i v-if = "m.route" class="el-icon-arrow-right rail-arrow" > < / i >
< / div >
< / div >
< div class = "rail-todo" >
< div
class = "rail-todo-item clickable"
: class = "{ alert: totaldeliver > 0 }"
@click ="handto(3)"
>
< span class = "rail-todo-label" > 待处理提货申请 < / span >
< span class = "rail-todo-value" > { { totaldeliver === null ? '--' : totaldeliver } } < / span >
< / div >
< div class = "rail-todo-item" >
< span class = "rail-todo-label" > 提货余额 < / span >
< span class = "rail-todo-value" > ¥ { { balance } } < / span >
< / div >
< / div >
< / div >
<!-- 右侧主区 -- >
< div class = "main-col" >
<!-- 实时 RTP -- >
< div class = "section-card" >
< div class = "section-header" >
< div class = "section-title" >
< span class = "title-bar" > < / span >
< span > 实时 RTP < / span >
< / div >
< span class = "section-sub" > 每 30 秒自动刷新 < / span >
< / div >
< div class = "rtp-grid" >
< div
v-for = "(item, idx) in visibleRtpList"
: key = "item.name || idx"
class = "rtp-card"
: class = "'rtp-theme-' + (idx % 5)"
>
< div class = "rtp-name" >
< span class = "rtp-dot" > < / span >
{ { item . name } }
< / div >
< div class = "rtp-value" >
{ { item . rtp !== null && item . rtp !== undefined ? item . rtp . toFixed ( 2 ) + '%' : '--' } }
< / div >
< div class = "rtp-progress" >
< div class = "rtp-progress-inner" : style = "{ width: Math.min(Number(item.rtp) || 0, 100) + '%' }" > < / div >
< / div >
< div class = "rtp-rows" >
< div class = "rtp-row" >
< span > 投注 < / span >
< span > ¥ { { item . bet !== null && item . bet !== undefined ? Number ( item . bet ) . toFixed ( 2 ) : '--' } } < / span >
< / div >
< div class = "rtp-row" >
< span > 奖励 < / span >
< span > ¥ { { item . reward !== null && item . reward !== undefined ? Number ( item . reward ) . toFixed ( 2 ) : '--' } } < / span >
< / div >
< / div >
< / div >
< div v-if = "!visibleRtpList.length" class="rtp-empty" > 暂无 RTP 数据 < / div >
< / div >
< / div >
< ! - - 经营数据 ( 次级指标带 ) — — 放在图表上方 : 图表最高 , 放最后滚到底正好 - - >
< div class = "section-card" >
< div class = "section-header" >
< div class = "section-title" >
< span class = "title-bar" > < / span >
< span > 经营数据 < / span >
< / div >
< / div >
< div class = "mini-stats" >
< div v-for = "s in miniStats" :key="s.key" class="mini-stat" >
< div class = "mini-icon" : class = "'theme-' + s.theme" >
< i :class = "s.icon" > < / i >
< / div >
< div class = "mini-info" >
< div class = "mini-label" >
< span > { { s . label } } < / span >
< el-tooltip v-if = "s.note" :content="s.note" placement="top" >
< i class = "el-icon-info mini-note" > < / i >
< / el-tooltip >
< / div >
< div class = "mini-value" > { { s . value } } < / div >
< / div >
< / div >
< / div >
< / div >
< div v-if = "!visibleRtpList.length" class="rtp-empty" > 暂无 RTP 数据 < / div >
< / div >
< / div >
< ! - - 流水统计图表 - - >
< div class = "section-card" >
< div class = "section-header" >
< div class = "section-title" >
< span class = "title-bar" > < / span >
< span > 流水统计 < / span >
< span class = "section-sub chart-sub" > { { chartTypeLabel } } < / span >
< / div >
< div class = "chart-tabs" >
< span : class = "['tab-item', chartType === 'hour' ? 'active' : '']" @click ="switchChartType('hour')" > 小时 < / span >
< span : class = "['tab-item', chartType === 'day' ? 'active' : '']" @click ="switchChartType('day')" > 天 < / span >
< span : class = "['tab-item', chartType === 'week' ? 'active' : '']" @click ="switchChartType('week')" > 周 < / span >
<!-- 流水统计图表 -- >
< div class = "section-card" >
< div class = "section-header" >
< div class = "section-title" >
< span class = "title-bar" > < / span >
< span > 流水统计 < / span >
< span class = "section-sub chart-sub" > { { chartTypeLabel } } < / span >
< / div >
< div class = "chart-tabs" >
< span : class = "['tab-item', chartType === 'hour' ? 'active' : '']" @click ="switchChartType('hour')" > 小时 < / span >
< span : class = "['tab-item', chartType === 'day' ? 'active' : '']" @click ="switchChartType('day')" > 天 < / span >
< span : class = "['tab-item', chartType === 'week' ? 'active' : '']" @click ="switchChartType('week')" > 周 < / span >
< / div >
< / div >
< div id = "lineChart" style = "width: 100%; height: 400px;" > < / div >
< / div >
< / div >
< div id = "lineChart" style = "width: 100%; height: 400px;" > < / div >
< / div >
< / div >
< / template >
@@ -155,17 +201,39 @@ export default {
chartTypeLabel ( ) {
return this . chartType === "hour" ? "今日(按小时)" : this . chartType === "week" ? "近 4 周" : "近 7 天" ;
} ,
statCards ( ) {
/* 左窄轨:只放「平台体量」三项,给足字号做层级;
其余指标下沉到右侧主区底部的「经营数据」带。 */
railMetrics ( ) {
return [
{
key : "online" ,
label : "在线用户" ,
note : "当前在线的用户数,点击可查看在线用户列表" ,
value : this . total === null ? "--" : this . total ,
route : 1
} ,
{
key : "totalRecharge" ,
label : "总充值" ,
note : "历史累计充值总额(排除无效用户)" ,
value : "¥" + this . totalRecord ,
route : 2
} ,
{
key : "totalFlow" ,
label : "总流水" ,
note : "历史累计全部游戏投注消费总额(已排除无效用户)" ,
value : "¥" + Number ( this . totalFlow ) . toFixed ( 2 )
}
] ;
} ,
/* 右侧主区底部:其余 6 项,紧凑成一条指标带 */
miniStats ( ) {
return [
{ key : "online" , label : "在线用户" , note : "当前在线的用户数,点击可查看在线用户列表" , value : this . total === null ? "--" : this . total , icon : "el-icon-user-solid" , theme : "blue" , route : 1 } ,
{ key : "totalRecharge" , label : "总充值" , note : "历史累计充值总额(排除无效用户)" , value : "¥" + this . totalRecord , icon : "el-icon-s-order" , theme : "orange" , route : 2 } ,
{ key : "deliver" , label : "待处理提货申请" , note : "待审核处理的提货申请数量,点击可前往处理" , value : this . totaldeliver === null ? "--" : this . totaldeliver , icon : "el-icon-shopping-cart-full" , theme : "green" , route : 3 } ,
{ key : "balance" , label : "提货余额" , note : "第三方发货平台账户余额,用于采购发货" , value : "$" + this . balance , icon : "el-icon-wallet" , theme : "red" } ,
{ key : "todayRecharge" , label : "今日总充值" , note : "今日0点至今的充值总额(已排除无效用户)" , value : "¥" + this . todayRecharge , icon : "el-icon-money" , theme : "purple" } ,
{ key : "todayBet" , label : "今日总投注" , note : "今日全部游戏的投注消费总额(已排除无效用户)" , value : "¥" + this . todayBet , icon : "el-icon-coin" , theme : "cyan" } ,
{ key : "todayReward" , label : "今日总返奖" , note : "今日全游戏产出饰品总价值(按背包产出统计,含已分解)" , value : "¥" + this . todayReward , icon : "el-icon-present" , theme : "pink" } ,
{ key : "deliveryAmount" , label : "总发货金额" , note : "已完成发货订单的采购价合计(已排除无效用户)" , value : "¥" + Number ( this . totalDeliveryAmount ) . toFixed ( 2 ) , icon : "el-icon-box" , theme : "amber" } ,
{ key : "totalFlow" , label : "总流水" , note : "历史累计全部游戏投注消费总额(已排除无效用户)" , value : "¥" + Number ( this . totalFlow ) . toFixed ( 2 ) , icon : "el-icon-data-line" , theme : "indigo" } ,
{ key : "historyReward" , label : "历史总返奖" , note : "全游戏产出饰品总价值(按背包产出统计,含已分解)" , value : "¥" + Number ( this . historyReward ) . toFixed ( 2 ) , icon : "el-icon-trophy" , theme : "teal" } ,
{ key : "rollRoom" , label : "Roll 房今日返奖" , note : "不含下注(仅饰品产出)" , value : "¥" + Number ( this . rollRoomReward || 0 ) . toFixed ( 2 ) , icon : "el-icon-house" , theme : "sky" }
] ;
@@ -480,107 +548,95 @@ export default {
}
}
/* ========== 统计卡片网格 ========== */
. stat - grid {
/* ========== 主体两栏:左窄轨 + 右主区 ========== */
. home - body {
display : grid ;
grid - template - columns : repeat ( 4 , 1 fr ) ;
gap : 16 px ;
margin - bottom : 20 px ;
grid - template - columns : 300 px minmax ( 0 , 1 fr ) ;
gap : 20 px ;
/* start:左轨按内容高度,不跟着右列拉长(否则底部空一大截) */
align - items : start ;
@ media ( max - width : 1400 px ) {
grid - template - columns : repeat ( 3 , 1 fr ) ;
}
@ media ( max - width : 1000 px ) {
grid - template - columns : repeat ( 2 , 1 fr ) ;
}
@ media ( max - width : 600 px ) {
grid - template - columns : 1 fr ;
@ media ( max - width : 1200 px ) {
grid - template - columns : minmax ( 0 , 1 fr ) ;
}
}
. stat - card {
position : relative ;
display : flex ;
align - items : center ;
/* ========== 左窄轨 ========== */
. rail {
/* 右列很长,左轨跟着滚,随时能看到核心数字 */
position : sticky ;
top : 16 px ;
padding : 20 px ;
background : # fffdfa ;
border - radius : 16 px ;
padding : 20 px ;
box - shadow : 0 4 px 14 px rgba ( 139 , 115 , 85 , 0.10 ) ;
border : 1 px solid rgba ( 212 , 160 , 160 , 0.55 ) ;
transition :
transform 0.5 s ease - in - out ,
box - shadow 0.5 s ease - in - out ,
border - color 0.5 s ease - in - out ;
overflow : hidden ;
& : hover {
/* 规范:悬停用「小幅放大 + ≤1deg 微旋」,避免机械式直上直下 */
transform : scale ( 1.015 ) rotate ( - 0.3 deg ) ;
border - color : # c08a8a ;
box - shadow : 0 10 px 26 px rgba ( 139 , 115 , 85 , 0.16 ) ;
@ media ( max - width : 1200 px ) {
position : static ;
}
}
. stat - icon {
transform : rotate ( - 1 deg ) scale ( 1.04 ) ;
}
. stat - arrow {
opacity : 1 ;
transform : translateX ( 0 ) ;
}
. rail - title {
font - size : 13 px ;
font - weight : 600 ;
color : # 7 a664e ;
letter - spacing : 0.5 px ;
margin - bottom : 2 px ;
}
. rail - metric {
position : relative ;
padding : 16 px 0 ;
border - top : 1 px solid # f3ece0 ;
& : first - child {
border - top : none ;
padding - top : 10 px ;
}
& . clickable {
cursor : pointer ;
}
}
. stat - icon {
flex - shrink : 0 ;
width : 54 px ;
height : 54 px ;
border - radius : 16 px ;
display : flex ;
align - items : center ;
justify - content : center ;
margin - right : 16 px ;
/* 底色与图标色由 .theme-* 提供:浅底 + 同色深图标(水彩感),
不再是一格一色的高饱和实心块 */
background : rgba ( 154 , 92 , 53 , 0.22 ) ;
transition : transform 0.5 s ease - in - out ;
i {
font - size : 26 px ;
color : # 9 a5c35 ;
}
}
. stat - info {
min - width : 0 ;
. stat - value {
font - size : 24 px ;
font - weight : 700 ;
color : # 4 f4130 ;
line - height : 1.2 ;
white - space : nowrap ;
overflow : hidden ;
text - overflow : ellipsis ;
}
. stat - label {
margin - top : 4 px ;
font - size : 13 px ;
color : # 7 a664e ;
. stat - note - icon {
margin - left : 4 px ;
color : # 7 a664e ;
cursor : help ;
& . clickable : hover {
. rail - metric - value {
color : # 9 a5c35 ;
}
. rail - arrow {
opacity : 1 ;
transform : translateX ( 0 ) ;
}
}
}
. stat - arrow {
. rail - metric - label {
display : flex ;
align - items : center ;
gap : 4 px ;
font - size : 13 px ;
color : # 7 a664e ;
. rail - note {
cursor : help ;
}
}
. rail - metric - value {
margin - top : 6 px ;
font - size : 28 px ;
font - weight : 700 ;
line - height : 1.15 ;
letter - spacing : - 0.4 px ;
color : # 4 f4130 ;
white - space : nowrap ;
overflow : hidden ;
text - overflow : ellipsis ;
}
. rail - arrow {
position : absolute ;
right : 16 px ;
right : 0 ;
top : 50 % ;
margin - top : - 7 px ;
font - size : 14 px ;
@@ -590,11 +646,74 @@ export default {
transition : all 0.25 s ease ;
}
/* 统计卡图标配色
11 个卡片只用 5 个田园色(陶土 / 草绿 / 苔绿 / 雏菊金 / 玫瑰粉),循环使用。
浅底 + 同色深图标。
tint 从 .14~.18 提到 .22~.26:原来太淡,小图标几乎"看不见"(用户反馈对比度不够)。
类名(blue/orange/...)只是 card.theme 的旧键名,沿用不改,免得动模板。 */
/* ========== 左轨待办区 ========== */
. rail - todo {
display : flex ;
flex - direction : column ;
gap : 10 px ;
margin - top : 12 px ;
padding - top : 16 px ;
border - top : 1 px solid # f3ece0 ;
}
. rail - todo - item {
display : flex ;
align - items : baseline ;
justify - content : space - between ;
gap : 8 px ;
padding : 10 px 12 px ;
border - radius : 10 px ;
background : # f6efe3 ;
border : 1 px solid rgba ( 212 , 160 , 160 , 0.40 ) ;
transition :
background - color 0.25 s ease ,
border - color 0.25 s ease ;
& . clickable {
cursor : pointer ;
}
& . clickable : hover {
background : # fbf5ea ;
border - color : # c08a8a ;
}
/* 有积压时用雏菊金描边 + 数值变色提醒。
刻意不给 background:留空才不会和 hover 的底色打架(同权重,靠顺序定胜负太脆) */
& . alert {
border - color : rgba ( 168 , 121 , 31 , 0.32 ) ;
. rail - todo - value {
color : # 7 d5512 ;
}
}
}
. rail - todo - label {
font - size : 12 px ;
color : # 7 a664e ;
}
. rail - todo - value {
font - size : 18 px ;
font - weight : 700 ;
line - height : 1.2 ;
color : # 4 f4130 ;
}
/* ========== 右侧主区 ========== */
. main - col {
display : flex ;
flex - direction : column ;
gap : 20 px ;
min - width : 0 ;
}
/* ========== 统计卡图标配色 ==========
浅底 + 同色深图标(水彩感)。五个田园色循环使用:
陶土 / 草绿 / 苔绿 / 雏菊金 / 玫瑰粉。
tint 用 .22~.26:太淡小图标会"看不见"。
类名(blue/orange/...)沿用旧键名不改,免得动逻辑。 */
. theme - blue ,
. theme - indigo {
background : rgba ( 154 , 92 , 53 , 0.22 ) ! important ;
@@ -627,14 +746,13 @@ export default {
i { color : # 8 f3f50 ! important ; }
}
/* ========== 区块卡片(RTP / 图表) ========== */
/* ========== 区块卡片(RTP / 图表 / 经营数据 ) ========== */
. section - card {
background : # faf6f0 ;
border - radius : 16 px ;
padding : 20 px 24 px ;
box - shadow : 0 6 px 18 px rgba ( 139 , 115 , 85 , 0.11 ) ;
border : 1 px solid rgba ( 212 , 160 , 160 , 0.55 ) ;
margin - bottom : 20 px ;
}
. section - header {
@@ -675,21 +793,13 @@ export default {
}
}
/* ========== RTP 卡片 ========== */
/* ========== RTP 卡片 ==========
右侧主区宽度随屏幕浮动(有左轨占位),用 auto-fit 自适应
—— 宽屏一行 5 张,窄屏自动折行,不再写死 5 列。 */
. rtp - grid {
display : grid ;
grid - template - columns : repeat ( 5 , 1 fr ) ;
grid - template - columns : repeat ( auto - fit , minmax ( 160 px , 1 fr ) ) ;
gap : 16 px ;
@ media ( max - width : 1400 px ) {
grid - template - columns : repeat ( 3 , 1 fr ) ;
}
@ media ( max - width : 900 px ) {
grid - template - columns : repeat ( 2 , 1 fr ) ;
}
@ media ( max - width : 600 px ) {
grid - template - columns : 1 fr ;
}
}
. rtp - card {
@@ -774,9 +884,7 @@ export default {
/* RTP 主题色(只给圆点 + 进度条上色)
★ 与上方折线图的颜色一一对应:同一游戏在哪儿都是同一个色,
这样「颜色」本身就带信息,不用每次重新认。
(原来是品红 #ec4899 / 青 #14b8a6 / 紫 #8b5cf6 / 翠绿 #10b981 / 琥珀 #f59e0b,
属于规范禁止的高饱和荧光色,已换掉。) */
这样「颜色」本身就带信息,不用每次重新认。 */
. rtp - theme - 0 { /* 疯狂转盘 */
. rtp - dot { background : # 7 a6a9c ; }
. rtp - progress - inner { background : linear - gradient ( 90 deg , # a89cc0 , # 7 a6a9c ) ; }
@@ -798,6 +906,73 @@ export default {
. rtp - progress - inner { background : linear - gradient ( 90 deg , # c69bb8 , # a35790 ) ; }
}
/* ========== 经营数据(次级指标带) ========== */
. mini - stats {
display : grid ;
grid - template - columns : repeat ( auto - fit , minmax ( 200 px , 1 fr ) ) ;
gap : 12 px ;
}
. mini - stat {
display : flex ;
align - items : center ;
gap : 10 px ;
padding : 12 px 14 px ;
border - radius : 12 px ;
background : # f6efe3 ;
border : 1 px solid rgba ( 212 , 160 , 160 , 0.40 ) ;
transition :
background - color 0.5 s ease - in - out ,
border - color 0.5 s ease - in - out ;
& : hover {
background : # fbf5ea ;
border - color : # c08a8a ;
}
}
. mini - icon {
flex - shrink : 0 ;
width : 34 px ;
height : 34 px ;
border - radius : 11 px ;
display : flex ;
align - items : center ;
justify - content : center ;
i {
font - size : 17 px ;
}
}
. mini - info {
min - width : 0 ;
}
. mini - label {
display : flex ;
align - items : center ;
gap : 4 px ;
font - size : 12 px ;
color : # 7 a664e ;
white - space : nowrap ;
. mini - note {
cursor : help ;
}
}
. mini - value {
margin - top : 2 px ;
font - size : 17 px ;
font - weight : 700 ;
line - height : 1.25 ;
color : # 4 f4130 ;
white - space : nowrap ;
overflow : hidden ;
text - overflow : ellipsis ;
}
/* ========== 图表切换标签 ========== */
. chart - tabs {
display : flex ;