ljw 1 год назад
Родитель
Сommit
c7f35f5044

+ 0 - 8
src/api/group.js

@@ -36,11 +36,3 @@ export function remove (data) {
36 36
     data,
37 37
   })
38 38
 }
39
-
40
-export function changePwd (data) {
41
-  return request({
42
-    url: '/group/changePwd',
43
-    method: 'post',
44
-    data,
45
-  })
46
-}

+ 16 - 0
src/api/login_log.js

@@ -0,0 +1,16 @@
1
+import request from '@/utils/request'
2
+
3
+export function list (params) {
4
+  return request({
5
+    url: '/login_log/list',
6
+    params,
7
+  })
8
+}
9
+
10
+export function remove (data) {
11
+  return request({
12
+    url: '/login_log/delete',
13
+    method: 'post',
14
+    data,
15
+  })
16
+}

+ 76 - 0
src/api/oauth.js

@@ -0,0 +1,76 @@
1
+import request from '@/utils/request'
2
+
3
+export function info (params) {
4
+  return request({
5
+    url: '/oauth/info',
6
+    params,
7
+  })
8
+
9
+}
10
+
11
+export function confirm (data) {
12
+  return request({
13
+    url: '/oauth/confirm',
14
+    method: 'post',
15
+    data,
16
+  })
17
+}
18
+
19
+export function bind (data) {
20
+  return request({
21
+    url: '/oauth/bind',
22
+    method: 'post',
23
+    data,
24
+  })
25
+}
26
+
27
+export function bindConfirm (data) {
28
+  return request({
29
+    url: '/oauth/bindConfirm',
30
+    method: 'post',
31
+    data,
32
+  })
33
+}
34
+export function unbind (data) {
35
+  return request({
36
+    url: '/oauth/unbind',
37
+    method: 'post',
38
+    data,
39
+  })
40
+}
41
+export function list (params) {
42
+  return request({
43
+    url: '/oauth/list',
44
+    params,
45
+  })
46
+}
47
+
48
+export function detail (id) {
49
+  return request({
50
+    url: `/oauth/detail/${id}`,
51
+  })
52
+}
53
+
54
+export function create (data) {
55
+  return request({
56
+    url: '/oauth/create',
57
+    method: 'post',
58
+    data,
59
+  })
60
+}
61
+
62
+export function update (data) {
63
+  return request({
64
+    url: '/oauth/update',
65
+    method: 'post',
66
+    data,
67
+  })
68
+}
69
+
70
+export function remove (data) {
71
+  return request({
72
+    url: '/oauth/delete',
73
+    method: 'post',
74
+    data,
75
+  })
76
+}

+ 0 - 8
src/api/peer.js

@@ -36,11 +36,3 @@ export function remove (data) {
36 36
     data,
37 37
   })
38 38
 }
39
-
40
-export function changePwd (data) {
41
-  return request({
42
-    url: '/peer/changePwd',
43
-    method: 'post',
44
-    data,
45
-  })
46
-}

+ 0 - 7
src/api/tag.js

@@ -37,10 +37,3 @@ export function remove (data) {
37 37
   })
38 38
 }
39 39
 
40
-export function changePwd (data) {
41
-  return request({
42
-    url: '/tag/changePwd',
43
-    method: 'post',
44
-    data,
45
-  })
46
-}

+ 7 - 0
src/api/user.js

@@ -67,3 +67,10 @@ export function changeCurPwd (data) {
67 67
     data,
68 68
   })
69 69
 }
70
+
71
+export function myOauth () {
72
+  return request({
73
+    url: '/user/myOauth',
74
+    method: 'post',
75
+  })
76
+}

+ 115 - 0
src/components/changePwdDialog.vue

@@ -0,0 +1,115 @@
1
+<template>
2
+  <el-dialog v-model="visible" width="50%">
3
+    <el-form ref="cpwd" :model="changePwdForm" :rules="chagePwdRules" label-width="120px" style="margin-top: 20px">
4
+      <el-form-item label="旧密码" prop="old_password">
5
+        <el-input v-model="changePwdForm.old_password" show-password></el-input>
6
+      </el-form-item>
7
+      <el-form-item label="新密码" prop="new_password">
8
+        <el-input v-model="changePwdForm.new_password" show-password></el-input>
9
+      </el-form-item>
10
+      <el-form-item label="确认密码" prop="confirmPwd">
11
+        <el-input v-model="changePwdForm.confirmPwd" show-password></el-input>
12
+      </el-form-item>
13
+      <el-form-item>
14
+        <el-button @click="cancelChangePwd">取消</el-button>
15
+        <el-button type="primary" @click="changePassword">确定</el-button>
16
+      </el-form-item>
17
+    </el-form>
18
+  </el-dialog>
19
+</template>
20
+
21
+<script setup>
22
+
23
+  import { reactive, ref } from 'vue'
24
+  import { ElMessageBox } from 'element-plus'
25
+  import { changeCurPwd } from '@/api/user'
26
+  import { useUserStore } from '@/store/user'
27
+
28
+  const props = defineProps({
29
+    visible: Boolean,
30
+  })
31
+
32
+  const emit = defineEmits(['update:visible'])
33
+
34
+  // Watch for changes to the prop and emit an event if necessary
35
+  // watch(() => props.visible, (newVal) => {
36
+  //   emit('update:visible', newVal);
37
+  // });
38
+  const showChangePwd = () => {
39
+    emit('update:visible', true)
40
+    changePwdForm.old_password = ''
41
+    changePwdForm.new_password = ''
42
+    changePwdForm.confirmPwd = ''
43
+  }
44
+  const changePwdForm = reactive({
45
+    old_password: '',
46
+    new_password: '',
47
+    confirmPwd: '',
48
+  })
49
+  const chagePwdRules = reactive({
50
+    old_password: [{ required: true, message: '请输入旧密码', trigger: 'blur' }],
51
+    new_password: [
52
+      { required: true, message: '请输入新密码', trigger: 'blur' },
53
+      {
54
+        validator: (rule, value, callback) => {
55
+          if (value === changePwdForm.old_password) {
56
+            callback(new Error('新密码不能与旧密码相同'))
57
+          } else {
58
+            callback()
59
+          }
60
+        },
61
+        trigger: 'blur',
62
+      }],
63
+    confirmPwd: [
64
+      { required: true, message: '请再次输入新密码', trigger: 'blur' },
65
+      {
66
+        validator: (rule, value, callback) => {
67
+          if (value !== changePwdForm.new_password) {
68
+            callback(new Error('两次输入密码不一致'))
69
+          } else {
70
+            callback()
71
+          }
72
+        },
73
+        trigger: 'blur',
74
+      },
75
+    ],
76
+  })
77
+  const cpwd = ref(null)
78
+  const cancelChangePwd = () => {
79
+    emit('update:visible', false)
80
+  }
81
+
82
+  const userStore = useUserStore()
83
+
84
+  const changePassword = async () => {
85
+    //验证
86
+    const valid = await cpwd.value.validate().catch(_ => false)
87
+    if (!valid) {
88
+      return
89
+    }
90
+    console.log('changePassword')
91
+    const confirm = await ElMessageBox.confirm('确定修改密码么?', {
92
+      confirmButtonText: '确定',
93
+      cancelButtonText: '取消',
94
+    }).catch(_ => false)
95
+    if (!confirm) {
96
+      return
97
+    }
98
+    const res = await changeCurPwd(changePwdForm).catch(_ => false)
99
+    if (!res) {
100
+      return
101
+    }
102
+    ElMessageBox.alert('修改成功', '修改密码', {
103
+      autofocus: true,
104
+      confirmButtonText: 'OK',
105
+      callback: (action) => {
106
+        userStore.logout()
107
+        window.location.reload()
108
+      },
109
+    })
110
+  }
111
+</script>
112
+
113
+<style scoped lang="scss">
114
+
115
+</style>

+ 3 - 83
src/layout/components/setting/index.vue

@@ -16,31 +16,14 @@
16 16
         </el-dropdown-menu>
17 17
       </template>
18 18
     </el-dropdown>
19
-    <el-dialog v-model="changePwdVisible" width="50%">
20
-      <el-form ref="cpwd" :model="changePwdForm" :rules="chagePwdRules" label-width="120px" style="margin-top: 20px">
21
-        <el-form-item label="旧密码" prop="old_password">
22
-          <el-input v-model="changePwdForm.old_password" show-password></el-input>
23
-        </el-form-item>
24
-        <el-form-item label="新密码" prop="new_password">
25
-          <el-input v-model="changePwdForm.new_password" show-password></el-input>
26
-        </el-form-item>
27
-        <el-form-item label="确认密码" prop="confirmPwd">
28
-          <el-input v-model="changePwdForm.confirmPwd" show-password></el-input>
29
-        </el-form-item>
30
-        <el-form-item>
31
-          <el-button @click="changePwdVisible=false">取消</el-button>
32
-          <el-button type="primary" @click="changePassword">确定</el-button>
33
-        </el-form-item>
34
-      </el-form>
35
-    </el-dialog>
19
+    <changePwdDialog v-model:visible="changePwdVisible"></changePwdDialog>
36 20
   </div>
37 21
 </template>
38 22
 
39 23
 <script setup>
40 24
   import { useUserStore } from '@/store/user'
41
-  import { changeCurPwd } from '@/api/user'
42
-  import { ElMessage, ElMessageBox } from 'element-plus'
43
-  import { reactive, ref } from 'vue'
25
+  import changePwdDialog from '@/components/changePwdDialog.vue'
26
+  import { ref } from 'vue'
44 27
 
45 28
   const userStore = useUserStore()
46 29
   const user = userStore
@@ -53,69 +36,6 @@
53 36
   const changePwdVisible = ref(false)
54 37
   const showChangePwd = () => {
55 38
     changePwdVisible.value = true
56
-    changePwdForm.old_password = ''
57
-    changePwdForm.new_password = ''
58
-    changePwdForm.confirmPwd = ''
59
-  }
60
-  const changePwdForm = reactive({
61
-    old_password: '',
62
-    new_password: '',
63
-    confirmPwd: '',
64
-  })
65
-  const chagePwdRules = reactive({
66
-    old_password: [{ required: true, message: '请输入旧密码', trigger: 'blur' }],
67
-    new_password: [
68
-      { required: true, message: '请输入新密码', trigger: 'blur' },
69
-      {
70
-        validator: (rule, value, callback) => {
71
-          if (value === changePwdForm.old_password) {
72
-            callback(new Error('新密码不能与旧密码相同'))
73
-          } else {
74
-            callback()
75
-          }
76
-        },
77
-        trigger: 'blur',
78
-      }],
79
-    confirmPwd: [
80
-      { required: true, message: '请再次输入新密码', trigger: 'blur' },
81
-      {
82
-        validator: (rule, value, callback) => {
83
-          if (value !== changePwdForm.new_password) {
84
-            callback(new Error('两次输入密码不一致'))
85
-          } else {
86
-            callback()
87
-          }
88
-        },
89
-        trigger: 'blur',
90
-      },
91
-    ],
92
-  })
93
-  const cpwd = ref(null)
94
-  const changePassword = async () => {
95
-    //验证
96
-    const valid = await cpwd.value.validate().catch(_ => false)
97
-    if (!valid) {
98
-      return
99
-    }
100
-    console.log('changePassword')
101
-    const confirm = await ElMessageBox.confirm('确定修改密码么?', {
102
-      confirmButtonText: '确定',
103
-      cancelButtonText: '取消',
104
-    }).catch(_ => false)
105
-    if (!confirm) {
106
-      return
107
-    }
108
-    const res = await changeCurPwd(changePwdForm).catch(_ => false)
109
-    if (!res) {
110
-      return
111
-    }
112
-    ElMessageBox.alert('修改成功', '修改密码', {
113
-      autofocus: true,
114
-      confirmButtonText: 'OK',
115
-      callback: (action) => {
116
-        logout()
117
-      },
118
-    })
119 39
   }
120 40
 </script>
121 41
 

+ 30 - 4
src/router/index.js

@@ -13,7 +13,16 @@ const constantRoutes = [
13 13
     component: () => import('@/views/error-page/404.vue'),
14 14
     hidden: true,
15 15
   },
16
-
16
+  {
17
+    path: '/oauth/:code',
18
+    component: () => import('@/views/oauth/login.vue'),
19
+    hidden: true,
20
+  },
21
+  {
22
+    path: '/oauth/bind/:code',
23
+    component: () => import('@/views/oauth/bind.vue'),
24
+    hidden: true,
25
+  },
17 26
 ]
