Просмотр исходного кода

Merge branch 'master' into changeOwnPasswd

Tao Chen 1 год назад
Родитель
Сommit
53bcc305e3

+ 9 - 1
src/api/user.js

@@ -79,6 +79,14 @@ export function groupUsers (data) {
79 79
   return request({
80 80
     url: '/user/groupUsers',
81 81
     method: 'post',
82
-    data
82
+    data,
83
+  })
84
+}
85
+
86
+export function register (data) {
87
+  return request({
88
+    url: '/user/register',
89
+    method: 'post',
90
+    data,
83 91
   })
84 92
 }

+ 1 - 1
src/permission.js

@@ -10,7 +10,7 @@ import { T } from '@/utils/i18n'
10 10
 
11 11
 NProgress.configure({ showSpinner: false }) // NProgress Configuration
12 12
 
13
-const whiteList = ['/login']
13
+const whiteList = ['/login', '/register']
14 14
 const routeStore = useRouteStore(pinia)
15 15
 const appStore = useAppStore(pinia)
16 16
 router.beforeEach(async (to, from, next) => {

+ 8 - 1
src/router/index.js

@@ -7,7 +7,12 @@ const constantRoutes = [
7 7
     meta: { title: 'Login' },
8 8
     component: () => import('@/views/login/login.vue'),
9 9
   },
10
-
10
+  {
11
+    path: '/register',
12
+    name: 'Register',
13
+    meta: { title: 'Register' },
14
+    component: () => import('@/views/register/index.vue'),
15
+  },
11 16
   {
12 17
     path: '/404',
13 18
     component: () => import('@/views/error-page/404.vue'),
@@ -15,11 +20,13 @@ const constantRoutes = [
15 20
   },
16 21
   {
17 22
     path: '/oauth/:code',
23
+    meta: { title: 'OauthLogin' },
18 24
     component: () => import('@/views/oauth/login.vue'),
19 25
     hidden: true,
20 26
   },
21 27
   {
22 28
     path: '/oauth/bind/:code',
29
+    meta: { title: 'OauthBind' },
23 30
     component: () => import('@/views/oauth/bind.vue'),
24 31
     hidden: true,
25 32
   },

+ 9 - 0
src/utils/i18n/en.json

@@ -438,5 +438,14 @@
438 438
   },
439 439
   "LastOnlineIp": {
440 440
     "One": "Last Online Ip"
441
+  },
442
+  "ConfirmPassword": {
443
+    "One": "Confirm Password"
444
+  },
445
+  "PasswordNotMatchConfirmPassword": {
446
+    "One": "Password not match Confirm Password"
447
+  },
448
+  "ToLogin": {
449
+    "One": "To Login"
441 450
   }
442 451
 }

+ 9 - 0
src/utils/i18n/ko.json

@@ -424,5 +424,14 @@
424 424
   },
425 425
   "LastOnlineIp": {
426 426
     "One": "마지막 온라인 IP"
427
+  },
428
+  "ConfirmPassword": {
429
+    "One": "비밀번호 확인"
430
+  },
431
+  "PasswordNotMatchConfirmPassword": {
432
+    "One": "비밀번호가 확인 비밀번호와 일치하지 않습니다"
433
+  },
434
+  "ToLogin": {
435
+    "One": "로그인하려면 클릭하십시오"
427 436
   }
428 437
 }

+ 9 - 0
src/utils/i18n/ru.json

@@ -438,6 +438,15 @@
438 438
   },
439 439
   "LastOnlineIp": {
440 440
     "One": "Последний IP онлайн"
441
+  },
442
+  "ConfirmPassword": {
443
+    "One": "Подтвердите пароль"
444
+  },
445
+  "PasswordNotMatchConfirmPassword": {
446
+    "One": "Пароль и подтверждение пароля не совпадают"
447
+  },
448
+  "ToLogin": {
449
+    "One": "Войти"
441 450
   }
442 451
 }
443 452
 

+ 8 - 3
src/utils/i18n/zh_CN.json

@@ -428,8 +428,7 @@
428 428
   "LastOnlineIp": {
429 429
     "One": "最后在线IP"
430 430
   },
431
-  "or login in with" :
432
-  {
431
+  "or login in with": {
433 432
     "One": "或使用以下登陆"
434 433
   },
435 434
   "Optional, default is" :
@@ -450,7 +449,13 @@
450 449
   "New PassWD": {
451 450
     "One": "新密码"
452 451
   },
453
-  "ConformPassWD": {
452
+  "ConfirmPassword": {
454 453
     "One": "确认密码"
454
+  },
455
+  "PasswordNotMatchConfirmPassword": {
456
+    "One": "密码与确认密码不匹配"
457
+  },
458
+  "ToLogin": {
459
+    "One": "去登录"
455 460
   }
456 461
 }

+ 15 - 17
src/views/login/login.vue

@@ -15,6 +15,7 @@
15 15
 
16 16
         <el-form-item>
17 17
           <el-button @click="login" type="primary" class="login-button">{{ T('Login') }}</el-button>
18
+          <el-button v-if="allowRegister" @click="register" class="login-button">{{ T('Register') }}</el-button>
18 19
         </el-form-item>
19 20
       </el-form>
20 21
 
@@ -26,7 +27,7 @@
26 27
         <div v-for="(option, index) in options" :key="index" class="oidc-option">
27 28
           <el-button @click="handleOIDCLogin(option.name)" class="oidc-btn">
28 29
             <img :src="getProviderImage(option.name)" alt="provider" class="oidc-icon" />
29
-            {{ T(option.name) }}
30
+            <span>{{ T(option.name) }}</span>
30 31
           </el-button>
31 32
         </div>
32 33
       </div>
@@ -103,23 +104,13 @@ const getProviderImage = (provider) => {
103 104
   return providerImageMap[provider] || providerImageMap.default;
104 105
 };
105 106
 
107
+const allowRegister = ref(false)
106 108
 const loadLoginOptions = async () => {
107 109
   try {
108
-    const res = await loginOptions().catch(() => []);
109
-    if (!Array.isArray(res) || !res.length) return console.warn('No valid response received');
110
-
111
-    const jsonPart = res[0].split('/')[1];
112
-    if (!jsonPart) return console.error('Invalid input string:', res[0]);
113
-
114
-    // const ops = JSON.parse(jsonPart).map(option => ({ name: option.name }));
115
-    // 不确定怎么处理webauth,不显示
116
-    // 解析 JSON,并过滤掉 "webauth" 类型的选项
117
-    const ops = JSON.parse(jsonPart)
118
-      .filter(option => option.name !== "webauth") // 排除 "webauth" 类型的选项
119
-      .map(option => ({ name: option.name })); // 创建新的对象数组
120
-    if (!ops.length) return;
121
-
122
-    options.push(...ops);
110
+    const res = await loginOptions().catch(_ => false);
111
+    if(!res || !res.data) return console.error('No valid response received');
112
+    res.data.ops.map(option => (options.push({ name: option }))); // 创建新的对象数组
113
+    allowRegister.value = res.data.register
123 114
   } catch (error) {
124 115
     console.error('Error loading login options:', error.message);
125 116
   }
@@ -141,6 +132,10 @@ onMounted(async () => {
141 132
     loadLoginOptions(); // 组件挂载后调用登录选项加载函数
142 133
   }
143 134
 });
135
+
136
+const register = () => {
137
+  router.push('/register')
138
+}
144 139
 </script>
145 140
 
146 141
 <style scoped lang="scss">
@@ -151,6 +146,7 @@ onMounted(async () => {
151 146
   height: 100vh;
152 147
   background-color: #2d3a4b;
153 148
   padding: 20px;
149
+  box-sizing: border-box;
154 150
 }
155 151
 
156 152
 .login-card {
@@ -180,6 +176,7 @@ h1 {
180 176
   width: 100%;
181 177
   height: 40px;
182 178
   margin-bottom: 20px;
179
+  margin-left: 0;
183 180
 }
184 181
 
185 182
 .divider {
@@ -230,6 +227,7 @@ h1 {
230 227
 .oidc-icon {
231 228
   width: 24px;
232 229
   height: 24px;
230
+  margin-right: 10px;
233 231
 }
234 232
 
235 233
 .login-logo {
@@ -255,4 +253,4 @@ h1 {
255 253
     }
256 254
   }
257 255
 }
258
-</style>
256
+</style>

+ 11 - 4
src/views/oauth/bind.vue

@@ -49,10 +49,17 @@
49 49
     const res = await bindConfirm({ code }).catch(_ => false)
50 50
     if (res) {
51 51
       resStatus.value = 1
52
-      ElMessage.success(T('OperationSuccessAndCloseAfter3Seconds'))
53
-      setTimeout(_ => {
54
-        out()
55
-      }, 3000)
52
+      if (res.data.device_type === 'webadmin') {
53
+        ElMessage.success(T('OperationSuccess'))
54
+        //后台登录
55
+        router.push('/')
56
+      } else {
57
+        ElMessage.success(T('OperationSuccessAndCloseAfter3Seconds'))
58
+        setTimeout(_ => {
59
+          out()
60
+        }, 3000)
61
+      }
62
+
56 63
     }
57 64
   }
58 65
   const out = () => {

+ 147 - 0
src/views/register/index.vue

@@ -0,0 +1,147 @@
1
+<template>
2
+  <div class="login-container">
3
+    <div class="login-card">
4
+      <img src="@/assets/logo.png" alt="logo" class="login-logo"/>
5
+      <el-form ref="f" :model="form" label-position="top" class="login-form" :rules="rules">
6
+        <el-form-item :label="T('Username')" prop="username">
7
+          <el-input v-model="form.username" class="login-input"></el-input>
8
+        </el-form-item>
9
+
10
+        <el-form-item :label="T('Password')" prop="password">
11
+          <el-input v-model="form.password" type="password" show-password
12
+                    class="login-input"></el-input>
13
+        </el-form-item>
14
+        <el-form-item :label="T('ConfirmPassword')" prop="confirm_password">
15
+          <el-input v-model="form.confirm_password" type="password" @keyup.enter.native="submit" show-password
16
+                    class="login-input"></el-input>
17
+        </el-form-item>
18
+        <el-form-item label="">
19
+          <el-button @click="submit" class="login-button" type="success">{{ T('Submit') }}</el-button>
20
+          <el-button @click="toLogin" class="login-button">{{ T('ToLogin') }}</el-button>
21
+        </el-form-item>
22
+      </el-form>
23
+    </div>
24
+  </div>
25
+</template>
26
+
27
+<script setup>
28
+  import { reactive, ref } from 'vue'
29
+  import { ElMessage } from 'element-plus'
30
+  import { T } from '@/utils/i18n'
31
+  import { useRoute, useRouter } from 'vue-router'
32
+  import { register } from '@/api/user'
33
+  import { useUserStore } from '@/store/user'
34
+
35
+  const router = useRouter()
36
+  const userStore = useUserStore()
37
+  const form = reactive({
38
+    username: '',
39
+    password: '',
40
+    confirm_password: '',
41
+  })
42
+  const rules = {
43
+    username: [
44
+      { required: true, message: T('ParamRequired', { param: T('Username') }), trigger: 'blur' },
45
+    ],
46
+    password: [
47
+      { required: true, message: T('ParamRequired', { param: T('Password') }), trigger: 'blur' },
48
+    ],
49
+    confirm_password: [
50
+      { required: true, message: T('ParamRequired', { param: T('ConfirmPassword') }), trigger: 'blur' },
51
+      {
52
+        validator: (rule, value, callback) => {
53
+          if (value !== form.password) {
54
+            callback(new Error(T('PasswordNotMatchConfirmPassword')))
55
+          } else {
56
+            callback()
57
+          }
58
+        }, trigger: 'blur',
59
+      },
60
+    ],
61
+  }
62
+  const f = ref(null)
63
+  const submit = async () => {
64
+    const v = await f.value.validate().catch(_ => false)
65
+    if (!v) {
66
+      return
67
+    }
68
+    const res = await register(form).catch(_ => false)
69
+    if (!res) {
70
+      return
71
+    }
72
+    userStore.saveUserData(res.data)
73
+    ElMessage.success('Submit')
74
+    router.push('/')
75
+  }
76
+  const toLogin = () => {
77
+    router.push('/login')
78
+
79
+  }
80
+</script>
81
+
82
+<style scoped lang="scss">
83
+.login-container {
84
+  display: flex;
85
+  justify-content: center;
86
+  align-items: center;
87
+  height: 100vh;
88
+  background-color: #2d3a4b;
89
+  padding: 20px;
90
+  box-sizing: border-box;
91
+}
92
+
93
+.login-card {
94
+  width: 360px;
95
+  background-color: #283342;
96
+  padding: 40px;
97
+  border-radius: 8px;
98
+  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
99
+  text-align: center;
100
+}
101
+
102
+h1 {
103
+  margin-bottom: 20px;
104
+  font-size: 24px;
105
+  font-weight: bold;
106
+}
107
+
108
+.login-form {
109
+  margin-bottom: 20px;
110
+}
111
+
112
+.login-input {
113
+  width: 100%;
114
+}
115
+
116
+.login-button {
117
+  width: 100%;
118
+  height: 40px;
119
+  margin-bottom: 20px;
120
+  margin-top: 20px;
121
+  margin-left: 0;
122
+}
123
+
124
+.login-logo {
125
+  width: 80px;
126
+  height: 80px;
127
+  margin: 0 auto 20px;
128
+  display: block;
129
+}
130
+
131
+.el-form-item {
132
+  ::v-deep(.el-form-item__label) {
133
+    color: #fff;
134
+  }
135
+
136
+  .el-input {
137
+    ::v-deep(.el-input__wrapper) {
138
+      border: 1px solid rgba(255, 255, 255, 0.1);
139
+      background: transparent;
140
+    }
141
+
142
+    ::v-deep(input) {
143
+      color: #fff;
144
+    }
145
+  }
146
+}
147
+</style>

+ 3 - 0
src/views/user/composables/index.js

@@ -91,6 +91,9 @@ export function useDel () {
91 91
     }
92 92
 
93 93
     const res = remove({ id }).catch(_ => false)
94
+    if (res) {
95
+      ElMessage.success(T('OperationSuccess'))
96
+    }
94 97
     return res
95 98
   }
96 99
   return {