Compare commits
620 Commits
v5.1.2-v2.
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9cae1bb675 | ||
|
|
6b9802dfe1 | ||
|
|
419e7bde9a | ||
|
|
3c7691a6b7 | ||
|
|
32ee077f1a | ||
|
|
6b8600a989 | ||
|
|
3724baa93a | ||
|
|
1e5f89817e | ||
|
|
c28a224d78 | ||
|
|
3008a8d7b0 | ||
|
|
56bb05d547 | ||
|
|
b4282f1423 | ||
|
|
f9c3958d5d | ||
|
|
0e210b90a2 | ||
|
|
6a17a0735d | ||
|
|
8284a87d36 | ||
|
|
cdad26bba6 | ||
|
|
ab9b1a1367 | ||
|
|
8048d80baa | ||
|
|
f1ef2b1083 | ||
|
|
5e1d44c2af | ||
|
|
55691695c4 | ||
|
|
53e7d03a1c | ||
|
|
9c84bf242c | ||
|
|
b89e9cee7f | ||
|
|
014bedd301 | ||
|
|
ceb6de9044 | ||
|
|
04c6131fb0 | ||
|
|
c9cfefdc3e | ||
|
|
fbe9254114 | ||
|
|
88056a5067 | ||
|
|
3da18c9464 | ||
|
|
b4a40c94dc | ||
|
|
c11b91a48e | ||
|
|
65da8dfa93 | ||
|
|
e10ef50288 | ||
|
|
4c607f6915 | ||
|
|
43b4e74c9c | ||
|
|
f84e95d735 | ||
|
|
dba12f25e2 | ||
|
|
257ececa52 | ||
|
|
7d36621c44 | ||
|
|
a29d03b231 | ||
|
|
ab99104240 | ||
|
|
153758df82 | ||
|
|
5e5fca8f6b | ||
|
|
d23bf73a2e | ||
|
|
ad7058b739 | ||
|
|
0dd5044bbe | ||
|
|
ae5dd09ba2 | ||
|
|
0e20743c28 | ||
|
|
9223fabde7 | ||
|
|
1282839f67 | ||
|
|
952f56ca2e | ||
|
|
e08f41dd72 | ||
|
|
2392f64233 | ||
|
|
1565ec1996 | ||
|
|
d95f358d1b | ||
|
|
7ea5199fd2 | ||
|
|
4280c7177d | ||
|
|
4472b24def | ||
|
|
33cf333b2a | ||
|
|
27a427eb97 | ||
|
|
e2e1ce4091 | ||
|
|
4013c06fea | ||
|
|
8a029f6c4c | ||
|
|
c785a9fb7f | ||
|
|
95cbd2f3af | ||
|
|
edacb79ccb | ||
|
|
0872624adc | ||
|
|
1bf03053e1 | ||
|
|
47c2724058 | ||
|
|
219ab65eb7 | ||
|
|
107b2d444b | ||
|
|
a8bb81c984 | ||
|
|
0ca453d549 | ||
|
|
093c05bda0 | ||
|
|
94dcc28c8a | ||
|
|
35b016b3ba | ||
|
|
f6d69e2bea | ||
|
|
9573343afc | ||
|
|
8f99c76e72 | ||
|
|
62f7d393f3 | ||
|
|
31037db627 | ||
|
|
4e0d946676 | ||
|
|
71dceeacc2 | ||
|
|
d59259737f | ||
|
|
8afe7c3931 | ||
|
|
d59738b473 | ||
|
|
2f35342782 | ||
|
|
720c822bb3 | ||
|
|
48b5d595df | ||
|
|
1034399fe4 | ||
|
|
ba257e2357 | ||
|
|
8bd26758dd | ||
|
|
1f1cd489be | ||
|
|
9c528d9a8c | ||
|
|
0472b823e7 | ||
|
|
f71cf3cfb4 | ||
|
|
8179ee8196 | ||
|
|
8b8099ad09 | ||
|
|
fd30362267 | ||
|
|
1878f49e8d | ||
|
|
ca0fe5ebae | ||
|
|
ba78f8cc0d | ||
|
|
a614dee5c6 | ||
|
|
463faba9b9 | ||
|
|
9dea8369e3 | ||
|
|
592fb84aa7 | ||
|
|
3019701856 | ||
|
|
1ea70dd3ce | ||
|
|
2a5ad70155 | ||
|
|
9c8e3404bb | ||
|
|
385bbb77a9 | ||
|
|
70f7c06e55 | ||
|
|
369f48ced5 | ||
|
|
7f15f0e15a | ||
|
|
7b48bd44a2 | ||
|
|
7affcd27b7 | ||
|
|
7de9f23226 | ||
|
|
afc35feb8c | ||
|
|
84d682a4a2 | ||
|
|
b29c5bd2fa | ||
|
|
e4c24a511a | ||
|
|
9955a52059 | ||
|
|
de22609196 | ||
|
|
e5de3f4e9d | ||
|
|
660d5b3d4f | ||
|
|
d5eac17097 | ||
|
|
6fe2317681 | ||
|
|
e9e8a2eaaf | ||
|
|
5ec984ac7d | ||
|
|
c98a14e2ac | ||
|
|
bbc656a26c | ||
|
|
722acf0ae7 | ||
|
|
15acd995f9 | ||
|
|
35b90aa746 | ||
|
|
de926211ef | ||
|
|
30e1ea1c6d | ||
|
|
8fa765f7be | ||
|
|
7321b4c4ca | ||
|
|
85608594bc | ||
|
|
fc8b795bd9 | ||
|
|
3a7fad80a8 | ||
|
|
f5d557fe80 | ||
|
|
8bff98ac72 | ||
|
|
8fadab9741 | ||
|
|
fba121f5c3 | ||
|
|
48e9f2c5c0 | ||
|
|
d8a395bfd1 | ||
|
|
597c8370d3 | ||
|
|
6b2838141a | ||
|
|
b3edce5a20 | ||
|
|
53424765f9 | ||
|
|
b23b123613 | ||
|
|
3eab423da5 | ||
|
|
2e627832ae | ||
|
|
8b5cf9a35f | ||
|
|
78798f99ea | ||
|
|
57f288c892 | ||
|
|
0815fa2978 | ||
|
|
7feead9afc | ||
|
|
98728828ad | ||
|
|
d9218fac24 | ||
|
|
6d290785ef | ||
|
|
f6400f3cbe | ||
|
|
9d7f3101b9 | ||
|
|
7784709cae | ||
|
|
b5a4ebe2f6 | ||
|
|
b814fb5105 | ||
|
|
e98d43f50d | ||
|
|
71b9d5d468 | ||
|
|
e341b45429 | ||
|
|
b2219cabec | ||
|
|
abc6e4f454 | ||
|
|
b1b63ebf7f | ||
|
|
acc760a20f | ||
|
|
62e3351bc7 | ||
|
|
4fe828faa4 | ||
|
|
21466ca8a1 | ||
|
|
21234379b3 | ||
|
|
44bf7e7212 | ||
|
|
e91d11876f | ||
|
|
a90f38734f | ||
|
|
761f97e143 | ||
|
|
bfcffc50e2 | ||
|
|
07b8bc65ec | ||
|
|
f241c187b3 | ||
|
|
ef535a3f33 | ||
|
|
a01545bc84 | ||
|
|
bdaddb4bf6 | ||
|
|
ace672dd0c | ||
|
|
0539fa3c1f | ||
|
|
18f89055e1 | ||
|
|
7a8620c994 | ||
|
|
e38d286c11 | ||
|
|
05e7e93cf1 | ||
|
|
f2adc5e5fa | ||
|
|
7be0723c31 | ||
|
|
eab4345198 | ||
|
|
e040820dae | ||
|
|
61b81f4692 | ||
|
|
aef5a02097 | ||
|
|
cc38d23d14 | ||
|
|
74c29dc13e | ||
|
|
b474a1cffc | ||
|
|
e3219c434a | ||
|
|
c28fbdfb27 | ||
|
|
ef19e97109 | ||
|
|
bb90dbc35b | ||
|
|
770861ed33 | ||
|
|
edf9529a10 | ||
|
|
a11679dc0d | ||
|
|
4d6a1ee73b | ||
|
|
b43158914b | ||
|
|
66580a05a8 | ||
|
|
dccfa219d7 | ||
|
|
b1f0b3c096 | ||
|
|
b95a49c7d7 | ||
|
|
2155d9f4b0 | ||
|
|
fb7bca27eb | ||
|
|
904ee32b24 | ||
|
|
4839a5152f | ||
|
|
c454efd713 | ||
|
|
e8bbe0ac15 | ||
|
|
b2a4157285 | ||
|
|
5682b5f9c2 | ||
|
|
a4eab94188 | ||
|
|
8994e3ad3e | ||
|
|
060f99dee5 | ||
|
|
aef170f374 | ||
|
|
bfc6e7b8da | ||
|
|
5e440a7dc4 | ||
|
|
3280baff16 | ||
|
|
bbe94610a2 | ||
|
|
4c12943e3c | ||
|
|
25189f3052 | ||
|
|
11e2695465 | ||
|
|
0d493f1c74 | ||
|
|
8d398d2940 | ||
|
|
801700044c | ||
|
|
3c2409169b | ||
|
|
490eecccc5 | ||
|
|
ecf2337205 | ||
|
|
9cc8195237 | ||
|
|
522087e18f | ||
|
|
b7e96dec38 | ||
|
|
71528c78ac | ||
|
|
2fc220f9d3 | ||
|
|
e0eed9c89a | ||
|
|
e423d8afcf | ||
|
|
100f44e197 | ||
|
|
b39e0ad6db | ||
|
|
2b1f5f2c70 | ||
|
|
e521af0eae | ||
|
|
b600fb34b5 | ||
|
|
e29beed8bd | ||
|
|
7772c7c7d6 | ||
|
|
ed82954643 | ||
|
|
1c28058141 | ||
|
|
f5410dfff4 | ||
|
|
1f93137b80 | ||
|
|
8a034662c7 | ||
|
|
7a9ccedadc | ||
|
|
84888c3c28 | ||
|
|
cc4186b578 | ||
|
|
fd01b5a61f | ||
|
|
00f7ab34ef | ||
|
|
b61865f45f | ||
|
|
4075b838fc | ||
|
|
116a214ce6 | ||
|
|
484db3c09e | ||
|
|
150f1d793c | ||
|
|
334ee0d40d | ||
|
|
76f4824d2b | ||
|
|
bf43f8aa3c | ||
|
|
0874e32597 | ||
|
|
23f372dad7 | ||
|
|
5ca894369a | ||
|
|
786f25bfd4 | ||
|
|
06a8d871f7 | ||
|
|
42b6668fd0 | ||
|
|
6ef9b9a741 | ||
|
|
98a8e96feb | ||
|
|
7c1525fd21 | ||
|
|
eb406fbb26 | ||
|
|
845b848b60 | ||
|
|
f3425493ed | ||
|
|
f203716f31 | ||
|
|
3b710d9e99 | ||
|
|
3960c31975 | ||
|
|
e3b5d7ca54 | ||
|
|
a06db1a6f9 | ||
|
|
247bf826d8 | ||
|
|
6eeb711d2f | ||
|
|
525be3cef0 | ||
|
|
a5931a78f6 | ||
|
|
971b4f60ec | ||
|
|
51e4c44fd0 | ||
|
|
a150c8c9a7 | ||
|
|
9dd7bf8990 | ||
|
|
24f1e8b4b1 | ||
|
|
22e8a57b31 | ||
|
|
ec0e6a167e | ||
|
|
5f582c09ba | ||
|
|
2ef0cf5bd5 | ||
|
|
cdbbca43c4 | ||
|
|
72ac227ecf | ||
|
|
e2f18565d1 | ||
|
|
9836d6d9bd | ||
|
|
4310e5e049 | ||
|
|
b19c2805e1 | ||
|
|
fdfb556c8e | ||
|
|
9ed3ae3fab | ||
|
|
9a02598c00 | ||
|
|
28a81f2e44 | ||
|
|
6f4891d677 | ||
|
|
a62a3de23c | ||
|
|
1d852da351 | ||
|
|
f0895e9419 | ||
|
|
1644070e97 | ||
|
|
4918552492 | ||
|
|
364dfb969d | ||
|
|
9726df3966 | ||
|
|
483af13741 | ||
|
|
25848ea99e | ||
|
|
9f13c29cd7 | ||
|
|
e1860a30fc | ||
|
|
34b929d352 | ||
|
|
1606dbd76f | ||
|
|
3c2d9100b5 | ||
|
|
2c8873402f | ||
|
|
e5727893bb | ||
|
|
335a1e6019 | ||
|
|
c0ee3182e1 | ||
|
|
9eb7b433b4 | ||
|
|
9193f0b84a | ||
|
|
076fc5deb1 | ||
|
|
58412035ad | ||
|
|
964db2dfce | ||
|
|
d58a75996e | ||
|
|
f5a5aaa255 | ||
|
|
1465a27a6e | ||
|
|
e5e43fe024 | ||
|
|
6c49b47344 | ||
|
|
358f11a0a3 | ||
|
|
6a83ed2aad | ||
|
|
e07c0b2b3a | ||
|
|
adadfcf8cb | ||
|
|
425386f6f4 | ||
|
|
ba24afce52 | ||
|
|
93d3371768 | ||
|
|
3f08ae6ac4 | ||
|
|
cc6284d3ac | ||
|
|
142effd4f2 | ||
|
|
e657a507e3 | ||
|
|
ebaec24213 | ||
|
|
7637582437 | ||
|
|
27d36f7970 | ||
|
|
762ebd4ede | ||
|
|
ad991e248a | ||
|
|
d1b58f0aaa | ||
|
|
1be1eaf7b4 | ||
|
|
fbcc967b14 | ||
|
|
817c65f7e2 | ||
|
|
b6dcc62f9c | ||
|
|
abb4f543b9 | ||
|
|
71f4d36c21 | ||
|
|
d1be442c6e | ||
|
|
9ef7fd3844 | ||
|
|
fec61f9650 | ||
|
|
c206ab5b4a | ||
|
|
fe1942c247 | ||
|
|
1f2add4b6b | ||
|
|
3b254cc16b | ||
|
|
3e7c2f26fa | ||
|
|
4143285ec6 | ||
|
|
72610ab194 | ||
|
|
3c713c9258 | ||
|
|
f95f6e8390 | ||
|
|
2c9c837c1b | ||
|
|
7904496df1 | ||
|
|
15c3d84ca2 | ||
|
|
e8a641f3a3 | ||
|
|
e354db74a7 | ||
|
|
61df97c4b9 | ||
|
|
75ecc4dd68 | ||
|
|
c467d95703 | ||
|
|
3fead37924 | ||
|
|
c8626b6893 | ||
|
|
38df345078 | ||
|
|
212361dc94 | ||
|
|
f237ee145a | ||
|
|
291d4b0040 | ||
|
|
64c56e9dd9 | ||
|
|
57d87df589 | ||
|
|
9de3f25f6a | ||
|
|
d98c2822d5 | ||
|
|
58e6fdef78 | ||
|
|
2c9daae153 | ||
|
|
c2fdbc66b2 | ||
|
|
57fd309907 | ||
|
|
7a335b3438 | ||
|
|
83bbac303e | ||
|
|
eb1b614eb1 | ||
|
|
a63543a5c7 | ||
|
|
3585761b81 | ||
|
|
34290d8cad | ||
|
|
f367b49dd6 | ||
|
|
7df347c1d4 | ||
|
|
ea106e44be | ||
|
|
b2e47f3938 | ||
|
|
b8e0f018f0 | ||
|
|
4ee46819c1 | ||
|
|
0c620ef5b4 | ||
|
|
9b00aaff93 | ||
|
|
b829867a5b | ||
|
|
736225fbfe | ||
|
|
3062c46b49 | ||
|
|
ed66231d19 | ||
|
|
3b8ecadc78 | ||
|
|
322e88d8e1 | ||
|
|
526ca728e1 | ||
|
|
26c0cdaef6 | ||
|
|
e7dc8e94c7 | ||
|
|
72ca7a1aae | ||
|
|
907dc4efb4 | ||
|
|
935a199f71 | ||
|
|
a19ac2d4ca | ||
|
|
437bc936fd | ||
|
|
0b8f48fb61 | ||
|
|
5d641e5de4 | ||
|
|
0d196e47e2 | ||
|
|
012e70c7e4 | ||
|
|
9243eee3c3 | ||
|
|
c449fb60a5 | ||
|
|
4660aa6e95 | ||
|
|
9968d14c39 | ||
|
|
e2254651ca | ||
|
|
7ed5f33e7b | ||
|
|
175c4a48ce | ||
|
|
9bcc4d631b | ||
|
|
861dc9fb16 | ||
|
|
8633a23d28 | ||
|
|
d67bd493c1 | ||
|
|
2d532d636a | ||
|
|
e2bdae5bf2 | ||
|
|
2df121072b | ||
|
|
7016888d5a | ||
|
|
3c34421bd2 | ||
|
|
187ae83731 | ||
|
|
c4c9905a91 | ||
|
|
219f3a7d6f | ||
|
|
44837e975c | ||
|
|
8c6cf5bc43 | ||
|
|
c21f0d7d77 | ||
|
|
8b6b624f6c | ||
|
|
03c736af82 | ||
|
|
7386e69033 | ||
|
|
2f60c96fbf | ||
|
|
45e1c46ce4 | ||
|
|
d7d3105c5c | ||
|
|
4aa42f5c20 | ||
|
|
c089f2497c | ||
|
|
e18dcbd86b | ||
|
|
a541d61c0c | ||
|
|
4b19f55bce | ||
|
|
b5042d13d2 | ||
|
|
b444f2b8e2 | ||
|
|
6b982984ac | ||
|
|
30f271359a | ||
|
|
9967a72e24 | ||
|
|
efeff40f2e | ||
|
|
2e299b58e2 | ||
|
|
1d7c3da86b | ||
|
|
5057976ad2 | ||
|
|
41f3d37db1 | ||
|
|
1fbc5afe75 | ||
|
|
b0541be0b8 | ||
|
|
d6803783ae | ||
|
|
945eec5418 | ||
|
|
73db6deec3 | ||
|
|
40a75dae31 | ||
|
|
64fb2899c9 | ||
|
|
9da3f1f2d7 | ||
|
|
70120a8f2f | ||
|
|
6b4d756a9d | ||
|
|
905426ad45 | ||
|
|
b4b0eba228 | ||
|
|
9f73ea2d32 | ||
|
|
2d804c7ce0 | ||
|
|
11e8e9d19a | ||
|
|
3e44c14286 | ||
|
|
04f1347ddd | ||
|
|
0442bd638d | ||
|
|
aeac77b90f | ||
|
|
b117a6f198 | ||
|
|
e1b03258e8 | ||
|
|
c71eb21416 | ||
|
|
c922ea3b9c | ||
|
|
d76f2b857d | ||
|
|
eec050a2dd | ||
|
|
b0087d1a50 | ||
|
|
4752b4eb01 | ||
|
|
2ea1807f34 | ||
|
|
5dfc25a493 | ||
|
|
c5b3c121c9 | ||
|
|
c417c6debe | ||
|
|
f6727d80b8 | ||
|
|
ceb6243cd0 | ||
|
|
cd129aa949 | ||
|
|
d186c800f0 | ||
|
|
31f4219f3d | ||
|
|
ad8565d29a | ||
|
|
66a31b620b | ||
|
|
e26f6423aa | ||
|
|
12461f4106 | ||
|
|
58422616e2 | ||
|
|
f6abd4cf59 | ||
|
|
f50ed6b2c5 | ||
|
|
3dba22c47c | ||
|
|
e1289d0abb | ||
|
|
9ea8b710ff | ||
|
|
e889348562 | ||
|
|
02daeead57 | ||
|
|
4253b2afbb | ||
|
|
0c39b593ce | ||
|
|
8761392c85 | ||
|
|
30cf3a2caa | ||
|
|
12458177be | ||
|
|
b6415c21be | ||
|
|
bc4358f4eb | ||
|
|
5dcfee4c07 | ||
|
|
a9cd5684b2 | ||
|
|
1c0e614d7d | ||
|
|
00a7a8ce23 | ||
|
|
ade0b3e29e | ||
|
|
3a3f873834 | ||
|
|
1edc953765 | ||
|
|
1383f5a7eb | ||
|
|
e4f18452c7 | ||
|
|
fdf64a6f08 | ||
|
|
f46a333778 | ||
|
|
eb364873d2 | ||
|
|
7666f724df | ||
|
|
f9b8922bed | ||
|
|
fb94a4d79c | ||
|
|
71047fdbfd | ||
|
|
2cb9b3ea47 | ||
|
|
3c27c18889 | ||
|
|
9defb9a669 | ||
|
|
8833eb205b | ||
|
|
15129079d4 | ||
|
|
d1c199d4bf | ||
|
|
97b4eef342 | ||
|
|
1dd9983f0f | ||
|
|
b8eb2dcb40 | ||
|
|
3b4f171d9f | ||
|
|
de33d4aaee | ||
|
|
803d44cac0 | ||
|
|
cc293536e2 | ||
|
|
5f6057253f | ||
|
|
d50c90afd1 | ||
|
|
6f9851a4e5 | ||
|
|
e7ff829502 | ||
|
|
0108df1334 | ||
|
|
542f73f0e6 | ||
|
|
5cb40abe18 | ||
|
|
26cfa7afc4 | ||
|
|
df1a84d5ae | ||
|
|
5b991be4c2 | ||
|
|
34c0d2d277 | ||
|
|
b2f00c4b71 | ||
|
|
63ba90cc10 | ||
|
|
3b18a476b7 | ||
|
|
15243ab713 | ||
|
|
b4cc7b3de0 | ||
|
|
fe3874396d | ||
|
|
fdf885c261 | ||
|
|
7b7377fa1b | ||
|
|
0c56f87e06 | ||
|
|
b783b8d991 | ||
|
|
4ea7376dbc | ||
|
|
5910032908 | ||
|
|
024783588e | ||
|
|
acba493c94 | ||
|
|
7073acb3ef | ||
|
|
075c02b8c3 | ||
|
|
31da01bdd1 | ||
|
|
dfdcee9b95 | ||
|
|
d3b630076a | ||
|
|
173c723c7c | ||
|
|
595a521a3c | ||
|
|
d08f14d86d | ||
|
|
b112b793d5 | ||
|
|
6432d165c7 | ||
|
|
e5772cb63c | ||
|
|
bb696b300f | ||
|
|
14f6a214f2 | ||
|
|
bbcedd02ba | ||
|
|
abf2777b2e | ||
|
|
3922c16601 | ||
|
|
321f21c498 | ||
|
|
085ce4cf43 | ||
|
|
c5daf629d9 | ||
|
|
20b7819f11 | ||
|
|
8b36cac09d | ||
|
|
0e99163f0c | ||
|
|
772885de35 | ||
|
|
1d557a24f9 | ||
|
|
597f2b4461 | ||
|
|
70a3348954 | ||
|
|
3204aa8461 | ||
|
|
5d44f9e231 | ||
|
|
1d5133b695 | ||
|
|
031d83828a | ||
|
|
0541af5abb | ||
|
|
1f71044cff | ||
|
|
f32a7105c3 | ||
|
|
b06f6a316b |
@@ -1,5 +1,6 @@
|
||||
# 页面标题
|
||||
VITE_APP_TITLE = RuoYi-Vue-Plus多租户管理系统
|
||||
VITE_APP_LOGO_TITLE = RuoYi-Vue-Plus
|
||||
|
||||
# 开发环境配置
|
||||
VITE_APP_ENV = 'development'
|
||||
@@ -11,13 +12,15 @@ VITE_APP_BASE_API = '/dev-api'
|
||||
VITE_APP_CONTEXT_PATH = '/'
|
||||
|
||||
# 监控地址
|
||||
VITE_APP_MONITRO_ADMIN = 'http://localhost:9090/admin/applications'
|
||||
VITE_APP_MONITOR_ADMIN = 'http://localhost:9090/admin/applications'
|
||||
|
||||
# powerjob 控制台地址
|
||||
VITE_APP_POWERJOB_ADMIN = 'http://localhost:7700/'
|
||||
# SnailJob 控制台地址
|
||||
VITE_APP_SNAILJOB_ADMIN = 'http://localhost:8800/snail-job'
|
||||
|
||||
VITE_APP_PORT = 80
|
||||
|
||||
# 接口加密功能开关(如需关闭 后端也必须对应关闭)
|
||||
VITE_APP_ENCRYPT = true
|
||||
# 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换
|
||||
VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
|
||||
# 接口响应解密 RSA 私钥与后端加密公钥对应 如更换需前后端一同更换
|
||||
@@ -26,5 +29,8 @@ VITE_APP_RSA_PRIVATE_KEY = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3C
|
||||
# 客户端id
|
||||
VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e'
|
||||
|
||||
# websocket 开关(开发环境默认关闭ws 因vite的bug导致如ws无法连接则会崩溃)
|
||||
# websocket 开关 默认使用sse推送
|
||||
VITE_APP_WEBSOCKET = false
|
||||
|
||||
# sse 开关
|
||||
VITE_APP_SSE = true
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# 页面标题
|
||||
VITE_APP_TITLE = RuoYi-Vue-Plus多租户管理系统
|
||||
VITE_APP_LOGO_TITLE = RuoYi-Vue-Plus
|
||||
|
||||
# 生产环境配置
|
||||
VITE_APP_ENV = 'production'
|
||||
@@ -8,10 +9,10 @@ VITE_APP_ENV = 'production'
|
||||
VITE_APP_CONTEXT_PATH = '/'
|
||||
|
||||
# 监控地址
|
||||
VITE_APP_MONITRO_ADMIN = '/admin/applications'
|
||||
VITE_APP_MONITOR_ADMIN = '/admin/applications'
|
||||
|
||||
# powerjob 控制台地址
|
||||
VITE_APP_POWERJOB_ADMIN = '/powerjob'
|
||||
# SnailJob 控制台地址
|
||||
VITE_APP_SNAILJOB_ADMIN = '/snail-job'
|
||||
|
||||
# 生产环境
|
||||
VITE_APP_BASE_API = '/prod-api'
|
||||
@@ -21,6 +22,8 @@ VITE_BUILD_COMPRESS = gzip
|
||||
|
||||
VITE_APP_PORT = 80
|
||||
|
||||
# 接口加密功能开关(如需关闭 后端也必须对应关闭)
|
||||
VITE_APP_ENCRYPT = true
|
||||
# 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换
|
||||
VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
|
||||
# 接口响应解密 RSA 私钥与后端加密公钥对应 如更换需前后端一同更换
|
||||
@@ -29,5 +32,8 @@ VITE_APP_RSA_PRIVATE_KEY = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3C
|
||||
# 客户端id
|
||||
VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e'
|
||||
|
||||
# websocket 开关
|
||||
VITE_APP_WEBSOCKET = true
|
||||
# websocket 开关 默认使用sse推送
|
||||
VITE_APP_WEBSOCKET = false
|
||||
|
||||
# sse 开关
|
||||
VITE_APP_SSE = true
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
*.sh
|
||||
node_modules
|
||||
*.md
|
||||
*.woff
|
||||
*.ttf
|
||||
.vscode
|
||||
.idea
|
||||
dist
|
||||
/public
|
||||
/docs
|
||||
.husky
|
||||
.local
|
||||
/bin
|
||||
.eslintrc.js
|
||||
prettier.config.js
|
||||
src/assets
|
||||
tailwind.config.js
|
||||
@@ -1,46 +1,27 @@
|
||||
{
|
||||
"globals": {
|
||||
"ComponentInternalInstance": true,
|
||||
"TransferKey": true,
|
||||
"ElFormRules": true,
|
||||
"CheckboxValueType": true,
|
||||
"PropType": true,
|
||||
"DateModelType": true,
|
||||
"UploadFile": true,
|
||||
"ElFormInstance": true,
|
||||
"ElTableInstance": true,
|
||||
"ElTreeInstance": true,
|
||||
"ElTreeSelectInstance": true,
|
||||
"ElSelectInstance": true,
|
||||
"ElUploadInstance": true,
|
||||
"ElCardInstance": true,
|
||||
"ElDialogInstance": true,
|
||||
"ElInputInstance": true,
|
||||
"ElInputNumberInstance": true,
|
||||
"ElRadioInstance": true,
|
||||
"ElRadioGroupInstance": true,
|
||||
"ElRadioButtonInstance": true,
|
||||
"ElCheckboxInstance": true,
|
||||
"ElCheckboxGroupInstance": true,
|
||||
"ElSwitchInstance": true,
|
||||
"ElDatePickerInstance": true,
|
||||
"ElTimePickerInstance": true,
|
||||
"ElTimeSelectInstance": true,
|
||||
"ElScrollbarInstance": true,
|
||||
"ElCascaderInstance": true,
|
||||
"ElColorPickerInstance": true,
|
||||
"ElRateInstance": true,
|
||||
"ElSliderInstance": true,
|
||||
"useRouter": true,
|
||||
"useRoute": true,
|
||||
"Component": true,
|
||||
"ComponentPublicInstance": true,
|
||||
"ComputedRef": true,
|
||||
"DirectiveBinding": true,
|
||||
"EffectScope": true,
|
||||
"ElTable": true,
|
||||
"ElSelect": true,
|
||||
"ElUpload": true,
|
||||
"ElForm": true,
|
||||
"ElTree": true,
|
||||
"ElLoading": true,
|
||||
"ElMessage": true,
|
||||
"ElMessageBox": true,
|
||||
"ElNotification": true,
|
||||
"ExtractDefaultPropTypes": true,
|
||||
"ExtractPropTypes": true,
|
||||
"ExtractPublicPropTypes": true,
|
||||
"InjectionKey": true,
|
||||
"MaybeRef": true,
|
||||
"MaybeRefOrGetter": true,
|
||||
"PropType": true,
|
||||
"Ref": true,
|
||||
"Slot": true,
|
||||
"Slots": true,
|
||||
"VNode": true,
|
||||
"WritableComputedRef": true,
|
||||
"acceptHMRUpdate": true,
|
||||
"asyncComputed": true,
|
||||
"autoResetRef": true,
|
||||
"computed": true,
|
||||
@@ -54,36 +35,51 @@
|
||||
"createEventHook": true,
|
||||
"createGlobalState": true,
|
||||
"createInjectionState": true,
|
||||
"createPinia": true,
|
||||
"createReactiveFn": true,
|
||||
"createRef": true,
|
||||
"createReusableTemplate": true,
|
||||
"createSharedComposable": true,
|
||||
"createTemplatePromise": true,
|
||||
"createUnrefFn": true,
|
||||
"customRef": true,
|
||||
"debouncedRef": true,
|
||||
"debouncedWatch": true,
|
||||
"defineAsyncComponent": true,
|
||||
"defineComponent": true,
|
||||
"defineStore": true,
|
||||
"eagerComputed": true,
|
||||
"effectScope": true,
|
||||
"extendRef": true,
|
||||
"getActivePinia": true,
|
||||
"getCurrentInstance": true,
|
||||
"getCurrentScope": true,
|
||||
"h": true,
|
||||
"ignorableWatch": true,
|
||||
"inject": true,
|
||||
"injectLocal": true,
|
||||
"isDefined": true,
|
||||
"isProxy": true,
|
||||
"isReactive": true,
|
||||
"isReadonly": true,
|
||||
"isRef": true,
|
||||
"makeDestructurable": true,
|
||||
"mapActions": true,
|
||||
"mapGetters": true,
|
||||
"mapState": true,
|
||||
"mapStores": true,
|
||||
"mapWritableState": true,
|
||||
"markRaw": true,
|
||||
"nextTick": true,
|
||||
"onActivated": true,
|
||||
"onBeforeMount": true,
|
||||
"onBeforeRouteLeave": true,
|
||||
"onBeforeRouteUpdate": true,
|
||||
"onBeforeUnmount": true,
|
||||
"onBeforeUpdate": true,
|
||||
"onClickOutside": true,
|
||||
"onDeactivated": true,
|
||||
"onElementRemoval": true,
|
||||
"onErrorCaptured": true,
|
||||
"onKeyStroke": true,
|
||||
"onLongPress": true,
|
||||
@@ -95,8 +91,10 @@
|
||||
"onStartTyping": true,
|
||||
"onUnmounted": true,
|
||||
"onUpdated": true,
|
||||
"onWatcherCleanup": true,
|
||||
"pausableWatch": true,
|
||||
"provide": true,
|
||||
"provideLocal": true,
|
||||
"reactify": true,
|
||||
"reactifyObject": true,
|
||||
"reactive": true,
|
||||
@@ -111,12 +109,14 @@
|
||||
"refThrottled": true,
|
||||
"refWithControl": true,
|
||||
"resolveComponent": true,
|
||||
"resolveDirective": true,
|
||||
"resolveRef": true,
|
||||
"resolveUnref": true,
|
||||
"setActivePinia": true,
|
||||
"setMapStoreSuffix": true,
|
||||
"shallowReactive": true,
|
||||
"shallowReadonly": true,
|
||||
"shallowRef": true,
|
||||
"storeToRefs": true,
|
||||
"syncRef": true,
|
||||
"syncRefs": true,
|
||||
"templateRef": true,
|
||||
@@ -126,6 +126,7 @@
|
||||
"toReactive": true,
|
||||
"toRef": true,
|
||||
"toRefs": true,
|
||||
"toValue": true,
|
||||
"triggerRef": true,
|
||||
"tryOnBeforeMount": true,
|
||||
"tryOnBeforeUnmount": true,
|
||||
@@ -136,11 +137,14 @@
|
||||
"unrefElement": true,
|
||||
"until": true,
|
||||
"useActiveElement": true,
|
||||
"useAnimate": true,
|
||||
"useArrayDifference": true,
|
||||
"useArrayEvery": true,
|
||||
"useArrayFilter": true,
|
||||
"useArrayFind": true,
|
||||
"useArrayFindIndex": true,
|
||||
"useArrayFindLast": true,
|
||||
"useArrayIncludes": true,
|
||||
"useArrayJoin": true,
|
||||
"useArrayMap": true,
|
||||
"useArrayReduce": true,
|
||||
@@ -157,9 +161,11 @@
|
||||
"useBrowserLocation": true,
|
||||
"useCached": true,
|
||||
"useClipboard": true,
|
||||
"useClipboardItems": true,
|
||||
"useCloned": true,
|
||||
"useColorMode": true,
|
||||
"useConfirmDialog": true,
|
||||
"useCountdown": true,
|
||||
"useCounter": true,
|
||||
"useCssModule": true,
|
||||
"useCssVar": true,
|
||||
@@ -198,6 +204,7 @@
|
||||
"useFullscreen": true,
|
||||
"useGamepad": true,
|
||||
"useGeolocation": true,
|
||||
"useId": true,
|
||||
"useIdle": true,
|
||||
"useImage": true,
|
||||
"useInfiniteScroll": true,
|
||||
@@ -206,6 +213,7 @@
|
||||
"useIntervalFn": true,
|
||||
"useKeyModifier": true,
|
||||
"useLastChanged": true,
|
||||
"useLink": true,
|
||||
"useLocalStorage": true,
|
||||
"useMagicKeys": true,
|
||||
"useManualRefHistory": true,
|
||||
@@ -213,6 +221,7 @@
|
||||
"useMediaQuery": true,
|
||||
"useMemoize": true,
|
||||
"useMemory": true,
|
||||
"useModel": true,
|
||||
"useMounted": true,
|
||||
"useMouse": true,
|
||||
"useMouseInElement": true,
|
||||
@@ -226,6 +235,8 @@
|
||||
"useOnline": true,
|
||||
"usePageLeave": true,
|
||||
"useParallax": true,
|
||||
"useParentElement": true,
|
||||
"usePerformanceObserver": true,
|
||||
"usePermission": true,
|
||||
"usePointer": true,
|
||||
"usePointerLock": true,
|
||||
@@ -235,10 +246,14 @@
|
||||
"usePreferredDark": true,
|
||||
"usePreferredLanguages": true,
|
||||
"usePreferredReducedMotion": true,
|
||||
"usePreferredReducedTransparency": true,
|
||||
"usePrevious": true,
|
||||
"useRafFn": true,
|
||||
"useRefHistory": true,
|
||||
"useResizeObserver": true,
|
||||
"useRoute": true,
|
||||
"useRouter": true,
|
||||
"useSSRWidth": true,
|
||||
"useScreenOrientation": true,
|
||||
"useScreenSafeArea": true,
|
||||
"useScriptTag": true,
|
||||
@@ -256,6 +271,7 @@
|
||||
"useStyleTag": true,
|
||||
"useSupported": true,
|
||||
"useSwipe": true,
|
||||
"useTemplateRef": true,
|
||||
"useTemplateRefsList": true,
|
||||
"useTextDirection": true,
|
||||
"useTextSelection": true,
|
||||
@@ -264,6 +280,7 @@
|
||||
"useThrottleFn": true,
|
||||
"useThrottledRefHistory": true,
|
||||
"useTimeAgo": true,
|
||||
"useTimeAgoIntl": true,
|
||||
"useTimeout": true,
|
||||
"useTimeoutFn": true,
|
||||
"useTimeoutPoll": true,
|
||||
@@ -291,8 +308,10 @@
|
||||
"watchArray": true,
|
||||
"watchAtMost": true,
|
||||
"watchDebounced": true,
|
||||
"watchDeep": true,
|
||||
"watchEffect": true,
|
||||
"watchIgnorable": true,
|
||||
"watchImmediate": true,
|
||||
"watchOnce": true,
|
||||
"watchPausable": true,
|
||||
"watchPostEffect": true,
|
||||
@@ -300,13 +319,6 @@
|
||||
"watchThrottled": true,
|
||||
"watchTriggerable": true,
|
||||
"watchWithFilter": true,
|
||||
"whenever": true,
|
||||
"ImportOption": true,
|
||||
"TreeType": true,
|
||||
"FieldOption": true,
|
||||
"PageData": true,
|
||||
"storeToRefs": true,
|
||||
"DictDataOption": true,
|
||||
"UploadOption": true
|
||||
"whenever": true
|
||||
}
|
||||
}
|
||||
|
||||
42
.eslintrc.js
@@ -1,42 +0,0 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
es2021: true,
|
||||
node: true
|
||||
},
|
||||
parser: 'vue-eslint-parser',
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:vue/vue3-essential',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'./.eslintrc-auto-import.json',
|
||||
'plugin:prettier/recommended'
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: '2020',
|
||||
sourceType: 'module',
|
||||
parser: '@typescript-eslint/parser'
|
||||
},
|
||||
plugins: ['vue', '@typescript-eslint'],
|
||||
rules: {
|
||||
'vue/multi-word-component-names': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'vue/no-v-model-argument': 'off',
|
||||
'@typescript-eslint/ban-types': [
|
||||
'error',
|
||||
{
|
||||
// 关闭空类型检查 {}
|
||||
extendDefaults: true,
|
||||
types: {
|
||||
'{}': false,
|
||||
Function: false
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
globals: {
|
||||
DialogOption: 'readonly',
|
||||
OptionType: 'readonly'
|
||||
}
|
||||
};
|
||||
1
.gitignore
vendored
@@ -22,6 +22,7 @@ selenium-debug.log
|
||||
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
pnpm-lock.yaml
|
||||
|
||||
# 编译生成的文件
|
||||
auto-imports.d.ts
|
||||
|
||||
20
.prettierrc
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"printWidth": 150,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"quoteProps": "preserve",
|
||||
"jsxSingleQuote": false,
|
||||
"bracketSameLine": false,
|
||||
"trailingComma": "none",
|
||||
"bracketSpacing": true,
|
||||
"embeddedLanguageFormatting": "auto",
|
||||
"arrowParens": "always",
|
||||
"requirePragma": false,
|
||||
"insertPragma": false,
|
||||
"proseWrap": "preserve",
|
||||
"htmlWhitespaceSensitivity": "css",
|
||||
"vueIndentScriptAndStyle": false,
|
||||
"endOfLine": "auto"
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/**
|
||||
* 代码格式化配置
|
||||
*/
|
||||
module.exports = {
|
||||
// 一行最多多少个字符
|
||||
printWidth: 150,
|
||||
// 指定每个缩进级别的空格数
|
||||
tabWidth: 2,
|
||||
// 使用制表符而不是空格缩进行
|
||||
useTabs: false,
|
||||
// 在语句末尾是否需要分号
|
||||
semi: true,
|
||||
// 是否使用单引号
|
||||
singleQuote: true,
|
||||
// 更改引用对象属性的时间 可选值"<as-needed|consistent|preserve>"
|
||||
quoteProps: 'as-needed',
|
||||
// 在JSX中使用单引号而不是双引号
|
||||
jsxSingleQuote: false,
|
||||
// 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"<none|es5|all>",默认none
|
||||
trailingComma: 'none',
|
||||
// 在对象文字中的括号之间打印空格
|
||||
bracketSpacing: true,
|
||||
// jsx 标签的反尖括号需要换行
|
||||
jsxBracketSameLine: false,
|
||||
embeddedLanguageFormatting: 'off',
|
||||
// 在单独的箭头函数参数周围包括括号 always:(x) => x \ avoid:x => x
|
||||
arrowParens: 'always',
|
||||
// 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码
|
||||
rangeStart: 0,
|
||||
rangeEnd: Infinity,
|
||||
// 指定要使用的解析器,不需要写文件开头的 @prettier
|
||||
requirePragma: false,
|
||||
// 不需要自动在文件开头插入 @prettier
|
||||
insertPragma: false,
|
||||
// 使用默认的折行标准 always\never\preserve
|
||||
proseWrap: 'preserve',
|
||||
// 指定HTML文件的全局空格敏感度 css\strict\ignore
|
||||
htmlWhitespaceSensitivity: 'css',
|
||||
// Vue文件脚本和样式标签缩进
|
||||
vueIndentScriptAndStyle: false,
|
||||
// 在 windows 操作系统中换行符通常是回车 (CR) 加换行分隔符 (LF),也就是回车换行(CRLF),
|
||||
// 然而在 Linux 和 Unix 中只使用简单的换行分隔符 (LF)。
|
||||
// 对应的控制字符为 "\n" (LF) 和 "\r\n"(CRLF)。auto意为保持现有的行尾
|
||||
// 换行符使用 lf 结尾是 可选值"<auto|lf|crlf|cr>"
|
||||
endOfLine: 'auto'
|
||||
};
|
||||
114
README.md
@@ -1,23 +1,31 @@
|
||||
## 平台简介
|
||||
|
||||
* 本仓库为前端技术栈 [Vue3](https://v3.cn.vuejs.org) + [TS](https://www.typescriptlang.org/) + [Element Plus](https://element-plus.org/zh-CN) + [Vite](https://cn.vitejs.dev) 版本。
|
||||
* 配套后端代码仓库地址
|
||||
* [RuoYi-Vue-Plus 5.X(注意版本号)](https://gitee.com/dromara/RuoYi-Vue-Plus)
|
||||
* [RuoYi-Cloud-Plus 2.X(注意版本号)](https://gitee.com/dromara/RuoYi-Cloud-Plus)
|
||||
- 本仓库为前端技术栈 [Vue3](https://v3.cn.vuejs.org) + [TS](https://www.typescriptlang.org/) + [Element Plus](https://element-plus.org/zh-CN) + [Vite](https://cn.vitejs.dev) 版本。
|
||||
- 成员项目: 基于 vben5(ant-design-vue) 的前端项目 [ruoyi-plus-vben5](https://gitee.com/dapppp/ruoyi-plus-vben5)
|
||||
- 成员项目: 基于soybean 的前端项目 [ruoyi-plus-soybean](https://gitee.com/xlsea/ruoyi-plus-soybean)
|
||||
|
||||
## 配套后端代码仓库地址
|
||||
|
||||
| 介绍 | 项目名 | 项目地址 |
|
||||
|------------|:-----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| 🔥 分布式集群框架 | RuoYi-Vue-Plus | - [Gitee](https://gitee.com/dromara/RuoYi-Vue-Plus)<br> - [GitHub](https://github.com/dromara/RuoYi-Vue-Plus)<br> - [GitCode](https://gitcode.com/dromara/RuoYi-Vue-Plus) |
|
||||
| 🔥 微服务框架 | RuoYi-Cloud-Plus | - [Gitee](https://gitee.com/dromara/RuoYi-Cloud-Plus)<br>- [GitHub](https://github.com/dromara/RuoYi-Cloud-Plus)<br> - [GitCode](https://gitcode.com/dromara/RuoYi-Cloud-Plus) |
|
||||
|
||||
## 分支说明
|
||||
|
||||
- ts分支(稳定发布主分支 生产可用)
|
||||
- dev分支(开发分支 开发过程中使用)
|
||||
|
||||
## 前端运行
|
||||
|
||||
```bash
|
||||
# 克隆项目
|
||||
git clone https://gitee.com/JavaLionLi/plus-ui.git
|
||||
|
||||
# 安装依赖
|
||||
npm install --registry=https://registry.npmmirror.com
|
||||
|
||||
# 启动服务
|
||||
npm run dev
|
||||
|
||||
# 构建生产环境
|
||||
# 构建生产环境
|
||||
npm run build:prod
|
||||
|
||||
# 前端访问地址 http://localhost:80
|
||||
@@ -25,51 +33,51 @@ npm run build:prod
|
||||
|
||||
## 本框架与RuoYi的业务差异
|
||||
|
||||
| 业务 | 功能说明 | 本框架 | RuoYi |
|
||||
|--------|-----------------------------------------|-----|------------------|
|
||||
| 租户管理 | 系统内租户的管理 如:租户套餐、过期时间、用户数量、企业信息等 | 支持 | 无 |
|
||||
| 租户套餐管理 | 系统内租户所能使用的套餐管理 如:套餐内所包含的菜单等 | 支持 | 无 |
|
||||
| 用户管理 | 用户的管理配置 如:新增用户、分配用户所属部门、角色、岗位等 | 支持 | 支持 |
|
||||
| 部门管理 | 配置系统组织机构(公司、部门、小组) 树结构展现支持数据权限 | 支持 | 支持 |
|
||||
| 岗位管理 | 配置系统用户所属担任职务 | 支持 | 支持 |
|
||||
| 菜单管理 | 配置系统菜单、操作权限、按钮权限标识等 | 支持 | 支持 |
|
||||
| 角色管理 | 角色菜单权限分配、设置角色按机构进行数据范围权限划分 | 支持 | 支持 |
|
||||
| 字典管理 | 对系统中经常使用的一些较为固定的数据进行维护 | 支持 | 支持 |
|
||||
| 参数管理 | 对系统动态配置常用参数 | 支持 | 支持 |
|
||||
| 通知公告 | 系统通知公告信息发布维护 | 支持 | 支持 |
|
||||
| 操作日志 | 系统正常操作日志记录和查询 系统异常信息日志记录和查询 | 支持 | 支持 |
|
||||
| 登录日志 | 系统登录日志记录查询包含登录异常 | 支持 | 支持 |
|
||||
| 文件管理 | 系统文件展示、上传、下载、删除等管理 | 支持 | 无 |
|
||||
| 文件配置管理 | 系统文件上传、下载所需要的配置信息动态添加、修改、删除等管理 | 支持 | 无 |
|
||||
| 在线用户管理 | 已登录系统的在线用户信息监控与强制踢出操作 | 支持 | 支持 |
|
||||
| 定时任务 | 运行报表、任务管理(添加、修改、删除)、日志管理、执行器管理等 | 支持 | 仅支持任务与日志管理 |
|
||||
| 代码生成 | 多数据源前后端代码的生成(java、html、xml、sql)支持CRUD下载 | 支持 | 仅支持单数据源 |
|
||||
| 系统接口 | 根据业务代码自动生成相关的api接口文档 | 支持 | 支持 |
|
||||
| 服务监控 | 监视集群系统CPU、内存、磁盘、堆栈、在线日志、Spring相关配置等 | 支持 | 仅支持单机CPU、内存、磁盘监控 |
|
||||
| 缓存监控 | 对系统的缓存信息查询,命令统计等。 | 支持 | 支持 |
|
||||
| 在线构建器 | 拖动表单元素生成相应的HTML代码。 | 支持 | 支持 |
|
||||
| 使用案例 | 系统的一些功能案例 | 支持 | 不支持 |
|
||||
| 业务 | 功能说明 | 本框架 | RuoYi |
|
||||
| ------------ | ------------------------------------------------------------- | ------ | ----------------------------- |
|
||||
| 租户管理 | 系统内租户的管理 如:租户套餐、过期时间、用户数量、企业信息等 | 支持 | 无 |
|
||||
| 租户套餐管理 | 系统内租户所能使用的套餐管理 如:套餐内所包含的菜单等 | 支持 | 无 |
|
||||
| 用户管理 | 用户的管理配置 如:新增用户、分配用户所属部门、角色、岗位等 | 支持 | 支持 |
|
||||
| 部门管理 | 配置系统组织机构(公司、部门、小组) 树结构展现支持数据权限 | 支持 | 支持 |
|
||||
| 岗位管理 | 配置系统用户所属担任职务 | 支持 | 支持 |
|
||||
| 菜单管理 | 配置系统菜单、操作权限、按钮权限标识等 | 支持 | 支持 |
|
||||
| 角色管理 | 角色菜单权限分配、设置角色按机构进行数据范围权限划分 | 支持 | 支持 |
|
||||
| 字典管理 | 对系统中经常使用的一些较为固定的数据进行维护 | 支持 | 支持 |
|
||||
| 参数管理 | 对系统动态配置常用参数 | 支持 | 支持 |
|
||||
| 通知公告 | 系统通知公告信息发布维护 | 支持 | 支持 |
|
||||
| 操作日志 | 系统正常操作日志记录和查询 系统异常信息日志记录和查询 | 支持 | 支持 |
|
||||
| 登录日志 | 系统登录日志记录查询包含登录异常 | 支持 | 支持 |
|
||||
| 文件管理 | 系统文件展示、上传、下载、删除等管理 | 支持 | 无 |
|
||||
| 文件配置管理 | 系统文件上传、下载所需要的配置信息动态添加、修改、删除等管理 | 支持 | 无 |
|
||||
| 在线用户管理 | 已登录系统的在线用户信息监控与强制踢出操作 | 支持 | 支持 |
|
||||
| 定时任务 | 运行报表、任务管理(添加、修改、删除)、日志管理、执行器管理等 | 支持 | 仅支持任务与日志管理 |
|
||||
| 代码生成 | 多数据源前后端代码的生成(java、html、xml、sql)支持CRUD下载 | 支持 | 仅支持单数据源 |
|
||||
| 系统接口 | 根据业务代码自动生成相关的api接口文档 | 支持 | 支持 |
|
||||
| 服务监控 | 监视集群系统CPU、内存、磁盘、堆栈、在线日志、Spring相关配置等 | 支持 | 仅支持单机CPU、内存、磁盘监控 |
|
||||
| 缓存监控 | 对系统的缓存信息查询,命令统计等。 | 支持 | 支持 |
|
||||
| 在线构建器 | 拖动表单元素生成相应的HTML代码。 | 支持 | 支持 |
|
||||
| 使用案例 | 系统的一些功能案例 | 支持 | 不支持 |
|
||||
|
||||
## 演示图例
|
||||
|
||||
| | |
|
||||
|--------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
| | |
|
||||
| ---------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
module.exports = {
|
||||
extends: ['@commitlint/config-conventional'],
|
||||
rules: {
|
||||
'type-enum': [
|
||||
2,
|
||||
'always',
|
||||
[
|
||||
'feat', // 新功能 feature
|
||||
'fix', // 修复 bug
|
||||
'docs', // 文档注释
|
||||
'style', // 代码格式
|
||||
'refactor', // 重构
|
||||
'perf', // 性能优化
|
||||
'test', // 增加测试
|
||||
'chore', // 构建过程或辅助工具的变动
|
||||
'revert', // 回退
|
||||
'build' // 打包
|
||||
]
|
||||
],
|
||||
'subject-case': [0]
|
||||
}
|
||||
};
|
||||
44
eslint.config.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import pluginVue from 'eslint-plugin-vue';
|
||||
import globals from 'globals';
|
||||
import prettier from 'eslint-plugin-prettier';
|
||||
import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript';
|
||||
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting';
|
||||
|
||||
export default defineConfigWithVueTs(
|
||||
{
|
||||
name: 'app/files-to-lint',
|
||||
files: ['**/*.{js,cjs,ts,mts,tsx,vue}']
|
||||
},
|
||||
|
||||
{
|
||||
name: 'app/files-to-ignore',
|
||||
ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**']
|
||||
},
|
||||
{
|
||||
languageOptions: {
|
||||
globals: globals.browser
|
||||
}
|
||||
},
|
||||
pluginVue.configs['flat/essential'],
|
||||
vueTsConfigs.recommended,
|
||||
skipFormatting,
|
||||
{
|
||||
plugins: { prettier },
|
||||
rules: {
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-this-alias': 'off',
|
||||
// vue
|
||||
'vue/multi-word-component-names': 'off',
|
||||
'vue/valid-define-props': 'off',
|
||||
'vue/no-v-model-argument': 'off',
|
||||
'prefer-rest-params': 'off',
|
||||
// prettier
|
||||
'prettier/prettier': 'error',
|
||||
// 允许使用空Object类型 {}
|
||||
'@typescript-eslint/no-empty-object-type': 'off',
|
||||
'@typescript-eslint/no-unused-expressions': 'off'
|
||||
}
|
||||
}
|
||||
);
|
||||
216
html/ie.html
33
index.html
@@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
@@ -6,10 +6,10 @@
|
||||
<meta name="renderer" content="webkit" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<title>RuoYi-Vue-Plus多租户管理系统</title>
|
||||
<title>%VITE_APP_TITLE%</title>
|
||||
<!--[if lt IE 11
|
||||
]><script>
|
||||
window.location.href='/html/ie.html';
|
||||
window.location.href = '/html/ie.html';
|
||||
</script><!
|
||||
[endif]-->
|
||||
<style>
|
||||
@@ -47,7 +47,7 @@
|
||||
margin: -75px 0 0 -75px;
|
||||
border-radius: 50%;
|
||||
border: 3px solid transparent;
|
||||
border-top-color: #FFF;
|
||||
border-top-color: #fff;
|
||||
-webkit-animation: spin 2s linear infinite;
|
||||
-ms-animation: spin 2s linear infinite;
|
||||
-moz-animation: spin 2s linear infinite;
|
||||
@@ -57,7 +57,7 @@
|
||||
}
|
||||
|
||||
#loader:before {
|
||||
content: "";
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
left: 5px;
|
||||
@@ -65,7 +65,7 @@
|
||||
bottom: 5px;
|
||||
border-radius: 50%;
|
||||
border: 3px solid transparent;
|
||||
border-top-color: #FFF;
|
||||
border-top-color: #fff;
|
||||
-webkit-animation: spin 3s linear infinite;
|
||||
-moz-animation: spin 3s linear infinite;
|
||||
-o-animation: spin 3s linear infinite;
|
||||
@@ -74,7 +74,7 @@
|
||||
}
|
||||
|
||||
#loader:after {
|
||||
content: "";
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
left: 15px;
|
||||
@@ -82,7 +82,7 @@
|
||||
bottom: 15px;
|
||||
border-radius: 50%;
|
||||
border: 3px solid transparent;
|
||||
border-top-color: #FFF;
|
||||
border-top-color: #fff;
|
||||
-moz-animation: spin 1.5s linear infinite;
|
||||
-o-animation: spin 1.5s linear infinite;
|
||||
-ms-animation: spin 1.5s linear infinite;
|
||||
@@ -90,7 +90,6 @@
|
||||
animation: spin 1.5s linear infinite;
|
||||
}
|
||||
|
||||
|
||||
@-webkit-keyframes spin {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
@@ -119,13 +118,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#loader-wrapper .loader-section {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 51%;
|
||||
height: 100%;
|
||||
background: #7171C6;
|
||||
background: #7171c6;
|
||||
z-index: 1000;
|
||||
-webkit-transform: translateX(0);
|
||||
-ms-transform: translateX(0);
|
||||
@@ -140,21 +138,20 @@
|
||||
right: 0;
|
||||
}
|
||||
|
||||
|
||||
.loaded #loader-wrapper .loader-section.section-left {
|
||||
-webkit-transform: translateX(-100%);
|
||||
-ms-transform: translateX(-100%);
|
||||
transform: translateX(-100%);
|
||||
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
||||
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
||||
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
}
|
||||
|
||||
.loaded #loader-wrapper .loader-section.section-right {
|
||||
-webkit-transform: translateX(100%);
|
||||
-ms-transform: translateX(100%);
|
||||
transform: translateX(100%);
|
||||
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
||||
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
||||
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
}
|
||||
|
||||
.loaded #loader {
|
||||
@@ -182,7 +179,7 @@
|
||||
|
||||
#loader-wrapper .load_title {
|
||||
font-family: 'Open Sans';
|
||||
color: #FFF;
|
||||
color: #fff;
|
||||
font-size: 19px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
@@ -197,7 +194,7 @@
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
font-size: 13px;
|
||||
color: #FFF;
|
||||
color: #fff;
|
||||
opacity: 0.5;
|
||||
}
|
||||
</style>
|
||||
|
||||
134
package.json
@@ -1,15 +1,18 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package",
|
||||
"name": "ruoyi-vue-plus",
|
||||
"version": "5.1.2",
|
||||
"version": "5.5.3-2.5.3",
|
||||
"description": "RuoYi-Vue-Plus多租户管理系统",
|
||||
"author": "LionLi",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite serve --mode development",
|
||||
"build:prod": "vite build --mode production &&vue-tsc --noEmit",
|
||||
"build:prod": "vite build --mode production",
|
||||
"build:dev": "vite build --mode development",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint src/**/*.{ts,js,vue} --fix",
|
||||
"prepare": "husky install",
|
||||
"lint:eslint": "eslint",
|
||||
"lint:eslint:fix": "eslint --fix",
|
||||
"prettier": "prettier --write ."
|
||||
},
|
||||
"repository": {
|
||||
@@ -17,68 +20,77 @@
|
||||
"url": "https://gitee.com/JavaLionLi/plus-ui.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "2.1.0",
|
||||
"@element-plus/icons-vue": "2.3.2",
|
||||
"@highlightjs/vue-plugin": "2.1.2",
|
||||
"@vueup/vue-quill": "1.2.0",
|
||||
"@vueuse/core": "9.5.0",
|
||||
"@vueuse/core": "13.9.0",
|
||||
"animate.css": "4.1.1",
|
||||
"await-to-js": "^3.0.0",
|
||||
"axios": "^1.3.4",
|
||||
"echarts": "5.4.0",
|
||||
"element-plus": "2.2.27",
|
||||
"await-to-js": "3.0.0",
|
||||
"axios": "1.13.1",
|
||||
"crypto-js": "4.2.0",
|
||||
"echarts": "5.6.0",
|
||||
"element-plus": "2.11.7",
|
||||
"file-saver": "2.0.5",
|
||||
"fuse.js": "6.6.2",
|
||||
"js-cookie": "3.0.1",
|
||||
"jsencrypt": "3.3.1",
|
||||
"crypto-js": "^4.1.1",
|
||||
"highlight.js": "11.11.1",
|
||||
"image-conversion": "2.1.1",
|
||||
"js-cookie": "3.0.5",
|
||||
"jsencrypt": "3.5.4",
|
||||
"nprogress": "0.2.0",
|
||||
"path-browserify": "1.0.1",
|
||||
"path-to-regexp": "6.2.0",
|
||||
"pinia": "2.0.22",
|
||||
"screenfull": "6.0.0",
|
||||
"vform3-builds": "3.0.8",
|
||||
"vue": "3.2.45",
|
||||
"vue-cropper": "1.0.3",
|
||||
"vue-i18n": "9.2.2",
|
||||
"vue-router": "4.1.4",
|
||||
"vue-types": "^5.0.3"
|
||||
"pinia": "3.0.3",
|
||||
"screenfull": "6.0.2",
|
||||
"vue": "3.5.22",
|
||||
"vue-cropper": "1.1.4",
|
||||
"vue-i18n": "11.1.12",
|
||||
"vue-json-pretty": "2.6.0",
|
||||
"vue-router": "4.6.3",
|
||||
"vue-types": "6.0.0",
|
||||
"vxe-table": "4.17.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify/json": "^2.2.40",
|
||||
"@intlify/unplugin-vue-i18n": "0.8.2",
|
||||
"@types/crypto-js": "^4.1.1",
|
||||
"@types/file-saver": "2.0.5",
|
||||
"@types/js-cookie": "3.0.3",
|
||||
"@types/node": "18.14.2",
|
||||
"@types/nprogress": "0.2.0",
|
||||
"@types/path-browserify": "^1.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.56.0",
|
||||
"@typescript-eslint/parser": "5.56.0",
|
||||
"@unocss/preset-attributify": "^0.50.6",
|
||||
"@unocss/preset-icons": "^0.50.6",
|
||||
"@unocss/preset-uno": "^0.50.6",
|
||||
"@vitejs/plugin-vue": "4.0.0",
|
||||
"@vue/compiler-sfc": "3.2.45",
|
||||
"autoprefixer": "10.4.14",
|
||||
"eslint": "8.36.0",
|
||||
"eslint-config-prettier": "8.8.0",
|
||||
"eslint-plugin-prettier": "4.2.1",
|
||||
"eslint-plugin-vue": "9.9.0",
|
||||
"fast-glob": "^3.2.11",
|
||||
"husky": "7.0.4",
|
||||
"postcss": "^8.4.21",
|
||||
"prettier": "2.8.6",
|
||||
"sass": "1.56.1",
|
||||
"typescript": "4.9.5",
|
||||
"unocss": "^0.50.6",
|
||||
"unplugin-auto-import": "0.13.0",
|
||||
"unplugin-icons": "0.15.1",
|
||||
"unplugin-vue-components": "0.23.0",
|
||||
"vite": "4.3.1",
|
||||
"@iconify/json": "^2.2.403",
|
||||
"@types/crypto-js": "4.2.2",
|
||||
"@types/file-saver": "2.0.7",
|
||||
"@types/js-cookie": "3.0.6",
|
||||
"@types/node": "^22.19.0",
|
||||
"@types/nprogress": "0.2.3",
|
||||
"@unocss/preset-attributify": "66.5.4",
|
||||
"@unocss/preset-icons": "66.5.4",
|
||||
"@unocss/preset-uno": "66.5.4",
|
||||
"@vitejs/plugin-vue": "5.2.4",
|
||||
"@vue/compiler-sfc": "3.5.22",
|
||||
"@vue/eslint-config-prettier": "10.2.0",
|
||||
"@vue/eslint-config-typescript": "14.6.0",
|
||||
"autoprefixer": "10.4.21",
|
||||
"eslint": "9.39.1",
|
||||
"eslint-plugin-prettier": "5.5.4",
|
||||
"eslint-plugin-vue": "9.33.0",
|
||||
"globals": "16.5.0",
|
||||
"prettier": "3.6.2",
|
||||
"sass": "1.93.3",
|
||||
"typescript": "~5.9.3",
|
||||
"unocss": "66.5.4",
|
||||
"unplugin-auto-import": "19.3.0",
|
||||
"unplugin-icons": "22.5.0",
|
||||
"unplugin-vue-components": "28.8.0",
|
||||
"unplugin-vue-setup-extend-plus": "1.0.1",
|
||||
"vite": "6.4.1",
|
||||
"vite-plugin-compression": "0.5.1",
|
||||
"vite-plugin-svg-icons": "2.0.1",
|
||||
"unplugin-vue-setup-extend-plus": "0.4.9",
|
||||
"vitest": "^0.29.7",
|
||||
"vue-eslint-parser": "9.1.0",
|
||||
"vue-tsc": "0.35.0"
|
||||
}
|
||||
"vite-plugin-svg-icons-ng": "^1.5.2",
|
||||
"vite-plugin-vue-devtools": "8.0.3",
|
||||
"vitest": "3.2.4",
|
||||
"vue-tsc": "^2.2.12"
|
||||
},
|
||||
"overrides": {
|
||||
"quill": "2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.15.0",
|
||||
"npm": ">=8.19.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"Chrome >= 87",
|
||||
"Edge >= 88",
|
||||
"Safari >= 14",
|
||||
"Firefox >= 78"
|
||||
]
|
||||
}
|
||||
|
||||
15
src/App.vue
@@ -1,21 +1,20 @@
|
||||
<template>
|
||||
<el-config-provider :locale="appStore.locale" :size="size">
|
||||
<el-config-provider :locale="appStore.locale" :size="appStore.size">
|
||||
<router-view />
|
||||
</el-config-provider>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import { handleThemeStyle } from '@/utils/theme'
|
||||
import useAppStore from '@/store/modules/app';
|
||||
import { useSettingsStore } from '@/store/modules/settings';
|
||||
import { handleThemeStyle } from '@/utils/theme';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
|
||||
const appStore = useAppStore();
|
||||
const size = computed(() => appStore.size as any);
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
// 初始化主题样式
|
||||
handleThemeStyle(useSettingsStore().theme)
|
||||
})
|
||||
})
|
||||
handleThemeStyle(useSettingsStore().theme);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -20,7 +20,8 @@ export function login(data: LoginData): AxiosPromise<LoginResult> {
|
||||
url: '/auth/login',
|
||||
headers: {
|
||||
isToken: false,
|
||||
isEncrypt: true
|
||||
isEncrypt: true,
|
||||
repeatSubmit: false
|
||||
},
|
||||
method: 'post',
|
||||
data: params
|
||||
@@ -38,7 +39,8 @@ export function register(data: any) {
|
||||
url: '/auth/register',
|
||||
headers: {
|
||||
isToken: false,
|
||||
isEncrypt: true
|
||||
isEncrypt: true,
|
||||
repeatSubmit: false
|
||||
},
|
||||
method: 'post',
|
||||
data: params
|
||||
@@ -49,6 +51,12 @@ export function register(data: any) {
|
||||
* 注销
|
||||
*/
|
||||
export function logout() {
|
||||
if (import.meta.env.VITE_APP_SSE === 'true') {
|
||||
request({
|
||||
url: '/resource/sse/close',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
return request({
|
||||
url: '/auth/logout',
|
||||
method: 'post'
|
||||
@@ -94,11 +102,11 @@ export function getInfo(): AxiosPromise<UserInfo> {
|
||||
}
|
||||
|
||||
// 获取租户列表
|
||||
export function getTenantList(): AxiosPromise<TenantInfo> {
|
||||
export function getTenantList(isToken: boolean): AxiosPromise<TenantInfo> {
|
||||
return request({
|
||||
url: '/auth/tenant/list',
|
||||
headers: {
|
||||
isToken: false
|
||||
isToken: isToken
|
||||
},
|
||||
method: 'get'
|
||||
});
|
||||
|
||||
@@ -18,3 +18,19 @@ export function forceLogout(tokenId: string) {
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
// 获取当前用户登录在线设备
|
||||
export function getOnline() {
|
||||
return request({
|
||||
url: '/monitor/online',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 删除当前在线设备
|
||||
export function delOnline(tokenId: string) {
|
||||
return request({
|
||||
url: '/monitor/online/myself/' + tokenId,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
@@ -64,12 +64,12 @@ export const delClient = (id: string | number | Array<string | number>) => {
|
||||
|
||||
/**
|
||||
* 状态修改
|
||||
* @param id ID
|
||||
* @param clientId 客户端id
|
||||
* @param status 状态
|
||||
*/
|
||||
export function changeStatus(id: number | string, status: string) {
|
||||
export function changeStatus(clientId: string, status: string) {
|
||||
const data = {
|
||||
id,
|
||||
clientId,
|
||||
status
|
||||
};
|
||||
return request({
|
||||
|
||||
@@ -7,7 +7,7 @@ export interface ClientVO {
|
||||
/**
|
||||
* 客户端id
|
||||
*/
|
||||
clientId: string | number;
|
||||
clientId: string;
|
||||
|
||||
/**
|
||||
* 客户端key
|
||||
|
||||
@@ -20,7 +20,7 @@ export function getConfig(configId: string | number): AxiosPromise<ConfigVO> {
|
||||
}
|
||||
|
||||
// 根据参数键名查询参数值
|
||||
export function getConfigKey(configKey: string): AxiosPromise<String> {
|
||||
export function getConfigKey(configKey: string): AxiosPromise<string> {
|
||||
return request({
|
||||
url: '/system/config/configKey/' + configKey,
|
||||
method: 'get'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { DeptForm, DeptQuery, DeptVO } from './types';
|
||||
import { DeptForm, DeptQuery, DeptTreeVO, DeptVO } from './types';
|
||||
|
||||
// 查询部门列表
|
||||
export const listDept = (query?: DeptQuery) => {
|
||||
@@ -11,6 +11,17 @@ export const listDept = (query?: DeptQuery) => {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 通过deptIds查询部门
|
||||
* @param deptIds
|
||||
*/
|
||||
export const optionSelect = (deptIds: (number | string)[]): AxiosPromise<DeptVO[]> => {
|
||||
return request({
|
||||
url: '/system/dept/optionselect?deptIds=' + deptIds,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
// 查询部门列表(排除节点)
|
||||
export const listDeptExcludeChild = (deptId: string | number): AxiosPromise<DeptVO[]> => {
|
||||
return request({
|
||||
@@ -27,14 +38,6 @@ export const getDept = (deptId: string | number): AxiosPromise<DeptVO> => {
|
||||
});
|
||||
};
|
||||
|
||||
// 查询部门下拉树结构
|
||||
export const treeselect = (): AxiosPromise<DeptVO[]> => {
|
||||
return request({
|
||||
url: '/system/dept/treeselect',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
// 新增部门
|
||||
export const addDept = (data: DeptForm) => {
|
||||
return request({
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
export interface DeptQuery extends PageQuery {
|
||||
deptName?: string;
|
||||
deptCategory?: string;
|
||||
status?: number;
|
||||
}
|
||||
|
||||
@@ -16,6 +17,7 @@ export interface DeptVO extends BaseEntity {
|
||||
children: DeptVO[];
|
||||
deptId: number | string;
|
||||
deptName: string;
|
||||
deptCategory: string;
|
||||
orderNum: number;
|
||||
leader: string;
|
||||
phone: string;
|
||||
@@ -26,6 +28,18 @@ export interface DeptVO extends BaseEntity {
|
||||
menuId: string | number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 部门类型
|
||||
*/
|
||||
export interface DeptTreeVO extends BaseEntity {
|
||||
id: number | string;
|
||||
label: string;
|
||||
parentId: number | string;
|
||||
weight: number;
|
||||
children: DeptTreeVO[];
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 部门表单类型
|
||||
*/
|
||||
@@ -35,6 +49,7 @@ export interface DeptForm {
|
||||
children?: DeptForm[];
|
||||
deptId?: number | string;
|
||||
deptName?: string;
|
||||
deptCategory?: string;
|
||||
orderNum?: number;
|
||||
leader?: string;
|
||||
phone?: string;
|
||||
|
||||
@@ -68,3 +68,11 @@ export const delMenu = (menuId: string | number) => {
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
|
||||
// 级联删除菜单
|
||||
export const cascadeDelMenu = (menuIds: Array<string | number>) => {
|
||||
return request({
|
||||
url: '/system/menu/cascade/' + menuIds,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import request from '@/utils/request';
|
||||
import { PostForm, PostQuery, PostVO } from './types';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { DeptTreeVO } from '../dept/types';
|
||||
|
||||
// 查询岗位列表
|
||||
export function listPost(query: PostQuery): AxiosPromise<PostVO[]> {
|
||||
@@ -19,6 +20,18 @@ export function getPost(postId: string | number): AxiosPromise<PostVO> {
|
||||
});
|
||||
}
|
||||
|
||||
// 获取岗位选择框列表
|
||||
export function optionselect(deptId?: number | string, postIds?: (number | string)[]): AxiosPromise<PostVO[]> {
|
||||
return request({
|
||||
url: '/system/post/optionselect',
|
||||
method: 'get',
|
||||
params: {
|
||||
postIds: postIds,
|
||||
deptId: deptId
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 新增岗位
|
||||
export function addPost(data: PostForm) {
|
||||
return request({
|
||||
@@ -44,3 +57,13 @@ export function delPost(postId: string | number | (string | number)[]) {
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询部门下拉树结构
|
||||
*/
|
||||
export const deptTreeSelect = (): AxiosPromise<DeptTreeVO[]> => {
|
||||
return request({
|
||||
url: '/system/post/deptTree',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
export interface PostVO extends BaseEntity {
|
||||
postId: number | string;
|
||||
deptId: number | string;
|
||||
postCode: string;
|
||||
postName: string;
|
||||
postCategory: string;
|
||||
deptName: string;
|
||||
postSort: number;
|
||||
status: string;
|
||||
remark: string;
|
||||
@@ -9,15 +12,20 @@ export interface PostVO extends BaseEntity {
|
||||
|
||||
export interface PostForm {
|
||||
postId: number | string | undefined;
|
||||
deptId: number | string | undefined;
|
||||
postCode: string;
|
||||
postName: string;
|
||||
postCategory: string;
|
||||
postSort: number;
|
||||
status: string;
|
||||
remark: string;
|
||||
}
|
||||
|
||||
export interface PostQuery extends PageQuery {
|
||||
deptId: number | string;
|
||||
belongDeptId: number | string;
|
||||
postCode: string;
|
||||
postName: string;
|
||||
postCategory: string;
|
||||
status: string;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,17 @@ export const listRole = (query: RoleQuery): AxiosPromise<RoleVO[]> => {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 通过roleIds查询角色
|
||||
* @param roleIds
|
||||
*/
|
||||
export const optionSelect = (roleIds: (number | string)[]): AxiosPromise<RoleVO[]> => {
|
||||
return request({
|
||||
url: '/system/role/optionselect?roleIds=' + roleIds,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询角色详细
|
||||
*/
|
||||
@@ -142,3 +153,8 @@ export const deptTreeSelect = (roleId: string | number): AxiosPromise<RoleDeptTr
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
export default {
|
||||
optionSelect,
|
||||
listRole
|
||||
};
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
// 绑定账号
|
||||
export function authBinding(source: string) {
|
||||
// 获取跳转URL
|
||||
export function authRouterUrl(source: string, tenantId: string) {
|
||||
return request({
|
||||
url: '/auth/binding/' + source,
|
||||
method: 'get'
|
||||
method: 'get',
|
||||
params: {
|
||||
tenantId: tenantId,
|
||||
domain: window.location.host
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,8 @@ export function addTenant(data: TenantForm) {
|
||||
url: '/system/tenant',
|
||||
method: 'post',
|
||||
headers: {
|
||||
isEncrypt: true
|
||||
isEncrypt: true,
|
||||
repeatSubmit: false
|
||||
},
|
||||
data: data
|
||||
});
|
||||
@@ -90,3 +91,19 @@ export function syncTenantPackage(tenantId: string | number, packageId: string |
|
||||
params: data
|
||||
});
|
||||
}
|
||||
|
||||
// 同步租户字典
|
||||
export function syncTenantDict() {
|
||||
return request({
|
||||
url: '/system/tenant/syncTenantDict',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 同步租户字典
|
||||
export function syncTenantConfig() {
|
||||
return request({
|
||||
url: '/system/tenant/syncTenantConfig',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DeptVO } from './../dept/types';
|
||||
import { DeptTreeVO } from './../dept/types';
|
||||
import { RoleVO } from '@/api/system/role/types';
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
@@ -17,6 +17,17 @@ export const listUser = (query: UserQuery): AxiosPromise<UserVO[]> => {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 通过用户ids查询用户
|
||||
* @param userIds
|
||||
*/
|
||||
export const optionSelect = (userIds: (number | string)[]): AxiosPromise<UserVO[]> => {
|
||||
return request({
|
||||
url: '/system/user/optionselect?userIds=' + userIds,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取用户详情
|
||||
* @param userId
|
||||
@@ -75,7 +86,8 @@ export const resetUserPwd = (userId: string | number, password: string) => {
|
||||
url: '/system/user/resetPwd',
|
||||
method: 'put',
|
||||
headers: {
|
||||
isEncrypt: true
|
||||
isEncrypt: true,
|
||||
repeatSubmit: false
|
||||
},
|
||||
data: data
|
||||
});
|
||||
@@ -134,7 +146,8 @@ export const updateUserPwd = (oldPassword: string, newPassword: string) => {
|
||||
url: '/system/user/profile/updatePwd',
|
||||
method: 'put',
|
||||
headers: {
|
||||
isEncrypt: true
|
||||
isEncrypt: true,
|
||||
repeatSubmit: false
|
||||
},
|
||||
data: data
|
||||
});
|
||||
@@ -189,7 +202,7 @@ export const listUserByDeptId = (deptId: string | number): AxiosPromise<UserVO[]
|
||||
/**
|
||||
* 查询部门下拉树结构
|
||||
*/
|
||||
export const deptTreeSelect = (): AxiosPromise<DeptVO[]> => {
|
||||
export const deptTreeSelect = (): AxiosPromise<DeptTreeVO[]> => {
|
||||
return request({
|
||||
url: '/system/user/deptTree',
|
||||
method: 'get'
|
||||
@@ -199,6 +212,7 @@ export const deptTreeSelect = (): AxiosPromise<DeptVO[]> => {
|
||||
export default {
|
||||
listUser,
|
||||
getUser,
|
||||
optionSelect,
|
||||
addUser,
|
||||
updateUser,
|
||||
delUser,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { DeptVO } from './../dept/types';
|
||||
import { RoleVO } from '@/api/system/role/types';
|
||||
import { PostVO } from '@/api/system/post/types';
|
||||
|
||||
@@ -16,10 +15,12 @@ export interface UserInfo {
|
||||
*/
|
||||
export interface UserQuery extends PageQuery {
|
||||
userName?: string;
|
||||
nickName?: string;
|
||||
phonenumber?: string;
|
||||
status?: string;
|
||||
deptId?: string | number;
|
||||
roleId?: string | number;
|
||||
userIds?: string | number | (string | number)[] | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -27,6 +28,7 @@ export interface UserQuery extends PageQuery {
|
||||
*/
|
||||
export interface UserVO extends BaseEntity {
|
||||
userId: string | number;
|
||||
tenantId: string;
|
||||
deptId: number;
|
||||
userName: string;
|
||||
nickName: string;
|
||||
@@ -40,7 +42,7 @@ export interface UserVO extends BaseEntity {
|
||||
loginIp: string;
|
||||
loginDate: string;
|
||||
remark: string;
|
||||
dept: DeptVO;
|
||||
deptName: string;
|
||||
roles: RoleVO[];
|
||||
roleIds: any;
|
||||
postIds: any;
|
||||
|
||||
@@ -28,7 +28,7 @@ export const getGenTable = (tableId: string | number): AxiosPromise<GenTableVO>
|
||||
};
|
||||
|
||||
// 修改代码生成信息
|
||||
export const updateGenTable = (data: DbTableForm) => {
|
||||
export const updateGenTable = (data: DbTableForm): AxiosPromise<GenTableVO> => {
|
||||
return request({
|
||||
url: '/tool/gen',
|
||||
method: 'put',
|
||||
@@ -37,7 +37,7 @@ export const updateGenTable = (data: DbTableForm) => {
|
||||
};
|
||||
|
||||
// 导入表
|
||||
export const importTable = (data: { tables: string; dataName: string }) => {
|
||||
export const importTable = (data: { tables: string; dataName: string }): AxiosPromise<GenTableVO> => {
|
||||
return request({
|
||||
url: '/tool/gen/importTable',
|
||||
method: 'post',
|
||||
|
||||
76
src/api/workflow/category/index.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { CategoryVO, CategoryForm, CategoryQuery, CategoryTreeVO } from '@/api/workflow/category/types';
|
||||
|
||||
/**
|
||||
* 查询流程分类列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
|
||||
export const listCategory = (query?: CategoryQuery): AxiosPromise<CategoryVO[]> => {
|
||||
return request({
|
||||
url: '/workflow/category/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询流程分类详细
|
||||
* @param categoryId
|
||||
*/
|
||||
export const getCategory = (categoryId: string | number): AxiosPromise<CategoryVO> => {
|
||||
return request({
|
||||
url: '/workflow/category/' + categoryId,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增流程分类
|
||||
* @param data
|
||||
*/
|
||||
export const addCategory = (data: CategoryForm) => {
|
||||
return request({
|
||||
url: '/workflow/category',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改流程分类
|
||||
* @param data
|
||||
*/
|
||||
export const updateCategory = (data: CategoryForm) => {
|
||||
return request({
|
||||
url: '/workflow/category',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除流程分类
|
||||
* @param categoryId
|
||||
*/
|
||||
export const delCategory = (categoryId: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: '/workflow/category/' + categoryId,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取流程分类树列表
|
||||
* @param query 流程实例id
|
||||
* @returns
|
||||
*/
|
||||
export const categoryTree = (query?: CategoryForm): AxiosPromise<CategoryTreeVO[]> => {
|
||||
return request({
|
||||
url: `/workflow/category/categoryTree`,
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
67
src/api/workflow/category/types.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
export interface CategoryTreeVO {
|
||||
id: number | string;
|
||||
label: string;
|
||||
parentId: number | string;
|
||||
weight: number;
|
||||
children: CategoryTreeVO[];
|
||||
}
|
||||
export interface CategoryVO {
|
||||
/**
|
||||
* 流程分类ID
|
||||
*/
|
||||
categoryId: string | number;
|
||||
|
||||
/**
|
||||
* 父级id
|
||||
*/
|
||||
parentId: string | number;
|
||||
|
||||
/**
|
||||
* 流程分类名称
|
||||
*/
|
||||
categoryName: string;
|
||||
|
||||
/**
|
||||
* 显示顺序
|
||||
*/
|
||||
orderNum: number;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
createTime: string;
|
||||
|
||||
/**
|
||||
* 子对象
|
||||
*/
|
||||
children: CategoryVO[];
|
||||
}
|
||||
|
||||
export interface CategoryForm extends BaseEntity {
|
||||
/**
|
||||
* 流程分类ID
|
||||
*/
|
||||
categoryId?: string | number;
|
||||
|
||||
/**
|
||||
* 流程分类名称
|
||||
*/
|
||||
categoryName?: string;
|
||||
|
||||
/**
|
||||
* 父流程分类id
|
||||
*/
|
||||
parentId?: string | number;
|
||||
|
||||
/**
|
||||
* 显示顺序
|
||||
*/
|
||||
orderNum?: number;
|
||||
}
|
||||
|
||||
export interface CategoryQuery {
|
||||
/**
|
||||
* 流程分类名称
|
||||
*/
|
||||
categoryName?: string;
|
||||
}
|
||||
170
src/api/workflow/definition/index.ts
Normal file
@@ -0,0 +1,170 @@
|
||||
import request from '@/utils/request';
|
||||
import { FlowDefinitionQuery, definitionXmlVO, FlowDefinitionForm, FlowDefinitionVo } from '@/api/workflow/definition/types';
|
||||
import { AxiosPromise } from 'axios';
|
||||
|
||||
/**
|
||||
* 获取流程定义列表
|
||||
* @param query 流程实例id
|
||||
* @returns
|
||||
*/
|
||||
export const listDefinition = (query: FlowDefinitionQuery): AxiosPromise<FlowDefinitionVo[]> => {
|
||||
return request({
|
||||
url: `/workflow/definition/list`,
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询未发布的流程定义列表
|
||||
* @param query 流程实例id
|
||||
* @returns
|
||||
*/
|
||||
export const unPublishList = (query: FlowDefinitionQuery): AxiosPromise<FlowDefinitionVo[]> => {
|
||||
return request({
|
||||
url: `/workflow/definition/unPublishList`,
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 通过流程定义id获取xml
|
||||
* @param definitionId 流程定义id
|
||||
* @returns
|
||||
*/
|
||||
export const definitionXml = (definitionId: string): AxiosPromise<definitionXmlVO> => {
|
||||
return request({
|
||||
url: `/workflow/definition/definitionXml/${definitionId}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除流程定义
|
||||
* @param id 流程定义id
|
||||
* @returns
|
||||
*/
|
||||
export const deleteDefinition = (id: string | string[]) => {
|
||||
return request({
|
||||
url: `/workflow/definition/${id}`,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 挂起/激活
|
||||
* @param definitionId 流程定义id
|
||||
* @param activityStatus 状态
|
||||
* @returns
|
||||
*/
|
||||
export const active = (definitionId: string, activityStatus: boolean) => {
|
||||
return request({
|
||||
url: `/workflow/definition/active/${definitionId}`,
|
||||
method: 'put',
|
||||
params: {
|
||||
active: activityStatus
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 通过zip或xml部署流程定义
|
||||
* @returns
|
||||
*/
|
||||
export function importDef(data: any) {
|
||||
return request({
|
||||
url: '/workflow/definition/importDef',
|
||||
method: 'post',
|
||||
data: data,
|
||||
headers: {
|
||||
repeatSubmit: false
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布流程定义
|
||||
* @param id 流程定义id
|
||||
* @returns
|
||||
*/
|
||||
export const publish = (id: string) => {
|
||||
return request({
|
||||
url: `/workflow/definition/publish/${id}`,
|
||||
method: 'put'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 取消发布流程定义
|
||||
* @param id 流程定义id
|
||||
* @returns
|
||||
*/
|
||||
export const unPublish = (id: string) => {
|
||||
return request({
|
||||
url: `/workflow/definition/unPublish/${id}`,
|
||||
method: 'put'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取流程定义xml字符串
|
||||
* @param id 流程定义id
|
||||
* @returns
|
||||
*/
|
||||
export const xmlString = (id: string) => {
|
||||
return request({
|
||||
url: `/workflow/definition/xmlString/${id}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增
|
||||
* @param data 参数
|
||||
* @returns
|
||||
*/
|
||||
export const add = (data: FlowDefinitionForm) => {
|
||||
return request({
|
||||
url: `/workflow/definition`,
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改
|
||||
* @param data 参数
|
||||
* @returns
|
||||
*/
|
||||
export const edit = (data: FlowDefinitionForm) => {
|
||||
return request({
|
||||
url: `/workflow/definition`,
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询详情
|
||||
* @param id 参数
|
||||
* @returns
|
||||
*/
|
||||
export const getInfo = (id: number | string) => {
|
||||
return request({
|
||||
url: `/workflow/definition/${id}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 复制流程定义
|
||||
* @param id 流程定义id
|
||||
* @returns
|
||||
*/
|
||||
export const copy = (id: string) => {
|
||||
return request({
|
||||
url: `/workflow/definition/copy/${id}`,
|
||||
method: 'post'
|
||||
});
|
||||
};
|
||||
34
src/api/workflow/definition/types.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
export interface FlowDefinitionQuery extends PageQuery {
|
||||
flowCode?: string;
|
||||
flowName?: string;
|
||||
category: string | number;
|
||||
isPublish?: number;
|
||||
}
|
||||
|
||||
export interface FlowDefinitionVo {
|
||||
id: string;
|
||||
flowName: string;
|
||||
flowCode: string;
|
||||
formPath: string;
|
||||
version: string;
|
||||
isPublish: number;
|
||||
activityStatus: number;
|
||||
createTime: Date;
|
||||
updateTime: Date;
|
||||
}
|
||||
|
||||
export interface FlowDefinitionForm {
|
||||
id: string;
|
||||
flowName: string;
|
||||
flowCode: string;
|
||||
category: string;
|
||||
ext: string;
|
||||
formPath: string;
|
||||
formCustom: string;
|
||||
modelValue: string;
|
||||
}
|
||||
|
||||
export interface definitionXmlVO {
|
||||
xml: string[];
|
||||
xmlStr: string;
|
||||
}
|
||||
125
src/api/workflow/instance/index.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
import request from '@/utils/request';
|
||||
import { FlowInstanceQuery, FlowInstanceVO } from '@/api/workflow/instance/types';
|
||||
import { AxiosPromise } from 'axios';
|
||||
|
||||
/**
|
||||
* 查询运行中实例列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const pageByRunning = (query: FlowInstanceQuery): AxiosPromise<FlowInstanceVO[]> => {
|
||||
return request({
|
||||
url: '/workflow/instance/pageByRunning',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询已完成实例列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const pageByFinish = (query: FlowInstanceQuery): AxiosPromise<FlowInstanceVO[]> => {
|
||||
return request({
|
||||
url: '/workflow/instance/pageByFinish',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 通过业务id获取历史流程图
|
||||
*/
|
||||
export const flowHisTaskList = (businessId: string | number) => {
|
||||
return request({
|
||||
url: `/workflow/instance/flowHisTaskList/${businessId}` + '?t' + Math.random(),
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 分页查询当前登录人单据
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const pageByCurrent = (query: FlowInstanceQuery): AxiosPromise<FlowInstanceVO[]> => {
|
||||
return request({
|
||||
url: '/workflow/instance/pageByCurrent',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 撤销流程
|
||||
* @param data 参数
|
||||
* @returns
|
||||
*/
|
||||
export const cancelProcessApply = (data: any) => {
|
||||
return request({
|
||||
url: `/workflow/instance/cancelProcessApply`,
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取流程变量
|
||||
* @param instanceId 实例id
|
||||
* @returns
|
||||
*/
|
||||
export const instanceVariable = (instanceId: string | number) => {
|
||||
return request({
|
||||
url: `/workflow/instance/instanceVariable/${instanceId}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param instanceIds 流程实例id
|
||||
* @returns
|
||||
*/
|
||||
export const deleteByInstanceIds = (instanceIds: Array<string | number> | string | number) => {
|
||||
return request({
|
||||
url: `/workflow/instance/deleteByInstanceIds/${instanceIds}`,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除历史流程实例
|
||||
* @param instanceIds
|
||||
*/
|
||||
export const deleteHisByInstanceIds = (instanceIds: Array<string | number> | string | number) => {
|
||||
return request({
|
||||
url: `/workflow/instance/deleteHisByInstanceIds/${instanceIds}`,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 作废流程
|
||||
* @param data 参数
|
||||
* @returns
|
||||
*/
|
||||
export const invalid = (data: any) => {
|
||||
return request({
|
||||
url: `/workflow/instance/invalid`,
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 修改流程变量
|
||||
* @param data 参数
|
||||
* @returns
|
||||
*/
|
||||
export const updateVariable = (data: any) => {
|
||||
return request({
|
||||
url: `/workflow/instance/updateVariable`,
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
28
src/api/workflow/instance/types.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { FlowTaskVO } from '@/api/workflow/task/types';
|
||||
|
||||
export interface FlowInstanceQuery extends PageQuery {
|
||||
category?: string | number;
|
||||
nodeName?: string;
|
||||
flowCode?: string;
|
||||
flowName?: string;
|
||||
createByIds?: string[] | number[];
|
||||
businessId?: string;
|
||||
}
|
||||
|
||||
export interface FlowInstanceVO extends BaseEntity {
|
||||
id: string | number;
|
||||
definitionId: string;
|
||||
flowName: string;
|
||||
flowCode: string;
|
||||
version: string;
|
||||
businessId: string;
|
||||
activityStatus: number;
|
||||
tenantId: string;
|
||||
createTime: string;
|
||||
createBy: string;
|
||||
flowStatus: string;
|
||||
flowStatusName: string;
|
||||
flowTaskList: FlowTaskVO[];
|
||||
businessCode: string;
|
||||
businessTitle: string;
|
||||
}
|
||||
75
src/api/workflow/leave/index.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { LeaveVO, LeaveQuery, LeaveForm } from '@/api/workflow/leave/types';
|
||||
|
||||
/**
|
||||
* 查询请假列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
|
||||
export const listLeave = (query?: LeaveQuery): AxiosPromise<LeaveVO[]> => {
|
||||
return request({
|
||||
url: '/workflow/leave/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询请假详细
|
||||
* @param id
|
||||
*/
|
||||
export const getLeave = (id: string | number): AxiosPromise<LeaveVO> => {
|
||||
return request({
|
||||
url: '/workflow/leave/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增请假
|
||||
* @param data
|
||||
*/
|
||||
export const addLeave = (data: LeaveForm): AxiosPromise<LeaveVO> => {
|
||||
return request({
|
||||
url: '/workflow/leave',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 提交请假并发起流程
|
||||
* @param data
|
||||
*/
|
||||
export const submitAndFlowStart = (data: LeaveForm): AxiosPromise<LeaveVO> => {
|
||||
return request({
|
||||
url: '/workflow/leave/submitAndFlowStart',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改请假
|
||||
* @param data
|
||||
*/
|
||||
export const updateLeave = (data: LeaveForm): AxiosPromise<LeaveVO> => {
|
||||
return request({
|
||||
url: '/workflow/leave',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除请假
|
||||
* @param id
|
||||
*/
|
||||
export const delLeave = (id: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: '/workflow/leave/' + id,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
26
src/api/workflow/leave/types.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
export interface LeaveVO {
|
||||
id: string | number;
|
||||
applyCode?: string;
|
||||
leaveType: string;
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
leaveDays: number;
|
||||
remark: string;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
export interface LeaveForm extends BaseEntity {
|
||||
id?: string | number;
|
||||
applyCode?: string;
|
||||
leaveType?: string;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
leaveDays?: number;
|
||||
remark?: string;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
export interface LeaveQuery extends PageQuery {
|
||||
startLeaveDays?: number;
|
||||
endLeaveDays?: number;
|
||||
}
|
||||
63
src/api/workflow/spel/index.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { SpelVO, SpelForm, SpelQuery } from '@/api/workflow/spel/types';
|
||||
|
||||
/**
|
||||
* 查询流程spel表达式定义列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
|
||||
export const listSpel = (query?: SpelQuery): AxiosPromise<SpelVO[]> => {
|
||||
return request({
|
||||
url: '/workflow/spel/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询流程spel表达式定义详细
|
||||
* @param id
|
||||
*/
|
||||
export const getSpel = (id: string | number): AxiosPromise<SpelVO> => {
|
||||
return request({
|
||||
url: '/workflow/spel/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增流程spel表达式定义
|
||||
* @param data
|
||||
*/
|
||||
export const addSpel = (data: SpelForm) => {
|
||||
return request({
|
||||
url: '/workflow/spel',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改流程spel表达式定义
|
||||
* @param data
|
||||
*/
|
||||
export const updateSpel = (data: SpelForm) => {
|
||||
return request({
|
||||
url: '/workflow/spel',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除流程spel表达式定义
|
||||
* @param id
|
||||
*/
|
||||
export const delSpel = (id: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: '/workflow/spel/' + id,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
111
src/api/workflow/spel/types.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
export interface SpelVO {
|
||||
/**
|
||||
* 主键id
|
||||
*/
|
||||
id: string | number;
|
||||
|
||||
/**
|
||||
* 组件名称
|
||||
*/
|
||||
componentName: string;
|
||||
|
||||
/**
|
||||
* 方法名
|
||||
*/
|
||||
methodName: string;
|
||||
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
methodParams: string;
|
||||
|
||||
/**
|
||||
* 预览spel值
|
||||
*/
|
||||
viewSpel: string;
|
||||
|
||||
/**
|
||||
* 状态(0正常 1停用)
|
||||
*/
|
||||
status: string;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
remark?: string;
|
||||
|
||||
}
|
||||
|
||||
export interface SpelForm extends BaseEntity {
|
||||
/**
|
||||
* 主键id
|
||||
*/
|
||||
id?: string | number;
|
||||
|
||||
/**
|
||||
* 组件名称
|
||||
*/
|
||||
componentName?: string;
|
||||
|
||||
/**
|
||||
* 方法名
|
||||
*/
|
||||
methodName?: string;
|
||||
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
methodParams?: string;
|
||||
|
||||
/**
|
||||
* 预览spel值
|
||||
*/
|
||||
viewSpel?: string;
|
||||
|
||||
/**
|
||||
* 状态(0正常 1停用)
|
||||
*/
|
||||
status?: string;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
remark?: string;
|
||||
|
||||
}
|
||||
|
||||
export interface SpelQuery extends PageQuery {
|
||||
|
||||
/**
|
||||
* 组件名称
|
||||
*/
|
||||
componentName?: string;
|
||||
|
||||
/**
|
||||
* 方法名
|
||||
*/
|
||||
methodName?: string;
|
||||
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
methodParams?: string;
|
||||
|
||||
/**
|
||||
* 预览spel值
|
||||
*/
|
||||
viewSpel?: string;
|
||||
|
||||
/**
|
||||
* 状态(0正常 1停用)
|
||||
*/
|
||||
status?: string;
|
||||
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
}
|
||||
|
||||
|
||||
|
||||
206
src/api/workflow/task/index.ts
Normal file
@@ -0,0 +1,206 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { TaskQuery, FlowTaskVO, TaskOperationBo } from '@/api/workflow/task/types';
|
||||
|
||||
/**
|
||||
* 查询待办列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const pageByTaskWait = (query: TaskQuery): AxiosPromise<FlowTaskVO[]> => {
|
||||
return request({
|
||||
url: '/workflow/task/pageByTaskWait',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询已办列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const pageByTaskFinish = (query: TaskQuery): AxiosPromise<FlowTaskVO[]> => {
|
||||
return request({
|
||||
url: '/workflow/task/pageByTaskFinish',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询当前用户的抄送列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const pageByTaskCopy = (query: TaskQuery): AxiosPromise<FlowTaskVO[]> => {
|
||||
return request({
|
||||
url: '/workflow/task/pageByTaskCopy',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 当前租户所有待办任务
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const pageByAllTaskWait = (query: TaskQuery): AxiosPromise<FlowTaskVO[]> => {
|
||||
return request({
|
||||
url: '/workflow/task/pageByAllTaskWait',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 当前租户所有已办任务
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const pageByAllTaskFinish = (query: TaskQuery): AxiosPromise<FlowTaskVO[]> => {
|
||||
return request({
|
||||
url: '/workflow/task/pageByAllTaskFinish',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 启动流程
|
||||
* @param data
|
||||
* @returns {*}
|
||||
*/
|
||||
export const startWorkFlow = (data: object): any => {
|
||||
return request({
|
||||
url: '/workflow/task/startWorkFlow',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 办理流程
|
||||
* @param data
|
||||
* @returns {*}
|
||||
*/
|
||||
export const completeTask = (data: object) => {
|
||||
return request({
|
||||
url: '/workflow/task/completeTask',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 任务驳回
|
||||
* @param data
|
||||
* @returns {*}
|
||||
*/
|
||||
export const backProcess = (data: any): any => {
|
||||
return request({
|
||||
url: '/workflow/task/backProcess',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取当前任务
|
||||
* @param taskId
|
||||
* @returns
|
||||
*/
|
||||
export const getTask = (taskId: string) => {
|
||||
return request({
|
||||
url: '/workflow/task/getTask/' + taskId,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改任务办理人
|
||||
* @param taskIdList
|
||||
* @param userId
|
||||
* @returns
|
||||
*/
|
||||
export const updateAssignee = (taskIdList: Array<string>, userId: string) => {
|
||||
return request({
|
||||
url: `/workflow/task/updateAssignee/${userId}`,
|
||||
method: 'put',
|
||||
data: taskIdList
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 终止任务
|
||||
* @returns
|
||||
*/
|
||||
export const terminationTask = (data: any) => {
|
||||
return request({
|
||||
url: `/workflow/task/terminationTask`,
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取可驳回得任务节点
|
||||
* @returns
|
||||
*/
|
||||
export const getBackTaskNode = (taskId: string | number, nodeCode: string) => {
|
||||
return request({
|
||||
url: `/workflow/task/getBackTaskNode/${taskId}/${nodeCode}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 任务操作 操作类型,委派 delegateTask、转办 transferTask、加签 addSignature、减签 reductionSignature
|
||||
* @returns
|
||||
*/
|
||||
export const taskOperation = (data: TaskOperationBo, operation: string) => {
|
||||
return request({
|
||||
url: `/workflow/task/taskOperation/${operation}`,
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取当前任务办理人
|
||||
* @param taskId 任务id
|
||||
* @returns
|
||||
*/
|
||||
export const currentTaskAllUser = (taskId: string | number) => {
|
||||
return request({
|
||||
url: `/workflow/task/currentTaskAllUser/${taskId}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取下一节点写
|
||||
* @param data参数
|
||||
* @returns
|
||||
*/
|
||||
export const getNextNodeList = (data: any): any => {
|
||||
return request({
|
||||
url: '/workflow/task/getNextNodeList',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 催办任务
|
||||
* @param data参数
|
||||
* @returns
|
||||
*/
|
||||
export const urgeTask = (data: any): any => {
|
||||
return request({
|
||||
url: '/workflow/task/urgeTask',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
58
src/api/workflow/task/types.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
export interface TaskQuery extends PageQuery {
|
||||
nodeName?: string;
|
||||
flowCode?: string;
|
||||
flowName?: string;
|
||||
createByIds?: string[] | number[];
|
||||
}
|
||||
|
||||
export interface ParticipantVo {
|
||||
groupIds?: string[] | number[];
|
||||
candidate: string[] | number[];
|
||||
candidateName: string[];
|
||||
claim: boolean;
|
||||
}
|
||||
export interface FlowTaskVO {
|
||||
id: string | number;
|
||||
createTime?: Date;
|
||||
updateTime?: Date;
|
||||
tenantId?: string;
|
||||
definitionId?: string;
|
||||
instanceId: string;
|
||||
flowName: string;
|
||||
businessId: string;
|
||||
nodeCode: string;
|
||||
nodeName: string;
|
||||
flowCode: string;
|
||||
flowStatus: string;
|
||||
formCustom: string;
|
||||
formPath: string;
|
||||
nodeType: number;
|
||||
nodeRatio: string | number;
|
||||
version?: string;
|
||||
applyNode?: boolean;
|
||||
buttonList?: ButtonList[];
|
||||
copyList?: FlowCopyVo[];
|
||||
varList?: Map<string, string>;
|
||||
businessCode: string;
|
||||
businessTitle: string;
|
||||
}
|
||||
|
||||
export interface ButtonList {
|
||||
code: string;
|
||||
show: boolean;
|
||||
}
|
||||
export interface FlowCopyVo {
|
||||
userId: string | number;
|
||||
userName: string;
|
||||
}
|
||||
|
||||
export interface TaskOperationBo {
|
||||
//委派/转办人的用户ID(必填,准对委派/转办人操作)
|
||||
userId?: string;
|
||||
//加签/减签人的用户ID列表(必填,针对加签/减签操作)
|
||||
userIds?: string[];
|
||||
//任务ID(必填)
|
||||
taskId: string | number;
|
||||
//意见或备注信息(可选)
|
||||
message?: string;
|
||||
}
|
||||
15
src/api/workflow/workflowCommon/index.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { RouterJumpVo } from '@/api/workflow/workflowCommon/types';
|
||||
|
||||
export default {
|
||||
routerJump(routerJumpVo: RouterJumpVo, proxy) {
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.push({
|
||||
path: routerJumpVo.formPath,
|
||||
query: {
|
||||
id: routerJumpVo.businessId,
|
||||
type: routerJumpVo.type,
|
||||
taskId: routerJumpVo.taskId
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
14
src/api/workflow/workflowCommon/types.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export interface RouterJumpVo {
|
||||
businessId: string;
|
||||
taskId: string | number;
|
||||
type: string;
|
||||
formCustom: string;
|
||||
formPath: string;
|
||||
}
|
||||
|
||||
export interface StartProcessBo {
|
||||
businessId: string | number;
|
||||
flowCode: string;
|
||||
variables: any;
|
||||
bizExt: any;
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1588670460195" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1314" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M230.4 307.712c13.824 0 25.088-11.264 25.088-25.088 0-100.352 81.92-182.272 182.272-182.272s182.272 81.408 182.272 182.272c0 13.824 11.264 25.088 25.088 25.088s25.088-11.264 24.576-25.088c0-127.488-103.936-231.936-231.936-231.936S205.824 154.624 205.824 282.624c-0.512 14.336 10.752 25.088 24.576 25.088z m564.736 234.496c-11.264 0-21.504 2.048-31.232 6.144 0-44.544-40.448-81.92-88.064-81.92-14.848 0-28.16 3.584-39.936 10.24-13.824-28.16-44.544-48.128-78.848-48.128-12.288 0-24.576 2.56-35.328 7.68V284.16c0-45.568-37.888-81.92-84.48-81.92s-84.48 36.864-84.48 81.92v348.672l-69.12-112.64c-18.432-28.16-58.368-36.864-91.136-19.968-26.624 14.336-46.592 47.104-30.208 88.064 3.072 8.192 76.8 205.312 171.52 311.296 0 0 28.16 24.576 43.008 58.88 4.096 9.728 13.312 15.36 22.528 15.36 3.072 0 6.656-0.512 9.728-2.048 12.288-5.12 18.432-19.968 12.8-32.256-19.456-44.544-53.76-74.752-53.76-74.752C281.6 768 209.408 573.44 208.384 570.88c-5.12-12.8-2.56-20.992 7.168-26.112 9.216-4.608 21.504-4.608 26.112 2.56l113.152 184.32c4.096 8.704 12.8 14.336 22.528 14.336 13.824 0 25.088-10.752 25.088-25.088V284.16c0-17.92 15.36-32.256 34.816-32.256s34.816 14.336 34.816 32.256v284.16c0 13.824 10.24 25.088 24.576 25.088 13.824 0 25.088-11.264 25.088-25.088v-57.344c0-17.92 15.36-32.768 34.816-32.768 19.968 0 37.376 15.36 37.376 32.768v95.232c0 7.168 3.072 13.312 7.68 17.92 4.608 4.608 10.752 7.168 17.92 7.168 13.824 0 24.576-11.264 24.576-25.088V547.84c0-18.432 13.824-32.256 32.256-32.256 20.48 0 38.912 15.36 38.912 32.256v95.232c0 13.824 11.264 25.088 25.088 25.088s24.576-11.264 25.088-25.088v-18.944c0-18.944 12.8-32.256 30.72-32.256 18.432 0 22.528 18.944 22.528 31.744 0 1.024-11.776 99.84-50.688 173.056-30.72 58.368-45.056 112.128-51.2 146.944-2.56 13.312 6.656 26.112 19.968 28.672 1.536 0 3.072 0.512 4.608 0.512 11.776 0 22.016-8.192 24.064-20.48 5.632-31.232 18.432-79.36 46.08-132.608 43.52-81.92 55.808-186.88 56.32-193.536-0.512-50.688-29.696-83.968-72.704-83.968z"></path></path></svg>
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1588670460195" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1314" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M230.4 307.712c13.824 0 25.088-11.264 25.088-25.088 0-100.352 81.92-182.272 182.272-182.272s182.272 81.408 182.272 182.272c0 13.824 11.264 25.088 25.088 25.088s25.088-11.264 24.576-25.088c0-127.488-103.936-231.936-231.936-231.936S205.824 154.624 205.824 282.624c-0.512 14.336 10.752 25.088 24.576 25.088z m564.736 234.496c-11.264 0-21.504 2.048-31.232 6.144 0-44.544-40.448-81.92-88.064-81.92-14.848 0-28.16 3.584-39.936 10.24-13.824-28.16-44.544-48.128-78.848-48.128-12.288 0-24.576 2.56-35.328 7.68V284.16c0-45.568-37.888-81.92-84.48-81.92s-84.48 36.864-84.48 81.92v348.672l-69.12-112.64c-18.432-28.16-58.368-36.864-91.136-19.968-26.624 14.336-46.592 47.104-30.208 88.064 3.072 8.192 76.8 205.312 171.52 311.296 0 0 28.16 24.576 43.008 58.88 4.096 9.728 13.312 15.36 22.528 15.36 3.072 0 6.656-0.512 9.728-2.048 12.288-5.12 18.432-19.968 12.8-32.256-19.456-44.544-53.76-74.752-53.76-74.752C281.6 768 209.408 573.44 208.384 570.88c-5.12-12.8-2.56-20.992 7.168-26.112 9.216-4.608 21.504-4.608 26.112 2.56l113.152 184.32c4.096 8.704 12.8 14.336 22.528 14.336 13.824 0 25.088-10.752 25.088-25.088V284.16c0-17.92 15.36-32.256 34.816-32.256s34.816 14.336 34.816 32.256v284.16c0 13.824 10.24 25.088 24.576 25.088 13.824 0 25.088-11.264 25.088-25.088v-57.344c0-17.92 15.36-32.768 34.816-32.768 19.968 0 37.376 15.36 37.376 32.768v95.232c0 7.168 3.072 13.312 7.68 17.92 4.608 4.608 10.752 7.168 17.92 7.168 13.824 0 24.576-11.264 24.576-25.088V547.84c0-18.432 13.824-32.256 32.256-32.256 20.48 0 38.912 15.36 38.912 32.256v95.232c0 13.824 11.264 25.088 25.088 25.088s24.576-11.264 25.088-25.088v-18.944c0-18.944 12.8-32.256 30.72-32.256 18.432 0 22.528 18.944 22.528 31.744 0 1.024-11.776 99.84-50.688 173.056-30.72 58.368-45.056 112.128-51.2 146.944-2.56 13.312 6.656 26.112 19.968 28.672 1.536 0 3.072 0.512 4.608 0.512 11.776 0 22.016-8.192 24.064-20.48 5.632-31.232 18.432-79.36 46.08-132.608 43.52-81.92 55.808-186.88 56.32-193.536-0.512-50.688-29.696-83.968-72.704-83.968z"></path></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
1
src/assets/icons/svg/caret-back.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="ionicon" viewBox="0 0 512 512"><path d="M321.94 98L158.82 237.78a24 24 0 000 36.44L321.94 414c15.57 13.34 39.62 2.28 39.62-18.22v-279.6c0-20.5-24.05-31.56-39.62-18.18z"/></svg>
|
||||
|
After Width: | Height: | Size: 223 B |
1
src/assets/icons/svg/caret-forward.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="ionicon" viewBox="0 0 512 512"><path d="M190.06 414l163.12-139.78a24 24 0 000-36.44L190.06 98c-15.57-13.34-39.62-2.28-39.62 18.22v279.6c0 20.5 24.05 31.56 39.62 18.18z"/></svg>
|
||||
|
After Width: | Height: | Size: 223 B |
1
src/assets/icons/svg/category.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1715954426124" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3305" width="200" height="200"><path d="M664.081597 1023.943114a78.246037 78.246037 0 0 1-78.985549-76.795456v-284.996471a78.27448 78.27448 0 0 1 78.985549-76.93767h280.843828A78.189152 78.189152 0 0 1 1023.939417 662.151187v284.996471a78.246037 78.246037 0 0 1-79.013992 76.795456z m-585.067605 0a78.246037 78.246037 0 0 1-78.985549-76.795456v-284.996471a78.160709 78.160709 0 0 1 78.985549-76.93767h280.786942a78.302923 78.302923 0 0 1 79.042434 76.93767v284.996471h-0.170656a78.246037 78.246037 0 0 1-78.985549 76.795456z m0-585.096048a78.217594 78.217594 0 0 1-78.985549-76.93767V76.912925a78.189152 78.189152 0 0 1 78.957106-76.795456h280.786942a78.27448 78.27448 0 0 1 79.042435 76.93767v284.996471a78.27448 78.27448 0 0 1-79.013992 76.795456z m589.675333-5.688552a77.193655 77.193655 0 0 1-77.990052-75.885288V75.888985a77.25054 77.25054 0 0 1 77.990052-75.942173h277.26004a77.25054 77.25054 0 0 1 77.961609 75.942173v281.384241a77.421197 77.421197 0 0 1-78.132266 75.885288z" p-id="3306" fill="currentColor"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
1
src/assets/icons/svg/finish.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1716006237008" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="12400" width="200" height="200"><path d="M738.826039 1005.166431c-150.226824 0-272.00251-121.916235-272.00251-272.303686 0-150.407529 121.775686-272.323765 272.00251-272.323765 150.206745 0 271.982431 121.916235 271.982432 272.323765 0 150.387451-121.775686 272.303686-271.982432 272.303686z m-0.040157-508.225255c-128.582275 0-232.789333 104.347608-232.789333 233.09051s104.207059 233.110588 232.789333 233.110589c128.562196 0 232.769255-104.367686 232.769255-233.110589 0-128.742902-104.207059-233.09051-232.769255-233.09051z m10.561255 318.243138s-3.694431 3.674353-7.408941 3.674353a18.010353 18.010353 0 0 1-25.941333 0l-74.10949-80.916079a17.66902 17.66902 0 0 1 0-25.740549c7.408941-7.368784 22.246902-7.368784 25.941333 0l63.006118 69.872941 129.686588-117.699764a18.010353 18.010353 0 0 1 25.941333 0 17.709176 17.709176 0 0 1 0 25.760627L749.347137 815.184314zM391.529412 682.666667H190.745098a20.078431 20.078431 0 0 1 0-40.156863h200.784314a20.078431 20.078431 0 1 1 0 40.156863zM170.666667 261.019608a20.078431 20.078431 0 0 1 20.078431-20.078432h481.882353a20.078431 20.078431 0 0 1 0 40.156863H190.745098a20.078431 20.078431 0 0 1-20.078431-20.078431z m341.333333 200.784314H190.745098a20.078431 20.078431 0 0 1 0-40.156863h321.254902a20.078431 20.078431 0 0 1 0 40.156863zM813.176471 120.470588a80.313725 80.313725 0 0 0-80.313726-80.313725H130.509804a80.313725 80.313725 0 0 0-80.313726 80.313725v762.980392a80.313725 80.313725 0 0 0 80.313726 80.313726h366.832941a346.112 346.112 0 0 0 40.417882 40.779294H130.509804a120.470588 120.470588 0 0 1-120.470588-120.470588V120.470588a120.470588 120.470588 0 0 1 120.470588-120.470588h602.352941a120.470588 120.470588 0 0 1 120.470588 120.470588v293.667137a340.188863 340.188863 0 0 0-40.156862-8.533333V120.470588z" fill="currentColor" p-id="12401"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
1
src/assets/icons/svg/model.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1715953291934" class="icon" viewBox="0 0 1061 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1715" id="mx_n_1715953291935" width="200" height="200"><path d="M447.122465 467.332105L49.240301 268.161564A33.501036 33.501036 0 0 0 0.136043 300.744763v441.020484a33.042117 33.042117 0 0 0 16.06214 27.994016L413.162511 1018.034062a33.959954 33.959954 0 0 0 17.438895 5.50702 33.042117 33.042117 0 0 0 33.042117-33.042118V497.161795a33.042117 33.042117 0 0 0-17.438895-29.82969zM398.018207 931.298504l-331.339011-208.348907v-367.134638l331.339011 162.915996zM1046.010843 263.572381a33.042117 33.042117 0 0 0-31.665363 0L550.838 467.332105a33.042117 33.042117 0 0 0-19.733487 30.288608v493.33717a33.042117 33.042117 0 0 0 49.563176 28.452934l463.048562-265.254776a33.042117 33.042117 0 0 0 16.521059-28.452934V291.566398a33.042117 33.042117 0 0 0-14.685386-27.994017z m-50.939931 441.020484L596.72983 931.298504v-413.026468l397.882163-176.224626zM991.399565 178.672496a33.042117 33.042117 0 0 0-22.486996-29.829689L550.838 1.530034a32.583199 32.583199 0 0 0-19.733487 0L83.659173 158.021173a33.042117 33.042117 0 0 0-4.130264 61.036134l397.882163 199.170541a33.042117 33.042117 0 0 0 14.685386 3.212428 33.959954 33.959954 0 0 0 13.30863 0l463.966399-205.595398a33.042117 33.042117 0 0 0 22.028078-37.172382zM494.391049 349.849021L180.490934 195.193555l358.874108-125.743613 328.126583 112.434982z m0 0" fill="currentColor" p-id="1716"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
1
src/assets/icons/svg/my-copy.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1716006583362" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="38505" width="200" height="200"><path d="M733.696 666.624l56.32-65.536-15.36-12.8c-41.472-34.816-87.552-61.44-136.192-79.36 75.264-49.152 124.928-134.144 124.928-230.912 0-152.576-123.904-276.48-276.48-276.48-74.24 0-143.872 28.672-195.584 80.384-52.224 51.712-80.896 121.344-80.896 195.584 0 92.16 45.568 174.08 115.2 224.256-81.408 26.624-156.672 74.752-215.552 144.896C34.304 736.768-4.096 850.944 0.512 968.192l1.024 20.48 86.528-4.608-1.024-19.968c-4.096-96.256 27.136-188.928 88.576-261.12 136.704-162.816 380.416-184.32 543.232-48.64l14.848 12.288zM296.96 278.016c0-106.496 83.456-189.952 189.952-189.952 104.96 0 189.952 84.992 189.952 189.952 0 106.496-83.456 189.952-189.952 189.952S296.96 384.512 296.96 278.016z m690.688 522.24H802.304c13.824-16.896 32.256-38.4 55.808-67.072 7.68-8.192 11.776-19.456 10.752-31.744-1.024-11.776-6.144-22.528-15.36-29.696-8.192-7.68-19.456-11.264-31.232-10.752-12.288 1.024-23.04 6.656-30.208 15.872-38.4 45.568-96.256 114.176-101.376 119.808-7.68 7.68-10.752 15.36-13.312 22.528-4.096 8.704-4.096 16.384-4.096 24.064 0 5.632 0 12.8 3.584 23.04 2.56 7.68 6.144 15.872 13.824 23.552l104.96 124.416 4.096 2.048c9.216 4.096 18.432 6.144 26.624 6.144 8.704 0 21.504-4.096 28.672-11.776 8.704-8.704 13.824-19.968 14.336-31.744 0-10.752-3.584-20.48-11.264-28.16l-54.272-63.488h183.296c19.456 0 35.84-18.944 36.352-43.008v-0.512c0.512-25.088-14.848-43.52-35.84-43.52z" fill="currentColor" p-id="38506"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
1
src/assets/icons/svg/my-task.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1715953932254" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11266" width="200" height="200"><path d="M955.59576334 565.84816928C921.71190561 470.40769794 828.48968821 401.08719229 717.79871055 401.08719229c-111.30100029 0-204.91141024 70.09689085-238.29616047 166.36920831h-253.43576475c-26.23088067 0-47.5261393 24.84446618-47.5261329 55.45640711s21.2952586 55.4564006 47.5261329 55.45640054h242.73267482c6.54385326 41.3150179 23.95716606 79.02537397 49.30074631 110.91280755h-292.03342113c-26.23088067 0-47.5261393 24.84446618-47.5261329 55.45640061s21.2952586 55.4564006 47.5261329 55.45640059h214.0617167a376.93717152 376.93717152 0 0 0-33.44021086 110.91280754h-292.47706801a46.36155474 46.36155474 0 0 1-45.91790151-46.8052016V169.1685161c0-25.06629616 21.23980441-45.36333956 47.35977009-45.3633331h63.38666838v55.45640059c0 50.07713168 67.6013528 55.4564006 110.91280122 55.45640061 43.31144833 0 88.50841878 5.76746809 88.5084187-55.45640061v-55.45640059h44.53149358v55.45640059c0 61.22386864 38.15400297 55.4564006 88.84115713 55.45640061 50.74260854 0 88.56387296 5.76746809 88.56387308-55.45640061v-55.45640059h44.19875511v55.45640059c0 61.22386864 38.43128719 55.4564006 89.11844137 55.45640061s110.91280771-2.82827659 110.91280118-55.45640061v-55.45640059h63.44212904a47.69250853 47.69250853 0 0 1 47.74796275 47.63704778l-0.22182994 394.4059385zM407.96379074 345.63079175h-181.95245955c-26.23088067 0-47.5261393 24.84446618-47.52613946 55.45640054s21.2952586 55.4564006 47.52613946 55.45640711h181.95245955c26.23088067 0 47.5261393-24.84446618 47.52613268-55.45640711s-21.2952586-55.4564006-47.52613268-55.45640054z m325.75090957-166.36920816c-30.61193437 0-55.4564006-18.63335139-55.45640712-41.59230208v-83.18460405c0-22.95895071 24.84446618-41.59230206 55.45640712-41.592302s55.4564006 18.63335139 55.4564006 41.592302v83.18460405c0 22.95895071-24.84446618 41.59230206-55.4564006 41.59230208z m-222.71291552 0c-30.61193437 0-55.4009463-18.63335139-55.4009464-41.59230208v-83.18460405c0-22.95895071 24.84446618-41.59230206 55.4009464-41.592302 30.61193437 0 55.4564006 18.63335139 55.45640054 41.592302v83.18460405c-0.0554542 22.95895071-24.84446618 41.59230206-55.45640054 41.59230208z m-220.6610244 0c-30.61193437 0-55.4009463-18.63335139-55.40094634-41.59230208v-83.18460405c0-22.95895071 24.84446618-41.59230206 55.40094634-41.592302 30.61193437 0 55.4564006 18.63335139 55.45640063 41.592302v83.18460405c0 22.95895071-24.84446618 41.59230206-55.45640063 41.59230208z m443.31847922 665.92048622c-91.28124099 0-165.53736215-74.69977454-165.53736218-166.59103817 0-91.83580289 74.2561211-166.59103164 165.53736218-166.59103164s165.53736215 74.75522879 165.53736218 166.59103164-74.2561211 166.59103164-165.53736218 166.59103817z m-115.73750824-29.33644132c32.1647114 24.45627358 71.98241282 39.59587158 115.68205395 39.59587144 44.14329437 0 84.34918863-15.47233629 116.68026924-40.3722568a235.13514545 235.13514545 0 0 1 105.14533952 195.98292729h-443.6512175c0-80.35632762 41.92504052-153.94697186 106.14355479-195.20654193z" fill="currentColor" p-id="11267"></path></svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
1
src/assets/icons/svg/process-definition.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1716005059256" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3268" width="200" height="200"><path d="M497.798958 952.225272A345.95419 345.95419 0 0 1 359.273733 892.652247a171.541601 171.541601 0 0 0 7.177473-47.37132A179.436821 179.436821 0 0 0 211.417793 665.126359a345.95419 345.95419 0 0 1 185.896546-380.40606 179.436821 179.436821 0 0 0 317.244299 0 351.696169 351.696169 0 0 1 143.549456 128.476763 35.169617 35.169617 0 0 0 30.145386 16.508188 37.322859 37.322859 0 0 0 19.379177-5.024231 36.605111 36.605111 0 0 0 10.766209-46.653574 424.188644 424.188644 0 0 0-183.743304-160.775391v-13.637198a180.872315 180.872315 0 1 0-358.873642 0 129.194511 129.194511 0 0 0 0 15.79044A423.470897 423.470897 0 0 0 132.465591 600.529103a467.253481 467.253481 0 0 0 6.459726 71.774728A180.154568 180.154568 0 0 0 187.014385 1024a178.001326 178.001326 0 0 0 139.96072-68.185992 430.64837 430.64837 0 0 0 158.62215 62.444014h6.459725a35.887364 35.887364 0 0 0 5.741978-71.774729z m57.419783-861.29674a109.097587 109.097587 0 1 1-108.37984 110.533082A109.097587 109.097587 0 0 1 555.218741 90.928532zM187.014385 952.225272a109.097587 109.097587 0 1 1 108.37984-108.37984A109.097587 109.097587 0 0 1 187.014385 952.225272zM933.471559 617.755038l-104.791103-71.774728a35.887364 35.887364 0 0 0-48.089068 8.612967L560.242972 858.918125a37.322859 37.322859 0 0 0-6.459725 24.403408l9.330714 104.073356a35.169617 35.169617 0 0 0 14.354946 25.838902 38.758353 38.758353 0 0 0 21.532418 7.177473h7.177473l98.331378-21.532419a38.758353 38.758353 0 0 0 22.250166-14.354946L945.673263 665.126359a36.605111 36.605111 0 0 0 5.741978-27.274397 37.322859 37.322859 0 0 0-17.943682-20.096924zM675.800285 930.692853l-45.218079 9.330715-4.306484-49.524563 193.791766-262.695505 45.218079 29.427638z m311.50232-399.067489l-103.355608-66.750497a35.887364 35.887364 0 0 0-49.524563 10.048462 35.169617 35.169617 0 0 0 10.766209 49.524562L947.826505 593.35163a34.45187 34.45187 0 0 0 20.096924 5.741979 37.322859 37.322859 0 0 0 30.145386-15.790441 36.605111 36.605111 0 0 0-10.76621-51.677804z" fill="currentColor" p-id="3269"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
29
src/assets/icons/svg/topiam.svg
Normal file
@@ -0,0 +1,29 @@
|
||||
<svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_446_540)">
|
||||
<path d="M113.069 160.072C103.717 170.743 93.0453 180.216 81.5345 188.609C61.5105 174.46 44.3642 156.595 30.9349 135.971C23.5009 124.46 17.2659 112.11 12.4697 99.0407C9.592 91.3668 7.19392 83.3332 5.27545 75.2996C2.03803 61.3907 0.359375 47.0022 0.359375 32.1341C0.359375 30.6953 0.359375 29.1365 0.359375 27.6977C6.35459 23.9806 12.7095 20.7432 19.0644 17.7456C20.7431 32.1341 24.1004 46.043 28.8966 59.3524C31.6544 66.9063 34.7719 74.3404 38.4889 81.4147C44.604 93.5251 52.0381 104.796 60.4314 115.228C75.1796 133.093 92.9254 148.321 113.069 160.072Z" fill="url(#paint0_linear_446_540)"/>
|
||||
<path d="M196.643 67.6256C195.084 76.3786 192.926 84.8918 190.168 93.1652C178.897 91.1269 167.266 90.0477 155.276 90.0477C154.197 90.0477 153.118 90.0477 152.039 90.0477C126.859 90.4074 102.878 95.6832 80.9352 105.036C72.302 94.8439 64.868 83.453 58.9927 71.3427C81.6546 61.8702 106.475 56.7144 132.614 56.7144C141.487 56.7144 150.24 57.3139 158.753 58.5129C171.823 60.1916 184.533 63.3091 196.643 67.6256Z" fill="url(#paint1_linear_446_540)"/>
|
||||
<path d="M199.64 34.0528C199.64 39.2087 199.401 44.3646 199.041 49.4005C186.691 44.1247 173.621 40.048 160.072 37.53C148.321 35.2518 136.211 34.0528 123.981 34.0528C97.7218 34.0528 72.6619 39.3286 49.88 48.9209C42.6858 51.9185 35.7313 55.3958 29.0167 59.2327C24.2205 46.0432 20.8632 32.0144 19.1846 17.6259C26.6186 14.1487 34.2925 11.271 42.2062 8.75301C60.3117 3.11751 79.4964 0 99.4005 0C119.904 0 139.568 3.23741 158.153 9.11272C172.782 13.789 186.691 20.2638 199.52 28.1775C199.64 30.2159 199.64 32.1343 199.64 34.0528Z" fill="url(#paint2_linear_446_540)"/>
|
||||
<path d="M190.168 93.2855C182.494 116.547 170.384 137.65 154.796 155.875C149.76 161.751 144.364 167.386 138.609 172.542C126.858 183.214 113.789 192.446 99.7601 200C93.4052 196.523 87.41 192.686 81.5347 188.609C93.0455 180.336 103.717 170.744 113.069 160.072C117.866 154.676 122.302 148.921 126.499 143.046C137.65 127.098 146.403 109.233 152.158 90.1679C153.237 90.1679 154.316 90.1679 155.396 90.1679C167.146 90.048 178.777 91.1272 190.168 93.2855Z" fill="url(#paint3_linear_446_540)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_446_540" x1="15.8569" y1="27.5782" x2="86.4712" y2="182.06" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#57A4F7"/>
|
||||
<stop offset="1" stop-color="#2158F9"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_446_540" x1="58.9501" y1="80.8427" x2="196.648" y2="80.8427" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#2158F9"/>
|
||||
<stop offset="1" stop-color="#33E1E5"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_446_540" x1="19.1564" y1="29.6353" x2="199.647" y2="29.6353" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#255DF9"/>
|
||||
<stop offset="1" stop-color="#7C35BA"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_446_540" x1="95.3808" y1="192.567" x2="174.674" y2="97.4815" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#54A0F7"/>
|
||||
<stop offset="1" stop-color="#2158F9"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_446_540">
|
||||
<rect width="200" height="200" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
1
src/assets/icons/svg/waiting.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1716005941920" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6808" width="200" height="200"><path d="M739.555556 512a256 256 0 1 1 0 512 256 256 0 0 1 0-512z m18.887111-512a180.167111 180.167111 0 0 1 179.882666 169.870222l0.284445 10.24v311.068445a28.444444 28.444444 0 0 1-56.433778 5.12l-0.455111-5.12V180.110222a123.278222 123.278222 0 0 0-114.460445-122.936889l-8.817777-0.341333H209.237333a123.278222 123.278222 0 0 0-122.993777 114.460444l-0.284445 8.817778v662.641778c0 65.080889 50.460444 118.385778 114.460445 122.88l8.817777 0.341333h283.875556a28.444444 28.444444 0 0 1 5.12 56.433778l-5.12 0.455111h-283.875556a180.167111 180.167111 0 0 1-179.882666-169.927111l-0.284445-10.24V180.167111A180.167111 180.167111 0 0 1 198.997333 0.227556L209.237333 0h549.205334zM739.555556 568.888889a199.111111 199.111111 0 1 0 0 398.222222 199.111111 199.111111 0 0 0 0-398.222222z m115.712 314.026667a14.222222 14.222222 0 0 1 0 28.444444h-222.890667a14.222222 14.222222 0 0 1 0-28.444444h222.890667z m-45.738667-227.555556a74.126222 74.126222 0 0 1-37.660445 95.459556v24.234666c0 6.257778 5.12 11.377778 11.377778 11.377778h51.313778c19.057778-0.170667 34.645333 16.042667 34.929778 36.295111v25.486222a11.377778 11.377778 0 0 1-11.377778 11.377778h-228.579556a11.377778 11.377778 0 0 1-11.377777-11.377778v-25.486222c0.512-20.48 16.213333-36.693333 35.271111-36.295111h51.143111a11.377778 11.377778 0 0 0 11.377778-11.377778v-24.291555c-16.440889-7.793778-32.426667-21.674667-39.253334-38.570667a73.500444 73.500444 0 0 1 36.295111-95.459556c35.328-16.497778 81.123556 0.967111 96.540445 38.684445zM360.789333 682.666667a28.444444 28.444444 0 0 1 5.12 56.433777l-5.12 0.455112H199.111111a28.444444 28.444444 0 0 1-5.12-56.433778L199.111111 682.666667h161.678222z m113.777778-227.555556a28.444444 28.444444 0 0 1 5.12 56.433778L474.510222 512H199.111111a28.444444 28.444444 0 0 1-5.12-56.433778L199.111111 455.111111h275.456zM768 227.555556a28.444444 28.444444 0 0 1 5.12 56.433777L768 284.444444H199.111111a28.444444 28.444444 0 0 1-5.12-56.433777L199.111111 227.555556h568.888889z" fill="currentColor" p-id="6809"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
1
src/assets/icons/svg/workflow.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1716004936483" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2712" width="200" height="200"><path d="M1024.99477 113.778v227.555a57.458 57.458 0 0 1-58.027 56.89H734.86277a57.458 57.458 0 0 1-58.027-56.89v-56.889H560.83877v455.112h115.996v-56.89a57.458 57.458 0 0 1 58.027-56.888h231.936a57.458 57.458 0 0 1 58.197 56.889v227.555a57.458 57.458 0 0 1-58.027 56.89H734.86277a57.458 57.458 0 0 1-58.027-56.89v-56.889H502.86877a57.458 57.458 0 0 1-58.027-56.889V568.89L274.51677 735.972a46.763 46.763 0 0 1-65.252 0l-195.754-192a44.658 44.658 0 0 1 0-64l195.754-192.057a46.763 46.763 0 0 1 65.252 0L445.01277 455.11V227.556a57.458 57.458 0 0 1 58.027-56.89h173.966v-56.888a57.458 57.458 0 0 1 58.026-56.89h231.936a57.458 57.458 0 0 1 58.027 56.89z" fill="currentColor" p-id="2713"></path></svg>
|
||||
|
After Width: | Height: | Size: 844 B |
@@ -1,4 +1,4 @@
|
||||
@import './variables.module.scss';
|
||||
@use './variables.module.scss' as *;
|
||||
|
||||
@mixin colorBtn($color) {
|
||||
background: $color;
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
// cover some element-ui styles
|
||||
.el-collapse {
|
||||
.collapse__title {
|
||||
font-weight: 600;
|
||||
padding: 0 8px;
|
||||
font-size: 1.2em;
|
||||
line-height: 1.1em;
|
||||
}
|
||||
.el-collapse-item__content {
|
||||
padding: 0 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.el-divider--horizontal {
|
||||
margin-bottom: 10px;
|
||||
@@ -68,6 +78,12 @@
|
||||
.el-dialog__body {
|
||||
padding: 15px !important;
|
||||
}
|
||||
.el-dialog__header {
|
||||
padding: 16px 16px 8px 16px;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 1px solid var(--brder-color);
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,3 +130,24 @@
|
||||
.el-dropdown .el-dropdown-link {
|
||||
color: var(--el-color-primary) !important;
|
||||
}
|
||||
|
||||
/* 当 el-form 的 inline 属性为 true 时 */
|
||||
/* 设置 label 的宽度默认为 68px */
|
||||
.el-form--inline .el-form-item__label {
|
||||
width: 68px;
|
||||
}
|
||||
|
||||
/* 设置 el-select 的宽度默认为 240px */
|
||||
.el-form--inline .el-select {
|
||||
width: 240px;
|
||||
}
|
||||
|
||||
/* 设置 el-input 的宽度默认为 240px */
|
||||
.el-form--inline .el-input {
|
||||
width: 240px;
|
||||
}
|
||||
|
||||
/* 设置 el-message-box 消息弹框内容强制换行 */
|
||||
.el-message-box .el-message-box__message {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
@import './variables.module.scss';
|
||||
@import './mixin.scss';
|
||||
@import './transition.scss';
|
||||
@import './element-ui.scss';
|
||||
@import './sidebar.scss';
|
||||
@import './btn.scss';
|
||||
@import './ruoyi.scss';
|
||||
@import 'animate.css';
|
||||
@import 'element-plus/dist/index.css';
|
||||
@use './variables.module.scss' as *;
|
||||
@use './mixin.scss';
|
||||
@use './transition.scss';
|
||||
@use './element-ui.scss';
|
||||
@use './sidebar.scss';
|
||||
@use './btn.scss';
|
||||
@use './ruoyi.scss';
|
||||
@use 'animate.css';
|
||||
@use 'element-plus/dist/index.css';
|
||||
|
||||
body {
|
||||
height: 100%;
|
||||
@@ -14,7 +14,14 @@ body {
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeLegibility;
|
||||
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
|
||||
font-family:
|
||||
Helvetica Neue,
|
||||
Helvetica,
|
||||
PingFang SC,
|
||||
Hiragino Sans GB,
|
||||
Microsoft YaHei,
|
||||
Arial,
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
label {
|
||||
@@ -155,10 +162,6 @@ aside {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@use './variables.module.scss' as *;
|
||||
|
||||
/**
|
||||
* 通用css样式布局处理
|
||||
* Copyright (c) 2019 ruoyi
|
||||
@@ -114,10 +116,9 @@ h6 {
|
||||
|
||||
/** 表格布局 **/
|
||||
.pagination-container {
|
||||
// position: relative;
|
||||
height: 25px;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 15px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 20px;
|
||||
padding: 10px 20px !important;
|
||||
}
|
||||
|
||||
@@ -130,11 +131,6 @@ h6 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pagination-container .el-pagination {
|
||||
//right: 0;
|
||||
//position: absolute;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.pagination-container .el-pagination > .el-pagination__jump {
|
||||
display: none !important;
|
||||
@@ -201,8 +197,6 @@ h6 {
|
||||
}
|
||||
|
||||
.card-box {
|
||||
padding-right: 15px;
|
||||
padding-left: 15px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@use './variables.module.scss' as *;
|
||||
|
||||
#app {
|
||||
.main-container {
|
||||
height: 100%;
|
||||
@@ -24,11 +26,14 @@
|
||||
z-index: 1001;
|
||||
overflow: hidden;
|
||||
-webkit-box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35);
|
||||
box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35);
|
||||
box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.1);
|
||||
|
||||
// reset element-ui css
|
||||
.horizontal-collapse-transition {
|
||||
transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;
|
||||
transition:
|
||||
0s width ease-in-out,
|
||||
0s padding-left ease-in-out,
|
||||
0s padding-right ease-in-out;
|
||||
}
|
||||
|
||||
.scrollbar-wrapper {
|
||||
@@ -60,7 +65,7 @@
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
margin-right: 16px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.el-menu {
|
||||
@@ -83,12 +88,16 @@
|
||||
// menu hover
|
||||
.theme-dark .sub-menu-title-noDropdown,
|
||||
.theme-dark .el-sub-menu__title {
|
||||
border-radius: 8px;
|
||||
margin: 1px 5px 1px 5px;
|
||||
&:hover {
|
||||
background-color: $base-sub-menu-title-hover !important;
|
||||
}
|
||||
}
|
||||
.sub-menu-title-noDropdown,
|
||||
.el-sub-menu__title {
|
||||
border-radius: 8px;
|
||||
margin: 1px 5px 1px 5px;
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.05) !important;
|
||||
}
|
||||
@@ -100,38 +109,66 @@
|
||||
|
||||
& .nest-menu .el-sub-menu > .el-sub-menu__title,
|
||||
& .el-sub-menu .el-menu-item {
|
||||
min-width: $base-sidebar-width !important;
|
||||
&:hover {
|
||||
min-width: calc($base-sidebar-width - 20px) !important;
|
||||
border-radius: 8px;
|
||||
height: 45px;
|
||||
margin: 1px 5px 1px 5px;
|
||||
&:not(.is-active):hover {
|
||||
background-color: rgba(0, 0, 0, 0.1) !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
& .theme-dark .nest-menu .el-sub-menu > .el-sub-menu__title,
|
||||
& .theme-dark .el-sub-menu .el-menu-item {
|
||||
background-color: $base-sub-menu-background !important;
|
||||
border-radius: 8px;
|
||||
height: 45px;
|
||||
margin: 1px 5px 1px 5px;
|
||||
|
||||
&:hover {
|
||||
&.is-active {
|
||||
background-color: var(--el-menu-active-color) !important;
|
||||
color: #fff;
|
||||
}
|
||||
&:not(.is-active):hover {
|
||||
// you can use $sub-menuHover
|
||||
background-color: $base-sub-menu-hover !important;
|
||||
}
|
||||
}
|
||||
|
||||
& .theme-dark .nest-menu .el-sub-menu > .el-sub-menu__title,
|
||||
& .theme-dark .el-menu-item {
|
||||
&:hover {
|
||||
border-radius: 8px;
|
||||
height: 45px;
|
||||
margin: 1px 5px 1px 5px;
|
||||
|
||||
&.is-active {
|
||||
background-color: var(--el-menu-active-color) !important;
|
||||
color: #fff;
|
||||
}
|
||||
&:not(.is-active):hover {
|
||||
// you can use $sub-menuHover
|
||||
background-color: $base-menu-hover !important;
|
||||
}
|
||||
}
|
||||
|
||||
& .nest-menu .el-sub-menu > .el-sub-menu__title,
|
||||
& .el-menu-item {
|
||||
&:hover {
|
||||
border-radius: 8px;
|
||||
height: 45px;
|
||||
margin: 1px 5px 1px 5px;
|
||||
|
||||
&.is-active {
|
||||
background-color: var(--el-menu-active-color) !important;
|
||||
color: #fff;
|
||||
}
|
||||
&:not(.is-active):hover {
|
||||
// you can use $sub-menuHover
|
||||
background-color: rgba(0, 0, 0, 0.04) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 收起菜单后的样式
|
||||
.hideSidebar {
|
||||
.sidebar-container {
|
||||
width: 54px !important;
|
||||
@@ -144,29 +181,48 @@
|
||||
.sub-menu-title-noDropdown {
|
||||
padding: 0 !important;
|
||||
position: relative;
|
||||
height: 45px;
|
||||
// 选中状态的菜单
|
||||
&.is-active {
|
||||
background-color: var(--el-menu-active-color) !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.el-tooltip {
|
||||
padding: 0 !important;
|
||||
|
||||
.svg-icon {
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-sub-menu {
|
||||
|
||||
& .el-sub-menu {
|
||||
overflow: hidden;
|
||||
border-radius: 8px;
|
||||
.el-sub-menu__title.el-tooltip__trigger {
|
||||
border-radius: 8px;
|
||||
height: 45px;
|
||||
}
|
||||
|
||||
// 选中状态的菜单
|
||||
&.is-active .el-sub-menu__title.el-tooltip__trigger {
|
||||
background-color: var(--el-menu-active-color) !important;
|
||||
}
|
||||
|
||||
|
||||
& > .el-sub-menu__title {
|
||||
padding: 0 !important;
|
||||
|
||||
.svg-icon {
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-menu--collapse {
|
||||
.is-active .svg-icon {
|
||||
fill: #fff;
|
||||
}
|
||||
.svg-icon {
|
||||
display: flex;
|
||||
margin: auto;
|
||||
height: 100%;
|
||||
// 这里设置width会跟随sidebar-container的transition 不符合预期
|
||||
}
|
||||
.el-sub-menu {
|
||||
& > .el-sub-menu__title {
|
||||
& > span {
|
||||
@@ -228,3 +284,13 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
// 收起菜单后悬浮的菜单样式
|
||||
.el-popper.is-pure{
|
||||
border-radius: 8px;
|
||||
.el-menu--popup{
|
||||
border-radius: 8px;
|
||||
}
|
||||
.el-menu-item{
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
transition: opacity 0.28s;
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-enter-from,
|
||||
.fade-leave-active {
|
||||
opacity: 0;
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
.fade-transform-enter {
|
||||
.fade-transform-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateX(-30px);
|
||||
}
|
||||
@@ -34,7 +34,7 @@
|
||||
transition: all 0.5s;
|
||||
}
|
||||
|
||||
.breadcrumb-enter,
|
||||
.breadcrumb-enter-from,
|
||||
.breadcrumb-leave-active {
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 全局SCSS变量
|
||||
:root {
|
||||
--menuBg: #304156;
|
||||
--menuBg: #1f2d3d;
|
||||
--menuColor: #bfcbd9;
|
||||
--menuActiveText: #f4f4f5;
|
||||
--menuHover: #263445;
|
||||
@@ -10,10 +10,23 @@
|
||||
--subMenuHover: #001528;
|
||||
--subMenuTitleHover: #293444;
|
||||
|
||||
// 菜单栏缩进
|
||||
--el-menu-base-level-padding: 12px;
|
||||
--el-menu-level-padding: 8px;
|
||||
--el-menu-item-height: 50px;
|
||||
|
||||
--fixedHeaderBg: #ffffff;
|
||||
--tableHeaderBg: #f8f8f9;
|
||||
--tableHeaderTextColor: #515a6e;
|
||||
|
||||
// ele
|
||||
--brder-color: #e8e8e8;
|
||||
|
||||
// 添加 tag 相关变量
|
||||
--tags-view-active-bg: var(--el-color-primary);
|
||||
--tags-view-active-border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
html.dark {
|
||||
--menuBg: #1d1e1f;
|
||||
--menuColor: #bfcbd9;
|
||||
@@ -33,6 +46,40 @@ html.dark {
|
||||
.el-tree-node__content {
|
||||
--el-color-primary-light-9: #262727;
|
||||
}
|
||||
|
||||
.el-button--primary {
|
||||
--el-button-bg-color: var(--el-color-primary-dark-6);
|
||||
--el-button-border-color: var(--el-color-primary-light-2);
|
||||
}
|
||||
|
||||
.el-switch {
|
||||
--el-switch-on-color: var(--el-color-primary-dark-6);
|
||||
--el-switch-border-color: var(--el-color-primary-light-2);
|
||||
}
|
||||
|
||||
.el-tag--primary {
|
||||
--el-tag-bg-color: var(--el-color-primary-dark-6);
|
||||
--el-tag-border-color: var(--el-color-primary-light-2);
|
||||
}
|
||||
|
||||
// 在深色模式下使用更深的颜色
|
||||
--tags-view-active-bg: var(--el-color-primary-dark-6);
|
||||
--tags-view-active-border-color: var(--el-color-primary-light-2);
|
||||
// vxe-table 主题
|
||||
--vxe-font-color: #98989e;
|
||||
--vxe-primary-color: #2c7ecf;
|
||||
--vxe-icon-background-color: #98989e;
|
||||
--vxe-table-font-color: #98989e;
|
||||
--vxe-table-resizable-color: #95969a;
|
||||
--vxe-table-header-background-color: #28282a;
|
||||
--vxe-table-body-background-color: #151518;
|
||||
--vxe-table-background-color: #4a5663;
|
||||
--vxe-table-border-width: 1px;
|
||||
--vxe-table-border-color: #37373a;
|
||||
--vxe-toolbar-background-color: #37373a;
|
||||
|
||||
// ele
|
||||
--brder-color: #37373a;
|
||||
}
|
||||
|
||||
// base color
|
||||
@@ -90,4 +137,4 @@ $base-sidebar-width: 200px;
|
||||
dangerColor: $--color-danger;
|
||||
infoColor: $--color-info;
|
||||
warningColor: $--color-warning;
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,7 @@
|
||||
<el-breadcrumb class="app-breadcrumb" separator="/">
|
||||
<transition-group name="breadcrumb">
|
||||
<el-breadcrumb-item v-for="(item, index) in levelList" :key="item.path">
|
||||
<span v-if="item.redirect === 'noRedirect' || index == levelList.length - 1" class="no-redirect">{{
|
||||
item.meta?.title }}</span>
|
||||
<span v-if="item.redirect === 'noRedirect' || index == levelList.length - 1" class="no-redirect">{{ item.meta?.title }}</span>
|
||||
<a v-else @click.prevent="handleLink(item)">{{ item.meta?.title }}</a>
|
||||
</el-breadcrumb-item>
|
||||
</transition-group>
|
||||
@@ -11,42 +10,69 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { RouteLocationMatched } from 'vue-router'
|
||||
import { RouteLocationMatched } from 'vue-router';
|
||||
import { usePermissionStore } from '@/store/modules/permission';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const levelList = ref<RouteLocationMatched[]>([])
|
||||
const permissionStore = usePermissionStore();
|
||||
const levelList = ref<RouteLocationMatched[]>([]);
|
||||
|
||||
const getBreadcrumb = () => {
|
||||
// only show routes with meta.title
|
||||
let matched = route.matched.filter(item => item.meta && item.meta.title);
|
||||
const first = matched[0]
|
||||
let matched = [];
|
||||
const pathNum = findPathNum(route.path);
|
||||
// multi-level menu
|
||||
if (pathNum > 2) {
|
||||
const reg = /\/\w+/gi;
|
||||
const pathList = route.path.match(reg).map((item, index) => {
|
||||
if (index !== 0) item = item.slice(1);
|
||||
return item;
|
||||
});
|
||||
getMatched(pathList, permissionStore.defaultRoutes, matched);
|
||||
} else {
|
||||
matched = route.matched.filter((item) => item.meta && item.meta.title);
|
||||
}
|
||||
// 判断是否为首页
|
||||
if (!isDashboard(first)) {
|
||||
matched = ([{ path: '/index', meta: { title: '首页' } }] as any).concat(matched)
|
||||
if (!isDashboard(matched[0])) {
|
||||
matched = [{ path: '/index', meta: { title: '首页' } }].concat(matched);
|
||||
}
|
||||
levelList.value = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
|
||||
}
|
||||
levelList.value = matched.filter((item) => item.meta && item.meta.title && item.meta.breadcrumb !== false);
|
||||
};
|
||||
const findPathNum = (str, char = '/') => {
|
||||
if (typeof str !== 'string' || str.length === 0) return 0;
|
||||
return str.split(char).length - 1;
|
||||
};
|
||||
const getMatched = (pathList, routeList, matched) => {
|
||||
const data = routeList.find((item) => item.path == pathList[0] || (item.name += '').toLowerCase() == pathList[0]);
|
||||
if (data) {
|
||||
matched.push(data);
|
||||
if (data.children && pathList.length) {
|
||||
pathList.shift();
|
||||
getMatched(pathList, data.children, matched);
|
||||
}
|
||||
}
|
||||
};
|
||||
const isDashboard = (route: RouteLocationMatched) => {
|
||||
const name = route && route.name as string
|
||||
const name = route && (route.name as string);
|
||||
if (!name) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
return name.trim() === 'Index'
|
||||
}
|
||||
const handleLink = (item: RouteLocationMatched) => {
|
||||
const { redirect, path } = item
|
||||
redirect ? router.push(redirect as string) : router.push(path)
|
||||
}
|
||||
return name.trim() === 'Index';
|
||||
};
|
||||
const handleLink = (item) => {
|
||||
const { redirect, path } = item;
|
||||
redirect ? router.push(redirect) : router.push(path);
|
||||
};
|
||||
|
||||
watchEffect(() => {
|
||||
// if you go to the redirect page, do not update the breadcrumbs
|
||||
if (route.path.startsWith('/redirect/')) return
|
||||
getBreadcrumb()
|
||||
})
|
||||
if (route.path.startsWith('/redirect/')) return;
|
||||
getBreadcrumb();
|
||||
});
|
||||
onMounted(() => {
|
||||
getBreadcrumb();
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
<!-- 代码构建 -->
|
||||
<script setup lang="ts">
|
||||
|
||||
const props = defineProps({
|
||||
showBtn: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
formJson: {
|
||||
type: Object,
|
||||
default: undefined
|
||||
}
|
||||
})
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const buildRef = ref();
|
||||
const emits = defineEmits(['reJson', 'saveDesign']);
|
||||
|
||||
|
||||
|
||||
//获取表单json
|
||||
const getJson = () => {
|
||||
const formJson = JSON.stringify(buildRef.value.getFormJson())
|
||||
const fieldJson = JSON.stringify(buildRef.value.getFieldWidgets())
|
||||
let data = {
|
||||
formJson, fieldJson
|
||||
}
|
||||
emits("saveDesign", data)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (props.formJson) {
|
||||
buildRef.value.setFormJson(props.formJson)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<v-form-designer
|
||||
class="build"
|
||||
ref="buildRef"
|
||||
:designer-config="{ importJsonButton: true, exportJsonButton: true, exportCodeButton: true, generateSFCButton: true, formTemplates: true }"
|
||||
>
|
||||
<template #customToolButtons v-if="showBtn">
|
||||
<el-button link type="primary" icon="Select" @click="getJson">保存</el-button>
|
||||
</template>
|
||||
</v-form-designer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.build {
|
||||
margin: 0 !important;
|
||||
overflow-y: auto !important;
|
||||
|
||||
& header.main-header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
& .right-toolbar-con {
|
||||
text-align: right !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,62 +0,0 @@
|
||||
<!-- 动态表单渲染 -->
|
||||
<script setup name="Render">
|
||||
|
||||
const props = defineProps({
|
||||
formJson: {
|
||||
type: [String, Object],
|
||||
default: ""
|
||||
},
|
||||
formData: {
|
||||
type: [String, Object],
|
||||
default: ""
|
||||
},
|
||||
isView: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const vFormRef = ref(null)
|
||||
// 获取表单数据-异步
|
||||
const getFormData = () => {
|
||||
return vFormRef.value.getFormData()
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置表单内容
|
||||
* @param {表单配置} formConf
|
||||
* formConfig:{ formTemplate:表单模板,formData:表单数据,hiddenField:需要隐藏的字段字符串集合,disabledField:需要禁用的自读字符串集合}
|
||||
*/
|
||||
const initForm = (formConf) => {
|
||||
const { formTemplate, formData, hiddenField, disabledField } = toRaw(formConf)
|
||||
if (formTemplate) {
|
||||
vFormRef.value.setFormJson(formTemplate)
|
||||
if (formData) {
|
||||
vFormRef.value.setFormData(formData)
|
||||
}
|
||||
if (disabledField && disabledField.length > 0) {
|
||||
setTimeout(() => {
|
||||
vFormRef.value.disableWidgets(disabledField)
|
||||
}, 200)
|
||||
}
|
||||
if (hiddenField && hiddenField.length > 0) {
|
||||
setTimeout(() => {
|
||||
vFormRef.value.hideWidgets(hiddenField)
|
||||
}, 200)
|
||||
}
|
||||
if (props.isView) {
|
||||
console.log(props.isView)
|
||||
setTimeout(() => {
|
||||
vFormRef.value.disableForm()
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ getFormData, initForm })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="">
|
||||
<v-form-render ref="vFormRef" :form-json="formJson" :form-data="formData" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,20 +1,32 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-for="(item, index) in options">
|
||||
<template v-if="values.includes(item.value)">
|
||||
<span v-if="(item.elTagType === 'default' || item.elTagType === '') && (item.elTagClass === '' || item.elTagClass == null)"
|
||||
:key="item.value" :index="index" :class="item.elTagClass">
|
||||
{{ item.label + " " }}
|
||||
<template v-if="isValueMatch(item.value)">
|
||||
<span
|
||||
v-if="(item.elTagType === 'default' || item.elTagType === '') && (item.elTagClass === '' || item.elTagClass == null)"
|
||||
:key="item.value"
|
||||
:index="index"
|
||||
:class="item.elTagClass"
|
||||
>
|
||||
{{ item.label + ' ' }}
|
||||
</span>
|
||||
<el-tag
|
||||
v-else
|
||||
:disable-transitions="true"
|
||||
:key="item.value + ''"
|
||||
:disable-transitions="true"
|
||||
:index="index"
|
||||
:type="(item.elTagType === 'primary' || item.elTagType === 'default')? '' : item.elTagType"
|
||||
:type="
|
||||
item.elTagType === 'primary' ||
|
||||
item.elTagType === 'success' ||
|
||||
item.elTagType === 'info' ||
|
||||
item.elTagType === 'warning' ||
|
||||
item.elTagType === 'danger'
|
||||
? item.elTagType
|
||||
: 'primary'
|
||||
"
|
||||
:class="item.elTagClass"
|
||||
>
|
||||
{{ item.label + " " }}
|
||||
{{ item.label + ' ' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</template>
|
||||
@@ -25,60 +37,62 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { propTypes } from '@/utils/propTypes';
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
// 数据
|
||||
options: {
|
||||
type: Array as PropType<DictDataOption[]>,
|
||||
default: null,
|
||||
},
|
||||
// 当前的值
|
||||
value: [Number, String, Array] as PropType<number | string | Array<number | string>>,
|
||||
// 当未找到匹配的数据时,显示value
|
||||
showValue: propTypes.bool.def(true),
|
||||
separator: propTypes.string.def(","),
|
||||
interface Props {
|
||||
options: Array<DictDataOption>;
|
||||
value: number | string | Array<number | string>;
|
||||
showValue?: boolean;
|
||||
separator?: string;
|
||||
}
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
showValue: true,
|
||||
separator: ','
|
||||
});
|
||||
|
||||
const values = computed(() => {
|
||||
if (props.value === '' || props.value === null || typeof props.value === "undefined") return []
|
||||
return Array.isArray(props.value) ? props.value.map(item => '' + item) : String(props.value).split(props.separator);
|
||||
if (props.value === '' || props.value === null || typeof props.value === 'undefined') return [];
|
||||
if (typeof props.value === 'number' || typeof props.value === 'boolean') return [props.value]
|
||||
return Array.isArray(props.value) ? props.value.map((item) => '' + item) : String(props.value).split(props.separator);
|
||||
});
|
||||
|
||||
const unmatch = computed(() => {
|
||||
if (props.options?.length == 0 || props.value === '' || props.value === null || typeof props.value === "undefined") return false
|
||||
if (props.options?.length == 0 || props.value === '' || props.value === null || typeof props.value === 'undefined') return false;
|
||||
// 传入值为非数组
|
||||
values.value.forEach(item => {
|
||||
if (!props.options.some(v => v.value === item)) {
|
||||
return true // 如果有未匹配项,将标志设置为true
|
||||
let unmatch = false; // 添加一个标志来判断是否有未匹配项
|
||||
values.value.forEach((item) => {
|
||||
if (!props.options.some((v) => v.value == item)) {
|
||||
unmatch = true; // 如果有未匹配项,将标志设置为true
|
||||
}
|
||||
})
|
||||
return false // 返回标志的值
|
||||
});
|
||||
return unmatch; // 返回标志的值
|
||||
});
|
||||
|
||||
const unmatchArray = computed(() => {
|
||||
// 记录未匹配的项
|
||||
// 记录未匹配的项
|
||||
const itemUnmatchArray: Array<string | number> = [];
|
||||
if (props.value !== '' && props.value !== null && typeof props.value !== "undefined") {
|
||||
values.value.forEach(item => {
|
||||
if (!props.options.some(v => v.value === item)) {
|
||||
if (props.value !== '' && props.value !== null && typeof props.value !== 'undefined') {
|
||||
values.value.forEach((item) => {
|
||||
if (!props.options.some((v) => v.value === item)) {
|
||||
itemUnmatchArray.push(item);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
// 没有value不显示
|
||||
return handleArray(itemUnmatchArray);
|
||||
});
|
||||
|
||||
const handleArray = (array: Array<string | number>) => {
|
||||
if (array.length === 0) return "";
|
||||
if (array.length === 0) return '';
|
||||
return array.reduce((pre, cur) => {
|
||||
return pre + " " + cur;
|
||||
return pre + ' ' + cur;
|
||||
});
|
||||
};
|
||||
|
||||
const isValueMatch = (itemValue: any) => {
|
||||
return values.value.some(val => val == itemValue)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style lang="scss" scoped>
|
||||
.el-tag + .el-tag {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-upload
|
||||
v-if="type === 'url'"
|
||||
:action="upload.url"
|
||||
:before-upload="handleBeforeUpload"
|
||||
:on-success="handleUploadSuccess"
|
||||
@@ -9,28 +10,30 @@
|
||||
name="file"
|
||||
:show-file-list="false"
|
||||
:headers="upload.headers"
|
||||
ref="uploadRef"
|
||||
v-if="type === 'url'"
|
||||
>
|
||||
<i ref="uploadRef"></i>
|
||||
</el-upload>
|
||||
<div class="editor">
|
||||
<quill-editor
|
||||
ref="quillEditorRef"
|
||||
v-model:content="content"
|
||||
contentType="html"
|
||||
@textChange="(e: any) => $emit('update:modelValue', content)"
|
||||
:options="options"
|
||||
:style="styles"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="editor">
|
||||
<quill-editor
|
||||
ref="quillEditorRef"
|
||||
v-model:content="content"
|
||||
content-type="html"
|
||||
:options="options"
|
||||
:style="styles"
|
||||
@text-change="(e: any) => $emit('update:modelValue', content)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { QuillEditor, Quill } from '@vueup/vue-quill';
|
||||
import '@vueup/vue-quill/dist/vue-quill.snow.css';
|
||||
|
||||
import { QuillEditor, Quill } from '@vueup/vue-quill';
|
||||
import { propTypes } from '@/utils/propTypes';
|
||||
import { globalHeaders } from "@/utils/request";
|
||||
import { globalHeaders } from '@/utils/request';
|
||||
|
||||
defineEmits(['update:modelValue']);
|
||||
|
||||
const props = defineProps({
|
||||
/* 编辑器的内容 */
|
||||
@@ -52,46 +55,47 @@ const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const upload = reactive<UploadOption>({
|
||||
headers: globalHeaders(),
|
||||
url: import.meta.env.VITE_APP_BASE_API + '/resource/oss/upload'
|
||||
})
|
||||
});
|
||||
const quillEditorRef = ref();
|
||||
const uploadRef = ref<HTMLDivElement>();
|
||||
|
||||
const options = ref({
|
||||
theme: "snow",
|
||||
const options = ref<any>({
|
||||
theme: 'snow',
|
||||
bounds: document.body,
|
||||
debug: "warn",
|
||||
debug: 'warn',
|
||||
modules: {
|
||||
// 工具栏配置
|
||||
toolbar: {
|
||||
container: [
|
||||
["bold", "italic", "underline", "strike"], // 加粗 斜体 下划线 删除线
|
||||
["blockquote", "code-block"], // 引用 代码块
|
||||
[{ list: "ordered" }, { list: "bullet" }], // 有序、无序列表
|
||||
[{ indent: "-1" }, { indent: "+1" }], // 缩进
|
||||
[{ size: ["small", false, "large", "huge"] }], // 字体大小
|
||||
[{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题
|
||||
[{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色
|
||||
[{ align: [] }], // 对齐方式
|
||||
["clean"], // 清除文本格式
|
||||
["link", "image", "video"] // 链接、图片、视频
|
||||
['bold', 'italic', 'underline', 'strike'], // 加粗 斜体 下划线 删除线
|
||||
['blockquote', 'code-block'], // 引用 代码块
|
||||
[{ list: 'ordered' }, { list: 'bullet' }], // 有序、无序列表
|
||||
[{ indent: '-1' }, { indent: '+1' }], // 缩进
|
||||
[{ size: ['small', false, 'large', 'huge'] }], // 字体大小
|
||||
[{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题
|
||||
[{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色
|
||||
[{ align: [] }], // 对齐方式
|
||||
['clean'], // 清除文本格式
|
||||
['link', 'image', 'video'] // 链接、图片、视频
|
||||
],
|
||||
handlers: {
|
||||
image: function (value: any) {
|
||||
image: (value: boolean) => {
|
||||
if (value) {
|
||||
// 调用element图片上传
|
||||
(document.querySelector(".editor-img-uploader>.el-upload") as HTMLDivElement)?.click();
|
||||
uploadRef.value.click();
|
||||
} else {
|
||||
Quill.format("image", true);
|
||||
Quill.format('image', true);
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
placeholder: "请输入内容",
|
||||
readOnly: props.readOnly,
|
||||
placeholder: '请输入内容',
|
||||
readOnly: props.readOnly
|
||||
});
|
||||
|
||||
const styles = computed(() => {
|
||||
let style: any = {};
|
||||
const style: any = {};
|
||||
if (props.minHeight) {
|
||||
style.minHeight = `${props.minHeight}px`;
|
||||
}
|
||||
@@ -99,37 +103,41 @@ const styles = computed(() => {
|
||||
style.height = `${props.height}px`;
|
||||
}
|
||||
return style;
|
||||
})
|
||||
});
|
||||
|
||||
const content = ref("");
|
||||
watch(() => props.modelValue, (v) => {
|
||||
if (v !== content.value) {
|
||||
content.value = v === undefined ? "<p></p>" : v;
|
||||
}
|
||||
}, { immediate: true });
|
||||
const content = ref('');
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(v: string) => {
|
||||
if (v !== content.value) {
|
||||
content.value = v || '<p></p>';
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 图片上传成功返回图片地址
|
||||
const handleUploadSuccess = (res: any) => {
|
||||
// 如果上传成功
|
||||
if (res.code === 200) {
|
||||
// 获取富文本实例
|
||||
let quill = toRaw(quillEditorRef.value).getQuill();
|
||||
const quill = toRaw(quillEditorRef.value).getQuill();
|
||||
// 获取光标位置
|
||||
let length = quill.selection.savedRange.index;
|
||||
const length = quill.selection.savedRange.index;
|
||||
// 插入图片,res为服务器返回的图片链接地址
|
||||
quill.insertEmbed(length, "image", res.data.url);
|
||||
quill.insertEmbed(length, 'image', res.data.url);
|
||||
// 调整光标到最后
|
||||
quill.setSelection(length + 1);
|
||||
proxy?.$modal.closeLoading();
|
||||
} else {
|
||||
proxy?.$modal.loading(res.msg);
|
||||
proxy?.$modal.msgError('图片插入失败');
|
||||
proxy?.$modal.closeLoading();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 图片上传前拦截
|
||||
const handleBeforeUpload = (file: any) => {
|
||||
const type = ["image/jpeg", "image/jpg", "image/png", "image/svg"];
|
||||
const type = ['image/jpeg', 'image/jpg', 'image/png', 'image/svg'];
|
||||
const isJPG = type.includes(file.type);
|
||||
//检验文件格式
|
||||
if (!isJPG) {
|
||||
@@ -146,13 +154,12 @@ const handleBeforeUpload = (file: any) => {
|
||||
}
|
||||
proxy?.$modal.loading('正在上传文件,请稍候...');
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// 图片失败拦截
|
||||
const handleUploadError = (err: any) => {
|
||||
console.error(err);
|
||||
proxy?.$modal.msgError('上传文件失败');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@@ -167,71 +174,71 @@ const handleUploadError = (err: any) => {
|
||||
.quill-img {
|
||||
display: none;
|
||||
}
|
||||
.ql-snow .ql-tooltip[data-mode="link"]::before {
|
||||
content: "请输入链接地址:";
|
||||
.ql-snow .ql-tooltip[data-mode='link']::before {
|
||||
content: '请输入链接地址:';
|
||||
}
|
||||
.ql-snow .ql-tooltip.ql-editing a.ql-action::after {
|
||||
border-right: 0;
|
||||
content: "保存";
|
||||
content: '保存';
|
||||
padding-right: 0;
|
||||
}
|
||||
.ql-snow .ql-tooltip[data-mode="video"]::before {
|
||||
content: "请输入视频地址:";
|
||||
.ql-snow .ql-tooltip[data-mode='video']::before {
|
||||
content: '请输入视频地址:';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item::before {
|
||||
content: "14px";
|
||||
content: '14px';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before {
|
||||
content: "10px";
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='small']::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='small']::before {
|
||||
content: '10px';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before {
|
||||
content: "18px";
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='large']::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='large']::before {
|
||||
content: '18px';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before {
|
||||
content: "32px";
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='huge']::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='huge']::before {
|
||||
content: '32px';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item::before {
|
||||
content: "文本";
|
||||
content: '文本';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
|
||||
content: "标题1";
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='1']::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='1']::before {
|
||||
content: '标题1';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
|
||||
content: "标题2";
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='2']::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='2']::before {
|
||||
content: '标题2';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
|
||||
content: "标题3";
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='3']::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='3']::before {
|
||||
content: '标题3';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
|
||||
content: "标题4";
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='4']::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='4']::before {
|
||||
content: '标题4';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
|
||||
content: "标题5";
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='5']::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='5']::before {
|
||||
content: '标题5';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
|
||||
content: "标题6";
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='6']::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='6']::before {
|
||||
content: '标题6';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item::before {
|
||||
content: "标准字体";
|
||||
content: '标准字体';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before {
|
||||
content: "衬线字体";
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value='serif']::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value='serif']::before {
|
||||
content: '衬线字体';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before {
|
||||
content: "等宽字体";
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value='monospace']::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value='monospace']::before {
|
||||
content: '等宽字体';
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,41 +1,43 @@
|
||||
<template>
|
||||
<div class="upload-file">
|
||||
<el-upload
|
||||
ref="fileUploadRef"
|
||||
multiple
|
||||
:action="uploadFileUrl"
|
||||
:before-upload="handleBeforeUpload"
|
||||
:file-list="fileList"
|
||||
:limit="limit"
|
||||
:accept="fileAccept"
|
||||
:on-error="handleUploadError"
|
||||
:on-exceed="handleExceed"
|
||||
:on-success="handleUploadSuccess"
|
||||
:show-file-list="false"
|
||||
:headers="headers"
|
||||
class="upload-file-uploader"
|
||||
ref="fileUploadRef"
|
||||
v-if="!disabled"
|
||||
>
|
||||
<!-- 上传按钮 -->
|
||||
<el-button type="primary">选取文件</el-button>
|
||||
</el-upload>
|
||||
<!-- 上传提示 -->
|
||||
<div class="el-upload__tip" v-if="showTip">
|
||||
<div v-if="showTip && !disabled" class="el-upload__tip">
|
||||
请上传
|
||||
<template v-if="fileSize">
|
||||
大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
|
||||
</template>
|
||||
<template v-if="fileType">
|
||||
格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b>
|
||||
格式为 <b style="color: #f56c6c">{{ fileType.join('/') }}</b>
|
||||
</template>
|
||||
的文件
|
||||
</div>
|
||||
<!-- 文件列表 -->
|
||||
<transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul">
|
||||
<li :key="file.uid" class="el-upload-list__item ele-upload-list__item-content" v-for="(file, index) in fileList">
|
||||
<li v-for="(file, index) in fileList" :key="file.uid" class="el-upload-list__item ele-upload-list__item-content">
|
||||
<el-link :href="`${file.url}`" :underline="false" target="_blank">
|
||||
<span class="el-icon-document"> {{ getFileName(file.name) }} </span>
|
||||
</el-link>
|
||||
<div class="ele-upload-list__item-content-action">
|
||||
<el-link :underline="false" @click="handleDelete(index)" type="danger">删除</el-link>
|
||||
<el-button type="danger" v-if="!disabled" link @click="handleDelete(index)">删除</el-button>
|
||||
</div>
|
||||
</li>
|
||||
</transition-group>
|
||||
@@ -43,20 +45,25 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { listByIds, delOss } from "@/api/system/oss";
|
||||
import { propTypes } from '@/utils/propTypes';
|
||||
import { globalHeaders } from "@/utils/request";
|
||||
import { delOss, listByIds } from '@/api/system/oss';
|
||||
import { globalHeaders } from '@/utils/request';
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: [String, Object, Array],
|
||||
// 数量限制
|
||||
limit: propTypes.number.def(5),
|
||||
// 大小限制(MB)
|
||||
fileSize: propTypes.number.def(5),
|
||||
// 文件类型, 例如['png', 'jpg', 'jpeg']
|
||||
fileType: propTypes.array.def(["doc", "xls", "ppt", "txt", "pdf"]),
|
||||
// 是否显示提示
|
||||
isShowTip: propTypes.bool.def(true),
|
||||
modelValue: {
|
||||
type: [String, Object, Array],
|
||||
default: () => []
|
||||
},
|
||||
// 数量限制
|
||||
limit: propTypes.number.def(5),
|
||||
// 大小限制(MB)
|
||||
fileSize: propTypes.number.def(5),
|
||||
// 文件类型, 例如['png', 'jpg', 'jpeg']
|
||||
fileType: propTypes.array.def(['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'pdf']),
|
||||
// 是否显示提示
|
||||
isShowTip: propTypes.bool.def(true),
|
||||
// 禁用组件(仅查看文件)
|
||||
disabled: propTypes.bool.def(false)
|
||||
});
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
@@ -65,153 +72,170 @@ const number = ref(0);
|
||||
const uploadList = ref<any[]>([]);
|
||||
|
||||
const baseUrl = import.meta.env.VITE_APP_BASE_API;
|
||||
const uploadFileUrl = ref(baseUrl + "/resource/oss/upload"); // 上传文件服务器地址
|
||||
const uploadFileUrl = ref(baseUrl + '/resource/oss/upload'); // 上传文件服务器地址
|
||||
const headers = ref(globalHeaders());
|
||||
|
||||
const fileList = ref<any[]>([]);
|
||||
const showTip = computed(
|
||||
() => props.isShowTip && (props.fileType || props.fileSize)
|
||||
);
|
||||
const showTip = computed(() => props.isShowTip && (props.fileType || props.fileSize));
|
||||
|
||||
const fileUploadRef = ref<ElUploadInstance>();
|
||||
|
||||
watch(() => props.modelValue, async val => {
|
||||
// 监听 fileType 变化,更新 fileAccept
|
||||
const fileAccept = computed(() => props.fileType.map((type) => `.${type}`).join(','));
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
async (val) => {
|
||||
if (val) {
|
||||
let temp = 1;
|
||||
// 首先将值转为数组
|
||||
let list = [];
|
||||
if (Array.isArray(val)) {
|
||||
list = val;
|
||||
} else {
|
||||
const res = await listByIds(val as string)
|
||||
list = res.data.map((oss) => {
|
||||
const data = { name: oss.originalName, url: oss.url, ossId: oss.ossId };
|
||||
return data;
|
||||
});
|
||||
}
|
||||
// 然后将数组转为对象数组
|
||||
fileList.value = list.map(item => {
|
||||
item = { name: item.name, url: item.url, ossId: item.ossId };
|
||||
item.uid = item.uid || new Date().getTime() + temp++;
|
||||
return item;
|
||||
let temp = 1;
|
||||
// 首先将值转为数组
|
||||
let list: any[] = [];
|
||||
if (Array.isArray(val)) {
|
||||
list = val;
|
||||
} else {
|
||||
const res = await listByIds(val);
|
||||
list = res.data.map((oss) => {
|
||||
return {
|
||||
name: oss.originalName,
|
||||
url: oss.url,
|
||||
ossId: oss.ossId
|
||||
};
|
||||
});
|
||||
}
|
||||
// 然后将数组转为对象数组
|
||||
fileList.value = list.map((item) => {
|
||||
item = { name: item.name, url: item.url, ossId: item.ossId };
|
||||
item.uid = item.uid || new Date().getTime() + temp++;
|
||||
return item;
|
||||
});
|
||||
} else {
|
||||
fileList.value = [];
|
||||
return [];
|
||||
fileList.value = [];
|
||||
return [];
|
||||
}
|
||||
}, { deep: true, immediate: true });
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
|
||||
// 上传前校检格式和大小
|
||||
const handleBeforeUpload = (file: any) => {
|
||||
// 校检文件类型
|
||||
if (props.fileType.length) {
|
||||
const fileName = file.name.split('.');
|
||||
const fileExt = fileName[fileName.length - 1];
|
||||
const isTypeOk = props.fileType.indexOf(fileExt) >= 0;
|
||||
if (!isTypeOk) {
|
||||
proxy?.$modal.msgError(`文件格式不正确, 请上传${props.fileType.join("/")}格式文件!`);
|
||||
return false;
|
||||
}
|
||||
// 校检文件类型
|
||||
if (props.fileType.length) {
|
||||
const fileName = file.name.split('.');
|
||||
const fileExt = fileName[fileName.length - 1];
|
||||
const isTypeOk = props.fileType.indexOf(fileExt) >= 0;
|
||||
if (!isTypeOk) {
|
||||
proxy?.$modal.msgError(`文件格式不正确, 请上传${props.fileType.join('/')}格式文件!`);
|
||||
return false;
|
||||
}
|
||||
// 校检文件大小
|
||||
if (props.fileSize) {
|
||||
const isLt = file.size / 1024 / 1024 < props.fileSize;
|
||||
if (!isLt) {
|
||||
proxy?.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// 校检文件名是否包含特殊字符
|
||||
if (file.name.includes(',')) {
|
||||
proxy?.$modal.msgError('文件名不正确,不能包含英文逗号!');
|
||||
return false;
|
||||
}
|
||||
// 校检文件大小
|
||||
if (props.fileSize) {
|
||||
const isLt = file.size / 1024 / 1024 < props.fileSize;
|
||||
if (!isLt) {
|
||||
proxy?.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`);
|
||||
return false;
|
||||
}
|
||||
proxy?.$modal.loading("正在上传文件,请稍候...");
|
||||
number.value++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
proxy?.$modal.loading('正在上传文件,请稍候...');
|
||||
number.value++;
|
||||
return true;
|
||||
};
|
||||
|
||||
// 文件个数超出
|
||||
const handleExceed = () => {
|
||||
proxy?.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`);
|
||||
}
|
||||
proxy?.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`);
|
||||
};
|
||||
|
||||
// 上传失败
|
||||
const handleUploadError = () => {
|
||||
proxy?.$modal.msgError("上传文件失败");
|
||||
}
|
||||
proxy?.$modal.msgError('上传文件失败');
|
||||
};
|
||||
|
||||
// 上传成功回调
|
||||
const handleUploadSuccess = (res: any, file: UploadFile) => {
|
||||
if (res.code === 200) {
|
||||
uploadList.value.push({ name: res.data.fileName, url: res.data.url, ossId: res.data.ossId });
|
||||
uploadedSuccessfully();
|
||||
} else {
|
||||
number.value--;
|
||||
proxy?.$modal.closeLoading();
|
||||
proxy?.$modal.msgError(res.msg);
|
||||
fileUploadRef.value?.handleRemove(file);
|
||||
uploadedSuccessfully();
|
||||
}
|
||||
}
|
||||
if (res.code === 200) {
|
||||
uploadList.value.push({
|
||||
name: res.data.fileName,
|
||||
url: res.data.url,
|
||||
ossId: res.data.ossId
|
||||
});
|
||||
uploadedSuccessfully();
|
||||
} else {
|
||||
number.value--;
|
||||
proxy?.$modal.closeLoading();
|
||||
proxy?.$modal.msgError(res.msg);
|
||||
fileUploadRef.value?.handleRemove(file);
|
||||
uploadedSuccessfully();
|
||||
}
|
||||
};
|
||||
|
||||
// 删除文件
|
||||
const handleDelete = (index: number) => {
|
||||
let ossId = fileList.value[index].ossId;
|
||||
delOss(ossId);
|
||||
fileList.value.splice(index, 1);
|
||||
emit("update:modelValue", listToString(fileList.value));
|
||||
}
|
||||
const ossId = fileList.value[index].ossId;
|
||||
delOss(ossId);
|
||||
fileList.value.splice(index, 1);
|
||||
emit('update:modelValue', listToString(fileList.value));
|
||||
};
|
||||
|
||||
// 上传结束处理
|
||||
const uploadedSuccessfully = () => {
|
||||
if (number.value > 0 && uploadList.value.length === number.value) {
|
||||
fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value);
|
||||
uploadList.value = [];
|
||||
number.value = 0;
|
||||
emit("update:modelValue", listToString(fileList.value));
|
||||
proxy?.$modal.closeLoading();
|
||||
}
|
||||
}
|
||||
if (number.value > 0 && uploadList.value.length === number.value) {
|
||||
fileList.value = fileList.value.filter((f) => f.url !== undefined).concat(uploadList.value);
|
||||
uploadList.value = [];
|
||||
number.value = 0;
|
||||
emit('update:modelValue', listToString(fileList.value));
|
||||
proxy?.$modal.closeLoading();
|
||||
}
|
||||
};
|
||||
|
||||
// 获取文件名称
|
||||
const getFileName = (name: string) => {
|
||||
// 如果是url那么取最后的名字 如果不是直接返回
|
||||
if (name.lastIndexOf("/") > -1) {
|
||||
return name.slice(name.lastIndexOf("/") + 1);
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
// 如果是url那么取最后的名字 如果不是直接返回
|
||||
if (name.lastIndexOf('/') > -1) {
|
||||
return name.slice(name.lastIndexOf('/') + 1);
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
};
|
||||
|
||||
// 对象转成指定字符串分隔
|
||||
const listToString = (list: any[], separator?: string) => {
|
||||
let strs = "";
|
||||
separator = separator || ",";
|
||||
list.forEach(item => {
|
||||
if (item.ossId) {
|
||||
strs += item.ossId + separator;
|
||||
}
|
||||
})
|
||||
return strs != "" ? strs.substring(0, strs.length - 1) : "";
|
||||
}
|
||||
let strs = '';
|
||||
separator = separator || ',';
|
||||
list.forEach((item) => {
|
||||
if (item.ossId) {
|
||||
strs += item.ossId + separator;
|
||||
}
|
||||
});
|
||||
return strs != '' ? strs.substring(0, strs.length - 1) : '';
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
.upload-file-uploader {
|
||||
margin-bottom: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.upload-file-list .el-upload-list__item {
|
||||
border: 1px solid #e4e7ed;
|
||||
line-height: 2;
|
||||
margin-bottom: 10px;
|
||||
position: relative;
|
||||
border: 1px solid #e4e7ed;
|
||||
line-height: 2;
|
||||
margin-bottom: 10px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.upload-file-list .ele-upload-list__item-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: inherit;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.ele-upload-list__item-content-action .el-link {
|
||||
margin-right: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div style="padding: 0 15px;" @click="toggleClick">
|
||||
<div style="padding: 0 15px" @click="toggleClick">
|
||||
<svg :class="{ 'is-active': isActive }" class="hamburger" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="64" height="64">
|
||||
<path
|
||||
d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z"
|
||||
@@ -13,12 +13,12 @@ import { propTypes } from '@/utils/propTypes';
|
||||
|
||||
defineProps({
|
||||
isActive: propTypes.bool.def(false)
|
||||
})
|
||||
});
|
||||
|
||||
const emit = defineEmits(['toggleClick'])
|
||||
const emit = defineEmits(['toggleClick']);
|
||||
const toggleClick = () => {
|
||||
emit('toggleClick');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -1,193 +0,0 @@
|
||||
<template>
|
||||
<div :class="{ 'show': show }" class="header-search">
|
||||
<svg-icon class-name="search-icon" icon-class="search" @click.stop="click"/>
|
||||
<el-select
|
||||
ref="headerSearchSelectRef"
|
||||
v-model="search"
|
||||
:remote-method="querySearch"
|
||||
filterable
|
||||
default-first-option
|
||||
remote
|
||||
placeholder="Search"
|
||||
class="header-search-select"
|
||||
@change="change"
|
||||
>
|
||||
<el-option v-for="option in options" :key="option.item.path" :value="option.item"
|
||||
:label="option.item.title.join(' > ')"/>
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="HeaderSearch">
|
||||
import Fuse from 'fuse.js';
|
||||
import {getNormalPath} from '@/utils/ruoyi';
|
||||
import {isHttp} from '@/utils/validate';
|
||||
import usePermissionStore from '@/store/modules/permission';
|
||||
import {RouteOption} from 'vue-router';
|
||||
|
||||
type Router = Array<{
|
||||
path: string;
|
||||
title: string[];
|
||||
}>
|
||||
|
||||
const search = ref('');
|
||||
const options = ref<any>([]);
|
||||
const searchPool = ref<Router>([]);
|
||||
const show = ref(false);
|
||||
const fuse = ref();
|
||||
const headerSearchSelectRef = ref<ElSelectInstance>();
|
||||
const router = useRouter();
|
||||
const routes = computed(() => usePermissionStore().routes);
|
||||
|
||||
const click = () => {
|
||||
show.value = !show.value
|
||||
if (show.value) {
|
||||
headerSearchSelectRef.value && headerSearchSelectRef.value.focus()
|
||||
}
|
||||
};
|
||||
const close = () => {
|
||||
headerSearchSelectRef.value && headerSearchSelectRef.value.blur()
|
||||
options.value = []
|
||||
show.value = false
|
||||
}
|
||||
const change = (val: any) => {
|
||||
const path = val.path;
|
||||
const query = val.query;
|
||||
if (isHttp(path)) {
|
||||
// http(s):// 路径新窗口打开
|
||||
const pindex = path.indexOf("http");
|
||||
window.open(path.substr(pindex, path.length), "_blank");
|
||||
} else {
|
||||
if (query) {
|
||||
router.push({ path: path, query: JSON.parse(query) });
|
||||
} else {
|
||||
router.push(path)
|
||||
}
|
||||
}
|
||||
search.value = ''
|
||||
options.value = []
|
||||
nextTick(() => {
|
||||
show.value = false
|
||||
})
|
||||
}
|
||||
const initFuse = (list: Router) => {
|
||||
fuse.value = new Fuse(list, {
|
||||
shouldSort: true,
|
||||
threshold: 0.4,
|
||||
location: 0,
|
||||
distance: 100,
|
||||
minMatchCharLength: 1,
|
||||
keys: [{
|
||||
name: 'title',
|
||||
weight: 0.7
|
||||
}, {
|
||||
name: 'path',
|
||||
weight: 0.3
|
||||
}]
|
||||
})
|
||||
}
|
||||
// Filter out the routes that can be displayed in the sidebar
|
||||
// And generate the internationalized title
|
||||
const generateRoutes = (routes: RouteOption[], basePath = '', prefixTitle: string[] = []) => {
|
||||
let res: Router = []
|
||||
routes.forEach(r => {
|
||||
// skip hidden router
|
||||
if (!r.hidden) {
|
||||
const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path;
|
||||
const data = {
|
||||
path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,
|
||||
title: [...prefixTitle],
|
||||
query: ''
|
||||
}
|
||||
if (r.meta && r.meta.title) {
|
||||
data.title = [...data.title, r.meta.title];
|
||||
if (r.redirect !== 'noRedirect') {
|
||||
// only push the routes with title
|
||||
// special case: need to exclude parent router without redirect
|
||||
res.push(data);
|
||||
}
|
||||
}
|
||||
|
||||
if (r.query) {
|
||||
data.query = r.query
|
||||
}
|
||||
|
||||
// recursive child routes
|
||||
if (r.children) {
|
||||
const tempRoutes = generateRoutes(r.children, data.path, data.title);
|
||||
if (tempRoutes.length >= 1) {
|
||||
res = [...res, ...tempRoutes];
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
return res;
|
||||
}
|
||||
const querySearch = (query: string) => {
|
||||
if (query !== '') {
|
||||
options.value = fuse.value.search(query)
|
||||
} else {
|
||||
options.value = []
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
searchPool.value = generateRoutes(routes.value);
|
||||
})
|
||||
|
||||
// watchEffect(() => {
|
||||
// searchPool.value = generateRoutes(routes.value)
|
||||
// })
|
||||
|
||||
watch(show, (value) => {
|
||||
if (value) {
|
||||
document.body.addEventListener('click', close)
|
||||
} else {
|
||||
document.body.removeEventListener('click', close)
|
||||
}
|
||||
})
|
||||
|
||||
watch(searchPool, (list) => {
|
||||
initFuse(list)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.header-search {
|
||||
font-size: 0 !important;
|
||||
|
||||
.search-icon {
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.header-search-select {
|
||||
font-size: 18px;
|
||||
transition: width 0.2s;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
background: transparent;
|
||||
border-radius: 0;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
|
||||
:deep(.el-input__inner) {
|
||||
border-radius: 0;
|
||||
border: 0;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
box-shadow: none !important;
|
||||
border-bottom: 1px solid #d9d9d9;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
&.show {
|
||||
.header-search-select {
|
||||
width: 210px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="relative" :style="{ width: width }">
|
||||
<el-input v-model="modelValue" readonly @click="visible = !visible" placeholder="点击选择图标">
|
||||
<div class="relative" :style="{ 'width': width }">
|
||||
<el-input v-model="modelValue" readonly placeholder="点击选择图标" @click="visible = !visible">
|
||||
<template #prepend>
|
||||
<svg-icon :icon-class="modelValue" />
|
||||
</template>
|
||||
@@ -8,18 +8,18 @@
|
||||
|
||||
<el-popover shadow="none" :visible="visible" placement="bottom-end" trigger="click" :width="450">
|
||||
<template #reference>
|
||||
<div @click="visible = !visible" class="cursor-pointer text-[#999] absolute right-[10px] top-0 height-[32px] leading-[32px]">
|
||||
<div class="cursor-pointer text-[#999] absolute right-[10px] top-0 height-[32px] leading-[32px]" @click="visible = !visible">
|
||||
<i-ep-caret-top v-show="visible"></i-ep-caret-top>
|
||||
<i-ep-caret-bottom v-show="!visible"></i-ep-caret-bottom>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-input class="p-2" v-model="filterValue" placeholder="搜索图标" clearable @input="filterIcons" />
|
||||
<el-input v-model="filterValue" class="p-2" placeholder="搜索图标" clearable @input="filterIcons" />
|
||||
|
||||
<el-scrollbar height="w-[200px]">
|
||||
<ul class="icon-list">
|
||||
<el-tooltip v-for="(iconName, index) in iconNames" :key="index" :content="iconName" placement="bottom" effect="light">
|
||||
<li :class="['icon-item', {active: modelValue == iconName}]" @click="selectedIcon(iconName)">
|
||||
<li :class="['icon-item', { active: modelValue == iconName }]" @click="selectedIcon(iconName)">
|
||||
<svg-icon color="var(--el-text-color-regular)" :icon-class="iconName" />
|
||||
</li>
|
||||
</el-tooltip>
|
||||
@@ -50,13 +50,11 @@ const filterValue = ref('');
|
||||
*/
|
||||
const filterIcons = () => {
|
||||
if (filterValue.value) {
|
||||
iconNames.value = icons.filter(iconName =>
|
||||
iconName.includes(filterValue.value)
|
||||
);
|
||||
iconNames.value = icons.filter((iconName) => iconName.includes(filterValue.value));
|
||||
} else {
|
||||
iconNames.value = icons;
|
||||
}
|
||||
}
|
||||
};
|
||||
/**
|
||||
* 选择图标
|
||||
* @param iconName 选择的图标名称
|
||||
@@ -64,12 +62,12 @@ const filterIcons = () => {
|
||||
const selectedIcon = (iconName: string) => {
|
||||
emit('update:modelValue', iconName);
|
||||
visible.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
.el-scrollbar {
|
||||
max-height: calc(50vh - 100px)!important;
|
||||
max-height: calc(50vh - 100px) !important;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.el-divider--horizontal {
|
||||
@@ -99,8 +97,8 @@ const selectedIcon = (iconName: string) => {
|
||||
}
|
||||
}
|
||||
.active {
|
||||
border-color: var(--el-color-primary);
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
border-color: var(--el-color-primary);
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -15,11 +15,11 @@ const props = defineProps({
|
||||
src: propTypes.string.def(''),
|
||||
width: {
|
||||
type: [Number, String],
|
||||
default: ""
|
||||
default: ''
|
||||
},
|
||||
height: {
|
||||
type: [Number, String],
|
||||
default: ""
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
@@ -27,29 +27,28 @@ const realSrc = computed(() => {
|
||||
if (!props.src) {
|
||||
return;
|
||||
}
|
||||
let real_src = props.src.split(",")[0];
|
||||
const real_src = props.src.split(',')[0];
|
||||
return real_src;
|
||||
});
|
||||
|
||||
const realSrcList = computed(() => {
|
||||
if (!props.src) {
|
||||
return;
|
||||
return [];
|
||||
}
|
||||
let real_src_list = props.src.split(",");
|
||||
let srcList: string[] = [];
|
||||
real_src_list.forEach(item => {
|
||||
const real_src_list = props.src.split(',');
|
||||
const srcList: string[] = [];
|
||||
real_src_list.forEach((item: string) => {
|
||||
if (item.trim() === '') {
|
||||
return;
|
||||
}
|
||||
return srcList.push(item);
|
||||
});
|
||||
return srcList;
|
||||
});
|
||||
|
||||
const realWidth = computed(() =>
|
||||
typeof props.width == "string" ? props.width : `${props.width}px`
|
||||
);
|
||||
const realWidth = computed(() => (typeof props.width == 'string' ? props.width : `${props.width}px`));
|
||||
|
||||
const realHeight = computed(() =>
|
||||
typeof props.height == "string" ? props.height : `${props.height}px`
|
||||
);
|
||||
const realHeight = computed(() => (typeof props.height == 'string' ? props.height : `${props.height}px`));
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
<template>
|
||||
<div class="component-upload-image">
|
||||
<el-upload
|
||||
ref="imageUploadRef"
|
||||
multiple
|
||||
:action="uploadImgUrl"
|
||||
list-type="picture-card"
|
||||
:on-success="handleUploadSuccess"
|
||||
:before-upload="handleBeforeUpload"
|
||||
:limit="limit"
|
||||
:accept="fileAccept"
|
||||
:on-error="handleUploadError"
|
||||
:on-exceed="handleExceed"
|
||||
ref="imageUpload"
|
||||
:before-remove="handleDelete"
|
||||
:show-file-list="true"
|
||||
:headers="headers"
|
||||
@@ -22,13 +23,13 @@
|
||||
</el-icon>
|
||||
</el-upload>
|
||||
<!-- 上传提示 -->
|
||||
<div class="el-upload__tip" v-if="showTip">
|
||||
<div v-if="showTip" class="el-upload__tip">
|
||||
请上传
|
||||
<template v-if="fileSize">
|
||||
大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
|
||||
</template>
|
||||
<template v-if="fileType">
|
||||
格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b>
|
||||
格式为 <b style="color: #f56c6c">{{ fileType.join('/') }}</b>
|
||||
</template>
|
||||
的文件
|
||||
</div>
|
||||
@@ -40,177 +41,202 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { listByIds, delOss } from "@/api/system/oss";
|
||||
import { ComponentInternalInstance } from "vue";
|
||||
import { OssVO } from "@/api/system/oss/types";
|
||||
import { listByIds, delOss } from '@/api/system/oss';
|
||||
import { OssVO } from '@/api/system/oss/types';
|
||||
import { propTypes } from '@/utils/propTypes';
|
||||
import {globalHeaders} from "@/utils/request";
|
||||
import { globalHeaders } from '@/utils/request';
|
||||
import { compressAccurately } from 'image-conversion';
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: [String, Object, Array],
|
||||
// 图片数量限制
|
||||
limit: propTypes.number.def(5),
|
||||
// 大小限制(MB)
|
||||
fileSize: propTypes.number.def(5),
|
||||
// 文件类型, 例如['png', 'jpg', 'jpeg']
|
||||
fileType: propTypes.array.def(["png", "jpg", "jpeg"]),
|
||||
// 是否显示提示
|
||||
isShowTip: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
modelValue: {
|
||||
type: [String, Object, Array],
|
||||
default: () => []
|
||||
},
|
||||
// 图片数量限制
|
||||
limit: propTypes.number.def(5),
|
||||
// 大小限制(MB)
|
||||
fileSize: propTypes.number.def(5),
|
||||
// 文件类型, 例如['png', 'jpg', 'jpeg']
|
||||
fileType: propTypes.array.def(['png', 'jpg', 'jpeg']),
|
||||
// 是否显示提示
|
||||
isShowTip: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否支持压缩,默认否
|
||||
compressSupport: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 压缩目标大小,单位KB。默认300KB以上文件才压缩,并压缩至300KB以内
|
||||
compressTargetSize: propTypes.number.def(300)
|
||||
});
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
const number = ref(0);
|
||||
const uploadList = ref<any[]>([]);
|
||||
const dialogImageUrl = ref("");
|
||||
const dialogImageUrl = ref('');
|
||||
const dialogVisible = ref(false);
|
||||
|
||||
const baseUrl = import.meta.env.VITE_APP_BASE_API;
|
||||
const uploadImgUrl = ref(baseUrl + "/resource/oss/upload"); // 上传的图片服务器地址
|
||||
const uploadImgUrl = ref(baseUrl + '/resource/oss/upload'); // 上传的图片服务器地址
|
||||
const headers = ref(globalHeaders());
|
||||
|
||||
const fileList = ref<any[]>([]);
|
||||
const showTip = computed(
|
||||
() => props.isShowTip && (props.fileType || props.fileSize)
|
||||
);
|
||||
const showTip = computed(() => props.isShowTip && (props.fileType || props.fileSize));
|
||||
|
||||
const imageUploadRef = ref<ElUploadInstance>();
|
||||
|
||||
watch(() => props.modelValue, async val => {
|
||||
// 监听 fileType 变化,更新 fileAccept
|
||||
const fileAccept = computed(() => props.fileType.map((type) => `.${type}`).join(','));
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
async (val: string) => {
|
||||
if (val) {
|
||||
// 首先将值转为数组
|
||||
let list: OssVO[] = [];
|
||||
if (Array.isArray(val)) {
|
||||
list = val as OssVO[];
|
||||
// 首先将值转为数组
|
||||
let list: OssVO[] = [];
|
||||
if (Array.isArray(val)) {
|
||||
list = val as OssVO[];
|
||||
} else {
|
||||
const res = await listByIds(val);
|
||||
list = res.data;
|
||||
}
|
||||
// 然后将数组转为对象数组
|
||||
fileList.value = list.map((item) => {
|
||||
// 字符串回显处理 如果此处存的是url可直接回显 如果存的是id需要调用接口查出来
|
||||
let itemData;
|
||||
if (typeof item === 'string') {
|
||||
itemData = { name: item, url: item };
|
||||
} else {
|
||||
const res = await listByIds(val as string)
|
||||
list = res.data
|
||||
// 此处name使用ossId 防止删除出现重名
|
||||
itemData = { name: item.ossId, url: item.url, ossId: item.ossId };
|
||||
}
|
||||
// 然后将数组转为对象数组
|
||||
fileList.value = list.map(item => {
|
||||
// 字符串回显处理 如果此处存的是url可直接回显 如果存的是id需要调用接口查出来
|
||||
let itemData;
|
||||
if (typeof item === "string") {
|
||||
itemData = { name: item, url: item };
|
||||
} else {
|
||||
// 此处name使用ossId 防止删除出现重名
|
||||
itemData = { name: item.ossId, url: item.url, ossId: item.ossId };
|
||||
}
|
||||
return itemData;
|
||||
});
|
||||
return itemData;
|
||||
});
|
||||
} else {
|
||||
fileList.value = [];
|
||||
return [];
|
||||
fileList.value = [];
|
||||
return [];
|
||||
}
|
||||
}, { deep: true, immediate: true });
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
|
||||
/** 上传前loading加载 */
|
||||
const handleBeforeUpload = (file: any) => {
|
||||
let isImg = false;
|
||||
if (props.fileType.length) {
|
||||
let fileExtension = "";
|
||||
if (file.name.lastIndexOf(".") > -1) {
|
||||
fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
|
||||
}
|
||||
isImg = props.fileType.some((type: any) => {
|
||||
if (file.type.indexOf(type) > -1) return true;
|
||||
if (fileExtension && fileExtension.indexOf(type) > -1) return true;
|
||||
return false;
|
||||
});
|
||||
} else {
|
||||
isImg = file.type.indexOf("image") > -1;
|
||||
let isImg = false;
|
||||
if (props.fileType.length) {
|
||||
let fileExtension = '';
|
||||
if (file.name.lastIndexOf('.') > -1) {
|
||||
fileExtension = file.name.slice(file.name.lastIndexOf('.') + 1);
|
||||
}
|
||||
if (!isImg) {
|
||||
proxy?.$modal.msgError(
|
||||
`文件格式不正确, 请上传${props.fileType.join("/")}图片格式文件!`
|
||||
);
|
||||
return false;
|
||||
isImg = props.fileType.some((type: any) => {
|
||||
if (file.type.indexOf(type) > -1) return true;
|
||||
if (fileExtension && fileExtension.indexOf(type) > -1) return true;
|
||||
return false;
|
||||
});
|
||||
} else {
|
||||
isImg = file.type.indexOf('image') > -1;
|
||||
}
|
||||
if (!isImg) {
|
||||
proxy?.$modal.msgError(`文件格式不正确, 请上传${props.fileType.join('/')}图片格式文件!`);
|
||||
return false;
|
||||
}
|
||||
if (file.name.includes(',')) {
|
||||
proxy?.$modal.msgError('文件名不正确,不能包含英文逗号!');
|
||||
return false;
|
||||
}
|
||||
if (props.fileSize) {
|
||||
const isLt = file.size / 1024 / 1024 < props.fileSize;
|
||||
if (!isLt) {
|
||||
proxy?.$modal.msgError(`上传头像图片大小不能超过 ${props.fileSize} MB!`);
|
||||
return false;
|
||||
}
|
||||
if (props.fileSize) {
|
||||
const isLt = file.size / 1024 / 1024 < props.fileSize;
|
||||
if (!isLt) {
|
||||
proxy?.$modal.msgError(`上传头像图片大小不能超过 ${props.fileSize} MB!`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
proxy?.$modal.loading("正在上传图片,请稍候...");
|
||||
}
|
||||
|
||||
//压缩图片,开启压缩并且大于指定的压缩大小时才压缩
|
||||
if (props.compressSupport && file.size / 1024 > props.compressTargetSize) {
|
||||
proxy?.$modal.loading('正在上传图片,请稍候...');
|
||||
number.value++;
|
||||
}
|
||||
return compressAccurately(file, props.compressTargetSize);
|
||||
} else {
|
||||
proxy?.$modal.loading('正在上传图片,请稍候...');
|
||||
number.value++;
|
||||
}
|
||||
};
|
||||
|
||||
// 文件个数超出
|
||||
const handleExceed = () => {
|
||||
proxy?.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`);
|
||||
}
|
||||
proxy?.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`);
|
||||
};
|
||||
|
||||
// 上传成功回调
|
||||
const handleUploadSuccess = (res: any, file: UploadFile) => {
|
||||
if (res.code === 200) {
|
||||
uploadList.value.push({ name: res.data.fileName, url: res.data.url, ossId: res.data.ossId });
|
||||
uploadedSuccessfully();
|
||||
} else {
|
||||
number.value--;
|
||||
proxy?.$modal.closeLoading();
|
||||
proxy?.$modal.msgError(res.msg);
|
||||
imageUploadRef.value?.handleRemove(file);
|
||||
uploadedSuccessfully();
|
||||
}
|
||||
}
|
||||
if (res.code === 200) {
|
||||
uploadList.value.push({ name: res.data.fileName, url: res.data.url, ossId: res.data.ossId });
|
||||
uploadedSuccessfully();
|
||||
} else {
|
||||
number.value--;
|
||||
proxy?.$modal.closeLoading();
|
||||
proxy?.$modal.msgError(res.msg);
|
||||
imageUploadRef.value?.handleRemove(file);
|
||||
uploadedSuccessfully();
|
||||
}
|
||||
};
|
||||
|
||||
// 删除图片
|
||||
const handleDelete = (file: UploadFile): boolean => {
|
||||
const findex = fileList.value.map(f => f.name).indexOf(file.name);
|
||||
if (findex > -1 && uploadList.value.length === number.value) {
|
||||
let ossId = fileList.value[findex].ossId;
|
||||
delOss(ossId);
|
||||
fileList.value.splice(findex, 1);
|
||||
emit("update:modelValue", listToString(fileList.value));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const findex = fileList.value.map((f) => f.name).indexOf(file.name);
|
||||
if (findex > -1 && uploadList.value.length === number.value) {
|
||||
const ossId = fileList.value[findex].ossId;
|
||||
delOss(ossId);
|
||||
fileList.value.splice(findex, 1);
|
||||
emit('update:modelValue', listToString(fileList.value));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// 上传结束处理
|
||||
const uploadedSuccessfully = () => {
|
||||
if (number.value > 0 && uploadList.value.length === number.value) {
|
||||
fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value);
|
||||
uploadList.value = [];
|
||||
number.value = 0;
|
||||
emit("update:modelValue", listToString(fileList.value));
|
||||
proxy?.$modal.closeLoading();
|
||||
}
|
||||
}
|
||||
if (number.value > 0 && uploadList.value.length === number.value) {
|
||||
fileList.value = fileList.value.filter((f) => f.url !== undefined).concat(uploadList.value);
|
||||
uploadList.value = [];
|
||||
number.value = 0;
|
||||
emit('update:modelValue', listToString(fileList.value));
|
||||
proxy?.$modal.closeLoading();
|
||||
}
|
||||
};
|
||||
|
||||
// 上传失败
|
||||
const handleUploadError = () => {
|
||||
proxy?.$modal.msgError("上传图片失败");
|
||||
proxy?.$modal.closeLoading();
|
||||
}
|
||||
proxy?.$modal.msgError('上传图片失败');
|
||||
proxy?.$modal.closeLoading();
|
||||
};
|
||||
|
||||
// 预览
|
||||
const handlePictureCardPreview = (file: any) => {
|
||||
dialogImageUrl.value = file.url;
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
dialogImageUrl.value = file.url;
|
||||
dialogVisible.value = true;
|
||||
};
|
||||
|
||||
// 对象转成指定字符串分隔
|
||||
const listToString = (list: any[], separator?: string) => {
|
||||
let strs = "";
|
||||
separator = separator || ",";
|
||||
for (let i in list) {
|
||||
if (undefined !== list[i].ossId && list[i].url.indexOf("blob:") !== 0) {
|
||||
strs += list[i].ossId + separator;
|
||||
}
|
||||
let strs = '';
|
||||
separator = separator || ',';
|
||||
for (const i in list) {
|
||||
if (undefined !== list[i].ossId && list[i].url.indexOf('blob:') !== 0) {
|
||||
strs += list[i].ossId + separator;
|
||||
}
|
||||
return strs != "" ? strs.substring(0, strs.length - 1) : "";
|
||||
}
|
||||
}
|
||||
return strs != '' ? strs.substring(0, strs.length - 1) : '';
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
// .el-upload--picture-card 控制加号部分
|
||||
:deep(.hide .el-upload--picture-card) {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -14,22 +14,21 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import SvgIcon from '@/components/SvgIcon/index.vue';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import SvgIcon from '@/components/SvgIcon/index.vue';
|
||||
|
||||
const appStore = useAppStore();
|
||||
const { locale } = useI18n();
|
||||
|
||||
|
||||
const message: any = {
|
||||
zh_CN: '切换语言成功!',
|
||||
en_US: 'Switch Language Successful!',
|
||||
}
|
||||
const handleLanguageChange = (lang: string) => {
|
||||
en_US: 'Switch Language Successful!'
|
||||
};
|
||||
const handleLanguageChange = (lang: any) => {
|
||||
locale.value = lang;
|
||||
appStore.changeLanguage(lang);
|
||||
ElMessage.success(message[lang] || '切换语言成功!');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<div :class="{ 'hidden': hidden }" class="pagination-container">
|
||||
<div :class="{ hidden: hidden }" class="pagination-container">
|
||||
<el-pagination
|
||||
:background="background"
|
||||
v-model:current-page="currentPage"
|
||||
v-model:page-size="pageSize"
|
||||
:background="background"
|
||||
:layout="layout"
|
||||
:page-sizes="pageSizes"
|
||||
:pager-count="pagerCount"
|
||||
@@ -14,71 +14,61 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'Pagination'
|
||||
}
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { scrollTo } from '@/utils/scroll-to'
|
||||
import { propTypes } from "@/utils/propTypes";
|
||||
<script setup name="Pagination" lang="ts">
|
||||
import { scrollTo } from '@/utils/scroll-to';
|
||||
import { propTypes } from '@/utils/propTypes';
|
||||
|
||||
const props = defineProps({
|
||||
total: propTypes.number,
|
||||
page: propTypes.number.def(1),
|
||||
limit: propTypes.number.def(20),
|
||||
pageSizes: {
|
||||
type: Array as PropType<number[]>,
|
||||
default: () => [10, 20, 30, 50]
|
||||
},
|
||||
// 移动端页码按钮的数量端默认值5
|
||||
pagerCount: propTypes.number.def(document.body.clientWidth < 992 ? 5 : 7),
|
||||
layout: propTypes.string.def('total, sizes, prev, pager, next, jumper'),
|
||||
background: propTypes.bool.def(true),
|
||||
autoScroll: propTypes.bool.def(true),
|
||||
hidden: propTypes.bool.def(false),
|
||||
float: propTypes.string.def('right')
|
||||
})
|
||||
total: propTypes.number,
|
||||
page: propTypes.number.def(1),
|
||||
limit: propTypes.number.def(20),
|
||||
pageSizes: { type: Array<number>, default: () => [10, 20, 30, 50] },
|
||||
// 移动端页码按钮的数量端默认值5
|
||||
pagerCount: propTypes.number.def(document.body.clientWidth < 992 ? 5 : 7),
|
||||
layout: propTypes.string.def('total, sizes, prev, pager, next, jumper'),
|
||||
background: propTypes.bool.def(true),
|
||||
autoScroll: propTypes.bool.def(true),
|
||||
hidden: propTypes.bool.def(false),
|
||||
float: propTypes.string.def('right')
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:page', 'update:limit', 'pagination']);
|
||||
const currentPage = computed({
|
||||
get() {
|
||||
return props.page
|
||||
},
|
||||
set(val) {
|
||||
emit('update:page', val)
|
||||
}
|
||||
})
|
||||
get() {
|
||||
return props.page;
|
||||
},
|
||||
set(val) {
|
||||
emit('update:page', val);
|
||||
}
|
||||
});
|
||||
const pageSize = computed({
|
||||
get() {
|
||||
return props.limit
|
||||
},
|
||||
set(val){
|
||||
emit('update:limit', val)
|
||||
}
|
||||
})
|
||||
get() {
|
||||
return props.limit;
|
||||
},
|
||||
set(val) {
|
||||
emit('update:limit', val);
|
||||
}
|
||||
});
|
||||
function handleSizeChange(val: number) {
|
||||
if (currentPage.value * val > props.total) {
|
||||
currentPage.value = 1
|
||||
}
|
||||
emit('pagination', { page: currentPage.value, limit: val })
|
||||
if (props.autoScroll) {
|
||||
scrollTo(0, 800)
|
||||
}
|
||||
if (currentPage.value * val > props.total) {
|
||||
currentPage.value = 1;
|
||||
}
|
||||
emit('pagination', { page: currentPage.value, limit: val });
|
||||
if (props.autoScroll) {
|
||||
scrollTo(0, 800);
|
||||
}
|
||||
}
|
||||
function handleCurrentChange(val: number) {
|
||||
emit('pagination', { page: val, limit: pageSize.value })
|
||||
if (props.autoScroll) {
|
||||
scrollTo(0, 800)
|
||||
}
|
||||
emit('pagination', { page: val, limit: pageSize.value });
|
||||
if (props.autoScroll) {
|
||||
scrollTo(0, 800);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.pagination-container {
|
||||
padding: 32px 16px;
|
||||
.el-pagination{
|
||||
.el-pagination {
|
||||
float: v-bind(float);
|
||||
}
|
||||
}
|
||||
|
||||
100
src/components/Process/MessageType.vue
Normal file
@@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<el-dialog v-model="visible" :title="props.title" width="50%" draggable :before-close="cancel" center :close-on-click-modal="false">
|
||||
<el-form v-loading="loading" ref="ruleFormRef" :model="form" :rules="rules" label-width="120px">
|
||||
<el-form-item label="消息提醒" prop="messageType">
|
||||
<el-checkbox-group v-model="form.messageType">
|
||||
<el-checkbox value="1" name="type" disabled>站内信</el-checkbox>
|
||||
<el-checkbox value="2" name="type">邮件</el-checkbox>
|
||||
<el-checkbox value="3" name="type">短信</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="消息内容" prop="message">
|
||||
<el-input v-model="form.message" type="textarea" resize="none" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer" style="float: right; padding-bottom: 20px">
|
||||
<el-button :disabled="buttonDisabled" type="primary" @click="submit(ruleFormRef)">确认</el-button>
|
||||
<el-button :disabled="buttonDisabled" @click="cancel">取消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { ComponentInternalInstance } from 'vue';
|
||||
import { ElForm, FormInstance } from 'element-plus';
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const emits = defineEmits(['submitCallback', 'cancelCallback']);
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: '提示'
|
||||
}
|
||||
});
|
||||
const ruleFormRef = ref<FormInstance>();
|
||||
//遮罩层
|
||||
const loading = ref(true);
|
||||
const visible = ref(false);
|
||||
const buttonDisabled = ref(true);
|
||||
const form = ref<Record<string, any>>({
|
||||
message: undefined,
|
||||
messageType: ['1']
|
||||
});
|
||||
const rules = reactive<Record<string, any>>({
|
||||
messageType: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择消息提醒',
|
||||
trigger: 'change'
|
||||
}
|
||||
],
|
||||
message: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入消息内容',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
});
|
||||
//确认
|
||||
//打开弹窗
|
||||
const open = async () => {
|
||||
reset();
|
||||
visible.value = true;
|
||||
loading.value = false;
|
||||
buttonDisabled.value = false;
|
||||
};
|
||||
//关闭弹窗
|
||||
const close = async () => {
|
||||
reset();
|
||||
visible.value = false;
|
||||
};
|
||||
const submit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
await formEl.validate((valid, fields) => {
|
||||
if (valid) {
|
||||
emits('submitCallback', form.value);
|
||||
}
|
||||
});
|
||||
};
|
||||
//取消
|
||||
const cancel = async () => {
|
||||
visible.value = false;
|
||||
buttonDisabled.value = false;
|
||||
emits('cancelCallback');
|
||||
};
|
||||
//重置
|
||||
const reset = async () => {
|
||||
form.value.taskIdList = [];
|
||||
form.value.message = '';
|
||||
form.value.messageType = ['1'];
|
||||
};
|
||||
/**
|
||||
* 对外暴露子组件方法
|
||||
*/
|
||||
defineExpose({
|
||||
open,
|
||||
close
|
||||
});
|
||||
</script>
|
||||
57
src/components/Process/approvalButton.vue
Normal file
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<div style="display: flex; justify-content: space-between">
|
||||
<div>
|
||||
<el-button v-if="submitButtonShow" :loading="props.buttonLoading" type="info" @click="submitForm('draft', mode)">暂存</el-button>
|
||||
<el-button v-if="submitButtonShow" :loading="props.buttonLoading" type="primary" @click="submitForm('submit', mode)">提 交</el-button>
|
||||
<el-button v-if="approvalButtonShow" :loading="props.buttonLoading" type="primary" @click="approvalVerifyOpen">审批</el-button>
|
||||
<el-button v-if="props.id && props.status !== 'draft'" type="primary" @click="handleApprovalRecord">流程进度</el-button>
|
||||
<slot />
|
||||
</div>
|
||||
<div>
|
||||
<el-button style="float: right" @click="goBack()">返回</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { propTypes } from '@/utils/propTypes';
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const props = defineProps({
|
||||
status: propTypes.string.def(''),
|
||||
pageType: propTypes.string.def(''),
|
||||
buttonLoading: propTypes.bool.def(false),
|
||||
id: propTypes.string.def('') || propTypes.number.def(),
|
||||
mode: propTypes.bool.def(false)
|
||||
});
|
||||
const emits = defineEmits(['submitForm', 'approvalVerifyOpen', 'handleApprovalRecord']);
|
||||
//暂存,提交
|
||||
const submitForm = async (type, mode) => {
|
||||
emits('submitForm', type, mode);
|
||||
};
|
||||
//审批
|
||||
const approvalVerifyOpen = async () => {
|
||||
emits('approvalVerifyOpen');
|
||||
};
|
||||
//审批记录
|
||||
const handleApprovalRecord = () => {
|
||||
emits('handleApprovalRecord');
|
||||
};
|
||||
|
||||
//校验提交按钮是否显示
|
||||
const submitButtonShow = computed(() => {
|
||||
return (
|
||||
props.pageType === 'add' ||
|
||||
(props.pageType === 'update' && props.status && (props.status === 'draft' || props.status === 'cancel' || props.status === 'back'))
|
||||
);
|
||||
});
|
||||
|
||||
//校验审批按钮是否显示
|
||||
const approvalButtonShow = computed(() => {
|
||||
return props.pageType === 'approval' && props.status && props.status === 'waiting';
|
||||
});
|
||||
|
||||
//返回
|
||||
const goBack = () => {
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.go(-1);
|
||||
};
|
||||
</script>
|
||||
127
src/components/Process/approvalRecord.vue
Normal file
@@ -0,0 +1,127 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<el-dialog v-model="visible" draggable title="审批记录" :width="props.width" :height="props.height" :close-on-click-modal="false">
|
||||
<el-tabs v-model="tabActiveName" class="demo-tabs">
|
||||
<el-tab-pane v-loading="loading" label="流程图" name="image" style="height: 68vh">
|
||||
<flowChart :ins-id="insId" v-if="insId" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane v-loading="loading" label="审批信息" name="info">
|
||||
<div>
|
||||
<el-table :data="historyList" style="width: 100%" border fit>
|
||||
<el-table-column type="index" label="序号" align="center" width="60"></el-table-column>
|
||||
<el-table-column prop="nodeName" label="任务名称" sortable align="center"></el-table-column>
|
||||
<el-table-column prop="approveName" :show-overflow-tooltip="true" label="办理人" sortable align="center">
|
||||
<template #default="scope">
|
||||
<template v-if="scope.row.approveName">
|
||||
<el-tag v-for="(item, index) in scope.row.approveName.split(',')" :key="index" type="success">{{ item }}</el-tag>
|
||||
</template>
|
||||
<template v-else> <el-tag type="success">无</el-tag></template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="flowStatus" label="状态" width="80" sortable align="center">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="wf_task_status" :value="scope.row.flowStatus"></dict-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="message" label="审批意见" :show-overflow-tooltip="true" sortable align="center"></el-table-column>
|
||||
<el-table-column prop="createTime" label="开始时间" width="160" :show-overflow-tooltip="true" sortable align="center"></el-table-column>
|
||||
<el-table-column prop="updateTime" label="结束时间" width="160" :show-overflow-tooltip="true" sortable align="center"></el-table-column>
|
||||
<el-table-column
|
||||
prop="runDuration"
|
||||
label="运行时长"
|
||||
width="140"
|
||||
:show-overflow-tooltip="true"
|
||||
sortable
|
||||
align="center"
|
||||
></el-table-column>
|
||||
<el-table-column prop="attachmentList" width="120" label="附件" align="center">
|
||||
<template #default="scope">
|
||||
<el-popover v-if="scope.row.attachmentList && scope.row.attachmentList.length > 0" placement="right" :width="310" trigger="click">
|
||||
<template #reference>
|
||||
<el-button type="primary" style="margin-right: 16px">附件</el-button>
|
||||
</template>
|
||||
<el-table border :data="scope.row.attachmentList">
|
||||
<el-table-column prop="originalName" width="202" :show-overflow-tooltip="true" label="附件名称"></el-table-column>
|
||||
<el-table-column prop="name" width="80" align="center" :show-overflow-tooltip="true" label="操作">
|
||||
<template #default="tool">
|
||||
<el-button type="text" @click="handleDownload(tool.row.ossId)">下载</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { flowHisTaskList } from '@/api/workflow/instance';
|
||||
import { propTypes } from '@/utils/propTypes';
|
||||
import { listByIds } from '@/api/system/oss';
|
||||
import FlowChart from '@/components/Process/flowChart.vue';
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { wf_task_status } = toRefs<any>(proxy?.useDict('wf_task_status'));
|
||||
const props = defineProps({
|
||||
width: propTypes.string.def('80%'),
|
||||
height: propTypes.string.def('100%')
|
||||
});
|
||||
const loading = ref(false);
|
||||
const visible = ref(false);
|
||||
const historyList = ref<Array<any>>([]);
|
||||
const tabActiveName = ref('image');
|
||||
const insId = ref(null);
|
||||
|
||||
//初始化查询审批记录
|
||||
const init = async (businessId: string | number) => {
|
||||
visible.value = true;
|
||||
loading.value = true;
|
||||
tabActiveName.value = 'image';
|
||||
historyList.value = [];
|
||||
flowHisTaskList(businessId).then((resp) => {
|
||||
if (resp.data) {
|
||||
historyList.value = resp.data.list;
|
||||
insId.value = resp.data.instanceId;
|
||||
if (historyList.value.length > 0) {
|
||||
historyList.value.forEach((item) => {
|
||||
if (item.ext) {
|
||||
getIds(item.ext).then((res) => {
|
||||
item.attachmentList = res.data;
|
||||
});
|
||||
} else {
|
||||
item.attachmentList = [];
|
||||
}
|
||||
});
|
||||
}
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
const getIds = async (ids: string | number) => {
|
||||
const res = await listByIds(ids);
|
||||
return res;
|
||||
};
|
||||
|
||||
/** 下载按钮操作 */
|
||||
const handleDownload = (ossId: string) => {
|
||||
proxy?.$download.oss(ossId);
|
||||
};
|
||||
|
||||
/**
|
||||
* 对外暴露子组件方法
|
||||
*/
|
||||
defineExpose({
|
||||
init
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
:deep(.el-dialog .el-dialog__body) {
|
||||
max-height: calc(100vh - 170px) !important;
|
||||
min-height: calc(100vh - 170px) !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
40
src/components/Process/flowChart.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div>
|
||||
<div style="height: 68vh" class="iframe-wrapper">
|
||||
<iframe :src="iframeUrl" style="width: 100%; height: 100%" frameborder="0" scrolling="no" class="custom-iframe" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getToken } from '@/utils/auth';
|
||||
|
||||
// Props 定义方式变化
|
||||
const props = defineProps({
|
||||
insId: {
|
||||
type: [String, Number],
|
||||
default: null
|
||||
}
|
||||
});
|
||||
|
||||
const iframeUrl = ref('');
|
||||
const baseUrl = import.meta.env.VITE_APP_BASE_API;
|
||||
|
||||
onMounted(async () => {
|
||||
const url = baseUrl + `/warm-flow-ui/index.html?id=${props.insId}&type=FlowChart&t=${Date.now()}`;
|
||||
iframeUrl.value = url + '&Authorization=Bearer ' + getToken() + '&clientid=' + import.meta.env.VITE_APP_CLIENT_ID;
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
.iframe-wrapper {
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.custom-iframe {
|
||||
width: 100%;
|
||||
border: none;
|
||||
background: transparent;
|
||||
}
|
||||
</style>
|
||||
154
src/components/Process/flowChartImg.vue
Normal file
@@ -0,0 +1,154 @@
|
||||
<template>
|
||||
<div
|
||||
ref="imageWrapperRef"
|
||||
class="image-wrapper"
|
||||
@wheel="handleMouseWheel"
|
||||
@mousedown="handleMouseDown"
|
||||
@mousemove="handleMouseMove"
|
||||
@mouseup="handleMouseUp"
|
||||
@mouseleave="handleMouseLeave"
|
||||
@dblclick="resetTransform"
|
||||
:style="transformStyle"
|
||||
>
|
||||
<el-card class="box-card">
|
||||
<el-image :src="props.imgUrl" class="scalable-image" />
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// Props 定义方式变化
|
||||
const props = defineProps({
|
||||
imgUrl: {
|
||||
type: String,
|
||||
default: () => ''
|
||||
}
|
||||
});
|
||||
|
||||
const imageWrapperRef = ref<HTMLElement | null>(null);
|
||||
const scale = ref(1); // 初始缩放比例
|
||||
const maxScale = 3; // 最大缩放比例
|
||||
const minScale = 0.5; // 最小缩放比例
|
||||
|
||||
let isDragging = false;
|
||||
let startX = 0;
|
||||
let startY = 0;
|
||||
let currentTranslateX = 0;
|
||||
let currentTranslateY = 0;
|
||||
|
||||
const handleMouseWheel = (event: WheelEvent) => {
|
||||
event.preventDefault();
|
||||
let newScale = scale.value - event.deltaY / 1000;
|
||||
newScale = Math.max(minScale, Math.min(newScale, maxScale));
|
||||
if (newScale !== scale.value) {
|
||||
scale.value = newScale;
|
||||
resetDragPosition(); // 重置拖拽位置,使图片居中
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseDown = (event: MouseEvent) => {
|
||||
if (scale.value > 1) {
|
||||
event.preventDefault(); // 阻止默认行为,防止拖拽
|
||||
isDragging = true;
|
||||
startX = event.clientX;
|
||||
startY = event.clientY;
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseMove = (event: MouseEvent) => {
|
||||
if (!isDragging || !imageWrapperRef.value) return;
|
||||
|
||||
const deltaX = event.clientX - startX;
|
||||
const deltaY = event.clientY - startY;
|
||||
startX = event.clientX;
|
||||
startY = event.clientY;
|
||||
|
||||
currentTranslateX += deltaX;
|
||||
currentTranslateY += deltaY;
|
||||
|
||||
// 边界检测,防止图片被拖出容器
|
||||
const bounds = getBounds();
|
||||
if (currentTranslateX > bounds.maxTranslateX) {
|
||||
currentTranslateX = bounds.maxTranslateX;
|
||||
} else if (currentTranslateX < bounds.minTranslateX) {
|
||||
currentTranslateX = bounds.minTranslateX;
|
||||
}
|
||||
|
||||
if (currentTranslateY > bounds.maxTranslateY) {
|
||||
currentTranslateY = bounds.maxTranslateY;
|
||||
} else if (currentTranslateY < bounds.minTranslateY) {
|
||||
currentTranslateY = bounds.minTranslateY;
|
||||
}
|
||||
|
||||
applyTransform();
|
||||
};
|
||||
|
||||
const handleMouseUp = () => {
|
||||
isDragging = false;
|
||||
};
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
isDragging = false;
|
||||
};
|
||||
|
||||
const resetTransform = () => {
|
||||
scale.value = 1;
|
||||
currentTranslateX = 0;
|
||||
currentTranslateY = 0;
|
||||
applyTransform();
|
||||
};
|
||||
|
||||
const resetDragPosition = () => {
|
||||
currentTranslateX = 0;
|
||||
currentTranslateY = 0;
|
||||
applyTransform();
|
||||
};
|
||||
|
||||
const applyTransform = () => {
|
||||
if (imageWrapperRef.value) {
|
||||
imageWrapperRef.value.style.transform = `translate(${currentTranslateX}px, ${currentTranslateY}px) scale(${scale.value})`;
|
||||
}
|
||||
};
|
||||
|
||||
const getBounds = () => {
|
||||
if (!imageWrapperRef.value) return { minTranslateX: 0, maxTranslateX: 0, minTranslateY: 0, maxTranslateY: 0 };
|
||||
|
||||
const imgRect = imageWrapperRef.value.getBoundingClientRect();
|
||||
const containerRect = imageWrapperRef.value.parentElement?.getBoundingClientRect() ?? imgRect;
|
||||
|
||||
const minTranslateX = (containerRect.width - imgRect.width * scale.value) / 2;
|
||||
const maxTranslateX = -(containerRect.width - imgRect.width * scale.value) / 2;
|
||||
const minTranslateY = (containerRect.height - imgRect.height * scale.value) / 2;
|
||||
const maxTranslateY = -(containerRect.height - imgRect.height * scale.value) / 2;
|
||||
|
||||
return { minTranslateX, maxTranslateX, minTranslateY, maxTranslateY };
|
||||
};
|
||||
|
||||
const transformStyle = computed(() => ({
|
||||
transition: isDragging ? 'none' : 'transform 0.2s ease'
|
||||
}));
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.image-wrapper {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
user-select: none; /* 禁用文本选择 */
|
||||
cursor: grab; /* 设置初始鼠标指针为可拖动 */
|
||||
}
|
||||
|
||||
.image-wrapper:active {
|
||||
cursor: grabbing; /* 当正在拖动时改变鼠标指针 */
|
||||
}
|
||||
|
||||
.scalable-image {
|
||||
object-fit: contain;
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
}
|
||||
</style>
|
||||
211
src/components/Process/processMeddle.vue
Normal file
@@ -0,0 +1,211 @@
|
||||
<template>
|
||||
<el-dialog v-model="visible" draggable title="流程干预" :width="props.width" :height="props.height" :close-on-click-modal="false">
|
||||
<el-descriptions v-loading="loading" class="margin-top" :title="`${task.flowName}(${task.flowCode})`" :column="2" border>
|
||||
<el-descriptions-item label="任务名称">{{ task.nodeName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="节点编码">{{ task.nodeCode }}</el-descriptions-item>
|
||||
<el-descriptions-item label="开始时间">{{ task.createTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="流程实例ID">{{ task.instanceId }}</el-descriptions-item>
|
||||
<el-descriptions-item label="版本号">{{ task.version }}.0</el-descriptions-item>
|
||||
<el-descriptions-item label="业务ID">{{ task.businessId }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button v-if="task.flowStatus === 'waiting'" :disabled="buttonDisabled" type="primary" @click="openTransferTask"> 转办 </el-button>
|
||||
<el-button
|
||||
v-if="task.flowStatus === 'waiting' && Number(task.nodeRatio) > 0"
|
||||
:disabled="buttonDisabled"
|
||||
type="primary"
|
||||
@click="openMultiInstanceUser"
|
||||
>
|
||||
加签
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="task.flowStatus === 'waiting' && Number(task.nodeRatio) > 0"
|
||||
:disabled="buttonDisabled"
|
||||
type="primary"
|
||||
@click="handleTaskUser"
|
||||
>
|
||||
减签
|
||||
</el-button>
|
||||
<el-button v-if="task.flowStatus === 'waiting'" :disabled="buttonDisabled" type="danger" @click="handleTerminationTask"> 终止 </el-button>
|
||||
</span>
|
||||
</template>
|
||||
<!-- 转办 -->
|
||||
<UserSelect ref="transferTaskRef" :multiple="false" @confirm-call-back="handleTransferTask"></UserSelect>
|
||||
<!-- 加签组件 -->
|
||||
<UserSelect ref="multiInstanceUserRef" :multiple="true" @confirm-call-back="addMultiInstanceUser"></UserSelect>
|
||||
<el-dialog v-model="deleteSignatureVisible" draggable title="减签人员" width="700px" height="400px" append-to-body :close-on-click-modal="false"
|
||||
><div>
|
||||
<el-table :data="deleteUserList" border>
|
||||
<el-table-column prop="nodeName" label="任务名称" />
|
||||
<el-table-column prop="nickName" label="办理人" />
|
||||
<el-table-column label="操作" align="center" width="160">
|
||||
<template #default="scope">
|
||||
<el-button type="danger" size="small" icon="Delete" @click="deleteMultiInstanceUser(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { propTypes } from '@/utils/propTypes';
|
||||
import { FlowTaskVO, TaskOperationBo } from '@/api/workflow/task/types';
|
||||
import UserSelect from '@/components/UserSelect';
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
import { getTask, taskOperation, currentTaskAllUser, terminationTask } from '@/api/workflow/task';
|
||||
const props = defineProps({
|
||||
width: propTypes.string.def('50%'),
|
||||
height: propTypes.string.def('100%')
|
||||
});
|
||||
const emits = defineEmits(['submitCallback']);
|
||||
const transferTaskRef = ref<InstanceType<typeof UserSelect>>();
|
||||
const multiInstanceUserRef = ref<InstanceType<typeof UserSelect>>();
|
||||
//遮罩层
|
||||
const loading = ref(true);
|
||||
//按钮
|
||||
const buttonDisabled = ref(true);
|
||||
const visible = ref(false);
|
||||
//减签弹窗
|
||||
const deleteSignatureVisible = ref(false);
|
||||
//可减签的人员
|
||||
const deleteUserList = ref<any>([]);
|
||||
//任务
|
||||
const task = ref<FlowTaskVO>({
|
||||
id: undefined,
|
||||
createTime: undefined,
|
||||
updateTime: undefined,
|
||||
tenantId: undefined,
|
||||
definitionId: undefined,
|
||||
instanceId: undefined,
|
||||
flowName: undefined,
|
||||
businessId: undefined,
|
||||
nodeCode: undefined,
|
||||
nodeName: undefined,
|
||||
flowCode: undefined,
|
||||
flowStatus: undefined,
|
||||
formCustom: undefined,
|
||||
formPath: undefined,
|
||||
nodeType: undefined,
|
||||
nodeRatio: undefined,
|
||||
version: undefined,
|
||||
applyNode: undefined,
|
||||
buttonList: []
|
||||
});
|
||||
|
||||
const open = (taskId: string) => {
|
||||
visible.value = true;
|
||||
getTask(taskId).then((response) => {
|
||||
loading.value = false;
|
||||
buttonDisabled.value = false;
|
||||
task.value = response.data;
|
||||
});
|
||||
};
|
||||
|
||||
//打开转办
|
||||
const openTransferTask = () => {
|
||||
transferTaskRef.value.open();
|
||||
};
|
||||
//转办
|
||||
const handleTransferTask = async (data) => {
|
||||
if (data && data.length > 0) {
|
||||
const taskOperationBo = reactive<TaskOperationBo>({
|
||||
userId: data[0].userId,
|
||||
taskId: task.value.id,
|
||||
message: ''
|
||||
});
|
||||
await proxy?.$modal.confirm('是否确认提交?');
|
||||
loading.value = true;
|
||||
buttonDisabled.value = true;
|
||||
await taskOperation(taskOperationBo, 'transferTask').finally(() => {
|
||||
loading.value = false;
|
||||
buttonDisabled.value = false;
|
||||
});
|
||||
visible.value = false;
|
||||
emits('submitCallback');
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
} else {
|
||||
proxy?.$modal.msgWarning('请选择用户!');
|
||||
}
|
||||
};
|
||||
//加签
|
||||
const openMultiInstanceUser = async () => {
|
||||
multiInstanceUserRef.value.open();
|
||||
};
|
||||
//加签
|
||||
const addMultiInstanceUser = async (data) => {
|
||||
if (data && data.length > 0) {
|
||||
const taskOperationBo = reactive<TaskOperationBo>({
|
||||
userIds: data.map((e) => e.userId),
|
||||
taskId: task.value.id,
|
||||
message: ''
|
||||
});
|
||||
await proxy?.$modal.confirm('是否确认提交?');
|
||||
loading.value = true;
|
||||
buttonDisabled.value = true;
|
||||
await taskOperation(taskOperationBo, 'addSignature').finally(() => {
|
||||
loading.value = false;
|
||||
buttonDisabled.value = false;
|
||||
});
|
||||
visible.value = false;
|
||||
emits('submitCallback');
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
} else {
|
||||
proxy?.$modal.msgWarning('请选择用户!');
|
||||
}
|
||||
};
|
||||
//减签
|
||||
const deleteMultiInstanceUser = async (row) => {
|
||||
await proxy?.$modal.confirm('是否确认提交?');
|
||||
loading.value = true;
|
||||
buttonDisabled.value = true;
|
||||
const taskOperationBo = reactive<TaskOperationBo>({
|
||||
userIds: [row.userId],
|
||||
taskId: task.value.id,
|
||||
message: ''
|
||||
});
|
||||
await taskOperation(taskOperationBo, 'reductionSignature').finally(() => {
|
||||
loading.value = false;
|
||||
buttonDisabled.value = false;
|
||||
});
|
||||
visible.value = false;
|
||||
emits('submitCallback');
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
};
|
||||
//获取办理人
|
||||
const handleTaskUser = async () => {
|
||||
const data = await currentTaskAllUser(task.value.id);
|
||||
deleteUserList.value = data.data;
|
||||
if (deleteUserList.value && deleteUserList.value.length > 0) {
|
||||
deleteUserList.value.forEach((e) => {
|
||||
e.nodeName = task.value.nodeName;
|
||||
});
|
||||
}
|
||||
deleteSignatureVisible.value = true;
|
||||
};
|
||||
|
||||
//终止任务
|
||||
const handleTerminationTask = async () => {
|
||||
const params = {
|
||||
taskId: task.value.id,
|
||||
comment: ''
|
||||
};
|
||||
await proxy?.$modal.confirm('是否确认终止?');
|
||||
loading.value = true;
|
||||
buttonDisabled.value = true;
|
||||
await terminationTask(params).finally(() => {
|
||||
loading.value = false;
|
||||
buttonDisabled.value = false;
|
||||
});
|
||||
visible.value = false;
|
||||
emits('submitCallback');
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
};
|
||||
/**
|
||||
* 对外暴露子组件方法
|
||||
*/
|
||||
defineExpose({
|
||||
open
|
||||
});
|
||||
</script>
|
||||
541
src/components/Process/submitVerify.vue
Normal file
@@ -0,0 +1,541 @@
|
||||
<template>
|
||||
<el-dialog v-model="dialog.visible" :title="dialog.title" width="50%" draggable :before-close="cancel" center :close-on-click-modal="false">
|
||||
<el-form v-loading="loading" :model="form" label-width="120px">
|
||||
<el-form-item label="消息提醒">
|
||||
<el-checkbox-group v-model="form.messageType">
|
||||
<el-checkbox value="1" name="type" disabled>站内信</el-checkbox>
|
||||
<el-checkbox value="2" name="type">邮件</el-checkbox>
|
||||
<el-checkbox value="3" name="type">短信</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="附件" v-if="buttonObj.file">
|
||||
<fileUpload v-model="form.fileId" :file-type="['png', 'jpg', 'jpeg', 'doc', 'docx', 'xlsx', 'xls', 'ppt', 'txt', 'pdf']" :file-size="20" />
|
||||
</el-form-item>
|
||||
<el-form-item label="抄送" v-if="buttonObj.copy">
|
||||
<el-button type="primary" icon="Plus" circle @click="openUserSelectCopy" />
|
||||
<el-tag v-for="user in selectCopyUserList" :key="user.userId" closable style="margin: 2px" @close="handleCopyCloseTag(user)">
|
||||
{{ user.userName }}
|
||||
</el-tag>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="buttonObj.pop && nestNodeList && nestNodeList.length > 0" label="下一步审批人" prop="assigneeMap">
|
||||
<div v-for="(item, index) in nestNodeList" :key="index" style="margin-bottom: 5px; width: 500px">
|
||||
<span>【{{ item.nodeName }}】:</span>
|
||||
<el-input v-if="false" v-model="form.assigneeMap[item.nodeCode]" />
|
||||
<el-input placeholder="请选择审批人" readonly v-model="nickName[item.nodeCode]">
|
||||
<template v-slot:append>
|
||||
<el-button @click="choosePeople(item)" icon="search">选择</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="task.flowStatus === 'waiting'" label="审批意见">
|
||||
<el-input v-model="form.message" type="textarea" resize="none" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button :disabled="buttonDisabled" type="primary" @click="handleCompleteTask"> 提交 </el-button>
|
||||
<el-button v-if="task.flowStatus === 'waiting' && buttonObj.trust" :disabled="buttonDisabled" type="primary" @click="openDelegateTask">
|
||||
委托
|
||||
</el-button>
|
||||
<el-button v-if="task.flowStatus === 'waiting' && buttonObj.transfer" :disabled="buttonDisabled" type="primary" @click="openTransferTask">
|
||||
转办
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="task.flowStatus === 'waiting' && Number(task.nodeRatio) > 0 && buttonObj.addSign"
|
||||
:disabled="buttonDisabled"
|
||||
type="primary"
|
||||
@click="openMultiInstanceUser"
|
||||
>
|
||||
加签
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="task.flowStatus === 'waiting' && Number(task.nodeRatio) > 0 && buttonObj.subSign"
|
||||
:disabled="buttonDisabled"
|
||||
type="primary"
|
||||
@click="handleTaskUser"
|
||||
>
|
||||
减签
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="task.flowStatus === 'waiting' && buttonObj.termination"
|
||||
:disabled="buttonDisabled"
|
||||
type="danger"
|
||||
@click="handleTerminationTask"
|
||||
>
|
||||
终止
|
||||
</el-button>
|
||||
<el-button v-if="task.flowStatus === 'waiting' && buttonObj.back" :disabled="buttonDisabled" type="danger" @click="handleBackProcessOpen">
|
||||
退回
|
||||
</el-button>
|
||||
<el-button :disabled="buttonDisabled" @click="cancel">取消</el-button>
|
||||
</span>
|
||||
</template>
|
||||
<!-- 抄送 -->
|
||||
<UserSelect ref="userSelectCopyRef" :multiple="true" :data="selectCopyUserIds" @confirm-call-back="userSelectCopyCallBack"></UserSelect>
|
||||
<!-- 转办 -->
|
||||
<UserSelect ref="transferTaskRef" :multiple="false" @confirm-call-back="handleTransferTask"></UserSelect>
|
||||
<!-- 委托 -->
|
||||
<UserSelect ref="delegateTaskRef" :multiple="false" @confirm-call-back="handleDelegateTask"></UserSelect>
|
||||
<!-- 加签组件 -->
|
||||
<UserSelect ref="multiInstanceUserRef" :multiple="true" @confirm-call-back="addMultiInstanceUser"></UserSelect>
|
||||
<!-- 弹窗选人 -->
|
||||
<UserSelect ref="porUserRef" :data="form.assigneeMap[nodeCode]" :multiple="true" :userIds="popUserIds" @confirm-call-back="handlePopUser"></UserSelect>
|
||||
|
||||
<!-- 驳回开始 -->
|
||||
<el-dialog v-model="backVisible" draggable title="驳回" width="40%" :close-on-click-modal="false">
|
||||
<el-form v-if="task.flowStatus === 'waiting'" v-loading="backLoading" :model="backForm" label-width="120px">
|
||||
<el-form-item label="驳回节点">
|
||||
<el-select v-model="backForm.nodeCode" clearable placeholder="请选择" style="width: 300px">
|
||||
<el-option v-for="item in taskNodeList" :key="item.nodeCode" :label="item.nodeName" :value="item.nodeCode" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="消息提醒">
|
||||
<el-checkbox-group v-model="backForm.messageType">
|
||||
<el-checkbox label="1" name="type" disabled>站内信</el-checkbox>
|
||||
<el-checkbox label="2" name="type">邮件</el-checkbox>
|
||||
<el-checkbox label="3" name="type">短信</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="task.flowStatus === 'waiting'" label="附件">
|
||||
<fileUpload
|
||||
v-model="backForm.fileId"
|
||||
:file-type="['png', 'jpg', 'jpeg', 'doc', 'docx', 'xlsx', 'xls', 'ppt', 'txt', 'pdf']"
|
||||
:file-size="20"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="审批意见">
|
||||
<el-input v-model="backForm.message" type="textarea" resize="none" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer" style="float: right; padding-bottom: 20px">
|
||||
<el-button :disabled="backButtonDisabled" type="primary" @click="handleBackProcess">确认</el-button>
|
||||
<el-button :disabled="backButtonDisabled" @click="backVisible = false">取消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<!-- 驳回结束 -->
|
||||
<el-dialog v-model="deleteSignatureVisible" draggable title="减签人员" width="700px" height="400px" append-to-body :close-on-click-modal="false">
|
||||
<div>
|
||||
<el-table :data="deleteUserList" border>
|
||||
<el-table-column prop="nodeName" label="任务名称" />
|
||||
<el-table-column prop="nickName" label="办理人" />
|
||||
<el-table-column label="操作" align="center" width="160">
|
||||
<template #default="scope">
|
||||
<el-button type="danger" size="small" icon="Delete" @click="deleteMultiInstanceUser(scope.row)">删除 </el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { ComponentInternalInstance } from 'vue';
|
||||
import { ElForm } from 'element-plus';
|
||||
import {
|
||||
completeTask,
|
||||
backProcess,
|
||||
getTask,
|
||||
taskOperation,
|
||||
terminationTask,
|
||||
getBackTaskNode,
|
||||
currentTaskAllUser,
|
||||
getNextNodeList
|
||||
} from '@/api/workflow/task';
|
||||
import UserSelect from '@/components/UserSelect';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
import { FlowCopyVo, FlowTaskVO, TaskOperationBo } from '@/api/workflow/task/types';
|
||||
|
||||
const userSelectCopyRef = ref<InstanceType<typeof UserSelect>>();
|
||||
const transferTaskRef = ref<InstanceType<typeof UserSelect>>();
|
||||
const delegateTaskRef = ref<InstanceType<typeof UserSelect>>();
|
||||
const multiInstanceUserRef = ref<InstanceType<typeof UserSelect>>();
|
||||
const porUserRef = ref<InstanceType<typeof UserSelect>>();
|
||||
|
||||
const props = defineProps({
|
||||
taskVariables: {
|
||||
type: Object as () => Record<string, any>,
|
||||
default: () => {}
|
||||
}
|
||||
});
|
||||
//遮罩层
|
||||
const loading = ref(true);
|
||||
//按钮
|
||||
const buttonDisabled = ref(true);
|
||||
//任务id
|
||||
const taskId = ref<string>('');
|
||||
//抄送人
|
||||
const selectCopyUserList = ref<FlowCopyVo[]>([]);
|
||||
//抄送人id
|
||||
const selectCopyUserIds = ref<string>(undefined);
|
||||
//自定义节点变量
|
||||
const varNodeList = ref<Map<string, string>>(undefined);
|
||||
//可减签的人员
|
||||
const deleteUserList = ref<any>([]);
|
||||
//弹窗可选择的人员id
|
||||
const popUserIds = ref<any>([]);
|
||||
//驳回是否显示
|
||||
const backVisible = ref(false);
|
||||
const backLoading = ref(true);
|
||||
const backButtonDisabled = ref(true);
|
||||
// 可驳回得任务节点
|
||||
const taskNodeList = ref([]);
|
||||
const nickName = ref({});
|
||||
//节点编码
|
||||
const nodeCode = ref<string>('');
|
||||
const buttonObj = ref<any>({
|
||||
pop: false,
|
||||
trust: false,
|
||||
transfer: false,
|
||||
addSign: false,
|
||||
subSign: false,
|
||||
termination: false,
|
||||
back: false
|
||||
});
|
||||
//下一节点列表
|
||||
const nestNodeList = ref([]);
|
||||
//任务
|
||||
const task = ref<FlowTaskVO>({
|
||||
id: undefined,
|
||||
createTime: undefined,
|
||||
updateTime: undefined,
|
||||
tenantId: undefined,
|
||||
definitionId: undefined,
|
||||
instanceId: undefined,
|
||||
flowName: undefined,
|
||||
businessId: undefined,
|
||||
nodeCode: undefined,
|
||||
nodeName: undefined,
|
||||
flowCode: undefined,
|
||||
flowStatus: undefined,
|
||||
formCustom: undefined,
|
||||
formPath: undefined,
|
||||
nodeType: undefined,
|
||||
nodeRatio: undefined,
|
||||
applyNode: false,
|
||||
buttonList: [],
|
||||
copyList: [],
|
||||
varList: undefined,
|
||||
businessCode: undefined,
|
||||
businessTitle: undefined
|
||||
});
|
||||
const dialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: '提示'
|
||||
});
|
||||
//减签弹窗
|
||||
const deleteSignatureVisible = ref(false);
|
||||
const form = ref<Record<string, any>>({
|
||||
taskId: undefined,
|
||||
message: undefined,
|
||||
assigneeMap: {},
|
||||
variables: {},
|
||||
messageType: ['1'],
|
||||
flowCopyList: []
|
||||
});
|
||||
const backForm = ref<Record<string, any>>({
|
||||
taskId: undefined,
|
||||
nodeCode: undefined,
|
||||
message: undefined,
|
||||
variables: {},
|
||||
messageType: ['1']
|
||||
});
|
||||
|
||||
//打开弹窗
|
||||
const openDialog = async (id?: string) => {
|
||||
selectCopyUserIds.value = undefined;
|
||||
selectCopyUserList.value = [];
|
||||
form.value.fileId = undefined;
|
||||
taskId.value = id;
|
||||
form.value.message = undefined;
|
||||
dialog.visible = true;
|
||||
loading.value = true;
|
||||
buttonDisabled.value = true;
|
||||
const response = await getTask(taskId.value);
|
||||
task.value = response.data;
|
||||
buttonObj.value = {};
|
||||
task.value.buttonList?.forEach((e) => {
|
||||
buttonObj.value[e.code] = e.show;
|
||||
});
|
||||
selectCopyUserList.value = task.value.copyList;
|
||||
selectCopyUserIds.value = task.value.copyList.map((e) => e.userId).join(',');
|
||||
varNodeList.value = task.value.varList;
|
||||
console.log('varNodeList', varNodeList.value)
|
||||
buttonDisabled.value = false;
|
||||
try {
|
||||
const data = {
|
||||
taskId: taskId.value,
|
||||
variables: props.taskVariables
|
||||
};
|
||||
const nextData = await getNextNodeList(data);
|
||||
nestNodeList.value = nextData.data;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {});
|
||||
const emits = defineEmits(['submitCallback', 'cancelCallback']);
|
||||
|
||||
/** 办理流程 */
|
||||
const handleCompleteTask = async () => {
|
||||
form.value.taskId = taskId.value;
|
||||
form.value.variables = props.taskVariables;
|
||||
let verify = false;
|
||||
if (buttonObj.value.pop && nestNodeList.value && nestNodeList.value.length > 0) {
|
||||
nestNodeList.value.forEach((e) => {
|
||||
if (
|
||||
Object.keys(form.value.assigneeMap).length === 0 ||
|
||||
form.value.assigneeMap[e.nodeCode] === '' ||
|
||||
form.value.assigneeMap[e.nodeCode] === null ||
|
||||
form.value.assigneeMap[e.nodeCode] === undefined
|
||||
) {
|
||||
verify = true;
|
||||
}
|
||||
});
|
||||
if (verify) {
|
||||
proxy?.$modal.msgWarning('请选择审批人!');
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
form.value.assigneeMap = {};
|
||||
}
|
||||
if (selectCopyUserList.value && selectCopyUserList.value.length > 0) {
|
||||
const flowCopyList = [];
|
||||
selectCopyUserList.value.forEach((e) => {
|
||||
const copyUser = {
|
||||
userId: e.userId,
|
||||
userName: e.userName
|
||||
};
|
||||
flowCopyList.push(copyUser);
|
||||
});
|
||||
form.value.flowCopyList = flowCopyList;
|
||||
}
|
||||
await proxy?.$modal.confirm('是否确认提交?');
|
||||
loading.value = true;
|
||||
buttonDisabled.value = true;
|
||||
try {
|
||||
await completeTask(form.value);
|
||||
dialog.visible = false;
|
||||
emits('submitCallback');
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
buttonDisabled.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 驳回弹窗打开 */
|
||||
const handleBackProcessOpen = async () => {
|
||||
backForm.value = {};
|
||||
backForm.value.messageType = ['1'];
|
||||
backVisible.value = true;
|
||||
backLoading.value = true;
|
||||
backButtonDisabled.value = true;
|
||||
const data = await getBackTaskNode(task.value.id, task.value.nodeCode);
|
||||
taskNodeList.value = data.data;
|
||||
backLoading.value = false;
|
||||
backButtonDisabled.value = false;
|
||||
backForm.value.nodeCode = taskNodeList.value[0].nodeCode;
|
||||
};
|
||||
/** 驳回流程 */
|
||||
const handleBackProcess = async () => {
|
||||
backForm.value.taskId = taskId.value;
|
||||
await proxy?.$modal.confirm('是否确认驳回到申请人?');
|
||||
loading.value = true;
|
||||
backLoading.value = true;
|
||||
backButtonDisabled.value = true;
|
||||
await backProcess(backForm.value).finally(() => {
|
||||
loading.value = false;
|
||||
buttonDisabled.value = false;
|
||||
});
|
||||
dialog.visible = false;
|
||||
backLoading.value = false;
|
||||
backButtonDisabled.value = false;
|
||||
emits('submitCallback');
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
};
|
||||
//取消
|
||||
const cancel = async () => {
|
||||
dialog.visible = false;
|
||||
buttonDisabled.value = false;
|
||||
nickName.value = {};
|
||||
form.value.assigneeMap = {};
|
||||
emits('cancelCallback');
|
||||
};
|
||||
//打开抄送人员
|
||||
const openUserSelectCopy = () => {
|
||||
userSelectCopyRef.value.open();
|
||||
};
|
||||
//确认抄送人员
|
||||
const userSelectCopyCallBack = (data: FlowCopyVo[]) => {
|
||||
if (data && data.length > 0) {
|
||||
selectCopyUserList.value = data;
|
||||
selectCopyUserIds.value = selectCopyUserList.value.map((item) => item.userId).join(',');
|
||||
}
|
||||
};
|
||||
//删除抄送人员
|
||||
const handleCopyCloseTag = (user: FlowCopyVo) => {
|
||||
const userId = user.userId;
|
||||
// 使用split删除用户
|
||||
const index = selectCopyUserList.value.findIndex((item) => item.userId === userId);
|
||||
selectCopyUserList.value.splice(index, 1);
|
||||
selectCopyUserIds.value = selectCopyUserList.value.map((item) => item.userId).join(',');
|
||||
};
|
||||
//加签
|
||||
const openMultiInstanceUser = async () => {
|
||||
multiInstanceUserRef.value.open();
|
||||
};
|
||||
//加签
|
||||
const addMultiInstanceUser = async (data) => {
|
||||
if (data && data.length > 0) {
|
||||
const taskOperationBo = reactive<TaskOperationBo>({
|
||||
userIds: data.map((e) => e.userId),
|
||||
taskId: taskId.value,
|
||||
message: form.value.message
|
||||
});
|
||||
await proxy?.$modal.confirm('是否确认提交?');
|
||||
loading.value = true;
|
||||
buttonDisabled.value = true;
|
||||
await taskOperation(taskOperationBo, 'addSignature').finally(() => {
|
||||
loading.value = false;
|
||||
buttonDisabled.value = false;
|
||||
});
|
||||
dialog.visible = false;
|
||||
emits('submitCallback');
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
} else {
|
||||
proxy?.$modal.msgWarning('请选择用户!');
|
||||
}
|
||||
};
|
||||
//减签
|
||||
const deleteMultiInstanceUser = async (row) => {
|
||||
await proxy?.$modal.confirm('是否确认提交?');
|
||||
loading.value = true;
|
||||
buttonDisabled.value = true;
|
||||
const taskOperationBo = reactive<TaskOperationBo>({
|
||||
userIds: [row.userId],
|
||||
taskId: taskId.value,
|
||||
message: form.value.message
|
||||
});
|
||||
await taskOperation(taskOperationBo, 'reductionSignature').finally(() => {
|
||||
loading.value = false;
|
||||
buttonDisabled.value = false;
|
||||
});
|
||||
dialog.visible = false;
|
||||
emits('submitCallback');
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
};
|
||||
//打开转办
|
||||
const openTransferTask = () => {
|
||||
transferTaskRef.value.open();
|
||||
};
|
||||
//转办
|
||||
const handleTransferTask = async (data) => {
|
||||
if (data && data.length > 0) {
|
||||
const taskOperationBo = reactive<TaskOperationBo>({
|
||||
userId: data[0].userId,
|
||||
taskId: taskId.value,
|
||||
message: form.value.message
|
||||
});
|
||||
await proxy?.$modal.confirm('是否确认提交?');
|
||||
loading.value = true;
|
||||
buttonDisabled.value = true;
|
||||
await taskOperation(taskOperationBo, 'transferTask').finally(() => {
|
||||
loading.value = false;
|
||||
buttonDisabled.value = false;
|
||||
});
|
||||
dialog.visible = false;
|
||||
emits('submitCallback');
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
} else {
|
||||
proxy?.$modal.msgWarning('请选择用户!');
|
||||
}
|
||||
};
|
||||
|
||||
//打开委托
|
||||
const openDelegateTask = () => {
|
||||
delegateTaskRef.value.open();
|
||||
};
|
||||
//委托
|
||||
const handleDelegateTask = async (data) => {
|
||||
if (data && data.length > 0) {
|
||||
const taskOperationBo = reactive<TaskOperationBo>({
|
||||
userId: data[0].userId,
|
||||
taskId: taskId.value,
|
||||
message: form.value.message
|
||||
});
|
||||
await proxy?.$modal.confirm('是否确认提交?');
|
||||
loading.value = true;
|
||||
buttonDisabled.value = true;
|
||||
await taskOperation(taskOperationBo, 'delegateTask').finally(() => {
|
||||
loading.value = false;
|
||||
buttonDisabled.value = false;
|
||||
});
|
||||
dialog.visible = false;
|
||||
emits('submitCallback');
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
} else {
|
||||
proxy?.$modal.msgWarning('请选择用户!');
|
||||
}
|
||||
};
|
||||
//终止任务
|
||||
const handleTerminationTask = async () => {
|
||||
const params = {
|
||||
taskId: taskId.value,
|
||||
comment: form.value.message
|
||||
};
|
||||
await proxy?.$modal.confirm('是否确认终止?');
|
||||
loading.value = true;
|
||||
buttonDisabled.value = true;
|
||||
await terminationTask(params).finally(() => {
|
||||
loading.value = false;
|
||||
buttonDisabled.value = false;
|
||||
});
|
||||
dialog.visible = false;
|
||||
emits('submitCallback');
|
||||
proxy?.$modal.msgSuccess('操作成功');
|
||||
};
|
||||
const handleTaskUser = async () => {
|
||||
const data = await currentTaskAllUser(taskId.value);
|
||||
deleteUserList.value = data.data;
|
||||
if (deleteUserList.value && deleteUserList.value.length > 0) {
|
||||
deleteUserList.value.forEach((e) => {
|
||||
e.nodeName = task.value.nodeName;
|
||||
});
|
||||
}
|
||||
deleteSignatureVisible.value = true;
|
||||
};
|
||||
// 选择人员
|
||||
const choosePeople = async (data) => {
|
||||
if (!data.permissionFlag) {
|
||||
proxy?.$modal.msgError('没有可选择的人员,请联系管理员!');
|
||||
}
|
||||
popUserIds.value = data.permissionFlag;
|
||||
nodeCode.value = data.nodeCode;
|
||||
porUserRef.value.open();
|
||||
};
|
||||
//确认选择
|
||||
const handlePopUser = async (userList) => {
|
||||
const userIds = userList.map((item) => {
|
||||
return item.userId;
|
||||
});
|
||||
const nickNames = userList.map((item) => {
|
||||
return item.nickName;
|
||||
});
|
||||
form.value.assigneeMap[nodeCode.value] = userIds.join(',');
|
||||
nickName.value[nodeCode.value] = nickNames.join(',');
|
||||
};
|
||||
|
||||
/**
|
||||
* 对外暴露子组件方法
|
||||
*/
|
||||
defineExpose({
|
||||
openDialog
|
||||
});
|
||||
</script>
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<div class="top-right-btn" :style="style">
|
||||
<el-row>
|
||||
<el-tooltip class="item" effect="dark" :content="showSearch ? '隐藏搜索' : '显示搜索'" placement="top" v-if="search">
|
||||
<el-tooltip v-if="search" class="item" effect="dark" :content="showSearch ? '隐藏搜索' : '显示搜索'" placement="top">
|
||||
<el-button circle icon="Search" @click="toggleSearch()" />
|
||||
</el-tooltip>
|
||||
<el-tooltip class="item" effect="dark" content="刷新" placement="top">
|
||||
<el-button circle icon="Refresh" @click="refresh()" />
|
||||
</el-tooltip>
|
||||
<el-tooltip class="item" effect="dark" content="显示/隐藏列" placement="top" v-if="columns">
|
||||
<el-tooltip v-if="columns" class="item" effect="dark" content="显示/隐藏列" placement="top">
|
||||
<div class="show-btn">
|
||||
<el-popover placement="bottom" trigger="click">
|
||||
<div class="tree-header">显示/隐藏列</div>
|
||||
@@ -15,9 +15,9 @@
|
||||
ref="columnRef"
|
||||
:data="columns"
|
||||
show-checkbox
|
||||
@check="columnChange"
|
||||
node-key="key"
|
||||
:props="{ label: 'label', children: 'children' }"
|
||||
:props="{ label: 'label', children: 'children' } as any"
|
||||
@check="columnChange"
|
||||
></el-tree>
|
||||
<template #reference>
|
||||
<el-button circle icon="Menu" />
|
||||
@@ -33,51 +33,49 @@
|
||||
import { propTypes } from '@/utils/propTypes';
|
||||
|
||||
const props = defineProps({
|
||||
showSearch: propTypes.bool.def(true),
|
||||
columns: {
|
||||
type: Array as PropType<FieldOption[]>,
|
||||
},
|
||||
search: propTypes.bool.def(true),
|
||||
gutter: propTypes.number.def(10),
|
||||
})
|
||||
showSearch: propTypes.bool.def(true),
|
||||
columns: propTypes.fieldOption,
|
||||
search: propTypes.bool.def(true),
|
||||
gutter: propTypes.number.def(10)
|
||||
});
|
||||
|
||||
const columnRef = ref<ElTreeInstance>();
|
||||
const emits = defineEmits(['update:showSearch', 'queryTable']);
|
||||
|
||||
const style = computed(() => {
|
||||
const ret: any = {};
|
||||
if (props.gutter) {
|
||||
ret.marginRight = `${props.gutter / 2}px`;
|
||||
}
|
||||
return ret;
|
||||
const ret: any = {};
|
||||
if (props.gutter) {
|
||||
ret.marginRight = `${props.gutter / 2}px`;
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
|
||||
// 搜索
|
||||
function toggleSearch() {
|
||||
emits("update:showSearch", !props.showSearch);
|
||||
emits('update:showSearch', !props.showSearch);
|
||||
}
|
||||
|
||||
// 刷新
|
||||
function refresh() {
|
||||
emits("queryTable");
|
||||
emits('queryTable');
|
||||
}
|
||||
|
||||
// 更改数据列的显示和隐藏
|
||||
function columnChange(...args: any[]) {
|
||||
props.columns?.forEach((item) => {
|
||||
item.visible = args[1].checkedKeys.includes(item.key);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// 显隐列初始默认隐藏列
|
||||
onMounted(() => {
|
||||
props.columns?.forEach((item) => {
|
||||
if (item.visible) {
|
||||
columnRef.value?.setChecked(item.key, true, false);
|
||||
// value.value.push(item.key);
|
||||
}
|
||||
})
|
||||
})
|
||||
props.columns?.forEach((item) => {
|
||||
if (item.visible) {
|
||||
columnRef.value?.setChecked(item.key, true, false);
|
||||
// value.value.push(item.key);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -93,7 +91,7 @@ onMounted(() => {
|
||||
.my-el-transfer {
|
||||
text-align: center;
|
||||
}
|
||||
.tree-header{
|
||||
.tree-header {
|
||||
width: 100%;
|
||||
line-height: 24px;
|
||||
text-align: center;
|
||||
|
||||
250
src/components/RoleSelect/index.vue
Normal file
@@ -0,0 +1,250 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog v-model="roleDialog.visible.value" :title="roleDialog.title.value" width="80%" append-to-body>
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<el-form-item label="角色名称" prop="roleName">
|
||||
<el-input v-model="queryParams.roleName" placeholder="请输入角色名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="权限字符" prop="roleKey">
|
||||
<el-input v-model="queryParams.roleKey" placeholder="请输入权限字符" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<el-tag v-for="role in selectRoleList" :key="role.roleId" closable style="margin: 2px" @close="handleCloseTag(role)">
|
||||
{{ role.roleName }}
|
||||
</el-tag>
|
||||
</template>
|
||||
|
||||
<vxe-table
|
||||
ref="tableRef"
|
||||
height="400px"
|
||||
border
|
||||
show-overflow
|
||||
:data="roleList"
|
||||
:loading="loading"
|
||||
:row-config="{ keyField: 'roleId' }"
|
||||
:checkbox-config="{ reserve: true, checkRowKeys: defaultSelectRoleIds }"
|
||||
highlight-current-row
|
||||
@checkbox-all="handleCheckboxAll"
|
||||
@checkbox-change="handleCheckboxChange"
|
||||
>
|
||||
<vxe-column type="checkbox" width="50" align="center" />
|
||||
<vxe-column v-if="false" key="roleId" label="角色编号" />
|
||||
<vxe-column field="roleName" title="角色名称" />
|
||||
<vxe-column field="roleKey" title="权限字符" />
|
||||
<vxe-column field="roleSort" title="显示顺序" width="100" />
|
||||
<vxe-column title="状态" align="center" width="100">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="sys_normal_disable" :value="scope.row.status"></dict-tag>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="createTime" title="创建时间" align="center">
|
||||
<template #default="scope">
|
||||
<span>{{ proxy.parseTime(scope.row.createTime) }}</span>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
|
||||
<pagination
|
||||
v-if="total > 0"
|
||||
v-model:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="pageList"
|
||||
/>
|
||||
</el-card>
|
||||
<template #footer>
|
||||
<el-button @click="close">取消</el-button>
|
||||
<el-button type="primary" @click="confirm">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { RoleVO, RoleQuery } from '@/api/system/role/types';
|
||||
import { VxeTableInstance } from 'vxe-table';
|
||||
import useDialog from '@/hooks/useDialog';
|
||||
import api from '@/api/system/role';
|
||||
interface PropType {
|
||||
modelValue?: RoleVO[] | RoleVO | undefined;
|
||||
multiple?: boolean;
|
||||
data?: string | number | (string | number)[];
|
||||
}
|
||||
const prop = withDefaults(defineProps<PropType>(), {
|
||||
multiple: true,
|
||||
modelValue: undefined,
|
||||
data: undefined
|
||||
});
|
||||
const emit = defineEmits(['update:modelValue', 'confirmCallBack']);
|
||||
|
||||
const router = useRouter();
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
|
||||
|
||||
const roleList = ref<RoleVO[]>();
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const total = ref(0);
|
||||
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
|
||||
const selectRoleList = ref<RoleVO[]>([]);
|
||||
|
||||
const roleDialog = useDialog({
|
||||
title: '角色选择'
|
||||
});
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const tableRef = ref<VxeTableInstance<RoleVO>>();
|
||||
|
||||
const queryParams = ref<RoleQuery>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
roleName: '',
|
||||
roleKey: '',
|
||||
status: ''
|
||||
});
|
||||
|
||||
const defaultSelectRoleIds = computed(() => computedIds(prop.data));
|
||||
|
||||
const confirm = () => {
|
||||
emit('update:modelValue', selectRoleList.value);
|
||||
emit('confirmCallBack', selectRoleList.value);
|
||||
roleDialog.closeDialog();
|
||||
};
|
||||
|
||||
const computedIds = (data) => {
|
||||
if (data instanceof Array) {
|
||||
return [...data];
|
||||
} else if (typeof data === 'string') {
|
||||
return data.split(',');
|
||||
} else if (typeof data === 'number') {
|
||||
return [data];
|
||||
} else {
|
||||
console.warn('<RoleSelect> The data type of data should be array or string or number, but I received other');
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询角色列表
|
||||
*/
|
||||
const getList = () => {
|
||||
loading.value = true;
|
||||
api.listRole(proxy?.addDateRange(queryParams.value, dateRange.value)).then((res) => {
|
||||
roleList.value = res.rows;
|
||||
total.value = res.total;
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
const pageList = async () => {
|
||||
await getList();
|
||||
const roles = roleList.value.filter((item) => {
|
||||
return selectRoleList.value.some((role) => role.roleId === item.roleId);
|
||||
});
|
||||
await tableRef.value.setCheckboxRow(roles, true);
|
||||
};
|
||||
/**
|
||||
* 搜索按钮操作
|
||||
*/
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
/** 重置 */
|
||||
const resetQuery = () => {
|
||||
dateRange.value = ['', ''];
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
const handleCheckboxChange = (checked) => {
|
||||
if (!prop.multiple && checked.checked) {
|
||||
tableRef.value.setCheckboxRow(selectRoleList.value, false);
|
||||
selectRoleList.value = [];
|
||||
}
|
||||
const row = checked.row;
|
||||
if (checked.checked) {
|
||||
selectRoleList.value.push(row);
|
||||
} else {
|
||||
selectRoleList.value = selectRoleList.value.filter((item) => {
|
||||
return item.roleId !== row.roleId;
|
||||
});
|
||||
}
|
||||
};
|
||||
const handleCheckboxAll = (checked) => {
|
||||
const rows = roleList.value;
|
||||
if (checked.checked) {
|
||||
rows.forEach((row) => {
|
||||
if (!selectRoleList.value.some((item) => item.roleId === row.roleId)) {
|
||||
selectRoleList.value.push(row);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
selectRoleList.value = selectRoleList.value.filter((item) => {
|
||||
return !rows.some((row) => row.roleId === item.roleId);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleCloseTag = (user: RoleVO) => {
|
||||
const roleId = user.roleId;
|
||||
// 使用split删除用户
|
||||
const index = selectRoleList.value.findIndex((item) => item.roleId === roleId);
|
||||
const rows = selectRoleList.value[index];
|
||||
tableRef.value?.setCheckboxRow(rows, false);
|
||||
selectRoleList.value.splice(index, 1);
|
||||
};
|
||||
/**
|
||||
* 初始化选中数据
|
||||
*/
|
||||
const initSelectRole = async () => {
|
||||
if (defaultSelectRoleIds.value.length > 0) {
|
||||
const { data } = await api.optionSelect(defaultSelectRoleIds.value);
|
||||
selectRoleList.value = data;
|
||||
const users = roleList.value.filter((item) => {
|
||||
return defaultSelectRoleIds.value.includes(String(item.roleId));
|
||||
});
|
||||
await nextTick(() => {
|
||||
tableRef.value.setCheckboxRow(users, true);
|
||||
});
|
||||
}
|
||||
};
|
||||
const close = () => {
|
||||
roleDialog.closeDialog();
|
||||
};
|
||||
watch(
|
||||
() => roleDialog.visible.value,
|
||||
(newValue: boolean) => {
|
||||
if (newValue) {
|
||||
initSelectRole();
|
||||
} else {
|
||||
tableRef.value.clearCheckboxReserve();
|
||||
tableRef.value.clearCheckboxRow();
|
||||
resetQuery();
|
||||
selectRoleList.value = [];
|
||||
}
|
||||
}
|
||||
);
|
||||
onMounted(() => {
|
||||
getList(); // 初始化列表数据
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
open: roleDialog.openDialog,
|
||||
close: roleDialog.closeDialog
|
||||
});
|
||||
</script>
|
||||
@@ -4,10 +4,10 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
const url = ref('https://plus-doc.dromara.org/');
|
||||
|
||||
function goto() {
|
||||
window.open(url.value)
|
||||
window.open(url.value);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
const url = ref('https://gitee.com/dromara/RuoYi-Vue-Plus');
|
||||
|
||||
function goto() {
|
||||
window.open(url.value)
|
||||
window.open(url.value);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -16,20 +16,20 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import useAppStore from "@/store/modules/app";
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
|
||||
const appStore = useAppStore();
|
||||
const size = computed(() => appStore.size);
|
||||
|
||||
const sizeOptions = ref([
|
||||
{ label: "较大", value: "large" },
|
||||
{ label: "默认", value: "default" },
|
||||
{ label: "稍小", value: "small" },
|
||||
{ label: '较大', value: 'large' },
|
||||
{ label: '默认', value: 'default' },
|
||||
{ label: '稍小', value: 'small' }
|
||||
]);
|
||||
|
||||
const handleSetSize = (size: string) => {
|
||||
appStore.setSize(size);
|
||||
}
|
||||
const handleSetSize = (size: 'large' | 'default' | 'small') => {
|
||||
appStore.setSize(size);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -8,20 +8,20 @@
|
||||
import { propTypes } from '@/utils/propTypes';
|
||||
|
||||
const props = defineProps({
|
||||
iconClass: propTypes.string.isRequired,
|
||||
className: propTypes.string.def(''),
|
||||
color: propTypes.string.def(''),
|
||||
})
|
||||
const iconName = computed(() => `#icon-${props.iconClass}`);
|
||||
iconClass: propTypes.string.isRequired,
|
||||
className: propTypes.string.def(''),
|
||||
color: propTypes.string.def('')
|
||||
});
|
||||
const iconName = computed(() => `#icon-${props.iconClass}`);
|
||||
const svgClass = computed(() => {
|
||||
if (props.className) {
|
||||
return `svg-icon ${props.className}`
|
||||
}
|
||||
return 'svg-icon'
|
||||
})
|
||||
if (props.className) {
|
||||
return `svg-icon ${props.className}`;
|
||||
}
|
||||
return 'svg-icon';
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scope lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
.sub-el-icon,
|
||||
.nav-icon {
|
||||
display: inline-block;
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
<template>
|
||||
<el-menu :default-active="activeMenu" mode="horizontal" @select="handleSelect" :ellipsis="false">
|
||||
<el-menu :default-active="activeMenu" mode="horizontal" :ellipsis="false" @select="handleSelect">
|
||||
<template v-for="(item, index) in topMenus">
|
||||
<el-menu-item :style="{'--theme': theme}" :index="item.path" :key="index" v-if="index < visibleNumber"
|
||||
><svg-icon
|
||||
v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
|
||||
:icon-class="item.meta ? item.meta.icon : '' " /> {{ item.meta?.title }}</el-menu-item
|
||||
<el-menu-item v-if="index < visibleNumber" :key="index" :style="{ '--theme': theme }" :index="item.path"
|
||||
><svg-icon v-if="item.meta && item.meta.icon && item.meta.icon !== '#'" :icon-class="item.meta ? item.meta.icon : ''" />
|
||||
{{ item.meta?.title }}</el-menu-item
|
||||
>
|
||||
</template>
|
||||
|
||||
<!-- 顶部菜单超出数量折叠 -->
|
||||
<el-sub-menu :style="{'--theme': theme}" index="more" v-if="topMenus.length > visibleNumber">
|
||||
<el-sub-menu v-if="topMenus.length > visibleNumber" :style="{ '--theme': theme }" index="more">
|
||||
<template #title>更多菜单</template>
|
||||
<template v-for="(item, index) in topMenus">
|
||||
<el-menu-item :index="item.path" :key="index" v-if="index >= visibleNumber"
|
||||
><svg-icon :icon-class="item.meta ? item.meta.icon : '' " /> {{ item.meta?.title }}</el-menu-item
|
||||
<el-menu-item v-if="index >= visibleNumber" :key="index" :index="item.path"
|
||||
><svg-icon :icon-class="item.meta ? item.meta.icon : ''" /> {{ item.meta?.title }}</el-menu-item
|
||||
>
|
||||
</template>
|
||||
</el-sub-menu>
|
||||
@@ -23,10 +22,10 @@
|
||||
<script setup lang="ts">
|
||||
import { constantRoutes } from '@/router';
|
||||
import { isHttp } from '@/utils/validate';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
import useSettingsStore from '@/store/modules/settings';
|
||||
import usePermissionStore from '@/store/modules/permission';
|
||||
import { RouteOption } from 'vue-router';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import { useSettingsStore } from '@/store/modules/settings';
|
||||
import { usePermissionStore } from '@/store/modules/permission';
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
// 顶部栏初始数
|
||||
const visibleNumber = ref<number>(-1);
|
||||
@@ -35,88 +34,91 @@ const currentIndex = ref<string>();
|
||||
// 隐藏侧边栏路由
|
||||
const hideList = ['/index', '/user/profile'];
|
||||
|
||||
const appStore = useAppStore()
|
||||
const settingsStore = useSettingsStore()
|
||||
const permissionStore = usePermissionStore()
|
||||
const appStore = useAppStore();
|
||||
const settingsStore = useSettingsStore();
|
||||
const permissionStore = usePermissionStore();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
// 主题颜色
|
||||
const theme = computed(() => settingsStore.theme);
|
||||
// 所有的路由信息
|
||||
const routers = computed(() => permissionStore.topbarRouters);
|
||||
const routers = computed(() => permissionStore.getTopbarRoutes());
|
||||
|
||||
// 顶部显示菜单
|
||||
const topMenus = computed(() => {
|
||||
let topMenus:RouteOption[] = [];
|
||||
const topMenus: RouteRecordRaw[] = [];
|
||||
routers.value.map((menu) => {
|
||||
if (menu.hidden !== true) {
|
||||
// 兼容顶部栏一级菜单内部跳转
|
||||
if (menu.path === "/") {
|
||||
topMenus.push(menu.children? menu.children[0] : menu);
|
||||
if (menu.path === '/' && menu.children) {
|
||||
topMenus.push(menu.children ? menu.children[0] : menu);
|
||||
} else {
|
||||
topMenus.push(menu);
|
||||
topMenus.push(menu);
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
return topMenus;
|
||||
})
|
||||
});
|
||||
|
||||
// 设置子路由
|
||||
const childrenMenus = computed(() => {
|
||||
let childrenMenus:RouteOption[] = [];
|
||||
const childrenMenus: RouteRecordRaw[] = [];
|
||||
routers.value.map((router) => {
|
||||
router.children?.forEach((item) => {
|
||||
if (item.parentPath === undefined) {
|
||||
if(router.path === "/") {
|
||||
item.path = "/" + item.path;
|
||||
if (router.path === '/') {
|
||||
item.path = '/' + item.path;
|
||||
} else {
|
||||
if(!isHttp(item.path)) {
|
||||
item.path = router.path + "/" + item.path;
|
||||
if (!isHttp(item.path)) {
|
||||
item.path = router.path + '/' + item.path;
|
||||
}
|
||||
}
|
||||
item.parentPath = router.path;
|
||||
}
|
||||
childrenMenus.push(item);
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
return constantRoutes.concat(childrenMenus);
|
||||
})
|
||||
});
|
||||
|
||||
// 默认激活的菜单
|
||||
const activeMenu = computed(() => {
|
||||
const path = route.path;
|
||||
let path = route.path;
|
||||
if (path === '/index') {
|
||||
path = '/system/user';
|
||||
}
|
||||
let activePath = path;
|
||||
if (path !== undefined && path.lastIndexOf("/") > 0 && hideList.indexOf(path) === -1) {
|
||||
if (path !== undefined && path.lastIndexOf('/') > 0 && hideList.indexOf(path) === -1) {
|
||||
const tmpPath = path.substring(1, path.length);
|
||||
activePath = "/" + tmpPath.substring(0, tmpPath.indexOf("/"));
|
||||
if (!route.meta.link) {
|
||||
appStore.toggleSideBarHide(false);
|
||||
activePath = '/' + tmpPath.substring(0, tmpPath.indexOf('/'));
|
||||
appStore.toggleSideBarHide(false);
|
||||
}
|
||||
} else if(!route.children) {
|
||||
} else if (!route.children) {
|
||||
activePath = path;
|
||||
appStore.toggleSideBarHide(true);
|
||||
}
|
||||
activeRoutes(activePath);
|
||||
return activePath;
|
||||
})
|
||||
});
|
||||
|
||||
const setVisibleNumber = () => {
|
||||
const width = document.body.getBoundingClientRect().width / 3;
|
||||
visibleNumber.value = parseInt(String(width / 85));
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelect = (key: string) => {
|
||||
currentIndex.value = key;
|
||||
const route = routers.value.find(item => item.path === key);
|
||||
const route = routers.value.find((item) => item.path === key);
|
||||
if (isHttp(key)) {
|
||||
// http(s):// 路径新窗口打开
|
||||
window.open(key, "_blank");
|
||||
window.open(key, '_blank');
|
||||
} else if (!route || !route.children) {
|
||||
// 没有子路由路径内部打开
|
||||
const routeMenu = childrenMenus.value.find(item => item.path === key);
|
||||
const routeMenu = childrenMenus.value.find((item) => item.path === key);
|
||||
if (routeMenu && routeMenu.query) {
|
||||
let query = JSON.parse(routeMenu.query);
|
||||
const query = JSON.parse(routeMenu.query);
|
||||
router.push({ path: key, query: query });
|
||||
} else {
|
||||
router.push({ path: key });
|
||||
@@ -127,35 +129,35 @@ const handleSelect = (key: string) => {
|
||||
activeRoutes(key);
|
||||
appStore.toggleSideBarHide(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const activeRoutes = (key: string) => {
|
||||
let routes:RouteOption[] = [];
|
||||
const routes: RouteRecordRaw[] = [];
|
||||
if (childrenMenus.value && childrenMenus.value.length > 0) {
|
||||
childrenMenus.value.map((item) => {
|
||||
if (key == item.parentPath || (key == "index" && "" == item.path)) {
|
||||
if (key == item.parentPath || (key == 'index' && '' == item.path)) {
|
||||
routes.push(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
if(routes.length > 0) {
|
||||
if (routes.length > 0) {
|
||||
permissionStore.setSidebarRouters(routes);
|
||||
} else {
|
||||
appStore.toggleSideBarHide(true);
|
||||
}
|
||||
return routes;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('resize', setVisibleNumber)
|
||||
})
|
||||
window.addEventListener('resize', setVisibleNumber);
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', setVisibleNumber)
|
||||
})
|
||||
window.removeEventListener('resize', setVisibleNumber);
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
setVisibleNumber()
|
||||
})
|
||||
setVisibleNumber();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@@ -168,7 +170,8 @@ onMounted(() => {
|
||||
margin: 0 10px !important;
|
||||
}
|
||||
|
||||
.topmenu-container.el-menu--horizontal > .el-menu-item.is-active, .el-menu--horizontal > .el-sub-menu.is-active .el-submenu__title {
|
||||
.topmenu-container.el-menu--horizontal > .el-menu-item.is-active,
|
||||
.el-menu--horizontal > .el-sub-menu.is-active .el-submenu__title {
|
||||
border-bottom: 2px solid #{'var(--theme)'} !important;
|
||||
color: #303133;
|
||||
}
|
||||
@@ -184,7 +187,9 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
/* 背景色隐藏 */
|
||||
.topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):focus, .topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):hover, .topmenu-container.el-menu--horizontal>.el-submenu .el-submenu__title:hover {
|
||||
.topmenu-container.el-menu--horizontal > .el-menu-item:not(.is-disabled):focus,
|
||||
.topmenu-container.el-menu--horizontal > .el-menu-item:not(.is-disabled):hover,
|
||||
.topmenu-container.el-menu--horizontal > .el-submenu .el-submenu__title:hover {
|
||||
background-color: #ffffff !important;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
<template>
|
||||
<div class="el-tree-select">
|
||||
<el-select
|
||||
style="width: 100%"
|
||||
v-model="valueId"
|
||||
ref="treeSelect"
|
||||
:filterable="true"
|
||||
:clearable="true"
|
||||
@clear="clearHandle"
|
||||
:filter-method="selectFilterData"
|
||||
:placeholder="placeholder"
|
||||
>
|
||||
<el-option :value="valueId" :label="valueTitle">
|
||||
<el-tree
|
||||
id="tree-option"
|
||||
ref="selectTree"
|
||||
:accordion="accordion"
|
||||
:data="options"
|
||||
:props="objMap"
|
||||
:node-key="objMap.value"
|
||||
:expand-on-click-node="false"
|
||||
:default-expanded-keys="defaultExpandedKey"
|
||||
:filter-node-method="filterNode"
|
||||
@node-click="handleNodeClick"
|
||||
></el-tree>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
const props = defineProps({
|
||||
/* 配置项 */
|
||||
objMap: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
value: 'id', // ID字段名
|
||||
label: 'label', // 显示名称
|
||||
children: 'children' // 子级字段名
|
||||
}
|
||||
}
|
||||
},
|
||||
/* 自动收起 */
|
||||
accordion: {
|
||||
type: Boolean,
|
||||
default: () => {
|
||||
return false
|
||||
}
|
||||
},
|
||||
/**当前双向数据绑定的值 */
|
||||
value: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
/**当前的数据 */
|
||||
options: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
/**输入框内部的文字 */
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const selectTree = ref<ElTreeSelectInstance>();
|
||||
|
||||
const emit = defineEmits(['update:value']);
|
||||
|
||||
const valueId = computed({
|
||||
get: () => props.value,
|
||||
set: (val) => {
|
||||
emit('update:value', val)
|
||||
}
|
||||
});
|
||||
const valueTitle = ref('');
|
||||
const defaultExpandedKey = ref<any[]>([]);
|
||||
|
||||
const initHandle = () => {
|
||||
nextTick(() => {
|
||||
const selectedValue = valueId.value;
|
||||
if (selectedValue !== null && typeof (selectedValue) !== 'undefined') {
|
||||
const node = selectTree.value?.getNode(selectedValue)
|
||||
if (node) {
|
||||
valueTitle.value = node.data[props.objMap.label]
|
||||
selectTree.value?.setCurrentKey(selectedValue) // 设置默认选中
|
||||
defaultExpandedKey.value = [selectedValue] // 设置默认展开
|
||||
}
|
||||
} else {
|
||||
clearHandle()
|
||||
}
|
||||
})
|
||||
}
|
||||
const handleNodeClick = (node: any) => {
|
||||
valueTitle.value = node[props.objMap.label]
|
||||
valueId.value = node[props.objMap.value];
|
||||
defaultExpandedKey.value = [];
|
||||
selectTree.value?.blur()
|
||||
selectFilterData('')
|
||||
}
|
||||
const selectFilterData = (val: any) => {
|
||||
selectTree.value?.filter(val)
|
||||
}
|
||||
const filterNode = (value: any, data: any) => {
|
||||
if (!value) return true
|
||||
return data[props.objMap['label']].indexOf(value) !== -1
|
||||
}
|
||||
const clearHandle = () => {
|
||||
valueTitle.value = ''
|
||||
valueId.value = ''
|
||||
defaultExpandedKey.value = [];
|
||||
clearSelected()
|
||||
}
|
||||
const clearSelected = () => {
|
||||
const allNode = document.querySelectorAll('#tree-option .el-tree-node')
|
||||
allNode.forEach((element) => element.classList.remove('is-current'))
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initHandle()
|
||||
})
|
||||
|
||||
watch(valueId, () => {
|
||||
initHandle();
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "@/assets/styles/variables.module.scss";
|
||||
|
||||
.el-scrollbar .el-scrollbar__view .el-select-dropdown__item {
|
||||
padding: 0;
|
||||
background-color: #fff;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.el-select-dropdown__item.selected {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
ul li .el-tree .el-tree-node__content {
|
||||
height: auto;
|
||||
padding: 0 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:deep(.el-tree-node__content:hover),
|
||||
:deep(.el-tree-node__content:active),
|
||||
:deep(.is-current > div:first-child),
|
||||
:deep(.el-tree-node__content:focus) {
|
||||
background-color: mix(#fff, $--color-primary, 90%);
|
||||
color: $--color-primary;
|
||||
}
|
||||
</style>
|
||||
311
src/components/UserSelect/index.vue
Normal file
@@ -0,0 +1,311 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog v-model="userDialog.visible.value" :title="userDialog.title.value" width="80%" append-to-body>
|
||||
<el-row :gutter="20">
|
||||
<!-- 部门树 -->
|
||||
<el-col :lg="4" :xs="24" style="">
|
||||
<el-card shadow="hover">
|
||||
<el-input v-model="deptName" placeholder="请输入部门名称" prefix-icon="Search" clearable />
|
||||
<el-tree
|
||||
ref="deptTreeRef"
|
||||
class="mt-2"
|
||||
node-key="id"
|
||||
:data="deptOptions"
|
||||
:props="{ label: 'label', children: 'children' } as any"
|
||||
:expand-on-click-node="false"
|
||||
:filter-node-method="filterNode"
|
||||
highlight-current
|
||||
default-expand-all
|
||||
@node-click="handleNodeClick"
|
||||
/>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :lg="20" :xs="24">
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
||||
<el-form-item label="用户名称" prop="userName">
|
||||
<el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="phonenumber">
|
||||
<el-input v-model="queryParams.phonenumber" placeholder="请输入手机号码" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="() => resetQuery()">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<el-card shadow="hover">
|
||||
<template v-if="prop.multiple" #header>
|
||||
<el-tag v-for="user in selectUserList" :key="user.userId" closable style="margin: 2px" @close="handleCloseTag(user)">
|
||||
{{ user.nickName }}
|
||||
</el-tag>
|
||||
</template>
|
||||
|
||||
<vxe-table
|
||||
ref="tableRef"
|
||||
height="400px"
|
||||
border
|
||||
show-overflow
|
||||
:data="userList"
|
||||
:loading="loading"
|
||||
:row-config="{ keyField: 'userId', isHover: true }"
|
||||
:checkbox-config="{ reserve: true, trigger: 'row', highlight: true, showHeader: prop.multiple }"
|
||||
@checkbox-all="handleCheckboxAll"
|
||||
@checkbox-change="handleCheckboxChange"
|
||||
>
|
||||
<vxe-column type="checkbox" width="50" align="center" />
|
||||
<vxe-column key="userId" title="用户编号" align="center" field="userId" />
|
||||
<vxe-column key="userName" title="用户名称" align="center" field="userName" />
|
||||
<vxe-column key="nickName" title="用户昵称" align="center" field="nickName" />
|
||||
<vxe-column key="deptName" title="部门" align="center" field="deptName" />
|
||||
<vxe-column key="phonenumber" title="手机号码" align="center" field="phonenumber" width="120" />
|
||||
<vxe-column key="status" title="状态" align="center">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="sys_normal_disable" :value="scope.row.status"></dict-tag>
|
||||
</template>
|
||||
</vxe-column>
|
||||
|
||||
<vxe-column title="创建时间" align="center" width="160">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.createTime }}</span>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
:total="total"
|
||||
@pagination="pageList"
|
||||
/>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="close">取消</el-button>
|
||||
<el-button type="primary" @click="confirm">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import api from '@/api/system/user';
|
||||
import { UserQuery, UserVO } from '@/api/system/user/types';
|
||||
import { DeptTreeVO, DeptVO } from '@/api/system/dept/types';
|
||||
import { VxeTableInstance } from 'vxe-table';
|
||||
import useDialog from '@/hooks/useDialog';
|
||||
|
||||
interface PropType {
|
||||
modelValue?: UserVO[] | UserVO | undefined;
|
||||
multiple?: boolean;
|
||||
data?: string | number | (string | number)[] | undefined;
|
||||
userIds?: string | number | (string | number)[] | undefined;
|
||||
}
|
||||
const prop = withDefaults(defineProps<PropType>(), {
|
||||
multiple: true,
|
||||
modelValue: undefined,
|
||||
data: undefined,
|
||||
userIds: undefined
|
||||
});
|
||||
const emit = defineEmits(['update:modelValue', 'confirmCallBack']);
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
|
||||
|
||||
const userList = ref<UserVO[]>();
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const total = ref(0);
|
||||
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
|
||||
const deptName = ref('');
|
||||
const deptOptions = ref<DeptTreeVO[]>([]);
|
||||
const selectUserList = ref<UserVO[]>([]);
|
||||
|
||||
const deptTreeRef = ref<ElTreeInstance>();
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const tableRef = ref<VxeTableInstance<UserVO>>();
|
||||
|
||||
const userDialog = useDialog({
|
||||
title: '用户选择'
|
||||
});
|
||||
|
||||
const queryParams = ref<UserQuery>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
userName: '',
|
||||
phonenumber: '',
|
||||
status: '',
|
||||
deptId: '',
|
||||
roleId: '',
|
||||
userIds: ''
|
||||
});
|
||||
|
||||
const defaultSelectUserIds = computed(() => computedIds(prop.data));
|
||||
|
||||
/** 根据名称筛选部门树 */
|
||||
watchEffect(
|
||||
() => {
|
||||
deptTreeRef.value?.filter(deptName.value);
|
||||
},
|
||||
{
|
||||
flush: 'post' // watchEffect会在DOM挂载或者更新之前就会触发,此属性控制在DOM元素更新后运行
|
||||
}
|
||||
);
|
||||
|
||||
const confirm = () => {
|
||||
emit('update:modelValue', selectUserList.value);
|
||||
emit('confirmCallBack', selectUserList.value);
|
||||
userDialog.closeDialog();
|
||||
};
|
||||
|
||||
const computedIds = (data) => {
|
||||
if (data === '' || data === null || data === undefined) {
|
||||
return [];
|
||||
}
|
||||
if (data instanceof Array) {
|
||||
return data.map((item) => String(item));
|
||||
} else if (typeof data === 'string') {
|
||||
return data.split(',');
|
||||
} else if (typeof data === 'number') {
|
||||
return [String(data)];
|
||||
} else {
|
||||
console.warn('<UserSelect> The data type of data should be array or string or number, but I received other');
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
/** 通过条件过滤节点 */
|
||||
const filterNode = (value: string, data: any) => {
|
||||
if (!value) return true;
|
||||
return data.label.indexOf(value) !== -1;
|
||||
};
|
||||
|
||||
/** 查询部门下拉树结构 */
|
||||
const getTreeSelect = async () => {
|
||||
const res = await api.deptTreeSelect();
|
||||
deptOptions.value = res.data;
|
||||
};
|
||||
|
||||
/** 查询用户列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
queryParams.value.userIds = prop.userIds;
|
||||
const res = await api.listUser(proxy?.addDateRange(queryParams.value, dateRange.value));
|
||||
loading.value = false;
|
||||
userList.value = res.rows;
|
||||
total.value = res.total;
|
||||
};
|
||||
|
||||
const pageList = async () => {
|
||||
await getList();
|
||||
const users = userList.value.filter((item) => {
|
||||
return selectUserList.value.some((user) => user.userId === item.userId);
|
||||
});
|
||||
await tableRef.value.setCheckboxRow(users, true);
|
||||
};
|
||||
|
||||
/** 节点单击事件 */
|
||||
const handleNodeClick = (data: DeptVO) => {
|
||||
queryParams.value.deptId = data.id;
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = (refresh = true) => {
|
||||
dateRange.value = ['', ''];
|
||||
queryFormRef.value?.resetFields();
|
||||
queryParams.value.pageNum = 1;
|
||||
queryParams.value.deptId = undefined;
|
||||
deptTreeRef.value?.setCurrentKey(undefined);
|
||||
refresh && handleQuery();
|
||||
};
|
||||
|
||||
const handleCheckboxChange = (checked) => {
|
||||
if (!prop.multiple && checked.checked) {
|
||||
tableRef.value.setCheckboxRow(selectUserList.value, false);
|
||||
selectUserList.value = [];
|
||||
}
|
||||
const row = checked.row;
|
||||
if (checked.checked) {
|
||||
selectUserList.value.push(row);
|
||||
} else {
|
||||
selectUserList.value = selectUserList.value.filter((item) => {
|
||||
return item.userId !== row.userId;
|
||||
});
|
||||
}
|
||||
};
|
||||
const handleCheckboxAll = (checked) => {
|
||||
const rows = userList.value;
|
||||
if (checked.checked) {
|
||||
rows.forEach((row) => {
|
||||
if (!selectUserList.value.some((item) => item.userId === row.userId)) {
|
||||
selectUserList.value.push(row);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
selectUserList.value = selectUserList.value.filter((item) => {
|
||||
return !rows.some((row) => row.userId === item.userId);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleCloseTag = (user: UserVO) => {
|
||||
const userId = user.userId;
|
||||
// 使用split删除用户
|
||||
const index = selectUserList.value.findIndex((item) => item.userId === userId);
|
||||
const rows = selectUserList.value[index];
|
||||
tableRef.value?.setCheckboxRow(rows, false);
|
||||
selectUserList.value.splice(index, 1);
|
||||
};
|
||||
|
||||
const initSelectUser = async () => {
|
||||
if (defaultSelectUserIds.value.length > 0) {
|
||||
const { data } = await api.optionSelect(defaultSelectUserIds.value);
|
||||
selectUserList.value = data;
|
||||
const users = userList.value.filter((item) => {
|
||||
return defaultSelectUserIds.value.includes(String(item.userId));
|
||||
});
|
||||
await nextTick(() => {
|
||||
tableRef.value.setCheckboxRow(users, true);
|
||||
});
|
||||
}
|
||||
};
|
||||
const close = () => {
|
||||
userDialog.closeDialog();
|
||||
};
|
||||
|
||||
watch(
|
||||
() => userDialog.visible.value,
|
||||
async (newValue: boolean) => {
|
||||
if (newValue) {
|
||||
await getTreeSelect(); // 初始化部门数据
|
||||
await getList(); // 初始化列表数据
|
||||
await initSelectUser();
|
||||
} else {
|
||||
tableRef.value.clearCheckboxReserve();
|
||||
tableRef.value.clearCheckboxRow();
|
||||
resetQuery(false);
|
||||
selectUserList.value = [];
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
defineExpose({
|
||||
open: userDialog.openDialog,
|
||||
close: userDialog.closeDialog
|
||||
});
|
||||
</script>
|
||||
@@ -9,18 +9,18 @@ import { propTypes } from '@/utils/propTypes';
|
||||
|
||||
const props = defineProps({
|
||||
src: propTypes.string.isRequired
|
||||
})
|
||||
});
|
||||
|
||||
const height = ref(document.documentElement.clientHeight - 94.5 + "px;")
|
||||
const loading = ref(true)
|
||||
const url = computed(() => props.src)
|
||||
const height = ref(document.documentElement.clientHeight - 94.5 + 'px;');
|
||||
const loading = ref(true);
|
||||
const url = computed(() => props.src);
|
||||
|
||||
onMounted(() => {
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
}, 300);
|
||||
window.onresize = function temp() {
|
||||
height.value = document.documentElement.clientHeight - 94.5 + "px;";
|
||||
height.value = document.documentElement.clientHeight - 94.5 + 'px;';
|
||||
};
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
* v-copyText 复制文本内容
|
||||
* Copyright (c) 2022 ruoyi
|
||||
*/
|
||||
import { DirectiveBinding } from 'vue';
|
||||
|
||||
export default {
|
||||
beforeMount(el: any, { value, arg }: any) {
|
||||
beforeMount(el: any, { value, arg }: DirectiveBinding) {
|
||||
if (arg === 'callback') {
|
||||
el.$copyCallback = value;
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Directive, DirectiveBinding } from 'vue';
|
||||
import useUserStore from '@/store/modules/user';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
/**
|
||||
* 操作权限处理
|
||||
*/
|
||||
@@ -9,7 +9,7 @@ export const hasPermi: Directive = {
|
||||
// 「其他角色」按钮权限校验
|
||||
const { value } = binding;
|
||||
if (value && value instanceof Array && value.length > 0) {
|
||||
const hasPermission = permissions.some((permi) => {
|
||||
const hasPermission = permissions.some((permi: string) => {
|
||||
return permi === '*:*:*' || value.includes(permi);
|
||||
});
|
||||
if (!hasPermission) {
|
||||
@@ -30,8 +30,8 @@ export const hasRoles: Directive = {
|
||||
const { value } = binding;
|
||||
const { roles } = useUserStore();
|
||||
if (value && value instanceof Array && value.length > 0) {
|
||||
const hasRole = roles.some((role) => {
|
||||
return role === 'admin' || value.includes(role);
|
||||
const hasRole = roles.some((role: string) => {
|
||||
return role === 'superadmin' || role === 'admin' || value.includes(role);
|
||||
});
|
||||
if (!hasRole) {
|
||||
el.parentNode && el.parentNode.removeChild(el);
|
||||
|
||||
5
src/enums/LanguageEnum.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export enum LanguageEnum {
|
||||
zh_CN = 'zh_CN',
|
||||
|
||||
en_US = 'en_US'
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
export enum SettingTypeEnum {
|
||||
TITLE = 'title',
|
||||
THEME = 'theme',
|
||||
SIDE_THEME = 'sideTheme',
|
||||
SHOW_SETTINGS = 'showSettings',
|
||||
TOP_NAV = 'topNav',
|
||||
TAGS_VIEW = 'tagsView',
|
||||
FIXED_HEADER = 'fixedHeader',
|
||||
SIDEBAR_LOGO = 'sidebarLogo',
|
||||
DYNAMIC_TITLE = 'dynamicTitle',
|
||||
ANIMATION_ENABLE = 'animationEnable',
|
||||
LAYOUT = 'layout',
|
||||
DARK = 'dark',
|
||||
|
||||
LAYOUT_SETTING = 'layout-setting'
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
export enum ThemeEnum {
|
||||
DARK = 'theme-dark',
|
||||
LIGHT = 'theme-light'
|
||||
}
|
||||
31
src/hooks/useDialog.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { Ref } from 'vue';
|
||||
|
||||
interface Options {
|
||||
title?: string;
|
||||
}
|
||||
interface Return {
|
||||
title: Ref<string>;
|
||||
visible: Ref<boolean>;
|
||||
openDialog: () => void;
|
||||
closeDialog: () => void;
|
||||
}
|
||||
export default (ops?: Options): Return => {
|
||||
const visible = ref(false);
|
||||
const title = ref(ops.title || '');
|
||||
|
||||
const openDialog = () => {
|
||||
visible.value = true;
|
||||
};
|
||||
|
||||
const closeDialog = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
return {
|
||||
title,
|
||||
visible,
|
||||
|
||||
openDialog,
|
||||
closeDialog
|
||||
};
|
||||
};
|
||||