diff options
| author | MHSanaei <mc.sanaei@gmail.com> | 2023-02-09 22:18:06 +0300 |
|---|---|---|
| committer | MHSanaei <mc.sanaei@gmail.com> | 2023-02-09 22:18:06 +0300 |
| commit | b73e4173a3c1e69e02ad6b4e3b43e425e57a5be9 (patch) | |
| tree | d95d2f5e903d97082e11eb9f9023c165b1bde388 /web/html | |
3x-ui
Diffstat (limited to 'web/html')
32 files changed, 2766 insertions, 0 deletions
diff --git a/web/html/common/head.html b/web/html/common/head.html new file mode 100644 index 00000000..f34ce62f --- /dev/null +++ b/web/html/common/head.html @@ -0,0 +1,17 @@ +{{define "head"}} +<head> + <meta charset="UTF-8"> + <meta name="renderer" content="webkit"> + <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="stylesheet" href="{{ .base_path }}assets/ant-design-vue@1.7.2/antd.min.css"> + <link rel="stylesheet" href="{{ .base_path }}assets/element-ui@2.15.0/theme-chalk/display.css"> + <link rel="stylesheet" href="{{ .base_path }}assets/css/custom.css?{{ .cur_ver }}"> + <style> + [v-cloak] { + display: none; + } + </style> + <title>{{ i18n .title}}</title> +</head> +{{end}}
\ No newline at end of file diff --git a/web/html/common/js.html b/web/html/common/js.html new file mode 100644 index 00000000..d400196b --- /dev/null +++ b/web/html/common/js.html @@ -0,0 +1,22 @@ +{{define "js"}} +<script src="{{ .base_path }}assets/vue@2.6.12/vue.min.js"></script> +<script src="{{ .base_path }}assets/moment/moment.min.js"></script> +<script src="{{ .base_path }}assets/ant-design-vue@1.7.2/antd.min.js"></script> +<script src="{{ .base_path }}assets/base64/base64.min.js"></script> +<script src="{{ .base_path }}assets/axios/axios.min.js"></script> +<script src="{{ .base_path }}assets/qs/qs.min.js"></script> +<script src="{{ .base_path }}assets/qrcode/qrious.min.js"></script> +<script src="{{ .base_path }}assets/clipboard/clipboard.min.js"></script> +<script src="{{ .base_path }}assets/uri/URI.min.js"></script> +<script src="{{ .base_path }}assets/js/axios-init.js?{{ .cur_ver }}"></script> +<script src="{{ .base_path }}assets/js/util/common.js?{{ .cur_ver }}"></script> +<script src="{{ .base_path }}assets/js/util/date-util.js?{{ .cur_ver }}"></script> +<script src="{{ .base_path }}assets/js/util/utils.js?{{ .cur_ver }}"></script> +<script src="{{ .base_path }}assets/js/model/xray.js?{{ .cur_ver }}"></script> +<script src="{{ .base_path }}assets/js/model/models.js?{{ .cur_ver }}"></script> +<script src="{{ .base_path }}assets/js/langs.js"></script> +<script> + const basePath = '{{ .base_path }}'; + axios.defaults.baseURL = basePath; +</script> +{{end}}
\ No newline at end of file diff --git a/web/html/common/prompt_modal.html b/web/html/common/prompt_modal.html new file mode 100644 index 00000000..91c26615 --- /dev/null +++ b/web/html/common/prompt_modal.html @@ -0,0 +1,67 @@ +{{define "promptModal"}} +<a-modal id="prompt-modal" v-model="promptModal.visible" :title="promptModal.title" + :closable="true" @ok="promptModal.ok" :mask-closable="false" + :ok-text="promptModal.okText" cancel-text='{{ i18n "cancel" }}'> + <a-input id="prompt-modal-input" :type="promptModal.type" + v-model="promptModal.value" + :autosize="{minRows: 10, maxRows: 20}" + @keydown.enter.native="promptModal.keyEnter" + @keydown.ctrl.83="promptModal.ctrlS"></a-input> +</a-modal> + +<script> + + const promptModal = { + title: '', + type: '', + value: '', + okText: '{{ i18n "sure"}}', + visible: false, + keyEnter(e) { + if (this.type !== 'textarea') { + e.preventDefault(); + this.ok(); + } + }, + ctrlS(e) { + if (this.type === 'textarea') { + e.preventDefault(); + promptModal.confirm(promptModal.value); + } + }, + ok() { + promptModal.close(); + promptModal.confirm(promptModal.value); + }, + confirm() {}, + open({ + title='', + type='text', + value='', + okText='{{ i18n "sure"}}', + confirm=() => {}, + }) { + this.title = title; + this.type = type; + this.value = value; + this.okText = okText; + this.confirm = confirm; + this.visible = true; + promptModalApp.$nextTick(() => { + document.querySelector('#prompt-modal-input').focus(); + }); + }, + close() { + this.visible = false; + } + }; + + const promptModalApp = new Vue({ + el: '#prompt-modal', + data: { + promptModal: promptModal, + }, + }); + +</script> +{{end}}
\ No newline at end of file diff --git a/web/html/common/qrcode_modal.html b/web/html/common/qrcode_modal.html new file mode 100644 index 00000000..c80f8a0e --- /dev/null +++ b/web/html/common/qrcode_modal.html @@ -0,0 +1,120 @@ +{{define "qrcodeModal"}} +<a-modal id="qrcode-modal" v-model="qrModal.visible" :title="qrModal.title" + :closable="true" width="300px" :ok-text="qrModal.okText" + cancel-text='{{ i18n "close" }}' :ok-button-props="{attrs:{id:'qr-modal-ok-btn'}}"> + <a-tag color="green" style="margin-bottom: 10px;display: block;text-align: center;" >click on QR Code to Copy</a-tag> + <canvas v-if="qrModal.inbound.protocol != Protocols.VMESS && qrModal.inbound.protocol != Protocols.VLESS" id="qrCode" style="width: 100%; height: 100%;"></canvas> + + <template v-if="qrModal.inbound.protocol === Protocols.VMESS" v-for="(vmess, index) in qrModal.inbound.settings.vmesses"> + <a-tag color="red" style="margin-bottom: 10px;display: block;text-align: center;" v-text="vmess.email"></a-tag> + <canvas @click="copyTextToClipboard(`qrCode-vmess-${vmess.id}`,index)" :id="`qrCode-vmess-${vmess.id}`" style="width: 100%; height: 100%;"></canvas> + <a-divider style="height: 2px; background-color: #7e7e7e" /> + </template> + + <template v-if="qrModal.inbound.protocol === Protocols.VLESS" v-for="(vless, index) in qrModal.inbound.settings.vlesses"> + <a-tag color="red" style="margin-bottom: 10px;display: block;text-align: center;" v-text="vless.email"></a-tag> + <canvas @click="copyTextToClipboard(`qrCode-vless-${vless.id}`,index)" :id="`qrCode-vless-${vless.id}`" style="width: 100%; height: 100%;"></canvas> + <a-divider style="height: 2px; background-color: #7e7e7e" /> + </template> +</a-modal> + +<script> + + const qrModal = { + title: '', + content: '', + inbound: new Inbound(), + dbInbound: new DBInbound(), + okText: '', + copyText: '', + qrcode: null, + clipboard: null, + visible: false, + show: function (title='', content='', dbInbound=new DBInbound(),okText='{{ i18n "copy" }}', copyText='') { + this.title = title; + this.content = content; + this.dbInbound = dbInbound; + this.inbound = dbInbound.toInbound(); + this.okText = okText; + if (ObjectUtil.isEmpty(copyText)) { + this.copyText = content; + } else { + this.copyText = copyText; + } + this.visible = true; + qrModalApp.$nextTick(() => { + if (this.clipboard === null) { + this.clipboard = new ClipboardJS('#qr-modal-ok-btn', { + text: () => this.copyText, + }); + this.clipboard.on('success', () => app.$message.success('{{ i18n "copied" }}')); + } + if (this.qrcode === null) { + this.qrcode = new QRious({ + element: document.querySelector('#qrCode'), + size: 260, + value: content, + }); + } else { + this.qrcode.value = content; + } + }); + }, + close: function () { + this.visible = false; + }, + }; + + const qrModalApp = new Vue({ + el: '#qrcode-modal', + data: { + qrModal: qrModal, + }, + methods: { + setQrCode(elmentId,index) { + content = qrModal.inbound.genLink(qrModal.dbInbound.address,qrModal.dbInbound.remark,index) + + new QRious({ + element: document.querySelector('#'+elmentId), + size: 260, + value: content, + }); + }, + copyTextToClipboard(elmentId,index) { + link = qrModal.inbound.genLink(qrModal.dbInbound.address,qrModal.dbInbound.remark,index) + this.qrModal.copyText = link + + this.qrModal.clipboard = new ClipboardJS('#' + elmentId, { + text: () => link, + }); + this.qrModal.clipboard.on('success', () => { + app.$message.success('{{ i18n "copied" }}') + this.qrModal.clipboard.destroy(); + }); + + + } + }, + updated() { + switch (qrModal.inbound.protocol) { + case Protocols.VMESS: + vmesses = qrModal.inbound.settings.vmesses + for (const index in vmesses) { + this.setQrCode("qrCode-vmess-" + vmesses[index].id ,index) + } + break; + case Protocols.VLESS: + vlesses = qrModal.inbound.settings.vlesses + + for (const index in vlesses) { + this.setQrCode("qrCode-vless-" + vlesses[index].id ,index) + } + break; + default: return null; + } + + } + }); + +</script> +{{end}}
\ No newline at end of file diff --git a/web/html/common/text_modal.html b/web/html/common/text_modal.html new file mode 100644 index 00000000..0ae04a88 --- /dev/null +++ b/web/html/common/text_modal.html @@ -0,0 +1,58 @@ +{{define "textModal"}} +<a-modal id="text-modal" v-model="txtModal.visible" :title="txtModal.title" + :closable="true" ok-text='{{ i18n "copy" }}' cancel-text='{{ i18n "close" }}' + :ok-button-props="{attrs:{id:'txt-modal-ok-btn'}}"> + <a-button v-if="!ObjectUtil.isEmpty(txtModal.fileName)" type="primary" style="margin-bottom: 10px;" + @click="downloader.download(txtModal.fileName, txtModal.content)"> + {{ i18n "download" }} [[ txtModal.fileName ]] + </a-button> + <a-input type="textarea" v-model="txtModal.content" + :autosize="{ minRows: 10, maxRows: 20}"></a-input> +</a-modal> + +<script> + + const txtModal = { + title: '', + content: '', + fileName: '', + qrcode: null, + clipboard: null, + visible: false, + show: function (title='', content='', fileName='') { + this.title = title; + this.content = content; + this.fileName = fileName; + this.visible = true; + textModalApp.$nextTick(() => { + if (this.clipboard === null) { + this.clipboard = new ClipboardJS('#txt-modal-ok-btn', { + text: () => this.content, + }); + this.clipboard.on('success', () => app.$message.success('{{ i18n "copied" }}')); + } + if (this.qrcode === null) { + this.qrcode = new QRious({ + element: document.querySelector('#qrCode'), + size: 260, + value: content, + }); + } else { + this.qrcode.value = content; + } + }); + }, + close: function () { + this.visible = false; + }, + }; + + const textModalApp = new Vue({ + el: '#text-modal', + data: { + txtModal: txtModal, + }, + }); + +</script> +{{end}}
\ No newline at end of file diff --git a/web/html/login.html b/web/html/login.html new file mode 100644 index 00000000..5138f15e --- /dev/null +++ b/web/html/login.html @@ -0,0 +1,123 @@ +<!DOCTYPE html> +<html lang="en"> +{{template "head" .}} +<style> + + #app { + padding-top: 100px; + } + + h1 { + text-align: center; + color: #fff; + margin: 20px 0 50px 0; + } + + .ant-btn, .ant-input { + height: 50px; + border-radius: 30px; + } + + .ant-input-affix-wrapper .ant-input-prefix { + left: 23px; + } + + .ant-input-affix-wrapper .ant-input:not(:first-child) { + padding-left: 50px; + } + + .selectLang{ + display: flex; + text-align: center; + justify-content: center; + } + +</style> +<body> +<a-layout id="app" v-cloak> + <transition name="list" appear> + <a-layout-content> + <a-row type="flex" justify="center"> + <a-col :xs="22" :sm="20" :md="16" :lg="12" :xl="8"> + <h1>{{ i18n "pages.login.title" }}</h1> + </a-col> + </a-row> + <a-row type="flex" justify="center"> + <a-col :xs="22" :sm="20" :md="16" :lg="12" :xl="8"> + <a-form> + <a-form-item> + <a-input v-model.trim="user.username" placeholder='{{ i18n "username" }}' + @keydown.enter.native="login" autofocus> + <a-icon slot="prefix" type="user" style="color: rgba(0,0,0,.25)"/> + </a-input> + </a-form-item> + <a-form-item> + <a-input type="password" v-model.trim="user.password" + placeholder='{{ i18n "password" }}' @keydown.enter.native="login"> + <a-icon slot="prefix" type="lock" style="color: rgba(0,0,0,.25)"/> + </a-input> + </a-form-item> + <a-form-item> + <a-button block @click="login" :loading="loading">{{ i18n "login" }}</a-button> + </a-form-item> + <a-form-item> + + <a-row justify="center" class="selectLang"> + <a-col :span="4"><span>Language : </span></a-col> + + <a-col :span="6"> + <a-select + ref="selectLang" + v-model="lang" + @change="setLang(lang)" + > + <a-select-option :value="l.value" label="China" v-for="l in supportLangs" > + <span role="img" aria-label="l.name" v-text="l.icon"></span> + <span v-text="l.name"></span> + </a-select-option> + </a-select> + </a-col> + + </a-row> + + + + </a-form-item> + </a-form> + </a-col> + </a-row> + </a-layout-content> + </transition> +</a-layout> +{{template "js" .}} +<script> + const leftColor = RandomUtil.randomIntRange(0x222222, 0xFFFFFF / 2).toString(16); + const rightColor = RandomUtil.randomIntRange(0xFFFFFF / 2, 0xDDDDDD).toString(16); + const deg = RandomUtil.randomIntRange(0, 360); + const background = `linear-gradient(${deg}deg, #${leftColor} 10%, #${rightColor} 100%)`; + document.querySelector('#app').style.background = background; + const app = new Vue({ + delimiters: ['[[', ']]'], + el: '#app', + data: { + loading: false, + user: new User(), + lang : "" + }, + created(){ + this.lang = getLang(); + }, + methods: { + async login() { + this.loading = true; + const msg = await HttpUtil.post('/login', this.user); + this.loading = false; + if (msg.success) { + location.href = basePath + 'xui/'; + } + } + } + }); +</script> +</body> +</html>
\ No newline at end of file diff --git a/web/html/xui/common_sider.html b/web/html/xui/common_sider.html new file mode 100644 index 00000000..f4499eeb --- /dev/null +++ b/web/html/xui/common_sider.html @@ -0,0 +1,73 @@ +{{define "menuItems"}} +<a-menu-item key="{{ .base_path }}xui/"> + <a-icon type="dashboard"></a-icon> + <span>{{ i18n "menu.dashboard"}}</span> +</a-menu-item> +<a-menu-item key="{{ .base_path }}xui/inbounds"> + <a-icon type="user"></a-icon> + <span>{{ i18n "menu.inbounds"}}</span> +</a-menu-item> +<a-menu-item key="{{ .base_path }}xui/setting"> + <a-icon type="setting"></a-icon> + <span>{{ i18n "menu.setting"}}</span> +</a-menu-item> +<!--<a-menu-item key="{{ .base_path }}xui/clients">--> +<!-- <a-icon type="laptop"></a-icon>--> +<!-- <span>client</span>--> +<!--</a-menu-item>--> +<a-sub-menu> + <template slot="title"> + <a-icon type="link"></a-icon> + <span>others</span> + </template> + <a-menu-item key="https://github.com/mhsanaei/3x-ui/"> + <a-icon type="github"></a-icon> + <span>Github</span> + </a-menu-item> + <a-menu-item key="https://t.me/xxxuiforever"> + <a-icon type="usergroup-add"></a-icon> + <span>Telegram Group</span> + </a-menu-item> +</a-sub-menu> +<a-menu-item key="{{ .base_path }}logout"> + <a-icon type="logout"></a-icon> + <span>{{ i18n "menu.logout"}}</span> +</a-menu-item> +{{end}} + + +{{define "commonSider"}} +<a-layout-sider id="sider" collapsible breakpoint="md" collapsed-width="0"> + <a-menu theme="dark" mode="inline" :selected-keys="['{{ .request_uri }}']" + @click="({key}) => key.startsWith('http') ? window.open(key) : location.href = key"> + {{template "menuItems" .}} + </a-menu> +</a-layout-sider> +<a-drawer id="sider-drawer" placement="left" :closable="false" + @close="siderDrawer.close()" + :visible="siderDrawer.visible" :wrap-style="{ padding: 0 }"> + <div class="drawer-handle" @click="siderDrawer.change()" slot="handle"> + <a-icon :type="siderDrawer.visible ? 'close' : 'menu-fold'"></a-icon> + </div> + <a-menu theme="light" mode="inline" :selected-keys="['{{ .request_uri }}']" + @click="({key}) => key.startsWith('http') ? window.open(key) : location.href = key"> + {{template "menuItems" .}} + </a-menu> +</a-drawer> +<script> + + const siderDrawer = { + visible: false, + show() { + this.visible = true; + }, + close() { + this.visible = false; + }, + change() { + this.visible = !this.visible; + } + }; + +</script> +{{end}} diff --git a/web/html/xui/component/inbound_info.html b/web/html/xui/component/inbound_info.html new file mode 100644 index 00000000..cbf156d1 --- /dev/null +++ b/web/html/xui/component/inbound_info.html @@ -0,0 +1,94 @@ +{{define "inboundInfoStream"}} +<p>{{ i18n "transmission" }}: <a-tag color="green">[[ inbound.network ]]</a-tag></p> + +<template v-if="inbound.isTcp || inbound.isWs || inbound.isH2"> + <p v-if="inbound.host">host: <a-tag color="green">[[ inbound.host ]]</a-tag></p> + <p v-else>{{ i18n "host" }}: <a-tag color="orange">{{ i18n "none" }}</a-tag></p> + + <p v-if="inbound.path">path: <a-tag color="green">[[ inbound.path ]]</a-tag></p> + <p v-else>{{ i18n "path" }}: <a-tag color="orange">{{ i18n "none" }}</a-tag></p> +</template> + +<template v-if="inbound.isQuic"> + <p>quic {{ i18n "encryption" }}: <a-tag color="green">[[ inbound.quicSecurity ]]</a-tag></p> + <p>quic {{ i18n "password" }}: <a-tag color="green">[[ inbound.quicKey ]]</a-tag></p> + <p>quic {{ i18n "camouflage" }}: <a-tag color="green">[[ inbound.quicType ]]</a-tag></p> +</template> + +<template v-if="inbound.isKcp"> + <p>kcp {{ i18n "encryption" }}: <a-tag color="green">[[ inbound.kcpType ]]</a-tag></p> + <p>kcp {{ i18n "password" }}: <a-tag color="green">[[ inbound.kcpSeed ]]</a-tag></p> +</template> + +<template v-if="inbound.isGrpc"> + <p>grpc serviceName: <a-tag color="green">[[ inbound.serviceName ]]</a-tag></p> +</template> + +<template v-if="inbound.tls || inbound.xtls"> + <p v-if="inbound.tls">tls: <a-tag color="green">{{ i18n "turnOn" }}</a-tag></p> + <p v-if="inbound.xtls">xtls: <a-tag color="green">{{ i18n "turnOn" }}</a-tag></p> +</template> +<template v-else> + <p>tls: <a-tag color="red">{{ i18n "closure" }}</a-tag></p> +</template> +<p v-if="inbound.tls"> + tls {{ i18n "domainName" }}: <a-tag :color="inbound.serverName ? 'green' : 'orange'">[[ inbound.serverName ? inbound.serverName : '' ]]</a-tag> +</p> +<p v-if="inbound.xtls"> + xtls {{ i18n "domainName" }}: <a-tag :color="inbound.serverName ? 'green' : 'orange'">[[ inbound.serverName ? inbound.serverName : '' ]]</a-tag> +</p> +{{end}} + + +{{define "component/inboundInfoComponent"}} +<div> + <p>{{ i18n "protocol"}}: <a-tag color="green">[[ dbInbound.protocol ]]</a-tag></p> + <p>{{ i18n "pages.inbounds.address"}}: <a-tag color="blue">[[ dbInbound.address ]]</a-tag></p> + <p>{{ i18n "pages.inbounds.port"}}: <a-tag color="green">[[ dbInbound.port ]]</a-tag></p> + + <template v-if="dbInbound.isVMess" v-for="(vmess, index) in inbound.settings.vmesses"> + <p>uuid: <a-tag color="green">[[ vmess.id ]]</a-tag></p> + <p>alterId: <a-tag color="green">[[ vmess.alterId ]]</a-tag></p> + <a-divider style="height: 2px; background-color: #7e7e7e" /> + </template> + + <template v-if="dbInbound.isVLess" v-for="(vless, index) in inbound.settings.vlesses"> + <p>uuid: <a-tag color="green">[[ vless.id ]]</a-tag></p> + <p v-if="inbound.isXTls">flow: <a-tag color="green">[[ vless.flow ]]</a-tag></p> + <a-divider style="height: 2px; background-color: #7e7e7e" /> + </template> + + <template v-if="dbInbound.isTrojan"> + <p>{{ i18n "password"}}: <a-tag color="green">[[ inbound.password ]]</a-tag></p> + </template> + + <template v-if="dbInbound.isSS"> + <p>{{ i18n "encryption"}}: <a-tag color="green">[[ inbound.method ]]</a-tag></p> + <p>{{ i18n "password"}}: <a-tag color="green">[[ inbound.password ]]</a-tag></p> + </template> + + <template v-if="dbInbound.isSocks"> + <p>{{ i18n "username"}}: <a-tag color="green">[[ inbound.username ]]</a-tag></p> + <p>{{ i18n "password"}}: <a-tag color="green">[[ inbound.password ]]</a-tag></p> + </template> + + <template v-if="dbInbound.isHTTP"> + <p>{{ i18n "username"}}: <a-tag color="green">[[ inbound.username ]]</a-tag></p> + <p>{{ i18n "password"}}: <a-tag color="green">[[ inbound.password ]]</a-tag></p> + </template> + + <template v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS"> + {{template "inboundInfoStream"}} + </template> +</div> +{{end}} + +{{define "component/inboundInfo"}} +<script> + Vue.component('inbound-info', { + delimiters: ['[[', ']]'], + props: ["dbInbound", "inbound"], + template: `{{template "component/inboundInfoComponent"}}`, + }); +</script> +{{end}}
\ No newline at end of file diff --git a/web/html/xui/component/setting.html b/web/html/xui/component/setting.html new file mode 100644 index 00000000..9f8e8cbc --- /dev/null +++ b/web/html/xui/component/setting.html @@ -0,0 +1,32 @@ +{{define "component/settingListItem"}} +<a-list-item style="padding: 20px"> + <a-row> + <a-col :lg="24" :xl="12"> + <a-list-item-meta :title="title" :description="desc"/> + </a-col> + <a-col :lg="24" :xl="12"> + <template v-if="type === 'text'"> + <a-input :value="value" @input="$emit('input', $event.target.value)"></a-input> + </template> + <template v-else-if="type === 'number'"> + <a-input type="number" :value="value" @input="$emit('input', $event.target.value)"></a-input> + </template> + <template v-else-if="type === 'textarea'"> + <a-textarea :value="value" @input="$emit('input', $event.target.value)" :auto-size="{ minRows: 10, maxRows: 10 }"></a-textarea> + </template> + <template v-else-if="type === 'switch'"> + <a-switch :checked="value" @change="value => $emit('input', value)"></a-switch> + </template> + </a-col> + </a-row> +</a-list-item> +{{end}} + +{{define "component/setting"}} +<script> + Vue.component('setting-list-item', { + props: ["type", "title", "desc", "value"], + template: `{{template "component/settingListItem"}}`, + }); +</script> +{{end}}
\ No newline at end of file diff --git a/web/html/xui/form/inbound.html b/web/html/xui/form/inbound.html new file mode 100644 index 00000000..1a2e7ca1 --- /dev/null +++ b/web/html/xui/form/inbound.html @@ -0,0 +1,106 @@ +{{define "form/inbound"}} +<!-- base --> +<a-form layout="inline"> + <a-form-item label='{{ i18n "remark" }}'> + <a-input v-model.trim="dbInbound.remark"></a-input> + </a-form-item> + <a-form-item label='{{ i18n "enable" }}'> + <a-switch v-model="dbInbound.enable"></a-switch> + </a-form-item> + <a-form-item label='{{ i18n "protocol" }}'> + <a-select v-model="inbound.protocol" style="width: 160px;"> + <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> + <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> + <a-form-item label='{{ i18n "pages.inbounds.po
|
