LoginView.vue 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. <template>
  2. <ElContainer class="h-full bg-cover bg-center" @keyup.enter="login" :style="{ backgroundImage: `url(${bg})` }">
  3. <ElMain class="backdrop-blur1 dark:backdrop-brightness-50 !flex flex-col items-center justify-center">
  4. <span class="text-3xl font-[sh] text-white [text-shadow:_0_1px_0_rgb(0_0_0_/_40%)]">{{ title }}</span>
  5. <ElCard class="w-full max-w-lg !rounded-xl mt-8 mb-16">
  6. <ElForm :model="model" :rules="rules" label-position="top" ref="form">
  7. <ElFormItem prop="username" label="用户名">
  8. <ElInput v-model="model.username"></ElInput>
  9. </ElFormItem>
  10. <ElFormItem prop="username" label="密码">
  11. <ElInput type="password" v-model="model.password"></ElInput>
  12. </ElFormItem>
  13. <ElFormItem prop="code" label="认证码">
  14. <div class="flex items-center justify-between w-full">
  15. <ElInput v-model="model.code" maxlength="6" class="max-w-[55%]"></ElInput>
  16. <div v-if="isTips" class="text-red-500 text-xs cursor-pointer pl-2"
  17. style="width: calc(50% - 5px); white-space: pre-wrap;">
  18. 注意:第一次登录请先<br>
  19. <span class="text-blue-500 text-xs cursor-pointer"
  20. @click="openQrCodeModal"> 绑定谷歌验证器</span>
  21. 再获取认证码
  22. </div>
  23. </div>
  24. </ElFormItem>
  25. </ElForm>
  26. <el-button class="mt-8 w-full !block" type="primary" @click="login" :loading="loading">登录</el-button>
  27. </ElCard>
  28. <div class="fixed top-0 right-0 px-4 py-4">
  29. <DarkSwitch />
  30. </div>
  31. </ElMain>
  32. </ElContainer>
  33. <ElDialog
  34. v-model="qrCodeModalVisible"
  35. width="350"
  36. center
  37. >
  38. <template #title>
  39. <div class="text-left">
  40. <div class="text-lg">
  41. 绑定谷歌验证器
  42. </div>
  43. <div class="text-xs">
  44. 请使用该App扫描下方二维码
  45. <a href="https://apkpure.com/cn/google-authenticator/com.google.android.apps.authenticator2/download"
  46. target="_blank" class="text-blue-500">(点击下载)</a>
  47. </div>
  48. </div>
  49. </template>
  50. <div v-if="qrCode">
  51. <img :src="qrCode" alt="Google Authenticator QR Code"
  52. style="width: 260px; height: 260px;"
  53. class="mx-auto" />
  54. </div>
  55. <div v-else>
  56. 正在加载二维码...
  57. </div>
  58. <div class="text-center text-xs">
  59. app出现认证码后,点击按钮返回登录页面输入认证码
  60. </div>
  61. <div>
  62. <el-button
  63. class="custom-button"
  64. @click="qrCodeModalVisible = false"
  65. >
  66. 确认绑定
  67. </el-button>
  68. </div>
  69. </ElDialog>
  70. </template>
  71. <script setup>
  72. import { ref } from 'vue'
  73. import DarkSwitch from '@/components/DarkSwitch.vue'
  74. import { http } from '@/plugins/http'
  75. import { useUserStore } from '@/stores/user'
  76. import { storeToRefs } from 'pinia'
  77. import { useRouter } from 'vue-router'
  78. import { ElMessage } from 'element-plus'
  79. import QRCode from 'qrcode'
  80. const bg = new URL(`../assets/${import.meta.env.VITE_BG}`, import.meta.url)
  81. const title = '47.98.193.71' === location.host ? '悟空传媒' : import.meta.env.VITE_TITLE
  82. const router = useRouter()
  83. const { user, setUser } = storeToRefs(useUserStore())
  84. const model = ref({
  85. username: '',
  86. password: '',
  87. code: ''
  88. })
  89. const rules = {
  90. username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
  91. password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
  92. code: [{ required: true, message: '请输入6位认证码', trigger: 'blur' }]
  93. }
  94. const form = ref(null)
  95. const loading = ref(false)
  96. function login() {
  97. form.value.validate().then(async () => {
  98. loading.value = true
  99. try {
  100. const res = await http.post(process.env.VITE_LOGIN_URL, model.value)
  101. http.setToken(res.access_token)
  102. router.replace({ name: 'home' })
  103. loading.value = false
  104. } catch (e) {
  105. loading.value = false
  106. ElMessage.error(e.message)
  107. }
  108. })
  109. }
  110. const isTips = ref(true)
  111. const qrCodeModalVisible = ref(false)
  112. const qrCode = ref(null)
  113. async function openQrCodeModal() {
  114. try {
  115. const url = await http.post('/auth/binding', model.value)
  116. if (url === 'success') {
  117. ElMessage.success('您已绑定过谷歌验证器,无需再次绑定.')
  118. } else {
  119. qrCodeModalVisible.value = true
  120. qrCode.value = null
  121. qrCode.value = await QRCode.toDataURL(url)
  122. }
  123. } catch (e) {
  124. ElMessage.error('获取二维码失败')
  125. }
  126. }
  127. </script>
  128. <style scoped>
  129. .custom-button {
  130. width: 260px;
  131. background-color: #2f86ea;
  132. color: white;
  133. text-align: center;
  134. display: block;
  135. margin: 15px auto 0;
  136. }
  137. .custom-button:hover {
  138. background-color: #197ced;
  139. }
  140. </style>