diff options
| author | Ho3ein <ho3ein.sanaei@gmail.com> | 2023-12-10 17:42:52 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-12-10 17:42:52 +0300 |
| commit | e3f1d3c892a1af48f27fdc36f273a55f38d13b40 (patch) | |
| tree | b11d0c1ed3c15c8f6f891a5e6df8e021d5db8ab6 /web/html | |
| parent | 36cf7c0a8fda915b51e75958ce729fd9a61a5c90 (diff) | |
| parent | 9fbe80f87f950673058f0001b3704251fa8b9243 (diff) | |
huge changes
Diffstat (limited to 'web/html')
42 files changed, 5208 insertions, 2463 deletions
diff --git a/web/html/common/head.html b/web/html/common/head.html index 8a89954d..4fa2ea8a 100644 --- a/web/html/common/head.html +++ b/web/html/common/head.html @@ -4,7 +4,7 @@ <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/ant-design-vue@1.7.8/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 }}"> <link rel=”icon” type=”image/x-icon” href="{{ .base_path }}assets/favicon.ico"> @@ -13,6 +13,20 @@ [v-cloak] { display: none; } + /* vazirmatn-regular - arabic_latin_latin-ext */ + @font-face { + font-display: swap; + font-family: 'Vazirmatn'; + font-style: normal; + font-weight: 400; + src: url('{{ .base_path }}assets/Vazirmatn-UI-NL-Regular.woff2') format('woff2'); + unicode-range: U+0600-06FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE80-FEFC, U+0030-0039; + } + body { + font-family: -apple-system, BlinkMacSystemFont, 'Vazirmatn', 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', + 'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif, 'Apple Color Emoji', + 'Segoe UI Emoji', 'Segoe UI Symbol'; + } </style> <title>{{ .host }}-{{ i18n .title}}</title> </head> diff --git a/web/html/common/js.html b/web/html/common/js.html index d400196b..661a0b87 100644 --- a/web/html/common/js.html +++ b/web/html/common/js.html @@ -1,19 +1,13 @@ {{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/ant-design-vue@1.7.8/antd.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 }}'; diff --git a/web/html/common/prompt_modal.html b/web/html/common/prompt_modal.html index 17a65ec1..edfad682 100644 --- a/web/html/common/prompt_modal.html +++ b/web/html/common/prompt_modal.html @@ -1,8 +1,7 @@ {{define "promptModal"}} <a-modal id="prompt-modal" v-model="promptModal.visible" :title="promptModal.title" :closable="true" @ok="promptModal.ok" :mask-closable="false" - :class="themeSwitcher.darkCardClass" - :ok-text="promptModal.okText" cancel-text='{{ i18n "cancel" }}'> + :ok-text="promptModal.okText" cancel-text='{{ i18n "cancel" }}' :class="themeSwitcher.currentTheme"> <a-input id="prompt-modal-input" :type="promptModal.type" v-model="promptModal.value" :autosize="{minRows: 10, maxRows: 20}" diff --git a/web/html/common/qrcode_modal.html b/web/html/common/qrcode_modal.html index 51dc38cb..4be7997d 100644 --- a/web/html/common/qrcode_modal.html +++ b/web/html/common/qrcode_modal.html @@ -1,7 +1,7 @@ {{define "qrcodeModal"}} <a-modal id="qrcode-modal" v-model="qrModal.visible" :title="qrModal.title" :closable="true" - :class="themeSwitcher.darkCardClass" + :class="themeSwitcher.currentTheme" :footer="null" width="300px"> <a-tag color="green" style="margin-bottom: 10px;display: block;text-align: center;"> @@ -13,7 +13,7 @@ </template> <a-divider>{{ i18n "pages.inbounds.client" }}</a-divider> <template v-for="(row, index) in qrModal.qrcodes"> - <a-tag color="orange" style="margin-top: 10px;display: block;text-align: center;">[[ row.remark ]]</a-tag> + <a-tag color="green" style="margin: 10px 0; display: block; text-align: center;">[[ row.remark ]]</a-tag> <canvas @click="copyToClipboard('qrCode-'+index, row.link)" :id="'qrCode-'+index" style="width: 100%; height: 100%;"></canvas> </template> </a-modal> @@ -22,39 +22,25 @@ const qrModal = { title: '', - clientIndex: 0, - inbound: new Inbound(), dbInbound: new DBInbound(), client: null, qrcodes: [], clipboard: null, visible: false, subId: '', - show: function (title = '', dbInbound = new DBInbound(), clientIndex = 0) { + show: function (title = '', dbInbound, client) { this.title = title; - this.clientIndex = clientIndex; this.dbInbound = dbInbound; this.inbound = dbInbound.toInbound(); - settings = JSON.parse(this.inbound.settings); - this.client = settings.clients[clientIndex]; - remark = [this.dbInbound.remark, ( this.client ? this.client.email : '')].filter(Boolean).join('-'); - address = this.dbInbound.address; + this.client = client; this.subId = ''; this.qrcodes = []; - if (this.inbound.tls && !ObjectUtil.isArrEmpty(this.inbound.stream.tls.settings.domains)) { - this.inbound.stream.tls.settings.domains.forEach((domain) => { - remarkText = [remark, domain.remark].filter(Boolean).join('-'); - this.qrcodes.push({ - remark: remarkText, - link: this.inbound.genLink(domain.domain, remarkText, clientIndex) - }); - }); - } else { + this.inbound.genAllLinks(this.dbInbound.remark, app.remarkModel, client).forEach(l => { this.qrcodes.push({ - remark: remark, - link: this.inbound.genLink(address, remark, clientIndex) + remark: l.remark, + link: l.link }); - } + }); this.visible = true; }, close: function () { @@ -86,8 +72,7 @@ }); }, genSubLink(subID) { - const { domain: host, port, tls: isTLS, path: base } = app.subSettings; - return buildURL({ host, port, isTLS, base, path: subID+'?name='+remark }); + return app.subSettings.subURI+subID+'?name='+subID; } }, updated() { diff --git a/web/html/common/text_modal.html b/web/html/common/text_modal.html index 1514051b..4fe2f175 100644 --- a/web/html/common/text_modal.html +++ b/web/html/common/text_modal.html @@ -1,7 +1,7 @@ {{define "textModal"}} <a-modal id="text-modal" v-model="txtModal.visible" :title="txtModal.title" :closable="true" ok-text='{{ i18n "copy" }}' cancel-text='{{ i18n "close" }}' - :class="themeSwitcher.darkCardClass" + :class="themeSwitcher.currentTheme" :ok-button-props="{attrs:{id:'txt-modal-ok-btn'}}"> <a-button v-if="!ObjectUtil.isEmpty(txtModal.fileName)" type="primary" style="margin-bottom: 10px;" :href="'data:application/text;charset=utf-8,' + encodeURIComponent(txtModal.content)" @@ -31,7 +31,10 @@ this.clipboard = new ClipboardJS('#txt-modal-ok-btn', { text: () => this.content, }); - this.clipboard.on('success', () => app.$message.success('{{ i18n "copied" }}')); + this.clipboard.on('success', () => { + app.$message.success('{{ i18n "copied" }}') + this.close(); + }); } }); }, diff --git a/web/html/login.html b/web/html/login.html index 61381a61..8f5a6f4d 100644 --- a/web/html/login.html +++ b/web/html/login.html @@ -2,65 +2,241 @@ <html lang="en"> {{template "head" .}} <style> - - #app { - padding-top: 100px; + h1 { + text-align: center; + margin: 20px 0 50px 0; + } + .ant-btn, + .ant-input { + height: 50px; + border-radius: 30px; + } + .ant-input-group-addon { + border-radius: 0 30px 30px 0; + width: 50px; + font-size: 18px; + } + .ant-input-affix-wrapper .ant-input-prefix { + left: 23px; + } + .ant-input-affix-wrapper .ant-input:not(:first-child) { + padding-left: 50px; + } + .centered { + display: flex; + text-align: center; + align-items: center; + justify-content: center; + } + .title { + font-size: 32px; + font-weight: bold; + } + #app { + overflow: hidden; + } + #login { + animation: charge 0.5s both; + background-color: #fff; + border-radius: 2rem; + padding: 3rem; + transition: all 0.3s; + } + #login:hover { + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.09); + } + @keyframes charge { + from { + transform: translateY(5rem); + opacity: 0; } - - h1 { - text-align: center; - color: #fff; - margin: 20px 0 50px 0; + to { + transform: translateY(0); + opacity: 1; } - - .ant-btn, .ant-input { - height: 50px; - border-radius: 30px; + } + @keyframes wave { + from { + transform: rotate(0deg); } - - .ant-input-group-addon { - border-radius: 0 30px 30px 0; - width: 50px; - font-size: 18px; + to { + transform: rotate(360deg); } - - .ant-input-affix-wrapper .ant-input-prefix { - left: 23px; + } + .wave { + opacity: 0.6; + position: absolute; + bottom: 40%; + left: 50%; + width: 6000px; + height: 6000px; + background-color: rgba(0, 135, 113, 0.08); + margin-left: -3000px; + transform-origin: 50% 48%; + border-radius: 46%; + animation: wave 72s infinite linear; + pointer-events: none; + } + .wave2 { + animation: wave 88s infinite linear; + opacity: 0.3; + } + .wave3 { + animation: wave 80s infinite linear; + opacity: 0.1; + } + .under { + background-color: #dbf5ed; + } + .dark .wave { + background: rgb(10 117 87 / 20%); + } + .dark .under { + background-color: #101828; + } + .dark #login { + background-color: #151f31; + } + .dark h1 { + color: rgba(255, 255, 255, 0.85); + } + .ant-btn-primary-login { + color: #008771; + background-color: #eef9f7; + border-color: #89d9cc; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.12); + box-shadow: none; + width: 100%; + } + .ant-btn-primary-login:focus, + .ant-btn-primary-login:hover { + color: #fff; + background-color: #006655; + border-color: #006655; + background-image: linear-gradient( + 270deg, + rgba(123, 199, 77, 0) 30%, + #009980, + rgba(123, 199, 77, 0) 100% + ); + background-repeat: no-repeat; + animation: ma-bg-move ease-in-out 5s infinite; + background-position-x: -500px; + width: 95%; + animation-delay: -0.5s; + box-shadow: 0 2px 0 rgba(0, 0, 0, 0.045); + } + .ant-btn-primary-login.active, + .ant-btn-primary-login:active { + color: #fff; + background-color: #006655; + border-color: #006655; + } + @keyframes ma-bg-move { + 0% { + background-position: -500px 0; } - - .ant-input-affix-wrapper .ant-input:not(:first-child) { - padding-left: 50px; + 50% { + background-position: 1000px 0; } - - .centered { - display: flex; - text-align: center; - align-items: center; - justify-content: center; + 100% { + background-position: 1000px 0; } - - .title { - font-size: 32px; - font-weight: bold; + } + .wave-btn-bg { + position: relative; + border-radius: 25px; + width: 100%; + } + .dark .wave-btn-bg { + color: #fff; + position: relative; + background-color: #0a7557; + border: 2px double transparent; + background-origin: border-box; + background-clip: padding-box, border-box; + background-size: 300%; + animation: wave-btn-tara 4s ease infinite; + transition: all 0.5s ease; + width: 100%; + } + .dark .wave-btn-bg-cl { + background-image: linear-gradient(rgba(13, 14, 33, 0), rgba(13, 14, 33, 0)), + radial-gradient(circle at left top, #006655, #009980, #006655) !important; + border-radius: 3em; + } + .dark .wave-btn-bg-cl:hover { + width: 95%; + } + .dark .wave-btn-bg-cl:before { + position: absolute; + content: ""; + top: -5px; + left: -5px; + bottom: -5px; + right: -5px; + z-index: -1; + background: inherit; + background-size: inherit; + border-radius: 4em; + opacity: 0; + transition: 0.5s; + } + .dark .wave-btn-bg-cl:hover::before { + opacity: 1; + filter: blur(20px); + animation: wave-btn-tara 8s linear infinite; + } + @keyframes wave-btn-tara { + to { + background-position: 300%; } - + } + .dark .ant-btn-primary-login { + font-size: 14px; + color: #fff; + text-align: center; + background-image: linear-gradient( + rgba(13, 14, 33, 0.45), + rgba(13, 14, 33, 0.35) + ); + border-radius: 2rem; + border: none; + outline: none; + background-color: transparent; + height: 46px; + position: relative; + white-space: nowrap; + cursor: pointer; + touch-action: manipulation; + padding: 0 15px; + width: 100%; + animation: none; + background-position-x: 0; + box-shadow: none; + } </style> <body> -<a-layout id="app" v-cloak class="login" :class="themeSwitcher.darkCardClass"> +<a-layout id="app" v-cloak :class="themeSwitcher.currentTheme"> <transition name="list" appear> - <a-layout-content> + <a-layout-content class="under" style="min-height: 0;"> + <div class='wave'></div> + <div class='wave wave2'></div> + <div class='wave wave3'></div> + <a-row type="flex" justify="center" align="middle" style="height: 100%; overflow: auto;"> + <a-col :xs="22" :sm="20" :md="14" :lg="10" :xl="8" :xxl="6" id="login" style="margin: 3rem 0;"> <a-row type="flex" justify="center"> - <a-col :xs="22" :sm="20" :md="16" :lg="12" :xl="8"> + <a-col> <h1 class="title">{{ 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-col span="24"> <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="'font-size: 16px;' + themeSwitcher.textStyle" /> + <a-icon slot="prefix" type="user" style="font-size: 16px;"/> </a-input> </a-form-item> <a-form-item> @@ -76,18 +252,20 @@ </a-form-item> <a-form-item> <a-row justify="center" class="centered"> - <a-button type="primary" :loading="loading" @click="login" :icon="loading ? 'poweroff' : undefined" - :style="loading ? { width: '50px' } : { display: 'block', width: '100%' }"> - [[ loading ? '' : '{{ i18n "login" }}' ]] - </a-button> + <div class="wave-btn-bg wave-btn-bg-cl"> + <a-button class="ant-btn-primary-login" type="primary" :loading="loading" @click="login" :icon="loading ? 'poweroff' : undefined" + :style="loading ? { width: '50px' } : { display: 'inline-block' }"> + [[ loading ? '' : '{{ i18n "login" }}' ]] + </a-button> + </div> </a-row> </a-form-item> <a-form-item> <a-row justify="center" class="centered"> - <a-col :span="12"> - <a-select ref="selectLang" v-model="lang" @change="setLang(lang)" :dropdown-class-name="themeSwitcher.darkCardClass"> - <a-select-option :value="l.value" :label="l.value" v-for="l in supportLangs"> - <span role="img" :aria-label="l.name" v-text="l.icon"></span> + <a-col :span="24"> + <a-select ref="selectLang" v-model="lang" @change="setLang(lang)" style="width: 150px;" :dropdown-class-name="themeSwitcher.currentTheme"> + <a-select-option :value="l.value" label="English" 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> @@ -96,12 +274,19 @@ </a-form-item> <a-form-item> <a-row justify="center" class="centered"> - <theme-switch /> + <a-col> + <a-icon type="bulb" :theme="themeSwitcher.isDarkTheme ? 'filled' : 'outlined'"></a-icon> + </a-col> + <a-col> + <theme-switch /> + </a-col> </a-row> </a-form-item> </a-form> </a-col> </a-row> + </a-col> + </a-row> </a-layout-content> </transition> </a-layout> @@ -109,6 +294,12 @@ {{template "component/themeSwitcher" .}} {{template "component/password" .}} <script> + class User { + constructor() { + this.username = ""; + this.password = ""; + } + } const app = new Vue({ delimiters: ['[[', ']]'], @@ -121,7 +312,6 @@ lang: "" }, async created() { - this.updateBackground(); this.lang = getLang(); this.secretEnable = await this.getSecretStatus(); }, @@ -143,21 +333,8 @@ return msg.obj; } }, - updateBackground() { - 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 = this.themeSwitcher.isDarkTheme ? colors.dark.bg : background; - }, - }, - watch: { - 'themeSwitcher.isDarkTheme'(newVal, oldVal) { - this.updateBackground(); - }, }, }); - </script> </body> -</html>
\ No newline at end of file +</html> diff --git a/web/html/xui/client_bulk_modal.html b/web/html/xui/client_bulk_modal.html index d5c60b76..e0295110 100644 --- a/web/html/xui/client_bulk_modal.html +++ b/web/html/xui/client_bulk_modal.html @@ -1,122 +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.darkCardClass" - :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.darkCardClass"> - <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 contacts: admin@thfree.ru |
