Browse Source

<tr> <td> instead of <a-form-item>

convert the form structure to use <tr> and <td> instead of <a-form-item>,
MHSanaei 1 year ago
parent
commit
0f1f3d8439

+ 196 - 123
web/html/xui/client_bulk_modal.html

@@ -1,134 +1,207 @@
 {{define "clientsBulkModal"}}
-<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"
-         :class="themeSwitcher.currentTheme"
-         :ok-text="clientsBulkModal.okText" cancel-text='{{ i18n "close" }}'>
+<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">
-        <a-form-item label='{{ i18n "pages.client.method" }}'>
-            <a-select v-model="clientsBulkModal.emailMethod" buttonStyle="solid" style="width: 350px" :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 [ BE CAREFUL! ]</a-select-option>
-            </a-select>
-        </a-form-item><br />
-        <a-form-item v-if="clientsBulkModal.emailMethod>1">
-            <span slot="label">{{ i18n "pages.client.first" }}</span>
-            <a-input-number v-model="clientsBulkModal.firstNum" :min="1"></a-input-number>
-        </a-form-item>
-        <a-form-item v-if="clientsBulkModal.emailMethod>1">
-            <span slot="label">{{ i18n "pages.client.last" }}</span>
-            <a-input-number v-model="clientsBulkModal.lastNum" :min="clientsBulkModal.firstNum"></a-input-number>
-        </a-form-item>
-        <a-form-item v-if="clientsBulkModal.emailMethod>0">
-            <span slot="label">{{ i18n "pages.client.prefix" }}</span>
-            <a-input v-model="clientsBulkModal.emailPrefix" style="width: 120px"></a-input>
-        </a-form-item>
-        <a-form-item v-if="clientsBulkModal.emailMethod>2">
-            <span slot="label">{{ i18n "pages.client.postfix" }}</span>
-            <a-input v-model="clientsBulkModal.emailPostfix" style="width: 120px"></a-input>
-        </a-form-item>
-        <a-form-item v-if="clientsBulkModal.emailMethod < 2">
-            <span slot="label">{{ i18n "pages.client.clientCount" }}</span>
-            <a-input-number v-model="clientsBulkModal.quantity" :min="1" :max="100"></a-input-number>
-        </a-form-item>
-        <a-form-item v-if="app.subSettings.enable">
-                <span slot="label">
-                    Subscription
+        <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>
-                </span>
-                <a-input v-model.trim="clientsBulkModal.subId"></a-input>
-        </a-form-item>
-        <a-form-item v-if="app.tgBotEnable">
-            <span slot="label">
-                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>
-            </span>
-            <a-input v-model.trim="clientsBulkModal.tgId"></a-input>
-        </a-form-item>
-        <br>
-        <a-form-item>
-            <span slot="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>
-            </span>
-            <a-input-number v-model="clientsBulkModal.limitIp" min="0"></a-input-number>
-        </a-form-item>
-        <br>
-        <a-form-item v-if="clientsBulkModal.inbound.xtls" label="Flow">
-            <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>
-        <a-form-item v-if="clientsBulkModal.inbound.canEnableTlsFlow()" label="Flow" layout="inline">
-            <a-select v-model="clientsBulkModal.flow" style="width: 200px" :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>
-            <span slot="label">
-                <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>
-            </span>
-        <a-input-number v-model="clientsBulkModal.totalGB" :min="0"></a-input-number>
-        </a-form-item>
-        <br>
-        <a-form-item label='{{ i18n "pages.client.delayedStart" }}'>
-            <a-switch v-model="clientsBulkModal.delayedStart" @click="clientsBulkModal.expiryTime=0"></a-switch>
-        </a-form-item>
-        <br>
-        <a-form-item label='{{ i18n "pages.client.expireDays" }}' v-if="clientsBulkModal.delayedStart">
-            <a-input-number v-model="delayedExpireDays" :min="0"></a-input-number>
-        </a-form-item>
-        <a-form-item v-else>
-            <span slot="label">
-                <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>
-            </span>
-            <a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
-                           :dropdown-class-name="themeSwitcher.darkCardClass"
-                           v-model="clientsBulkModal.expiryTime" style="width: 300px;"></a-date-picker>
-        </a-form-item>
-        <a-form-item v-if="clientsBulkModal.expiryTime != 0">
-            <span 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>
-            </span>
-            <a-input-number v-model.number="clientsBulkModal.reset" :min="0"></a-input-number>
-        </a-form-item>
+                </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>
 </a-modal>
 <script>

+ 218 - 163
web/html/xui/form/client.html

@@ -1,168 +1,223 @@
 {{define "form/client"}}
 <a-form layout="inline" v-if="client">
-    <a-form-item label='{{ i18n "pages.inbounds.enable" }}'>
-        <a-switch v-model="client.enable"></a-switch>
-    </a-form-item>
-    <br>
-    <a-form-item>
-        <span slot="label">
-            <span>{{ i18n "pages.inbounds.email" }}</span>
-            <a-tooltip>
-                <template slot="title">
-                    <span>{{ i18n "pages.inbounds.emailDesc" }}</span>
+    <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: 300px;"></a-input>
+            </td>
+        </tr>
+        <tr v-if="client.email && app.subSettings.enable">
+            <td>Subscription</td>
+            <td>
+                <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>
+                <a-form-item>
+                    <a-input v-model.trim="client.subId" style="width: 150px;"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr v-if="client.email && app.tgBotEnable">
+            <td>Telegram ID</td>
+            <td>
+                <a-tooltip>
+                    <template slot="title">
+                        <span>{{ i18n "pages.inbounds.telegramDesc" }}</span>
+                    </template>
+                    <a-icon type="question-circle" theme="filled"></a-icon>
+                </a-tooltip>
+                <a-form-item>
+                    <a-input v-model.trim="client.tgId"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>
+                <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>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-input-number v-model="client.limitIp" min="0"></a-input-number>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr v-if="client.email && client.limitIp > 0 && isEdit">
+            <td>
+                <span>{{ i18n "pages.inbounds.IPLimitlog" }}</span>
+                <a-tooltip>
+                    <template slot="title">
+                        <span>{{ i18n "pages.inbounds.IPLimitlogDesc" }}</span>
+                    </template>
+                    <a-icon type="question-circle" theme="filled"></a-icon>
+                </a-tooltip>
+                <a-tooltip>
+                    <template slot="title">
+                        <span>{{ i18n "pages.inbounds.IPLimitlogclear" }}</span>
+                    </template>
+                    <span style="color: #FF4D4F">
+                        <a-icon type="delete" @click="clearDBClientIps(client.email)"></a-icon>
+                    </span>
+                </a-tooltip>
+            </td>
+            <td>
+                <a-form layout="block">
+                    <a-textarea id="clientIPs" readonly @click="getDBClientIps(client.email)"
+                        placeholder="Click To Get IPs" :auto-size="{ minRows: 5, maxRows: 10 }">
+                    </a-textarea>
+                </a-form>
+            </td>
+        </tr>
+        <tr v-if="inbound.xtls">
+            <td>Flow</td>
+            <td>
+                <a-form-item>
+                    <a-select v-model="client.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 v-else-if="inbound.canEnableTlsFlow()">
+            <td>Flow</td>
+            <td>
+                <a-form-item>
+                    <a-select v-model="client.flow" style="width: 200px"
+                        :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>
+            <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="client._totalGB" :min="0"></a-input-number>
+                </a-form-item>
+                <template v-if="isEdit && clientStats">
+                    <br>
+                    <span> {{ i18n "usage" }}:</span>
+                    <a-tag :color="clientUsageColor(clientStats, app.trafficDiff)">
+                        [[ sizeFormat(clientStats.up) ]] /
+                        [[ sizeFormat(clientStats.down) ]]
+                        ([[ sizeFormat(clientStats.up + clientStats.down) ]])
+                    </a-tag>
+                    <a-tooltip>
+                        <template slot="title">{{ i18n "pages.inbounds.resetTraffic" }}</template>
+                        <a-icon type="retweet"
+                            @click="resetClientTraffic(client.email,clientStats.inboundId,$event.target)"
+                            v-if="client.email.length > 0"></a-icon>
+                    </a-tooltip>
                 </template>
