Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/MHSanaei/3x-ui.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMHSanaei <ho3ein.sanaei@gmail.com>2023-12-13 18:57:36 +0300
committerMHSanaei <ho3ein.sanaei@gmail.com>2023-12-13 19:03:11 +0300
commit8d18c8e98f1b6531d1997feb6933419d71401968 (patch)
tree1283d10c68f3a9b9b2cbeeec95fb34a84e9689e3
parent82e2241bdd9552f57d24c8de4fce6c5320efba4c (diff)
[gui] redesign forms
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
-rw-r--r--web/assets/css/custom.css70
-rw-r--r--web/assets/js/model/outbound.js10
-rw-r--r--web/assets/js/model/xray.js14
-rw-r--r--web/html/login.html5
-rw-r--r--web/html/xui/client_bulk_modal.html317
-rw-r--r--web/html/xui/form/client.html386
-rw-r--r--web/html/xui/form/inbound.html139
-rw-r--r--web/html/xui/form/outbound.html681
-rw-r--r--web/html/xui/form/protocol/dokodemo.html56
-rw-r--r--web/html/xui/form/protocol/http.html2
-rw-r--r--web/html/xui/form/protocol/shadowsocks.html74
-rw-r--r--web/html/xui/form/protocol/socks.html79
-rw-r--r--web/html/xui/form/protocol/trojan.html106
-rw-r--r--web/html/xui/form/protocol/vless.html112
-rw-r--r--web/html/xui/form/protocol/vmess.html40
-rw-r--r--web/html/xui/form/sniffing.html37
-rw-r--r--web/html/xui/form/stream/external_proxy.html46
-rw-r--r--web/html/xui/form/stream/stream_grpc.html26
-rw-r--r--web/html/xui/form/stream/stream_http.html37
-rw-r--r--web/html/xui/form/stream/stream_kcp.html118
-rw-r--r--web/html/xui/form/stream/stream_quic.html61
-rw-r--r--web/html/xui/form/stream/stream_settings.html7
-rw-r--r--web/html/xui/form/stream/stream_sockopt.html60
-rw-r--r--web/html/xui/form/stream/stream_tcp.html159
-rw-r--r--web/html/xui/form/stream/stream_ws.html29
-rw-r--r--web/html/xui/form/tls_settings.html564
-rw-r--r--web/html/xui/inbound_info_modal.html291
-rw-r--r--web/html/xui/inbound_modal.html5
-rw-r--r--web/html/xui/inbounds.html4
-rw-r--r--web/html/xui/xray.html5
-rw-r--r--web/html/xui/xray_reverse_modal.html102
-rw-r--r--web/html/xui/xray_rule_modal.html185
32 files changed, 1442 insertions, 2385 deletions
diff --git a/web/assets/css/custom.css b/web/assets/css/custom.css
index ae2dbf16..b633d644 100644
--- a/web/assets/css/custom.css
+++ b/web/assets/css/custom.css
@@ -55,7 +55,7 @@ style attribute {
}
.ant-table-tbody > tr > td,
.ant-table-thead > tr > th {
- padding: 12px 16px;
+ padding: 12px 8px;
overflow-wrap: break-word;
}
.ant-table-thead > tr > th {
@@ -93,7 +93,6 @@ style attribute {
.ant-table-body {
overflow-x: auto !important;
}
-
.ant-card-hoverable {
cursor: auto;
cursor: pointer;
@@ -133,6 +132,13 @@ style attribute {
margin: 0.5rem;
padding: 0.5rem;
}
+ .ant-modal-body {
+ padding: 10px;
+ }
+ .ant-form-item-label {
+ line-height: 1.5;
+ padding: 8px 0 0;
+ }
}
.ant-layout-content {
@@ -410,6 +416,10 @@ style attribute {
background-color: white;
}
+.ant-form-item {
+ margin-bottom: 0;
+}
+
.ant-setting-textarea {
margin-top: 1.5rem;
}
@@ -802,12 +812,6 @@ style attribute {
border-color: #fec093;
}
-.ant-modal-confirm-confirm .ant-modal-confirm-body>.anticon, .ant-modal-confirm-warning .ant-modal-confirm-body>.anticon,
-.ant-alert-warning .ant-alert-icon,
-.has-warning.has-feedback .ant-form-item-children-icon {
- color: #f37b24;
-}
-
.dark .has-warning .ant-input,
.dark .has-warning .ant-input:hover {
border-color: #784e1d;
@@ -1045,53 +1049,3 @@ li.ant-select-dropdown-menu-item:empty:after {
.ant-input-number {
overflow: clip;
}
-
-.tag-of-wrap {
- text-wrap: pretty;
- overflow-wrap: anywhere;
- max-width: 200px;
-}
-
-.tag-of-wrap-l {
- text-wrap: pretty;
- overflow-wrap: anywhere;
- max-width: 350px;
-}
-
-.ant-modal-body,
-.ant-collapse-content>.ant-collapse-content-box {
- overflow-x: auto;
-}
-
-.ant-calendar-year-panel-year:hover,
-.ant-calendar-decade-panel-decade:hover,
-.ant-calendar-month-panel-month:hover,
-.ant-dropdown-menu-item:hover,
-.ant-dropdown-menu-submenu-title:hover,
-.ant-select-dropdown-menu-item-active:not(.ant-select-dropdown-menu-item-disabled),
-.ant-select-dropdown-menu-item:hover:not(.ant-select-dropdown-menu-item-disabled),
-.ant-table-tbody
- > tr.ant-table-row-hover:not(.ant-table-expanded-row):not(
- .ant-table-row-selected
- )
- > td,
-.ant-table-tbody
- > tr:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected)
- > td,
-.ant-table-thead
- > tr.ant-table-row-hover:not(.ant-table-expanded-row):not(
- .ant-table-row-selected
- )
- > td,
-.ant-table-thead
- > tr:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected)
- > td {
- background-color: rgb(232 244 242);
-}
-
-.dark .ant-dropdown-menu-item:hover,
-.dark .ant-dropdown-menu-submenu-title:hover,
-.dark .ant-select-dropdown-menu-item-active:not(.ant-select-dropdown-menu-item-disabled),
-.dark .ant-select-dropdown-menu-item:hover:not(.ant-select-dropdown-menu-item-disabled) {
- background-color: #313f5a;
-}
diff --git a/web/assets/js/model/outbound.js b/web/assets/js/model/outbound.js
index 75d3b878..5aad52dd 100644
--- a/web/assets/js/model/outbound.js
+++ b/web/assets/js/model/outbound.js
@@ -476,7 +476,7 @@ class Outbound extends CommonClass {
if(data.length !=2) return null;
switch(data[0].toLowerCase()){
case Protocols.VMess:
- return this.fromVmessLink(JSON.parse(atob(data[1])));
+ return this.fromVmessLink(JSON.parse(Base64.decode(data[1])));
case Protocols.VLESS:
case Protocols.Trojan:
case 'ss':
@@ -493,8 +493,8 @@ class Outbound extends CommonClass {
if (network === 'tcp') {
stream.tcp = new TcpStreamSettings(
json.type,
- json.host ? json.host.split(','): [],
- json.path ? json.path.split(','): []);
+ json.host ?? '',
+ json.path ?? '');
} else if (network === 'kcp') {
stream.kcp = new KcpStreamSettings();
stream.type = json.type;
@@ -505,7 +505,7 @@ class Outbound extends CommonClass {
stream.network = 'http'
stream.http = new HttpStreamSettings(
json.path,
- json.host ? json.host.split(',') : []);
+ json.host);
} else if (network === 'quic') {
stream.quic = new QuicStreamSettings(
json.host ? json.host : 'none',
@@ -570,7 +570,7 @@ class Outbound extends CommonClass {
let sni=url.searchParams.get('sni') ?? '';
let sid=url.searchParams.get('sid') ?? '';
let spx=url.searchParams.get('spx') ?? '';
- stream.tls = new RealityStreamSettings(pbk, fp, sni, sid, spx);
+ stream.reality = new RealityStreamSettings(pbk, fp, sni, sid, spx);
}
let data = link.split('?');
diff --git a/web/assets/js/model/xray.js b/web/assets/js/model/xray.js
index 9478312e..e0c8a912 100644
--- a/web/assets/js/model/xray.js
+++ b/web/assets/js/model/xray.js
@@ -602,7 +602,7 @@ class XtlsStreamSettings extends XrayCommonClass {
alpn=[ALPN_OPTION.H2,ALPN_OPTION.HTTP1],
settings=new XtlsStreamSettings.Settings()) {
super();
- this.server = serverName;
+ this.sni = serverName;
this.certs = certificates;
this.alpn = alpn;
this.settings = settings;
@@ -636,7 +636,7 @@ class XtlsStreamSettings extends XrayCommonClass {
toJson() {
return {
- serverName: this.server,
+ serverName: this.sni,
certificates: XtlsStreamSettings.toJsonArray(this.certs),
alpn: this.alpn,
settings: this.settings,
@@ -1081,7 +1081,7 @@ class Inbound extends XrayCommonClass {
get serverName() {
if (this.stream.isTls) return this.stream.tls.sni;
- if (this.stream.isXtls) return this.stream.xtls.server;
+ if (this.stream.isXtls) return this.stream.xtls.sni;
if (this.stream.isReality) return this.stream.reality.serverNames;
return "";
}
@@ -1326,8 +1326,8 @@ class Inbound extends XrayCommonClass {
if(this.stream.xtls.settings.allowInsecure){
params.set("allowInsecure", "1");
}
- if (!ObjectUtil.isEmpty(this.stream.xtls.server)){
- params.set("sni", this.stream.xtls.server);
+ if (!ObjectUtil.isEmpty(this.stream.xtls.sni)){
+ params.set("sni", this.stream.xtls.sni);
}
params.set("flow", flow);
}
@@ -1533,8 +1533,8 @@ class Inbound extends XrayCommonClass {
if(this.stream.xtls.settings.allowInsecure){
params.set("allowInsecure", "1");
}
- if (this.stream.xtls.settings.serverName !== ''){
- params.set("sni", this.stream.xtls.settings.serverName);
+ if (!ObjectUtil.isEmpty(this.stream.xtls.sni)){
+ params.set("sni", this.stream.xtls.sni);
}
params.set("flow", flow);
}
diff --git a/web/html/login.html b/web/html/login.html
index 900dd176..520bbcb4 100644
--- a/web/html/login.html
+++ b/web/html/login.html
@@ -27,6 +27,7 @@
text-align: center;
align-items: center;
justify-content: center;
+ width: 100%;
}
.title {
font-size: 32px;
@@ -92,6 +93,10 @@
.dark h1 {
color: rgba(255, 255, 255, 0.85);
}
+ .ant-form-item {
+ margin-bottom: 16px;
+ }
+
.ant-btn-primary-login {
color: #008771;
background-color: #e8f4f2;
diff --git a/web/html/xui/client_bulk_modal.html b/web/html/xui/client_bulk_modal.html
index e0295110..1fa64306 100644
--- a/web/html/xui/client_bulk_modal.html
+++ b/web/html/xui/client_bulk_modal.html
@@ -2,206 +2,123 @@
<a-modal id="client-bulk-modal" v-model="clientsBulkModal.visible" :title="clientsBulkModal.title"
@ok="clientsBulkModal.ok" :confirm-loading="clientsBulkModal.confirmLoading" :closable="true" :mask-closable="false"
:ok-text="clientsBulkModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
- <a-form layout="inline">
- <table width="100%" class="ant-table-tbody">
- <tr>
- <td>{{ i18n "pages.client.method" }}</td>
- <td>
- <a-form-item>
- <a-select v-model="clientsBulkModal.emailMethod" buttonStyle="solid" style="width: 250px"
- :dropdown-class-name="themeSwitcher.currentTheme">
- <a-select-option :value="0">Random</a-select-option>
- <a-select-option :value="1">Random+Prefix</a-select-option>
- <a-select-option :value="2">Random+Prefix+Num</a-select-option>
- <a-select-option :value="3">Random+Prefix+Num+Postfix</a-select-option>
- <a-select-option :value="4">Prefix+Num+Postfix</a-select-option>
- </a-select>
- </a-form-item>
- </td>
- </tr>
- <tr v-if="clientsBulkModal.emailMethod>1">
- <td>{{ i18n "pages.client.first" }}</td>
- <td>
- <a-form-item>
- <a-input-number v-model="clientsBulkModal.firstNum" :min="1"></a-input-number>
- </a-form-item>
- </td>
- </tr>
- <tr v-if="clientsBulkModal.emailMethod>1">
- <td>{{ i18n "pages.client.last" }}</td>
- <td>
- <a-form-item>
- <a-input-number v-model="clientsBulkModal.lastNum"
- :min="clientsBulkModal.firstNum"></a-input-number>
- </a-form-item>
- </td>
- </tr>
- <tr v-if="clientsBulkModal.emailMethod>0">
- <td>{{ i18n "pages.client.prefix" }}</td>
- <td>
- <a-form-item>
- <a-input v-model="clientsBulkModal.emailPrefix" style="width: 250px"></a-input>
- </a-form-item>
- </td>
- </tr>
- <tr v-if="clientsBulkModal.emailMethod>2">
- <td>{{ i18n "pages.client.postfix" }}</td>
- <td>
- <a-form-item>
- <a-input v-model="clientsBulkModal.emailPostfix" style="width: 250px"></a-input>
- </a-form-item>
- </td>
- </tr>
- <tr v-if="clientsBulkModal.emailMethod < 2">
- <td>{{ i18n "pages.client.clientCount" }}</td>
- <td>
- <a-form-item>
- <a-input-number v-model="clientsBulkModal.quantity" :min="1" :max="100"></a-input-number>
- </a-form-item>
- </td>
- </tr>
- <tr v-if="clientsBulkModal.inbound.canEnableTlsFlow()">
- <td>Flow</td>
- <td>
- <a-form-item>
- <a-select v-model="clientsBulkModal.flow" style="width: 250px"
- :dropdown-class-name="themeSwitcher.currentTheme">
- <a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
- <a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
- </a-select>
- </a-form-item>
- </td>
- </tr>
- <tr v-if="app.subSettings.enable">
- <td>Subscription
- <a-tooltip>
- <template slot="title">
- <span>{{ i18n "pages.inbounds.subscriptionDesc" }}</span>
- </template>
- <a-icon @click="client.subId = RandomUtil.randomLowerAndNum(16)" type="sync"></a-icon>
- </a-tooltip>
- </td>
- <td>
- <a-form-item>
- <a-input v-model.trim="clientsBulkModal.subId" style="width: 250px"></a-input>
- </a-form-item>
- </td>
- </tr>
- <tr v-if="app.tgBotEnable">
- <td>Telegram ID
- <a-tooltip>
- <template slot="title">
- <span>{{ i18n "pages.inbounds.telegramDesc" }}</span>
- </template>
- <a-icon type="question-circle" theme="filled"></a-icon>
- </a-tooltip>
- </td>
- <td>
- <a-form-item>
- <a-input v-model.trim="clientsBulkModal.tgId" style="width: 250px"></a-input>
- </a-form-item>
- </td>
- </tr>
- <tr>
- <td>
- <label>
- <span>{{ i18n "pages.inbounds.IPLimit" }}</span>
- <a-tooltip>
- <template slot="title">
- <span>{{ i18n "pages.inbounds.IPLimitDesc" }}</span>
- </template>
- <a-icon type="question-circle" theme="filled"></a-icon>
- </a-tooltip>
- </label>
- </td>
- <td>
- <a-form-item>
- <a-input-number v-model="clientsBulkModal.limitIp" min="0"></a-input-number>
- </a-form-item>
- </td>
- </tr>
- <tr>
- <td v-if="clientsBulkModal.inbound.xtls">
- <label>Flow</label>
- </td>
- <td v-if="clientsBulkModal.inbound.xtls">
- <a-form-item>
- <a-select v-model="clientsBulkModal.flow" style="width: 200px"
- :dropdown-class-name="themeSwitcher.currentTheme">
- <a-select-option value="">{{ i18n "none" }}</a-select-option>
- <a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
- </a-select>
- </a-form-item>
- </td>
- </tr>
- <tr>
- <td>
- <span>{{ i18n "pages.inbounds.totalFlow" }}</span>(GB)
- <a-tooltip>
- <template slot="title">
- 0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
- </template>
- <a-icon type="question-circle" theme="filled"></a-icon>
- </a-tooltip>
- </td>
- <td>
- <a-form-item>
- <a-input-number v-model="clientsBulkModal.totalGB" :min="0"></a-input-number>
- </a-form-item>
- </td>
- </tr>
- <tr>
- <td>{{ i18n "pages.client.delayedStart" }}</td>
- <td>
- <a-form-item>
- <a-switch v-model="clientsBulkModal.delayedStart"
- @click="clientsBulkModal.expiryTime=0"></a-switch>
- </a-form-item>
- </td>
- </tr>
- <tr v-if="clientsBulkModal.delayedStart">
- <td>{{ i18n "pages.client.expireDays" }}</td>
- <td>
- <a-form-item>
- <a-input-number v-model.number="delayedExpireDays" :min="0"></a-input-number>
- </a-form-item>
- </td>
- </tr>
- <tr v-else>
- <td>
- <span>{{ i18n "pages.inbounds.expireDate" }}</span>
- <a-tooltip>
- <template slot="title">
- <span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span>
- </template>
- <a-icon type="question-circle" theme="filled"></a-icon>
- </a-tooltip>
- </td>
- <td>
- <a-form-item>
- <a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
- :dropdown-class-name="themeSwitcher.currentTheme" v-model="clientsBulkModal.expiryTime"
- style="width: 250px;"></a-date-picker>
- </a-form-item>
- </td>
- </tr>
- <tr v-if="clientsBulkModal.expiryTime != 0">
- <td>
- <span>{{ i18n "pages.client.renew" }}</span>
- <a-tooltip>
- <template slot="title">
- <span>{{ i18n "pages.client.renewDesc" }}</span>
- </template>
- <a-icon type="question-circle" theme="filled"></a-icon>
- </a-tooltip>
- </td>
- <td>
- <a-form-item>
- <a-input-number v-model.number="clientsBulkModal.reset" :min="0"></a-input-number>
- </a-form-item>
- </td>
- </tr>
- </table>
+ <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
+ <a-form-item label='{{ i18n "pages.client.method" }}'>
+ <a-select v-model="clientsBulkModal.emailMethod" buttonStyle="solid"
+ :dropdown-class-name="themeSwitcher.currentTheme">
+ <a-select-option :value="0">Random</a-select-option>
+ <a-select-option :value="1">Random+Prefix</a-select-option>
+ <a-select-option :value="2">Random+Prefix+Num</a-select-option>
+ <a-select-option :value="3">Random+Prefix+Num+Postfix</a-select-option>
+ <a-select-option :value="4">Prefix+Num+Postfix</a-select-option>
+ </a-select>
+ </a-form-item>
+ <a-form-item label='{{ i18n "pages.client.first" }}' v-if="clientsBulkModal.emailMethod>1">
+ <a-input-number v-model="clientsBulkModal.firstNum" :min="1"></a-input-number>
+ </a-form-item>
+ <a-form-item label='{{ i18n "pages.client.last" }}' v-if="clientsBulkModal.emailMethod>1">
+ <a-input-number v-model="clientsBulkModal.lastNum" :min="clientsBulkModal.firstNum"></a-input-number>
+ </a-form-item>
+ <a-form-item label='{{ i18n "pages.client.prefix" }}' v-if="clientsBulkModal.emailMethod>0">
+ <a-input v-model="clientsBulkModal.emailPrefix"></a-input>
+ </a-form-item>
+ <a-form-item label='{{ i18n "pages.client.postfix" }}' v-if="clientsBulkModal.emailMethod>2">
+ <a-input v-model="clientsBulkModal.emailPostfix"></a-input>
+ </a-form-item>
+ <a-form-item label='{{ i18n "pages.client.clientCount" }}' v-if="clientsBulkModal.emailMethod < 2">
+ <a-input-number v-model="clientsBulkModal.quantity" :min="1" :max="100"></a-input-number>
+ </a-form-item>
+ <a-form-item label='Flow' v-if="clientsBulkModal.inbound.canEnableTlsFlow()">
+ <a-select v-model="clientsBulkModal.flow" :dropdown-class-name="themeSwitcher.currentTheme">
+ <a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
+ <a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
+ </a-select>
+ </a-form-item>
+ <a-form-item label='Flow' v-if="clientsBulkModal.inbound.xtls">
+ <a-select v-model="clientsBulkModal.flow" :dropdown-class-name="themeSwitcher.currentTheme">
+ <a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
+ <a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
+ </a-select>
+ </a-form-item>
+ <a-form-item v-if="app.subSettings.enable">
+ <template slot="label">
+ <a-tooltip>
+ <template slot="title">
+ <span>{{ i18n "pages.inbounds.subscriptionDesc" }}</span>
+ </template>
+ Subscription
+ <a-icon @click="clientsBulkModal.subId = RandomUtil.randomLowerAndNum(16)" type="sync"></a-icon>
+ </a-tooltip>
+ </template>
+ <a-input v-model.trim="clientsBulkModal.subId"></a-input>
+ </a-form-item>
+ <a-form-item v-if="app.tgBotEnable">
+ <template slot="label">
+ <a-tooltip>
+ <template slot="title">
+ <span>{{ i18n "pages.inbounds.telegramDesc" }}</span>
+ </template>
+ Telegram ID
+ <a-icon type="question-circle" theme="filled"></a-icon>
+ </a-tooltip>
+ </template>
+ <a-input v-model.trim="clientsBulkModal.tgId"></a-input>
+ </a-form-item>
+ <a-form-item>
+ <template slot="label">
+ <a-tooltip>
+ <template slot="title">
+ <span>{{ i18n "pages.inbounds.IPLimitDesc" }}</span>
+ </template>
+ <span>{{ i18n "pages.inbounds.IPLimit" }} </span>
+ <a-icon type="question-circle" theme="filled"></a-icon>
+ </a-tooltip>
+ </template>
+ <a-input-number v-model="clientsBulkModal.limitIp" min="0"></a-input-number>
+ </a-form-item>
+ <a-form-item>
+ <template slot="label">
+ <a-tooltip>
+ <template slot="title">
+ 0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
+ </template>
+ {{ i18n "pages.inbounds.totalFlow" }} (GB)
+ <a-icon type="question-circle" theme="filled"></a-icon>
+ </a-tooltip>
+ </template>
+ <a-input-number v-model="clientsBulkModal.totalGB" :min="0"></a-input-number>
+ </a-form-item>
+ <a-form-item label='{{ i18n "pages.client.delayedStart" }}'>
+ <a-switch v-model="clientsBulkModal.delayedStart" @click="clientsBulkModal.expiryTime=0"></a-switch>
+ </a-form-item>
+ <a-form-item label='{{ i18n "pages.client.expireDays" }}' v-if="clientsBulkModal.delayedStart">
+ <a-input-number v-model.number="delayedExpireDays" :min="0"></a-input-number>
+ </a-form-item>
+ <a-form-item v-else>
+ <template slot="label">
+ <a-tooltip>
+ <template slot="title">
+ <span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span>
+ </template>
+ {{ i18n "pages.inbounds.expireDate" }}
+ <a-icon type="question-circle" theme="filled"></a-icon>
+ </a-tooltip>
+ </template>
+ <a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
+ :dropdown-class-name="themeSwitcher.currentTheme" v-model="clientsBulkModal.expiryTime"></a-date-picker>
+ </a-form-item>
+ <a-form-item v-if="clientsBulkModal.expiryTime != 0">
+ <template slot="label">
+ <span>{{ i18n "pages.client.renew" }}</span>
+ <a-tooltip>
+ <template slot="title">
+ <span>{{ i18n "pages.client.renewDesc" }}</span>
+ </template>
+ <a-icon type="question-circle" theme="filled"></a-icon>
+ </a-tooltip>
+ </template>
+ <a-input-number v-model.number="clientsBulkModal.reset" :min="0"></a-input-number>
+ </a-form-item>
</a-form>
</a-modal>
<script>
diff --git a/web/html/xui/form/client.html b/web/html/xui/form/client.html
index 0ade5241..e5236acc 100644
--- a/web/html/xui/form/client.html
+++ b/web/html/xui/form/client.html
@@ -1,223 +1,173 @@
{{define "form/client"}}
-<a-form layout="inline" v-if="client">
- <table width="100%" class="ant-table-tbody">
- <tr>
- <td>{{ i18n "pages.inbounds.enable" }}</td>
- <td>
- <a-form-item>
- <a-switch v-model="client.enable"></a-switch>
- </a-form-item>
- </td>
- </tr>
- <tr>
- <td>
- <span>{{ i18n "pages.inbounds.email" }}</span>
- <a-tooltip>
- <template slot="title">
- <span>{{ i18n "pages.inbounds.emailDesc" }}</span>
- </template>
- <a-icon type="sync" @click="client.email = RandomUtil.randomLowerAndNum(9)"></a-icon>
- </a-tooltip>
- </td>
- <td>
- <a-form-item>
- <a-input v-model.trim="client.email" style="width: 250px"></a-input>
- </a-form-item>
- </td>
- </tr>
- <tr v-if="inbound.protocol === Protocols.TROJAN || inbound.protocol === Protocols.SHADOWSOCKS">
- <td>password
- <a-icon v-if="inbound.protocol === Protocols.SHADOWSOCKS" @click="client.password = RandomUtil.randomShadowsocksPassword()" type="sync"> </a-icon>
- <a-icon v-if="inbound.protocol === Protocols.TROJAN" @click="client.password = RandomUtil.randomSeq(10)" type="sync"> </a-icon>
- </td>
- <td>
- <a-form-item>
- <a-input v-model.trim="client.password" style="width: 250px"></a-input>
- </a-form-item>
- </td>
- </tr>
- <tr v-if="inbound.protocol === Protocols.VMESS || inbound.protocol === Protocols.VLESS">
- <td>ID <a-icon @click="client.id = RandomUtil.randomUUID()" type="sync"></a-icon></td>
- <td>
- <a-input v-model.trim="client.id" style="width: 250px;"></a-input>
- </td>
- </tr>
- <tr v-if="client.email && app.subSettings.enable">
- <td>Subscription <a-icon @click="client.subId = RandomUtil.randomLowerAndNum(16)" type="sync"></a-icon></td>
- <td>
- <a-tooltip>
- <template slot="title">