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

+ 16 - 0
src/api/user_token.js

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

+ 1 - 0
src/global.js

@@ -1,6 +1,7 @@
1 1
 import { ref, reactive, watch } from 'vue'
2 2
 import { list as fetchUsers } from '@/api/user'
3 3
 
4
+// todo 缓存所有用户信息
4 5
 export function loadAllUsers () {
5 6
   const allUsers = ref([])
6 7
   const getAllUsers = async () => {

+ 6 - 0
src/router/index.js

@@ -142,6 +142,12 @@ export const asyncRoutes = [
142 142
         meta: { title: 'OauthManage', icon: 'Link' /*keepAlive: true*/ },
143 143
         component: () => import('@/views/oauth/index.vue'),
144 144
       },
145
+      {
146
+        path: '/userToken',
147
+        name: 'UserToken',
148
+        meta: { title: 'UserToken', icon: 'Ticket' /*keepAlive: true*/ },
149
+        component: () => import('@/views/user/token.vue'),
150
+      },
145 151
       {
146 152
         path: '/loginLog',
147 153
         name: 'LoginLog',

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

@@ -447,5 +447,8 @@
447 447
   },
448 448
   "ToLogin": {
449 449
     "One": "To Login"
450
+  },
451
+  "UserToken": {
452
+    "One": "User Token"
450 453
   }
451 454
 }

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

@@ -433,5 +433,8 @@
433 433
   },
434 434
   "ToLogin": {
435 435
     "One": "로그인하려면 클릭하십시오"
436
+  },
437
+  "UserToken": {
438
+    "One": "사용자 토큰"
436 439
   }
437 440
 }

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

@@ -447,6 +447,9 @@
447 447
   },
448 448
   "ToLogin": {
449 449
     "One": "Войти"
450
+  },
451
+  "UserToken": {
452
+    "One": "Токен пользователя"
450 453
   }
451 454
 }
452 455
 

+ 6 - 6
src/utils/i18n/zh_CN.json

@@ -431,16 +431,13 @@
431 431
   "or login in with": {
432 432
     "One": "或使用以下登陆"
433 433
   },
