mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2025-07-01 12:32:09 +00:00
feat: enhance client display with comments and improved styling across protocols
This commit is contained in:
parent
2e6faf69e6
commit
d448b6968c
6 changed files with 146 additions and 20 deletions
|
@ -41,14 +41,28 @@
|
|||
</template>
|
||||
</template>
|
||||
<template slot="client" slot-scope="text, client">
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
<template v-if="!isClientEnabled(record, client.email)">{{ i18n "depleted" }}</template>
|
||||
<template v-else-if="!client.enable">{{ i18n "disabled" }}</template>
|
||||
<template v-else-if="client.enable && isClientOnline(client.email)">{{ i18n "online" }}</template>
|
||||
</template>
|
||||
<a-badge :class="isClientOnline(client.email)? 'online-animation' : ''" :color="client.enable ? statsExpColor(record, client.email) : themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc'"></a-badge>
|
||||
</a-tooltip> [[ client.email ]]
|
||||
<div class="client-cell">
|
||||
<div class="client-info-row">
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
<template v-if="!isClientEnabled(record, client.email)">{{ i18n "depleted" }}</template>
|
||||
<template v-else-if="!client.enable">{{ i18n "disabled" }}</template>
|
||||
<template v-else-if="client.enable && isClientOnline(client.email)">{{ i18n "online" }}</template>
|
||||
</template>
|
||||
<a-badge :class="isClientOnline(client.email)? 'online-animation' : ''" :color="client.enable ? statsExpColor(record, client.email) : themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc'"></a-badge>
|
||||
</a-tooltip>
|
||||
<span class="client-email">[[ client.email ]]</span>
|
||||
</div>
|
||||
<div v-if="client.comment && client.comment.trim()" style="margin-left: 18px;">
|
||||
<a-tooltip v-if="client.comment.length > 50" :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template slot="title">
|
||||
[[ client.comment ]]
|
||||
</template>
|
||||
<span class="client-comment">[[ client.comment.substring(0, 47) + '...' ]]</span>
|
||||
</a-tooltip>
|
||||
<span v-else class="client-comment">[[ client.comment ]]</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template slot="traffic" slot-scope="text, client">
|
||||
<a-popover :overlay-class-name="themeSwitcher.currentTheme">
|
||||
|
@ -168,6 +182,15 @@
|
|||
<a-popover placement="bottomRight" :overlay-class-name="themeSwitcher.currentTheme" trigger="click">
|
||||
<template slot="content">
|
||||
<table>
|
||||
<tr v-if="client.comment && client.comment.trim()">
|
||||
<td colspan="3" :style="{ textAlign: 'left', borderBottom: '1px solid #f0f0f0', paddingBottom: '8px', marginBottom: '8px' }">
|
||||
<div :style="{ fontSize: '0.9em', fontWeight: '500', marginBottom: '4px' }">[[ client.email ]]</div>
|
||||
<div :style="{ fontSize: '0.85em', color: '#666', fontStyle: 'italic', wordBreak: 'break-word', maxWidth: '250px' }">[[ client.comment ]]</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-else>
|
||||
<td colspan="3" :style="{ textAlign: 'center', fontWeight: '500', paddingBottom: '8px' }">[[ client.email ]]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3" :style="{ textAlign: 'center' }">{{ i18n "pages.inbounds.traffic" }}</td>
|
||||
</tr>
|
||||
|
|
|
@ -13,7 +13,12 @@
|
|||
<th>Password</th>
|
||||
</tr>
|
||||
<tr v-for="(client, index) in inbound.settings.shadowsockses" :class="index % 2 == 1 ? 'client-table-odd-row' : ''">
|
||||
<td>[[ client.email ]]</td>
|
||||
<td>
|
||||
<div class="client-cell">
|
||||
<div class="client-email">[[ client.email ]]</div>
|
||||
<div v-if="client.comment && client.comment.trim()" class="client-comment">[[ client.comment ]]</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>[[ client.password ]]</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
@ -12,7 +12,12 @@
|
|||
<th>Password</th>
|
||||
</tr>
|
||||
<tr v-for="(client, index) in inbound.settings.trojans" :class="index % 2 == 1 ? 'client-table-odd-row' : ''">
|
||||
<td>[[ client.email ]]</td>
|
||||
<td>
|
||||
<div class="client-cell">
|
||||
<div class="client-email">[[ client.email ]]</div>
|
||||
<div v-if="client.comment && client.comment.trim()" class="client-comment">[[ client.comment ]]</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>[[ client.password ]]</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
@ -12,7 +12,12 @@
|
|||
<th>ID</th>
|
||||
</tr>
|
||||
<tr v-for="(client, index) in inbound.settings.vlesses" :class="index % 2 == 1 ? 'client-table-odd-row' : ''">
|
||||
<td>[[ client.email ]]</td>
|
||||
<td>
|
||||
<div class="client-cell">
|
||||
<div class="client-email">[[ client.email ]]</div>
|
||||
<div v-if="client.comment && client.comment.trim()" class="client-comment">[[ client.comment ]]</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>[[ client.id ]]</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
@ -13,7 +13,12 @@
|
|||
<th>{{ i18n "security" }}</th>
|
||||
</tr>
|
||||
<tr v-for="(client, index) in inbound.settings.vmesses" :class="index % 2 == 1 ? 'client-table-odd-row' : ''">
|
||||
<td>[[ client.email ]]</td>
|
||||
<td>
|
||||
<div class="client-cell">
|
||||
<div class="client-email">[[ client.email ]]</div>
|
||||
<div v-if="client.comment && client.comment.trim()" class="client-comment">[[ client.comment ]]</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>[[ client.id ]]</td>
|
||||
<td>[[ client.security ]]</td>
|
||||
</tr>
|
||||
|
|
|
@ -79,6 +79,54 @@
|
|||
max-width: 200px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.client-comment {
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
font-style: italic;
|
||||
line-height: 1.2;
|
||||
}
|
||||
.dark .client-comment {
|
||||
color: #bbb;
|
||||
}
|
||||
.client-email {
|
||||
font-weight: 500;
|
||||
}
|
||||
.client-cell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
.client-info-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
.client-popup-item {
|
||||
margin-bottom: 8px;
|
||||
padding: 4px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
.dark .client-popup-item {
|
||||
border-bottom: 1px solid #333;
|
||||
}
|
||||
.client-popup-item:last-child {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.client-popup-email {
|
||||
font-weight: 500;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.client-popup-comment {
|
||||
font-size: 11px;
|
||||
color: #666;
|
||||
font-style: italic;
|
||||
max-width: 200px;
|
||||
word-break: break-word;
|
||||
}
|
||||
.dark .client-popup-comment {
|
||||
color: #aaa;
|
||||
}
|
||||
.online-animation .ant-badge-status-dot {
|
||||
animation: onlineAnimation 1.2s linear infinite;
|
||||
}
|
||||
|
@ -382,25 +430,37 @@
|
|||
<a-tag :style="{ margin: '0' }" color="green">[[ clientCount[dbInbound.id].clients ]]</a-tag>
|
||||
<a-popover title='{{ i18n "disabled" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template slot="content">
|
||||
<div v-for="clientEmail in clientCount[dbInbound.id].deactive"><span>[[ clientEmail ]]</span></div>
|
||||
<div v-for="clientEmail in clientCount[dbInbound.id].deactive" :key="clientEmail" class="client-popup-item">
|
||||
<div class="client-popup-email">[[ clientEmail ]]</div>
|
||||
<div v-if="getClientWithComment(clientEmail, dbInbound.id).comment" class="client-popup-comment">[[ getClientWithComment(clientEmail, dbInbound.id).comment ]]</div>
|
||||
</div>
|
||||
</template>
|
||||
<a-tag :style="{ margin: '0', padding: '0 2px' }" v-if="clientCount[dbInbound.id].deactive.length">[[ clientCount[dbInbound.id].deactive.length ]]</a-tag>
|
||||
</a-popover>
|
||||
<a-popover title='{{ i18n "depleted" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template slot="content">
|
||||
<div v-for="clientEmail in clientCount[dbInbound.id].depleted"><span>[[ clientEmail ]]</span></div>
|
||||
<div v-for="clientEmail in clientCount[dbInbound.id].depleted" :key="clientEmail" class="client-popup-item">
|
||||
<div class="client-popup-email">[[ clientEmail ]]</div>
|
||||
<div v-if="getClientWithComment(clientEmail, dbInbound.id).comment" class="client-popup-comment">[[ getClientWithComment(clientEmail, dbInbound.id).comment ]]</div>
|
||||
</div>
|
||||
</template>
|
||||
<a-tag :style="{ margin: '0', padding: '0 2px' }" color="red" v-if="clientCount[dbInbound.id].depleted.length">[[ clientCount[dbInbound.id].depleted.length ]]</a-tag>
|
||||
</a-popover>
|
||||
<a-popover title='{{ i18n "depletingSoon" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template slot="content">
|
||||
<div v-for="clientEmail in clientCount[dbInbound.id].expiring"><span>[[ clientEmail ]]</span></div>
|
||||
<div v-for="clientEmail in clientCount[dbInbound.id].expiring" :key="clientEmail" class="client-popup-item">
|
||||
<div class="client-popup-email">[[ clientEmail ]]</div>
|
||||
<div v-if="getClientWithComment(clientEmail, dbInbound.id).comment" class="client-popup-comment">[[ getClientWithComment(clientEmail, dbInbound.id).comment ]]</div>
|
||||
</div>
|
||||
</template>
|
||||
<a-tag :style="{ margin: '0', padding: '0 2px' }" color="orange" v-if="clientCount[dbInbound.id].expiring.length">[[ clientCount[dbInbound.id].expiring.length ]]</a-tag>
|
||||
</a-popover>
|
||||
<a-popover title='{{ i18n "online" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template slot="content">
|
||||
<div v-for="clientEmail in clientCount[dbInbound.id].online"><span>[[ clientEmail ]]</span></div>
|
||||
<div v-for="clientEmail in clientCount[dbInbound.id].online" :key="clientEmail" class="client-popup-item">
|
||||
<div class="client-popup-email">[[ clientEmail ]]</div>
|
||||
<div v-if="getClientWithComment(clientEmail, dbInbound.id).comment" class="client-popup-comment">[[ getClientWithComment(clientEmail, dbInbound.id).comment ]]</div>
|
||||
</div>
|
||||
</template>
|
||||
<a-tag :style="{ margin: '0', padding: '0 2px' }" color="blue" v-if="clientCount[dbInbound.id].online.length">[[ clientCount[dbInbound.id].online.length ]]</a-tag>
|
||||
</a-popover>
|
||||
|
@ -479,25 +539,37 @@
|
|||
<a-tag :style="{ margin: '0' }" color="blue">[[ clientCount[dbInbound.id].clients ]]</a-tag>
|
||||
<a-popover title='{{ i18n "disabled" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template slot="content">
|
||||
<p v-for="clientEmail in clientCount[dbInbound.id].deactive">[[ clientEmail ]]</p>
|
||||
<div v-for="clientEmail in clientCount[dbInbound.id].deactive" :key="clientEmail" class="client-popup-item">
|
||||
<div class="client-popup-email">[[ clientEmail ]]</div>
|
||||
<div v-if="getClientWithComment(clientEmail, dbInbound.id).comment" class="client-popup-comment">[[ getClientWithComment(clientEmail, dbInbound.id).comment ]]</div>
|
||||
</div>
|
||||
</template>
|
||||
<a-tag :style="{ margin: '0', padding: '0 2px' }" v-if="clientCount[dbInbound.id].deactive.length">[[ clientCount[dbInbound.id].deactive.length ]]</a-tag>
|
||||
</a-popover>
|
||||
<a-popover title='{{ i18n "depleted" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template slot="content">
|
||||
<p v-for="clientEmail in clientCount[dbInbound.id].depleted">[[ clientEmail ]]</p>
|
||||
<div v-for="clientEmail in clientCount[dbInbound.id].depleted" :key="clientEmail" class="client-popup-item">
|
||||
<div class="client-popup-email">[[ clientEmail ]]</div>
|
||||
<div v-if="getClientWithComment(clientEmail, dbInbound.id).comment" class="client-popup-comment">[[ getClientWithComment(clientEmail, dbInbound.id).comment ]]</div>
|
||||
</div>
|
||||
</template>
|
||||
<a-tag :style="{ margin: '0', padding: '0 2px' }" color="red" v-if="clientCount[dbInbound.id].depleted.length">[[ clientCount[dbInbound.id].depleted.length ]]</a-tag>
|
||||
</a-popover>
|
||||
<a-popover title='{{ i18n "depletingSoon" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template slot="content">
|
||||
<p v-for="clientEmail in clientCount[dbInbound.id].expiring">[[ clientEmail ]]</p>
|
||||
<div v-for="clientEmail in clientCount[dbInbound.id].expiring" :key="clientEmail" class="client-popup-item">
|
||||
<div class="client-popup-email">[[ clientEmail ]]</div>
|
||||
<div v-if="getClientWithComment(clientEmail, dbInbound.id).comment" class="client-popup-comment">[[ getClientWithComment(clientEmail, dbInbound.id).comment ]]</div>
|
||||
</div>
|
||||
</template>
|
||||
<a-tag :style="{ margin: '0', padding: '0 2px' }" color="orange" v-if="clientCount[dbInbound.id].expiring.length">[[ clientCount[dbInbound.id].expiring.length ]]</a-tag>
|
||||
</a-popover>
|
||||
<a-popover title='{{ i18n "online" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template slot="content">
|
||||
<p v-for="clientEmail in clientCount[dbInbound.id].online">[[ clientEmail ]]</p>
|
||||
<div v-for="clientEmail in clientCount[dbInbound.id].online" :key="clientEmail" class="client-popup-item">
|
||||
<div class="client-popup-email">[[ clientEmail ]]</div>
|
||||
<div v-if="getClientWithComment(clientEmail, dbInbound.id).comment" class="client-popup-comment">[[ getClientWithComment(clientEmail, dbInbound.id).comment ]]</div>
|
||||
</div>
|
||||
</template>
|
||||
<a-tag :style="{ margin: '0', padding: '0 2px' }" color="green" v-if="clientCount[dbInbound.id].online.length">[[ clientCount[dbInbound.id].online.length ]]</a-tag>
|
||||
</a-popover>
|
||||
|
@ -716,6 +788,17 @@
|
|||
loading(spinning = true) {
|
||||
this.spinning = spinning;
|
||||
},
|
||||
getClientWithComment(email, inboundId) {
|
||||
const dbInbound = this.dbInbounds.find(inbound => inbound.id === inboundId);
|
||||
if (!dbInbound) return { email, comment: '' };
|
||||
|
||||
const inboundSettings = JSON.parse(dbInbound.settings);
|
||||
if (inboundSettings.clients) {
|
||||
const client = inboundSettings.clients.find(c => c.email === email);
|
||||
return client ? { email: client.email, comment: client.comment || '' } : { email, comment: '' };
|
||||
}
|
||||
return { email, comment: '' };
|
||||
},
|
||||
async getDBInbounds() {
|
||||
this.refreshing = true;
|
||||
const msg = await HttpUtil.post('/panel/inbound/list');
|
||||
|
|
Loading…
Reference in a new issue