Compare commits

..

176 Commits

Author SHA1 Message Date
ibuler
c9c2a37f30 perf: fix applet host detail org error 2025-03-27 19:06:45 +08:00
zhaojisen
c1fda10620 Fixed: Allowed Tag 2025-03-26 18:21:35 +08:00
zhaojisen
df1a0228d9 Fixed: XSS 2025-03-25 18:22:49 +08:00
ibuler
959ae0bb2c fix: applet host update error 2025-03-25 10:12:53 +08:00
zhaojisen
2b22c90701 Fixed: Clone Asset 2025-03-24 14:16:05 +08:00
ibuler
6d16473e4b fix: applet host create protocol err 2025-03-24 14:03:40 +08:00
feng
c046c28f6d perf: Login acl license 2025-03-21 14:24:53 +08:00
Bryan
68030d98b2 Merge pull request #4890 from jumpserver/dev
v4.8.0
2025-03-20 18:44:31 +08:00
w940853815
9cbdfacfd2 fix: remove TaskList from AccountPushExecutionInfo 2025-03-20 18:29:17 +08:00
ibuler
2dec820a0a perf: add default conn btn 2025-03-20 18:13:53 +08:00
ibuler
84a66d8c5a perf: asset update 2025-03-20 18:01:03 +08:00
ibuler
6eedfe994e perf: update automation submit 2025-03-20 17:37:54 +08:00
ibuler
f1982c2d8f perf: add no cache meta interface 2025-03-20 17:10:16 +08:00
zhaojisen
4dac455575 Fixed: Role Edit 2025-03-20 17:04:19 +08:00
ibuler
affb9e405e perf: update platform automation update 2025-03-20 16:54:20 +08:00
ibuler
2bf8090b60 perf: update clone error 2025-03-20 16:45:40 +08:00
ibuler
266ddc7b51 pr@dev@perf_update_role_etail 2025-03-20 16:45:40 +08:00
fit2bot
5831940be0 perf: update some clone (#4882)
* pr@dev@perf_update_role_etail

* perf: update clone error

* perf: update query set

* perf: update some clone

---------

Co-authored-by: ibuler <ibuler@qq.com>
2025-03-20 16:44:26 +08:00
zhaojisen
8ae747b9a5 Drawer 2025-03-20 16:28:20 +08:00
zhaojisen
8b69f25e7b Fixed: Account Change Secret 2025-03-20 16:13:41 +08:00
zhaojisen
883d44cede Fixed: Translate 2025-03-20 16:03:50 +08:00
zhaojisen
024051df76 Fixed: Plateform Clone 2025-03-20 15:53:19 +08:00
zhaojisen
564e530a96 Fixed: Amount Prevent 2025-03-20 15:38:16 +08:00
ibuler
d3a0f69b4c perf: update platform detail 2025-03-20 15:17:13 +08:00
w940853815
69f263e491 fix: Failed to change secret in batch. 2025-03-20 15:07:07 +08:00
zhaojisen
ad82040a54 Fixed: Account Detail 2025-03-20 15:01:47 +08:00
ibuler
d994c60378 perf: update account detail 2025-03-20 14:53:33 +08:00
zhaojisen
d7fddf405f Fixed: Drawer Detail 2025-03-20 14:44:23 +08:00
ibuler
ad9660b1f2 perf: update perm detail 2025-03-20 14:20:46 +08:00
ibuler
8a5c60a9bc perf: 优化 detail 中 component 2025-03-20 14:20:46 +08:00
zhaojisen
1782c8af74 Fixed: PAM Asset Detail 2025-03-20 12:46:42 +08:00
zhaojisen
ad55f6f6a6 Fixed: Resource Translate 2025-03-20 12:39:52 +08:00
ibuler
377d17b19d perf: after close action 2025-03-20 12:08:55 +08:00
老广
51e8b82bbf Merge pull request #4866 from jumpserver/pr@dev@update_drawer_title
perf: update title draw again
2025-03-20 11:56:19 +08:00
ibuler
881e2d05c7 perf: update title draw again 2025-03-20 11:53:21 +08:00
feng
44c39de459 perf: report 2025-03-20 11:23:31 +08:00
老广
e2f87e62f2 Merge pull request #4864 from jumpserver/pr@dev@update_title
perf: update get draw title
2025-03-20 11:18:42 +08:00
ibuler
f27d6f0e97 perf: update get draw title 2025-03-20 11:17:41 +08:00
feng
42d8414834 perf: report 2025-03-20 11:16:51 +08:00
zhaojisen
138bdad7ef Fixed: Execution Record 2025-03-20 11:08:35 +08:00
zhaojisen
9575936849 Fixed: Auth 2025-03-20 10:54:26 +08:00
老广
de3338c3f1 Merge pull request #4860 from jumpserver/pr@dev@perf_drawer_title
perf: for perfect drawer create update detail or any
2025-03-20 10:52:56 +08:00
ibuler
f95a78bec1 merge: with dev 2025-03-20 10:52:22 +08:00
ibuler
4be9e048e1 perf: for perfect drawer create update detail or any 2025-03-20 10:47:59 +08:00
老广
4f70bf770c Revert "perf: update detail drawer show"
This reverts commit 3361b00bd1.
2025-03-19 23:16:40 +08:00
ibuler
3361b00bd1 perf: update detail drawer show 2025-03-19 21:05:53 +08:00
ibuler
ac6a0f49da perf: update drawer show before and after handler 2025-03-19 19:02:51 +08:00
feng
c08f446464 perf: Change secret 2025-03-19 18:56:37 +08:00
jiangweidong
849d2829f4 fix: CTYun cannot sync 2025-03-19 17:59:20 +08:00
zhaojisen
5d6b48831c Fixed: Record 2025-03-19 17:53:31 +08:00
zhaojisen
d557957b34 Fixed: Connect Direct Filter SFTP 2025-03-19 17:20:29 +08:00
zhaojisen
08821663ce Fixed: Authj 2025-03-19 17:15:22 +08:00
w940853815
3d5ea46930 fix: Account filter error 2025-03-19 16:33:46 +08:00
zhaojisen
06c87b05cf Fixed: View Secret 2025-03-19 16:25:27 +08:00
ibuler
24ba0e38c1 perf: 解决 tab 中创建更新的问题 2025-03-19 16:17:41 +08:00
ibuler
adc80ce74a perf: update applet host 2025-03-19 16:17:41 +08:00
zhaojisen
d03fe5d95a Fixed: Asset Account Detail 2025-03-19 16:12:12 +08:00
w940853815
05499d6430 fix: Add account_deleted_accounts to RiskSummary 2025-03-19 15:26:29 +08:00
zhaojisen
74f619c961 Fixed: Asset Add Account Template 2025-03-19 15:22:24 +08:00
w940853815
b0aec69060 fix: Add statusMap for account status 2025-03-19 14:45:52 +08:00
zhaojisen
d7b7704b21 Fixed: Tickect Session 2025-03-19 12:42:46 +08:00
feng
2f8480eea6 perf: Asset domain 2025-03-19 11:26:55 +08:00
feng
bdb5c83814 perf: Licese 2025-03-18 18:56:13 +08:00
w940853815
082ecb15f7 fix: add change_password_add to discover confirm handler 2025-03-18 18:02:32 +08:00
zhaojisen
8981a8f74f Fixed: Tickect Session 2025-03-18 18:01:09 +08:00
ibuler
b8efbf1e82 perf: update handler formatter 2025-03-18 17:53:35 +08:00
ibuler
f7520b87bf perf: update risk handler 2025-03-18 17:53:35 +08:00
ibuler
a146c26e20 perf: risk handler add delete account action 2025-03-18 17:53:35 +08:00
w940853815
d0b8efc796 fix: update remote_present status on delete_remote command 2025-03-18 16:48:48 +08:00
zhaojisen
61e5b061e9 Fixed: Plateform Automation Update 2025-03-18 16:20:12 +08:00
w940853815
84bbe4de8b fix: Restrict add asset and gateway permissions for non-root organizations 2025-03-18 15:48:36 +08:00
zhaojisen
7c20d08ce2 Fixed: Change Secret Dashboard Jump 2025-03-18 15:24:48 +08:00
w940853815
1d288f6212 fix: Update IP group display format in detail view 2025-03-18 15:14:34 +08:00
w940853815
7664487a1c perf: Translate processing 2025-03-18 14:50:03 +08:00
feng
4502375ed0 perf: change secret 2025-03-18 13:30:21 +08:00
w940853815
6aab902762 fix: Create ssh_key set is_active error 2025-03-18 11:28:42 +08:00
ibuler
597b5ac4cf perf: update loading spinner 2025-03-18 11:24:57 +08:00
ibuler
e26fecfee8 perf: update detail tab page loading 2025-03-18 11:24:32 +08:00
ibuler
661fc3a3d8 perf: update drawer size and ticket detail 2025-03-18 11:23:59 +08:00
jiangweidong
f601e25302 fix: No account when syncing with the cloud 2025-03-18 09:47:51 +08:00
zhaojisen
63e4776355 Fixed: Remote Ttile 2025-03-17 18:18:10 +08:00
ibuler
d67d275ae3 perf: update cloud sync err 2025-03-17 18:04:11 +08:00
ibuler
a8a1a56f3d perf: 统一 automations 历史 2025-03-17 17:58:08 +08:00
zhaojisen
f730611102 Fixed: Word Spell 2025-03-17 17:54:04 +08:00
w940853815
2344203563 fix: Job detail error 2025-03-17 17:46:22 +08:00
zhaojisen
f1a79cc36d Fixed: Filter SFTP 2025-03-17 16:35:41 +08:00
zhaojisen
194ea09cab Fixed: Session Record 2025-03-17 16:20:11 +08:00
w940853815
b672288725 fix: Update counter naming for group and sudo changes in RiskSummary 2025-03-17 16:01:58 +08:00
zhaojisen
25974c7cf1 Fixed: Fail Account Pagegation 2025-03-17 15:45:57 +08:00
ibuler
155da5aecd perf: save to localStorage except cookie 2025-03-17 15:28:11 +08:00
w940853815
7c72bcf84a fix: Create/clone ssh public key failed 2025-03-17 15:16:57 +08:00
ibuler
5a40280baa perf: 优化 tab detail 中的一些问题 2025-03-17 15:08:26 +08:00
zhaojisen
1254a4105a Fixed: Tickets Session 2025-03-17 14:54:11 +08:00
zhaojisen
9a868ec50a Fixed: Ticket 2025-03-17 14:25:20 +08:00
zhaojisen
534f784fda Fixed: Remove _ 2025-03-17 11:19:50 +08:00
zhaojisen
6d83b868ed Fixed: Connect Token 2025-03-17 11:19:50 +08:00
zhaojisen
e37b813028 Fixed: Passkeys 2025-03-17 11:05:50 +08:00
ibuler
01d44bc84e perf: 优化计数卡 2025-03-17 10:54:05 +08:00
fit2bot
fa5f4348ea perf: 优化数量计算 (#4808)
Co-authored-by: ibuler <ibuler@qq.com>
2025-03-14 19:08:14 +08:00
feng
90b481c71d perf: translate 2025-03-14 18:27:36 +08:00
zhaojisen
6e8b9b4df8 Fixed: Account 2025-03-14 18:22:40 +08:00
Bryan
a861f77609 Merge pull request #4659 from jumpserver/dev
v4.7.0
2025-02-20 10:20:32 +08:00
Bryan
e58ec6057c Merge pull request #4600 from jumpserver/dev
v4.6.0
2025-01-15 14:39:21 +08:00
Bryan
7ab20c5885 Merge pull request #4537 from jumpserver/dev
v4.5.0
2024-12-19 15:57:44 +08:00
Bryan
e47ddb5355 Merge pull request #4451 from jumpserver/dev
v4.4.0
2024-11-21 19:01:11 +08:00
Bryan
56aa3caa83 Merge pull request #4410 from jumpserver/dev
v4.3.0
2024-10-17 14:56:18 +08:00
Bryan
19b1dc0dbc Merge pull request #4367 from jumpserver/dev
merge: from dev to master
2024-09-19 19:36:42 +08:00
Bryan
77ef172a23 Merge pull request #4320 from jumpserver/dev
v4.1.0
2024-08-15 21:42:42 +08:00
Bryan
4596887bf1 Merge pull request #4178 from jumpserver/dev
v4.0.0
2024-07-03 19:06:07 +08:00
Bryan
0a3dc30c85 Merge pull request #4072 from jumpserver/dev
v3.10.11-lts
2024-06-19 16:04:12 +08:00
Bryan
51d24bc8e5 Merge pull request #3941 from jumpserver/dev
v3.10.10-lts
2024-05-16 16:05:04 +08:00
Bryan
1b15a4d043 Merge pull request #3871 from jumpserver/dev
v3.10.9 (dev to master)
2024-04-22 19:44:33 +08:00
Bryan
7d3f818242 Merge pull request #3864 from jumpserver/v3.10
v3.10.8
2024-04-18 17:58:05 +08:00
Bryan
4e26f18d77 Merge pull request #3862 from jumpserver/dev
v3.10.8
2024-04-18 17:17:36 +08:00
Bryan
b22613617a Revert "build(deps): bump follow-redirects from 1.15.3 to 1.15.4"
This reverts commit e971cbf4a8.
2024-03-27 16:16:07 +08:00
dependabot[bot]
e971cbf4a8 build(deps): bump follow-redirects from 1.15.3 to 1.15.4
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.3 to 1.15.4.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.3...v1.15.4)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-27 16:11:26 +08:00
wangruidong
4672abae35 fix: 刷新页面根据搜索条件过滤出对应的资源 2024-03-04 19:17:05 +08:00
Bryan
ba36d72602 Merge pull request #3761 from jumpserver/master
v3.10.4 (branch-v3.10)
2024-02-29 16:26:13 +08:00
Bryan
4bfbbba4c5 Merge pull request #3760 from jumpserver/dev
v3.10.4
2024-02-29 16:15:33 +08:00
Bryan
ea038ce43a Merge pull request #3697 from jumpserver/master
v3.10.2
2024-01-17 13:34:12 +00:00
Bryan
e16b19666c Merge pull request #3696 from jumpserver/dev
v3.10.2
2024-01-17 13:33:22 +00:00
Bryan
c7f5409eb6 Merge pull request #3694 from jumpserver/master
v3.10.2
2024-01-17 07:35:33 -04:00
Bryan
fdbd7d2222 Merge pull request #3693 from jumpserver/dev
v3.10.2
2024-01-17 07:24:50 -04:00
Bryan
ddbaeeafea Merge pull request #3668 from jumpserver/master
v3.10.1
2023-12-29 11:34:04 +05:00
Bryan
efb0e9dacb Merge pull request #3665 from jumpserver/dev
v3.10.1
2023-12-29 11:14:54 +05:00
huailei
f6f8301ad5 Revert "perf: 账号收集翻译"
This reverts commit 9a63ae63d4.
2023-12-22 15:25:31 +08:00
“huailei000”
9a63ae63d4 perf: 账号收集翻译 2023-12-22 11:31:45 +08:00
Bryan
1e007ccda3 Merge pull request #3642 from jumpserver/dev
v3.10
2023-12-21 15:15:52 +05:00
老广
d1d0b06b53 Merge pull request #3546 from jumpserver/dev
v3.9.0
2023-11-16 18:25:10 +08:00
Bryan
5fb70d2f24 Merge pull request #3450 from jumpserver/dev
v3.8.0
2023-10-19 03:33:53 -05:00
Bryan
b54a95430f Merge pull request #3404 from jumpserver/dev
v3.7.0
2023-09-21 17:04:42 +08:00
Bryan
4d8b4c45af Merge pull request #3355 from jumpserver/dev
v3.6.0
2023-08-17 14:00:33 +05:00
Bryan
a6d642df60 Merge pull request #3283 from jumpserver/dev
v3.5.0
2023-07-20 19:04:29 +08:00
Jiangjie.Bai
2e74f1522f Merge pull request #3222 from jumpserver/dev
v3.4.0
2023-06-15 14:51:36 +08:00
Jiangjie.Bai
fe615e0314 Merge pull request #3219 from jumpserver/dev
v3.4.0
2023-06-15 14:17:46 +08:00
Jiangjie.Bai
09f734e6fc Merge pull request #3135 from jumpserver/dev
v3.3.0
2023-05-18 19:18:11 +08:00
Jiangjie.Bai
3117046342 Merge pull request #3061 from jumpserver/dev
v3.2.0
2023-04-20 18:40:08 +08:00
Bai
b68aecb5cc fix: 批量更新资产平台help-text 2023-04-20 18:39:22 +08:00
Jiangjie.Bai
1c9b155d97 Merge pull request #3057 from jumpserver/dev
v3.2.0
2023-04-20 18:22:46 +08:00
Jiangjie.Bai
75b1be9864 Merge pull request #3019 from jumpserver/dev
v3.2.0 rc2
2023-04-14 19:01:37 +08:00
Jiangjie.Bai
615c3c1cf4 Merge pull request #3014 from jumpserver/dev
v3.2.0 rc1
2023-04-13 20:02:38 +08:00
Jiangjie.Bai
4d82231af4 Merge pull request #3012 from jumpserver/dev
v3.2.0 rc1
2023-04-13 19:22:38 +08:00
“huailei000”
c6cf6571b6 perf: ldap导入用户列表-组织下拉框设置最大宽度 2023-03-16 16:44:36 +08:00
Bai
8ea990d070 fix: 修复创建资产添加账号模版报错问题 2023-03-16 16:44:36 +08:00
“huailei000”
f4a32170d5 perf: message 2023-03-16 16:44:36 +08:00
ibuler
073508675e perf: 添加默认的信息 2023-03-16 16:44:36 +08:00
Jiangjie.Bai
1d6ca0a93a Merge pull request #2924 from jumpserver/dev
v3.1.0 rc4
2023-03-15 19:46:31 +08:00
Jiangjie.Bai
36aea652d6 Merge pull request #2788 from jumpserver/dev
v3.0.0
2023-02-23 20:16:41 +08:00
Jiangjie.Bai
1a42ce90ab Merge pull request #2760 from jumpserver/dev
v3.0.0-rc-latest
2023-02-22 22:21:54 +08:00
Jiangjie.Bai
31a401b55d Merge pull request #2463 from jumpserver/dev
v3.0.0-rc4
2023-01-31 18:55:34 +08:00
Jiangjie.Bai
582a84178d Merge pull request #2187 from jumpserver/dev
v2.28.0
2022-11-17 17:44:19 +08:00
Jiangjie.Bai
9b9f7c936c Merge pull request #2184 from jumpserver/dev
v2.28.0-rc5
2022-11-17 14:18:15 +08:00
Jiangjie.Bai
2a6100957f Merge pull request #2182 from jumpserver/dev
v2.28.0-rc4
2022-11-16 21:08:55 +08:00
Jiangjie.Bai
16606d6a27 Merge pull request #2176 from jumpserver/dev
v2.28.0-rc2
2022-11-14 10:01:05 +08:00
Jiangjie.Bai
0a612f50e6 Merge pull request #2164 from jumpserver/dev
v2.28.0-rc1
2022-11-10 17:45:47 +08:00
Jiangjie.Bai
fe36fa9390 Merge pull request #2117 from jumpserver/dev
v2.27.0-rc4
2022-10-18 21:02:10 +08:00
Jiangjie.Bai
ba109900ec Merge pull request #2113 from jumpserver/dev
v2.27.0-rc3
2022-10-18 11:20:57 +08:00
Jiangjie.Bai
ec7768267f Merge pull request #2105 from jumpserver/dev
v2.27.0-rc2
2022-10-14 11:01:32 +08:00
Jiangjie.Bai
cc58b374ab Merge pull request #2101 from jumpserver/dev
v2.27.0-rc1
2022-10-13 17:44:53 +08:00
Jiangjie.Bai
04ffbb8fd6 Merge pull request #2097 from jumpserver/dev
v2.27.0-rc1
2022-10-13 15:14:40 +08:00
Jiangjie.Bai
49880f6739 Merge pull request #2059 from jumpserver/dev
v2.26.0
2022-09-15 17:49:44 +08:00
Jiangjie.Bai
e6f98d58c4 Merge pull request #2057 from jumpserver/dev
v2.26.0-rc4
2022-09-15 16:18:03 +08:00
Jiangjie.Bai
fd1f16d43c Merge pull request #2050 from jumpserver/dev
v2.26.0-rc2
2022-09-13 17:41:39 +08:00
Jiangjie.Bai
968b2415b1 Merge pull request #2043 from jumpserver/dev
v2.26.0-rc1
2022-09-08 15:46:44 +08:00
Jiangjie.Bai
776090d6ba Merge pull request #2001 from jumpserver/dev
v2.25.0
2022-08-18 16:12:45 +08:00
Jiangjie.Bai
3a37952288 Merge pull request #1996 from jumpserver/dev
v2.25.0-rc4
2022-08-17 16:53:23 +08:00
Jiangjie.Bai
62b8fc0e3b Merge pull request #1994 from jumpserver/dev
v2.25.0-rc3
2022-08-16 19:08:23 +08:00
Jiangjie.Bai
b2028869cb Merge pull request #1986 from jumpserver/dev
v2.25.0-rc2
2022-08-12 18:06:56 +08:00
Jiangjie.Bai
5277a725f8 Merge pull request #1973 from jumpserver/dev
v2.25.0-rc1
2022-08-11 14:11:59 +08:00
Jiangjie.Bai
f137788c1a Merge pull request #1957 from jumpserver/dev
v2.24.0-rc5
2022-07-20 19:06:03 +08:00
Jiangjie.Bai
f7d17c8de7 Merge pull request #1954 from jumpserver/dev
v2.24.0-rc4
2022-07-19 16:18:13 +08:00
Jiangjie.Bai
feea70b0be Merge pull request #1944 from jumpserver/dev
v2.24.0-rc3
2022-07-18 12:05:42 +08:00
Jiangjie.Bai
04696ef3d6 Merge pull request #1940 from jumpserver/dev
v2.24.0-rc2
2022-07-15 18:07:37 +08:00
Jiangjie.Bai
1731f4f788 Merge pull request #1934 from jumpserver/dev
v2.24.0-rc1
2022-07-14 18:27:51 +08:00
Jiangjie.Bai
6f25d93909 Merge pull request #1931 from jumpserver/dev
v2.24.0-rc1
2022-07-14 17:51:58 +08:00
Jiangjie.Bai
46461ec324 Merge pull request #1925 from jumpserver/dev
v2.24.0-rc1
2022-07-14 15:12:15 +08:00
129 changed files with 1178 additions and 1306 deletions

View File

@@ -24,7 +24,7 @@
z-index: 9999;
}
.spinner {
#loading .spinner {
width: 40px;
height: 40px;
border: 3px solid transparent;
@@ -33,7 +33,7 @@
animation: spin 1s linear infinite;
}
.spinner::after {
#loading .spinner::after {
content: '';
position: absolute;
top: -3px;

View File

@@ -234,6 +234,9 @@ export const accountFieldsMeta = (vm) => {
el: {},
hidden: (formValue) => {
const automation = vm.iPlatform.automation || {}
if (!vm.iPlatform.automation) {
return true
}
vm.fieldsMeta.params.el.method = vm.iPlatform.automation.push_account_method
vm.fieldsMeta.params.el.pushAccountParams = vm.iPlatform.automation.push_account_params
return !formValue.push_now ||

View File

@@ -184,7 +184,7 @@ export default {
},
handleCloseDrawer() {
this.iVisible = false
Reflect.deleteProperty(this.$route.query, 'flag')
// Reflect.deleteProperty(this.$route.query, 'flag')
},
handleAccountOperation(id, path, data) {
this.$axios.post(`/api/v1/accounts/accounts/${id}/${path}/`, data).then((res) => {

View File

@@ -185,6 +185,10 @@ export default {
width: '120px',
formatterArgs: {
can: () => vm.$hasPerm('accounts.view_account'),
getRoute: ({ row }) => ({
name: 'AccountDetail',
params: { id: row.id }
}),
getDrawerTitle({ row }) {
return `${row.username}@${row.asset.name}`
}
@@ -205,7 +209,6 @@ export default {
formatter: AccountConnectFormatter,
formatterArgs: {
buttonIcon: 'fa fa-desktop',
titleText: '可选协议',
url: '/api/v1/assets/assets/{id}',
can: () => this.currentUserIsSuperAdmin,
connectUrlTemplate: (row) => `/luna/pam_connect/${row.id}/${row.username}/${row.asset.id}/${row.asset.name}/`,
@@ -295,7 +298,7 @@ export default {
type: 'primary',
icon: 'plus',
can: () => {
return vm.$hasPerm('accounts.add_account') && !this.$store.getters.currentOrgIsRoot
return vm.$hasPerm('accounts.add_account') && !vm.$store.getters.currentOrgIsRoot
},
callback: () => {
setTimeout(() => {
@@ -311,7 +314,7 @@ export default {
title: this.$t('TemplateAdd'),
has: !(this.platform || this.asset),
can: () => {
return vm.$hasPerm('accounts.add_account') && !this.$store.getters.currentOrgIsRoot
return vm.$hasPerm('accounts.add_account') && !vm.$store.getters.currentOrgIsRoot
},
callback: async() => {
await this.getAssetDetail()
@@ -445,7 +448,7 @@ export default {
Object.assign(this.account, account)
},
addAccountSuccess() {
Reflect.deleteProperty(this.$route.query, 'flag')
// Reflect.deleteProperty(this.$route.query, 'flag')
this.isUpdateAccount = false
this.$refs.ListTable.reloadTable()
},

View File

@@ -151,7 +151,7 @@ export const accountOtherActions = (vm) => [
title: vm.$t('CopyToAsset'),
type: 'primary',
divided: true,
can: () => vm.$hasPerm('accounts.create_account') && !vm.$store.getters.currentOrgIsRoot,
can: () => vm.$hasPerm('accounts.add_account') && !vm.$store.getters.currentOrgIsRoot,
has: () => {
return !vm.asset
},
@@ -167,7 +167,7 @@ export const accountOtherActions = (vm) => [
name: 'MoveToOther',
title: vm.$t('MoveToAsset'),
type: 'primary',
can: () => vm.$hasPerm('accounts.create_account') && !vm.$store.getters.currentOrgIsRoot,
can: () => vm.$hasPerm('accounts.add_account') && !vm.$store.getters.currentOrgIsRoot,
has: () => {
return !vm.asset
},
@@ -223,7 +223,7 @@ export const accountQuickFilters = (vm) => [
{
label: vm.$t('NoLoginLongTime'),
filter: {
risk: 'long_time_no_login'
long_time_no_login: 'true'
}
},
{

View File

@@ -1,19 +1,19 @@
<template>
<div>
<div class="summary-header">
<el-tooltip :content="title" placement="top" :open-delay="500">
<el-tooltip :content="title" :open-delay="500" placement="top">
<span class="title">{{ title }}</span>
</el-tooltip>
</div>
<slot>
<h3 class="no-margins ">
<span
v-async="iCount"
:class="{ 'can-direct': canDirect }"
class="num"
:class="{ 'can-direct': body.canDirect ? true : false }"
@click="handleClick"
>
-
<span v-if="count === null"> - </span>
<span v-else>{{ count }}</span>
</span>
</h3>
</slot>
@@ -21,7 +21,6 @@
</template>
<script>
export default {
name: 'SummaryCard',
props: {
@@ -29,18 +28,18 @@ export default {
type: String,
default: ''
},
body: {
type: Object,
default: () => ({})
},
count: {
type: [Number, String, Promise],
default: 0
default: null
},
route: {
type: [String, Object],
default: ''
},
canDirect: {
type: Boolean,
default: false
},
callback: {
type: Function,
default: () => {
@@ -54,25 +53,13 @@ export default {
data() {
return {}
},
computed: {
iCount() {
const count = this.body.count || this.count
return count
},
iRoute() {
return this.body.route || this.route
},
iDisabled() {
return this.body.disabled === undefined ? this.disabled : this.body.disabled
}
},
methods: {
handleClick() {
if (this.iDisabled) {
if (this.disabled) {
return
}
if (this.iRoute) {
this.$router.push(this.iRoute)
if (this.route) {
this.$router.push(this.route)
return
}
this.callback.bind(this)()

View File

@@ -6,7 +6,7 @@
<div class="content">
<el-row justify="space-between" type="flex">
<el-col v-for="item of summaryItems" :key="item.title" :md="8" :sm="12" :xs="12">
<SummaryCard :body="item.body" :title="item.title" />
<SummaryCard :title="item.title" v-bind="item.body" />
</el-col>
</el-row>
</div>

View File

@@ -7,9 +7,9 @@
<SummaryCard
v-for="item of items"
:key="item.title"
:body="item.body"
:title="item.title"
class="summary-card"
v-bind="item.body"
/>
</div>
</div>

View File

@@ -4,7 +4,7 @@
:visible="iVisible"
class="processing-dialog"
height="300"
title="Processing"
:title="$tc('Processing')"
width="300"
@confirm="iVisible=false"
>

View File

@@ -30,6 +30,8 @@
</template>
<script>
import { getDrawerWidth } from '@/utils/common'
export default {
props: {
title: {
@@ -38,7 +40,9 @@ export default {
},
size: {
type: String,
default: '768px'
default: () => {
return getDrawerWidth()
}
},
component: {
type: [String, Function, Object],

View File

@@ -89,7 +89,7 @@ export default {
}
},
mounted() {
// console.log('Method: ', this.method)
// this.$log.debug('>>> Method: ', this.method)
this.optionUrlMetaAndGenerateColumns()
},
methods: {

View File

@@ -172,7 +172,10 @@ export default {
const endVal = this.countIndex(end)
for (let i = startVal; i < (endVal === 0 ? 48 : endVal); i++) {
const curWeek = this.weekTimeData[idNum]
curWeek.child[i].check = true
const curChild = curWeek.child[i]
if (curChild) {
curChild.check = true
}
}
},
// 计算索引

View File

@@ -1133,7 +1133,7 @@ export default {
* @public
*/
clearSelection() {
return this.selectStrategy.clearSelection()
return this.selectStrategy?.clearSelection()
},
// 弹窗相关
// 除非树形结构在操作列点击新增, 否则 row 是 MouseEvent

View File

@@ -13,15 +13,28 @@ class StrategyAbstract {
this.onSelect = this.onSelect.bind(this)
this.onSelectAll = this.onSelectAll.bind(this)
}
get elTable() {
return this.elDataTable.$refs.table
}
onSelectionChange() {}
onSelect() {}
onSelectAll() {}
toggleRowSelection() {}
clearSelection() {}
updateElTableSelection() {}
onSelectionChange() {
}
onSelect() {
}
onSelectAll() {
}
toggleRowSelection() {
}
clearSelection() {
}
updateElTableSelection() {
}
}
/**
@@ -34,14 +47,16 @@ class StrategyNormal extends StrategyAbstract {
onSelectionChange(val) {
this.elDataTable.selected = val
}
/**
* toggleRowSelection和clearSelection的表现与el-table一致
*/
toggleRowSelection(...args) {
return this.elTable.toggleRowSelection(...args)
}
clearSelection() {
return this.elTable.clearSelection()
return this.elTable?.clearSelection()
}
}
@@ -65,6 +80,7 @@ class StrategyPersistSelection extends StrategyAbstract {
const isChosen = selection.indexOf(row) > -1
this.toggleRowSelection(row, isChosen)
}
/**
* 用户切换当前页的多选
*/
@@ -85,7 +101,7 @@ class StrategyPersistSelection extends StrategyAbstract {
// 判断是全选还是取消全选
const shouldSelectAll = currentPageSelectedCount < selectableRows.length
this.elTable.clearSelection()
this.elTable?.clearSelection()
if (shouldSelectAll) {
selectableRows.forEach(row => {
@@ -112,6 +128,7 @@ class StrategyPersistSelection extends StrategyAbstract {
this.elDataTable.$emit('selection-change', this.elDataTable.selected)
}
/**
* toggleRowSelection和clearSelection管理elDataTable的selected数组
* 记得最后要将状态同步到el-table中
@@ -133,10 +150,12 @@ class StrategyPersistSelection extends StrategyAbstract {
this.elDataTable.$emit('toggle-row-selection', isSelected, row)
this.updateElTableSelection()
}
clearSelection() {
this.elDataTable.selected = []
this.updateElTableSelection()
}
/**
* 将selected状态同步到el-table中
*/
@@ -144,7 +163,7 @@ class StrategyPersistSelection extends StrategyAbstract {
const { data, id, selected } = this.elDataTable
const selectedIds = new Set(selected.map(r => r[id]))
this.elTable.clearSelection()
this.elTable?.clearSelection()
data.forEach(row => {
const shouldBeSelected = selectedIds.has(row[id])

View File

@@ -129,7 +129,7 @@ export default {
watch: {},
methods: {
getList() {
this.$refs.table.clearSelection()
this.$refs.table?.clearSelection()
return this.$refs.table.getList()
},
getData() {

View File

@@ -2,14 +2,11 @@
<Drawer
:component="component"
:component-listeners="listener"
:size="drawerSize"
:title="title"
:visible.sync="iVisible"
append-to-body
class="form-drawer"
destroy-on-close
v-bind="props"
@close="closeDrawer"
v-on="$listeners"
/>
</template>
@@ -49,31 +46,26 @@ export default {
}
},
computed: {
drawerSize() {
const drawerWidth = localStorage.getItem('drawerWidth')
if (drawerWidth && drawerWidth > 100 && drawerWidth < 2000) {
return drawerWidth + 'px'
}
const width = window.innerWidth
if (width >= 800) return '767px'
return '90%'
},
iVisible: {
get() {
return this.visible
},
set(val) {
this.$log.debug('>>> PageDrawer set to: ', val, this)
this.$emit('update:visible', val)
}
}
},
mounted() {
this.$log.debug('>>> PageDrawer component: ', this.component)
},
methods: {
closeDrawer() {
this.iVisible = false
// 关闭 Drawer 后,清空所有 params 参数
Reflect.ownKeys(this.$route.params).forEach(key => {
Reflect.deleteProperty(this.$route.params, key)
})
// Reflect.ownKeys(this.$route.params).forEach(key => {
// Reflect.deleteProperty(this.$route.params, key)
// })
}
}
}

View File

@@ -6,7 +6,8 @@
:table-config="iTableConfig"
v-bind="$attrs"
/>
<PageDrawer
<Drawer
v-if="drawerComponent"
:action="action"
:class="[action]"
:component="drawerComponent"
@@ -20,7 +21,7 @@
<script>
import ListTable from '../ListTable'
import PageDrawer from './PageDrawer.vue'
import Drawer from '@/components/Drawer/index.vue'
import { setUrlParam, toLowerCaseExcludeAbbr, toSentenceCase } from '@/utils/common'
import { mapGetters } from 'vuex'
@@ -29,7 +30,7 @@ const drawerType = [String, Function]
export default {
name: 'GenericListPage',
components: {
ListTable, PageDrawer
ListTable, Drawer
},
props: {
detailDrawer: {
@@ -71,18 +72,14 @@ export default {
},
data() {
return {
title: '',
drawerTitle: '',
action: '',
visible: false,
drawerVisible: false,
drawerComponent: ''
}
},
computed: {
...mapGetters(['inDrawer']),
drawerTitle() {
return this.getDefaultTitle()
},
iHeaderActions() {
const actions = this.headerActions
if (!actions.onCreate) {
@@ -91,14 +88,11 @@ export default {
return actions
},
iTableConfig() {
const config = {
...this.tableConfig
}
const config = _.cloneDeep(this.tableConfig)
const actionMap = {
'columnsMeta.actions.formatterArgs.onUpdate': this.onUpdate,
'columnsMeta.actions.formatterArgs.onClone': this.onClone,
'columnsMeta.name.formatterArgs.drawer': true,
'columnsMeta.name.formatterArgs.drawerComponent': this.detailDrawer
'columnsMeta.name.formatterArgs.onClick': this.onDetail
}
for (const [key, value] of Object.entries(actionMap)) {
if (_.get(config, key)) {
@@ -108,11 +102,13 @@ export default {
}
const columnsMeta = config.columnsMeta
for (const value of Object.values(columnsMeta)) {
if (
value.formatter && value.formatter.name === 'AmountFormatter' &&
value.formatterArgs && !value.formatterArgs.drawer
) {
value.formatterArgs.drawer = this.detailDrawer
const formatter = value?.formatter
const formatterArgs = value?.formatterArgs
// console.log('>>> name: ', key)
// console.log('>>> formatter: ', formatter)
const detailFormaters = ['AmountFormatter', 'DetailFormatter']
if (formatter && detailFormaters.includes(formatter.name) && formatterArgs.drawer !== false) {
formatterArgs.onClick = this.onDetail
}
}
return config
@@ -127,12 +123,79 @@ export default {
this.drawerVisible = false
this.reloadTable()
}
},
drawerVisible: {
handler(val, oldVal) {
this.$log.debug('>>> drawerVisible changed: ', oldVal, '->', val)
if (!val && oldVal) {
this.$nextTick(() => {
this.afterCloseDrawer()
})
}
}
}
},
mounted() {
this.routeFreeze = {
params: _.cloneDeep(this.$route.params),
query: _.cloneDeep(this.$route.query)
}
this.$log.debug('>>> DrawerListTable mounted: ', this.routeFreeze)
},
destroyed() {
this.$log.debug('>>> DrawerListTable destroyed')
},
activated() {
this.$log.debug('>>> DrawerListTable activated')
},
deactivated() {
this.$log.debug('>>> DrawerListTable deactivated')
},
methods: {
getDefaultTitle() {
afterCloseDrawer() {
// 清空路由参数, 恢复路由参数
for (const key of ['params', 'query']) {
const curValue = this.$route[key] || {}
for (const k in curValue) {
this.$route[key][k] = ''
}
const value = this.routeFreeze[key] || {}
for (const k in value) {
this.$route[key][k] = value[k]
}
}
this.drawerComponent = ''
},
getDetailDrawerTitle({ col, row, cellValue, payload = {}}) {
this.$log.debug('>>> getDetailDrawerTitle: ', col, row, cellValue, payload)
const { detailRoute = {}, formatterArgs = {}} = payload
const getTitle = formatterArgs.getDrawerTitle
this.$log.debug('>>> getTitle: ', getTitle)
if (getTitle && typeof getTitle === 'function') {
return getTitle({ col, row, cellValue })
}
if (formatterArgs.title) {
return formatterArgs.title
}
const resolvedRoute = this.resolveRoute(detailRoute)
let title = cellValue || row.name
if (formatterArgs.getTitle) {
title = formatterArgs.getTitle({ col, row, cellValue })
}
let resource = resolvedRoute?.meta?.title || resolvedRoute?.name || ''
resource = resource.replace('Detail', '').replace('详情', '')
if (resource) {
title = `${resource}: ${title}`
}
return title
},
getActionDrawerTitle({ action, row, col, cellValue, payload }) {
if (action === 'detail') {
return this.getDetailDrawerTitle({ col, row, cellValue, payload })
}
let title = this.title
let dispatchAction = ''
if (!title && this.resource) {
title = this.resource
}
@@ -144,13 +207,15 @@ export default {
if (!title) {
title = this.$t('NoTitle')
}
const action = this.action
let actionLabel = ''
if (action === 'clone' || action === 'create') {
dispatchAction = this.$t('Create')
actionLabel = this.$t('Create')
} else if (action === 'update') {
dispatchAction = this.$t('Update')
actionLabel = this.$t('Update')
} else if (action === 'detail') {
actionLabel = this.$t('Detail')
}
title = dispatchAction + this.$t('WordSep') + toLowerCaseExcludeAbbr(title)
title = actionLabel + this.$t('WordSep') + toLowerCaseExcludeAbbr(title)
return title
},
getDefaultDrawer(action) {
@@ -177,37 +242,103 @@ export default {
return component
}
},
async showDrawer(action) {
this.action = action
if (action === 'create') {
this.drawerComponent = this.createDrawer
} else if (action === 'update') {
this.drawerComponent = this.updateDrawer || this.createDrawer
} else if (action === 'detail') {
this.drawerComponent = this.detailDrawer
} else if (action === 'clone') {
this.drawerComponent = this.createDrawer || this.getDefaultDrawer('create')
} else {
this.drawerComponent = this.createDrawer
resolveRoute(route) {
const routes = this.$router.resolve(route)
if (!routes) {
return
}
if (!this.drawerComponent) {
this.drawerComponent = this.getDefaultDrawer(action)
const matched = routes.resolved.matched.filter(item => item.name === route.name && item.components)
if (matched.length === 0) {
return
}
if (this.getDrawerTitle) {
const actionMeta = await this.$store.getters['common/drawerActionMeta']
this.title = this.getDrawerTitle({ action, ...actionMeta })
if (matched[0] && matched[0].components?.default) {
return matched[0]
}
this.drawerVisible = true
},
onCreate(meta) {
if (!meta) {
meta = {}
getDetailComponent({ detailRoute }) {
if (!detailRoute) {
return this.detailDrawer
}
this.$log.debug('>>> getDetailComponent: ', detailRoute)
const route = this.resolveRoute(detailRoute)
let component = null
if (route) {
component = route.components.default
}
if (!component) {
component = this.detailDrawer
}
return component
},
getDrawerComponent(action, payload) {
this.$log.debug('>>> getDrawerComponent: ', action, payload)
switch (action) {
case 'create':
return this.createDrawer
case 'update':
return this.updateDrawer || this.createDrawer
case 'detail':
return this.getDetailComponent(payload)
case 'clone':
return this.createDrawer || this.getDefaultDrawer('create')
default:
return this.createDrawer
}
},
async showDrawer(action, { row = {}, col = {}, query = {}, cellValue = '', payload = {}} = {}) {
try {
// 1. 先重置状态
this.drawerVisible = false
this.action = action
for (const key in query) {
this.$route.query[key] = query[key]
}
// 2. 等待下一个 tick确保状态已重置
await this.$nextTick()
// 3. 设置组件
this.drawerComponent = this.getDrawerComponent(action, payload)
this.$log.debug('>>> drawerComponent: ', this.drawerComponent)
this.drawerTitle = this.getActionDrawerTitle({ action, row, col, cellValue, payload })
// 4. 如果没有组件,尝试获取默认组件
if (!this.drawerComponent) {
this.drawerComponent = this.getDefaultDrawer(action)
}
// 5. 如果还是没有组件,报错
if (!this.drawerComponent) {
throw new Error(`No drawer component found for action: ${action}`)
}
// 6. 获取标题
if (this.getDrawerTitle) {
const actionMeta = await this.$store.getters['common/drawerActionMeta']
this.title = this.getDrawerTitle({ action, ...actionMeta })
}
// 7. 等待下一个 tick确保组件已设置
await this.$nextTick()
// 8. 显示抽屉
this.drawerVisible = true
this.$log.debug('Drawer initialized:', {
title: this.title,
visible: this.drawerVisible,
component: this.drawerComponent,
action: this.action,
'this': this,
'vm': this.vm
})
} catch (error) {
console.error('Failed to show drawer:', error)
this.drawerVisible = false
this.drawerComponent = ''
}
this.$store.dispatch('common/setDrawerActionMeta', {
action: 'create', ...meta
}).then(() => {
this.showDrawer('create')
})
},
reloadTable() {
if (this.reloadOrderQuery) {
@@ -215,21 +346,47 @@ export default {
}
this.$refs.ListTable.reloadTable()
},
onClone({ row, col }) {
this.$store.dispatch('common/setDrawerActionMeta', {
action: 'clone', row: row, col: col, id: row.id
}).then(() => {
this.showDrawer('clone')
async onDetail({ row, col, cellValue, detailRoute, formatterArgs }) {
this.$log.debug('>>> onDetail: ', detailRoute, formatterArgs)
this.$route.params.id = row.id
// 因为使用 detail formatter 时id 可能并非 row 的,比如 execution 的 task id
const query = detailRoute?.query || {}
const params = detailRoute?.params || {}
for (const key in query) {
this.$route.query[key] = query[key]
}
for (const key in params) {
this.$route.params[key] = params[key]
}
// 有可能来自 params 或者 row
const id = params.id || row.id
await this.$store.dispatch('common/setDrawerActionMeta', {
action: 'detail', row: row, col: col, id: id
})
await this.showDrawer('detail', { row, col, cellValue, payload: { detailRoute, formatterArgs }})
},
onUpdate({ row, col }) {
async onCreate(meta) {
if (!meta) {
meta = {}
}
this.$route.params.id = ''
await this.$store.dispatch('common/setDrawerActionMeta', { action: 'create', ...meta })
await this.showDrawer('create', meta)
},
async onClone({ row, col, query = {}}) {
this.$route.params.id = ''
await this.$store.dispatch('common/setDrawerActionMeta', {
action: 'clone', row: row, col: col, id: row.id
})
await this.showDrawer('clone', { query })
},
async onUpdate({ row, col, query = {}}) {
this.$route.params.id = row.id
this.$route.params.action = 'update'
this.$store.dispatch('common/setDrawerActionMeta', {
await this.$store.dispatch('common/setDrawerActionMeta', {
action: 'update', row: row, col: col, id: row.id
}).then(() => {
this.showDrawer('update')
})
await this.showDrawer('update', { query })
}
}
}

View File

@@ -117,7 +117,7 @@ export default {
}
},
mounted() {
// console.log('Object: ', this.object)
// this.$log.debug('>>> Object: ', this.object)
},
methods: {
isDisabled(action) {

View File

@@ -490,6 +490,9 @@ export default {
}
.btn-groups {
display: flex;
justify-content: flex-end;
::v-deep .el-button.is-disabled {
cursor: not-allowed;
}

View File

@@ -20,7 +20,7 @@
>
{{ option.label }}
<span v-if="option.hasCount">
(<span v-async="getCount(option)">-</span>)
<span>({{ option.count }})</span>
</span>
<!-- <i class="el-icon-circle-check" />-->
</span>
@@ -32,19 +32,13 @@
<span v-for="item of iSummary" :key="item.title" class="summary-block">
<SummaryCard
:class="item.active ? 'active' : ''"
:count="getCount(item)"
:count="item.count"
:title="item.title"
@click="handleFilterClick(item)"
/>
</span>
</div>
</div>
<div class="expand-bar-wrap">
<div class="expand-bar" @click="toggle">
<i :class="isExpand ? 'expand': 'shrink' " class="fa fa-angle-double-up" />
<span v-show="!isExpand"> 展开过滤器 </span>
</div>
</div>
</div>
</div>
</template>
@@ -76,10 +70,11 @@ export default {
},
data() {
return {
iFilters: this.cleanFilters(),
iSummary: this.cleanSummary(),
iFilters: [],
iSummary: [],
filtered: {},
activeFilters: []
activeFilters: [],
reCount: false
}
},
computed: {
@@ -92,7 +87,32 @@ export default {
}
}
},
watch: {
tableUrl() {
this.generateCount()
}
},
mounted() {
this.iFilters = this.cleanFilters()
this.iSummary = this.cleanSummary()
this.generateCount()
},
methods: {
async generateCount() {
this.iFilters.forEach(category => {
category.options.forEach(option => {
if (option.hasCount) {
option.count = null
this.getCount(option)
}
})
})
this.iSummary.forEach(item => {
item.count = null
this.getCount(item)
})
},
async getCount(item) {
if (item.count || item.count === 0) {
return item.count
@@ -117,6 +137,7 @@ export default {
category: 'summary',
label: item.title,
...item,
count: null,
filter: item.filter || {},
active: false
}
@@ -170,138 +191,138 @@ export default {
}
</script>
<style lang='scss' scoped>
.quick-filter {
background: white;
padding: 10px 10px 10px 20px;
margin-bottom: 10px;
display: flex;
place-content: stretch flex-end;
justify-content: center;
align-content: stretch;
box-shadow: 0 1px 1px 0 rgba(54, 58, 80, .32);
.quick-filter {
background: white;
padding: 10px 10px 10px 20px;
margin-bottom: 10px;
display: flex;
place-content: stretch flex-end;
justify-content: center;
align-content: stretch;
box-shadow: 0 1px 1px 0 rgba(54, 58, 80, .32);
&.shrink {
background: inherit;
padding: 0;
margin-bottom: 0;
box-shadow: none;
}
&.shrink {
background: inherit;
padding: 0;
margin-bottom: 0;
box-shadow: none;
}
.quick-filter-wrap {
display: inline-block;
width: calc(100% - 70px);
.quick-filter-wrap {
display: inline-block;
width: calc(100% - 70px);
.summary-zone {
padding-top: 10px;
display: flex;
justify-content: space-between;
}
.summary-zone {
padding-top: 10px;
display: flex;
justify-content: space-between;
}
.summary-block {
.active {
::v-deep .no-margins .num {
color: var(--color-primary);
.summary-block {
.active {
::v-deep .no-margins .num {
color: var(--color-primary);
&::after {
content: "\e720";
font-family: element-icons !important;
font-size: 13px;
line-height: 1;
}
}
}
}
&::after {
content: "\e720";
font-family: element-icons !important;
font-size: 13px;
line-height: 1;
}
}
}
}
.quick-filter-zone {
display: flex;
justify-content: flex-start;
flex-wrap: wrap; /* 允许 item-zone 换行 */
gap: 10px;
.quick-filter-zone {
display: flex;
justify-content: flex-start;
flex-wrap: wrap; /* 允许 item-zone 换行 */
gap: 10px;
h5 {
font-weight: 600;
text-transform: uppercase;
font-size: 12px;
margin-bottom: .5rem;
line-height: 1.2;
display: inline-block;
}
h5 {
font-weight: 600;
text-transform: uppercase;
font-size: 12px;
margin-bottom: .5rem;
line-height: 1.2;
display: inline-block;
}
.item-zone {
margin-right: 30px;
margin-bottom: 5px;
}
.item-zone {
margin-right: 30px;
margin-bottom: 5px;
}
.item {
display: inline-block;
margin-right: 8px;
color: #303133;
font-size: 12px;
cursor: pointer;
.item {
display: inline-block;
margin-right: 8px;
color: #303133;
font-size: 12px;
cursor: pointer;
&::after {
content: "";
margin-left: 4px;
margin-bottom: 2px;
vertical-align: middle;
width: 1px; /* 分割线宽度 */
height: 8px; /* 分割线高度 */
background-color: var(--color-icon-primary); /* 分割线颜色 */
display: inline-block;
}
&::after {
content: "";
margin-left: 4px;
margin-bottom: 2px;
vertical-align: middle;
width: 1px; /* 分割线宽度 */
height: 8px; /* 分割线高度 */
background-color: var(--color-icon-primary); /* 分割线颜色 */
display: inline-block;
}
&:last-child::after {
display: none;
}
&:last-child::after {
display: none;
}
i {
visibility: hidden;
margin-left: -3px;
}
i {
visibility: hidden;
margin-left: -3px;
}
&.active {
color: var(--color-primary);
&.active {
color: var(--color-primary);
i {
visibility: visible;
}
}
i {
visibility: visible;
}
}
&:hover {
color: var(--color-primary);
}
}
&:hover {
color: var(--color-primary);
}
}
ul {
list-style: none outside none;
margin-block-start: 0;
padding-left: 0;
}
}
}
}
ul {
list-style: none outside none;
margin-block-start: 0;
padding-left: 0;
}
}
}
}
.filter-options {
display: block;
}
.filter-options {
display: block;
}
.expand-bar-wrap {
margin: auto 0;
min-width: 60px;
.expand-bar-wrap {
margin: auto 0;
min-width: 60px;
.expand-bar {
float: right;
display: block;
cursor: pointer;
.expand-bar {
float: right;
display: block;
cursor: pointer;
i {
padding: 5px;
i {
padding: 5px;
&.shrink {
transform: rotate(180deg);
}
}
}
}
&.shrink {
transform: rotate(180deg);
}
}
}
}
</style>

View File

@@ -103,7 +103,8 @@ export default {
initQuery: {},
tablePath: new URL(this.tableConfig.url || '', 'http://127.0.0.1').pathname,
objStorage: new ObjectLocalStorage('filterExpand'),
iFilterExpand: null
iFilterExpand: null,
reloadTable: _.debounce(this._reloadTable, 300)
}
},
computed: {
@@ -281,7 +282,7 @@ export default {
handleSelectionChange(val) {
this.selectedRows = val
},
reloadTable() {
_reloadTable() {
this.dataTable?.getList()
},
updateInitQuery(attrs) {

View File

@@ -112,7 +112,7 @@ export default {
const res = await this.$axios.get(url)
if (res && res.protocols.length > 0) {
const protocol = res.protocols[0]
const protocol = res.protocols.filter(protocol => protocol.name !== 'sftp')[0]
this.formatterArgs.setMapItem(this.row.id, protocol.name)
this.handleWindowOpen(this.row, protocol.name)
@@ -127,7 +127,8 @@ export default {
const url = this.formatterArgs.url.replace('{id}', assetId)
const res = await this.$axios.get(url)
if (res) this.protocols = res.protocols
// 暂将 SFTP 过滤
if (res) this.protocols = res.protocols.filter(protocol => protocol.name !== 'sftp')
} catch (e) {
throw new Error(`Error getting protocols: ${e}`)
}

View File

@@ -37,6 +37,22 @@ const defaultUpdateCallback = function({ row, col }) {
this.$router.push(route)
}
const defaultViewCallback = function({ row, col }) {
const id = row.id
let route = { params: { id: id }}
const viewRoute = this.colActions.viewRoute
if (typeof updateRoute === 'object') {
route = Object.assign(route, viewRoute)
} else if (typeof updateRoute === 'function') {
route = viewRoute({ row, col })
} else {
route.name = viewRoute
}
this.$router.push(route)
}
const defaultCloneCallback = function({ row, col }) {
const id = row.id
let route = { query: { clone_from: id }}
@@ -105,6 +121,7 @@ export default {
cloneRoute: this.$route.name.replace('List', 'Create'),
performDelete: defaultPerformDelete,
onUpdate: defaultUpdateCallback,
onView: defaultViewCallback,
onDelete: defaultDeleteCallback,
onClone: defaultCloneCallback,
extraActions: []

View File

@@ -1,5 +1,5 @@
<template>
<DetailFormatter :col="col" :row="row" :prevant-click="formatterArgs.prevantClick">
<DetailFormatter :col="col" :row="row" :prevent-click="formatterArgs.preventClick">
<template>
<el-popover
:disabled="!showItems"
@@ -43,7 +43,7 @@ export default {
async: false,
ajax: {},
title: '',
prevantClick: false
preventClick: false
}
}
}

View File

@@ -13,7 +13,7 @@
</slot>
</el-link>
<Drawer
v-if="formatterArgs.drawer && drawerVisible"
v-if="formatterArgs.drawer && drawerComponent && drawerVisible"
:component="drawerComponent"
:has-footer="false"
:title="drawerTitle"
@@ -56,7 +56,7 @@ export default {
}
}
},
prevantClick: {
preventClick: {
type: Boolean,
default: false
}
@@ -85,7 +85,7 @@ export default {
disabled() {
let can = this.formatterArgs.can
if (typeof can === 'function') {
can = can(this.col)
can = can({ col: this.col, row: this.row })
}
return !can
},
@@ -104,74 +104,21 @@ export default {
}
}
},
watch: {
drawerVisible(val) {
this.$log.debug('>>> DetailFormatter drawerVisible: ', val)
if (!val) {
this.drawerComponent = ''
}
}
},
methods: {
getResource() {
const route = this.resolveRoute()
if (!route) {
return
}
const resource = route.meta.title || route.name
return resource.replace(' details', '').replace('详情', '')
},
getDrawerTitle() {
let title = this.cellValue || this.row.name
if (this.formatterArgs?.getDrawerTitle && typeof this.formatterArgs.getDrawerTitle === 'function') {
title = this.formatterArgs.getDrawerTitle({
col: this.col,
row: this.row,
cellValue: this.cellValue
})
}
const resource = this.getResource()
if (resource) {
title = `${resource}: ${title}`
}
return title
},
resolveRoute() {
const route = this.getDetailRoute()
const routes = this.$router.resolve(route)
if (!routes) {
return
}
const matched = routes.resolved.matched.filter(item => item.name === route.name && item.components)
if (matched.length === 0) {
return
}
if (matched[0] && matched[0].components?.default) {
return matched[0]
}
},
getRouteComponent() {
const route = this.resolveRoute()
if (route) {
return route.components.default
}
},
showDrawer() {
if (this.formatterArgs.drawerComponent) {
this.drawerComponent = this.formatterArgs.drawerComponent
} else {
this.drawerComponent = this.getRouteComponent()
}
const route = this.getDetailRoute()
if (route?.query?.tab) {
this.$cookie.set(route.name, route.query.tab, 1)
this.$route.query.tab = route.query.tab
}
const payload = {
action: 'detail',
row: this.row,
getTitle() {
return this.formatterArgs.getTitle({
col: this.col,
id: route.params.id || this.row.id
}
this.$store.dispatch('common/setDrawerActionMeta', payload).then(() => {
this.drawerTitle = this.getDrawerTitle(payload)
this.drawerVisible = true
row: this.row,
cellValue: this.cellValue,
index: this.index
})
},
handleClick() {
@@ -180,14 +127,10 @@ export default {
}
if (this.formatterArgs.onClick) {
return this.formatterArgs.onClick(this.callbackArgs)
return this.formatterArgs.onClick({ ...this.callbackArgs, detailRoute: this.getDetailRoute(), formatterArgs: this.formatterArgs })
}
if (this.formatterArgs.drawer) {
return this.showDrawer()
}
if (this.prevantClick) {
if (this.preventClick) {
return
}

View File

@@ -97,12 +97,15 @@ export default {
}
this.processing = true
this.$axios.post(`/api/v1/accounts/account-risks/handle/`, data).then(() => {
if (cmd === 'add_account') {
if (cmd === 'add_account' || cmd === 'change_password_add') {
this.row.present = true
}
if (cmd === 'ignore') {
this.row.status = { 'value': statusMap.ignored }
}
if (cmd === 'delete_remote') {
this.row.remote_present = false
}
this.row.status = { 'value': statusMap.confirmed }
}).finally(() => {
setTimeout(() => {

View File

@@ -171,6 +171,7 @@ export default {
}
},
onEditBlur() {
this.isEdit = false
this.$emit('input', this.realValue)
}
}

View File

@@ -244,10 +244,12 @@ export default {
delete routeFilter.search
}
const asFilterTags = _.cloneDeep(this.filterTags)
this.filterTags = {
...asFilterTags,
...routeFilter
}
setTimeout(() => {
this.filterTags = {
...asFilterTags,
...routeFilter
}
}, 100)
},
getValueLabel(key, value) {
for (const field of this.options) {

View File

@@ -7,7 +7,9 @@
:style="{width: treeWidth}"
class="left"
>
<span v-if="component === 'AutoDataZTree'" class="title">{{ $t('AssetTree') }}</span>
<span v-if="component === 'AutoDataZTree'" class="title">
{{ $t('AssetTree') }}
</span>
<component
:is="component"
:key="componentTreeKey"

View File

@@ -53,24 +53,24 @@ export default {
resizeObserver: null,
span: 12,
isShow: true,
iValue: this.value
iValue: this.sanitizeContent(this.value)
}
},
computed: {
sanitizedValue() {
// 转义特殊字符
let content = this.iValue.replace(/\\/g, '\\\\').replace(/\$/g, '\\$')
const content = this.iValue.replace(/\\/g, '\\\\').replace(/\$/g, '\\$')
// 使用 DOMPurify 进行 XSS 过滤
content = DOMPurify.sanitize(content)
return content
return this.sanitizeContent(content)
}
},
watch: {
value(newVal) {
this.iValue = this.sanitizeContent(newVal)
}
},
mounted() {
this.$nextTick(() => {
this.resizeObserver = new ResizeObserver(entries => {
// 监听高度变化
const height = entries[0].target.offsetHeight
if (height) {
this.height = height
@@ -90,8 +90,19 @@ export default {
this.resizeObserver = null
},
methods: {
sanitizeContent(content) {
if (!content) return ''
return DOMPurify.sanitize(content, {
ALLOWED_TAGS: ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ul', 'ol', 'li', 'strong', 'em', 'code', 'pre', 'blockquote', 'a', 'img'],
FORBID_TAGS: ['script', 'style', 'iframe', 'frame', 'object', 'embed'],
FORBID_ATTR: ['onerror', 'onload', 'onclick', 'onmouseover']
})
},
onChange() {
this.$emit('change', this.iValue)
const sanitizedValue = this.sanitizeContent(this.iValue)
this.iValue = sanitizedValue
this.$emit('change', sanitizedValue)
},
onView() {
this.isShow = !this.isShow

View File

@@ -69,6 +69,8 @@ export const is_periodic = {
}
}
export const taskStatusFormatterMeta = {}
export const periodicMeta = {
is_periodic,
interval,

View File

@@ -6,20 +6,23 @@ import store from '@/store'
import { isSameView } from '@/utils/jms'
import { toSentenceCase } from '@/utils/common'
// NProgress.configure({
// showSpinner: false
// }) // NProgress Configuration
//
function beforeRouteChange(to, from, next) {
localStorage.setItem('activeTab', '')
}
router.beforeEach(async(to, from, next) => {
// start progress bar
// NProgress.start()
try {
await store.dispatch('common/cleanDrawerActionMeta')
await startup({ to, from, next })
if (to.name && from.name && to.name !== from.name) {
await beforeRouteChange(to, from, next)
}
next()
} catch (e) {
const msg = 'Start service error: ' + e
// debug(e)
console.log(msg)
}
})

View File

@@ -134,14 +134,7 @@ export default {
// 获取提交的方法
submitMethod: {
type: [Function, String],
default: function() {
const params = this.$route.params
if (params.id) {
return 'put'
} else {
return 'post'
}
}
default: null
},
// 获取创建和更新的url function
getUrl: {
@@ -306,7 +299,7 @@ export default {
this.$log.debug('Object init is: ', this.object, this.method)
await this.setDrawerMeta()
this.setMethod()
// console.log('Set method: ', this.method, this.action)
// this.$log.debug('Set method: ', this.method, this.action)
try {
const values = await this.getFormValue()
@@ -333,7 +326,10 @@ export default {
} else {
this.method = this.submitMethod
}
// console.log('Drawer: ', this.drawer, this.submitMethod)
// this.$log.debug('Drawer: ', this.drawer, this.submitMethod, this.action)
if (!this.drawer && !this.method) {
this.method = this.$route.params['id'] ? 'put' : 'post'
}
if (this.drawer && !this.submitMethod) {
if (this.action === 'clone' || this.action === 'create') {
this.method = 'post'
@@ -424,6 +420,7 @@ export default {
if (needGetObjectDetail === null) {
needGetObjectDetail = this.isUpdateMethod() || this.action === 'clone'
}
// this.$log.debug('Get form value: ', needGetObjectDetail, this.needGetObjectDetail, this.isUpdateMethod(), this.action)
if (!needGetObjectDetail) {
return Object.assign(this.form, this.initial)
}

View File

@@ -1,6 +1,5 @@
<template>
<TabPage
v-if="!loading"
:active-menu.sync="iActiveMenu"
:submenu="iSubmenu"
:title="iTitle"
@@ -12,7 +11,9 @@
<ActionsGroup slot="headingRightSide" :actions="pageActions" class="header-buttons" />
</span>
</template>
<slot />
<div v-if="!loading">
<slot />
</div>
</TabPage>
</template>

View File

@@ -1,5 +1,5 @@
<template>
<Page v-if="!loading" :title="title" class="tab-page" v-bind="$attrs">
<Page :title="title" class="tab-page" v-bind="$attrs">
<template #headingRightSide>
<slot name="headingRightSide" />
</template>
@@ -45,7 +45,7 @@
<el-alert v-if="helpMessage" type="success">
<span v-sanitize="helpMessage" class="announcement-main" />
</el-alert>
<transition appear mode="out-in" name="fade-transform">
<transition v-if="!loading" appear mode="out-in" name="fade-transform">
<slot>
<keep-alive>
<component :is="computeActiveComponent" />
@@ -124,6 +124,13 @@ export default {
return needActiveComponent
}
},
watch: {
activeMenu: {
handler(newValue) {
this.iActiveMenu = newValue
}
}
},
created() {
this.iActiveMenu = this.getPropActiveTab()
this.loading = false
@@ -132,14 +139,14 @@ export default {
handleTabClick(tab) {
this.$emit('tab-click', tab)
this.iActiveMenu = tab.name
this.$cookie.set('activeTab', tab.name, 1)
localStorage.setItem('activeTab', tab.name)
},
getPropActiveTab() {
let activeTab = ''
const preActiveTabs = [
this.$route.query['tab'],
this.$cookie.get('activeTab'),
localStorage.getItem('activeTab'),
this.activeMenu
]

View File

@@ -110,14 +110,14 @@ export default [
action: 'update'
},
hidden: true
},
{
path: ':id',
component: () => import('@/views/accounts/AccountTemplate/Detail/index.vue'),
name: 'AccountTemplateDetail',
meta: { title: i18n.t('AccountTemplate') },
hidden: true
}
// {
// path: ':id',
// component: () => import('@/views/accounts/AccountTemplate/AccountTemplateDetail/index.vue'),
// name: 'AccountTemplateDetail',
// meta: { title: i18n.t('AccountTemplate') },
// hidden: true
// }
]
}
]

View File

@@ -25,7 +25,8 @@ export default [
title: i18n.t('UserLoginACLs'),
app: 'acls',
resource: 'loginacl',
disableOrgsChange: true
disableOrgsChange: true,
licenseRequired: true
},
children: [
{

View File

@@ -28,7 +28,7 @@ export default [
},
{
path: 'create',
component: () => import('@/views/accounts/AccountDiscover/TaskCreateUpdate'),
component: () => import('@/views/accounts/AccountDiscover/AccountDiscoverTaskCreateUpdate.vue'),
name: 'AccountDiscoverTaskCreate',
hidden: true,
meta: {
@@ -50,7 +50,7 @@ export default [
},
{
path: ':id/update',
component: () => import('@/views/accounts/AccountDiscover/TaskCreateUpdate'),
component: () => import('@/views/accounts/AccountDiscover/AccountDiscoverTaskCreateUpdate.vue'),
name: 'AccountDiscoverTaskUpdate',
hidden: true,
meta: {
@@ -62,7 +62,7 @@ export default [
},
{
path: 'executions',
component: () => import('@/views/accounts/AccountDiscover/TaskExecutionList.vue'),
component: () => import('@/views/accounts/AccountDiscover/AccountDiscoverExecutionList.vue'),
name: 'AccountDiscoverTaskExecutionList',
hidden: true,
meta: {

View File

@@ -44,8 +44,9 @@ const mutations = {
const actions = {
// get user info
getUrlMeta({ commit, state }, { url }) {
const ignoreCache = url.includes('_meta_cache')
const meta = state.metaMap[url]
if (meta) {
if (meta && !ignoreCache) {
return new Promise((resolve, reject) => {
resolve(meta)
})

View File

@@ -702,3 +702,7 @@ li.rmenu i.fa {
div.el-loading-parent--relative {
min-height: 150px;
}
.el-drawer__header .drawer-title {
color: var(--color-text-primary);
}

View File

@@ -78,6 +78,18 @@ export function replaceAllUUID(string, replacement = '*') {
return string
}
// 写个函数, id 设置到路径中,而不是 query 中, 确保已 / 结尾, 如果已 / 结尾,则不添加
export function setUrlId(url, id) {
const urlArray = url.split('?')
const baseUrl = _.trimEnd(urlArray[0], '/')
if (urlArray.length === 1) {
url = `${baseUrl}/${id}/`
} else {
url = `${baseUrl}/${id}/?${urlArray[1]}`
}
return url
}
export function setUrlParam(url, name, value) {
const urlArray = url.split('?')
if (urlArray.length === 1) {
@@ -374,6 +386,16 @@ export function openNewWindow(url) {
window.open(url, '_blank', params)
}
export function getDrawerWidth() {
const drawerWidth = localStorage.getItem('drawerWidth')
if (drawerWidth && drawerWidth > 100 && drawerWidth < 2000) {
return drawerWidth + 'px'
}
const width = window.innerWidth
if (width >= 1500) return '1080px'
return '90%'
}
export class ObjectLocalStorage {
constructor(key) {
this.key = key

View File

@@ -1,140 +1,26 @@
<template>
<div>
<GenericListTable :header-actions="headerActions" :table-config="tableConfig" />
<ReportDialog :visible.sync="visible" :url="reportUrl" />
</div>
<BaseExecutionList
:automation-route="automationRoute"
:detail-route="detailRoute"
:resource="resource"
:url="url"
/>
</template>
<script>
import GenericListTable from '@/layout/components/GenericListTable/index.vue'
import { openTaskPage } from '@/utils/jms'
import { DetailFormatter } from '@/components/Table/TableFormatters'
import ReportDialog from '@/components/Dialog/ReportDialog.vue'
import BaseExecutionList from '@/views/accounts/Automation/BaseExecutionList.vue'
export default {
name: 'AccountBackupExecutionList',
components: {
GenericListTable,
ReportDialog
},
props: {
object: {
type: Object,
required: false,
default: () => ({})
}
BaseExecutionList
},
data() {
const vm = this
return {
visible: false,
reportUrl: '',
tableConfig: {
url: '/api/v1/accounts/account-backup-plan-executions/',
columns: [
'automation', 'backup_name', 'trigger',
'date_start', 'date_finished', 'duration', 'actions'
],
columnsShow: {
default: [
'automation', 'backup_name', 'trigger',
'date_start', 'date_finished', 'actions'
]
},
columnsMeta: {
automation: {
label: this.$t('ID'),
formatter: DetailFormatter,
formatterArgs: {
route: 'AccountBackupExecutionDetail',
getRoute: ({ row }) => ({
name: 'AccountBackupExecutionDetail',
params: { id: row.id }
}),
getDrawerTitle: ({ row }) => row.id,
getTitle: ({ row }) => row.id.slice(0, 8),
drawer: true,
can: this.$hasPerm('accounts.view_backupaccountexecution')
}
},
backup_name: {
label: this.$t('DisplayName'),
formatter: DetailFormatter,
formatterArgs: {
drawer: true,
getTitle: ({ row }) => row.automation.name,
getDrawerTitle: ({ row }) => row.automation.name,
getRoute: ({ row }) => ({
name: 'AccountBackupDetail',
params: { id: row.automation.id }
})
},
id: ({ row }) => row.automation
},
duration: {
label: this.$t('TimeDelta'),
formatter: function(row) {
return row.duration + 's'
}
},
actions: {
formatterArgs: {
hasDelete: false,
hasUpdate: false,
hasClone: false,
extraActions: [
{
name: 'log',
type: 'primary',
can: this.$hasPerm('accounts.view_backupaccountexecution'),
title: this.$t('Log'),
callback: function({ row }) {
openTaskPage(row['id'])
}
},
{
name: 'report',
title: this.$t('Report'),
type: 'success',
can: this.$hasPerm('accounts.view_backupaccountexecution'),
callback: function({ row }) {
vm.visible = true
vm.reportUrl = `/api/v1/accounts/account-backup-plan-executions/${row.id}/report/`
}
}
]
}
}
}
},
headerActions: {
searchConfig: {
getUrlQuery: true,
options: [
{
label: this.$t('TaskID'),
value: 'automation_id'
},
{
label: this.$t('DisplayName'),
value: 'automation__name'
}
]
},
hasRefresh: true,
hasRightActions: true,
hasLeftActions: false,
hasMoreActions: false,
hasExport: false,
hasImport: false,
hasCreate: false
}
}
},
mounted() {
const plan_id = this.$route.query.plan_id
if (plan_id !== undefined) {
this.tableConfig.url = `${this.tableConfig.url}?plan_id=${plan_id}`
url: '/api/v1/accounts/account-backup-plan-executions/',
detailRoute: 'AccountBackupExecutionDetail',
automationRoute: 'AccountBackupDetail',
resource: 'backupaccountexecution'
}
}
}

View File

@@ -67,7 +67,7 @@ export default {
can: vm.$hasPerm('accounts.view_changesecretexecution'),
getRoute({ row }) {
return {
name: 'AccountChangeSecretList',
name: 'AccountChangeSecretDetail',
query: {
tab: 'AccountChangeSecretExecutionList',
automation_id: row.id

View File

@@ -1,7 +1,7 @@
<template>
<TwoCol>
<template>
<GenericListTable
<DrawerListTable
ref="listTable"
:header-actions="headerActions"
:table-config="tableConfig"
@@ -15,7 +15,7 @@
</template>
<script>
import GenericListTable from '@/layout/components/GenericListTable/index.vue'
import DrawerListTable from '@/components/Table/DrawerListTable/index.vue'
import RelationCard from '@/components/Cards/RelationCard/index.vue'
import AssetRelationCard from '@/components/Apps/AssetRelationCard/index.vue'
import { DeleteActionFormatter, DetailFormatter } from '@/components/Table/TableFormatters'
@@ -24,8 +24,7 @@ import TwoCol from '@/layout/components/Page/TwoColPage.vue'
export default {
name: 'AccountChangeSecretAsset',
components: {
TwoCol,
GenericListTable, RelationCard, AssetRelationCard
TwoCol, DrawerListTable, RelationCard, AssetRelationCard
},
props: {
object: {

View File

@@ -10,7 +10,7 @@
</template>
<script>
import GenericListTable from '@/layout/components/GenericListTable/index.vue'
import { GenericListTable } from '@/layout/components'
import { ActionsFormatter, DetailFormatter } from '@/components/Table/TableFormatters'
import { openTaskPage } from '@/utils/jms'
import RecordViewSecret from '@/components/Apps/ChangeSecret/RecordViewSecret.vue'
@@ -40,7 +40,7 @@ export default {
can: this.$hasPerm('assets.view_asset'),
getTitle: ({ row }) => row.asset.name,
getDrawerTitle: ({ row }) => row.asset.name,
getRoute({ row }) {
getRoute: ({ row }) => {
return {
name: 'AssetDetail',
params: { id: row.asset.id }
@@ -56,7 +56,7 @@ export default {
can: this.$hasPerm('accounts.view_account'),
getTitle: ({ row }) => row.account.username,
getDrawerTitle: ({ row }) => row.account.username,
getRoute({ row }) {
getRoute: ({ row }) => {
return {
name: 'AssetAccountDetail',
params: { id: row.account.id }

View File

@@ -1,157 +1,43 @@
<template>
<div>
<GenericListTable :header-actions="headerActions" :table-config="tableConfig" />
<ReportDialog :visible.sync="visible" :url="reportUrl" />
</div>
<BaseExecutionList
:automation-route="automationRoute"
:custom-actions="customActions"
:detail-route="detailRoute"
:resource="resource"
:url="url"
/>
</template>
<script>
import GenericListTable from '@/layout/components/GenericListTable/index.vue'
import { openTaskPage } from '@/utils/jms'
import { DetailFormatter } from '@/components/Table/TableFormatters'
import ReportDialog from '@/components/Dialog/ReportDialog.vue'
import BaseExecutionList from '@/views/accounts/Automation/BaseExecutionList.vue'
export default {
name: 'AccountChangeSecretExecutionList',
components: {
ReportDialog,
GenericListTable
BaseExecutionList
},
data() {
const vm = this
const params = new URLSearchParams(this.$route.params).toString()
return {
visible: false,
reportUrl: '',
tableConfig: {
url: '/api/v1/accounts/change-secret-executions/',
columns: [
'automation', 'change_secret_name', 'asset_amount', 'node_amount', 'status',
'trigger', 'date_start', 'date_finished', 'actions'
],
columnsShow: {
default: [
'automation', 'change_secret_name', 'trigger', 'status',
'date_start', 'date_finished', 'actions'
]
},
columnsMeta: {
automation: {
label: this.$t('ID'),
formatter: DetailFormatter,
formatterArgs: {
route: 'AccountChangeSecretExecutionDetail',
getRoute: ({ row }) => ({
name: 'AccountChangeSecretExecutionDetail',
params: { id: row.id }
}),
getDrawerTitle: ({ row }) => row.id,
getTitle: ({ row }) => row.id.slice(0, 8),
drawer: true,
can: this.$hasPerm('accounts.view_changesecretexecution')
url: `/api/v1/accounts/change-secret-executions/?${params}`,
detailRoute: 'AccountChangeSecretExecutionDetail',
automationRoute: 'AccountChangeSecretDetail',
resource: 'changesecretexecution',
customActions: {
name: 'record',
title: this.$t('Record'),
can: this.$hasPerm('accounts.view_changesecretrecord'),
callback: function({ row }) {
return this.$router.push({
name: 'AccountChangeSecretList',
query: {
tab: 'ChangeSecretRecord',
execution_id: row.id
}
},
change_secret_name: {
label: this.$t('DisplayName'),
formatter: DetailFormatter,
formatterArgs: {
drawer: true,
getDrawerTitle({ row }) {
return row.automation.name
},
getTitle: ({ row }) => row.automation.name,
getRoute: ({ row }) => ({
name: 'AccountChangeSecretDetail',
params: { id: row.automation.id }
})
},
id: ({ row }) => row.automation
},
asset_amount: {
label: this.$t('AssetsOfNumber'),
formatter: function(row) {
return <span>{row.snapshot.asset_amount}</span>
}
},
node_amount: {
label: this.$t('NodeOfNumber'),
formatter: function(row) {
return <span>{row.snapshot.node_amount}</span>
}
},
status: {
label: this.$t('Result')
},
actions: {
formatterArgs: {
hasDelete: false,
hasUpdate: false,
hasClone: false,
extraActions: [
{
name: 'log',
type: 'primary',
can: this.$hasPerm('accounts.view_changesecretexecution'),
title: this.$t('Log'),
callback: function({ row }) {
openTaskPage(row['id'])
}
},
{
name: 'report',
title: this.$t('Report'),
type: 'success',
can: this.$hasPerm('accounts.view_changesecretexecution'),
callback: function({ row }) {
vm.visible = true
vm.reportUrl = `/api/v1/accounts/change-secret-executions/${row.id}/report/`
}
},
{
name: 'record',
title: this.$t('Record'),
can: this.$hasPerm('accounts.view_changesecretrecord'),
callback: function({ row }) {
return this.$router.push({
name: 'AccountChangeSecretList',
query: {
tab: 'ChangeSecretRecord',
execution_id: row.id
}
})
}
}
]
}
}
})
}
},
headerActions: {
searchConfig: {
getUrlQuery: true,
options: [
{
label: this.$t('TaskID'),
value: 'automation_id'
},
{
label: this.$t('DisplayName'),
value: 'automation__name'
}
]
},
hasLeftActions: false,
hasMoreActions: false,
hasExport: false,
hasImport: false,
hasCreate: false
}
}
},
mounted() {
const automation_id = this.$route.query.automation_id
if (automation_id !== undefined) {
this.tableConfig.url = `${this.tableConfig.url}?automation_id=${automation_id}`
}
}
}
</script>

View File

@@ -49,9 +49,18 @@ export default {
{
title: this.$t('Total'),
body: {
route: { name: 'AccountChangeSecret', query: { tab: 'AccountChangeSecretList' }},
canDirect: true,
count: this.data.total_count_change_secrets
count: this.data.total_count_change_secrets,
callback: () => {
this.$eventBus.$emit('change-tab',
{
name: 'AccountChangeSecretList',
payload: {
days: this.days
}
}
)
}
}
},
{
@@ -73,9 +82,16 @@ export default {
{
title: this.$t('Total'),
body: {
route: { name: 'AccountChangeSecret', query: { tab: 'AccountChangeSecretExecutionList' }},
canDirect: true,
count: this.data.total_count_change_secret_executions
count: this.data.total_count_change_secret_executions,
callback: () => {
this.$eventBus.$emit('change-tab', {
name: 'AccountChangeSecretExecutionList',
payload: {
days: this.days
}
})
}
}
},
{

View File

@@ -29,12 +29,13 @@ export default {
data() {
const vm = this
return {
url: '/api/v1/accounts/change-secret-records/dashboard/',
showViewSecretDialog: false,
cardConfig: {
title: this.$tc('ChangeSecretFailAccounts')
title: vm.$tc('ChangeSecretFailAccounts')
},
tableConfig: {
url: `/api/v1/accounts/change-secret-records/dashboard/?days=${this.days}`,
url: vm.tableUrl(),
columns: [
'asset', 'account', 'date_finished', 'is_success', 'error', 'actions'
],
@@ -138,6 +139,17 @@ export default {
}
}
}
},
watch: {
days() {
this.$refs.HomeCard.$refs.ListTable.tableConfig.url = this.tableUrl()
this.$refs.HomeCard.$refs.ListTable.reloadTable()
}
},
methods: {
tableUrl() {
return `/api/v1/accounts/change-secret-records/dashboard/?days=${this.days}`
}
}
}
</script>

View File

@@ -107,6 +107,10 @@ export default {
::v-deep #HomeCard .el-card.no-border {
height: 100%;
margin-bottom: unset !important;
.ListTable .el-pagination {
display: block;
}
}
}

View File

@@ -45,6 +45,18 @@ export default {
},
computed: {
...mapGetters(['hasValidLicense'])
},
mounted() {
this.$eventBus.$on('change-tab', this.handleChangeTab)
},
beforeDestroy() {
this.$eventBus.$off('change-tab', this.handleChangeTab)
},
methods: {
handleChangeTab({ name, payload }) {
this.config.activeMenu = name
Object.assign(this.$route.params, { ...payload })
}
}
}
</script>

View File

@@ -0,0 +1,34 @@
<template>
<BaseExecutionList
:automation-route="automationRoute"
:detail-route="detailRoute"
:resource="resource"
:url="url"
/>
</template>
<script>
import BaseExecutionList from '@/views/accounts/Automation/BaseExecutionList.vue'
export default {
name: 'AccountDiscoverTaskExecutionList',
components: {
BaseExecutionList
},
props: {
object: {
type: Object,
required: false,
default: () => ({})
}
},
data() {
return {
url: '/api/v1/accounts/gather-account-executions/',
detailRoute: 'AccountDiscoverExecutionDetail',
automationRoute: 'AccountDiscoverTaskDetail',
resource: 'gatheraccountsexecution'
}
}
}
</script>

View File

@@ -21,7 +21,7 @@ export default {
data() {
const vm = this
return {
createDrawer: () => import('@/views/accounts/AccountDiscover/TaskCreateUpdate.vue'),
createDrawer: () => import('@/views/accounts/AccountDiscover/AccountDiscoverTaskCreateUpdate.vue'),
detailDrawer: () => import('@/views/accounts/AccountDiscover/TaskDetail/index.vue'),
showViewSecretDialog: false,
showTableUpdateDrawer: false,

View File

@@ -9,7 +9,7 @@
<script>
import { GenericDetailPage, TabPage } from '@/layout/components'
import Detail from './Detail.vue'
import TaskExecutionList from '../TaskExecutionList.vue'
import TaskExecutionList from '../AccountDiscoverExecutionList.vue'
export default {
name: 'AccountDiscoverTaskDetail',

View File

@@ -35,7 +35,7 @@ export default {
title: this.$t('ExecutionHistory'),
name: 'AccountDiscoverTaskExecutionList',
hidden: !this.$hasPerm('accounts.view_gatheraccountsexecution'),
component: () => import('@/views/accounts/AccountDiscover/TaskExecutionList.vue')
component: () => import('@/views/accounts/AccountDiscover/AccountDiscoverExecutionList.vue')
}
]
}

View File

@@ -1,164 +1,46 @@
<template>
<div>
<GenericListTable
ref="listTable"
:detail-drawer="detailDrawer"
:header-actions="headerActions"
:table-config="tableConfig"
/>
<ReportDialog :url="reportUrl" :visible.sync="visible" />
</div>
<BaseExecutionList
:automation-route="automationRoute"
:resource-label="resourceLabel"
:custom-actions="customActions"
:detail-drawer="detailDrawer"
:detail-route="detailRoute"
:resource="resource"
:url="url"
/>
</template>
<script>
import GenericListTable from '@/layout/components/GenericListTable/index.vue'
import { openTaskPage } from '@/utils/jms'
import { DetailFormatter } from '@/components/Table/TableFormatters'
import ReportDialog from '@/components/Dialog/ReportDialog'
import BaseExecutionList from '@/views/accounts/Automation/BaseExecutionList.vue'
export default {
name: 'AccountPushExecutionList',
components: {
GenericListTable,
ReportDialog
BaseExecutionList
},
data() {
const vm = this
return {
visible: false,
reportUrl: '',
url: '/api/v1/accounts/push-account-executions/',
automationRoute: 'AccountPushDetail',
detailRoute: 'AccountPushExecutionDetail',
resource: 'pushaccountexecution',
resourceLabel: this.$t('AccountPushExecution'),
detailDrawer: () => import('@/views/accounts/AccountPush/ExecutionDetail/index.vue'),
tableConfig: {
url: '/api/v1/accounts/push-account-executions/',
columns: [
'automation', 'push_user_name', 'asset_amount', 'node_amount', 'status',
'trigger', 'date_start', 'date_finished', 'actions'
],
columnsShow: {
default: [
'automation', 'push_user_name', 'trigger', 'status',
'date_start', 'date_finished', 'actions'
]
},
columnsMeta: {
automation: {
label: this.$t('ID'),
formatter: DetailFormatter,
formatterArgs: {
route: 'AccountPushExecutionDetail',
getRoute: ({ row }) => ({
name: 'AccountPushExecutionDetail',
params: { id: row.id }
}),
getDrawerTitle: ({ row }) => row.id,
getTitle: ({ row }) => row.id.slice(0, 8),
drawer: true,
can: this.$hasPerm('accounts.view_pushaccountexecution')
customActions: {
name: 'record',
title: this.$t('Record'),
can: this.$hasPerm('accounts.view_changesecretrecord'),
callback: function({ row }) {
return this.$router.push({
name: 'AccountPush',
query: {
tab: 'AccountPushRecord',
execution_id: row.id
}
},
push_user_name: {
label: this.$t('DisplayName'),
formatter: DetailFormatter,
formatterArgs: {
drawer: true,
getTitle: ({ row }) => row.automation?.name,
getRoute: ({ row }) => ({
name: 'AccountPushDetail',
params: { id: row.automation.id }
}),
getDrawerTitle({ row }) {
return row.automation?.name
}
},
id: ({ row }) => row.automation
},
asset_amount: {
label: this.$t('AssetsOfNumber'),
formatter: function(row) {
return <span>{row.snapshot.asset_amount}</span>
}
},
node_amount: {
label: this.$t('NodeOfNumber'),
formatter: function(row) {
return <span>{row.snapshot.node_amount}</span>
}
},
status: {
label: this.$t('Result')
},
actions: {
formatterArgs: {
hasDelete: false,
hasUpdate: false,
hasClone: false,
extraActions: [
{
name: 'log',
type: 'primary',
can: this.$hasPerm('accounts.view_pushaccountexecution'),
title: this.$t('Log'),
callback: function({ row }) {
openTaskPage(row['id'])
}
},
{
name: 'report',
title: this.$t('Report'),
can: this.$hasPerm('accounts.view_pushaccountexecution'),
callback: function({ row }) {
vm.visible = true
vm.reportUrl = `/api/v1/accounts/push-account-executions/${row.id}/report/`
}
},
{
name: 'record',
title: this.$t('Record'),
can: this.$hasPerm('accounts.view_pushsecretrecord'),
callback: function({ row }) {
return this.$router.push({
name: 'AccountPushList',
query: {
tab: 'AccountPushRecord',
execution_id: row.id
}
})
}
}
]
}
}
})
}
},
headerActions: {
searchConfig: {
getUrlQuery: true,
options: [
{
label: this.$t('TaskID'),
value: 'automation_id'
},
{
label: this.$t('DisplayName'),
value: 'automation__name'
}
]
},
hasSearch: true,
hasRefresh: true,
hasRightActions: true,
hasLeftActions: false,
hasMoreActions: false,
hasExport: false,
hasImport: false
}
}
},
mounted() {
const automation_id = this.$route.query.automation_id
if (automation_id !== undefined) {
this.tableConfig.url = `${this.tableConfig.url}?automation_id=${automation_id}`
}
}
}
</script>

View File

@@ -4,7 +4,6 @@
:create-drawer="createDrawer"
:detail-drawer="detailDrawer"
:header-actions="headerActions"
:resource="$tc('AccountTemplate')"
:table-config="tableConfig"
/>
<ViewSecret
@@ -23,6 +22,7 @@ import { ActionsFormatter } from '@/components/Table/TableFormatters'
import ViewSecret from '@/components/Apps/AccountListTable/ViewSecret'
// import { DrawerListTable as ListTable } from '@/components'
import ListTable from '@/components/Table/DrawerListTable'
import { DetailFormatter } from '@/components/Table/TableFormatters'
export default {
name: 'AccountTemplateTable',
@@ -47,6 +47,20 @@ export default {
default: ['name', 'username', 'secret_type', 'has_secret', 'privileged', 'actions']
},
columnsMeta: {
name: {
formatter: DetailFormatter,
formatterArgs: {
getTitle: ({ row }) => {
return row.name
},
getRoute: ({ row }) => {
return {
name: 'AccountTemplateDetail',
params: { id: row.id }
}
}
}
},
privileged: {
width: '120px',
formatterArgs: {

View File

@@ -1,12 +1,15 @@
<template>
<div>
<GenericListTable :header-actions="headerActions" :table-config="tableConfig" />
<ReportDialog :visible.sync="visible" :url="reportUrl" />
<DrawerListTable
:header-actions="headerActions"
:table-config="tableConfig"
/>
<ReportDialog :url="reportUrl" :visible.sync="visible" />
</div>
</template>
<script>
import GenericListTable from '@/layout/components/GenericListTable/index.vue'
import DrawerListTable from '@/components/Table/DrawerListTable/index.vue'
import { openTaskPage } from '@/utils/jms'
import { DetailFormatter } from '@/components/Table/TableFormatters'
@@ -18,13 +21,28 @@ export default {
name: 'AccountDiscoverTaskExecutionList',
components: {
ReportDialog,
GenericListTable
DrawerListTable
},
props: {
object: {
url: {
type: String,
required: true
},
detailRoute: {
type: String,
required: true
},
automationRoute: {
type: String,
required: true
},
resource: {
type: String,
required: true
},
customActions: {
type: Object,
required: false,
default: () => ({})
default: null
}
},
data() {
@@ -36,7 +54,7 @@ export default {
currentTemplate: null,
drawerTitle: '',
tableConfig: {
url: '/api/v1/accounts/gather-account-executions/',
url: this.url,
columns: [
'id', 'automation', 'status', 'trigger',
'date_start', 'date_finished', 'actions'
@@ -45,10 +63,11 @@ export default {
id: {
label: this.$t('ID'),
formatter: DetailFormatter,
width: '100px',
formatterArgs: {
route: 'AccountDiscoverExecutionDetail',
route: this.detailRoute,
getRoute: ({ row }) => ({
name: 'AccountDiscoverExecutionDetail',
name: this.detailRoute,
params: { id: row.id }
}),
getTitle: ({ row }) => row.id.slice(0, 8),
@@ -64,19 +83,21 @@ export default {
width: '135px'
},
automation: {
label: this.$t('Name'),
label: this.$t('Task'),
formatter: DetailFormatter,
minWidth: '180px',
formatterArgs: {
getTitle: ({ row }) => row.automation.name,
getTitle: ({ row }) => row.snapshot.name,
getDrawerTitle: ({ row }) => row.snapshot.name,
getRoute: ({ row }) => ({
name: 'AccountDiscoverTaskDetail',
name: this.automationRoute,
params: { id: row.automation.id }
}),
getDrawerTitle: ({ row }) => row.automation.name,
can: ({ row }) => row.automation?.id,
drawer: true
},
id: ({ row }) => row.automation,
can: this.$hasPerm('accounts.view_gatheraccountsexecution')
can: this.$hasPerm('accounts.view_' + this.resource)
},
date_start: {
width: null
@@ -99,10 +120,11 @@ export default {
name: 'report',
title: this.$t('Report'),
type: 'success',
can: this.$hasPerm('accounts.view_gatheraccountsexecution'),
can: this.$hasPerm('accounts.view_' + this.resource),
callback: function({ row }) {
vm.visible = true
vm.reportUrl = `/api/v1/accounts/gather-account-executions/${row.id}/report/`
const url = this.url.split('?')[0]
vm.reportUrl = `${url}${row.id}/report/`
}
}
]
@@ -137,14 +159,19 @@ export default {
if (automation_id !== undefined) {
this.tableConfig.url = `${this.tableConfig.url}?automation_id=${automation_id}`
}
const defaultExtraActions = this.tableConfig.columnsMeta.actions.formatterArgs.extraActions
if (this.customActions) {
this.tableConfig.columnsMeta.actions.formatterArgs.extraActions = [
...defaultExtraActions,
this.customActions
]
}
},
methods: {
handleDetailCallback(row) {
this.$route.params.id = row.id
this.$route.query.type = 'pam'
this.currentTemplate = 'AccountDiscoverExecutionDetail'
this.showTableUpdateDrawer = true
}
}

View File

@@ -22,14 +22,12 @@ export default {
formatter: DetailFormatter,
formatterArgs: {
drawer: true,
can: this.$hasPerm('assets.view_asset'),
getTitle: ({ row }) => row.asset.name,
getDrawerTitle: ({ row }) => row.asset.name,
getRoute: ({ row }) => ({
name: 'AssetDetail',
params: { id: row.asset.id },
query: { tab: 'Basic' }
})
params: { id: row.asset.id }
}),
can: this.$hasPerm('assets.view_asset'),
getTitle: ({ row }) => row.asset.name
}
}
}

View File

@@ -1,21 +1,19 @@
<template>
<div>
<GenericListTable ref="listTable" :header-actions="headerActions" :table-config="tableConfig" />
<ReportDialog :url="reportUrl" :visible.sync="visible" />
</div>
<BaseExecutionList
:automation-route="automationRoute"
:detail-route="detailRoute"
:resource="resource"
:url="url"
/>
</template>
<script>
import GenericListTable from '@/layout/components/GenericListTable/index.vue'
import { openTaskPage } from '@/utils/jms'
import { DetailFormatter } from '@/components/Table/TableFormatters'
import ReportDialog from '@/components/Dialog/ReportDialog.vue'
import BaseExecutionList from '@/views/accounts/Automation/BaseExecutionList.vue'
export default {
name: 'CheckAccountExecutionList',
components: {
ReportDialog,
GenericListTable
BaseExecutionList
},
props: {
object: {
@@ -25,124 +23,11 @@ export default {
}
},
data() {
const vm = this
return {
visible: false,
reportUrl: '',
tableConfig: {
url: '/api/v1/accounts/check-account-executions/',
columns: [
'automation', 'task_name', 'asset_amount',
'node_amount', 'status', 'trigger',
'date_start', 'date_finished', 'actions'
],
columnsShow: {
default: [
'automation', 'task_name', 'status',
'date_start', 'date_finished', 'actions'
]
},
columnsMeta: {
automation: {
label: this.$t('ID'),
formatter: DetailFormatter,
formatterArgs: {
getRoute: ({ row }) => ({
name: 'RiskHistoryExecutionDetail',
params: { id: row.id }
}),
getTitle: ({ row }) => row.id.slice(0, 8),
getDrawerTitle: ({ row }) => row.id,
drawer: true,
can: this.$hasPerm('accounts.view_checkaccountexecution')
}
},
task_name: {
label: this.$t('DisplayName'),
formatter: DetailFormatter,
formatterArgs: {
drawer: true,
getTitle: ({ row }) => row.automation.name,
getRoute: ({ row }) => ({
name: 'AccountCheckDetail',
params: { id: row.automation.id }
}),
getDrawerTitle: ({ row }) => row.automation.name
},
id: ({ row }) => row.automation
},
asset_amount: {
label: this.$t('AssetsOfNumber'),
formatter: function(row) {
return <span>{row.snapshot.asset_amount}</span>
}
},
node_amount: {
label: this.$t('NodeOfNumber'),
formatter: function(row) {
return <span>{row.snapshot.node_amount}</span>
}
},
status: {
label: this.$t('Result')
},
actions: {
formatterArgs: {
hasDelete: false,
hasUpdate: false,
hasClone: false,
extraActions: [
{
name: 'log',
type: 'primary',
can: this.$hasPerm('accounts.view_checkaccountexecution'),
title: this.$t('Log'),
callback: function({ row }) {
openTaskPage(row['id'])
}
},
{
name: 'report',
title: this.$t('Report'),
type: 'success',
can: this.$hasPerm('accounts.view_checkaccountexecution'),
callback: function({ row }) {
vm.visible = true
vm.reportUrl = `/api/v1/accounts/check-account-executions/${row.id}/report/`
}
}
]
}
}
}
},
headerActions: {
searchConfig: {
options: [
{
label: this.$t('TaskID'),
value: 'automation_id'
},
{
label: this.$t('DisplayName'),
value: 'automation__name'
}
]
},
hasSearch: true,
hasRefresh: true,
hasRightActions: true,
hasLeftActions: false,
hasMoreActions: false,
hasExport: false,
hasImport: false
}
}
},
mounted() {
const automation_id = this.$route.query.automation_id
if (automation_id !== undefined) {
this.tableConfig.url = `${this.tableConfig.url}?automation_id=${automation_id}`
url: '/api/v1/accounts/check-account-executions/',
automationRoute: 'AccountCheckDetail',
detailRoute: 'RiskHistoryExecutionDetail',
resource: 'checkaccountexecution'
}
}
}

View File

@@ -44,7 +44,7 @@ export default {
formatterArgs: {
async: false,
drawer: false,
prevantClick: true
preventClick: true
}
},
nodes: {
@@ -52,7 +52,7 @@ export default {
formatterArgs: {
async: false,
drawer: false,
prevantClick: true
preventClick: true
}
},
secret_strategy: {

View File

@@ -113,6 +113,7 @@ export default {
}
},
username: {
label: this.$t('Account'),
width: '120px'
},
risk: {

View File

@@ -1,12 +1,33 @@
import i18n from '@/i18n/i18n'
const accountExistCache = new Map()
async function checkUserExist() {
if (!this.row.username) {
return false
async function checkAccountExist(username, asset) {
const cacheKey = `${username}-${asset}`
if (accountExistCache.has(cacheKey)) {
return accountExistCache.get(cacheKey)
}
const url = `/api/v1/accounts/accounts/?username=${encodeURIComponent(this.row.username)}&asset=${this.row.asset.id}`
const url = `/api/v1/accounts/accounts/?username=${encodeURIComponent(username)}&asset=${asset}`
const data = await this.$axios.get(url)
return data.length > 0
const result = data.length > 0
accountExistCache.set(cacheKey, result)
return result
}
async function checkAccountsExist() {
// 批量选择,所有都存在返回 true
if (this.rows.length > 0) {
for (const row of this.rows) {
if (!await checkAccountExist.call(this, row.username, row.asset.id)) {
return false
}
}
return true
}
// 单个选择
if (this.row.username) {
return await checkAccountExist.call(this, this.row.username, this.row.asset.id)
}
return false
}
export const riskActions = [
@@ -30,7 +51,7 @@ export const riskActions = [
label: i18n.t('AddAccount'),
has: ['new_found'],
disabled: async function() {
return await checkUserExist.call(this)
return await checkAccountsExist.call(this)
}
},
{
@@ -38,15 +59,26 @@ export const riskActions = [
label: i18n.t('AddAccountAfterChangingPassword'),
has: async function() {
const risks = ['new_found', 'long_time_password', 'password_expired']
return risks.includes(this.row.risk.value) && !await checkUserExist.call(this)
return risks.includes(this.row.risk.value) && !await checkAccountsExist.call(this)
}
},
{
name: 'change_password',
label: i18n.t('ChangePassword'),
has: async function() {
const risks = ['long_time_password', 'weak_password', 'password_expired', 'leaked_password', 'repeated_password']
return risks.includes(this.row.risk.value) && await checkUserExist.call(this)
const risks = [
'long_time_password', 'weak_password', 'password_expired',
'leaked_password', 'repeated_password'
]
return risks.includes(this.row.risk.value) && await checkAccountsExist.call(this)
}
},
{
name: 'delete_account',
label: i18n.t('DeleteAccount'),
has: async function() {
const risks = ['account_deleted']
return risks.includes(this.row.risk.value) && await checkAccountsExist.call(this)
}
},
// {

View File

@@ -164,7 +164,6 @@ export default {
data
)
} catch (e) {
row.status = { value: '4', label: this.$t('Failed') }
this.$emit('processDone', { index: i, row })
continue
}

View File

@@ -32,12 +32,12 @@ export default {
title: this.$t('Basic'),
name: 'AccountPushExecutionInfo',
hidden: () => !this.$hasPerm('accounts.view_pushaccountexecution')
},
{
title: this.$t('TaskList'),
name: 'AccountPushExecutionTaskList',
hidden: () => !this.$hasPerm('accounts.view_changesecretrecord')
}
// {
// title: this.$t('TaskList'),
// name: 'AccountPushExecutionTaskList',
// hidden: () => !this.$hasPerm('accounts.view_changesecretrecord')
// }
],
getTitle: this.getExecutionTitle

View File

@@ -1,6 +1,10 @@
import { toSafeLocalDateStr } from '@/utils/time'
import { ActionsFormatter, DetailFormatter, DiscoverConfirmFormatter } from '@/components/Table/TableFormatters'
export const statusMap = {
pending: '0',
confirmed: '1',
ignored: '2'
}
export const gatherAccountTableConfig = (vm, url) => {
if (!url) {
url = '/api/v1/accounts/gathered-accounts/'
@@ -37,6 +41,7 @@ export const gatherAccountTableConfig = (vm, url) => {
}
},
username: {
label: vm.$t('Account'),
formatter: DetailFormatter,
formatterArgs: {
can: true,
@@ -70,9 +75,9 @@ export const gatherAccountTableConfig = (vm, url) => {
confirm: ({ row }) => {
vm.$axios.put(
`/api/v1/accounts/gathered-accounts/status/`,
{ status: 'confirmed', ids: [row.id] }
{ status: statusMap.confirmed, ids: [row.id] }
).then(res => {
row.status = 'confirmed'
row.status = statusMap.confirmed
}).catch(() => {
row.status = vm.$t('Error')
})
@@ -80,9 +85,9 @@ export const gatherAccountTableConfig = (vm, url) => {
ignore: ({ row }) => {
vm.$axios.put(
`/api/v1/accounts/gathered-accounts/status/`,
{ status: 'ignored', ids: [row.id] }
{ status: statusMap.ignored, ids: [row.id] }
).then(res => {
row.status = 'ignored'
row.status = statusMap.ignored
}).catch(() => {
row.status = vm.$t('Error')
})
@@ -138,7 +143,7 @@ export const gatherAccountHeaderActions = (vm) => {
})
vm.$axios.put(
`/api/v1/accounts/gathered-accounts/status/`,
{ ids: ids, status: 'confirmed' }
{ ids: ids, status: statusMap.confirmed }
).then(() => {
vm.$message.success(vm.$tc('SyncSuccessMsg'))
}).catch(err => {

View File

@@ -54,12 +54,12 @@ export default {
'name', 'user_display',
{
key: vm.$t('IpGroup'),
value: () => vm.object.rules.ip_group.toString()
value: vm.object.rules.ip_group.join(', ')
},
'dataVal', 'action_display', 'priority', 'date_created', 'created_by',
{
key: vm.$t('LoginConfirmUser'),
value: () => vm.object.reviewers.map(item => item.name).join(', ')
value: vm.object.reviewers.map(item => item.name).join(', ')
},
'comment'
]

View File

@@ -2,6 +2,7 @@
<ListTable
ref="listTable"
:create-drawer="createDrawer"
:detail-drawer="detailDrawer"
:header-actions="headerActions"
:resource="$t('UserLoginACL')"
:table-config="tableConfig"
@@ -23,6 +24,8 @@ export default {
},
data() {
return {
createDrawer: () => import('./UserLoginACLCreateUpdate.vue'),
detailDrawer: () => import('./UserDetail/index.vue'),
tableConfig: {
url: this.url,
columnsExclude: ['rules', 'users'],
@@ -63,8 +66,7 @@ export default {
hasImport: false,
hasMoreActions: false,
createRoute: 'UserLoginACLCreate'
},
createDrawer: () => import('./UserLoginACLCreateUpdate.vue')
}
}
},
activated() {

View File

@@ -44,7 +44,6 @@ export default {
}
},
data() {
const vm = this
return {
loading: true,
platform: {},
@@ -68,16 +67,15 @@ export default {
fieldsMeta: {},
performSubmit(validValues) {
let url = this.url
const { id = '', action } = vm.meta
const { id = '' } = this.$route.params
const values = _.cloneDeep(validValues)
let submitMethod = id ? 'put' : 'post'
const submitMethod = id ? 'put' : 'post'
if (values.nodes && values.nodes.length === 0) {
delete values['nodes']
}
if (action === 'update') {
if (submitMethod === 'put') {
url = getUpdateObjURL(url, id)
delete values['accounts']
} else {
@@ -87,12 +85,6 @@ export default {
return item
})
}
if (vm.meta?.payload === 'pam_asset_clone' || vm.meta.row?.payload === 'pam_asset_clone') {
submitMethod = 'post'
url = this.url
}
return this.$axios[submitMethod](url, values)
}
}
@@ -104,7 +96,6 @@ export default {
methods: {
async init() {
try {
await this.getMeta()
await this.genConfig()
await this.setInitial()
await this.setPlatformConstrains()
@@ -114,11 +105,11 @@ export default {
},
async genConfig() {
const { addFields, addFieldsMeta, defaultConfig } = this
defaultConfig.fieldsMeta = assetFieldsMeta(this, this.meta.type)
defaultConfig.fieldsMeta = assetFieldsMeta(this, this.$route.query.type)
let url = this.url
const { id = '' } = this.meta
if (this.meta.platform && !id) {
url = setUrlParam(url, 'platform', this.meta.platform)
const { id = '', platform } = this.$route.query
if (platform && !id) {
url = setUrlParam(url, 'platform', platform)
}
// 过滤类型为null, undefined 的元素
defaultConfig.fields = defaultConfig.fields.filter(Boolean)
@@ -141,31 +132,18 @@ export default {
}
this.iConfig = config
},
async getMeta() {
let meta = await this.$store.dispatch('common/getDrawerActionMeta')
if (!meta || !meta.action) {
meta = {
action: 'create',
platform: this.$route.query.platform || '',
type: this.$route.query.type || '',
category: this.$route.query.category || '',
node: this.$route.query.node || this.$route.query.node_id || ''
}
}
this.meta = meta
},
async setInitial() {
const { defaultConfig } = this
const { node, platform } = this.meta
const { node, platform } = this.$route.query
const nodesInitial = node ? [node] : []
const platformId = this.changePlatformID ? this.changePlatformID : (platform || 'Linux')
const platformId = this.changePlatformID || this.$route.query.platform || (platform || 'Linux')
const url = `/api/v1/assets/platforms/${platformId}/`
this.platform = await this.$axios.get(url)
const initial = {
labels: [],
is_active: true,
nodes: nodesInitial,
platform: parseInt(platformId),
platform: parseInt(this.platform.id),
protocols: []
}
if (this.updateInitial) {

View File

@@ -110,7 +110,7 @@ export default {
},
computed: {
refTable() {
return this.$refs.listTable.$refs.dataTable.$refs.dataTable
return this.$refs.listTable.$refs.ListTable.$refs.dataTable.$refs.dataTable
},
iVisible: {
get() {

View File

@@ -78,7 +78,3 @@ export default {
}
}
</script>
<style scoped>
</style>

View File

@@ -57,16 +57,26 @@
:visible.sync="templateDialogVisible"
/>
</div>
<Drawer
:title="$t('Account')"
:component="drawerComponent"
:has-footer="false"
:visible.sync="drawerVisible"
class="detail-drawer"
/>
</div>
</template>
<script>
import AddAccountDialog from './AddAccountDialog'
import Drawer from '@/components/Drawer/index.vue'
import AccountTemplateDialog from './AccountTemplateDialog'
export default {
name: 'AssetAccounts',
components: {
Drawer,
AddAccountDialog,
AccountTemplateDialog
},
@@ -92,10 +102,11 @@ export default {
accounts: accounts,
drawerRefName: null,
account: {},
pamDrawerShow: false,
drawerVisible: false,
initial: false,
addAccountDialogVisible: false,
templateDialogVisible: false
templateDialogVisible: false,
drawerComponent: () => import('@/views/assets/Asset/AssetDetail')
}
},
watch: {
@@ -156,13 +167,11 @@ export default {
return
}
this.$router.push({
name: 'AssetDetail',
params: { id: assetId },
query: {
tab: 'Account'
}
this.$store.dispatch('common/setDrawerActionMeta', {
action: 'detail', row: {}, col: {}, id: assetId
})
this.drawerVisible = true
}
}
}
@@ -242,4 +251,13 @@ export default {
height: 100%;
}
}
.detail-drawer {
::v-deep {
.el-drawer__header {
border-bottom: none;
padding-bottom: 1px;
}
}
}
</style>

View File

@@ -59,7 +59,7 @@ export default {
return {
title: this.$t('Test'),
templateDialogVisible: false,
columnsDefault: ['name', 'username', 'asset'],
columnsDefault: ['name', 'username', 'asset', 'connect'],
headerExtraActions: [
{
name: this.$t('AccountTemplate'),

View File

@@ -16,7 +16,7 @@
</template>
<script>
import ListTable from '@/components/Table/ListTable'
import { DrawerListTable as ListTable } from '@/components'
import { GenericListTableDialog } from '@/layout/components'
import { DetailFormatter } from '@/components/Table/TableFormatters'
import PermUserGroupCard from './components/PermUserGroupCard'

View File

@@ -12,7 +12,7 @@
<script>
import BaseList from './components/BaseList'
import { ActionsFormatter } from '@/components/Table/TableFormatters'
import GatewayDialog from '@/components/Apps/GatewayDialog'
import GatewayDialog from '@/components/Apps/GatewayTestDialog'
import { openTaskPage } from '@/utils/jms'
export default {

View File

@@ -43,7 +43,7 @@
import ListTable from '@/components/Table/DrawerListTable'
import AssetBulkUpdateDialog from './AssetBulkUpdateDialog'
import PlatformDialog from '../components/PlatformDialog'
import GatewayDialog from '@/components/Apps/GatewayDialog'
import GatewayDialog from '@/components/Apps/GatewayTestDialog'
import AccountDiscoverDialog from './AccountDiscoverDialog.vue'
import AccountCreateUpdate from '@/components/Apps/AccountListTable/AccountCreateUpdate.vue'
import { getDefaultConfig } from './const'
@@ -121,7 +121,7 @@ export default {
]
const createAction = {
name: 'create',
title: 'Create',
title: this.$t('Create'),
type: 'primary',
icon: '',
split: true,
@@ -215,23 +215,19 @@ export default {
},
methods: {
async updateOrCloneAsset(row, action) {
if (action === 'update') {
this.$route.params.id = row.id
}
const meta = {
action: action,
id: row.id,
this.createDrawer = this.drawer[row.category.value]
const query = {
platform: row.platform.id,
type: row.type.value,
category: row.category.value,
row: row,
payload: row.payload
category: row.category.value
}
await this.$store.dispatch('common/setDrawerActionMeta', meta)
this.createDrawer = this.drawer[row.category.value]
setTimeout(() => {
this.$refs.ListTable.showDrawer(action)
}, 100)
if (action === 'clone') {
return this.$refs.ListTable.onClone({ row, query })
}
this.$refs.ListTable.onUpdate({ row, query })
},
createAsset(platform) {
this.showPlatform = false
@@ -242,15 +238,8 @@ export default {
category: platform.category.value,
node: this.$route.query?.node || this.$route.query?.node_id || ''
}
for (const key in createProps) {
this.$route.query[key] = createProps[key]
}
this.$store.dispatch('common/setDrawerActionMeta', {
action: 'create',
...createProps
}).then(() => {
this.$refs.ListTable.showDrawer('create')
})
this.$log.debug('createProps', createProps)
this.$refs.ListTable.onCreate({ query: createProps })
},
handleAssetBulkUpdate() {
this.updateSelectedDialogSetting.visible = false

View File

@@ -194,7 +194,7 @@ export function getDefaultConfig(vm) {
formatter: ActionsFormatter,
formatterArgs: {
onUpdate: ({ row }) => onAction(row, 'Update'),
onClone: ({ row }) => onAction({ ...row, payload: 'pam_asset_clone' }, 'Clone'),
onClone: ({ row }) => onAction(row, 'Clone'),
performDelete: ({ row }) => {
const id = row.id
const url = `/api/v1/assets/assets/${id}/`
@@ -235,7 +235,7 @@ export function getDefaultConfig(vm) {
divided: true,
can: ({ row }) => !vm.$store.getters.currentOrgIsRoot,
callback: ({ row }) => {
vm.asset.id = row.id
vm.asset = row
vm.showAddDialog = true
}
},

View File

@@ -14,6 +14,7 @@
/>
<SyncDialog
v-if="onlineSyncVisible"
:object="object"
:visible.sync="onlineSyncVisible"
/>
</div>

View File

@@ -120,9 +120,6 @@ export default {
action: 'sync_task', account_id: this.object.id
}))
}
this.ws.onclose = () => {
this.$emit('update:visible', false)
}
this.ws.onerror = () => {
this.$message.error(this.$tc('ConnectWebSocketError'))
}

View File

@@ -9,7 +9,6 @@
:title="$tc('CloudSyncConfig')"
:visible.sync="iVisible"
v-bind="$attrs"
width="1051px"
v-on="$listeners"
>
<el-row :gutter="5" style="padding: 10px">

View File

@@ -1,7 +1,7 @@
<template>
<div class="cloud-select-wrap">
<el-row :gutter="12">
<el-col v-for="p in providers" :key="p.name" :style="p.imageCSS ? p.imageCSS : ''">
<el-col v-for="p in providers" :key="p.name">
<el-card
:body-style="{ display: 'flex', alignItems: 'center', justifyContent: 'flex-start' }"
:class="selected === p.name ? 'active': ''"
@@ -79,7 +79,7 @@ export default {
<style lang='scss' scoped>
.buttons {
margin-top: 10px;
margin-top: 30px;
}
.cloud-select-wrap {

View File

@@ -4,7 +4,6 @@
:close-on-press-escape="false"
:destroy-on-close="true"
:show-buttons="false"
:show-close="false"
:title="$tc('SyncOnline')"
:visible.sync="iVisible"
v-on="$listeners"

View File

@@ -19,7 +19,7 @@ export const volcengine = 'volcengine'
export const qingcloud_private = 'qingcloud_private'
export const huaweicloud_private = 'huaweicloud_private'
export const state_private = 'state_private'
export const ctyun_private = 'ctyun_private'
export const openstack = 'openstack'
export const zstack = 'zstack'
export const nutanix = 'nutanix'
@@ -37,7 +37,7 @@ export const publicCloudProviders = [
]
export const privateCloudProviders = [
vmware, qingcloud_private, huaweicloud_private, state_private,
vmware, qingcloud_private, huaweicloud_private, ctyun_private,
openstack, zstack, nutanix, fc, scp, apsara_stack
]
@@ -168,9 +168,9 @@ export const ACCOUNT_PROVIDER_ATTRS_MAP = {
attrs: ['api_endpoint', 'username', 'password'],
image: require('@/assets/img/cloud/huawei.svg')
},
[state_private]: {
name: state_private,
title: i18n.t('StatePrivate'),
[ctyun_private]: {
name: ctyun_private,
title: i18n.t('CTYunPrivate'),
attrs: ['access_key_id', 'access_key_secret', 'api_endpoint', 'cert_file', 'key_file'],
image: require('@/assets/img/cloud/state.svg')
},
@@ -191,7 +191,7 @@ export const ACCOUNT_PROVIDER_ATTRS_MAP = {
title: i18n.t('LAN'),
attrs: ['ip_group', 'test_port', 'test_timeout', 'hostname_prefix'],
image: require('@/assets/img/cloud/lan.svg'),
imageCSS: { height: '250px' }
imageCSS: {}
}
}

View File

@@ -13,7 +13,7 @@
</template>
<script>
import BaseList from '../../Asset/AssetList/components/BaseList'
import BaseList from '@/views/assets/Asset/AssetList/components/BaseList'
import AddAssetDialog from '@/views/assets/Domain/components/AddAssetDialog.vue'
import TwoCol from '@/layout/components/Page/TwoColPage.vue'
@@ -60,9 +60,13 @@ export default {
name: 'AddAsset',
title: this.$t('Add'),
type: 'primary',
can: !this.$store.getters.currentOrgIsRoot,
callback: () => {
this.$route.params.id = this.object.id
this.addAssetSetting.addAssetDialogVisible = true
setTimeout(() => {
this.$route.params.id = null
}, 500)
}
}
]
@@ -100,6 +104,7 @@ export default {
methods: {
handleAddAssetDialogClose() {
this.addAssetSetting.addAssetDialogVisible = false
this.$route.params.id = null
this.reloadTable()
},
removeAsset(rows) {

View File

@@ -5,18 +5,18 @@
:create-drawer="createDrawer"
:detail-drawer="detailDrawer"
:header-actions="headerActions"
:table-config="tableConfig"
:resource="$tc('Gateway')"
:table-config="tableConfig"
/>
<GatewayDialog
:cell="cell"
:port="port"
:visible.sync="visible"
<GatewayTestDialog
:cell="testConfig.cell"
:port="testConfig.port"
:visible.sync="testConfig.visible"
/>
<AddGatewayDialog
v-if="AddGatewaySetting.addGatewayDialogVisible"
v-if="addGatewaySetting.addGatewayDialogVisible"
:object="transObject"
:setting="AddGatewaySetting"
:setting="addGatewaySetting"
@close="handleAddGatewayDialogClose"
/>
</TwoCol>
@@ -24,7 +24,7 @@
<script>
import { GenericListTable } from '@/layout/components'
import GatewayDialog from '@/components/Apps/GatewayDialog'
import GatewayTestDialog from '@/components/Apps/GatewayTestDialog'
import { connectivityMeta } from '@/components/Apps/AccountListTable/const'
import { ArrayFormatter, ChoicesFormatter, DetailFormatter, TagsFormatter } from '@/components/Table/TableFormatters'
import AddGatewayDialog from '@/views/assets/Domain/components/AddGatewayDialog'
@@ -34,7 +34,7 @@ export default {
components: {
TwoCol,
GenericListTable,
GatewayDialog,
GatewayTestDialog,
AddGatewayDialog
},
props: {
@@ -51,6 +51,11 @@ export default {
createDrawer: () => import('@/views/assets/Domain/DomainDetail/GatewayCreateUpdate.vue'),
detailDrawer: () => import('@/views/assets/Asset/AssetDetail'),
transObject: {},
testConfig: {
port: 0,
visible: false,
cell: ''
},
tableConfig: {
url: `/api/v1/assets/gateways/?domain=${this.object.id}`,
columnsExclude: [
@@ -133,20 +138,19 @@ export default {
can: this.$hasPerm('assets.test_assetconnectivity') && !this.$store.getters.currentOrgIsRoot,
title: this.$t('TestConnection'),
callback: function(val) {
this.visible = true
vm.testConfig.visible = true
const port = val.row.protocols.find(item => item.name === 'ssh').port
if (!port) {
return this.$message.error(this.$tc('BadRequestErrorMsg'))
} else {
this.port = port
this.cell = val.row.id
vm.testConfig.port = port
vm.testConfig.cell = val.row.id
}
}.bind(this)
}
]
}
}
}
},
headerActions: {
@@ -170,6 +174,7 @@ export default {
{
name: 'GatewayAdd',
title: this.$t('Add'),
can: !this.$store.getters.currentOrgIsRoot,
callback: async() => {
// 由于修改成为了抽屉形式,导致传入到 AddGateway 组件中的 obj 任然为最初的数量,就会导致新增的 item 依然会出现可选的情况
// 此时修改为在打开 AddGateway 额外从 tableConfig.url 的接口中获取最新的 gateways 数目
@@ -192,23 +197,16 @@ export default {
}
vm.$nextTick(() => {
this.AddGatewaySetting.addGatewayDialogVisible = true
this.addGatewaySetting.addGatewayDialogVisible = true
})
}
}
],
onCreate: () => {
vm.$route.query.domain = vm.object.id
vm.$route.query.platform_type = 'linux'
vm.$route.query.category = 'host'
vm.$refs.ListTable.onCreate()
vm.$refs.ListTable.onCreate({ query: { domain: vm.object.id, platform_type: 'linux', category: 'host' }})
}
},
port: 0,
cell: '',
visible: false,
AddGatewaySetting: {
addGatewaySetting: {
addGatewayDialogVisible: false
}
}

View File

@@ -25,7 +25,7 @@ export default {
TaskDetail: {},
config: {
activeMenu: 'Detail',
url: '/api/v1/assets/domains',
url: '/api/v1/assets/domains/',
submenu: [
{
title: this.$t('Basic'),

View File

@@ -42,6 +42,7 @@ export default {
}
},
data() {
const vm = this
return {
formConfig: {
getUrl: () => {
@@ -74,7 +75,7 @@ export default {
cleanFormValue(values) {
const data = []
values.assets.forEach(item => {
const d = { id: item, domain: this.$route.params.id }
const d = { id: item, domain: vm.object.id }
data.push(d)
})
return data

View File

@@ -124,8 +124,8 @@ export default {
this.initial.su_method = this.suMethodLimits[0]
},
async setCategories() {
const category = this.$route.query._category
const type = this.$route.query._type
const category = this.$route.query.category
const type = this.$route.query.type
const state = await this.$store.dispatch('assets/getAssetCategories')
this.fieldsMeta.category_type.el.options = state.assetCategoriesCascader
if (category && type) {
@@ -135,8 +135,8 @@ export default {
return new Promise((resolve, reject) => resolve(true))
},
async setConstraints() {
const category = this.$route.query._category
const type = this.$route.query._type
const category = this.$route.query.category
const type = this.$route.query.type
const url = `/api/v1/assets/categories/constraints/?category=${category}&type=${type}`
const constraints = await this.$axios.get(url)
this.defaultOptions = constraints

View File

@@ -5,9 +5,10 @@
</template>
<script>
import IBox from '../../../../components/Common/IBox'
import IBox from '@/components/Common/IBox'
import { GenericCreateUpdateForm } from '@/layout/components'
import { platformFieldsMeta, setAutomations, updateAutomationParams } from '../const'
import { setUrlId } from '@/utils/common'
import { mapGetters } from 'vuex'
export default {
@@ -30,7 +31,7 @@ export default {
ansible_enabled: true
}
},
url: `/api/v1/assets/platforms/${this.object.id}/`,
url: `/api/v1/assets/platforms/?_meta_cache=0`,
disabled: !canEdit,
hasReset: false,
hasDetailInMsg: false,
@@ -67,7 +68,8 @@ export default {
if (!this.canSubmit || !this.isSystemAdmin) {
return this.$message.error(this.$tc('NoPermission'))
}
this.$axios.patch(`${this.url}${this.object.id}/`, validValues).then(() => {
const url = setUrlId(this.url, this.object.id)
this.$axios.patch(url, validValues).then(() => {
this.$message.success(this.$tc('UpdateSuccessMsg'))
})
}

View File

@@ -35,7 +35,7 @@
<script>
import { IBox } from '@/components'
import AutoDetailCard from '@/components/Cards/DetailCard/auto'
import QuickActions from '../../../../components/Common/QuickActions'
import QuickActions from '@/components/Common/QuickActions'
import PlatformDetailUpdateDialog from './PlatformDetailUpdateDialog'
import ProtocolSelector from '@/components/Form/FormFields/ProtocolSelector'
import TwoCol from '@/layout/components/Page/TwoColPage.vue'

View File

@@ -38,7 +38,7 @@ export default {
name: 'Assets'
},
{
title: this.$t('Automations'),
title: this.$t('Automation'),
name: 'Automation'
}
],
@@ -72,7 +72,3 @@ export default {
}
}
</script>
<style scoped>
</style>

View File

@@ -109,12 +109,13 @@ export default {
actions: {
formatterArgs: {
canClone: () => vm.$hasPerm('assets.add_platform'),
onClone({ row }) {
vm.$refs.genericListTable.onClone({ row, query: { type: row.type.value, category: row.category.value }})
},
canUpdate: ({ row }) => !row.internal && vm.$hasPerm('assets.change_platform'),
canDelete: ({ row }) => !row.internal && vm.$hasPerm('assets.delete_platform'),
onUpdate({ row, col }) {
vm.$route.query._type = row.type.value
vm.$route.query._category = row.category.value
vm.$refs.genericListTable.onUpdate({ row, col })
vm.$refs.genericListTable.onUpdate({ row, col, query: { type: row.type.value, category: row.category.value }})
}
}
}
@@ -135,10 +136,7 @@ export default {
},
moreCreates: {
callback: (item) => {
this.$router.push({
query: { _type: item.name, _category: item.category }
})
this.$refs.genericListTable.onCreate()
this.$refs.genericListTable.onCreate({ query: { type: item.name, category: item.category }})
},
dropdown: []
}

View File

@@ -1,10 +0,0 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"sourceMap": true
},
"exclude": [
"node_modules"
]
}

View File

@@ -42,8 +42,9 @@ function updatePlatformProtocols(vm, platformType, updateForm, isPlatformChanged
'username_selector': setting.username_selector
})
}
// 这里不能清空,比如 gateway 切换时protocol 没有变化,就会出现 bug, tapd: 1053282
// updateForm({ protocols: [] })
vm.iConfig.fieldsMeta.protocols.el.choices = platformProtocols
updateForm({ protocols: [] })
}), 100)
}

View File

@@ -6,7 +6,7 @@
<div class="content">
<el-row justify="space-between" type="flex">
<el-col v-for="item of summaryItems" :key="item.title" :md="8" :sm="12" :xs="12">
<SummaryCard :body="item.body" :title="item.title" />
<SummaryCard :title="item.title" v-bind="item.body" />
</el-col>
</el-row>
</div>

View File

@@ -24,9 +24,10 @@ export default {
counter: {
total_long_time_no_login_accounts: '.',
total_new_found_accounts: '.',
total_group_changed_accounts: '.',
total_sudo_changed_accounts: '.',
total_groups_changed_accounts: '.',
total_sudoers_changed_accounts: '.',
total_authorized_keys_changed_accounts: '.',
total_account_deleted_accounts: '.',
total_password_expired_accounts: '.',
total_long_time_password_accounts: '.',
total_weak_password_accounts: '.',
@@ -50,20 +51,25 @@ export default {
value: this.counter.total_new_found_accounts
},
{
description: 'group_changed',
name: this.$t('GroupChanged'),
value: this.counter.total_group_changed_accounts
description: 'groups_changed',
name: this.$t('GroupsChanged'),
value: this.counter.total_groups_changed_accounts
},
{
description: 'sudo_changed',
name: this.$t('SudoChanged'),
value: this.counter.total_sudo_changed_accounts
description: 'sudoers_changed',
name: this.$t('SudoersChanged'),
value: this.counter.total_sudoers_changed_accounts
},
{
description: 'authorized_keys_changed',
name: this.$t('AuthorizedKeysChanged'),
value: this.counter.total_authorized_keys_changed_accounts
},
{
description: 'account_deleted',
name: this.$t('AccountDeleted'),
value: this.counter.total_account_deleted_accounts
},
{
description: 'password_expired',
name: this.$t('PasswordExpired'),
@@ -204,9 +210,10 @@ export default {
params: {
total_long_time_no_login_accounts: 1,
total_new_found_accounts: 1,
total_group_changed_accounts: 1,
total_sudo_changed_accounts: 1,
total_groups_changed_accounts: 1,
total_sudoers_changed_accounts: 1,
total_authorized_keys_changed_accounts: 1,
total_account_deleted_accounts: 1,
total_password_expired_accounts: 1,
total_long_time_password_accounts: 1,
total_weak_password_accounts: 1,

View File

@@ -12,12 +12,11 @@
<MissionSummery class="mission-summery" />
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="14" :xs="24">
<el-col v-if="$store.getters.hasValidLicense" :span="14" :xs="24">
<AccountSecretSummary class="account-secret-summary" />
</el-col>
<el-col :span="10" :xs="24">
<el-col :span="$store.getters.hasValidLicense ? 10: 24" :xs="24">
<RiskSummary class="risk-summary" />
</el-col>
</el-row>

View File

@@ -245,22 +245,17 @@ export default {
this.initial.runAfterSave = true
this.instantTask = true
this.createSuccessNextRoute = { name: 'Adhoc' }
this.ready = true
})
} else {
this.ready = true
}
break
case 'playbook':
this.initial.type = 'playbook'
if (this.$route.query.id) {
this.initial.playbook = this.$route.query.id
this.ready = true
} else {
this.ready = true
}
break
}
this.ready = true
},
methods: {
submitForm(form, btn) {

View File

@@ -105,10 +105,7 @@ export default {
canUpdate: this.$hasPerm('ops.change_job') && !this.$store.getters.currentOrgIsRoot,
updateRoute: 'JobUpdate',
onUpdate: ({ row, col }) => {
vm.$router.push({
query: { _type: this.type }
})
vm.$refs.ListTable.onUpdate({ row, col })
vm.$refs.ListTable.onUpdate({ row, col, query: { _type: this.type }})
},
hasDelete: true,
canDelete: this.$hasPerm('ops.delete_job'),
@@ -141,10 +138,7 @@ export default {
},
headerActions: {
onCreate: () => {
vm.$router.push({
query: { _type: this.type }
})
vm.$refs.ListTable.onCreate()
vm.$refs.ListTable.onCreate({ query: { _type: this.type }})
},
hasRefresh: true,
hasExport: false,

Some files were not shown because too many files have changed in this diff Show More