diff --git a/frontend/src/pages/inbounds/ClientRowTable.vue b/frontend/src/pages/inbounds/ClientRowTable.vue
index 3659d643..67b19966 100644
--- a/frontend/src/pages/inbounds/ClientRowTable.vue
+++ b/frontend/src/pages/inbounds/ClientRowTable.vue
@@ -31,6 +31,7 @@ const props = defineProps({
onlineClients: { type: Array, default: () => [] },
lastOnlineMap: { type: Object, default: () => ({}) },
isDarkTheme: { type: Boolean, default: false },
+ pageSize: { type: Number, default: 0 },
});
const emit = defineEmits([
@@ -46,6 +47,20 @@ const emit = defineEmits([
const inbound = computed(() => props.dbInbound.toInbound());
const clients = computed(() => inbound.value?.clients || []);
+const currentPage = ref(1);
+const paginatedClients = computed(() => {
+ if (!props.pageSize || props.pageSize <= 0) return clients.value;
+ const start = (currentPage.value - 1) * props.pageSize;
+ return clients.value.slice(start, start + props.pageSize);
+});
+
+watch([clients, () => props.pageSize], () => {
+ const total = clients.value.length;
+ const size = props.pageSize > 0 ? props.pageSize : (total || 1);
+ const maxPage = Math.max(1, Math.ceil(total / size));
+ if (currentPage.value > maxPage) currentPage.value = maxPage;
+});
+
// === Per-client stats lookup =======================================
const statsMap = computed(() => {
const m = new Map();
@@ -246,7 +261,7 @@ function confirmBulkDelete() {
{{ t('pages.inbounds.expireDate') }}
-
-
@@ -687,6 +706,12 @@ function confirmBulkDelete() {
padding: 0 !important;
}
+.client-list-pagination {
+ display: flex;
+ justify-content: center;
+ padding: 10px 16px 4px;
+}
+
/* ===== Mobile card list =========================================== */
.client-list.is-mobile {
display: flex;
diff --git a/frontend/src/pages/inbounds/InboundList.vue b/frontend/src/pages/inbounds/InboundList.vue
index 6841ed3a..88b39bc8 100644
--- a/frontend/src/pages/inbounds/InboundList.vue
+++ b/frontend/src/pages/inbounds/InboundList.vue
@@ -122,13 +122,19 @@ const visibleInbounds = computed(() => {
// `key`-driven so we can render via the body-cell slot below. AD-Vue 4's
// `responsive` array still works on column defs. Computed so column
// labels react to live locale switches.
+const hasAnyRemark = computed(() =>
+ props.dbInbounds.some((i) => typeof i?.remark === 'string' && i.remark.trim() !== ''),
+);
+
const desktopColumns = computed(() => {
const cols = [
- { title: 'ID', dataIndex: 'id', key: 'id', align: 'right', width: 30, responsive: ['xs'] },
+ { title: 'ID', dataIndex: 'id', key: 'id', align: 'right', width: 30 },
{ title: t('pages.inbounds.operate'), key: 'action', align: 'center', width: 30 },
{ title: t('pages.inbounds.enable'), key: 'enable', align: 'center', width: 35 },
- { title: t('pages.inbounds.remark'), dataIndex: 'remark', key: 'remark', align: 'center', width: 60 },
];
+ if (hasAnyRemark.value) {
+ cols.push({ title: t('pages.inbounds.remark'), dataIndex: 'remark', key: 'remark', align: 'center', width: 60 });
+ }
if (props.nodesById.size > 0) {
cols.push({ title: t('pages.inbounds.node'), key: 'node', align: 'center', width: 60 });
}
@@ -401,6 +407,7 @@ function showQrCodeMenu(dbInbound) {
emit('edit-client', p)" @qrcode-client="(p) => emit('qrcode-client', p)"
@info-client="(p) => emit('info-client', p)"
@reset-traffic-client="(p) => emit('reset-traffic-client', p)"
@@ -421,7 +428,8 @@ function showQrCodeMenu(dbInbound) {
emit('edit-client', p)"
+ :last-online-map="lastOnlineMap" :is-dark-theme="isDarkTheme" :page-size="pageSize"
+ @edit-client="(p) => emit('edit-client', p)"
@qrcode-client="(p) => emit('qrcode-client', p)" @info-client="(p) => emit('info-client', p)"
@reset-traffic-client="(p) => emit('reset-traffic-client', p)"
@delete-client="(p) => emit('delete-client', p)"