fix(admin): 优化列表布局间距、滚动固定与侧栏子路由选中

统一嵌套卡片内边距与 Tabs 外壳样式,联赛单场支持表头/分页固定滚动;子页面下代理玩家菜单保持高亮。
This commit is contained in:
2026-06-22 16:48:40 +08:00
parent 78b426a388
commit b9257cc50a
7 changed files with 149 additions and 84 deletions

View File

@@ -65,7 +65,7 @@ body::-webkit-scrollbar {
height: 100%;
min-height: 0;
overflow: hidden;
gap: 14px;
gap: 18px;
}
.admin-list-page > .page-toolbar,
.admin-list-page > .filter-card,
@@ -244,7 +244,48 @@ body::-webkit-scrollbar {
.admin-list-page > .list-panel {
border: 1px solid var(--border);
background: var(--bg-card);
padding: 0 10px 10px;
padding: 14px 16px 16px;
}
/* Tabs / 筛选壳内的 list-panel 不再套第二层卡片,避免顶边重叠 */
.admin-list-page > .mgr-tabs-shell .list-panel,
.admin-list-page .el-tab-pane > .list-panel,
.list-chrome > .list-panel {
border: none;
box-shadow: none;
background: transparent;
padding: 0;
border-radius: 0;
overflow: visible;
}
/* 代理管理等 Tabs 页:外层壳作为唯一卡片 */
.admin-list-page > .mgr-tabs-shell {
flex: 1;
min-height: 0;
border: 1px solid var(--border);
border-radius: 10px;
background: #ffffff;
box-shadow: var(--shadow);
padding: 14px 16px 16px;
}
.admin-list-page > .mgr-tabs-shell .mgr-top-tabs .el-tabs__header {
margin-bottom: 14px;
}
.admin-list-page > .filter-card .el-card__body {
padding: 16px 18px 18px;
}
.admin-list-page > .filter-card .el-tabs__header {
margin-bottom: 14px;
}
.list-chrome__row + .list-hint {
margin-top: 2px;
padding-top: 4px;
padding-bottom: 0;
}
.admin-list-page > .data-card .el-card__body {
flex: 1;
@@ -285,10 +326,43 @@ body::-webkit-scrollbar {
.admin-list-page .list-hint {
flex-shrink: 0;
margin: 0;
padding: 6px 0 8px;
padding: 4px 0 12px;
font-size: 11px;
color: var(--text-muted);
line-height: 1.4;
line-height: 1.45;
}
.admin-list-page .list-panel-toolbar + .list-hint {
padding-top: 0;
margin-top: -2px;
}
.admin-list-page .list-panel-toolbar {
flex-shrink: 0;
align-items: flex-start;
justify-content: space-between;
gap: 12px;
padding: 2px 0 14px;
margin-bottom: 2px;
border-bottom: 1px solid var(--border-soft);
--list-chrome-control-h: 32px;
--el-component-size: 32px;
}
.admin-list-page .list-panel-toolbar :deep(.el-form-item) {
margin-bottom: 0 !important;
margin-right: 12px;
}
.admin-list-page .list-panel-toolbar :deep(.el-input__wrapper),
.admin-list-page .list-panel-toolbar :deep(.el-select__wrapper) {
height: var(--list-chrome-control-h) !important;
min-height: var(--list-chrome-control-h) !important;
}
.admin-list-page .list-panel-toolbar :deep(.el-button:not(.is-link)) {
height: var(--list-chrome-control-h) !important;
min-height: var(--list-chrome-control-h) !important;
}
.admin-list-page .pager {
flex-shrink: 0;
@@ -1250,7 +1324,7 @@ input:-webkit-autofill:focus {
.admin-list-page {
display: flex;
flex-direction: column;
gap: 16px;
gap: 18px;
color: var(--text);
}
@@ -1281,8 +1355,11 @@ input:-webkit-autofill:focus {
.list-toolbar + .table-wrap,
.filter-bar + .table-wrap,
.filters + .table-wrap,
.list-panel-toolbar + .table-wrap {
margin-top: 2px;
.list-panel-toolbar + .table-wrap,
.list-panel-toolbar + .admin-table-wrap,
.list-hint + .table-wrap,
.list-hint + .admin-table-wrap {
margin-top: 0;
}
.table-wrap,
@@ -1301,8 +1378,9 @@ input:-webkit-autofill:focus {
.list-chrome > .admin-table-wrap,
.list-chrome > .table-panel,
.list-chrome > .list-panel {
border-color: var(--border-soft);
border: none;
box-shadow: none;
background: transparent;
}
.table-scroll,

View File

@@ -77,7 +77,7 @@ const adminMenus = computed(() => {
const items: AdminMenuItem[] = [
{ key: 'dashboard', path: '/', label: t('nav.dashboard'), icon: 'dashboard', matchPrefix: true, permissions: [AdminPerm.reports], excludeRoles: ['SUPPORT'] },
{ key: 'matches', path: '/matches', label: t('nav.matches'), icon: 'matches', matchPrefix: true, permissions: [AdminPerm.matches] },
{ key: 'users', path: '/users', label: t('nav.agents_players'), icon: 'users', permissions: [AdminPerm.usersView, AdminPerm.agentsView] },
{ key: 'users', path: '/users', label: t('nav.agents_players'), icon: 'users', matchPrefix: true, permissions: [AdminPerm.usersView, AdminPerm.agentsView] },
{ key: 'finance-logs', path: '/finance-logs', label: t('nav.finance_logs'), icon: 'finance', permissions: [AdminPerm.reports], excludeRoles: ['MATCH_ADMIN'] },
{ key: 'deposit', path: '/deposit', label: t('nav.deposit_manage'), icon: 'deposit', matchPrefix: true, permissions: [AdminPerm.depositManage, AdminPerm.depositReview] },
{ key: 'cashback', path: '/cashback', label: t('nav.cashback'), icon: 'cashback', permissions: [AdminPerm.cashback], excludeRoles: ['MATCH_ADMIN', 'SUPPORT'] },
@@ -117,15 +117,17 @@ function isDepositSectionPath(path: string) {
return path === '/deposit' || path === '/deposit-orders' || path === '/payment-methods';
}
function isMenuActive(menu: { path: string; matchPrefix?: boolean }, path: string) {
if (path === menu.path) return true;
if (!menu.matchPrefix) return false;
if (menu.path === '/') return isDashboardSectionPath(path);
if (menu.path === '/deposit') return isDepositSectionPath(path);
if (menu.path === '/matches') return isMatchesSectionPath(path);
return path.startsWith(`${menu.path}/`);
}
const currentLabel = computed(() => {
const hit = menus.value.find((m) => {
if ('matchPrefix' in m && m.matchPrefix) {
if (m.path === '/') return isDashboardSectionPath(route.path);
if (m.path === '/deposit') return isDepositSectionPath(route.path);
return isMatchesSectionPath(route.path);
}
return route.path === m.path;
});
const hit = menus.value.find((m) => isMenuActive(m, route.path));
return hit?.label ?? '';
});
@@ -226,17 +228,7 @@ watch(() => route.path, () => {
<RouterLink
v-for="m in menus" :key="m.path" :to="m.path"
class="nav-item"
:class="{
active:
route.path === m.path ||
('matchPrefix' in m &&
m.matchPrefix &&
(m.path === '/'
? isDashboardSectionPath(route.path)
: m.path === '/deposit'
? isDepositSectionPath(route.path)
: isMatchesSectionPath(route.path))),
}"
:class="{ active: isMenuActive(m, route.path) }"
@click="onNavClick"
@mouseenter="prefetchNavRoute(m.path)"
@focus="prefetchNavRoute(m.path)"

View File

@@ -2173,8 +2173,8 @@ provide(agentPlayerActionsKey, {
.mgr-toolbar-actions {
position: absolute;
right: 0;
top: 0;
right: 16px;
top: 14px;
z-index: 2;
display: flex;
align-items: center;
@@ -2238,12 +2238,9 @@ provide(agentPlayerActionsKey, {
display: flex;
flex-direction: column;
}
.mgr-top-tabs :deep(.el-tabs__header) {
margin-bottom: 8px;
}
.mgr-top-tabs :deep(.el-tabs__item) {
font-size: 14px;
font-weight: 500;
font-weight: 700;
}
.freeze-agent-intro {
@@ -2258,19 +2255,7 @@ provide(agentPlayerActionsKey, {
gap: 10px;
}
/* ─── Table toolbar ─── */
.list-panel-toolbar {
flex-shrink: 0;
display: flex;
align-items: flex-start;
justify-content: space-between;
flex-wrap: wrap;
gap: 12px;
padding: 10px 0 8px;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
--list-chrome-control-h: 32px;
--el-component-size: 32px;
}
/* ─── Table toolbar(间距见 App.vue 全局 .list-panel-toolbar ─── */
.list-panel-toolbar .list-chrome__grow {
flex: 1 1 280px;
min-width: 0;
@@ -2280,19 +2265,6 @@ provide(agentPlayerActionsKey, {
.list-panel-toolbar .list-chrome__actions {
flex-shrink: 0;
}
.list-panel-toolbar :deep(.el-form-item) {
margin-bottom: 0 !important;
margin-right: 12px;
}
.list-panel-toolbar :deep(.el-input__wrapper),
.list-panel-toolbar :deep(.el-select__wrapper) {
height: var(--list-chrome-control-h) !important;
min-height: var(--list-chrome-control-h) !important;
}
.list-panel-toolbar :deep(.el-button:not(.is-link)) {
height: var(--list-chrome-control-h) !important;
min-height: var(--list-chrome-control-h) !important;
}
/* ─── Expansion ─── */
.expand-panel {
@@ -2526,14 +2498,6 @@ provide(agentPlayerActionsKey, {
gap: 8px;
}
.mgr-top-tabs :deep(.el-tabs__item) {
font-weight: 700;
}
.list-panel-toolbar {
border-bottom-color: var(--border-soft);
}
.expand-panel {
background: #fbfaf7;
border-color: var(--border);

View File

@@ -719,13 +719,11 @@ function onLeagueArchived() {
margin-bottom: 16px;
}
/* 列表表格随内容增高,滚动交给外层 table-wrap仅赛事行 */
.matches-page .table-wrap .el-table {
height: auto !important;
}
.matches-page .table-wrap :deep(.el-table__header),
.matches-page .table-wrap :deep(.el-table__body) {
width: 100% !important;
/* 联赛列表沿用 admin-list-page 内滚:表头固定、分页贴底 */
.matches-page .table-wrap :deep(.el-table__header-wrapper) {
position: sticky;
top: 0;
z-index: 2;
}
.list-panel :deep(.row-navigable) {

View File

@@ -211,7 +211,7 @@ onUnmounted(() => {
}
.agent-direct-players-page :deep(.admin-subnav) {
margin-bottom: 10px;
margin-bottom: 14px;
}
.agent-direct-players-page .list-panel {

View File

@@ -192,12 +192,21 @@ watch(leagueId, () => {
min-height: 0;
}
.league-matches-page :deep(.admin-subnav) {
flex-shrink: 0;
margin-bottom: 14px;
}
.league-matches-page .list-panel {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
overflow: hidden;
border: none;
background: transparent;
padding: 0;
box-shadow: none;
}
.team-country-select {

View File

@@ -466,7 +466,8 @@ defineExpose({ reload: load });
</div>
</div>
<el-table v-loading="loading" :data="matches" stripe row-key="id" class="nested-match-table">
<div class="nested-table-wrap">
<el-table v-loading="loading" :data="matches" stripe row-key="id" class="nested-match-table">
<el-table-column type="index" :index="(i: number) => (matchPage - 1) * matchPageSize + i + 1" :label="t('common.seq')" width="70" align="center" />
<el-table-column :label="t('match.col.matchup')" min-width="180">
<template #default="{ row }">
@@ -579,8 +580,10 @@ defineExpose({ reload: load });
</div>
</template>
</el-table-column>
</el-table>
<p v-if="!loading && !matches.length" class="empty-hint">{{ t('match.no_fixtures') }}</p>
</el-table>
<p v-if="!loading && !matches.length" class="empty-hint">{{ t('match.no_fixtures') }}</p>
</div>
<div v-if="matchTotal > matchPageSize" class="nested-pager">
<el-pagination
v-model:current-page="matchPage"
@@ -633,21 +636,26 @@ defineExpose({ reload: load });
<style scoped>
.league-matches-panel {
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
height: 100%;
width: min(100%, calc(100vw - 272px));
max-width: calc(100vw - 272px);
padding: 10px 12px 12px;
overflow-x: auto;
overflow-y: visible;
padding: 14px 14px 14px;
overflow: hidden;
background: #fbfaf7;
}
.nested-panel-toolbar {
flex-shrink: 0;
display: flex;
flex-wrap: wrap;
align-items: flex-start;
justify-content: space-between;
gap: 10px 16px;
margin-bottom: 10px;
padding: 8px 12px;
margin: 2px 0 12px;
padding: 10px 14px;
background: #ffffff;
border: 1px solid var(--border-soft, #eaeaea);
border-radius: 6px;
@@ -677,10 +685,26 @@ defineExpose({ reload: load });
color: var(--text-muted);
white-space: nowrap;
}
.nested-table-wrap {
flex: 1;
min-height: 0;
overflow: auto;
border: 1px solid var(--border-soft, #eaeaea);
border-radius: 6px;
background: #ffffff;
}
.nested-table-wrap :deep(.el-table__header-wrapper) {
position: sticky;
top: 0;
z-index: 2;
background: var(--el-table-header-bg-color, #fafafa);
}
.nested-pager {
flex-shrink: 0;
display: flex;
justify-content: flex-end;
padding-top: 8px;
background: #fbfaf7;
}
.actions-col-header {
display: inline-flex;