-            </a-tooltip>
-        </span>
-        <a-icon @click="client.email = RandomUtil.randomLowerAndNum(8)" type="sync"> </a-icon>
-        <a-input v-model.trim="client.email" style="width: 200px;"></a-input>
-    </a-form-item>
-    <a-form-item label="Password" v-if="inbound.protocol === Protocols.TROJAN || inbound.protocol === Protocols.SHADOWSOCKS">
-        <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>
-        <a-input v-model.trim="client.password" style="width: 300px;"></a-input>
-    </a-form-item>
-    <br>
-    <a-form-item label="ID" v-if="inbound.protocol === Protocols.VMESS || inbound.protocol === Protocols.VLESS">
-        <a-icon @click="client.id = RandomUtil.randomUUID()" type="sync"> </a-icon>
-        <a-input v-model.trim="client.id" style="width: 300px;"></a-input>
-    </a-form-item>
-	<a-form-item v-if="client.email && app.subSettings.enable">
-        <span slot="label">
-            Subscription
-            <a-tooltip>
-                <template slot="title">
-                    <span>{{ i18n "pages.inbounds.subscriptionDesc" }}</span>
-                </template>
-                <a-icon type="question-circle" theme="filled"></a-icon>
-            </a-tooltip>
-        </span>
-        <a-icon @click="client.subId = RandomUtil.randomLowerAndNum(16)" type="sync"> </a-icon>
-        <a-input v-model.trim="client.subId" style="width: 150px;"></a-input>
-    </a-form-item>
-    <a-form-item v-if="client.email && app.tgBotEnable" >
-        <span slot="label">
-            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>
-        </span>
-        <a-input v-model.trim="client.tgId"></a-input>
-    </a-form-item>
-	<a-form-item>
-		<span slot="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>
-		</span>
-		<a-input-number v-model="client.limitIp" min="0"></a-input-number>
-	</a-form-item>
-	<a-form-item v-if="client.email && client.limitIp > 0 && isEdit">
-        <span slot="label">
-          <span>{{ i18n "pages.inbounds.IPLimitlog" }}</span>
-          <a-tooltip>
-            <template slot="title">
-              <span>{{ i18n "pages.inbounds.IPLimitlogDesc" }}</span>
-            </template>
-            <a-icon type="question-circle" theme="filled"></a-icon>
-          </a-tooltip>
-          <a-tooltip>
-            <template slot="title">
-              <span>{{ i18n "pages.inbounds.IPLimitlogclear" }}</span>
-            </template>
-            <span style="color: #FF4D4F">
-              <a-icon type="delete" @click="clearDBClientIps(client.email)"></a-icon>
-            </span>
-          </a-tooltip>
-        </span>
-        <a-form layout="block">
-          <a-textarea id="clientIPs" readonly 
-            @click="getDBClientIps(client.email)"
-            placeholder="Click To Get IPs"
-            :auto-size="{ minRows: 5, maxRows: 10 }"
-          >
-          </a-textarea>
-        </a-form>
-    </a-form-item>
-    <br>
-    <a-form-item v-if="inbound.xtls" label="Flow">
-        <a-select v-model="client.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>
-    <a-form-item v-else-if="inbound.canEnableTlsFlow()" label="Flow">
-        <a-select v-model="client.flow" style="width: 200px" :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>
-        <span slot="label">
-            <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>
-        </span>
-        <a-input-number v-model="client._totalGB" :min="0"></a-input-number>
-        <template v-if="isEdit && clientStats">
-            <br>
-            <span> {{ i18n "usage" }}:</span>
-            <a-tag :color="clientUsageColor(clientStats, app.trafficDiff)">
-                [[ sizeFormat(clientStats.up) ]] / 
-                [[ sizeFormat(clientStats.down) ]]
-                ([[ sizeFormat(clientStats.up + clientStats.down) ]])
-            </a-tag>
-            <a-tooltip>
-                <template slot="title">{{ i18n "pages.inbounds.resetTraffic" }}</template>
-                <a-icon type="retweet" @click="resetClientTraffic(client.email,clientStats.inboundId,$event.target)"
-                        v-if="client.email.length > 0"></a-icon>
-            </a-tooltip>
-        </template>        
-    </a-form-item>
-    <br>
-    <a-form-item label='{{ i18n "pages.client.delayedStart" }}'>
-        <a-switch v-model="delayedStart" @click="client._expiryTime=0"></a-switch>
-    </a-form-item>
-    <br>
-    <a-form-item label='{{ i18n "pages.client.expireDays" }}' v-if="delayedStart">
-        <a-input-number v-model="delayedExpireDays" :min="0"></a-input-number>
-    </a-form-item>
-    <a-form-item v-else>
-        <span slot="label">
-            <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>
-        </span>
-        <a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
-                       :dropdown-class-name="themeSwitcher.currentTheme"
-                       v-model="client._expiryTime" style="width: 170px;"></a-date-picker>
-        <a-tag color="red" v-if="isEdit && isExpiry">Expired</a-tag>
-    </a-form-item>
-    <a-form-item v-if="client.expiryTime != 0">
-        <span 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>
-        </span>
-        <a-input-number v-model.number="client.reset" :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="delayedStart" @click="client._expiryTime=0"></a-switch>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr v-if="delayedStart">
+            <td>{{ i18n "pages.client.expireDays" }}</td>
+            <td>
+                <a-form-item>
+                    <a-input-number v-model="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="client._expiryTime"
+                        style="width: 170px;"></a-date-picker>
+                    <a-tag color="red" v-if="isEdit && isExpiry">Expired</a-tag>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr v-if="client.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="client.reset" :min="0"></a-input-number>
+                </a-form-item>
+            </td>
+        </tr>
+    </table>
 </a-form>
 {{end}}

+ 82 - 51
web/html/xui/form/inbound.html

@@ -1,63 +1,94 @@
 {{define "form/inbound"}}
 <!-- base -->
 <a-form layout="inline">
-    <a-form-item label='{{ i18n "enable" }}'>
-        <a-switch v-model="dbInbound.enable"></a-switch>
-    </a-form-item>
-    <br>
-    <a-form-item label='{{ i18n "remark" }}'>
-        <a-input v-model.trim="dbInbound.remark"></a-input>
-    </a-form-item>
-    <a-form-item label='{{ i18n "protocol" }}'>
-        <a-select v-model="inbound.protocol" style="width: 160px;" :disabled="isEdit" :dropdown-class-name="themeSwitcher.currentTheme">
-            <a-select-option v-for="p in Protocols" :key="p" :value="p">[[ p ]]</a-select-option>
-        </a-select>
-    </a-form-item>
-    <a-form-item>
-        <span slot="label">
-            {{ i18n "monitor" }}
-            <a-tooltip>
+    <table width="100%" class="ant-table-tbody">
+        <tr>
+            <td>{{ i18n "enable" }}</td>
+            <td>
+                <a-form-item>
+                    <a-switch v-model="dbInbound.enable"></a-switch>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>{{ i18n "remark" }}</td>
+            <td>
+                <a-form-item>
+                    <a-input v-model.trim="dbInbound.remark" style="width: 250px;"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>{{ i18n "protocol" }}</td>
+            <td>
+                <a-form-item>
+                    <a-select v-model="inbound.protocol" style="width: 250px;" :disabled="isEdit" :dropdown-class-name="themeSwitcher.currentTheme">
+                        <a-select-option v-for="p in Protocols" :key="p" :value="p">[[ p ]]</a-select-option>
+                    </a-select>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>{{ i18n "monitor" }}
+                <a-tooltip>
                 <template slot="title">
                     <span>{{ i18n "pages.inbounds.monitorDesc" }}</span>
                 </template>
                 <a-icon type="question-circle" theme="filled"></a-icon>
-            </a-tooltip>
-        </span>
-        <a-input v-model.trim="inbound.listen"></a-input>
-    </a-form-item>
-    <br>
-    <a-form-item label='{{ i18n "pages.inbounds.port" }}'>
-        <a-input-number v-model="inbound.port"></a-input-number>
-    </a-form-item>
-    <br>
-    <a-form-item>
-        <span slot="label">
-            <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>
-        </span>
-        <a-input-number v-model="dbInbound.totalGB" :min="0"></a-input-number>
-    </a-form-item>
-    <a-form-item>
-        <span slot="label">
-            <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>
-        </span>
-        <a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
-                       :dropdown-class-name="themeSwitcher.currentTheme"
-                       v-model="dbInbound._expiryTime" style="width: 250px;"></a-date-picker>
-    </a-form-item>
+                </a-tooltip>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-input v-model.trim="inbound.listen" style="width: 250px;"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>{{ i18n "pages.inbounds.port" }}</td>
+            <td>
+                <a-form-item>
+                    <a-input-number v-model.number="inbound.port"></a-input-number>
+                </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="dbInbound.totalGB" :min="0"></a-input-number>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <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="dbInbound._expiryTime" style="width: 250px;"></a-date-picker>
+                </a-form-item>
+            </td>
+        </tr>
+    </table>
 </a-form>
 
+
 <!-- vmess settings -->
 <template v-if="inbound.protocol === Protocols.VMESS">
     {{template "form/vmess"}}

+ 38 - 18
web/html/xui/form/protocol/dokodemo.html