18 27
 export const asyncRoutes = [
19 28
   // {
@@ -35,18 +44,24 @@ export const asyncRoutes = [
35 44
   {
36 45
     path: '/my',
37 46
     name: 'My',
38
-    redirect: '/my/tag/index',
47
+    redirect: '/my/info',
39 48
     meta: { title: '我的', icon: 'UserFilled' },
40 49
     component: () => import('@/layout/index.vue'),
41 50
     children: [
42 51
       {
43 52
         path: '/',
53
+        name: 'MyInfo',
54
+        meta: { title: '个人信息', icon: 'User' /*keepAlive: true*/ },
55
+        component: () => import('@/views/my/info.vue'),
56
+      },
57
+      {
58
+        path: 'address_book',
44 59
         name: 'MyAddressBookList',
45 60
         meta: { title: '地址簿管理', icon: 'Notebook' /*keepAlive: true*/ },
46 61
         component: () => import('@/views/my/address_book/index.vue'),
47 62
       },
48 63
       {
49
-        path: 'tag/index',
64
+        path: 'tag',
50 65
         name: 'MyTagList',
51 66
         meta: { title: '标签管理', icon: 'CollectionTag' /*keepAlive: true*/ },
52 67
         component: () => import('@/views/my/tag/index.vue'),
@@ -103,7 +118,18 @@ export const asyncRoutes = [
103 118
         meta: { title: '标签管理', icon: 'CollectionTag' /*keepAlive: true*/ },
104 119
         component: () => import('@/views/tag/index.vue'),
105 120
       },
106
-
121
+      {
122
+        path: '/oauth',
123
+        name: 'Oauth',
124
+        meta: { title: 'Oauth管理', icon: 'Link' /*keepAlive: true*/ },
125
+        component: () => import('@/views/oauth/index.vue'),
126
+      },
127
+      {
128
+        path: '/loginLog',
129
+        name: 'LoginLog',
130
+        meta: { title: '登录日志', icon: 'List' /*keepAlive: true*/ },
131
+        component: () => import('@/views/login/log.vue'),
132
+      },
107 133
     ],
108 134
   },
109 135
 ]

+ 61 - 0
src/views/login/log.js

@@ -0,0 +1,61 @@
1
+import { reactive } from 'vue'
2
+import { list, remove } from '@/api/login_log'
3
+import { ElMessage, ElMessageBox } from 'element-plus'
4
+import { useRoute } from 'vue-router'
5
+
6
+export function useRepositories () {
7
+  const route = useRoute()
8
+  const user_id = route.query?.user_id
9
+
10
+  const listRes = reactive({
11
+    list: [], total: 0, loading: false,
12
+  })
13
+  const listQuery = reactive({
14
+    page: 1,
15
+    page_size: 10,
16
+    is_my: 0,
17
+    user_id: user_id ? parseInt(user_id) : null,
18
+  })
19
+
20
+  const getList = async () => {
21
+    listRes.loading = true
22
+    const res = await list(listQuery).catch(_ => false)
23
+    listRes.loading = false
24
+    if (res) {
25
+      listRes.list = res.data.list
26
+      listRes.total = res.data.total
27
+    }
28
+  }
29
+  const handlerQuery = () => {
30
+    if (listQuery.page === 1) {
31
+      getList()
32
+    } else {
33
+      listQuery.page = 1
34
+    }
35
+  }
36
+
37
+  const del = async (row) => {
38
+    const cf = await ElMessageBox.confirm('确定删除么?', {
39
+      confirmButtonText: '确定',
40
+      cancelButtonText: '取消',
41
+      type: 'warning',
42
+    }).catch(_ => false)
43
+    if (!cf) {
44
+      return false
45
+    }
46
+
47
+    const res = await remove({ id: row.id }).catch(_ => false)
48
+    if (res) {
49
+      ElMessage.success('操作成功')
50
+      getList()
51
+    }
52
+  }
53
+
54
+  return {
55
+    listRes,
56
+    listQuery,
57
+    getList,
58
+    handlerQuery,
59
+    del,
60
+  }
61
+}

+ 86 - 0
src/views/login/log.vue

@@ -0,0 +1,86 @@
1
+<template>
2
+  <div>
3
+    <el-card class="list-query" shadow="hover">
4
+      <el-form inline label-width="60px">
5
+        <el-form-item label="用户">
6
+          <el-select v-model="listQuery.user_id" clearable>
7
+            <el-option
8
+                v-for="item in allUsers"
9
+                :key="item.id"
10
+                :label="item.username"
11
+                :value="item.id"
12
+            ></el-option>
13
+          </el-select>
14
+        </el-form-item>
15
+        <el-form-item>
16
+          <el-button type="primary" @click="handlerQuery">筛选</el-button>
17
+          <el-button type="danger" @click="toAdd">添加</el-button>
18
+        </el-form-item>
19
+      </el-form>
20
+    </el-card>
21
+    <el-card class="list-body" shadow="hover">
22
+      <!--      <el-tag type="danger" style="margin-bottom: 10px">不建议在此操作地址簿,可能会造成数据不同步</el-tag>-->
23
+      <el-table :data="listRes.list" v-loading="listRes.loading" border>
24
+        <el-table-column prop="id" label="id" align="center" width="100"/>
25
+        <el-table-column label="所属用户" align="center" width="120">
26
+          <template #default="{row}">
27
+            <span v-if="row.user_id"> <el-tag>{{ allUsers?.find(u => u.id === row.user_id)?.username }}</el-tag> </span>
28
+          </template>
29
+        </el-table-column>
30
+        <el-table-column prop="client" label="client" align="center" width="120"/>
31
+        <el-table-column prop="uuid" label="uuid" align="center"/>
32
+        <el-table-column prop="ip" label="ip" align="center" width="150"/>
33
+        <el-table-column prop="type" label="type" align="center" width="100"/>
34
+        <el-table-column prop="platform" label="platform" align="center" width="120"/>
35
+        <el-table-column prop="created_at" label="时间" align="center"/>
36
+        <el-table-column label="操作" align="center" width="400">
37
+          <template #default="{row}">
38
+            <el-button type="danger" @click="del(row)">删除</el-button>
39
+          </template>
40
+        </el-table-column>
41
+      </el-table>
42
+    </el-card>
43
+    <el-card class="list-page" shadow="hover">
44
+      <el-pagination background
45
+                     layout="prev, pager, next, sizes, jumper"
46
+                     :page-sizes="[10,20,50,100]"
47
+                     v-model:page-size="listQuery.page_size"
48
+                     v-model:current-page="listQuery.page"
49
+                     :total="listRes.total">
50
+      </el-pagination>
51
+    </el-card>
52
+  </div>
53
+</template>
54
+
55
+<script setup>
56
+  import { onActivated, onMounted, watch } from 'vue'
57
+  import { loadAllUsers } from '@/global'
58
+  import { useRepositories } from '@/views/login/log.js'
59
+
60
+  const { allUsers, getAllUsers } = loadAllUsers()
61
+  getAllUsers()
62
+
63
+  const {
64
+    listRes,
65
+    listQuery,
66
+    getList,
67
+    handlerQuery,
68
+    del,
69
+  } = useRepositories()
70
+
71
+  onMounted(getList)
72
+  onActivated(getList)
73
+
74
+  watch(() => listQuery.page, getList)
75
+
76
+  watch(() => listQuery.page_size, handlerQuery)
77
+
78
+</script>
79
+
80
+<style scoped lang="scss">
81
+.list-query .el-select {
82
+  --el-select-width: 160px;
83
+}
84
+
85
+
86
+</style>

+ 44 - 29
src/views/login/login.vue

@@ -28,9 +28,22 @@
28 28
       const userStore = useUserStore()
29 29
       const route = useRoute()
30 30
       const router = useRouter()
31
+
32
+      let platform = window.navigator.platform
33
+      if (navigator.platform.indexOf('Mac') === 0) {
34
+        platform = 'mac'
35
+      } else if (navigator.platform.indexOf('Win') === 0) {
36
+        platform = 'windows'
37
+      } else if (navigator.platform.indexOf('Linux armv') === 0) {
38
+        platform = 'android'
39
+      } else if (navigator.platform.indexOf('Linux') === 0) {
40
+        platform = 'linux'
41
+      }
42
+
31 43
       const form = reactive({
32 44
         username: '',
33 45
         password: '',
46
+        platform: platform,
34 47
       })
35 48
       const redirect = route.query?.redirect
36 49
       const login = async () => {
@@ -50,42 +63,44 @@
50 63
 </script>
51 64
 
52 65
 <style scoped lang="scss">
53
-  .login {
54
-    width: 100vw;
55
-    height: 100vh;
56
-    background-color: #2d3a4b;
57
-    padding-top: 200px;
58
-    box-sizing: border-box;
59
-    .tips {
60
-      font-size: 12px;
61
-      color: #fff;
62
-      margin-left: 60px;
63
-    }
66
+.login {
67
+  width: 100vw;
68
+  height: 100vh;
69
+  background-color: #2d3a4b;
70
+  padding-top: 25vh;
71
+  box-sizing: border-box;
64 72
 
65
-    .login-card {
66
-      width: 500px;
67
-      background-color: #283342;
68
-      color: #fff;
69
-      border: none;
70
-      margin: 0 auto;
71
-      .el-form-item {
73
+  .tips {
74
+    font-size: 12px;
75
+    color: #fff;
76
+    margin-left: 60px;
77
+  }
72 78
 
73
-        ::v-deep(.el-form-item__label) {
74
-          color: #fff;
75
-        }
79
+  .login-card {
80
+    max-width: 500px;
81
+    background-color: #283342;
82
+    color: #fff;
83
+    border: none;
84
+    margin: 0 auto;
85
+
86
+    .el-form-item {
87
+
88
+      ::v-deep(.el-form-item__label) {
89
+        color: #fff;
90
+      }
76 91
 
77
-        .el-input {
92
+      .el-input {
78 93
 
79
-          ::v-deep(.el-input__wrapper) {
80
-            border: 1px solid rgba(255, 255, 255, 0.1);
81
-            background: transparent;
82
-          }
94
+        ::v-deep(.el-input__wrapper) {
95
+          border: 1px solid rgba(255, 255, 255, 0.1);
96
+          background: transparent;
97
+        }
83 98
 
84
-          ::v-deep(input) {
85
-            color: #fff;
86
-          }
99
+        ::v-deep(input) {
100
+          color: #fff;
87 101
         }
88 102
       }
89 103
     }
90 104
   }
105
+}
91 106
 </style>

+ 86 - 0
src/views/my/info.vue

@@ -0,0 +1,86 @@
1
+<template>
2
+  <div>
3
+    <el-card title="个人信息">
4
+      <el-form class="info-form" ref="form" label-width="120px" label-suffix=":">
5
+        <el-form-item label="用户名">
6
+          <div>{{ userStore.username }}</div>
7
+        </el-form-item>
8
+        <el-form-item label="密码" prop="password">
9
+          <el-button type="danger" @click="showChangePwd">修改密码</el-button>
10
+        </el-form-item>
11
+        <el-form-item label="OIDC">
12
+          <el-table :data="oidcData" border fit>
13
+            <el-table-column label="平台" prop="third_type" align="center"></el-table-column>
14
+            <el-table-column label="状态" prop="status" align="center">
15
+              <template #default="{ row }">
16
+                <el-tag v-if="row.status === 1" type="success">已绑定</el-tag>
17
+                <el-tag v-else type="danger">未绑定</el-tag>
18
+              </template>
19
+            </el-table-column>
20
+            <el-table-column label="操作" align="center" width="200">
21
+              <template #default="{ row }">
22
+                <el-button v-if="row.status === 1" type="danger" size="small" @click="toUnBind(row)">解除绑定</el-button>
23
+                <el-button v-else type="success" size="small" @click="toBind(row)">去绑定</el-button>
24
+              </template>
25
+            </el-table-column>
26
+          </el-table>
27
+        </el-form-item>
28
+      </el-form>
29
+    </el-card>
30
+    <changePwdDialog v-model:visible="changePwdVisible"></changePwdDialog>
31
+  </div>
32
+</template>
33
+
34
+<script setup>
35
+  import changePwdDialog from '@/components/changePwdDialog.vue'
36
+  import { ref } from 'vue'
37
+  import { useUserStore } from '@/store/user'
38
+  import { bind, unbind } from '@/api/oauth'
39
+  import { myOauth } from '@/api/user'
40
+  import { ElMessageBox } from 'element-plus'
41
+
42
+  const userStore = useUserStore()
43
+  const changePwdVisible = ref(false)
44
+  const showChangePwd = () => {
45
+    changePwdVisible.value = true
46
+  }
47
+  const oidcData = ref([])
48
+  const getMyOauth = async () => {
49
+    const res = await myOauth().catch(_ => false)
50
+    if (res) {
51
+      oidcData.value = res.data
52
+    }
53
+
54
+  }
55
+  getMyOauth()
56
+  const toBind = async (row) => {
57
+    const res = await bind({ op: row.third_type }).catch(_ => false)
58
+    if (res) {
59
+      const { code, url } = res.data
60
+      window.open(url)
61
+    }
62
+  }
63
+  const toUnBind = async (row) => {
64
+    const cf = await ElMessageBox.confirm('确定解除绑定么?', {
65
+      confirmButtonText: '确定',
66
+      cancelButtonText: '取消',
67
+      type: 'warning',
68
+    }).catch(_ => false)
69
+    if (!cf) {
70
+      return false
71
+    }
72
+    const res = await unbind({ op: row.third_type }).catch(_ => false)
73
+    if (res) {
74
+      getMyOauth()
75
+    }
76
+
77
+  }
78
+</script>
79
+
80
+<style scoped lang="scss">
81
+.info-form {
82
+  width: 600px;
83
+  margin: 0 auto;
84
+
85
+}
86
+</style>

+ 94 - 0
src/views/oauth/bind.vue

@@ -0,0 +1,94 @@
1
+<template>
2
+  <div class="oauth">
3
+    <el-card class="card">
4
+      <h2>您正在授权绑定</h2>
5
+      <el-form class="info" label-width="100px">
6
+        <el-form-item label="平台">
7
+          <div class="impt">{{ oauthInfo.op }}</div>
8
+        </el-form-item>
9
+        <el-form-item label="用户名">
10
+          <div class="impt">{{ oauthInfo.third_name }}</div>
11
+        </el-form-item>
12
+        <el-form-item label-width="0">
13
+          <el-button style="width: 100%" v-if="!resStatus" type="success" size="large" @click="toConfirm">绑定</el-button>
14
+        </el-form-item>
15
+        <el-form-item label-width="0">
16
+          <el-button style="width: 100%" size="large" @click="out">关闭页面</el-button>
17
+        </el-form-item>
18
+      </el-form>
19
+      如果不是您操作的授权,请直接关闭页面
20
+    </el-card>
21
+  </div>
22
+</template>
23
+
24
+<script setup>
25
+  import { ref, onMounted } from 'vue'
26
+  import { info, confirm, bindConfirm } from '@/api/oauth'
27
+  import { useRoute, useRouter } from 'vue-router'
28
+  import { ElMessage } from 'element-plus'
29
+
30
+  const oauthInfo = ref({})
31
+  const route = useRoute()
32
+  const router = useRouter()
33
+  const code = route.params?.code
34
+  if (!code) {
35
+    router.push('/')
36
+  }
37
+  const getInfo = async () => {
38
+    const res = await info({ code }).catch(_ => false)
39
+    if (res) {
40
+      oauthInfo.value = res.data
41
+    } else {
42
+      router.push('/')
43
+    }
44
+  }
45
+  getInfo()
46
+  const resStatus = ref(0)
47
+  const toConfirm = async () => {
48
+    const res = await bindConfirm({ code }).catch(_ => false)
49
+    if (res) {
50
+      resStatus.value = 1
51
+      ElMessage.success('操作成功,3秒后将自动关闭本页面')
52
+      setTimeout(_ => {
53
+        out()
54
+      }, 3000)
55
+    }
56
+  }
57
+  const out = () => {
58
+    window.close()
59
+  }
60
+</script>
61
+
62
+<style scoped lang="scss">
63
+.oauth {
64
+  width: 100vw;
65
+  height: 100vh;
66
+  background-color: #2d3a4b;
67
+  padding-top: 25vh;
68
+  box-sizing: border-box;
69
+
70
+  .card {
71
+    max-width: 500px;
72
+    background-color: #283342;
73
+    color: #fff;
74
+    border: none;
75
+    margin: 0 auto;
76
+    text-align: center;
77
+
78
+    .info {
79
+      display: block;
80
+      line-height: 30px;
81
+      margin-bottom: 50px;
82
+
83
+      ::v-deep(.el-form-item__label) {
84
+        color: #fff;
85
+      }
86
+    }
87
+
88
+    .impt {
89
+      font-weight: bold;
90
+      font-size: 20px;
91
+    }
92
+  }
93
+}
94
+</style>

+ 177 - 0
src/views/oauth/index.vue

@@ -0,0 +1,177 @@
1
+<template>
2
+  <div>
3
+    <el-card class="list-query" shadow="hover">
4
+      <el-form inline label-width="60px">
5
+        <el-form-item>
6
+          <el-button type="primary" @click="handlerQuery">筛选</el-button>
7
+          <el-button type="danger" @click="toAdd">添加</el-button>
8
+        </el-form-item>
9
+      </el-form>
10
+    </el-card>
11
+    <el-card class="list-body" shadow="hover">
12
+      <el-table :data="listRes.list" v-loading="listRes.loading" border>
13
+        <el-table-column prop="id" label="id" align="center"/>
14
+        <el-table-column prop="op" label="名称" align="center"/>
15
+        <el-table-column prop="auto_register" label="自动注册" align="center"/>
16
+        <el-table-column prop="created_at" label="创建时间" align="center"/>
17
+        <el-table-column prop="updated_at" label="更新时间" align="center"/>
18
+        <el-table-column label="操作" align="center">
19
+          <template #default="{row}">
20
+            <el-button @click="toEdit(row)">编辑</el-button>
21
+            <el-button type="danger" @click="del(row)">删除</el-button>
22
+          </template>
23
+        </el-table-column>
24
+      </el-table>
25
+    </el-card>
26
+    <el-card class="list-page" shadow="hover">
27
+      <el-pagination background
28
+                     layout="prev, pager, next, sizes, jumper"
29
+                     :page-sizes="[10,20,50,100]"
30
+                     v-model:page-size="listQuery.page_size"
31
+                     v-model:current-page="listQuery.page"
32
+                     :total="listRes.total">
33
+      </el-pagination>
34
+    </el-card>
35
+    <el-dialog v-model="formVisible" :title="!formData.id?'创建':'修改'" width="800">
36
+      <el-form class="dialog-form" ref="form" :model="formData" :rules="rules" label-width="120px">
37
+        <el-form-item label="ClientId" prop="client_id">
38
+          <el-input v-model="formData.client_id"></el-input>
39
+        </el-form-item>
40
+        <el-form-item label="ClientSecret" prop="client_secret">
41
+          <el-input v-model="formData.client_secret"></el-input>
42
+        </el-form-item>
43
+        <el-form-item label="RedirectUrl" prop="redirect_url">
44
+          <el-input v-model="formData.redirect_url"></el-input>
45
+        </el-form-item>
46
+        <el-form-item label="类型" prop="op" >
47
+          <el-radio-group v-model="formData.op" :disabled="!!formData.id">
48
+            <el-radio v-for="item in ops" :key="item.value" :label="item.value" style="display: block">
49
+              {{ item.label }}
50
+            </el-radio>
51
+          </el-radio-group>
52
+        </el-form-item>
53
+        <el-form-item label="自动注册" prop="open_auto_register">
54
+          <el-switch v-model="formData.auto_register"
55
+                     :active-value="true"
56
+                     :inactive-value="false"
57
+          ></el-switch>
58
+          <div style="display: block;margin-left: 10px">如果开启,当用户使用oauth登录而没绑定时,会自动注册一个账号</div>
59
+        </el-form-item>
60
+        <el-form-item>
61
+          <el-button @click="formVisible = false">取消</el-button>
62
+          <el-button @click="submit" type="primary">提交</el-button>
63
+        </el-form-item>
64
+      </el-form>
65
+    </el-dialog>
66
+  </div>
67
+</template>
68
+
69
+<script setup>
70
+  import { onMounted, reactive, watch, ref, onActivated } from 'vue'
71
+  import { list, create, update, detail, remove } from '@/api/oauth'
72
+  import { ElMessage, ElMessageBox } from 'element-plus'
73
+
74
+  const listRes = reactive({
75
+    list: [], total: 0, loading: false,
76
+  })
77
+  const listQuery = reactive({
78
+    page: 1,
79
+    page_size: 10,
80
+  })
81
+  const ops = [
82
+    { value: 'github', label: 'Github' },
83
+  ]
84
+  const getList = async () => {
85
+    listRes.loading = true
86
+    const res = await list(listQuery).catch(_ => false)
87
+    listRes.loading = false
88
+    if (res) {
89
+      listRes.list = res.data.list
90
+      listRes.total = res.data.total
91
+    }
92
+  }
93
+  const handlerQuery = () => {
94
+    if (listQuery.page === 1) {
95
+      getList()
96
+    } else {
97
+      listQuery.page = 1
98
+    }
99
+  }
100
+
101
+  const del = async (row) => {
102
+    const cf = await ElMessageBox.confirm('确定删除么?', {
103
+      confirmButtonText: '确定',
104
+      cancelButtonText: '取消',
105
+      type: 'warning',
106
+    }).catch(_ => false)
107
+    if (!cf) {
108
+      return false
109
+    }
110
+
111
+    const res = await remove({ id: row.id }).catch(_ => false)
112
+    if (res) {
113
+      ElMessage.success('操作成功')
114
+      getList()
115
+    }
116
+  }
117
+  onMounted(getList)
118
+  onActivated(getList)
119
+
120
+  watch(() => listQuery.page, getList)
121
+
122
+  watch(() => listQuery.page_size, handlerQuery)
123
+
124
+  const formVisible = ref(false)
125
+  const formData = reactive({
126
+    id: 0,
127
+    op: '',
128
+    client_id: '',
129
+    client_secret: '',
130
+    redirect_url: '',
131
+    auto_register: false,
132
+  })
133
+  const rules = {
134
+    client_id: [{ required: true, message: '请输入ClientId', trigger: 'blur' }],
135
+    client_secret: [{ required: true, message: '请输入ClientSecret', trigger: 'blur' }],
136
+    redirect_url: [{ required: true, message: '请输入RedirectUrl', trigger: 'blur' }],
137
+    op: [{ required: true, message: '请选择类型', trigger: 'blur' }],
138
+  }
139
+  const toEdit = (row) => {
140
+    formVisible.value = true
141
+    formData.id = row.id
142
+    formData.op = row.op
143
+    formData.client_id = row.client_id
144
+    formData.client_secret = row.client_secret
145
+    formData.redirect_url = row.redirect_url
146
+    formData.auto_register = row.auto_register
147
+
148
+  }
149
+  const toAdd = () => {
150
+    formVisible.value = true
151
+    formData.id = 0
152
+    formData.op = ''
153
+    formData.client_id = ''
154
+    formData.client_secret = ''
155
+    formData.redirect_url = ''
156
+    formData.auto_register = false
157
+  }
158
+  const form = ref(null)
159
+  const submit = async () => {
160
+    const v = await form.value.validate().catch(err => false)
161
+    if (!v) {
162
+      return
163
+    }
164
+    const api = formData.id ? update : create
165
+    const res = await api(formData).catch(_ => false)
166
+    if (res) {
167
+      ElMessage.success('操作成功')
168
+      formVisible.value = false
169
+      getList()
170
+    }
171
+  }
172
+
173
+</script>
174
+
175
+<style scoped lang="scss">
176
+
177
+</style>

+ 95 - 0
src/views/oauth/login.vue

@@ -0,0 +1,95 @@
1
+<template>
2
+  <div class="oauth">
3
+    <el-card class="card">
4
+      <h2>您正在授权登录</h2>
5
+      <el-form class="info" label-width="100px">
6
+        <el-form-item label="设备">
7
+          <div class="impt">{{ oauthInfo.device_name }}</div>
8
+        </el-form-item>
9
+        <el-form-item label="ID">
10
+          <div class="impt">{{ oauthInfo.id }}</div>
11
+        </el-form-item>
12
+        <el-form-item label-width="0">
13
+          <el-button style="width: 100%" v-if="!resStatus" type="success" size="large" @click="toConfirm">授权登录</el-button>
14
+        </el-form-item>
15
+        <el-form-item label-width="0">
16
+          <el-button style="width: 100%" size="large" @click="out">关闭页面</el-button>
17
+        </el-form-item>
18
+      </el-form>
19
+      如果不是您操作的授权,请直接关闭页面
20
+    </el-card>
21
+  </div>
22
+</template>
23
+
24
+<script setup>
25
+  import { ref, onMounted } from 'vue'
26
+  import { info, confirm } from '@/api/oauth'
27
+  import { useRoute, useRouter } from 'vue-router'
28
+  import { ElMessage } from 'element-plus'
29
+
30
+  const oauthInfo = ref({})
31
+  const route = useRoute()
32
+  const router = useRouter()
33
+  const code = route.params?.code
34
+  if (!code) {
35
+    // router.push('/')
36
+  }
37
+  const getInfo = async () => {
38
+    const res = await info({ code }).catch(_ => false)
39
+    if (res) {
40
+      oauthInfo.value = res.data
41
+    } else {
42
+      // router.push('/')
43
+    }
44
+  }
45
+  getInfo()
46
+  const resStatus = ref(0)
47
+  const toConfirm = async () => {
48
+    const res = await confirm({ code }).catch(_ => false)
49
+    if (res) {
50
+      resStatus.value = 1
51
+      ElMessage.success('操作成功,3秒后将自动关闭本页面')
52
+      setTimeout(_ => {
53
+        out()
54
+      }, 3000)
55
+    }
56
+  }
57
+  const out = () => {
58
+    window.close()
59
+  }
60
+
61
+</script>
62
+
63
+<style scoped lang="scss">
64
+.oauth {
65
+  width: 100vw;
66
+  height: 100vh;
67
+  background-color: #2d3a4b;
68
+  padding-top: 25vh;
69
+  box-sizing: border-box;
70
+
71
+  .card {
72
+    max-width: 500px;
73
+    background-color: #283342;
74
+    color: #fff;
75
+    border: none;
76
+    margin: 0 auto;
77
+    text-align: center;
78
+
79
+    .info {
80
+      display: block;
81
+      line-height: 30px;
82
+      margin-bottom: 50px;
83
+
84
+      ::v-deep(.el-form-item__label) {
85
+        color: #fff;
86
+      }
87
+    }
88
+
89
+    .impt {
90
+      font-weight: bold;
91
+      font-size: 20px;
92
+    }
93
+  }
94
+}
95
+</style>