| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547 | <!DOCTYPE html><html lang="en">{{template "head" .}}<style>  html * {    -webkit-font-smoothing: antialiased;    -moz-osx-font-smoothing: grayscale;  }  h1 {    text-align: center;/*    margin: 20px 0 50px 0;*/    height: 110px;  }  .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;    width: 100%;  }  .title {    font-size: 32px;  }  .title b {    font-weight: bold !important;  }  #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;    }    to {      transform: translateY(0);      opacity: 1;    }  }  .under {    background-color: #c7ebe2;    z-index: 0;  }  .dark .under {    background-color: #0f2d32;  }  .dark #login {    background-color: #101113;  }  .dark h1 {    color: rgba(255, 255, 255);  }  .ant-form-item {    margin-bottom: 16px;  }  .ant-btn-primary-login {    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;    }    50% {      background-position: 1000px 0;    }    100% {      background-position: 1000px 0;    }  }  .wave-btn-bg {    position: relative;    border-radius: 25px;    width: 100%;    transition: all 0.3s cubic-bezier(.645,.045,.355,1);  }  .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%;    width: 100%;    z-index: 1;  }  .dark .wave-btn-bg:hover {animation: wave-btn-tara 4s ease infinite;}  .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;  }  .waves-header {    position: fixed;    width: 100%;    text-align: center;    background-color: #dbf5ed;    color: white;    z-index: -1;  }  .dark .waves-header {    background-color: #0a2227;  }  .waves-inner-header {    height: 50vh;    width: 100%;    margin: 0;    padding: 0;  }  .waves {    position: relative;    width: 100%;    height: 15vh;    margin-bottom: -8px; /*Fix for safari gap*/    min-height: 100px;    max-height: 150px;  }  .parallax > use {    animation: move-forever 25s cubic-bezier(0.55, 0.5, 0.45, 0.5) infinite;  }  .dark .parallax > use {    fill: #0f2d32;  }  .parallax > use:nth-child(1) {    animation-delay: -2s;    animation-duration: 4s;    opacity: 0.2;  }  .parallax > use:nth-child(2) {    animation-delay: -3s;    animation-duration: 7s;    opacity: 0.4;  }  .parallax > use:nth-child(3) {    animation-delay: -4s;    animation-duration: 10s;    opacity: 0.6;  }  .parallax > use:nth-child(4) {  animation-delay: -5s;  animation-duration: 13s;  }  @keyframes move-forever {    0% {      transform: translate3d(-90px, 0, 0);    }    100% {      transform: translate3d(85px, 0, 0);    }  }  @media (max-width: 768px) {    .waves {      height: 40px;      min-height: 40px;    }  }  .words-wrapper {    width: 100%;    display: inline-block;    position: relative;    text-align: center;  }  .words-wrapper b {    width: 100%;    display: inline-block;    position: absolute;    left: 0;    top: 0;  }  .words-wrapper b.is-visible {    position: relative;  }  .headline.zoom .words-wrapper {    -webkit-perspective: 300px;    -moz-perspective: 300px;    perspective: 300px;  }  .headline {    display: flex;    justify-content: center;    align-items: center;  }  .headline.zoom b {    opacity: 0;  }  .headline.zoom b.is-visible {    opacity: 1;    -webkit-animation: zoom-in 0.8s;    -moz-animation: zoom-in 0.8s;    animation: cubic-bezier(0.215, 0.610, 0.355, 1.000) zoom-in 0.8s;  }  .headline.zoom b.is-hidden {    -webkit-animation: zoom-out 0.8s;    -moz-animation: zoom-out 0.8s;    animation: cubic-bezier(0.215, 0.610, 0.355, 1.000) zoom-out 0.4s;  }  @-webkit-keyframes zoom-in {    0% {      opacity: 0;      -webkit-transform: translateZ(100px);    }    100% {      opacity: 1;      -webkit-transform: translateZ(0);    }  }  @-moz-keyframes zoom-in {    0% {      opacity: 0;      -moz-transform: translateZ(100px);    }    100% {      opacity: 1;      -moz-transform: translateZ(0);    }  }  @keyframes zoom-in {    0% {      opacity: 0;      -webkit-transform: translateZ(100px);      -moz-transform: translateZ(100px);      -ms-transform: translateZ(100px);      -o-transform: translateZ(100px);      transform: translateZ(100px);    }    100% {      opacity: 1;      -webkit-transform: translateZ(0);      -moz-transform: translateZ(0);      -ms-transform: translateZ(0);      -o-transform: translateZ(0);      transform: translateZ(0);    }  }  @-webkit-keyframes zoom-out {    0% {      opacity: 1;      -webkit-transform: translateZ(0);    }    100% {      opacity: 0;      -webkit-transform: translateZ(-100px);    }  }  @-moz-keyframes zoom-out {    0% {      opacity: 1;      -moz-transform: translateZ(0);    }    100% {      opacity: 0;      -moz-transform: translateZ(-100px);    }  }  @keyframes zoom-out {    0% {      opacity: 1;      -webkit-transform: translateZ(0);      -moz-transform: translateZ(0);      -ms-transform: translateZ(0);      -o-transform: translateZ(0);      transform: translateZ(0);    }    100% {      opacity: 0;      -webkit-transform: translateZ(-100px);      -moz-transform: translateZ(-100px);      -ms-transform: translateZ(-100px);      -o-transform: translateZ(-100px);      transform: translateZ(-100px);    }  }</style><body><a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">    <transition name="list" appear>        <a-layout-content class="under" style="min-height: 0;">            <div class="waves-header">                <div class="waves-inner-header"></div>                <svg class="waves" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 24 150 28" preserveAspectRatio="none" shape-rendering="auto">                    <defs>                        <path id="gentle-wave" d="M-160 44c30 0 58-18 88-18s 58 18 88 18 58-18 88-18 58 18 88 18 v44h-352z" />                    </defs>                    <g class="parallax">                        <use xlink:href="#gentle-wave" x="48" y="0" fill="rgba(0, 135, 113, 0.08)" />                        <use xlink:href="#gentle-wave" x="48" y="3" fill="rgba(0, 135, 113, 0.08)" />                        <use xlink:href="#gentle-wave" x="48" y="5" fill="rgba(0, 135, 113, 0.08)" />                        <use xlink:href="#gentle-wave" x="48" y="7" fill="#c7ebe2" />                    </g>                </svg>            </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 style="width: 100%;">                    <h1 class="title headline zoom">                      <span class="words-wrapper">                        <b class="is-visible">{{ i18n "pages.login.title" }}</b>                        <b>3X-UI</b>                      </span>                    </h1>                </a-col>            </a-row>            <a-row type="flex" justify="center">                <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;"/>                            </a-input>                        </a-form-item>                        <a-form-item>                            <password-input icon="lock" v-model.trim="user.password"                                            placeholder='{{ i18n "password" }}' @keydown.enter.native="login">                            </password-input>                        </a-form-item>                        <a-form-item v-if="secretEnable">                            <password-input icon="key" v-model.trim="user.loginSecret"                                            placeholder='{{ i18n "secretToken" }}' @keydown.enter.native="login">                            </password-input>                        </a-input>                        </a-form-item>                        <a-form-item>                            <a-row justify="center" class="centered">                                <div class="wave-btn-bg wave-btn-bg-cl" :style="loading ? { width: '52px' } : { display: 'inline-block' }">                                    <a-button class="ant-btn-primary-login" type="primary" :loading="loading" @click="login" :icon="loading ? 'poweroff' : undefined">                                        [[ loading ? '' : '{{ i18n "login" }}' ]]                                    </a-button>                                </div>                            </a-row>                        </a-form-item>                        <a-form-item>                            <a-row justify="center" class="centered">                                <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>                                </a-col>                            </a-row>                        </a-form-item>                        <a-form-item>                            <a-row justify="center" class="centered">                                <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>{{template "js" .}}{{template "component/themeSwitcher" .}}{{template "component/password" .}}<script>    class User {        constructor() {            this.username = "";            this.password = "";        }    }    const app = new Vue({        delimiters: ['[[', ']]'],        el: '#app',        data: {            themeSwitcher,            loading: false,            user: new User(),            secretEnable: false,            lang: ""        },        async created() {            this.lang = getLang();            this.secretEnable = await this.getSecretStatus();        },        methods: {            async login() {                this.loading = true;                const msg = await HttpUtil.post('/login', this.user);                this.loading = false;                if (msg.success) {                    location.href = basePath + 'panel/';                }            },            async getSecretStatus() {                this.loading = true;                const msg = await HttpUtil.post('/getSecretStatus');                this.loading = false;                if (msg.success) {                    this.secretEnable = msg.obj;                    return msg.obj;                }            },        },    });    document.addEventListener("DOMContentLoaded", function() {        var animationDelay = 2000;        initHeadline();        function initHeadline() {            animateHeadline(document.querySelectorAll('.headline'));        }        function animateHeadline(headlines) {            var duration = animationDelay;            headlines.forEach(function(headline) {                setTimeout(function() {                    hideWord(headline.querySelector('.is-visible'));                }, duration);            });        }        function hideWord(word) {            var nextWord = takeNext(word);            switchWord(word, nextWord);            setTimeout(function() {                hideWord(nextWord);            }, animationDelay);        }        function takeNext(word) {            return (word.nextElementSibling) ? word.nextElementSibling : word.parentElement.firstElementChild;        }        function switchWord(oldWord, newWord) {            oldWord.classList.remove('is-visible');            oldWord.classList.add('is-hidden');            newWord.classList.remove('is-hidden');            newWord.classList.add('is-visible');        }    });</script></body></html>
 |