@@ -1,22 +1,42 @@
 {{define "form/dokodemo"}}
 <a-form layout="inline">
-    <a-form-item label='{{ i18n "pages.inbounds.targetAddress"}}'>
-        <a-input v-model.trim="inbound.settings.address"></a-input>
-    </a-form-item>
-    <a-form-item label='{{ i18n "pages.inbounds.destinationPort"}}'>
-        <a-input-number v-model="inbound.settings.port"></a-input-number>
-    </a-form-item>
-    <br>
-    <a-form-item label='{{ i18n "pages.inbounds.network"}}'>
-        <a-select v-model="inbound.settings.network" style="width: 100px;" :dropdown-class-name="themeSwitcher.currentTheme">
-            <a-select-option value="tcp,udp">TCP+UDP</a-select-option>
-            <a-select-option value="tcp">TCP</a-select-option>
-            <a-select-option value="udp">UDP</a-select-option>
-        </a-select>
-    </a-form-item>
-    <br>
-    <a-form-item label="FollowRedirect">
-        <a-switch v-model="inbound.settings.followRedirect"></a-switch>
-    </a-form-item>
+    <table width="100%" class="ant-table-tbody">
+        <tr>
+            <td>{{ i18n "pages.inbounds.targetAddress"}}</td>
+            <td>
+                <a-form-item>
+                    <a-input v-model.trim="inbound.settings.address"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>{{ i18n "pages.inbounds.destinationPort"}}</td>
+            <td>
+                <a-form-item>
+                    <a-input-number v-model.number="inbound.settings.port"></a-input-number>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>{{ i18n "pages.inbounds.network"}}</td>
+            <td>
+                <a-form-item>
+                    <a-select v-model="inbound.settings.network" style="width: 100px;" :dropdown-class-name="themeSwitcher.currentTheme">
+                        <a-select-option value="tcp,udp">tcp+udp</a-select-option>
+                        <a-select-option value="tcp">tcp</a-select-option>
+                        <a-select-option value="udp">udp</a-select-option>
+                    </a-select>
+                </a-form-item>           
+            </td>
+        </tr>
+        <tr>
+            <td>FollowRedirect</td>
+            <td>
+                <a-form-item>
+                    <a-switch v-model="inbound.settings.followRedirect"></a-switch>
+                </a-form-item>
+            </td>
+        </tr>
+    </table>
 </a-form>
 {{end}}

+ 13 - 11
web/html/xui/form/protocol/http.html

@@ -1,19 +1,21 @@
 {{define "form/http"}}
 <a-form layout="inline">
-    <a-form-item>
-        <a-row>
-            <a-button type="primary" size="small" @click="inbound.settings.addAccount(new Inbound.SocksSettings.SocksAccount())">+</a-button>
-        </a-row>
-        <a-input-group v-for="(account, index) in inbound.settings.accounts">
-            <a-input style="width: 45%" v-model.trim="account.user"
-                addon-before='{{ i18n "username" }}'></a-input>
-            <a-input style="width: 55%" v-model.trim="account.pass"
-                addon-before='{{ i18n "password" }}'>
+        <table style="width: 100%; text-align: center; margin-bottom: 10px;">
+            <tr>
+                <td width="45%">{{ i18n "username" }}</td>
+                <td width="45%">{{ i18n "password" }}</td>
+                <td><a-button size="small" @click="inbound.settings.addAccount(new Inbound.HttpSettings.HttpAccount())">+</a-button></td>
+            </tr>
+        </table>
+        <a-input-group compact v-for="(account, index) in inbound.settings.accounts" style="margin-bottom: 10px;">
+            <a-input style="width: 50%" v-model.trim="account.user" placeholder='{{ i18n "username" }}'>
+                <template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
+            </a-input>
+            <a-input style="width: 50%" v-model.trim="account.pass" placeholder='{{ i18n "password" }}'>
                 <template slot="addonAfter">
-                    <a-button type="primary" size="small" @click="inbound.settings.delAccount(index)">-</a-button>
+                    <a-button size="small" @click="inbound.settings.delAccount(index)">-</a-button>
                 </template>
             </a-input>
         </a-input-group>
-    </a-form-item>
 </a-form>
 {{end}}

+ 37 - 21
web/html/xui/form/protocol/shadowsocks.html

@@ -1,7 +1,7 @@
 {{define "form/shadowsocks"}}
-<a-form layout="inline" style="padding: 10px 0px;">
+<a-form layout="inline">
     <template v-if="inbound.isSSMultiUser">
-    <a-collapse activeKey="0" v-for="(client, index) in inbound.settings.shadowsockses.slice(0,1)" v-if="!isEdit">
+    <a-collapse activeKey="0" v-for="(client, index) in inbound.settings.shadowsockses.slice(0,1)" v-if="!isEdit">  
         <a-collapse-panel header='{{ i18n "pages.inbounds.client" }}'>
             {{template "form/client"}}
         </a-collapse-panel>
@@ -20,24 +20,40 @@
             </table>
         </a-collapse-panel>
     </a-collapse>
-</template>
-</a-form>
-<a-form layout="inline">
-    <a-form-item label='{{ i18n "encryption" }}'>
-        <a-select v-model="inbound.settings.method" style="width: 250px;" :dropdown-class-name="themeSwitcher.currentTheme" @change="SSMethodChange">
-            <a-select-option v-for="method in SSMethods" :value="method">[[ method ]]</a-select-option>
-        </a-select>
-    </a-form-item>
-    <a-form-item v-if="inbound.isSS2022" label='{{ i18n "password" }}'>
-        <a-icon @click="inbound.settings.password = RandomUtil.randomShadowsocksPassword()" type="sync"> </a-icon>
-        <a-input v-model.trim="inbound.settings.password" style="width: 250px;"></a-input>
-    </a-form-item>
-    <a-form-item label='{{ i18n "pages.inbounds.network" }}'>
-        <a-select v-model="inbound.settings.network" style="width: 100px;" :dropdown-class-name="themeSwitcher.currentTheme">
-            <a-select-option value="tcp,udp">TCP+UDP</a-select-option>
-            <a-select-option value="tcp">TCP</a-select-option>
-            <a-select-option value="udp">UDP</a-select-option>
-        </a-select>
-    </a-form-item>
+    </template>
+    <table width="100%" class="ant-table-tbody">
+        <tr>
+            <td>{{ i18n "encryption" }}</td>
+            <td>
+                <a-form-item>
+                    <a-select v-model="inbound.settings.method" style="width: 250px;" @change="SSMethodChange" :dropdown-class-name="themeSwitcher.currentTheme">
+                        <a-select-option v-for="method in SSMethods" :value="method">[[ method ]]</a-select-option>
+                    </a-select>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr v-if="inbound.isSS2022">
+            <td>{{ i18n "password" }}
+                <a-icon @click="inbound.settings.password = RandomUtil.randomShadowsocksPassword()" type="sync"> </a-icon>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-input v-model.trim="inbound.settings.password" style="width: 250px"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>{{ i18n "pages.inbounds.network" }}</td>
+            <td>
+                <a-form-item>
+                    <a-select v-model="inbound.settings.network" style="width: 100px;" :dropdown-class-name="themeSwitcher.currentTheme">
+                        <a-select-option value="tcp,udp">tcp+udp</a-select-option>
+                        <a-select-option value="tcp">tcp</a-select-option>
+                        <a-select-option value="udp">udp</a-select-option>
+                    </a-select>
+                </a-form-item>
+            </td>
+        </tr>
+    </table>
 </a-form>
 {{end}}

+ 48 - 29
web/html/xui/form/protocol/socks.html

@@ -1,33 +1,52 @@
 {{define "form/socks"}}
 <a-form layout="inline">
