Compare commits
657 Commits
1.1.0
...
1.2.2-back
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f415664acf | ||
![]() |
786b617179 | ||
![]() |
e6ee1f57b4 | ||
![]() |
feab6b3b30 | ||
![]() |
7f1548b343 | ||
![]() |
2d4ac33046 | ||
![]() |
17e2a02281 | ||
![]() |
096545c5a1 | ||
![]() |
04dff33ac5 | ||
![]() |
cfa18c2b8e | ||
![]() |
13354955db | ||
![]() |
056dee009f | ||
![]() |
1309c8425e | ||
![]() |
f0ded13df1 | ||
![]() |
e78d367cea | ||
![]() |
bb683804f4 | ||
![]() |
209214f6a3 | ||
![]() |
ab003826a3 | ||
![]() |
817c4a265d | ||
![]() |
8ed893fb21 | ||
![]() |
55f5e6bd0c | ||
![]() |
62d03605a3 | ||
![]() |
6170da0870 | ||
![]() |
e2a577de24 | ||
![]() |
89d963c81a | ||
![]() |
a9b7bf6442 | ||
![]() |
a66e13eca6 | ||
![]() |
b37ed48b9d | ||
![]() |
e78f4e984d | ||
![]() |
9f70a61c24 | ||
![]() |
4b9cfcb867 | ||
![]() |
f86c9f90ad | ||
![]() |
3229899c40 | ||
![]() |
31a6ab59fb | ||
![]() |
11083d5b7e | ||
![]() |
7b5fb4f164 | ||
![]() |
34789645f7 | ||
![]() |
f380452ef0 | ||
![]() |
decd9c55e5 | ||
![]() |
d8cac9bb00 | ||
![]() |
1dacd96e3c | ||
![]() |
e815f0ff89 | ||
![]() |
43534b6142 | ||
![]() |
5ea6b4a8d8 | ||
![]() |
a53ca3faf1 | ||
![]() |
86fdd6c93b | ||
![]() |
0e0661fe02 | ||
![]() |
86ce65e0ea | ||
![]() |
c3eb4fab13 | ||
![]() |
7a476372e1 | ||
![]() |
5e421ce607 | ||
![]() |
279fc98d76 | ||
![]() |
36a78dda90 | ||
![]() |
1d8676f456 | ||
![]() |
0c3dd92592 | ||
![]() |
d33261d0c2 | ||
![]() |
4da1bb9896 | ||
![]() |
7041c6a106 | ||
![]() |
12ffb310bf | ||
![]() |
d9799fec70 | ||
![]() |
4570d5b54b | ||
![]() |
d49e3e81a4 | ||
![]() |
579b1b486c | ||
![]() |
eba372062e | ||
![]() |
c9ccd2bbab | ||
![]() |
5aff8bac10 | ||
![]() |
1a12687027 | ||
![]() |
a221d2b491 | ||
![]() |
98d5d607b6 | ||
![]() |
9ee5369f35 | ||
![]() |
ccd99eb24d | ||
![]() |
c5c6760b5d | ||
![]() |
fbb0d641db | ||
![]() |
c07281bf41 | ||
![]() |
24bad09c74 | ||
![]() |
cddf71e600 | ||
![]() |
9f82052c71 | ||
![]() |
e0eb57d38d | ||
![]() |
b6b97accb1 | ||
![]() |
799934171a | ||
![]() |
10ebf03698 | ||
![]() |
cd258fbb52 | ||
![]() |
6cba181fad | ||
![]() |
f9504cece3 | ||
![]() |
182f1c9da8 | ||
![]() |
6e0c79411b | ||
![]() |
de27e8691d | ||
![]() |
e7b009786b | ||
![]() |
5e7aeaf12e | ||
![]() |
bc6818f531 | ||
![]() |
f78bc4e4f7 | ||
![]() |
cd77063f68 | ||
![]() |
0be1a0825d | ||
![]() |
551841bdd7 | ||
![]() |
5262233312 | ||
![]() |
52dc3e1788 | ||
![]() |
a9f9031f49 | ||
![]() |
f7c00cd8f7 | ||
![]() |
bdc1cb6d3b | ||
![]() |
b8ed931eb6 | ||
![]() |
8edc7c8ea4 | ||
![]() |
ba3fc0fe10 | ||
![]() |
a37dccdec0 | ||
![]() |
1df9236a53 | ||
![]() |
049ccca3e0 | ||
![]() |
00c4501d13 | ||
![]() |
061fcf926d | ||
![]() |
7e7a5f3fd4 | ||
![]() |
f8bb396dc4 | ||
![]() |
a832edce0d | ||
![]() |
af3fdeb1da | ||
![]() |
67d1f299b3 | ||
![]() |
cb7c0ecaa2 | ||
![]() |
64d3a21153 | ||
![]() |
a8019ed88a | ||
![]() |
4959574f21 | ||
![]() |
e89d1a0520 | ||
![]() |
7f0b40b565 | ||
![]() |
bc2040bbdc | ||
![]() |
a3d105c04c | ||
![]() |
40a75a9904 | ||
![]() |
463d50328f | ||
![]() |
3e3d56b82e | ||
![]() |
379778412b | ||
![]() |
93199b4dc6 | ||
![]() |
1e3990f41d | ||
![]() |
e225159cce | ||
![]() |
195ceec9b4 | ||
![]() |
8c75d13ab4 | ||
![]() |
fd4ecfeb4c | ||
![]() |
5bd73867b6 | ||
![]() |
0497383ec5 | ||
![]() |
22e6f28464 | ||
![]() |
4c80ba2c3a | ||
![]() |
48e7146b05 | ||
![]() |
fa2f8c15d0 | ||
![]() |
300d6f5609 | ||
![]() |
29f9f3de31 | ||
![]() |
a84569c0e1 | ||
![]() |
15a7793dce | ||
![]() |
a3bad43c16 | ||
![]() |
0f2e107e5c | ||
![]() |
2006754889 | ||
![]() |
ac9e76ae93 | ||
![]() |
233817c2ed | ||
![]() |
4d4e909652 | ||
![]() |
0e090bc80a | ||
![]() |
114126dfc4 | ||
![]() |
1a47c88d84 | ||
![]() |
429002ade2 | ||
![]() |
5611f6c7f5 | ||
![]() |
1a166a797c | ||
![]() |
261be4a3a6 | ||
![]() |
087b9126b2 | ||
![]() |
c08f9efb1a | ||
![]() |
0eaa63b2c2 | ||
![]() |
3f0f4d50a1 | ||
![]() |
14b7296200 | ||
![]() |
77cd005f15 | ||
![]() |
6716e0c979 | ||
![]() |
cd6d413f08 | ||
![]() |
1877888846 | ||
![]() |
b1bc6f632d | ||
![]() |
2d0859a727 | ||
![]() |
509b268fba | ||
![]() |
8816086b37 | ||
![]() |
11dba43d83 | ||
![]() |
9b68aacc27 | ||
![]() |
dcd633bb39 | ||
![]() |
c3129663eb | ||
![]() |
816d1f5a69 | ||
![]() |
bef98489b1 | ||
![]() |
8cc903c0e1 | ||
![]() |
02fba565e0 | ||
![]() |
256a29dae1 | ||
![]() |
13087a10b7 | ||
![]() |
27a3888e35 | ||
![]() |
fb0ec05ff8 | ||
![]() |
76c4aa2c55 | ||
![]() |
e1c503e51e | ||
![]() |
5965755caa | ||
![]() |
2bef75e044 | ||
![]() |
1ad54561b0 | ||
![]() |
ee9c271854 | ||
![]() |
c372a564d8 | ||
![]() |
a9cae15d98 | ||
![]() |
42e322012c | ||
![]() |
8cf6e8ec75 | ||
![]() |
21dd7bd6c0 | ||
![]() |
a49f7f8816 | ||
![]() |
996f498bfc | ||
![]() |
79d4d2fb22 | ||
![]() |
684cc83256 | ||
![]() |
2378728d7f | ||
![]() |
93673fd095 | ||
![]() |
f81dffd072 | ||
![]() |
b785bc5704 | ||
![]() |
6719e2679f | ||
![]() |
cb9c8db5ba | ||
![]() |
a2637313f8 | ||
![]() |
7277b3f98b | ||
![]() |
467689525f | ||
![]() |
c1d0a8fe3e | ||
![]() |
1a04a05b79 | ||
![]() |
b8bffd884c | ||
![]() |
e57df4e522 | ||
![]() |
ca7373cd63 | ||
![]() |
d00e37a619 | ||
![]() |
ed631b6358 | ||
![]() |
7a3c211db7 | ||
![]() |
8276026b01 | ||
![]() |
624beb6fa0 | ||
![]() |
c3fdeda1ca | ||
![]() |
0440ac84fd | ||
![]() |
9f6bee86f0 | ||
![]() |
7606b86854 | ||
![]() |
6e3b468303 | ||
![]() |
170b9baca6 | ||
![]() |
e10cbe23b9 | ||
![]() |
d34838bdd8 | ||
![]() |
c979c23e6b | ||
![]() |
516d0b8dc8 | ||
![]() |
99c7fd72f8 | ||
![]() |
2828e7a7b6 | ||
![]() |
eb7b7c33d2 | ||
![]() |
16162c01ed | ||
![]() |
160d4a0a82 | ||
![]() |
a60ff01c3a | ||
![]() |
51c29fcc9c | ||
![]() |
74198a0edc | ||
![]() |
8df6e3860f | ||
![]() |
d5482decb8 | ||
![]() |
9a15896125 | ||
![]() |
721042ed5f | ||
![]() |
5d8206f291 | ||
![]() |
451dca4793 | ||
![]() |
9f5a5c5fc4 | ||
![]() |
69556310e5 | ||
![]() |
79a14ab5d3 | ||
![]() |
891a59a323 | ||
![]() |
d98bc819d8 | ||
![]() |
c11cca2c2e | ||
![]() |
02d42de133 | ||
![]() |
95650da33b | ||
![]() |
5444098ab7 | ||
![]() |
273d28be4c | ||
![]() |
d8acb86427 | ||
![]() |
23588b02d9 | ||
![]() |
ac913595a9 | ||
![]() |
6569265fab | ||
![]() |
0726067bff | ||
![]() |
a37a16c70e | ||
![]() |
e72f82d4ee | ||
![]() |
2bf317c617 | ||
![]() |
e554106eb9 | ||
![]() |
5033ea2e82 | ||
![]() |
d1ef1192b2 | ||
![]() |
d50b4fa47c | ||
![]() |
78806a0933 | ||
![]() |
bbbdbfa912 | ||
![]() |
06cccc53fa | ||
![]() |
831a1abac7 | ||
![]() |
a48a7a9444 | ||
![]() |
e838537356 | ||
![]() |
543fb0cf91 | ||
![]() |
2b850a5b89 | ||
![]() |
6cf8f57ed4 | ||
![]() |
ada7fdc78d | ||
![]() |
e53ac28331 | ||
![]() |
cc5adaf02f | ||
![]() |
1a535e5202 | ||
![]() |
103b1a6f4a | ||
![]() |
62da4edae4 | ||
![]() |
801c640724 | ||
![]() |
844a9b5013 | ||
![]() |
081d2aed23 | ||
![]() |
26431faf6c | ||
![]() |
4d81b9d18d | ||
![]() |
cf9edbb1c4 | ||
![]() |
e9dc613548 | ||
![]() |
3af22f7e91 | ||
![]() |
2135cb8ece | ||
![]() |
376aad5d26 | ||
![]() |
27ba45aa75 | ||
![]() |
de17007788 | ||
![]() |
e86a7906fe | ||
![]() |
4a8e6abc06 | ||
![]() |
4ed02e1ebb | ||
![]() |
e3025176ff | ||
![]() |
6e4b7f4695 | ||
![]() |
39dfa81f58 | ||
![]() |
2eb7fed9f4 | ||
![]() |
ff8d5ca351 | ||
![]() |
07c4ad05f4 | ||
![]() |
5234f2ef54 | ||
![]() |
548c2e5500 | ||
![]() |
ec2c6eff6f | ||
![]() |
15fe82c62f | ||
![]() |
70808e7d85 | ||
![]() |
cb5ecf4a8a | ||
![]() |
f8c9d41776 | ||
![]() |
c7a7f94cc5 | ||
![]() |
cf24596e4d | ||
![]() |
2e4ae78ee0 | ||
![]() |
24045455e8 | ||
![]() |
5d877f049f | ||
![]() |
68a7e790d8 | ||
![]() |
7ca161ed75 | ||
![]() |
677889f266 | ||
![]() |
24a4935e85 | ||
![]() |
c7253f468f | ||
![]() |
0281441318 | ||
![]() |
8ab0bd2212 | ||
![]() |
31b7551852 | ||
![]() |
4439533b09 | ||
![]() |
a320bc3778 | ||
![]() |
9a660827a6 | ||
![]() |
aa95783fc9 | ||
![]() |
94721d7e66 | ||
![]() |
58c361c908 | ||
![]() |
58dac45dde | ||
![]() |
af8052ce49 | ||
![]() |
4db697e714 | ||
![]() |
fc43527cf1 | ||
![]() |
72d82ac94b | ||
![]() |
7f239133b5 | ||
![]() |
4e79182c7a | ||
![]() |
cf044cc679 | ||
![]() |
5e82866370 | ||
![]() |
a44ff73dd3 | ||
![]() |
acd87b2250 | ||
![]() |
8b01f8316d | ||
![]() |
3c427086e7 | ||
![]() |
994b49fb87 | ||
![]() |
6ec8f4ced9 | ||
![]() |
78fe591111 | ||
![]() |
a873c74a7c | ||
![]() |
3bebdfda77 | ||
![]() |
1b5b0f4736 | ||
![]() |
e7eb0577ec | ||
![]() |
5577f8d339 | ||
![]() |
b1a6c0f880 | ||
![]() |
1853ba1d60 | ||
![]() |
d304e8a2a3 | ||
![]() |
2b19cea0d8 | ||
![]() |
85cbb3b842 | ||
![]() |
968c44572a | ||
![]() |
3201b843a8 | ||
![]() |
6946471d96 | ||
![]() |
02e0927a9c | ||
![]() |
911b52e938 | ||
![]() |
0a9e85bb58 | ||
![]() |
db5b727300 | ||
![]() |
10b3a16f79 | ||
![]() |
9f15cd47ea | ||
![]() |
9a259642af | ||
![]() |
8702d1ddfe | ||
![]() |
80558b9cf1 | ||
![]() |
7a9ea1d4dc | ||
![]() |
6307ff5759 | ||
![]() |
2805590755 | ||
![]() |
75d059f15a | ||
![]() |
86013b44a6 | ||
![]() |
877fa1b2d1 | ||
![]() |
239c0f60fb | ||
![]() |
9c65206ab0 | ||
![]() |
59e039896e | ||
![]() |
4d7fe96c87 | ||
![]() |
1b9583c2d0 | ||
![]() |
95aced6209 | ||
![]() |
2c3f247c08 | ||
![]() |
728f4339e6 | ||
![]() |
d28cf0d98f | ||
![]() |
f56ba6ba33 | ||
![]() |
ada0a7edf6 | ||
![]() |
129059b7a5 | ||
![]() |
3de8104e44 | ||
![]() |
b641b0eb15 | ||
![]() |
a97c998be5 | ||
![]() |
b22d900e27 | ||
![]() |
181e38733c | ||
![]() |
e09e95c3f5 | ||
![]() |
13d9b42ca7 | ||
![]() |
2667bffd34 | ||
![]() |
ffdbd3435a | ||
![]() |
0f2909172d | ||
![]() |
540108ac8d | ||
![]() |
3319310c11 | ||
![]() |
5ea280b611 | ||
![]() |
e3188acbc4 | ||
![]() |
15e6209aa3 | ||
![]() |
3c1df9c880 | ||
![]() |
7d9ddd4698 | ||
![]() |
2e3d385747 | ||
![]() |
4fe44611d3 | ||
![]() |
8b58440e00 | ||
![]() |
28caf89748 | ||
![]() |
3558bbf6a0 | ||
![]() |
dac65eb262 | ||
![]() |
34d091adfa | ||
![]() |
06f76bea0a | ||
![]() |
4b41f8d9dc | ||
![]() |
7e0bb9d06e | ||
![]() |
3e565666c6 | ||
![]() |
f735e8e536 | ||
![]() |
395b45fa48 | ||
![]() |
219cde1e0e | ||
![]() |
593916d6aa | ||
![]() |
e52ec4f9b9 | ||
![]() |
38805a0e1f | ||
![]() |
3d7f3aef58 | ||
![]() |
a216db37fe | ||
![]() |
68cf6cd790 | ||
![]() |
6ca385f1bc | ||
![]() |
0a3b54e320 | ||
![]() |
bdfb757219 | ||
![]() |
5e5f4fbb97 | ||
![]() |
d7d3186ef0 | ||
![]() |
ac8e1b672f | ||
![]() |
1c9ea51780 | ||
![]() |
75b766eeba | ||
![]() |
c978d14ea2 | ||
![]() |
e2864974db | ||
![]() |
993ac11eb6 | ||
![]() |
63429789ea | ||
![]() |
0f756503ff | ||
![]() |
e7c443a63a | ||
![]() |
238809ecfd | ||
![]() |
e56672864f | ||
![]() |
ed9448d1ba | ||
![]() |
330aaf69b2 | ||
![]() |
cf5e6a1f1e | ||
![]() |
f6faeb034e | ||
![]() |
2efb5b71c3 | ||
![]() |
22c1f86ca1 | ||
![]() |
ce4af37fd8 | ||
![]() |
f446cbf9e5 | ||
![]() |
7581fb381f | ||
![]() |
bedf19993d | ||
![]() |
e558087bcf | ||
![]() |
698daf46c7 | ||
![]() |
831696e275 | ||
![]() |
6f9a805f0c | ||
![]() |
982ccd57c6 | ||
![]() |
c74369fd76 | ||
![]() |
7ad45c897b | ||
![]() |
0410f1e1be | ||
![]() |
ccbc385bc8 | ||
![]() |
7fbf7b189a | ||
![]() |
be208fe915 | ||
![]() |
1d3729aa24 | ||
![]() |
b0d44b07b4 | ||
![]() |
cbca9ffd95 | ||
![]() |
ed465d2b5b | ||
![]() |
fbcb3c6caf | ||
![]() |
e6e2c84f45 | ||
![]() |
8f9215ffad | ||
![]() |
6183d22b20 | ||
![]() |
97b91aaf7c | ||
![]() |
7577f17dd9 | ||
![]() |
4fab8251c7 | ||
![]() |
d308da6ba1 | ||
![]() |
7c4dfdc1c2 | ||
![]() |
b9a4b709db | ||
![]() |
2aa4756a3e | ||
![]() |
991ada31ba | ||
![]() |
43adc943b9 | ||
![]() |
4a20156f3d | ||
![]() |
137f753471 | ||
![]() |
b29b41fc73 | ||
![]() |
a7d9697397 | ||
![]() |
7ec3cfb3fd | ||
![]() |
eec6f41f6a | ||
![]() |
2cc918f79d | ||
![]() |
3014b62086 | ||
![]() |
07b1ad121c | ||
![]() |
e419b03cab | ||
![]() |
e0b4bf0956 | ||
![]() |
686e09a1f8 | ||
![]() |
018ddc75c6 | ||
![]() |
d085736bac | ||
![]() |
4778ead7c2 | ||
![]() |
7825333190 | ||
![]() |
131310efef | ||
![]() |
a13f808aa2 | ||
![]() |
305549e7f2 | ||
![]() |
958c8b4f21 | ||
![]() |
41b415362c | ||
![]() |
1178f7a0bf | ||
![]() |
359d837dee | ||
![]() |
373766691f | ||
![]() |
bac0275624 | ||
![]() |
0fc0f13064 | ||
![]() |
b75a8e6a2b | ||
![]() |
68ab73bdb5 | ||
![]() |
4c1fc4a11e | ||
![]() |
03f166f8a4 | ||
![]() |
d42daf9ce0 | ||
![]() |
d1862fba27 | ||
![]() |
f0db3d6b79 | ||
![]() |
0efbf57152 | ||
![]() |
1d842b1b87 | ||
![]() |
48ca7aca8c | ||
![]() |
827ef2e403 | ||
![]() |
4df62b563e | ||
![]() |
21d37a1be0 | ||
![]() |
fe236ea929 | ||
![]() |
05b4b61c6e | ||
![]() |
e2b91f88ab | ||
![]() |
e89c9c1c6a | ||
![]() |
eeba7b50f9 | ||
![]() |
7ab00250bf | ||
![]() |
9896a67c21 | ||
![]() |
db38ef522f | ||
![]() |
845f2a2abd | ||
![]() |
9b73792dc9 | ||
![]() |
fccbe44cf7 | ||
![]() |
e23486dbc6 | ||
![]() |
935df713f3 | ||
![]() |
17c7ce8f21 | ||
![]() |
24b9aa44d2 | ||
![]() |
014e6d38a0 | ||
![]() |
12f216c0e7 | ||
![]() |
ae3f7cb909 | ||
![]() |
32117b73aa | ||
![]() |
e1414b2b10 | ||
![]() |
839e2e4321 | ||
![]() |
e8992a1d16 | ||
![]() |
3c4af23edf | ||
![]() |
e3a93970f4 | ||
![]() |
7b9866158b | ||
![]() |
3fb286b552 | ||
![]() |
7461693aa7 | ||
![]() |
3afb8395a7 | ||
![]() |
0950022f09 | ||
![]() |
227cf1e72b | ||
![]() |
a3e98aedf9 | ||
![]() |
ac3ec4746f | ||
![]() |
36939f36ee | ||
![]() |
07325d4c5e | ||
![]() |
da512f166c | ||
![]() |
f2692d4a85 | ||
![]() |
9a9492bbee | ||
![]() |
d2e3d44c14 | ||
![]() |
06c777ee81 | ||
![]() |
68a45c25c8 | ||
![]() |
3a9f670ec6 | ||
![]() |
4f96194084 | ||
![]() |
a478721f07 | ||
![]() |
ff1c6a9de9 | ||
![]() |
edad22c23a | ||
![]() |
5ffa2be89a | ||
![]() |
8dc00185ea | ||
![]() |
79edae70d6 | ||
![]() |
f9d964bdc3 | ||
![]() |
7c834308f0 | ||
![]() |
253abc5ef1 | ||
![]() |
5f55799572 | ||
![]() |
54a9ff088f | ||
![]() |
63db6ee4d7 | ||
![]() |
73502677ff | ||
![]() |
dedba18553 | ||
![]() |
95d8522b9f | ||
![]() |
56d77021c4 | ||
![]() |
f4a1cc72d9 | ||
![]() |
d8d884ecc1 | ||
![]() |
a70ae0f603 | ||
![]() |
41b69b5f93 | ||
![]() |
c8e01a70b6 | ||
![]() |
4b98a143c7 | ||
![]() |
593ed62585 | ||
![]() |
afdf186fa4 | ||
![]() |
40448b9365 | ||
![]() |
f85badf482 | ||
![]() |
12f25cf3a2 | ||
![]() |
90c7bf625a | ||
![]() |
198f65b467 | ||
![]() |
3a4518db0e | ||
![]() |
af622d852f | ||
![]() |
046476f8bd | ||
![]() |
34a11ce64d | ||
![]() |
24e889adbe | ||
![]() |
d214da3303 | ||
![]() |
da0ddfc8c7 | ||
![]() |
eb782a9080 | ||
![]() |
c8dd9bbf0b | ||
![]() |
3587ec54eb | ||
![]() |
733f43bc47 | ||
![]() |
0428b5447e | ||
![]() |
8a64165a2a | ||
![]() |
28f8ee440d | ||
![]() |
53e1a6fdf7 | ||
![]() |
012808a1da | ||
![]() |
f32a949482 | ||
![]() |
883e6c5033 | ||
![]() |
41093a39b0 | ||
![]() |
ab988cf82e | ||
![]() |
40bb93afcd | ||
![]() |
dbcb7138f2 | ||
![]() |
fe58af2e78 | ||
![]() |
94c68c966e | ||
![]() |
77083abcc5 | ||
![]() |
1302092798 | ||
![]() |
ec53bf8084 | ||
![]() |
5fa31aa97e | ||
![]() |
924eeca280 | ||
![]() |
d6c0ed6429 | ||
![]() |
b87d41bada | ||
![]() |
2f37942961 | ||
![]() |
acd305bd9d | ||
![]() |
788a29a8cb | ||
![]() |
3bd5ef4523 | ||
![]() |
9e8e92a8e0 | ||
![]() |
979bf1b431 | ||
![]() |
1e74e7c0fb | ||
![]() |
240573e354 | ||
![]() |
36c6a65d22 | ||
![]() |
a814bed79e | ||
![]() |
86e52ce58a | ||
![]() |
9ddaba5333 | ||
![]() |
5b079471b9 | ||
![]() |
8cc73cf59c | ||
![]() |
fc28f4ec7e | ||
![]() |
995e9d6fdc | ||
![]() |
a89711610d | ||
![]() |
67c2b13713 | ||
![]() |
1ff1e4a8d7 | ||
![]() |
ea8af98324 | ||
![]() |
43a0ce6d76 | ||
![]() |
3ba0e169b2 | ||
![]() |
dc15accd04 | ||
![]() |
12d53db740 | ||
![]() |
10b6fd26dc | ||
![]() |
06e56338d8 | ||
![]() |
16622e62de | ||
![]() |
75779da35c | ||
![]() |
94efcec7da | ||
![]() |
a3d0d2ed34 | ||
![]() |
45024f5dcb | ||
![]() |
90dc00b168 | ||
![]() |
ba36ce8836 | ||
![]() |
db5c1487cb | ||
![]() |
57d5a919d2 | ||
![]() |
546c0928fb | ||
![]() |
4c487fe85f | ||
![]() |
5e44aa9283 | ||
![]() |
c6b3ca9815 | ||
![]() |
d69618e491 | ||
![]() |
26bec4222f | ||
![]() |
4005023fd4 | ||
![]() |
8617d3dd1e | ||
![]() |
632081e828 | ||
![]() |
6b9acf09dc | ||
![]() |
2c6edafeb2 | ||
![]() |
9cf0573921 | ||
![]() |
da7d61b160 | ||
![]() |
f11523953f |
@@ -20,6 +20,12 @@ body:
|
||||
options:
|
||||
- label: 未点赞(star)问题直接删除, 不予理会
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: 基础框架问题(如布局,modal,drawer等)请提交给vben官方
|
||||
options:
|
||||
- label: 我确认是业务问题而非基础框架问题
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 如何复现
|
||||
|
18
.github/CODEOWNERS
vendored
18
.github/CODEOWNERS
vendored
@@ -1,14 +1,14 @@
|
||||
# default onwer
|
||||
* anncwb@126.com vince292007@gmail.com
|
||||
* anncwb@126.com vince292007@gmail.com netfan@foxmail.com
|
||||
|
||||
# vben core onwer
|
||||
/.github/ anncwb@126.com vince292007@gmail.com
|
||||
/.vscode/ anncwb@126.com vince292007@gmail.com
|
||||
/packages/ anncwb@126.com vince292007@gmail.com
|
||||
/packages/@core/ anncwb@126.com vince292007@gmail.com
|
||||
/internal/ anncwb@126.com vince292007@gmail.com
|
||||
/scripts/ anncwb@126.com vince292007@gmail.com
|
||||
/.github/ anncwb@126.com vince292007@gmail.com netfan@foxmail.com
|
||||
/.vscode/ anncwb@126.com vince292007@gmail.com netfan@foxmail.com
|
||||
/packages/ anncwb@126.com vince292007@gmail.com netfan@foxmail.com
|
||||
/packages/@core/ anncwb@126.com vince292007@gmail.com netfan@foxmail.com
|
||||
/internal/ anncwb@126.com vince292007@gmail.com netfan@foxmail.com
|
||||
/scripts/ anncwb@126.com vince292007@gmail.com netfan@foxmail.com
|
||||
|
||||
# vben team onwer
|
||||
apps/ anncwb@126.com vince292007@gmail.com @vbenjs/team-v5
|
||||
docs/ anncwb@126.com vince292007@gmail.com @vbenjs/team-v5
|
||||
apps/ anncwb@126.com vince292007@gmail.com netfan@foxmail.com @vbenjs/team-v5
|
||||
docs/ anncwb@126.com vince292007@gmail.com netfan@foxmail.com @vbenjs/team-v5
|
||||
|
2
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@@ -62,7 +62,7 @@ body:
|
||||
description: Before submitting the issue, please make sure you do the following
|
||||
# description: By submitting this issue, you agree to follow our [Code of Conduct](https://example.com).
|
||||
options:
|
||||
- label: Read the [docs](https://anncwb.github.io/vue-vben-admin-doc/)
|
||||
- label: Read the [docs](https://doc.vben.pro/)
|
||||
required: true
|
||||
- label: Ensure the code is up to date. (Some issues have been fixed in the latest version)
|
||||
required: true
|
||||
|
2
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
2
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
@@ -62,7 +62,7 @@ body:
|
||||
label: Validations
|
||||
description: Before submitting the issue, please make sure you do the following
|
||||
options:
|
||||
- label: Read the [docs](https://anncwb.github.io/vue-vben-admin-doc/)
|
||||
- label: Read the [docs](https://doc.vben.pro/)
|
||||
required: true
|
||||
- label: Ensure the code is up to date. (Some issues have been fixed in the latest version)
|
||||
required: true
|
||||
|
17
.github/workflows/deploy.yml
vendored
17
.github/workflows/deploy.yml
vendored
@@ -153,3 +153,20 @@ jobs:
|
||||
username: ${{ secrets.WEB_NAIVE_FTP_ACCOUNT }}
|
||||
password: ${{ secrets.WEB_NAIVE_FTP_PASSWORD }}
|
||||
local-dir: ./apps/web-naive/dist/
|
||||
|
||||
rerun-on-failure:
|
||||
name: Rerun on failure
|
||||
needs:
|
||||
- deploy-playground-ftp
|
||||
- deploy-docs-ftp
|
||||
- deploy-antd-ftp
|
||||
- deploy-ele-ftp
|
||||
- deploy-naive-ftp
|
||||
if: failure() && fromJSON(github.run_attempt) < 10
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Retry ${{ fromJSON(github.run_attempt) }} of 10
|
||||
env:
|
||||
GH_REPO: ${{ github.repository }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: gh workflow run rerun.yml -F run_id=${{ github.run_id }}
|
||||
|
19
.github/workflows/rerun.yml
vendored
Normal file
19
.github/workflows/rerun.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: Rerun workflow
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
run_id:
|
||||
description: The workflow id to relanch
|
||||
required: true
|
||||
jobs:
|
||||
rerun:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: rerun ${{ inputs.run_id }}
|
||||
env:
|
||||
GH_REPO: ${{ github.repository }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
gh run watch ${{ inputs.run_id }} > /dev/null 2>&1
|
||||
gh run rerun ${{ inputs.run_id }} --failed
|
@@ -1,4 +1,10 @@
|
||||
export default {
|
||||
'*.md': ['prettier --cache --ignore-unknown --write'],
|
||||
'*.vue': [
|
||||
'prettier --write',
|
||||
'eslint --cache --fix',
|
||||
'stylelint --fix --allow-empty-input',
|
||||
],
|
||||
'*.{js,jsx,ts,tsx}': [
|
||||
'prettier --cache --ignore-unknown --write',
|
||||
'eslint --cache --fix',
|
||||
@@ -7,14 +13,8 @@ export default {
|
||||
'prettier --cache --ignore-unknown --write',
|
||||
'stylelint --fix --allow-empty-input',
|
||||
],
|
||||
'*.md': ['prettier --cache --ignore-unknown --write'],
|
||||
'*.vue': [
|
||||
'prettier --write',
|
||||
'eslint --cache --fix',
|
||||
'stylelint --fix --allow-empty-input',
|
||||
],
|
||||
'package.json': ['prettier --cache --write'],
|
||||
'{!(package)*.json,*.code-snippets,.!(browserslist)*rc}': [
|
||||
'prettier --cache --write--parser json',
|
||||
],
|
||||
'package.json': ['prettier --cache --write'],
|
||||
};
|
||||
|
8
.vscode/launch.json
vendored
8
.vscode/launch.json
vendored
@@ -9,7 +9,7 @@
|
||||
"url": "http://localhost:5555",
|
||||
"env": { "NODE_ENV": "development" },
|
||||
"sourceMaps": true,
|
||||
"webRoot": "${workspaceFolder}"
|
||||
"webRoot": "${workspaceFolder}/playground"
|
||||
},
|
||||
{
|
||||
"type": "chrome",
|
||||
@@ -18,7 +18,7 @@
|
||||
"url": "http://localhost:5666",
|
||||
"env": { "NODE_ENV": "development" },
|
||||
"sourceMaps": true,
|
||||
"webRoot": "${workspaceFolder}"
|
||||
"webRoot": "${workspaceFolder}/apps/web-antd"
|
||||
},
|
||||
{
|
||||
"type": "chrome",
|
||||
@@ -27,7 +27,7 @@
|
||||
"url": "http://localhost:5777",
|
||||
"env": { "NODE_ENV": "development" },
|
||||
"sourceMaps": true,
|
||||
"webRoot": "${workspaceFolder}"
|
||||
"webRoot": "${workspaceFolder}/apps/web-ele"
|
||||
},
|
||||
{
|
||||
"type": "chrome",
|
||||
@@ -36,7 +36,7 @@
|
||||
"url": "http://localhost:5888",
|
||||
"env": { "NODE_ENV": "development" },
|
||||
"sourceMaps": true,
|
||||
"webRoot": "${workspaceFolder}"
|
||||
"webRoot": "${workspaceFolder}/apps/web-naive"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@@ -99,7 +99,8 @@
|
||||
"**/.stylelintcache": true,
|
||||
"**/.DS_Store": true,
|
||||
"**/vite.config.mts.*": true,
|
||||
"**/tea.yaml": true
|
||||
"**/tea.yaml": true,
|
||||
"**/public/tinymce/**": true
|
||||
},
|
||||
"files.watcherExclude": {
|
||||
"**/.git/objects/**": true,
|
||||
@@ -227,5 +228,6 @@
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"editor.linkedEditing": true, // 自动同步更改html标签,
|
||||
"vscodeCustomCodeColor.highlightValue": "v-access", // v-access显示的颜色
|
||||
"vscodeCustomCodeColor.highlightValueColor": "#CCFFFF"
|
||||
"vscodeCustomCodeColor.highlightValueColor": "#CCFFFF",
|
||||
"oxc.enable": false
|
||||
}
|
||||
|
102
CHANGELOG.md
102
CHANGELOG.md
@@ -1,3 +1,105 @@
|
||||
# 1.2.2
|
||||
|
||||
**FEATURES**
|
||||
|
||||
- 代码生成支持路径方式生成
|
||||
- 代码生成 支持选择表单生成类型(需要模板支持)
|
||||
- 工作流 支持按钮权限
|
||||
|
||||
# 1.2.1
|
||||
|
||||
# BUG FIXES
|
||||
|
||||
- 客户端管理 错误的status disabled
|
||||
- modal/drawer升级后zIndex(2000)会遮挡Tinymce的下拉框zIndex(1300)
|
||||
|
||||
# 1.2.0
|
||||
|
||||
**REFACTOR**
|
||||
|
||||
- 菜单选择组件重构为Table形式
|
||||
- 字典相关功能重构 采用一个Map储存字典(之前为两个Map)
|
||||
- 代码生成配置页面重构 去除步骤条
|
||||
|
||||
**Features**
|
||||
|
||||
- 对接后端工作流
|
||||
- ~~通用的vxe-table排序事件(排序逻辑改为在排序事件中处理而非在api处理)~~
|
||||
- getDict/getDictOptions 提取公共逻辑 减少冗余代码
|
||||
- 字典新增对Number类型的支持 -> `getDictOptions('', true);`即可获取number类型的value
|
||||
- 文件上传 增加上传进度条 下方上传提示
|
||||
- 图片上传 增加上传进度条 下方上传提示
|
||||
- oss下载进度提示
|
||||
|
||||
**BUG FIXES**
|
||||
|
||||
- 字典项为空时getDict方法无限调用接口(无奈兼容 不给字典item本来就是错误用法)
|
||||
- 表格排序翻页会丢失排序参数
|
||||
- 下载文件时(responseType === 'blob')需要判断下载失败(返回json而非二进制)的情况
|
||||
- requestClient缺失i18n内容
|
||||
|
||||
**OTHERS**
|
||||
|
||||
- 用户管理 新增只获取一次(mounted)默认密码而非每次打开modal都获取
|
||||
- `apps/web-antd/src/utils/dict.ts` `getDict`方法将于下个版本删除 使用`getDictOptions`替代
|
||||
- VxeTable升级V4.10.0
|
||||
- 移除`@deprecated` `apps/web-antd/src/adapter/vxe-table.ts`的`tableCheckboxEvent`方法
|
||||
- 移除`由于更新方案弃用的` `apps/web-antd/src/adapter/vxe-table.ts`的`vxeSortEvent`方法
|
||||
- 移除apps下的ele和naive目录
|
||||
|
||||
# 1.1.3
|
||||
|
||||
**REFACTOR**
|
||||
|
||||
- 重构: 判断vxe-table的复选框是否选中
|
||||
|
||||
**Bug Fixes**
|
||||
|
||||
- 节点树在编辑 & 空数组(不勾选)情况 勾选节点会造成watch延迟触发 导致会带上父节点id造成id重复
|
||||
- 节点树在节点独立情况下的控制台warning: Invalid prop: type check failed for prop "value". Expected Array, got Object
|
||||
|
||||
**Others**
|
||||
|
||||
- 角色管理 优化Drawer布局
|
||||
- unplugin-vue-components插件(默认未开启) 需要排除Button组件 全局已经默认导入了
|
||||
|
||||
**BUG FIXES**
|
||||
|
||||
- 操作日志详情 在description组件中json预览样式异常
|
||||
- 微服务版本 区间查询和中文搜索条件一起使用 无法正确查询
|
||||
|
||||
# 1.1.2
|
||||
|
||||
**Features**
|
||||
|
||||
- Options转Enum工具函数
|
||||
|
||||
**OTHERS**
|
||||
|
||||
- 菜单管理 改为虚拟滚动
|
||||
- 移除requestClient的一些冗余参数
|
||||
- 主动退出登录(右上角个人选项)不需要带跳转地址
|
||||
|
||||
**BUG FIXES**
|
||||
|
||||
- 语言 漏加Content-Language请求头
|
||||
- 用户管理/岗位管理 左边部门树错误emit导致会调用两次列表api
|
||||
|
||||
# 1.1.1
|
||||
|
||||
**REFACTOR**
|
||||
|
||||
- 使用VxeTable重构OAuth账号绑定列表(替代antdv的Table)
|
||||
- commonDownloadExcel方法 支持处理区间选择器字段导出excel
|
||||
|
||||
**BUG FIXES**
|
||||
|
||||
- 修复在Modal/Drawer中使用VxeTable时, 第二次打开表单参数依旧为第一次提交的参数
|
||||
|
||||
**OTHERS**
|
||||
|
||||
- 废弃downloadExcel方法 统一使用commonDownloadExcel方法
|
||||
|
||||
# 1.1.0
|
||||
|
||||
**FEATURES**
|
||||
|
@@ -2,21 +2,21 @@
|
||||
|
||||
## 提示
|
||||
|
||||
该仓库使用vben最新版本v5开发, ~~老版本v2地址(不维护)~~ [前往](https://gitee.com/dapppp/ruoyi-plus-vben)
|
||||
该仓库使用vben最新版本v5开发
|
||||
|
||||
v5版本采用分仓(包)目录结构, 具体开发路径为: `根目录/apps/web-antd`
|
||||
|
||||
目前对应后端版本: **5.2.3/2.2.3**
|
||||
目前对应后端版本: **分布式5.3.0/微服务2.2.2**
|
||||
|
||||
V1.1.0版本已支持离线图标
|
||||
|
||||
## 进度
|
||||
V1.2.0版本对接warmflow工作流
|
||||
|
||||
**工作流相关模块等待后端重构后开发**
|
||||
`微服务最新版暂不支持warmflow工作流`
|
||||
|
||||
基础功能已经开发完毕
|
||||
`微服务最新版暂不支持warmflow工作流`
|
||||
|
||||
👉 [更新日志](https://gitee.com/dapppp/ruoyi-plus-vben5/blob/main/CHANGELOG.md)
|
||||
`微服务最新版暂不支持warmflow工作流`
|
||||
|
||||
## 简介
|
||||
|
||||
@@ -24,9 +24,9 @@ V1.1.0版本已支持离线图标
|
||||
|
||||
| 组件/框架 | 版本 |
|
||||
| :------------- | :----- |
|
||||
| vben | 5.4.1 |
|
||||
| ant-design-vue | 4.2.5 |
|
||||
| vue | 3.5.11 |
|
||||
| vben | 5.5.3 |
|
||||
| ant-design-vue | 4.2.6 |
|
||||
| vue | 3.5.13 |
|
||||
|
||||
对应后端项目: **(分布式 5.X 分支 微服务 2.分支)**
|
||||
|
||||
@@ -52,9 +52,17 @@ admin 账号: admin admin123
|
||||
|
||||
[RuoYi-Plus 文档地址](https://plus-doc.dromara.org/#/)
|
||||
|
||||
## 关于表单
|
||||
|
||||
如果你觉得`useVbenForm`难度很大, 完全可以**使用原生antd表单**进行开发, 不一定非得用`useVbenForm`进行开发
|
||||
|
||||
`apps/web-antd/src/views/system/notice/notice-modal.vue`即`通知公告modal`使用**原生antd form**进行(反向🤔)重构, 不想用`useVbenForm`可参考该页面进行表单开发
|
||||
|
||||
复杂表单(如各种联动, 需要自定义样式布局, 需要自定义组件)**优先使用原生表单**(反正说了也没人听听😅)
|
||||
|
||||
## 预览图
|
||||
|
||||
         
|
||||
        
|
||||
|
||||
## 安装使用
|
||||
|
||||
@@ -189,9 +197,3 @@ pnpm build:antd
|
||||
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt=" Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt=" Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
|
||||
| :-: | :-: | :-: | :-: | :-: |
|
||||
| not support | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
|
||||
|
||||
## 捐赠
|
||||
|
||||
如果项目帮助到您 可以考虑请作者喝杯咖啡 万分感谢您对开源的支持!
|
||||
|
||||
<img src=https://plus.dapdap.top/minio-server/plus/2024/03/16/98a9d704eb0c4c04b721bf7799217571.jpg height=360px />
|
||||
|
15
apps/backend-mock/api/system/dept/.post.ts
Normal file
15
apps/backend-mock/api/system/dept/.post.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { verifyAccessToken } from '~/utils/jwt-utils';
|
||||
import {
|
||||
sleep,
|
||||
unAuthorizedResponse,
|
||||
useResponseSuccess,
|
||||
} from '~/utils/response';
|
||||
|
||||
export default eventHandler(async (event) => {
|
||||
const userinfo = verifyAccessToken(event);
|
||||
if (!userinfo) {
|
||||
return unAuthorizedResponse(event);
|
||||
}
|
||||
await sleep(600);
|
||||
return useResponseSuccess(null);
|
||||
});
|
15
apps/backend-mock/api/system/dept/[id].delete.ts
Normal file
15
apps/backend-mock/api/system/dept/[id].delete.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { verifyAccessToken } from '~/utils/jwt-utils';
|
||||
import {
|
||||
sleep,
|
||||
unAuthorizedResponse,
|
||||
useResponseSuccess,
|
||||
} from '~/utils/response';
|
||||
|
||||
export default eventHandler(async (event) => {
|
||||
const userinfo = verifyAccessToken(event);
|
||||
if (!userinfo) {
|
||||
return unAuthorizedResponse(event);
|
||||
}
|
||||
await sleep(1000);
|
||||
return useResponseSuccess(null);
|
||||
});
|
15
apps/backend-mock/api/system/dept/[id].put.ts
Normal file
15
apps/backend-mock/api/system/dept/[id].put.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { verifyAccessToken } from '~/utils/jwt-utils';
|
||||
import {
|
||||
sleep,
|
||||
unAuthorizedResponse,
|
||||
useResponseSuccess,
|
||||
} from '~/utils/response';
|
||||
|
||||
export default eventHandler(async (event) => {
|
||||
const userinfo = verifyAccessToken(event);
|
||||
if (!userinfo) {
|
||||
return unAuthorizedResponse(event);
|
||||
}
|
||||
await sleep(2000);
|
||||
return useResponseSuccess(null);
|
||||
});
|
61
apps/backend-mock/api/system/dept/list.ts
Normal file
61
apps/backend-mock/api/system/dept/list.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { faker } from '@faker-js/faker';
|
||||
import { verifyAccessToken } from '~/utils/jwt-utils';
|
||||
import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response';
|
||||
|
||||
const formatterCN = new Intl.DateTimeFormat('zh-CN', {
|
||||
timeZone: 'Asia/Shanghai',
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
});
|
||||
|
||||
function generateMockDataList(count: number) {
|
||||
const dataList = [];
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const dataItem: Record<string, any> = {
|
||||
id: faker.string.uuid(),
|
||||
pid: 0,
|
||||
name: faker.commerce.department(),
|
||||
status: faker.helpers.arrayElement([0, 1]),
|
||||
createTime: formatterCN.format(
|
||||
faker.date.between({ from: '2021-01-01', to: '2022-12-31' }),
|
||||
),
|
||||
remark: faker.lorem.sentence(),
|
||||
};
|
||||
if (faker.datatype.boolean()) {
|
||||
dataItem.children = Array.from(
|
||||
{ length: faker.number.int({ min: 1, max: 5 }) },
|
||||
() => ({
|
||||
id: faker.string.uuid(),
|
||||
pid: dataItem.id,
|
||||
name: faker.commerce.department(),
|
||||
status: faker.helpers.arrayElement([0, 1]),
|
||||
createTime: formatterCN.format(
|
||||
faker.date.between({ from: '2023-01-01', to: '2023-12-31' }),
|
||||
),
|
||||
remark: faker.lorem.sentence(),
|
||||
}),
|
||||
);
|
||||
}
|
||||
dataList.push(dataItem);
|
||||
}
|
||||
|
||||
return dataList;
|
||||
}
|
||||
|
||||
const mockData = generateMockDataList(10);
|
||||
|
||||
export default eventHandler(async (event) => {
|
||||
const userinfo = verifyAccessToken(event);
|
||||
if (!userinfo) {
|
||||
return unAuthorizedResponse(event);
|
||||
}
|
||||
|
||||
const listData = structuredClone(mockData);
|
||||
|
||||
return useResponseSuccess(listData);
|
||||
});
|
12
apps/backend-mock/api/system/menu/list.ts
Normal file
12
apps/backend-mock/api/system/menu/list.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { verifyAccessToken } from '~/utils/jwt-utils';
|
||||
import { MOCK_MENU_LIST } from '~/utils/mock-data';
|
||||
import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response';
|
||||
|
||||
export default eventHandler(async (event) => {
|
||||
const userinfo = verifyAccessToken(event);
|
||||
if (!userinfo) {
|
||||
return unAuthorizedResponse(event);
|
||||
}
|
||||
|
||||
return useResponseSuccess(MOCK_MENU_LIST);
|
||||
});
|
28
apps/backend-mock/api/system/menu/name-exists.ts
Normal file
28
apps/backend-mock/api/system/menu/name-exists.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { verifyAccessToken } from '~/utils/jwt-utils';
|
||||
import { MOCK_MENU_LIST } from '~/utils/mock-data';
|
||||
import { unAuthorizedResponse } from '~/utils/response';
|
||||
|
||||
const namesMap: Record<string, any> = {};
|
||||
|
||||
function getNames(menus: any[]) {
|
||||
menus.forEach((menu) => {
|
||||
namesMap[menu.name] = String(menu.id);
|
||||
if (menu.children) {
|
||||
getNames(menu.children);
|
||||
}
|
||||
});
|
||||
}
|
||||
getNames(MOCK_MENU_LIST);
|
||||
|
||||
export default eventHandler(async (event) => {
|
||||
const userinfo = verifyAccessToken(event);
|
||||
if (!userinfo) {
|
||||
return unAuthorizedResponse(event);
|
||||
}
|
||||
const { id, name } = getQuery(event);
|
||||
|
||||
return (name as string) in namesMap &&
|
||||
(!id || namesMap[name as string] !== String(id))
|
||||
? useResponseSuccess(true)
|
||||
: useResponseSuccess(false);
|
||||
});
|
28
apps/backend-mock/api/system/menu/path-exists.ts
Normal file
28
apps/backend-mock/api/system/menu/path-exists.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { verifyAccessToken } from '~/utils/jwt-utils';
|
||||
import { MOCK_MENU_LIST } from '~/utils/mock-data';
|
||||
import { unAuthorizedResponse } from '~/utils/response';
|
||||
|
||||
const pathMap: Record<string, any> = { '/': 0 };
|
||||
|
||||
function getPaths(menus: any[]) {
|
||||
menus.forEach((menu) => {
|
||||
pathMap[menu.path] = String(menu.id);
|
||||
if (menu.children) {
|
||||
getPaths(menu.children);
|
||||
}
|
||||
});
|
||||
}
|
||||
getPaths(MOCK_MENU_LIST);
|
||||
|
||||
export default eventHandler(async (event) => {
|
||||
const userinfo = verifyAccessToken(event);
|
||||
if (!userinfo) {
|
||||
return unAuthorizedResponse(event);
|
||||
}
|
||||
const { id, path } = getQuery(event);
|
||||
|
||||
return (path as string) in pathMap &&
|
||||
(!id || pathMap[path as string] !== String(id))
|
||||
? useResponseSuccess(true)
|
||||
: useResponseSuccess(false);
|
||||
});
|
83
apps/backend-mock/api/system/role/list.ts
Normal file
83
apps/backend-mock/api/system/role/list.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { faker } from '@faker-js/faker';
|
||||
import { verifyAccessToken } from '~/utils/jwt-utils';
|
||||
import { getMenuIds, MOCK_MENU_LIST } from '~/utils/mock-data';
|
||||
import { unAuthorizedResponse, usePageResponseSuccess } from '~/utils/response';
|
||||
|
||||
const formatterCN = new Intl.DateTimeFormat('zh-CN', {
|
||||
timeZone: 'Asia/Shanghai',
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
});
|
||||
|
||||
const menuIds = getMenuIds(MOCK_MENU_LIST);
|
||||
|
||||
function generateMockDataList(count: number) {
|
||||
const dataList = [];
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const dataItem: Record<string, any> = {
|
||||
id: faker.string.uuid(),
|
||||
name: faker.commerce.product(),
|
||||
status: faker.helpers.arrayElement([0, 1]),
|
||||
createTime: formatterCN.format(
|
||||
faker.date.between({ from: '2022-01-01', to: '2025-01-01' }),
|
||||
),
|
||||
permissions: faker.helpers.arrayElements(menuIds),
|
||||
remark: faker.lorem.sentence(),
|
||||
};
|
||||
|
||||
dataList.push(dataItem);
|
||||
}
|
||||
|
||||
return dataList;
|
||||
}
|
||||
|
||||
const mockData = generateMockDataList(100);
|
||||
|
||||
export default eventHandler(async (event) => {
|
||||
const userinfo = verifyAccessToken(event);
|
||||
if (!userinfo) {
|
||||
return unAuthorizedResponse(event);
|
||||
}
|
||||
|
||||
const {
|
||||
page = 1,
|
||||
pageSize = 20,
|
||||
name,
|
||||
id,
|
||||
remark,
|
||||
startTime,
|
||||
endTime,
|
||||
status,
|
||||
} = getQuery(event);
|
||||
let listData = structuredClone(mockData);
|
||||
if (name) {
|
||||
listData = listData.filter((item) =>
|
||||
item.name.toLowerCase().includes(String(name).toLowerCase()),
|
||||
);
|
||||
}
|
||||
if (id) {
|
||||
listData = listData.filter((item) =>
|
||||
item.id.toLowerCase().includes(String(id).toLowerCase()),
|
||||
);
|
||||
}
|
||||
if (remark) {
|
||||
listData = listData.filter((item) =>
|
||||
item.remark?.toLowerCase()?.includes(String(remark).toLowerCase()),
|
||||
);
|
||||
}
|
||||
if (startTime) {
|
||||
listData = listData.filter((item) => item.createTime >= startTime);
|
||||
}
|
||||
if (endTime) {
|
||||
listData = listData.filter((item) => item.createTime <= endTime);
|
||||
}
|
||||
if (['0', '1'].includes(status as string)) {
|
||||
listData = listData.filter((item) => item.status === Number(status));
|
||||
}
|
||||
return usePageResponseSuccess(page as string, pageSize as string, listData);
|
||||
});
|
@@ -1,6 +1,6 @@
|
||||
import { faker } from '@faker-js/faker';
|
||||
import { verifyAccessToken } from '~/utils/jwt-utils';
|
||||
import { unAuthorizedResponse } from '~/utils/response';
|
||||
import { unAuthorizedResponse, usePageResponseSuccess } from '~/utils/response';
|
||||
|
||||
function generateMockDataList(count: number) {
|
||||
const dataList = [];
|
||||
@@ -43,6 +43,31 @@ export default eventHandler(async (event) => {
|
||||
|
||||
await sleep(600);
|
||||
|
||||
const { page, pageSize } = getQuery(event);
|
||||
return usePageResponseSuccess(page as string, pageSize as string, mockData);
|
||||
const { page, pageSize, sortBy, sortOrder } = getQuery(event);
|
||||
const listData = structuredClone(mockData);
|
||||
if (sortBy && Reflect.has(listData[0], sortBy as string)) {
|
||||
listData.sort((a, b) => {
|
||||
if (sortOrder === 'asc') {
|
||||
if (sortBy === 'price') {
|
||||
return (
|
||||
Number.parseFloat(a[sortBy as string]) -
|
||||
Number.parseFloat(b[sortBy as string])
|
||||
);
|
||||
} else {
|
||||
return a[sortBy as string] > b[sortBy as string] ? 1 : -1;
|
||||
}
|
||||
} else {
|
||||
if (sortBy === 'price') {
|
||||
return (
|
||||
Number.parseFloat(b[sortBy as string]) -
|
||||
Number.parseFloat(a[sortBy as string])
|
||||
);
|
||||
} else {
|
||||
return a[sortBy as string] < b[sortBy as string] ? 1 : -1;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return usePageResponseSuccess(page as string, pageSize as string, listData);
|
||||
});
|
||||
|
@@ -1,7 +1,19 @@
|
||||
export default defineEventHandler((event) => {
|
||||
import { forbiddenResponse, sleep } from '~/utils/response';
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
event.node.res.setHeader(
|
||||
'Access-Control-Allow-Origin',
|
||||
event.headers.get('Origin') ?? '*',
|
||||
);
|
||||
if (event.method === 'OPTIONS') {
|
||||
event.node.res.statusCode = 204;
|
||||
event.node.res.statusMessage = 'No Content.';
|
||||
return 'OK';
|
||||
} else if (
|
||||
['DELETE', 'PATCH', 'POST', 'PUT'].includes(event.method) &&
|
||||
event.path.startsWith('/api/system/')
|
||||
) {
|
||||
await sleep(Math.floor(Math.random() * 2000));
|
||||
return forbiddenResponse(event, '演示环境,禁止修改');
|
||||
}
|
||||
});
|
||||
|
@@ -9,7 +9,8 @@ export default defineNitroConfig({
|
||||
cors: true,
|
||||
headers: {
|
||||
'Access-Control-Allow-Credentials': 'true',
|
||||
'Access-Control-Allow-Headers': '*',
|
||||
'Access-Control-Allow-Headers':
|
||||
'Accept, Authorization, Content-Length, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-CSRF-TOKEN, X-Requested-With',
|
||||
'Access-Control-Allow-Methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Expose-Headers': '*',
|
||||
|
@@ -14,7 +14,7 @@ export function setRefreshTokenCookie(
|
||||
) {
|
||||
setCookie(event, 'jwt', refreshToken, {
|
||||
httpOnly: true,
|
||||
maxAge: 24 * 60 * 60 * 1000,
|
||||
maxAge: 24 * 60 * 60, // unit: seconds
|
||||
sameSite: 'none',
|
||||
secure: true,
|
||||
});
|
||||
|
@@ -4,6 +4,7 @@ export interface UserInfo {
|
||||
realName: string;
|
||||
roles: string[];
|
||||
username: string;
|
||||
homePath?: string;
|
||||
}
|
||||
|
||||
export const MOCK_USERS: UserInfo[] = [
|
||||
@@ -20,6 +21,7 @@ export const MOCK_USERS: UserInfo[] = [
|
||||
realName: 'Admin',
|
||||
roles: ['admin'],
|
||||
username: 'admin',
|
||||
homePath: '/workspace',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
@@ -27,6 +29,7 @@ export const MOCK_USERS: UserInfo[] = [
|
||||
realName: 'Jack',
|
||||
roles: ['user'],
|
||||
username: 'jack',
|
||||
homePath: '/analytics',
|
||||
},
|
||||
];
|
||||
|
||||
@@ -50,13 +53,12 @@ export const MOCK_CODES = [
|
||||
|
||||
const dashboardMenus = [
|
||||
{
|
||||
component: 'BasicLayout',
|
||||
meta: {
|
||||
order: -1,
|
||||
title: 'page.dashboard.title',
|
||||
},
|
||||
name: 'Dashboard',
|
||||
path: '/',
|
||||
path: '/dashboard',
|
||||
redirect: '/analytics',
|
||||
children: [
|
||||
{
|
||||
@@ -113,7 +115,6 @@ const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
|
||||
|
||||
return [
|
||||
{
|
||||
component: 'BasicLayout',
|
||||
meta: {
|
||||
icon: 'ic:baseline-view-in-ar',
|
||||
keepAlive: true,
|
||||
@@ -184,3 +185,206 @@ export const MOCK_MENUS = [
|
||||
username: 'jack',
|
||||
},
|
||||
];
|
||||
|
||||
export const MOCK_MENU_LIST = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Workspace',
|
||||
status: 1,
|
||||
type: 'menu',
|
||||
icon: 'mdi:dashboard',
|
||||
path: '/workspace',
|
||||
component: '/dashboard/workspace/index',
|
||||
meta: {
|
||||
icon: 'carbon:workspace',
|
||||
title: 'page.dashboard.workspace',
|
||||
affixTab: true,
|
||||
order: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
meta: {
|
||||
icon: 'carbon:settings',
|
||||
order: 9997,
|
||||
title: 'system.title',
|
||||
badge: 'new',
|
||||
badgeType: 'normal',
|
||||
badgeVariants: 'primary',
|
||||
},
|
||||
status: 1,
|
||||
type: 'catalog',
|
||||
name: 'System',
|
||||
path: '/system',
|
||||
children: [
|
||||
{
|
||||
id: 201,
|
||||
pid: 2,
|
||||
path: '/system/menu',
|
||||
name: 'SystemMenu',
|
||||
authCode: 'System:Menu:List',
|
||||
status: 1,
|
||||
type: 'menu',
|
||||
meta: {
|
||||
icon: 'carbon:menu',
|
||||
title: 'system.menu.title',
|
||||
},
|
||||
component: '/system/menu/list',
|
||||
children: [
|
||||
{
|
||||
id: 20_101,
|
||||
pid: 201,
|
||||
name: 'SystemMenuCreate',
|
||||
status: 1,
|
||||
type: 'button',
|
||||
authCode: 'System:Menu:Create',
|
||||
meta: { title: 'common.create' },
|
||||
},
|
||||
{
|
||||
id: 20_102,
|
||||
pid: 201,
|
||||
name: 'SystemMenuEdit',
|
||||
status: 1,
|
||||
type: 'button',
|
||||
authCode: 'System:Menu:Edit',
|
||||
meta: { title: 'common.edit' },
|
||||
},
|
||||
{
|
||||
id: 20_103,
|
||||
pid: 201,
|
||||
name: 'SystemMenuDelete',
|
||||
status: 1,
|
||||
type: 'button',
|
||||
authCode: 'System:Menu:Delete',
|
||||
meta: { title: 'common.delete' },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 202,
|
||||
pid: 2,
|
||||
path: '/system/dept',
|
||||
name: 'SystemDept',
|
||||
status: 1,
|
||||
type: 'menu',
|
||||
authCode: 'System:Dept:List',
|
||||
meta: {
|
||||
icon: 'carbon:container-services',
|
||||
title: 'system.dept.title',
|
||||
},
|
||||
component: '/system/dept/list',
|
||||
children: [
|
||||
{
|
||||
id: 20_401,
|
||||
pid: 201,
|
||||
name: 'SystemDeptCreate',
|
||||
status: 1,
|
||||
type: 'button',
|
||||
authCode: 'System:Dept:Create',
|
||||
meta: { title: 'common.create' },
|
||||
},
|
||||
{
|
||||
id: 20_402,
|
||||
pid: 201,
|
||||
name: 'SystemDeptEdit',
|
||||
status: 1,
|
||||
type: 'button',
|
||||
authCode: 'System:Dept:Edit',
|
||||
meta: { title: 'common.edit' },
|
||||
},
|
||||
{
|
||||
id: 20_403,
|
||||
pid: 201,
|
||||
name: 'SystemDeptDelete',
|
||||
status: 1,
|
||||
type: 'button',
|
||||
authCode: 'System:Dept:Delete',
|
||||
meta: { title: 'common.delete' },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
meta: {
|
||||
badgeType: 'dot',
|
||||
order: 9998,
|
||||
title: 'demos.vben.title',
|
||||
icon: 'carbon:data-center',
|
||||
},
|
||||
name: 'Project',
|
||||
path: '/vben-admin',
|
||||
type: 'catalog',
|
||||
status: 1,
|
||||
children: [
|
||||
{
|
||||
id: 901,
|
||||
pid: 9,
|
||||
name: 'VbenDocument',
|
||||
path: '/vben-admin/document',
|
||||
component: 'IFrameView',
|
||||
type: 'embedded',
|
||||
status: 1,
|
||||
meta: {
|
||||
icon: 'carbon:book',
|
||||
iframeSrc: 'https://doc.vben.pro',
|
||||
title: 'demos.vben.document',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 902,
|
||||
pid: 9,
|
||||
name: 'VbenGithub',
|
||||
path: '/vben-admin/github',
|
||||
component: 'IFrameView',
|
||||
type: 'link',
|
||||
status: 1,
|
||||
meta: {
|
||||
icon: 'carbon:logo-github',
|
||||
link: 'https://github.com/vbenjs/vue-vben-admin',
|
||||
title: 'Github',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 903,
|
||||
pid: 9,
|
||||
name: 'VbenAntdv',
|
||||
path: '/vben-admin/antdv',
|
||||
component: 'IFrameView',
|
||||
type: 'link',
|
||||
status: 0,
|
||||
meta: {
|
||||
icon: 'carbon:hexagon-vertical-solid',
|
||||
badgeType: 'dot',
|
||||
link: 'https://ant.vben.pro',
|
||||
title: 'demos.vben.antdv',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
component: '_core/about/index',
|
||||
type: 'menu',
|
||||
status: 1,
|
||||
meta: {
|
||||
icon: 'lucide:copyright',
|
||||
order: 9999,
|
||||
title: 'demos.vben.about',
|
||||
},
|
||||
name: 'About',
|
||||
path: '/about',
|
||||
},
|
||||
];
|
||||
|
||||
export function getMenuIds(menus: any[]) {
|
||||
const ids: number[] = [];
|
||||
menus.forEach((item) => {
|
||||
ids.push(item.id);
|
||||
if (item.children && item.children.length > 0) {
|
||||
ids.push(...getMenuIds(item.children));
|
||||
}
|
||||
});
|
||||
return ids;
|
||||
}
|
||||
|
@@ -21,5 +21,5 @@ VITE_GLOB_RSA_PRIVATE_KEY=MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuP
|
||||
# 客户端id
|
||||
VITE_GLOB_APP_CLIENT_ID=e5cd7e4891bf95d1d19206ce24a7b32e
|
||||
|
||||
# 开启WEBSOCKET
|
||||
VITE_GLOB_WEBSOCKET_ENABLE=false
|
||||
# 开启SSE
|
||||
VITE_GLOB_SSE_ENABLE=true
|
||||
|
@@ -27,6 +27,6 @@ VITE_GLOB_RSA_PRIVATE_KEY=MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuP
|
||||
# 客户端id
|
||||
VITE_GLOB_APP_CLIENT_ID=e5cd7e4891bf95d1d19206ce24a7b32e
|
||||
|
||||
# 开启WEBSOCKET
|
||||
VITE_GLOB_WEBSOCKET_ENABLE=false
|
||||
# 开启SSE
|
||||
VITE_GLOB_SSE_ENABLE=true
|
||||
|
||||
|
9
apps/web-antd/.vscode/settings.json
vendored
9
apps/web-antd/.vscode/settings.json
vendored
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"editor.tabSize": 2,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit",
|
||||
"source.fixAll.stylelint": "explicit"
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/web-antd",
|
||||
"version": "1.1.0",
|
||||
"version": "1.2.2",
|
||||
"homepage": "https://vben.pro",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -3,12 +3,14 @@
|
||||
* 可用于 vben-form、vben-modal、vben-drawer 等组件使用,
|
||||
*/
|
||||
|
||||
import type { Component } from 'vue';
|
||||
|
||||
import type { BaseFormComponentType } from '@vben/common-ui';
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import type { Component, SetupContext } from 'vue';
|
||||
import { h } from 'vue';
|
||||
import { defineComponent, getCurrentInstance, h, ref } from 'vue';
|
||||
|
||||
import { globalShareState } from '@vben/common-ui';
|
||||
import { ApiComponent, globalShareState, IconPicker } from '@vben/common-ui';
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import {
|
||||
@@ -43,14 +45,36 @@ const withDefaultPlaceholder = <T extends Component>(
|
||||
component: T,
|
||||
type: 'input' | 'select',
|
||||
) => {
|
||||
return (props: any, { attrs, slots }: Omit<SetupContext, 'expose'>) => {
|
||||
const placeholder = props?.placeholder || $t(`ui.placeholder.${type}`);
|
||||
return h(component, { ...props, ...attrs, placeholder }, slots);
|
||||
};
|
||||
return defineComponent({
|
||||
inheritAttrs: false,
|
||||
name: component.name,
|
||||
setup: (props: any, { attrs, expose, slots }) => {
|
||||
const placeholder =
|
||||
props?.placeholder ||
|
||||
attrs?.placeholder ||
|
||||
$t(`ui.placeholder.${type}`);
|
||||
// 透传组件暴露的方法
|
||||
const innerRef = ref();
|
||||
const publicApi: Recordable<any> = {};
|
||||
expose(publicApi);
|
||||
const instance = getCurrentInstance();
|
||||
instance?.proxy?.$nextTick(() => {
|
||||
for (const key in innerRef.value) {
|
||||
if (typeof innerRef.value[key] === 'function') {
|
||||
publicApi[key] = innerRef.value[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
return () =>
|
||||
h(component, { ...props, ...attrs, placeholder, ref: innerRef }, slots);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
|
||||
export type ComponentType =
|
||||
| 'ApiSelect'
|
||||
| 'ApiTreeSelect'
|
||||
| 'AutoComplete'
|
||||
| 'Checkbox'
|
||||
| 'CheckboxGroup'
|
||||
@@ -58,6 +82,7 @@ export type ComponentType =
|
||||
| 'DefaultButton'
|
||||
| 'Divider'
|
||||
| 'FileUpload'
|
||||
| 'IconPicker'
|
||||
| 'ImageUpload'
|
||||
| 'Input'
|
||||
| 'InputNumber'
|
||||
@@ -83,7 +108,38 @@ async function initComponentAdapter() {
|
||||
// 如果你的组件体积比较大,可以使用异步加载
|
||||
// Button: () =>
|
||||
// import('xxx').then((res) => res.Button),
|
||||
|
||||
ApiSelect: (props, { attrs, slots }) => {
|
||||
return h(
|
||||
ApiComponent,
|
||||
{
|
||||
placeholder: $t('ui.placeholder.select'),
|
||||
...props,
|
||||
...attrs,
|
||||
component: Select,
|
||||
loadingSlot: 'suffixIcon',
|
||||
visibleEvent: 'onDropdownVisibleChange',
|
||||
modelPropName: 'value',
|
||||
},
|
||||
slots,
|
||||
);
|
||||
},
|
||||
ApiTreeSelect: (props, { attrs, slots }) => {
|
||||
return h(
|
||||
ApiComponent,
|
||||
{
|
||||
placeholder: $t('ui.placeholder.select'),
|
||||
...props,
|
||||
...attrs,
|
||||
component: TreeSelect,
|
||||
fieldNames: { label: 'label', value: 'value', children: 'children' },
|
||||
loadingSlot: 'suffixIcon',
|
||||
modelPropName: 'value',
|
||||
optionsPropName: 'treeData',
|
||||
visibleEvent: 'onVisibleChange',
|
||||
},
|
||||
slots,
|
||||
);
|
||||
},
|
||||
AutoComplete,
|
||||
Checkbox,
|
||||
CheckboxGroup,
|
||||
@@ -93,6 +149,19 @@ async function initComponentAdapter() {
|
||||
return h(Button, { ...props, attrs, type: 'default' }, slots);
|
||||
},
|
||||
Divider,
|
||||
IconPicker: (props, { attrs, slots }) => {
|
||||
return h(
|
||||
IconPicker,
|
||||
{
|
||||
iconSlot: 'addonAfter',
|
||||
inputComponent: Input,
|
||||
modelValueProp: 'value',
|
||||
...props,
|
||||
...attrs,
|
||||
},
|
||||
slots,
|
||||
);
|
||||
},
|
||||
Input: withDefaultPlaceholder(Input, 'input'),
|
||||
InputNumber: withDefaultPlaceholder(InputNumber, 'input'),
|
||||
InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
|
||||
|
@@ -1,10 +1,8 @@
|
||||
import { h, type Ref } from 'vue';
|
||||
import type { VxeGridPropTypes } from '@vben/plugins/vxe-table';
|
||||
|
||||
import {
|
||||
setupVbenVxeTable,
|
||||
useVbenVxeGrid,
|
||||
type VxeGridDefines,
|
||||
} from '@vben/plugins/vxe-table';
|
||||
import { h } from 'vue';
|
||||
|
||||
import { setupVbenVxeTable, useVbenVxeGrid } from '@vben/plugins/vxe-table';
|
||||
|
||||
import { Button, Image } from 'ant-design-vue';
|
||||
|
||||
@@ -43,7 +41,7 @@ setupVbenVxeTable({
|
||||
// 鼠标移入行显示 hover 样式
|
||||
isHover: true,
|
||||
// 点击行高亮
|
||||
isCurrent: true,
|
||||
isCurrent: false,
|
||||
},
|
||||
columnConfig: {
|
||||
// 可拖拽列宽
|
||||
@@ -106,15 +104,32 @@ export { useVbenVxeGrid };
|
||||
export type * from '@vben/plugins/vxe-table';
|
||||
|
||||
/**
|
||||
* 通用的表格复选框是否选中事件
|
||||
* @param checked 是否选中
|
||||
* @returns function
|
||||
* 判断vxe-table的复选框是否选中
|
||||
* @param tableApi api
|
||||
* @returns boolean
|
||||
*/
|
||||
export function tableCheckboxEvent(checked: Ref<boolean>) {
|
||||
const event: (params: VxeGridDefines.CheckboxChangeEventParams) => void = (
|
||||
params,
|
||||
) => {
|
||||
checked.value = params.$table.getCheckboxRecords().length > 0;
|
||||
};
|
||||
return event;
|
||||
export function vxeCheckboxChecked(
|
||||
tableApi: ReturnType<typeof useVbenVxeGrid>[1],
|
||||
) {
|
||||
return tableApi?.grid?.getCheckboxRecords?.()?.length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用的 排序参数添加到请求参数中
|
||||
* @param params 请求参数
|
||||
* @param sortList vxe-table的排序参数
|
||||
*/
|
||||
export function addSortParams(
|
||||
params: Record<string, any>,
|
||||
sortList: VxeGridPropTypes.ProxyAjaxQuerySortCheckedParams[],
|
||||
) {
|
||||
// 这里是排序取消 length为0 就不添加参数了
|
||||
if (sortList.length === 0) {
|
||||
return;
|
||||
}
|
||||
// 支持单/多字段排序
|
||||
const orderByColumn = sortList.map((item) => item.field).join(',');
|
||||
const isAsc = sortList.map((item) => item.order).join(',');
|
||||
params.orderByColumn = orderByColumn;
|
||||
params.isAsc = isAsc;
|
||||
}
|
||||
|
10
apps/web-antd/src/api/common.d.ts
vendored
10
apps/web-antd/src/api/common.d.ts
vendored
@@ -21,14 +21,22 @@ export interface PageResult<T = any> {
|
||||
|
||||
/**
|
||||
* 分页查询参数
|
||||
*
|
||||
* 排序支持的用法如下:
|
||||
* {isAsc:"asc",orderByColumn:"id"} order by id asc
|
||||
* {isAsc:"asc",orderByColumn:"id,createTime"} order by id asc,create_time asc
|
||||
* {isAsc:"desc",orderByColumn:"id,createTime"} order by id desc,create_time desc
|
||||
* {isAsc:"asc,desc",orderByColumn:"id,createTime"} order by id asc,create_time desc
|
||||
*
|
||||
* @param pageNum 当前页
|
||||
* @param pageSize 每页大小
|
||||
* @param orderByColumn 排序字段
|
||||
* @param isAsc 是否升序
|
||||
*/
|
||||
export interface PageQuery {
|
||||
isAsc?: boolean;
|
||||
isAsc?: string;
|
||||
orderByColumn?: string;
|
||||
pageNum?: number;
|
||||
pageSize?: number;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
@@ -4,7 +4,10 @@ import { useAppConfig } from '@vben/hooks';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
const { clientId } = useAppConfig(import.meta.env, import.meta.env.PROD);
|
||||
const { clientId, sseEnable } = useAppConfig(
|
||||
import.meta.env,
|
||||
import.meta.env.PROD,
|
||||
);
|
||||
|
||||
export namespace AuthApi {
|
||||
/**
|
||||
@@ -96,6 +99,12 @@ export function doLogout() {
|
||||
* @returns void
|
||||
*/
|
||||
export function seeConnectionClose() {
|
||||
/**
|
||||
* 未开启sse 不需要处理
|
||||
*/
|
||||
if (!sseEnable) {
|
||||
return;
|
||||
}
|
||||
return requestClient.get<void>('/resource/sse/close');
|
||||
}
|
||||
|
||||
|
@@ -19,7 +19,7 @@ export interface MenuMeta {
|
||||
* @param name 菜单名
|
||||
* @param path 菜单路径
|
||||
* @param hidden 是否隐藏
|
||||
* @param component 组件名称 Laout
|
||||
* @param component 组件名称 Layout
|
||||
* @param alwaysShow 总是显示
|
||||
* @param query 路由参数(json形式)
|
||||
* @param meta 路由信息
|
||||
|
@@ -1,12 +1,27 @@
|
||||
import type { AxiosRequestConfig } from '@vben/request';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
/**
|
||||
* Axios上传进度事件
|
||||
*/
|
||||
export type AxiosProgressEvent = AxiosRequestConfig['onUploadProgress'];
|
||||
|
||||
/**
|
||||
* 通过单文件上传接口
|
||||
* @param file 上传的文件
|
||||
* @param onUploadProgress 上传进度事件 非必传
|
||||
* @returns 上传结果
|
||||
*/
|
||||
export function uploadApi(file: Blob | File) {
|
||||
return requestClient.upload('/resource/oss/upload', { file });
|
||||
export function uploadApi(
|
||||
file: Blob | File,
|
||||
onUploadProgress?: AxiosProgressEvent,
|
||||
) {
|
||||
return requestClient.upload(
|
||||
'/resource/oss/upload',
|
||||
{ file },
|
||||
{ onUploadProgress, timeout: 60_000 },
|
||||
);
|
||||
}
|
||||
/**
|
||||
* 默认上传结果
|
||||
|
@@ -1,75 +1,5 @@
|
||||
import { isObject, isString } from '@vben/utils';
|
||||
|
||||
import { requestClient } from './request';
|
||||
|
||||
const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss';
|
||||
|
||||
export function joinTimestamp<T extends boolean>(
|
||||
join: boolean,
|
||||
restful: T,
|
||||
): T extends true ? string : object;
|
||||
|
||||
export function joinTimestamp(join: boolean, restful = false): object | string {
|
||||
if (!join) {
|
||||
return restful ? '' : {};
|
||||
}
|
||||
const now = Date.now();
|
||||
if (restful) {
|
||||
return `?_t=${now}`;
|
||||
}
|
||||
return { _t: now };
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: Format request parameter time
|
||||
*/
|
||||
export function formatRequestDate(params: Record<string, any>) {
|
||||
if (Object.prototype.toString.call(params) !== '[object Object]') {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const key in params) {
|
||||
const format = params[key]?.format ?? null;
|
||||
if (format && typeof format === 'function') {
|
||||
params[key] = params[key].format(DATE_TIME_FORMAT);
|
||||
}
|
||||
if (isString(key)) {
|
||||
const value = params[key];
|
||||
if (value) {
|
||||
try {
|
||||
params[key] = isString(value) ? value.trim() : value;
|
||||
} catch (error: any) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isObject(params[key])) {
|
||||
formatRequestDate(params[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the object as a parameter to the URL
|
||||
* @param baseUrl url
|
||||
* @param obj
|
||||
* @returns {string}
|
||||
* eg:
|
||||
* let obj = {a: '3', b: '4'}
|
||||
* setObjToUrlParams('www.baidu.com', obj)
|
||||
* ==>www.baidu.com?a=3&b=4
|
||||
*/
|
||||
export function setObjToUrlParams(baseUrl: string, obj: any): string {
|
||||
let parameters = '';
|
||||
for (const key in obj) {
|
||||
parameters += `${key}=${encodeURIComponent(obj[key])}&`;
|
||||
}
|
||||
parameters = parameters.replace(/&$/, '');
|
||||
return /\?$/.test(baseUrl)
|
||||
? baseUrl + parameters
|
||||
: baseUrl.replace(/\/?$/, '?') + parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: contentType
|
||||
*/
|
||||
|
@@ -17,6 +17,11 @@ export function onlineDeviceList() {
|
||||
return requestClient.get<PageResult<OnlineUser>>(Api.root);
|
||||
}
|
||||
|
||||
/**
|
||||
* 这里的分页参数无效 返回的是全部的分页
|
||||
* @param params 请求参数
|
||||
* @returns 结果
|
||||
*/
|
||||
export function onlineList(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<OnlineUser>>(Api.onlineList, { params });
|
||||
}
|
||||
@@ -36,5 +41,5 @@ export function forceLogout(tokenId: string) {
|
||||
* @returns void
|
||||
*/
|
||||
export function forceLogout2(tokenId: string) {
|
||||
return requestClient.postWithMsg<void>(`${Api.root}/${tokenId}`);
|
||||
return requestClient.deleteWithMsg<void>(`${Api.root}/myself/${tokenId}`);
|
||||
}
|
||||
|
@@ -12,20 +12,36 @@ enum Api {
|
||||
root = '/monitor/operlog',
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作日志分页
|
||||
* @param params 查询参数
|
||||
* @returns 分页结果
|
||||
*/
|
||||
export function operLogList(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<OperationLog>>(Api.operLogList, {
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除操作日志
|
||||
* @param operIds id/ids
|
||||
*/
|
||||
export function operLogDelete(operIds: IDS) {
|
||||
return requestClient.deleteWithMsg<void>(`${Api.root}/${operIds}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空全部分页日志
|
||||
*/
|
||||
export function operLogClean() {
|
||||
return requestClient.deleteWithMsg<void>(Api.operLogClean);
|
||||
}
|
||||
|
||||
export function operLogExport(data: any) {
|
||||
/**
|
||||
* 导出操作日志
|
||||
* @param data 查询参数
|
||||
*/
|
||||
export function operLogExport(data: Partial<OperationLog>) {
|
||||
return commonExport(Api.operLogExport, data);
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
/**
|
||||
* 该文件可自行根据业务逻辑进行调整
|
||||
*/
|
||||
|
||||
import type { HttpResponse } from '@vben/request';
|
||||
|
||||
import { useAppConfig } from '@vben/hooks';
|
||||
@@ -10,9 +11,9 @@ import {
|
||||
authenticateResponseInterceptor,
|
||||
errorMessageResponseInterceptor,
|
||||
RequestClient,
|
||||
stringify,
|
||||
} from '@vben/request';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
import { isString } from '@vben/utils';
|
||||
|
||||
import { message, Modal } from 'ant-design-vue';
|
||||
import { isEmpty, isNull } from 'lodash-es';
|
||||
@@ -27,8 +28,6 @@ import {
|
||||
} from '#/utils/encryption/crypto';
|
||||
import * as encryptUtil from '#/utils/encryption/jsencrypt';
|
||||
|
||||
import { formatRequestDate, joinTimestamp, setObjToUrlParams } from './helper';
|
||||
|
||||
const { apiURL, clientId, enableEncrypt } = useAppConfig(
|
||||
import.meta.env,
|
||||
import.meta.env.PROD,
|
||||
@@ -46,16 +45,10 @@ function createRequestClient(baseURL: string) {
|
||||
baseURL,
|
||||
// 消息提示类型
|
||||
errorMessageMode: 'message',
|
||||
// 格式化提交参数时间
|
||||
formatDate: true,
|
||||
// 是否返回原生响应 比如:需要获取响应头时使用该属性
|
||||
isReturnNativeResponse: false,
|
||||
// 需要对返回数据进行处理
|
||||
isTransformResponse: true,
|
||||
// post请求的时候添加参数到url
|
||||
joinParamsToUrl: false,
|
||||
// 是否加入时间戳
|
||||
joinTime: false,
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -99,54 +92,28 @@ function createRequestClient(baseURL: string) {
|
||||
*/
|
||||
const language = preferences.app.locale.replace('-', '_');
|
||||
config.headers['Accept-Language'] = language;
|
||||
config.headers['Content-Language'] = language;
|
||||
// 添加全局clientId
|
||||
config.headers.clientId = clientId;
|
||||
|
||||
const { encrypt, formatDate, joinParamsToUrl, joinTime = true } = config;
|
||||
const params = config.params || {};
|
||||
const data = config.data || false;
|
||||
// TODO: 这块要重构 复杂度太高了
|
||||
formatDate && data && !isString(data) && formatRequestDate(data);
|
||||
if (config.method?.toUpperCase() === 'GET') {
|
||||
if (isString(params)) {
|
||||
// 兼容restful风格
|
||||
config.url = `${config.url + params}${joinTimestamp(joinTime, true)}`;
|
||||
config.params = undefined;
|
||||
} else {
|
||||
// 给 get 请求加上时间戳参数,避免从缓存中拿数据。
|
||||
config.params = Object.assign(
|
||||
params || {},
|
||||
joinTimestamp(joinTime, false),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (isString(params)) {
|
||||
// 兼容restful风格
|
||||
config.url = config.url + params;
|
||||
config.params = undefined;
|
||||
} else {
|
||||
formatDate && formatRequestDate(params);
|
||||
if (
|
||||
Reflect.has(config, 'data') &&
|
||||
config.data &&
|
||||
(Object.keys(config.data).length > 0 ||
|
||||
config.data instanceof FormData)
|
||||
) {
|
||||
config.data = data;
|
||||
config.params = params;
|
||||
} else {
|
||||
// 非GET请求如果没有提供data,则将params视为data
|
||||
config.data = params;
|
||||
config.params = undefined;
|
||||
}
|
||||
if (joinParamsToUrl) {
|
||||
config.url = setObjToUrlParams(
|
||||
config.url as string,
|
||||
Object.assign({}, config.params, config.data),
|
||||
);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 格式化get/delete参数
|
||||
* 如果包含自定义的paramsSerializer则不走此逻辑
|
||||
*/
|
||||
if (
|
||||
['DELETE', 'GET'].includes(config.method?.toUpperCase() || '') &&
|
||||
config.params &&
|
||||
!config.paramsSerializer
|
||||
) {
|
||||
/**
|
||||
* 1. 格式化参数 微服务在传递区间时间选择(后端的params Map类型参数)需要格式化key 否则接收不到
|
||||
* 2. 数组参数需要格式化 后端才能正常接收 会变成arr=1&arr=2&arr=3的格式来接收
|
||||
*/
|
||||
config.paramsSerializer = (params) =>
|
||||
stringify(params, { arrayFormat: 'repeat' });
|
||||
}
|
||||
|
||||
const { encrypt } = config;
|
||||
// 全局开启请求加密功能 && 该请求开启 && 是post/put请求
|
||||
if (
|
||||
enableEncrypt &&
|
||||
@@ -168,14 +135,14 @@ function createRequestClient(baseURL: string) {
|
||||
});
|
||||
|
||||
// 通用的错误处理, 如果没有进入上面的错误处理逻辑,就会进入这里
|
||||
// 主要处理http状态码不为200的情况 必须放在在下面的响应拦截器之前
|
||||
// 主要处理http状态码不为200(如网络异常/离线)的情况 必须放在在下面的响应拦截器之前
|
||||
client.addResponseInterceptor(
|
||||
errorMessageResponseInterceptor((msg: string) => message.error(msg)),
|
||||
);
|
||||
|
||||
client.addResponseInterceptor<HttpResponse>({
|
||||
fulfilled: (response) => {
|
||||
const encryptKey = (response.headers || {})['encrypt-key'];
|
||||
fulfilled: async (response) => {
|
||||
const encryptKey = (response.headers ?? {})['encrypt-key'];
|
||||
if (encryptKey) {
|
||||
/** RSA私钥解密 拿到解密秘钥的base64 */
|
||||
const base64Str = encryptUtil.decrypt(encryptKey);
|
||||
@@ -198,39 +165,57 @@ function createRequestClient(baseURL: string) {
|
||||
// 不进行任何处理,直接返回
|
||||
// 用于页面代码可能需要直接获取code,data,message这些信息时开启
|
||||
if (!isTransformResponse) {
|
||||
return response.data;
|
||||
/**
|
||||
* 需要判断下载二进制的情况 正常是返回二进制 报错会返回json
|
||||
* 当type为blob且content-type为application/json时 则判断已经下载出错
|
||||
*/
|
||||
if (
|
||||
response.config.responseType === 'blob' &&
|
||||
response.headers['content-type']?.includes?.('application/json')
|
||||
) {
|
||||
// 这时候的data为blob类型
|
||||
const blob = response.data as unknown as Blob;
|
||||
// 拿到字符串转json对象
|
||||
response.data = JSON.parse(await blob.text());
|
||||
// 然后按正常逻辑执行下面的代码(判断业务状态码)
|
||||
} else {
|
||||
// 其他情况 直接返回
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
|
||||
const axiosResponseData = response.data;
|
||||
if (!axiosResponseData) {
|
||||
throw new Error($t('fallback.http.apiRequestFailed'));
|
||||
throw new Error($t('http.apiRequestFailed'));
|
||||
}
|
||||
|
||||
// ruoyi-plus没有采用严格的{code, msg, data}模式
|
||||
// 后端并没有采用严格的{code, msg, data}模式
|
||||
const { code, data, msg, ...other } = axiosResponseData;
|
||||
|
||||
// 这里逻辑可以根据项目进行修改
|
||||
// 业务状态码为200则请求成功
|
||||
const hasSuccess = Reflect.has(axiosResponseData, 'code') && code === 200;
|
||||
if (hasSuccess) {
|
||||
let successMsg = msg;
|
||||
|
||||
if (isNull(successMsg) || isEmpty(successMsg)) {
|
||||
successMsg = $t(`fallback.http.operationSuccess`);
|
||||
successMsg = $t(`http.operationSuccess`);
|
||||
}
|
||||
|
||||
if (response.config.successMessageMode === 'modal') {
|
||||
Modal.success({
|
||||
content: successMsg,
|
||||
title: $t('fallback.http.successTip'),
|
||||
title: $t('http.successTip'),
|
||||
});
|
||||
} else if (response.config.successMessageMode === 'message') {
|
||||
message.success(successMsg);
|
||||
}
|
||||
// 分页情况下为code msg rows total 并没有data字段
|
||||
// 如果有data 直接返回data 没有data将剩余参数(...other)封装为data返回
|
||||
// 需要考虑data为null的情况(比如查询为空)
|
||||
// 需要考虑data为null的情况(比如查询为空) 所以这里直接判断undefined
|
||||
if (data !== undefined) {
|
||||
return data;
|
||||
}
|
||||
// 没有data 将其他参数包装为data
|
||||
return other;
|
||||
}
|
||||
// 在此处根据自己项目的实际情况对不同的code执行不同的操作
|
||||
@@ -243,7 +228,7 @@ function createRequestClient(baseURL: string) {
|
||||
return;
|
||||
}
|
||||
isLogoutProcessing = true;
|
||||
const _msg = '登录超时, 请重新登录';
|
||||
const _msg = $t('http.loginTimeout');
|
||||
const userStore = useAuthStore();
|
||||
userStore.logout().finally(() => {
|
||||
message.error(_msg);
|
||||
@@ -264,13 +249,13 @@ function createRequestClient(baseURL: string) {
|
||||
if (response.config.errorMessageMode === 'modal') {
|
||||
Modal.error({
|
||||
content: timeoutMsg,
|
||||
title: $t('fallback.http.errorTip'),
|
||||
title: $t('http.errorTip'),
|
||||
});
|
||||
} else if (response.config.errorMessageMode === 'message') {
|
||||
message.error(timeoutMsg);
|
||||
}
|
||||
|
||||
throw new Error(timeoutMsg || $t('fallback.http.apiRequestFailed'));
|
||||
throw new Error(timeoutMsg || $t('http.apiRequestFailed'));
|
||||
},
|
||||
});
|
||||
|
||||
|
@@ -12,30 +12,60 @@ enum Api {
|
||||
root = '/system/client',
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询客户端分页列表
|
||||
* @param params 请求参数
|
||||
* @returns 列表
|
||||
*/
|
||||
export function clientList(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<Client>>(Api.clientList, { params });
|
||||
}
|
||||
|
||||
export function clientExport(data: any) {
|
||||
/**
|
||||
* 导出客户端excel
|
||||
* @param data 请求参数
|
||||
*/
|
||||
export function clientExport(data: Partial<Client>) {
|
||||
return commonExport(Api.clientExport, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 客户端详情
|
||||
* @param id id
|
||||
* @returns 详情
|
||||
*/
|
||||
export function clientInfo(id: ID) {
|
||||
return requestClient.get<Client>(`${Api.root}/${id}`);
|
||||
}
|
||||
|
||||
export function clientAdd(data: any) {
|
||||
/**
|
||||
* 客户端新增
|
||||
* @param data 参数
|
||||
*/
|
||||
export function clientAdd(data: Partial<Client>) {
|
||||
return requestClient.postWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
export function clientUpdate(data: any) {
|
||||
/**
|
||||
* 客户端修改
|
||||
* @param data 参数
|
||||
*/
|
||||
export function clientUpdate(data: Partial<Client>) {
|
||||
return requestClient.putWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 客户端状态修改
|
||||
* @param data 状态
|
||||
*/
|
||||
export function clientChangeStatus(data: any) {
|
||||
return requestClient.putWithMsg<void>(Api.root, data);
|
||||
return requestClient.putWithMsg<void>(Api.clientChangeStatus, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 客户端删除
|
||||
* @param ids id集合
|
||||
*/
|
||||
export function clientRemove(ids: IDS) {
|
||||
return requestClient.deleteWithMsg(`${Api.root}/${ids}`);
|
||||
return requestClient.deleteWithMsg<void>(`${Api.root}/${ids}`);
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import type { Config } from './model';
|
||||
import type { SysConfig } from './model';
|
||||
|
||||
import type { ID, IDS, PageQuery, PageResult } from '#/api/common';
|
||||
|
||||
@@ -13,15 +13,24 @@ enum Api {
|
||||
root = '/system/config',
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统参数分页列表
|
||||
* @param params 请求参数
|
||||
* @returns 列表
|
||||
*/
|
||||
export function configList(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<Config>>(Api.configList, { params });
|
||||
return requestClient.get<PageResult<SysConfig>>(Api.configList, { params });
|
||||
}
|
||||
|
||||
export function configInfo(configId: ID) {
|
||||
return requestClient.get<Config>(`${Api.root}/${configId}`);
|
||||
return requestClient.get<SysConfig>(`${Api.root}/${configId}`);
|
||||
}
|
||||
|
||||
export function configExport(data: any) {
|
||||
/**
|
||||
* 导出
|
||||
* @param data 参数
|
||||
*/
|
||||
export function configExport(data: Partial<SysConfig>) {
|
||||
return commonExport(Api.configExport, data);
|
||||
}
|
||||
|
||||
@@ -33,14 +42,26 @@ export function configRefreshCache() {
|
||||
return requestClient.deleteWithMsg<void>(Api.configRefreshCache);
|
||||
}
|
||||
|
||||
export function configUpdate(data: any) {
|
||||
/**
|
||||
* 更新系统配置
|
||||
* @param data 参数
|
||||
*/
|
||||
export function configUpdate(data: Partial<SysConfig>) {
|
||||
return requestClient.putWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
export function configAdd(data: any) {
|
||||
/**
|
||||
* 新增系统配置
|
||||
* @param data 参数
|
||||
*/
|
||||
export function configAdd(data: Partial<SysConfig>) {
|
||||
return requestClient.postWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除配置
|
||||
* @param configIds ids
|
||||
*/
|
||||
export function configRemove(configIds: IDS) {
|
||||
return requestClient.deleteWithMsg<void>(`${Api.root}/${configIds}`);
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
export interface Config {
|
||||
export interface SysConfig {
|
||||
configId: number;
|
||||
configName: string;
|
||||
configKey: string;
|
||||
|
@@ -10,7 +10,11 @@ enum Api {
|
||||
root = '/system/dept',
|
||||
}
|
||||
|
||||
export function deptList(params?: any) {
|
||||
/**
|
||||
* 部门列表
|
||||
* @returns list
|
||||
*/
|
||||
export function deptList(params?: { deptName?: string; status?: string }) {
|
||||
return requestClient.get<Dept[]>(Api.deptList, { params });
|
||||
}
|
||||
|
||||
@@ -23,15 +27,28 @@ export function deptNodeList(deptId: ID) {
|
||||
return requestClient.get<Dept[]>(`${Api.deptNodeInfo}/${deptId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 部门详情
|
||||
* @param deptId 部门id
|
||||
* @returns 部门信息
|
||||
*/
|
||||
export function deptInfo(deptId: ID) {
|
||||
return requestClient.get<Dept>(`${Api.root}/${deptId}`);
|
||||
}
|
||||
|
||||
export function deptAdd(data: any) {
|
||||
/**
|
||||
* 部门新增
|
||||
* @param data 参数
|
||||
*/
|
||||
export function deptAdd(data: Partial<Dept>) {
|
||||
return requestClient.postWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
export function deptUpdate(data: any) {
|
||||
/**
|
||||
* 部门更新
|
||||
* @param data 参数
|
||||
*/
|
||||
export function deptUpdate(data: Partial<Dept>) {
|
||||
return requestClient.putWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
|
@@ -34,7 +34,7 @@ export function dictDataList(params?: PageQuery) {
|
||||
* @param data 表单参数
|
||||
* @returns blob
|
||||
*/
|
||||
export function dictDataExport(data: any) {
|
||||
export function dictDataExport(data: Partial<DictData>) {
|
||||
return commonExport(Api.dictDataExport, data);
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ export function dictDataRemove(dictIds: IDS) {
|
||||
* @param data 表单参数
|
||||
* @returns void
|
||||
*/
|
||||
export function dictDataAdd(data: any) {
|
||||
export function dictDataAdd(data: Partial<DictData>) {
|
||||
return requestClient.postWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ export function dictDataAdd(data: any) {
|
||||
* @param data 表单参数
|
||||
* @returns void
|
||||
*/
|
||||
export function dictDataUpdate(data: any) {
|
||||
export function dictDataUpdate(data: Partial<DictData>) {
|
||||
return requestClient.putWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
|
@@ -27,7 +27,7 @@ export function dictTypeList(params?: PageQuery) {
|
||||
* @param data 表单参数
|
||||
* @returns blob
|
||||
*/
|
||||
export function dictTypeExport(data: any) {
|
||||
export function dictTypeExport(data: Partial<DictType>) {
|
||||
return commonExport(Api.dictTypeExport, data);
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ export function refreshDictTypeCache() {
|
||||
* @param data 表单参数
|
||||
* @returns void
|
||||
*/
|
||||
export function dictTypeAdd(data: any) {
|
||||
export function dictTypeAdd(data: Partial<DictType>) {
|
||||
return requestClient.postWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ export function dictTypeAdd(data: any) {
|
||||
* @param data 表单参数
|
||||
* @returns void
|
||||
*/
|
||||
export function dictTypeUpdate(data: any) {
|
||||
export function dictTypeUpdate(data: Partial<DictType>) {
|
||||
return requestClient.putWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
@@ -76,6 +76,7 @@ export function dictTypeInfo(dictId: ID) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 这个在ele用到 v5用不上
|
||||
* 下拉框 返回值和list一样
|
||||
* @returns options
|
||||
*/
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import type { Menu, MenuOption, MenuResp } from './model';
|
||||
import type { Menu, MenuOption, MenuQuery, MenuResp } from './model';
|
||||
|
||||
import type { ID, IDS } from '#/api/common';
|
||||
|
||||
@@ -12,22 +12,44 @@ enum Api {
|
||||
tenantPackageMenuTreeselect = '/system/menu/tenantPackageMenuTreeselect',
|
||||
}
|
||||
|
||||
export function menuList(params?: any) {
|
||||
/**
|
||||
* 菜单列表
|
||||
* @param params 参数
|
||||
* @returns 列表
|
||||
*/
|
||||
export function menuList(params?: MenuQuery) {
|
||||
return requestClient.get<Menu[]>(Api.menuList, { params });
|
||||
}
|
||||
|
||||
/**
|
||||
* 菜单详情
|
||||
* @param menuId 菜单id
|
||||
* @returns 菜单详情
|
||||
*/
|
||||
export function menuInfo(menuId: ID) {
|
||||
return requestClient.get<Menu>(`${Api.root}/${menuId}`);
|
||||
}
|
||||
|
||||
export function menuAdd(data: any) {
|
||||
/**
|
||||
* 菜单新增
|
||||
* @param data 参数
|
||||
*/
|
||||
export function menuAdd(data: Partial<Menu>) {
|
||||
return requestClient.postWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
export function menuUpdate(data: any) {
|
||||
/**
|
||||
* 菜单更新
|
||||
* @param data 参数
|
||||
*/
|
||||
export function menuUpdate(data: Partial<Menu>) {
|
||||
return requestClient.putWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 菜单删除
|
||||
* @param menuIds ids
|
||||
*/
|
||||
export function menuRemove(menuIds: IDS) {
|
||||
return requestClient.deleteWithMsg<void>(`${Api.root}/${menuIds}`);
|
||||
}
|
||||
|
11
apps/web-antd/src/api/system/menu/model.d.ts
vendored
11
apps/web-antd/src/api/system/menu/model.d.ts
vendored
@@ -33,6 +33,8 @@ export interface MenuOption {
|
||||
weight: number;
|
||||
children: MenuOption[];
|
||||
key: string; // 实际上不存在 ide报错
|
||||
menuType: string;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,3 +46,12 @@ export interface MenuResp {
|
||||
checkedKeys: number[];
|
||||
menus: MenuOption[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 菜单表单查询
|
||||
*/
|
||||
export interface MenuQuery {
|
||||
menuName?: string;
|
||||
visible?: string;
|
||||
status?: string;
|
||||
}
|
||||
|
@@ -9,22 +9,44 @@ enum Api {
|
||||
root = '/system/notice',
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知公告分页
|
||||
* @param params 分页参数
|
||||
* @returns 分页结果
|
||||
*/
|
||||
export function noticeList(params?: PageQuery) {
|
||||
return requestClient.get<Notice[]>(Api.noticeList, { params });
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知公告详情
|
||||
* @param noticeId id
|
||||
* @returns 详情
|
||||
*/
|
||||
export function noticeInfo(noticeId: ID) {
|
||||
return requestClient.get<Notice>(`${Api.root}/${noticeId}`);
|
||||
}
|
||||
|
||||
export function noticeAdd(data: any) {
|
||||
/**
|
||||
* 通知公告新增
|
||||
* @param data 参数
|
||||
*/
|
||||
export function noticeAdd(data: Partial<Notice>) {
|
||||
return requestClient.postWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知公告更新
|
||||
* @param data 参数
|
||||
*/
|
||||
export function noticeUpdate(data: any) {
|
||||
return requestClient.putWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知公告删除
|
||||
* @param noticeIds ids
|
||||
*/
|
||||
export function noticeRemove(noticeIds: IDS) {
|
||||
return requestClient.deleteWithMsg<void>(`${Api.root}/${noticeIds}`);
|
||||
}
|
||||
|
@@ -21,12 +21,12 @@ export function ossConfigInfo(ossConfigId: ID) {
|
||||
}
|
||||
|
||||
// 添加新的OSS配置
|
||||
export function ossConfigAdd(data: any) {
|
||||
export function ossConfigAdd(data: Partial<OssConfig>) {
|
||||
return requestClient.postWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
// 更新现有的OSS配置
|
||||
export function ossConfigUpdate(data: any) {
|
||||
export function ossConfigUpdate(data: Partial<OssConfig>) {
|
||||
return requestClient.putWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
|
@@ -1,3 +1,5 @@
|
||||
import type { AxiosRequestConfig } from '@vben/request';
|
||||
|
||||
import type { OssFile } from './model';
|
||||
|
||||
import type { ID, IDS, PageQuery, PageResult } from '#/api/common';
|
||||
@@ -13,20 +15,30 @@ enum Api {
|
||||
root = '/resource/oss',
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件list
|
||||
* @param params 参数
|
||||
* @returns 分页
|
||||
*/
|
||||
export function ossList(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<OssFile>>(Api.ossList, { params });
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询文件信息 返回为数组
|
||||
* @param ossIds id数组
|
||||
* @returns 信息数组
|
||||
*/
|
||||
export function ossInfo(ossIds: IDS) {
|
||||
return requestClient.get<OssFile>(`${Api.ossInfo}/${ossIds}`);
|
||||
return requestClient.get<OssFile[]>(`${Api.ossInfo}/${ossIds}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @deprecated 使用apps/web-antd/src/api/core/upload.ts uploadApi方法
|
||||
* @param file 文件
|
||||
* @returns void
|
||||
*/
|
||||
export function ossUpload(file: any) {
|
||||
export function ossUpload(file: Blob | File) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
return requestClient.postWithMsg(Api.ossUpload, formData, {
|
||||
@@ -38,16 +50,26 @@ export function ossUpload(file: any) {
|
||||
/**
|
||||
* 下载文件 返回为二进制
|
||||
* @param ossId ossId
|
||||
* @param onDownloadProgress 下载进度(可选)
|
||||
* @returns blob
|
||||
*/
|
||||
export function ossDownload(ossId: ID) {
|
||||
export function ossDownload(
|
||||
ossId: ID,
|
||||
onDownloadProgress?: AxiosRequestConfig['onDownloadProgress'],
|
||||
) {
|
||||
return requestClient.get<Blob>(`${Api.ossDownload}/${ossId}`, {
|
||||
responseType: 'blob',
|
||||
timeout: 30 * 1000,
|
||||
isTransformResponse: false,
|
||||
onDownloadProgress,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
* @param ossIds id数组
|
||||
* @returns void
|
||||
*/
|
||||
export function ossRemove(ossIds: IDS) {
|
||||
return requestClient.deleteWithMsg<void>(`${Api.root}/${ossIds}`);
|
||||
}
|
||||
|
@@ -21,26 +21,56 @@ export function postList(params?: PageQuery) {
|
||||
return requestClient.get<Post[]>(Api.postList, { params });
|
||||
}
|
||||
|
||||
export function postExport(data: any) {
|
||||
/**
|
||||
* 导出岗位信息
|
||||
* @param data 请求参数
|
||||
* @returns blob
|
||||
*/
|
||||
export function postExport(data: Partial<Post>) {
|
||||
return commonExport(Api.postExport, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询岗位信息
|
||||
* @param postId id
|
||||
* @returns 岗位信息
|
||||
*/
|
||||
export function postInfo(postId: ID) {
|
||||
return requestClient.get<Post>(`${Api.root}/${postId}`);
|
||||
}
|
||||
|
||||
export function postAdd(data: any) {
|
||||
/**
|
||||
* 岗位新增
|
||||
* @param data 参数
|
||||
* @returns void
|
||||
*/
|
||||
export function postAdd(data: Partial<Post>) {
|
||||
return requestClient.postWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
export function postUpdate(data: any) {
|
||||
/**
|
||||
* 岗位更新
|
||||
* @param data 参数
|
||||
* @returns void
|
||||
*/
|
||||
export function postUpdate(data: Partial<Post>) {
|
||||
return requestClient.putWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 岗位删除
|
||||
* @param postIds ids
|
||||
* @returns void
|
||||
*/
|
||||
export function postRemove(postIds: IDS) {
|
||||
return requestClient.deleteWithMsg<void>(`${Api.root}/${postIds}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据部门id获取岗位下拉列表
|
||||
* @param deptId 部门id
|
||||
* @returns 岗位
|
||||
*/
|
||||
export function postOptionSelect(deptId: ID) {
|
||||
return requestClient.get<Post[]>(Api.postSelect, { params: { deptId } });
|
||||
}
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import type { User } from '../user/model';
|
||||
import type { DeptResp, Role } from './model';
|
||||
|
||||
import type { ID, IDS, PageQuery, PageResult } from '#/api/common';
|
||||
@@ -20,30 +21,65 @@ enum Api {
|
||||
root = '/system/role',
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询角色分页列表
|
||||
* @param params 搜索条件
|
||||
* @returns 分页列表
|
||||
*/
|
||||
export function roleList(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<Role>>(Api.roleList, { params });
|
||||
}
|
||||
|
||||
export function roleExport(data: any) {
|
||||
/**
|
||||
* 导出角色信息
|
||||
* @param data 查询参数
|
||||
* @returns blob
|
||||
*/
|
||||
export function roleExport(data: Partial<Role>) {
|
||||
return commonExport(Api.roleExport, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询角色信息
|
||||
* @param roleId 角色id
|
||||
* @returns 角色信息
|
||||
*/
|
||||
export function roleInfo(roleId: ID) {
|
||||
return requestClient.get<Role>(`${Api.root}/${roleId}`);
|
||||
}
|
||||
|
||||
export function roleAdd(data: any) {
|
||||
/**
|
||||
* 角色新增
|
||||
* @param data 参数
|
||||
* @returns void
|
||||
*/
|
||||
export function roleAdd(data: Partial<Role>) {
|
||||
return requestClient.postWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
export function roleUpdate(data: any) {
|
||||
/**
|
||||
* 角色更新
|
||||
* @param data 参数
|
||||
* @returns void
|
||||
*/
|
||||
export function roleUpdate(data: Partial<Role>) {
|
||||
return requestClient.putWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
export function roleChangeStatus(data: any) {
|
||||
/**
|
||||
* 修改角色状态
|
||||
* @param data 参数
|
||||
* @returns void
|
||||
*/
|
||||
export function roleChangeStatus(data: Partial<Role>) {
|
||||
return requestClient.putWithMsg<void>(Api.roleChangeStatus, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色删除
|
||||
* @param roleIds ids
|
||||
* @returns void
|
||||
*/
|
||||
export function roleRemove(roleIds: IDS) {
|
||||
return requestClient.deleteWithMsg<void>(`${Api.root}/${roleIds}`);
|
||||
}
|
||||
@@ -57,12 +93,20 @@ export function roleDataScope(data: any) {
|
||||
return requestClient.putWithMsg<void>(Api.roleDataScope, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 全局并没有用到这个方法
|
||||
*/
|
||||
export function roleOptionSelect(params?: any) {
|
||||
return requestClient.get(Api.roleOptionSelect, { params });
|
||||
}
|
||||
|
||||
export function roleAllocatedList(params: any) {
|
||||
return requestClient.get(Api.roleAllocatedList, { params });
|
||||
/**
|
||||
* 已分配角色的用户分页
|
||||
* @param params 请求参数
|
||||
* @returns 分页
|
||||
*/
|
||||
export function roleAllocatedList(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<User>>(Api.roleAllocatedList, { params });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,28 +115,26 @@ export function roleAllocatedList(params: any) {
|
||||
* @returns void
|
||||
*/
|
||||
export function roleUnallocatedList(params: any) {
|
||||
return requestClient.get(Api.roleUnallocatedList, { params });
|
||||
return requestClient.get<PageResult<User>>(Api.roleUnallocatedList, {
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消授权
|
||||
* @param data {userId: 2, roleId: "2"}
|
||||
* 取消用户角色授权
|
||||
* @returns void
|
||||
*/
|
||||
export function roleAuthCancel(data: any) {
|
||||
export function roleAuthCancel(data: { roleId: ID; userId: ID }) {
|
||||
return requestClient.putWithMsg<void>(Api.roleAuthCancel, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量取消授权
|
||||
* @param roleId
|
||||
* @param userIds
|
||||
* @param roleId 角色ID
|
||||
* @param userIds 用户ID集合
|
||||
* @returns void
|
||||
*/
|
||||
export function roleAuthCancelAll(
|
||||
roleId: number | string,
|
||||
userIds: number[] | string[],
|
||||
) {
|
||||
export function roleAuthCancelAll(roleId: ID, userIds: IDS) {
|
||||
return requestClient.putWithMsg<void>(
|
||||
`${Api.roleAuthCancelAll}?roleId=${roleId}&userIds=${userIds.join(',')}`,
|
||||
);
|
||||
@@ -100,21 +142,18 @@ export function roleAuthCancelAll(
|
||||
|
||||
/**
|
||||
* 批量授权用户
|
||||
* @param roleId
|
||||
* @param userIds
|
||||
* @param roleId 角色ID
|
||||
* @param userIds 用户ID集合
|
||||
* @returns void
|
||||
*/
|
||||
export function roleSelectAll(
|
||||
roleId: number | string,
|
||||
userIds: number[] | string[],
|
||||
) {
|
||||
export function roleSelectAll(roleId: ID, userIds: IDS) {
|
||||
return requestClient.putWithMsg<void>(
|
||||
`${Api.roleAuthSelectAll}?roleId=${roleId}&userIds=${userIds.join(',')}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 部门树
|
||||
* 根据角色id获取部门树
|
||||
* @param roleId 角色id
|
||||
* @returns DeptResp
|
||||
*/
|
||||
|
@@ -1,5 +1,7 @@
|
||||
import type { SocialInfo } from './model';
|
||||
|
||||
import type { ID } from '#/api/common';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
enum Api {
|
||||
@@ -15,6 +17,9 @@ export function socialList() {
|
||||
return requestClient.get<SocialInfo[]>(Api.socialList);
|
||||
}
|
||||
|
||||
export function socialInfo(id: number | string) {
|
||||
/**
|
||||
* @deprecated 并没有用到这个方法
|
||||
*/
|
||||
export function socialInfo(id: ID) {
|
||||
return requestClient.get(`${Api.root}/${id}`);
|
||||
}
|
||||
|
@@ -13,37 +13,75 @@ enum Api {
|
||||
root = '/system/tenant/package',
|
||||
}
|
||||
|
||||
/**
|
||||
* 租户套餐分页列表
|
||||
* @param params 请求参数
|
||||
* @returns 分页列表
|
||||
*/
|
||||
export function packageList(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<TenantPackage>>(Api.packageList, {
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
// 下拉框
|
||||
/**
|
||||
* 租户套餐下拉框
|
||||
* @returns 下拉框
|
||||
*/
|
||||
export function packageSelectList() {
|
||||
return requestClient.get<TenantPackage[]>(Api.packageSelectList);
|
||||
}
|
||||
|
||||
export function packageExport(data: any) {
|
||||
/**
|
||||
* 租户套餐导出
|
||||
* @param data 参数
|
||||
* @returns blob
|
||||
*/
|
||||
export function packageExport(data: Partial<TenantPackage>) {
|
||||
return commonExport(Api.packageExport, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 租户套餐信息
|
||||
* @param id id
|
||||
* @returns 信息
|
||||
*/
|
||||
export function packageInfo(id: ID) {
|
||||
return requestClient.get<TenantPackage>(`${Api.root}/${id}`);
|
||||
}
|
||||
|
||||
export function packageAdd(data: any) {
|
||||
/**
|
||||
* 租户套餐新增
|
||||
* @param data data
|
||||
* @returns void
|
||||
*/
|
||||
export function packageAdd(data: Partial<TenantPackage>) {
|
||||
return requestClient.postWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
export function packageUpdate(data: any) {
|
||||
/**
|
||||
* 租户套餐更新
|
||||
* @param data data
|
||||
* @returns void
|
||||
*/
|
||||
export function packageUpdate(data: Partial<TenantPackage>) {
|
||||
return requestClient.putWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
export function packageChangeStatus(data: any) {
|
||||
return requestClient.putWithMsg(Api.packageChangeStatus, data);
|
||||
/**
|
||||
* 租户套餐状态变更
|
||||
* @param data data
|
||||
* @returns void
|
||||
*/
|
||||
export function packageChangeStatus(data: Partial<TenantPackage>) {
|
||||
return requestClient.putWithMsg<void>(Api.packageChangeStatus, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 租户套餐移除
|
||||
* @param ids ids
|
||||
* @returns void
|
||||
*/
|
||||
export function packageRemove(ids: IDS) {
|
||||
return requestClient.deleteWithMsg(`${Api.root}/${ids}`);
|
||||
return requestClient.deleteWithMsg<void>(`${Api.root}/${ids}`);
|
||||
}
|
||||
|
@@ -16,14 +16,29 @@ enum Api {
|
||||
tenantSyncPackage = '/system/tenant/syncTenantPackage',
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询租户分页列表
|
||||
* @param params 参数
|
||||
* @returns 分页
|
||||
*/
|
||||
export function tenantList(params?: PageQuery) {
|
||||
return requestClient.get<Tenant[]>(Api.tenantList, { params });
|
||||
}
|
||||
|
||||
export function tenantExport(data: any) {
|
||||
/**
|
||||
* 租户导出
|
||||
* @param data data
|
||||
* @returns void
|
||||
*/
|
||||
export function tenantExport(data: Partial<Tenant>) {
|
||||
return commonExport(Api.tenantExport, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询租户信息
|
||||
* @param id id
|
||||
* @returns 租户信息
|
||||
*/
|
||||
export function tenantInfo(id: ID) {
|
||||
return requestClient.get<Tenant>(`${Api.root}/${id}`);
|
||||
}
|
||||
@@ -33,18 +48,33 @@ export function tenantInfo(id: ID) {
|
||||
* @param data data
|
||||
* @returns void
|
||||
*/
|
||||
export function tenantAdd(data: any) {
|
||||
export function tenantAdd(data: Partial<Tenant>) {
|
||||
return requestClient.postWithMsg<void>(Api.root, data, { encrypt: true });
|
||||
}
|
||||
|
||||
export function tenantUpdate(data: any) {
|
||||
/**
|
||||
* 租户更新
|
||||
* @param data data
|
||||
* @returns void
|
||||
*/
|
||||
export function tenantUpdate(data: Partial<Tenant>) {
|
||||
return requestClient.putWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
export function tenantStatusChange(data: any) {
|
||||
/**
|
||||
* 租户状态更新
|
||||
* @param data data
|
||||
* @returns void
|
||||
*/
|
||||
export function tenantStatusChange(data: Partial<Tenant>) {
|
||||
return requestClient.putWithMsg(Api.tenantStatus, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 租户删除
|
||||
* @param ids ids
|
||||
* @returns void
|
||||
*/
|
||||
export function tenantRemove(ids: IDS) {
|
||||
return requestClient.deleteWithMsg(`${Api.root}/${ids}`);
|
||||
}
|
||||
@@ -70,17 +100,12 @@ export function tenantDynamicClear() {
|
||||
* 租户套餐同步
|
||||
* @param tenantId 租户id
|
||||
* @param packageId 套餐id
|
||||
* @param showMsg 是否显示成功信息
|
||||
* @returns void
|
||||
*/
|
||||
export function tenantSyncPackage(
|
||||
tenantId: string,
|
||||
packageId: string,
|
||||
showMsg = true,
|
||||
) {
|
||||
export function tenantSyncPackage(tenantId: string, packageId: string) {
|
||||
return requestClient.get<void>(Api.tenantSyncPackage, {
|
||||
params: { packageId, tenantId },
|
||||
successMessageMode: showMsg ? 'message' : 'none',
|
||||
successMessageMode: 'message',
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -9,7 +9,7 @@ export interface Tenant {
|
||||
id: number;
|
||||
intro: string;
|
||||
licenseNumber?: any;
|
||||
packageId?: string;
|
||||
packageId: string;
|
||||
remark?: string;
|
||||
status: string;
|
||||
tenantId: string;
|
||||
|
@@ -38,7 +38,7 @@ export function userList(params?: PageQuery) {
|
||||
* @param data data
|
||||
* @returns blob
|
||||
*/
|
||||
export function userExport(data: any) {
|
||||
export function userExport(data: Partial<User>) {
|
||||
return commonExport(Api.userExport, data);
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ export function findUserInfo(userId?: ID) {
|
||||
* @param data data
|
||||
* @returns void
|
||||
*/
|
||||
export function userAdd(data: any) {
|
||||
export function userAdd(data: Partial<User>) {
|
||||
return requestClient.postWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ export function userAdd(data: any) {
|
||||
* @param data data
|
||||
* @returns void
|
||||
*/
|
||||
export function userUpdate(data: any) {
|
||||
export function userUpdate(data: Partial<User>) {
|
||||
return requestClient.putWithMsg<void>(Api.root, data);
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ export function userUpdate(data: any) {
|
||||
* @param data data
|
||||
* @returns void
|
||||
*/
|
||||
export function userStatusChange(data: any) {
|
||||
export function userStatusChange(data: Partial<User>) {
|
||||
return requestClient.putWithMsg<void>(Api.userStatusChange, data);
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import type { GenInfo } from './model';
|
||||
|
||||
import type { ID, IDS } from '#/api/common';
|
||||
import type { ID, IDS, PageQuery } from '#/api/common';
|
||||
|
||||
import { ContentTypeEnum } from '#/api/helper';
|
||||
import { requestClient } from '#/api/request';
|
||||
@@ -19,7 +19,7 @@ enum Api {
|
||||
syncDb = '/tool/gen/synchDb',
|
||||
}
|
||||
// 查询代码生成列表
|
||||
export function generatedList(params: any) {
|
||||
export function generatedList(params?: PageQuery) {
|
||||
return requestClient.get(Api.generatedList, { params });
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export function genInfo(tableId: ID) {
|
||||
}
|
||||
|
||||
// 查询数据库列表
|
||||
export function readyToGenList(params: any) {
|
||||
export function readyToGenList(params?: PageQuery) {
|
||||
return requestClient.get(Api.readyToGenList, { params });
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ export function editSave(data: any) {
|
||||
export function genRemove(tableIds: IDS) {
|
||||
return requestClient.deleteWithMsg(`${Api.root}/${tableIds}`);
|
||||
}
|
||||
|
||||
// 预览代码
|
||||
export function previewCode(tableId: ID) {
|
||||
return requestClient.get<{ [key: string]: string }>(
|
||||
@@ -76,8 +77,8 @@ export function genDownload(tableId: ID) {
|
||||
}
|
||||
|
||||
// 生成代码(自定义路径)
|
||||
export function genDownloadWithPath(tableId: ID) {
|
||||
return requestClient.get(`${Api.download}/${tableId}`);
|
||||
export function genWithPath(tableId: ID) {
|
||||
return requestClient.get<void>(`${Api.genCode}/${tableId}`);
|
||||
}
|
||||
|
||||
// 同步数据库
|
||||
|
1
apps/web-antd/src/api/tool/gen/model.d.ts
vendored
1
apps/web-antd/src/api/tool/gen/model.d.ts
vendored
@@ -177,6 +177,7 @@ export interface Info {
|
||||
// 树表需要添加此属性
|
||||
params?: any;
|
||||
popupComponent?: string;
|
||||
formComponent?: string;
|
||||
}
|
||||
|
||||
export interface GenInfo {
|
||||
|
@@ -1,9 +1,22 @@
|
||||
import type { CategoryForm, CategoryQuery, CategoryVO } from './model';
|
||||
import type {
|
||||
CategoryForm,
|
||||
CategoryQuery,
|
||||
CategoryTree,
|
||||
CategoryVO,
|
||||
} from './model';
|
||||
|
||||
import type { ID, IDS } from '#/api/common';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
/**
|
||||
* 获取流程分类树列表
|
||||
* @returns tree
|
||||
*/
|
||||
export function categoryTree() {
|
||||
return requestClient.get<CategoryTree[]>('/workflow/category/categoryTree');
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询流程分类列表
|
||||
* @param params
|
||||
|
@@ -30,6 +30,7 @@ export interface CategoryVO {
|
||||
* 子对象
|
||||
*/
|
||||
children: CategoryVO[];
|
||||
key: string;
|
||||
}
|
||||
|
||||
export interface CategoryForm extends BaseEntity {
|
||||
@@ -85,3 +86,12 @@ export interface CategoryQuery {
|
||||
*/
|
||||
params?: any;
|
||||
}
|
||||
|
||||
export interface CategoryTree {
|
||||
id: number;
|
||||
parentId: number;
|
||||
label: string;
|
||||
weight: number;
|
||||
children: CategoryTree[];
|
||||
key: string;
|
||||
}
|
||||
|
155
apps/web-antd/src/api/workflow/definition/index.ts
Normal file
155
apps/web-antd/src/api/workflow/definition/index.ts
Normal file
@@ -0,0 +1,155 @@
|
||||
import type { ProcessDefinition } from './model';
|
||||
|
||||
import type { ID, IDS, PageQuery, PageResult } from '#/api/common';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
/**
|
||||
* 全部的流程定义
|
||||
* @param params 查询参数
|
||||
* @returns 分页
|
||||
*/
|
||||
export function workflowDefinitionList(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<ProcessDefinition>>(
|
||||
'/workflow/definition/list',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 未发布的流程定义
|
||||
* @param params 查询参数
|
||||
* @returns 分页
|
||||
*/
|
||||
export function unPublishList(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<ProcessDefinition>>(
|
||||
'/workflow/definition/unPublishList',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取历史流程定义列表
|
||||
* @param flowCode
|
||||
* @returns ProcessDefinition[]
|
||||
*/
|
||||
export function getHisListByKey(flowCode: string) {
|
||||
return requestClient.get<ProcessDefinition[]>(
|
||||
`/workflow/definition/getHisListByKey/${flowCode}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取流程定义详细信息
|
||||
* @param id id
|
||||
* @returns ProcessDefinition
|
||||
*/
|
||||
export function workflowDefinitionInfo(id: ID) {
|
||||
return requestClient.get<ProcessDefinition>(`/workflow/definition/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增流程定义
|
||||
* @param data
|
||||
*/
|
||||
export function workflowDefinitionAdd(data: any) {
|
||||
return requestClient.postWithMsg<void>('/workflow/definition', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新流程定义
|
||||
* @param data
|
||||
*/
|
||||
export function workflowDefinitionUpdate(data: any) {
|
||||
return requestClient.putWithMsg<void>('/workflow/definition', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布流程定义
|
||||
* @param id id
|
||||
* @returns boolean
|
||||
*/
|
||||
export function workflowDefinitionPublish(id: ID) {
|
||||
return requestClient.putWithMsg<boolean>(
|
||||
`/workflow/definition/publish/${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消发布流程定义
|
||||
* @param id id
|
||||
* @returns boolean
|
||||
*/
|
||||
export function workflowDefinitionUnPublish(id: ID) {
|
||||
return requestClient.putWithMsg<boolean>(
|
||||
`/workflow/definition/unPublish/${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除流程定义
|
||||
* @param ids idList
|
||||
*/
|
||||
export function workflowDefinitionDelete(ids: IDS) {
|
||||
return requestClient.deleteWithMsg<void>(`/workflow/definition/${ids}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制流程定义
|
||||
* @param id id
|
||||
*/
|
||||
export function workflowDefinitionCopy(id: ID) {
|
||||
return requestClient.postWithMsg<void>(`/workflow/definition/copy/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入流程定义
|
||||
* @returns boolean
|
||||
*/
|
||||
export function workflowDefinitionImport(data: {
|
||||
category: ID;
|
||||
file: Blob | File;
|
||||
}) {
|
||||
return requestClient.postWithMsg<boolean>(
|
||||
'/workflow/definition/importDef',
|
||||
data,
|
||||
{ headers: { 'Content-Type': 'multipart/form-data' } },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出流程定义
|
||||
* @param id id
|
||||
* @returns blob
|
||||
*/
|
||||
export function workflowDefinitionExport(id: ID) {
|
||||
return requestClient.postWithMsg<Blob>(
|
||||
`/workflow/definition/exportDef/${id}`,
|
||||
{},
|
||||
{
|
||||
responseType: 'blob',
|
||||
isTransformResponse: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取流程定义xml字符串
|
||||
* @param id id
|
||||
* @returns xml
|
||||
*/
|
||||
export function workflowDefinitionXml(id: ID) {
|
||||
return requestClient.get<string>(`/workflow/definition/xmlString/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活/挂起流程定义
|
||||
* @param id 流程定义id
|
||||
* @param active 激活/挂起
|
||||
* @returns boolean
|
||||
*/
|
||||
export function workflowDefinitionActive(id: ID, active: boolean) {
|
||||
return requestClient.putWithMsg<boolean>(
|
||||
`/workflow/definition/active/${id}?active=${active}`,
|
||||
);
|
||||
}
|
19
apps/web-antd/src/api/workflow/definition/model.d.ts
vendored
Normal file
19
apps/web-antd/src/api/workflow/definition/model.d.ts
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
export interface ProcessDefinition {
|
||||
id: string;
|
||||
createTime: string;
|
||||
updateTime: string;
|
||||
tenantId: string;
|
||||
delFlag: string;
|
||||
flowCode: string;
|
||||
flowName: string;
|
||||
category: string;
|
||||
categoryName: string;
|
||||
version: string;
|
||||
isPublish: number;
|
||||
formCustom: string;
|
||||
formPath: string;
|
||||
activityStatus: number;
|
||||
listenerType?: any;
|
||||
listenerPath?: any;
|
||||
ext?: any;
|
||||
}
|
120
apps/web-antd/src/api/workflow/instance/index.ts
Normal file
120
apps/web-antd/src/api/workflow/instance/index.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import type { TaskInfo } from '../task/model';
|
||||
import type { FlowInfoResponse } from './model';
|
||||
|
||||
import type { ID, IDS, PageQuery, PageResult } from '#/api/common';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
/**
|
||||
* @param businessId 业务ID
|
||||
* @returns TaskInfo
|
||||
*/
|
||||
export function getTaskByBusinessId(businessId: string) {
|
||||
return requestClient.get<TaskInfo>(
|
||||
`/workflow/instance/getInfo/${businessId}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询正在运行的流程实例
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function pageByRunning(params?: PageQuery) {
|
||||
return requestClient.get('/workflow/instance/pageByRunning', { params });
|
||||
}
|
||||
|
||||
/**
|
||||
* pageByFinish
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export function pageByFinish(params?: PageQuery) {
|
||||
return requestClient.get('/workflow/instance/pageByFinish', { params });
|
||||
}
|
||||
|
||||
/**
|
||||
* 按照业务id删除流程实例
|
||||
* @param businessIds 业务id
|
||||
*/
|
||||
export function deleteByBusinessIds(businessIds: IDS) {
|
||||
return requestClient.deleteWithMsg<void>(
|
||||
`/workflow/instance/deleteByBusinessIds${businessIds}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按照实例id删除流程实例
|
||||
* @param instanceIds 实例id
|
||||
*/
|
||||
export function deleteByInstanceIds(instanceIds: IDS) {
|
||||
return requestClient.deleteWithMsg<void>(
|
||||
`/workflow/instance/deleteByInstanceIds/${instanceIds}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤销流程
|
||||
* @param data
|
||||
*/
|
||||
export function cancelProcessApply(data: { businessId: ID; message?: string }) {
|
||||
return requestClient.putWithMsg<void>(
|
||||
'/workflow/instance/cancelProcessApply',
|
||||
data,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活/挂起流程实例
|
||||
* @param instanceId
|
||||
* @param active
|
||||
*/
|
||||
export function workflowInstanceActive(instanceId: ID, active: boolean) {
|
||||
return requestClient.putWithMsg<void>(
|
||||
`/workflow/instance/active/${instanceId}?active=${active}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录人发起的流程实例
|
||||
* @param params
|
||||
* @returns PageResult<Flow>
|
||||
*/
|
||||
export function pageByCurrent(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<TaskInfo>>(
|
||||
'/workflow/instance/pageByCurrent',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取流程图,流程记录
|
||||
* @param businessId 业务标识
|
||||
* @returns 流程图,流程记录
|
||||
*/
|
||||
export function flowInfo(businessId: string) {
|
||||
return requestClient.get<FlowInfoResponse>(
|
||||
`/workflow/instance/flowImage/${businessId}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取流程变量
|
||||
* @param instanceId
|
||||
* @returns Map<string,any>
|
||||
*/
|
||||
export function instanceVariable(instanceId: string) {
|
||||
return requestClient.get<Record<string, any>>(
|
||||
`/workflow/instance/variable/${instanceId}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 作废流程
|
||||
*/
|
||||
export function workflowInstanceInvalid(data: {
|
||||
comment?: string;
|
||||
id: string;
|
||||
}) {
|
||||
return requestClient.postWithMsg<void>('/workflow/instance/invalid', data);
|
||||
}
|
41
apps/web-antd/src/api/workflow/instance/model.d.ts
vendored
Normal file
41
apps/web-antd/src/api/workflow/instance/model.d.ts
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
export interface Flow {
|
||||
id: string;
|
||||
createTime: string;
|
||||
updateTime: string;
|
||||
tenantId: string;
|
||||
delFlag: string;
|
||||
definitionId: string;
|
||||
flowName?: any;
|
||||
instanceId: string;
|
||||
taskId: string;
|
||||
cooperateType: number;
|
||||
cooperateTypeName: string;
|
||||
businessId?: any;
|
||||
nodeCode: string;
|
||||
nodeName: string;
|
||||
nodeType: number;
|
||||
targetNodeCode: string;
|
||||
targetNodeName: string;
|
||||
approver: string;
|
||||
approveName: string;
|
||||
collaborator?: any;
|
||||
permissionList?: any;
|
||||
skipType: string;
|
||||
flowStatus: string;
|
||||
flowTaskStatus?: any;
|
||||
flowStatusName?: any;
|
||||
message: string;
|
||||
ext: null | string;
|
||||
createBy?: any;
|
||||
formCustom: string;
|
||||
formPath: string;
|
||||
flowCode?: any;
|
||||
version?: any;
|
||||
runDuration: string;
|
||||
nickName?: any;
|
||||
}
|
||||
|
||||
export interface FlowInfoResponse {
|
||||
image: string;
|
||||
list: Flow[];
|
||||
}
|
172
apps/web-antd/src/api/workflow/task/index.ts
Normal file
172
apps/web-antd/src/api/workflow/task/index.ts
Normal file
@@ -0,0 +1,172 @@
|
||||
import type {
|
||||
CompleteTaskReqData,
|
||||
NextNodeInfo,
|
||||
StartWorkFlowReqData,
|
||||
TaskInfo,
|
||||
TaskOperationData,
|
||||
TaskOperationType,
|
||||
} from './model';
|
||||
|
||||
import type { ID, IDS, PageQuery, PageResult } from '#/api/common';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
/**
|
||||
* 启动任务
|
||||
* @param data
|
||||
*/
|
||||
export function startWorkFlow(data: StartWorkFlowReqData) {
|
||||
return requestClient.post<{
|
||||
processInstanceId: string;
|
||||
taskId: string;
|
||||
}>('/workflow/task/startWorkFlow', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 办理任务
|
||||
* @param data
|
||||
*/
|
||||
export function completeTask(data: CompleteTaskReqData) {
|
||||
return requestClient.postWithMsg<void>('/workflow/task/completeTask', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前用户的待办任务
|
||||
* @param params
|
||||
*/
|
||||
export function pageByTaskWait(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<TaskInfo>>(
|
||||
'/workflow/task/pageByTaskWait',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前用户的已办任务
|
||||
* @param params
|
||||
*/
|
||||
export function pageByTaskFinish(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<TaskInfo>>(
|
||||
'/workflow/task/pageByTaskFinish',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有待办任务
|
||||
* @param params
|
||||
*/
|
||||
export function pageByAllTaskWait(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<TaskInfo>>(
|
||||
'/workflow/task/pageByAllTaskWait',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询已办任务
|
||||
* @param params
|
||||
*/
|
||||
export function pageByAllTaskFinish(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<TaskInfo>>(
|
||||
'/workflow/task/pageByAllTaskFinish',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前用户的抄送
|
||||
* @param params
|
||||
*/
|
||||
export function pageByTaskCopy(params?: PageQuery) {
|
||||
return requestClient.get<PageResult<TaskInfo>>(
|
||||
'/workflow/task/pageByTaskCopy',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据taskId查询代表任务
|
||||
* @param taskId 任务id
|
||||
* @returns info
|
||||
*/
|
||||
export function getTaskByTaskId(taskId: string) {
|
||||
return requestClient.get<TaskInfo>(`/workflow/task/getTask/${taskId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 终止任务
|
||||
*/
|
||||
export function terminationTask(data: { comment?: string; taskId: string }) {
|
||||
return requestClient.postWithMsg<void>(
|
||||
'/workflow/task/terminationTask',
|
||||
data,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务操作
|
||||
* @param taskOperationData 参数
|
||||
* @param taskOperation 操作类型,委派 delegateTask、转办 transferTask、加签 addSignature、减签 reductionSignature
|
||||
*/
|
||||
export function taskOperation(
|
||||
taskOperationData: TaskOperationData,
|
||||
taskOperation: TaskOperationType,
|
||||
) {
|
||||
return requestClient.postWithMsg<void>(
|
||||
`/workflow/task/taskOperation/${taskOperation}`,
|
||||
taskOperationData,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改任务办理人
|
||||
* @param taskIdList 任务id
|
||||
* @param userId 办理人id
|
||||
*/
|
||||
export function updateAssignee(taskIdList: IDS, userId: ID) {
|
||||
return requestClient.putWithMsg<void>(
|
||||
`/workflow/task/updateAssignee/${userId}`,
|
||||
taskIdList,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 驳回审批
|
||||
* @param data 参数
|
||||
*/
|
||||
export function backProcess(data: any) {
|
||||
return requestClient.postWithMsg<void>('/workflow/task/backProcess', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可驳回节点
|
||||
* @param definitionId 流程定义ID
|
||||
* @param nodeCode 当前节点编码
|
||||
*/
|
||||
export function getBackTaskNode(definitionId: string, nodeCode: string) {
|
||||
return requestClient.get<{ nodeCode: string; nodeName: string }[]>(
|
||||
`/workflow/task/getBackTaskNode/${definitionId}/${nodeCode}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前任务的所有办理人
|
||||
* @param taskId 任务id
|
||||
*/
|
||||
export function currentTaskAllUser(taskId: ID) {
|
||||
return requestClient.get<any>(`/workflow/task/currentTaskAllUser/${taskId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下一节点
|
||||
* @param data data
|
||||
* @param data.taskId taskId
|
||||
* @returns NextNodeInfo
|
||||
*/
|
||||
export function getNextNodeList(data: { taskId: string }) {
|
||||
return requestClient.post<NextNodeInfo[]>(
|
||||
'/workflow/task/getNextNodeList',
|
||||
data,
|
||||
);
|
||||
}
|
108
apps/web-antd/src/api/workflow/task/model.d.ts
vendored
Normal file
108
apps/web-antd/src/api/workflow/task/model.d.ts
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
export interface ButtonWithPermission {
|
||||
code: string;
|
||||
value: null | string;
|
||||
show: boolean;
|
||||
}
|
||||
|
||||
export interface TaskInfo {
|
||||
id: string;
|
||||
categoryName: string;
|
||||
createTime: string;
|
||||
updateTime: string;
|
||||
tenantId: string;
|
||||
delFlag?: any;
|
||||
definitionId: string;
|
||||
instanceId: string;
|
||||
flowName: string;
|
||||
businessId: string;
|
||||
nodeCode: string;
|
||||
nodeName: string;
|
||||
nodeType: number;
|
||||
permissionList?: any;
|
||||
userList?: any;
|
||||
formCustom: string;
|
||||
formPath?: any;
|
||||
flowCode: string;
|
||||
version: string;
|
||||
flowStatus: string;
|
||||
flowStatusName: string;
|
||||
assigneeIds: string;
|
||||
assigneeNames: string;
|
||||
processedBy: string;
|
||||
type: string;
|
||||
nodeRatio?: string;
|
||||
createBy: string;
|
||||
createByName: string;
|
||||
targetNodeName?: string;
|
||||
buttonList: ButtonWithPermission[];
|
||||
}
|
||||
|
||||
export interface CompleteTaskReqData {
|
||||
messageType: string[];
|
||||
flowCopyList: { userId: string; userName: string }[];
|
||||
taskId: ID;
|
||||
taskVariables: Record<string, any>;
|
||||
variables: any;
|
||||
// 附件ID 1,2,3,4形式
|
||||
fileId?: string;
|
||||
// 选人 key为节点code value为用户ID join(,)
|
||||
assigneeMap: { [key: string]: string };
|
||||
}
|
||||
|
||||
export interface StartWorkFlowReqData {
|
||||
/**
|
||||
* 业务ID
|
||||
*/
|
||||
businessId: ID;
|
||||
/**
|
||||
* flowCode
|
||||
*/
|
||||
flowCode: string;
|
||||
/**
|
||||
* 流程变量
|
||||
*/
|
||||
variables: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface TaskOperationData {
|
||||
message?: string;
|
||||
taskId: ID;
|
||||
// 单个操作人
|
||||
userId?: ID;
|
||||
// 多个操作人
|
||||
userIds?: IDS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作类型,委派 delegateTask、转办 transferTask、加签 addSignature、减签 reductionSignature
|
||||
*/
|
||||
export type TaskOperationType =
|
||||
| 'addSignature'
|
||||
| 'delegateTask'
|
||||
| 'reductionSignature'
|
||||
| 'transferTask';
|
||||
|
||||
export interface NextNodeInfo {
|
||||
skipList: string[];
|
||||
id: string;
|
||||
createTime: string;
|
||||
updateTime: string;
|
||||
tenantId: string;
|
||||
delFlag: string;
|
||||
nodeType: number;
|
||||
definitionId: string;
|
||||
nodeCode: string;
|
||||
nodeName: string;
|
||||
permissionFlag: string;
|
||||
nodeRatio: string;
|
||||
coordinate: string;
|
||||
version: string;
|
||||
anyNodeSkip: any;
|
||||
listenerType: any;
|
||||
listenerPath: any;
|
||||
handlerType: any;
|
||||
handlerPath: any;
|
||||
formCustom: string;
|
||||
formPath: any;
|
||||
ext: string;
|
||||
}
|
@@ -1,6 +1,8 @@
|
||||
import { createApp, watchEffect } from 'vue';
|
||||
|
||||
import { registerAccessDirective } from '@vben/access';
|
||||
import { initTippy, registerLoadingDirective } from '@vben/common-ui';
|
||||
import { MotionPlugin } from '@vben/plugins/motion';
|
||||
import { preferences } from '@vben/preferences';
|
||||
import { initStores } from '@vben/stores';
|
||||
import '@vben/styles';
|
||||
@@ -19,10 +21,24 @@ async function bootstrap(namespace: string) {
|
||||
// 初始化组件适配器
|
||||
await initComponentAdapter();
|
||||
|
||||
// // 设置弹窗的默认配置
|
||||
// setDefaultModalProps({
|
||||
// fullscreenButton: false,
|
||||
// });
|
||||
// // 设置抽屉的默认配置
|
||||
// setDefaultDrawerProps({
|
||||
// zIndex: 1020,
|
||||
// });
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
// 全局组件
|
||||
setupGlobalComponent(app);
|
||||
// 注册v-loading指令
|
||||
registerLoadingDirective(app, {
|
||||
loading: 'loading', // 在这里可以自定义指令名称,也可以明确提供false表示不注册这个指令
|
||||
spinning: 'spinning',
|
||||
});
|
||||
|
||||
// 国际化 i18n 配置
|
||||
await setupI18n(app);
|
||||
@@ -33,9 +49,15 @@ async function bootstrap(namespace: string) {
|
||||
// 安装权限指令
|
||||
registerAccessDirective(app);
|
||||
|
||||
// 初始化 tippy
|
||||
initTippy(app);
|
||||
|
||||
// 配置路由及路由守卫
|
||||
app.use(router);
|
||||
|
||||
// 配置Motion插件
|
||||
app.use(MotionPlugin);
|
||||
|
||||
// 动态更新标题
|
||||
watchEffect(() => {
|
||||
if (preferences.app.dynamicTitle) {
|
||||
|
@@ -1,4 +1,3 @@
|
||||
export { default as CropperImage } from './src/cropper.vue';
|
||||
|
||||
export { default as CropperAvatar } from './src/cropper-avatar.vue';
|
||||
export { default as CropperImage } from './src/cropper.vue';
|
||||
export type { Cropper } from './src/typing';
|
||||
|
@@ -1,18 +1,14 @@
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
computed,
|
||||
type CSSProperties,
|
||||
type PropType,
|
||||
ref,
|
||||
unref,
|
||||
watch,
|
||||
watchEffect,
|
||||
} from 'vue';
|
||||
import type { ButtonProps } from 'ant-design-vue';
|
||||
|
||||
import type { CSSProperties, PropType } from 'vue';
|
||||
|
||||
import { computed, ref, unref, watch, watchEffect } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { $t as t } from '@vben/locales';
|
||||
|
||||
import { type ButtonProps, message } from 'ant-design-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import cropperModal from './cropper-modal.vue';
|
||||
|
||||
|
@@ -1,7 +1,9 @@
|
||||
<script lang="ts" setup>
|
||||
import type { PropType } from 'vue';
|
||||
|
||||
import type { CropendResult, Cropper } from './typing';
|
||||
|
||||
import { type PropType, ref } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { $t as t } from '@vben/locales';
|
||||
|
@@ -1,5 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import type { CSSProperties, PropType } from 'vue';
|
||||
|
||||
import { computed, onMounted, onUnmounted, ref, unref, useAttrs } from 'vue';
|
||||
|
||||
import { useDebounceFn } from '@vueuse/core';
|
||||
|
@@ -2,19 +2,11 @@
|
||||
import type { CardSize } from 'ant-design-vue/es/card/Card';
|
||||
import type { DescriptionsProps } from 'ant-design-vue/es/descriptions';
|
||||
|
||||
import type { CSSProperties, PropType, Slots } from 'vue';
|
||||
|
||||
import type { DescInstance, DescItem, DescriptionProps } from './typing';
|
||||
|
||||
import {
|
||||
computed,
|
||||
type CSSProperties,
|
||||
defineComponent,
|
||||
type PropType,
|
||||
ref,
|
||||
type Slots,
|
||||
toRefs,
|
||||
unref,
|
||||
useAttrs,
|
||||
} from 'vue';
|
||||
import { computed, defineComponent, ref, toRefs, unref, useAttrs } from 'vue';
|
||||
|
||||
import { Card, Descriptions } from 'ant-design-vue';
|
||||
import { get, isFunction } from 'lodash-es';
|
||||
|
@@ -1,9 +1,10 @@
|
||||
import type { Recordable } from '@vben/types';
|
||||
import type { DescriptionsProps } from 'ant-design-vue/es/descriptions';
|
||||
import type { JSX } from 'vue/jsx-runtime';
|
||||
|
||||
import type { CSSProperties, VNode } from 'vue';
|
||||
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
export interface DescItem {
|
||||
labelMinWidth?: number;
|
||||
contentMinWidth?: number;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { type VNode } from 'vue';
|
||||
import type { VNode } from 'vue';
|
||||
|
||||
import { Tag } from 'ant-design-vue';
|
||||
|
||||
|
@@ -1,3 +1,4 @@
|
||||
<!-- eslint-disable eqeqeq -->
|
||||
<script setup lang="ts">
|
||||
import type { DictData } from '#/api/system/dict/dict-data-model';
|
||||
|
||||
@@ -17,7 +18,6 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
});
|
||||
|
||||
const color = computed<string>(() => {
|
||||
// eslint-disable-next-line eqeqeq
|
||||
const current = props.dicts.find((item) => item.dictValue == props.value);
|
||||
const listClass = current?.listClass ?? '';
|
||||
// 是否为默认的颜色
|
||||
@@ -31,13 +31,11 @@ const color = computed<string>(() => {
|
||||
});
|
||||
|
||||
const cssClass = computed<string>(() => {
|
||||
// eslint-disable-next-line eqeqeq
|
||||
const current = props.dicts.find((item) => item.dictValue == props.value);
|
||||
return current?.cssClass ?? '';
|
||||
});
|
||||
|
||||
const label = computed<number | string>(() => {
|
||||
// eslint-disable-next-line eqeqeq
|
||||
const current = props.dicts.find((item) => item.dictValue == props.value);
|
||||
return current?.dictLabel ?? 'unknown';
|
||||
});
|
||||
|
@@ -1 +1,2 @@
|
||||
export { default as OptionsTag } from './src/options-tag.vue';
|
||||
export { default as TableSwitch } from './src/table-switch.vue';
|
||||
|
21
apps/web-antd/src/components/table/src/options-tag.vue
Normal file
21
apps/web-antd/src/components/table/src/options-tag.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<script setup lang="tsx">
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { Tag } from 'ant-design-vue';
|
||||
|
||||
defineOptions({ name: 'OptionsTag' });
|
||||
|
||||
const props = defineProps<{
|
||||
options: { color?: string; label: string; value: number | string }[];
|
||||
value: number | string;
|
||||
}>();
|
||||
|
||||
const found = computed(() =>
|
||||
props.options.find((item) => item.value === props.value),
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Tag v-if="found" :color="found.color">{{ found.label }}</Tag>
|
||||
<span v-else>未知</span>
|
||||
</template>
|
@@ -105,11 +105,7 @@ function filterOption(input: string, option: TenantOption) {
|
||||
show-search
|
||||
@deselect="onDeselect"
|
||||
@select="onSelected"
|
||||
>
|
||||
<template #suffixIcon>
|
||||
<span class="icon-mdi--company"></span>
|
||||
</template>
|
||||
</Select>
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@@ -2,6 +2,10 @@
|
||||
import type { IPropTypes } from '@tinymce/tinymce-vue/lib/cjs/main/ts/components/EditorPropTypes';
|
||||
import type { Editor as EditorType } from 'tinymce/tinymce';
|
||||
|
||||
import type { PropType } from 'vue';
|
||||
|
||||
import type { UploadResult } from '#/api/core/upload';
|
||||
|
||||
import {
|
||||
computed,
|
||||
nextTick,
|
||||
@@ -9,7 +13,6 @@ import {
|
||||
onBeforeUnmount,
|
||||
onDeactivated,
|
||||
onMounted,
|
||||
type PropType,
|
||||
ref,
|
||||
unref,
|
||||
useAttrs,
|
||||
@@ -22,7 +25,7 @@ import { buildShortUUID } from '@vben/utils';
|
||||
import Editor from '@tinymce/tinymce-vue';
|
||||
import { isNumber } from 'lodash-es';
|
||||
|
||||
import { uploadApi, type UploadResult } from '#/api/core/upload';
|
||||
import { uploadApi } from '#/api/core/upload';
|
||||
|
||||
import { bindHandlers } from './helper';
|
||||
import ImgUpload from './img-upload.vue';
|
||||
@@ -342,7 +345,7 @@ function handleDone(name: string, url: string) {
|
||||
v-if="!initOptions.inline && init"
|
||||
v-model="modelValue"
|
||||
:init="initOptions"
|
||||
:style="{ visibility: 'hidden' }"
|
||||
:style="{ visibility: 'hidden', zIndex: 3000 }"
|
||||
:tinymce-script-src="tinymceScriptSrc"
|
||||
license-key="gpl"
|
||||
/>
|
||||
@@ -350,6 +353,17 @@ function handleDone(name: string, url: string) {
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
/***
|
||||
由于modal/drawer的zIndex升级后为2000
|
||||
这里会造成遮挡 修改为更高的zIndex
|
||||
*/
|
||||
.tox.tox-silver-sink.tox-tinymce-aux {
|
||||
/** 该样式默认为1300的zIndex */
|
||||
z-index: 2025;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/**
|
||||
隐藏右上角upgrade按钮
|
||||
|
@@ -4,9 +4,9 @@ const validEvents = new Set([
|
||||
'onBeforeAddUndo',
|
||||
'onBeforeExecCommand',
|
||||
'onBeforeGetContent',
|
||||
'onBeforePaste',
|
||||
'onBeforeRenderUI',
|
||||
'onBeforeSetContent',
|
||||
'onBeforePaste',
|
||||
'onBlur',
|
||||
'onChange',
|
||||
'onClearUndos',
|
||||
@@ -42,8 +42,8 @@ const validEvents = new Set([
|
||||
'onMouseOver',
|
||||
'onMouseUp',
|
||||
'onNodeChange',
|
||||
'onObjectResizeStart',
|
||||
'onObjectResized',
|
||||
'onObjectResizeStart',
|
||||
'onObjectSelected',
|
||||
'onPaste',
|
||||
'onPostProcess',
|
||||
|
@@ -48,14 +48,6 @@ function handleChange(info: Record<string, any>) {
|
||||
const name = file?.name;
|
||||
|
||||
switch (status) {
|
||||
case 'uploading': {
|
||||
if (!uploading) {
|
||||
emit('uploading', name);
|
||||
uploading = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 'done': {
|
||||
// http 200会走到这里 需要再次判断
|
||||
const { response } = file;
|
||||
@@ -77,6 +69,14 @@ function handleChange(info: Record<string, any>) {
|
||||
|
||||
break;
|
||||
}
|
||||
case 'uploading': {
|
||||
if (!uploading) {
|
||||
emit('uploading', name);
|
||||
uploading = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
// No default
|
||||
}
|
||||
}
|
||||
|
@@ -1 +1,2 @@
|
||||
export { default as MenuSelectTable } from './src/menu-select-table.vue';
|
||||
export { default as TreeSelectPanel } from './src/tree-select-panel.vue';
|
||||
|
85
apps/web-antd/src/components/tree/src/data.tsx
Normal file
85
apps/web-antd/src/components/tree/src/data.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
import type { ID } from '#/api/common';
|
||||
import type { MenuOption } from '#/api/system/menu/model';
|
||||
|
||||
import { h, markRaw } from 'vue';
|
||||
|
||||
import { FolderIcon, MenuIcon, OkButtonIcon, VbenIcon } from '@vben/icons';
|
||||
|
||||
export interface Permission {
|
||||
checked: boolean;
|
||||
id: ID;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface MenuPermissionOption extends MenuOption {
|
||||
permissions: Permission[];
|
||||
}
|
||||
|
||||
const menuTypes = {
|
||||
C: { icon: markRaw(MenuIcon), value: '菜单' },
|
||||
F: { icon: markRaw(OkButtonIcon), value: '按钮' },
|
||||
M: { icon: markRaw(FolderIcon), value: '目录' },
|
||||
};
|
||||
|
||||
export const nodeOptions = [
|
||||
{ label: '节点关联', value: true },
|
||||
{ label: '节点独立', value: false },
|
||||
];
|
||||
|
||||
export const columns: VxeGridProps['columns'] = [
|
||||
{
|
||||
type: 'checkbox',
|
||||
title: '菜单名称',
|
||||
field: 'label',
|
||||
treeNode: true,
|
||||
headerAlign: 'left',
|
||||
align: 'left',
|
||||
width: 230,
|
||||
},
|
||||
{
|
||||
title: '图标',
|
||||
field: 'icon',
|
||||
width: 80,
|
||||
slots: {
|
||||
default: ({ row }) => {
|
||||
if (row?.icon === '#') {
|
||||
return '';
|
||||
}
|
||||
return (
|
||||
<span class={'flex justify-center'}>
|
||||
<VbenIcon icon={row.icon} />
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
field: 'menuType',
|
||||
width: 80,
|
||||
slots: {
|
||||
default: ({ row }) => {
|
||||
const current = menuTypes[row.menuType as 'C' | 'F' | 'M'];
|
||||
if (!current) {
|
||||
return '未知';
|
||||
}
|
||||
return (
|
||||
<span class="flex items-center justify-center gap-1">
|
||||
{h(current.icon, { class: 'size-[18px]' })}
|
||||
<span>{current.value}</span>
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '权限标识',
|
||||
field: 'permissions',
|
||||
headerAlign: 'left',
|
||||
align: 'left',
|
||||
slots: {
|
||||
default: 'permissions',
|
||||
},
|
||||
},
|
||||
];
|
133
apps/web-antd/src/components/tree/src/helper.tsx
Normal file
133
apps/web-antd/src/components/tree/src/helper.tsx
Normal file
@@ -0,0 +1,133 @@
|
||||
import type { MenuPermissionOption } from './data';
|
||||
|
||||
import type { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import type { MenuOption } from '#/api/system/menu/model';
|
||||
|
||||
import { eachTree, treeToList } from '@vben/utils';
|
||||
|
||||
import { difference, isEmpty, isUndefined } from 'lodash-es';
|
||||
|
||||
/**
|
||||
* 权限列设置是否全选
|
||||
* @param record 行记录
|
||||
* @param checked 是否选中
|
||||
*/
|
||||
export function setPermissionsChecked(
|
||||
record: MenuPermissionOption,
|
||||
checked: boolean,
|
||||
) {
|
||||
if (record?.permissions?.length > 0) {
|
||||
// 全部设置为选中
|
||||
record.permissions.forEach((permission) => {
|
||||
permission.checked = checked;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前行 & 所有子节点选中状态
|
||||
* @param record 行
|
||||
* @param checked 是否选中
|
||||
*/
|
||||
export function rowAndChildrenChecked(
|
||||
record: MenuPermissionOption,
|
||||
checked: boolean,
|
||||
) {
|
||||
// 当前行选中
|
||||
setPermissionsChecked(record, checked);
|
||||
// 所有子节点选中
|
||||
record?.children?.forEach?.((permission) => {
|
||||
rowAndChildrenChecked(permission as MenuPermissionOption, checked);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* void方法 会直接修改原始数据
|
||||
* 将树结构转为 tree+permissions结构
|
||||
* @param menus 后台返回的menu
|
||||
*/
|
||||
export function menusWithPermissions(menus: MenuOption[]) {
|
||||
eachTree(menus, (item: MenuPermissionOption) => {
|
||||
if (item.children && item.children.length > 0) {
|
||||
/**
|
||||
* 所有为按钮的节点提取出来
|
||||
* 需要注意 这里需要过滤目录下直接是按钮的情况item.menuType !== 'M'
|
||||
* 将按钮往children添加而非加到permissions
|
||||
*/
|
||||
const permissions = item.children.filter(
|
||||
(child: MenuOption) => child.menuType === 'F' && item.menuType !== 'M',
|
||||
);
|
||||
// 取差集
|
||||
const diffCollection = difference(item.children, permissions);
|
||||
// 更新后的children 即去除按钮
|
||||
item.children = diffCollection;
|
||||
|
||||
// permissions作为字段添加到item
|
||||
const permissionsArr = permissions.map((permission) => {
|
||||
return {
|
||||
id: permission.id,
|
||||
label: permission.label,
|
||||
checked: false,
|
||||
};
|
||||
});
|
||||
item.permissions = permissionsArr;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置表格选中
|
||||
* @param checkedKeys 选中的keys
|
||||
* @param menus 菜单 转换后的菜单
|
||||
* @param tableApi api
|
||||
* @param association 是否节点关联
|
||||
*/
|
||||
export function setTableChecked(
|
||||
checkedKeys: (number | string)[],
|
||||
menus: MenuPermissionOption[],
|
||||
tableApi: ReturnType<typeof useVbenVxeGrid>['1'],
|
||||
association: boolean,
|
||||
) {
|
||||
// tree转list
|
||||
const menuList: MenuPermissionOption[] = treeToList(menus);
|
||||
// 拿到勾选的行数据
|
||||
let checkedRows = menuList.filter((item) => checkedKeys.includes(item.id));
|
||||
|
||||
/**
|
||||
* 节点独立切换到节点关联 只需要最末尾的数据 即children为空
|
||||
*/
|
||||
if (!association) {
|
||||
checkedRows = checkedRows.filter(
|
||||
(item) => isUndefined(item.children) || isEmpty(item.children),
|
||||
);
|
||||
}
|
||||
|
||||
// 设置行选中 & permissions选中
|
||||
checkedRows.forEach((item) => {
|
||||
tableApi.grid.setCheckboxRow(item, true);
|
||||
if (item?.permissions?.length > 0) {
|
||||
item.permissions.forEach((permission) => {
|
||||
if (checkedKeys.includes(permission.id)) {
|
||||
permission.checked = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 节点独立切换到节点关联
|
||||
* 勾选后还需要过滤权限没有任何勾选的情况 这时候取消行的勾选
|
||||
*/
|
||||
if (!association) {
|
||||
const emptyRows = checkedRows.filter((item) => {
|
||||
if (isUndefined(item.permissions) || isEmpty(item.permissions)) {
|
||||
return false;
|
||||
}
|
||||
return item.permissions.every(
|
||||
(permission) => permission.checked === false,
|
||||
);
|
||||
});
|
||||
// 设置为不选中
|
||||
tableApi.grid.setCheckboxRow(emptyRows, false);
|
||||
}
|
||||
}
|
62
apps/web-antd/src/components/tree/src/hook.tsx
Normal file
62
apps/web-antd/src/components/tree/src/hook.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import type { TourProps } from 'ant-design-vue';
|
||||
|
||||
import { defineComponent, ref } from 'vue';
|
||||
|
||||
import { useLocalStorage } from '@vueuse/core';
|
||||
import { Tour } from 'ant-design-vue';
|
||||
|
||||
/**
|
||||
* 全屏引导
|
||||
* @returns value
|
||||
*/
|
||||
export function useFullScreenGuide() {
|
||||
const open = ref(false);
|
||||
/**
|
||||
* 是否已读 只显示一次
|
||||
*/
|
||||
const read = useLocalStorage('menu_select_fullscreen_read', false);
|
||||
|
||||
function openGuide() {
|
||||
if (!read.value) {
|
||||
open.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
function closeGuide() {
|
||||
open.value = false;
|
||||
read.value = true;
|
||||
}
|
||||
|
||||
const steps: TourProps['steps'] = [
|
||||
{
|
||||
title: '提示',
|
||||
description: '点击这里可以全屏',
|
||||
target: () =>
|
||||
document.querySelector(
|
||||
'div#menu-select-table .vxe-tools--operate > button[title="全屏"]',
|
||||
)!,
|
||||
},
|
||||
];
|
||||
|
||||
const FullScreenGuide = defineComponent({
|
||||
name: 'FullScreenGuide',
|
||||
inheritAttrs: false,
|
||||
setup() {
|
||||
return () => (
|
||||
<Tour
|
||||
onClose={closeGuide}
|
||||
open={open.value}
|
||||
steps={steps}
|
||||
zIndex={9999}
|
||||
/>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
FullScreenGuide,
|
||||
openGuide,
|
||||
closeGuide,
|
||||
};
|
||||
}
|
408
apps/web-antd/src/components/tree/src/menu-select-table.vue
Normal file
408
apps/web-antd/src/components/tree/src/menu-select-table.vue
Normal file
@@ -0,0 +1,408 @@
|
||||
<!--
|
||||
不兼容也不会兼容一些错误用法
|
||||
比如: 菜单下放目录 菜单下放菜单
|
||||
比如: 按钮下放目录 按钮下放菜单 按钮下放按钮
|
||||
-->
|
||||
<script setup lang="tsx">
|
||||
import type { RadioChangeEvent } from 'ant-design-vue';
|
||||
|
||||
import type { MenuPermissionOption } from './data';
|
||||
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
import type { MenuOption } from '#/api/system/menu/model';
|
||||
|
||||
import { nextTick, onMounted, ref, shallowRef, watch } from 'vue';
|
||||
|
||||
import { cloneDeep, findGroupParentIds } from '@vben/utils';
|
||||
|
||||
import { Alert, Checkbox, RadioGroup, Space } from 'ant-design-vue';
|
||||
import { uniq } from 'lodash-es';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
|
||||
import { columns, nodeOptions } from './data';
|
||||
import {
|
||||
menusWithPermissions,
|
||||
rowAndChildrenChecked,
|
||||
setPermissionsChecked,
|
||||
setTableChecked,
|
||||
} from './helper';
|
||||
import { useFullScreenGuide } from './hook';
|
||||
|
||||
defineOptions({
|
||||
name: 'MenuSelectTable',
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
checkedKeys: (number | string)[];
|
||||
defaultExpandAll?: boolean;
|
||||
menus: MenuOption[];
|
||||
}>(),
|
||||
{
|
||||
/**
|
||||
* 是否默认展开全部
|
||||
*/
|
||||
defaultExpandAll: true,
|
||||
/**
|
||||
* 注意这里不是双向绑定 需要调用getCheckedKeys实例方法来获取真正选中的节点
|
||||
*/
|
||||
checkedKeys: () => [],
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* 是否节点关联
|
||||
*/
|
||||
const association = defineModel('association', {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
});
|
||||
|
||||
const gridOptions: VxeGridProps = {
|
||||
checkboxConfig: {
|
||||
// checkbox显示的字段
|
||||
labelField: 'label',
|
||||
// 是否严格模式 即节点不关联
|
||||
checkStrictly: !association.value,
|
||||
},
|
||||
size: 'small',
|
||||
columns,
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
pagerConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
proxyConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: false,
|
||||
custom: false,
|
||||
},
|
||||
rowConfig: {
|
||||
isHover: false,
|
||||
isCurrent: false,
|
||||
keyField: 'id',
|
||||
},
|
||||
/**
|
||||
* 开启虚拟滚动
|
||||
* 数据量小可以选择关闭
|
||||
* 如果遇到样式问题(空白、错位 滚动等)可以选择关闭虚拟滚动
|
||||
*/
|
||||
scrollY: {
|
||||
enabled: true,
|
||||
gt: 0,
|
||||
},
|
||||
treeConfig: {
|
||||
parentField: 'parentId',
|
||||
rowField: 'id',
|
||||
transform: false,
|
||||
},
|
||||
// 溢出换行显示
|
||||
showOverflow: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* 用于界面显示选中的数量
|
||||
*/
|
||||
const checkedNum = ref(0);
|
||||
/**
|
||||
* 更新选中的数量
|
||||
*/
|
||||
function updateCheckedNumber() {
|
||||
checkedNum.value = getCheckedKeys().length;
|
||||
}
|
||||
|
||||
const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
gridOptions,
|
||||
gridEvents: {
|
||||
// 勾选事件
|
||||
checkboxChange: (params) => {
|
||||
// 选中还是取消选中
|
||||
const checked = params.checked;
|
||||
// 行
|
||||
const record = params.row;
|
||||
if (association.value) {
|
||||
// 节点关联
|
||||
// 设置所有子节点选中状态
|
||||
rowAndChildrenChecked(record, checked);
|
||||
} else {
|
||||
// 节点独立
|
||||
// 点行会勾选/取消全部权限 点权限不会勾选行
|
||||
setPermissionsChecked(record, checked);
|
||||
}
|
||||
updateCheckedNumber();
|
||||
},
|
||||
// 全选事件
|
||||
checkboxAll: (params) => {
|
||||
const records = params.$grid.getData();
|
||||
records.forEach((item) => {
|
||||
rowAndChildrenChecked(item, params.checked);
|
||||
});
|
||||
updateCheckedNumber();
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* 设置表格选中
|
||||
* @param menus menu
|
||||
* @param keys 选中的key
|
||||
* @param triggerOnchange 节点独立情况 不需要触发onChange(false)
|
||||
*/
|
||||
function setCheckedByKeys(
|
||||
menus: MenuPermissionOption[],
|
||||
keys: (number | string)[],
|
||||
triggerOnchange: boolean,
|
||||
) {
|
||||
menus.forEach((item) => {
|
||||
// 设置行选中
|
||||
if (keys.includes(item.id)) {
|
||||
tableApi.grid.setCheckboxRow(item, true);
|
||||
}
|
||||
// 设置权限columns选中
|
||||
if (item.permissions && item.permissions.length > 0) {
|
||||
// 遍历 设置勾选
|
||||
item.permissions.forEach((permission) => {
|
||||
if (keys.includes(permission.id)) {
|
||||
permission.checked = true;
|
||||
// 手动触发onChange来选中 节点独立情况不需要处理
|
||||
triggerOnchange && handlePermissionChange(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 设置children选中
|
||||
if (item.children && item.children.length > 0) {
|
||||
setCheckedByKeys(item.children as any, keys, triggerOnchange);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const { FullScreenGuide, openGuide } = useFullScreenGuide();
|
||||
onMounted(() => {
|
||||
/**
|
||||
* 加载表格数据 转为指定结构
|
||||
*/
|
||||
watch(
|
||||
() => props.menus,
|
||||
async (menus) => {
|
||||
const clonedMenus = cloneDeep(menus);
|
||||
menusWithPermissions(clonedMenus);
|
||||
// console.log(clonedMenus);
|
||||
await tableApi.grid.loadData(clonedMenus);
|
||||
// 展开全部 默认true
|
||||
if (props.defaultExpandAll) {
|
||||
await nextTick();
|
||||
setExpandOrCollapse(true);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* 节点关联变动 更新表格勾选效果
|
||||
*/
|
||||
watch(association, (value) => {
|
||||
tableApi.setGridOptions({
|
||||
checkboxConfig: {
|
||||
checkStrictly: !value,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* checkedKeys依赖menus
|
||||
* 要注意加载顺序
|
||||
* !!!要在外部确保menus先加载!!!
|
||||
*/
|
||||
watch(
|
||||
() => props.checkedKeys,
|
||||
(value) => {
|
||||
const allCheckedKeys = uniq([...value]);
|
||||
// 获取表格data 如果checkedKeys在menus的watch之前触发 这里会拿到空 导致勾选异常
|
||||
const records = tableApi.grid.getData();
|
||||
setCheckedByKeys(records, allCheckedKeys, association.value);
|
||||
updateCheckedNumber();
|
||||
|
||||
// 全屏引导
|
||||
setTimeout(openGuide, 1000);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
// 缓存上次(切换节点关系前)选中的keys
|
||||
const lastCheckedKeys = shallowRef<(number | string)[]>([]);
|
||||
/**
|
||||
* 节点关联变动 事件
|
||||
*/
|
||||
async function handleAssociationChange(e: RadioChangeEvent) {
|
||||
lastCheckedKeys.value = getCheckedKeys();
|
||||
// 清空全部permissions选中
|
||||
const records = tableApi.grid.getData();
|
||||
records.forEach((item) => {
|
||||
rowAndChildrenChecked(item, false);
|
||||
});
|
||||
// 需要清空全部勾选
|
||||
await tableApi.grid.clearCheckboxRow();
|
||||
// 滚动到顶部
|
||||
await tableApi.grid.scrollTo(0, 0);
|
||||
|
||||
// 节点切换 不同的选中
|
||||
setTableChecked(lastCheckedKeys.value, records, tableApi, !e.target.value);
|
||||
|
||||
updateCheckedNumber();
|
||||
}
|
||||
|
||||
/**
|
||||
* 全部展开/折叠
|
||||
* @param expand 是否展开
|
||||
*/
|
||||
function setExpandOrCollapse(expand: boolean) {
|
||||
tableApi.grid?.setAllTreeExpand(expand);
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限列表 checkbox勾选的事件
|
||||
* @param row 行
|
||||
*/
|
||||
function handlePermissionChange(row: any) {
|
||||
// 节点关联
|
||||
if (association.value) {
|
||||
const checkedPermissions = row.permissions.filter(
|
||||
(item: any) => item.checked === true,
|
||||
);
|
||||
// 有一条选中 则整个行选中
|
||||
if (checkedPermissions.length > 0) {
|
||||
tableApi.grid.setCheckboxRow(row, true);
|
||||
}
|
||||
// 无任何选中 则整个行不选中
|
||||
if (checkedPermissions.length === 0) {
|
||||
tableApi.grid.setCheckboxRow(row, false);
|
||||
}
|
||||
}
|
||||
// 节点独立 不处理
|
||||
updateCheckedNumber();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取勾选的key
|
||||
* @param records 行记录列表
|
||||
* @param addCurrent 是否添加当前行的id
|
||||
*/
|
||||
function getKeys(records: MenuPermissionOption[], addCurrent: boolean) {
|
||||
const allKeys: (number | string)[] = [];
|
||||
records.forEach((item) => {
|
||||
// 处理children
|
||||
if (item.children && item.children.length > 0) {
|
||||
const keys = getKeys(item.children as MenuPermissionOption[], addCurrent);
|
||||
allKeys.push(...keys);
|
||||
} else {
|
||||
// 当前行的id
|
||||
addCurrent && allKeys.push(item.id);
|
||||
// 当前行权限id 获取已经选中的
|
||||
if (item.permissions && item.permissions.length > 0) {
|
||||
const ids = item.permissions
|
||||
.filter((m) => m.checked === true)
|
||||
.map((m) => m.id);
|
||||
allKeys.push(...ids);
|
||||
}
|
||||
}
|
||||
});
|
||||
return uniq(allKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取选中的key
|
||||
*/
|
||||
function getCheckedKeys() {
|
||||
// 节点关联
|
||||
if (association.value) {
|
||||
const records = tableApi?.grid?.getCheckboxRecords?.() ?? [];
|
||||
// 子节点
|
||||
const nodeKeys = getKeys(records, true);
|
||||
// 所有父节点
|
||||
const parentIds = findGroupParentIds(props.menus, nodeKeys as number[]);
|
||||
// 拼接 去重
|
||||
const realKeys = uniq([...parentIds, ...nodeKeys]);
|
||||
return realKeys;
|
||||
}
|
||||
// 节点独立
|
||||
|
||||
// 勾选的行
|
||||
const records = tableApi?.grid?.getCheckboxRecords?.() ?? [];
|
||||
// 全部数据 用于获取permissions
|
||||
const allRecords = tableApi?.grid?.getData?.() ?? [];
|
||||
// 表格已经选中的行ids
|
||||
const checkedIds = records.map((item) => item.id);
|
||||
// 所有已经勾选权限的ids
|
||||
const permissionIds = getKeys(allRecords, false);
|
||||
// 合并 去重
|
||||
const allIds = uniq([...checkedIds, ...permissionIds]);
|
||||
return allIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* 暴露给外部使用 获取已选中的key
|
||||
*/
|
||||
defineExpose({
|
||||
getCheckedKeys,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex h-full flex-col" id="menu-select-table">
|
||||
<BasicTable>
|
||||
<template #toolbar-actions>
|
||||
<RadioGroup
|
||||
v-model:value="association"
|
||||
:options="nodeOptions"
|
||||
button-style="solid"
|
||||
option-type="button"
|
||||
@change="handleAssociationChange"
|
||||
/>
|
||||
<Alert class="mx-2" type="info">
|
||||
<template #message>
|
||||
<div>
|
||||
已选中
|
||||
<span class="text-primary mx-1 font-semibold">
|
||||
{{ checkedNum }}
|
||||
</span>
|
||||
个节点
|
||||
</div>
|
||||
</template>
|
||||
</Alert>
|
||||
</template>
|
||||
<template #toolbar-tools>
|
||||
<Space>
|
||||
<a-button @click="setExpandOrCollapse(false)">
|
||||
{{ $t('pages.common.collapse') }}
|
||||
</a-button>
|
||||
<a-button @click="setExpandOrCollapse(true)">
|
||||
{{ $t('pages.common.expand') }}
|
||||
</a-button>
|
||||
</Space>
|
||||
</template>
|
||||
<template #permissions="{ row }">
|
||||
<div class="flex flex-wrap gap-x-3 gap-y-1">
|
||||
<Checkbox
|
||||
v-for="permission in row.permissions"
|
||||
:key="permission.id"
|
||||
v-model:checked="permission.checked"
|
||||
@change="() => handlePermissionChange(row)"
|
||||
>
|
||||
{{ permission.label }}
|
||||
</Checkbox>
|
||||
</div>
|
||||
</template>
|
||||
</BasicTable>
|
||||
<!-- 全屏引导 -->
|
||||
<FullScreenGuide />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:deep(.ant-alert) {
|
||||
padding: 4px 8px;
|
||||
}
|
||||
</style>
|
@@ -3,11 +3,14 @@ import type { CheckboxChangeEvent } from 'ant-design-vue/es/checkbox/interface';
|
||||
import type { DataNode } from 'ant-design-vue/es/tree';
|
||||
import type { CheckInfo } from 'ant-design-vue/es/vc-tree/props';
|
||||
|
||||
import { computed, nextTick, onMounted, type PropType, ref, watch } from 'vue';
|
||||
import type { PropType, SetupContext } from 'vue';
|
||||
|
||||
import { computed, nextTick, onMounted, ref, useSlots, watch } from 'vue';
|
||||
|
||||
import { findGroupParentIds, treeToList } from '@vben/utils';
|
||||
|
||||
import { Checkbox, Tree } from 'ant-design-vue';
|
||||
import { uniq } from 'lodash-es';
|
||||
|
||||
/** 需要禁止透传 */
|
||||
defineOptions({ inheritAttrs: false });
|
||||
@@ -73,6 +76,8 @@ const checkedRealKeys = ref<(number | string)[]>([]);
|
||||
/**
|
||||
* 取第一次的menuTree id 设置到checkedMenuKeys
|
||||
* 主要为了解决没有任何修改 直接点击保存的情况
|
||||
*
|
||||
* length为0情况(即新增时候没有勾选节点) 勾选这里会延迟触发 节点会拼接上父节点 导致ID重复
|
||||
*/
|
||||
const stop = watch([checkedKeys, () => props.treeData], () => {
|
||||
if (
|
||||
@@ -86,7 +91,10 @@ const stop = watch([checkedKeys, () => props.treeData], () => {
|
||||
checkedKeys.value as any,
|
||||
{ id: props.fieldNames.key },
|
||||
);
|
||||
checkedRealKeys.value = [...parentIds, ...checkedKeys.value];
|
||||
/**
|
||||
* uniq 解决上面的id重复问题
|
||||
*/
|
||||
checkedRealKeys.value = uniq([...parentIds, ...checkedKeys.value]);
|
||||
stop();
|
||||
}
|
||||
if (!props.checkStrictly && checkedKeys.value.length > 0) {
|
||||
@@ -98,19 +106,21 @@ const stop = watch([checkedKeys, () => props.treeData], () => {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param checkedKeys 已经选中的子节点的ID
|
||||
* @param checkedStateKeys 已经选中的子节点的ID
|
||||
* @param info info.halfCheckedKeys为父节点的ID
|
||||
*/
|
||||
type CheckedState<T = number | string> =
|
||||
| { checked: T[]; halfChecked: T[] }
|
||||
| T[];
|
||||
function handleChecked(checkedKeys: CheckedState, info: CheckInfo) {
|
||||
| T[]
|
||||
| { checked: T[]; halfChecked: T[] };
|
||||
function handleChecked(checkedStateKeys: CheckedState, info: CheckInfo) {
|
||||
// 数组的话为节点关联
|
||||
if (Array.isArray(checkedKeys)) {
|
||||
if (Array.isArray(checkedStateKeys)) {
|
||||
const halfCheckedKeys: number[] = (info.halfCheckedKeys || []) as number[];
|
||||
checkedRealKeys.value = [...halfCheckedKeys, ...checkedKeys];
|
||||
checkedRealKeys.value = [...halfCheckedKeys, ...checkedStateKeys];
|
||||
} else {
|
||||
checkedRealKeys.value = [...checkedKeys.checked];
|
||||
checkedRealKeys.value = [...checkedStateKeys.checked];
|
||||
// fix: Invalid prop: type check failed for prop "value". Expected Array, got Object
|
||||
checkedKeys.value = [...checkedStateKeys.checked];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,9 +147,10 @@ function handleCheckStrictlyChange(e: CheckboxChangeEvent) {
|
||||
|
||||
/**
|
||||
* 暴露方法来获取用于提交的全部节点
|
||||
* uniq去重(保险方案)
|
||||
*/
|
||||
defineExpose({
|
||||
getCheckedKeys: () => checkedRealKeys.value,
|
||||
getCheckedKeys: () => uniq(checkedRealKeys.value),
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
@@ -148,6 +159,8 @@ onMounted(async () => {
|
||||
expandedKeys.value = allKeys.value;
|
||||
}
|
||||
});
|
||||
|
||||
const slots = useSlots() as SetupContext['slots'];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -196,7 +209,7 @@ onMounted(async () => {
|
||||
@check="handleChecked"
|
||||
>
|
||||
<template
|
||||
v-for="slotName in Object.keys($slots)"
|
||||
v-for="slotName in Object.keys(slots)"
|
||||
:key="slotName"
|
||||
#[slotName]="data"
|
||||
>
|
||||
|
@@ -2,6 +2,10 @@
|
||||
import type { UploadFile, UploadProps } from 'ant-design-vue';
|
||||
import type { UploadRequestOption } from 'ant-design-vue/lib/vc-upload/interface';
|
||||
|
||||
import type { AxiosResponse } from '@vben/request';
|
||||
|
||||
import type { AxiosProgressEvent } from '#/api';
|
||||
|
||||
import { ref, toRefs, watch } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
@@ -26,7 +30,10 @@ const props = withDefaults(
|
||||
* 需自行改造 ./helper/checkFileType方法
|
||||
*/
|
||||
accept?: string[];
|
||||
api?: (...args: any[]) => Promise<any>;
|
||||
api?: (
|
||||
file: Blob | File,
|
||||
onUploadProgress?: AxiosProgressEvent,
|
||||
) => Promise<AxiosResponse<any>>;
|
||||
disabled?: boolean;
|
||||
helpText?: string;
|
||||
// 最大数量的文件,Infinity不限制
|
||||
@@ -38,6 +45,10 @@ const props = withDefaults(
|
||||
// support xxx.xxx.xx
|
||||
// 返回的字段 默认url
|
||||
resultField?: 'fileName' | 'ossId' | 'url' | string;
|
||||
/**
|
||||
* 是否显示下面的描述
|
||||
*/
|
||||
showDescription?: boolean;
|
||||
value?: string[];
|
||||
}>(),
|
||||
{
|
||||
@@ -50,6 +61,7 @@ const props = withDefaults(
|
||||
multiple: false,
|
||||
api: uploadApi,
|
||||
resultField: '',
|
||||
showDescription: true,
|
||||
},
|
||||
);
|
||||
const emit = defineEmits(['change', 'update:value', 'delete']);
|
||||
@@ -144,12 +156,19 @@ async function customRequest(info: UploadRequestOption<any>) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const res = await api?.(info.file);
|
||||
// 进度条事件
|
||||
const progressEvent: AxiosProgressEvent = (e) => {
|
||||
const percent = Math.trunc((e.loaded / e.total!) * 100);
|
||||
info.onProgress!({ percent });
|
||||
};
|
||||
const res = await api?.(info.file as File, progressEvent);
|
||||
/**
|
||||
* 由getValue处理 传对象过去
|
||||
* 直接传string(id)会被转为Number
|
||||
* 内部的逻辑由requestClient.upload处理 这里不用判断业务状态码 不符合会自动reject
|
||||
*/
|
||||
info.onSuccess!(res);
|
||||
message.success($t('component.upload.uploadSuccess'));
|
||||
// 获取
|
||||
const value = getValue();
|
||||
isInnerOperate.value = true;
|
||||
@@ -191,6 +210,7 @@ function getValue() {
|
||||
:max-count="maxNumber"
|
||||
:multiple="multiple"
|
||||
list-type="text"
|
||||
:progress="{ showInfo: true }"
|
||||
@remove="handleRemove"
|
||||
>
|
||||
<div v-if="fileList && fileList.length < maxNumber">
|
||||
@@ -199,14 +219,21 @@ function getValue() {
|
||||
{{ $t('component.upload.upload') }}
|
||||
</a-button>
|
||||
</div>
|
||||
<div v-if="showDescription" class="mt-2 flex flex-wrap items-center">
|
||||
请上传不超过
|
||||
<div class="text-primary mx-1 font-bold">{{ maxSize }}MB</div>
|
||||
的
|
||||
<div class="text-primary mx-1 font-bold">{{ accept.join('/') }}</div>
|
||||
格式文件
|
||||
</div>
|
||||
</Upload>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
<style>
|
||||
.ant-upload-select-picture-card i {
|
||||
color: #999;
|
||||
font-size: 32px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.ant-upload-select-picture-card .ant-upload-text {
|
||||
|
@@ -2,15 +2,20 @@
|
||||
import type { UploadFile, UploadProps } from 'ant-design-vue';
|
||||
import type { UploadRequestOption } from 'ant-design-vue/lib/vc-upload/interface';
|
||||
|
||||
import type { AxiosResponse } from '@vben/request';
|
||||
|
||||
import type { AxiosProgressEvent } from '#/api';
|
||||
|
||||
import { ref, toRefs, watch } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { PlusOutlined } from '@ant-design/icons-vue';
|
||||
import { message, Modal, Upload } from 'ant-design-vue';
|
||||
import { isArray, isFunction, isObject, isString } from 'lodash-es';
|
||||
import { isArray, isFunction, isObject, isString, uniqueId } from 'lodash-es';
|
||||
|
||||
import { uploadApi } from '#/api';
|
||||
import { ossInfo } from '#/api/system/oss';
|
||||
|
||||
import { checkImageFileType, defaultImageAccept } from './helper';
|
||||
import { UploadResultStatus } from './typing';
|
||||
@@ -24,7 +29,10 @@ const props = withDefaults(
|
||||
* 包括拓展名(不带点) 文件头(image/png等 不包括泛写法即image/*)
|
||||
*/
|
||||
accept?: string[];
|
||||
api?: (...args: any[]) => Promise<any>;
|
||||
api?: (
|
||||
file: Blob | File,
|
||||
onUploadProgress?: AxiosProgressEvent,
|
||||
) => Promise<AxiosResponse<any>>;
|
||||
disabled?: boolean;
|
||||
helpText?: string;
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
@@ -37,7 +45,11 @@ const props = withDefaults(
|
||||
multiple?: boolean;
|
||||
// support xxx.xxx.xx
|
||||
// 返回的字段 默认url
|
||||
resultField?: 'fileName' | 'ossId' | 'url' | string;
|
||||
resultField?: 'fileName' | 'ossId' | 'url';
|
||||
/**
|
||||
* 是否显示下面的描述
|
||||
*/
|
||||
showDescription?: boolean;
|
||||
value?: string | string[];
|
||||
}>(),
|
||||
{
|
||||
@@ -50,7 +62,8 @@ const props = withDefaults(
|
||||
accept: () => defaultImageAccept,
|
||||
multiple: false,
|
||||
api: uploadApi,
|
||||
resultField: '',
|
||||
resultField: 'url',
|
||||
showDescription: true,
|
||||
},
|
||||
);
|
||||
const emit = defineEmits(['change', 'update:value', 'delete']);
|
||||
@@ -74,7 +87,7 @@ const isFirstRender = ref<boolean>(true);
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
(v) => {
|
||||
async (v) => {
|
||||
if (isInnerOperate.value) {
|
||||
isInnerOperate.value = false;
|
||||
return;
|
||||
@@ -90,19 +103,40 @@ watch(
|
||||
}
|
||||
// 直接赋值 可能为string | string[]
|
||||
value = v;
|
||||
fileList.value = _fileList.map((item, i) => {
|
||||
if (item && isString(item)) {
|
||||
return {
|
||||
uid: `${-i}`,
|
||||
name: item.slice(Math.max(0, item.lastIndexOf('/') + 1)),
|
||||
status: 'done',
|
||||
url: item,
|
||||
};
|
||||
} else if (item && isObject(item)) {
|
||||
return item;
|
||||
const withUrlList: UploadProps['fileList'] = [];
|
||||
for (const item of _fileList) {
|
||||
// ossId情况
|
||||
if (props.resultField === 'ossId') {
|
||||
const resp = await ossInfo([item]);
|
||||
if (item && isString(item)) {
|
||||
withUrlList.push({
|
||||
uid: item, // ossId作为uid 方便getValue获取
|
||||
name: item.slice(Math.max(0, item.lastIndexOf('/') + 1)),
|
||||
status: 'done',
|
||||
url: resp?.[0]?.url,
|
||||
});
|
||||
} else if (item && isObject(item)) {
|
||||
withUrlList.push({
|
||||
...(item as any),
|
||||
uid: item,
|
||||
url: resp?.[0]?.url,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// 非ossId情况
|
||||
if (item && isString(item)) {
|
||||
withUrlList.push({
|
||||
uid: uniqueId(),
|
||||
name: item.slice(Math.max(0, item.lastIndexOf('/') + 1)),
|
||||
status: 'done',
|
||||
url: item,
|
||||
});
|
||||
} else if (item && isObject(item)) {
|
||||
withUrlList.push(item);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}) as UploadProps['fileList'];
|
||||
}
|
||||
fileList.value = withUrlList;
|
||||
}
|
||||
if (!isFirstRender.value) {
|
||||
emit('change', value);
|
||||
@@ -182,12 +216,19 @@ async function customRequest(info: UploadRequestOption<any>) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const res = await api?.(info.file);
|
||||
// 进度条事件
|
||||
const progressEvent: AxiosProgressEvent = (e) => {
|
||||
const percent = Math.trunc((e.loaded / e.total!) * 100);
|
||||
info.onProgress!({ percent });
|
||||
};
|
||||
const res = await api?.(info.file as File, progressEvent);
|
||||
/**
|
||||
* 由getValue处理 传对象过去
|
||||
* 直接传string(id)会被转为Number
|
||||
* 内部的逻辑由requestClient.upload处理 这里不用判断业务状态码 不符合会自动reject
|
||||
*/
|
||||
info.onSuccess!(res);
|
||||
message.success($t('component.upload.uploadSuccess'));
|
||||
// 获取
|
||||
const value = getValue();
|
||||
isInnerOperate.value = true;
|
||||
@@ -200,12 +241,17 @@ async function customRequest(info: UploadRequestOption<any>) {
|
||||
}
|
||||
|
||||
function getValue() {
|
||||
console.log(fileList.value);
|
||||
const list = (fileList.value || [])
|
||||
.filter((item) => item?.status === UploadResultStatus.DONE)
|
||||
.map((item: any) => {
|
||||
if (item?.response && props?.resultField) {
|
||||
return item?.response?.[props.resultField];
|
||||
}
|
||||
// ossId兼容 uid为ossId直接返回
|
||||
if (props.resultField === 'ossId' && item.uid) {
|
||||
return item.uid;
|
||||
}
|
||||
// 适用于已经有图片 回显的情况 会默认在init处理为{url: 'xx'}
|
||||
if (item?.url) {
|
||||
return item.url;
|
||||
@@ -237,6 +283,7 @@ function getValue() {
|
||||
:list-type="listType"
|
||||
:max-count="maxNumber"
|
||||
:multiple="multiple"
|
||||
:progress="{ showInfo: true }"
|
||||
@preview="handlePreview"
|
||||
@remove="handleRemove"
|
||||
>
|
||||
@@ -245,6 +292,16 @@ function getValue() {
|
||||
<div style="margin-top: 8px">{{ $t('component.upload.upload') }}</div>
|
||||
</div>
|
||||
</Upload>
|
||||
<div
|
||||
v-if="showDescription"
|
||||
class="mt-2 flex flex-wrap items-center text-[14px]"
|
||||
>
|
||||
请上传不超过
|
||||
<div class="text-primary mx-1 font-bold">{{ maxSize }}MB</div>
|
||||
的
|
||||
<div class="text-primary mx-1 font-bold">{{ accept.join('/') }}</div>
|
||||
格式文件
|
||||
</div>
|
||||
<Modal
|
||||
:footer="null"
|
||||
:open="previewOpen"
|
||||
@@ -256,10 +313,10 @@ function getValue() {
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
<style>
|
||||
.ant-upload-select-picture-card i {
|
||||
color: #999;
|
||||
font-size: 32px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.ant-upload-select-picture-card .ant-upload-text {
|
||||
|
@@ -15,7 +15,7 @@ export interface FileItem {
|
||||
percent: number;
|
||||
file: File;
|
||||
status?: UploadResultStatus;
|
||||
response?: { fileName: string; ossId: string; url: string } | Recordable<any>;
|
||||
response?: Recordable<any> | { fileName: string; ossId: string; url: string };
|
||||
uuid: string;
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { computed, unref } from 'vue';
|
||||
import type { Ref } from 'vue';
|
||||
|
||||
import { computed, unref } from 'vue';
|
||||
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
export function useUploadType({
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, watch } from 'vue';
|
||||
import { computed, h, onMounted, watch } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { AuthenticationLoginExpiredModal } from '@vben/common-ui';
|
||||
@@ -8,6 +8,7 @@ import { useWatermark } from '@vben/hooks';
|
||||
import {
|
||||
BookOpenText,
|
||||
CircleHelp,
|
||||
GiteeIcon,
|
||||
GitHubOutlined,
|
||||
UserOutlined,
|
||||
} from '@vben/icons';
|
||||
@@ -55,6 +56,15 @@ const menus = computed(() => {
|
||||
icon: UserOutlined,
|
||||
text: $t('ui.widgets.profile'),
|
||||
},
|
||||
{
|
||||
handler: () => {
|
||||
openWindow('https://gitee.com/dapppp/ruoyi-plus-vben5', {
|
||||
target: '_blank',
|
||||
});
|
||||
},
|
||||
icon: () => h(GiteeIcon, { class: 'text-red-800' }),
|
||||
text: 'Gitee项目地址',
|
||||
},
|
||||
{
|
||||
handler: () => {
|
||||
openWindow(VBEN_GITHUB_URL, {
|
||||
@@ -62,7 +72,7 @@ const menus = computed(() => {
|
||||
});
|
||||
},
|
||||
icon: GitHubOutlined,
|
||||
text: 'GitHub',
|
||||
text: 'Vben官方地址',
|
||||
},
|
||||
{
|
||||
handler: () => {
|
||||
@@ -84,11 +94,14 @@ const menus = computed(() => {
|
||||
});
|
||||
|
||||
const avatar = computed(() => {
|
||||
return userStore.userInfo?.avatar ?? preferences.app.defaultAvatar;
|
||||
return userStore.userInfo?.avatar || preferences.app.defaultAvatar;
|
||||
});
|
||||
|
||||
async function handleLogout() {
|
||||
await authStore.logout();
|
||||
/**
|
||||
* 主动登出不需要带跳转地址
|
||||
*/
|
||||
await authStore.logout(false);
|
||||
resetRoutes();
|
||||
}
|
||||
|
||||
|
@@ -1,7 +1,9 @@
|
||||
import type { LocaleSetupOptions, SupportedLanguagesType } from '@vben/locales';
|
||||
import type { Locale } from 'ant-design-vue/es/locale';
|
||||
|
||||
import type { App } from 'vue';
|
||||
|
||||
import type { LocaleSetupOptions, SupportedLanguagesType } from '@vben/locales';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import {
|
||||
@@ -30,7 +32,8 @@ const localesMap = loadLocalesMapFromDir(
|
||||
*/
|
||||
async function loadMessages(lang: SupportedLanguagesType) {
|
||||
const [appLocaleMessages] = await Promise.all([
|
||||
localesMap[lang](),
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
localesMap[lang]!(),
|
||||
loadThirdPartyMessage(lang),
|
||||
]);
|
||||
return appLocaleMessages.default;
|
||||
|
7
apps/web-antd/src/locales/langs/en-US/http.json
Normal file
7
apps/web-antd/src/locales/langs/en-US/http.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"apiRequestFailed": "Operation failed",
|
||||
"operationSuccess": "Operation Success",
|
||||
"successTip": "Success Tip",
|
||||
"errorTip": "Error Tip",
|
||||
"loginTimeout": "Login timeout, please log in again"
|
||||
}
|
7
apps/web-antd/src/locales/langs/zh-CN/http.json
Normal file
7
apps/web-antd/src/locales/langs/zh-CN/http.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"apiRequestFailed": "请求出错,请稍候重试",
|
||||
"operationSuccess": "操作成功",
|
||||
"successTip": "成功提示",
|
||||
"errorTip": "错误提示",
|
||||
"loginTimeout": "登录超时, 请重新登录"
|
||||
}
|
@@ -47,4 +47,18 @@ export const overridesPreferences = defineOverridesPreferences({
|
||||
*/
|
||||
semiDarkSidebar: false,
|
||||
},
|
||||
/**
|
||||
* !!! 更改配置后请清空浏览器缓存
|
||||
* 在这里更换logo
|
||||
* source可选值:
|
||||
* 1. 本地public目录下的图片 需要加上/ 比如:/logo.png
|
||||
* 2. 网络图片链接
|
||||
* 3. vite导入的图片 import xxx from 'xxx.png'
|
||||
*
|
||||
* !!! 更改配置后请清空浏览器缓存
|
||||
*/
|
||||
// logo: {
|
||||
// enable: true,
|
||||
// source: '',
|
||||
// },
|
||||
});
|
||||
|
@@ -4,13 +4,15 @@ import type {
|
||||
RouteRecordStringComponent,
|
||||
} from '@vben/types';
|
||||
|
||||
import type { Menu } from '#/api';
|
||||
|
||||
import { generateAccessible } from '@vben/access';
|
||||
import { preferences } from '@vben/preferences';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
import { getAllMenusApi, type Menu } from '#/api';
|
||||
import { getAllMenusApi } from '#/api';
|
||||
import { BasicLayout, IFrameView } from '#/layouts';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
@@ -207,7 +209,7 @@ async function generateAccess(options: GenerateMenuAndRoutesOptions) {
|
||||
const vbenMenuList = backMenuToVbenMenu(backMenuList);
|
||||
// 特别注意 这里要深拷贝
|
||||
const menuList = [...cloneDeep(localMenuList), ...vbenMenuList];
|
||||
console.log('menuList', menuList);
|
||||
// console.log('menuList', menuList);
|
||||
return menuList;
|
||||
},
|
||||
// 可以指定没有权限跳转403页面
|
||||
|
@@ -54,7 +54,9 @@ function setupAccessGuard(router: Router) {
|
||||
if (coreRouteNames.includes(to.name as string)) {
|
||||
if (to.path === LOGIN_PATH && accessStore.accessToken) {
|
||||
return decodeURIComponent(
|
||||
(to.query?.redirect as string) || DEFAULT_HOME_PATH,
|
||||
(to.query?.redirect as string) ||
|
||||
userStore.userInfo?.homePath ||
|
||||
DEFAULT_HOME_PATH,
|
||||
);
|
||||
}
|
||||
return true;
|
||||
@@ -72,7 +74,10 @@ function setupAccessGuard(router: Router) {
|
||||
return {
|
||||
path: LOGIN_PATH,
|
||||
// 如不需要,直接删除 query
|
||||
query: { redirect: encodeURIComponent(to.fullPath) },
|
||||
query:
|
||||
to.fullPath === DEFAULT_HOME_PATH
|
||||
? {}
|
||||
: { redirect: encodeURIComponent(to.fullPath) },
|
||||
// 携带当前跳转的页面,登录后重新跳转该页面
|
||||
replace: true,
|
||||
};
|
||||
@@ -102,7 +107,10 @@ function setupAccessGuard(router: Router) {
|
||||
accessStore.setAccessMenus(accessibleMenus);
|
||||
accessStore.setAccessRoutes(accessibleRoutes);
|
||||
accessStore.setIsAccessChecked(true);
|
||||
const redirectPath = (from.query.redirect ?? to.fullPath) as string;
|
||||
const redirectPath = (from.query.redirect ??
|
||||
(to.path === DEFAULT_HOME_PATH
|
||||
? userInfo.homePath || DEFAULT_HOME_PATH
|
||||
: to.fullPath)) as string;
|
||||
|
||||
return {
|
||||
...router.resolve(decodeURIComponent(redirectPath)),
|
||||
|
@@ -2,7 +2,7 @@ import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import { DEFAULT_HOME_PATH, LOGIN_PATH } from '@vben/constants';
|
||||
|
||||
import { AuthPageLayout } from '#/layouts';
|
||||
import { AuthPageLayout, BasicLayout } from '#/layouts';
|
||||
import { $t } from '#/locales';
|
||||
import Login from '#/views/_core/authentication/login.vue';
|
||||
|
||||
@@ -21,13 +21,21 @@ const fallbackNotFoundRoute: RouteRecordRaw = {
|
||||
|
||||
/** 基本路由,这些路由是必须存在的 */
|
||||
const coreRoutes: RouteRecordRaw[] = [
|
||||
/**
|
||||
* 根路由
|
||||
* 使用基础布局,作为所有页面的父级容器,子级就不必配置BasicLayout。
|
||||
* 此路由必须存在,且不应修改
|
||||
*/
|
||||
{
|
||||
component: BasicLayout,
|
||||
meta: {
|
||||
hideInBreadcrumb: true,
|
||||
title: 'Root',
|
||||
},
|
||||
name: 'Root',
|
||||
path: '/',
|
||||
redirect: DEFAULT_HOME_PATH,
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
component: () => import('#/views/_core/social-callback/index.vue'),
|
||||
|
@@ -3,6 +3,7 @@ import type { RouteRecordRaw } from 'vue-router';
|
||||
import { mergeRouteModules, traverseTreeValues } from '@vben/utils';
|
||||
|
||||
import { coreRoutes, fallbackNotFoundRoute } from './core';
|
||||
import { workflowIframeRoutes } from './workflow-iframe';
|
||||
|
||||
const dynamicRouteFiles = import.meta.glob('./modules/**/*.ts', {
|
||||
eager: true,
|
||||
@@ -17,20 +18,24 @@ const dynamicRoutes: RouteRecordRaw[] = mergeRouteModules(dynamicRouteFiles);
|
||||
|
||||
/** 外部路由列表,访问这些页面可以不需要Layout,可能用于内嵌在别的系统(不会显示在菜单中) */
|
||||
// const externalRoutes: RouteRecordRaw[] = mergeRouteModules(externalRouteFiles);
|
||||
/** 不需要权限的菜单列表(会显示在菜单中) */
|
||||
// const staticRoutes: RouteRecordRaw[] = mergeRouteModules(staticRouteFiles);
|
||||
const staticRoutes: RouteRecordRaw[] = [];
|
||||
const externalRoutes: RouteRecordRaw[] = [];
|
||||
|
||||
/** 路由列表,由基本路由+静态路由组成 */
|
||||
/** 路由列表,由基本路由、外部路由和404兜底路由组成
|
||||
* 无需走权限验证(会一直显示在菜单中) */
|
||||
const routes: RouteRecordRaw[] = [
|
||||
...coreRoutes,
|
||||
...externalRoutes,
|
||||
...workflowIframeRoutes,
|
||||
fallbackNotFoundRoute,
|
||||
];
|
||||
|
||||
/** 基本路由(登录, 第三方登录, 注册等) + workflowIframe路由不需要拦截 */
|
||||
const basicRoutes = [...coreRoutes, ...workflowIframeRoutes];
|
||||
/** 基本路由列表,这些路由不需要进入权限拦截 */
|
||||
const coreRouteNames = traverseTreeValues(coreRoutes, (route) => route.name);
|
||||
const coreRouteNames = traverseTreeValues(basicRoutes, (route) => route.name);
|
||||
|
||||
/** 有权限校验的路由列表,包含动态路由和静态路由 */
|
||||
const accessRoutes = [...dynamicRoutes, ...staticRoutes];
|
||||
export { accessRoutes, coreRouteNames, routes };
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user