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

+ 0 - 1
src/api/address_book.js

@@ -52,4 +52,3 @@ export function shareByWebClient (data) {
52 52
     data,
53 53
   })
54 54
 }
55
-

+ 39 - 0
src/api/address_book_collection.js

@@ -0,0 +1,39 @@
1
+import request from '@/utils/request'
2
+
3
+export function list (params) {
4
+  return request({
5
+    url: '/address_book_collection/list',
6
+    params,
7
+  })
8
+}
9
+
10
+export function detail (id) {
11
+  return request({
12
+    url: `/address_book_collection/detail/${id}`,
13
+  })
14
+}
15
+
16
+export function create (data) {
17
+  return request({
18
+    url: '/address_book_collection/create',
19
+    method: 'post',
20
+    data,
21
+  })
22
+}
23
+
24
+export function update (data) {
25
+  return request({
26
+    url: '/address_book_collection/update',
27
+    method: 'post',
28
+    data,
29
+  })
30
+}
31
+
32
+export function remove (data) {
33
+  return request({
34
+    url: '/address_book_collection/delete',
35
+    method: 'post',
36
+    data,
37
+  })
38
+}
39
+

+ 46 - 0
src/api/address_book_collection_rule.js

@@ -0,0 +1,46 @@
1
+import request from '@/utils/request'
2
+
3
+export function list (params) {
4
+  return request({
5
+    url: '/address_book_collection_rule/list',
6
+    params,
7
+  })
8
+}
9
+
10
+export function detail (id) {
11
+  return request({
12
+    url: `/address_book_collection_rule/detail/${id}`,
13
+  })
14
+}
15
+
16
+export function create (data) {
17
+  return request({
18
+    url: '/address_book_collection_rule/create',
19
+    method: 'post',
20
+    data,
21
+  })
22
+}
23
+
24
+export function update (data) {
25
+  return request({
26
+    url: '/address_book_collection_rule/update',
27
+    method: 'post',
28
+    data,
29
+  })
30
+}
31
+
32
+export function remove (data) {
33
+  return request({
34
+    url: '/address_book_collection_rule/delete',
35
+    method: 'post',
36
+    data,
37
+  })
38
+}
39
+
40
+export function batchCreate (data) {
41
+  return request({
42
+    url: '/address_book_collection_rule/batchCreate',
43
+    method: 'post',
44
+    data,
45
+  })
46
+}

+ 8 - 0
src/api/user.js

@@ -74,3 +74,11 @@ export function myOauth () {
74 74
     method: 'post',
75 75
   })
76 76
 }
77
+
78
+export function groupUsers (data) {
79
+  return request({
80
+    url: '/user/groupUsers',
81
+    method: 'post',
82
+    data
83
+  })
84
+}

Разница между файлами не показана из-за своего большого размера
+ 33 - 0
src/components/icons/platform.vue


+ 12 - 1
src/router/index.js

@@ -54,6 +54,12 @@ export const asyncRoutes = [
54 54
         meta: { title: 'Info', icon: 'User' /*keepAlive: true*/ },
55 55
         component: () => import('@/views/my/info.vue'),
56 56
       },
57
+      {
58
+        path: 'address_book_collection',
59
+        name: 'MyAddressBookCollection',
60
+        meta: { title: 'AddressBookName', icon: 'Collection' /*keepAlive: true*/ },
61
+        component: () => import('@/views/my/address_book/collection.vue'),
62
+      },
57 63
       {
58 64
         path: 'address_book',
59 65
         name: 'MyAddressBookList',
@@ -105,7 +111,12 @@ export const asyncRoutes = [
105 111
         meta: { title: 'UserEdit', hide: true },
106 112
         component: () => import('@/views/user/edit.vue'),
107 113
       },
108
-
114
+      {
115
+        path: 'addressBookName',
116
+        name: 'UserAddressBookName',
117
+        meta: { title: 'AddressBookNameManage', icon: 'Collection' /*keepAlive: true*/ },
118
+        component: () => import('@/views/address_book/collection.vue'),
119
+      },
109 120
       {
110 121
         path: 'addressBook',
111 122
         name: 'UserAddressBook',

+ 6 - 2
src/styles/style.scss

@@ -8,6 +8,7 @@ $sideBarWidth: 210px;
8 8
   --basicBlack: #000000;
9 9
   --basicWhite: #ffffff;
10 10
   --primaryColor: #409eff;
11
+  --tag-bg-color: #efefef;
11 12
 }
12 13
 
13 14
 .list-body {
@@ -29,15 +30,18 @@ $sideBarWidth: 210px;
29 30
   }
30 31
 }
31 32
 
32
-.table-actions{
33
-  .el-button{
33
+.table-actions {
34
+  .el-button {
34 35
     margin-top: 5px;
35 36
     margin-bottom: 5px;
36 37
     margin-left: 5px;
37 38
     margin-right: 5px;
38 39
   }
39 40
 }
41
+
40 42
 html.dark {
41 43
   /* 自定义深色背景颜色 */
42 44
   //--el-bg-color: #626aef;
45
+  --tag-bg-color: #24252b;
46
+  --basicBlack: #fff;
43 47
 }

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

@@ -399,5 +399,41 @@
399 399
   },
400 400
   "ToLocal": {
401 401
     "One": "To Local"
402
+  },
403
+  "AddressBookName": {
404
+    "One": "Address Book Name"
405
+  },
406
+  "AddRule": {
407
+    "One": "Add Rule"
408
+  },
409
+  "ShareRules": {
410
+    "One": "Share Rules"
411
+  },
412
+  "Rule": {
413
+    "One": "Rule"
414
+  },
415
+  "Read": {
416
+    "One": "Read"
417
+  },
418
+  "ReadWrite": {
419
+    "One": "Read And Write"
420
+  },
421
+  "FullControl": {
422
+    "One": "Full Control"
423
+  },
424
+  "ShareTo": {
425
+    "One": "Share To"
426
+  },
427
+  "MyAddressBook": {
428
+    "One": "My Address Book"
429
+  },
430
+  "AddressBook": {
431
+    "One": "Address Book"
432
+  },
433
+  "AddressBookNameManage": {
434
+    "One": "Address Book Names"
435
+  },
436
+  "MyAddressBookTips": {
437
+    "One": "\"My Address Book\" is the default of the system, cannot be modified or deleted"
402 438
   }
403 439
 }

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

@@ -385,5 +385,41 @@
385 385
   },
386 386
   "ToLocal": {
387 387
     "One": "로컬로"
388
+  },
389
+  "AddressBookName": {
390
+    "One": "주소록 이름"
391
+  },
392
+  "AddRule": {
393
+    "One": "규칙 추가"
394
+  },
395
+  "ShareRules": {
396
+    "One": "공유 규칙"
397
+  },
398
+  "Rule": {
399
+    "One": "규칙"
400
+  },
401
+  "Read": {
402
+    "One": "읽기"
403
+  },
404
+  "ReadWrite": {
405
+    "One": "읽기/쓰기"
406
+  },
407
+  "FullControl": {
408
+    "One": "전체 제어"
409
+  },
410
+  "ShareTo": {
411
+    "One": "공유 대상"
412
+  },
413
+  "MyAddressBook": {
414
+    "One": "내 주소록"
415
+  },
416
+  "AddressBook": {
417
+    "One": "주소록"
418
+  },
419
+  "AddressBookNameManage": {
420
+    "One": "주소록 이름 관리"
421
+  },
422
+  "MyAddressBookTips": {
423
+    "One": "\"내 주소록\"은 시스템의 기본값이며 수정하거나 삭제할 수 없습니다."
388 424
   }
389 425
 }

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

@@ -399,6 +399,42 @@
399 399
   },
400 400
   "ToLocal": {
401 401
     "One": "К локальному"
402
+  },
403
+  "AddressBookName": {
404
+    "One": "Имя адресной книги"
405
+  },
406
+  "AddRule": {
407
+    "One": "Добавить правило"
408
+  },
409
+  "ShareRules": {
410
+    "One": "Правила обмена"
411
+  },
412
+  "Rule": {
413
+    "One": "Правило"
414
+  },
415
+  "Read": {
416
+    "One": "Чтение"
417
+  },
418
+  "ReadWrite": {
419
+    "One": "Чтение и запись"
420
+  },
421
+  "FullControl": {
422
+    "One": "Полный доступ"
423
+  },
424
+  "ShareTo": {
425
+    "One": "Поделиться с"
426
+  },
427
+  "MyAddressBook": {
428
+    "One": "Моя адресная книга"
429
+  },
430
+  "AddressBook": {
431
+    "One": "Адресная книга"
432
+  },
433
+  "AddressBookNameManage": {
434
+    "One": "именами адресных книг"
435
+  },
436
+  "MyAddressBookTips": {
437
+    "One": "«Моя адресная книга» является стандартным для системы, не может быть изменена или удалена"
402 438
   }
403 439
 }
404 440
 

+ 39 - 0
src/utils/i18n/zh_CN.json

@@ -385,5 +385,44 @@
385 385
   },
386 386
   "ToLocal": {
387 387
     "One": "到本地"
388
+  },
389
+  "AddressBookName": {
390
+    "One": "地址簿名称"
391
+  },
392
+  "AddressBookCollection": {
393
+    "One": "地址簿集"
394
+  },
395
+  "AddRule": {
396
+    "One": "添加规则"
397
+  },
398
+  "ShareRules": {
399
+    "One": "分享规则"
400
+  },
401
+  "Rule": {
402
+    "One": "规则"
403
+  },
404
+  "Read": {
405
+    "One": "读取"
406
+  },
407
+  "ReadWrite": {
408
+    "One": "读写"
409
+  },
410
+  "FullControl": {
411
+    "One": "完全控制"
412
+  },
413
+  "ShareTo": {
414
+    "One": "分享给"
415
+  },
416
+  "MyAddressBook": {
417
+    "One": "我的地址簿"
418
+  },
419
+  "AddressBook": {
420
+    "One": "地址簿"
421
+  },
422
+  "AddressBookNameManage": {
423
+    "One": "地址簿名称"
424
+  },
425
+  "MyAddressBookTips": {
426
+    "One": "\"我的地址簿\"是系统默认,不可修改、删除"
388 427
   }
389 428
 }

+ 95 - 0
src/views/address_book/collection.js

@@ -0,0 +1,95 @@
1
+import { reactive, ref } from 'vue'
2
+import { create, list, remove, update } from '@/api/address_book_collection'
3
+import { ElMessage, ElMessageBox } from 'element-plus'
4
+import { T } from '@/utils/i18n'
5
+
6
+export function useRepositories (is_my) {
7
+  const listRes = reactive({
8
+    list: [], total: 0, loading: false,
9
+  })
10
+  const listQuery = reactive({
11
+    page: 1,
12
+    page_size: 10,
13
+    is_my,
14
+    name: null,
15
+    user_id: null,
16
+  })
17
+
18
+  const getList = async () => {
19
+    listRes.loading = true
20
+    const res = await list(listQuery).catch(_ => false)
21
+    listRes.loading = false
22
+    if (res) {
23
+      listRes.list = res.data.list
24
+      listRes.total = res.data.total
25
+    }
26
+  }
27
+  const handlerQuery = () => {
28
+    if (listQuery.page === 1) {
29
+      getList()
30
+    } else {
31
+      listQuery.page = 1
32
+    }
33
+  }
34
+
35
+  const del = async (row) => {
36
+    const cf = await ElMessageBox.confirm(T('Confirm?', { param: T('Delete') }), {
37
+      confirmButtonText: T('Confirm'),
38
+      cancelButtonText: T('Cancel'),
39
+      type: 'warning',
40
+    }).catch(_ => false)
41
+    if (!cf) {
42
+      return false
43
+    }
44
+
45
+    const res = await remove({ id: row.id }).catch(_ => false)
46
+    if (res) {
47
+      ElMessage.success(T('OperationSuccess'))
48
+      getList()
49
+    }
50
+  }
51
+
52
+  const formVisible = ref(false)
53
+  const formData = reactive({
54
+    id: 0,
55
+    name: '',
56
+  })
57
+
58
+  const toEdit = (row) => {
59
+    formVisible.value = true
60
+    //将row中的数据赋值给formData
61
+    Object.keys(formData).forEach(key => {
62
+      formData[key] = row[key]
63
+    })
64
+
65
+  }
66
+  const toAdd = () => {
67
+    formVisible.value = true
68
+    //重置formData
69
+    Object.keys(formData).forEach(key => {
70
+      formData[key] = undefined
71
+    })
72
+
73
+  }
74
+  const submit = async () => {
75
+    const api = formData.id ? update : create
76
+    const res = await api(formData).catch(_ => false)
77
+    if (res) {
78
+      ElMessage.success(T('OperationSuccess'))
79
+      formVisible.value = false
80
+      getList()
81
+    }
82
+  }
83
+  return {
84
+    listRes,
85
+    listQuery,
86
+    getList,
87
+    handlerQuery,
88
+    del,
89
+    formVisible,
90
+    formData,
91
+    toEdit,
92
+    toAdd,
93
+    submit,
94
+  }
95
+}

+ 122 - 0
src/views/address_book/collection.vue

@@ -0,0 +1,122 @@
1
+<template>
2
+  <div>
3
+    <el-card class="list-query" shadow="hover">
4
+      <el-form inline label-width="80px">
5
+        <el-form-item :label="T('Owner')">
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">{{ T('Filter') }}</el-button>
17
+          <el-button type="danger" @click="toAdd">{{ T('Add') }}</el-button>
18
+        </el-form-item>
19
+      </el-form>
20
+    </el-card>
21
+    <el-card class="list-body" shadow="hover">
22
+      <el-table :data="listRes.list" v-loading="listRes.loading" border>
23
+        <el-table-column prop="id" label="id" align="center"/>
24
+        <el-table-column prop="user_id" :label="T('Owner')" align="center">
25
+          <template #default="{row}">
26
+            <span v-if="row.user_id"> <el-tag>{{ allUsers?.find(u => u.id === row.user_id)?.username }}</el-tag> </span>
27
+          </template>
28
+        </el-table-column>
29
+        <el-table-column prop="name" :label="T('AddressBook')" align="center"/>
30
+        <el-table-column prop="created_at" :label="T('CreatedAt')" align="center"/>
31
+        <!--        <el-table-column prop="updated_at" label="更新时间" align="center"/>-->
32
+        <el-table-column :label="T('Actions')" align="center" class-name="table-actions" width="600" fixed="right">
33
+          <template #default="{row}">
34
+            <el-button type="primary" @click="showRules(row)">{{ T('ShareRules') }}</el-button>
35
+            <el-button @click="toEdit(row)">{{ T('Edit') }}</el-button>
36
+            <el-button type="danger" @click="del(row)">{{ T('Delete') }}</el-button>
37
+          </template>
38
+        </el-table-column>
39
+      </el-table>
40
+    </el-card>
41
+    <el-card class="list-page" shadow="hover">
42
+      <el-pagination background
43
+                     layout="prev, pager, next, sizes, jumper"
44
+                     :page-sizes="[10,20,50,100]"
45
+                     v-model:page-size="listQuery.page_size"
46
+                     v-model:current-page="listQuery.page"
47
+                     :total="listRes.total">
48
+      </el-pagination>
49
+    </el-card>
50
+    <el-dialog v-model="formVisible" width="800" :title="!formData.id?T('Create') :T('Update') ">
51
+      <el-form class="dialog-form" ref="form" :model="formData" label-width="120px">
52
+        <el-form-item :label="T('Owner')" prop="user_id" required>
53
+          <el-select v-model="formData.user_id">
54
+            <el-option
55
+                v-for="item in allUsers"
56
+                :key="item.id"
57
+                :label="item.username"
58
+                :value="item.id"
59
+            ></el-option>
60
+          </el-select>
61
+        </el-form-item>
62
+        <el-form-item :label="T('Name')" prop="name" required>
63
+          <el-input v-model="formData.name"></el-input>
64
+        </el-form-item>
65
+        <el-form-item>
66
+          <el-button @click="formVisible = false">{{ T('Cancel') }}</el-button>
67
+          <el-button @click="submit" type="primary">{{ T('Submit') }}</el-button>
68
+        </el-form-item>
69
+      </el-form>
70
+    </el-dialog>
71
+    <el-dialog v-model="rulesVisible" :title="T('ShareRules')" destroy-on-close top="5vh" width="80%">
72
+      <Rule :collection="clickRow" :is_my="0"></Rule>
73
+    </el-dialog>
74
+
75
+  </div>
76
+</template>
77
+
78
+<script setup>
79
+  import { T } from '@/utils/i18n'
80
+  import { ref } from 'vue'
81
+  import { useRepositories } from '@/views/address_book/collection'
82
+  import { onActivated, onMounted, watch } from 'vue'
83
+  import Rule from '@/views/address_book/rule.vue'
84
+  import { loadAllUsers } from '@/global'
85
+
86
+  const { allUsers, getAllUsers } = loadAllUsers()
87
+  getAllUsers()
88
+  const {
89
+    listRes,
90
+    listQuery,
91
+    getList,
92
+    handlerQuery,
93
+    del,
94
+    formVisible,
95
+    formData,
96
+    toEdit,
97
+    toAdd,
98
+    submit,
99
+  } = useRepositories()
100
+
101
+  listQuery.is_my = 0
102
+
103
+  onMounted(getList)
104
+  onActivated(getList)
105
+
106
+  watch(() => listQuery.page, getList)
107
+
108
+  watch(() => listQuery.page_size, handlerQuery)
109
+
110
+  const clickRow = ref({})
111
+  const rulesVisible = ref(false)
112
+  const showRules = (row) => {
113
+    clickRow.value = row
114
+    rulesVisible.value = true
115
+    console.log('showRules')
116
+  }
117
+
118
+</script>
119
+
120
+<style scoped lang="scss">
121
+
122
+</style>

+ 77 - 7
src/views/address_book/index.js

@@ -1,20 +1,40 @@
1
-import { reactive, ref } from 'vue'
1
+import { reactive, ref, watch } from 'vue'
2 2
 import { create, list, remove, update } from '@/api/address_book'
3 3
 import { ElMessage, ElMessageBox } from 'element-plus'
4 4
 import { T } from '@/utils/i18n'