-    <a-form-item label='{{ i18n "password" }}'>
-        <a-switch :checked="inbound.settings.auth === 'password'"
-                  @change="checked => inbound.settings.auth = checked ? 'password' : 'noauth'"></a-switch>
-    </a-form-item>
-    <br>
-    <template v-if="inbound.settings.auth === 'password'">
-        <a-form-item>
-            <a-row>
-                <a-button type="primary" size="small" @click="inbound.settings.addAccount(new Inbound.SocksSettings.SocksAccount())">+</a-button>
-            </a-row>
-            <a-input-group v-for="(account, index) in inbound.settings.accounts">
-                <a-input style="width: 45%" v-model.trim="account.user"
-                    addon-before='{{ i18n "username" }}'></a-input>
-                <a-input style="width: 55%" v-model.trim="account.pass"
-                    addon-before='{{ i18n "password" }}'>
-                    <template slot="addonAfter">
-                        <a-button type="primary" size="small" @click="inbound.settings.delAccount(index)">-</a-button>
-                    </template>
-                </a-input>
-            </a-input-group>
-        </a-form-item>
-    </template>
-    <br>
-    <a-form-item label='{{ i18n "pages.inbounds.enable" }} udp'>
-        <a-switch v-model="inbound.settings.udp"></a-switch>
-    </a-form-item>
-    <a-form-item v-if="inbound.settings.udp" label="IP">
-        <a-input v-model.trim="inbound.settings.ip"></a-input>
-    </a-form-item>
+    <table width="100%" class="ant-table-tbody">
+        <tr>
+            <td style="width: 30%;">{{ i18n "password" }}</td>
+            <td>
+                <a-form-item>
+                    <a-switch :checked="inbound.settings.auth === 'password'"
+                              @change="checked => inbound.settings.auth = checked ? 'password' : 'noauth'"></a-switch>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr v-if="inbound.settings.auth === 'password'">
+            <td colspan="2">
+                <table style="width: 100%; text-align: center; margin-bottom: 10px;">
+                    <tr>
+                        <td width="45%">{{ i18n "username" }}</td>
+                        <td width="45%">{{ i18n "password" }}</td>
+                        <td><a-button size="small" @click="inbound.settings.addAccount(new Inbound.SocksSettings.SocksAccount())">+</a-button></td>
+                    </tr>
+                </table>
+                <a-input-group compact v-for="(account, index) in inbound.settings.accounts" style="margin-bottom: 10px;">
+                    <a-input style="width: 50%" v-model.trim="account.user" placeholder='{{ i18n "username" }}'>
+                        <template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
+                    </a-input>
+                    <a-input style="width: 50%" v-model.trim="account.pass" placeholder='{{ i18n "password" }}'>
+                        <template slot="addonAfter">
+                            <a-button size="small" @click="inbound.settings.delAccount(index)">-</a-button>
+                        </template>
+                    </a-input>
+                </a-input-group>
+            </td>
+        </tr>
+        <tr>
+            <td>{{ i18n "pages.inbounds.enable" }} udp</td>
+            <td>
+                <a-form-item>
+                    <a-switch v-model="inbound.settings.udp"></a-switch>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr v-if="inbound.settings.udp">
+            <td>IP</td>
+            <td>
+                <a-form-item>
+                    <a-input v-model.trim="inbound.settings.ip"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+    </table>
 </a-form>
 {{end}}

+ 18 - 6
web/html/xui/form/stream/stream_grpc.html

@@ -1,10 +1,22 @@
 {{define "form/streamGRPC"}}
 <a-form layout="inline">
-    <a-form-item label="ServiceName">
-        <a-input v-model.trim="inbound.stream.grpc.serviceName"></a-input>
-    </a-form-item>
-    <a-form-item label="Multi Mode">
-        <a-switch v-model="inbound.stream.grpc.multiMode"></a-switch>
-    </a-form-item>
+    <table width="100%" class="ant-table-tbody">
+        <tr>
+            <td>serviceName</td>
+            <td>
+                <a-form-item>
+                    <a-input v-model.trim="inbound.stream.grpc.serviceName" style="width: 250px;"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>MultiMode</td>
+            <td>
+                <a-form-item>
+                    <a-switch v-model="inbound.stream.grpc.multiMode"></a-switch>
+                </a-form-item>
+            </td>
+        </tr>
+    </table>
 </a-form>
 {{end}}

+ 20 - 8
web/html/xui/form/stream/stream_http.html

@@ -1,12 +1,24 @@
 {{define "form/streamHTTP"}}
 <a-form layout="inline">
-    <a-form-item label='{{ i18n "path" }}'>
-        <a-input v-model.trim="inbound.stream.http.path"></a-input>
-    </a-form-item>
-    <a-form-item label="Host">
-        <a-row v-for="(host, index) in inbound.stream.http.host">
-            <a-input v-model.trim="inbound.stream.http.host[index]"></a-input>
-        </a-row>
-    </a-form-item>
+    <table width="100%" class="ant-table-tbody">
+        <tr>
+            <td>{{ i18n "path" }}</td>
+            <td>
+                <a-form-item>
+                    <a-input v-model.trim="inbound.stream.http.path" style="width: 250px;"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>host</td>
+            <td>
+                <a-form-item>
+                    <a-row v-for="(host, index) in inbound.stream.http.host">
+                        <a-input v-model.trim="inbound.stream.http.host[index]" style="width: 250px;"></a-input>
+                    </a-row>
+                </a-form-item>
+            </td>
+        </tr>
+    </table>
 </a-form>
 {{end}}

+ 82 - 43
web/html/xui/form/stream/stream_kcp.html

@@ -1,47 +1,86 @@
 {{define "form/streamKCP"}}
 <a-form layout="inline">
-    <a-form-item label='{{ i18n "camouflage" }}'>
-        <a-select v-model="inbound.stream.kcp.type" style="width: 280px;" :dropdown-class-name="themeSwitcher.currentTheme">
-            <a-select-option value="none">None (Not Camouflage)</a-select-option>
-            <a-select-option value="srtp">SRTP (Camouflage Video Call)</a-select-option>
-            <a-select-option value="utp">UTP (Camouflage BT Download)</a-select-option>
-            <a-select-option value="wechat-video">Wechat-Video (Camouflage WeChat Video)</a-select-option>
-            <a-select-option value="dtls">DTLS (Camouflage DTLS 1.2 Packages)</a-select-option>
-            <a-select-option value="wireguard">Wireguard (Camouflage Wireguard Packages)</a-select-option>
-        </a-select>
-    </a-form-item>
-    <br>
-    <a-form-item label='{{ i18n "password" }}'>
-        <a-icon @click="inbound.stream.kcp.seed = RandomUtil.randomSeq(10)" type="sync"> </a-icon>
-        <a-input v-model="inbound.stream.kcp.seed" style="width: 150px;" ></a-input>
-    </a-form-item>
-    <br>
-    <a-form-item label="MTU">
-        <a-input-number v-model="inbound.stream.kcp.mtu"></a-input-number>
-    </a-form-item>
-    <br>
-    <a-form-item label="TTI (ms)">
-        <a-input-number v-model="inbound.stream.kcp.tti"></a-input-number>
-    </a-form-item>
-    <br>
-    <a-form-item label="Uplink Capacity (MB/S)">
-        <a-input-number v-model="inbound.stream.kcp.upCap"></a-input-number>
-    </a-form-item>
-    <br>
-    <a-form-item label="Downlink Capacity (MB/S)">
-        <a-input-number v-model="inbound.stream.kcp.downCap"></a-input-number>
-    </a-form-item>
-    <br>
-    <a-form-item label="Congestion">
-        <a-switch v-model="inbound.stream.kcp.congestion"></a-switch>
-    </a-form-item>
-    <br>
-    <a-form-item label="Read Buffer Size (MB)">
-        <a-input-number v-model="inbound.stream.kcp.readBuffer"></a-input-number>
-    </a-form-item>
-    <br>
-    <a-form-item label="Write Buffer Size (MB)">
-        <a-input-number v-model="inbound.stream.kcp.writeBuffer"></a-input-number>
-    </a-form-item>
+    <table width="100%" class="ant-table-tbody">
+        <tr>
+            <td>{{ i18n "camouflage" }}</td>
+            <td>
+                <a-form-item>
+                    <a-select v-model="inbound.stream.kcp.type" style="width: 250px;"
+                        :dropdown-class-name="themeSwitcher.currentTheme">
+                        <a-select-option value="none">None (Not Camouflage)</a-select-option>
+                        <a-select-option value="srtp">SRTP (Camouflage Video Call)</a-select-option>
+                        <a-select-option value="utp">UTP (Camouflage BT Download)</a-select-option>
+                        <a-select-option value="wechat-video">Wechat-Video (Camouflage WeChat Video)</a-select-option>
+                        <a-select-option value="dtls">DTLS (Camouflage DTLS 1.2 Packages)</a-select-option>
+                        <a-select-option value="wireguard">Wireguard (Camouflage Wireguard Packages)</a-select-option>
+                    </a-select>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>{{ i18n "password" }}</td>
+            <td>
+                <a-form-item>
+                    <a-input v-model="inbound.stream.kcp.seed" style="width: 250px;"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>MTU</td>
+            <td>
+                <a-form-item>
+                    <a-input-number v-model.number="inbound.stream.kcp.mtu"></a-input-number>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>TTI (ms)</td>
+            <td>
+                <a-form-item>
+                    <a-input-number v-model.number="inbound.stream.kcp.tti"></a-input-number>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>Uplink Capacity (MB/S)</td>
+            <td>
+                <a-form-item>
+                    <a-input-number v-model.number="inbound.stream.kcp.upCap"></a-input-number>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>Downlink Capacity (MB/S)</td>
+            <td>
+                <a-form-item>
+                    <a-input-number v-model.number="inbound.stream.kcp.downCap"></a-input-number>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>Congestion</td>
+            <td>
+                <a-form-item>
+                    <a-switch v-model="inbound.stream.kcp.congestion"></a-switch>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>Read Buffer Size (MB)</td>
+            <td>
+                <a-form-item>
+                    <a-input-number v-model.number="inbound.stream.kcp.readBuffer"></a-input-number>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>Write Buffer Size (MB)</td>
+            <td>
+                <a-form-item>
+                    <a-input-number v-model.number="inbound.stream.kcp.writeBuffer"></a-input-number>
+                </a-form-item>
+            </td>
+        </tr>
+    </table>
 </a-form>
 {{end}}

