diff options
| author | Shishkevich D. <135337715+shishkevichd@users.noreply.github.com> | 2025-06-21 20:27:09 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-06-21 20:27:09 +0300 |
| commit | 4b20f16024769a072fa7c665d605e38134ef2920 (patch) | |
| tree | 8177fc89b390607c328b8ddfc2c79f8e4af888ca /web | |
| parent | d642774a4493912e76dbc294dce834cf5b635324 (diff) | |
refactor: new loading logic, icons for tabs
Diffstat (limited to 'web')
| -rw-r--r-- | web/html/inbounds.html | 954 | ||||
| -rw-r--r-- | web/html/index.html | 41 | ||||
| -rw-r--r-- | web/html/settings.html | 126 | ||||
| -rw-r--r-- | web/html/settings/xray/advanced.html | 2 | ||||
| -rw-r--r-- | web/html/xray.html | 2682 |
5 files changed, 1959 insertions, 1846 deletions
diff --git a/web/html/inbounds.html b/web/html/inbounds.html index 7b00ae5e..e1267190 100644 --- a/web/html/inbounds.html +++ b/web/html/inbounds.html @@ -148,9 +148,9 @@ <a-sidebar></a-sidebar> <a-layout id="content-layout"> <a-layout-content> - <a-spin :spinning="spinning" :delay="500" tip='{{ i18n "loading"}}'> + <a-spin :spinning="loadingStates.spinning" :delay="500" tip='{{ i18n "loading"}}'> <transition name="list" appear> - <a-alert type="error" v-if="showAlert" :style="{ marginBottom: '10px' }" + <a-alert type="error" v-if="showAlert && loadingStates.fetched" :style="{ marginBottom: '10px' }" message='{{ i18n "secAlertTitle" }}' color="red" description='{{ i18n "secAlertSsl" }}' @@ -158,499 +158,508 @@ </a-alert> </transition> <transition name="list" appear> - <a-card size="small" :style="{ padding: '16px' }" hoverable> - <a-row> - <a-col :sm="12" :md="6"> - <a-custom-statistic title='{{ i18n "pages.inbounds.totalDownUp" }}' :value="`${SizeFormatter.sizeFormat(total.up)} / ${SizeFormatter.sizeFormat(total.down)}`"> - <template #prefix> - <a-icon type="swap"></a-icon> - </template> - </a-custom-statistic> - </a-col> - <a-col :sm="12" :md="6"> - <a-custom-statistic title='{{ i18n "pages.inbounds.totalUsage" }}' :value="SizeFormatter.sizeFormat(total.up + total.down)" :style="{ marginTop: isMobile ? '10px' : 0 }"> - <template #prefix> - <a-icon type="pie-chart"></a-icon> - </template> - </a-custom-statistic> - </a-col> - <a-col :sm="12" :md="6"> - <a-custom-statistic title='{{ i18n "pages.inbounds.inboundCount" }}' :value="dbInbounds.length" :style="{ marginTop: isMobile ? '10px' : 0 }"> - <template #prefix> - <a-icon type="bars"></a-icon> - </template> - </a-custom-statistic> - </a-col> - <a-col :sm="12" :md="6"> - <a-custom-statistic title='{{ i18n "clients" }}' value=" " :style="{ marginTop: isMobile ? '10px' : 0 }"> - <template #prefix> - <a-space direction="horizontal"> - <a-icon type="team"></a-icon> - <div> - <a-back-top :target="() => document.getElementById('content-layout')" visibility-height="200"></a-back-top> - <a-tag color="green">[[ total.clients ]]</a-tag> - <a-popover title='{{ i18n "disabled" }}' :overlay-class-name="themeSwitcher.currentTheme"> - <template slot="content"> - <div v-for="clientEmail in total.deactive"><span>[[ clientEmail ]]</span></div> - </template> - <a-tag v-if="total.deactive.length">[[ total.deactive.length ]]</a-tag> - </a-popover> - <a-popover title='{{ i18n "depleted" }}' :overlay-class-name="themeSwitcher.currentTheme"> - <template slot="content"> - <div v-for="clientEmail in total.depleted"><span>[[ clientEmail ]]</span></div> - </template> - <a-tag color="red" v-if="total.depleted.length">[[ total.depleted.length ]]</a-tag> - </a-popover> - <a-popover title='{{ i18n "depletingSoon" }}' :overlay-class-name="themeSwitcher.currentTheme"> - <template slot="content"> - <div v-for="clientEmail in total.expiring"><span>[[ clientEmail ]]</span></div> - </template> - <a-tag color="orange" v-if="total.expiring.length">[[ total.expiring.length ]]</a-tag> - </a-popover> - <a-popover title='{{ i18n "online" }}' :overlay-class-name="themeSwitcher.currentTheme"> - <template slot="content"> - <div v-for="clientEmail in onlineClients"><span>[[ clientEmail ]]</span></div> - </template> - <a-tag color="blue" v-if="onlineClients.length">[[ onlineClients.length ]]</a-tag> - </a-popover> - </div> - </a-space> - </template> - </a-custom-statistic> - </a-col> - </a-row> - </a-card> - </transition> - <transition name="list" appear> - <a-card hoverable> - <template #title> - <a-space direction="horizontal"> - <a-button type="primary" icon="plus" @click="openAddInbound"> - <template v-if="!isMobile">{{ i18n "pages.inbounds.addInbound" }}</template> - </a-button> - <a-dropdown :trigger="['click']"> - <a-button type="primary" icon="menu"> - <template v-if="!isMobile">{{ i18n "pages.inbounds.generalActions" }}</template> - </a-button> - <a-menu slot="overlay" @click="a => generalActions(a)" :theme="themeSwitcher.currentTheme"> - <a-menu-item key="import"> - <a-icon type="import"></a-icon> - {{ i18n "pages.inbounds.importInbound" }} - </a-menu-item> - <a-menu-item key="export"> - <a-icon type="export"></a-icon> - {{ i18n "pages.inbounds.export" }} - </a-menu-item> - <a-menu-item key="subs" v-if="subSettings.enable"> - <a-icon type="export"></a-icon> - {{ i18n "pages.inbounds.export" }} - {{ i18n "pages.settings.subSettings" }} - </a-menu-item> - <a-menu-item key="resetInbounds"> - <a-icon type="reload"></a-icon> - {{ i18n "pages.inbounds.resetAllTraffic" }} - </a-menu-item> - <a-menu-item key="resetClients"> - <a-icon type="file-done"></a-icon> - {{ i18n "pages.inbounds.resetAllClientTraffics" }} - </a-menu-item> - <a-menu-item key="delDepletedClients" :style="{ color: '#FF4D4F' }"> - <a-icon type="rest"></a-icon> - {{ i18n "pages.inbounds.delDepletedClients" }} - </a-menu-item> - </a-menu> - </a-dropdown> - </a-space> - </template> - <template #extra> - <a-button-group> - <a-button icon="sync" @click="manualRefresh" :loading="refreshing"></a-button> - <a-popover placement="bottomRight" trigger="click" :overlay-class-name="themeSwitcher.currentTheme"> - <template #title> - <div class="ant-custom-popover-title"> - <a-switch v-model="isRefreshEnabled" @change="toggleRefresh" size="small"></a-switch> - <span>{{ i18n "pages.inbounds.autoRefresh" }}</span> - </div> - </template> - <template #content> - <a-space direction="vertical"> - <span>{{ i18n "pages.inbounds.autoRefreshInterval" }}</span> - <a-select v-model="refreshInterval" - :disabled="!isRefreshEnabled" - :style="{ width: '100%' }" - @change="changeRefreshInterval" - :dropdown-class-name="themeSwitcher.currentTheme"> - <a-select-option v-for="key in [5,10,30,60]" :value="key*1000">[[ key ]]s</a-select-option> - </a-select> - </a-space> - </template> - <a-button icon="down"></a-button> - </a-popover> - </a-button-group> - </template> - <a-space direction="vertical"> - <div :style="isMobile ? {} : { display: 'flex', alignItems: 'center', justifyContent: 'flex-start' }"> - <a-switch v-model="enableFilter" - :style="isMobile ? { marginBottom: '.5rem', display: 'flex' } : { marginRight: '.5rem' }" - @change="toggleFilter"> - <a-icon slot="checkedChildren" type="search"></a-icon> - <a-icon slot="unCheckedChildren" type="filter"></a-icon> - </a-switch> - <a-input v-if="!enableFilter" v-model.lazy="searchKey" placeholder='{{ i18n "search" }}' autofocus :style="{ maxWidth: '300px' }" :size="isMobile ? 'small' : ''"></a-input> - <a-radio-group v-if="enableFilter" v-model="filterBy" @change="filterInbounds" button-style="solid" :size="isMobile ? 'small' : ''"> - <a-radio-button value="">{{ i18n "none" }}</a-radio-button> - <a-radio-button value="deactive">{{ i18n "disabled" }}</a-radio-button> - <a-radio-button value="depleted">{{ i18n "depleted" }}</a-radio-button> - <a-radio-button value="expiring">{{ i18n "depletingSoon" }}</a-radio-button> - <a-radio-button value="online">{{ i18n "online" }}</a-radio-button> - </a-radio-group> - </div> - <a-table :columns="isMobile ? mobileColumns : columns" :row-key="dbInbound => dbInbound.id" - :data-source="searchedInbounds" - :scroll="isMobile ? {} : { x: 1000 }" - :pagination=pagination(searchedInbounds) - :expand-icon-as-cell="false" - :expand-row-by-click="false" - :expand-icon-column-index="0" - :indent-size="0" - :row-class-name="dbInbound => (dbInbound.isMultiUser() ? '' : 'hideExpandIcon')" - :style="{ marginTop: '10px' }" - :locale='{ filterConfirm: `{{ i18n "confirm" }}`, filterReset: `{{ i18n "reset" }}`, emptyText: `{{ i18n "noData" }}` }'> - <template slot="action" slot-scope="text, dbInbound"> - <a-dropdown :trigger="['click']"> - <a-icon @click="e => e.preventDefault()" type="more" :style="{ fontSize: '20px', textDecoration: 'solid' }"></a-icon> - <a-menu slot="overlay" @click="a => clickAction(a, dbInbound)" :theme="themeSwitcher.currentTheme"> - <a-menu-item key="edit"> - <a-icon type="edit"></a-icon> - {{ i18n "edit" }} - </a-menu-item> - <a-menu-item key="qrcode" v-if="(dbInbound.isSS && !dbInbound.toInbound().isSSMultiUser) || dbInbound.isWireguard"> - <a-icon type="qrcode"></a-icon> - {{ i18n "qrCode" }} - </a-menu-item> - <template v-if="dbInbound.isMultiUser()"> - <a-menu-item key="addClient"> - <a-icon type="user-add"></a-icon> - {{ i18n "pages.client.add"}} - </a-menu-item> - <a-menu-item key="addBulkClient"> - <a-icon type="usergroup-add"></a-icon> - {{ i18n "pages.client.bulk"}} - </a-menu-item> - <a-menu-item key="resetClients"> - <a-icon type="file-done"></a-icon> - {{ i18n "pages.inbounds.resetInboundClientTraffics"}} + <a-row v-if="!loadingStates.fetched"> + <a-card :style="{ textAlign: 'center', padding: '30px 0', marginTop: '10px', background: 'transparent', border: 'none' }"> + <a-spin tip='{{ i18n "loading" }}'></a-spin> + </a-card> + </a-row> + <a-row :gutter="[isMobile ? 8 : 16, isMobile ? 0 : 12]" v-else> + <a-col> + <a-card size="small" :style="{ padding: '16px' }" hoverable> + <a-row> + <a-col :sm="12" :md="6"> + <a-custom-statistic title='{{ i18n "pages.inbounds.totalDownUp" }}' :value="`${SizeFormatter.sizeFormat(total.up)} / ${SizeFormatter.sizeFormat(total.down)}`"> + <template #prefix> + <a-icon type="swap"></a-icon> + </template> + </a-custom-statistic> + </a-col> + <a-col :sm="12" :md="6"> + <a-custom-statistic title='{{ i18n "pages.inbounds.totalUsage" }}' :value="SizeFormatter.sizeFormat(total.up + total.down)" :style="{ marginTop: isMobile ? '10px' : 0 }"> + <template #prefix> + <a-icon type="pie-chart"></a-icon> + </template> + </a-custom-statistic> + </a-col> + <a-col :sm="12" :md="6"> + <a-custom-statistic title='{{ i18n "pages.inbounds.inboundCount" }}' :value="dbInbounds.length" :style="{ marginTop: isMobile ? '10px' : 0 }"> + <template #prefix> + <a-icon type="bars"></a-icon> + </template> + </a-custom-statistic> + </a-col> + <a-col :sm="12" :md="6"> + <a-custom-statistic title='{{ i18n "clients" }}' value=" " :style="{ marginTop: isMobile ? '10px' : 0 }"> + <template #prefix> + <a-space direction="horizontal"> + <a-icon type="team"></a-icon> + <div> + <a-back-top :target="() => document.getElementById('content-layout')" visibility-height="200"></a-back-top> + <a-tag color="green">[[ total.clients ]]</a-tag> + <a-popover title='{{ i18n "disabled" }}' :overlay-class-name="themeSwitcher.currentTheme"> + <template slot="content"> + <div v-for="clientEmail in total.deactive"><span>[[ clientEmail ]]</span></div> + </template> + <a-tag v-if="total.deactive.length">[[ total.deactive.length ]]</a-tag> + </a-popover> + <a-popover title='{{ i18n "depleted" }}' :overlay-class-name="themeSwitcher.currentTheme"> + <template slot="content"> + <div v-for="clientEmail in total.depleted"><span>[[ clientEmail ]]</span></div> + </template> + <a-tag color="red" v-if="total.depleted.length">[[ total.depleted.length ]]</a-tag> + </a-popover> + <a-popover title='{{ i18n "depletingSoon" }}' :overlay-class-name="themeSwitcher.currentTheme"> + <template slot="content"> + <div v-for="clientEmail in total.expiring"><span>[[ clientEmail ]]</span></div> + </template> + <a-tag color="orange" v-if="total.expiring.length">[[ total.expiring.length ]]</a-tag> + </a-popover> + <a-popover title='{{ i18n "online" }}' :overlay-class-name="themeSwitcher.currentTheme"> + <template slot="content"> + <div v-for="clientEmail in onlineClients"><span>[[ clientEmail ]]</span></div> + </template> + <a-tag color="blue" v-if="onlineClients.length">[[ onlineClients.length ]]</a-tag> + </a-popover> + </div> + </a-space> + </template> + </a-custom-statistic> + </a-col> + </a-row> + </a-card> + </a-col> + <a-col> + <a-card hoverable> + <template #title> + <a-space direction="horizontal"> + <a-button type="primary" icon="plus" @click="openAddInbound"> + <template v-if="!isMobile">{{ i18n "pages.inbounds.addInbound" }}</template> + </a-button> + <a-dropdown :trigger="['click']"> + <a-button type="primary" icon="menu"> + <template v-if="!isMobile">{{ i18n "pages.inbounds.generalActions" }}</template> + </a-button> + <a-menu slot="overlay" @click="a => generalActions(a)" :theme="themeSwitcher.currentTheme"> + <a-menu-item key="import"> + <a-icon type="import"></a-icon> + {{ i18n "pages.inbounds.importInbound" }} </a-menu-item> <a-menu-item key="export"> <a-icon type="export"></a-icon> - {{ i18n "pages.inbounds.export"}} + {{ i18n "pages.inbounds.export" }} </a-menu-item> <a-menu-item key="subs" v-if="subSettings.enable"> <a-icon type="export"></a-icon> - {{ i18n "pages.inbounds.export"}} - {{ i18n "pages.settings.subSettings" }} + {{ i18n "pages.inbounds.export" }} - {{ i18n "pages.settings.subSettings" }} + </a-menu-item> + <a-menu-item key="resetInbounds"> + <a-icon type="reload"></a-icon> + {{ i18n "pages.inbounds.resetAllTraffic" }} + </a-menu-item> + <a-menu-item key="resetClients"> + <a-icon type="file-done"></a-icon> + {{ i18n "pages.inbounds.resetAllClientTraffics" }} </a-menu-item> <a-menu-item key="delDepletedClients" :style="{ color: '#FF4D4F' }"> <a-icon type="rest"></a-icon> {{ i18n "pages.inbounds.delDepletedClients" }} </a-menu-item> - </template> - <template v-else> - <a-menu-item key="showInfo"> - <a-icon type="info-circle"></a-icon> - {{ i18n "info"}} - </a-menu-item> - </template> - <a-menu-item key="clipboard"> - <a-icon type="copy"></a-icon> - {{ i18n "pages.inbounds.exportInbound" }} - </a-menu-item> - <a-menu-item key="resetTraffic"> - <a-icon type="retweet"></a-icon> {{ i18n "pages.inbounds.resetTraffic" }} - </a-menu-item> - <a-menu-item key="clone"> - <a-icon type="block"></a-icon> {{ i18n "pages.inbounds.clone"}} - </a-menu-item> - <a-menu-item key="delete"> - <span :style="{ color: '#FF4D4F' }"> - <a-icon type="delete"></a-icon> {{ i18n "delete"}} - </span> - </a-menu-item> - <a-menu-item v-if="isMobile"> - <a-switch size="small" v-model="dbInbound.enable" @change="switchEnable(dbInbound.id,dbInbound.enable)"></a-switch> - {{ i18n "pages.inbounds.enable" }} - </a-menu-item> - </a-menu> - </a-dropdown> + </a-menu> + </a-dropdown> + </a-space> </template> - <template slot="protocol" slot-scope="text, dbInbound"> - <a-tag :style="{ margin: '0' }" color="purple">[[ dbInbound.protocol ]]</a-tag> - <template v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS"> - <a-tag :style="{ margin: '0' }" color="green">[[ dbInbound.toInbound().stream.network ]]</a-tag> - <a-tag :style="{ margin: '0' }" v-if="dbInbound.toInbound().stream.isTls" color="blue">TLS</a-tag> - <a-tag :style="{ margin: '0' }" v-if="dbInbound.toInbound().stream.isReality" color="blue">Reality</a-tag> - </template> - </template> - <template slot="clients" slot-scope="text, dbInbound"> - <template v-if="clientCount[dbInbound.id]"> - <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" :key="clientEmail" class="client-popup-item"> - <span>[[ clientEmail ]]</span> - <a-tooltip :overlay-class-name="themeSwitcher.currentTheme"> - <template #title> - [[ getClientWithComment(clientEmail, dbInbound.id).comment ]] - </template> - <a-icon type="message" v-if="getClientWithComment(clientEmail, dbInbound.id).comment"></a-icon> - </a-tooltip> - </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" :key="clientEmail" class="client-popup-item"> - <span>[[ clientEmail ]]</span> - <a-tooltip :overlay-class-name="themeSwitcher.currentTheme"> - <template #title> - [[ getClientWithComment(clientEmail, dbInbound.id).comment ]] - </template> - <a-icon type="message" v-if="getClientWithComment(clientEmail, dbInbound.id).comment"></a-icon> - </a-tooltip> + <template #extra> + <a-button-group> + <a-button icon="sync" @click="manualRefresh" :loading="refreshing"></a-button> + <a-popover placement="bottomRight" trigger="click" :overlay-class-name="themeSwitcher.currentTheme"> + <template #title> + <div class="ant-custom-popover-title"> + <a-switch v-model="isRefreshEnabled" @change="toggleRefresh" size="small"></a-switch> + <span>{{ i18n "pages.inbounds.autoRefresh" }}</span> </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" :key="clientEmail" class="client-popup-item"> - <span>[[ clientEmail ]]</span> - <a-tooltip :overlay-class-name="themeSwitcher.currentTheme"> - <template #title> - [[ getClientWithComment(clientEmail, dbInbound.id).comment ]] - </template> - <a-icon type="message" v-if="getClientWithComment(clientEmail, dbInbound.id).comment"></a-icon> - </a-tooltip> - </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" :key="clientEmail" class="client-popup-item"> - <span>[[ clientEmail ]]</span> - <a-tooltip :overlay-class-name="themeSwitcher.currentTheme"> - <template #title> - [[ getClientWithComment(clientEmail, dbInbound.id).comment ]] - </template> - <a-icon type="message" v-if="getClientWithComment(clientEmail, dbInbound.id).comment"></a-icon> - </a-tooltip> - </div> + <template #content> + <a-space direction="vertical"> + <span>{{ i18n "pages.inbounds.autoRefreshInterval" }}</span> + <a-select v-model="refreshInterval" + :disabled="!isRefreshEnabled" + :style="{ width: '100%' }" + @change="changeRefreshInterval" + :dropdown-class-name="themeSwitcher.currentTheme"> + <a-select-option v-for="key in [5,10,30,60]" :value="key*1000">[[ key ]]s</a-select-option> + </a-select> + </a-space> </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-button icon="down"></a-button> </a-popover> - </template> + </a-button-group> </template> - <template slot="traffic" slot-scope="text, dbInbound"> - <a-popover :overlay-class-name="themeSwitcher.currentTheme"> - <template slot="content"> - <table cellpadding="2" width="100%"> - <tr> - <td>↑[[ SizeFormatter.sizeFormat(dbInbound.up) ]]</td> - <td>↓[[ SizeFormatter.sizeFormat(dbInbound.down) ]]</td> - </tr> - <tr v-if="dbInbound.total > 0 && dbInbound.up + dbInbound.down < dbInbound.total"> - <td>{{ i18n "remained" }}</td> - <td>[[ SizeFormatter.sizeFormat(dbInbound.total - dbInbound.up - dbInbound.down) ]]</td> - </tr> - </table> + <a-space direction="vertical"> + <div :style="isMobile ? {} : { display: 'flex', alignItems: 'center', justifyContent: 'flex-start' }"> + <a-switch v-model="enableFilter" + :style="isMobile ? { marginBottom: '.5rem', display: 'flex' } : { marginRight: '.5rem' }" + @change="toggleFilter"> + <a-icon slot="checkedChildren" type="search"></a-icon> + <a-icon slot="unCheckedChildren" type="filter"></a-icon> + </a-switch> + <a-input v-if="!enableFilter" v-model.lazy="searchKey" placeholder='{{ i18n "search" }}' autofocus :style="{ maxWidth: '300px' }" :size="isMobile ? 'small' : ''"></a-input> + <a-radio-group v-if="enableFilter" v-model="filterBy" @change="filterInbounds" button-style="solid" :size="isMobile ? 'small' : ''"> + <a-radio-button value="">{{ i18n "none" }}</a-radio-button> + <a-radio-button value="deactive">{{ i18n "disabled" }}</a-radio-button> + <a-radio-button value="depleted">{{ i18n "depleted" }}</a-radio-button> + <a-radio-button value="expiring">{{ i18n "depletingSoon" }}</a-radio-button> + <a-radio-button value="online">{{ i18n "online" }}</a-radio-button> + </a-radio-group> + </div> + <a-table :columns="isMobile ? mobileColumns : columns" :row-key="dbInbound => dbInbound.id" + :data-source="searchedInbounds" + :scroll="isMobile ? {} : { x: 1000 }" + :pagination=pagination(searchedInbounds) + :expand-icon-as-cell="false" + :expand-row-by-click="false" + :expand-icon-column-index="0" + :indent-size="0" + :row-class-name="dbInbound => (dbInbound.isMultiUser() ? '' : 'hideExpandIcon')" + :style="{ marginTop: '10px' }" + :locale='{ filterConfirm: `{{ i18n "confirm" }}`, filterReset: `{{ i18n "reset" }}`, emptyText: `{{ i18n "noData" }}` }'> + <template slot="action" slot-scope="text, dbInbound"> + <a-dropdown :trigger="['click']"> + <a-icon @click="e => e.preventDefault()" type="more" :style="{ fontSize: '20px', textDecoration: 'solid' }"></a-icon> + <a-menu slot="overlay" @click="a => clickAction(a, dbInbound)" :theme="themeSwitcher.currentTheme"> + <a-menu-item key="edit"> + <a-icon type="edit"></a-icon> + {{ i18n "edit" }} + </a-menu-item> + <a-menu-item key="qrcode" v-if="(dbInbound.isSS && !dbInbound.toInbound().isSSMultiUser) || dbInbound.isWireguard"> + <a-icon type="qrcode"></a-icon> + {{ i18n "qrCode" }} + </a-menu-item> + <template v-if="dbInbound.isMultiUser()"> + <a-menu-item key="addClient"> + <a-icon type="user-add"></a-icon> + {{ i18n "pages.client.add"}} + </a-menu-item> + <a-menu-item key="addBulkClient"> + <a-icon type="usergroup-add"></a-icon> + {{ i18n "pages.client.bulk"}} + </a-menu-item> + <a-menu-item key="resetClients"> + <a-icon type="file-done"></a-icon> + {{ i18n "pages.inbounds.resetInboundClientTraffics"}} + </a-menu-item> + <a-menu-item key="export"> + <a-icon type="export"></a-icon> + {{ i18n "pages.inbounds.export"}} + </a-menu-item> + <a-menu-item key="subs" v-if="subSettings.enable"> + <a-icon type="export"></a-icon> + {{ i18n "pages.inbounds.export"}} - {{ i18n "pages.settings.subSettings" }} + </a-menu-item> + <a-menu-item key="delDepletedClients" :style="{ color: '#FF4D4F' }"> + <a-icon type="rest"></a-icon> + {{ i18n "pages.inbounds.delDepletedClients" }} + </a-menu-item> + </template> + <template v-else> + <a-menu-item key="showInfo"> + <a-icon type="info-circle"></a-icon> + {{ i18n "info"}} + </a-menu-item> + </template> + <a-menu-item key="clipboard"> + <a-icon type="copy"></a-icon> + {{ i18n "pages.inbounds.exportInbound" }} + </a-menu-item> + <a-menu-item key="resetTraffic"> + <a-icon type="retweet"></a-icon> {{ i18n "pages.inbounds.resetTraffic" }} + </a-menu-item> + <a-menu-item key="clone"> + <a-icon type="block"></a-icon> {{ i18n "pages.inbounds.clone"}} + </a-menu-item> + <a-menu-item key="delete"> + <span :style="{ color: '#FF4D4F' }"> + <a-icon type="delete"></a-icon> {{ i18n "delete"}} + </span> + </a-menu-item> + <a-menu-item v-if="isMobile"> + <a-switch size="small" v-model="dbInbound.enable" @change="switchEnable(dbInbound.id,dbInbound.enable)"></a-switch> + {{ i18n "pages.inbounds.enable" }} + </a-menu-item> + </a-menu> + </a-dropdown> + </template> + <template slot="protocol" slot-scope="text, dbInbound"> + <a-tag :style="{ margin: '0' }" color="purple">[[ dbInbound.protocol ]]</a-tag> + <template v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS"> + <a-tag :style="{ margin: '0' }" color="green">[[ dbInbound.toInbound().stream.network ]]</a-tag> + <a-tag :style="{ margin: '0' }" v-if="dbInbound.toInbound().stream.isTls" color="blue">TLS</a-tag> + <a-tag :style="{ margin: '0' }" v-if="dbInbound.toInbound().stream.isReality" color="blue">Reality</a-tag> + </template> </template> - <a-tag :color="ColorUtils.usageColor(dbInbound.up + dbInbound.down, app.trafficDiff, dbInbound.total)"> - [[ SizeFormatter.sizeFormat(dbInbound.up + dbInbound.down) ]] / - <template v-if="dbInbound.total > 0"> - [[ SizeFormatter.sizeFormat(dbInbound.total) ]] + <template slot="clients" slot-scope="text, dbInbound"> + <template v-if="clientCount[dbInbound.id]"> + <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" :key="clientEmail" class="client-popup-item"> + <span>[[ clientEmail ]]</span> + <a-tooltip :overlay-class-name="themeSwitcher.currentTheme"> + <template #title> + [[ getClientWithComment(clientEmail, dbInbound.id).comment ]] + </template> + <a-icon type="message" v-if="getClientWithComment(clientEmail, dbInbound.id).comment"></a-icon> + </a-tooltip> + </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" :key="clientEmail" class="client-popup-item"> + <span>[[ clientEmail ]]</span> + <a-tooltip :overlay-class-name="themeSwitcher.currentTheme"> + <template #title> + [[ getClientWithComment(clientEmail, dbInbound.id).comment ]] + </template> + <a-icon type="message" v-if="getClientWithComment(clientEmail, dbInbound.id).comment"></a-icon> + </a-tooltip> + </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" :key="clientEmail" class="client-popup-item"> + <span>[[ clientEmail ]]</span> + <a-tooltip :overlay-class-name="themeSwitcher.currentTheme"> + <template #title> + [[ getClientWithComment(clientEmail, dbInbound.id).comment ]] + </template> + <a-icon type="message" v-if="getClientWithComment(clientEmail, dbInbound.id).comment"></a-icon> + </a-tooltip> + </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"> + <templat
|