5
+import { useRepositories as useCollectionRepositories } from '@/views/address_book/collection'
6
+import { useRepositories as useTagRepositories } from '@/views/tag/index'
7
+import { loadAllUsers } from '@/global'
8
+
9
+export function useRepositories (is_my = 0) {
10
+
11
+  const { allUsers, getAllUsers } = loadAllUsers()
12
+
13
+  const {
14
+    listRes: collectionListRes,
15
+    listQuery: collectionListQuery,
16
+    getList: getCollectionList,
17
+  } = useCollectionRepositories(is_my)
18
+  collectionListQuery.page_size = 9999
19
+  const {
20
+    listRes: tagListRes,
21
+    listQuery: tagListQuery,
22
+    getList: getTagList,
23
+  } = useTagRepositories(is_my)
24
+  tagListQuery.page_size = 9999
5 25
 
6
-export function useRepositories (user_id) {
7 26
   const listRes = reactive({
8 27
     list: [], total: 0, loading: false,
9 28
   })
10 29
   const listQuery = reactive({
11 30
     page: 1,
12 31
     page_size: 10,
13
-    is_my: 0,
32
+    is_my,
14 33
     id: null,
15 34
     user_id: null,
16 35
     username: null,
17 36
     hostname: null,
37
+    collection_id: null,
18 38
   })
19 39
 
20 40
   const getList = async () => {
@@ -52,10 +72,10 @@ export function useRepositories (user_id) {
52 72
   }
53 73
 
54 74
   const platformList = [
55
-    { label: 'Windows', value: 'Windows' },
56
-    { label: 'Linux', value: 'Linux' },
57
-    { label: 'Mac OS', value: 'Mac OS' },
58
-    { label: 'Android', value: 'Android' },
75
+    { label: 'Windows', value: 'Windows', icon: 'windows' },
76
+    { label: 'Linux', value: 'Linux', icon: 'linux' },
77
+    { label: 'Mac OS', value: 'Mac OS', icon: 'mac' },
78
+    { label: 'Android', value: 'Android', icon: 'android' },
59 79
   ]
60 80
   const formVisible = ref(false)
61 81
   const formData = reactive({
@@ -76,6 +96,7 @@ export function useRepositories (user_id) {
76 96
     'user_id': null,
77 97
     user_ids: [],
78 98
     'username': '',
99
+    collection_id: null,
79 100
   })
80 101
 
81 102
   const toEdit = (row) => {
@@ -84,6 +105,10 @@ export function useRepositories (user_id) {
84 105
     Object.keys(formData).forEach(key => {
85 106
       formData[key] = row[key]
86 107
     })
108
+    collectionListQuery.user_id = row.user_id
109
+    getCollectionList()
110
+    tagListQuery.collection_id = row.collection_id
111
+    getTagList()
87 112
 
88 113
   }
89 114
   const toAdd = () => {
@@ -126,6 +151,36 @@ export function useRepositories (user_id) {
126 151
     shareToWebClientForm.hash = row.hash
127 152
     shareToWebClientVisible.value = true
128 153
   }
154
+
155
+  const changeQueryUser = async (val) => {
156
+    tagListRes.list = []
157
+    listQuery.collection_id = null
158
+    if (!val) {
159
+      collectionListRes.list = []
160
+    } else {
161
+      collectionListQuery.user_id = val
162
+      getCollectionList()
163
+    }
164
+  }
165
+  const changeUser = async (val) => {
166
+    tagListRes.list = []
167
+    formData.tags = []
168
+    formData.collection_id = 0
169
+    if (!val) {
170
+      collectionListRes.list = []
171
+    } else {
172
+      collectionListQuery.user_id = val
173
+      getCollectionList()
174
+    }
175
+  }
176
+  const changeCollection = async (val) => {
177
+    tagListRes.list = []
178
+    formData.tags = []
179
+    tagListQuery.user_id = formData.user_id
180
+    tagListQuery.collection_id = val
181
+    getTagList()
182
+  }
183
+
129 184
   return {
130 185
     listRes,
131 186
     listQuery,
@@ -141,5 +196,20 @@ export function useRepositories (user_id) {
141 196
     shareToWebClientVisible,
142 197
     shareToWebClientForm,
143 198
     toShowShare,
199
+
200
+    collectionListQuery,
201
+    getCollectionList,
202
+    collectionListRes,
203
+
204
+    tagListQuery,
205
+    getTagList,
206
+    tagListRes,
207
+
208
+    allUsers,
209
+    getAllUsers,
210
+
211
+    changeQueryUser,
212
+    changeUser,
213
+    changeCollection,
144 214
   }
145 215
 }

+ 51 - 32
src/views/address_book/index.vue

@@ -1,9 +1,9 @@
1 1
 <template>
2 2
   <div>
3 3
     <el-card class="list-query" shadow="hover">
4
-      <el-form inline label-width="80px">
4
+      <el-form inline label-width="120px">
5 5
         <el-form-item :label="T('Owner')">
6
-          <el-select v-model="listQuery.user_id" clearable>
6
+          <el-select v-model="listQuery.user_id" clearable @change="changeQueryUser">
7 7
             <el-option
8 8
                 v-for="item in allUsers"
9 9
                 :key="item.id"
@@ -12,6 +12,12 @@
12 12
             ></el-option>
13 13
           </el-select>
14 14
         </el-form-item>
15
+        <el-form-item :label="T('AddressBookName')">
16
+          <el-select v-model="listQuery.collection_id" clearable>
17
+            <el-option :value="0" :label="T('MyAddressBook')"></el-option>
18
+            <el-option v-for="c in collectionListRes.list" :key="c.id" :label="c.name" :value="c.id"></el-option>
19
+          </el-select>
20
+        </el-form-item>
15 21
         <el-form-item :label="T('Id')">
16 22
           <el-input v-model="listQuery.id" clearable></el-input>
17 23
         </el-form-item>
@@ -32,7 +38,13 @@
32 38
       <el-table :data="listRes.list" v-loading="listRes.loading" border>
33 39
         <el-table-column prop="id" label="id" align="center" width="200">
34 40
           <template #default="{row}">
35
-            <span>{{ row.id }} <el-icon @click="handleClipboard(row.id, $event)"><CopyDocument/></el-icon></span>
41
+            <div>
42
+              <PlatformIcons :name="platformList.find(p=>p.label===row.platform)?.icon" style="width: 20px;height: 20px;display: inline-block" color="var(--basicBlack)"/>
43
+              {{ row.id }}
44
+              <el-icon @click="handleClipboard(row.id, $event)">
45
+                <CopyDocument/>
46
+              </el-icon>
47
+            </div>
36 48
           </template>
37 49
         </el-table-column>
38 50
         <el-table-column :label="T('Owner')" align="center" width="200">
@@ -40,10 +52,14 @@
40 52
             <span v-if="row.user_id"> <el-tag>{{ allUsers?.find(u => u.id === row.user_id)?.username }}</el-tag> </span>
41 53
           </template>
42 54
         </el-table-column>
55
+        <el-table-column prop="collection_id" :label="T('AddressBookName')" align="center" width="150">
56
+          <template #default="{row}">
57
+            <span v-if="row.collection_id === 0">{{ T('MyAddressBook') }}</span>
58
+            <span v-else>{{ row.collection?.name }}</span>
59
+          </template>
60
+        </el-table-column>
43 61
         <el-table-column prop="username" :label="T('Username')" align="center" width="150"/>
44 62
         <el-table-column prop="hostname" :label="T('Hostname')" align="center" width="150"/>
45
-
46
-        <el-table-column prop="platform" :label="T('Platform')" align="center" width="120"/>
47 63
         <el-table-column prop="tags" :label="T('Tags')" align="center"/>
48 64
         <!--        <el-table-column prop="created_at" label="创建时间" align="center"/>-->
49 65
         <!--        <el-table-column prop="updated_at" label="更新时间" align="center"/>-->
@@ -81,6 +97,12 @@
81 97
             ></el-option>
82 98
           </el-select>
83 99
         </el-form-item>
100
+        <el-form-item :label="T('AddressBookName')">
101
+          <el-select v-model="formData.collection_id" clearable @change="changeCollection">
102
+            <el-option :value="0" :label="T('MyAddressBook')"></el-option>
103
+            <el-option v-for="c in collectionListRes.list" :key="c.id" :label="c.name" :value="c.id"></el-option>
104
+          </el-select>
105
+        </el-form-item>
84 106
         <el-form-item label="id" prop="id" required>
85 107
           <el-input v-model="formData.id"></el-input>
86 108
         </el-form-item>
@@ -116,7 +138,7 @@
116 138
         <el-form-item :label="T('Tags')" prop="tags">
117 139
           <el-select v-model="formData.tags" multiple>
118 140
             <el-option
119
-                v-for="item in tagList"
141
+                v-for="item in tagListRes.list"
120 142
                 :key="item.name"
121 143
                 :label="item.name"
122 144
                 :value="item.name"
@@ -146,44 +168,32 @@
146 168
         </el-form-item>
147 169
       </el-form>
148 170
     </el-dialog>
149
-<!--    <el-dialog v-model="shareToWebClientVisible" width="900" :close-on-click-modal="false">
150
-      <shareByWebClient :id="shareToWebClientForm.id"
151
-                        :hash="shareToWebClientForm.hash"
152
-                        @cancel="shareToWebClientVisible=false"
153
-                        @success=""/>
154
-    </el-dialog>-->
171
+    <!--    <el-dialog v-model="shareToWebClientVisible" width="900" :close-on-click-modal="false">
172
+          <shareByWebClient :id="shareToWebClientForm.id"
173
+                            :hash="shareToWebClientForm.hash"
174
+                            @cancel="shareToWebClientVisible=false"
175
+                            @success=""/>
176
+        </el-dialog>-->
155 177
   </div>
156 178
 </template>
157 179
 
158 180
 <script setup>
159 181
   import { onActivated, onMounted, reactive, ref, watch } from 'vue'
160
-  import { list as fetchTagList } from '@/api/tag'
161
-  import { loadAllUsers } from '@/global'
162 182
   import { useRepositories } from '@/views/address_book/index'
163 183
   import { toWebClientLink, getPeerSlat } from '@/utils/webclient'
164 184
   import { T } from '@/utils/i18n'
165 185
   import { useRoute } from 'vue-router'
166
-  import shareByWebClient from '@/views/address_book/components/shareByWebClient.vue'
167 186
   import { connectByClient } from '@/utils/peer'
168 187
   import { useAppStore } from '@/store/app'
169 188
   import { handleClipboard } from '@/utils/clipboard'
170 189
   import { CopyDocument } from '@element-plus/icons'
190
+  import PlatformIcons from '@/components/icons/platform.vue'
191
+
192
+
171 193
 
172 194
   const appStore = useAppStore()
173 195
   const route = useRoute()
174
-  const { allUsers, getAllUsers } = loadAllUsers()
175
-  getAllUsers()
176
-  const changeUser = (v) => {
177
-    formData.tags = []
178
-    fetchTagListData(v)
179
-  }
180
-  const tagList = ref([])
181
-  const fetchTagListData = async (user_id) => {
182
-    const res = await fetchTagList({ user_id }).catch(_ => false)
183
-    if (res) {
184
-      tagList.value = res.data.list
185
-    }
186
-  }
196
+
187 197
 
188 198
   const {
189 199
     listRes,
@@ -197,15 +207,24 @@
197 207
     toEdit,
198 208
     toAdd,
199 209
     submit,
200
-    shareToWebClientVisible,
201
-    shareToWebClientForm,
202
-    toShowShare,
210
+    // shareToWebClientVisible,
211
+    // shareToWebClientForm,
212
+    // toShowShare,
213
+    collectionListRes,
214
+
215
+    tagListRes,
216
+
217
+    allUsers, getAllUsers,
218
+
219
+    changeQueryUser,
220
+    changeUser,
221
+    changeCollection
203 222
   } = useRepositories()
204 223
 
205 224
   if (route.query?.user_id) {
206 225
     listQuery.user_id = parseInt(route.query.user_id)
207 226
   }
208
-
227
+  onMounted(getAllUsers)
209 228
   onMounted(getList)
210 229
   onActivated(getList)
211 230
 

+ 112 - 0
src/views/address_book/rule.js

@@ -0,0 +1,112 @@
1
+import { computed, reactive, ref } from 'vue'
2
+import { create, list, remove, update } from '@/api/address_book_collection_rule'
3
+import { groupUsers } from '@/api/user'
4
+import { ElMessage, ElMessageBox } from 'element-plus'
5
+import { T } from '@/utils/i18n'
6
+import collection from '@element-plus/icons/lib/Collection'
7
+
8
+export function useRepositories (is_my) {
9
+  const listRes = reactive({
10
+    list: [], total: 0, loading: false,
11
+  })
12
+  const listQuery = reactive({
13
+    page: 1,
14
+    page_size: 10,
15
+    collection_id: null,
16
+    is_my,
17
+  })
18
+
19
+  const getList = async () => {
20
+    listRes.loading = true
21
+    const res = await list(listQuery).catch(_ => false)
22
+    listRes.loading = false
23
+    if (res) {
24
+      listRes.list = res.data.list
25
+      listRes.total = res.data.total
26
+    }
27
+  }
28
+  const handlerQuery = () => {
29
+    if (listQuery.page === 1) {
30
+      getList()
31
+    } else {
32
+      listQuery.page = 1
33
+    }
34
+  }
35
+
36
+  const del = async (row) => {
37
+    const cf = await ElMessageBox.confirm(T('Confirm?', { param: T('Delete') }), {
38
+      confirmButtonText: T('Confirm'),
39
+      cancelButtonText: T('Cancel'),
40
+      type: 'warning',
41
+    }).catch(_ => false)
42
+    if (!cf) {
43
+      return false
44
+    }
45
+
46
+    const res = await remove({ id: row.id }).catch(_ => false)
47
+    if (res) {
48
+      ElMessage.success(T('OperationSuccess'))
49
+      getList()
50
+    }
51
+  }
52
+
53
+  const rules = computed(_ => [
54
+    { label: T('Read'), value: 1 },
55
+    { label: T('ReadWrite'), value: 2 },
56
+    { label: T('FullControl'), value: 3 },
57
+  ])
58
+
59
+  const formVisible = ref(false)
60
+  const formData = reactive({
61
+    id: 0,
62
+    collection_id: null,
63
+    type: 1,
64
+    rule: 1,
65
+    to_id: null,
66
+    user_id: null,
67
+  })
68
+
69
+  const toEdit = (row) => {
70
+    formVisible.value = true
71
+    //将row中的数据赋值给formData
72
+    Object.keys(formData).forEach(key => {
73
+      formData[key] = row[key]
74
+    })
75
+
76
+  }
77
+  const toAdd = () => {
78
+    formVisible.value = true
79
+
80
+  }
81
+  const submit = async () => {
82
+    const api = formData.id ? update : create
83
+    const res = await api(formData).catch(_ => false)
84
+    if (res) {
85
+      ElMessage.success(T('OperationSuccess'))
86
+      formVisible.value = false
87
+      getList()
88
+    }
89
+  }
90
+  const groupUsersList = ref([])
91
+  const getGroupUsers = async () => {
92
+    const res = await groupUsers({ user_id: formData.user_id }).catch(_ => false)
93
+    if (res) {
94
+      groupUsersList.value = res.data
95
+    }
96
+  }
97
+  return {
98
+    listRes,
99
+    listQuery,
100
+    getList,
101
+    handlerQuery,
102
+    del,
103
+    formVisible,
104
+    formData,
105
+    toEdit,
106
+    toAdd,
107
+    submit,
108
+    rules,
109
+    groupUsersList,
110
+    getGroupUsers,
111
+  }
112
+}

+ 127 - 0
src/views/address_book/rule.vue

@@ -0,0 +1,127 @@
1
+<template>
2
+  <div>
3
+    <el-card class="list-query" shadow="hover">
4
+      <el-form inline label-width="80px">
5
+        <el-form-item>
6
+          <el-button type="primary" @click="handlerQuery">{{ T('Filter') }}</el-button>
7
+          <el-button type="danger" @click="toAdd">{{ T('Add') }}</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="rule" :label="T('Rule')" align="center">
14
+          <template #default="{row}">
15
+            <div>
16
+              {{ rules.find(r => r.value === row.rule)?.label }}
17
+            </div>
18
+          </template>
19
+        </el-table-column>
20
+        <el-table-column prop="to_id" :label="T('ShareTo')" align="center">
21
+          <template #default="{row}">
22
+            <div v-if="row.type===1">
23
+              {{ groupUsersList.find(u => u.id === row.to_id)?.username }}
24
+            </div>
25
+          </template>
26
+        </el-table-column>
27
+        <el-table-column prop="created_at" :label="T('CreatedAt')" align="center"/>
28
+        <!--        <el-table-column prop="updated_at" label="更新时间" align="center"/>-->
29
+        <el-table-column :label="T('Actions')" align="center" class-name="table-actions" width="300" fixed="right">
30
+          <template #default="{row}">
31
+            <el-button @click="toEdit(row)">{{ T('Edit') }}</el-button>
32
+            <el-button type="danger" @click="del(row)">{{ T('Delete') }}</el-button>
33
+          </template>
34
+        </el-table-column>
35
+      </el-table>
36
+    </el-card>
37
+    <el-card class="list-page" shadow="hover">
38
+      <el-pagination background
39
+                     layout="prev, pager, next, sizes, jumper"
40
+                     :page-sizes="[10,20,50,100]"
41
+                     v-model:page-size="listQuery.page_size"
42
+                     v-model:current-page="listQuery.page"
43
+                     :total="listRes.total">
44
+      </el-pagination>
45
+    </el-card>
46
+    <el-dialog v-model="formVisible" width="800" :title="!formData.id?T('Create') :T('Update') " :close-on-click-modal="false">
47
+      <el-form class="dialog-form" ref="form" :model="formData" label-width="120px">
48
+        <el-form-item :label="T('AddressBookName')">
49
+          {{ props.collection.name }}
50
+        </el-form-item>
51
+        <el-form-item :label="T('Rule')" prop="rule" required>
52
+          <el-radio-group v-model="formData.rule">
53
+            <el-radio v-for="item in rules" :key="item.value" :label="item.value">
54
+              {{ item.label }}
55
+            </el-radio>
56
+          </el-radio-group>
57
+        </el-form-item>
58
+        <el-form-item :label="T('ShareTo')" prop="to_id" required>
59
+<!--          <el-input-number v-model="formData.to_id"></el-input-number>-->
60
+          <el-select v-model="formData.to_id">
61
+            <el-option
62
+                v-for="item in groupUsersList"
63
+                :key="item.id"
64
+                :label="item.username"
65
+                :value="item.id"
66
+                :disabled="!item.status"
67
+            ></el-option>
68
+          </el-select>
69
+        </el-form-item>
70
+        <el-form-item>
71
+          <el-button @click="formVisible = false">{{ T('Cancel') }}</el-button>
72
+          <el-button @click="submit" type="primary">{{ T('Submit') }}</el-button>
73
+        </el-form-item>
74
+      </el-form>
75
+    </el-dialog>
76
+  </div>
77
+</template>
78
+
79
+<script setup>
80
+
81
+  import { T } from '@/utils/i18n'
82
+  import { useRepositories } from '@/views/address_book/rule'
83
+  import { onActivated, onMounted, watch } from 'vue'
84
+
85
+  const props = defineProps({
86
+    collection: {
87
+      type: Object,
88
+      required: true,
89
+    },
90
+    is_my: {
91
+      type: Number,
92
+      default: 0,
93
+    },
94
+  })
95
+  const {
96
+    listRes,
97
+    listQuery,
98
+    getList,
99
+    handlerQuery,
100
+    del,
101
+    formVisible,
102
+    formData,
103
+    toEdit,
104
+    toAdd,
105
+    submit,
106
+    rules,
107
+    groupUsersList,
108
+    getGroupUsers,
109
+  } = useRepositories(props.is_my)
110
+
111
+  formData.collection_id = props.collection.id
112
+  formData.user_id = props.collection.user_id
113
+  listQuery.collection_id = props.collection.id
114
+
115
+  onMounted(getGroupUsers)
116
+  onMounted(getList)
117
+  onActivated(getList)
118
+
119
+  watch(() => listQuery.page, getList)
120
+
121
+  watch(() => listQuery.page_size, handlerQuery)
122
+
123
+</script>
124
+
125
+<style scoped lang="scss">
126
+
127
+</style>

+ 91 - 0
src/views/my/address_book/collection.vue

@@ -0,0 +1,91 @@
1
+<template>
2
+  <div>
3
+    <el-card class="list-query" shadow="hover">
4
+      <el-form inline label-width="80px">
5
+        <el-form-item>
6
+          <el-button type="primary" @click="handlerQuery">{{ T('Filter') }}</el-button>
7
+          <el-button type="danger" @click="toAdd">{{ T('Add') }}</el-button>
8
+        </el-form-item>
9
+      </el-form>
10
+    </el-card>
11
+    <el-card class="list-body" shadow="hover">
12
+      <el-tag type="danger" effect="light" style="margin-bottom: 10px">{{T('MyAddressBookTips')}}</el-tag>
13
+      <el-table :data="listRes.list" v-loading="listRes.loading" border>
14
+        <!--        <el-table-column prop="id" label="id" align="center"/>-->
15
+        <el-table-column prop="name" :label="T('Name')" align="center"/>
16
+        <el-table-column prop="created_at" :label="T('CreatedAt')" align="center"/>
17
+        <!--        <el-table-column prop="updated_at" label="更新时间" align="center"/>-->
18
+        <el-table-column :label="T('Actions')" align="center" class-name="table-actions" width="600" fixed="right">
19
+          <template #default="{row}">
20
+            <el-button type="primary" @click="showRules(row)">{{ T('ShareRules') }}</el-button>
21
+            <el-button @click="toEdit(row)">{{ T('Edit') }}</el-button>
22
+            <el-button type="danger" @click="del(row)">{{ T('Delete') }}</el-button>
23
+          </template>
24
+        </el-table-column>
25
+      </el-table>
26
+    </el-card>
27
+    <el-card class="list-page" shadow="hover">
28
+      <el-pagination background
29
+                     layout="prev, pager, next, sizes, jumper"
30
+                     :page-sizes="[10,20,50,100]"
31
+                     v-model:page-size="listQuery.page_size"
32
+                     v-model:current-page="listQuery.page"
33
+                     :total="listRes.total">
34
+      </el-pagination>
35
+    </el-card>
36
+    <el-dialog v-model="formVisible" width="800" :title="!formData.id?T('Create') :T('Update') ">
37
+      <el-form class="dialog-form" ref="form" :model="formData" label-width="120px">
38
+        <el-form-item :label="T('Name')" prop="name" required>
39
+          <el-input v-model="formData.name"></el-input>
40
+        </el-form-item>
41
+        <el-form-item>
42
+          <el-button @click="formVisible = false">{{ T('Cancel') }}</el-button>
43
+          <el-button @click="submit" type="primary">{{ T('Submit') }}</el-button>
44
+        </el-form-item>
45
+      </el-form>
46
+    </el-dialog>
47
+    <el-dialog v-model="rulesVisible" :title="T('ShareRules')" destroy-on-close top="5vh" width="80%">
48
+      <Rule :collection="clickRow" :is_my="1"></Rule>
49
+    </el-dialog>
50
+
51
+  </div>
52
+</template>
53
+
54
+<script setup>
55
+  import { T } from '@/utils/i18n'
56
+  import { ref } from 'vue'
57
+  import { useRepositories } from '@/views/address_book/collection'
58
+  import { onActivated, onMounted, watch } from 'vue'
59
+  import Rule from '@/views/address_book/rule.vue'
60
+
61
+  const {
62
+    listRes,
63
+    listQuery,
64
+    getList,
65
+    handlerQuery,
66
+    del,
67
+    formVisible,
68
+    formData,
69
+    toEdit,
70
+    toAdd,
71
+    submit,
72
+  } = useRepositories(1)
73
+
74
+  onMounted(getList)
75
+
76
+  watch(() => listQuery.page, getList)
77
+
78
+  watch(() => listQuery.page_size, handlerQuery)
79
+
80
+  const clickRow = ref({})
81
+  const rulesVisible = ref(false)
82
+  const showRules = (row) => {
83
+    clickRow.value = row
84
+    rulesVisible.value = true
85
+  }
86
+
87
+</script>
88
+
89
+<style scoped lang="scss">
90
+
91
+</style>

+ 39 - 18
src/views/my/address_book/index.vue

@@ -1,7 +1,13 @@
1 1
 <template>
2 2
   <div>
3 3
     <el-card class="list-query" shadow="hover">
4
-      <el-form inline label-width="80px">
4
+      <el-form inline label-width="120px">
5
+        <el-form-item :label="T('AddressBookName')">
6
+          <el-select v-model="listQuery.collection_id" clearable>
7
+            <el-option :value="0" :label="T('MyAddressBook')"></el-option>
8
+            <el-option v-for="c in collectionListRes.list" :key="c.id" :label="c.name" :value="c.id"></el-option>
9
+          </el-select>
10
+        </el-form-item>
5 11
         <el-form-item :label="T('Id')">
6 12
           <el-input v-model="listQuery.id" clearable></el-input>
7 13
         </el-form-item>
@@ -22,12 +28,24 @@
22 28
       <el-table :data="listRes.list" v-loading="listRes.loading" border>
23 29
         <el-table-column prop="id" label="id" align="center" width="200">
24 30
           <template #default="{row}">
25
-            <span>{{ row.id }} <el-icon @click="handleClipboard(row.id, $event)"><CopyDocument/></el-icon></span>
31
+            <div>
32
+              <PlatformIcons :name="platformList.find(p=>p.label===row.platform)?.icon" style="width: 20px;height: 20px;display: inline-block" color="var(--basicBlack)"/>
33
+              {{ row.id }}
34
+              <el-icon @click="handleClipboard(row.id, $event)">
35
+                <CopyDocument/>
36
+              </el-icon>
37
+            </div>
38
+          </template>
39
+        </el-table-column>
40
+        <el-table-column prop="collection_id" :label="T('AddressBookName')" align="center" width="150">
41
+          <template #default="{row}">
42
+            <span v-if="row.collection_id === 0">{{ T('MyAddressBook') }}</span>
43
+            <span v-else>{{ collectionListRes.list.find(c => c.id === row.collection_id)?.name }}</span>
26 44
           </template>
27 45
         </el-table-column>
28 46
         <el-table-column prop="username" :label="T('Username')" align="center" width="150"/>
29 47
         <el-table-column prop="hostname" :label="T('Hostname')" align="center" width="150"/>
30
-        <el-table-column prop="platform" :label="T('Platform')" align="center" width="120"/>
48
+        <!--        <el-table-column prop="platform" :label="T('Platform')" align="center" width="120"/>-->
31 49
         <el-table-column prop="tags" :label="T('Tags')" align="center"/>
32 50
         <!--        <el-table-column prop="created_at" label="创建时间" align="center"/>-->
33 51
         <!--        <el-table-column prop="updated_at" label="更新时间" align="center"/>-->
@@ -55,6 +73,12 @@
55 73
     </el-card>
56 74
     <el-dialog v-model="formVisible" width="800" :title="!formData.row_id?T('Create') :T('Update') ">
57 75
       <el-form class="dialog-form" ref="form" :model="formData" label-width="120px">
76
+        <el-form-item :label="T('AddressBookName')" required prop="collection_id">
77
+          <el-select v-model="formData.collection_id" clearable @change="changeCollection">
78
+            <el-option :value="0" :label="T('MyAddressBook')"></el-option>
79
+            <el-option v-for="c in collectionListRes.list" :key="c.id" :label="c.name" :value="c.id"></el-option>
80
+          </el-select>
81
+        </el-form-item>
58 82
         <el-form-item label="id" prop="id" required>
59 83
           <el-input v-model="formData.id"></el-input>
60 84
         </el-form-item>
@@ -90,7 +114,7 @@
90 114
         <el-form-item :label="T('Tags')" prop="tags">
91 115
           <el-select v-model="formData.tags" multiple>
92 116
             <el-option
93
-                v-for="item in tagList"
117
+                v-for="item in tagListRes.list"
94 118
                 :key="item.name"
95 119
                 :label="item.name"
96 120
                 :value="item.name"
@@ -130,7 +154,7 @@
130 154
 </template>
131 155
 
132 156
 <script setup>
133
-  import { onActivated, onMounted, reactive, ref, watch } from 'vue'
157
+  import { onActivated, onMounted, ref, watch } from 'vue'
134 158
   import { list as fetchTagList } from '@/api/tag'
135 159
   import { useRepositories } from '@/views/address_book'
136 160
   import { toWebClientLink } from '@/utils/webclient'
@@ -140,16 +164,10 @@
140 164
   import { connectByClient } from '@/utils/peer'
141 165
   import { handleClipboard } from '@/utils/clipboard'
142 166
   import { CopyDocument } from '@element-plus/icons'
167
+  import PlatformIcons from '@/components/icons/platform.vue'
143 168
 
169
+  const is_my = 1
144 170
   const appStore = useAppStore()
145
-  const tagList = ref([])
146
-  const fetchTagListData = async () => {
147
-    const res = await fetchTagList({ is_my: 1 }).catch(_ => false)
148
-    if (res) {
149
-      tagList.value = res.data.list
150
-    }
151
-  }
152
-  fetchTagListData()
153 171
 
154 172
   const {
155 173
     listRes,
@@ -166,17 +184,20 @@
166 184
     shareToWebClientVisible,
167 185
     shareToWebClientForm,
168 186
     toShowShare,
169
-  } = useRepositories()
170
-
171
-  listQuery.is_my = 1
172
-
187
+    // collectionListQuery,
188
+    collectionListRes,
189
+    getCollectionList,
190
+    tagListRes,
191
+    changeCollection,
192
+  } = useRepositories(is_my)
193
+
194
+  onMounted(getCollectionList)
173 195
   onMounted(getList)
174 196
   onActivated(getList)
175 197
 
176 198
   watch(() => listQuery.page, getList)
177 199
 
178 200
   watch(() => listQuery.page_size, handlerQuery)
179
-
180 201
 </script>
181 202
 
182 203
 <style scoped lang="scss">

+ 313 - 0
src/views/my/address_book/indexv2.vue

@@ -0,0 +1,313 @@
1
+<template>
2
+  <el-container class="address-book-con">
3
+    <el-aside width="200px" class="aside">
4
+      <el-select style="width: 100%;" v-model="listQuery.collection_id" filterable>
5
+        <el-option :value="0" :label="T('MyAddressBook')"></el-option>
6
+        <el-option v-for="c in collectionListRes.list" :key="c.id" :label="c.name" :value="c.id"></el-option>
7
+      </el-select>
8
+      <div class="aside-tags">
9
+        <div class="top" style="width: 100%">标签</div>
10
+        <div v-for="t in tagListRes.list"
11
+             :key="t.id" class="tag"
12
+             :class="{checked: checkedTags.includes(t.name)}"
13
+             :style="{backgroundColor: checkedTags.includes(t.name)?t.color:''}"
14
+             @click="toggleTag(t)"
15
+        >
16
+          <span class="dot" :style="{background:checkedTags.includes(t.name)?'#fff':t.color}"></span> <span>{{ t.name }}</span>
17
+        </div>
18
+
19
+      </div>
20
+    </el-aside>
21
+
22
+    <el-main class="con">
23
+      <el-card class="list-query" shadow="hover">
24
+        <el-form inline label-width="80px">
25
+<!--          <el-form-item :label="T('Name')">
26
+            <el-select v-model="listQuery.collection_id" clearable>
27
+              <el-option :value="0" :label="T('MyAddressBook')"></el-option>
28
+              <el-option v-for="c in collectionListRes.list" :key="c.id" :label="c.name" :value="c.id"></el-option>
29
+            </el-select>
30
+          </el-form-item>-->
31
+          <el-form-item :label="T('Id')">
32
+            <el-input v-model="listQuery.id" clearable></el-input>
33
+          </el-form-item>
34
+          <el-form-item :label="T('Username')">
35
+            <el-input v-model="listQuery.username" clearable></el-input>
36
+          </el-form-item>
37
+          <el-form-item :label="T('Hostname')">
38
+            <el-input v-model="listQuery.hostname" clearable></el-input>
39
+          </el-form-item>
40
+          <el-form-item>
41
+            <el-button type="primary" @click="handlerQuery">{{ T('Filter') }}</el-button>
42
+            <el-button type="danger" @click="toAdd">{{ T('Add') }}</el-button>
43
+          </el-form-item>
44
+        </el-form>
45
+      </el-card>
46
+      <el-card class="list-body" shadow="hover">
47
+        <!--      <el-tag type="danger" style="margin-bottom: 10px">不建议在此操作地址簿,可能会造成数据不同步</el-tag>-->
48
+        <el-table :data="listRes.list" v-loading="listRes.loading" border>
49
+          <el-table-column prop="id" label="id" align="center" width="200">
50
+            <template #default="{row}">
51
+              <span>{{ row.id }} <el-icon @click="handleClipboard(row.id, $event)"><CopyDocument/></el-icon></span>
52
+            </template>
53
+          </el-table-column>
54
+          <el-table-column prop="collection_id" :label="T('Name')" align="center" width="150">
55
+            <template #default="{row}">
56
+              <span v-if="row.collection_id === 0">{{ T('MyAddressBook') }}</span>
57
+              <span v-else>{{ collectionListRes.list.find(c => c.id === row.collection_id)?.name }}</span>
58
+            </template>
59
+          </el-table-column>
60
+          <el-table-column prop="username" :label="T('Username')" align="center" width="150"/>
61
+          <el-table-column prop="hostname" :label="T('Hostname')" align="center" width="150"/>
62
+          <el-table-column prop="platform" :label="T('Platform')" align="center" width="120"/>
63
+          <el-table-column prop="tags" :label="T('Tags')" align="center"/>
64
+          <!--        <el-table-column prop="created_at" label="创建时间" align="center"/>-->
65
+          <!--        <el-table-column prop="updated_at" label="更新时间" align="center"/>-->
66
+          <el-table-column prop="alias" :label="T('Alias')" align="center" width="150"/>
67
+          <el-table-column prop="hash" :label="T('Hash')" align="center" width="150" show-overflow-tooltip/>
68
+          <el-table-column :label="T('Actions')" align="center" class-name="table-actions" width="600" fixed="right">
69
+            <template #default="{row}">
70
+              <el-button type="success" @click="connectByClient(row.id)">{{ T('Link') }}</el-button>
71
+              <el-button v-if="appStore.setting.appConfig.web_client" type="success" @click="toWebClientLink(row)">Web Client</el-button>
72
+              <el-button v-if="appStore.setting.appConfig.web_client" type="primary" @click="toShowShare(row)">{{ T('ShareByWebClient') }}</el-button>
73
+              <el-button @click="toEdit(row)">{{ T('Edit') }}</el-button>
74
+              <el-button type="danger" @click="del(row)">{{ T('Delete') }}</el-button>
75
+            </template>
76
+          </el-table-column>
77
+        </el-table>
78
+      </el-card>
79
+      <el-card class="list-page" shadow="hover">
80
+        <el-pagination background
81
+                       layout="prev, pager, next, sizes, jumper"
82
+                       :page-sizes="[10,20,50,100]"
83
+                       v-model:page-size="listQuery.page_size"
84
+                       v-model:current-page="listQuery.page"
85
+                       :total="listRes.total">
86
+        </el-pagination>
87
+      </el-card>
88
+    </el-main>
89
+
90
+
91
+    <el-dialog v-model="formVisible" width="800" :title="!formData.row_id?T('Create') :T('Update') ">
92
+      <el-form class="dialog-form" ref="form" :model="formData" label-width="120px">
93
+        <el-form-item label="id" prop="id" required>
94
+          <el-input v-model="formData.id"></el-input>
95
+        </el-form-item>
96
+        <el-form-item :label="T('Username')" prop="username">
97
+          <el-input v-model="formData.username"></el-input>
98
+        </el-form-item>
99
+        <el-form-item :label="T('Alias')" prop="alias">
100
+          <el-input v-model="formData.alias"></el-input>
101
+        </el-form-item>
102
+        <el-form-item :label="T('Hash')" prop="hash">
103
+          <el-input v-model="formData.hash"></el-input>
104
+        </el-form-item>
105
+        <el-form-item :label="T('Hostname')" prop="hostname">
106
+          <el-input v-model="formData.hostname"></el-input>
107
+        </el-form-item>
108
+        <!--        <el-form-item :label="T('LoginName')" prop="loginName">
109
+                  <el-input v-model="formData.loginName"></el-input>
110
+                </el-form-item>-->
111
+        <!--        <el-form-item :label="T('Password')" prop="password">
112
+                          <el-input v-model="formData.password"></el-input>
113
+                        </el-form-item>-->
114
+        <el-form-item :label="T('Platform')" prop="platform">
115
+          <el-select v-model="formData.platform">
116
+            <el-option
117
+                v-for="item in platformList"
118
+                :key="item.value"
119
+                :label="item.label"
120
+                :value="item.value"
121
+            ></el-option>
122
+          </el-select>
123
+        </el-form-item>
124
+
125
+        <el-form-item :label="T('Tags')" prop="tags">
126
+          <el-select v-model="formData.tags" multiple>
127
+            <el-option
128
+                v-for="item in tagList"
129
+                :key="item.name"
130
+                :label="item.name"
131
+                :value="item.name"
132
+            ></el-option>
133
+          </el-select>
134
+        </el-form-item>
135
+        <!-- <el-form-item label="强制中继" prop="forceAlwaysRelay" required>
136
+                 <el-switch v-model="formData.forceAlwaysRelay"></el-switch>
137
+               </el-form-item>
138
+          <el-form-item label="在线" prop="online">
139
+                 <el-switch v-model="formData.online"></el-switch>
140
+               </el-form-item>
141
+               <el-form-item label="rdp端口" prop="rdpPort">
142
+                 <el-input v-model="formData.rdpPort"></el-input>
143
+               </el-form-item>
144
+               <el-form-item label="rdp用户名" prop="rdpUsername">
145
+                 <el-input v-model="formData.rdpUsername"></el-input>
146
+               </el-form-item>
147
+               <el-form-item label="同一服务器" prop="sameServer">
148
+                 <el-switch v-model="formData.sameServer"></el-switch>
149
+               </el-form-item>-->
150
+
151
+
152
+        <el-form-item>
153
+          <el-button @click="formVisible = false">{{ T('Cancel') }}</el-button>
154
+          <el-button @click="submit" type="primary">{{ T('Submit') }}</el-button>
155
+        </el-form-item>
156
+      </el-form>
157
+    </el-dialog>
158
+    <el-dialog v-model="shareToWebClientVisible" width="900" :close-on-click-modal="false">
159
+      <shareByWebClient :id="shareToWebClientForm.id"
160
+                        :hash="shareToWebClientForm.hash"
161
+                        @cancel="shareToWebClientVisible=false"
162
+                        @success=""/>
163
+    </el-dialog>
164
+  </el-container>
165
+</template>
166
+
167
+<script setup>
168
+  import { onActivated, onMounted, reactive, ref, watch } from 'vue'
169
+  import { list as fetchTagList } from '@/api/tag'
170
+  import { useRepositories } from '@/views/address_book'
171
+  import { useRepositories as useCollectionRepositories } from '@/views/address_book/collection'
172
+  import { useRepositories as useTagRepositories } from '@/views/tag/index'
173
+  import { toWebClientLink } from '@/utils/webclient'
174
+  import { T } from '@/utils/i18n'
175
+  import shareByWebClient from '@/views/address_book/components/shareByWebClient.vue'
176
+  import { useAppStore } from '@/store/app'
177
+  import { connectByClient } from '@/utils/peer'
178
+  import { handleClipboard } from '@/utils/clipboard'
179
+  import { CopyDocument } from '@element-plus/icons'
180
+
181
+  const appStore = useAppStore()
182
+  const tagList = ref([])
183
+  const fetchTagListData = async () => {
184
+    const res = await fetchTagList({ is_my: 1 }).catch(_ => false)
185
+    if (res) {
186
+      tagList.value = res.data.list
187
+    }
188
+  }
189
+  fetchTagListData()
190
+
191
+  const {
192
+    listRes,
193
+    listQuery,
194
+    getList,
195
+    handlerQuery,
196
+    del,
197
+    formVisible,
198
+    platformList,
199
+    formData,
200
+    toEdit,
201
+    toAdd,
202
+    submit,
203
+    shareToWebClientVisible,
204
+    shareToWebClientForm,
205
+    toShowShare,
206
+  } = useRepositories()
207
+
208
+  listQuery.is_my = 1
209
+
210
+  onMounted(getList)
211
+  onActivated(getList)
212
+
213
+  watch(() => listQuery.page, getList)
214
+
215
+  watch(() => listQuery.page_size, handlerQuery)
216
+  const {
217
+    listRes: collectionListRes,
218
+    listQuery: collectionListQuery,
219
+    getList: getCollectionList,
220
+  } = useCollectionRepositories()
221
+  collectionListQuery.is_my = 1
222
+  collectionListQuery.page_size = 999
223
+  onMounted(getCollectionList)
224
+
225
+  const alert = (msg) => {
226
+    window.alert(msg)
227
+  }
228
+
229
+  const {
230
+    listRes: tagListRes,
231
+    listQuery: tagListQuery,
232
+    getList: getTagList,
233
+  } = useTagRepositories()
234
+  tagListQuery.is_my = 1
235
+  tagListQuery.page_size = 999
236
+  onMounted(getTagList)
237
+  const checkedTags = ref(['111'])
238
+  const toggleTag = async (tag) => {
239
+    if (checkedTags.value.includes(tag.name)) {
240
+      checkedTags.value = checkedTags.value.filter(t => t !== tag.name)
241
+    } else {
242
+      checkedTags.value.push(tag.name)
243
+    }
244
+    // listQuery.tags = checkedTags.value.join(',')
245
+    // getList()
246
+  }
247
+</script>
248
+
249
+<style scoped lang="scss">
250
+.app-main {
251
+  border-left: 0;
252
+}
253
+
254
+.address-book-con {
255
+  height: calc(100vh - 120px);
256
+
257
+  .aside {
258
+    .aside-tags {
259
+      margin-top: 15px;
260
+      border: 1px solid #eee;
261
+      border-radius: 5px;
262
+      min-height: calc(100vh - 200px);
263
+      padding: 10px;
264
+      display: flex;
265
+      flex-wrap: wrap;
266
+      align-content: start;
267
+
268
+      .tag {
269
+        margin-right: 10px;
270
+        margin-bottom: 5px;
271
+        padding: 0 10px;
272
+        display: flex;
273
+        align-items: center;
274
+        justify-content: center;
275
+        line-height: 1;
276
+        height: 28px;
277
+        background-color: var(--tag-bg-color);
278
+        font-size: 14px;
279
+        border-radius: 8px;
280
+        cursor: pointer;
281
+
282
+        &.checked {
283
+          color: #fff;
284
+        }
285
+
286
+        span {
287
+          display: block;
288
+        }
289
+
290
+        .dot {
291
+          width: 8px;
292
+          height: 8px;
293
+          display: block;
294
+          border-radius: 50%;
295
+          line-height: 1;
296
+          font-size: 12px;
297
+          margin-right: 6px;
298
+        }
299
+
300
+      }
301
+    }
302
+  }
303
+
304
+  .con {
305
+    padding-top: 0;
306
+  }
307
+
308
+  .name-menu {
309
+    height: calc(100vh - 75px);
310
+  }
311
+
312
+}
313
+</style>

+ 31 - 12
src/views/my/tag/index.vue

@@ -1,7 +1,13 @@
1 1
 <template>
2 2
   <div>
3 3
     <el-card class="list-query" shadow="hover">
4
-      <el-form inline label-width="80px">
4
+      <el-form inline label-width="120px">
5
+        <el-form-item :label="T('AddressBookName')">
6
+          <el-select v-model="listQuery.collection_id" clearable>
7
+            <el-option :value="0" :label="T('MyAddressBook')"></el-option>
8
+            <el-option v-for="c in collectionListRes.list" :key="c.id" :label="c.name" :value="c.id"></el-option>
9
+          </el-select>
10
+        </el-form-item>
5 11
         <el-form-item>
6 12
           <el-button type="primary" @click="handlerQuery">{{ T('Filter') }}</el-button>
7 13
           <el-button type="danger" @click="toAdd">{{ T('Add') }}</el-button>
@@ -11,15 +17,17 @@
11 17
     <el-card class="list-body" shadow="hover">
12 18
       <el-table :data="listRes.list" v-loading="listRes.loading" border>
13 19
         <el-table-column prop="id" label="id" align="center"/>
20
+        <el-table-column prop="collection_id" :label="T('AddressBook')" align="center" width="150">
21
+          <template #default="{row}">
22
+            <span v-if="row.collection_id === 0">{{ T('MyAddressBook') }}</span>
23
+            <span v-else>{{ collectionListRes.list.find(c => c.id === row.collection_id)?.name }}</span>
24
+          </template>
25
+        </el-table-column>
14 26
         <el-table-column prop="name" :label="T('Name')" align="center"/>
15 27
         <el-table-column prop="color" :label="T('Color')" align="center">
16 28
           <template #default="{row}">
17 29
             <div class="colors">
18
-              <div style="background-color: #efeff2" class="colorbox">
19
-                <div :style="{backgroundColor: row.color}" class="dot">
20
-                </div>
21
-              </div>
22
-              <div style="background-color: #24252b" class="colorbox">
30
+              <div style="background-color: var(--tag-bg-color)" class="colorbox">
23 31
                 <div :style="{backgroundColor: row.color}" class="dot">
24 32
                 </div>
25 33
               </div>
@@ -47,18 +55,19 @@
47 55
     </el-card>
48 56
     <el-dialog v-model="formVisible" :title="!formData.id?T('Create'):T('Update')" width="800">
49 57
       <el-form class="dialog-form" ref="form" :model="formData" label-width="120px">
58
+        <el-form-item :label="T('AddressBookName')">
59
+          <el-select v-model="formData.collection_id" clearable>
60
+            <el-option :value="0" :label="T('MyAddressBook')"></el-option>
61
+            <el-option v-for="c in collectionListRes.list" :key="c.id" :label="c.name" :value="c.id"></el-option>
62
+          </el-select>
63
+        </el-form-item>
50 64
         <el-form-item :label="T('Name')" prop="name" required>
51 65
           <el-input v-model="formData.name"></el-input>
52 66
         </el-form-item>
53 67
         <el-form-item :label="T('Color')" prop="color" required>
54 68
           <el-color-picker v-model="formData.color" show-alpha @active-change="activeChange"></el-color-picker>
55
-          <br>
56 69
           <div class="colors">
57
-            <div style="background-color: #efeff2" class="colorbox">
58
-              <div :style="{backgroundColor: currentColor}" class="dot">
59
-              </div>
60
-            </div>
61
-            <div style="background-color: #24252b" class="colorbox">
70
+            <div style="background-color: var(--tag-bg-color)" class="colorbox">
62 71
               <div :style="{backgroundColor: currentColor}" class="dot">
63 72
               </div>
64 73
             </div>
@@ -77,6 +86,7 @@
77 86
   import { onMounted, watch, onActivated } from 'vue'
78 87
   import { useRepositories } from '@/views/tag'
79 88
   import { T } from '@/utils/i18n'
89
+  import { useRepositories as useCollectionRepositories } from '@/views/address_book/collection'
80 90
 
81 91
   const {
82 92
     listRes,
@@ -102,6 +112,15 @@
102 112
 
103 113
   watch(() => listQuery.page_size, handlerQuery)
104 114
 
115
+  const {
116
+    listRes: collectionListRes,
117
+    listQuery: collectionListQuery,
118
+    getList: getCollectionList,
119
+  } = useCollectionRepositories()
120
+  collectionListQuery.is_my = 1
121
+  collectionListQuery.page_size = 999
122
+  onMounted(getCollectionList)
123
+
105 124
 </script>
106 125
 
107 126
 <style scoped lang="scss">

+ 48 - 4
src/views/tag/index.js

@@ -3,8 +3,18 @@ import { create, list, remove, update } from '@/api/tag'
3 3
 import { ElMessage, ElMessageBox } from 'element-plus'
4 4
 import { useRoute } from 'vue-router'
5 5
 import { T } from '@/utils/i18n'
6
+import { loadAllUsers } from '@/global'
7
+import { useRepositories as useCollectionRepositories } from '@/views/address_book/collection'
6 8
 
7
-export function useRepositories () {
9
+export function useRepositories (is_my = 0) {
10
+  const { allUsers, getAllUsers } = loadAllUsers()
11
+
12
+  const {
13
+    listRes: collectionListRes,
14
+    listQuery: collectionListQuery,
15
+    getList: getCollectionList,
16
+  } = useCollectionRepositories(is_my)
17
+  collectionListQuery.page_size = 9999
8 18
   //获取query
9 19
   const route = useRoute()
10 20
   const user_id = route.query?.user_id
@@ -14,8 +24,9 @@ export function useRepositories () {
14 24
   const listQuery = reactive({
15 25
     page: 1,
16 26
     page_size: 10,
17
-    is_my: 0,
27
+    is_my,
18 28
     user_id: user_id ? parseInt(user_id) : null,
29
+    collection_id: null,
19 30
   })
20 31
 
21 32
   const flutterColor2rgba = (color) => {
@@ -108,6 +119,7 @@ export function useRepositories () {
108 119
     name: '',
109 120
     color: 0,
110 121
     user_id: 0,
122
+    collection_id: 0,
111 123
   })
112 124
   const currentColor = ref('')
113 125
   const activeChange = (c) => {
@@ -121,16 +133,24 @@ export function useRepositories () {
121 133
     formData.name = row.name
122 134
     formData.color = row.color
123 135
     formData.user_id = row.user_id
136
+    formData.collection_id = row.collection_id
137
+    collectionListQuery.user_id = row.user_id
138
+    getCollectionList()
124 139
   }
125 140
   const toAdd = () => {
126 141
     formVisible.value = true
127 142
     formData.id = 0
128 143
     formData.name = ''
129
-    formData.color = 0
130
-    formData.user_id = 0
144
+    formData.color = ''
145
+    formData.user_id = null
146
+    formData.collection_id = 0
131 147
   }
132 148
   const submit = async () => {
133 149
     console.log(formData)
150
+    if (!formData.color) {
151
+      ElMessage.error('请选择颜色')
152
+      return
153
+    }
134 154
     const api = formData.id ? update : create
135 155
     const data = {
136 156
       ...formData,
@@ -144,6 +164,25 @@ export function useRepositories () {
144 164
       getList()
145 165
     }
146 166
   }
167
+
168
+  const changeQueryUser = async (val) => {
169
+    listQuery.collection_id = null
170
+    if (!val) {
171
+      collectionListRes.list = []
172
+    } else {
173
+      collectionListQuery.user_id = val
174
+      getCollectionList()
175
+    }
176
+  }
177
+  const changeUser = async (val) => {
178
+    formData.collection_id = 0
179
+    if (!val) {
180
+      collectionListRes.list = []
181
+    } else {
182
+      collectionListQuery.user_id = val
183
+      getCollectionList()
184
+    }
185
+  }
147 186
   return {
148 187
     listRes,
149 188
     listQuery,
@@ -157,5 +196,10 @@ export function useRepositories () {
157 196
     submit,
158 197
     activeChange,
159 198
     currentColor,
199
+
200
+    collectionListRes,
201
+    allUsers, getAllUsers,
202
+    changeQueryUser,
203
+    changeUser,
160 204
   }
161 205
 }

+ 38 - 28
src/views/tag/index.vue

@@ -1,9 +1,9 @@
1 1
 <template>
2 2
   <div>
3 3
     <el-card class="list-query" shadow="hover">
4
-      <el-form inline label-width="80px">
4
+      <el-form inline label-width="120px">
5 5
         <el-form-item :label="T('Owner')">
6
-          <el-select v-model="listQuery.user_id" clearable>
6
+          <el-select v-model="listQuery.user_id" clearable @change="changeQueryUser">
7 7
             <el-option
8 8
                 v-for="item in allUsers"
9 9
                 :key="item.id"
@@ -12,6 +12,12 @@
12 12
             ></el-option>
13 13
           </el-select>
14 14
         </el-form-item>
15
+        <el-form-item :label="T('AddressBookName')">
16
+          <el-select v-model="listQuery.collection_id" clearable>
17
+            <el-option :value="0" :label="T('MyAddressBook')"></el-option>
18
+            <el-option v-for="c in collectionListRes.list" :key="c.id" :label="c.name" :value="c.id"></el-option>
19
+          </el-select>
20
+        </el-form-item>
15 21
         <el-form-item>
16 22
           <el-button type="primary" @click="handlerQuery">{{ T('Filter') }}</el-button>
17 23
           <el-button type="danger" @click="toAdd">{{ T('Add') }}</el-button>
@@ -26,15 +32,17 @@
26 32
             <span v-if="row.user_id"> <el-tag>{{ allUsers?.find(u => u.id === row.user_id)?.username }}</el-tag> </span>
27 33
           </template>
28 34
         </el-table-column>
35
+        <el-table-column prop="collection_id" :label="T('AddressBookName')" align="center" width="150">
36
+          <template #default="{row}">
37
+            <span v-if="row.collection_id === 0">{{ T('MyAddressBook') }}</span>
38
+            <span v-else>{{ row.collection?.name }}</span>
39
+          </template>
40
+        </el-table-column>
29 41
         <el-table-column prop="name" :label="T('Name')" align="center"/>
30 42
         <el-table-column prop="color" :label="T('Color')" align="center">
31 43
           <template #default="{row}">
32 44
             <div class="colors">
33
-              <div style="background-color: #efeff2" class="colorbox">
34
-                <div :style="{backgroundColor: row.color}" class="dot">
35
-                </div>
36
-              </div>
37
-              <div style="background-color: #24252b" class="colorbox">
45
+              <div style="background-color: var(--tag-bg-color)" class="colorbox">
38 46
                 <div :style="{backgroundColor: row.color}" class="dot">
39 47
                 </div>
40 48
               </div>
@@ -43,7 +51,7 @@
43 51
         </el-table-column>
44 52
         <el-table-column prop="created_at" :label="T('CreatedAt')" align="center"/>
45 53
         <el-table-column prop="updated_at" :label="T('UpdatedAt')" align="center"/>
46
-        <el-table-column label="操作" align="center">
54
+        <el-table-column :label="T('Actions')" align="center" width="250">
47 55
           <template #default="{row}">
48 56
             <el-button @click="toEdit(row)">{{ T('Edit') }}</el-button>
49 57
             <el-button type="danger" @click="del(row)">{{ T('Delete') }}</el-button>
@@ -62,33 +70,34 @@
62 70
     </el-card>
63 71
     <el-dialog v-model="formVisible" :title="!formData.id?T('Create'):T('Update')" width="800">
64 72
       <el-form class="dialog-form" ref="form" :model="formData" label-width="120px">
73
+        <el-form-item :label="T('Owner')" prop="user_id" required>
74
+          <el-select v-model="formData.user_id" @change="changeUser">
75
+            <el-option
76
+                v-for="item in allUsers"
77
+                :key="item.id"
78
+                :label="item.username"
79
+                :value="item.id"
80
+            ></el-option>
81
+          </el-select>
82
+        </el-form-item>
83
+        <el-form-item :label="T('AddressBookName')" prop="collection_id" required>
84
+          <el-select v-model="formData.collection_id" clearable>
85
+            <el-option :value="0" :label="T('MyAddressBook')"></el-option>
86
+            <el-option v-for="c in collectionListRes.list" :key="c.id" :label="c.name" :value="c.id"></el-option>
87
+          </el-select>
88
+        </el-form-item>
65 89
         <el-form-item :label="T('Name')" prop="name" required>
66 90
           <el-input v-model="formData.name"></el-input>
67 91
         </el-form-item>
68 92
         <el-form-item :label="T('Color')" prop="color" required>
69 93
           <el-color-picker v-model="formData.color" show-alpha @active-change="activeChange"></el-color-picker>
70
-          <br>
71 94
           <div class="colors">
72
-            <div style="background-color: #efeff2" class="colorbox">
73
-              <div :style="{backgroundColor: currentColor}" class="dot">
74
-              </div>
75
-            </div>
76
-            <div style="background-color: #24252b" class="colorbox">
95
+            <div style="background-color: var(--tag-bg-color)" class="colorbox">
77 96
               <div :style="{backgroundColor: currentColor}" class="dot">
78 97
               </div>
79 98
             </div>
80 99
           </div>
81 100
         </el-form-item>
82
-        <el-form-item :label="T('Owner')" prop="user_id" required>
83
-          <el-select v-model="formData.user_id">
84
-            <el-option
85
-                v-for="item in allUsers"
86
-                :key="item.id"
87
-                :label="item.username"
88
-                :value="item.id"
89
-            ></el-option>
90
-          </el-select>
91
-        </el-form-item>
92 101
         <el-form-item>
93 102
           <el-button @click="formVisible = false">{{ T('Cancel') }}</el-button>
94 103
           <el-button @click="submit" type="primary">{{ T('Submit') }}</el-button>
@@ -100,12 +109,9 @@
100 109
 
101 110
 <script setup>
102 111
   import { onMounted, reactive, watch, ref, onActivated } from 'vue'
103
-  import { loadAllUsers } from '@/global'
104 112
   import { useRepositories } from '@/views/tag/index'
105 113
   import { T } from '@/utils/i18n'
106 114
 
107
-  const { allUsers, getAllUsers } = loadAllUsers()
108
-  getAllUsers()
109 115
   const {
110 116
     listRes,
111 117
     listQuery,
@@ -119,8 +125,12 @@
119 125
     submit,
120 126
     activeChange,
121 127
     currentColor,
128
+    collectionListRes,
129
+    allUsers, getAllUsers,
130
+    changeQueryUser,
131
+    changeUser,
122 132
   } = useRepositories()
123
-
133
+  onMounted(getAllUsers)
124 134
   onMounted(getList)
125 135
   onActivated(getList)
126 136