+ 39 - 21
web/html/xui/form/stream/stream_quic.html

@@ -1,25 +1,43 @@
 {{define "form/streamQUIC"}}
 <a-form layout="inline">
-    <a-form-item label='{{ i18n "pages.inbounds.stream.quic.encryption" }}'>
-        <a-select v-model="inbound.stream.quic.security" style="width: 165px;" :dropdown-class-name="themeSwitcher.currentTheme">
-            <a-select-option value="none">none</a-select-option>
-            <a-select-option value="aes-128-gcm">aes-128-gcm</a-select-option>
-            <a-select-option value="chacha20-poly1305">chacha20-poly1305</a-select-option>
-        </a-select>
-    </a-form-item>
-    <a-form-item label='{{ i18n "password" }}'>
-        <a-icon @click="inbound.stream.quic.key = RandomUtil.randomSeq(10)" type="sync"> </a-icon>
-        <a-input v-model.trim="inbound.stream.quic.key" style="width: 150px;"></a-input>
-    </a-form-item>
-    <a-form-item label='{{ i18n "camouflage" }}'>
-        <a-select v-model="inbound.stream.quic.type" style="width: 280px;" :dropdown-class-name="themeSwitcher.currentTheme">
-            <a-select-option value="none">none (not camouflage)</a-select-option>
-            <a-select-option value="srtp">srtp (camouflage video call)</a-select-option>
-            <a-select-option value="utp">utp (camouflage BT download)</a-select-option>
-            <a-select-option value="wechat-video">wechat-video (camouflage WeChat video)</a-select-option>
-            <a-select-option value="dtls">dtls (camouflage DTLS 1.2 packages)</a-select-option>
-            <a-select-option value="wireguard">wireguard (camouflage wireguard packages)</a-select-option>
-        </a-select>
-    </a-form-item>
+    <table width="100%" class="ant-table-tbody">
+        <tr>
+            <td>{{ i18n "pages.inbounds.stream.quic.encryption" }}</td>
+            <td>
+                <a-form-item>
+                    <a-select v-model="inbound.stream.quic.security" style="width: 250px;"
+                        :dropdown-class-name="themeSwitcher.currentTheme">
+                        <a-select-option value="none">none</a-select-option>
+                        <a-select-option value="aes-128-gcm">aes-128-gcm</a-select-option>
+                        <a-select-option value="chacha20-poly1305">chacha20-poly1305</a-select-option>
+                    </a-select>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>{{ i18n "password" }}</td>
+            <td>
+                <a-form-item>
+                    <a-input v-model.trim="inbound.stream.quic.key" style="width: 250px;"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>{{ i18n "camouflage" }}</td>
+            <td>
+                <a-form-item>
+                    <a-select v-model="inbound.stream.quic.type" style="width: 280px;"
+                        :dropdown-class-name="themeSwitcher.currentTheme">
+                        <a-select-option value="none">none (not camouflage)</a-select-option>
+                        <a-select-option value="srtp">srtp (camouflage video call)</a-select-option>
+                        <a-select-option value="utp">utp (camouflage BT download)</a-select-option>
+                        <a-select-option value="wechat-video">wechat-video (camouflage WeChat video)</a-select-option>
+                        <a-select-option value="dtls">dtls (camouflage DTLS 1.2 packages)</a-select-option>
+                        <a-select-option value="wireguard">wireguard (camouflage wireguard packages)</a-select-option>
+                    </a-select>
+                </a-form-item>
+            </td>
+        </tr>
+    </table>
 </a-form>
 {{end}}

+ 2 - 1
web/html/xui/form/stream/stream_settings.html

@@ -2,7 +2,8 @@
 <!-- select stream network -->
 <a-form layout="inline">
     <a-form-item label='{{ i18n "transmission" }}'>
-        <a-select v-model="inbound.stream.network" @change="streamNetworkChange" :dropdown-class-name="themeSwitcher.currentTheme" style="width: 100px;">
+        <a-select v-model="inbound.stream.network" @change="streamNetworkChange"
+            :dropdown-class-name="themeSwitcher.currentTheme" style="width: 100px;">
             <a-select-option value="tcp">TCP</a-select-option>
             <a-select-option value="kcp">KCP</a-select-option>
             <a-select-option value="ws">WS</a-select-option>

+ 96 - 64
web/html/xui/form/stream/stream_tcp.html

@@ -1,7 +1,7 @@
 {{define "form/streamTCP"}}
 <!-- tcp type -->
 <a-form layout="inline">
-    <a-form-item label="AcceptProxyProtocol">
+    <a-form-item label="Accept Proxy Protocol" v-if="inbound.canEnableTls()">
         <a-switch v-model="inbound.stream.tcp.acceptProxyProtocol"></a-switch>
     </a-form-item>
     <a-form-item label='HTTP {{ i18n "camouflage" }}'>
@@ -14,68 +14,100 @@
 
 <!-- tcp request -->
 <a-form v-if="inbound.stream.tcp.type === 'http'" layout="inline">
-    <a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestVersion" }}'>
-        <a-input v-model.trim="inbound.stream.tcp.request.version"></a-input>
-    </a-form-item>
-    <a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestMethod" }}'>
-        <a-input v-model.trim="inbound.stream.tcp.request.method"></a-input>
-    </a-form-item>
-    <a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestPath" }}'>
-        <a-button size="small" @click="inbound.stream.tcp.request.addPath('/')">+</a-button>
-        <a-row v-for="(path, index) in inbound.stream.tcp.request.path">
-            <a-input v-model.trim="inbound.stream.tcp.request.path[index]" style="width: 200px;">
-                <a-button size="small" slot="addonAfter"
-                @click="inbound.stream.tcp.request.removePath(index)"
-                v-if="inbound.stream.tcp.request.path.length>1">-</a-button>
-            </a-input>
-        </a-row>
-    </a-form-item>
-    <br>
-    <a-form-item>
-        <a-row>
-            <span>{{ i18n "pages.inbounds.stream.general.requestHeader" }}:</span>
-            <a-button type="primary" size="small" style="margin-left: 10px" @click="inbound.stream.tcp.request.addHeader('Host', 'xxx.com')">+</a-button>
-        </a-row>
-        <a-input-group v-for="(header, index) in inbound.stream.tcp.request.headers">
-            <a-input style="width: 50%" v-model.trim="header.name"
-                     addon-before='{{ i18n "pages.inbounds.stream.general.name" }}'></a-input>
-            <a-input style="width: 50%" v-model.trim="header.value"
-                     addon-before='{{ i18n "pages.inbounds.stream.general.value" }}'>
-                <template slot="addonAfter">
-                    <a-button type="primary" size="small" style="margin-left: 10px" @click="inbound.stream.tcp.request.removeHeader(index)">-</a-button>
-                </template>
-            </a-input>
-        </a-input-group>
-    </a-form-item>
-    
-</a-form>
-
-<!-- tcp response -->
-<a-form v-if="inbound.stream.tcp.type === 'http'" layout="inline">
-    <a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseVersion" }}'>
-        <a-input v-model.trim="inbound.stream.tcp.response.version"></a-input>
-    </a-form-item>
-    <a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseStatus" }}'>
-        <a-input v-model.trim="inbound.stream.tcp.response.status"></a-input>
-    </a-form-item>
-    <a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseStatusDescription" }}'>
-        <a-input v-model.trim="inbound.stream.tcp.response.reason"></a-input>
-    </a-form-item>
-    <a-form-item>
-        <a-row>
-            <span>{{ i18n "pages.inbounds.stream.tcp.responseHeader" }}:</span>
-            <a-button type="primary" size="small" style="margin-left: 10px" @click="inbound.stream.tcp.response.addHeader('Content-Type', 'application/octet-stream')">+</a-button>
-        </a-row>
-        <a-input-group v-for="(header, index) in inbound.stream.tcp.response.headers">
-            <a-input style="width: 50%" v-model.trim="header.name"
-                     addon-before='{{ i18n "pages.inbounds.stream.general.name" }}'></a-input>
-            <a-input style="width: 50%" v-model.trim="header.value"
-                     addon-before='{{ i18n "pages.inbounds.stream.general.value" }}'>
-                <template slot="addonAfter">
-                    <a-button type="primary" size="small" style="margin-left: 10px" @click="inbound.stream.tcp.response.removeHeader(index)">-</a-button>
-                </template>
-            </a-input>
-        </a-input-group>
-    </a-form-item>
+    <table width="100%" class="ant-table-tbody">
+        <tr>
+            <td>{{ i18n "pages.inbounds.stream.tcp.requestVersion" }}</td>
+            <td>
+                <a-form-item>
+                    <a-input v-model.trim="inbound.stream.tcp.request.version" style="width: 200px;"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>{{ i18n "pages.inbounds.stream.tcp.requestMethod" }}</td>
+            <td>
+                <a-form-item>
+                    <a-input v-model.trim="inbound.stream.tcp.request.method" style="width: 200px;"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td style="vertical-align: top; padding-top: 10px;">{{ i18n "pages.inbounds.stream.tcp.requestPath" }}
+                <a-button size="small" @click="inbound.stream.tcp.request.addPath('/')">+</a-button>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-row v-for="(path, index) in inbound.stream.tcp.request.path">
+                        <a-input v-model.trim="inbound.stream.tcp.request.path[index]" style="width: 200px;">
+                            <a-button size="small" slot="addonAfter"
+                            @click="inbound.stream.tcp.request.removePath(index)"
+                            v-if="inbound.stream.tcp.request.path.length>1">-</a-button>
+                        </a-input>
+                    </a-row>
+                </a-form-item> 
+            </td>
+        </tr>
+        <tr>
+            <td colspan="2" width="100%">
+                <a-form-item>
+                    <span>{{ i18n "pages.inbounds.stream.general.requestHeader" }}:</span>
+                    <a-button size="small" style="margin-left: 10px" @click="inbound.stream.tcp.request.addHeader('', '')">+</a-button>
+                    <a-input-group compact v-for="(header, index) in inbound.stream.tcp.request.headers">
+                        <a-input style="width: 50%" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
+                            <template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
+                        </a-input>
+                        <a-input style="width: 50%" v-model.trim="header.value" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
+                            <a-button slot="addonAfter" size="small" @click="inbound.stream.tcp.request.removeHeader(index)">-</a-button>
+                        </a-input>
+                    </a-input-group>
+                </a-form-item>
+            </td>
+        </tr>
+        <!-- tcp response -->
+        <tr>
+            <td>{{ i18n "pages.inbounds.stream.tcp.responseVersion" }}</td>
+            <td>
+                <a-form-item>
+                    <a-input v-model.trim="inbound.stream.tcp.response.version" style="width: 200px;"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>{{ i18n "pages.inbounds.stream.tcp.responseStatus" }}</td>
+            <td>
+                <a-form-item>
+                    <a-input v-model.trim="inbound.stream.tcp.response.status" style="width: 200px;"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>{{ i18n "pages.inbounds.stream.tcp.responseStatusDescription" }}</td>
+            <td>
+                <a-form-item>
+                    <a-input v-model.trim="inbound.stream.tcp.response.reason" style="width: 200px;"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td colspan="2" width="100%">
+                <a-form-item>
+                    <span>{{ i18n "pages.inbounds.stream.tcp.responseHeader" }}:</span>
+                    <a-button size="small" style="margin-left: 10px"
+                        @click="inbound.stream.tcp.response.addHeader('Content-Type', 'application/octet-stream')">+</a-button>
+                    <a-input-group compact v-for="(header, index) in inbound.stream.tcp.response.headers">
+                        <a-input style="width: 50%" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
+                            <template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
+                        </a-input>
+                        <a-input style="width: 50%" v-model.trim="header.value"
+                                    placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
+                            <template slot="addonAfter">
+                                <a-button size="small" @click="inbound.stream.tcp.response.removeHeader(index)">-</a-button>
+                            </template>
+                        </a-input>
+                    </a-input-group>
+                </a-form-item>
+            </td>
+        </tr>
+    </table>
 </a-form>
 {{end}}

+ 6 - 4
web/html/xui/form/stream/stream_ws.html

@@ -11,15 +11,17 @@
     <a-form-item>
         <a-row>
             <span>{{ i18n "pages.inbounds.stream.general.requestHeader" }}:</span>
-            <a-button type="primary" size="small" style="margin-left: 10px" @click="inbound.stream.ws.addHeader('Host', '')">+</a-button>
+            <a-button type="primary" size="small" style="margin-left: 10px"
+                @click="inbound.stream.ws.addHeader('Host', '')">+</a-button>
         </a-row>
         <a-input-group v-for="(header, index) in inbound.stream.ws.headers">
             <a-input style="width: 50%" v-model.trim="header.name"
-                     addon-before='{{ i18n "pages.inbounds.stream.general.name"}}'></a-input>
+                addon-before='{{ i18n "pages.inbounds.stream.general.name"}}'></a-input>
             <a-input style="width: 50%" v-model.trim="header.value"
-                     addon-before='{{ i18n "pages.inbounds.stream.general.value" }}'>
+                addon-before='{{ i18n "pages.inbounds.stream.general.value" }}'>
                 <template slot="addonAfter">
-                    <a-button type="primary" size="small" style="margin-left: 10px" @click="inbound.stream.ws.removeHeader(index)">-</a-button>
+                    <a-button type="primary" size="small" style="margin-left: 10px"
+                        @click="inbound.stream.ws.removeHeader(index)">-</a-button>
                 </template>
             </a-input>
         </a-input-group>

+ 390 - 170
web/html/xui/form/tls_settings.html

@@ -2,187 +2,407 @@
 <!-- tls enable -->
 <a-form layout="inline" v-if="inbound.canEnableTls()">
     <a-divider style="margin:0;"></a-divider>
-    <a-form-item label='{{ i18n "security" }}'>
-        <a-radio-group v-model="inbound.stream.security" button-style="solid">
-            <a-radio-button value="none">{{ i18n "none" }}</a-radio-button>
-            <a-tooltip>
-                <template slot="title">
-                    <span>{{ i18n "pages.inbounds.xtlsDesc" }}</span>
-                </template>
-                <a-radio-button v-if="inbound.canEnableXtls()" value="xtls">XTLS</a-radio-button>
-            </a-tooltip>
-            <a-tooltip>
-                <template slot="title">
-                    <span>{{ i18n "pages.inbounds.realityDesc" }}</span>
-                </template>
-                <a-radio-button v-if="inbound.canEnableReality()" value="reality">Reality</a-radio-button>
-            </a-tooltip>
-            <a-radio-button value="tls">TLS</a-radio-button>
-        </a-radio-group>
-    </a-form-item>
+    <table width="100%" class="ant-table-tbody">
+        <tr>
+            <td>
+                <span>{{ i18n "security" }}</span>
+            </td>
+            <td>
+                <a-radio-group v-model="inbound.stream.security" button-style="solid">
+                    <a-radio-button value="none">{{ i18n "none" }}</a-radio-button>
+                    <a-tooltip>
+                        <template slot="title">
+                            <span>{{ i18n "pages.inbounds.xtlsDesc" }}</span>
+                        </template>
+                        <a-radio-button v-if="inbound.canEnableXtls()" value="xtls">XTLS</a-radio-button>
+                    </a-tooltip>
+                    <a-tooltip>
+                        <template slot="title">
+                            <span>{{ i18n "pages.inbounds.realityDesc" }}</span>
+                        </template>
+                        <a-radio-button v-if="inbound.canEnableReality()" value="reality">Reality</a-radio-button>
+                    </a-tooltip>
+                    <a-radio-button value="tls">TLS</a-radio-button>
+                </a-radio-group>
+            </td>
+        </tr>
+    </table>
 </a-form>
 
 <!-- tls settings -->
 <a-form v-if="inbound.tls" layout="inline">
