├── .env.development
├── .env.production
├── .gitignore
├── LICENSE
├── README.md
├── index.html
├── jsconfig.json
├── package-lock.json
├── package.json
├── public
└── favicon.ico
├── src
├── App.vue
├── api
│ ├── address_book.js
│ ├── address_book_collection.js
│ ├── address_book_collection_rule.js
│ ├── audit.js
│ ├── config.js
│ ├── device_group.js
│ ├── file.js
│ ├── group.js
│ ├── login.js
│ ├── login_log.js
│ ├── my
│ │ ├── address_book.js
│ │ ├── address_book_collection.js
│ │ ├── address_book_collection_rule.js
│ │ ├── login_log.js
│ │ ├── peer.js
│ │ ├── share_record.js
│ │ └── tag.js
│ ├── oauth.js
│ ├── peer.js
│ ├── rustdesk.js
│ ├── share_record.js
│ ├── tag.js
│ ├── user.js
│ └── user_token.js
├── assets
│ ├── github.png
│ ├── google.png
│ ├── logo.png
│ ├── oidc.png
│ └── webauth.png
├── components
│ ├── changePwdDialog.vue
│ ├── form
│ │ ├── address.vue
│ │ └── upload
│ │ │ ├── imageUpload.vue
│ │ │ ├── imagesUpload.vue
│ │ │ ├── local.js
│ │ │ └── oss.js
│ └── icons
│ │ └── platform.vue
├── global.js
├── layout
│ ├── components
│ │ ├── aside.vue
│ │ ├── header.vue
│ │ ├── menu
│ │ │ ├── index.vue
│ │ │ └── item.vue
│ │ ├── setting
│ │ │ └── index.vue
│ │ └── tags
│ │ │ └── index.vue
│ └── index.vue
├── main.js
├── permission.js
├── router
│ └── index.js
├── store
│ ├── app.js
│ ├── index.js
│ ├── router.js
│ ├── tags.js
│ └── user.js
├── styles
│ └── style.scss
├── utils
│ ├── auth.js
│ ├── clipboard.js
│ ├── common_options.js
│ ├── file.js
│ ├── i18n.js
│ ├── i18n
│ │ ├── en.json
│ │ ├── es.json
│ │ ├── fr.json
│ │ ├── ko.json
│ │ ├── ru.json
│ │ ├── zh_CN.json
│ │ └── zh_TW.json
│ ├── pca.json
│ ├── peer.js
│ ├── request.js
│ ├── time.js
│ ├── webclient.js
│ └── webclient
│ │ ├── message.ts
│ │ ├── rendezvous.ts
│ │ └── websock.ts
└── views
│ ├── address_book
│ ├── collection.js
│ ├── collection.vue
│ ├── components
│ │ └── shareByWebClient.vue
│ ├── index.js
│ ├── index.vue
│ ├── rule.js
│ └── rule.vue
│ ├── audit
│ ├── connList.vue
│ ├── fileList.vue
│ └── reponsitories.js
│ ├── error-page
│ └── 404.vue
│ ├── group
│ ├── deviceGroupList.vue
│ └── index.vue
│ ├── index
│ └── index.vue
│ ├── login
│ ├── log.js
│ ├── log.vue
│ └── login.vue
│ ├── my
│ ├── address_book
│ │ ├── collection.vue
│ │ ├── index.vue
│ │ └── indexv2.vue
│ ├── info.vue
│ ├── login_log
│ │ └── index.vue
│ ├── peer
│ │ └── index.vue
│ ├── share_record
│ │ └── index.vue
│ └── tag
│ │ └── index.vue
│ ├── oauth
│ ├── bind.vue
│ ├── index.vue
│ └── login.vue
│ ├── peer
│ ├── createABForm.vue
│ └── index.vue
│ ├── register
│ └── index.vue
│ ├── rustdesk
│ ├── always_use_relay.vue
│ ├── blacklist.vue
│ ├── blocklist.vue
│ ├── control.vue
│ ├── must_login.vue
│ ├── options.js
│ ├── relay_servers.vue
│ └── usage.vue
│ ├── share_record
│ ├── index.js
│ └── index.vue
│ ├── tag
│ ├── index.js
│ └── index.vue
│ └── user
│ ├── composables
│ ├── edit.js
│ └── index.js
│ ├── edit.vue
│ ├── index.vue
│ ├── token.js
│ └── token.vue
└── vite.config.js
/.env.development:
--------------------------------------------------------------------------------
1 | ENV = 'development'
2 |
3 | VITE_DEV_PORT = 8888
4 | VITE_SERVER_API = /api/admin
5 | VITE_SERVER_PATH = http://127.0.0.1:21114
6 |
7 |
--------------------------------------------------------------------------------
/.env.production:
--------------------------------------------------------------------------------
1 | ENV = 'production'
2 |
3 | VITE_DEV_PORT = 5000
4 | VITE_SERVER_API =/api/admin
5 | VITE_SERVER_PATH = http://127.0.0.1:5000
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | dist/
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | **/*.log
8 |
9 |
10 | # Editor directories and files
11 | .idea
12 | .vscode
13 | *.suo
14 | *.ntvs*
15 | *.njsproj
16 | *.sln
17 | *.local
18 |
19 | yarn.lock
20 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016-2021 vue-manage-system
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # RustDesk API Web
2 | # 基于 Vue3 + Element Plus 的后台, 适用于 [RustDesk API](https://github.com/lejianwen/rustdesk-api)
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | # 安装步骤
15 |
16 | ```shell
17 | git clone https://github.com/lejianwen/rustdesk-api-web
18 | cd rustdesk-api-web
19 | npm install
20 |
21 | // 本地开发
22 | npm run dev
23 |
24 | // 打包
25 | npm run build
26 |
27 | ```
28 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Rustdesk API Admin
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "./",
4 | "paths": {
5 | "@/*": [
6 | "src/*"
7 | ]
8 | }
9 | },
10 | "exclude": [
11 | "node_modules"
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hello-vue3",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "dev": "vite --host",
6 | "build": "vite build",
7 | "serve": "vite preview"
8 | },
9 | "dependencies": {
10 | "axios": "1.7.4",
11 | "clipboard": "2.0.4",
12 | "element-plus": "^2.8.2",
13 | "fast-sha256": "^1.3.0",
14 | "js-cookie": "^3.0.1",
15 | "marked": "^15.0.4",
16 | "normalize.css": "^8.0.1",
17 | "nprogress": "^0.2.0",
18 | "pinia": "2.2.8",
19 | "vue": "3.5.13",
20 | "vue-router": "^4.0.12"
21 | },
22 | "devDependencies": {
23 | "@element-plus/icons": "0.0.11",
24 | "@vitejs/plugin-vue": "5.2.1",
25 | "dotenv": "^10.0.0",
26 | "qs": "^6.10.2",
27 | "sass": "^1.43.4",
28 | "sass-loader": "^12.3.0",
29 | "ts-proto": "^1.141.1",
30 | "vite": "6.0.9"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lejianwen/rustdesk-api-web/3084e40039b9421422766299296f8483cbf89561/public/favicon.ico
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
18 |
20 |
--------------------------------------------------------------------------------
/src/api/address_book.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function list (params) {
4 | return request({
5 | url: '/address_book/list',
6 | params,
7 | })
8 | }
9 |
10 | export function detail (id) {
11 | return request({
12 | url: `/address_book/detail/${id}`,
13 | })
14 | }
15 |
16 | export function create (data) {
17 | return request({
18 | url: '/address_book/create',
19 | method: 'post',
20 | data,
21 | })
22 | }
23 |
24 | export function update (data) {
25 | return request({
26 | url: '/address_book/update',
27 | method: 'post',
28 | data,
29 | })
30 | }
31 |
32 | export function remove (data) {
33 | return request({
34 | url: '/address_book/delete',
35 | method: 'post',
36 | data,
37 | })
38 | }
39 |
40 | export function batchCreate (data) {
41 | return request({
42 | url: '/address_book/batchCreate',
43 | method: 'post',
44 | data,
45 | })
46 | }
47 |
48 | export function shareByWebClient (data) {
49 | return request({
50 | url: '/address_book/shareByWebClient',
51 | method: 'post',
52 | data,
53 | })
54 | }
55 |
56 | export function batchCreateFromPeers (data) {
57 | return request({
58 | url: '/address_book/batchCreateFromPeers',
59 | method: 'post',
60 | data,
61 | })
62 | }
63 |
64 |
--------------------------------------------------------------------------------
/src/api/address_book_collection.js:
--------------------------------------------------------------------------------
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 |
40 |
--------------------------------------------------------------------------------
/src/api/address_book_collection_rule.js:
--------------------------------------------------------------------------------
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 | }
47 |
--------------------------------------------------------------------------------
/src/api/audit.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function list (params) {
4 | return request({
5 | url: '/audit_conn/list',
6 | params,
7 | })
8 | }
9 |
10 | export function remove (data) {
11 | return request({
12 | url: '/audit_conn/delete',
13 | method: 'post',
14 | data,
15 | })
16 | }
17 |
18 | export function batchDelete (data) {
19 | return request({
20 | url: '/audit_conn/batchDelete',
21 | method: 'post',
22 | data,
23 | })
24 | }
25 |
26 | export function fileList (params) {
27 | return request({
28 | url: '/audit_file/list',
29 | params,
30 | })
31 | }
32 |
33 | export function fileRemove (data) {
34 | return request({
35 | url: '/audit_file/delete',
36 | method: 'post',
37 | data,
38 | })
39 | }
40 |
41 | export function fileBatchDelete (data) {
42 | return request({
43 | url: '/audit_file/batchDelete',
44 | method: 'post',
45 | data,
46 | })
47 | }
48 |
--------------------------------------------------------------------------------
/src/api/config.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function server () {
4 | return request({
5 | url: '/config/server',
6 | method: 'get',
7 | })
8 | }
9 |
10 | export function app () {
11 | return request({
12 | url: '/config/app',
13 | method: 'get',
14 | })
15 | }
16 |
17 | export function admin () {
18 | return request({
19 | url: '/config/admin',
20 | method: 'get',
21 | })
22 | }
23 |
--------------------------------------------------------------------------------
/src/api/device_group.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function list (params) {
4 | return request({
5 | url: '/device_group/list',
6 | params,
7 | })
8 | }
9 |
10 | export function detail (id) {
11 | return request({
12 | url: `/device_group/detail/${id}`,
13 | })
14 | }
15 |
16 | export function create (data) {
17 | return request({
18 | url: '/device_group/create',
19 | method: 'post',
20 | data,
21 | })
22 | }
23 |
24 | export function update (data) {
25 | return request({
26 | url: '/device_group/update',
27 | method: 'post',
28 | data,
29 | })
30 | }
31 |
32 | export function remove (data) {
33 | return request({
34 | url: '/device_group/delete',
35 | method: 'post',
36 | data,
37 | })
38 | }
39 |
--------------------------------------------------------------------------------
/src/api/file.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function ossToken () {
4 | return request({
5 | url: '/file/oss_token',
6 | })
7 | }
8 |
--------------------------------------------------------------------------------
/src/api/group.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function list (params) {
4 | return request({
5 | url: '/group/list',
6 | params,
7 | })
8 | }
9 |
10 | export function detail (id) {
11 | return request({
12 | url: `/group/detail/${id}`,
13 | })
14 | }
15 |
16 | export function create (data) {
17 | return request({
18 | url: '/group/create',
19 | method: 'post',
20 | data,
21 | })
22 | }
23 |
24 | export function update (data) {
25 | return request({
26 | url: '/group/update',
27 | method: 'post',
28 | data,
29 | })
30 | }
31 |
32 | export function remove (data) {
33 | return request({
34 | url: '/group/delete',
35 | method: 'post',
36 | data,
37 | })
38 | }
39 |
--------------------------------------------------------------------------------
/src/api/login.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function loginOptions () {
4 | return request({
5 | url: '/login-options',
6 | method: 'get',
7 | })
8 | }
9 |
10 | export function oidcAuth (data) {
11 | return request({
12 | url: '/oidc/auth',
13 | method: 'post',
14 | data,
15 | })
16 | }
17 |
18 | export function oidcQuery (params) {
19 | return request({
20 | url: '/oidc/auth-query',
21 | method: 'get',
22 | params,
23 | })
24 | }
25 |
26 | export function captcha () {
27 | return request({
28 | url: '/captcha',
29 | method: 'get',
30 | })
31 | }
32 |
--------------------------------------------------------------------------------
/src/api/login_log.js:
--------------------------------------------------------------------------------
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 | }
17 |
18 | export function batchDelete (data) {
19 | return request({
20 | url: '/login_log/batchDelete',
21 | method: 'post',
22 | data,
23 | })
24 | }
25 |
--------------------------------------------------------------------------------
/src/api/my/address_book.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function list (params) {
4 | return request({
5 | url: '/my/address_book/list',
6 | params,
7 | })
8 | }
9 |
10 | export function batchCreateFromPeers (data) {
11 | return request({
12 | url: '/my/address_book/batchCreateFromPeers',
13 | method: 'post',
14 | data,
15 | })
16 | }
17 |
18 | export function batchUpdateTags (data) {
19 | return request({
20 | url: '/my/address_book/batchUpdateTags',
21 | method: 'post',
22 | data,
23 | })
24 | }
25 |
26 | export function create (data) {
27 | return request({
28 | url: '/my/address_book/create',
29 | method: 'post',
30 | data,
31 | })
32 | }
33 |
34 | export function update (data) {
35 | return request({
36 | url: '/my/address_book/update',
37 | method: 'post',
38 | data,
39 | })
40 | }
41 |
42 | export function remove (data) {
43 | return request({
44 | url: '/my/address_book/delete',
45 | method: 'post',
46 | data,
47 | })
48 | }
49 |
--------------------------------------------------------------------------------
/src/api/my/address_book_collection.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function list (params) {
4 | return request({
5 | url: '/my/address_book_collection/list',
6 | params,
7 | })
8 | }
9 |
10 | export function create (data) {
11 | return request({
12 | url: '/my/address_book_collection/create',
13 | method: 'post',
14 | data,
15 | })
16 | }
17 |
18 | export function update (data) {
19 | return request({
20 | url: '/my/address_book_collection/update',
21 | method: 'post',
22 | data,
23 | })
24 | }
25 |
26 | export function remove (data) {
27 | return request({
28 | url: '/my/address_book_collection/delete',
29 | method: 'post',
30 | data,
31 | })
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/src/api/my/address_book_collection_rule.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function list (params) {
4 | return request({
5 | url: '/my/address_book_collection_rule/list',
6 | params,
7 | })
8 | }
9 |
10 | export function create (data) {
11 | return request({
12 | url: '/my/address_book_collection_rule/create',
13 | method: 'post',
14 | data,
15 | })
16 | }
17 |
18 | export function update (data) {
19 | return request({
20 | url: '/my/address_book_collection_rule/update',
21 | method: 'post',
22 | data,
23 | })
24 | }
25 |
26 | export function remove (data) {
27 | return request({
28 | url: '/my/address_book_collection_rule/delete',
29 | method: 'post',
30 | data,
31 | })
32 | }
33 |
34 | export function batchCreate (data) {
35 | return request({
36 | url: '/my/address_book_collection_rule/batchCreate',
37 | method: 'post',
38 | data,
39 | })
40 | }
41 |
--------------------------------------------------------------------------------
/src/api/my/login_log.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function list (params) {
4 | return request({
5 | url: '/my/login_log/list',
6 | params,
7 | })
8 | }
9 |
10 | export function remove (data) {
11 | return request({
12 | url: '/my/login_log/delete',
13 | method: 'post',
14 | data,
15 | })
16 | }
17 |
18 | export function batchDelete (data) {
19 | return request({
20 | url: '/my/login_log/batchDelete',
21 | method: 'post',
22 | data,
23 | })
24 | }
25 |
--------------------------------------------------------------------------------
/src/api/my/peer.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function list (params) {
4 | return request({
5 | url: '/my/peer/list',
6 | params,
7 | })
8 | }
9 |
--------------------------------------------------------------------------------
/src/api/my/share_record.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function list (params) {
4 | return request({
5 | url: '/my/share_record/list',
6 | params,
7 | })
8 | }
9 |
10 | export function remove (data) {
11 | return request({
12 | url: '/my/share_record/delete',
13 | method: 'post',
14 | data,
15 | })
16 | }
17 |
18 | export function batchDelete (data) {
19 | return request({
20 | url: '/my/share_record/batchDelete',
21 | method: 'post',
22 | data,
23 | })
24 | }
25 |
--------------------------------------------------------------------------------
/src/api/my/tag.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function list (params) {
4 | return request({
5 | url: '/my/tag/list',
6 | params,
7 | })
8 | }
9 |
10 | export function create (data) {
11 | return request({
12 | url: '/my/tag/create',
13 | method: 'post',
14 | data,
15 | })
16 | }
17 |
18 | export function update (data) {
19 | return request({
20 | url: '/my/tag/update',
21 | method: 'post',
22 | data,
23 | })
24 | }
25 |
26 | export function remove (data) {
27 | return request({
28 | url: '/my/tag/delete',
29 | method: 'post',
30 | data,
31 | })
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/src/api/oauth.js:
--------------------------------------------------------------------------------
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 | }
77 |
--------------------------------------------------------------------------------
/src/api/peer.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function list (params) {
4 | return request({
5 | url: '/peer/list',
6 | params,
7 | })
8 | }
9 |
10 | export function detail (id) {
11 | return request({
12 | url: `/peer/detail/${id}`,
13 | })
14 | }
15 |
16 | export function create (data) {
17 | return request({
18 | url: '/peer/create',
19 | method: 'post',
20 | data,
21 | })
22 | }
23 |
24 | export function update (data) {
25 | return request({
26 | url: '/peer/update',
27 | method: 'post',
28 | data,
29 | })
30 | }
31 |
32 | export function remove (data) {
33 | return request({
34 | url: '/peer/delete',
35 | method: 'post',
36 | data,
37 | })
38 | }
39 |
40 | export function batchRemove (data) {
41 | return request({
42 | url: '/peer/batchDelete',
43 | method: 'post',
44 | data,
45 | })
46 | }
47 |
48 | export function simpleData (data) {
49 | return request({
50 | url: '/peer/simpleData',
51 | method: 'post',
52 | data,
53 | })
54 | }
55 |
--------------------------------------------------------------------------------
/src/api/rustdesk.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function list (params) {
4 | return request({
5 | url: '/rustdesk/cmdList',
6 | params,
7 | })
8 | }
9 |
10 | export function create (data) {
11 | return request({
12 | url: '/rustdesk/cmdCreate',
13 | method: 'post',
14 | data,
15 | })
16 | }
17 |
18 | export function update (data) {
19 | return request({
20 | url: '/rustdesk/cmdUpdate',
21 | method: 'post',
22 | data,
23 | })
24 | }
25 |
26 | export function remove (data) {
27 | return request({
28 | url: '/rustdesk/cmdDelete',
29 | method: 'post',
30 | data,
31 | })
32 | }
33 |
34 | export function sendCmd (data) {
35 | return request({
36 | url: '/rustdesk/sendCmd',
37 | method: 'post',
38 | data,
39 | })
40 | }
41 |
--------------------------------------------------------------------------------
/src/api/share_record.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function list (params) {
4 | return request({
5 | url: '/share_record/list',
6 | params,
7 | })
8 | }
9 |
10 | export function remove (data) {
11 | return request({
12 | url: '/share_record/delete',
13 | method: 'post',
14 | data,
15 | })
16 | }
17 |
18 | export function batchDelete (data) {
19 | return request({
20 | url: '/share_record/batchDelete',
21 | method: 'post',
22 | data,
23 | })
24 | }
25 |
--------------------------------------------------------------------------------
/src/api/tag.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function list (params) {
4 | return request({
5 | url: '/tag/list',
6 | params,
7 | })
8 | }
9 |
10 | export function detail (id) {
11 | return request({
12 | url: `/tag/detail/${id}`,
13 | })
14 | }
15 |
16 | export function create (data) {
17 | return request({
18 | url: '/tag/create',
19 | method: 'post',
20 | data,
21 | })
22 | }
23 |
24 | export function update (data) {
25 | return request({
26 | url: '/tag/update',
27 | method: 'post',
28 | data,
29 | })
30 | }
31 |
32 | export function remove (data) {
33 | return request({
34 | url: '/tag/delete',
35 | method: 'post',
36 | data,
37 | })
38 | }
39 |
40 |
--------------------------------------------------------------------------------
/src/api/user.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function login (data) {
4 | return request({
5 | url: '/login',
6 | method: 'post',
7 | data,
8 | })
9 | }
10 |
11 | export function current () {
12 | return request({
13 | url: '/user/current',
14 | method: 'get',
15 | })
16 | }
17 |
18 | export function list (params) {
19 | return request({
20 | url: '/user/list',
21 | params,
22 | })
23 | }
24 |
25 | export function detail (id) {
26 | return request({
27 | url: `/user/detail/${id}`,
28 | })
29 | }
30 |
31 | export function create (data) {
32 | return request({
33 | url: '/user/create',
34 | method: 'post',
35 | data,
36 | })
37 | }
38 |
39 | export function update (data) {
40 | return request({
41 | url: '/user/update',
42 | method: 'post',
43 | data,
44 | })
45 | }
46 |
47 | export function remove (data) {
48 | return request({
49 | url: '/user/delete',
50 | method: 'post',
51 | data,
52 | })
53 | }
54 |
55 | export function changePwd (data) {
56 | return request({
57 | url: '/user/changePwd',
58 | method: 'post',
59 | data,
60 | })
61 | }
62 |
63 | export function changeCurPwd (data) {
64 | return request({
65 | url: '/user/changeCurPwd',
66 | method: 'post',
67 | data,
68 | })
69 | }
70 |
71 | export function myOauth () {
72 | return request({
73 | url: '/user/myOauth',
74 | method: 'post',
75 | })
76 | }
77 |
78 | export function myPeer (params) {
79 | return request({
80 | url: '/user/myPeer',
81 | params,
82 | })
83 | }
84 |
85 | export function groupUsers (data) {
86 | return request({
87 | url: '/user/groupUsers',
88 | method: 'post',
89 | data,
90 | })
91 | }
92 |
93 | export function register (data) {
94 | return request({
95 | url: '/user/register',
96 | method: 'post',
97 | data,
98 | })
99 | }
100 |
--------------------------------------------------------------------------------
/src/api/user_token.js:
--------------------------------------------------------------------------------
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 | }
17 |
18 | export function batchRemove (data) {
19 | return request({
20 | url: '/user_token/batchDelete',
21 | method: 'post',
22 | data,
23 | })
24 | }
25 |
--------------------------------------------------------------------------------
/src/assets/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lejianwen/rustdesk-api-web/3084e40039b9421422766299296f8483cbf89561/src/assets/github.png
--------------------------------------------------------------------------------
/src/assets/google.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lejianwen/rustdesk-api-web/3084e40039b9421422766299296f8483cbf89561/src/assets/google.png
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lejianwen/rustdesk-api-web/3084e40039b9421422766299296f8483cbf89561/src/assets/logo.png
--------------------------------------------------------------------------------
/src/assets/oidc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lejianwen/rustdesk-api-web/3084e40039b9421422766299296f8483cbf89561/src/assets/oidc.png
--------------------------------------------------------------------------------
/src/assets/webauth.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lejianwen/rustdesk-api-web/3084e40039b9421422766299296f8483cbf89561/src/assets/webauth.png
--------------------------------------------------------------------------------
/src/components/changePwdDialog.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | {{ T('Cancel') }}
15 | {{ T('Confirm') }}
16 |
17 |
18 |
19 |
20 |
21 |
118 |
119 |
122 |
--------------------------------------------------------------------------------
/src/components/form/address.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
96 |
97 |
100 |
--------------------------------------------------------------------------------
/src/components/form/upload/imageUpload.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
148 |
149 |
199 |
--------------------------------------------------------------------------------
/src/components/form/upload/local.js:
--------------------------------------------------------------------------------
1 | import { getToken } from '@/utils/auth'
2 |
3 | export function useLocal (beforeUp, host) {
4 | const fileUploadData = {}
5 | const fileUploadHost = host
6 | const headers = { 'api-token': getToken() }
7 | const beforeFileUpload = async (file) => {
8 | if (beforeUp) {
9 | const br = await beforeUp(file)
10 | if (!br) { return Promise.reject() }
11 | }
12 | return Promise.resolve()
13 | }
14 |
15 | return {
16 | fileUploadData,
17 | fileUploadHost,
18 | beforeFileUpload,
19 | headers,
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/components/form/upload/oss.js:
--------------------------------------------------------------------------------
1 | import { ossToken } from '@/api/file'
2 | import { random_filename } from '@/utils/file'
3 | import { reactive, ref } from 'vue'
4 |
5 | export function useOss (beforeUp, multiple) {
6 | let fileUploadData = reactive({
7 | policy: '',
8 | OSSAccessKeyId: '',
9 | success_action_status: '200', // 让服务端返回200,不然,默认会返回204
10 | callback: '',
11 | signature: '',
12 | 'x:dir': '',
13 | })
14 | const fileExpire = ref(0)
15 | const fileUploadHost = ref('')
16 |
17 | const beforeFileUpload = async (file) => {
18 | if (beforeUp) {
19 | const br = await beforeUp(file)
20 | if (!br) { return Promise.reject() }
21 | }
22 |
23 | const now = Date.parse(new Date()) / 1000
24 | if (fileExpire.value < now) {
25 | const res = await ossToken()
26 | const obj = JSON.parse(res.data)
27 | fileExpire.value = parseInt(obj['expire'])
28 | fileUploadData.policy = obj['policy']
29 | fileUploadData.OSSAccessKeyId = obj['accessid']
30 | fileUploadData.callback = obj['callback']
31 | fileUploadData.signature = obj['signature']
32 | fileUploadData['x:dir'] = obj['dir']
33 | fileUploadHost.value = obj['host']
34 | }
35 | //多选文件时需要这个,不然每个文件上传的都是一样的data
36 | if (multiple) {
37 | await new Promise(resolve => {
38 | setTimeout(() => { resolve() }, 50)
39 | })
40 | }
41 | fileUploadData['x:origin_filename'] = file.name
42 | fileUploadData.key = fileUploadData['x:dir'] + random_filename(file.name)
43 | return Promise.resolve()
44 | }
45 |
46 | return {
47 | fileUploadHost,
48 | fileUploadData,
49 | beforeFileUpload,
50 | headers: {},
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/components/icons/platform.vue:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 |
29 |
30 |
35 |
--------------------------------------------------------------------------------
/src/global.js:
--------------------------------------------------------------------------------
1 | import { ref, reactive, watch } from 'vue'
2 | import { list as fetchUsers } from '@/api/user'
3 |
4 | // todo 缓存所有用户信息
5 | export function loadAllUsers () {
6 | const allUsers = ref([])
7 | const getAllUsers = async () => {
8 | const res = await fetchUsers({ page_size: 9999 }).catch(_ => false)
9 | if (res) {
10 | allUsers.value = res.data.list
11 | }
12 | }
13 |
14 | return {
15 | allUsers,
16 | getAllUsers,
17 | }
18 |
19 | }
20 |
21 |
--------------------------------------------------------------------------------
/src/layout/components/aside.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
15 |
16 |
18 |
--------------------------------------------------------------------------------
/src/layout/components/header.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
40 |
41 |
70 |
73 |
--------------------------------------------------------------------------------
/src/layout/components/menu/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 |
44 |
45 |
55 |
57 |
--------------------------------------------------------------------------------
/src/layout/components/menu/item.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 | {{ T(route.meta?.title) || T(route.name) }}
11 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | {{ T(parseRoute(route).meta?.title) || T(parseRoute(route).name) }}
22 |
23 |
24 |
25 |
52 |
53 |
55 |
--------------------------------------------------------------------------------
/src/layout/components/setting/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
20 |
35 |
52 |
53 |
54 |
55 |
56 |
84 |
85 |
113 |
--------------------------------------------------------------------------------
/src/layout/components/tags/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 | {{ T(t.title) }}
11 |
12 |
13 |
14 |
73 |
74 |
84 |
--------------------------------------------------------------------------------
/src/layout/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
11 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
46 |
47 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import 'element-plus/dist/index.css'
3 | import App from './App.vue'
4 | import ElementPlus from 'element-plus'
5 | import zhCn from 'element-plus/es/locale/lang/zh-cn'
6 | import { router } from '@/router'
7 | import 'normalize.css/normalize.css'
8 | import { pinia } from '@/store'
9 | import '@/permission'
10 | import 'element-plus/theme-chalk/dark/css-vars.css'
11 | import '@/styles/style.scss'
12 | import * as ElementIcons from '@element-plus/icons'
13 |
14 | const app = createApp(App)
15 | app.use(ElementPlus, { locale: zhCn })
16 | app.use(pinia)
17 | app.use(router)
18 | for (let icon in ElementIcons){
19 | app.component("ElIcon" +icon ,ElementIcons[icon])
20 | }
21 | app.mount('#app')
22 |
--------------------------------------------------------------------------------
/src/permission.js:
--------------------------------------------------------------------------------
1 | import { router } from '@/router'
2 | import { useRouteStore } from '@/store/router'
3 | import { useUserStore } from '@/store/user'
4 | import { getToken } from '@/utils/auth'
5 | import { pinia } from '@/store'
6 | import NProgress from 'nprogress' // progress bar
7 | import 'nprogress/nprogress.css'
8 | import { useAppStore } from '@/store/app' // progress bar style
9 | import { T } from '@/utils/i18n'
10 |
11 | NProgress.configure({ showSpinner: false }) // NProgress Configuration
12 |
13 | const whiteList = ['/login', '/register']
14 | const routeStore = useRouteStore(pinia)
15 | const appStore = useAppStore(pinia)
16 | appStore.getAdminConfig()
17 | router.beforeEach(async (to, from, next) => {
18 |
19 | document.title = T(to.meta?.title) + ' - ' + appStore.setting.title
20 | NProgress.start()
21 |
22 | const token = getToken()
23 | if (!token) {
24 | //无token,跳转到登录
25 | if (whiteList.indexOf(to.path) !== -1) {
26 | next()
27 | } else {
28 | next(`/login?redirect=${to.path}`)
29 | }
30 |
31 | } else {
32 | //有token
33 |
34 | const userStore = useUserStore(pinia)
35 |
36 | if (!userStore.route_names.length) {
37 | const info = await userStore.info()
38 | if (!info) {
39 | userStore.logout()
40 | next(`/login?redirect=${to.path}`)
41 | } else {
42 | next({ ...to, replace: true })
43 | }
44 | }/* else if (to.path === '/404') {
45 | next({path: to.redirectedFrom?.fullPath, replace: true})
46 | }*/ else {
47 | next()
48 | }
49 | }
50 | })
51 |
52 | router.afterEach(() => {
53 | NProgress.done()
54 | })
55 |
--------------------------------------------------------------------------------
/src/store/app.js:
--------------------------------------------------------------------------------
1 | import { defineStore, acceptHMRUpdate } from 'pinia'
2 | import logo from '@/assets/logo.png'
3 | import zhCn from 'element-plus/es/locale/lang/zh-cn'
4 | import en from 'element-plus/es/locale/lang/en'
5 | import ko from 'element-plus/es/locale/lang/ko'
6 | import ru from 'element-plus/es/locale/lang/ru'
7 | import fr from 'element-plus/es/locale/lang/fr'
8 | import es from 'element-plus/es/locale/lang/es'
9 | import zhTw from 'element-plus/es/locale/lang/zh-tw'
10 | import { admin, app } from '@/api/config'
11 |
12 | const langs = {
13 | 'zh-CN': { name: '中文', value: zhCn, sideBarWidth: '210px' },
14 | 'en': { name: 'English', value: en, sideBarWidth: '230px' },
15 | 'fr': { name: 'Français', value: fr, sideBarWidth: '280px' },
16 | 'ko': { name: '한국어', value: ko, sideBarWidth: '230px' },
17 | 'ru': { name: 'русский', value: ru, sideBarWidth: '250px' },
18 | 'es': { name: 'español', value: es, sideBarWidth: '280px' },
19 | 'zh-TW': { name: '中文繁体', value: zhTw, sideBarWidth: '210px' },
20 | }
21 | const defaultLang = localStorage.getItem('lang') || navigator.language || 'zh-CN'
22 | export const useAppStore = defineStore({
23 | id: 'App',
24 | state: () => ({
25 | setting: {
26 | title: 'Rustdesk API Admin',
27 | hello: '',
28 | sideIsCollapse: false,
29 | logo,
30 | langs: langs,
31 | lang: defaultLang,
32 | locale: langs[defaultLang] ? langs[defaultLang] : langs['en'],
33 | appConfig: {
34 | web_client: 1,
35 | },
36 | },
37 | }),
38 |
39 | actions: {
40 | sideCollapse () {
41 | this.setting.sideIsCollapse = !this.setting.sideIsCollapse
42 | },
43 | setLang (lang) {
44 | console.log('setLang', lang)
45 | this.setting.lang = lang
46 | this.setting.locale = langs[lang]
47 | localStorage.setItem('lang', lang)
48 | },
49 | changeLang (v) {
50 | this.setLang(v)
51 | },
52 | loadConfig () {
53 | this.getAppConfig()
54 | this.getAdminConfig()
55 | },
56 | getAppConfig () {
57 | console.log('getAppConfig')
58 | return app().then(res => {
59 | this.setting.appConfig = res.data
60 | })
61 | },
62 | getAdminConfig () {
63 | console.log('getAdminConfig')
64 | return admin().then(res => {
65 | this.replaceAdminTitle(res.data.title)
66 | this.setting.hello = res.data.hello
67 | })
68 | },
69 | replaceAdminTitle(newTitle){
70 | document.title = document.title.replace(`- ${this.setting.title}`, `- ${newTitle}`)
71 | this.setting.title = newTitle
72 | },
73 | },
74 | })
75 |
76 | if (import.meta.hot) {
77 | import.meta.hot.accept(acceptHMRUpdate(useAppStore, import.meta.hot))
78 | }
79 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import { createPinia } from 'pinia'
2 |
3 | export const pinia = createPinia()
4 |
--------------------------------------------------------------------------------
/src/store/router.js:
--------------------------------------------------------------------------------
1 | import { defineStore, acceptHMRUpdate } from 'pinia'
2 | import { lastRoutes, asyncRoutes, router } from '@/router'
3 |
4 | function filterRoute (routes, enableNames) {
5 | return routes.filter(route => {
6 | if (route.children && route.children.length) {
7 | return enableNames.includes(route.name) || route.children.some(r => enableNames.includes(r.name))
8 | } else {
9 | return enableNames.includes(route.name)
10 | }
11 | }).map(route => {
12 | if (route.children && route.children.length) {
13 | return {
14 | ...route,
15 | children: filterRoute(route.children, enableNames),
16 | }
17 | } else {
18 | return { ...route }
19 | }
20 | })
21 | }
22 |
23 | export const useRouteStore = defineStore({
24 | id: 'router',
25 | state: () => ({
26 | routes: [],
27 | activeRoute: '',
28 | loaded: 0,
29 | keepAlive: [],
30 | }),
31 | actions: {
32 | addRoutes (accessRouteNames) {
33 | if (accessRouteNames.includes('*')) {
34 | this.routes = asyncRoutes
35 | } else {
36 | this.routes = filterRoute(asyncRoutes, accessRouteNames)
37 | }
38 |
39 | this.routes.forEach(route => {
40 | router.addRoute(route)
41 | })
42 | lastRoutes.forEach(route => {
43 | router.addRoute(route)
44 | })
45 | this.addKeepAlive(this.routes)
46 | },
47 | addKeepAlive (route) {
48 | if (route instanceof Array) {
49 | route.forEach(r => {
50 | this.addKeepAlive(r)
51 | })
52 | } else if (route.children && route.children.length) {
53 | this.addKeepAlive(route.children)
54 | } else if (route.meta?.keepAlive && !this.keepAlive.includes(route.name)) {
55 | this.keepAlive.push(route.name)
56 | }
57 | },
58 |
59 | },
60 | })
61 |
62 | if (import.meta.hot) {
63 | import.meta.hot.accept(acceptHMRUpdate(useRouteStore, import.meta.hot))
64 | }
65 |
--------------------------------------------------------------------------------
/src/store/tags.js:
--------------------------------------------------------------------------------
1 | import { defineStore, acceptHMRUpdate } from 'pinia'
2 |
3 | export const useTagsStore = defineStore({
4 | id: 'tags',
5 | state: () => ({
6 | tags: [],
7 | cached: [],
8 | }),
9 | actions: {
10 | initTags () {
11 | // this.tags.push(
12 | // {
13 | // name: 'Home',
14 | // path: '/Home',
15 | // title: '首页',
16 | // active: false,
17 | // closeable: false,
18 | // keepAlive: false,
19 | // })
20 | },
21 | addTag (route) {
22 | const tags = this.tags
23 | if (tags.find(t => t.name === route.name)) {
24 | tags.forEach(t => t.active = false)
25 | tags.find(t => t.name === route.name).active = true
26 | } else {
27 | tags.forEach(t => t.active = false)
28 | if (route.meta?.keepAlive) {
29 | this.addCachedTag(route.name)
30 | }
31 | tags.push({
32 | name: route.name,
33 | path: route.fullPath,
34 | title: route.meta?.title || route.name,
35 | active: true,
36 | closeable: true,
37 | keepAlive: route.meta?.keepAlive,
38 | })
39 |
40 | }
41 | this.$patch({ tags })
42 | },
43 | removeTag (tag) {
44 | let tags = this.tags
45 | if (tags.find(t => t.name === tag.name)) {
46 | const index = tags.findIndex(t => t.name === tag.name)
47 | if (index > -1) {
48 | if (tags[index].keepAlive) {
49 | this.removeCachedTag(tags[index].name)
50 | }
51 | tags.splice(index, 1)
52 | }
53 | }
54 | this.$patch({ tags })
55 | },
56 | addCachedTag (name) {
57 | if (!this.cached.includes(name)) {
58 | this.cached.push(name)
59 | }
60 | },
61 | removeCachedTag (name) {
62 | if (this.cached.includes(name)) {
63 | this.cached.splice(this.cached.indexOf(name), 1)
64 | }
65 |
66 | },
67 |
68 | },
69 | })
70 |
71 | if (import.meta.hot) {
72 | import.meta.hot.accept(acceptHMRUpdate(useTagsStore, import.meta.hot))
73 | }
74 |
--------------------------------------------------------------------------------
/src/store/user.js:
--------------------------------------------------------------------------------
1 | import { defineStore, acceptHMRUpdate } from 'pinia'
2 | import { current, login } from '@/api/user'
3 | import { setToken, removeToken, setCode, removeCode } from '@/utils/auth'
4 | import { useRouteStore } from '@/store/router'
5 | import { useAppStore } from '@/store/app'
6 | import { oidcAuth, oidcQuery } from '@/api/login'
7 |
8 | export const useUserStore = defineStore({
9 | id: 'user',
10 | state: () => ({
11 | nickname: '',
12 | username: '',
13 | email: '',
14 | token: '',
15 | role: '',
16 | avatar: '',
17 | route_names: [],
18 | }),
19 |
20 | actions: {
21 | logout () {
22 | removeToken()
23 | removeCode()
24 | this.$patch({
25 | name: '',
26 | role: {},
27 | })
28 | },
29 |
30 | saveUserData (userData) {
31 | // useAppStore().getAppConfig()
32 | setToken(userData.token)
33 | //
34 | localStorage.setItem('user_info', JSON.stringify({ name: userData.username }))
35 | this.$patch({
36 | ...userData,
37 | })
38 | if (userData.route_names && userData.route_names.length) {
39 | useRouteStore().addRoutes(userData.route_names)
40 | }
41 | },
42 |
43 | async login (form) {
44 | const res = await login(form).catch(e => e)
45 | console.log('login', res)
46 | if (!res.code) {
47 | useAppStore().loadConfig()
48 | const userData = res.data
49 | this.saveUserData(userData)
50 | return userData
51 | } else {
52 | return Promise.reject(res)
53 | }
54 | },
55 | async info () {
56 | const res = await current().catch(_ => false)
57 | if (res) {
58 | useAppStore().loadConfig()
59 | const userData = res.data
60 | setToken(userData.token)
61 | this.$patch({
62 | ...userData,
63 | })
64 | useRouteStore().addRoutes(userData.route_names)
65 | return userData
66 | }
67 | return false
68 | },
69 | async oidc (provider, platform, browser) {
70 | // oidc data need to be implement
71 | const data = {
72 | deviceInfo: {
73 | name: navigator.userAgent, // 使用浏览器的 User-Agent 作为设备名
74 | os: platform, // 获取操作系统信息
75 | type: 'webadmin', // any vaule
76 | },
77 | id: `${platform}-${browser}`,
78 | op: provider, // 传入的 provider
79 | uuid: '',//crypto.randomUUID(), // 自动生成 UUID
80 | }
81 | const res = await oidcAuth(data).catch(_ => false)
82 | if (res) {
83 | const { code, url } = res.data
84 | setCode(code)
85 | if (provider == 'webauth') {
86 | window.open(url)
87 | } else {
88 | window.location.href = url
89 | }
90 | }
91 | },
92 | async query (code) {
93 | const params = { 'code': code, uuid: '' }
94 | const res = await oidcQuery(params).catch(_ => false)
95 | if (res) {
96 | removeCode()
97 | useAppStore().loadConfig()
98 | const userData = res.data
99 | this.saveUserData(userData)
100 | return userData
101 | }
102 | return false
103 | },
104 | },
105 | })
106 |
107 | if (import.meta.hot) {
108 | import.meta.hot.accept(acceptHMRUpdate(useUserStore, import.meta.hot))
109 | }
110 |
--------------------------------------------------------------------------------
/src/styles/style.scss:
--------------------------------------------------------------------------------
1 | $basicBlack: #000000;
2 | $basicWhite: #ffffff;
3 |
4 | $primaryColor: #409eff;
5 |
6 | :root {
7 | --basicBlack: #000000;
8 | --basicWhite: #ffffff;
9 | --primaryColor: #409eff;
10 | --tag-bg-color: #efefef;
11 | }
12 |
13 | .list-body {
14 | margin: 10px 0;
15 | }
16 |
17 | .dialog-form {
18 | max-width: 600px;
19 | margin: 20px auto;
20 | }
21 |
22 | .list-query {
23 | .el-select {
24 | --el-select-width: 160px;
25 | }
26 |
27 | .el-input {
28 | --el-input-width: 160px;
29 | }
30 | }
31 |
32 | .table-actions {
33 | .el-button {
34 | margin-top: 5px;
35 | margin-bottom: 5px;
36 | margin-left: 5px;
37 | margin-right: 5px;
38 | }
39 | }
40 |
41 | html.dark {
42 | /* 自定义深色背景颜色 */
43 | //--el-bg-color: #626aef;
44 | --tag-bg-color: #24252b;
45 | --basicBlack: #fff;
46 | }
47 |
--------------------------------------------------------------------------------
/src/utils/auth.js:
--------------------------------------------------------------------------------
1 | const TokenKey = 'access_token'
2 | const OidcCode = 'oidc_code'
3 | const OidcCodeExpiry = 'oidc_code_expiry';
4 |
5 | export function getToken () {
6 | return localStorage.getItem(TokenKey)
7 | }
8 |
9 | export function setToken (token) {
10 | localStorage.setItem(`wc-option:local:access_token`, token)
11 | return localStorage.setItem(TokenKey, token)
12 | }
13 |
14 | export function removeToken () {
15 | return localStorage.removeItem(TokenKey)
16 | }
17 |
18 | // 设置 code,并存储当前时间戳(单位:毫秒)
19 | export function setCode(code) {
20 | const now = Date.now(); // 当前时间戳(毫秒)
21 | const expiry = now + 60 * 1000; // 60 秒后过期
22 |
23 | localStorage.setItem(OidcCode, code); // 存储 code
24 | localStorage.setItem(OidcCodeExpiry, expiry); // 存储过期时间戳
25 | }
26 |
27 | // 获取 code,如果已过期则删除并返回 null
28 | export function getCode() {
29 | const expiry = localStorage.getItem(OidcCodeExpiry); // 获取过期时间戳
30 | const now = Date.now(); // 当前时间戳
31 |
32 | if (expiry && now > parseInt(expiry)) {
33 | // 如果已过期,删除 code 和过期时间
34 | removeCode();
35 | return null;
36 | }
37 | return localStorage.getItem(OidcCode); // 返回 code(如果未过期)
38 | }
39 |
40 | // 删除 code 和过期时间
41 | export function removeCode() {
42 | localStorage.removeItem(OidcCode);
43 | localStorage.removeItem(OidcCodeExpiry);
44 | }
45 |
--------------------------------------------------------------------------------
/src/utils/clipboard.js:
--------------------------------------------------------------------------------
1 | import Clipboard from 'clipboard'
2 | import { ElMessage } from 'element-plus'
3 | import { T } from '@/utils/i18n'
4 |
5 | export function handleClipboard (text, event) {
6 | const clipboard = new Clipboard(event.target.toString(), {
7 | text: () => text,
8 | })
9 | clipboard.on('success', () => {
10 | ElMessage.success(T('CopySuccess'))
11 | clipboard.destroy()
12 | })
13 | clipboard.on('error', () => {
14 | ElMessage.error(T('CopyFailed'))
15 | clipboard.destroy()
16 | })
17 | clipboard.onClick(event)
18 | }
19 |
20 | export function copyImage (targetNode) {
21 | if (window.getSelection) {
22 | // chrome等主流浏览器
23 | var selection = window.getSelection()
24 | selection.removeAllRanges()
25 | var range = document.createRange()
26 | range.selectNode(targetNode)
27 | selection.addRange(range)
28 | } else if (document.body.createTextRange) {
29 | console.log('IE')
30 | // ie
31 | const range = document.body.createTextRange()
32 | range.moveToElementText(targetNode)
33 | range.select()
34 | }
35 | document.execCommand('copy')
36 | }
37 |
--------------------------------------------------------------------------------
/src/utils/common_options.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | export const ENABLE_STATUS = 1
4 | export const DISABLE_STATUS = 2
5 |
--------------------------------------------------------------------------------
/src/utils/file.js:
--------------------------------------------------------------------------------
1 | export function get_suffix (filename) {
2 | var pos = filename.lastIndexOf('.')
3 | var suffix = ''
4 | if (pos !== -1) {
5 | suffix = filename.substring(pos)
6 | }
7 | return suffix
8 | }
9 |
10 | export function random_string (len) {
11 | len = len || 32
12 | var chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'
13 | var maxPos = chars.length
14 | var pwd = ''
15 | for (let i = 0; i < len; i++) {
16 | pwd += chars.charAt(Math.floor(Math.random() * maxPos))
17 | }
18 | return pwd
19 | }
20 |
21 | export function random_filename (filename) {
22 | var suffix = get_suffix(filename)
23 | var time = new Date()
24 | var time2 = new Date('2020/01/01')
25 | return Math.ceil((time.getTime() - time2.getTime()) / 1000) + '_' + random_string(10) + suffix
26 | }
27 |
28 | export function utf8_to_b64 (str) {
29 | return window.btoa(unescape(encodeURIComponent(str)))
30 | }
31 |
32 | export function b64_to_utf8 (str) {
33 | return decodeURIComponent(escape(window.atob(str)))
34 | }
35 |
36 | export function jsonToCsv (data) {
37 | let csv = ''
38 | let keys = Object.keys(data[0])
39 | csv += keys.join(',') + '\n'
40 | data.forEach(row => {
41 | csv += keys.map(key => `"${row[key]}"`).join(',') + '\n'
42 | })
43 | return new Blob([csv], { type: 'text/csv' })
44 | }
45 |
46 | export function downBlob (blob, filename) {
47 | const url = window.URL.createObjectURL(blob)
48 | const a = document.createElement('a')
49 | a.href = url
50 | a.download = filename
51 | document.body.appendChild(a)
52 | a.click()
53 | setTimeout(() => {
54 | window.URL.revokeObjectURL(url)
55 | document.body.removeChild(a)
56 | })
57 | }
58 |
59 | export function sizeFormat (size) {
60 | if (size < 1024) {
61 | return size + 'B'
62 | } else if (size < 1024 * 1024) {
63 | return (size / 1024).toFixed(2) + 'KB'
64 | } else if (size < 1024 * 1024 * 1024) {
65 | return (size / 1024 / 1024).toFixed(2) + 'MB'
66 | } else {
67 | return (size / 1024 / 1024 / 1024).toFixed(2) + 'GB'
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/utils/i18n.js:
--------------------------------------------------------------------------------
1 | import en from '@/utils/i18n/en.json'
2 | import fr from '@/utils/i18n/fr.json'
3 | import zhCN from '@/utils/i18n/zh_CN.json'
4 | import ko from '@/utils/i18n/ko.json'
5 | import ru from '@/utils/i18n/ru.json'
6 | import es from '@/utils/i18n/es.json'
7 | import zhTW from '@/utils/i18n/zh_TW.json'
8 | import { useAppStore } from '@/store/app'
9 |
10 | const trans = {
11 | 'en': en,
12 | 'fr': fr,
13 | 'zh-CN': zhCN,
14 | 'ko': ko,
15 | 'ru': ru,
16 | 'es': es,
17 | 'zh-TW': zhTW,
18 | }
19 | export function T (key, params, num = 0) {
20 | const appStore = useAppStore()
21 | const lang = appStore.setting.lang
22 | const tran = trans[lang]?.[key]
23 | if (!tran) {
24 | return key
25 | }
26 | const msg = num > 1 ? (tran.Other ? tran.Other : tran.One) : tran.One
27 | //msg 是这样 {name} is name
28 | //params 是这样 {name: 'zhangsan'}
29 | //替换
30 | return msg.replace(/{(\w+)}/g, function (match, key) {
31 | return params[key] || match
32 | })
33 | }
34 |
--------------------------------------------------------------------------------
/src/utils/peer.js:
--------------------------------------------------------------------------------
1 | export const connectByClient = (id) => {
2 | //不新开窗口打开url protocol ,格式是 rustdesk://
3 | // window.open(`rustdesk://${row.id}`)
4 | let a = document.createElement('a')
5 | a.href = `rustdesk://${id}`
6 | a.target = '_self'
7 | a.click()
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/src/utils/request.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import { ElMessage } from 'element-plus'
3 | import { getToken, removeToken } from '@/utils/auth'
4 | import { useUserStore } from '@/store/user'
5 | import { pinia } from '@/store'
6 | import { useAppStore } from '@/store/app'
7 |
8 | // create an axios instance
9 | const service = axios.create({
10 | baseURL: import.meta.env.VITE_SERVER_API,
11 | withCredentials: true, // send cookies when cross-domain requests
12 | timeout: 50000, // request timeout
13 | })
14 |
15 | // request interceptor
16 | service.interceptors.request.use(
17 | config => {
18 | if (!config.headers) {
19 | config.headers = {}
20 | }
21 | const userStore = useUserStore(pinia)
22 |
23 | const token = userStore.token || getToken()
24 | if (token) {
25 | config.headers['api-token'] = token
26 | }
27 |
28 | const app = useAppStore()
29 | const lang = app.setting.lang
30 | if (lang) {
31 | // console.log('lang', lang)
32 | config.headers['Accept-Language'] = lang
33 | }
34 |
35 | return config
36 | },
37 | error => {
38 | // do something with request error
39 | return Promise.reject(error)
40 | },
41 | )
42 |
43 | // response interceptor
44 | service.interceptors.response.use(
45 | /**
46 | * If you want to get http information such as headers or status
47 | * Please return response => response
48 | */
49 |
50 | /**
51 | * Determine the request status by custom code
52 | * Here is just an example
53 | * You can also judge the status by HTTP Status Code
54 | */
55 | response => {
56 | const res = response.data
57 |
58 | // for the endpoint /login-options
59 | // I'm not sure if this is a good idea
60 | if (Array.isArray(res)) {
61 | return res;
62 | }
63 |
64 | // if the custom code is not 20000, it is judged as an error.
65 | if (res.code !== 0) {
66 | ElMessage({
67 | message: res.message || 'error',
68 | type: 'error',
69 | duration: 5 * 1000,
70 | })
71 |
72 | if (res.code === 403) {
73 | removeToken()
74 | window.location.reload()
75 | }
76 | return Promise.reject(res)
77 | } else {
78 | return res
79 | }
80 | },
81 | error => {
82 | if (error.code === 'ECONNABORTED'
83 | && error.message.indexOf('timeout') > -1) {
84 | error.message = 'Connection Time Out!'
85 | }
86 | ElMessage({
87 | message: error.message,
88 | type: 'error',
89 | duration: 5 * 1000,
90 | })
91 | return Promise.reject(error)
92 | },
93 | )
94 |
95 | export default service
96 |
--------------------------------------------------------------------------------
/src/utils/time.js:
--------------------------------------------------------------------------------
1 | import { T } from '@/utils/i18n'
2 |
3 | export function timeAgo (time) {
4 | let now = new Date().getTime()
5 | let after = new Date(time).getTime()
6 | let dis = now - after
7 | if (dis < 60 * 1000) {
8 | return T('JustNow')
9 | } else if (dis < 60 * 60 * 1000) {
10 | const num = Math.floor(dis / (60 * 1000))
11 | return T('MinutesAgo', { param: num }, num)
12 | } else if (dis < 24 * 60 * 60 * 1000) {
13 | const num = Math.floor(dis / (60 * 60 * 1000))
14 | return T('HoursAgo', { param: num }, num)
15 | } else if (dis < 30 * 24 * 60 * 60 * 1000) {
16 | const num = Math.floor(dis / (24 * 60 * 60 * 1000))
17 | return T('DaysAgo', { param: num }, num)
18 | } else if (dis < 12 * 30 * 24 * 60 * 60 * 1000) {
19 | const num = Math.floor(dis / (30 * 24 * 60 * 60 * 1000))
20 | return T('MonthsAgo', { param: num }, num)
21 | } else {
22 | const num = Math.floor(dis / (12 * 30 * 24 * 60 * 60 * 1000))
23 | return T('YearsAgo', { param: num }, num)
24 | }
25 | }
26 |
27 | export function formatTime (unix, format = 'yyyy-MM-dd hh:mm:ss') {
28 | let date = new Date(unix)
29 | let o = {
30 | 'M+': date.getMonth() + 1,
31 | 'd+': date.getDate(),
32 | 'h+': date.getHours(),
33 | 'm+': date.getMinutes(),
34 | 's+': date.getSeconds(),
35 | 'q+': Math.floor((date.getMonth() + 3) / 3),
36 | S: date.getMilliseconds(),
37 | }
38 | if (/(y+)/.test(format)) {
39 | format = format.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))
40 | }
41 | for (let k in o) {
42 | if (new RegExp('(' + k + ')').test(format)) {
43 | format = format.replace(RegExp.$1, RegExp.$1.length === 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length))
44 | }
45 | }
46 | return format
47 | }
48 |
--------------------------------------------------------------------------------
/src/utils/webclient.js:
--------------------------------------------------------------------------------
1 | import { ref } from 'vue'
2 | import { server } from '@/api/config'
3 | import Websock from '@/utils/webclient/websock'
4 | import * as rendezvous from '@/utils/webclient/rendezvous'
5 | import * as message from '@/utils/webclient/message'
6 | import { ElMessageBox } from 'element-plus'
7 | import { T } from '@/utils/i18n'
8 |
9 | const prefix = 'wc-'
10 |
11 | export const toWebClientLink = (row) => {
12 | //v2
13 | window.open(`${rustdeskConfig.value.api_server}/webclient2/#/${row.id}`)
14 | }
15 |
16 | export const rustdeskConfig = ref({})
17 |
18 | export async function loadRustdeskConfig () {
19 | console.log('loadRustdeskConfig')
20 | if (rustdeskConfig.value.id_server === undefined || rustdeskConfig.value.key === undefined) {
21 | const res = await server().catch(_ => false)
22 | if (res) {
23 | rustdeskConfig.value = res.data
24 | localStorage.setItem(`${prefix}custom-rendezvous-server`, res.data.id_server)
25 | localStorage.setItem(`${prefix}key`, res.data.key)
26 | localStorage.setItem(`${prefix}api-server`, res.data.api_server)
27 | }
28 | }
29 | return {
30 | rustdeskConfig,
31 | }
32 | }
33 |
34 | loadRustdeskConfig()
35 |
36 | export async function getPeerSlat (id) {
37 | const [addr, port] = rustdeskConfig.value.id_server.split(':')
38 | if (!addr) {
39 | return
40 | }
41 | const scheme = location.protocol === 'https:' ? 'wss' : 'ws'
42 | const ws = new Websock(`${scheme}://${addr}:21118`, true)
43 | await ws.open()
44 | const conn_type = rendezvous.ConnType.DEFAULT_CONN
45 | const nat_type = rendezvous.NatType.SYMMETRIC
46 | const punch_hole_request = rendezvous.PunchHoleRequest.fromPartial({
47 | id,
48 | licence_key: rustdeskConfig.value.key || undefined,
49 | conn_type,
50 | nat_type,
51 | token: undefined,
52 | })
53 | ws.sendRendezvous({ punch_hole_request })
54 | //rendezvous.RendezvousMessage
55 | const msg = (await ws.next())
56 | ws.close()
57 | console.log(new Date() + ': Got relay response', msg)
58 | const phr = msg.punch_hole_response
59 | const rr = msg.relay_response
60 | if (phr) {
61 | if (phr?.other_failure) {
62 | this.msgbox('error', 'Error', phr?.other_failure)
63 | return
64 | }
65 | if (phr.failure != rendezvous.PunchHoleResponse_Failure.UNRECOGNIZED) {
66 | switch (phr?.failure) {
67 | case rendezvous.PunchHoleResponse_Failure.ID_NOT_EXIST:
68 | ElMessageBox.alert(T('IDNotExist'), T('Error'))
69 | break
70 | case rendezvous.PunchHoleResponse_Failure.OFFLINE:
71 | ElMessageBox.alert(T('RemoteDesktopOffline'), T('Error'))
72 | break
73 | case rendezvous.PunchHoleResponse_Failure.LICENSE_MISMATCH:
74 | ElMessageBox.alert(T('KeyMismatch'), T('Error'))
75 | break
76 | case rendezvous.PunchHoleResponse_Failure.LICENSE_OVERUSE:
77 | ElMessageBox.alert(T('KeyOveruse'), T('Error'))
78 | break
79 | }
80 | }
81 | return false
82 | } else if (rr) {
83 | const uuid = rr.uuid
84 | console.log(new Date() + ': Connecting to relay server')
85 |
86 | const _ws = new Websock(`${scheme}://${addr}:21119`, false)
87 | await _ws.open()
88 | console.log(new Date() + ': Connected to relay server')
89 | const request_relay = rendezvous.RequestRelay.fromPartial({
90 | licence_key: rustdeskConfig.value.key || undefined,
91 | uuid,
92 | })
93 | _ws.sendRendezvous({ request_relay })
94 |
95 | //暂不支持pk
96 | const public_key = message.PublicKey.fromPartial({})
97 | _ws?.sendMessage({ public_key })
98 | // const secure = (await this.secure(pk)) || false;
99 | // globals.pushEvent("connection_ready", { secure, direct: false });
100 | while (true) {
101 | const msg = (await _ws?.next())
102 | console.log('msg', msg)
103 | if (msg?.hash) {
104 | console.log('hash msg.....', msg.hash)
105 | _ws.close()
106 | return msg.hash
107 | }
108 | }
109 | return false
110 | }
111 |
112 | }
113 |
114 | export function getV2ShareUrl (token) {
115 | return `${rustdeskConfig.value.api_server}/webclient2/#/?share_token=${token}`
116 | }
117 |
--------------------------------------------------------------------------------
/src/utils/webclient/websock.ts:
--------------------------------------------------------------------------------
1 | import * as message from "./message.js";
2 | import * as rendezvous from "./rendezvous.js";
3 |
4 | type Keys = "message" | "open" | "close" | "error";
5 |
6 | export default class Websock {
7 | _websocket: WebSocket;
8 | _eventHandlers: { [key in Keys]: Function };
9 | _buf: (rendezvous.RendezvousMessage | message.Message)[];
10 | _status: any;
11 | _latency: number;
12 | _secretKey: [Uint8Array, number, number] | undefined;
13 | _uri: string;
14 | _isRendezvous: boolean;
15 |
16 | constructor(uri: string, isRendezvous: boolean = true) {
17 | this._eventHandlers = {
18 | message: (_: any) => {},
19 | open: () => {},
20 | close: () => {},
21 | error: () => {},
22 | };
23 | this._uri = uri;
24 | this._status = "";
25 | this._buf = [];
26 | this._websocket = new WebSocket(uri);
27 | this._websocket.onmessage = this._recv_message.bind(this);
28 | this._websocket.binaryType = "arraybuffer";
29 | this._latency = new Date().getTime();
30 | this._isRendezvous = isRendezvous;
31 | }
32 |
33 | latency(): number {
34 | return this._latency;
35 | }
36 |
37 | setSecretKey(key: Uint8Array) {
38 | this._secretKey = [key, 0, 0];
39 | }
40 |
41 | sendMessage(json: message.DeepPartial) {
42 | let data = message.Message.encode(
43 | message.Message.fromPartial(json)
44 | ).finish();
45 | // let k = this._secretKey;
46 | // if (k) {
47 | // k[1] += 1;
48 | // data = globals.encrypt(data, k[1], k[0]);
49 | // }
50 | this._websocket.send(data);
51 | }
52 |
53 | sendRendezvous(data: rendezvous.DeepPartial) {
54 | this._websocket.send(
55 | rendezvous.RendezvousMessage.encode(
56 | rendezvous.RendezvousMessage.fromPartial(data)
57 | ).finish()
58 | );
59 | }
60 |
61 | parseMessage(data: Uint8Array) {
62 | return message.Message.decode(data);
63 | }
64 |
65 | parseRendezvous(data: Uint8Array) {
66 | return rendezvous.RendezvousMessage.decode(data);
67 | }
68 |
69 | // Event Handlers
70 | off(evt: Keys) {
71 | this._eventHandlers[evt] = () => {};
72 | }
73 |
74 | on(evt: Keys, handler: Function) {
75 | this._eventHandlers[evt] = handler;
76 | }
77 |
78 | async open(timeout: number = 12000): Promise {
79 | return new Promise((resolve, reject) => {
80 | setTimeout(() => {
81 | if (this._status != "open") {
82 | reject(this._status || "Timeout");
83 | }
84 | }, timeout);
85 | this._websocket.onopen = () => {
86 | this._latency = new Date().getTime() - this._latency;
87 | this._status = "open";
88 | console.debug(">> WebSock.onopen");
89 | if (this._websocket?.protocol) {
90 | console.info(
91 | "Server choose sub-protocol: " + this._websocket.protocol
92 | );
93 | }
94 |
95 | this._eventHandlers.open();
96 | console.info("WebSock.onopen");
97 | resolve(this);
98 | };
99 | this._websocket.onclose = (e) => {
100 | if (this._status == "open") {
101 | // e.code 1000 means that the connection was closed normally.
102 | //
103 | }
104 | this._status = e;
105 | console.error("WebSock.onclose: ");
106 | console.error(e);
107 | this._eventHandlers.close(e);
108 | reject("Reset by the peer");
109 | };
110 | this._websocket.onerror = (e: any) => {
111 | if (!this._status) {
112 | reject("Failed to connect to " + (this._isRendezvous ? "rendezvous" : "relay") + " server");
113 | return;
114 | }
115 | this._status = e;
116 | console.error("WebSock.onerror: ")
117 | console.error(e);
118 | this._eventHandlers.error(e);
119 | };
120 | });
121 | }
122 |
123 | async next(
124 | timeout = 12000
125 | ): Promise {
126 | const func = (
127 | resolve: (value: rendezvous.RendezvousMessage | message.Message) => void,
128 | reject: (reason: any) => void,
129 | tm0: number
130 | ) => {
131 | // console.log('next')
132 | if (this._buf.length) {
133 | resolve(this._buf[0]);
134 | this._buf.splice(0, 1);
135 | } else {
136 | if (this._status != "open") {
137 | reject(this._status);
138 | return;
139 | }
140 | if (new Date().getTime() > tm0 + timeout) {
141 | reject("Timeout");
142 | } else {
143 | setTimeout(() => func(resolve, reject, tm0), 1);
144 | }
145 | }
146 | };
147 | return new Promise((resolve, reject) => {
148 | func(resolve, reject, new Date().getTime());
149 | });
150 | }
151 |
152 | close() {
153 | this._status = "";
154 | if (this._websocket) {
155 | if (
156 | this._websocket.readyState === WebSocket.OPEN ||
157 | this._websocket.readyState === WebSocket.CONNECTING
158 | ) {
159 | console.info("Closing WebSocket connection");
160 | this._websocket.close();
161 | }
162 |
163 | this._websocket.onmessage = () => {};
164 | }
165 | }
166 |
167 | _recv_message(e: any) {
168 | if (e.data instanceof window.ArrayBuffer) {
169 | let bytes = new Uint8Array(e.data);
170 | // const k = this._secretKey;
171 | // if (k) {
172 | // k[2] += 1;
173 | // bytes = globals.decrypt(bytes, k[2], k[0]);
174 | // }
175 | this._buf.push(
176 | this._isRendezvous
177 | ? this.parseRendezvous(bytes)
178 | : this.parseMessage(bytes)
179 | );
180 | }
181 | this._eventHandlers.message(e.data);
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/src/views/address_book/collection.js:
--------------------------------------------------------------------------------
1 | import { reactive, ref } from 'vue'
2 | import { list as admin_list, create as admin_create, update as admin_update, remove as admin_remove } from '@/api/address_book_collection'
3 | import { list as my_list, create as my_create, update as my_update, remove as my_remove } from '@/api/my/address_book_collection'
4 | import { ElMessage, ElMessageBox } from 'element-plus'
5 | import { T } from '@/utils/i18n'
6 |
7 | const apis = {
8 | admin: { list: admin_list, remove: admin_remove, update: admin_update, create: admin_create },
9 | my: { list: my_list, remove: my_remove, create: my_create, update: my_update },
10 | }
11 |
12 | export function useRepositories (api_type = 'my') {
13 | const listRes = reactive({
14 | list: [], total: 0, loading: false,
15 | })
16 | const listQuery = reactive({
17 | page: 1,
18 | page_size: 10,
19 | name: null,
20 | user_id: null,
21 | })
22 |
23 | const getList = async () => {
24 | listRes.loading = true
25 | const res = await apis[api_type].list(listQuery).catch(_ => false)
26 | listRes.loading = false
27 | if (res) {
28 | listRes.list = res.data.list
29 | listRes.total = res.data.total
30 | }
31 | }
32 | const handlerQuery = () => {
33 | if (listQuery.page === 1) {
34 | getList()
35 | } else {
36 | listQuery.page = 1
37 | }
38 | }
39 |
40 | const del = async (row) => {
41 | const cf = await ElMessageBox.confirm(T('Confirm?', { param: T('Delete') }), {
42 | confirmButtonText: T('Confirm'),
43 | cancelButtonText: T('Cancel'),
44 | type: 'warning',
45 | }).catch(_ => false)
46 | if (!cf) {
47 | return false
48 | }
49 |
50 | const res = await apis[api_type].remove({ id: row.id }).catch(_ => false)
51 | if (res) {
52 | ElMessage.success(T('OperationSuccess'))
53 | getList()
54 | }
55 | }
56 |
57 | const formVisible = ref(false)
58 | const formData = reactive({
59 | id: 0,
60 | name: '',
61 | })
62 |
63 | const toEdit = (row) => {
64 | formVisible.value = true
65 | //将row中的数据赋值给formData
66 | Object.keys(formData).forEach(key => {
67 | formData[key] = row[key]
68 | })
69 |
70 | }
71 | const toAdd = () => {
72 | formVisible.value = true
73 | //重置formData
74 | Object.keys(formData).forEach(key => {
75 | formData[key] = undefined
76 | })
77 |
78 | }
79 | const submit = async () => {
80 | const api = formData.id ? apis[api_type].update : apis[api_type].create
81 | const res = await api(formData).catch(_ => false)
82 | if (res) {
83 | ElMessage.success(T('OperationSuccess'))
84 | formVisible.value = false
85 | getList()
86 | }
87 | }
88 | return {
89 | listRes,
90 | listQuery,
91 | getList,
92 | handlerQuery,
93 | del,
94 | formVisible,
95 | formData,
96 | toEdit,
97 | toAdd,
98 | submit,
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/views/address_book/collection.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
13 |
14 |
15 |
16 | {{ T('Filter') }}
17 | {{ T('Add') }}
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | {{ allUsers?.find(u => u.id === row.user_id)?.username }}
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | {{ T('ShareRules') }}
35 | {{ T('Edit') }}
36 | {{ T('Delete') }}
37 |
38 |
39 |
40 |
41 |
42 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | {{ T('Cancel') }}
67 | {{ T('Submit') }}
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
119 |
120 |
123 |
--------------------------------------------------------------------------------
/src/views/address_book/components/shareByWebClient.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ formData.id }}
5 |
6 |
17 |
18 |
19 |
20 |
21 |
22 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | {{ T('Cancel') }}
39 | {{ T('Submit') }}
40 | {{ T('Close') }}
41 |
42 |
43 |
44 |
45 |
145 |
146 |
149 |
--------------------------------------------------------------------------------
/src/views/address_book/rule.js:
--------------------------------------------------------------------------------
1 | import { computed, reactive, ref } from 'vue'
2 | import { list as admin_list, create as admin_create, update as admin_update, remove as admin_remove } from '@/api/address_book_collection_rule'
3 | import { list as my_list, create as my_create, update as my_update, remove as my_remove } from '@/api/my/address_book_collection_rule'
4 | import { groupUsers } from '@/api/user'
5 | import { ElMessage, ElMessageBox } from 'element-plus'
6 | import { T } from '@/utils/i18n'
7 |
8 | const apis = {
9 | admin: { list: admin_list, remove: admin_remove, update: admin_update, create: admin_create },
10 | my: { list: my_list, remove: my_remove, create: my_create, update: my_update },
11 | }
12 |
13 | export function useRepositories (api_type = 'my') {
14 | const listRes = reactive({
15 | list: [], total: 0, loading: false,
16 | })
17 | const listQuery = reactive({
18 | page: 1,
19 | page_size: 10,
20 | collection_id: null,
21 | })
22 |
23 | const getList = async () => {
24 | listRes.loading = true
25 | const res = await apis[api_type].list(listQuery).catch(_ => false)
26 | listRes.loading = false
27 | if (res) {
28 | listRes.list = res.data.list
29 | listRes.total = res.data.total
30 | }
31 | }
32 | const handlerQuery = () => {
33 | if (listQuery.page === 1) {
34 | getList()
35 | } else {
36 | listQuery.page = 1
37 | }
38 | }
39 |
40 | const del = async (row) => {
41 | const cf = await ElMessageBox.confirm(T('Confirm?', { param: T('Delete') }), {
42 | confirmButtonText: T('Confirm'),
43 | cancelButtonText: T('Cancel'),
44 | type: 'warning',
45 | }).catch(_ => false)
46 | if (!cf) {
47 | return false
48 | }
49 |
50 | const res = await apis[api_type].remove({ id: row.id }).catch(_ => false)
51 | if (res) {
52 | ElMessage.success(T('OperationSuccess'))
53 | getList()
54 | }
55 | }
56 |
57 | const rules = computed(_ => [
58 | { label: T('Read'), value: 1 },
59 | { label: T('ReadWrite'), value: 2 },
60 | { label: T('FullControl'), value: 3 },
61 | ])
62 | const TYPE_U = 1
63 | const TYPE_G = 2
64 | const types = computed(_ => [
65 | { label: T('Group'), value: TYPE_G },
66 | { label: T('User'), value: TYPE_U },
67 | ])
68 | const formVisible = ref(false)
69 | const formData = reactive({
70 | id: 0,
71 | collection_id: null,
72 | type: TYPE_U,
73 | rule: 1,
74 | g_id: null,
75 | u_id: null,
76 | to_id: null,
77 | user_id: null,
78 | })
79 |
80 | const toEdit = (row) => {
81 | formVisible.value = true
82 | //将row中的数据赋值给formData
83 | Object.keys(formData).forEach(key => {
84 | formData[key] = row[key]
85 | })
86 | if (row.type === TYPE_U) {
87 | formData.u_id = row.to_id
88 | formData.g_id = users.value.find(u => u.id === row.to_id)?.group_id
89 | } else {
90 | formData.g_id = row.to_id
91 | formData.u_id = null
92 | }
93 | }
94 | const toAdd = () => {
95 | //初始化formData
96 | formData.id = 0
97 | formData.type = TYPE_U
98 | formData.rule = 1
99 | formData.g_id = null
100 | formData.u_id = null
101 |
102 | formVisible.value = true
103 |
104 | }
105 | const submit = async () => {
106 | const api = formData.id ? apis[api_type].update : apis[api_type].create
107 | const form = {
108 | ...formData,
109 | }
110 | form.to_id = form.type === TYPE_G ? form.g_id : form.u_id
111 | const res = await api(form).catch(_ => false)
112 | if (res) {
113 | ElMessage.success(T('OperationSuccess'))
114 | formVisible.value = false
115 | getList()
116 | }
117 | }
118 | const groups = ref([])
119 | const users = ref([])
120 | const getGroupUsers = async () => {
121 | const res = await groupUsers().catch(_ => false)
122 | if (res) {
123 | groups.value = res.data.groups.map(item => {
124 | if (!item.children) {
125 | item.children = []
126 | }
127 | res.data.users.map(u => {
128 | if (item.id === u.group_id) {
129 | item.children.push(u)
130 | }
131 | })
132 | return item
133 | })
134 | users.value = res.data.users
135 | }
136 | }
137 | const changeGId = () => {
138 | formData.u_id = null
139 | }
140 | return {
141 | listRes,
142 | listQuery,
143 | getList,
144 | handlerQuery,
145 | del,
146 | formVisible,
147 | formData,
148 | toEdit,
149 | toAdd,
150 | submit,
151 | rules,
152 | types,
153 | groups,
154 | users,
155 | getGroupUsers,
156 | TYPE_G,
157 | TYPE_U,
158 | changeGId,
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/src/views/address_book/rule.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{ T('Filter') }}
7 | {{ T('Add') }}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | {{ rules.find(r => r.value === row.rule)?.label }}
17 |
18 |
19 |
20 |
21 |
22 |
23 | {{ types.find(t => t.value === row.type)?.label }}
24 |
25 |
26 |
27 |
28 |
29 |
30 | {{ users.find(u => u.id === row.to_id)?.username }}
31 |
32 |
33 | {{ groups.find(g => g.id === row.to_id)?.name }}
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | {{ T('Edit') }}
42 | {{ T('Delete') }}
43 |
44 |
45 |
46 |
47 |
48 |
54 |
55 |
56 |
57 |
58 |
59 | {{ props.collection.name }}
60 |
61 |
62 |
63 |
64 | {{ item.label }}
65 |
66 |
67 |
68 |
69 |
70 |
71 | {{ item.label }}
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
85 |
86 |
87 |
88 |
89 |
95 |
96 |
97 |
98 |
99 | {{ T('Cancel') }}
100 | {{ T('Submit') }}
101 |
102 |
103 |
104 |
105 |
106 |
107 |
157 |
158 |
161 |
--------------------------------------------------------------------------------
/src/views/audit/connList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | {{ T('Filter') }}
13 | {{ T('BatchDelete') }}
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | {{ T('File') }}
28 | {{ T('Common') }}
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | {{ T('Delete') }}
37 |
38 |
39 |
40 |
41 |
42 |
48 |
49 |
50 |
51 |
52 |
53 |
84 |
85 |
88 |
--------------------------------------------------------------------------------
/src/views/audit/reponsitories.js:
--------------------------------------------------------------------------------
1 | import { reactive } from 'vue'
2 | import { list, remove, fileList, fileRemove, batchDelete, fileBatchDelete } from '@/api/audit'
3 | import { ElMessage, ElMessageBox } from 'element-plus'
4 | import { useRoute } from 'vue-router'
5 | import { formatTime } from '@/utils/time'
6 | import { T } from '@/utils/i18n'
7 |
8 | export function useRepositories () {
9 | const listRes = reactive({
10 | list: [], total: 0, loading: false,
11 | })
12 | const listQuery = reactive({
13 | page: 1,
14 | page_size: 10,
15 | peer_id: null,
16 | from_peer: null,
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.map(item => {
25 | item.close_time = item.close_time ? formatTime(item.close_time * 1000) : '-'
26 | return item
27 | })
28 | listRes.total = res.data.total
29 | }
30 | }
31 | const handlerQuery = () => {
32 | if (listQuery.page === 1) {
33 | getList()
34 | } else {
35 | listQuery.page = 1
36 | }
37 | }
38 |
39 | const del = async (row) => {
40 | const cf = await ElMessageBox.confirm(T('Confirm?', { param: T('Delete') }), {
41 | confirmButtonText: T('Confirm'),
42 | cancelButtonText: T('Cancel'),
43 | type: 'warning',
44 | }).catch(_ => false)
45 | if (!cf) {
46 | return false
47 | }
48 |
49 | const res = await remove({ id: row.id }).catch(_ => false)
50 | if (res) {
51 | ElMessage.success(T('OperationSuccess'))
52 | getList()
53 | }
54 | }
55 | const batchdel = async (rows) => {
56 | const ids = rows.map(r => r.id)
57 | if (!ids.length) {
58 | ElMessage.warning(T('PleaseSelectData'))
59 | return false
60 | }
61 | const cf = await ElMessageBox.confirm(T('Confirm?', { param: T('BatchDelete') }), {
62 | confirmButtonText: T('Confirm'),
63 | cancelButtonText: T('Cancel'),
64 | type: 'warning',
65 | }).catch(_ => false)
66 | if (!cf) {
67 | return false
68 | }
69 |
70 | const res = await batchDelete({ ids }).catch(_ => false)
71 | if (res) {
72 | ElMessage.success(T('OperationSuccess'))
73 | getList()
74 | }
75 | }
76 | return {
77 | listRes,
78 | listQuery,
79 | getList,
80 | handlerQuery,
81 | del,
82 | batchdel,
83 | }
84 | }
85 |
86 | export function useFileRepositories () {
87 | const listRes = reactive({
88 | list: [], total: 0, loading: false,
89 | })
90 | const listQuery = reactive({
91 | page: 1,
92 | page_size: 10,
93 | peer_id: null,
94 | from_peer: null,
95 | })
96 |
97 | const getList = async () => {
98 | listRes.loading = true
99 | const res = await fileList(listQuery).catch(_ => false)
100 | listRes.loading = false
101 | if (res) {
102 | listRes.list = res.data.list.map(item => {
103 | item.info = item.info ? JSON.parse(item.info) : '-'
104 | return item
105 | })
106 | listRes.total = res.data.total
107 | }
108 | }
109 | const handlerQuery = () => {
110 | if (listQuery.page === 1) {
111 | getList()
112 | } else {
113 | listQuery.page = 1
114 | }
115 | }
116 |
117 | const del = async (row) => {
118 | const cf = await ElMessageBox.confirm(T('Confirm?', { param: T('Delete') }), {
119 | confirmButtonText: T('Confirm'),
120 | cancelButtonText: T('Cancel'),
121 | type: 'warning',
122 | }).catch(_ => false)
123 | if (!cf) {
124 | return false
125 | }
126 |
127 | const res = await fileRemove({ id: row.id }).catch(_ => false)
128 | if (res) {
129 | ElMessage.success(T('OperationSuccess'))
130 | getList()
131 | }
132 | }
133 | const batchdel = async (rows) => {
134 | const ids = rows.map(r => r.id)
135 | if (!ids.length) {
136 | ElMessage.warning(T('PleaseSelectData'))
137 | return false
138 | }
139 | const cf = await ElMessageBox.confirm(T('Confirm?', { param: T('BatchDelete') }), {
140 | confirmButtonText: T('Confirm'),
141 | cancelButtonText: T('Cancel'),
142 | type: 'warning',
143 | }).catch(_ => false)
144 | if (!cf) {
145 | return false
146 | }
147 |
148 | const res = await fileBatchDelete({ ids }).catch(_ => false)
149 | if (res) {
150 | ElMessage.success(T('OperationSuccess'))
151 | getList()
152 | }
153 | }
154 | return {
155 | listRes,
156 | listQuery,
157 | getList,
158 | handlerQuery,
159 | del,
160 | batchdel,
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/src/views/error-page/404.vue:
--------------------------------------------------------------------------------
1 |
2 | 404
3 |
4 |
5 |
10 |
11 |
14 |
--------------------------------------------------------------------------------
/src/views/group/deviceGroupList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 | {{ T('Filter') }}
10 | {{ T('Add') }}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | {{ T('Edit') }}
23 | {{ T('Delete') }}
24 |
25 |
26 |
27 |
28 |
29 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | {{ T('Cancel') }}
44 | {{ T('Submit') }}
45 |
46 |
47 |
48 |
49 |
50 |
51 |
135 |
136 |
139 |
--------------------------------------------------------------------------------
/src/views/group/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 | {{ T('Filter') }}
10 | {{ T('Add') }}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | {{ T('CommonGroup') }}
21 | {{ T('SharedGroup') }}
22 |
23 |
24 |
25 |
26 |
27 |
28 | {{ T('Edit') }}
29 | {{ T('Delete') }}
30 |
31 |
32 |
33 |
34 |
35 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | {{ item.label }}
52 | {{ item.note }}
53 |
54 |
55 |
56 |
57 | {{ T('Cancel') }}
58 | {{ T('Submit') }}
59 |
60 |
61 |
62 |
63 |
64 |
65 |
153 |
154 |
157 |
--------------------------------------------------------------------------------
/src/views/index/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
24 |
25 |
76 |
--------------------------------------------------------------------------------
/src/views/login/log.js:
--------------------------------------------------------------------------------
1 | import { reactive, ref } from 'vue'
2 | import { list as admin_fetchPeers } from '@/api/peer'
3 | import { list as my_fetchPeers } from '@/api/my/peer'
4 | import { ElMessage, ElMessageBox } from 'element-plus'
5 | import { useRoute } from 'vue-router'
6 | import { T } from '@/utils/i18n'
7 | import { batchDelete as admin_batchDelete, list as admin_list, remove as admin_remove } from '@/api/login_log'
8 | import { batchDelete as my_batchDelete, list as my_list, remove as my_remove } from '@/api/my/login_log'
9 |
10 | const apis = {
11 | admin: { batchDelete: admin_batchDelete, list: admin_list, remove: admin_remove, fetchPeers: admin_fetchPeers },
12 | my: { batchDelete: my_batchDelete, list: my_list, remove: my_remove, fetchPeers: my_fetchPeers },
13 | }
14 |
15 | export function useRepositories (api_type = 'my') {
16 |
17 | const listRes = reactive({
18 | list: [], total: 0, loading: false,
19 | })
20 | const listQuery = reactive({
21 | page: 1,
22 | page_size: 10,
23 | is_my: 0,
24 | user_id: null,
25 | })
26 |
27 | const getList = async () => {
28 | listRes.loading = true
29 | const res = await apis[api_type].list(listQuery).catch(_ => false)
30 | listRes.loading = false
31 | if (res) {
32 | //通过uuid补全peer信息
33 | const uuids = res.data.list.filter(item => item.uuid&&item.client==='client'&&!item.device_id).map(item => item.uuid)
34 | if(uuids.length > 0){
35 | //uuids去重
36 | const uniqueUuids = [...new Set(uuids)]
37 | const peers = await apis[api_type].fetchPeers({ uuids: uniqueUuids }).catch(_ => false)
38 | if (peers?.data?.list) {
39 | res.data.list.forEach(item => {
40 | if (item.uuid) {
41 | item.peer = peers.data.list.find(peer => peer.uuid === item.uuid)
42 | }
43 | })
44 | }
45 | }
46 |
47 | listRes.list = res.data.list
48 | listRes.total = res.data.total
49 | }
50 | }
51 | const handlerQuery = () => {
52 | if (listQuery.page === 1) {
53 | getList()
54 | } else {
55 | listQuery.page = 1
56 | }
57 | }
58 |
59 | const del = async (row) => {
60 | const cf = await ElMessageBox.confirm(T('Confirm?', { param: T('Delete') }), {
61 | confirmButtonText: T('Confirm'),
62 | cancelButtonText: T('Cancel'),
63 | type: 'warning',
64 | }).catch(_ => false)
65 | if (!cf) {
66 | return false
67 | }
68 |
69 | const res = await apis[api_type].remove({ id: row.id }).catch(_ => false)
70 | if (res) {
71 | ElMessage.success(T('OperationSuccess'))
72 | getList()
73 | }
74 | }
75 |
76 | const batchdel = async (rows) => {
77 | const ids = rows.map(r => r.id)
78 | if (!ids.length) {
79 | ElMessage.warning(T('PleaseSelectData'))
80 | return false
81 | }
82 | const cf = await ElMessageBox.confirm(T('Confirm?', { param: T('BatchDelete') }), {
83 | confirmButtonText: T('Confirm'),
84 | cancelButtonText: T('Cancel'),
85 | type: 'warning',
86 | }).catch(_ => false)
87 | if (!cf) {
88 | return false
89 | }
90 |
91 | const res = await apis[api_type].batchDelete({ ids }).catch(_ => false)
92 | if (res) {
93 | ElMessage.success(T('OperationSuccess'))
94 | getList()
95 | }
96 | }
97 |
98 | return {
99 | listRes,
100 | listQuery,
101 | getList,
102 | handlerQuery,
103 | del,
104 | batchdel,
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/views/login/log.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
13 |
14 |
15 |
16 | {{ T('Filter') }}
17 | {{ T('BatchDelete') }}
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | {{ allUsers?.find(u => u.id === row.user_id)?.username }}
28 |
29 |
30 |
31 |
32 |
33 | {{ row.device_id ? row.device_id : peer?.id }}
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | {{ T('Delete') }}
44 |
45 |
46 |
47 |
48 |
49 |
55 |
56 |
57 |
58 |
59 |
60 |
95 |
96 |
103 |
--------------------------------------------------------------------------------
/src/views/my/address_book/collection.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{ T('Filter') }}
7 | {{ T('Add') }}
8 |
9 |
10 |
11 |
12 | {{ T('MyAddressBookTips') }}
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | {{ T('ShareRules') }}
22 | {{ T('Edit') }}
23 | {{ T('Delete') }}
24 |
25 |
26 |
27 |
28 |
29 |
30 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | {{ T('Cancel') }}
45 | {{ T('Submit') }}
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
99 |
100 |
103 |
--------------------------------------------------------------------------------
/src/views/my/info.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{ userStore.username }}
7 |
8 |
9 | {{ userStore.email }}
10 |
11 |
12 | {{ T('ChangePassword') }}
13 |
14 |
15 |
16 |
17 |
18 |
19 | {{ T('HasBind') }}
20 | {{ T('NoBind') }}
21 |
22 |
23 |
24 |
25 | {{ T('UnBind') }}
26 | {{ T('ToBind') }}
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
92 |
93 |
100 |
--------------------------------------------------------------------------------
/src/views/my/login_log/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{ T('Filter') }}
7 | {{ T('BatchDelete') }}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | {{ row.device_id ? row.device_id : peer?.id }}
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | {{ T('Delete') }}
28 |
29 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
75 |
76 |
83 |
--------------------------------------------------------------------------------
/src/views/my/share_record/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{ T('Filter') }}
7 | {{ T('BatchDelete') }}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | {{ row.expire ? row.expire : T('Forever') }}
20 |
21 |
22 |
23 |
24 | {{ T('Delete') }}
25 |
26 |
27 |
28 |
29 |
30 |
36 |
37 |
38 |
39 |
40 |
41 |
69 |
70 |
77 |
--------------------------------------------------------------------------------
/src/views/my/tag/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | {{ T('Filter') }}
13 | {{ T('Add') }}
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | {{ T('MyAddressBook') }}
23 | {{ collectionListRes.list.find(c => c.id === row.collection_id)?.name }}
24 |
25 |
26 |
27 |
28 |
29 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | {{ T('Edit') }}
42 | {{ T('Delete') }}
43 |
44 |
45 |
46 |
47 |
48 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
75 |
76 |
77 | {{ T('Cancel') }}
78 | {{ T('Submit') }}
79 |
80 |
81 |
82 |
83 |
84 |
85 |
122 |
123 |
151 |
--------------------------------------------------------------------------------
/src/views/oauth/bind.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ T('OauthBinding') }}
5 |
6 |
7 | {{ oauthInfo.op }}
8 |
9 |
10 | {{ oauthInfo.third_name }}
11 |
12 |
13 | {{ T('Bind') }}
14 |
15 |
16 | {{ T('Close') }}
17 |
18 |
19 | {{ T('OauthCloseNote') }}
20 |
21 |
22 |
23 |
24 |
69 |
70 |
103 |
--------------------------------------------------------------------------------
/src/views/oauth/login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ T('OauthLogining') }}
5 |
6 |
7 | {{ oauthInfo.device_name }}
8 |
9 |
10 | {{ oauthInfo.id }}
11 |
12 |
13 | {{ T('ConfirmOauth') }}
14 |
15 |
16 | {{ T('Close') }}
17 |
18 |
19 | {{ T('OauthCloseNote') }}
20 |
21 |
22 |
23 |
24 |
63 |
64 |
97 |
--------------------------------------------------------------------------------
/src/views/peer/createABForm.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
40 |
41 |
42 |
43 |
44 |
45 |
51 |
52 |
53 |
54 | {{ T('Cancel') }}
55 | {{ T('Submit') }}
56 |
57 |
58 |
59 |
126 |
127 |
130 |
--------------------------------------------------------------------------------
/src/views/register/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |

5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
17 |
18 |
19 |
21 |
22 |
23 | {{ T('Submit') }}
24 | {{ T('ToLogin') }}
25 |
26 |
27 |
28 |
29 |
30 |
31 |
91 |
92 |
158 |
--------------------------------------------------------------------------------
/src/views/rustdesk/always_use_relay.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | {{ T('Refresh') }}
14 | {{ T('Save') }}
15 |
16 |
17 |
18 |
19 |
65 |
66 |
67 |
70 |
--------------------------------------------------------------------------------
/src/views/rustdesk/blacklist.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | {{ T('Refresh') }}
14 | {{ T('Add') }}
15 | {{ T('Delete') }}
16 |
17 |
18 |
19 |
20 |
21 |
22 | 多个IP以 | 分割
23 | , 全部填 all
24 |
25 |
26 | {{ T('Cancel') }}
27 | {{ T('Submit') }}
28 |
29 |
30 |
31 |
32 |
33 |
91 |
94 |
--------------------------------------------------------------------------------
/src/views/rustdesk/blocklist.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | {{ T('Refresh') }}
14 | {{ T('Add') }}
15 | {{ T('Delete') }}
16 |
17 |
18 |
19 |
20 |
21 |
22 | 多个IP以 | 分割
23 | , 全部填 all
24 |
25 |
26 | {{ T('Cancel') }}
27 | {{ T('Submit') }}
28 |
29 |
30 |
31 |
32 |
33 |
91 |
94 |
--------------------------------------------------------------------------------
/src/views/rustdesk/must_login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | {{ T('Refresh') }}
14 | {{ T('Save') }}
15 |
16 |
17 |
18 |
19 |
63 |
64 |
65 |
68 |
--------------------------------------------------------------------------------
/src/views/rustdesk/options.js:
--------------------------------------------------------------------------------
1 |
2 | export const ID_TARGET = '21115'
3 |
4 | export const RELAY_TARGET = '21117'
5 |
--------------------------------------------------------------------------------
/src/views/rustdesk/relay_servers.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | {{ T('Refresh') }}
14 | {{ T('Save') }}
15 |
16 |
17 |
18 |
19 |
63 |
66 |
--------------------------------------------------------------------------------
/src/views/rustdesk/usage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | {{ T('Refresh') }}
21 |
22 |
23 |
24 |
25 |
58 |
63 |
--------------------------------------------------------------------------------
/src/views/share_record/index.js:
--------------------------------------------------------------------------------
1 | import { reactive, ref } from 'vue'
2 | import { batchDelete as admin_batchDelete, list as admin_list, remove as admin_remove } from '@/api/share_record'
3 | import { batchDelete as my_batchDelete, list as my_list, remove as my_remove } from '@/api/my/share_record'
4 | import { ElMessage, ElMessageBox } from 'element-plus'
5 | import { T } from '@/utils/i18n'
6 |
7 | const apis = {
8 | admin: { batchDelete: admin_batchDelete, list: admin_list, remove: admin_remove },
9 | my: { batchDelete: my_batchDelete, list: my_list, remove: my_remove },
10 | }
11 |
12 | export function useRepositories (api_type = 'my') {
13 | const listRes = reactive({
14 | list: [], total: 0, loading: false,
15 | })
16 | const listQuery = reactive({
17 | page: 1,
18 | page_size: 10,
19 | })
20 |
21 | const getList = async () => {
22 | listRes.loading = true
23 | const res = await apis[api_type].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('Delete') }), {
40 | confirmButtonText: T('Confirm'),
41 | cancelButtonText: T('Cancel'),
42 | type: 'warning',
43 | }).catch(_ => false)
44 | if (!cf) {
45 | return false
46 | }
47 | const res = await apis[api_type].remove({ id: row.id }).catch(_ => false)
48 | if (res) {
49 | ElMessage.success(T('OperationSuccess'))
50 | getList()
51 | }
52 | }
53 |
54 | const multipleSelection = ref([])
55 | const toBatchDelete = async () => {
56 | if (multipleSelection.value.length === 0) {
57 | return
58 | }
59 |
60 | const ids = multipleSelection.value.map(r => r.id)
61 | if (!ids.length) {
62 | ElMessage.warning(T('PleaseSelectData'))
63 | return false
64 | }
65 | const cf = await ElMessageBox.confirm(T('Confirm?', { param: T('BatchDelete') }), {
66 | confirmButtonText: T('Confirm'),
67 | cancelButtonText: T('Cancel'),
68 | type: 'warning',
69 | }).catch(_ => false)
70 | if (!cf) {
71 | return false
72 | }
73 |
74 | const res = await apis[api_type].batchDelete({ ids }).catch(_ => false)
75 | if (res) {
76 | ElMessage.success(T('OperationSuccess'))
77 | getList()
78 | }
79 | }
80 |
81 | const expired = (row) => {
82 | if (row.expire === 0) {
83 | return false
84 | }
85 | const now = new Date().getTime()
86 | const created_at = new Date(row.created_at).getTime()
87 | return row.expire * 1000 + created_at < now
88 | }
89 |
90 | return {
91 | listRes,
92 | listQuery,
93 | getList,
94 | handlerQuery,
95 | del,
96 | multipleSelection,
97 | toBatchDelete,
98 | expired,
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/views/share_record/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
13 |
14 |
15 |
16 | {{ T('Filter') }}
17 | {{ T('BatchDelete') }}
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | {{ allUsers?.find(u => u.id === row.user_id)?.username }}
28 |
29 |
30 |
31 |
32 |
33 |
34 | {{ row.expire ? row.expire : T('Forever') }}
35 |
36 |
37 |
38 |
39 | {{ T('Delete') }}
40 |
41 |
42 |
43 |
44 |
45 |
51 |
52 |
53 |
54 |
55 |
56 |
90 |
91 |
98 |
--------------------------------------------------------------------------------
/src/views/user/composables/edit.js:
--------------------------------------------------------------------------------
1 | import { ref, onMounted, reactive, watch } from 'vue'
2 | import { create, detail, update, remove } from '@/api/user'
3 | import { ElMessage, ElMessageBox } from 'element-plus'
4 | import { useRouter } from 'vue-router'
5 | import { list as groups } from '@/api/group'
6 | import { T } from '@/utils/i18n'
7 |
8 | export function useGetDetail (id) {
9 | let item = ref({}) //保留原始值
10 | let form = ref({})
11 | const groupsList = ref([])
12 | const getDetail = async (id) => {
13 | const res = await detail(id)
14 | item.value = { ...res.data }
15 | form.value = { ...res.data }
16 | }
17 | if (id > 0) {
18 | onMounted(_ => {getDetail(id)})
19 | }
20 |
21 | const getGroups = async () => {
22 | const res = await groups({ page_size: 9999 }).catch(_ => false)
23 | if (res) {
24 | groupsList.value = res.data.list
25 | }
26 | }
27 | onMounted(getGroups)
28 | return {
29 | form,
30 | item,
31 | getDetail,
32 | groupsList,
33 | }
34 | }
35 |
36 | export function useSubmit (form, id) {
37 | const root = ref(null)
38 | const router = useRouter()
39 | const rules = reactive({
40 | username: [{ required: true, message: T('ParamRequired', { param: T('Username') }) }],
41 | // email: [{ required: true, message: T('ParamRequired', { param: T('Email') }) }],
42 | group_id: [{ required: true, message: T('ParamRequired', { param: T('Group') }) }],
43 | // nickname: [{ required: true, message: '昵称是必须的' }],
44 | status: [{ required: true, message: T('ParamRequired', { param: T('Status') }) }],
45 | })
46 |
47 | const validate = async () => {
48 | const res = await root.value.validate().catch(err => false)
49 | return res
50 | }
51 |
52 | const submitCreate = async () => {
53 | const res = await create(form.value).catch(_ => false)
54 | return res.code === 0
55 | }
56 |
57 | const submitUpdate = async () => {
58 | const res = await update(form.value).catch(_ => false)
59 | return res.code === 0
60 | }
61 | const submitFunc = id > 0 ? submitUpdate : submitCreate
62 |
63 | const submit = async () => {
64 | const v = await validate()
65 | if (!v) {
66 | return
67 | }
68 |
69 | const res = await submitFunc()
70 | if (res) {
71 | ElMessage.success(T('OperationSuccess'))
72 | router.back()
73 | }
74 | }
75 |
76 | const cancel = () => {
77 | router.back()
78 | }
79 |
80 | return {
81 | root,
82 | rules,
83 | validate,
84 | submit,
85 | cancel,
86 | }
87 | }
88 |
89 |
90 |
--------------------------------------------------------------------------------
/src/views/user/composables/index.js:
--------------------------------------------------------------------------------
1 | import { onMounted, reactive, watch } from 'vue'
2 | import { list, remove, changePwd } from '@/api/user'
3 | import { list as groups } from '@/api/group'
4 | import { useRouter } from 'vue-router'
5 | import { ElMessageBox, ElMessage } from 'element-plus'
6 | import { T } from '@/utils/i18n'
7 |
8 | export function useRepositories () {
9 |
10 | const listRes = reactive({
11 | list: [], total: 0, loading: false,
12 | groups: [],
13 | })
14 | const listQuery = reactive({
15 | page: 1,
16 | page_size: 10,
17 | username: '',
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 |
30 | const handlerQuery = () => {
31 | if (listQuery.page === 1) {
32 | getList()
33 | } else {
34 | listQuery.page = 1
35 | //由watch 触发
36 | }
37 | }
38 |
39 | const getGroups = async () => {
40 | const res = await groups({ page_size: 9999 }).catch(_ => false)
41 | if (res) {
42 | listRes.groups = res.data.list
43 | }
44 | }
45 | onMounted(getGroups)
46 |
47 | onMounted(getList)
48 |
49 | watch(() => listQuery.page, getList)
50 | watch(() => listQuery.page_size, handlerQuery)
51 | return {
52 | listRes,
53 | listQuery,
54 | handlerQuery,
55 | getList,
56 | getGroups,
57 | }
58 | }
59 |
60 | export function useToEditOrAdd () {
61 | const router = useRouter()
62 | const toEdit = (row) => {
63 | router.push('/user/edit/' + row.id)
64 | }
65 | const toAdd = () => {
66 | router.push('/user/add')
67 | }
68 | const toTag = (row) => {
69 | router.push('/user/tag/?user_id=' + row.id)
70 | }
71 | const toAddressBook = (row) => {
72 | router.push('/user/addressBook/?user_id=' + row.id)
73 | }
74 | return {
75 | toAdd,
76 | toEdit,
77 | toTag,
78 | toAddressBook,
79 | }
80 | }
81 |
82 | export function useDel () {
83 | const del = async (id) => {
84 | const cf = await ElMessageBox.confirm(T('Confirm?', { param: T('Delete') }), {
85 | confirmButtonText: T('Confirm'),
86 | cancelButtonText: T('Cancel'),
87 | type: 'warning',
88 | }).catch(_ => false)
89 | if (!cf) {
90 | return false
91 | }
92 |
93 | const res = remove({ id }).catch(_ => false)
94 | if (res) {
95 | ElMessage.success(T('OperationSuccess'))
96 | }
97 | return res
98 | }
99 | return {
100 | del,
101 | }
102 | }
103 |
104 | export function useChangePwd () {
105 | const changePass = async (admin) => {
106 | const input = await ElMessageBox.prompt(T('PleaseInputNewPassword'), T('ResetPassword'), {
107 | confirmButtonText: T('Confirm'),
108 | cancelButtonText: T('Cancel'),
109 | }).catch(_ => false)
110 | if (!input) {
111 | return
112 | }
113 | const confirm = await ElMessageBox.confirm(T('Confirm?', { param: T('ResetPassword') }), {
114 | confirmButtonText: T('Confirm'),
115 | cancelButtonText: T('Cancel'),
116 | }).catch(_ => false)
117 | if (!confirm) {
118 | return
119 | }
120 | const res = await changePwd({ id: admin.id, password: input.value }).catch(_ => false)
121 | if (!res) {
122 | return
123 | }
124 | ElMessage.success(T('OperationSuccess'))
125 | }
126 |
127 | return { changePass }
128 | }
129 |
--------------------------------------------------------------------------------
/src/views/user/edit.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
21 |
22 |
23 |
24 |
28 |
29 |
30 |
34 |
35 |
36 | {{ T('Cancel') }}
37 | {{ T('Submit') }}
38 |
39 |
40 |
41 |
42 |
43 |
55 |
56 |
60 |
--------------------------------------------------------------------------------
/src/views/user/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | {{ T('Filter') }}
10 | {{ T('Add') }}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | {{ listRes.groups?.find(g => g.id === row.group_id)?.name }}
23 | -
24 |
25 |
26 |
27 |
28 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | {{ T('UserTags') }}
40 | {{ T('UserAddressBook') }}
41 | {{ T('Edit') }}
42 | {{ T('ResetPassword') }}
43 | {{ T('Delete') }}
44 |
45 |
46 |
47 |
48 |
49 |
55 |
56 |
57 |
58 |
59 |
60 |
103 |
104 |
106 |
--------------------------------------------------------------------------------
/src/views/user/token.js:
--------------------------------------------------------------------------------
1 | import { reactive } from 'vue'
2 | import { batchRemove, 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 | const batchDelete = async (ids) => {
56 | const cf = await ElMessageBox.confirm(T('Confirm?', { param: T('BatchDelete') }), {
57 | confirmButtonText: T('Confirm'),
58 | cancelButtonText: T('Cancel'),
59 | type: 'warning',
60 | }).catch(_ => false)
61 | if (!cf) {
62 | return false
63 | }
64 |
65 | const res = await batchRemove({ ids }).catch(_ => false)
66 | if (res) {
67 | ElMessage.success(T('OperationSuccess'))
68 | getList()
69 | }
70 | }
71 |
72 | return {
73 | listRes,
74 | listQuery,
75 | getList,
76 | handlerQuery,
77 | del,
78 | batchDelete,
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/views/user/token.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
13 |
14 |
15 |
16 | {{ T('Filter') }}
17 | {{ T('BatchDelete') }}
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | {{ allUsers?.find(u => u.id === row.user_id)?.username }}
28 |
29 |
30 |
31 |
32 | {{ maskToken(row.token) }}
33 |
34 |
35 |
36 |
37 |
38 | {{ row.expired_at ? new Date(row.expired_at * 1000).toLocaleString() : '-' }}
39 |
40 |
41 |
42 |
43 | {{ T('Logout') }}
44 |
45 |
46 |
47 |
48 |
49 |
55 |
56 |
57 |
58 |
59 |
60 |
103 |
104 |
111 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import * as path from 'path'
3 | import * as dotenv from 'dotenv'
4 | import * as fs from 'fs'
5 | import vue from '@vitejs/plugin-vue'
6 |
7 | const NODE_ENV = process.env.NODE_ENV || 'development'
8 | const envFile = `.env.${NODE_ENV}`
9 | const envConfig = dotenv.parse(fs.readFileSync(envFile))
10 | for (const k in envConfig) {
11 | process.env[k] = envConfig[k]
12 | }
13 |
14 | let alias = {
15 | '@': path.resolve(__dirname, './src'),
16 | 'vue$': 'vue/dist/vue.runtime.esm-bundler.js',
17 | }
18 |
19 | const conf = {
20 | base: './', // index.html文件所在位置
21 | root: './', // js导入的资源路径,src
22 | server: {
23 | open: true,
24 | port: process.env.VITE_DEV_PORT,
25 | proxy: {
26 | [process.env.VITE_SERVER_API]: {
27 | target: process.env.VITE_SERVER_PATH,
28 | // rewrite: path => path.replace(/^\/api/, '/api'), //为了模拟
29 | changeOrigin: true,
30 | },
31 | },
32 | },
33 | build: {
34 | target: 'es2020',
35 | minify: 'esbuild', // 是否进行压缩,boolean | 'terser' | 'esbuild',默认使用 esbuild
36 | manifest: false, // 是否产出maifest.json
37 | sourcemap: false, // 是否产出soucemap.json
38 | emptyOutDir: true,
39 | outDir: 'dist', // 产出目录
40 | rollupOptions: {
41 | output: {
42 | manualChunks (id) {
43 | if (id.includes('node_modules')) {
44 | const arr = id.toString().split('node_modules/')[1].split('/')
45 | switch (arr[0]) {
46 | case '@popperjs':
47 | case '@vue':
48 | case 'axios':
49 | case 'element-plus':
50 | case '@element-plus':
51 | return '_' + arr[0]
52 | default :
53 | return '__vendor'
54 | }
55 | }else if(id.includes('Gwen-admin/src')){
56 | //src 下的都打包到一起 不然很多小文件
57 | return 'gwen'
58 | }
59 | },
60 | chunkFileNames: 'static/chunk/[name]-[hash].js',
61 | entryFileNames: 'static/entry/[name]-[hash].js',
62 | assetFileNames: 'static/[ext]/[name]-[hash].[ext]'
63 | },
64 | },
65 | },
66 | css: {
67 | preprocessorOptions: {
68 | scss: {
69 | javascriptEnabled: true,
70 | },
71 | },
72 | },
73 | resolve: {
74 | alias,
75 | },
76 | plugins: [
77 | vue(),
78 | ],
79 | }
80 |
81 | // https://vitejs.dev/config/
82 | export default defineConfig(conf)
83 |
--------------------------------------------------------------------------------