index.vue 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842
  1. <template>
  2. <div class="login-container">
  3. <div class="login-info">
  4. <img :src="require(`@/assets/logo/logo1.png`)"/>
  5. <div>
  6. <div class="title">{{$t('login.shuoming.title')}}</div>
  7. <div class="title">{{$t('login.shuoming.tips')}}</div>
  8. </div>
  9. </div>
  10. <el-form
  11. :model="loginForm"
  12. :rules="rules"
  13. autocomplete="off"
  14. class="login-form"
  15. label-position="left"
  16. ref="loginForm"
  17. >
  18. <el-tabs v-model="activeName" @tab-click="handleClick">
  19. <el-tab-pane label="扫码登录" name="first">
  20. <div class="saomaDom">
  21. <img :src="require(`@/assets/logo/saoma.png`)"/>
  22. <el-select
  23. v-model="opValue"
  24. :span="2"
  25. @change="selectChange"
  26. :placeholder="$t('common.pleaseSelect')"
  27. >
  28. <el-option
  29. v-for="item in options"
  30. :key="item.value"
  31. :label="item.name"
  32. :value="item.value"
  33. >
  34. </el-option>
  35. </el-select>
  36. <div>请选择上下料站并扫码登录<el-input id="qcodeUserInput" name="qcodeUserInput" type="password" style="z-index: -100" v-model="loginForm.qcodeUser" onfocus="this.style.imeMode = 'disabled'" @input="showQcodeUser()"/></div>
  37. </div>
  38. </el-tab-pane>
  39. <el-tab-pane label="密码登录" name="second">
  40. <div class="title-container">
  41. <h3 class="title">{{$t('login.title')}}</h3>
  42. <lang-select class="set-language"/>
  43. </div>
  44. <span v-if="login.type === 'up'">
  45. <el-form-item prop="tenant" v-show="isMultiTenant">
  46. <el-input
  47. :placeholder="$t('login.tenant')"
  48. @keyup.enter.native="handleLogin"
  49. autocomplete="off"
  50. name="tenantView"
  51. prefix-icon="el-icon-user"
  52. ref="tenant"
  53. type="text"
  54. v-model="loginForm.tenantView"
  55. />
  56. </el-form-item>
  57. <el-form-item prop="account">
  58. <el-input
  59. :placeholder="$t('login.username')"
  60. @keyup.enter.native="handleLogin"
  61. autocomplete="off"
  62. name="account"
  63. prefix-icon="el-icon-user"
  64. ref="account"
  65. type="text"
  66. v-model="loginForm.account"
  67. />
  68. </el-form-item>
  69. <el-form-item prop="password">
  70. <el-input
  71. :placeholder="$t('login.password')"
  72. :show-password="true"
  73. @keyup.enter.native="handleLogin"
  74. autocomplete="off"
  75. name="password"
  76. prefix-icon="el-icon-key"
  77. ref="password"
  78. type="password"
  79. v-model="loginForm.password"
  80. />
  81. </el-form-item>
  82. <el-form-item class="code-input" prop="code" v-show="isCaptcha">
  83. <el-input
  84. :placeholder="$t('login.code')"
  85. @keyup.enter.native="handleLogin"
  86. autocomplete="off"
  87. name="code"
  88. prefix-icon="el-icon-lock"
  89. ref="code"
  90. style="width: 70%"
  91. type="text"
  92. v-model="loginForm.code"
  93. />
  94. </el-form-item>
  95. <img
  96. v-show="isCaptcha"
  97. :src="imageCode"
  98. @click="getCodeImage"
  99. alt="codeImage"
  100. class="code-image"
  101. />
  102. <el-button
  103. :loading="loading"
  104. @click.native.prevent="handleLogin"
  105. style="width:100%;margin-bottom:14px;"
  106. type="primary"
  107. >{{ $t("login.logIn") }}</el-button
  108. >
  109. </span>
  110. <span v-if="login.type === 'social'">
  111. {{ $t("login.chooseToSignIn") }}
  112. <div>
  113. <template v-for="(l, index) in logo">
  114. <div :key="index" class="logo-wrapper">
  115. <img
  116. :class="{ radius: l.radius }"
  117. :src="resolveLogo(l.img)"
  118. @click="socialLogin(l.name)"
  119. alt
  120. />
  121. </div>
  122. </template>
  123. </div>
  124. </span>
  125. <span style="margin-top: -1rem" v-if="login.type === 'bind'">
  126. <el-tabs @tab-click="handleTabClick" v-model="tabActiveName">
  127. <el-tab-pane :label="$t('common.bindLogin')" name="bindLogin">
  128. <el-form-item prop="bindAccount">
  129. <el-input
  130. :placeholder="$t('login.account')"
  131. autocomplete="off"
  132. name="bindAccount"
  133. prefix-icon="el-icon-user"
  134. ref="bindAccount"
  135. type="text"
  136. v-model="loginForm.bindAccount"
  137. />
  138. </el-form-item>
  139. <el-form-item prop="bindPassword">
  140. <el-input
  141. :placeholder="$t('login.password')"
  142. :show-password="true"
  143. autocomplete="off"
  144. name="bindPassword"
  145. prefix-icon="el-icon-key"
  146. ref="bindPassword"
  147. type="password"
  148. v-model="loginForm.bindPassword"
  149. />
  150. </el-form-item>
  151. <el-button
  152. :loading="loading"
  153. @click.native.prevent="bindLogin"
  154. style="width:100%;margin-bottom:14px;"
  155. type="primary"
  156. >{{ $t("common.bindLogin") }}</el-button
  157. >
  158. </el-tab-pane>
  159. <el-tab-pane :label="$t('common.signLogin')" name="signLogin">
  160. <el-form-item prop="signAccount">
  161. <el-input
  162. :placeholder="$t('login.account')"
  163. autocomplete="off"
  164. name="signAccount"
  165. prefix-icon="el-icon-user"
  166. ref="signAccount"
  167. type="text"
  168. v-model="loginForm.signAccount"
  169. />
  170. </el-form-item>
  171. <el-form-item prop="signPassword">
  172. <el-input
  173. :placeholder="$t('login.password')"
  174. :show-password="true"
  175. autocomplete="off"
  176. name="signPassword"
  177. prefix-icon="el-icon-key"
  178. ref="signPassword"
  179. type="password"
  180. v-model="loginForm.signPassword"
  181. />
  182. </el-form-item>
  183. <el-button
  184. :loading="loading"
  185. @click.native.prevent="signLogin"
  186. style="width:100%;margin-bottom:14px;"
  187. type="primary"
  188. >{{ $t("common.signLogin") }}</el-button
  189. >
  190. </el-tab-pane>
  191. </el-tabs>
  192. </span>
  193. <!--<el-dropdown class="login-type" placement="top-end">
  194. <span class="el-dropdown-link">
  195. <el-link type="primary">{{ $t("login.ortherLoginType") }}</el-link>
  196. </span>
  197. <el-dropdown-menu slot="dropdown">
  198. <el-dropdown-item
  199. :disabled="login.type === 'up'"
  200. @click.native="login.type = 'up'"
  201. >{{ $t("login.type.up") }}
  202. </el-dropdown-item
  203. >
  204. <el-dropdown-item
  205. :disabled="login.type === 'social'"
  206. @click.native="login.type = 'social'"
  207. >{{ $t("login.type.social") }}
  208. </el-dropdown-item
  209. >
  210. </el-dropdown-menu>
  211. </el-dropdown>-->
  212. </el-tab-pane>
  213. </el-tabs>
  214. </el-form>
  215. <span class="login-footer">
  216. © 2019 - 2022
  217. <span>{{$t('login.shuoming.tips')}}</span>
  218. </span>
  219. </div>
  220. </template>
  221. <script>
  222. import LangSelect from "@/components/LangSelect";
  223. import db from "@/utils/localstorage";
  224. import {randomNum} from "@/utils";
  225. import {socialLoginUrl} from "@/settings";
  226. import loginApi from "@/api/Login.js";
  227. import oauthApi from '@/api/Oauth'
  228. import {Base64} from 'js-base64';
  229. import productionResourcesMgrApi from "@/api/resourceProductMgr/productionResourcesMgr";
  230. export default {
  231. name: "Login",
  232. components: {LangSelect},
  233. data() {
  234. return {
  235. options: [],
  236. opValue:'',
  237. activeName: 'first',
  238. //是否启用多租户
  239. isMultiTenant:
  240. false,
  241. isCaptcha:
  242. process.env.VUE_APP_IS_CAPTCHA === "true" ? true : false,
  243. tabActiveName: "bindLogin",
  244. login: {
  245. type: "up"
  246. },
  247. logo: [
  248. {img: "gitee.png", name: "gitee", radius: true},
  249. {img: "github.png", name: "github", radius: true},
  250. {img: "tencent_cloud.png", name: "tencent_cloud", radius: true},
  251. {img: "qq.png", name: "qq", radius: false},
  252. {img: "dingtalk.png", name: "dingtalk", radius: true},
  253. {img: "microsoft.png", name: "microsoft", radius: false}
  254. ],
  255. loginForm: {
  256. account: "zuihou",
  257. password: "", // zuihou
  258. tenantView: "0000", //显示用的
  259. tenant: "", //传递给后端的
  260. key: randomNum(24, 16),
  261. code: "",
  262. grantType: process.env.VUE_APP_IS_CAPTCHA === "true" ? "captcha" : "password",
  263. bindAccount: "",
  264. bindPassword: "",
  265. signAccount: "",
  266. signPassword: "",
  267. qcodeUser: "",
  268. stationResourceId: "",
  269. },
  270. rules: {
  271. account: {
  272. required: true,
  273. message: this.$t("rules.require"),
  274. trigger: "blur"
  275. },
  276. tenantView: {
  277. required: true,
  278. message: this.$t("rules.require"),
  279. trigger: "blur"
  280. },
  281. password: {
  282. required: true,
  283. message: this.$t("rules.require"),
  284. trigger: "blur"
  285. },
  286. code: {
  287. validator: (rule, value, callback) => {
  288. if (this.isCaptcha && value === '') {
  289. callback(this.$t("rules.require"))
  290. } else {
  291. callback()
  292. }
  293. callback()
  294. }, trigger: 'blur'
  295. },
  296. bindAccount: {
  297. required: true,
  298. message: this.$t("rules.require"),
  299. trigger: "blur"
  300. },
  301. bindPassword: {
  302. required: true,
  303. message: this.$t("rules.require"),
  304. trigger: "blur"
  305. },
  306. signAccount: [
  307. {
  308. required: true,
  309. message: this.$t("rules.require"),
  310. trigger: "blur"
  311. },
  312. {
  313. min: 4,
  314. max: 10,
  315. message: this.$t("rules.range4to10"),
  316. trigger: "blur"
  317. }
  318. ],
  319. signPassword: [
  320. {
  321. required: true,
  322. message: this.$t("rules.require"),
  323. trigger: "blur"
  324. },
  325. {
  326. min: 6,
  327. max: 20,
  328. message: this.$t("rules.range6to20"),
  329. trigger: "blur"
  330. }
  331. ]
  332. },
  333. authUser: null,
  334. loading: false,
  335. showDialog: false,
  336. redirect: undefined,
  337. otherQuery: {},
  338. imageCode: "",
  339. page: {
  340. width: window.screen.width * 0.5,
  341. height: window.screen.height * 0.5
  342. }
  343. };
  344. },
  345. created() {
  346. this.getStations()
  347. },
  348. mounted() {
  349. db.clear();
  350. this.getCodeImage();
  351. this.$nextTick(
  352. window.onload = function () { // 光标默认位置
  353. var oInput = document.getElementById('qcodeUserInput');
  354. oInput.focus();
  355. }
  356. )
  357. },
  358. destroyed() {
  359. window.removeEventListener("message", this.resolveSocialLogin);
  360. },
  361. methods: {
  362. // Tabs切换-事件
  363. handleClick(tab, event) {
  364. // tab切换初始化focus及内容
  365. if(tab.name === 'first'){
  366. this.$nextTick(
  367. window.onload = function () { // 光标默认位置
  368. document.getElementById('qcodeUserInput').value = "";
  369. var oInput = document.getElementById('qcodeUserInput');
  370. oInput.focus();
  371. }
  372. )
  373. }
  374. console.log(tab, event);
  375. },
  376. getCodeImage() {
  377. loginApi
  378. .getCaptcha(this.loginForm.key)
  379. .then(response => {
  380. const res = response.data;
  381. if (res.byteLength <= 100) {
  382. this.$message({
  383. message: this.$t("tips.systemError"),
  384. type: "error"
  385. });
  386. }
  387. return (
  388. "data:image/png;base64," +
  389. btoa(
  390. new Uint8Array(res).reduce(
  391. (data, byte) => data + String.fromCharCode(byte),
  392. ""
  393. )
  394. )
  395. );
  396. })
  397. .then(res => {
  398. this.imageCode = res;
  399. })
  400. .catch(e => {
  401. if (e.toString().indexOf("429") !== -1) {
  402. this.$message({
  403. message: this.$t("tips.tooManyRequest"),
  404. type: "error"
  405. });
  406. } else {
  407. this.$message({
  408. message: this.$t("tips.getCodeImageFailed"),
  409. type: "error"
  410. });
  411. }
  412. });
  413. },
  414. handleTabClick(tab) {
  415. this.tabActiveName = tab.name;
  416. },
  417. resolveLogo(logo) {
  418. return require(`@/assets/logo/${logo}`);
  419. },
  420. socialLogin(oauthType) {
  421. const url = `${socialLoginUrl}/${oauthType}/login`;
  422. window.open(
  423. url,
  424. "newWindow",
  425. `resizable=yes, height=${this.page.height}, width=${this.page.width}, top=10%, left=10%, toolbar=no, menubar=no, scrollbars=no, resizable=no,location=no, status=no`
  426. );
  427. window.addEventListener("message", this.resolveSocialLogin, false);
  428. },
  429. resolveSocialLogin(e) {
  430. const data = e.data;
  431. const that = this;
  432. if (data.message === "not_bind") {
  433. that.login.type = "bind";
  434. const authUser = data.data;
  435. that.authUser = authUser;
  436. that
  437. .$confirm(
  438. that.$t("common.current") +
  439. authUser.source +
  440. that.$t("common.socialAccount") +
  441. authUser.nickname +
  442. that.$t("common.socialTips"),
  443. that.$t("common.tips"),
  444. {
  445. confirmButtonText: that.$t("common.signLogin"),
  446. cancelButtonText: that.$t("common.bindLogin"),
  447. type: "warning"
  448. }
  449. )
  450. .then(() => {
  451. that.tabActiveName = "signLogin";
  452. })
  453. .catch(() => {
  454. that.tabActiveName = "bindLogin";
  455. });
  456. } else if (data.message === "social_login_success") {
  457. that.saveLoginData(data.data);
  458. that.getUserDetailInfo();
  459. that.loginSuccessCallback(data.account);
  460. } else {
  461. // do nothing
  462. }
  463. },
  464. bindLogin() {
  465. let account_c = false;
  466. let password_c = false;
  467. this.$refs.loginForm.validateField("bindAccount", e => {
  468. if (!e) {
  469. account_c = true;
  470. }
  471. });
  472. this.$refs.loginForm.validateField("bindPassword", e => {
  473. if (!e) {
  474. password_c = true;
  475. }
  476. });
  477. if (account_c && password_c) {
  478. this.loading = true;
  479. const that = this;
  480. const params = {
  481. bindAccount: that.loginForm.bindAccount,
  482. bindPassword: that.loginForm.bindPassword,
  483. ...that.authUser
  484. };
  485. params.token = null;
  486. that
  487. .$post("auth/social/bind/login", params)
  488. .then(r => {
  489. const data = r.data.data;
  490. this.saveLoginData(data);
  491. this.getUserDetailInfo();
  492. this.loginSuccessCallback(that.loginForm.bindAccount);
  493. })
  494. .catch(error => {
  495. console.error(error);
  496. that.loading = false;
  497. });
  498. }
  499. },
  500. signLogin() {
  501. let account_c = false;
  502. let password_c = false;
  503. this.$refs.loginForm.validateField("signAccount", e => {
  504. if (!e) {
  505. account_c = true;
  506. }
  507. });
  508. this.$refs.loginForm.validateField("signPassword", e => {
  509. if (!e) {
  510. password_c = true;
  511. }
  512. });
  513. if (account_c && password_c) {
  514. this.loading = true;
  515. const that = this;
  516. const params = {
  517. bindAccount: that.loginForm.signAccount,
  518. bindPassword: that.loginForm.signPassword,
  519. ...that.authUser
  520. };
  521. params.token = null;
  522. that
  523. .$post("auth/social/sign/login", params)
  524. .then(r => {
  525. const data = r.data.data;
  526. this.saveLoginData(data);
  527. this.getUserDetailInfo();
  528. this.loginSuccessCallback(that.loginForm.signAccount);
  529. })
  530. .catch(error => {
  531. console.error(error);
  532. that.loading = false;
  533. });
  534. }
  535. },
  536. handleLogin() {
  537. this.loginForm.tenant = `${Base64.encode(this.loginForm.tenantView)}`;
  538. this.$refs.loginForm.validate((valid) => {
  539. if (valid) {
  540. this.loginSubmit();
  541. } else {
  542. return false
  543. }
  544. })
  545. },
  546. loginSubmit() {
  547. this.loading = true;
  548. const that = this;
  549. this.$store.commit("account/setTenant", this.loginForm.tenant);
  550. loginApi.login(this.loginForm).then(response => {
  551. const res = response.data;
  552. if (res.isSuccess) {
  553. that.saveLoginData(res.data['token'], res.data['refreshToken'], res.data['expiration']);
  554. that.saveUserInfo(res.data);
  555. that.getResource();
  556. } else {
  557. that.getCodeImage();
  558. }
  559. }).finally(() => that.loading = false);
  560. },
  561. saveLoginData(token, refreshToken, expiration) {
  562. this.$store.commit("account/setToken", token);
  563. this.$store.commit("account/setRefreshToken", refreshToken);
  564. this.$store.commit("account/setExpireTime", expiration);
  565. },
  566. saveUserInfo(user) {
  567. this.$store.commit("account/setUser", {
  568. });
  569. this.$store.commit("account/setUser", {
  570. id: user.userId,
  571. account: user.account,
  572. name: user.name,
  573. avatar: user.avatar,
  574. expire: user.expire,
  575. workDescribe: user.workDescribe,
  576. roles: user.roles
  577. });
  578. },
  579. getResource() {
  580. oauthApi.getResource().then(response => {
  581. const res = response.data;
  582. if (res.isSuccess) {
  583. const permissionsList = res.data;
  584. this.$store.commit("account/setPermissions", permissionsList ? permissionsList : []);
  585. this.loginSuccess();
  586. } else {
  587. this.getCodeImage();
  588. }
  589. });
  590. },
  591. loginSuccess() {
  592. this.$message({
  593. message: this.$t("tips.loginSuccess"),
  594. type: "success"
  595. });
  596. this.$router.push("/");
  597. },
  598. loginSuccessCallback(user) {
  599. console.log(user);
  600. },
  601. qcodelogin(qcodeUserValue) {
  602. this.loading = true;
  603. const that = this;
  604. this.loginForm.tenant = `${Base64.encode(this.loginForm.tenantView)}`;
  605. this.$store.commit("account/setTenant", this.loginForm.tenant);
  606. this.loginForm.qcodeUserValue = qcodeUserValue
  607. this.loginForm.grantType="password";
  608. loginApi.qcodelogin(this.loginForm).then(response => {
  609. const res = response.data;
  610. if (res.isSuccess) {
  611. that.saveLoginData(res.data['token'], res.data['refreshToken'], res.data['expiration']);
  612. that.saveUserInfo(res.data);
  613. that.getResource();
  614. } else {
  615. that.getCodeImage();
  616. }
  617. }).finally(() => that.loading = false);
  618. },
  619. showQcodeUser(){
  620. console.log(this.loginForm.qcodeUser.substr(this.loginForm.qcodeUser.length-1,1));
  621. if(this.loginForm.qcodeUser.substr(this.loginForm.qcodeUser.length-1,1) == "#"){
  622. this.qcodelogin()
  623. }
  624. },
  625. getStations() {
  626. productionResourcesMgrApi.getStations({}).then((res) => {
  627. res = res.data;
  628. if (res.isSuccess) {
  629. if (res.data.length > 0) {
  630. this.options = res.data;
  631. }
  632. }
  633. });
  634. },
  635. selectChange(value) {
  636. console.log(value);
  637. this.loginForm.stationResourceId = value;
  638. this.$nextTick(
  639. window.onload = function () { // 光标默认位置
  640. document.getElementById('qcodeUserInput').value = "";
  641. var oInput = document.getElementById('qcodeUserInput');
  642. oInput.focus();
  643. }
  644. )
  645. },
  646. }
  647. };
  648. </script>
  649. <style lang="scss">
  650. .login-form {
  651. .el-tabs__item {
  652. font-size: 16px;
  653. color: white;
  654. }
  655. .el-tabs__item.is-active {
  656. color: #1890ff;
  657. }
  658. .el-tabs__active-bar {
  659. background: none;
  660. }
  661. }
  662. </style>
  663. <style lang="scss" scoped>
  664. $bg: #2d3a4b;
  665. $dark_gray: #aaa;
  666. $light_gray: #eee;
  667. .login-container {
  668. /*background: url(../../assets/logo/loginBg.gif) 50% no-repeat;
  669. background-size: cover;*/
  670. /*background: url(../../assets/background.jpg) 50% no-repeat;*/
  671. /*background: url(../../assets/logo/logoBg1.jpg) 50% no-repeat;*/
  672. /*background: url(../../assets/logo/logoBg2.png) 50% no-repeat;
  673. background-size: cover;*/
  674. background: #000000;
  675. width: 100%;
  676. height: 100vh;
  677. .saomaDom {
  678. text-align: center;
  679. color: white;
  680. cursor: pointer;
  681. }
  682. .login-info {
  683. display: flex;
  684. align-items: center;
  685. position: absolute;
  686. left: 10%;
  687. top: 44%;
  688. margin-top: -100px;
  689. color: #fff;
  690. img{
  691. width: 300px;
  692. margin-right: 40px;
  693. }
  694. .title {
  695. font-size: 1.8rem;
  696. font-weight: 600;
  697. }
  698. .title:first-child{
  699. margin-bottom: 15px;
  700. }
  701. .sub-title {
  702. font-size: 1.5rem;
  703. margin: 0.3rem 0 0.7rem 1rem;
  704. }
  705. .desc {
  706. font-size: 0.96rem;
  707. line-height: 1.9rem;
  708. }
  709. }
  710. .login-form {
  711. position: absolute;
  712. top: 45%;
  713. left: 70%;
  714. margin: -180px 0 0 -160px;
  715. width: 320px;
  716. height: 440px;
  717. padding: 10px 36px 36px 36px;
  718. background: #4F535B;
  719. border-radius: 3px;
  720. .el-tabs__item {
  721. font-size: 16px;
  722. }
  723. .code-input {
  724. width: 50%;
  725. display: inline-block;
  726. vertical-align: middle;
  727. }
  728. .code-image {
  729. display: inline-block;
  730. vertical-align: top;
  731. cursor: pointer;
  732. }
  733. .login-type {
  734. text-align: right;
  735. display: inline-block;
  736. width: 100%;
  737. }
  738. .logo-wrapper {
  739. display: inline-block;
  740. margin: 10px 0;
  741. img {
  742. width: 1.9rem;
  743. display: inline-block;
  744. margin: 0.8rem 0.8rem -0.8rem 0.8rem;
  745. cursor: pointer;
  746. &.radius {
  747. border-radius: 50%;
  748. }
  749. }
  750. }
  751. }
  752. .login-footer {
  753. position: fixed;
  754. bottom: 1rem;
  755. width: 100%;
  756. text-align: center;
  757. color: white;
  758. font-size: 0.85rem;
  759. line-height: 1rem;
  760. height: 1rem;
  761. }
  762. .tips {
  763. font-size: 14px;
  764. color: #fff;
  765. margin-bottom: 10px;
  766. span {
  767. &:first-of-type {
  768. margin-right: 16px;
  769. }
  770. }
  771. }
  772. .title-container {
  773. position: relative;
  774. .title {
  775. font-size: 20px;
  776. color: #FFFFFF;
  777. margin: 0 auto 40px auto;
  778. text-align: center;
  779. font-weight: bold;
  780. }
  781. .set-language {
  782. color: #aaa;
  783. position: absolute;
  784. top: 3px;
  785. font-size: 18px;
  786. right: 0;
  787. cursor: pointer;
  788. }
  789. }
  790. .thirdparty-button {
  791. position: absolute;
  792. right: 0;
  793. bottom: 6px;
  794. }
  795. @media only screen and (max-width: 470px) {
  796. .thirdparty-button {
  797. display: none;
  798. }
  799. }
  800. @media screen and (max-width: 1100px) {
  801. .login-info {
  802. left: 8%;
  803. }
  804. }
  805. @media screen and (max-width: 970px) {
  806. .login-form {
  807. left: 50%;
  808. }
  809. .login-info {
  810. display: none;
  811. }
  812. }
  813. }
  814. </style>