-    <a-form-item label="CipherSuites">
-        <a-select v-model="inbound.stream.tls.cipherSuites" style="width: 300px" :dropdown-class-name="themeSwitcher.currentTheme">
-            <a-select-option value="">auto</a-select-option>
-            <a-select-option v-for="key,value in TLS_CIPHER_OPTION" :value="key">[[ value ]]</a-select-option>
-        </a-select>
-    </a-form-item>
-    <a-form-item label="Min/Max Version">
-        <a-input-group compact>
-            <a-select style="width: 60px" v-model="inbound.stream.tls.minVersion" :dropdown-class-name="themeSwitcher.currentTheme">
-                <a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
-            </a-select>
-            <a-select style="width: 60px" v-model="inbound.stream.tls.maxVersion" :dropdown-class-name="themeSwitcher.currentTheme">
-                <a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
-            </a-select>
-        </a-input-group>
-    </a-form-item>
-    <a-form-item label="SNI" placeholder="Server Name Indication">
-        <a-input v-model.trim="inbound.stream.tls.server" style="width: 250px"></a-input>
-    </a-form-item>
-    <a-form-item label="uTLS">
-        <a-select v-model="inbound.stream.tls.settings.fingerprint"
-                  style="width: 170px" :dropdown-class-name="themeSwitcher.currentTheme">
-            <a-select-option value=''>None</a-select-option>
-            <a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
-        </a-select>
-    </a-form-item>
-    <a-form-item label="Alpn">
-        <a-select
-            mode="multiple"
-            style="width: 250px"
-            :dropdown-class-name="themeSwitcher.currentTheme"
-            v-model="inbound.stream.tls.alpn">
-            <a-select-option v-for="alpn in ALPN_OPTION" :value="alpn">[[ alpn ]]</a-select-option>
-        </a-select>
-    </a-form-item>
-    <br>
-    <a-form-item label="Allow insecure">
-        <a-switch v-model="inbound.stream.tls.settings.allowInsecure"></a-switch>
-    </a-form-item>
-    <br>
-    <a-form-item label="Reject Unknown SNI">
-        <a-switch v-model="inbound.stream.tls.rejectUnknownSni"></a-switch>
-    </a-form-item>
-    <template v-for="cert,index in inbound.stream.tls.certs">
-        <a-form-item label='{{ i18n "certificate" }}'>
-            <a-radio-group v-model="cert.useFile" button-style="solid">
-                <a-radio-button :value="true">{{ i18n "pages.inbounds.certificatePath" }}</a-radio-button>
-                <a-radio-button :value="false">{{ i18n "pages.inbounds.certificateContent" }}</a-radio-button>
-            </a-radio-group>
-            <a-button v-if="index === 0" type="primary" size="small" @click="inbound.stream.tls.addCert()" style="margin-left: 10px">+</a-button>
-            <a-button v-if="inbound.stream.tls.certs.length>1" type="primary" size="small" @click="inbound.stream.tls.removeCert(index)" style="margin-left: 10px">-</a-button>
-        </a-form-item>
-        <template v-if="cert.useFile">
-            <a-form-item label='{{ i18n "pages.inbounds.publicKeyPath" }}'>
-                <a-input v-model.trim="cert.certFile" style="width:300px;"></a-input>
-            </a-form-item>
-            <a-form-item label='{{ i18n "pages.inbounds.keyPath" }}'>
-                <a-input v-model.trim="cert.keyFile" style="width:300px;"></a-input>
-            </a-form-item>
-            <a-button type="primary" icon="import" @click="setDefaultCertData(index)">{{ i18n "pages.inbounds.setDefaultCert" }}</a-button>
-        </template>
-        <template v-else>
-            <a-form-item label='{{ i18n "pages.inbounds.publicKeyContent" }}'>
-                <a-input type="textarea" :rows="3" style="width:300px;" v-model="cert.cert"></a-input>
-            </a-form-item>
-            <a-form-item label='{{ i18n "pages.inbounds.keyContent" }}'>
-                <a-input type="textarea" :rows="3" style="width:300px;" v-model="cert.key"></a-input>
-            </a-form-item>
-        </template>
-        <br>
-        <a-form-item label="ocspStapling">
-            <a-input-number v-model.number="cert.ocspStapling" :min="0"></a-input-number>
-        </a-form-item>
-    </template>
+    <table width="100%" class="ant-table-tbody">
+        <tr>
+            <td>
+                <span>CipherSuites</span>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-select v-model="inbound.stream.tls.cipherSuites" style="width: 300px"
+                        :dropdown-class-name="themeSwitcher.currentTheme">
+                        <a-select-option value="">auto</a-select-option>
+                        <a-select-option v-for="key,value in TLS_CIPHER_OPTION" :value="key">[[ value
+                            ]]</a-select-option>
+                    </a-select>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>
+                <span>Min/Max Version</span>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-input-group compact>
+                        <a-select style="width: 60px" v-model="inbound.stream.tls.minVersion"
+                            :dropdown-class-name="themeSwitcher.currentTheme">
+                            <a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
+                        </a-select>
+                        <a-select style="width: 60px" v-model="inbound.stream.tls.maxVersion"
+                            :dropdown-class-name="themeSwitcher.currentTheme">
+                            <a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
+                        </a-select>
+                    </a-input-group>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>
+                <span>SNI</span>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-input v-model.trim="inbound.stream.tls.server" style="width: 250px"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>
+                <span>uTLS</span>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-select v-model="inbound.stream.tls.settings.fingerprint" style="width: 170px"
+                        :dropdown-class-name="themeSwitcher.currentTheme">
+                        <a-select-option value=''>None</a-select-option>
+                        <a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
+                    </a-select>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>
+                <span>Alpn</span>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-select mode="multiple" style="width: 250px" :dropdown-class-name="themeSwitcher.currentTheme"
+                        v-model="inbound.stream.tls.alpn">
+                        <a-select-option v-for="alpn in ALPN_OPTION" :value="alpn">[[ alpn ]]</a-select-option>
+                    </a-select>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>
+                <span>Allow insecure</span>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-switch v-model="inbound.stream.tls.settings.allowInsecure"></a-switch>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>
+                <span>Reject Unknown SNI</span>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-switch v-model="inbound.stream.tls.rejectUnknownSni"></a-switch>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr v-for="cert,index in inbound.stream.tls.certs">
+            <td>
+                <span>{{ i18n "certificate" }}</span>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-radio-group v-model="cert.useFile" button-style="solid">
+                        <a-radio-button :value="true">{{ i18n "pages.inbounds.certificatePath" }}</a-radio-button>
+                        <a-radio-button :value="false">{{ i18n "pages.inbounds.certificateContent" }}</a-radio-button>
+                    </a-radio-group>
+                    <a-button v-if="index === 0" type="primary" size="small" @click="inbound.stream.tls.addCert()"
+                        style="margin-left: 10px">+</a-button>
+                    <a-button v-if="inbound.stream.tls.certs.length>1" type="primary" size="small"
+                        @click="inbound.stream.tls.removeCert(index)" style="margin-left: 10px">-</a-button>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr v-for="cert,index in inbound.stream.tls.certs" v-if="cert.useFile">
+            <td>
+                <span>{{ i18n "pages.inbounds.publicKeyPath" }}</span>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-input v-model.trim="cert.certFile" style="width:300px;"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr v-for="cert,index in inbound.stream.tls.certs" v-if="cert.useFile">
+            <td>
+                <span>{{ i18n "pages.inbounds.keyPath" }}</span>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-input v-model.trim="cert.keyFile" style="width:300px;"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr v-for="cert,index in inbound.stream.tls.certs" v-if="cert.useFile">
+            <td>
+                <a-button type="primary" icon="import" @click="setDefaultCertData(index)">{{ i18n
+                    "pages.inbounds.setDefaultCert" }}</a-button>
+            </td>
+        </tr>
+        <tr v-for="cert,index in inbound.stream.tls.certs" v-else>
+            <td>
+                <span>{{ i18n "pages.inbounds.publicKeyContent" }}</span>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-input type="textarea" :rows="3" style="width:300px;" v-model="cert.cert"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr v-for="cert,index in inbound.stream.tls.certs" v-else>
+            <td>
+                <span>{{ i18n "pages.inbounds.keyContent" }}</span>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-input type="textarea" :rows="3" style="width:300px;" v-model="cert.key"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr v-for="cert,index in inbound.stream.tls.certs">
+            <td>
+                <span>ocspStapling</span>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-input-number v-model.number="cert.ocspStapling" :min="0"></a-input-number>
+                </a-form-item>
+            </td>
+        </tr>
+    </table>
 </a-form>
 
 <!-- xtls settings -->
 <a-form v-else-if="inbound.xtls" layout="inline">
