Compare commits
662 Commits
config_ruf
...
v2.4.15
Author | SHA1 | Date | |
---|---|---|---|
|
5aff5d728d | ||
|
464b9d5394 | ||
|
9c4cc20e13 | ||
|
c3c8602207 | ||
|
c961e288af | ||
|
e22b0c5290 | ||
|
900cf1f617 | ||
|
f28ad4d041 | ||
|
d2698b05c0 | ||
|
6f1332148c | ||
|
5fa18eeb00 | ||
|
03fdf5c004 | ||
|
f277cf088e | ||
|
b1f8a0024b | ||
|
aae43a53b5 | ||
|
8c2cdb1ca4 | ||
|
57d4bf5548 | ||
|
5e7c6199bf | ||
|
28dca7f086 | ||
|
4a3c21eec4 | ||
|
5d28c28023 | ||
|
ba6edb3abe | ||
|
2f1d57cee1 | ||
|
4111c634d9 | ||
|
f1fababa3d | ||
|
fe6373422e | ||
|
b3ea776886 | ||
|
c4d2ce313d | ||
|
20103a0fe6 | ||
|
394e2aeac6 | ||
|
8f7d78c26c | ||
|
7eecf3cec3 | ||
|
f6e9c443f7 | ||
|
857cbd82fd | ||
|
9a14296e02 | ||
|
f638b52759 | ||
|
78da728105 | ||
|
eb69029a51 | ||
|
07a097eba2 | ||
|
e843e3eac9 | ||
|
7308cfa6c2 | ||
|
64ea4fb21f | ||
|
e15cefaa38 | ||
|
f32339b969 | ||
|
131d213a73 | ||
|
ff98777689 | ||
|
383d4c88ed | ||
|
bb7157e292 | ||
|
b1a82f1a67 | ||
|
de86ea3852 | ||
|
bf05ea240e | ||
|
8ec0d619d7 | ||
|
61f8c463bc | ||
|
9b4dc3e43b | ||
|
9e69be8256 | ||
|
251b9e7fd5 | ||
|
f3cc12f1f9 | ||
|
56f03e1624 | ||
|
42ad2b6dde | ||
|
5aba1ff257 | ||
|
417e8fe349 | ||
|
02235d8cc0 | ||
|
00c7a644a2 | ||
|
f3e8757450 | ||
|
f0749341ba | ||
|
89da671e46 | ||
|
0e60aae076 | ||
|
4dfa97d404 | ||
|
9b778f9bc7 | ||
|
eafb5f053a | ||
|
834054e216 | ||
|
a97cabbedc | ||
|
ae77852d5f | ||
|
611ee40dca | ||
|
c0d55b2126 | ||
|
2cc4499ef9 | ||
|
1268404bca | ||
|
570a9203c4 | ||
|
adae7b5519 | ||
|
8a91ec7b11 | ||
|
92fca65383 | ||
|
4b8e6c2841 | ||
|
ab240cb003 | ||
|
61e62e4740 | ||
|
1fd72d6c78 | ||
|
51e16f6b23 | ||
|
037378e384 | ||
|
631871a8cf | ||
|
6e02f6a21f | ||
|
a2224ba2ac | ||
|
11a289aac9 | ||
|
55ab04dd28 | ||
|
256a4f4844 | ||
|
018a349336 | ||
|
8f62227adb | ||
|
de51cb3e21 | ||
|
ecb069cf14 | ||
|
937cb84393 | ||
|
40a4db06b5 | ||
|
cc98f903ea | ||
|
fb7471ce04 | ||
|
e2872f041e | ||
|
250fde127c | ||
|
73dbb14944 | ||
|
73c9a6fa72 | ||
|
09d957db79 | ||
|
b73d796891 | ||
|
e7cbd0caa9 | ||
|
3e4c385d91 | ||
|
3aac012ee9 | ||
|
78d762cacc | ||
|
c668ba7d3f | ||
|
542a876ead | ||
|
68b7497bba | ||
|
dfbf3d462d | ||
|
692708fcba | ||
|
330b64edb3 | ||
|
63a3074cb7 | ||
|
31b8cf49dc | ||
|
b01c335456 | ||
|
002fef09e2 | ||
|
175778a162 | ||
|
5050a1bef5 | ||
|
46a6cf67d6 | ||
|
4e857c2775 | ||
|
835df1bdeb | ||
|
579339d13c | ||
|
629967ce82 | ||
|
3a00bfd236 | ||
|
2e97ebd895 | ||
|
eb6a813cbc | ||
|
ff78face48 | ||
|
d55433c438 | ||
|
daf0254616 | ||
|
6b32009955 | ||
|
d53288c1fb | ||
|
586d820a08 | ||
|
6776be4599 | ||
|
ff2b8ea198 | ||
|
ed46a1e1c1 | ||
|
0dc614fb46 | ||
|
bc66d33ce0 | ||
|
d5db68d7d0 | ||
|
b22b8b286b | ||
|
dd4f3b0e9c | ||
|
688f4e0ea4 | ||
|
c1813f525d | ||
|
b405e28498 | ||
|
fa32758462 | ||
|
29995b660a | ||
|
b96fc06a62 | ||
|
c7f30b63ff | ||
|
5bff69a8a8 | ||
|
d5e60fab88 | ||
|
190f452118 | ||
|
98a4824364 | ||
|
c0f9baea79 | ||
|
d4b661c77f | ||
|
75cd7bde77 | ||
|
ec912d3a65 | ||
|
42f02b4986 | ||
|
a13b999820 | ||
|
5f53b0dd0e | ||
|
df22085ff9 | ||
|
06148b402d | ||
|
3fe020505a | ||
|
b34e83124f | ||
|
cdc52d3f80 | ||
|
b3a80d5678 | ||
|
a2e3061bba | ||
|
a8eb5126ea | ||
|
adac2129fc | ||
|
e660c901ce | ||
|
ff002c0a1e | ||
|
88593d6da7 | ||
|
6fa0dd5bc5 | ||
|
3200942373 | ||
|
4fd705cc59 | ||
|
74827ce187 | ||
|
4ed1eb6062 | ||
|
7792204658 | ||
|
8621108906 | ||
|
6437af19b9 | ||
|
735ddb334c | ||
|
4a8032202e | ||
|
c7acea6422 | ||
|
ac4c93de8e | ||
|
8d044cf935 | ||
|
54747fa789 | ||
|
545f1bb30b | ||
|
dc77bca17c | ||
|
4973278c5a | ||
|
d1c9361e47 | ||
|
28c57cacd9 | ||
|
711dcc4bd7 | ||
|
491d3cce00 | ||
|
27354a3927 | ||
|
78495eb976 | ||
|
ae900c7d3b | ||
|
50134e6a0b | ||
|
65ef58dea9 | ||
|
0a2e7aa99f | ||
|
8875e75883 | ||
|
2f03639c57 | ||
|
49bc5d94a9 | ||
|
39354e1293 | ||
|
d3714f3ecf | ||
|
729a616282 | ||
|
2d3a290aa3 | ||
|
9e885a5b12 | ||
|
f5822d7cba | ||
|
21ea553e74 | ||
|
e63038d1b6 | ||
|
d56806f511 | ||
|
7ac7fdc08e | ||
|
ba11707146 | ||
|
d49dc8a067 | ||
|
6bfb34fe2a | ||
|
2c7ed8c32d | ||
|
5b275af54e | ||
|
dde7ec6246 | ||
|
9181817e96 | ||
|
46b54bb7f2 | ||
|
fe63310c4e | ||
|
27c733aa2c | ||
|
2a8e9e684e | ||
|
095190a785 | ||
|
ef25c94b5d | ||
|
06ae1bcf13 | ||
|
9ead4e7d8d | ||
|
994a28dd25 | ||
|
74b587e46c | ||
|
091cd882bd | ||
|
73093db467 | ||
|
66e268ce68 | ||
|
a41d1a5e97 | ||
|
b4b728fe28 | ||
|
d16462d8b7 | ||
|
de7d98c0b4 | ||
|
51332c7236 | ||
|
bf1076fe4a | ||
|
3454a98cfb | ||
|
506dcbb40e | ||
|
5ac4517187 | ||
|
761e98884b | ||
|
073654624e | ||
|
df54244ff1 | ||
|
27e9919198 | ||
|
dc8b1a5de2 | ||
|
d8a7728f1d | ||
|
82881965fb | ||
|
1bb62022f1 | ||
|
ed445a8d82 | ||
|
3626b1a97e | ||
|
32529fba9b | ||
|
a042b4fe39 | ||
|
a0631414dc | ||
|
5266cb5b88 | ||
|
c7d4bec988 | ||
|
099ddd6ca9 | ||
|
bd813174b1 | ||
|
0a43680d6e | ||
|
976c6cfe91 | ||
|
cf594f04ba | ||
|
4232094aed | ||
|
d08827d086 | ||
|
d25ae532cd | ||
|
9fbb6ee64d | ||
|
b62f0e96fd | ||
|
c1bcd0ce45 | ||
|
c8b55c34eb | ||
|
4b5906770f | ||
|
4188ac7252 | ||
|
2efbc6474a | ||
|
03eac0c4d2 | ||
|
2a861250eb | ||
|
8fc19d8b7c | ||
|
430d2ff6d0 | ||
|
2517009d70 | ||
|
67da360d80 | ||
|
24c56fb259 | ||
|
37d5da65de | ||
|
2224ebd533 | ||
|
bf6331d215 | ||
|
b18b90ab4e | ||
|
702e17a7a4 | ||
|
a7586aa140 | ||
|
ad3f96431c | ||
|
1515820713 | ||
|
7728b57878 | ||
|
a419eefd72 | ||
|
a44e5f6cf1 | ||
|
7d46e92c2d | ||
|
4117cf87ec | ||
|
9e0fe0b818 | ||
|
2a8f1ab9a4 | ||
|
c0fe99b8c7 | ||
|
42feb4b862 | ||
|
482d34993b | ||
|
7ff309b8b8 | ||
|
98eb47d44f | ||
|
9ab0f624ef | ||
|
3f3eda8b3c | ||
|
f788adc8cf | ||
|
693ae4ff05 | ||
|
a1a9d99eb4 | ||
|
e045e0fb43 | ||
|
09376dbd2b | ||
|
7fda5a1e7b | ||
|
113b84763f | ||
|
190170acad | ||
|
513d2af4b8 | ||
|
4588bd8996 | ||
|
082da5fade | ||
|
013b116eb5 | ||
|
208d29165b | ||
|
d510330cde | ||
|
ea4f0fc2a5 | ||
|
9bcdaacdc4 | ||
|
5045581ddf | ||
|
232913172c | ||
|
157e1809ed | ||
|
ab8ccf7d1b | ||
|
ae1f0f6b4f | ||
|
ff47e0ade6 | ||
|
0dd272fb04 | ||
|
6bc5c1516d | ||
|
4a4b9e6ef0 | ||
|
6e3f9478b3 | ||
|
691051c254 | ||
|
8f066e95a6 | ||
|
75bca39bf6 | ||
|
3360e4d0fe | ||
|
521fcd0ba2 | ||
|
fc113425cb | ||
|
81a76a9632 | ||
|
9ec105ca37 | ||
|
1e1c92a3ef | ||
|
0b3cad8215 | ||
|
ed41a72900 | ||
|
27854d4e0a | ||
|
0372459f9d | ||
|
f56378e2b5 | ||
|
00b276d5e7 | ||
|
3faefbe8d3 | ||
|
4261f6fb57 | ||
|
c98199e98e | ||
|
82ea1ddc79 | ||
|
995e581315 | ||
|
ec8f626b8f | ||
|
ec884c92e1 | ||
|
9ee2776bdd | ||
|
7186bdac9c | ||
|
0a9964375a | ||
|
bff45d00b6 | ||
|
e429ad59ff | ||
|
ace160ae19 | ||
|
7036fe023c | ||
|
341f5dba53 | ||
|
5883c1616e | ||
|
bee0a3a3e5 | ||
|
7a79a8bbf7 | ||
|
100a889cb8 | ||
|
b093569453 | ||
|
3919dfdfbb | ||
|
ef85ba2542 | ||
|
3d5c2ec5bc | ||
|
c143d6ae5b | ||
|
10b273ee81 | ||
|
855cb91b31 | ||
|
ffae57642c | ||
|
9ed1108c20 | ||
|
72b2f8b6de | ||
|
20f3e917fe | ||
|
c430515377 | ||
|
a6e2aca281 | ||
|
18313b7bd1 | ||
|
dbc44a8ad6 | ||
|
f143e30cf5 | ||
|
4beece5a6e | ||
|
920295d955 | ||
|
6eb8ae1dac | ||
|
f1f86ce25a | ||
|
090007487d | ||
|
aa98a304c1 | ||
|
fe22e363b4 | ||
|
2d2fb6e1d6 | ||
|
9894c77300 | ||
|
4ea947f741 | ||
|
e5ab2c2573 | ||
|
5581fa8d0f | ||
|
76c939fe5c | ||
|
6e9ce08e2c | ||
|
092a8b9b92 | ||
|
b45fd0cbbb | ||
|
5320ecfd62 | ||
|
1d253d7ad3 | ||
|
73d53f0440 | ||
|
d4a37af183 | ||
|
e03849b054 | ||
|
faed3fe6a2 | ||
|
21c9d9accd | ||
|
a06599ce33 | ||
|
2b69217136 | ||
|
03a3b8b169 | ||
|
cd319421d5 | ||
|
c918d54ea5 | ||
|
cf0ad7bad6 | ||
|
e0c8263542 | ||
|
275e8b15f3 | ||
|
6ff942c107 | ||
|
d3c87ee500 | ||
|
a4f65e7fc6 | ||
|
10527bf9b8 | ||
|
0414121c27 | ||
|
edde467c87 | ||
|
d525e1ec54 | ||
|
91c49b690f | ||
|
05453becf9 | ||
|
5fe27f8678 | ||
|
0924b8846f | ||
|
62a669159a | ||
|
2933bf1efa | ||
|
981f8b0145 | ||
|
213bda671c | ||
|
837aabfe77 | ||
|
cc599d414a | ||
|
01fcd7f80e | ||
|
af254ddbeb | ||
|
422e89c6c6 | ||
|
c2c11b38ed | ||
|
45d3f57228 | ||
|
c711c3d3fd | ||
|
2ae4aeee67 | ||
|
46238b8b51 | ||
|
b1528ec511 | ||
|
8ef4edb7d2 | ||
|
0172206c7c | ||
|
9d5bdf1b0d | ||
|
c0726b228d | ||
|
5b314aa907 | ||
|
c9f0de9838 | ||
|
2db41dd992 | ||
|
89e492c1f3 | ||
|
1aeb9a2702 | ||
|
b090a88b76 | ||
|
d5c479f7e5 | ||
|
b342258e75 | ||
|
3d716eff3e | ||
|
9791a184e3 | ||
|
142b8c95c5 | ||
|
363b89011f | ||
|
321372fa88 | ||
|
fc27dc6e64 | ||
|
8ad98f4ba4 | ||
|
9b050fa7fa | ||
|
a30826827a | ||
|
691b50aec4 | ||
|
55ca2d5eb5 | ||
|
779cd1ea9e | ||
|
7d53180a2f | ||
|
b6c41c00dd | ||
|
fff5679f6e | ||
|
7556dfe56b | ||
|
44b6f2b2ad | ||
|
f5607d96f3 | ||
|
2e85a9971b | ||
|
8d177266dc | ||
|
42d870ea4e | ||
|
424cad2130 | ||
|
430308a679 | ||
|
cc7570a4b2 | ||
|
413c0dfdfe | ||
|
f8fee771c4 | ||
|
3d05cef7cd | ||
|
30477f736e | ||
|
0cd5c0277b | ||
|
4fcddd1010 | ||
|
b269ef894f | ||
|
ea13432e4c | ||
|
c8a68bd185 | ||
|
f9ee895f58 | ||
|
59d9f2c79a | ||
|
f629558d60 | ||
|
4cbf70e756 | ||
|
40b9bec122 | ||
|
17b27d492a | ||
|
dd8f66a3fa | ||
|
d501436e3d | ||
|
8c6389b4f8 | ||
|
63ed73fa14 | ||
|
ef90506916 | ||
|
c0541b7e50 | ||
|
388134b38c | ||
|
f57d640c5d | ||
|
42c506ded8 | ||
|
373346e71e | ||
|
983cb4041c | ||
|
718fca57d2 | ||
|
2b1b0333ff | ||
|
9bb787d3f4 | ||
|
9f5979c1b1 | ||
|
b58230fd56 | ||
|
8e6ce05c04 | ||
|
b8d93bc9eb | ||
|
968ef93153 | ||
|
e0a0113e69 | ||
|
7181f2879a | ||
|
ff1626ff07 | ||
|
dca8781a03 | ||
|
43c5d74661 | ||
|
b1d0bdb536 | ||
|
2420631ed5 | ||
|
deabebb922 | ||
|
cd451060e3 | ||
|
82d4c4f961 | ||
|
79f5dac661 | ||
|
1a7c3fcd21 | ||
|
606be7f8e8 | ||
|
7f66f7688b | ||
|
25ad2192fd | ||
|
881b8cc1fa | ||
|
68905281f2 | ||
|
3cb78f30d2 | ||
|
ebce839eaa | ||
|
3170048ea9 | ||
|
976cac6742 | ||
|
99e5a932ae | ||
|
058585504f | ||
|
6d50a13cf0 | ||
|
198ecad13a | ||
|
1660139b27 | ||
|
0155eb98a2 | ||
|
c0c05bca86 | ||
|
32227d375a | ||
|
eb9c20a295 | ||
|
b826de4195 | ||
|
c3b55c2850 | ||
|
33313b6206 | ||
|
00fbe15a19 | ||
|
9e5c926bfd | ||
|
f0467c3d3b | ||
|
6758b994f8 | ||
|
d8646f8e7c | ||
|
0fa95aed36 | ||
|
d82356444a | ||
|
b8fa68ec8d | ||
|
78c542e71d | ||
|
6594863934 | ||
|
61e8c61fc6 | ||
|
ec9b5a0fa0 | ||
|
3664994cc5 | ||
|
366311e59b | ||
|
03f2ff912d | ||
|
ff9af51465 | ||
|
04fc588935 | ||
|
f215e887c9 | ||
|
3e6955fc7a | ||
|
b1cfb88308 | ||
|
d1af0eba79 | ||
|
f41873dc8c | ||
|
63562007df | ||
|
a99ecb9ea5 | ||
|
08f9bd9071 | ||
|
17c851f354 | ||
|
3382195a25 | ||
|
cd70b16eb3 | ||
|
2a98c00d97 | ||
|
5044af5490 | ||
|
320dc07676 | ||
|
95d3b0233a | ||
|
093466ef06 | ||
|
ceacc0ab15 | ||
|
cb3a9d9432 | ||
|
23fd56cf28 | ||
|
dd1c783919 | ||
|
8296e9a552 | ||
|
590565bdf0 | ||
|
9e2bf3d1f0 | ||
|
7547b67805 | ||
|
4abe4d7e8f | ||
|
0ebd52f3fd | ||
|
4cc2e47f02 | ||
|
b16bfdfa03 | ||
|
ece24080d5 | ||
|
a0ea8a193b | ||
|
d8e09d449e | ||
|
44c99e0f2e | ||
|
7110b4bcb3 | ||
|
3989859945 | ||
|
29ed4dd708 | ||
|
dbcbe33ba0 | ||
|
ed340a1c33 | ||
|
17ee7622b4 | ||
|
51877778bf | ||
|
1de8b492ea | ||
|
1fe38c4ec3 | ||
|
92bff08b84 | ||
|
37d0dacd2e | ||
|
e22f45fd25 | ||
|
56ef0a055a | ||
|
522991e23b | ||
|
ff061d4d2e | ||
|
5b84a704b1 | ||
|
3ba83821f2 | ||
|
45b82aa52d | ||
|
c99790bda2 | ||
|
7272880062 | ||
|
8130dd92ab | ||
|
9ca2d38307 | ||
|
e61bbdbcb7 | ||
|
88960c3082 | ||
|
0aa433882e | ||
|
d4f5713e0a | ||
|
790204ea28 | ||
|
927ed393d3 | ||
|
bf92b89a5f | ||
|
7ddaa143da | ||
|
157361cc7a | ||
|
dcf768376a | ||
|
e339ad2c23 | ||
|
8b10c5c72d | ||
|
bd13f6b744 | ||
|
934d00e87d | ||
|
9d421993a0 | ||
|
6751f673d5 | ||
|
c420a2195a | ||
|
ec3a6e0b6e | ||
|
ab0a5399f7 | ||
|
33e63c762f | ||
|
53b2a228ae | ||
|
c687e7ad9b | ||
|
ea3cdbd5f5 | ||
|
1ebe74d966 | ||
|
1f935d25a2 | ||
|
14e4dbacac | ||
|
f7a9257e16 | ||
|
99562df6cd | ||
|
630013cb85 | ||
|
6351d3af64 | ||
|
2d96048828 | ||
|
3e67bb94bc | ||
|
a26c9ff542 | ||
|
252003f76d | ||
|
928149f2a0 | ||
|
0283af812c | ||
|
6ccb2e74e0 | ||
|
5016464016 | ||
|
b787bd9b7b | ||
|
11f69d8679 | ||
|
c3168035ed | ||
|
5140c0a58f | ||
|
ee97582579 | ||
|
83e9172722 | ||
|
341e687987 | ||
|
93d9804127 | ||
|
f3d42cb356 | ||
|
04b9a3c929 | ||
|
e98160b84a | ||
|
7c769a53da | ||
|
08dc343083 | ||
|
52e654ff60 |
11
README.md
@@ -27,6 +27,7 @@
|
||||
|
||||
<img src=docs/images/dashboard.png />
|
||||
|
||||
[查看更多展示](docs/screenshot.md)
|
||||
|
||||
### 相关文章
|
||||
|
||||
@@ -50,12 +51,14 @@
|
||||
|
||||
### 主要功能
|
||||
|
||||
- 自定义模型和模型关系,模型属性支持下拉列表、字体颜色、计算属性等高级特性
|
||||
- 支持计算机、网络设备、存储设备、数据库、中间件、公有云资源等自动发现
|
||||
- 模型属性支持索引、多值、默认排序、字体颜色,支持计算属性
|
||||
- 支持自动发现、定时巡检、文件导入
|
||||
- 支持资源、层级、关系视图展示
|
||||
- 支持模型间关系配置和展示
|
||||
- 细粒度访问控制,完备的操作日志
|
||||
- 通用的资源搜索和关系搜索
|
||||
- 支持IP地址管理(IPAM), 数据中心基础设施管理(DCIM)
|
||||
- 支持跨模型搜索
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -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
|
@@ -256,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]
|
||||
@@ -346,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})
|
||||
|
@@ -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']:
|
||||
|
@@ -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
|
||||
@@ -550,7 +550,7 @@ 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)
|
||||
@@ -1268,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)
|
||||
|
||||
@@ -1536,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():
|
||||
@@ -1555,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():
|
||||
@@ -1592,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
|
||||
|
||||
@@ -1676,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)
|
||||
|
@@ -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):
|
||||
@@ -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))
|
||||
|
||||
|
@@ -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)
|
||||
|
||||
|
@@ -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 = []
|
||||
|
@@ -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,
|
||||
|
@@ -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!")
|
||||
# 只有管理员才可以定义执行机器为: 所有节点!
|
||||
|
@@ -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:
|
||||
|
@@ -55,6 +55,7 @@ def str2datetime(x):
|
||||
return datetime.datetime.strptime(x, "%Y-%m-%d %H:%M")
|
||||
|
||||
|
||||
|
||||
class ValueTypeMap(object):
|
||||
deserialize = {
|
||||
ValueTypeEnum.INT: string2int,
|
||||
|
@@ -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):
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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"],
|
||||
|
@@ -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
|
||||
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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 = []
|
||||
|
@@ -1,6 +1,7 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
import base64
|
||||
from typing import Set
|
||||
|
||||
import elasticsearch
|
||||
import redis
|
||||
|
@@ -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))
|
||||
|
@@ -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))
|
||||
|
@@ -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)
|
||||
|
@@ -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"
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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)
|
||||
|
||||
|
||||
|
@@ -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'
|
||||
|
@@ -58,4 +58,3 @@ python-magic==0.4.27
|
||||
jsonpath==0.82.2
|
||||
networkx>=3.1
|
||||
ipaddress>=1.0.23
|
||||
ruff==0.8.3
|
||||
|
@@ -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 {
|
||||
|
@@ -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
|
||||
@@ -313,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)
|
||||
|
@@ -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>
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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 {
|
||||
|
@@ -291,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;
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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({
|
||||
|
@@ -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'
|
||||
})
|
||||
}
|
||||
|
@@ -1,370 +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'
|
||||
|
||||
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;
|
||||
z-index: 1;
|
||||
|
||||
.btn-wave-hover(@primary-color_4, -1);
|
||||
}
|
||||
}
|
||||
</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>
|
||||
|
@@ -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>
|
||||
|
@@ -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"
|
||||
|
@@ -35,8 +35,8 @@
|
||||
:key="`${item.id}_${index}`"
|
||||
class="preference-search-tag"
|
||||
:style="{
|
||||
backgroundColor: item.id === currentPreferenceSearch ? '#2f54eb' : '',
|
||||
color: item.id === currentPreferenceSearch ? '#fff' : '',
|
||||
backgroundColor: item.id === currentPreferenceSearch ? '#2f54eb' : '#fafafa',
|
||||
color: item.id === currentPreferenceSearch ? '#fff' : '#000000a6',
|
||||
}"
|
||||
>
|
||||
<span @click="clickPreferenceSearch(item)">{{ item.name }}</span>
|
||||
@@ -189,10 +189,6 @@ export default {
|
||||
> i {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
.preference-search-delete {
|
||||
color: #a9a9a9;
|
||||
|
@@ -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"
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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>
|
||||
|
@@ -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',
|
||||
}"
|
||||
|
@@ -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>
|
||||
|
@@ -186,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}',
|
||||
@@ -897,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
|
||||
|
@@ -186,9 +186,6 @@ const cmdb_zh = {
|
||||
botSelect: '请选择机器人',
|
||||
refAttributeTips: '标题、内容可以引用该模型的属性值,引用方法为: {{ attr_name }}',
|
||||
webhookRefAttributeTips: '请求参数可以引用该模型的属性值,引用方法为: {{ attr_name }}',
|
||||
testSend: '测试发送',
|
||||
testSendTip: '请先保存触发器',
|
||||
testSendSuccess: '发送成功',
|
||||
newTrigger: '新增触发器',
|
||||
editTriggerTitle: '编辑触发器 {name}',
|
||||
newTriggerTitle: '新增触发器 {name}',
|
||||
@@ -896,11 +893,7 @@ if __name__ == "__main__":
|
||||
deviceName: '设备名',
|
||||
removeDevice: '删除设备',
|
||||
moveDevice: '移动设备',
|
||||
rackDetail: '机柜详情',
|
||||
calcUnitFreeCount: '计算机柜空闲U数',
|
||||
calcUnitFreeCountTip: '后台计算中,稍后刷新页面查看结果',
|
||||
calcUnitFreeCountTip1: '计算触发成功,稍后刷新页面查看结果',
|
||||
calcUnitFreeCountTip2: '确认要计算所有机柜的空闲U数?'
|
||||
rackDetail: '机柜详情'
|
||||
}
|
||||
}
|
||||
export default cmdb_zh
|
||||
|
@@ -104,16 +104,6 @@ export default {
|
||||
.cmdb-batch-upload-tips {
|
||||
color: @primary-color;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: linear-gradient(90deg, @primary-color_2 50%, transparent 0) repeat-x,
|
||||
linear-gradient(90deg, @primary-color_2 50%, transparent 0) repeat-x,
|
||||
linear-gradient(0deg, @primary-color_2 50%, transparent 0) repeat-y,
|
||||
linear-gradient(0deg, @primary-color_2 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;
|
||||
background-color: @primary-color_7;
|
||||
}
|
||||
}
|
||||
.ant-upload.ant-upload-drag .ant-upload-drag-container {
|
||||
vertical-align: baseline;
|
||||
|
@@ -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
|
||||
|
@@ -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()
|
||||
},
|
||||
|
@@ -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,
|
||||
}
|
||||
},
|
||||
|
@@ -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
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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>
|
||||
|
@@ -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 {
|
||||
|
@@ -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,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@@ -1194,7 +1194,7 @@ export default {
|
||||
.selected {
|
||||
background-color: @primary-color_3;
|
||||
.ci-types-left-detail-title {
|
||||
color: @primary-color;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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"
|
||||
|
@@ -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>
|
||||
|
@@ -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;
|
||||
@@ -816,7 +797,7 @@ export default {
|
||||
<style lang="less">
|
||||
.chart-wrapper {
|
||||
.ant-form-item {
|
||||
margin-bottom: 8px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -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>
|
||||
@@ -134,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 {
|
||||
@@ -166,9 +153,7 @@ export default {
|
||||
],
|
||||
|
||||
viewDetailCITypeId: 0,
|
||||
viewDetailAttrObj: {},
|
||||
|
||||
calculatedFreeUnitCount: false,
|
||||
viewDetailAttrObj: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -274,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'))
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -313,11 +281,7 @@ export default {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
&-calc {
|
||||
border-top: dashed 1px #e8e8e8;
|
||||
}
|
||||
|
||||
&-menu-icon {
|
||||
&-add-icon {
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
|
@@ -170,7 +170,6 @@ export default {
|
||||
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 rgba(122, 140, 204, 0.30);
|
||||
}
|
||||
}
|
||||
|
||||
&-http {
|
||||
width: 263px;
|
||||
height: 142px;
|
||||
@@ -312,10 +305,6 @@ export default {
|
||||
max-width: 30px !important;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0px 6px 28px 0px rgba(122, 140, 204, 0.30);
|
||||
}
|
||||
}
|
||||
}
|
||||
.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;
|
||||
|
@@ -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
|
||||
|
@@ -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 {
|
||||
|
@@ -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')
|
||||
|
@@ -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
|
||||
}
|
||||
},
|
||||
|
||||
|
@@ -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>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="operation-history">
|
||||
<div>
|
||||
<a-card :bordered="false">
|
||||
<a-tabs default-active-key="1">
|
||||
<a-tab-pane key="1" :tab="$t('cmdb.history.ciChange')">
|
||||
@@ -40,10 +40,4 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.operation-history {
|
||||
/deep/ .ant-tabs-tab {
|
||||
padding-top: 0px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style></style>
|
||||
|
@@ -1,11 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-form
|
||||
:colon="false"
|
||||
:labelCol="{ span:4 }"
|
||||
:wrapperCol="{ span:20 }"
|
||||
labelAlign="left"
|
||||
>
|
||||
<a-form :colon="false">
|
||||
<a-row :gutter="24">
|
||||
<a-col
|
||||
:sm="24"
|
||||
@@ -15,7 +10,12 @@
|
||||
v-for="attr in attrList.slice(0,3)"
|
||||
:key="attr.name"
|
||||
>
|
||||
<a-form-item :label="attr.alias || attr.name">
|
||||
<a-form-item
|
||||
:label="attr.alias || attr.name "
|
||||
:labelCol="{span:4}"
|
||||
:wrapperCol="{span:20}"
|
||||
labelAlign="right"
|
||||
>
|
||||
<a-select
|
||||
v-model="queryParams[attr.name]"
|
||||
:placeholder="$t('cmdb.history.pleaseSelect')"
|
||||
@@ -57,7 +57,12 @@
|
||||
:key="'expand_' + item.name"
|
||||
v-for="item in attrList.slice(3)"
|
||||
>
|
||||
<a-form-item :label="item.alias || item.name">
|
||||
<a-form-item
|
||||
:label="item.alias || item.name"
|
||||
:label-col="{ span: 4 }"
|
||||
:wrapper-col="{ span: 20 }"
|
||||
labelAlign="right"
|
||||
>
|
||||
<a-select
|
||||
v-model="queryParams[item.name]"
|
||||
:placeholder="$t('cmdb.history.pleaseSelect')"
|
||||
@@ -92,7 +97,7 @@
|
||||
</template>
|
||||
</a-row>
|
||||
<a-row>
|
||||
<a-col :span="24" :style="{ textAlign: 'right', marginBottom: '20px', marginTop: '-4px'}">
|
||||
<a-col :span="24" :style="{ textAlign: 'right' , marginBottom: '10px' }">
|
||||
<a-button type="primary" html-type="submit" @click="handleSearch">
|
||||
{{ $t('query') }}
|
||||
</a-button>
|
||||
|
@@ -114,11 +114,7 @@
|
||||
:placeholder="$t('cmdb.preference.searchPlaceholder')"
|
||||
/>
|
||||
<div v-for="group in filterCiTypeData" :key="group.id">
|
||||
<p
|
||||
@click="changeGroupExpand(group)"
|
||||
:style="{ display: 'inline-block', cursor: 'pointer' }"
|
||||
class="cmdb-preference-right-group-title"
|
||||
>
|
||||
<p @click="changeGroupExpand(group)" :style="{ display: 'inline-block', cursor: 'pointer' }">
|
||||
<a-icon :type="expandKeys.includes(group.id) ? 'caret-down' : 'caret-right'" />{{ group.name }}({{
|
||||
group.ci_types ? group.ci_types.length : 0
|
||||
}})
|
||||
@@ -628,16 +624,6 @@ export default {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
padding-top: 24px;
|
||||
|
||||
&-group-title {
|
||||
width: 300px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
&:hover {
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.cmdb-preference-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
@@ -1697,10 +1697,6 @@ export default {
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: @border-radius-box;
|
||||
|
||||
.ant-tabs-tab {
|
||||
padding-top: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -61,7 +61,7 @@
|
||||
:href="`/cmdb/cidetail/${col.reference_type_id}/${id}`"
|
||||
target="_blank"
|
||||
>
|
||||
{{ getReferenceAttrValue(id, col) }}
|
||||
{{ id }}
|
||||
</a>
|
||||
</template>
|
||||
<template #default="{row}" v-else-if="col.is_choice">
|
||||
@@ -118,7 +118,6 @@ import SearchForm from '../../../components/searchForm/SearchForm.vue'
|
||||
import CreateInstanceForm from '../../ci/modules/CreateInstanceForm.vue'
|
||||
import { getCITypeAttributesById } from '@/modules/cmdb/api/CITypeAttr'
|
||||
import { SUB_NET_CITYPE_NAME, SCOPE_CITYPE_NAME, ADDRESS_CITYPE_NAME } from '@/modules/cmdb/views/ipam/constants.js'
|
||||
import { getCITypes } from '@/modules/cmdb/api/CIType'
|
||||
|
||||
export default {
|
||||
name: 'AddTableModal',
|
||||
@@ -141,9 +140,6 @@ export default {
|
||||
ancestor_ids: undefined,
|
||||
attrList1: [],
|
||||
showCreateBtn: true, // 是否展示新增按钮
|
||||
|
||||
referenceShowAttrNameMap: {},
|
||||
referenceCIIdMap: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -178,7 +174,6 @@ export default {
|
||||
|
||||
await getSubscribeAttributes(this.addTypeId).then((res) => {
|
||||
this.preferenceAttrList = res.attributes // 已经订阅的全部列
|
||||
this.handleReferenceShowAttrName()
|
||||
})
|
||||
getCITypeAttributesById(this.addTypeId).then((res) => {
|
||||
this.attrList = res.attributes
|
||||
@@ -222,8 +217,6 @@ export default {
|
||||
}
|
||||
this.loading = false
|
||||
})
|
||||
|
||||
this.handleReferenceCIIdMap()
|
||||
})
|
||||
.catch(() => {
|
||||
this.loading = false
|
||||
@@ -237,83 +230,6 @@ export default {
|
||||
}
|
||||
return []
|
||||
},
|
||||
|
||||
async handleReferenceShowAttrName() {
|
||||
const needRequiredCITypeIds = this.preferenceAttrList?.filter((attr) => attr?.is_reference && attr?.reference_type_id).map((attr) => attr.reference_type_id) || []
|
||||
if (!needRequiredCITypeIds.length) {
|
||||
this.referenceShowAttrNameMap = {}
|
||||
return
|
||||
}
|
||||
|
||||
const res = await getCITypes({
|
||||
type_ids: needRequiredCITypeIds.join(',')
|
||||
})
|
||||
|
||||
const map = {}
|
||||
res.ci_types.forEach((ciType) => {
|
||||
map[ciType.id] = ciType?.show_name || ciType?.unique_name || ''
|
||||
})
|
||||
|
||||
this.referenceShowAttrNameMap = map
|
||||
},
|
||||
|
||||
async handleReferenceCIIdMap() {
|
||||
const referenceTypeCol = this.preferenceAttrList.filter((attr) => attr?.is_reference && attr?.reference_type_id) || []
|
||||
if (!this.tableData?.length || !referenceTypeCol?.length) {
|
||||
this.referenceCIIdMap = {}
|
||||
return
|
||||
}
|
||||
|
||||
const map = {}
|
||||
this.tableData.forEach((row) => {
|
||||
referenceTypeCol.forEach((col) => {
|
||||
const ids = Array.isArray(row[col.name]) ? row[col.name] : row[col.name] ? [row[col.name]] : []
|
||||
if (ids.length) {
|
||||
if (!map?.[col.reference_type_id]) {
|
||||
map[col.reference_type_id] = {}
|
||||
}
|
||||
ids.forEach((id) => {
|
||||
map[col.reference_type_id][id] = {}
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
if (!Object.keys(map).length) {
|
||||
this.referenceCIIdMap = {}
|
||||
return
|
||||
}
|
||||
|
||||
const allRes = await Promise.all(
|
||||
Object.keys(map).map((key) => {
|
||||
return searchCI({
|
||||
q: `_type:${key},_id:(${Object.keys(map[key]).join(';')})`,
|
||||
count: 9999
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
allRes.forEach((res) => {
|
||||
res.result.forEach((item) => {
|
||||
if (map?.[item._type]?.[item._id]) {
|
||||
map[item._type][item._id] = item
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
this.referenceCIIdMap = map
|
||||
},
|
||||
|
||||
getReferenceAttrValue(id, col) {
|
||||
const ci = this?.referenceCIIdMap?.[col?.reference_type_id]?.[id]
|
||||
if (!ci) {
|
||||
return id
|
||||
}
|
||||
|
||||
const attrName = this.referenceShowAttrNameMap?.[col.reference_type_id]
|
||||
return ci?.[attrName] || id
|
||||
},
|
||||
|
||||
onSelectChange() {},
|
||||
handleClose() {
|
||||
this.$refs.xTable.clearCheckboxRow()
|
||||
|
@@ -19,11 +19,13 @@
|
||||
>
|
||||
<treeselect
|
||||
:value="selectCITypeIds"
|
||||
class="custom-treeselect custom-treeselect-white filter-content-ciTypes"
|
||||
class="custom-treeselect custom-treeselect-bgcAndBorder filter-content-ciTypes"
|
||||
:style="{
|
||||
width: '400px',
|
||||
zIndex: '1000',
|
||||
'--custom-height': '32px',
|
||||
'--custom-bg-color': '#FFF',
|
||||
'--custom-border': '1px solid #d9d9d9',
|
||||
'--custom-multiple-lineHeight': '32px',
|
||||
}"
|
||||
:multiple="true"
|
||||
|
@@ -78,3 +78,21 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.save-condition-modal {
|
||||
/deep/ .ant-modal-close-x {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
}
|
||||
|
||||
/deep/ .ant-modal-body {
|
||||
padding: 24px 18px;
|
||||
}
|
||||
|
||||
/deep/ .ant-modal-footer {
|
||||
padding: 10px 18px 18px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -512,31 +512,15 @@ export default {
|
||||
padding: 14px 18px;
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
/deep/ .filter-content-ciTypes {
|
||||
&:not(.vue-treeselect--disabled):not(.vue-treeselect--focused) {
|
||||
.vue-treeselect__control {
|
||||
border: solid 1px transparent;
|
||||
|
||||
&:hover {
|
||||
border-color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-input {
|
||||
width: 100%;
|
||||
|
||||
/deep/ .ant-input {
|
||||
border: solid 1px transparent;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
border-color: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
&-suffix {
|
||||
@@ -548,12 +532,8 @@ export default {
|
||||
width: 100%;
|
||||
|
||||
/deep/ .ant-select-selection {
|
||||
border: solid 1px transparent;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
|
||||
&:hover {
|
||||
border-color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -164,12 +164,8 @@ export default {
|
||||
/deep/ & > input {
|
||||
height: 100%;
|
||||
margin-left: 10px;
|
||||
border: solid 1px transparent;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
|
||||
&:focus {
|
||||
border-color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -116,10 +116,6 @@ body {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin-right: 10px;
|
||||
|
||||
&:hover {
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.topmenu {
|
||||
@@ -564,7 +560,7 @@ body {
|
||||
top: 10px;
|
||||
border-radius: 5px;
|
||||
&:hover {
|
||||
color: @primary-color;
|
||||
background: #c1bfbf;
|
||||
}
|
||||
.anticon {
|
||||
margin-right: 0;
|
||||
@@ -665,7 +661,7 @@ body {
|
||||
margin-right: 5px !important;
|
||||
}
|
||||
&:hover {
|
||||
background-color: @primary-color_5;
|
||||
background-color: #e7e7e7;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -873,10 +869,6 @@ body {
|
||||
align-items: center;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: #597ef7 !important;
|
||||
}
|
||||
}
|
||||
.vue-treeselect__placeholder,
|
||||
.vue-treeselect__single-value {
|
||||
@@ -893,46 +885,15 @@ body {
|
||||
border-radius: 2px !important;
|
||||
border-color: #e4e7ed;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: #597ef7 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-vue-treeselect__control(
|
||||
@bgColor: @primary-color_7,
|
||||
@border: none,
|
||||
@hoverBgColor: none,
|
||||
@hoverBorderColor: none,
|
||||
) {
|
||||
.vue-treeselect__control {
|
||||
background-color: @bgColor;
|
||||
border: @border;
|
||||
}
|
||||
|
||||
.mixin(@borderColor) when (iscolor(@borderColor)) {
|
||||
border-color: @borderColor !important;
|
||||
}
|
||||
|
||||
&:not(.vue-treeselect--disabled):not(.vue-treeselect--focused) {
|
||||
.vue-treeselect__control {
|
||||
&:hover {
|
||||
background-color: if(iscolor(@hoverBgColor), @hoverBgColor, @bgColor);
|
||||
.mixin(@hoverBorderColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
.custom-vue-treeselect__control(@bgColor:@primary-color_7,@border:none) {
|
||||
background-color: @bgColor;
|
||||
border: @border;
|
||||
}
|
||||
|
||||
.custom-treeselect {
|
||||
.custom-vue-treeselect__control(
|
||||
@primary-color_7,
|
||||
none,
|
||||
@primary-color_5,
|
||||
none
|
||||
);
|
||||
|
||||
.vue-treeselect__control {
|
||||
.custom-vue-treeselect__control();
|
||||
height: var(--custom-height) !important;
|
||||
border-radius: 2px !important;
|
||||
}
|
||||
@@ -948,34 +909,12 @@ body {
|
||||
.vue-treeselect__limit-tip-text {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&.vue-treeselect--focused {
|
||||
.vue-treeselect__control {
|
||||
border: 1px solid @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 自定义背景颜色和border
|
||||
.custom-treeselect-bgcAndBorder {
|
||||
.custom-vue-treeselect__control(
|
||||
var(--custom-bg-color),
|
||||
var(--custom-border),
|
||||
var(--custom-hover-bg-color),
|
||||
var(--custom-hover-border-color)
|
||||
);
|
||||
.custom-treeselect-bgcAndBorder .vue-treeselect__control {
|
||||
.custom-vue-treeselect__control(var(--custom-bg-color), var(--custom-border));
|
||||
}
|
||||
|
||||
// 白色背景
|
||||
.custom-treeselect-white {
|
||||
.custom-vue-treeselect__control(
|
||||
#fff,
|
||||
1px solid #d9d9d9,
|
||||
none,
|
||||
@primary-color
|
||||
);
|
||||
}
|
||||
|
||||
// 自定义背景颜色和border
|
||||
.custom-treeselect.vue-treeselect--multi {
|
||||
.vue-treeselect__multi-value,
|
||||
@@ -1086,20 +1025,6 @@ body {
|
||||
background-color: @primary-color_7;
|
||||
border: none;
|
||||
}
|
||||
|
||||
&,
|
||||
.ant-input,
|
||||
.ant-time-picker-input {
|
||||
&:not([disabled]) {
|
||||
&:hover {
|
||||
background-color: @primary-color_5;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background-color: @primary-color_7;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.ops-input.ant-input {
|
||||
background-color: @primary-color_7;
|
||||
@@ -1238,31 +1163,13 @@ body {
|
||||
}
|
||||
|
||||
// button
|
||||
.ops-button-ghost {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
&.ant-btn-background-ghost.ant-btn-primary {
|
||||
border-color: @primary-color_8;
|
||||
background-color: @primary-color_5 !important;
|
||||
box-shadow: none;
|
||||
|
||||
.btn-wave-hover(@primary-color_4);
|
||||
|
||||
&[disabled] {
|
||||
border-color: #d9d9d9;
|
||||
background-color: @primary-color_7!important;
|
||||
}
|
||||
|
||||
&:not([disabled]):hover {
|
||||
color: #3F75FF;
|
||||
border-color: transparent;
|
||||
}
|
||||
}
|
||||
.ops-button-ghost.ant-btn-background-ghost.ant-btn-primary {
|
||||
background-color: @primary-color_5!important;
|
||||
border-color: @primary-color_8;
|
||||
}
|
||||
|
||||
.ant-btn-primary:not(.ant-btn-background-ghost) {
|
||||
.btn-wave-hover(#3F75FF);
|
||||
.ops-button-ghost.ant-btn-background-ghost.ant-btn-primary[disabled] {
|
||||
border-color: #d9d9d9;
|
||||
background-color: @primary-color_7!important;
|
||||
}
|
||||
|
||||
// button
|
||||
@@ -1307,25 +1214,15 @@ body {
|
||||
|
||||
//modal
|
||||
.ant-modal-content {
|
||||
.ant-modal-close-x {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
line-height: 56px;
|
||||
}
|
||||
|
||||
.ant-modal-header {
|
||||
border-bottom: none;
|
||||
padding: 22px 22px;
|
||||
|
||||
.ant-modal-title {
|
||||
padding-left: 10px;
|
||||
border-left: 4px solid @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-footer {
|
||||
border-top: none;
|
||||
padding: 16px 22px 22px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -70,46 +70,3 @@
|
||||
background-color: @primary-color_3;
|
||||
color: @primary-color;
|
||||
}
|
||||
|
||||
.btn-wave-hover(
|
||||
@hoverBgColor,
|
||||
@bgZIndex: 0
|
||||
) {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
& > * {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&:not([disabled])::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
z-index: @bgZIndex;
|
||||
width: 100%;
|
||||
aspect-ratio: 1/1;
|
||||
border-radius: 50%;
|
||||
|
||||
background-color: @hoverBgColor;
|
||||
-webkit-transform: scale(0) translate(-50%, -50%);
|
||||
-ms-transform: scale(0) translate(-50%, -50%);
|
||||
transform: scale(0) translate(-50%, -50%);
|
||||
transform-origin: left top;
|
||||
|
||||
-webkit-transition: -webkit-transform 0.3s ease-out;
|
||||
transition: -webkit-transform 0.3s ease-out;
|
||||
transition: transform 0.3s ease-out;
|
||||
transition: transform 0.3s ease-out, -webkit-transform 0.3s ease-out;
|
||||
}
|
||||
|
||||
&:not([disabled]):hover {
|
||||
&::after {
|
||||
-webkit-transform: scale(2) translate(-50%, -50%);
|
||||
-ms-transform: scale(2) translate(-50%, -50%);
|
||||
transform: scale(2) translate(-50%, -50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -41,7 +41,7 @@ services:
|
||||
- redis
|
||||
|
||||
cmdb-api:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-api:2.4.16
|
||||
image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-api:2.4.15
|
||||
container_name: cmdb-api
|
||||
env_file:
|
||||
- .env
|
||||
@@ -64,14 +64,14 @@ services:
|
||||
gunicorn --workers=4 autoapp:app -b 0.0.0.0:5000 -D
|
||||
|
||||
celery -A celery_worker.celery worker -E -Q one_cmdb_async --autoscale=4,1 --logfile=one_cmdb_async.log -D
|
||||
celery -A celery_worker.celery worker -E -Q acl_async --logfile=one_acl_async.log --autoscale=4,1 -D
|
||||
celery -A celery_worker.celery worker -E -Q acl_async --logfile=one_acl_async.log --autoscale=2,1 -D
|
||||
|
||||
nohup flask cmdb-trigger > trigger.log 2>&1 &
|
||||
flask cmdb-init-cache
|
||||
flask cmdb-init-acl
|
||||
flask init-import-user-from-acl
|
||||
flask init-department
|
||||
nohup flask cmdb-patch -v 2.4.16 &
|
||||
nohup flask cmdb-patch -v 2.4.15 &
|
||||
flask cmdb-counter > counter.log 2>&1
|
||||
networks:
|
||||
new:
|
||||
@@ -84,7 +84,7 @@ services:
|
||||
test: "ps aux|grep -v grep|grep -v '1 root'|grep gunicorn || exit 1"
|
||||
|
||||
cmdb-ui:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-ui:2.4.16
|
||||
image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-ui:2.4.15
|
||||
container_name: cmdb-ui
|
||||
depends_on:
|
||||
cmdb-api:
|
||||
|
@@ -1,5 +1,8 @@
|
||||
|
||||
<h2 align="center">Simple, lightweight, and versatile operational CMDB</h2>
|
||||
<p align="center">
|
||||
<a href="https://veops.cn"><img src="images/logo.png" alt="维易CMDB" width="300"/></a>
|
||||
</p>
|
||||
<h3 align="center">Simple, lightweight, and versatile operational CMDB</h3>
|
||||
<p align="center">
|
||||
<a href="https://github.com/veops/cmdb/blob/master/LICENSE"><img src="https://img.shields.io/badge/License-AGPLv3-brightgreen" alt="License: GPLv3"></a>
|
||||
<a href="https:https://github.com/sendya/ant-design-pro-vue"><img src="https://img.shields.io/badge/UI-Ant%20Design%20Pro%20Vue-brightgreen" alt="UI"></a>
|
||||
@@ -21,6 +24,17 @@
|
||||
|
||||
## Overview
|
||||
|
||||
### System Overview
|
||||
|
||||
<img src=images/dashboard.png />
|
||||
|
||||
[View more screenshots](screenshot.md)
|
||||
|
||||
### Document
|
||||
|
||||
- <a href="https://zhuanlan.zhihu.com/p/98453732" target="_blank">Design Document</a>
|
||||
- <a href="https://github.com/veops/cmdb/tree/master/docs/cmdb_api.md" target="_blank">API Documentation</a>
|
||||
- <a href="https://mp.weixin.qq.com/s/EflmmJ-qdUkddTx2hRt3pA" target="_blank">Practice of Tree View</a>
|
||||
|
||||
### Features
|
||||
|
||||
@@ -37,12 +51,12 @@
|
||||
|
||||
### Main Features
|
||||
|
||||
- Custom models and model relationships, with model attributes supporting advanced features such as dropdown lists, font colors, and computed attributes.
|
||||
- Support for automatic discovery of computers, network devices, storage devices, databases, middleware, public cloud resources, etc.
|
||||
- Support for displaying resource, hierarchy, and relationship views.
|
||||
- Model attributes support indexing, multiple values, default sorting, font color, and computed properties.
|
||||
- Support automatic discovery, scheduled inspections, and file import.
|
||||
- Support resource, tree view, and relationship view display.
|
||||
- Support configuration and display of relationships between models.
|
||||
- Fine-grained access control and comprehensive operation logs.
|
||||
- General resource and relationship search capabilities.
|
||||
- Support for IP Address Management (IPAM) and Data Center Infrastructure Management (DCIM).
|
||||
- Support cross-model search.
|
||||
|
||||
|
||||
|
||||
@@ -54,7 +68,7 @@
|
||||
|
||||
### One-Click Docker Quick Build
|
||||
|
||||
[//]: # (> Method 1)
|
||||
> Method 1
|
||||
- step 1: **Prepare: install Docker and Docker Compose (v2)**
|
||||
- step 2: copy the repository
|
||||
```shell
|
||||
@@ -64,20 +78,13 @@ git clone https://github.com/veops/cmdb.git
|
||||
```
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
[//]: # (> M**ethod 2 Usefull for linux os.)
|
||||
|
||||
[//]: # (- step 1: **Prepare: install Docker and Docker Compose (v2)**)
|
||||
|
||||
[//]: # (- step 2: directly use the install.sh file in the project's root directory to `install`, `start`, `pause`, `status`, `delete`, and `uninstall` the application. )
|
||||
|
||||
[//]: # (```shell)
|
||||
|
||||
[//]: # (curl -so install.sh https://raw.githubusercontent.com/veops/cmdb/master/install.sh )
|
||||
|
||||
[//]: # (sh install.sh install)
|
||||
|
||||
[//]: # (```**)
|
||||
> Method 2 Usefull for linux os.
|
||||
- step 1: **Prepare: install Docker and Docker Compose (v2)**
|
||||
- step 2: directly use the install.sh file in the project's root directory to `install`, `start`, `pause`, `status`, `delete`, and `uninstall` the application.
|
||||
```shell
|
||||
curl -so install.sh https://raw.githubusercontent.com/veops/cmdb/master/install.sh
|
||||
sh install.sh install
|
||||
```
|
||||
|
||||
|
||||
### [Local Setup](local_en.md)
|
||||
@@ -93,9 +100,13 @@ docker compose up -d
|
||||
## Contributing
|
||||
|
||||
1. Fork it
|
||||
2. Create your feature branch (`git checkout -b my-feature`)
|
||||
3. Commit your changes (`git commit -am 'Add some feature'`)
|
||||
4. Push to the branch (`git push origin my-feature`)
|
||||
5. Create new Pull Request
|
||||
1. Create your feature branch (`git checkout -b my-feature`)
|
||||
1. Commit your changes (`git commit -am 'Add some feature'`)
|
||||
1. Push to the branch (`git push origin my-feature`)
|
||||
1. Create new Pull Request
|
||||
|
||||
---
|
||||
|
||||
_**Welcome to pay attention to our public account, click to contact us, join WeChat, QQ operation and maintenance group, and get more product and industry related information**_
|
||||
|
||||

|
||||
|
BIN
docs/images/0.png
Normal file
After Width: | Height: | Size: 260 KiB |
BIN
docs/images/1.png
Normal file
After Width: | Height: | Size: 98 KiB |
BIN
docs/images/10.jpg
Normal file
After Width: | Height: | Size: 57 KiB |
BIN
docs/images/2.png
Normal file
After Width: | Height: | Size: 273 KiB |
BIN
docs/images/3.png
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
docs/images/4.png
Normal file
After Width: | Height: | Size: 78 KiB |
BIN
docs/images/5.png
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
docs/images/6.png
Normal file
After Width: | Height: | Size: 365 KiB |
BIN
docs/images/7.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
docs/images/8.png
Normal file
After Width: | Height: | Size: 245 KiB |
BIN
docs/images/9.png
Normal file
After Width: | Height: | Size: 200 KiB |
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 191 KiB |
29
docs/screenshot.md
Normal file
@@ -0,0 +1,29 @@
|
||||
### 系统概览
|
||||
|
||||
- 服务树
|
||||
|
||||

|
||||
|
||||
- 资源视图
|
||||
|
||||

|
||||
|
||||
- 订阅页面
|
||||
|
||||

|
||||
|
||||
- 模型配置
|
||||
|
||||

|
||||
|
||||
- 属性设置
|
||||
|
||||

|
||||
|
||||
- 资源搜索
|
||||
|
||||

|
||||
|
||||
- 定制仪表盘
|
||||
|
||||

|