434
-  "Optional, default is" :
435
-  {
434
+  "Optional, default is": {
436 435
     "One": "可选, 默认值是"
437 436
   },
438
-  "Check your IdP docs, without":
439
-  {
437
+  "Check your IdP docs, without": {
440 438
     "One": "检查IdP的文档, 不包含"
441 439
   },
442
-  "For OIDC login without a password, enter any 4-20 letters":
443
-  {
440
+  "For OIDC login without a password, enter any 4-20 letters": {
444 441
     "One": "如果通过 OIDC 登录且未设置密码,请输入任意 4-20 个字符"
445 442
   },
446 443
   "OldPassword": {
@@ -457,5 +454,8 @@
457 454
   },
458 455
   "ToLogin": {
459 456
     "One": "去登录"
457
+  },
458
+  "UserToken": {
459
+    "One": "用户Token"
460 460
   }
461 461
 }

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

@@ -1,5 +1,6 @@
1 1
 import { reactive } from 'vue'
2 2
 import { list, remove } from '@/api/login_log'
3
+import { list as fetchPeers } from '@/api/peer'
3 4
 import { ElMessage, ElMessageBox } from 'element-plus'
4 5
 import { useRoute } from 'vue-router'
5 6
 import { T } from '@/utils/i18n'
@@ -23,6 +24,15 @@ export function useRepositories () {
23 24
     const res = await list(listQuery).catch(_ => false)
24 25
     listRes.loading = false
25 26
     if (res) {
27
+      const uuids = res.data.list.filter(item => item.uuid).map(item => item.uuid)
28
+      const peers = await fetchPeers({ uuids }).catch(_ => false)
29
+      if (peers?.data?.list) {
30
+        res.data.list.forEach(item => {
31
+          if (item.uuid) {
32
+            item.peer = peers.data.list.find(peer => peer.uuid === item.uuid)
33
+          }
34
+        })
35
+      }
26 36
       listRes.list = res.data.list
27 37
       listRes.total = res.data.total
28 38
     }

+ 5 - 4
src/views/login/log.vue

@@ -14,7 +14,6 @@
14 14
         </el-form-item>
15 15
         <el-form-item>
16 16
           <el-button type="primary" @click="handlerQuery">筛选</el-button>
17
-          <el-button type="danger" @click="toAdd">添加</el-button>
18 17
         </el-form-item>
19 18
       </el-form>
20 19
     </el-card>
@@ -22,18 +21,19 @@
22 21
       <!--      <el-tag type="danger" style="margin-bottom: 10px">不建议在此操作地址簿,可能会造成数据不同步</el-tag>-->
23 22
       <el-table :data="listRes.list" v-loading="listRes.loading" border>
24 23
         <el-table-column prop="id" label="ID" align="center" width="100"/>
25
-        <el-table-column label="所属用户" align="center" width="120">
24
+        <el-table-column :label="T('Owner')" align="center" width="120">
26 25
           <template #default="{row}">
27 26
             <span v-if="row.user_id"> <el-tag>{{ allUsers?.find(u => u.id === row.user_id)?.username }}</el-tag> </span>
28 27
           </template>
29 28
         </el-table-column>
30 29
         <el-table-column prop="client" label="client" align="center" width="120"/>
30
+        <el-table-column prop="peer.id" :label="T('Peer')" align="center"/>
31 31
         <el-table-column prop="uuid" label="uuid" align="center"/>
32 32
         <el-table-column prop="ip" label="ip" align="center" width="150"/>
33 33
         <el-table-column prop="type" label="type" align="center" width="100"/>
34 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">
35
+        <el-table-column prop="created_at" :label="T('CreatedAt')" align="center"/>
36
+        <el-table-column :label="T('Actions')" align="center" width="400">
37 37
           <template #default="{row}">
38 38
             <el-button type="danger" @click="del(row)">删除</el-button>
39 39
           </template>
@@ -56,6 +56,7 @@
56 56
   import { onActivated, onMounted, watch } from 'vue'
57 57
   import { loadAllUsers } from '@/global'
58 58
   import { useRepositories } from '@/views/login/log.js'
59
+  import { T } from '@/utils/i18n'
59 60
 
60 61
   const { allUsers, getAllUsers } = loadAllUsers()
61 62
   getAllUsers()

+ 62 - 0
src/views/user/token.js

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

+ 96 - 0
src/views/user/token.vue

@@ -0,0 +1,96 @@
1
+<template>
2
+  <div>
3
+    <el-card class="list-query" shadow="hover">
4
+      <el-form inline label-width="80px">
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-form-item>
18
+      </el-form>
19
+    </el-card>
20
+    <el-card class="list-body" shadow="hover">
21
+      <el-table :data="listRes.list" v-loading="listRes.loading" border>
22
+        <el-table-column prop="id" label="id" align="center" width="100"/>
23
+        <el-table-column :label="T('Owner')" align="center">
24
+          <template #default="{row}">
25
+            <span v-if="row.user_id"> <el-tag>{{ allUsers?.find(u => u.id === row.user_id)?.username }}</el-tag> </span>
26
+          </template>
27
+        </el-table-column>
28
+        <el-table-column :label="T('Token')" align="center">
29
+          <template #default="{row}">
30
+            <span> {{ maskToken(row.token) }} </span>
31
+          </template>
32
+        </el-table-column>
33
+        <el-table-column prop="created_at" :label="T('CreatedAt')" align="center"/>
34
+        <el-table-column :label="T('ExpireTime')" prop="expired_at" align="center">
35
+          <template #default="{row}">
36
+            <el-tag :type="expired(row)?'info':'success'">{{ row.expired_at ? new Date(row.expired_at * 1000).toLocaleString() : '-' }}</el-tag>
37
+          </template>
38
+        </el-table-column>
39
+        <el-table-column :label="T('Actions')" align="center" width="400">
40
+          <template #default="{row}">
41
+            <el-button type="danger" @click="del(row)">{{ T('Logout') }}</el-button>
42
+          </template>
43
+        </el-table-column>
44
+      </el-table>
45
+    </el-card>
46
+    <el-card class="list-page" shadow="hover">
47
+      <el-pagination background
48
+                     layout="prev, pager, next, sizes, jumper"
49
+                     :page-sizes="[10,20,50,100]"
50
+                     v-model:page-size="listQuery.page_size"
51
+                     v-model:current-page="listQuery.page"
52
+                     :total="listRes.total">
53
+      </el-pagination>
54
+    </el-card>
55
+  </div>
56
+</template>
57
+
58
+<script setup>
59
+  import { onActivated, onMounted, watch } from 'vue'
60
+  import { loadAllUsers } from '@/global'
61
+  import { useRepositories } from '@/views/user/token.js'
62
+  import { T } from '@/utils/i18n'
63
+
64
+  const { allUsers, getAllUsers } = loadAllUsers()
65
+  getAllUsers()
66
+
67
+  const {
68
+    listRes,
69
+    listQuery,
70
+    getList,
71
+    handlerQuery,
72
+    del,
73
+  } = useRepositories()
74
+
75
+  onMounted(getList)
76
+  onActivated(getList)
77
+
78
+  watch(() => listQuery.page, getList)
79
+
80
+  watch(() => listQuery.page_size, handlerQuery)
81
+  const maskToken = (token) => {
82
+    return token.slice(0, 4) + '****' + token.slice(-4)
83
+  }
84
+  const expired = (row) => {
85
+    const now = new Date().getTime()
86
+    return row.expired_at * 1000 < now
87
+  }
88
+</script>
89
+
90
+<style scoped lang="scss">
91
+.list-query .el-select {
92
+  --el-select-width: 160px;
93
+}
94
+
95
+
96
+</style>