-    <a-form-item label="SNI" placeholder="Server Name Indication">
-        <a-input v-model.trim="inbound.stream.xtls.server" style="width: 250px"></a-input>
-    </a-form-item>
-    <a-form-item label="Alpn">
-        <a-select
-            mode="multiple"
-            style="width: 250px"
-            :dropdown-class-name="themeSwitcher.currentTheme"
-            v-model="inbound.stream.xtls.alpn">
-            <a-select-option v-for="alpn in ALPN_OPTION" :value="alpn">[[ alpn ]]</a-select-option>
-        </a-select>
-    </a-form-item>
-    <a-form-item label="Allow insecure">
-        <a-switch v-model="inbound.stream.xtls.settings.allowInsecure"></a-switch>
-    </a-form-item>
-    <template v-for="cert,index in inbound.stream.xtls.certs">
-        <a-form-item label='{{ i18n "certificate" }}'>
-            <a-radio-group v-model="cert.useFile" button-style="solid">
-                <a-radio-button :value="true">{{ i18n "pages.inbounds.certificatePath" }}</a-radio-button>
-                <a-radio-button :value="false">{{ i18n "pages.inbounds.certificateContent" }}</a-radio-button>
-            </a-radio-group>
-            <a-button v-if="index === 0" type="primary" size="small" @click="inbound.stream.xtls.addCert()" style="margin-left: 10px">+</a-button>
-            <a-button v-if="inbound.stream.xtls.certs.length>1" type="primary" size="small" @click="inbound.stream.xtls.removeCert(index)" style="margin-left: 10px">-</a-button> 
-        </a-form-item>
-        <template v-if="cert.useFile">
-            <a-form-item label='{{ i18n "pages.inbounds.publicKeyPath" }}'>
-                <a-input v-model.trim="cert.certFile" style="width:300px;"></a-input>
-            </a-form-item>
-            <a-form-item label='{{ i18n "pages.inbounds.keyPath" }}'>
-                <a-input v-model.trim="cert.keyFile" style="width:300px;"></a-input>
-            </a-form-item>
-            <a-button type="primary" icon="import" @click="setDefaultCertXtls(index)">{{ i18n "pages.inbounds.setDefaultCert" }}</a-button>
-        </template>
-        <template v-else>
-            <a-form-item label='{{ i18n "pages.inbounds.publicKeyContent" }}'>
-                <a-input type="textarea" :rows="3" style="width:300px;" v-model="cert.cert"></a-input>
-            </a-form-item>
-            <a-form-item label='{{ i18n "pages.inbounds.keyContent" }}'>
-                <a-input type="textarea" :rows="3" style="width:300px;" v-model="cert.key"></a-input>
-            </a-form-item>
-        </template>
-    </template>
+    <table width="100%" class="ant-table-tbody">
+        <tr>
+            <td>
+                <span>SNI</span>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-input v-model.trim="inbound.stream.xtls.server" style="width: 250px"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>
+                <span>Alpn</span>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-select mode="multiple" style="width: 250px" :dropdown-class-name="themeSwitcher.currentTheme"
+                        v-model="inbound.stream.xtls.alpn">
+                        <a-select-option v-for="alpn in ALPN_OPTION" :value="alpn">[[ alpn ]]</a-select-option>
+                    </a-select>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>
+                <span>Allow insecure</span>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-switch v-model="inbound.stream.xtls.settings.allowInsecure"></a-switch>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr v-for="cert,index in inbound.stream.xtls.certs">
+            <td>
+                <span>{{ i18n "certificate" }}</span>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-radio-group v-model="cert.useFile" button-style="solid">
+                        <a-radio-button :value="true">{{ i18n "pages.inbounds.certificatePath" }}</a-radio-button>
+                        <a-radio-button :value="false">{{ i18n "pages.inbounds.certificateContent" }}</a-radio-button>
+                    </a-radio-group>
+                    <a-button v-if="index === 0" type="primary" size="small" @click="inbound.stream.xtls.addCert()"
+                        style="margin-left: 10px">+</a-button>
+                    <a-button v-if="inbound.stream.xtls.certs.length>1" type="primary" size="small"
+                        @click="inbound.stream.xtls.removeCert(index)" style="margin-left: 10px">-</a-button>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr v-for="cert,index in inbound.stream.xtls.certs" v-if="cert.useFile">
+            <td>
+                <span>{{ i18n "pages.inbounds.publicKeyPath" }}</span>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-input v-model.trim="cert.certFile" style="width:300px;"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr v-for="cert,index in inbound.stream.xtls.certs" v-if="cert.useFile">
+            <td>
+                <span>{{ i18n "pages.inbounds.keyPath" }}</span>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-input v-model.trim="cert.keyFile" style="width:300px;"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr v-for="cert,index in inbound.stream.xtls.certs" v-if="cert.useFile">
+            <td>
+                <a-button type="primary" icon="import" @click="setDefaultCertXtls(index)">{{ i18n
+                    "pages.inbounds.setDefaultCert" }}</a-button>
+            </td>
+        </tr>
+        <tr v-for="cert,index in inbound.stream.xtls.certs" v-else>
+            <td>
+                <span>{{ i18n "pages.inbounds.publicKeyContent" }}</span>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-input type="textarea" :rows="3" style="width:300px;" v-model="cert.cert"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr v-for="cert,index in inbound.stream.xtls.certs" v-else>
+            <td>
+                <span>{{ i18n "pages.inbounds.keyContent" }}</span>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-input type="textarea" :rows="3" style="width:300px;" v-model="cert.key"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+    </table>
 </a-form>
 
 <!-- reality settings -->
 <a-form v-else-if="inbound.reality" layout="inline">
-    <a-form-item label="Show">
-        <a-switch v-model="inbound.stream.reality.show">
-        </a-switch>
-    </a-form-item>
-    <a-form-item label="xVer">
-        <a-input-number v-model="inbound.stream.reality.xver" :min="0" style="width: 60px"></a-input-number>
-    </a-form-item>
-    <a-form-item label="uTLS">
-        <a-select v-model="inbound.stream.reality.settings.fingerprint" 
-                    style="width: 135px" :dropdown-class-name="themeSwitcher.currentTheme">
-            <a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
-        </a-select>
-    </a-form-item>
-	<a-form-item label="Dest">
-        <a-input v-model.trim="inbound.stream.reality.dest" style="width: 300px"></a-input>
-    </a-form-item>
-    <a-form-item label="Server Names">
-        <a-input v-model.trim="inbound.stream.reality.serverNames" style="width: 300px"></a-input>
-    </a-form-item>
-    <a-form-item label="ShortIds">
-        <a-icon @click="inbound.stream.reality.shortIds = RandomUtil.randomShortId()" type="sync"> </a-icon>
-        <a-input v-model.trim="inbound.stream.reality.shortIds" style="width: 150px;"></a-input>
-    </a-form-item>
-    <br>
-    <a-form-item label="SpiderX">
-        <a-input v-model.trim="inbound.stream.reality.settings.spiderX" style="width: 150px;"></a-input>
-    </a-form-item>
-    <a-form-item label="Private Key">
-        <a-input v-model.trim="inbound.stream.reality.privateKey" style="width: 300px"></a-input>
-    </a-form-item>
-    <a-form-item label="Public Key">
-        <a-input v-model.trim="inbound.stream.reality.settings.publicKey" style="width: 300px"></a-input>
-    </a-form-item>
-    <a-form-item>
-        <a-button type="primary" icon="import" @click="getNewX25519Cert">Get New Key</a-button>
-    </a-form-item>
+    <table width="100%" class="ant-table-tbody">
+        <tr>
+            <td>
+                <span>Show</span>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-switch v-model="inbound.stream.reality.show"></a-switch>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>
+                <span>xVer</span>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-input-number v-model="inbound.stream.reality.xver" :min="0" style="width: 60px"></a-input-number>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>
+                <span>uTLS</span>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-select v-model="inbound.stream.reality.settings.fingerprint" style="width: 135px"
+                        :dropdown-class-name="themeSwitcher.currentTheme">
+                        <a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
+                    </a-select>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>
+                <span>Dest</span>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-input v-model.trim="inbound.stream.reality.dest" style="width: 300px"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>
+                <span>Server Names</span>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-input v-model.trim="inbound.stream.reality.serverNames" style="width: 300px"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>
+                <span>ShortIds</span> <a-icon @click="inbound.stream.reality.shortIds = RandomUtil.randomShortId()" type="sync"> </a-icon>
+            </td>
+            <td>
+                <a-form-item>
+                    
+                    <a-input v-model.trim="inbound.stream.reality.shortIds" style="width: 150px;"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>
+                <span>SpiderX</span>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-input v-model.trim="inbound.stream.reality.settings.spiderX" style="width: 150px;"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>
+                <span>Private Key</span>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-input v-model.trim="inbound.stream.reality.privateKey" style="width: 300px"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td>
+                <span>Public Key</span>
+            </td>
+            <td>
+                <a-form-item>
+                    <a-input v-model.trim="inbound.stream.reality.settings.publicKey" style="width: 300px"></a-input>
+                </a-form-item>
+            </td>
+        </tr>
+        <tr>
+            <td colspan="2">
+                <a-button type="primary" icon="import" @click="getNewX25519Cert">Get New Key</a-button>
+            </td>
+        </tr>
+    </table>
 </a-form>
 {{end}}