Quellcode durchsuchen

feat(LoginView): 增加谷歌验证器绑定功能

- 在登录页面添加认证码输入框和绑定提示
- 实现打开二维码模态框的功能
- 优化登录逻辑,支持谷歌验证器认证
- 移除了一些不必要的控制台日志输出
wuyi vor 1 Jahr
Ursprung
Commit
1a3e3c51df
3 geänderte Dateien mit 95 neuen und 5 gelöschten Zeilen
  1. 1 0
      package.json
  2. 0 3
      src/views/DealerView.vue
  3. 94 2
      src/views/LoginView.vue

+ 1 - 0
package.json

@@ -24,6 +24,7 @@
     "moment": "^2.30.1",
     "moment": "^2.30.1",
     "moment-timezone": "^0.5.45",
     "moment-timezone": "^0.5.45",
     "pinia": "^2.2.1",
     "pinia": "^2.2.1",
+    "qrcode": "^1.5.4",
     "qs": "^6.13.0",
     "qs": "^6.13.0",
     "resolve-url": "^0.2.1",
     "resolve-url": "^0.2.1",
     "socket.io-client": "^4.7.5",
     "socket.io-client": "^4.7.5",

+ 0 - 3
src/views/DealerView.vue

@@ -325,8 +325,6 @@ const rate = async (userId) => {
         inputPattern: /^\d+(\.\d{1,2})?$/,
         inputPattern: /^\d+(\.\d{1,2})?$/,
         inputErrorMessage: '费率不能超出俩位小数!'
         inputErrorMessage: '费率不能超出俩位小数!'
     }).then(async ({ value }) => {
     }).then(async ({ value }) => {
-        console.log(userId)
-        console.log(value)
         const url = '/balance/updateRate/' + userId + '/' + value
         const url = '/balance/updateRate/' + userId + '/' + value
         await http.get(url)
         await http.get(url)
         table.value.refresh()
         table.value.refresh()
@@ -437,7 +435,6 @@ function passwordEdit(row) {
 }
 }
 
 
 async function changePassword() {
 async function changePassword() {
-    console.log('passwordValue', passwordValue.value)
     try {
     try {
         await http.post('/users/adminUpdatePassword', {
         await http.post('/users/adminUpdatePassword', {
             userId: passwordValue.value.id,
             userId: passwordValue.value.id,

+ 94 - 2
src/views/LoginView.vue

@@ -10,6 +10,18 @@
                     <ElFormItem prop="username" label="密码">
                     <ElFormItem prop="username" label="密码">
                         <ElInput type="password" v-model="model.password"></ElInput>
                         <ElInput type="password" v-model="model.password"></ElInput>
                     </ElFormItem>
                     </ElFormItem>
+                    <ElFormItem prop="code" label="认证码">
+                        <div class="flex items-center justify-between w-full">
+                            <ElInput v-model="model.code" maxlength="6" class="max-w-[55%]"></ElInput>
+                            <div v-if="isTips" class="text-red-500 text-xs cursor-pointer pl-2"
+                                 style="width: calc(50% - 5px); white-space: pre-wrap;">
+                                注意:第一次登录请先<br>
+                                <span class="text-blue-500 text-xs cursor-pointer"
+                                      @click="openQrCodeModal"> 绑定谷歌验证器</span>
+                                再获取认证码
+                            </div>
+                        </div>
+                    </ElFormItem>
                 </ElForm>
                 </ElForm>
                 <el-button class="mt-8 w-full !block" type="primary" @click="login" :loading="loading">登录</el-button>
                 <el-button class="mt-8 w-full !block" type="primary" @click="login" :loading="loading">登录</el-button>
             </ElCard>
             </ElCard>
@@ -18,6 +30,46 @@
             </div>
             </div>
         </ElMain>
         </ElMain>
     </ElContainer>
     </ElContainer>
+
+    <ElDialog
+        v-model="qrCodeModalVisible"
+        width="350"
+        center
+    >
+        <template #title>
+            <div class="text-left">
+                <div class="text-lg">
+                    绑定谷歌验证器
+                </div>
+                <div class="text-xs">
+                    请使用该App扫描下方二维码
+                    <a href="https://apkpure.com/cn/google-authenticator/com.google.android.apps.authenticator2/download"
+                       target="_blank" class="text-blue-500">(点击下载)</a>
+                </div>
+            </div>
+        </template>
+
+        <div v-if="qrCode">
+            <img :src="qrCode" alt="Google Authenticator QR Code"
+                 style="width: 260px; height: 260px;"
+                 class="mx-auto" />
+        </div>
+        <div v-else>
+            正在加载二维码...
+        </div>
+        <div class="text-center text-xs">
+            app出现认证码后,点击按钮返回登录页面输入认证码
+        </div>
+        <div>
+            <el-button
+                class="custom-button"
+                @click="qrCodeModalVisible = false"
+            >
+                确认绑定
+            </el-button>
+        </div>
+    </ElDialog>
+
 </template>
 </template>
 <script setup>
 <script setup>
 import { ref } from 'vue'
 import { ref } from 'vue'
@@ -27,6 +79,7 @@ import { useUserStore } from '@/stores/user'
 import { storeToRefs } from 'pinia'
 import { storeToRefs } from 'pinia'
 import { useRouter } from 'vue-router'
 import { useRouter } from 'vue-router'
 import { ElMessage } from 'element-plus'
 import { ElMessage } from 'element-plus'
+import QRCode from 'qrcode'
 
 
 const bg = new URL(`../assets/${import.meta.env.VITE_BG}`, import.meta.url)
 const bg = new URL(`../assets/${import.meta.env.VITE_BG}`, import.meta.url)
 const title = '47.98.193.71' === location.host ? '悟空传媒' : import.meta.env.VITE_TITLE
 const title = '47.98.193.71' === location.host ? '悟空传媒' : import.meta.env.VITE_TITLE
@@ -34,14 +87,17 @@ const router = useRouter()
 const { user, setUser } = storeToRefs(useUserStore())
 const { user, setUser } = storeToRefs(useUserStore())
 const model = ref({
 const model = ref({
     username: '',
     username: '',
-    password: ''
+    password: '',
+    code: ''
 })
 })
 const rules = {
 const rules = {
     username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
     username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
-    password: [{ required: true, message: '请输入密码', trigger: 'blur' }]
+    password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
+    code: [{ required: true, message: '请输入6位认证码', trigger: 'blur' }]
 }
 }
 const form = ref(null)
 const form = ref(null)
 const loading = ref(false)
 const loading = ref(false)
+
 function login() {
 function login() {
     form.value.validate().then(async () => {
     form.value.validate().then(async () => {
         loading.value = true
         loading.value = true
@@ -56,4 +112,40 @@ function login() {
         }
         }
     })
     })
 }
 }
+
+const isTips = ref(true)
+
+const qrCodeModalVisible = ref(false)
+const qrCode = ref(null)
+
+async function openQrCodeModal() {
+
+    try {
+        const url = await http.post('/auth/binding', model.value)
+        if (url === 'success') {
+            ElMessage.success('您已绑定过谷歌验证器,无需再次绑定.')
+        } else {
+            qrCodeModalVisible.value = true
+            qrCode.value = null
+            qrCode.value = await QRCode.toDataURL(url)
+        }
+    } catch (e) {
+        ElMessage.error('获取二维码失败')
+    }
+}
 </script>
 </script>
+
+<style scoped>
+.custom-button {
+    width: 260px;
+    background-color: #2f86ea;
+    color: white;
+    text-align: center;
+    display: block;
+    margin: 15px auto 0;
+}
+
+.custom-button:hover {
+    background-color: #197ced;
+}
+</style>