mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-06 21:24:10 +00:00
fix(ui): align traffic/expiry cell columns across all rows
This commit is contained in:
parent
df6b46d9c9
commit
7792715dc4
3 changed files with 65 additions and 44 deletions
|
|
@ -83,24 +83,22 @@
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</template>
|
</template>
|
||||||
<table>
|
<div class="tr-table-box">
|
||||||
<tr class="tr-table-box">
|
<div class="tr-table-rt">[[ SizeFormatter.sizeFormat(getSumStats(record, client.email)) ]]</div>
|
||||||
<td class="tr-table-rt"> [[ SizeFormatter.sizeFormat(getSumStats(record, client.email)) ]] </td>
|
<div class="tr-table-bar" v-if="!client.enable">
|
||||||
<td class="tr-table-bar" v-if="!client.enable">
|
<a-progress :stroke-color="themeSwitcher.isDarkTheme ? 'rgb(72 84 105)' : '#bcbcbc'" :show-info="false" :percent="statsProgress(record, client.email)" />
|
||||||
<a-progress :stroke-color="themeSwitcher.isDarkTheme ? 'rgb(72 84 105)' : '#bcbcbc'" :show-info="false" :percent="statsProgress(record, client.email)" />
|
</div>
|
||||||
</td>
|
<div class="tr-table-bar" v-else-if="client.totalGB > 0">
|
||||||
<td class="tr-table-bar" v-else-if="client.totalGB > 0">
|
<a-progress :stroke-color="clientStatsColor(record, client.email)" :show-info="false" :status="isClientDepleted(record, client.email)? 'exception' : ''" :percent="statsProgress(record, client.email)" />
|
||||||
<a-progress :stroke-color="clientStatsColor(record, client.email)" :show-info="false" :status="isClientDepleted(record, client.email)? 'exception' : ''" :percent="statsProgress(record, client.email)" />
|
</div>
|
||||||
</td>
|
<div v-else class="infinite-bar tr-table-bar">
|
||||||
<td v-else class="infinite-bar tr-table-bar">
|
<a-progress :show-info="false" :percent="100"></a-progress>
|
||||||
<a-progress :show-info="false" :percent="100"></a-progress>
|
</div>
|
||||||
</td>
|
<div class="tr-table-lt">
|
||||||
<td class="tr-table-lt">
|
<template v-if="client.totalGB > 0">[[ client._totalGB + "GB" ]]</template>
|
||||||
<template v-if="client.totalGB > 0">[[ client._totalGB + "GB" ]]</template>
|
<span v-else class="tr-infinity-ch">∞</span>
|
||||||
<span v-else class="tr-infinity-ch">∞</span>
|
</div>
|
||||||
</td>
|
</div>
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</a-popover>
|
</a-popover>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -114,15 +112,13 @@
|
||||||
<span v-if="client.expiryTime < 0">{{ i18n "pages.client.delayedStart" }}</span>
|
<span v-if="client.expiryTime < 0">{{ i18n "pages.client.delayedStart" }}</span>
|
||||||
<span v-else>[[ IntlUtil.formatDate(client.expiryTime) ]]</span>
|
<span v-else>[[ IntlUtil.formatDate(client.expiryTime) ]]</span>
|
||||||
</template>
|
</template>
|
||||||
<table>
|
<div class="tr-table-box">
|
||||||
<tr class="tr-table-box">
|
<div class="tr-table-rt">[[ IntlUtil.formatRelativeTime(client.expiryTime) ]]</div>
|
||||||
<td class="tr-table-rt"> [[ IntlUtil.formatRelativeTime(client.expiryTime) ]] </td>
|
<div class="infinite-bar tr-table-bar">
|
||||||
<td class="infinite-bar tr-table-bar">
|
<a-progress :show-info="false" :status="isClientDepleted(record, client.email)? 'exception' : ''" :percent="expireProgress(client.expiryTime, client.reset)" />
|
||||||
<a-progress :show-info="false" :status="isClientDepleted(record, client.email)? 'exception' : ''" :percent="expireProgress(client.expiryTime, client.reset)" />
|
</div>
|
||||||
</td>
|
<div class="tr-table-lt">[[ client.reset + "d" ]]</div>
|
||||||
<td class="tr-table-lt">[[ client.reset + "d" ]]</td>
|
</div>
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</a-popover>
|
</a-popover>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
|
|
|
||||||
|
|
@ -742,6 +742,7 @@
|
||||||
:columns="isMobile ? innerMobileColumns : innerColumns"
|
:columns="isMobile ? innerMobileColumns : innerColumns"
|
||||||
:data-source="getInboundClients(record)"
|
:data-source="getInboundClients(record)"
|
||||||
:pagination=pagination(getInboundClients(record))
|
:pagination=pagination(getInboundClients(record))
|
||||||
|
:scroll="isMobile ? {} : { x: 'max-content' }"
|
||||||
:style="{ margin: `-10px ${isMobile ? '2px' : '22px'} -11px` }">
|
:style="{ margin: `-10px ${isMobile ? '2px' : '22px'} -11px` }">
|
||||||
{{template "component/aClientTable"}}
|
{{template "component/aClientTable"}}
|
||||||
</a-table>
|
</a-table>
|
||||||
|
|
@ -1067,13 +1068,13 @@
|
||||||
}];
|
}];
|
||||||
|
|
||||||
const innerColumns = [
|
const innerColumns = [
|
||||||
{ title: '{{ i18n "pages.inbounds.operate" }}', width: 70, scopedSlots: { customRender: 'actions' } },
|
{ title: '{{ i18n "pages.inbounds.operate" }}', width: 140, scopedSlots: { customRender: 'actions' } },
|
||||||
{ title: '{{ i18n "pages.inbounds.enable" }}', width: 30, scopedSlots: { customRender: 'enable' } },
|
{ title: '{{ i18n "pages.inbounds.enable" }}', width: 60, scopedSlots: { customRender: 'enable' } },
|
||||||
{ title: '{{ i18n "online" }}', width: 32, scopedSlots: { customRender: 'online' } },
|
{ title: '{{ i18n "online" }}', width: 80, scopedSlots: { customRender: 'online' } },
|
||||||
{ title: '{{ i18n "pages.inbounds.client" }}', width: 80, scopedSlots: { customRender: 'client' } },
|
{ title: '{{ i18n "pages.inbounds.client" }}', width: 160, scopedSlots: { customRender: 'client' } },
|
||||||
{ title: '{{ i18n "pages.inbounds.traffic" }}', width: 80, align: 'center', scopedSlots: { customRender: 'traffic' } },
|
{ title: '{{ i18n "pages.inbounds.traffic" }}', width: 200, align: 'center', scopedSlots: { customRender: 'traffic' } },
|
||||||
{ title: '{{ i18n "pages.inbounds.allTimeTraffic" }}', width: 60, align: 'center', scopedSlots: { customRender: 'allTime' } },
|
{ title: '{{ i18n "pages.inbounds.allTimeTraffic" }}', width: 110, align: 'center', scopedSlots: { customRender: 'allTime' } },
|
||||||
{ title: '{{ i18n "pages.inbounds.expireDate" }}', width: 80, align: 'center', scopedSlots: { customRender: 'expiryTime' } },
|
{ title: '{{ i18n "pages.inbounds.expireDate" }}', width: 180, align: 'center', scopedSlots: { customRender: 'expiryTime' } },
|
||||||
];
|
];
|
||||||
|
|
||||||
const innerMobileColumns = [
|
const innerMobileColumns = [
|
||||||
|
|
@ -2282,27 +2283,51 @@
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Traffic cell — text on the sides sizes to its content, the progress bar
|
/* Traffic / expiry cell — flex layout:
|
||||||
takes whatever's left. Without this, fixed-width text cells leave gaps
|
- Side text (.tr-table-rt / .tr-table-lt) sizes to content.
|
||||||
around short values like "∞" and clip long ones like "999.99 GB". */
|
- Progress bar (.tr-table-bar) takes whatever's left and is allowed to
|
||||||
|
shrink (min-width: 0) so the cell never visually overflows its column,
|
||||||
|
no matter how long the surrounding values are ("999.99 GB", "365d").
|
||||||
|
A flex <div> replaces the previous <table>/<tr>/<td> hack — table layout
|
||||||
|
ignored width: 100% on the row, so the row grew to its content width and
|
||||||
|
pushed past the a-table column boundary. */
|
||||||
.inbounds-page .tr-table-box {
|
.inbounds-page .tr-table-box {
|
||||||
display: inline-flex;
|
display: flex;
|
||||||
gap: 6px;
|
gap: 8px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 2px 10px;
|
||||||
|
border-radius: 100px;
|
||||||
|
background: rgba(255, 255, 255, 0.04);
|
||||||
}
|
}
|
||||||
|
/* Fixed widths so the bar starts/ends at the same X position across all
|
||||||
|
rows — without this, "126.45 MB" and "0 B" pushed the bar to different
|
||||||
|
spots, which read as misalignment in the column. */
|
||||||
.inbounds-page .tr-table-rt,
|
.inbounds-page .tr-table-rt,
|
||||||
.inbounds-page .tr-table-lt {
|
.inbounds-page .tr-table-lt {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
font-variant-numeric: tabular-nums;
|
font-variant-numeric: tabular-nums;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.inbounds-page .tr-table-rt {
|
||||||
|
text-align: end;
|
||||||
|
flex-basis: 70px;
|
||||||
|
min-width: 70px;
|
||||||
|
}
|
||||||
|
.inbounds-page .tr-table-lt {
|
||||||
|
text-align: start;
|
||||||
|
flex-basis: 28px;
|
||||||
|
min-width: 28px;
|
||||||
}
|
}
|
||||||
.inbounds-page .tr-table-rt { text-align: end; }
|
|
||||||
.inbounds-page .tr-table-lt { text-align: start; }
|
|
||||||
.inbounds-page .tr-table-bar {
|
.inbounds-page .tr-table-bar {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 0;
|
||||||
min-width: 60px;
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make the progress widget fill its flex cell, and align the inner fill
|
/* Make the progress widget fill its flex cell, and align the inner fill
|
||||||
|
|
|
||||||
|
|
@ -206,7 +206,7 @@
|
||||||
{ title: '{{ i18n "pages.xray.outbound.tag"}}', dataIndex: 'tag', align: 'center', width: 50 },
|
{ title: '{{ i18n "pages.xray.outbound.tag"}}', dataIndex: 'tag', align: 'center', width: 50 },
|
||||||
{ title: '{{ i18n "protocol"}}', align: 'center', width: 50, scopedSlots: { customRender: 'protocol' } },
|
{ title: '{{ i18n "protocol"}}', align: 'center', width: 50, scopedSlots: { customRender: 'protocol' } },
|
||||||
{ title: '{{ i18n "pages.xray.outbound.address"}}', align: 'center', width: 50, scopedSlots: { customRender: 'address' } },
|
{ title: '{{ i18n "pages.xray.outbound.address"}}', align: 'center', width: 50, scopedSlots: { customRender: 'address' } },
|
||||||
{ title: '{{ i18n "pages.inbounds.traffic" }}', align: 'center', width: 50, scopedSlots: { customRender: 'traffic' } },
|
{ title: '{{ i18n "pages.inbounds.traffic" }}', align: 'center', width: 180, scopedSlots: { customRender: 'traffic' } },
|
||||||
{ title: '{{ i18n "pages.xray.outbound.testResult" }}', align: 'center', width: 120, scopedSlots: { customRender: 'testResult' } },
|
{ title: '{{ i18n "pages.xray.outbound.testResult" }}', align: 'center', width: 120, scopedSlots: { customRender: 'testResult' } },
|
||||||
{ title: '{{ i18n "pages.xray.outbound.test" }}', align: 'center', width: 60, scopedSlots: { customRender: 'test' } },
|
{ title: '{{ i18n "pages.xray.outbound.test" }}', align: 'center', width: 60, scopedSlots: { customRender: 'test' } },
|
||||||
];
|
];
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue