Compare commits

..

662 Commits

Author SHA1 Message Date
pycook
5aff5d728d fix(api): check rack u slot 2024-11-27 15:39:53 +08:00
pycook
464b9d5394 chore: release v2.4.15 2024-11-27 15:14:58 +08:00
Leo Song
9c4cc20e13 Merge pull request #643 from veops/dev_ui_dcim
Dev UI dcim
2024-11-27 11:15:15 +08:00
songlh
c3c8602207 feat(ui): dcim - update rack list filter 2024-11-27 11:14:21 +08:00
songlh
c961e288af feat(ui): add dcim 2024-11-27 10:26:05 +08:00
pycook
e22b0c5290 feat(api): dcim dev (#642) 2024-11-26 18:56:59 +08:00
pycook
900cf1f617 feat(api): update ipam 2024-11-25 20:19:01 +08:00
Leo Song
f28ad4d041 Merge pull request #639 from veops/dev_ui_ipam
feat(ui): ipam - add batch assign
2024-11-13 10:04:53 +08:00
songlh
d2698b05c0 feat(ui): ipam - add batch assign 2024-11-13 10:03:13 +08:00
Leo Song
6f1332148c Merge pull request #638 from veops/dev_ui_ipam
fix(ui): ipam - filter search value error
2024-11-12 10:59:04 +08:00
songlh
5fa18eeb00 fix(ui): ipam - filter search value error 2024-11-12 10:58:09 +08:00
pycook
03fdf5c004 chore: release v2.4.14 2024-11-11 19:02:05 +08:00
pycook
f277cf088e fix(api): ipam assign address 2024-11-11 18:56:09 +08:00
pycook
b1f8a0024b Dev api ipam (#637)
* feat: ipam api

* fix: ipam
2024-11-11 18:17:37 +08:00
Leo Song
aae43a53b5 Merge pull request #636 from veops/dev_ui_ipam
feat(ui): add ipam
2024-11-11 16:50:35 +08:00
songlh
8c2cdb1ca4 feat(ui): add ipam 2024-11-11 16:49:53 +08:00
thexqn
57d4bf5548 fix(search): correct type_id usage in CI relation filtering (#633) 2024-11-11 15:53:24 +08:00
dagongren
5e7c6199bf feat:add employee work_region (#634)
* feat:add employee work_region

* env
2024-11-07 11:52:55 +08:00
Zhuohao Li
28dca7f086 fix permission bug (#632)
不同的appid下可能有相同的resource type name.
2024-10-27 14:04:34 +08:00
pycook
4a3c21eec4 feat(api): add builtin attributes (#631) 2024-10-22 18:21:07 +08:00
Leo Song
5d28c28023 Merge pull request #630 from veops/dev_ui_241022
fix(ui): update userPanel style
2024-10-22 14:12:14 +08:00
songlh
ba6edb3abe fix(ui): update userPanel style 2024-10-22 14:11:36 +08:00
Leo Song
2f1d57cee1 Merge pull request #629 from veops/dev_ui_241022
feat(ui): add userPanel component
2024-10-22 14:01:02 +08:00
songlh
4111c634d9 feat(ui): add userPanel component 2024-10-22 13:59:38 +08:00
pycook
f1fababa3d feat(api): save relation search option 2024-10-18 11:03:31 +08:00
pycook
fe6373422e chore: release v2.4.13 2024-10-18 09:52:26 +08:00
pycook
b3ea776886 Merge pull request #628 from veops/dev_api_relation_path_search
feat(api): relation path search
2024-10-17 19:47:34 +08:00
pycook
c4d2ce313d feat(api): relation path search 2024-10-17 19:46:39 +08:00
Leo Song
20103a0fe6 Merge pull request #627 from veops/dev_ui_241017
feat(ui): add relation search
2024-10-17 17:56:13 +08:00
songlh
394e2aeac6 feat(ui): add relation search 2024-10-17 17:55:36 +08:00
pycook
8f7d78c26c Merge pull request #623 from veops/dev_api_relation_path_search
Dev api relation path search
2024-09-30 17:33:45 +08:00
pycook
7eecf3cec3 feat(api): add api /ci_type_relations/path 2024-09-26 20:32:21 +08:00
pycook
f6e9c443f7 Merge pull request #622 from novohool/master
Update cache support for environment variables in settings.example.py
2024-09-26 18:09:54 +08:00
pycook
857cbd82fd feat(api): add relation path search 2024-09-26 17:59:08 +08:00
novohool
9a14296e02 Update settings.example.py 2024-09-26 17:00:51 +08:00
pycook
f638b52759 fix(api): change records of attribute values for date and datetime 2024-09-25 19:37:08 +08:00
pycook
78da728105 fix(api): search for multiple CIType 2024-09-24 17:46:27 +08:00
pycook
eb69029a51 fix(api): ci relations search 2024-09-23 19:46:43 +08:00
Leo Song
07a097eba2 Merge pull request #619 from veops/dev_ui_240920
feat: update computed attr tip
2024-09-20 15:36:55 +08:00
songlh
e843e3eac9 feat: update computed attr tip 2024-09-20 15:36:19 +08:00
Leo Song
7308cfa6c2 Merge pull request #617 from veops/dev_ui_240914
dev_ui_240914
2024-09-14 17:28:42 +08:00
songlh
64ea4fb21f fix(ui): operation history search expand error 2024-09-14 17:27:57 +08:00
songlh
e15cefaa38 fix(ui): employeeTreeSelect display error 2024-09-14 17:26:33 +08:00
pycook
f32339b969 Merge pull request #616 from thexqn/optimize_history
feat: Add show_attr value column to operation history table
2024-09-14 11:55:01 +08:00
thexqn
131d213a73 优化CITypeCache的调用方式 2024-09-14 11:30:45 +08:00
thexqn
ff98777689 feat(cmdb): 添加操作历史表的唯一值列 (Add unique value column to operation history table) 2024-09-14 01:13:07 +08:00
thexqn
383d4c88ed feat: Add unique value column to operation history table 2024-09-13 23:44:40 +08:00
Leo Song
bb7157e292 Merge pull request #615 from veops/dev_ui_240913
feat(ui): add employeeTreeSelect otherOptions prop
2024-09-13 18:36:48 +08:00
songlh
b1a82f1a67 feat(ui): add employeeTreeSelect otherOptions prop 2024-09-13 18:36:24 +08:00
pycook
de86ea3852 fix(api): remote ip for login log 2024-09-10 11:41:35 +08:00
pycook
bf05ea240e feat(api): acl supports channel 2024-09-09 15:28:20 +08:00
Leo Song
8ec0d619d7 Merge pull request #613 from veops/dev_ui_240909
feat(ui): add SplitPane calcBasedParent prop
2024-09-09 10:45:27 +08:00
songlh
61f8c463bc feat(ui): add SplitPane calcBasedParent prop 2024-09-09 10:44:58 +08:00
Leo Song
9b4dc3e43b Merge pull request #611 from veops/dev_ui_240903
feat: update icon select
2024-09-03 16:41:18 +08:00
songlh
9e69be8256 feat: update icon select 2024-09-03 16:40:46 +08:00
pycook
251b9e7fd5 chore: release v2.4.12 2024-09-03 14:18:53 +08:00
Leo Song
f3cc12f1f9 Merge pull request #610 from veops/dev_ui_240903
fix(ui): build error
2024-09-03 13:15:43 +08:00
songlh
56f03e1624 fix(ui): build error 2024-09-03 13:14:56 +08:00
Leo Song
42ad2b6dde Merge pull request #609 from veops/dev_ui_240903
feat: update resource search
2024-09-03 11:30:08 +08:00
songlh
5aba1ff257 feat: update resource search 2024-09-03 11:29:32 +08:00
pycook
417e8fe349 perf(api): resource search supports recent searches and my favorites 2024-09-02 16:56:06 +08:00
Leo Song
02235d8cc0 Merge pull request #607 from veops/dev_ui_240828
fix(ui): ci choice attr error
2024-08-28 18:52:43 +08:00
songlh
00c7a644a2 fix(ui): ci choice attr error 2024-08-28 18:52:20 +08:00
pycook
f3e8757450 fix(api): CIType templates import 2024-08-28 17:52:15 +08:00
Leo Song
f0749341ba Merge pull request #606 from veops/dev_ui_240828
feat(ui): update ui
2024-08-28 16:55:45 +08:00
songlh
89da671e46 feat(ui): update ui 2024-08-28 16:55:07 +08:00
Leo Song
0e60aae076 Merge pull request #605 from veops/dev_ui_240827
Dev UI 240827
2024-08-27 10:33:30 +08:00
songlh
4dfa97d404 fix(ui): resource search export error 2024-08-27 10:32:53 +08:00
songlh
9b778f9bc7 fix(ui): update create attr icon 2024-08-27 10:32:25 +08:00
pycook
eafb5f053a fix(api): custom dashboard for enum type 2024-08-26 22:31:58 +08:00
Leo Song
834054e216 Merge pull request #604 from veops/dev_ui_240826
feat: export remove reference attr
2024-08-26 22:22:43 +08:00
LH_R
a97cabbedc feat: export remove reference attr 2024-08-26 22:21:25 +08:00
Leo Song
ae77852d5f Merge pull request #603 from veops/dev_ui_240826
fix(ui): define value filter error
2024-08-26 21:40:03 +08:00
LH_R
611ee40dca fix(ui): define value filter error 2024-08-26 21:38:02 +08:00
pycook
c0d55b2126 Merge branch 'master' of github.com:veops/cmdb 2024-08-26 19:50:44 +08:00
pycook
2cc4499ef9 fix(api): custom dashboard 2024-08-26 19:50:22 +08:00
Leo Song
1268404bca Merge pull request #602 from veops/dev_ui_240826
fix(ui): menu icon display
2024-08-26 19:49:46 +08:00
songlh
570a9203c4 fix(ui): menu icon display 2024-08-26 19:47:23 +08:00
pycook
adae7b5519 chore: release v2.4.11 2024-08-26 18:44:23 +08:00
Leo Song
8a91ec7b11 Merge pull request #601 from veops/dev_ui_240826
fix(ui): some bugs
2024-08-26 18:35:44 +08:00
songlh
92fca65383 fix(ui): some bugs 2024-08-26 18:34:42 +08:00
Leo Song
4b8e6c2841 Merge pull request #600 from veops/dev_ui_240826
fix(ui): update builtIn params
2024-08-26 16:03:37 +08:00
songlh
ab240cb003 fix(ui): update builtIn params 2024-08-26 16:02:05 +08:00
Leo Song
61e62e4740 Merge pull request #599 from veops/dev_ui_240826
feat(ui) update CMDBFilterComp label
2024-08-26 15:16:07 +08:00
songlh
1fd72d6c78 feat(ui) update CMDBFilterComp label 2024-08-26 15:14:52 +08:00
Leo Song
51e16f6b23 Merge pull request #598 from veops/dev_ui_240826
Dev UI 240826
2024-08-26 15:09:01 +08:00
songlh
037378e384 fix(ui): create ad plugin params 2024-08-26 15:08:19 +08:00
songlh
631871a8cf feat(ui): update ci type choice config 2024-08-26 15:05:11 +08:00
pycook
6e02f6a21f fix(api): in query 2024-08-26 13:29:03 +08:00
pycook
a2224ba2ac Merge pull request #597 from veops/dev_api_0826
feat(api): enum supports
2024-08-26 12:15:05 +08:00
pycook
11a289aac9 feat(api): enum supports 2024-08-26 12:14:14 +08:00
Leo Song
55ab04dd28 Merge pull request #596 from thexqn/fix_order_bug
修复在继承模型的情况下,非继承属性与继承属性的排序以及其他分组的排序提示问题Fix order bug
2024-08-26 11:20:15 +08:00
thexqn
256a4f4844 清理多余的router-view 2024-08-23 16:55:21 +08:00
thexqn
018a349336 feat: 修复在继承模型的情况下,非继承属性与继承属性的排序以及其他分组的排序的问题 2024-08-23 16:50:37 +08:00
thexqn
8f62227adb feat: 修复在继承模型的情况下,非继承属性与继承属性的排序以及其他分组的排序的问题 2024-08-23 16:29:29 +08:00
thexqn
de51cb3e21 Merge branch 'veops:master' into master 2024-08-23 14:56:12 +08:00
Leo Song
ecb069cf14 Merge pull request #594 from veops/dev_ui_240820
feat(ui): add bool and reference type
2024-08-20 15:31:45 +08:00
songlh
937cb84393 feat(ui): add bool and reference type 2024-08-20 15:31:11 +08:00
pycook
40a4db06b5 Merge pull request #593 from veops/dev_api_0820
feat(api): supports bool and reference
2024-08-20 13:51:44 +08:00
pycook
cc98f903ea feat(api): supports bool and reference 2024-08-20 13:49:51 +08:00
kinyXu
fb7471ce04 feat: add attribute sorted tips for non-inherited attributes 2024-08-20 11:48:44 +08:00
Leo Song
e2872f041e Merge pull request #591 from veops/dev_ui_240813
refactor(ui): ci table
2024-08-13 17:15:15 +08:00
songlh
250fde127c refactor(ui): ci table 2024-08-13 17:14:05 +08:00
pycook
73dbb14944 Merge pull request #590 from lgphone/patch-1
bugfix: cmdb-api  auto_discovery add unique_value param
2024-08-07 16:05:56 +08:00
YangEver
73c9a6fa72 bugfix: cmdb-api auto_discovery add unique_value param
自动发现接口需要根据unique_value参数进行数据唯一性校验,此参数为必填项
2024-08-07 15:50:42 +08:00
Leo Song
09d957db79 Merge pull request #589 from veops/dev_ui_240807
Dev UI 240807
2024-08-07 14:42:01 +08:00
songlh
b73d796891 fix(ui): dashboard chart config 2024-08-07 14:41:22 +08:00
songlh
e7cbd0caa9 feat(ui): update common settings btn 2024-08-07 14:40:54 +08:00
pycook
3e4c385d91 fix(api): Dashboard using display attributes 2024-08-06 19:59:16 +08:00
pycook
3aac012ee9 chore: release v2.4.10 2024-07-31 16:42:26 +08:00
Leo Song
78d762cacc Merge pull request #588 from veops/dev_ui_240731
feat(ui): update ci type
2024-07-31 16:01:17 +08:00
songlh
c668ba7d3f feat(ui): update ci type 2024-07-31 16:00:40 +08:00
pycook
542a876ead fix(api): delete item for multi-value attributes 2024-07-30 20:05:21 +08:00
pycook
68b7497bba Merge pull request #587 from thexqn/master
修复在用了计算属性的情况下,批量上传功能可能出现的错误.
2024-07-30 09:18:30 +08:00
thexqn
dfbf3d462d 修复在用了计算属性的情况下,批量上传功能可能出现的错误. 2024-07-30 01:17:53 +08:00
pycook
692708fcba feat(api): Multi-valued attribute values ​​support adding and deleting 2024-07-29 19:55:07 +08:00
pycook
330b64edb3 chore: release v2.4.9 2024-07-26 17:03:00 +08:00
Leo Song
63a3074cb7 Merge pull request #585 from veops/fix_ui_240726
fix: discovery card eye btn
2024-07-26 16:50:43 +08:00
songlh
31b8cf49dc fix: discovery card eye btn 2024-07-26 16:49:28 +08:00
Leo Song
b01c335456 Merge pull request #584 from veops/dev_ui_240726
feat(ui): update auto discovery
2024-07-26 10:41:19 +08:00
songlh
002fef09e2 feat(ui): update auto discovery 2024-07-26 10:40:37 +08:00
pycook
175778a162 perf(api): auto discovery (#582) 2024-07-25 17:45:26 +08:00
Leo Song
5050a1bef5 Merge pull request #581 from veops/dev_ui_240722
feat: add accounts config
2024-07-22 17:39:18 +08:00
songlh
46a6cf67d6 feat: add accounts config 2024-07-22 17:38:48 +08:00
Leo Song
4e857c2775 Merge pull request #580 from veops/dev_ui_240716
feat: add history export
2024-07-16 13:46:40 +08:00
songlh
835df1bdeb feat: add history export 2024-07-16 13:45:31 +08:00
ivonGwy
579339d13c change pic 2024-07-15 16:23:34 +08:00
ivonGwy
629967ce82 change pic 2024-07-15 16:22:20 +08:00
pycook
3a00bfd236 chore: update docker compose 2024-07-11 14:29:34 +08:00
pycook
2e97ebd895 chore: release v2.4.8 2024-07-10 19:43:01 +08:00
Leo Song
eb6a813cbc Merge pull request #578 from veops/dev_ui_24071002
feat(ui): update
2024-07-10 19:19:08 +08:00
songlh
ff78face48 feat(ui): update 2024-07-10 19:18:22 +08:00
pycook
d55433c438 fix(api): computed attributes for multi values (#577) 2024-07-10 19:18:03 +08:00
Leo Song
daf0254616 Merge pull request #575 from veops/dev_ui_240710
fix: topoview search error
2024-07-10 10:12:11 +08:00
songlh
6b32009955 fix: topoview search error 2024-07-10 10:11:40 +08:00
Leo Song
d53288c1fb Merge pull request #574 from veops/dev_ui_240709
feat: update auto discovery
2024-07-09 09:45:25 +08:00
songlh
586d820a08 feat: update auto discovery 2024-07-09 09:44:28 +08:00
pycook
6776be4599 fix(api): auto discovery update
fix(api): auto discovery update
2024-07-08 18:03:21 +08:00
pycook
ff2b8ea198 perf(api): relationships built by attribute values (#572) 2024-07-08 11:42:18 +08:00
Leo Song
ed46a1e1c1 Merge pull request #571 from veops/dev_ui_240703
feat: add http attr mapping
2024-07-03 18:49:23 +08:00
songlh
0dc614fb46 feat: add http attr mapping 2024-07-03 18:47:55 +08:00
pycook
bc66d33ce0 fix(api): auto discovery configuration save password
fix(api): auto discovery configuration save password
2024-07-02 21:32:30 +08:00
pycook
d5db68d7d0 feat(api): auto discovery supports mapping (#569) 2024-07-02 20:19:50 +08:00
Leo Song
b22b8b286b Merge pull request #568 from veops/dev_ui_240628
dev_ui_240628
2024-06-28 17:43:30 +08:00
songlh
dd4f3b0e9c feat: update model export 2024-06-28 17:42:20 +08:00
songlh
688f4e0ea4 fix(ui): load ci type error 2024-06-28 17:42:10 +08:00
pycook
c1813f525d chore: update docker compose 2024-06-27 21:33:19 +08:00
pycook
b405e28498 chore: release v2.4.7 2024-06-27 21:30:02 +08:00
pycook
fa32758462 perf(api): CIType templates download (#567) 2024-06-27 20:54:38 +08:00
Leo Song
29995b660a Merge pull request #566 from veops/dev_ui_0627
feat: update model config
2024-06-27 19:41:58 +08:00
songlh
b96fc06a62 feat: update model config 2024-06-27 19:41:24 +08:00
Leo Song
c7f30b63ff Merge pull request #565 from veops/dev_ui_0625
fix(ui): auto discovery
2024-06-25 17:36:00 +08:00
songlh
5bff69a8a8 fix(ui): auto discovery 2024-06-25 17:35:29 +08:00
pycook
d5e60fab88 chore: update ui dockerfile 2024-06-24 21:40:38 +08:00
Jared Tan
190f452118 chore: fix UI docker build and makes UI/API docker build parallel execution (#563)
* try fix UI docker build

* parallel execution

* polish

* polish

* polish

* update
2024-06-24 21:00:57 +08:00
Leo Song
98a4824364 Merge pull request #564 from veops/dev_ui_ad_0624
feat: update ad ui
2024-06-24 14:26:33 +08:00
songlh
c0f9baea79 feat: update ad ui 2024-06-24 14:25:56 +08:00
pycook
d4b661c77f fix(api): commands cmdb-patch 2024-06-21 18:22:56 +08:00
pycook
75cd7bde77 fix(api): auto discovery permission 2024-06-21 12:47:12 +08:00
Leo Song
ec912d3a65 Merge pull request #562 from veops/fix_ui_2.4.6
fix(ui): some bugs
2024-06-21 11:49:53 +08:00
songlh
42f02b4986 fix(ui): some bugs 2024-06-21 11:49:12 +08:00
simontigers
a13b999820 Merge pull request #561 from veops/dev_common_perm
fix(api): auto_discovery add new perms
2024-06-21 10:25:35 +08:00
simontigers
5f53b0dd0e fix(api): auto_discovery add new perms 2024-06-21 10:25:13 +08:00
Leo Song
df22085ff9 Merge pull request #560 from veops/fix_ui_topology
fix(ui): topology view error
2024-06-20 22:21:27 +08:00
LH_R
06148b402d fix(ui): topology view error 2024-06-20 22:20:25 +08:00
pycook
3fe020505a chore: release v2.4.6 2024-06-20 20:31:10 +08:00
pycook
b34e83124f perf(api): auto discovery has been upgraded (#559) 2024-06-20 20:30:04 +08:00
Leo Song
cdc52d3f80 Merge pull request #558 from veops/dev_ui_ad
fix: build error
2024-06-20 20:03:40 +08:00
LH_R
b3a80d5678 fix: build error 2024-06-20 19:54:15 +08:00
Leo Song
a2e3061bba Merge pull request #557 from veops/dev_ui_ad
feat(ui): auto discovery
2024-06-20 17:29:06 +08:00
songlh
a8eb5126ea feat(ui): auto discovery 2024-06-20 17:28:09 +08:00
pycook
adac2129fc chore: update Dockerfile-UI 2024-06-20 13:20:42 +08:00
pycook
e660c901ce chore: update Dockerfile-UI 2024-06-20 11:07:57 +08:00
pycook
ff002c0a1e chore: update Dockerfile-UI 2024-06-20 09:47:56 +08:00
Leo Song
88593d6da7 Merge pull request #555 from veops/fix_ui_lint
fix(ui): lint error
2024-06-18 11:42:53 +08:00
songlh
6fa0dd5bc5 fix(ui): lint error 2024-06-18 11:42:22 +08:00
Jared Tan
3200942373 polish ci and remove es build (#553) 2024-06-18 10:31:33 +08:00
pycook
4fd705cc59 feat(api): add table c_ad_ci_type_relations 2024-06-18 10:22:04 +08:00
Jared Tan
74827ce187 add workflow (#552) 2024-06-18 09:29:00 +08:00
Leo Song
4ed1eb6062 Merge pull request #551 from veops/fix_bug_538
fix: issue #538
2024-06-17 14:41:51 +08:00
songlh
7792204658 fix: issue #538 2024-06-17 14:41:24 +08:00
Leo Song
8621108906 Merge pull request #550 from veops/fix_bug_operation_history
fix: operation history table
2024-06-14 17:27:49 +08:00
songlh
6437af19b9 fix: operation history table 2024-06-14 17:27:13 +08:00
Leo Song
735ddb334c Merge pull request #542 from veops/fix_issue_540
fix: issue #540
2024-06-12 15:00:08 +08:00
songlh
4a8032202e fix: issue #540 2024-06-12 14:59:14 +08:00
Leo Song
c7acea6422 Merge pull request #539 from veops/fix_computed_code
fix: computed code area tab
2024-06-11 15:03:20 +08:00
songlh
ac4c93de8e fix: computed code area tab 2024-06-11 15:02:37 +08:00
pycook
8d044cf935 chore(docker compose): add api health check 2024-06-09 20:58:27 +08:00
pycook
54747fa789 feat(ui): update iconfont 2024-06-07 10:41:26 +08:00
pycook
545f1bb30b Dev dynamic attribute (#535)
* feat: dynamic attribute

* feat(api): dynamic attribute
2024-06-07 10:39:40 +08:00
pycook
dc77bca17c feat: dynamic attribute (#534) 2024-06-07 10:29:32 +08:00
Leo Song
4973278c5a Merge pull request #532 from veops/fix_bug_530
fix: ci topo expand error
2024-06-06 14:06:26 +08:00
songlh
d1c9361e47 fix: ci topo expand error 2024-06-06 14:05:32 +08:00
Leo Song
28c57cacd9 Merge pull request #531 from veops/dev_ui_240606
feat: update topology view
2024-06-06 11:10:35 +08:00
songlh
711dcc4bd7 feat: update topology view 2024-06-06 11:08:58 +08:00
simontigers
491d3cce00 Merge pull request #529 from veops/fix_decorator_perms_role_required
fix: decorator_perms_role_required
2024-06-04 19:23:58 +08:00
simontigers
27354a3927 fix: decorator_perms_role_required 2024-06-04 19:23:22 +08:00
Leo Song
78495eb976 Merge pull request #528 from veops/feat/dev_ui_240604
feat(ui): update model relation
2024-06-04 12:05:42 +08:00
songlh
ae900c7d3b feat(ui): update model relation 2024-06-04 12:04:26 +08:00
pycook
50134e6a0b feat(api): attribute association supports multiple groups (#527) 2024-06-04 11:34:54 +08:00
pycook
65ef58dea9 feat: update docker-compose 2024-05-30 13:18:28 +08:00
pycook
0a2e7aa99f feat: put the mysql password in .env 2024-05-30 13:08:18 +08:00
pycook
8875e75883 fix(acl): add relation 2024-05-30 09:33:30 +08:00
pycook
2f03639c57 chore: release v2.4.5 2024-05-29 13:32:40 +08:00
pycook
49bc5d94a9 fix(api): topology view read permission 2024-05-29 11:34:02 +08:00
pycook
39354e1293 feat(ui): update components CMDBExprDrawer 2024-05-28 20:16:39 +08:00
pycook
d3714f3ecf feat(ui): relation-graph upgrade to 2.1.42 2024-05-28 20:11:53 +08:00
pycook
729a616282 feat(ui): topology view (#525) 2024-05-28 20:03:10 +08:00
fxiang21
2d3a290aa3 fix: cmdb-inner-secrets-init bug 2024-05-28 19:57:16 +08:00
pycook
9e885a5b12 feat(api): i18n update 2024-05-28 18:08:15 +08:00
simontigers
f5822d7cba Merge pull request #524 from simontigers/common_cmdb_app_perm
fix: cmdb app perms
2024-05-28 17:55:08 +08:00
hu.sima
21ea553e74 fix: cmdb app perms 2024-05-28 17:54:51 +08:00
pycook
e63038d1b6 feat(api): topology view (#523)
* feat(api): topology views crud

* feat(api): topology view

* feat(api): topology view api done

* feat(api): topology view is done
2024-05-28 17:50:09 +08:00
simontigers
d56806f511 Merge pull request #521 from simontigers/common_cmdb_app_perm
feat: CMDB add TopologyView resource
2024-05-28 16:21:25 +08:00
simontigers
7ac7fdc08e feat: CMDB add TopologyView resource 2024-05-28 16:20:56 +08:00
pycook
ba11707146 feat(ui): resource views router 2024-05-21 17:58:06 +08:00
pycook
d49dc8a067 fix(api): hot loading is blocked in development mode 2024-05-21 13:14:40 +08:00
pycook
6bfb34fe2a Dev UI 240520 (#517)
* feat(ui): Model configuration supports search models

* fix(ui): Jump to the first subscription by default
2024-05-20 14:10:33 +08:00
thexqn
2c7ed8c32d chore: Update local.md with instructions for setting up MySQL and Redis services (#515) 2024-05-20 13:39:05 +08:00
pycook
5b275af54e fix(api): exception when calling webhook (#516) 2024-05-20 13:22:54 +08:00
pycook
dde7ec6246 feat(ui): Resources and Preference support grouping 2024-05-19 21:55:36 +08:00
pycook
9181817e96 feat(api): my preference support grouping (#513) 2024-05-18 22:55:01 +08:00
pycook
46b54bb7f2 fix(ui): some bugs (#512) 2024-05-17 12:07:56 +08:00
pycook
fe63310c4e Dev api 240517 (#511)
* fix(api): list values delete

* fix(acl): role rebuild cache
2024-05-17 11:20:53 +08:00
pycook
27c733aa2c docs: update sql 2024-05-16 20:59:30 +08:00
pycook
2a8e9e684e fix(ui): issue#490 2024-05-02 21:28:06 +08:00
pycook
095190a785 fix(api): unique constraint (#505) 2024-05-02 21:22:40 +08:00
pycook
ef25c94b5d fix(api): permissions for CIType group editing 2024-04-29 15:18:47 +08:00
pycook
06ae1bcf13 docs: update build_api_key 2024-04-29 15:11:12 +08:00
pycook
9ead4e7d8d chore: release v2.4.4 2024-04-29 14:44:33 +08:00
pycook
994a28dd25 feat(ui): baseline rollback (#502) 2024-04-29 10:10:07 +08:00
simontigers
74b587e46c Merge pull request #501 from simontigers/common_decorator_perms
fix: role base app perm
2024-04-29 09:27:36 +08:00
hu.sima
091cd882bd fix: role base app perm 2024-04-29 09:26:23 +08:00
simontigers
73093db467 Merge pull request #500 from simontigers/common_decorator_perms
fix(api): decorator_perms_role_required
2024-04-28 19:43:22 +08:00
hu.sima
66e268ce68 fix(api): decorator_perms_role_required 2024-04-28 19:41:50 +08:00
simontigers
a41d1a5e97 Merge pull request #499 from simontigers/common_decorator_perms
feat(api): role perm
2024-04-28 19:22:43 +08:00
hu.sima
b4b728fe28 feat(api): role perm 2024-04-28 19:22:10 +08:00
pycook
d16462d8b7 feat(api): ci baseline rollback (#498) 2024-04-28 19:19:14 +08:00
kdyq007
de7d98c0b4 feat(api): Add sorting function to ci list attribute (#495)
Co-authored-by: sherlock <sherlock@gmail.com>
2024-04-27 09:20:24 +08:00
pycook
51332c7236 feat(ui): CI change logs related itsm 2024-04-24 20:09:59 +08:00
dagongren
bf1076fe4a feat:update cs && update style (#488) 2024-04-23 12:20:27 +08:00
dagongren
3454a98cfb fix(cmdb-ui):service tree search (#487) 2024-04-19 13:32:12 +08:00
dagongren
506dcbb40e fix(cmdb-ui):fix service tree change table page (#486) 2024-04-19 11:46:51 +08:00
dagongren
5ac4517187 style (#482) 2024-04-18 10:49:39 +08:00
pycook
761e98884b chore: add volumes cmdb_cache-data in docker-compose 2024-04-18 10:02:57 +08:00
pycook
073654624e fix(api): commands cmdb-init-cache 2024-04-17 21:37:18 +08:00
dagongren
df54244ff1 fix(cmdb-ui):service tree key (#480) 2024-04-17 20:42:16 +08:00
pycook
27e9919198 chore: release v2.4.3 2024-04-17 19:35:35 +08:00
dagongren
dc8b1a5de2 feat(cmdb-ui):citype show attr && service tree search (#479) 2024-04-17 17:59:21 +08:00
pycook
d8a7728f1d feat(api): custom attribute display (#478) 2024-04-17 17:50:46 +08:00
simontigers
82881965fb Merge pull request #474 from simontigers/common_check_new_columns
Common check new columns
2024-04-16 15:35:15 +08:00
hu.sima
1bb62022f1 fix(api): check new column support enum change 2024-04-16 15:34:03 +08:00
hu.sima
ed445a8d82 fix(api): secrets_shares Import ERROR 2024-04-16 15:33:36 +08:00
pycook
3626b1a97e feat(api): service tree search by keywords (#471) 2024-04-15 20:04:56 +08:00
loveiwei
32529fba9b fix: support sealing and unsealing secret in multiple process(more than one workers started by gunicorn) (#469)
* fix: 解决在麒麟系统上使用docker安装时使用celery -D启动 celery 可能出现的问题

* fix: 解决在麒麟系统上使用docker安装时使用celery -D启动 celery 可能出现的问题

* fix: NoneType happend while unsealing the secret funtion, cancel the address check while unseal and seal

* fix: unseal secret function

* fix: remove depens_on in docker-compose

* fix: support sealing and unsealing secret in multiple process(more than one workers started by gunicorn)
2024-04-15 18:08:47 +08:00
dagongren
a042b4fe39 fix(cmdb-ui):ci detail relation repeatly ciid (#468) 2024-04-15 13:50:50 +08:00
dagongren
a0631414dc style: global static.less (#467) 2024-04-12 15:18:52 +08:00
pycook
5266cb5b88 release: 2.4.2 2024-04-03 15:55:13 +08:00
dagongren
c7d4bec988 feat(cmdb-ui): attributes relation (#463) 2024-04-03 15:27:54 +08:00
pycook
099ddd6ca9 feat(api): rebuild relation by attribute (#462) 2024-04-03 15:13:43 +08:00
pycook
bd813174b1 feat(api): build relation by attributes (#461) 2024-04-02 09:19:51 +08:00
dagongren
0a43680d6e feat:add icons (#460) 2024-04-01 17:37:00 +08:00
dagongren
976c6cfe91 fix:topmenu shake & change logo (#459) 2024-04-01 15:11:24 +08:00
pycook
cf594f04ba fix(api): import CIType 2024-03-29 15:50:07 +08:00
ivonGwy
4232094aed fix: discover scripts (#458)
Co-authored-by: wang-liang0615 <dhuwl0615@163.com>
2024-03-29 15:48:46 +08:00
dagongren
d08827d086 style and 文案变更 (#457) 2024-03-29 15:02:18 +08:00
pycook
d25ae532cd fix(api): CIType template import 2024-03-29 14:20:56 +08:00
pycook
9fbb6ee64d docs: docker-compose changed to docker compose 2024-03-29 13:27:23 +08:00
pycook
b62f0e96fd Merge branch 'master' of github.com:veops/cmdb 2024-03-29 13:14:00 +08:00
pycook
c1bcd0ce45 fix(acl): del resource 2024-03-29 13:13:38 +08:00
dagongren
c8b55c34eb i18n (#456) 2024-03-29 13:11:52 +08:00
pycook
4b5906770f release: v2.4.1 2024-03-29 12:47:23 +08:00
dagongren
4188ac7252 i18n (#455) 2024-03-29 12:23:23 +08:00
dagongren
2efbc6474a style && service tree define (#454) 2024-03-29 11:53:43 +08:00
pycook
03eac0c4d2 pref(api): error tips for out of range value (#453) 2024-03-29 11:46:50 +08:00
dagongren
2a861250eb fix:icon/filter/router...and some bugs (#451) 2024-03-29 10:50:14 +08:00
dagongren
8fc19d8b7c icon font && opsTable (#450) 2024-03-28 20:59:32 +08:00
dagongren
430d2ff6d0 fix:cmdbgrant (#449) 2024-03-28 20:16:47 +08:00
dagongren
2517009d70 fix:Login (#448) 2024-03-28 19:56:54 +08:00
dagongren
67da360d80 Dev UI 240328 (#447)
* feat:ui 全面升级

* feat:ui全面升级
2024-03-28 19:53:54 +08:00
dagongren
24c56fb259 feat:ui 全面升级 (#446)
* feat:ui 全面升级

* feat:ui全面升级
2024-03-28 19:47:46 +08:00
pycook
37d5da65de Dev api 240328 (#445)
* feat(api): login api supports parameter auth_with_ldap

* fix(api): transfer attribute
2024-03-28 19:12:47 +08:00
dagongren
2224ebd533 feat:ui 全面升级 (#444) 2024-03-28 18:38:15 +08:00
pycook
bf6331d215 fix(api): batch import ci relation 2024-03-26 20:38:39 +08:00
pycook
b18b90ab4e fix(api): import CIType
fix(api): import CIType
2024-03-26 16:53:10 +08:00
pycook
702e17a7a4 fix(api): revoke service tree node permissions
fix(api): revoke service tree node permissions
2024-03-26 12:05:22 +08:00
pycook
a7586aa140 feat(api): support service tree editing (#437) 2024-03-26 10:58:11 +08:00
simontigers
ad3f96431c Merge pull request #431 from simontigers/common_employee_edit_department_in_acl
fix(api): common_employee_edit department in acl role
2024-03-25 11:46:30 +08:00
hu.sima
1515820713 fix(api): common_employee_edit department in acl role 2024-03-25 11:46:04 +08:00
simontigers
7728b57878 Merge pull request #430 from simontigers/common_file_ext_check
fix(api): check file ext with magic
2024-03-25 11:17:36 +08:00
hu.sima
a419eefd72 fix(api): check file ext with magic 2024-03-25 11:16:04 +08:00
simontigers
a44e5f6cf1 Merge pull request #429 from simontigers/common_check_new_columns
fix(api): common check new columns
2024-03-22 17:52:19 +08:00
simontigers
7d46e92c2d fix(api): common check new columns 2024-03-22 16:48:16 +08:00
pycook
4117cf87ec Merge branch 'master' of github.com:veops/cmdb 2024-03-20 11:56:49 +08:00
pycook
9e0fe0b818 fix: custom dashboard 2024-03-20 11:56:39 +08:00
dagongren
2a8f1ab9a4 style:update global.less (#426) 2024-03-19 10:01:38 +08:00
pycook
c0fe99b8c7 release: 2.3.13 2024-03-18 20:35:51 +08:00
dagongren
42feb4b862 feat(cmdb-ui):service tree grant (#425) 2024-03-18 19:59:16 +08:00
pycook
482d34993b Dev api 0308 (#424)
* feat(api): grant by node in relation view

* fix(api): When removing attributes, remove the unique constraint

* feat(api): grant by service tree
2024-03-18 19:57:25 +08:00
simontigers
7ff309b8b8 fix(api): edit employee depart with rid=0 (#420) 2024-03-12 17:46:50 +08:00
rustrover
98eb47d44f fix: some typos (#415)
Signed-off-by: gcmutator <329964069@qq.com>
Co-authored-by: gcmutator <329964069@qq.com>
2024-03-11 15:04:38 +08:00
pycook
9ab0f624ef fix(api): remove ACL resources when deleting CIType (#414) 2024-03-08 16:31:03 +08:00
pycook
3f3eda8b3c fix(api): issule #412, unique value restrictions (#413) 2024-03-05 16:21:27 +08:00
pycook
f788adc8cf feat(api): multi-id search (#411)
_id:(id1;id2)
2024-03-04 15:15:34 +08:00
simontigers
693ae4ff05 fix: deploy init common (#407) 2024-03-01 17:21:32 +08:00
pycook
a1a9d99eb4 release: v2.3.12 2024-03-01 17:04:38 +08:00
dagongren
e045e0fb43 fix(cmdb-ui):to lowercase (#406) 2024-03-01 13:52:30 +08:00
pycook
09376dbd2b feat(api): CIType inheritance (#405) 2024-03-01 13:51:13 +08:00
dagongren
7fda5a1e7b feat(cmdb-ui):ci type inherit (#404) 2024-03-01 13:39:20 +08:00
dagongren
113b84763f feat:ci detail share (#403) 2024-02-27 16:13:28 +08:00
dagongren
190170acad fix(cmdb-ui):triggers webhook headers (#402) 2024-02-26 13:46:40 +08:00
pycook
513d2af4b8 feat(api): Remove many-to-many restrictions (#401) 2024-02-26 10:17:53 +08:00
pycook
4588bd8996 fix(api): db-setup commands (#399)
fix(api): db-setup commands
2024-02-23 11:05:11 +08:00
dagongren
082da5fade fix(cmdb-ui):resource search common attrs (#397) 2024-02-22 16:19:12 +08:00
pycook
013b116eb5 feat(acl): login channel add ssh options (#396) 2024-02-21 18:10:44 +08:00
simontigers
208d29165b fix: grant common perm after create new employee (#394) 2024-02-04 13:48:02 +08:00
dagongren
d510330cde fix(cmdb-ui):fix multiple default value (#395) 2024-02-04 11:49:44 +08:00
wang-liang0615
ea4f0fc2a5 fix(ui):login email-》username (#393) 2024-01-31 15:52:27 +08:00
pycook
9bcdaacdc4 docs: update init sql
docs: update init sql
2024-01-26 13:57:36 +08:00
pycook
5045581ddf feat(api): Auto-increment id can be used as primary key (#391) 2024-01-26 13:12:17 +08:00
simontigers
232913172c fix: change common_setting task queue (#390) 2024-01-25 17:39:52 +08:00
pycook
157e1809ed release: 2.3.11 2024-01-13 15:06:51 +08:00
pycook
ab8ccf7d1b ui: lint 2024-01-12 18:21:47 +08:00
wang-liang0615
ae1f0f6b4f lint regexSelect (#382) 2024-01-12 18:09:03 +08:00
wang-liang0615
ff47e0ade6 feat:citype regex check & pref:edit is_list (#380) 2024-01-12 17:09:44 +08:00
pycook
0dd272fb04 feat(api): password supports regular check 2024-01-12 16:56:10 +08:00
pycook
6bc5c1516d feat(api): Attributes support regular check (#379)
feat(api): Attributes support regular check
2024-01-12 13:05:37 +08:00
wang-liang0615
4a4b9e6ef0 feat(cmdb-ui):preference citype order (#378) 2024-01-12 11:14:53 +08:00
pycook
6e3f9478b3 Dev api 240111 (#377)
* feat(api): My subscription supports CIType sorting

* feat(api): db change
2024-01-11 18:01:37 +08:00
pycook
691051c254 perf(api): /api/v0.1/ci/adc/statistics
perf(api): /api/v0.1/ci/adc/statistics
2024-01-11 10:10:01 +08:00
pycook
8f066e95a6 fix(api): grant by attr (#373) 2024-01-10 16:52:27 +08:00
pycook
75bca39bf6 fix(api): commands add-user 2024-01-10 11:56:39 +08:00
pycook
3360e4d0fe feat(db): set variable sql_mode
feat(db): set variable sql_mode
2024-01-10 10:28:15 +08:00
wang-liang0615
521fcd0ba2 fix(ui):some fix (#370)
* pref(ui):some bugfix & some style

* fix(ui):some fix
2024-01-10 09:46:02 +08:00
wang-liang0615
fc113425cb pref(ui):some bugfix & some style (#369) 2024-01-09 14:48:13 +08:00
pycook
81a76a9632 fix(api): cmdb-init-acl commands (#368) 2024-01-09 10:04:53 +08:00
wang-liang0615
9ec105ca37 fix(ui):logout (#365) 2024-01-04 16:12:20 +08:00
wang-liang0615
1e1c92a3ef refactor(ui):extract pager components (#364) 2024-01-04 13:41:07 +08:00
pycook
0b3cad8215 feat: update docker-compose 2024-01-04 10:26:47 +08:00
pycook
ed41a72900 release: v2.3.10 2024-01-03 19:23:42 +08:00
wang-liang0615
27854d4e0a fix(acl-ui):operation history (#363) 2024-01-03 17:41:42 +08:00
wang-liang0615
0372459f9d fix(ui) (#362) 2024-01-03 17:09:25 +08:00
simontigers
f56378e2b5 fix(api): common edit department return (#359)
fix(api): common edit department return (#359)
2024-01-03 16:42:14 +08:00
wang-liang0615
00b276d5e7 format(ui) (#361) 2024-01-03 16:35:50 +08:00
wang-liang0615
3faefbe8d3 feat(ui):i18n (#360)
* feat(ui):i18n

* i18n

* feat(ui):i18n
2024-01-03 16:03:15 +08:00
simontigers
4261f6fb57 fix(api): common department allow parent (#358)
* fix(api): common department edit method

* fix(api): common department edit method

* fix(api): common department allow parent
2024-01-03 15:19:08 +08:00
wang-liang0615
c98199e98e feat(ui):i18n (#357)
* feat(ui):i18n

* i18n
2024-01-03 14:58:47 +08:00
simontigers
82ea1ddc79 fix(api): common department edit method (#356)
* fix(api): common department edit method

* fix(api): common department edit method
2024-01-03 14:31:54 +08:00
simontigers
995e581315 fix(api): common department edit method (#355) 2024-01-03 14:26:40 +08:00
pycook
ec8f626b8f docs: update install (#354) 2024-01-03 13:58:35 +08:00
wang-liang0615
ec884c92e1 feat(ui):i18n (#352) 2024-01-03 13:29:38 +08:00
simontigers
9ee2776bdd fix(api): common i18n wide (#351)
* fix(api): common_i18n wide

* fix(api): department i18n
2024-01-03 13:26:58 +08:00
simontigers
7186bdac9c fix(api): common_i18n wide (#350) 2024-01-03 12:29:49 +08:00
wang-liang0615
0a9964375a feat(ui):add packages (#349) 2024-01-03 09:42:32 +08:00
wang-liang0615
bff45d00b6 i18n (#348) 2024-01-02 18:04:53 +08:00
wang-liang0615
e429ad59ff feat(ui):i18n (#347)
* feat(acl-ui):i18n

* feat(base-ui):i18n

* feat(cmdb-ui):i18n
2024-01-02 17:53:07 +08:00
pycook
ace160ae19 Dev api 231229 (#345)
* fix(api): predefined value for float

* feat(api): update public clouds config

* feat(api): commands add-user support is_admin
2023-12-29 13:44:23 +08:00
wang-liang0615
7036fe023c style(ui):global.less (#344) 2023-12-28 11:00:00 +08:00
wang-liang0615
341f5dba53 fix(cmdb-ui):model relation table (#343) 2023-12-27 13:24:01 +08:00
wang-liang0615
5883c1616e feat:Batch import and download templates support predefined values (#342) 2023-12-27 13:18:05 +08:00
simontigers
bee0a3a3e5 feat(api): common i18n (#340) 2023-12-26 10:06:15 +08:00
pycook
7a79a8bbf7 feat(api): i18n
feat(api): i18n
2023-12-25 21:51:44 +08:00
pycook
100a889cb8 fix(api): CI revoke permission (#337) 2023-12-25 12:15:20 +08:00
kdyq007
b093569453 [更新] 修复 LDAP 登录失败的问题 (#336)
Co-authored-by: sherlock <sherlock@gmail.com>
2023-12-25 09:36:31 +08:00
pycook
3919dfdfbb release: 2.3.9 2023-12-23 12:51:09 +08:00
pycook
ef85ba2542 feat(api): update cmdb-init-acl commands (#335) 2023-12-23 12:44:01 +08:00
pycook
3d5c2ec5bc Merge branch 'master' of github.com:veops/cmdb 2023-12-23 12:31:52 +08:00
pycook
c143d6ae5b fix(api): role grant 2023-12-23 12:30:52 +08:00
simontigers
10b273ee81 feat(api): add update_last_login_by_uid (#333) 2023-12-22 18:43:20 +08:00
wang-liang0615
855cb91b31 feat(cmdb-ui):ci type import&export,pref(cmdb-ui):download ci xlsx name, pref(cmdb-ui):ci detail history merge row method (#331)
* pref(cmdb-ui):download ci xlsx name

* pref(cmdb-ui):ci detail history merge row method

* feat(cmdb-ui):ci type import&export
2023-12-22 15:42:20 +08:00
pycook
ffae57642c fix(api): ci relation search
fix(api): ci relation search
2023-12-22 15:35:02 +08:00
simontigers
9ed1108c20 feat(api): add get_file_binary_str and save (#329) 2023-12-22 15:33:05 +08:00
simontigers
72b2f8b6de fix(api): refresh rid after create and import employee (#328) 2023-12-22 15:24:48 +08:00
pycook
20f3e917fe pref(api): import and export of CIType templates
pref(api): import and export of CIType templates
2023-12-22 14:32:03 +08:00
pycook
c430515377 fix(api): add CI (#326) 2023-12-22 11:19:16 +08:00
simontigers
a6e2aca281 fix(api): svg upload (#321) 2023-12-21 18:58:35 +08:00
wang-liang0615
18313b7bd1 fix(acl_ui):permission (#325) 2023-12-21 17:22:49 +08:00
wang-liang0615
dbc44a8ad6 fix(ui):common double menu (#324) 2023-12-21 14:59:39 +08:00
wang-liang0615
f143e30cf5 feat(ui):批量导入模型根据create权限过滤&&模型配置页面权限 (#323) 2023-12-21 14:23:38 +08:00
wang-liang0615
4beece5a6e fix:open triggerForm from attributeCard (#322) 2023-12-21 14:18:00 +08:00
pycook
920295d955 feat: Fixed db volume name 2023-12-21 10:26:51 +08:00
pycook
6eb8ae1dac fix(api): CAS authentication 2023-12-20 12:10:00 +08:00
wang-liang0615
f1f86ce25a feat(ui):api_host annotation (#320) 2023-12-19 14:22:40 +08:00
pycook
090007487d fix(api): oauth2.0 authentication 2023-12-19 13:07:21 +08:00
wang-liang0615
aa98a304c1 feat:add auth common api_host (#319) 2023-12-19 11:23:27 +08:00
pycook
fe22e363b4 fix(api): ldap authentication 2023-12-19 00:16:56 +08:00
pycook
2d2fb6e1d6 release: v2.3.8 2023-12-18 20:08:19 +08:00
pycook
9894c77300 feat(ui): lint 2023-12-18 19:25:22 +08:00
simontigers
4ea947f741 fix: auth config (#318) 2023-12-18 18:27:06 +08:00
simontigers
e5ab2c2573 fix: auth config (#317) 2023-12-18 16:52:24 +08:00
wang-liang0615
5581fa8d0f lint(ui) (#316) 2023-12-18 16:41:21 +08:00
wang-liang0615
76c939fe5c fix(ui):401 redirect && feat(ui):add auth ldap test (#315) 2023-12-18 16:30:02 +08:00
wang-liang0615
6e9ce08e2c pref(cmdb-ui):change adt key & add adt alias (#314) 2023-12-18 16:07:52 +08:00
pycook
092a8b9b92 pref(api): A CIType allows repeated binding of auto-discovery rules (#313) 2023-12-16 17:56:14 +08:00
wang-liang0615
b45fd0cbbb fix:is_list edit bug (#312)
* feat(ui):auth setting

* fix:is_list edit bug
2023-12-15 13:19:55 +08:00
simontigers
5320ecfd62 fix(api): common_data (#311) 2023-12-15 10:56:04 +08:00
wang-liang0615
1d253d7ad3 feat(ui):auth setting (#310) 2023-12-15 10:33:38 +08:00
pycook
73d53f0440 pref(api): authentication and login log (#308)
* pref(api): authentication and login log

* feat(api): ldap and OAuth2.0
2023-12-14 19:53:08 +08:00
simontigers
d4a37af183 feat(api): auth config api (#309) 2023-12-14 19:39:21 +08:00
wang-liang0615
e03849b054 fix(cmdb-ui):batch upload cancel bug && download error (#306) 2023-12-13 14:50:53 +08:00
simontigers
faed3fe6a2 fix(api): get_employee_notice check data None (#305)
* fix(api): get_employee_notice check data None

* fix(api): remove path when save messager url
2023-12-13 09:45:42 +08:00
pycook
21c9d9accd feat(api): support OAuth2.0 and OIDC authentication, it has been tested with casdoor
feat(api): support OAuth2.0 and OIDC authentication, it has been tested with casdoor
2023-12-12 20:29:57 +08:00
wang-liang0615
a06599ce33 pref(cmdb-ui):batch upload for date type (#301) 2023-12-12 14:53:12 +08:00
pycook
2b69217136 fix(api): time data format
fix(api): time data format
2023-12-12 14:37:21 +08:00
wang-liang0615
03a3b8b169 Revert "pref(cmdb-ui):batch upload for date type (#298)" (#299)
This reverts commit cd319421d5.
2023-12-12 13:51:19 +08:00
wang-liang0615
cd319421d5 pref(cmdb-ui):batch upload for date type (#298)
* fix(cmdb-ui):set localstorage '' after unsubscribe ci

* pref(cmdb-ui):batch upload for date type
2023-12-12 13:38:08 +08:00
pycook
c918d54ea5 perf(api): ci delete (#297) 2023-12-12 11:09:32 +08:00
wang-liang0615
cf0ad7bad6 fix(cmdb-ui):set localstorage '' after unsubscribe ci (#296) 2023-12-12 09:38:50 +08:00
pycook
e0c8263542 feat(api): cas is compatible with casdoor
feat(api): cas is compatible with casdoor
2023-12-11 20:58:18 +08:00
pycook
275e8b15f3 Dev api 231211 (#294)
* fix(api): cas authentication

* feat(api): add lz4 package
2023-12-11 19:30:09 +08:00
simontigers
6ff942c107 feat(api): upload file save db (#292) 2023-12-11 18:22:33 +08:00
gmailnovo
d3c87ee500 feat: Handle '/dev/stdout' in Logger Configuration 2023-12-07 11:27:45 +08:00
simontigers
a4f65e7fc6 fix(api): Common create employee (#287)
* fix(api): add add_from arg in create employee

* fix(api): add check in acl call common after add user
2023-12-06 17:12:37 +08:00
wang-liang0615
10527bf9b8 pref(cmdb-ui):ci upload&delete concurrent 6 (#286) 2023-12-06 14:33:25 +08:00
pycook
0414121c27 docs: update local install 2023-11-30 16:18:33 +08:00
simontigers
edde467c87 fix(api): common_data delele (#282) 2023-11-30 13:03:09 +08:00
pycook
d525e1ec54 feat(api): only the role cmdb_admin can modify the CIType group (#280) 2023-11-29 17:40:12 +08:00
pycook
91c49b690f fix(api): get relation history 2023-11-28 20:37:36 +08:00
pycook
05453becf9 release: 2.3.7 2023-11-24 14:53:53 +08:00
pycook
5fe27f8678 feat(api): issue #212 (#279) 2023-11-24 10:26:48 +08:00
wang-liang0615
0924b8846f feat(cmdb-ui):多对多关系&&仪表盘色卡调整 (#271) 2023-11-24 10:25:56 +08:00
loveiwei
62a669159a doc: change content in readme_en (#278) 2023-11-23 20:10:25 +08:00
loveiwei
2933bf1efa feature: add a new management script, such as install,start,pause,del… (#277)
* feature: add a new management script, such as install,start,pause,delete,uninstall

* doc: add install.sh method in readme

* doc: add install.sh method in readme, change install.sh for macos support

* doc: add install.sh method in readme

* doc: add install.sh method in readme
2023-11-23 17:45:53 +08:00
loveiwei
981f8b0145 Fix deploy 1700028675 (#272)
* fix: Solving the timezone issue in Redis, as well as the problem of MySQL logs always being in UTC timezone.

* fix: change the config path of slow_log into /tmp directory in mysqld.cnf file
2023-11-22 18:34:58 +08:00
pycook
213bda671c docs: local install (#270) 2023-11-16 20:54:08 +08:00
loveiwei
837aabfe77 fix: Solving the timezone issue in Redis, as well as the problem of MySQL logs always being in UTC timezone. (#268) 2023-11-15 20:48:52 +08:00
wang-liang0615
cc599d414a feat(acl-ui):resources table resizable (#267) 2023-11-14 09:37:45 +08:00
pycook
01fcd7f80e Dev api 231108 (#264)
* perf(api): commands add-user

* feat(api): add commands cmdb-agent-init
2023-11-09 11:32:54 +08:00
pycook
af254ddbeb Dev api 1107 (#263)
feat: update cmdb.sql
2023-11-08 16:11:36 +08:00
wang-liang0615
422e89c6c6 ui package update (#261)
* update(cmdb-ui):update packages && delete yarn.lock

* update:gitignore
2023-11-08 16:06:48 +08:00
wang-liang0615
c2c11b38ed Revert "Dev UI 231108 (#256)" (#260)
This reverts commit 45d3f57228.
2023-11-08 14:43:37 +08:00
wang-liang0615
45d3f57228 Dev UI 231108 (#256)
* pref(cmdb-ui):update packages

* pref(cmdb-ui):update packages
2023-11-08 14:32:17 +08:00
pycook
c711c3d3fd feat(api): encrypting webhook configurations (#255) 2023-11-07 17:15:24 +08:00
pycook
2ae4aeee67 fix(api): Code scanning alerts (#254) 2023-11-06 14:27:30 +08:00
pycook
46238b8b51 Dev api 1103 (#252)
feat(api): update requirements
2023-11-06 13:24:08 +08:00
dependabot[bot]
b1528ec511 build(deps): bump browserify-sign from 4.2.1 to 4.2.2 in /cmdb-ui (#241)
Bumps [browserify-sign](https://githubfast.com/crypto-browserify/browserify-sign) from 4.2.1 to 4.2.2.
- [Changelog](https://githubfast.com/browserify/browserify-sign/blob/main/CHANGELOG.md)
- [Commits](https://githubfast.com/crypto-browserify/browserify-sign/compare/v4.2.1...v4.2.2)

---
updated-dependencies:
- dependency-name: browserify-sign
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-06 13:13:29 +08:00
pycook
8ef4edb7d2 fix(api): search sort misses cache (#251) 2023-11-03 12:01:44 +08:00
pycook
0172206c7c fix(api commands): cmdb-password-data-migrate (#249) 2023-11-02 20:43:39 +08:00
simontigers
9d5bdf1b0d style(common-setting): clean warning (#246) 2023-10-30 17:35:34 +08:00
pycook
c0726b228d fix(api): secrets 2023-10-30 17:23:42 +08:00
pycook
5b314aa907 feat: add inner password storage and optimize flask command about inner cmdb (#248)
Co-authored-by: fxiang21 <fxiang21@126.com>
2023-10-30 16:48:53 +08:00
wang-liang0615
c9f0de9838 Dev UI 231030 (#247)
* config(ui):useEncryption default false

* fix(cmdb-ui):ident 4

* fix(cmdb-ui):relation views
2023-10-30 12:38:05 +08:00
pycook
2db41dd992 Dev api password (#244)
* fix: delete CI password data

* fix(api): update CI password to flush cache
2023-10-29 11:42:07 +08:00
pycook
89e492c1f3 fix: delete CI password data (#243) 2023-10-29 10:53:29 +08:00
pycook
1aeb9a2702 fix(cmdb-ui): CI update password 2023-10-28 23:07:01 +08:00
pycook
b090a88b76 release: v2.3.6 2023-10-28 17:51:13 +08:00
pycook
d5c479f7e5 feat(cmdb-api): ci password 2023-10-28 16:55:02 +08:00
pycook
b342258e75 feat(cmdb-api): CI password data store (#242)
* add secrets,for test

* feat: vault SDK (#238)

* feat: vault SDK

* docs: i18n

* perf(vault): format code

* feat(secrets): support vault

* feat: add inner password storage

* feat: secrets

* feat: add inner password storage

* feat: add secrets feature

* perf(secrets): review

---------

Co-authored-by: fxiang21 <fxiang21@126.com>
Co-authored-by: Mimo <osatmnzn@gmail.com>
2023-10-28 16:19:00 +08:00
wang-liang0615
3d716eff3e feat:预定义值支持脚本&&密码存储&&一些bugfix (#239) 2023-10-27 11:10:43 +08:00
ivonGwy
9791a184e3 Doc (#235)
* change assignees
2023-10-25 16:00:04 +08:00
ivonGwy
142b8c95c5 Doc (#234)
* final template
2023-10-25 15:52:12 +08:00
ivonGwy
363b89011f Doc (#232)
* fix bugs
2023-10-25 14:54:23 +08:00
ivonGwy
321372fa88 Update issue templates 2023-10-25 14:46:44 +08:00
ivonGwy
fc27dc6e64 Doc (#231)
* add issue template
2023-10-25 14:36:36 +08:00
ivonGwy
8ad98f4ba4 Update issue templates 2023-10-25 14:06:24 +08:00
ivonGwy
9b050fa7fa Update issue templates 2023-10-25 14:04:53 +08:00
pycook
a30826827a fix(api): add ci (#230) 2023-10-25 13:51:29 +08:00
ivonGwy
691b50aec4 Doc (#229)
* change reandme
2023-10-25 13:19:30 +08:00
kdyq007
55ca2d5eb5 关闭前端密码加密;加强 ldap 用户验证 (#216)
* [更新] python-ldap 更新到 ldap3

* [更新] 关闭前端密码加密;加强 ldap 用户验证

* Update app.js

---------

Co-authored-by: sherlock <sherlock@gmail.com>
Co-authored-by: pycook <pycook@126.com>
2023-10-24 19:47:46 +08:00
pycook
779cd1ea9e feat: Predefined values support executing scripts (#227) 2023-10-24 19:32:43 +08:00
simontigers
7d53180a2f fix: add_employee_from_acl (#225) 2023-10-24 14:20:40 +08:00
wang-liang0615
b6c41c00dd fix:关系视图删除关系接口传参修改 (#224)
* fix:acl新增用户展示异常问题

* fix:关系视图删除关系接口传参修改
2023-10-24 06:04:21 +08:00
wang-liang0615
fff5679f6e fix:acl新增用户展示异常问题 (#223) 2023-10-24 05:59:08 +08:00
pycook
7556dfe56b feat: add cryptography to requirements 2023-10-23 14:37:01 +08:00
pycook
44b6f2b2ad fix: acl cache 2023-10-23 13:57:06 +08:00
Evan Sung
f5607d96f3 fix(common): fix 'ACLManager' object has no attribute 'create_app' (#217) 2023-10-21 11:38:19 +08:00
Evan Sung
2e85a9971b fix(ci_cache): ci cache async args (#215) 2023-10-20 12:05:19 +08:00
kdyq007
8d177266dc feat: python-ldap 更新到 ldap3 (#214)
Co-authored-by: sherlock <sherlock@gmail.com>
2023-10-20 09:36:38 +08:00
pycook
42d870ea4e Dev api 20231019 (#210)
* fix(acl): get resources

* fix(celery worker): db server has gone away
2023-10-19 11:51:34 +08:00
wang-liang0615
424cad2130 fix:ci relation add type filter (#208) 2023-10-18 14:06:28 +08:00
pycook
430308a679 fix: ci relation statistics 2023-10-18 13:35:01 +08:00
pycook
cc7570a4b2 docs: api doc 2023-10-17 12:06:37 +08:00
wang-liang0615
413c0dfdfe feat:webhook body 支持非json (#203) 2023-10-17 10:44:38 +08:00
Evan Sung
f8fee771c4 Feature db migrate 20231013 (#202)
* feat(db): support flask migrate

* minor

---------

Co-authored-by: s01249 <songbing@smyfinancial.com>
2023-10-13 16:24:49 +08:00
Evan Sung
3d05cef7cd feat(db): support flask migrate (#201)
Co-authored-by: s01249 <songbing@smyfinancial.com>
2023-10-13 15:55:26 +08:00
simontigers
30477f736e fix: common perms (#200) 2023-10-12 16:02:35 +08:00
wang-liang0615
0cd5c0277b pref:批量上传&资源管理小优化 (#199) 2023-10-12 15:06:39 +08:00
ivonGwy
4fcddd1010 Merge pull request #197 from veops/doc
Doc
2023-10-11 14:34:05 +08:00
ivonGwy
b269ef894f change wechat pic size 2023-10-11 14:33:19 +08:00
ivonGwy
ea13432e4c change wechat pic 2023-10-11 14:28:49 +08:00
pycook
c8a68bd185 Dev api (#196)
* docs: update

* docs: README & Makefile
2023-10-11 13:40:15 +08:00
wang-liang0615
f9ee895f58 Dev UI 231009 (#195)
* pref:用户密钥非必填

* fix:chartColor undefined
2023-10-11 09:12:04 +08:00
pycook
59d9f2c79a docs: update (#194) 2023-10-10 16:53:30 +08:00
wang-liang0615
f629558d60 pref:用户密钥非必填 (#193) 2023-10-10 09:25:24 +08:00
pycook
4cbf70e756 release: v2.3.5 2023-10-09 20:55:30 +08:00
pycook
40b9bec122 feat: get messenger url from common setting 2023-10-09 20:25:27 +08:00
wang-liang0615
17b27d492a 前端更新 (#192)
* fix:add package

* fix:notice_info为null的情况

* fix:2 bugs

* feat:1.common增加通知配置 2.cmdb预定义值webhook&其他模型

* fix:json 不支持预定义值

* fix:json 不支持预定义值

* fix:删除代码
2023-10-09 19:52:19 +08:00
simontigers
dd8f66a3fa fix: init company structure resource (#191)
* fix: init company structure resource

* fix: notice_info null
2023-10-09 19:25:49 +08:00
wang-liang0615
d501436e3d 前端更新 (#189)
* fix:add package

* fix:notice_info为null的情况

* fix:2 bugs

* feat:1.common增加通知配置 2.cmdb预定义值webhook&其他模型

* fix:json 不支持预定义值

* fix:json 不支持预定义值
2023-10-09 17:43:34 +08:00
simontigers
8c6389b4f8 feat: notice_config access messenger (#190) 2023-10-09 17:32:20 +08:00
pycook
63ed73fa14 fix: delete user role 2023-10-09 15:40:18 +08:00
pycook
ef90506916 feat: The definition of attribute choice values supports webhook and other model attribute values. 2023-10-09 15:33:18 +08:00
wang-liang0615
c0541b7e50 Dev UI (#186)
* fix:add package

* fix:notice_info为null的情况

* fix:2 bugs
2023-09-28 09:45:11 +08:00
pycook
388134b38c release 2.3.4 2023-09-27 11:37:18 +08:00
simontigers
f57d640c5d feat: add api get_notice_by_ids (#184) 2023-09-27 09:54:30 +08:00
wang-liang0615
42c506ded8 前端更新 (#183)
* fix:add package

* fix:notice_info为null的情况
2023-09-27 09:18:33 +08:00
pycook
373346e71e feat: ci triggers 2023-09-26 21:18:34 +08:00
wang-liang0615
983cb4041c fix:add package (#182) 2023-09-26 21:12:10 +08:00
wang-liang0615
718fca57d2 Merge pull request #181 from veops/dev_ui
前端更新
2023-09-26 20:34:27 +08:00
wang-liang0615
2b1b0333ff Merge branch 'master' into dev_ui 2023-09-26 20:34:14 +08:00
wang-liang0615
9bb787d3f4 fix:topo图相同节点出现两次的bug 2023-09-26 20:13:12 +08:00
wang-liang0615
9f5979c1b1 feat:wangeditor 注册自定义组件 2023-09-26 20:07:00 +08:00
wang-liang0615
b58230fd56 delete:删除getwx 2023-09-26 20:04:38 +08:00
simontigers
8e6ce05c04 feat: common notice config (#180) 2023-09-26 19:44:20 +08:00
wang-liang0615
b8d93bc9eb feat: UI更新 触发器 (#179)
* feat:新增api&适配

* feat:触发器

* add packages & 注释代码

* feat: webhook tips
2023-09-26 18:25:04 +08:00
wang-liang0615
968ef93153 feat: webhook tips 2023-09-26 18:17:23 +08:00
wang-liang0615
e0a0113e69 add packages & 注释代码 2023-09-26 17:35:41 +08:00
wang-liang0615
7181f2879a feat:触发器 2023-09-26 17:01:31 +08:00
wang-liang0615
ff1626ff07 feat:新增api&适配 2023-09-26 16:26:25 +08:00
pycook
dca8781a03 fix: ci_cache 2023-09-25 15:46:07 +08:00
wang-liang0615
43c5d74661 Merge pull request #178 from veops/dev_ui
前端更新:仪表盘优化
2023-09-25 14:52:09 +08:00
wang-liang0615
b1d0bdb536 pref:仪表盘优化 2023-09-25 14:50:08 +08:00
wang-liang0615
2420631ed5 Merge branch 'master' of github.com:veops/cmdb into dev_ui 2023-09-25 14:43:34 +08:00
pycook
deabebb922 refactor: CI triggers 2023-09-22 17:39:54 +08:00
simontigers
cd451060e3 fix: icon svg support (#177) 2023-09-20 15:56:57 +08:00
pycook
82d4c4f961 fix date search 2023-09-18 18:15:02 +08:00
pycook
79f5dac661 fix dashboard compute 2023-09-18 13:04:50 +08:00
pycook
1a7c3fcd21 release v2.3.3 2023-09-15 17:57:39 +08:00
pycook
606be7f8e8 dashboard ui update 2023-09-15 17:36:10 +08:00
simontigers
7f66f7688b feat: init resource for backend (#176) 2023-09-15 15:30:30 +08:00
pycook
25ad2192fd enhance dashboard 2023-09-15 15:26:20 +08:00
pycook
881b8cc1fa cmdb-api/api/lib/resp_format.py 2023-09-12 20:01:30 +08:00
pycook
68905281f2 Detect circular dependencies when adding CIType relationships 2023-09-12 20:00:56 +08:00
wang-liang0615
3cb78f30d2 计算属性 触发计算 (#174) 2023-09-11 19:16:05 +08:00
pycook
ebce839eaa fix upload template and add /api/v0.1/attributes/<int:attr_id>/calc_computed_attribute 2023-09-11 19:15:31 +08:00
wang-liang0615
3170048ea9 Merge branch 'master' of github.com:veops/cmdb into dev_ui 2023-09-11 17:35:44 +08:00
wang-liang0615
976cac6742 计算属性 触发计算 2023-09-11 17:34:51 +08:00
pycook
99e5a932ae release 2.3.2 2023-09-07 13:44:51 +08:00
pycook
058585504f Merge pull request #172 from veops/dev_ui
新建ci及批量导入时,新建关系
2023-09-07 11:04:49 +08:00
wang-liang0615
6d50a13cf0 新建ci及批量导入时,新建关系 2023-09-07 10:25:18 +08:00
pycook
198ecad13a Merge branch 'master' of github.com:veops/cmdb 2023-09-07 10:12:55 +08:00
pycook
1660139b27 Add CI relationship when creating CI, the text value removes the escape 2023-09-07 10:12:42 +08:00
pycook
0155eb98a2 Merge pull request #171 from ronething/fix/makefile
optimize: makefile help
2023-09-05 20:34:16 +08:00
ashing
c0c05bca86 fix: review
Signed-off-by: ashing <axingfly@gmail.com>
2023-09-05 20:33:07 +08:00
ashing
32227d375a fix: review
Signed-off-by: ashing <axingfly@gmail.com>
2023-09-05 20:29:29 +08:00
ashing
eb9c20a295 fix: review
Signed-off-by: ashing <axingfly@gmail.com>
2023-09-05 20:21:20 +08:00
ashing
b826de4195 optimize: makefile help
Signed-off-by: ashing <axingfly@gmail.com>
2023-09-05 20:06:31 +08:00
pycook
c3b55c2850 Merge pull request #170 from ronething/feat/xx
feat: support docker deploy mysql and redis
2023-09-05 19:28:47 +08:00
ivonGwy
33313b6206 Merge pull request #169 from veops/doc
add document link
2023-09-05 15:41:52 +08:00
ivonGwy
00fbe15a19 add document link 2023-09-05 15:40:31 +08:00
ashing
9e5c926bfd feat: support docker deploy mysql and redis
Signed-off-by: ashing <axingfly@gmail.com>
2023-09-05 15:26:50 +08:00
wang-liang0615
f0467c3d3b Merge pull request #168 from veops/dev_ui
UI更新
2023-09-05 15:23:43 +08:00
wang-liang0615
6758b994f8 Merge branch 'master' of github.com:veops/cmdb into dev_ui 2023-09-05 15:22:18 +08:00
wang-liang0615
d8646f8e7c 模型关联 展示反向关系 2023-09-05 15:22:08 +08:00
pycook
0fa95aed36 Merge branch 'master' of github.com:veops/cmdb 2023-09-05 14:49:53 +08:00
pycook
d82356444a move Dockerfile to docs 2023-09-05 14:49:34 +08:00
wang-liang0615
b8fa68ec8d Merge pull request #167 from veops/dev_ui
sub menu color
2023-09-04 16:34:26 +08:00
wang-liang0615
78c542e71d sub menu color 2023-09-04 16:33:35 +08:00
wang-liang0615
6594863934 Merge pull request #166 from veops/dev_ui
ui更新
2023-09-04 13:15:27 +08:00
wang-liang0615
61e8c61fc6 Merge branch 'master' of github.com:veops/cmdb into dev_ui 2023-09-04 13:14:35 +08:00
wang-liang0615
ec9b5a0fa0 sidebar 2023-09-04 13:14:11 +08:00
pycook
3664994cc5 import format 2023-09-02 12:09:41 +08:00
pycook
366311e59b format 2023-09-01 18:07:44 +08:00
pycook
03f2ff912d fix delete choice values 2023-08-31 16:02:24 +08:00
pycook
ff9af51465 fix delete choice values 2023-08-31 15:18:15 +08:00
wang-liang0615
04fc588935 Merge pull request #165 from veops/dev_ui
proxy
2023-08-31 13:31:26 +08:00
wang-liang0615
f215e887c9 proxy 2023-08-31 13:28:15 +08:00
pycook
3e6955fc7a Merge pull request #162 from simontigers/cmdb_icon_manage
feat: add cmdb custom icon manage
2023-08-31 11:15:09 +08:00
pycook
b1cfb88308 Merge pull request #163 from veops/dev_ui
支持上传自定义图标
2023-08-31 11:14:42 +08:00
hu.sima
d1af0eba79 feat: add cmdb custom icon manage 2023-08-31 10:49:56 +08:00
wang-liang0615
f41873dc8c 支持上传自定义图标 2023-08-31 10:05:11 +08:00
pycook
63562007df fix update attribute 2023-08-30 13:34:10 +08:00
pycook
a99ecb9ea5 Merge branch 'master' of github.com:veops/cmdb 2023-08-29 14:49:21 +08:00
pycook
08f9bd9071 The default value of USE_ACL is set to True 2023-08-29 14:49:09 +08:00
pycook
17c851f354 Merge pull request #161 from simontigers/common_setting_format
fix: company info create
2023-08-29 11:01:25 +08:00
hu.sima
3382195a25 fix: company info create 2023-08-29 10:56:48 +08:00
pycook
cd70b16eb3 Merge branch 'master' of github.com:veops/cmdb 2023-08-25 11:01:24 +08:00
pycook
2a98c00d97 update ad_ci when deleting ci 2023-08-25 10:59:38 +08:00
wang-liang0615
5044af5490 Merge pull request #160 from veops/dev_ui
前端更新
2023-08-25 10:12:31 +08:00
wang-liang0615
320dc07676 fix 新增类型回车键发送两次请求 2023-08-25 10:11:09 +08:00
wang-liang0615
95d3b0233a fix 新增类型回车键发送两次请求 2023-08-25 10:08:04 +08:00
pycook
093466ef06 Merge pull request #158 from EvanSung/perf_20230824_optimize_ad_ci_relation
perf(ad_ci_relation): optimize ad_ci relation
2023-08-24 16:26:43 +08:00
EvanSung
ceacc0ab15 perf(ad_ci_relation): optimize ad_ci relation 2023-08-24 14:16:12 +08:00
pycook
cb3a9d9432 docker-compose add flask db-setup 2023-08-24 11:32:09 +08:00
pycook
23fd56cf28 vxe-table-plugin-export-xlsx==2.0.0 2023-08-24 11:06:28 +08:00
pycook
dd1c783919 add config CACHE_REDIS_PASSWORD and fix delete ci_type 2023-08-23 18:05:28 +08:00
pycook
8296e9a552 fix update ci 2023-08-22 11:34:40 +08:00
pycook
590565bdf0 Register api and commands with absolute paths 2023-08-21 20:08:23 +08:00
pycook
9e2bf3d1f0 fix merge conflict 2023-08-21 11:55:49 +08:00
pycook
7547b67805 fix g.user 2023-08-21 11:54:33 +08:00
pycook
4abe4d7e8f version: 2.3.1 2023-08-20 11:24:53 +08:00
pycook
0ebd52f3fd lint 2023-08-20 11:23:55 +08:00
pycook
4cc2e47f02 Merge pull request #157 from EvanSung/fix_20230817_guser_issue
fix(acl): g user issue
2023-08-17 22:12:45 +08:00
EvanSung
b16bfdfa03 fix(acl): g user issue 2023-08-17 18:40:45 +08:00
pycook
ece24080d5 fix MyJSONEncoder 2023-08-16 21:28:27 +08:00
pycook
a0ea8a193b Merge pull request #155 from veops/dev_ui
前端更新
2023-08-16 13:01:13 +08:00
pycook
d8e09d449e Merge branch 'master' of github.com:veops/cmdb 2023-08-16 13:00:44 +08:00
pycook
44c99e0f2e Delete user without soft delete 2023-08-16 13:00:30 +08:00
wang-liang0615
7110b4bcb3 Merge branch 'master' of github.com:veops/cmdb into dev_ui 2023-08-16 10:09:47 +08:00
wang-liang0615
3989859945 delete user 2023-08-16 10:09:25 +08:00
pycook
29ed4dd708 Merge pull request #154 from simontigers/common_setting_format
fix: init-import-user-from-acl
2023-08-15 20:47:36 +08:00
hu.sima
dbcbe33ba0 fix: init-import-user-from-acl 2023-08-15 20:45:28 +08:00
pycook
ed340a1c33 Merge pull request #153 from simontigers/common_setting_format
fix: import_user_from_acl
2023-08-15 20:25:09 +08:00
hu.sima
17ee7622b4 fix: import_user_from_acl 2023-08-15 20:19:45 +08:00
pycook
51877778bf Merge branch 'master' of github.com:veops/cmdb 2023-08-15 19:48:11 +08:00
pycook
1de8b492ea [update] delete roles, users, attributes 2023-08-15 19:47:59 +08:00
wang-liang0615
1fe38c4ec3 Merge pull request #152 from veops/dev_ui
前端更新
2023-08-15 19:47:03 +08:00
wang-liang0615
92bff08b84 属性库 2023-08-15 19:34:17 +08:00
wang-liang0615
37d0dacd2e 属性库 2023-08-15 19:26:49 +08:00
wang-liang0615
e22f45fd25 属性库 2023-08-15 19:21:09 +08:00
wang-liang0615
56ef0a055a 属性库 2023-08-15 19:10:26 +08:00
wang-liang0615
522991e23b 后台管理-模型关联 关系删除&&筛选 2023-08-15 15:02:46 +08:00
pycook
ff061d4d2e update gitattributes 2023-08-15 13:41:45 +08:00
wang-liang0615
5b84a704b1 Merge pull request #150 from EvanSung/optimize_20230810_acl_resource_fe
refactor(fe): reduce the width of resource mgt table
2023-08-10 19:32:49 +08:00
pycook
3ba83821f2 Merge pull request #148 from simontigers/common_setting_format
fix: default arg value
2023-08-10 19:31:18 +08:00
pycook
45b82aa52d Merge pull request #149 from veops/dev_ui
ui更新:password
2023-08-10 19:28:24 +08:00
EvanSung
c99790bda2 refactor(fe): reduce the width of resource mgt table 2023-08-10 19:23:41 +08:00
wang-liang0615
7272880062 Merge branch 'master' of github.com:veops/cmdb into dev_ui 2023-08-10 19:21:28 +08:00
wang-liang0615
8130dd92ab 增加密码明文传输 2023-08-10 19:21:10 +08:00
hu.sima
9ca2d38307 fix: default arg value 2023-08-10 19:05:56 +08:00
pycook
e61bbdbcb7 Merge pull request #147 from simontigers/common_setting_format
fix: remove useless
2023-08-10 19:01:25 +08:00
hu.sima
88960c3082 fix: remove useless 2023-08-10 18:55:32 +08:00
pycook
0aa433882e Merge pull request #146 from simontigers/common_setting_format
Common setting format
2023-08-10 18:23:24 +08:00
hu.sima
d4f5713e0a fix: remove unused column 2023-08-10 16:29:52 +08:00
hu.sima
790204ea28 style: format common setting 2023-08-10 15:30:01 +08:00
pycook
927ed393d3 Merge pull request #145 from EvanSung/optimize_20230810_auth_require
optimize(auth): auth request json
2023-08-10 11:24:23 +08:00
EvanSung
bf92b89a5f optimize(auth): auth request json 2023-08-10 10:43:59 +08:00
pycook
7ddaa143da fix celery config 2023-08-08 16:33:24 +08:00
pycook
157361cc7a Merge branch 'master' of github.com:veops/cmdb 2023-08-08 13:16:14 +08:00
pycook
dcf768376a upgrade celery 2023-08-08 13:16:07 +08:00
pycook
e339ad2c23 Merge pull request #144 from veops/dev_ui
UI更新:fix preferenceList=>attrList
2023-08-08 09:21:10 +08:00
wang-liang0615
8b10c5c72d Merge branch 'master' of github.com:veops/cmdb into dev_ui 2023-08-08 09:11:24 +08:00
wang-liang0615
bd13f6b744 fix preferenceList=>attrList 2023-08-08 09:11:03 +08:00
pycook
934d00e87d upgrade flask to 2.3.2 and replace g.user with current_user 2023-08-06 21:54:18 +08:00
pycook
9d421993a0 Merge pull request #138 from lovvvve/fix_ldap
fix ldap login
2023-08-04 11:31:58 +08:00
pycook
6751f673d5 Merge pull request #142 from veops/dev_ui
ci 批量更新和删除的异步处理
2023-08-04 09:27:55 +08:00
wang-liang0615
c420a2195a Merge branch 'master' of github.com:veops/cmdb into dev_ui 2023-08-03 16:54:47 +08:00
wang-liang0615
ec3a6e0b6e ci 批量更新和删除的异步处理 2023-08-03 16:54:27 +08:00
pycook
ab0a5399f7 Merge pull request #139 from EvanSung/fix-post-acltrigger-session-invalid
fix(trigger): session invalid issue
2023-08-02 19:33:05 +08:00
songbing01249
33e63c762f fix(trigger): session invalid issue 2023-08-02 18:22:42 +08:00
lovvvve
53b2a228ae fix ldap login 2023-08-01 11:27:29 +00:00
pycook
c687e7ad9b Merge pull request #134 from veops/dependabot/pip/cmdb-api/pillow-9.3.0
Bump pillow from 9.2.0 to 9.3.0 in /cmdb-api
2023-08-01 15:57:02 +08:00
pycook
ea3cdbd5f5 Merge pull request #135 from simontigers/remove_pandas
fix: remove pandas
2023-08-01 15:55:15 +08:00
hu.sima
1ebe74d966 fix: remove pandas 2023-08-01 15:32:44 +08:00
dependabot[bot]
1f935d25a2 Bump pillow from 9.2.0 to 9.3.0 in /cmdb-api
Bumps [pillow](https://github.com/python-pillow/Pillow) from 9.2.0 to 9.3.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/9.2.0...9.3.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-01 05:51:16 +00:00
pycook
14e4dbacac Merge branch 'master' of github.com:veops/cmdb 2023-08-01 13:47:34 +08:00
pycook
f7a9257e16 fix dependabot alerts 2023-08-01 13:47:11 +08:00
pycook
99562df6cd fix dependabot alerts 2023-08-01 13:46:47 +08:00
pycook
630013cb85 Merge pull request #130 from veops/dependabot/pip/cmdb-api/certifi-2023.7.22
Bump certifi from 2023.5.7 to 2023.7.22 in /cmdb-api
2023-08-01 13:14:25 +08:00
pycook
6351d3af64 Merge pull request #132 from veops/dev_ui
删除角色相关
2023-07-31 19:54:19 +08:00
wang-liang0615
2d96048828 删除角色相关 2023-07-31 19:52:06 +08:00
pycook
3e67bb94bc Merge branch 'master' of github.com:veops/cmdb 2023-07-31 18:39:46 +08:00
pycook
a26c9ff542 fix delete ci_type 2023-07-31 18:39:33 +08:00
pycook
252003f76d Merge pull request #131 from veops/dev_ui
前端acl
2023-07-28 18:03:36 +08:00
wang-liang0615
928149f2a0 common-setting 2023-07-27 15:47:13 +08:00
wang-liang0615
0283af812c acl 2023-07-27 15:30:27 +08:00
wang-liang0615
6ccb2e74e0 fix acl change page size 2023-07-27 15:08:25 +08:00
dependabot[bot]
5016464016 Bump certifi from 2023.5.7 to 2023.7.22 in /cmdb-api
Bumps [certifi](https://github.com/certifi/python-certifi) from 2023.5.7 to 2023.7.22.
- [Commits](https://github.com/certifi/python-certifi/compare/2023.05.07...2023.07.22)

---
updated-dependencies:
- dependency-name: certifi
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-25 23:35:30 +00:00
pycook
b787bd9b7b Merge pull request #129 from veops/dev_ui
前端更新
2023-07-25 18:19:47 +08:00
wang-liang0615
11f69d8679 Merge branch 'master' of github.com:veops/cmdb into dev_ui 2023-07-25 13:11:03 +08:00
wang-liang0615
c3168035ed 授权高亮提示 2023-07-25 13:10:45 +08:00
pycook
5140c0a58f add command cmdb-index-table-upgrade 2023-07-25 10:31:30 +08:00
wang-liang0615
ee97582579 style 新建属性行错乱 2023-07-25 10:18:22 +08:00
pycook
83e9172722 废弃3个表: c_value_datetime c_value_floats c_value_integers, time类型属性值增加写入校验 2023-07-24 21:55:00 +08:00
pycook
341e687987 禁止删除唯一标识的属性 2023-07-21 15:58:41 +08:00
pycook
93d9804127 Merge branch 'master' of github.com:veops/cmdb 2023-07-20 18:37:14 +08:00
pycook
f3d42cb356 fix docker-compose 2023-07-20 18:36:32 +08:00
pycook
04b9a3c929 Merge pull request #127 from veops/dev_ui
fix currentValueType
2023-07-20 15:39:11 +08:00
wang-liang0615
e98160b84a fix currentValueType 2023-07-20 15:30:12 +08:00
pycook
7c769a53da 更新架构图 2023-07-20 11:01:25 +08:00
pycook
08dc343083 update readme 2023-07-20 11:01:25 +08:00
pycook
52e654ff60 update readme 2023-07-20 11:01:25 +08:00
141 changed files with 2247 additions and 3774 deletions

1
.gitignore vendored
View File

@@ -78,3 +78,4 @@ cmdb-ui/npm-debug.log*
cmdb-ui/yarn-debug.log*
cmdb-ui/yarn-error.log*
cmdb-ui/package-lock.json
start.sh

View File

@@ -27,6 +27,7 @@
<img src=docs/images/dashboard.png />
[查看更多展示](docs/screenshot.md)
### 相关文章
@@ -50,12 +51,14 @@
### 主要功能
- 自定义模型和模型关系,模型属性支持下拉列表、字体颜色计算属性等高级特性
- 支持计算机、网络设备、存储设备、数据库、中间件、公有云资源等自动发现
- 模型属性支持索引、多值、默认排序、字体颜色,支持计算属性
- 支持自动发现、定时巡检、文件导入
- 支持资源、层级、关系视图展示
- 支持模型间关系配置和展示
- 细粒度访问控制,完备的操作日志
- 通用的资源搜索和关系搜索
- 支持IP地址管理(IPAM), 数据中心基础设施管理(DCIM)
- 支持跨模型搜索

View File

@@ -1,79 +0,0 @@
line-length = 120
cache-dir = ".ruff_cache"
target-version = "py310"
unsafe-fixes = true
show-fixes = true
[lint]
select = [
"E",
"F",
"I",
"TCH",
# W
"W505",
# PT
"PT018",
# SIM
"SIM101",
"SIM114",
# PGH
"PGH004",
# PL
"PLE1142",
# RUF
"RUF100",
# UP
"UP007"
]
preview = true
ignore = ["FURB101"]
[lint.flake8-pytest-style]
mark-parentheses = false
parametrize-names-type = "list"
parametrize-values-row-type = "list"
parametrize-values-type = "tuple"
[lint.flake8-unused-arguments]
ignore-variadic-names = true
[lint.isort]
lines-between-types = 1
order-by-type = true
[lint.per-file-ignores]
"**/api/v1/*.py" = ["TCH"]
"**/model/*.py" = ["TCH003"]
"**/models/__init__.py" = ["F401", "F403"]
"**/tests/*.py" = ["E402"]
"celery_worker.py" = ["F401"]
"api/views/entry.py" = ["I001"]
"migrations/*.py" = ["I001", "E402"]
"*.py" = ["I001"]
"api/views/common_setting/department.py" = ["F841"]
"api/lib/common_setting/upload_file.py" = ["F841"]
"api/lib/common_setting/acl.py" = ["F841"]
"**/__init__.py" = ["F822"]
"api/tasks/*.py" = ["E722"]
"api/views/cmdb/*.py" = ["E722"]
"api/views/acl/*.py" = ["E722"]
"api/lib/secrets/*.py" = ["E722", "F841"]
"api/lib/utils.py" = ["E722", "E731"]
"api/lib/perm/authentication/cas/*" = ["E113", "F841"]
"api/lib/perm/acl/*" = ["E722"]
"api/lib/*" = ["E721", "F722"]
"api/lib/cmdb/*" = ["F722", "E722"]
"api/lib/cmdb/search/ci/es/search.py" = ["F841", "SIM114"]
"api/lib/cmdb/search/ci/db/search.py" = ["F841"]
"api/lib/cmdb/value.py" = ["F841"]
"api/lib/cmdb/history.py" = ["E501"]
"api/commands/common.py" = ["E722"]
"api/commands/click_cmdb.py" = ["E722"]
"api/lib/perm/auth.py" = ["SIM114"]
[format]
preview = true
quote-style = "single"
docstring-code-format = true
skip-magic-trailing-comma = false

View File

@@ -228,12 +228,6 @@ def cmdb_trigger():
"""
from api.lib.cmdb.ci import CITriggerManager
current_app.test_request_context().push()
if not UserCache.get('worker'):
from api.lib.perm.acl.user import UserCRUD
UserCRUD.add(username='worker', password=uuid.uuid4().hex, email='worker@xxx.com')
login_user(UserCache.get('worker'))
current_day = datetime.datetime.today().strftime("%Y-%m-%d")
trigger2cis = dict()
trigger2completed = dict()
@@ -262,10 +256,10 @@ def cmdb_trigger():
trigger2cis[trigger.id] = (trigger, ready_cis)
else:
cur = trigger2cis[trigger.id]
cur_ci_ids = {_ci.ci_id for _ci in cur[1]}
cur_ci_ids = {i.ci_id for i in cur[1]}
trigger2cis[trigger.id] = (
trigger, cur[1] + [_ci for _ci in ready_cis if _ci.ci_id not in cur_ci_ids
and _ci.ci_id not in trigger2completed.get(trigger.id, {})])
trigger, cur[1] + [i for i in ready_cis if i.ci_id not in cur_ci_ids
and i.ci_id not in trigger2completed.get(trigger.id, {})])
for tid in trigger2cis:
trigger, cis = trigger2cis[tid]
@@ -352,7 +346,7 @@ def cmdb_inner_secrets_init(address):
if valid_address(address):
token = current_app.config.get("INNER_TRIGGER_TOKEN", "") if not token else token
if not token:
token = click.prompt('Enter root token', hide_input=True, confirmation_prompt=False)
token = click.prompt(f'Enter root token', hide_input=True, confirmation_prompt=False)
assert token is not None
resp = requests.post("{}/api/v0.1/secrets/auto_seal".format(address.strip("/")),
headers={"Inner-Token": token})

View File

@@ -415,7 +415,7 @@ class AttributeManager(object):
db.session.rollback()
current_app.logger.error("update attribute error, {0}".format(str(e)))
return abort(400, ErrFormat.update_attribute_failed.format(("id={}".format(_id))))
return abort(400, ErrFormat.update_attribute_failed.format(("id=".format(_id))))
new = attr.to_dict()
if not new['choice_web_hook'] and new['is_choice']:

View File

@@ -295,7 +295,7 @@ class CIManager(object):
db.session.commit()
value_table = TableMap(attr_name=attr.name).table
with redis_lock.Lock(rd.r, "auto_inc_id_{}".format(attr.name), expire=10):
with redis_lock.Lock(rd.r, "auto_inc_id_{}".format(attr.name)):
max_v = value_table.get_by(attr_id=attr.id, only_query=True).order_by(
getattr(value_table, 'value').desc()).first()
if max_v is not None:
@@ -393,7 +393,7 @@ class CIManager(object):
ci = None
record_id = None
password_dict = {}
with redis_lock.Lock(rd.r, ci_type.name, expire=10):
with redis_lock.Lock(rd.r, ci_type.name):
db.session.commit()
if (unique_key.default and unique_key.default.get('default') == AttributeDefaultValueEnum.AUTO_INC_ID and
@@ -524,14 +524,10 @@ class CIManager(object):
raw_dict = copy.deepcopy(ci_dict)
ci_attr2type_attr = {type_attr.attr_id: type_attr for type_attr, _ in attrs}
unique_name = None
for _, attr in attrs:
if attr.default and attr.default.get('default') == AttributeDefaultValueEnum.UPDATED_AT:
ci_dict[attr.name] = now
if attr.id == ci_type.unique_id:
unique_name = attr.name
value_manager = AttributeValueManager()
password_dict = dict()
@@ -554,15 +550,14 @@ class CIManager(object):
limit_attrs = self._valid_ci_for_no_read(ci) if not _is_admin else {}
record_id = None
with redis_lock.Lock(rd.r, ci_type.name, expire=10):
with redis_lock.Lock(rd.r, ci_type.name):
db.session.commit()
self._valid_unique_constraint(ci.type_id, ci_dict, ci_id)
ci_dict = {k: v for k, v in ci_dict.items() if k in ci_type_attrs_name}
key2attr = value_manager.valid_attr_value(ci_dict, ci.type_id, ci.id, ci_type_attrs_name,
ci_attr2type_attr=ci_attr2type_attr,
unique_name=unique_name)
ci_attr2type_attr=ci_attr2type_attr)
if computed_attrs:
value_manager.handle_ci_compute_attributes(ci_dict, computed_attrs, ci)
@@ -1273,9 +1268,7 @@ class CIRelationManager(object):
else:
type_relation = CITypeRelation.get_by_id(relation_type_id)
with redis_lock.Lock(rd.r,
"ci_relation_add_{}_{}".format(first_ci.type_id, second_ci.type_id),
expire=10):
with redis_lock.Lock(rd.r, "ci_relation_add_{}_{}".format(first_ci.type_id, second_ci.type_id)):
cls._check_constraint(first_ci_id, first_ci.type_id, second_ci_id, second_ci.type_id, type_relation)
@@ -1541,8 +1534,7 @@ class CITriggerManager(object):
ci_dict.update(attr_dict)
@classmethod
def _exec_webhook(cls, operate_type, webhook, ci_dict, trigger_id, trigger_name, record_id,
ci_id=None, app=None, record_history=True):
def _exec_webhook(cls, operate_type, webhook, ci_dict, trigger_id, trigger_name, record_id, ci_id=None, app=None):
app = app or current_app
with app.app_context():
@@ -1560,20 +1552,19 @@ class CITriggerManager(object):
current_app.logger.warning("exec webhook failed: {}".format(e))
response = e
is_ok = False
if record_history:
CITriggerHistoryManager.add(operate_type,
record_id,
ci_dict.get('_id'),
trigger_id,
trigger_name,
is_ok=is_ok,
webhook=response)
CITriggerHistoryManager.add(operate_type,
record_id,
ci_dict.get('_id'),
trigger_id,
trigger_name,
is_ok=is_ok,
webhook=response)
return is_ok
@classmethod
def _exec_notify(cls, operate_type, notify, ci_dict, trigger_id, trigger_name, record_id,
ci_id=None, app=None, record_history=True):
def _exec_notify(cls, operate_type, notify, ci_dict, trigger_id, trigger_name, record_id, ci_id=None, app=None):
app = app or current_app
with app.app_context():
@@ -1597,14 +1588,13 @@ class CITriggerManager(object):
response = "{}\n{}".format(response, e)
is_ok = False
if record_history:
CITriggerHistoryManager.add(operate_type,
record_id,
ci_dict.get('_id'),
trigger_id,
trigger_name,
is_ok=is_ok,
notify=response.strip())
CITriggerHistoryManager.add(operate_type,
record_id,
ci_dict.get('_id'),
trigger_id,
trigger_name,
is_ok=is_ok,
notify=response.strip())
return is_ok
@@ -1681,47 +1671,25 @@ class CITriggerManager(object):
return result
@classmethod
def trigger_notify(cls, trigger, ci, only_test=False):
def trigger_notify(cls, trigger, ci):
"""
only for date attribute
:param trigger:
:param ci:
:param only_test:
:return:
"""
if (trigger.option.get('notifies', {}).get('notify_at') == datetime.datetime.now().strftime("%H:%M") or
not trigger.option.get('notifies', {}).get('notify_at')) or only_test:
not trigger.option.get('notifies', {}).get('notify_at')):
if trigger.option.get('webhooks'):
threading.Thread(
target=cls._exec_webhook,
args=(None, trigger.option['webhooks'], None, trigger.id,
trigger.option.get('name'), None,
ci and ci.ci_id,
current_app._get_current_object(), not only_test)).start()
threading.Thread(target=cls._exec_webhook, args=(
None, trigger.option['webhooks'], None, trigger.id, trigger.option.get('name'), None, ci.ci_id,
current_app._get_current_object())).start()
elif trigger.option.get('notifies'):
threading.Thread(target=cls._exec_notify, args=(
None, trigger.option['notifies'], None, trigger.id,
trigger.option.get('name'), None,
ci and ci.ci_id,
current_app._get_current_object(), not only_test)).start()
None, trigger.option['notifies'], None, trigger.id, trigger.option.get('name'), None, ci.ci_id,
current_app._get_current_object())).start()
return True
return False
@classmethod
def trigger_notify_test(cls, type_id, trigger_id):
trigger = CITypeTrigger.get_by_id(trigger_id) or abort(
404, ErrFormat.ci_type_trigger_not_found.format(trigger_id))
ci_type = CITypeCache.get(type_id) or abort(404, ErrFormat.ci_type_not_found.format(type_id))
attr = AttributeCache.get(ci_type.unique_id)
value_table = TableMap(attr=attr).table
if not value_table:
return
value = value_table.get_by(attr_id=attr.id, only_query=True).join(
CI, value_table.ci_id == CI.id).filter(CI.type_id == type_id).first()
cls.trigger_notify(trigger, value, only_test=True)

View File

@@ -421,10 +421,7 @@ class CITypeGroupManager(object):
group_types = set()
for group in groups:
for t in sorted(CITypeGroupItem.get_by(group_id=group['id']), key=lambda x: x['order'] or 0):
ci_type = CITypeCache.get(t['type_id'])
if ci_type is None:
continue
ci_type = ci_type.to_dict()
ci_type = CITypeCache.get(t['type_id']).to_dict()
if type_ids is not None and ci_type['id'] not in type_ids:
continue
if resources is None or (ci_type and ci_type['name'] in resources):
@@ -722,6 +719,9 @@ class CITypeAttributeManager(object):
ci_cache.apply_async(args=(ci.id, None, None), queue=CMDB_QUEUE)
for item in PreferenceShowAttributes.get_by(type_id=type_id, attr_id=attr_id, to_dict=False):
item.soft_delete(commit=False)
child_ids = CITypeInheritanceManager.recursive_children(type_id)
for _type_id in [type_id] + child_ids:
for item in CITypeUniqueConstraint.get_by(type_id=_type_id, to_dict=False):
@@ -737,9 +737,6 @@ class CITypeAttributeManager(object):
item = CITypeTrigger.get_by(type_id=_type_id, attr_id=attr_id, to_dict=False, first=True)
item and item.soft_delete(commit=False)
for item in PreferenceShowAttributes.get_by(type_id=_type_id, attr_id=attr_id, to_dict=False):
item.soft_delete(commit=False)
for item in (CITypeRelation.get_by(parent_id=type_id, to_dict=False) +
CITypeRelation.get_by(child_id=type_id, to_dict=False)):
if item.parent_id == type_id and attr_id in (item.parent_attr_ids or []):
@@ -862,15 +859,15 @@ class CITypeRelationManager(object):
graph = nx.DiGraph()
def get_children(_id, _graph):
def get_children(_id):
children = CITypeRelation.get_by(parent_id=_id, to_dict=False)
for i in children:
if i.child_id != _id:
_graph.add_edge(i.parent_id, i.child_id)
get_children(i.child_id, _graph)
graph.add_edge(i.parent_id, i.child_id)
get_children(i.child_id)
get_children(source_type_id, graph)
get_children(source_type_id)
paths = list(nx.all_simple_paths(graph, source_type_id, target_type_ids))
@@ -1145,14 +1142,13 @@ class CITypeAttributeGroupManager(object):
else:
group_pos = group2pos[group['name']]
attr = None
for i in items:
if i.attr_id in id2attr:
attr = id2attr[i.attr_id]
attr['inherited'] = group['inherited']
attr['inherited_from'] = group.get('inherited_from')
result[group_pos]['attributes'].append(attr)
else:
continue
if i.attr_id in attr2pos:
result[attr2pos[i.attr_id][0]]['attributes'].remove(attr2pos[i.attr_id][1])

View File

@@ -44,7 +44,7 @@ class RackManager(DCIMBase):
CIManager().update(_id, _sync=True, **kwargs)
if RackBuiltinAttributes.U_COUNT in kwargs:
payload = {RackBuiltinAttributes.FREE_U_COUNT: cls.calc_u_free_count(_id)}
payload = {RackBuiltinAttributes.FREE_U_COUNT: cls._calc_u_free_count(_id)}
CIManager().update(_id, _sync=True, **payload)
@@ -57,7 +57,7 @@ class RackManager(DCIMBase):
CIManager().update(ci['_id'], **payload)
@staticmethod
def calc_u_free_count(rack_id, device_id=None, u_start=None, u_count=None):
def _calc_u_free_count(rack_id, device_id=None, u_start=None, u_count=None):
rack = CIManager.get_ci_by_id(rack_id, need_children=False)
if not rack.get(RackBuiltinAttributes.U_COUNT):
return 0
@@ -122,8 +122,8 @@ class RackManager(DCIMBase):
CIManager().update(rack['_id'], **payload)
def add_device(self, rack_id, device_id, u_start, u_count=None):
with (redis_lock.Lock(rd.r, "DCIM_RACK_OPERATE_{}".format(rack_id), expire=10)):
self.calc_u_free_count(rack_id, device_id, u_start, u_count)
with (redis_lock.Lock(rd.r, "DCIM_RACK_OPERATE_{}".format(rack_id))):
self._calc_u_free_count(rack_id, device_id, u_start, u_count)
self.add_relation(rack_id, device_id)
@@ -133,16 +133,16 @@ class RackManager(DCIMBase):
CIManager().update(device_id, _sync=True, **payload)
payload = {
RackBuiltinAttributes.FREE_U_COUNT: self.calc_u_free_count(rack_id, device_id, u_start, u_count)}
RackBuiltinAttributes.FREE_U_COUNT: self._calc_u_free_count(rack_id, device_id, u_start, u_count)}
CIManager().update(rack_id, _sync=True, **payload)
OperateHistoryManager().add(operate_type=OperateTypeEnum.ADD_DEVICE, rack_id=rack_id, ci_id=device_id)
def remove_device(self, rack_id, device_id):
with (redis_lock.Lock(rd.r, "DCIM_RACK_OPERATE_{}".format(rack_id), expire=10)):
with (redis_lock.Lock(rd.r, "DCIM_RACK_OPERATE_{}".format(rack_id))):
CIRelationManager.delete_3(rack_id, device_id, apply_async=False, valid=False)
payload = {RackBuiltinAttributes.FREE_U_COUNT: self.calc_u_free_count(rack_id)}
payload = {RackBuiltinAttributes.FREE_U_COUNT: self._calc_u_free_count(rack_id)}
CIManager().update(rack_id, _sync=True, **payload)
payload = {RackBuiltinAttributes.U_START: None}
@@ -151,8 +151,8 @@ class RackManager(DCIMBase):
OperateHistoryManager().add(operate_type=OperateTypeEnum.REMOVE_DEVICE, rack_id=rack_id, ci_id=device_id)
def move_device(self, rack_id, device_id, to_u_start):
with (redis_lock.Lock(rd.r, "DCIM_RACK_OPERATE_{}".format(rack_id), expire=10)):
payload = {RackBuiltinAttributes.FREE_U_COUNT: self.calc_u_free_count(rack_id, device_id, to_u_start)}
with (redis_lock.Lock(rd.r, "DCIM_RACK_OPERATE_{}".format(rack_id))):
payload = {RackBuiltinAttributes.FREE_U_COUNT: self._calc_u_free_count(rack_id, device_id, to_u_start)}
CIManager().update(rack_id, _sync=True, **payload)
CIManager().update(device_id, _sync=True, **{RackBuiltinAttributes.U_START: to_u_start})
@@ -160,8 +160,8 @@ class RackManager(DCIMBase):
OperateHistoryManager().add(operate_type=OperateTypeEnum.MOVE_DEVICE, rack_id=rack_id, ci_id=device_id)
def migrate_device(self, rack_id, device_id, to_rack_id, to_u_start):
with (redis_lock.Lock(rd.r, "DCIM_RACK_OPERATE_{}".format(rack_id), expire=10)):
self.calc_u_free_count(to_rack_id, device_id, to_u_start)
with (redis_lock.Lock(rd.r, "DCIM_RACK_OPERATE_{}".format(rack_id))):
self._calc_u_free_count(to_rack_id, device_id, to_u_start)
if rack_id != to_rack_id:
CIRelationManager.delete_3(rack_id, device_id, apply_async=False, valid=False)
@@ -169,14 +169,15 @@ class RackManager(DCIMBase):
self.add_relation(to_rack_id, device_id)
payload = {
RackBuiltinAttributes.FREE_U_COUNT: self.calc_u_free_count(to_rack_id, device_id, to_u_start)}
RackBuiltinAttributes.FREE_U_COUNT: self._calc_u_free_count(to_rack_id, device_id, to_u_start)}
CIManager().update(to_rack_id, _sync=True, **payload)
CIManager().update(device_id, _sync=True, **{RackBuiltinAttributes.U_START: to_u_start})
if rack_id != to_rack_id:
payload = {RackBuiltinAttributes.FREE_U_COUNT: self.calc_u_free_count(rack_id)}
payload = {RackBuiltinAttributes.FREE_U_COUNT: self._calc_u_free_count(rack_id)}
CIManager().update(rack_id, _sync=True, **payload)
OperateHistoryManager().add(operate_type=OperateTypeEnum.REMOVE_DEVICE, rack_id=rack_id, ci_id=device_id)
OperateHistoryManager().add(operate_type=OperateTypeEnum.ADD_DEVICE, rack_id=to_rack_id, ci_id=device_id)

View File

@@ -31,11 +31,10 @@ class IpAddressManager(object):
return numfound, result
def _get_cis(self, subnet_id, ips):
q = "_type:{},{}:({})".format(self.type_id, IPAddressBuiltinAttributes.IP, ";".join(ips or []))
response, _, _, _, _, _ = RelationSearch([subnet_id], level=[1], query=q, count=1000000).search()
def _get_cis(self, ips):
response, _, _, _, _, _ = SearchFromDB(
"_type:{},{}:({})".format(self.type_id, IPAddressBuiltinAttributes.IP, ";".join(ips or [])),
count=10000000, parent_node_perm_passed=True).search()
return response
@@ -92,8 +91,8 @@ class IpAddressManager(object):
else:
return abort(400, ErrFormat.ipam_address_model_not_found)
with (redis_lock.Lock(rd.r, "IPAM_ASSIGN_ADDRESS_{}".format(subnet_id), expire=10)):
cis = self._get_cis(subnet_id, ips)
with (redis_lock.Lock(rd.r, "IPAM_ASSIGN_ADDRESS_{}".format(subnet_id))):
cis = self._get_cis(ips)
ip2ci = {ci[IPAddressBuiltinAttributes.IP]: ci for ci in cis}
ci_ids = []

View File

@@ -43,7 +43,7 @@ class ScanHistoryManager(DBMixin):
if kwargs.get('ips'):
from api.lib.cmdb.ipam.address import IpAddressManager
IpAddressManager().assign_ips(kwargs['ips'], ci_id, kwargs.get('cidr'),
IpAddressManager().assign_ips(kwargs['ips'], None, kwargs.get('cidr'),
**{IPAddressBuiltinAttributes.IS_USED: 1})
scan_rule = IPAMSubnetScan.get_by(ci_id=ci_id, first=True, to_dict=False)

View File

@@ -163,7 +163,7 @@ class CIFilterPermsCRUD(DBMixin):
def add(self, **kwargs):
kwargs = self._can_add(**kwargs) or kwargs
with redis_lock.Lock(rd.r, 'CMDB_FILTER_{}_{}'.format(kwargs['type_id'], kwargs['rid']), expire=10):
with redis_lock.Lock(rd.r, 'CMDB_FILTER_{}_{}'.format(kwargs['type_id'], kwargs['rid'])):
request_id_filter = {}
if kwargs.get('id_filter'):
obj = self.cls.get_by(type_id=kwargs.get('type_id'),
@@ -232,7 +232,7 @@ class CIFilterPermsCRUD(DBMixin):
pass
def delete(self, **kwargs):
with redis_lock.Lock(rd.r, 'CMDB_FILTER_{}_{}'.format(kwargs['type_id'], kwargs['rid']), expire=10):
with redis_lock.Lock(rd.r, 'CMDB_FILTER_{}_{}'.format(kwargs['type_id'], kwargs['rid'])):
obj = self.cls.get_by(type_id=kwargs.get('type_id'),
rid=kwargs.get('rid'),
id_filter=None,
@@ -249,7 +249,7 @@ class CIFilterPermsCRUD(DBMixin):
def delete2(self, **kwargs):
with redis_lock.Lock(rd.r, 'CMDB_FILTER_{}_{}'.format(kwargs['type_id'], kwargs['rid']), expire=10):
with redis_lock.Lock(rd.r, 'CMDB_FILTER_{}_{}'.format(kwargs['type_id'], kwargs['rid'])):
obj = self.cls.get_by(type_id=kwargs.get('type_id'),
rid=kwargs.get('rid'),
ci_filter=None,

View File

@@ -16,9 +16,8 @@ class ErrFormat(CommonErrFormat):
argument_file_not_found = _l("The file doesn't seem to be uploaded") # 文件似乎并未上传
attribute_not_found = _l("Attribute {} does not exist!") # 属性 {} 不存在!
# 该属性是模型的唯一标识,不能被删除!
attribute_is_unique_id = _l(
"This attribute is the unique identifier of the model and cannot be deleted!")
"This attribute is the unique identifier of the model and cannot be deleted!") # 该属性是模型的唯一标识,不能被删除!
attribute_is_ref_by_type = _l(
"This attribute is referenced by model {} and cannot be deleted!") # 该属性被模型 {} 引用, 不能删除!
attribute_value_type_cannot_change = _l(
@@ -130,8 +129,7 @@ class ErrFormat(CommonErrFormat):
adr_default_ref_once = _l("The default auto-discovery rule is already referenced by model {}!")
# unique_key方法必须返回非空字符串!
adr_unique_key_required = _l("The unique_key method must return a non-empty string!")
# attributes方法必须返回的是list
adr_plugin_attributes_list_required = _l("The attributes method must return a list")
adr_plugin_attributes_list_required = _l("The attributes method must return a list") # attributes方法必须返回的是list
# attributes方法返回的list不能为空!
adr_plugin_attributes_list_no_empty = _l("The list returned by the attributes method cannot be empty!")
# 只有管理员才可以定义执行机器为: 所有节点!

View File

@@ -107,12 +107,3 @@ FROM
WHERE c_value_index_datetime.value LIKE "{0}") AS {1}
GROUP BY {1}.ci_id
"""
QUERY_CI_BY_NO_ATTR_IN = """
SELECT *
FROM
(SELECT c_value_index_texts.ci_id
FROM c_value_index_texts
WHERE c_value_index_texts.value in ({0})) AS {1}
GROUP BY {1}.ci_id
"""

View File

@@ -6,7 +6,6 @@ from __future__ import unicode_literals
import copy
import six
import time
from flask import abort
from flask import current_app
from flask_login import current_user
from jinja2 import Template
@@ -28,7 +27,6 @@ from api.lib.cmdb.search.ci.db.query_sql import FACET_QUERY
from api.lib.cmdb.search.ci.db.query_sql import QUERY_CI_BY_ATTR_NAME
from api.lib.cmdb.search.ci.db.query_sql import QUERY_CI_BY_ID
from api.lib.cmdb.search.ci.db.query_sql import QUERY_CI_BY_NO_ATTR
from api.lib.cmdb.search.ci.db.query_sql import QUERY_CI_BY_NO_ATTR_IN
from api.lib.cmdb.search.ci.db.query_sql import QUERY_CI_BY_TYPE
from api.lib.cmdb.search.ci.db.query_sql import QUERY_UNION_CI_ATTRIBUTE_IS_NULL
from api.lib.cmdb.utils import TableMap
@@ -143,7 +141,7 @@ class Search(object):
if str(ci_type.id) in self.type_id_list:
self.type_id_list.remove(str(ci_type.id))
type_id_list.remove(str(ci_type.id))
sub.extend([i for i in queries[1:] if isinstance(i, (six.string_types, list))])
sub.extend([i for i in queries[1:] if isinstance(i, six.string_types)])
sub.insert(0, "_type:{}".format(ci_type.id))
queries.append(dict(operator="|", queries=sub))
@@ -153,9 +151,7 @@ class Search(object):
if not self.fl:
self.fl = set(self.type2filter_perms[ci_type.id]['attr_filter'])
else:
fl = set(self.fl) & set(self.type2filter_perms[ci_type.id]['attr_filter'])
not fl and abort(400, ErrFormat.ci_filter_perm_attr_no_permission.format(self.fl))
self.fl = fl
self.fl = set(self.fl) & set(self.type2filter_perms[ci_type.id]['attr_filter'])
else:
self.fl = self.fl or {}
if not self.fl or isinstance(self.fl, dict):
@@ -437,14 +433,11 @@ class Search(object):
if not q.startswith("("):
raise SearchError(ErrFormat.ci_search_Parentheses_invalid)
if ":" not in q: # multi-line search
result.append(q[1:-1].split(';'))
else:
operator, q = self._operator_proc(q)
if q.endswith(")"):
result.append(dict(operator=operator, queries=[q[1:-1]]))
operator, q = self._operator_proc(q)
if q.endswith(")"):
result.append(dict(operator=operator, queries=[q[1:-1]]))
sub = dict(operator=operator, queries=[q[1:]])
sub = dict(operator=operator, queries=[q[1:]])
elif q.endswith(")") and sub:
sub['queries'].append(q[:-1])
result.append(copy.deepcopy(sub))
@@ -532,31 +525,22 @@ class Search(object):
query_sql = ""
for q in queries:
# current_app.logger.debug(q)
_query_sql = ""
if isinstance(q, dict):
if len(q['queries']) == 1 and ";" in q['queries'][0]:
values = q['queries'][0].split(";")
in_values = ",".join("'{0}'".format(v) for v in values)
_query_sql = QUERY_CI_BY_NO_ATTR_IN.format(in_values, alias)
operator = q['operator']
else:
alias, _query_sql, operator = self.__query_build_by_field(q['queries'], True, True, alias,
is_sub=True)
operator = q['operator']
alias, _query_sql, operator = self.__query_build_by_field(q['queries'], True, True, alias, is_sub=True)
# current_app.logger.info(_query_sql)
# current_app.logger.info((operator, is_first, alias))
operator = q['operator']
elif ":" in q and not q.startswith("*"):
alias, _query_sql, operator = self.__query_by_attr(q, queries, alias, is_sub)
elif q == "*":
continue
elif q:
if not isinstance(q, list):
q = q.replace("'", "\\'")
q = q.replace('"', '\\"')
q = q.replace("*", "%").replace('\\n', '%')
_query_sql = QUERY_CI_BY_NO_ATTR.format(q, alias)
else:
_query_sql = QUERY_CI_BY_NO_ATTR_IN.format(",".join("'{0}'".format(v) for v in q), alias)
q = q.replace("'", "\\'")
q = q.replace('"', '\\"')
q = q.replace("*", "%").replace('\\n', '%')
_query_sql = QUERY_CI_BY_NO_ATTR.format(q, alias)
if is_first and _query_sql and not self.only_type_query:
query_sql = "SELECT * FROM ({0}) AS {1}".format(_query_sql, alias)

View File

@@ -64,7 +64,7 @@ class Search(object):
self.ancestor_ids = ancestor_ids
self.descendant_ids = descendant_ids
self.root_parent_path = root_parent_path or []
self.root_parent_path = root_parent_path
self.has_m2m = has_m2m or False
if not self.has_m2m:
if self.ancestor_ids:

View File

@@ -55,6 +55,7 @@ def str2datetime(x):
return datetime.datetime.strptime(x, "%Y-%m-%d %H:%M")
class ValueTypeMap(object):
deserialize = {
ValueTypeEnum.INT: string2int,

View File

@@ -136,7 +136,7 @@ class AttributeValueManager(object):
if not re.compile(expr).match(str(value)):
return abort(400, ErrFormat.attribute_value_invalid2.format(alias, value))
def _validate(self, attr, value, value_table, ci=None, type_id=None, ci_id=None, type_attr=None, unique_name=None):
def _validate(self, attr, value, value_table, ci=None, type_id=None, ci_id=None, type_attr=None):
if not attr.is_reference:
ci = ci or {}
v = self._deserialize_value(attr.alias, attr.value_type, value)
@@ -146,7 +146,7 @@ class AttributeValueManager(object):
else:
v = value or None
(attr.is_unique or attr.name == unique_name) and self._check_is_unique(
attr.is_unique and self._check_is_unique(
value_table, attr, ci and ci.id or ci_id, ci and ci.type_id or type_id, v)
self._check_is_required(ci and ci.type_id or type_id, attr, v, type_attr=type_attr)
if attr.is_reference:
@@ -237,10 +237,7 @@ class AttributeValueManager(object):
if computed_value is not None:
ci_dict[attr['name']] = computed_value
def valid_attr_value(self, ci_dict, type_id, ci_id, name2attr,
alias2attr=None,
ci_attr2type_attr=None,
unique_name=None):
def valid_attr_value(self, ci_dict, type_id, ci_id, name2attr, alias2attr=None, ci_attr2type_attr=None):
key2attr = dict()
alias2attr = alias2attr or {}
ci_attr2type_attr = ci_attr2type_attr or {}
@@ -271,8 +268,7 @@ class AttributeValueManager(object):
else:
value = self._validate(attr, value, value_table, ci=None, type_id=type_id, ci_id=ci_id,
type_attr=ci_attr2type_attr.get(attr.id),
unique_name=unique_name)
type_attr=ci_attr2type_attr.get(attr.id))
ci_dict[key] = value
except BadRequest as e:
raise

View File

@@ -1,6 +1,6 @@
import functools
from flask import abort, session, current_app
from flask import abort, session
from api.lib.common_setting.acl import ACLManager
from api.lib.common_setting.resp_format import ErrFormat
from api.lib.perm.acl.acl import is_app_admin
@@ -15,7 +15,6 @@ def perms_role_required(app_name, resource_type_name, resource_name, perm, role_
try:
has_perms = acl.role_has_perms(session["acl"]['rid'], resource_name, resource_type_name, perm)
except Exception as e:
current_app.logger.error(f"acl role_has_perms err: {e}")
# resource_type not exist, continue check role
if role_name:
if role_name not in session.get("acl", {}).get("parentRoles", []) and not is_app_admin(app_name):

View File

@@ -476,7 +476,7 @@ class EditDepartmentInACL(object):
for employee in e_list:
employee_acl_rid = employee.get('e_acl_rid')
if employee_acl_rid == 0:
result.append("employee_acl_rid == 0")
result.append(f"employee_acl_rid == 0")
continue
cls.remove_single_employee_from_old_department(acl, employee, result)
@@ -501,8 +501,8 @@ class EditDepartmentInACL(object):
acl.remove_user_from_role(employee.get('e_acl_rid'), payload)
current_app.logger.info(f"remove {employee.get('e_acl_rid')} from {d_acl_rid}")
except Exception as e:
err = f"remove_user_from_role e_acl_rid: {employee.get('e_acl_rid')}, parent_id: {d_acl_rid}, err: {e}"
result.append(err)
result.append(
f"remove_user_from_role employee_acl_rid: {employee.get('e_acl_rid')}, parent_id: {d_acl_rid}, err: {e}")
return True
@@ -548,7 +548,7 @@ class EditDepartmentInACL(object):
for employee in e_list:
employee_acl_rid = employee.get('e_acl_rid')
if employee_acl_rid == 0:
result.append("employee_acl_rid == 0")
result.append(f"employee_acl_rid == 0")
continue
cls.remove_single_employee_from_old_department(acl, employee, result)

View File

@@ -4,7 +4,7 @@ import traceback
from datetime import datetime
import requests
from flask import abort, current_app
from flask import abort
from flask_login import current_user
from sqlalchemy import or_, literal_column, func, not_, and_
from werkzeug.datastructures import MultiDict
@@ -478,7 +478,7 @@ class EmployeeCRUD(object):
Employee.deleted == 0,
Employee.block == block,
]
if isinstance(department_id, list):
if type(department_id) == list:
if len(department_id) == 0:
return []
else:
@@ -702,7 +702,6 @@ class EmployeeCRUD(object):
try:
last_login = datetime.strptime(last_login, '%Y-%m-%d %H:%M:%S')
except Exception as e:
current_app.logger.error(f"strptime {last_login} err: {e}")
last_login = datetime.now()
else:
last_login = datetime.now()
@@ -713,7 +712,6 @@ class EmployeeCRUD(object):
)
return last_login
except Exception as e:
current_app.logger.error(f"update last_login err: {e}")
return

View File

@@ -2,7 +2,7 @@ import requests
from api.lib.common_setting.const import BotNameMap
from api.lib.common_setting.resp_format import ErrFormat
from api.models.common_setting import NoticeConfig
from api.models.common_setting import CompanyInfo, NoticeConfig
from wtforms import Form
from wtforms import StringField
from wtforms import validators

View File

@@ -48,9 +48,7 @@ class CMDBApp(BaseApp):
{"page": "Model_Relationships", "page_cn": "模型关系", "perms": ["read"]},
{"page": "Operation_Audit", "page_cn": "操作审计", "perms": ["read"]},
{"page": "Relationship_Types", "page_cn": "关系类型", "perms": ["read"]},
{"page": "Auto_Discovery", "page_cn": "自动发现",
"perms": ["read", "create_plugin", "update_plugin", "delete_plugin"]
},
{"page": "Auto_Discovery", "page_cn": "自动发现", "perms": ["read", "create_plugin", "update_plugin", "delete_plugin"]},
{"page": "TopologyView", "page_cn": "拓扑视图",
"perms": ["read", "create_topology_group", "update_topology_group", "delete_topology_group",
"create_topology_view"],

View File

@@ -6,7 +6,7 @@ from functools import wraps
from flask import abort
from flask import request
from api.lib.perm.acl.cache import AppCache
from api.lib.perm.acl.cache import AppCache, AppAccessTokenCache
from api.lib.perm.acl.resp_format import ErrFormat

View File

@@ -138,14 +138,14 @@ class HasResourceRoleCache(object):
@classmethod
def add(cls, rid, app_id):
with redis_lock.Lock(rd.r, 'HasResourceRoleCache', expire=10):
with redis_lock.Lock(rd.r, 'HasResourceRoleCache'):
c = cls.get(app_id)
c[rid] = 1
cache.set(cls.PREFIX_KEY.format(app_id), c, timeout=0)
@classmethod
def remove(cls, rid, app_id):
with redis_lock.Lock(rd.r, 'HasResourceRoleCache', expire=10):
with redis_lock.Lock(rd.r, 'HasResourceRoleCache'):
c = cls.get(app_id)
c.pop(rid, None)
cache.set(cls.PREFIX_KEY.format(app_id), c, timeout=0)

View File

@@ -1,5 +1,8 @@
# -*- coding:utf-8 -*-
import time
import redis_lock
import six
from flask import abort
@@ -142,7 +145,7 @@ class RoleRelationCRUD(object):
@classmethod
def add(cls, role, parent_id, child_ids, app_id):
with redis_lock.Lock(rd.r, "ROLE_RELATION_ADD", expire=10):
with redis_lock.Lock(rd.r, "ROLE_RELATION_ADD"):
db.session.commit()
result = []

View File

@@ -1,6 +1,7 @@
# -*- coding:utf-8 -*-
import base64
from typing import Set
import elasticsearch
import redis

View File

@@ -105,8 +105,8 @@ class User(CRUDModel, SoftDeleteMixin):
_password = db.Column("password", db.String(80))
key = db.Column(db.String(32), nullable=False)
secret = db.Column(db.String(32), nullable=False)
date_joined = db.Column(db.DateTime, default=datetime.now)
last_login = db.Column(db.DateTime, default=datetime.now)
date_joined = db.Column(db.DateTime, default=datetime.utcnow)
last_login = db.Column(db.DateTime, default=datetime.utcnow)
block = db.Column(db.Boolean, default=False)
has_logined = db.Column(db.Boolean, default=False)
wx_id = db.Column(db.String(32))

View File

@@ -32,7 +32,7 @@ from api.models.acl import Trigger
def role_rebuild(rids, app_id):
rids = rids if isinstance(rids, list) else [rids]
for rid in rids:
with redis_lock.Lock(rd.r, "ROLE_REBUILD_{}_{}".format(rid, app_id), expire=10):
with redis_lock.Lock(rd.r, "ROLE_REBUILD_{}_{}".format(rid, app_id)):
RoleRelationCache.rebuild(rid, app_id)
current_app.logger.info("Role {0} App {1} rebuild..........".format(rids, app_id))

View File

@@ -1,4 +1,4 @@
# -*- coding:utf-8 -*-
# -*- coding:utf-8 -*-
import datetime
@@ -145,7 +145,7 @@ def ci_delete_trigger(trigger, operate_type, ci_dict):
@flush_db
@reconnect_db
def ci_relation_cache(parent_id, child_id, ancestor_ids):
with redis_lock.Lock(rd.r, "CIRelation_{}".format(parent_id), expire=10):
with redis_lock.Lock(rd.r, "CIRelation_{}".format(parent_id)):
children = rd.get([parent_id], REDIS_PREFIX_CI_RELATION)[0]
children = json.loads(children) if children is not None else {}
@@ -223,7 +223,7 @@ def ci_relation_add(parent_dict, child_id, uid):
@celery.task(name="cmdb.ci_relation_delete", queue=CMDB_QUEUE)
@reconnect_db
def ci_relation_delete(parent_id, child_id, ancestor_ids):
with redis_lock.Lock(rd.r, "CIRelation_{}".format(parent_id), expire=10):
with redis_lock.Lock(rd.r, "CIRelation_{}".format(parent_id)):
children = rd.get([parent_id], REDIS_PREFIX_CI_RELATION)[0]
children = json.loads(children) if children is not None else {}
@@ -374,25 +374,3 @@ def build_relations_for_ad_accept(adc, ci_id, ad_key2attr):
source=RelationSourceEnum.AUTO_DISCOVERY)
except:
pass
@celery.task(name="cmdb.dcim_calc_u_free_count", queue=CMDB_QUEUE)
@reconnect_db
def dcim_calc_u_free_count():
from api.lib.cmdb.ci import CIManager
from api.lib.cmdb.dcim.rack import RackManager
from api.lib.cmdb.dcim.const import RackBuiltinAttributes
if not has_request_context():
current_app.test_request_context().push()
login_user(UserCache.get('worker'))
try:
rack_m = RackManager()
except Exception:
return
racks = CI.get_by(type_id=rack_m.type_id, to_dict=False)
for rack in racks:
payload = {RackBuiltinAttributes.FREE_U_COUNT: rack_m.calc_u_free_count(rack.id)}
CIManager().update(rack.id, **payload)

View File

@@ -2,14 +2,14 @@
import json
from io import BytesIO
from flask import abort
from flask import current_app
from flask import request
from io import BytesIO
from api.lib.cmdb.cache import AttributeCache
from api.lib.cmdb.cache import CITypeCache
from api.lib.cmdb.ci import CITriggerManager
from api.lib.cmdb.ci_type import CITypeAttributeGroupManager
from api.lib.cmdb.ci_type import CITypeAttributeManager
from api.lib.cmdb.ci_type import CITypeGroupManager
@@ -497,16 +497,6 @@ class CITypeTriggerView(APIView):
return self.jsonify(code=200)
class CITypeTriggerTestView(APIView):
url_prefix = ("/ci_types/<int:type_id>/triggers/<int:_id>/test_notify",)
@has_perm_from_args("type_id", ResourceTypeEnum.CI, PermEnum.CONFIG, CITypeManager.get_name_by_id)
def post(self, type_id, _id):
CITriggerManager().trigger_notify_test(type_id, _id)
return self.jsonify(code=200)
class CITypeGrantView(APIView):
url_prefix = "/ci_types/<int:type_id>/roles/<int:rid>/grant"

View File

@@ -2,14 +2,12 @@
from flask import request
from api.lib.cmdb.const import CMDB_QUEUE
from api.lib.cmdb.dcim.const import RackBuiltinAttributes
from api.lib.cmdb.dcim.rack import RackManager
from api.lib.common_setting.decorator import perms_role_required
from api.lib.common_setting.role_perm_base import CMDBApp
from api.lib.decorator import args_required
from api.resource import APIView
from api.tasks.cmdb import dcim_calc_u_free_count
app_cli = CMDBApp()
@@ -89,14 +87,3 @@ class RackDeviceMigrateView(APIView):
device_id=device_id,
to_u_start=to_u_start,
to_rack_id=to_rack_id)
class RackCalcUFreeCountView(APIView):
url_prefix = ("/dcim/rack/calc_u_free_count",)
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.DCIM,
app_cli.op.read, app_cli.admin_name)
def post(self):
dcim_calc_u_free_count.apply_async(queue=CMDB_QUEUE)
return self.jsonify(code=200)

View File

@@ -131,7 +131,7 @@ class EmployeeChangePasswordWithACLID(APIView):
if not password:
abort(400, ErrFormat.password_is_required)
EmployeeCRUD.change_password_by_uid(_uid, password)
data = EmployeeCRUD.change_password_by_uid(_uid, password)
return self.jsonify(200)

View File

@@ -6,7 +6,7 @@ import magic
from api.lib.common_setting.const import MIMEExtMap
from api.lib.common_setting.resp_format import ErrFormat
from api.lib.common_setting.upload_file import generate_new_file_name, CommonFileCRUD
from api.lib.common_setting.upload_file import allowed_file, generate_new_file_name, CommonFileCRUD
from api.resource import APIView
prefix = '/file'

View File

@@ -58,4 +58,3 @@ python-magic==0.4.27
jsonpath==0.82.2
networkx>=3.1
ipaddress>=1.0.23
ruff==0.8.3

View File

@@ -54,54 +54,6 @@
<div class="content unicode" style="display: block;">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont">&#xea0b;</span>
<div class="name">veops-servicetree</div>
<div class="code-name">&amp;#xea0b;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea0a;</span>
<div class="name">veops-switch (1)</div>
<div class="code-name">&amp;#xea0a;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea09;</span>
<div class="name">veops-label</div>
<div class="code-name">&amp;#xea09;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea08;</span>
<div class="name">top_acl</div>
<div class="code-name">&amp;#xea08;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea06;</span>
<div class="name">top_ticket</div>
<div class="code-name">&amp;#xea06;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea07;</span>
<div class="name">top_agent</div>
<div class="code-name">&amp;#xea07;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea05;</span>
<div class="name">itsm-table_download</div>
<div class="code-name">&amp;#xea05;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea04;</span>
<div class="name">itsm-image_download</div>
<div class="code-name">&amp;#xea04;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xea02;</span>
<div class="name">veops-rear</div>
@@ -6210,9 +6162,9 @@
<pre><code class="language-css"
>@font-face {
font-family: 'iconfont';
src: url('iconfont.woff2?t=1735191938771') format('woff2'),
url('iconfont.woff?t=1735191938771') format('woff'),
url('iconfont.ttf?t=1735191938771') format('truetype');
src: url('iconfont.woff2?t=1732673294759') format('woff2'),
url('iconfont.woff?t=1732673294759') format('woff'),
url('iconfont.ttf?t=1732673294759') format('truetype');
}
</code></pre>
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@@ -6238,78 +6190,6 @@
<div class="content font-class">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont veops-servicetree"></span>
<div class="name">
veops-servicetree
</div>
<div class="code-name">.veops-servicetree
</div>
</li>
<li class="dib">
<span class="icon iconfont veops-switch1"></span>
<div class="name">
veops-switch (1)
</div>
<div class="code-name">.veops-switch1
</div>
</li>
<li class="dib">
<span class="icon iconfont veops-label"></span>
<div class="name">
veops-label
</div>
<div class="code-name">.veops-label
</div>
</li>
<li class="dib">
<span class="icon iconfont top_acl"></span>
<div class="name">
top_acl
</div>
<div class="code-name">.top_acl
</div>
</li>
<li class="dib">
<span class="icon iconfont top_ticket"></span>
<div class="name">
top_ticket
</div>
<div class="code-name">.top_ticket
</div>
</li>
<li class="dib">
<span class="icon iconfont top_agent"></span>
<div class="name">
top_agent
</div>
<div class="code-name">.top_agent
</div>
</li>
<li class="dib">
<span class="icon iconfont itsm-table_download"></span>
<div class="name">
itsm-table_download
</div>
<div class="code-name">.itsm-table_download
</div>
</li>
<li class="dib">
<span class="icon iconfont itsm-image_download"></span>
<div class="name">
itsm-image_download
</div>
<div class="code-name">.itsm-image_download
</div>
</li>
<li class="dib">
<span class="icon iconfont veops-rear"></span>
<div class="name">
@@ -15472,70 +15352,6 @@
<div class="content symbol">
<ul class="icon_lists dib-box">
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#veops-servicetree"></use>
</svg>
<div class="name">veops-servicetree</div>
<div class="code-name">#veops-servicetree</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#veops-switch1"></use>
</svg>
<div class="name">veops-switch (1)</div>
<div class="code-name">#veops-switch1</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#veops-label"></use>
</svg>
<div class="name">veops-label</div>
<div class="code-name">#veops-label</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#top_acl"></use>
</svg>
<div class="name">top_acl</div>
<div class="code-name">#top_acl</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#top_ticket"></use>
</svg>
<div class="name">top_ticket</div>
<div class="code-name">#top_ticket</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#top_agent"></use>
</svg>
<div class="name">top_agent</div>
<div class="code-name">#top_agent</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#itsm-table_download"></use>
</svg>
<div class="name">itsm-table_download</div>
<div class="code-name">#itsm-table_download</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#itsm-image_download"></use>
</svg>
<div class="name">itsm-image_download</div>
<div class="code-name">#itsm-image_download</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#veops-rear"></use>

View File

@@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 3857903 */
src: url('iconfont.woff2?t=1735191938771') format('woff2'),
url('iconfont.woff?t=1735191938771') format('woff'),
url('iconfont.ttf?t=1735191938771') format('truetype');
src: url('iconfont.woff2?t=1732673294759') format('woff2'),
url('iconfont.woff?t=1732673294759') format('woff'),
url('iconfont.ttf?t=1732673294759') format('truetype');
}
.iconfont {
@@ -13,38 +13,6 @@
-moz-osx-font-smoothing: grayscale;
}
.veops-servicetree:before {
content: "\ea0b";
}
.veops-switch1:before {
content: "\ea0a";
}
.veops-label:before {
content: "\ea09";
}
.top_acl:before {
content: "\ea08";
}
.top_ticket:before {
content: "\ea06";
}
.top_agent:before {
content: "\ea07";
}
.itsm-table_download:before {
content: "\ea05";
}
.itsm-image_download:before {
content: "\ea04";
}
.veops-rear:before {
content: "\ea02";
}

File diff suppressed because one or more lines are too long

View File

@@ -5,62 +5,6 @@
"css_prefix_text": "",
"description": "",
"glyphs": [
{
"icon_id": "42930714",
"name": "veops-servicetree",
"font_class": "veops-servicetree",
"unicode": "ea0b",
"unicode_decimal": 59915
},
{
"icon_id": "42921461",
"name": "veops-switch (1)",
"font_class": "veops-switch1",
"unicode": "ea0a",
"unicode_decimal": 59914
},
{
"icon_id": "42857659",
"name": "veops-label",
"font_class": "veops-label",
"unicode": "ea09",
"unicode_decimal": 59913
},
{
"icon_id": "42790685",
"name": "top_acl",
"font_class": "top_acl",
"unicode": "ea08",
"unicode_decimal": 59912
},
{
"icon_id": "42790687",
"name": "top_ticket",
"font_class": "top_ticket",
"unicode": "ea06",
"unicode_decimal": 59910
},
{
"icon_id": "42790686",
"name": "top_agent",
"font_class": "top_agent",
"unicode": "ea07",
"unicode_decimal": 59911
},
{
"icon_id": "42732510",
"name": "itsm-table_download",
"font_class": "itsm-table_download",
"unicode": "ea05",
"unicode_decimal": 59909
},
{
"icon_id": "42732515",
"name": "itsm-image_download",
"font_class": "itsm-image_download",
"unicode": "ea04",
"unicode_decimal": 59908
},
{
"icon_id": "42510712",
"name": "veops-rear",

Binary file not shown.

View File

@@ -106,7 +106,7 @@
<CIReferenceAttr
v-if="getAttr(item.property).is_reference && (item.exp === 'is' || item.exp === '~is')"
:style="{ width: '175px' }"
class="select-filter-component ops-select-bg"
class="select-filter-component"
:referenceTypeId="getAttr(item.property).reference_type_id"
:disabled="disabled"
v-model="item.value"
@@ -114,7 +114,7 @@
<a-select
v-else-if="getAttr(item.property).is_bool && (item.exp === 'is' || item.exp === '~is')"
v-model="item.value"
class="select-filter-component ops-select-bg"
class="select-filter-component"
:style="{ width: '175px' }"
:disabled="disabled"
:placeholder="$t('placeholder2')"
@@ -398,6 +398,7 @@ export default {
/deep/ .ant-select-selection {
height: 24px;
background: #f7f8fa;
line-height: 24px;
border: none;

View File

@@ -1,302 +1,302 @@
<template>
<div>
<a-popover
v-if="isDropdown"
v-model="visible"
trigger="click"
:placement="placement"
overlayClassName="table-filter"
@visibleChange="visibleChange"
>
<slot name="popover_item">
<a-button type="primary" ghost>{{ $t('cmdbFilterComp.conditionFilter') }}<a-icon type="filter"/></a-button>
</slot>
<template slot="content">
<Expression
:needAddHere="needAddHere"
v-model="ruleList"
:canSearchPreferenceAttrList="canSearchPreferenceAttrList.filter((attr) => !attr.is_password)"
:disabled="disabled"
/>
<a-divider :style="{ margin: '10px 0' }" />
<div style="width:554px">
<a-space :style="{ display: 'flex', justifyContent: 'flex-end' }">
<a-button type="primary" size="small" @click="handleSubmit">{{ $t('confirm') }}</a-button>
<a-button size="small" @click="handleClear">{{ $t('clear') }}</a-button>
</a-space>
</div>
</template>
</a-popover>
<Expression
:needAddHere="needAddHere"
v-else
v-model="ruleList"
:canSearchPreferenceAttrList="canSearchPreferenceAttrList.filter((attr) => !attr.is_password)"
:disabled="disabled"
/>
</div>
</template>
<script>
import { v4 as uuidv4 } from 'uuid'
import Expression from './expression.vue'
import { advancedExpList, compareTypeList } from './constants'
export default {
name: 'FilterComp',
components: { Expression },
props: {
canSearchPreferenceAttrList: {
type: Array,
required: true,
default: () => [],
},
expression: {
type: String,
default: '',
},
regQ: {
type: String,
default: '(?<=q=).+(?=&)|(?<=q=).+$',
},
placement: {
type: String,
default: 'bottomRight',
},
isDropdown: {
type: Boolean,
default: true,
},
needAddHere: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
},
data() {
return {
advancedExpList,
compareTypeList,
visible: false,
ruleList: [],
filterExp: '',
}
},
methods: {
visibleChange(open, isInitOne = true) {
// isInitOne 初始化exp为空时ruleList是否默认给一条
// const regQ = /(?<=q=).+(?=&)|(?<=q=).+$/g
const exp = this.expression.match(new RegExp(this.regQ, 'g'))
? this.expression.match(new RegExp(this.regQ, 'g'))[0]
: null
if (open && exp) {
const expArray = exp.split(',').map((item) => {
let has_not = ''
const key = item.split(':')[0]
const val = item
.split(':')
.slice(1)
.join(':')
let type, property, exp, value, min, max, compareType
if (key.includes('-')) {
type = 'or'
if (key.includes('~')) {
property = key.substring(2)
has_not = '~'
} else {
property = key.substring(1)
}
} else {
type = 'and'
if (key.includes('~')) {
property = key.substring(1)
has_not = '~'
} else {
property = key
}
}
const in_reg = /(?<=\().+(?=\))/g
const range_reg = /(?<=\[).+(?=\])/g
const compare_reg = /(?<=>=|<=|>(?!=)|<(?!=)).+/
if (val === '*') {
exp = has_not + 'value'
value = ''
} else if (in_reg.test(val)) {
exp = has_not + 'in'
value = val.match(in_reg)[0]
} else if (range_reg.test(val)) {
exp = has_not + 'range'
value = val.match(range_reg)[0]
min = value.split('_TO_')[0]
max = value.split('_TO_')[1]
} else if (compare_reg.test(val)) {
exp = has_not + 'compare'
value = val.match(compare_reg)[0]
const _compareType = val.substring(0, val.match(compare_reg)['index'])
const idx = compareTypeList.findIndex((item) => item.label === _compareType)
compareType = compareTypeList[idx].value
} else if (!val.includes('*')) {
exp = has_not + 'is'
value = val
} else {
const resList = [
['contain', /(?<=\*).*(?=\*)/g],
['end_with', /(?<=\*).+/g],
['start_with', /.+(?=\*)/g],
]
for (let i = 0; i < 3; i++) {
const reg = resList[i]
if (reg[1].test(val)) {
exp = has_not + reg[0]
value = val.match(reg[1])[0]
break
}
}
}
return {
id: uuidv4(),
type,
property,
exp,
value,
min,
max,
compareType,
}
})
this.ruleList = [...expArray]
} else if (open) {
const _canSearchPreferenceAttrList = this.canSearchPreferenceAttrList.filter((attr) => !attr.is_password)
this.ruleList = isInitOne
? [
{
id: uuidv4(),
type: 'and',
property:
_canSearchPreferenceAttrList && _canSearchPreferenceAttrList.length
? _canSearchPreferenceAttrList[0].name
: undefined,
exp: 'is',
value: null,
},
]
: []
}
},
handleClear() {
this.ruleList = [
{
id: uuidv4(),
type: 'and',
property: this.canSearchPreferenceAttrList[0].name,
exp: 'is',
value: null,
},
]
this.filterExp = ''
this.visible = false
this.$emit('setExpFromFilter', this.filterExp)
},
handleSubmit() {
if (this.ruleList && this.ruleList.length) {
this.ruleList[0].type = 'and' // 增删后以防万一第一个不是and
this.filterExp = ''
const expList = this.ruleList.map((rule) => {
let singleRuleExp = ''
let _exp = rule.exp
if (rule.type === 'or') {
singleRuleExp += '-'
}
if (rule.exp.includes('~')) {
singleRuleExp += '~'
_exp = rule.exp.split('~')[1]
}
singleRuleExp += `${rule.property}:`
if (_exp === 'is') {
singleRuleExp += `${rule.value ?? ''}`
}
if (_exp === 'contain') {
singleRuleExp += `*${rule.value ?? ''}*`
}
if (_exp === 'start_with') {
singleRuleExp += `${rule.value ?? ''}*`
}
if (_exp === 'end_with') {
singleRuleExp += `*${rule.value ?? ''}`
}
if (_exp === 'value') {
singleRuleExp += `*`
}
if (_exp === 'in') {
singleRuleExp += `(${rule.value ?? ''})`
}
if (_exp === 'range') {
singleRuleExp += `[${rule.min}_TO_${rule.max}]`
}
if (_exp === 'compare') {
const idx = compareTypeList.findIndex((item) => item.value === rule.compareType)
singleRuleExp += `${compareTypeList[idx].label}${rule.value ?? ''}`
}
return singleRuleExp
})
this.filterExp = expList.join(',')
this.$emit('setExpFromFilter', this.filterExp)
} else {
this.$emit('setExpFromFilter', '')
}
this.visible = false
},
},
}
</script>
<style lang="less" scoped>
.table-filter {
.table-filter-add {
margin-top: 10px;
& > a {
padding: 2px 8px;
&:hover {
background-color: @primary-color_5;
border-radius: 5px;
}
}
}
.table-filter-extra-icon {
padding: 0px 2px;
&:hover {
display: inline-block;
border-radius: 5px;
background-color: #f0faff;
}
}
}
</style>
<style lang="less">
.table-filter-extra-operation {
.ant-popover-inner-content {
padding: 3px 4px;
.operation {
cursor: pointer;
width: 90px;
height: 30px;
line-height: 30px;
padding: 3px 4px;
border-radius: 5px;
transition: all 0.3s;
&:hover {
background-color: #f0faff;
}
> .anticon {
margin-right: 10px;
}
}
}
}
</style>
<template>
<div>
<a-popover
v-if="isDropdown"
v-model="visible"
trigger="click"
:placement="placement"
overlayClassName="table-filter"
@visibleChange="visibleChange"
>
<slot name="popover_item">
<a-button type="primary" ghost>{{ $t('cmdbFilterComp.conditionFilter') }}<a-icon type="filter"/></a-button>
</slot>
<template slot="content">
<Expression
:needAddHere="needAddHere"
v-model="ruleList"
:canSearchPreferenceAttrList="canSearchPreferenceAttrList.filter((attr) => !attr.is_password)"
:disabled="disabled"
/>
<a-divider :style="{ margin: '10px 0' }" />
<div style="width:554px">
<a-space :style="{ display: 'flex', justifyContent: 'flex-end' }">
<a-button type="primary" size="small" @click="handleSubmit">{{ $t('confirm') }}</a-button>
<a-button size="small" @click="handleClear">{{ $t('clear') }}</a-button>
</a-space>
</div>
</template>
</a-popover>
<Expression
:needAddHere="needAddHere"
v-else
v-model="ruleList"
:canSearchPreferenceAttrList="canSearchPreferenceAttrList.filter((attr) => !attr.is_password)"
:disabled="disabled"
/>
</div>
</template>
<script>
import { v4 as uuidv4 } from 'uuid'
import Expression from './expression.vue'
import { advancedExpList, compareTypeList } from './constants'
export default {
name: 'FilterComp',
components: { Expression },
props: {
canSearchPreferenceAttrList: {
type: Array,
required: true,
default: () => [],
},
expression: {
type: String,
default: '',
},
regQ: {
type: String,
default: '(?<=q=).+(?=&)|(?<=q=).+$',
},
placement: {
type: String,
default: 'bottomRight',
},
isDropdown: {
type: Boolean,
default: true,
},
needAddHere: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
},
data() {
return {
advancedExpList,
compareTypeList,
visible: false,
ruleList: [],
filterExp: '',
}
},
methods: {
visibleChange(open, isInitOne = true) {
// isInitOne 初始化exp为空时ruleList是否默认给一条
// const regQ = /(?<=q=).+(?=&)|(?<=q=).+$/g
const exp = this.expression.match(new RegExp(this.regQ, 'g'))
? this.expression.match(new RegExp(this.regQ, 'g'))[0]
: null
if (open && exp) {
const expArray = exp.split(',').map((item) => {
let has_not = ''
const key = item.split(':')[0]
const val = item
.split(':')
.slice(1)
.join(':')
let type, property, exp, value, min, max, compareType
if (key.includes('-')) {
type = 'or'
if (key.includes('~')) {
property = key.substring(2)
has_not = '~'
} else {
property = key.substring(1)
}
} else {
type = 'and'
if (key.includes('~')) {
property = key.substring(1)
has_not = '~'
} else {
property = key
}
}
const in_reg = /(?<=\().+(?=\))/g
const range_reg = /(?<=\[).+(?=\])/g
const compare_reg = /(?<=>=|<=|>(?!=)|<(?!=)).+/
if (val === '*') {
exp = has_not + 'value'
value = ''
} else if (in_reg.test(val)) {
exp = has_not + 'in'
value = val.match(in_reg)[0]
} else if (range_reg.test(val)) {
exp = has_not + 'range'
value = val.match(range_reg)[0]
min = value.split('_TO_')[0]
max = value.split('_TO_')[1]
} else if (compare_reg.test(val)) {
exp = has_not + 'compare'
value = val.match(compare_reg)[0]
const _compareType = val.substring(0, val.match(compare_reg)['index'])
const idx = compareTypeList.findIndex((item) => item.label === _compareType)
compareType = compareTypeList[idx].value
} else if (!val.includes('*')) {
exp = has_not + 'is'
value = val
} else {
const resList = [
['contain', /(?<=\*).*(?=\*)/g],
['end_with', /(?<=\*).+/g],
['start_with', /.+(?=\*)/g],
]
for (let i = 0; i < 3; i++) {
const reg = resList[i]
if (reg[1].test(val)) {
exp = has_not + reg[0]
value = val.match(reg[1])[0]
break
}
}
}
return {
id: uuidv4(),
type,
property,
exp,
value,
min,
max,
compareType,
}
})
this.ruleList = [...expArray]
} else if (open) {
const _canSearchPreferenceAttrList = this.canSearchPreferenceAttrList.filter((attr) => !attr.is_password)
this.ruleList = isInitOne
? [
{
id: uuidv4(),
type: 'and',
property:
_canSearchPreferenceAttrList && _canSearchPreferenceAttrList.length
? _canSearchPreferenceAttrList[0].name
: undefined,
exp: 'is',
value: null,
},
]
: []
}
},
handleClear() {
this.ruleList = [
{
id: uuidv4(),
type: 'and',
property: this.canSearchPreferenceAttrList[0].name,
exp: 'is',
value: null,
},
]
this.filterExp = ''
this.visible = false
this.$emit('setExpFromFilter', this.filterExp)
},
handleSubmit() {
if (this.ruleList && this.ruleList.length) {
this.ruleList[0].type = 'and' // 增删后以防万一第一个不是and
this.filterExp = ''
const expList = this.ruleList.map((rule) => {
let singleRuleExp = ''
let _exp = rule.exp
if (rule.type === 'or') {
singleRuleExp += '-'
}
if (rule.exp.includes('~')) {
singleRuleExp += '~'
_exp = rule.exp.split('~')[1]
}
singleRuleExp += `${rule.property}:`
if (_exp === 'is') {
singleRuleExp += `${rule.value ?? ''}`
}
if (_exp === 'contain') {
singleRuleExp += `*${rule.value ?? ''}*`
}
if (_exp === 'start_with') {
singleRuleExp += `${rule.value ?? ''}*`
}
if (_exp === 'end_with') {
singleRuleExp += `*${rule.value ?? ''}`
}
if (_exp === 'value') {
singleRuleExp += `*`
}
if (_exp === 'in') {
singleRuleExp += `(${rule.value ?? ''})`
}
if (_exp === 'range') {
singleRuleExp += `[${rule.min}_TO_${rule.max}]`
}
if (_exp === 'compare') {
const idx = compareTypeList.findIndex((item) => item.value === rule.compareType)
singleRuleExp += `${compareTypeList[idx].label}${rule.value ?? ''}`
}
return singleRuleExp
})
this.filterExp = expList.join(',')
this.$emit('setExpFromFilter', this.filterExp)
} else {
this.$emit('setExpFromFilter', '')
}
this.visible = false
},
},
}
</script>
<style lang="less" scoped>
.table-filter {
.table-filter-add {
margin-top: 10px;
& > a {
padding: 2px 8px;
&:hover {
background-color: #f0faff;
border-radius: 5px;
}
}
}
.table-filter-extra-icon {
padding: 0px 2px;
&:hover {
display: inline-block;
border-radius: 5px;
background-color: #f0faff;
}
}
}
</style>
<style lang="less">
.table-filter-extra-operation {
.ant-popover-inner-content {
padding: 3px 4px;
.operation {
cursor: pointer;
width: 90px;
height: 30px;
line-height: 30px;
padding: 3px 4px;
border-radius: 5px;
transition: all 0.3s;
&:hover {
background-color: #f0faff;
}
> .anticon {
margin-right: 10px;
}
}
}
}
</style>

View File

@@ -341,7 +341,7 @@ export default {
},
}
</script>
<style lang="less" scoped>
<style scoped>
.pop_btn {
text-align: right;
margin-top: 24px;

View File

@@ -11,19 +11,10 @@
.ant-input {
box-shadow: none;
border: none;
background-color: #F7F8FA;
height: 30px;
line-height: 30px;
border-radius: 30px;
&:focus {
border: solid 1px #B1C9FF;
}
}
.cmdb-side-menu-search-focused {
.ant-input {
border: solid 1px #B1C9FF;
}
}
.ant-input-suffix {

View File

@@ -92,7 +92,7 @@ export default {
e.preventDefault()
e.stopPropagation()
this.$confirm({
title: this.$t('warning'),
title: this.$t('alert'),
content: this.$t('cmdb.preference.confirmcancelSub2', { name: menu.meta.title }),
onOk() {
const citypeId = menu.meta.typeId
@@ -171,6 +171,7 @@ export default {
},
renderMenuItem(menu) {
const isShowDot = menu.path.substr(0, 22) === '/cmdb/instances/types/'
const isShowGrant = menu.path.substr(0, 20) === '/cmdb/relationviews/'
const target = menu.meta.target || null
const tag = target && 'a' || 'router-link'
const props = { to: { name: menu.name } }
@@ -204,9 +205,11 @@ export default {
<a-icon type="menu" ref="extraEllipsis" class="custom-menu-extra-ellipsis"></a-icon>
</a-popover>
}
{isShowGrant && <a-icon class="custom-menu-extra-ellipsis" onClick={e => this.handlePerm(e, menu, 'RelationView')} type="user-add" />}
</span>
</tag>
{isShowDot && <CMDBGrant ref="cmdbGrantCIType" resourceType="CIType" app_id="cmdb" />}
{isShowGrant && <CMDBGrant ref="cmdbGrantRelationView" resourceType="RelationView" app_id="cmdb" />}
</Item>
)
},
@@ -310,7 +313,10 @@ export default {
<Item class={styles['cmdb-side-menu-search']}>
<a-input
ref="cmdbSideMenuSearchInputRef"
class={`ops-input ${this.$route.name === 'cmdb_resource_search' ? 'cmdb-side-menu-search-focused' : ''}`}
class={styles['cmdb-side-menu-search-input']}
style={{
border: this.$route.name === 'cmdb_resource_search' ? 'solid 1px #B1C9FF' : ''
}}
placeholder={this.$t('cmdbSearch')}
onPressEnter={(e) => {
this.jumpCMDBSearch(e.target.value)

View File

@@ -1,121 +1,121 @@
<template>
<vxe-table v-bind="$attrs" v-on="new$listeners" ref="xTable">
<slot></slot>
<template #empty>
<slot name="empty">
<div :style="{ paddingTop: '10px' }">
<img :style="{ width: '140px', height: '120px' }" :src="require('@/assets/data_empty.png')" />
<div>{{ $t('noData') }}</div>
</div>
</slot>
</template>
<template #loading>
<slot name="loading"></slot>
</template>
</vxe-table>
</template>
<script>
import _ from 'lodash'
// 该组件使用方法与vxe-table一致但调用它的方法时需先调用getVxetableRef()获取到vxe-table实体
export default {
name: 'OpsTable',
data() {
return {
// isShifting: false,
// lastIndex: -1,
lastSelected: [],
currentSelected: [],
}
},
computed: {
new$listeners() {
if (!Object.keys(this.$listeners).length) {
return this.$listeners
}
return Object.assign(this.$listeners, {
// 在这里覆盖原有的change事件
// 'checkbox-change': this.selectChangeEvent,
'checkbox-range-change': this.checkboxRangeChange,
'checkbox-range-start': this.checkboxRangeStart,
'checkbox-range-end': this.checkboxRangeEnd,
})
},
},
mounted() {
// window.onkeydown = (e) => {
// if (e.key === 'Shift') {
// this.isShifting = true
// }
// }
// window.onkeyup = (e) => {
// if (e.key === 'Shift') {
// this.isShifting = false
// this.lastIndex = -1
// }
// }
},
beforeDestroy() {
// window.onkeydown = ''
// window.onkeyup = ''
},
methods: {
getVxetableRef() {
return this.$refs.xTable
},
// selectChangeEvent(e) {
// const xTable = this.$refs.xTable
// const { lastIndex } = this
// const currentIndex = e.rowIndex
// const { tableData } = xTable.getTableData()
// if (lastIndex > -1 && this.isShifting) {
// let start = lastIndex
// let end = currentIndex
// if (lastIndex > currentIndex) {
// start = currentIndex
// end = lastIndex
// }
// const rangeData = tableData.slice(start, end + 1)
// xTable.setCheckboxRow(rangeData, true)
// }
// this.lastIndex = currentIndex
// this.$emit('checkbox-change', { ...e, records: xTable.getCheckboxRecords() })
// },
checkboxRangeStart(e) {
const xTable = this.$refs.xTable
const lastSelected = xTable.getCheckboxRecords()
const selectedReserve = xTable.getCheckboxReserveRecords()
this.lastSelected = [...lastSelected, ...selectedReserve]
this.$emit('checkbox-range-start', e)
},
checkboxRangeChange(e) {
const xTable = this.$refs.xTable
xTable.setCheckboxRow(this.lastSelected, true)
this.currentSelected = e.records
// this.lastSelected = [...new Set([...this.lastSelected, ...e.records])]
this.$emit('checkbox-range-change', {
...e,
records: [...xTable.getCheckboxRecords(), ...xTable.getCheckboxReserveRecords()],
})
},
checkboxRangeEnd(e) {
const xTable = this.$refs.xTable
const isAllSelected = this.currentSelected.every((item) => {
const _idx = this.lastSelected.findIndex((ele) => _.isEqual(ele, item))
return _idx > -1
})
if (isAllSelected) {
xTable.setCheckboxRow(this.currentSelected, false)
}
this.currentSelected = []
this.lastSelected = []
this.$emit('checkbox-range-end', {
...e,
records: [...xTable.getCheckboxRecords(), ...xTable.getCheckboxReserveRecords()],
})
},
},
}
</script>
<style lang="less"></style>
<template>
<vxe-table v-bind="$attrs" v-on="new$listeners" ref="xTable">
<slot></slot>
<template #empty>
<slot name="empty">
<div :style="{ paddingTop: '10px' }">
<img :style="{ width: '140px', height: '90px' }" :src="require('@/assets/data_empty.png')" />
<div>{{ $t('noData') }}</div>
</div>
</slot>
</template>
<template #loading>
<slot name="loading"></slot>
</template>
</vxe-table>
</template>
<script>
import _ from 'lodash'
// 该组件使用方法与vxe-table一致但调用它的方法时需先调用getVxetableRef()获取到vxe-table实体
export default {
name: 'OpsTable',
data() {
return {
// isShifting: false,
// lastIndex: -1,
lastSelected: [],
currentSelected: [],
}
},
computed: {
new$listeners() {
if (!Object.keys(this.$listeners).length) {
return this.$listeners
}
return Object.assign(this.$listeners, {
// 在这里覆盖原有的change事件
// 'checkbox-change': this.selectChangeEvent,
'checkbox-range-change': this.checkboxRangeChange,
'checkbox-range-start': this.checkboxRangeStart,
'checkbox-range-end': this.checkboxRangeEnd,
})
},
},
mounted() {
// window.onkeydown = (e) => {
// if (e.key === 'Shift') {
// this.isShifting = true
// }
// }
// window.onkeyup = (e) => {
// if (e.key === 'Shift') {
// this.isShifting = false
// this.lastIndex = -1
// }
// }
},
beforeDestroy() {
// window.onkeydown = ''
// window.onkeyup = ''
},
methods: {
getVxetableRef() {
return this.$refs.xTable
},
// selectChangeEvent(e) {
// const xTable = this.$refs.xTable
// const { lastIndex } = this
// const currentIndex = e.rowIndex
// const { tableData } = xTable.getTableData()
// if (lastIndex > -1 && this.isShifting) {
// let start = lastIndex
// let end = currentIndex
// if (lastIndex > currentIndex) {
// start = currentIndex
// end = lastIndex
// }
// const rangeData = tableData.slice(start, end + 1)
// xTable.setCheckboxRow(rangeData, true)
// }
// this.lastIndex = currentIndex
// this.$emit('checkbox-change', { ...e, records: xTable.getCheckboxRecords() })
// },
checkboxRangeStart(e) {
const xTable = this.$refs.xTable
const lastSelected = xTable.getCheckboxRecords()
const selectedReserve = xTable.getCheckboxReserveRecords()
this.lastSelected = [...lastSelected, ...selectedReserve]
this.$emit('checkbox-range-start', e)
},
checkboxRangeChange(e) {
const xTable = this.$refs.xTable
xTable.setCheckboxRow(this.lastSelected, true)
this.currentSelected = e.records
// this.lastSelected = [...new Set([...this.lastSelected, ...e.records])]
this.$emit('checkbox-range-change', {
...e,
records: [...xTable.getCheckboxRecords(), ...xTable.getCheckboxReserveRecords()],
})
},
checkboxRangeEnd(e) {
const xTable = this.$refs.xTable
const isAllSelected = this.currentSelected.every((item) => {
const _idx = this.lastSelected.findIndex((ele) => _.isEqual(ele, item))
return _idx > -1
})
if (isAllSelected) {
xTable.setCheckboxRow(this.currentSelected, false)
}
this.currentSelected = []
this.lastSelected = []
this.$emit('checkbox-range-end', {
...e,
records: [...xTable.getCheckboxRecords(), ...xTable.getCheckboxReserveRecords()],
})
},
},
}
</script>
<style lang="less"></style>

View File

@@ -226,16 +226,16 @@ export default {
right: 4px;
display: none;
&:hover {
color: @primary-color;
color: #1f78d1;
}
}
&:hover .ant-transfer-list-icon {
display: inline;
background-color: @primary-color_4;
background-color: #c0eaff;
border-radius: 4px;
}
}
.ant-transfer-list-content-item-selected {
background-color: ~'@{primary-color_8}35';
background-color: #f0faff;
}
</style>

View File

@@ -195,11 +195,11 @@ export default {
background-color: #fff;
height: calc(100vh - 64px);
margin-bottom: -24px;
padding: 20px;
padding: 24px;
.acl-resource-types-header {
width: 100%;
display: inline-flex;
margin-bottom: 20px;
margin-bottom: 15px;
align-items: center;
}
}

View File

@@ -358,11 +358,11 @@ export default {
background-color: #fff;
height: calc(100vh - 64px);
margin-bottom: -24px;
padding: 8px 20px 20px 20px;
padding: 12px 24px 24px 24px;
.acl-resources-header {
width: 100%;
display: inline-flex;
margin-bottom: 20px;
margin-bottom: 15px;
align-items: center;
justify-content: space-between;
.ant-switch {

View File

@@ -58,7 +58,10 @@
:title="$t('acl.visualRole')"
:width="120"
align="center"
:filters="visualRoleFilters"
:filters="[
{ label: $t('yes'), value: 1 },
{ label: $t('no'), value: 0 },
]"
:filterMultiple="false"
:filter-method="
({ value, row }) => {
@@ -152,10 +155,6 @@ export default {
pageSizeOptions: ['20', '50', '100', '200'],
searchName: '',
filterTableValue: { user_role: 1, user_only: 0 },
visualRoleFilters: [
{ label: this.$t('yes'), value: 1 },
{ label: this.$t('no'), value: 0 }
]
}
},
computed: {
@@ -292,11 +291,11 @@ export default {
background-color: #fff;
height: calc(100vh - 64px);
margin-bottom: -24px;
padding: 20px;
padding: 24px;
.acl-roles-header {
width: 100%;
display: inline-flex;
margin-bottom: 20px;
margin-bottom: 15px;
align-items: center;
.ant-checkbox-wrapper {
margin-left: auto;

View File

@@ -326,11 +326,11 @@ export default {
background-color: #fff;
height: calc(100vh - 64px);
margin-bottom: -24px;
padding: 20px;
padding: 24px;
.acl-trigger-header {
width: 100%;
display: inline-flex;
margin-bottom: 20px;
margin-bottom: 15px;
align-items: center;
}
}

View File

@@ -207,13 +207,6 @@ export function deleteTrigger(type_id, id) {
})
}
export function testTrigger(type_id, id) {
return axios({
url: `/v0.1/ci_types/${type_id}/triggers/${id}/test_notify`,
method: 'post',
})
}
// CMDB的模型和实例的授权接口
export function grantCiType(type_id, rid, data) {
return axios({

View File

@@ -84,10 +84,3 @@ export function getDCIMHistoryOperate(params) {
params
})
}
export function calcUnitFreeCount() {
return axios({
url: `/v0.1/dcim/rack/calc_u_free_count`,
method: 'POST'
})
}

View File

@@ -187,7 +187,7 @@ export default {
width: 12px;
height: 12px;
background-color: @primary-color;
border: solid 3px @primary-color_4;
border: solid 3px #E2E7FC;
border-radius: 50%;
}

View File

@@ -1,156 +1,150 @@
<template>
<div class="ci-type-grant">
<vxe-table
ref="xTable"
size="mini"
stripe
class="ops-stripe-table"
:data="filterTableData"
:max-height="`${tableHeight}px`"
:row-class-name="(params) => getCurrentRowClass(params, addedRids)"
>
<vxe-column field="name"></vxe-column>
<vxe-column v-for="col in columns" :key="col" :field="col" :title="permMap[col]">
<template #default="{row}">
<ReadCheckbox
v-if="['read'].includes(col.split('_')[0])"
:value="row[col.split('_')[0]]"
:valueKey="col"
:rid="row.rid"
@openReadGrantModal="() => openReadGrantModal(col, row)"
/>
<a-checkbox v-else-if="col === 'grant'" :checked="row[col]" @click="clickGrant(col, row)"></a-checkbox>
<a-checkbox @change="(e) => handleChange(e, col, row)" v-else v-model="row[col]"></a-checkbox>
</template>
</vxe-column>
<template #empty>
<div v-if="loading()" class="ci-type-grant-loading">
<a-icon type="loading" /> {{ $t('loading') }}
</div>
<div v-else>
<img :style="{ width: '100px' }" :src="require('@/assets/data_empty.png')" />
<div>{{ $t('noData') }}</div>
</div>
</template>
</vxe-table>
<a-space>
<span class="grant-button" @click="grantDepart">{{ $t('cmdb.components.grantUser') }}</span>
<span class="grant-button" @click="grantRole">{{ $t('cmdb.components.grantRole') }}</span>
</a-space>
</div>
</template>
<script>
import _ from 'lodash'
import { permMap } from './constants.js'
import { grantCiType, revokeCiType } from '../../api/CIType'
import ReadCheckbox from './readCheckbox.vue'
import { getCurrentRowClass } from './utils'
export default {
name: 'CiTypeGrant',
components: { ReadCheckbox },
inject: ['loading', 'isModal'],
props: {
CITypeId: {
type: Number,
default: null,
},
tableData: {
type: Array,
default: () => [],
},
grantType: {
type: String,
default: 'ci_type',
},
addedRids: {
type: Array,
default: () => [],
},
},
computed: {
filterTableData() {
const _tableData = this.tableData.filter((data) => {
const _intersection = _.intersection(
Object.keys(data),
this.columns.map((col) => col.split('_')[0])
)
return _intersection && _intersection.length
})
return _.uniqBy(_tableData, (item) => item.rid)
},
columns() {
if (this.grantType === 'ci_type') {
return ['config', 'grant']
}
return ['read_attr', 'read_ci', 'create', 'update', 'delete']
},
windowHeight() {
return this.$store.state.windowHeight
},
tableHeight() {
if (this.isModal) {
return (this.windowHeight - 104) / 2
}
return (this.windowHeight - 104) / 2 - 116
},
permMap() {
return permMap()
}
},
methods: {
getCurrentRowClass,
async handleChange(e, col, row) {
if (e.target.checked) {
await grantCiType(this.CITypeId, row.rid, { perms: [col] }).catch(() => {
this.$emit('getTableData')
})
} else {
await revokeCiType(this.CITypeId, row.rid, { perms: [col] }).catch(() => {
this.$emit('getTableData')
})
}
},
grantDepart() {
this.$emit('grantDepart', this.grantType)
},
grantRole() {
this.$emit('grantRole', this.grantType)
},
openReadGrantModal(col, row) {
this.$emit('openReadGrantModal', col, row)
},
clickGrant(col, row, rowIndex) {
if (!row[col]) {
this.handleChange({ target: { checked: true } }, col, row)
const _idx = this.tableData.findIndex((item) => item.rid === row.rid)
this.$set(this.tableData, _idx, { ...this.tableData[_idx], grant: true })
} else {
const that = this
this.$confirm({
title: that.$t('warning'),
content: that.$t('cmdb.components.confirmRevoke', { name: `${row.name}` }),
onOk() {
that.handleChange({ target: { checked: false } }, col, row)
const _idx = that.tableData.findIndex((item) => item.rid === row.rid)
that.$set(that.tableData, _idx, { ...that.tableData[_idx], grant: false })
},
})
}
},
},
}
</script>
<style lang="less" scoped>
.ci-type-grant {
padding: 10px 0;
&-loading {
height: 200px;
line-height: 200px;
color: @primary-color;
}
}
</style>
<template>
<div class="ci-type-grant">
<vxe-table
ref="xTable"
size="mini"
stripe
class="ops-stripe-table"
:data="filterTableData"
:max-height="`${tableHeight}px`"
:row-style="(params) => getCurrentRowStyle(params, addedRids)"
>
<vxe-column field="name"></vxe-column>
<vxe-column v-for="col in columns" :key="col" :field="col" :title="permMap[col]">
<template #default="{row}">
<ReadCheckbox
v-if="['read'].includes(col.split('_')[0])"
:value="row[col.split('_')[0]]"
:valueKey="col"
:rid="row.rid"
@openReadGrantModal="() => openReadGrantModal(col, row)"
/>
<a-checkbox v-else-if="col === 'grant'" :checked="row[col]" @click="clickGrant(col, row)"></a-checkbox>
<a-checkbox @change="(e) => handleChange(e, col, row)" v-else v-model="row[col]"></a-checkbox>
</template>
</vxe-column>
<template #empty>
<div v-if="loading()" style="height: 200px; line-height: 200px;color:#2F54EB">
<a-icon type="loading" /> {{ $t('loading') }}
</div>
<div v-else>
<img :style="{ width: '100px' }" :src="require('@/assets/data_empty.png')" />
<div>{{ $t('noData') }}</div>
</div>
</template>
</vxe-table>
<a-space>
<span class="grant-button" @click="grantDepart">{{ $t('cmdb.components.grantUser') }}</span>
<span class="grant-button" @click="grantRole">{{ $t('cmdb.components.grantRole') }}</span>
</a-space>
</div>
</template>
<script>
import _ from 'lodash'
import { permMap } from './constants.js'
import { grantCiType, revokeCiType } from '../../api/CIType'
import ReadCheckbox from './readCheckbox.vue'
import { getCurrentRowStyle } from './utils'
export default {
name: 'CiTypeGrant',
components: { ReadCheckbox },
inject: ['loading', 'isModal'],
props: {
CITypeId: {
type: Number,
default: null,
},
tableData: {
type: Array,
default: () => [],
},
grantType: {
type: String,
default: 'ci_type',
},
addedRids: {
type: Array,
default: () => [],
},
},
computed: {
filterTableData() {
const _tableData = this.tableData.filter((data) => {
const _intersection = _.intersection(
Object.keys(data),
this.columns.map((col) => col.split('_')[0])
)
return _intersection && _intersection.length
})
return _.uniqBy(_tableData, (item) => item.rid)
},
columns() {
if (this.grantType === 'ci_type') {
return ['config', 'grant']
}
return ['read_attr', 'read_ci', 'create', 'update', 'delete']
},
windowHeight() {
return this.$store.state.windowHeight
},
tableHeight() {
if (this.isModal) {
return (this.windowHeight - 104) / 2
}
return (this.windowHeight - 104) / 2 - 116
},
permMap() {
return permMap()
}
},
methods: {
getCurrentRowStyle,
async handleChange(e, col, row) {
if (e.target.checked) {
await grantCiType(this.CITypeId, row.rid, { perms: [col] }).catch(() => {
this.$emit('getTableData')
})
} else {
await revokeCiType(this.CITypeId, row.rid, { perms: [col] }).catch(() => {
this.$emit('getTableData')
})
}
},
grantDepart() {
this.$emit('grantDepart', this.grantType)
},
grantRole() {
this.$emit('grantRole', this.grantType)
},
openReadGrantModal(col, row) {
this.$emit('openReadGrantModal', col, row)
},
clickGrant(col, row, rowIndex) {
if (!row[col]) {
this.handleChange({ target: { checked: true } }, col, row)
const _idx = this.tableData.findIndex((item) => item.rid === row.rid)
this.$set(this.tableData, _idx, { ...this.tableData[_idx], grant: true })
} else {
const that = this
this.$confirm({
title: that.$t('warning'),
content: that.$t('cmdb.components.confirmRevoke', { name: `${row.name}` }),
onOk() {
that.handleChange({ target: { checked: false } }, col, row)
const _idx = that.tableData.findIndex((item) => item.rid === row.rid)
that.$set(that.tableData, _idx, { ...that.tableData[_idx], grant: false })
},
})
}
},
},
}
</script>
<style lang="less" scoped>
.ci-type-grant {
padding: 10px 0;
}
</style>

View File

@@ -1,382 +1,371 @@
<template>
<div class="cmdb-grant" :style="{ }">
<template v-if="cmdbGrantType.includes('ci_type')">
<div class="cmdb-grant-title">{{ $t('cmdb.components.ciTypeGrant') }}</div>
<CiTypeGrant
:CITypeId="CITypeId"
:tableData="tableData"
grantType="ci_type"
@grantDepart="grantDepart"
@grantRole="grantRole"
@getTableData="getTableData"
ref="grant_ci_type"
:addedRids="addedRids"
/>
</template>
<template
v-if="
cmdbGrantType.includes('ci_type,ci') || (cmdbGrantType.includes('ci') && !cmdbGrantType.includes('ci_type'))
"
>
<div class="cmdb-grant-title">{{ $t('cmdb.components.ciGrant') }}</div>
<CiTypeGrant
:CITypeId="CITypeId"
:tableData="tableData"
grantType="ci"
@grantDepart="grantDepart"
@grantRole="grantRole"
@getTableData="getTableData"
@openReadGrantModal="openReadGrantModal"
ref="grant_ci"
:addedRids="addedRids"
/>
</template>
<template v-if="cmdbGrantType.includes('type_relation')">
<div class="cmdb-grant-title">{{ $t('cmdb.components.relationGrant') }}</div>
<TypeRelationGrant
:typeRelationIds="typeRelationIds"
:tableData="tableData"
grantType="type_relation"
@grantDepart="grantDepart"
@grantRole="grantRole"
@getTableData="getTableData"
ref="grant_type_relation"
:addedRids="addedRids"
/>
</template>
<template v-if="cmdbGrantType.includes('relation_view')">
<div class="cmdb-grant-title">{{ resourceTypeName }}{{ $t('cmdb.components.perm') }}</div>
<RelationViewGrant
:resourceTypeName="resourceTypeName"
:tableData="tableData"
grantType="relation_view"
@grantDepart="grantDepart"
@grantRole="grantRole"
@getTableData="getTableData"
ref="grant_relation_view"
:addedRids="addedRids"
/>
</template>
<template v-if="cmdbGrantType.includes('TopologyView')">
<div class="cmdb-grant-title">{{ resourceTypeName }}{{ $t('cmdb.components.perm') }}</div>
<TopologyViewGrant
:resourceTypeName="resourceTypeName"
:tableData="tableData"
:viewId="CITypeId"
grantType="TopologyView"
@grantDepart="grantDepart"
@grantRole="grantRole"
@getTableData="getTableData"
ref="grantTopologyView"
:addedRids="addedRids"
/>
</template>
<GrantModal ref="grantModal" @handleOk="handleOk" />
<ReadGrantModal ref="readGrantModal" :CITypeId="CITypeId" @updateTableDataRead="updateTableDataRead" />
</div>
</template>
<script>
import { mapState } from 'vuex'
import CiTypeGrant from './ciTypeGrant.vue'
import TypeRelationGrant from './typeRelationGrant.vue'
import { searchResource } from '@/modules/acl/api/resource'
import { getResourcePerms } from '@/modules/acl/api/permission'
import GrantModal from './grantModal.vue'
import ReadGrantModal from './readGrantModal'
import RelationViewGrant from './relationViewGrant.vue'
import TopologyViewGrant from './topologyViewGrant.vue'
import { getCITypeGroupById, ciTypeFilterPermissions } from '../../api/CIType'
import { CI_DEFAULT_ATTR } from '@/modules/cmdb/utils/const.js'
export default {
name: 'GrantComp',
components: { CiTypeGrant, TypeRelationGrant, RelationViewGrant, TopologyViewGrant, GrantModal, ReadGrantModal },
props: {
CITypeId: {
type: Number,
default: null,
},
resourceTypeName: {
type: String,
default: '',
},
resourceType: {
type: String,
default: 'CIType',
},
app_id: {
type: String,
default: 'cmdb',
},
cmdbGrantType: {
type: String,
default: 'ci_type,ci',
},
typeRelationIds: {
type: Array,
default: null,
},
isModal: {
type: Boolean,
default: false,
},
},
inject: ['resource_type'],
data() {
return {
tableData: [],
grantType: '',
resource_id: null,
attrGroup: [],
filerPerimissions: {},
loading: false,
addedRids: [], // added rid this time
}
},
computed: {
...mapState({
allEmployees: (state) => state.user.allEmployees,
allDepartments: (state) => state.user.allDepartments,
}),
child_resource_type() {
return this.resource_type()
},
windowHeight() {
return this.$store.state.windowHeight
},
},
provide() {
return {
attrGroup: () => {
return this.attrGroup
},
filerPerimissions: () => {
return this.filerPerimissions
},
loading: () => {
return this.loading
},
isModal: this.isModal,
}
},
watch: {
resourceTypeName: {
immediate: true,
handler() {
this.init()
},
},
CITypeId: {
immediate: true,
handler() {
if (this.CITypeId && this.cmdbGrantType.includes('ci')) {
this.getFilterPermissions()
this.getAttrGroup()
}
},
},
},
mounted() {},
methods: {
getAttrGroup() {
getCITypeGroupById(this.CITypeId, { need_other: true }).then((res) => {
this.attrGroup = res
})
},
getFilterPermissions() {
ciTypeFilterPermissions(this.CITypeId).then((res) => {
Object.keys(res).forEach((key) => {
const attr_filter = res?.[key]?.attr_filter
if (attr_filter?.length) {
res[key].attr_filter = attr_filter.filter((item) => ![CI_DEFAULT_ATTR.UPDATE_USER, CI_DEFAULT_ATTR.UPDATE_TIME].includes(item))
}
})
this.filerPerimissions = res
})
},
async init() {
const _find = this.child_resource_type.groups.find((item) => item.name === this.resourceType)
const resource_type_id = _find?.id ?? 0
const res = await searchResource({
app_id: this.app_id,
resource_type_id,
page_size: 9999,
})
const _tempFind = res.resources.find((item) => item.name === this.resourceTypeName)
console.log(this.resourceTypeName)
this.resource_id = _tempFind?.id || 0
this.getTableData()
},
async getTableData() {
this.loading = true
const _tableData = await getResourcePerms(this.resource_id, { need_users: 0 })
const perms = []
for (const key in _tableData) {
const obj = {}
obj.name = key
_tableData[key].perms.forEach((perm) => {
obj[`${perm.name}`] = true
obj.rid = perm?.rid ?? null
})
perms.push(obj)
}
this.tableData = perms
this.loading = false
},
// Grant the department in common-setting and get the roleid from it
grantDepart(grantType) {
this.$refs.grantModal.open('depart')
this.grantType = grantType
},
// Grant the oldest role permissions
grantRole(grantType) {
this.$refs.grantModal.open('role')
this.grantType = grantType
},
handleOk(params, type) {
const { grantType } = this
let rids
if (type === 'depart') {
rids = [
...params.department.map((rid) => {
const _find = this.allDepartments.find((dep) => dep.acl_rid === rid)
return { rid, name: _find?.department_name ?? rid }
}),
...params.user.map((rid) => {
const _find = this.allEmployees.find((dep) => dep.acl_rid === rid)
return { rid, name: _find?.nickname ?? rid }
}),
]
}
if (type === 'role') {
rids = [
...params.map((role) => {
return { rid: role.id, name: role.name }
}),
]
}
if (grantType === 'ci_type') {
this.tableData.unshift(
...rids.map(({ rid, name }) => {
const _find = this.tableData.find((item) => item.rid === rid)
return {
rid,
name,
conifg: false,
grant: false,
..._find,
}
})
)
}
if (grantType === 'ci') {
this.tableData.unshift(
...rids.map(({ rid, name }) => {
const _find = this.tableData.find((item) => item.rid === rid)
return {
rid,
name,
read_attr: false,
read_ci: false,
create: false,
update: false,
delete: false,
..._find,
}
})
)
}
if (grantType === 'type_relation') {
this.tableData.unshift(
...rids.map(({ rid, name }) => {
return {
rid,
name,
create: false,
grant: false,
delete: false,
}
})
)
}
if (grantType === 'relation_view') {
this.tableData.unshift(
...rids.map(({ rid, name }) => {
return {
rid,
name,
read: false,
grant: false,
}
})
)
}
if (grantType === 'TopologyView') {
this.tableData.unshift(
...rids.map(({ rid, name }) => {
return {
rid,
name,
read: false,
update: false,
delete: false,
grant: false,
}
})
)
}
this.addedRids = rids
this.$nextTick(() => {
setTimeout(() => {
this.$refs[`grant_${grantType}`].$refs.xTable.elemStore['main-body-wrapper'].scrollTo(0, 0)
}, 300)
})
},
openReadGrantModal(col, row) {
this.$refs.readGrantModal.open(col, row)
},
updateTableDataRead(row, hasRead) {
const _idx = this.tableData.findIndex((item) => item.rid === row.rid)
this.$set(this.tableData, _idx, { ...this.tableData[_idx], read: hasRead })
this.getFilterPermissions()
},
},
}
</script>
<style lang="less" scoped>
.cmdb-grant {
position: relative;
padding: 0 20px;
overflow: auto;
.cmdb-grant-title {
border-left: 4px solid @primary-color;
padding-left: 10px;
}
}
</style>
<style lang="less">
.cmdb-grant {
.grant-button {
padding: 6px 8px;
color: @primary-color;
background-color: @primary-color_5;
border-radius: 2px;
cursor: pointer;
margin: 15px 0;
display: inline-block;
transition: all 0.3s;
z-index: 1;
.btn-wave-hover(@primary-color_4, -1);
}
.grant-table-row-focus {
background-color: @primary-color_8;
}
}
</style>
<template>
<div class="cmdb-grant" :style="{ }">
<template v-if="cmdbGrantType.includes('ci_type')">
<div class="cmdb-grant-title">{{ $t('cmdb.components.ciTypeGrant') }}</div>
<CiTypeGrant
:CITypeId="CITypeId"
:tableData="tableData"
grantType="ci_type"
@grantDepart="grantDepart"
@grantRole="grantRole"
@getTableData="getTableData"
ref="grant_ci_type"
:addedRids="addedRids"
/>
</template>
<template
v-if="
cmdbGrantType.includes('ci_type,ci') || (cmdbGrantType.includes('ci') && !cmdbGrantType.includes('ci_type'))
"
>
<div class="cmdb-grant-title">{{ $t('cmdb.components.ciGrant') }}</div>
<CiTypeGrant
:CITypeId="CITypeId"
:tableData="tableData"
grantType="ci"
@grantDepart="grantDepart"
@grantRole="grantRole"
@getTableData="getTableData"
@openReadGrantModal="openReadGrantModal"
ref="grant_ci"
:addedRids="addedRids"
/>
</template>
<template v-if="cmdbGrantType.includes('type_relation')">
<div class="cmdb-grant-title">{{ $t('cmdb.components.relationGrant') }}</div>
<TypeRelationGrant
:typeRelationIds="typeRelationIds"
:tableData="tableData"
grantType="type_relation"
@grantDepart="grantDepart"
@grantRole="grantRole"
@getTableData="getTableData"
ref="grant_type_relation"
:addedRids="addedRids"
/>
</template>
<template v-if="cmdbGrantType.includes('relation_view')">
<div class="cmdb-grant-title">{{ resourceTypeName }}{{ $t('cmdb.components.perm') }}</div>
<RelationViewGrant
:resourceTypeName="resourceTypeName"
:tableData="tableData"
grantType="relation_view"
@grantDepart="grantDepart"
@grantRole="grantRole"
@getTableData="getTableData"
ref="grant_relation_view"
:addedRids="addedRids"
/>
</template>
<template v-if="cmdbGrantType.includes('TopologyView')">
<div class="cmdb-grant-title">{{ resourceTypeName }}{{ $t('cmdb.components.perm') }}</div>
<TopologyViewGrant
:resourceTypeName="resourceTypeName"
:tableData="tableData"
:viewId="CITypeId"
grantType="TopologyView"
@grantDepart="grantDepart"
@grantRole="grantRole"
@getTableData="getTableData"
ref="grantTopologyView"
:addedRids="addedRids"
/>
</template>
<GrantModal ref="grantModal" @handleOk="handleOk" />
<ReadGrantModal ref="readGrantModal" :CITypeId="CITypeId" @updateTableDataRead="updateTableDataRead" />
</div>
</template>
<script>
import { mapState } from 'vuex'
import CiTypeGrant from './ciTypeGrant.vue'
import TypeRelationGrant from './typeRelationGrant.vue'
import { searchResource } from '@/modules/acl/api/resource'
import { getResourcePerms } from '@/modules/acl/api/permission'
import GrantModal from './grantModal.vue'
import ReadGrantModal from './readGrantModal'
import RelationViewGrant from './relationViewGrant.vue'
import TopologyViewGrant from './topologyViewGrant.vue'
import { getCITypeGroupById, ciTypeFilterPermissions } from '../../api/CIType'
export default {
name: 'GrantComp',
components: { CiTypeGrant, TypeRelationGrant, RelationViewGrant, TopologyViewGrant, GrantModal, ReadGrantModal },
props: {
CITypeId: {
type: Number,
default: null,
},
resourceTypeName: {
type: String,
default: '',
},
resourceType: {
type: String,
default: 'CIType',
},
app_id: {
type: String,
default: 'cmdb',
},
cmdbGrantType: {
type: String,
default: 'ci_type,ci',
},
typeRelationIds: {
type: Array,
default: null,
},
isModal: {
type: Boolean,
default: false,
},
},
inject: ['resource_type'],
data() {
return {
tableData: [],
grantType: '',
resource_id: null,
attrGroup: [],
filerPerimissions: {},
loading: false,
addedRids: [], // added rid this time
}
},
computed: {
...mapState({
allEmployees: (state) => state.user.allEmployees,
allDepartments: (state) => state.user.allDepartments,
}),
child_resource_type() {
return this.resource_type()
},
windowHeight() {
return this.$store.state.windowHeight
},
},
provide() {
return {
attrGroup: () => {
return this.attrGroup
},
filerPerimissions: () => {
return this.filerPerimissions
},
loading: () => {
return this.loading
},
isModal: this.isModal,
}
},
watch: {
resourceTypeName: {
immediate: true,
handler() {
this.init()
},
},
CITypeId: {
immediate: true,
handler() {
if (this.CITypeId && this.cmdbGrantType.includes('ci')) {
this.getFilterPermissions()
this.getAttrGroup()
}
},
},
},
mounted() {},
methods: {
getAttrGroup() {
getCITypeGroupById(this.CITypeId, { need_other: true }).then((res) => {
this.attrGroup = res
})
},
getFilterPermissions() {
ciTypeFilterPermissions(this.CITypeId).then((res) => {
this.filerPerimissions = res
})
},
async init() {
const _find = this.child_resource_type.groups.find((item) => item.name === this.resourceType)
const resource_type_id = _find?.id ?? 0
const res = await searchResource({
app_id: this.app_id,
resource_type_id,
page_size: 9999,
})
const _tempFind = res.resources.find((item) => item.name === this.resourceTypeName)
console.log(this.resourceTypeName)
this.resource_id = _tempFind?.id || 0
this.getTableData()
},
async getTableData() {
this.loading = true
const _tableData = await getResourcePerms(this.resource_id, { need_users: 0 })
const perms = []
for (const key in _tableData) {
const obj = {}
obj.name = key
_tableData[key].perms.forEach((perm) => {
obj[`${perm.name}`] = true
obj.rid = perm?.rid ?? null
})
perms.push(obj)
}
this.tableData = perms
this.loading = false
},
// Grant the department in common-setting and get the roleid from it
grantDepart(grantType) {
this.$refs.grantModal.open('depart')
this.grantType = grantType
},
// Grant the oldest role permissions
grantRole(grantType) {
this.$refs.grantModal.open('role')
this.grantType = grantType
},
handleOk(params, type) {
const { grantType } = this
let rids
if (type === 'depart') {
rids = [
...params.department.map((rid) => {
const _find = this.allDepartments.find((dep) => dep.acl_rid === rid)
return { rid, name: _find?.department_name ?? rid }
}),
...params.user.map((rid) => {
const _find = this.allEmployees.find((dep) => dep.acl_rid === rid)
return { rid, name: _find?.nickname ?? rid }
}),
]
}
if (type === 'role') {
rids = [
...params.map((role) => {
return { rid: role.id, name: role.name }
}),
]
}
if (grantType === 'ci_type') {
this.tableData.unshift(
...rids.map(({ rid, name }) => {
const _find = this.tableData.find((item) => item.rid === rid)
return {
rid,
name,
conifg: false,
grant: false,
..._find,
}
})
)
}
if (grantType === 'ci') {
this.tableData.unshift(
...rids.map(({ rid, name }) => {
const _find = this.tableData.find((item) => item.rid === rid)
return {
rid,
name,
read_attr: false,
read_ci: false,
create: false,
update: false,
delete: false,
..._find,
}
})
)
}
if (grantType === 'type_relation') {
this.tableData.unshift(
...rids.map(({ rid, name }) => {
return {
rid,
name,
create: false,
grant: false,
delete: false,
}
})
)
}
if (grantType === 'relation_view') {
this.tableData.unshift(
...rids.map(({ rid, name }) => {
return {
rid,
name,
read: false,
grant: false,
}
})
)
}
if (grantType === 'TopologyView') {
this.tableData.unshift(
...rids.map(({ rid, name }) => {
return {
rid,
name,
read: false,
update: false,
delete: false,
grant: false,
}
})
)
}
this.addedRids = rids
this.$nextTick(() => {
setTimeout(() => {
this.$refs[`grant_${grantType}`].$refs.xTable.elemStore['main-body-wrapper'].scrollTo(0, 0)
}, 300)
})
},
openReadGrantModal(col, row) {
this.$refs.readGrantModal.open(col, row)
},
updateTableDataRead(row, hasRead) {
const _idx = this.tableData.findIndex((item) => item.rid === row.rid)
this.$set(this.tableData, _idx, { ...this.tableData[_idx], read: hasRead })
this.getFilterPermissions()
},
},
}
</script>
<style lang="less" scoped>
.cmdb-grant {
position: relative;
padding: 0 20px;
overflow: auto;
.cmdb-grant-title {
border-left: 4px solid @primary-color;
padding-left: 10px;
}
}
</style>
<style lang="less">
.cmdb-grant {
.grant-button {
padding: 6px 8px;
color: @primary-color;
background-color: @primary-color_5;
border-radius: 2px;
cursor: pointer;
margin: 15px 0;
display: inline-block;
transition: all 0.3s;
&:hover {
box-shadow: 2px 3px 4px @primary-color_5;
}
}
}
</style>

View File

@@ -1,98 +1,98 @@
<template>
<div class="ci-relation-grant">
<vxe-table
ref="xTable"
size="mini"
stripe
class="ops-stripe-table"
:data="tableData"
:max-height="`${tableHeight}px`"
:row-class-name="(params) => getCurrentRowClass(params, addedRids)"
>
<vxe-column field="name"></vxe-column>
<vxe-column v-for="col in columns" :key="col" :field="col" :title="permMap[col]">
<template #default="{row}">
<a-checkbox @change="(e) => handleChange(e, col, row)" v-model="row[col]"></a-checkbox>
</template>
</vxe-column>
</vxe-table>
<a-space>
<span class="grant-button" @click="grantDepart">{{ $t('cmdb.components.grantUser') }}</span>
<span class="grant-button" @click="grantRole">{{ $t('cmdb.components.grantRole') }}</span>
</a-space>
</div>
</template>
<script>
import { permMap } from './constants.js'
import { grantRelationView, revokeRelationView } from '../../api/preference.js'
import { getCurrentRowClass } from './utils'
export default {
name: 'RelationViewGrant',
inject: ['loading', 'isModal'],
props: {
resourceTypeName: {
type: String,
default: '',
},
tableData: {
type: Array,
default: () => [],
},
grantType: {
type: String,
default: 'relation_view',
},
addedRids: {
type: Array,
default: () => [],
},
},
data() {
return {
columns: ['read', 'grant'],
}
},
computed: {
windowHeight() {
return this.$store.state.windowHeight
},
tableHeight() {
if (this.isModal) {
return (this.windowHeight - 104) / 2
}
return (this.windowHeight - 104) / 2 - 116
},
permMap() {
return permMap()
}
},
methods: {
getCurrentRowClass,
grantDepart() {
this.$emit('grantDepart', this.grantType)
},
grantRole() {
this.$emit('grantRole', this.grantType)
},
handleChange(e, col, row) {
if (e.target.checked) {
grantRelationView(row.rid, { perms: [col], name: this.resourceTypeName }).catch(() => {
this.$emit('getTableData')
})
} else {
revokeRelationView(row.rid, { perms: [col], name: this.resourceTypeName }).catch(() => {
this.$emit('getTableData')
})
}
},
},
}
</script>
<style lang="less" scoped>
.ci-relation-grant {
padding: 10px 0;
}
</style>
<template>
<div class="ci-relation-grant">
<vxe-table
ref="xTable"
size="mini"
stripe
class="ops-stripe-table"
:data="tableData"
:max-height="`${tableHeight}px`"
:row-style="(params) => getCurrentRowStyle(params, addedRids)"
>
<vxe-column field="name"></vxe-column>
<vxe-column v-for="col in columns" :key="col" :field="col" :title="permMap[col]">
<template #default="{row}">
<a-checkbox @change="(e) => handleChange(e, col, row)" v-model="row[col]"></a-checkbox>
</template>
</vxe-column>
</vxe-table>
<a-space>
<span class="grant-button" @click="grantDepart">{{ $t('cmdb.components.grantUser') }}</span>
<span class="grant-button" @click="grantRole">{{ $t('cmdb.components.grantRole') }}</span>
</a-space>
</div>
</template>
<script>
import { permMap } from './constants.js'
import { grantRelationView, revokeRelationView } from '../../api/preference.js'
import { getCurrentRowStyle } from './utils'
export default {
name: 'RelationViewGrant',
inject: ['loading', 'isModal'],
props: {
resourceTypeName: {
type: String,
default: '',
},
tableData: {
type: Array,
default: () => [],
},
grantType: {
type: String,
default: 'relation_view',
},
addedRids: {
type: Array,
default: () => [],
},
},
data() {
return {
columns: ['read', 'grant'],
}
},
computed: {
windowHeight() {
return this.$store.state.windowHeight
},
tableHeight() {
if (this.isModal) {
return (this.windowHeight - 104) / 2
}
return (this.windowHeight - 104) / 2 - 116
},
permMap() {
return permMap()
}
},
methods: {
getCurrentRowStyle,
grantDepart() {
this.$emit('grantDepart', this.grantType)
},
grantRole() {
this.$emit('grantRole', this.grantType)
},
handleChange(e, col, row) {
if (e.target.checked) {
grantRelationView(row.rid, { perms: [col], name: this.resourceTypeName }).catch(() => {
this.$emit('getTableData')
})
} else {
revokeRelationView(row.rid, { perms: [col], name: this.resourceTypeName }).catch(() => {
this.$emit('getTableData')
})
}
},
},
}
</script>
<style lang="less" scoped>
.ci-relation-grant {
padding: 10px 0;
}
</style>

View File

@@ -1,118 +1,122 @@
<template>
<a-modal :visible="visible" @cancel="handleCancel" @ok="handleOK" :title="$t('revoke')">
<a-form-model :model="form" :label-col="{ span: 4 }" :wrapper-col="{ span: 16 }">
<a-form-model-item :label="$t('user')">
<EmployeeTreeSelect
class="custom-treeselect custom-treeselect-white"
:style="{
'--custom-height': '32px',
lineHeight: '32px',
'--custom-multiple-lineHeight': '18px',
}"
:multiple="true"
v-model="form.users"
:placeholder="$t('cmdb.serviceTree.userPlaceholder')"
:idType="2"
departmentKey="acl_rid"
employeeKey="acl_rid"
/>
</a-form-model-item>
<a-form-model-item :label="$t('role')">
<treeselect
v-model="form.roles"
:multiple="true"
:options="filterAllRoles"
class="custom-treeselect custom-treeselect-white"
:style="{
'--custom-height': '32px',
lineHeight: '32px',
'--custom-multiple-lineHeight': '18px',
}"
:limit="10"
:limitText="(count) => `+ ${count}`"
:normalizer="
(node) => {
return {
id: node.id,
label: node.name,
}
}
"
appendToBody
zIndex="1050"
:placeholder="$t('cmdb.serviceTree.rolePlaceholder')"
@search-change="searchRole"
/>
</a-form-model-item>
</a-form-model>
</a-modal>
</template>
<script>
import EmployeeTreeSelect from '@/views/setting/components/employeeTreeSelect.vue'
import { getAllDepAndEmployee } from '@/api/company'
import { searchRole } from '@/modules/acl/api/role'
export default {
name: 'RevokeModal',
components: { EmployeeTreeSelect },
data() {
return {
visible: false,
form: {
users: undefined,
roles: undefined,
},
allTreeDepAndEmp: [],
allRoles: [],
filterAllRoles: [],
}
},
provide() {
return {
provide_allTreeDepAndEmp: () => {
return this.allTreeDepAndEmp
},
}
},
mounted() {
this.getAllDepAndEmployee()
this.loadRoles()
},
methods: {
async loadRoles() {
const res = await searchRole({ page_size: 9999, app_id: 'cmdb', is_all: true })
this.allRoles = res.roles
this.filterAllRoles = this.allRoles.slice(0, 100)
},
getAllDepAndEmployee() {
getAllDepAndEmployee({ block: 0 }).then((res) => {
this.allTreeDepAndEmp = res
})
},
open() {
this.visible = true
this.$nextTick(() => {
this.form = {
users: undefined,
roles: undefined,
}
})
},
handleCancel() {
this.visible = false
},
searchRole(searchQuery) {
this.filterAllRoles = this.allRoles
.filter((item) => item.name.toLowerCase().includes(searchQuery.toLowerCase()))
.slice(0, 100)
},
handleOK() {
this.$emit('handleRevoke', this.form)
this.handleCancel()
},
},
}
</script>
<style></style>
<template>
<a-modal :visible="visible" @cancel="handleCancel" @ok="handleOK" :title="$t('revoke')">
<a-form-model :model="form" :label-col="{ span: 4 }" :wrapper-col="{ span: 16 }">
<a-form-model-item :label="$t('user')">
<EmployeeTreeSelect
class="custom-treeselect custom-treeselect-bgcAndBorder"
:style="{
'--custom-height': '32px',
lineHeight: '32px',
'--custom-bg-color': '#fff',
'--custom-border': '1px solid #d9d9d9',
'--custom-multiple-lineHeight': '18px',
}"
:multiple="true"
v-model="form.users"
:placeholder="$t('cmdb.serviceTree.userPlaceholder')"
:idType="2"
departmentKey="acl_rid"
employeeKey="acl_rid"
/>
</a-form-model-item>
<a-form-model-item :label="$t('role')">
<treeselect
v-model="form.roles"
:multiple="true"
:options="filterAllRoles"
class="custom-treeselect custom-treeselect-bgcAndBorder"
:style="{
'--custom-height': '32px',
lineHeight: '32px',
'--custom-bg-color': '#fff',
'--custom-border': '1px solid #d9d9d9',
'--custom-multiple-lineHeight': '18px',
}"
:limit="10"
:limitText="(count) => `+ ${count}`"
:normalizer="
(node) => {
return {
id: node.id,
label: node.name,
}
}
"
appendToBody
zIndex="1050"
:placeholder="$t('cmdb.serviceTree.rolePlaceholder')"
@search-change="searchRole"
/>
</a-form-model-item>
</a-form-model>
</a-modal>
</template>
<script>
import EmployeeTreeSelect from '@/views/setting/components/employeeTreeSelect.vue'
import { getAllDepAndEmployee } from '@/api/company'
import { searchRole } from '@/modules/acl/api/role'
export default {
name: 'RevokeModal',
components: { EmployeeTreeSelect },
data() {
return {
visible: false,
form: {
users: undefined,
roles: undefined,
},
allTreeDepAndEmp: [],
allRoles: [],
filterAllRoles: [],
}
},
provide() {
return {
provide_allTreeDepAndEmp: () => {
return this.allTreeDepAndEmp
},
}
},
mounted() {
this.getAllDepAndEmployee()
this.loadRoles()
},
methods: {
async loadRoles() {
const res = await searchRole({ page_size: 9999, app_id: 'cmdb', is_all: true })
this.allRoles = res.roles
this.filterAllRoles = this.allRoles.slice(0, 100)
},
getAllDepAndEmployee() {
getAllDepAndEmployee({ block: 0 }).then((res) => {
this.allTreeDepAndEmp = res
})
},
open() {
this.visible = true
this.$nextTick(() => {
this.form = {
users: undefined,
roles: undefined,
}
})
},
handleCancel() {
this.visible = false
},
searchRole(searchQuery) {
this.filterAllRoles = this.allRoles
.filter((item) => item.name.toLowerCase().includes(searchQuery.toLowerCase()))
.slice(0, 100)
},
handleOK() {
this.$emit('handleRevoke', this.form)
this.handleCancel()
},
},
}
</script>
<style></style>

View File

@@ -8,7 +8,7 @@
:data="tableData"
:max-height="`${tableHeight}px`"
:scroll-y="{enabled: true}"
:row-class-name="(params) => getCurrentRowClass(params, addedRids)"
:row-style="(params) => getCurrentRowStyle(params, addedRids)"
>
<vxe-column field="name"></vxe-column>
<vxe-column v-for="col in columns" :key="col" :field="col" :title="permMap[col]">
@@ -27,7 +27,7 @@
<script>
import { permMap } from './constants.js'
import { grantTopologyView, revokeTopologyView } from '@/modules/cmdb/api/topology.js'
import { getCurrentRowClass } from './utils'
import { getCurrentRowStyle } from './utils'
export default {
name: 'TopologyViewGrant',
inject: ['loading', 'isModal'],
@@ -73,7 +73,7 @@ export default {
}
},
methods: {
getCurrentRowClass,
getCurrentRowStyle,
grantDepart() {
this.$emit('grantDepart', this.grantType)
},

View File

@@ -1,100 +1,100 @@
<template>
<div class="ci-relation-grant">
<vxe-table
ref="xTable"
size="mini"
stripe
class="ops-stripe-table"
:data="tableData"
:max-height="`${tableHeight}px`"
:row-class-name="(params) => getCurrentRowClass(params, addedRids)"
>
<vxe-column field="name"></vxe-column>
<vxe-column v-for="col in columns" :key="col" :field="col" :title="permMap[col]">
<template #default="{row}">
<a-checkbox @change="(e) => handleChange(e, col, row)" v-model="row[col]"></a-checkbox>
</template>
</vxe-column>
</vxe-table>
<a-space>
<span class="grant-button" @click="grantDepart">{{ $t('cmdb.components.grantUser') }}</span>
<span class="grant-button" @click="grantRole">{{ $t('cmdb.components.grantRole') }}</span>
</a-space>
</div>
</template>
<script>
import { permMap } from './constants.js'
import { grantTypeRelation, revokeTypeRelation } from '../../api/CITypeRelation.js'
import { getCurrentRowClass } from './utils'
export default {
name: 'TypeRelationGrant',
inject: ['loading', 'isModal'],
props: {
tableData: {
type: Array,
default: () => [],
},
grantType: {
type: String,
default: 'type_relation',
},
typeRelationIds: {
type: Array,
default: null,
},
addedRids: {
type: Array,
default: () => [],
},
},
data() {
return {
columns: ['create', 'grant', 'delete'],
}
},
computed: {
windowHeight() {
return this.$store.state.windowHeight
},
tableHeight() {
if (this.isModal) {
return (this.windowHeight - 104) / 2
}
return (this.windowHeight - 104) / 2 - 116
},
permMap() {
return permMap()
}
},
methods: {
getCurrentRowClass,
grantDepart() {
this.$emit('grantDepart', this.grantType)
},
grantRole() {
this.$emit('grantRole', this.grantType)
},
handleChange(e, col, row) {
const first = this.typeRelationIds[0]
const second = this.typeRelationIds[1]
if (e.target.checked) {
grantTypeRelation(first, second, row.rid, { perms: [col] }).catch(() => {
this.$emit('getTableData')
})
} else {
revokeTypeRelation(first, second, row.rid, { perms: [col] }).catch(() => {
this.$emit('getTableData')
})
}
},
},
}
</script>
<style lang="less" scoped>
.ci-relation-grant {
padding: 10px 0;
}
</style>
<template>
<div class="ci-relation-grant">
<vxe-table
ref="xTable"
size="mini"
stripe
class="ops-stripe-table"
:data="tableData"
:max-height="`${tableHeight}px`"
:row-style="(params) => getCurrentRowStyle(params, addedRids)"
>
<vxe-column field="name"></vxe-column>
<vxe-column v-for="col in columns" :key="col" :field="col" :title="permMap[col]">
<template #default="{row}">
<a-checkbox @change="(e) => handleChange(e, col, row)" v-model="row[col]"></a-checkbox>
</template>
</vxe-column>
</vxe-table>
<a-space>
<span class="grant-button" @click="grantDepart">{{ $t('cmdb.components.grantUser') }}</span>
<span class="grant-button" @click="grantRole">{{ $t('cmdb.components.grantRole') }}</span>
</a-space>
</div>
</template>
<script>
import { permMap } from './constants.js'
import { grantTypeRelation, revokeTypeRelation } from '../../api/CITypeRelation.js'
import { getCurrentRowStyle } from './utils'
export default {
name: 'TypeRelationGrant',
inject: ['loading', 'isModal'],
props: {
tableData: {
type: Array,
default: () => [],
},
grantType: {
type: String,
default: 'type_relation',
},
typeRelationIds: {
type: Array,
default: null,
},
addedRids: {
type: Array,
default: () => [],
},
},
data() {
return {
columns: ['create', 'grant', 'delete'],
}
},
computed: {
windowHeight() {
return this.$store.state.windowHeight
},
tableHeight() {
if (this.isModal) {
return (this.windowHeight - 104) / 2
}
return (this.windowHeight - 104) / 2 - 116
},
permMap() {
return permMap()
}
},
methods: {
getCurrentRowStyle,
grantDepart() {
this.$emit('grantDepart', this.grantType)
},
grantRole() {
this.$emit('grantRole', this.grantType)
},
handleChange(e, col, row) {
const first = this.typeRelationIds[0]
const second = this.typeRelationIds[1]
if (e.target.checked) {
grantTypeRelation(first, second, row.rid, { perms: [col] }).catch(() => {
this.$emit('getTableData')
})
} else {
revokeTypeRelation(first, second, row.rid, { perms: [col] }).catch(() => {
this.$emit('getTableData')
})
}
},
},
}
</script>
<style lang="less" scoped>
.ci-relation-grant {
padding: 10px 0;
}
</style>

View File

@@ -3,10 +3,12 @@
:disabled="disabled"
ref="cmdb_type_select"
:disable-branch-nodes="true"
class="custom-treeselect custom-treeselect-white"
class="custom-treeselect custom-treeselect-bgcAndBorder"
:style="{
'--custom-height': '30px',
lineHeight: '30px'
lineHeight: '30px',
'--custom-bg-color': '#fff',
'--custom-border': '1px solid #d9d9d9',
}"
v-model="currenCiType"
:multiple="multiple"

View File

@@ -16,8 +16,12 @@
<template v-for="(item, index) in preferenceSearchList.slice(0, 3)">
<span
v-if="item.name.length > 6"
:class="['preference-search-tag', item.id === currentPreferenceSearch ? 'preference-search-tag-focus' : '']"
class="preference-search-tag"
:key="`${item.id}_${index}`"
:style="{
backgroundColor: item.id === currentPreferenceSearch ? '#2f54eb' : '',
color: item.id === currentPreferenceSearch ? '#fff' : '',
}"
>
<a-tooltip :title="item.name">
<span @click="clickPreferenceSearch(item)">{{ `${item.name.slice(0, 6)}...` }}</span>
@@ -29,7 +33,11 @@
<span
v-else
:key="`${item.id}_${index}`"
:class="['preference-search-tag', item.id === currentPreferenceSearch ? 'preference-search-tag-focus' : '']"
class="preference-search-tag"
:style="{
backgroundColor: item.id === currentPreferenceSearch ? '#2f54eb' : '#fafafa',
color: item.id === currentPreferenceSearch ? '#fff' : '#000000a6',
}"
>
<span @click="clickPreferenceSearch(item)">{{ item.name }}</span>
<a-popconfirm :title="$t('cmdb.ciType.confirmDelete2')" @confirm="deletePreferenceSearch(item)">
@@ -181,15 +189,6 @@ export default {
> i {
font-size: 12px;
}
&:hover {
color: @primary-color;
}
&-focus {
background-color: @primary-color;
color: #FFFFFF !important;
}
}
.preference-search-delete {
color: #a9a9a9;

View File

@@ -5,11 +5,13 @@
<a-space>
<treeselect
v-if="type === 'resourceSearch'"
class="custom-treeselect"
class="custom-treeselect custom-treeselect-bgcAndBorder"
:style="{
width: '200px',
marginRight: '10px',
'--custom-height': '32px',
'--custom-bg-color': '#fff',
'--custom-border': '1px solid #d9d9d9',
'--custom-multiple-lineHeight': '16px',
}"
v-model="currenCiType"
@@ -53,7 +55,7 @@
<a-icon
type="search"
slot="suffix"
:class="['search-form-bar-input-icon', fuzzySearch ? 'search-form-bar-input-icon-focus' : '']"
:style="{ color: fuzzySearch ? '#2f54eb' : '#d9d9d9', cursor: 'pointer' }"
@click="emitRefresh"
/>
<a-tooltip slot="prefix" placement="bottom" :overlayStyle="{ maxWidth: '550px', whiteSpace: 'pre-line' }">
@@ -308,16 +310,6 @@ export default {
justify-content: space-between;
align-items: center;
height: 32px;
&-input-icon {
cursor: pointer;
color: #d9d9d9;
&-focus {
color: @primary-color;
}
}
.search-form-bar-filter {
.ops_display_wrapper(transparent);

View File

@@ -135,15 +135,9 @@ export default {
border: 1px solid #f3f4f6;
}
.authorization-input {
border: 1px solid transparent;
border: none;
&:focus {
box-shadow: none;
border-color: @primary-color;
}
&:hover {
border-color: @primary-color;
}
}
}

View File

@@ -29,7 +29,7 @@
<tr v-for="(item, index) in headers" :key="item.id">
<td><a-input class="headers-input" v-model="item.key" :placeholder="$t('cmdb.components.param', { param: `${index + 1}` })" /></td>
<td><a-input class="headers-input" v-model="item.value" :placeholder="$t('cmdb.components.value', { value: `${index + 1}` })" /></td>
<td class="headers-delete">
<td>
<a style="color:red">
<ops-icon type="icon-xianxing-delete" @click="deleteParam(index)" />
</a>
@@ -92,20 +92,10 @@ export default {
border: 1px solid #f3f4f6;
}
.headers-input {
border: 1px solid transparent;
border: none;
&:focus {
box-shadow: none;
border-color: @primary-color;
}
&:hover {
border-color: @primary-color;
}
}
.headers-delete {
text-align: center;
}
}
</style>

View File

@@ -3,10 +3,12 @@
<a-input-group compact>
<treeselect
:disable-branch-nodes="true"
class="custom-treeselect custom-treeselect-white"
class="custom-treeselect custom-treeselect-bgcAndBorder"
:style="{
'--custom-height': '30px',
lineHeight: '30px',
'--custom-bg-color': '#fff',
'--custom-border': '1px solid #d9d9d9',
display: 'inline-block',
width: '100px',
}"

View File

@@ -23,7 +23,7 @@
<tr v-for="(item, index) in parameters" :key="item.id">
<td><a-input class="parameters-input" v-model="item.key" :placeholder="$t('cmdb.components.param', { param: `${index + 1}` })" /></td>
<td><a-input class="parameters-input" v-model="item.value" :placeholder="$t('cmdb.components.value', { value: `${index + 1}` })" /></td>
<td class="parameters-delete">
<td>
<a style="color:red">
<ops-icon type="icon-xianxing-delete" @click="deleteParam(index)" />
</a>
@@ -91,20 +91,10 @@ export default {
border: 1px solid #f3f4f6;
}
.parameters-input {
border: 1px solid transparent;
border: none;
&:focus {
box-shadow: none;
border-color: @primary-color;
}
&:hover {
border-color: @primary-color;
}
}
.parameters-delete {
text-align: center;
}
}
</style>

View File

@@ -26,8 +26,7 @@ const cmdb_en = {
ad: 'AutoDiscovery',
cidetail: 'CI Detail',
scene: 'Scene',
dcim: 'DCIM',
serviceTree: 'Service Tree'
dcim: 'DCIM'
},
ciType: {
ciType: 'CIType',
@@ -187,9 +186,6 @@ const cmdb_en = {
botSelect: 'Please select a robot',
refAttributeTips: 'The title and content can reference the attribute value of the CIType. The reference method is: {{ attr_name }}',
webhookRefAttributeTips: 'Request parameters can reference the attribute value of the model. The reference method is: {{ attr_name }}',
testSend: 'Test Send',
testSendTip: 'Please save the trigger first',
testSendSuccess: 'Send Success',
newTrigger: 'Add trigger',
editTriggerTitle: 'Edit trigger {name}',
newTriggerTitle: 'Add trigger {name}',
@@ -315,10 +311,6 @@ const cmdb_en = {
enum: 'Enum',
ciGrantTip: `Filter conditions can be changed dynamically using {{}} referenced variables, currently user variables are supported, such as {{user.uid}},{{user.username}},{{user.email}},{{user.nickname}}`,
searchInputTip: 'Please search for resource keywords',
columnSearchInputTip: '192.168.1.1\n192.168.1.2\n192.168.1.3',
rowSearchMode: 'Single Row',
columnSearchMode: 'Multi Row',
columnSearchTip: 'Supports retrieval of short texts only',
resourceSearch: 'Resource Search',
recentSearch: 'Recent Search',
myCollection: 'My Collection',
@@ -902,11 +894,7 @@ if __name__ == "__main__":
deviceName: 'Device Name',
removeDevice: 'Remove Device',
moveDevice: 'Move Device',
rackDetail: 'Rack Detail',
calcUnitFreeCount: 'Calculate Rack Free Unit Count',
calcUnitFreeCountTip: 'Calculating in the background, refresh the page later to see the result',
calcUnitFreeCountTip1: 'Calculate Trigger Success, refresh the page later to see the result',
calcUnitFreeCountTip2: `Confirm that you want to calculate the number of free Units for all rack?`
rackDetail: 'Rack Detail'
}
}
export default cmdb_en

View File

@@ -26,8 +26,7 @@ const cmdb_zh = {
ad: '自动发现',
cidetail: 'CI 详情',
scene: '场景',
dcim: '数据中心',
serviceTree: '服务树'
dcim: '数据中心'
},
ciType: {
ciType: '模型',
@@ -187,9 +186,6 @@ const cmdb_zh = {
botSelect: '请选择机器人',
refAttributeTips: '标题、内容可以引用该模型的属性值,引用方法为: {{ attr_name }}',
webhookRefAttributeTips: '请求参数可以引用该模型的属性值,引用方法为: {{ attr_name }}',
testSend: '测试发送',
testSendTip: '请先保存触发器',
testSendSuccess: '发送成功',
newTrigger: '新增触发器',
editTriggerTitle: '编辑触发器 {name}',
newTriggerTitle: '新增触发器 {name}',
@@ -315,10 +311,6 @@ const cmdb_zh = {
enum: '枚举',
ciGrantTip: `筛选条件可使用{{}}引用变量实现动态变化,目前支持用户变量,如{{user.uid}},{{user.username}},{{user.email}},{{user.nickname}}`,
searchInputTip: '请搜索资源关键字',
columnSearchInputTip: '192.168.1.1\n192.168.1.2\n192.168.1.3',
rowSearchMode: '单行',
columnSearchMode: '多行',
columnSearchTip: '仅支持检索短文本',
resourceSearch: '资源搜索',
recentSearch: '最近搜索',
myCollection: '我的收藏',
@@ -901,11 +893,7 @@ if __name__ == "__main__":
deviceName: '设备名',
removeDevice: '删除设备',
moveDevice: '移动设备',
rackDetail: '机柜详情',
calcUnitFreeCount: '计算机柜空闲U数',
calcUnitFreeCountTip: '后台计算中,稍后刷新页面查看结果',
calcUnitFreeCountTip1: '计算触发成功,稍后刷新页面查看结果',
calcUnitFreeCountTip2: '确认要计算所有机柜的空闲U数'
rackDetail: '机柜详情'
}
}
export default cmdb_zh

View File

@@ -27,17 +27,6 @@ const genCmdbRoutes = async () => {
name: 'cmdb_disabled1',
meta: { title: 'cmdb.menu.resources', disabled: true },
},
{
path: '/cmdb/relationviews/:viewId?',
name: 'cmdb_relation_views',
component: () => import('../views/relation_views/index'),
meta: {
title: 'cmdb.menu.serviceTree',
appName: 'cmdb',
icon: 'veops-servicetree',
keepAlive: false
},
},
{
path: '/cmdb/resourceviews',
name: 'cmdb_resource_views',
@@ -205,14 +194,15 @@ const genCmdbRoutes = async () => {
} else {
routes.redirect = '/cmdb/dashboard'
}
if (relation?.name2id?.length === 0) {
const relationViewRouteIndex = routes.children?.findIndex?.((route) => route.name === 'cmdb_relation_views')
if (relationViewRouteIndex >= 0) {
routes.children.splice(relationViewRouteIndex, 1)
const relationViews = relation.name2id.map(item => {
return {
path: `/cmdb/relationviews/${item[1]}`,
name: `cmdb_relation_views_${item[1]}`,
component: () => import('../views/relation_views/index'),
meta: { title: item[0], icon: 'ops-cmdb-relation', selectedIcon: 'ops-cmdb-relation', keepAlive: false, name: item[0] },
}
}
})
routes.children.splice(resourceViewsIndex, 0, ...relationViews)
return routes
}

View File

@@ -14,7 +14,7 @@
<p v-html="$t('cmdb.batch.drawTips1')"></p>
<p v-html="$t('cmdb.batch.drawTips2')"></p>
<div v-for="item in fileList" :key="item.name" class="cmdb-batch-upload-dragger-file">
<span><a-icon type="file" class="cmdb-batch-upload-dragger-file-icon"/>{{ item.name }}</span>
<span><a-icon type="file" :style="{ color: '#2F54EB', marginRight: '5px' }" />{{ item.name }}</span>
<a-progress :status="progressStatus" :percent="percent" />
</div>
</a-upload-dragger>
@@ -92,17 +92,18 @@ export default {
}
.ant-upload.ant-upload-drag {
border: none;
background: ~'linear-gradient(90deg, @{text-color_5} 50%, transparent 0) repeat-x 0 0 / 15px 1px, linear-gradient(90deg, @{text-color_5} 50%, transparent 0) repeat-x 0 100% / 15px 1px, linear-gradient(0deg, @{text-color_5} 50%, transparent 0) repeat-y 0 0 / 1px 15px, linear-gradient(0deg, @{text-color_5} 50%, transparent 0) repeat-y 100% 0 / 1px 15px';
background: linear-gradient(90deg, @text-color_5 50%, transparent 0) repeat-x,
linear-gradient(90deg, @text-color_5 50%, transparent 0) repeat-x,
linear-gradient(0deg, @text-color_5 50%, transparent 0) repeat-y,
linear-gradient(0deg, @text-color_5 50%, transparent 0) repeat-y;
background-size: 15px 1px, 15px 1px, 1px 15px, 1px 15px;
background-position: 0 0, 0 100%, 0 0, 100% 0;
.ant-upload-drag-container > i {
font-size: 60px;
}
.cmdb-batch-upload-tips {
color: @primary-color;
}
&:hover {
background: ~'linear-gradient(90deg, @{primary-color_2} 50%, transparent 0) repeat-x 0 0 / 15px 1px, linear-gradient(90deg, @{primary-color_2} 50%, transparent 0) repeat-x 0 100% / 15px 1px, linear-gradient(0deg, @{primary-color_2} 50%, transparent 0) repeat-y 0 0 / 1px 15px, linear-gradient(0deg, @{primary-color_2} 50%, transparent 0) repeat-y 100% 0 / 1px 15px, @{primary-color_7}';
}
}
.ant-upload.ant-upload-drag .ant-upload-drag-container {
vertical-align: baseline;
@@ -128,11 +129,6 @@ export default {
white-space: nowrap;
margin-right: 10px;
}
&-icon {
color: @primary-color;
margin-right: 5px;
}
}
.cmdb-batch-upload-tips {
width: 50%;

View File

@@ -9,18 +9,22 @@
"
:title="$t('cmdb.ci.attributeDesc')"
width="72%"
:bodyStyle="{ height: '100vh', paddingTop: '16px' }"
:bodyStyle="{ height: '100vh' }"
>
<a-input
v-model="searchKey"
:style="{ display: 'inline-block', width: '244px', marginBottom: '16px' }"
class="ops-input ops-input-radius"
type="search"
:placeholder="$t('cmdb.ci.tips5')"
@keyup="searchAttributes"
>
<a-icon type="search" slot="suffix" />
</a-input>
<vxe-toolbar>
<template #buttons>
<a-input
v-model="searchKey"
:style="{ display: 'inline-block', width: '244px' }"
class="ops-input ops-input-radius"
type="search"
:placeholder="$t('cmdb.ci.tips5')"
@keyup="searchAttributes"
>
<a-icon type="search" slot="suffix" />
</a-input>
</template>
</vxe-toolbar>
<a-spin :spinning="loading">
<vxe-table

View File

@@ -45,7 +45,7 @@
:href="`/cmdb/cidetail/${column.params.attr.reference_type_id}/${id}`"
target="_blank"
>
{{ getReferenceName(id, column) }}
{{ id }}
</a>
</template>
<template #operation_default="{ row }">
@@ -102,7 +102,7 @@
:href="`/cmdb/cidetail/${column.params.attr.reference_type_id}/${id}`"
target="_blank"
>
{{ getReferenceName(id, column) }}
{{ id }}
</a>
</template>
<template #operation_default="{ row }">
@@ -133,11 +133,9 @@
import _ from 'lodash'
import { getCITypeChildren, getCITypeParent, getCanEditByParentIdChildId } from '@/modules/cmdb/api/CITypeRelation'
import { searchCIRelation, deleteCIRelationView } from '@/modules/cmdb/api/CIRelation'
import { searchCI } from '@/modules/cmdb/api/ci'
import CiDetailRelationTopo from './ciDetailRelationTopo/index.vue'
import Node from './ciDetailRelationTopo/node.js'
import AddTableModal from '../../relation_views/modules/AddTableModal.vue'
export default {
name: 'CiDetailRelation',
components: { CiDetailRelationTopo, AddTableModal },
@@ -174,8 +172,7 @@ export default {
topoData: {
nodes: {},
edges: []
},
referenceCINameMap: {}
}
}
},
computed: {
@@ -214,14 +211,9 @@ export default {
},
methods: {
async init(isFirst) {
const ci_types_list = this.ci_types()
const _findCiType = ci_types_list.find((item) => item.id === this.typeId)
if (!_findCiType) {
return
}
await Promise.all([this.getParentCITypes(), this.getChildCITypes()])
Promise.all([this.getFirstCIs(), this.getSecondCIs()]).then(() => {
const ci_types_list = this.ci_types()
this.handleTopoData()
if (
isFirst &&
@@ -231,8 +223,6 @@ export default {
this.$refs.ciDetailRelationTopo.exsited_ci = this.exsited_ci
this.$refs.ciDetailRelationTopo.setTopoData(this.topoData)
}
this.handleReferenceCINameMap()
})
},
async getFirstCIs() {
@@ -404,98 +394,6 @@ export default {
this.secondCIColumns = secondCIColumns
this.secondCIJsonAttr = secondCIJsonAttr
},
async handleReferenceCINameMap() {
const CITypes = _.unionBy(
[
...this.parentCITypes,
...this.childCITypes
],
'id'
)
const CIList = _.unionBy(
_.flatten(
[
...Object.values(this.firstCIs),
...Object.values(this.secondCIs)
]
),
'_id'
)
const CIMap = {}
CIList.forEach((ci) => {
if (!CIMap[ci._type]) {
CIMap[ci._type] = []
}
CIMap[ci._type].push(ci)
})
const referenceCINameMap = {}
CITypes.forEach((CIType) => {
CIType.attributes.forEach((attr) => {
if (attr?.is_reference && attr?.reference_type_id) {
const currentCIList = CIMap[CIType.id]
if (currentCIList?.length) {
currentCIList.forEach((ci) => {
const ids = Array.isArray(ci[attr.name]) ? ci[attr.name] : ci[attr.name] ? [ci[attr.name]] : []
if (ids.length) {
if (!referenceCINameMap?.[attr.reference_type_id]) {
referenceCINameMap[attr.reference_type_id] = {}
}
ids.forEach((id) => {
referenceCINameMap[attr.reference_type_id][id] = ''
})
}
})
}
}
})
})
if (!Object.keys(referenceCINameMap).length) {
return
}
const allRes = await Promise.all(
Object.keys(referenceCINameMap).map((key) => {
return searchCI({
q: `_type:${key},_id:(${Object.keys(referenceCINameMap[key]).join(';')})`,
count: 9999
})
})
)
const CITypeList = this.ci_types()
const showNameMap = {}
Object.keys(referenceCINameMap).forEach((id) => {
const CIType = CITypeList.find((CIType) => Number(CIType.id) === Number(id))
showNameMap[id] = {
show_name: CIType?.show_name,
unique_key: CIType?.unique_key
}
})
allRes.forEach((res) => {
res.result.forEach((item) => {
if (referenceCINameMap?.[item._type]?.[item._id] === '') {
const showName = showNameMap?.[item._type]
referenceCINameMap[item._type][item._id] = item?.[showName?.show_name] ?? item?.[showName?.unique_key] ?? ''
}
})
})
this.referenceCINameMap = referenceCINameMap
},
getReferenceName(id, column) {
const typeId = column?.params?.attr?.reference_type_id
return this.referenceCINameMap?.[typeId]?.[id] || id
},
reload() {
this.init()
},

View File

@@ -33,8 +33,8 @@
</a-tab-pane>
<a-tab-pane key="tab_3">
<span slot="tab"><a-icon type="clock-circle" />{{ $t('cmdb.ci.history') }}</span>
<div :style="{ padding: '16px 24px 24px', height: '100%' }">
<a-space :style="{ marginBottom: '16px', display: 'flex' }">
<div :style="{ padding: '24px', height: '100%' }">
<a-space :style="{ 'margin-bottom': '10px', display: 'flex' }">
<a-button type="primary" class="ops-button-ghost" ghost @click="handleRollbackCI()">
<ops-icon type="shishizhuangtai" />{{ $t('cmdb.ci.rollback') }}
</a-button>
@@ -180,7 +180,7 @@ export default {
ci_types: [],
hasPermission: true,
itsmInstalled: true,
tableHeight: this.attributeHistoryTableHeight || (this.$store.state.windowHeight - 130),
tableHeight: this.attributeHistoryTableHeight || (this.$store.state.windowHeight - 120),
initQueryLoading: true,
}
},

View File

@@ -95,7 +95,7 @@
attr.name,
{
rules: [{ required: attr.is_required, message: $t('placeholder1') + `${attr.alias || attr.name}` }],
initialValue: attr.default && attr.default.default !== undefined && attr.default.default !== null ? attr.default.default : null,
initialValue: attr.default && attr.default.default ? attr.default.default : null,
},
]"
style="width: 100%"
@@ -148,7 +148,6 @@
</template>
<script>
import _ from 'lodash'
import moment from 'moment'
import JsonEditor from '../../../components/JsonEditor/jsonEditor.vue'
import CIReferenceAttr from '@/components/ciReferenceAttr/index.vue'
@@ -211,7 +210,7 @@ export default {
},
getChoiceDefault(attr) {
if (_.isNil(attr?.default?.default)) {
if (!attr?.default?.default) {
return attr.is_list ? [] : null
}

View File

@@ -61,20 +61,16 @@ export default {
justify-content: center;
font-size: 12px;
font-weight: 400;
color: @text-color_2;
background-color: @primary-color_7;
color: #4E5969;
background-color: #F7F8FA;
width: 105px;
height: 32px;
cursor: pointer;
&-active {
border: solid 1px @primary-color_8;
background-color: @primary-color_4;
color: @primary-color;
}
&:hover {
color: @primary-color;
border: solid 1px #B1C9FF;
background-color: #E1EFFF;
color: #2F54EB;
}
}
}

View File

@@ -154,17 +154,6 @@ export default {
margin-left: 6px;
}
&:hover {
background-color: @primary-color_5;
.attr-ad-tab-edit {
display: inline-block;
}
.attr-ad-tab-delete {
display: inline-block;
}
}
&_active {
border: solid 1px @primary-color_8;
background-color: @primary-color_6;
@@ -172,9 +161,14 @@ export default {
.attr-ad-tab-name {
color: @primary-color;
}
}
&:hover {
background-color: @primary-color_6;
&:hover {
.attr-ad-tab-edit {
display: inline-block;
}
.attr-ad-tab-delete {
display: inline-block;
}
}
}
@@ -184,11 +178,6 @@ export default {
background-color: @primary-color_7;
font-size: 12px;
color: @text-color_4;
&:hover {
background-color: @primary-color_5;
color: @primary-color;
}
}
}
</style>

View File

@@ -3,14 +3,12 @@
<div class="attr-ad-header attr-ad-header-margin">{{ $t('cmdb.ciType.configCheckTitle') }}</div>
<div class="attr-ad-content">
<div class="ad-test-title-info">{{ $t('cmdb.ciType.checkTestTip') }}</div>
<a-button
type="primary"
class="ops-button-ghost ad-test-btn"
ghost
<div
class="ad-test-btn"
@click="showCheckModal"
>
{{ $t('cmdb.ciType.checkTestBtn') }}
</a-button>
</div>
<div class="ad-test-btn-info">{{ $t('cmdb.ciType.checkTestTip2') }}</div>
<!-- <div
class="ad-test-btn"
@@ -142,6 +140,15 @@ export default {
.ad-test-btn {
margin-top: 30px;
padding: 5px 12px;
background-color: #F4F9FF;
border: solid 1px @primary-color_8;
display: inline-block;
cursor: pointer;
color: @link-color;
font-size: 12px;
font-weight: 400;
}
.ad-test-btn-info {

View File

@@ -47,8 +47,8 @@
<a-descriptions layout="horizontal" bordered size="small" :column="2">
<a-descriptions-item v-for="item in propertyList" :key="item.property" :label="item.label">
<ops-icon
:style="{ color: property[item.property] ? '#7f97fa' : '', fontSize: '10px' }"
:type="`ops-${item.property}-disabled`"
:class="['attribute-card-footer-icon', property[item.property] ? 'attribute-card-footer-icon-mark' : '']"
/>
</a-descriptions-item>
<a-descriptions-item label></a-descriptions-item>
@@ -58,7 +58,7 @@
<ops-icon
v-for="item in propertyList.filter((p) => property[p.property])"
:key="item.property"
class="attribute-card-footer-icon attribute-card-footer-icon-mark"
:style="{ color: '#7f97fa', fontSize: '10px' }"
:type="`ops-${item.property}-disabled`"
/>
</a-space>
@@ -247,13 +247,13 @@ export default {
.attribute-card {
width: 172px;
height: 75px;
background-color: @primary-color_6;
background: @primary-color_6;
border-radius: 2px;
position: relative;
margin-bottom: 16px;
transition: all 0.3s;
&:hover {
box-shadow: 0 4px 12px @primary-color_8;
box-shadow: 0 4px 12px #4e5ea066;
.attribute-card-operation {
visibility: visible !important;
}
@@ -342,15 +342,6 @@ export default {
padding: 2px 0 2px 5px;
}
}
.attribute-card-footer-icon {
font-size: 10px;
&-mark {
color: @primary-color_2;
}
}
.attribute-card-inherited {
background: @primary-color_7;
.attribute-card-footer {
@@ -365,10 +356,10 @@ export default {
justify-content: center;
cursor: pointer;
position: relative;
background-color: inherit !important;
background-color: inherit;
&:hover {
box-shadow: none !important;
background-color: @primary-color_6 !important;
box-shadow: none;
background-color: @primary-color_6;
}
&:after {
content: '';

View File

@@ -659,7 +659,7 @@ export default {
} else {
this.$nextTick(() => {
this.form.setFieldsValue({
default_value: _record?.default?.default ?? null,
default_value: _record.default && _record.default.default ? _record.default.default : null,
})
})
}

View File

@@ -140,7 +140,7 @@
:type="ci.icon.split('$$')[0]"
/>
</template>
<span class="primary-color" v-else>{{ ci.name[0].toUpperCase() }}</span>
<span :style="{ color: '#2f54eb' }" v-else>{{ ci.name[0].toUpperCase() }}</span>
</span>
</div>
<span class="ci-types-left-detail-title">{{ ci.alias || ci.name }}</span>
@@ -1194,7 +1194,7 @@ export default {
.selected {
background-color: @primary-color_3;
.ci-types-left-detail-title {
color: @primary-color;
font-weight: 700;
}
}
}

View File

@@ -35,7 +35,7 @@
:title="$t('cmdb.ciType.choiceWebhookTips')"
>
<a-icon
class="tab-webhook-filter-icon"
style="position:absolute;top:3px;left:-17px;color:#2f54eb;"
type="question-circle"
theme="filled"
/>
@@ -61,11 +61,13 @@
:disable-branch-nodes="true"
:class="{
'custom-treeselect': true,
'custom-treeselect-white': true,
'custom-treeselect-bgcAndBorder': true,
}"
:style="{
'--custom-height': '32px',
lineHeight: '32px',
'--custom-bg-color': '#fff',
'--custom-border': '1px solid #d9d9d9',
'--custom-multiple-lineHeight': '14px',
}"
v-model="choice_other.type_ids"
@@ -553,7 +555,7 @@ export default {
&-tag {
background-color: #E1EFFF;
color: @primary-color;
color: #2F54EB;
font-size: 10px;
font-weight: 400;
padding: 0 3px;
@@ -575,13 +577,6 @@ export default {
}
}
.tab-webhook-filter-icon {
position: absolute;
top: 3px;
left: -17px;
color: @primary-color;
}
.script-tip {
font-size: 12px;
line-height: 22px;

View File

@@ -363,7 +363,7 @@ export default {
width: 12px;
height: 12px;
background-color: @primary-color;
border: solid 3px @primary-color_4;
border: solid 3px #E2E7FC;
border-radius: 50%
}

View File

@@ -31,7 +31,7 @@
<vxe-column field="source_ci_type_name" :title="$t('cmdb.ciType.sourceCIType')"></vxe-column>
<vxe-column field="relation_type" :title="$t('cmdb.ciType.relationType')">
<template #default="{row}">
<span class="primary-color" v-if="row.isParent">{{ $t('cmdb.ciType.isParent') }}</span>
<span style="color:#2f54eb" v-if="row.isParent">{{ $t('cmdb.ciType.isParent') }}</span>
{{ row.relation_type }}
</template>
</vxe-column>
@@ -700,7 +700,7 @@ export default {
}
/deep/ .relation-table-parent {
background-color: @primary-color_5 !important;
background-color: #f5f8ff !important;
}
}
</style>

View File

@@ -198,11 +198,13 @@
:disable-branch-nodes="true"
:class="{
'custom-treeselect': true,
'custom-treeselect-white': true,
'custom-treeselect-bgcAndBorder': true,
}"
:style="{
'--custom-height': '32px',
lineHeight: '32px',
'--custom-bg-color': '#fff',
'--custom-border': '1px solid #d9d9d9',
'--custom-multiple-lineHeight': '14px',
}"
v-model="selectedBot"
@@ -240,18 +242,6 @@
</a-col>
</a-row>
</a-checkbox-group>
<a-row v-if="category === 2">
<a-button
@click="clickTestSend"
:disabled="!dateForm.attr_id"
type="primary"
ghost
class="ops-button-ghost"
>
{{ $t('cmdb.ciType.testSend') }}
</a-button>
</a-row>
</a-form-model-item>
</a-form-model>
<div class="auto-complete-wrapper" v-if="triggerAction === '3'">
@@ -283,7 +273,7 @@
<script>
import _ from 'lodash'
import { addTrigger, updateTrigger, deleteTrigger, testTrigger } from '../../api/CIType'
import { addTrigger, updateTrigger, deleteTrigger } from '../../api/CIType'
import FilterComp from '@/components/CMDBFilterComp'
import EmployeeTreeSelect from '@/views/setting/components/employeeTreeSelect.vue'
import Webhook from '../../components/webhook'
@@ -579,13 +569,10 @@ export default {
}
if (this.triggerId) {
await updateTrigger(this.CITypeId, this.triggerId, params)
this.$message.success(this.$t('editSuccess'))
} else {
const res = await addTrigger(this.CITypeId, params)
this.triggerId = res.id
this.$message.success(this.$t('createSuccess'))
await addTrigger(this.CITypeId, params)
}
this.handleCancel()
if (this.refresh) {
this.refresh()
}
@@ -627,15 +614,6 @@ export default {
this.searchValue = item.label
this.dag_id = item.id
},
async clickTestSend() {
if (!this.triggerId) {
this.$message.warning(this.$t('cmdb.ciType.testSendTip'))
return
}
await testTrigger(this.CITypeId, this.triggerId)
this.$message.success(this.$t('cmdb.ciType.testSendSuccess'))
}
},
}
</script>

View File

@@ -24,7 +24,7 @@
<vxe-column field="attr_ids" :title="$t('cmdb.ciType.attributes')" :edit-render="{}">
<template #default="{ row }">
<template v-for="(attr, index) in row.attr_ids">
<span :key="attr" class="primary-color">{{ getDisplayName(attr) }}</span>
<span :key="attr" :style="{ color: '#2f54eb' }">{{ getDisplayName(attr) }}</span>
<span :key="`_${attr}`" v-if="index !== row.attr_ids.length - 1"> + </span>
</template>
</template>

View File

@@ -19,7 +19,7 @@
:type="ciType.icon.split('$$')[0]"
/>
</template>
<span class="primary-color" v-else>{{ ciType.name[0].toUpperCase() }}</span>
<span :style="{ color: '#2f54eb' }" v-else>{{ ciType.name[0].toUpperCase() }}</span>
</div>
<span :style="{ ...options.fontConfig }">{{ toThousands(data) }}</span>
</div>

View File

@@ -148,7 +148,7 @@
:type="ciType.icon.split('$$')[0]"
/>
</template>
<span class="primary-color" v-else>{{ ciType.name[0].toUpperCase() }}</span>
<span :style="{ color: '#2f54eb' }" v-else>{{ ciType.name[0].toUpperCase() }}</span>
</template>
<span :style="{ color: '#000' }"> {{ form.name }}</span>
</div>
@@ -195,14 +195,12 @@
</template>
</div>
<a-form-model-item
:label="$t('cmdb.custom_dashboard.showIcon')"
prop="showIcon"
:label-col="{ span: 0 }"
:wrapper-col="{ span: 23 }"
:label-col="{ span: 5 }"
:wrapper-col="{ span: 18 }"
>
<div class="chart-left-show-icon">
<span class="chart-left-show-icon-label">{{ $t('cmdb.custom_dashboard.showIcon') }}:</span>
<a-switch v-model="form.showIcon"></a-switch>
</div>
<a-switch v-model="form.showIcon"></a-switch>
</a-form-model-item>
</a-form-model>
</div>
@@ -735,9 +733,6 @@ export default {
width: 92%;
position: relative;
padding: 12px;
margin-top: 4px;
display: inline-block;
.chart-left-preview-operation {
color: #86909c;
position: absolute;
@@ -758,26 +753,12 @@ export default {
background-position-x: center;
background-position-y: center;
}
&-show-icon {
display: flex;
align-items: center;
&-label {
flex-shrink: 0;
margin-right: 8px;
}
}
}
.chart-right {
width: 50%;
h4 {
font-weight: 700;
color: #000;
&:not(:first-child) {
margin-top: 14px;
}
}
.chart-right-type {
display: flex;
@@ -800,7 +781,7 @@ export default {
}
}
.chart-right-type-box-selected {
background-color: @primary-color_3;
background-color: #e5f1ff;
}
}
.chart-width {
@@ -816,7 +797,7 @@ export default {
<style lang="less">
.chart-wrapper {
.ant-form-item {
margin-bottom: 8px;
margin-bottom: 0;
}
}
</style>

View File

@@ -63,7 +63,7 @@
:type="getCiType(item).icon.split('$$')[0]"
/>
</template>
<span class="primary-color" v-else>{{ getCiType(item).name[0].toUpperCase() }}</span>
<span :style="{ color: '#2f54eb' }" v-else>{{ getCiType(item).name[0].toUpperCase() }}</span>
</template>
<span :style="{ color: item.options.chartType === 'count' ? item.options.fontColor : '#000' }">{{
item.options.name

View File

@@ -268,18 +268,18 @@ export default {
margin-right: 2px;
font-size: 12px;
font-weight: 400;
color: #2F54EB;
color: #3F75FF;
}
&-icon {
font-size: 12px;
color: #2F54EB;
color: #3F75FF;
}
}
&:hover {
background-color: #FFFFFF;
box-shadow: ~'0px 22px 33px 0px @{primary-color}15';
box-shadow: 0px 22px 33px 0px rgba(41, 65, 126, 0.25);
z-index: 2;
.rack-grid-item-name {

View File

@@ -21,24 +21,11 @@
<a>
<a-icon
type="plus-circle"
class="dcim-tree-header-menu-icon"
class="dcim-tree-header-add-icon"
/>
{{ $t(addActionTitle[type]) }}
</a>
</a-menu-item>
<a-menu-item
class="dcim-tree-header-calc"
@click="calcUnitFreeCount"
>
<a>
<ops-icon
type="veops-refresh"
class="dcim-tree-header-menu-icon"
/>
{{ $t('cmdb.dcim.calcUnitFreeCount') }}
</a>
</a-menu-item>
</a-menu>
</a-dropdown>
</div>
@@ -58,11 +45,15 @@
>
<ops-icon
:type="treeNodeData.icon"
:class="['dcim-tree-node-icon', treeNodeData.dcimType === DCIM_TYPE.REGION ? 'primary-color' : '']"
class="dcim-tree-node-icon"
:style="{ color: treeNodeData.iconColor }"
/>
<a-tooltip :title="treeNodeData.title">
<span
:class="['dcim-tree-node-title', treeKey === treeNodeData.key ? 'primary-color' : '']"
class="dcim-tree-node-title"
:style="{
color: treeKey === treeNodeData.key ? '#2F54EB' : ''
}"
>
{{ treeNodeData.title }}
</span>
@@ -130,7 +121,7 @@
<script>
import _ from 'lodash'
import { DCIM_TYPE, DCIM_TYPE_NAME_MAP } from '../constants.js'
import { deleteDCIM, calcUnitFreeCount } from '@/modules/cmdb/api/dcim.js'
import { deleteDCIM } from '@/modules/cmdb/api/dcim.js'
import CIDetailDrawer from '@/modules/cmdb/views/ci/modules/ciDetailDrawer.vue'
export default {
@@ -160,12 +151,9 @@ export default {
DCIM_TYPE.REGION,
DCIM_TYPE.IDC
],
DCIM_TYPE,
viewDetailCITypeId: 0,
viewDetailAttrObj: {},
calculatedFreeUnitCount: false,
viewDetailAttrObj: {}
}
},
computed: {
@@ -271,23 +259,6 @@ export default {
this.$refs.CIdetailRef.create(node._id)
})
})
},
calcUnitFreeCount() {
if (this.calculatedFreeUnitCount) {
this.$message.info(this.$t('cmdb.dcim.calcUnitFreeCountTip'))
} else {
this.$confirm({
title: this.$t('tip'),
content: this.$t('cmdb.dcim.calcUnitFreeCountTip2'),
onOk: () => {
calcUnitFreeCount().then(() => {
this.calculatedFreeUnitCount = true
this.$message.success(this.$t('cmdb.dcim.calcUnitFreeCountTip1'))
})
}
})
}
}
}
}
@@ -310,11 +281,7 @@ export default {
padding: 0px;
}
&-calc {
border-top: dashed 1px #e8e8e8;
}
&-menu-icon {
&-add-icon {
margin-right: 6px;
}
}
@@ -362,7 +329,6 @@ export default {
&-icon {
font-size: 12px;
flex-shrink: 0;
color: #A5A9BC;
}
&-title {

View File

@@ -117,8 +117,8 @@
}"
@click="addDevice(index)"
>
<a-icon
type="plus-circle"
<ops-icon
type="monitor-add"
class="rack-container-main-list-gap-icon"
/>
<span
@@ -492,13 +492,12 @@ export default {
&-icon {
font-size: 12px;
display: none;
color: @primary-color;
}
&-text {
font-size: 12px;
font-weight: 400;
color: @primary-color;
color: rgba(0, 87, 255, 0.80);
margin-left: 6px;
display: none;
}
@@ -509,7 +508,7 @@ export default {
}
&:hover {
background-color: @primary-color_4;
background-color: #D5DDEE;
.rack-container-main-list-gap-icon {
display: inline-block;

View File

@@ -165,12 +165,11 @@ export default {
display: inline-block;
width: 180px;
height: 105px;
box-shadow: 0px 2px 8px @primary-color_3;
box-shadow: 0px 2px 8px rgba(122, 140, 204, 0.25);
border-radius: 4px;
position: relative;
margin-bottom: 40px;
margin-right: 40px;
cursor: pointer;
&-inner {
position: absolute;
@@ -295,12 +294,6 @@ export default {
}
}
&, &.discovery-card-small {
&:hover {
box-shadow: 0px 6px 20px 0px @primary-color_3;
}
}
&-http {
width: 263px;
height: 142px;
@@ -312,10 +305,6 @@ export default {
max-width: 30px !important;
}
}
&:hover {
box-shadow: 0px 6px 28px 0px @primary-color_3;
}
}
}
.discovery-card-small {
@@ -323,7 +312,7 @@ export default {
height: 80px;
cursor: pointer;
}
.discovery-card-small:hover,
.discovery-card-small-selected {
.discovery-top {
background-color: #f0f1f5;

View File

@@ -25,24 +25,18 @@
:fileList="[]"
:beforeUpload="beforeUpload"
>
<a-button
type="primary"
class="ops-button-ghost"
ghost
>
<a class="setting-discovery-header-action-btn">
<a-icon type="upload" />
{{ $t('cmdb.ad.upload') }}
</a-button>
</a>
</a-upload>
<a-button
type="primary"
class="ops-button-ghost"
ghost
<a
@click="download"
class="setting-discovery-header-action-btn"
>
<a-icon type="download" />
{{ $t('cmdb.ad.download') }}
</a-button>
</a>
</div>
</div>
<div
@@ -70,7 +64,7 @@
class="setting-discovery-add"
@click="handleOpenEditDrawer(null, 'add', DISCOVERY_CATEGORY_TYPE.PLUGIN)"
>
<a-icon class="setting-discovery-add-icon" type="plus-circle" />
<a-icon type="plus-circle" theme="twoTone" />
<span class="setting-discovery-add-text">
{{ $t('cmdb.ad.addPlugin') }}
</span>
@@ -380,10 +374,6 @@ export default {
justify-content: center;
cursor: pointer;
&-icon {
color: @primary-color_9;
}
&-text {
color: @text-color_3;
font-size: 12px;

View File

@@ -24,7 +24,7 @@
:type="ciType.icon.split('$$')[0]"
/>
</template>
<span class="primary-color" v-else>{{ ciType.name[0].toUpperCase() }}</span>
<span :style="{ color: '#2f54eb' }" v-else>{{ ciType.name[0].toUpperCase() }}</span>
</span>
<span :title="ciType.alias || ciType.name" class="cmdb-adc-side-name">{{ ciType.alias || ciType.name }}</span>
</div>
@@ -46,15 +46,10 @@
<span @click="batchDelete">{{ $t('delete') }}</span>
<span>{{ $t('cmdb.ci.selectRows', { rows: selectedCount }) }}</span>
</span>
<a-button
type="primary"
ghost
class="ops-button-ghost discovery-ci-log"
@click="clickLog"
>
<div @click="clickLog" class="discovery-ci-log">
<ops-icon type="a-cmdb-log1" />
<span>{{ $t('cmdb.ad.log') }}</span>
</a-button>
</div>
</div>
<ops-table
show-overflow
@@ -463,7 +458,16 @@ export default {
}
.discovery-ci-log {
cursor: pointer;
background-color: #F4F9FF;
border: solid 1px @primary-color_8;
color: @primary-color;
font-size: 12px;
padding: 5px 12px;
margin-left: auto;
display: flex;
align-items: center;
gap: 6px;
}
.checkbox-hover-table {

View File

@@ -26,7 +26,10 @@
/>
<a-tooltip :title="treeNodeData.title">
<span
:class="['ipam-tree-node-title', treeKey === treeNodeData.key ? 'primary-color' : '']"
class="ipam-tree-node-title"
:style="{
color: treeKey === treeNodeData.key ? '#2F54EB' : ''
}"
>
{{ treeNodeData.title }}
</span>

View File

@@ -120,7 +120,7 @@ export default {
if (attrList.length) {
_.remove(attrList, (item) => {
return ['subnet_mask', 'gateway', 'name', 'mac_address', 'is_used', 'ip', 'ipam_address_id'].includes(item.name)
return ['subnet_mask', 'gateway', 'name', 'mac_address', 'is_used', 'ip'].includes(item.name)
})
const assingStatusIndex = attrList.findIndex((attr) => attr.name === 'assign_status')

View File

@@ -115,7 +115,6 @@
:referenceShowAttrNameMap="referenceShowAttrNameMap"
:referenceCIIdMap="referenceCIIdMap"
:columnWidth="columnWidth"
:addressCITypeId="addressCITypeId"
@openAssign="openAssign"
@recycle="handleRecycle"
@selectChange="handleTableSelectChange"
@@ -183,7 +182,6 @@ export default {
currentSelectScope: '',
columns: [],
attrList: [],
attributes: {},
subnetData: {},
referenceShowAttrNameMap: {},
referenceCIIdMap: {},
@@ -214,17 +212,6 @@ export default {
],
}
},
provide() {
return {
handleSearch: this.getIPList,
attrList: () => {
return this.attrList
},
attributes: () => {
return this.attributes
}
}
},
computed: {
addressNullTip() {
if (
@@ -327,13 +314,11 @@ export default {
async getColumns() {
const getAttrRes = await getCITypeAttributesById(this.addressCITypeId)
this.attributes = _.cloneDeep(getAttrRes)
this.attrList = _.cloneDeep(getAttrRes.attributes)
const attrList = getAttrRes.attributes
this.attrList = _.cloneDeep(attrList)
const filterAttrList = _.remove(attrList, (item) => {
return ['ip', 'subnet_mask', 'assign_status', 'is_used', '_updated_by', '_updated_at', 'ipam_address_id'].includes(item.name)
return ['ip', 'subnet_mask', 'assign_status', 'is_used', '_updated_by', '_updated_at'].includes(item.name)
})
const columns = []
@@ -504,14 +489,9 @@ export default {
const totalWidth = Object.values(columnWidth).reduce((acc, cur) => acc + cur, 0)
if (totalWidth < wrapWidth) {
this.columnWidth = {
ip: 130
}
this.columnWidth = {}
} else {
this.columnWidth = {
...columnWidth,
ip: 130
}
this.columnWidth = columnWidth
}
},

View File

@@ -111,12 +111,6 @@
<a-tooltip v-else :title="$t('cmdb.ipam.assign')">
<a @click="assignAddress(row)"><ops-icon type="monitor-add2" /></a>
</a-tooltip>
<a-tooltip v-if="row._ip_status !== ADDRESS_STATUS.OFFLINE_UNASSIGNED" :title="$t('cmdb.ci.viewRelation')">
<a @click="openRelation(row)">
<a-icon type="retweet" />
</a>
</a-tooltip>
</div>
</template>
</vxe-table-column>
@@ -147,8 +141,6 @@
</template>
</a-pagination>
</div>
<CIDetailDrawer ref="detail" :typeId="addressCITypeId" />
</div>
</template>
@@ -157,13 +149,8 @@ import _ from 'lodash'
import { mapState } from 'vuex'
import { STATUS_COLOR, STATUS_LABEL, ADDRESS_STATUS } from './constants.js'
import CIDetailDrawer from '@/modules/cmdb/views/ci/modules/ciDetailDrawer.vue'
export default {
name: 'TableIP',
components: {
CIDetailDrawer
},
props: {
columns: {
type: Array,
@@ -184,10 +171,6 @@ export default {
columnWidth: {
type: Object,
default: () => {}
},
addressCITypeId: {
type: Number,
default: 0
}
},
data() {
@@ -301,12 +284,6 @@ export default {
const ips = records?.map?.((item) => item.ip) || []
this.$emit('selectChange', ips)
},
openRelation(row) {
if (row._id) {
this.$refs.detail.create(row._id, 'tab_2', '2')
}
}
}
}
</script>

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