Compare commits
465 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1689b1b579 | ||
|
|
d1a4110c64 | ||
|
|
38ab7c9cb4 | ||
|
|
b342e407ed | ||
|
|
be550a78f6 | ||
|
|
ccd39474c7 | ||
|
|
386ec5ade0 | ||
|
|
ee6b3b535c | ||
|
|
d5ee57f04d | ||
|
|
b6c70bad45 | ||
|
|
921d35367b | ||
|
|
253bd47c75 | ||
|
|
8fa10cd8c1 | ||
|
|
01e5b52500 | ||
|
|
9c921adc5b | ||
|
|
b90962943a | ||
|
|
ad4fc6ea9b | ||
|
|
62310ab863 | ||
|
|
ef7b8cd98f | ||
|
|
5e128ebf04 | ||
|
|
b324d98125 | ||
|
|
8329728b81 | ||
|
|
ba220e9d55 | ||
|
|
6b2ef71296 | ||
|
|
bcf7802f94 | ||
|
|
769e2cb897 | ||
|
|
d55caf2278 | ||
|
|
69ca6677e9 | ||
|
|
3d94626ff1 | ||
|
|
5458819ef5 | ||
|
|
2ff8aef1bf | ||
|
|
16556ddb84 | ||
|
|
aef6fe9229 | ||
|
|
e5460ae3cc | ||
|
|
b6f4cbfb4f | ||
|
|
c83fcb7f26 | ||
|
|
ab0d126c49 | ||
|
|
fd3142bc19 | ||
|
|
af80d8e89b | ||
|
|
c98eefe3ec | ||
|
|
84ec0a7e1c | ||
|
|
4babe3e05d | ||
|
|
8213f6f8d7 | ||
|
|
30329ea4db | ||
|
|
4b856c4905 | ||
|
|
418f4a4785 | ||
|
|
2d31aeecd1 | ||
|
|
b3b3ca3fe4 | ||
|
|
5a4ac549f6 | ||
|
|
f4cdeb3dc5 | ||
|
|
7b01d4722f | ||
|
|
019c73ceca | ||
|
|
10fef4e2d9 | ||
|
|
c613b185da | ||
|
|
1bb7ce6805 | ||
|
|
9b3d8b5a06 | ||
|
|
5f603e6652 | ||
|
|
a20bd6933b | ||
|
|
13ea3ae9b3 | ||
|
|
a73920f4c3 | ||
|
|
543d54f844 | ||
|
|
4bd374e747 | ||
|
|
295cf5e066 | ||
|
|
2824e1325d | ||
|
|
cd9e27bcf3 | ||
|
|
9600e450af | ||
|
|
8e56667760 | ||
|
|
1eac218910 | ||
|
|
27962cd25f | ||
|
|
3de32945f2 | ||
|
|
9593b68d33 | ||
|
|
9166fc50aa | ||
|
|
fb5342594f | ||
|
|
733ff867e9 | ||
|
|
d3f9b43b12 | ||
|
|
30ab479315 | ||
|
|
9d3559cddb | ||
|
|
6d3edff5b6 | ||
|
|
619471369d | ||
|
|
2d328234a1 | ||
|
|
06acfe1ee3 | ||
|
|
a55cba4c1f | ||
|
|
61f8a07753 | ||
|
|
e0da9e1a87 | ||
|
|
3ba3ea6317 | ||
|
|
834e40d6f2 | ||
|
|
f93cccd849 | ||
|
|
0127b43374 | ||
|
|
5cea38e95c | ||
|
|
964b8aa5f6 | ||
|
|
33616de6c8 | ||
|
|
6bf490f7da | ||
|
|
3e31f7783f | ||
|
|
05f5122c0b | ||
|
|
93de208ac0 | ||
|
|
272466bbbf | ||
|
|
8639372513 | ||
|
|
49acf0c7c9 | ||
|
|
232cbad5bd | ||
|
|
aa2f9d4f50 | ||
|
|
99aa7d3361 | ||
|
|
72decd970a | ||
|
|
fa4e0b3752 | ||
|
|
6615229003 | ||
|
|
24300e6e50 | ||
|
|
f8445ab2e4 | ||
|
|
9645decf59 | ||
|
|
f34c33b0d8 | ||
|
|
5cd20b1e22 | ||
|
|
d65c565127 | ||
|
|
2f654a1772 | ||
|
|
380f728de2 | ||
|
|
dffdaa8d68 | ||
|
|
1f33204697 | ||
|
|
0566606bfb | ||
|
|
e6f95d0cd8 | ||
|
|
8eda2cd814 | ||
|
|
961634981a | ||
|
|
50aadb574d | ||
|
|
a114457c6f | ||
|
|
9274d88c76 | ||
|
|
97bf39f031 | ||
|
|
158cab9f9b | ||
|
|
bc42efe703 | ||
|
|
e2e1d2ad78 | ||
|
|
772f2695e7 | ||
|
|
5df1144cd0 | ||
|
|
943a7344f6 | ||
|
|
5867559502 | ||
|
|
fdd1eda9ec | ||
|
|
1d7c17e253 | ||
|
|
95484877a3 | ||
|
|
b01d242cbc | ||
|
|
cfd25b0a8d | ||
|
|
2f08c07c20 | ||
|
|
fd5e02c1f4 | ||
|
|
a549edb174 | ||
|
|
1c8f20440c | ||
|
|
5fb09c1c4a | ||
|
|
b7260ed982 | ||
|
|
aec0150a26 | ||
|
|
5c5dfcac89 | ||
|
|
32b9611eb5 | ||
|
|
12881db9ef | ||
|
|
bb8508abbd | ||
|
|
d6565bd2d9 | ||
|
|
b4633efcba | ||
|
|
0a003359ea | ||
|
|
c7e74774de | ||
|
|
dfefd0452d | ||
|
|
38d69c9e6f | ||
|
|
ac26df6827 | ||
|
|
99787950a8 | ||
|
|
80723496d0 | ||
|
|
ec440f13b1 | ||
|
|
8a9c7e54e2 | ||
|
|
67ca25cec4 | ||
|
|
4ca7b2e023 | ||
|
|
aea8b55e82 | ||
|
|
55a4b0ad1b | ||
|
|
f4124db320 | ||
|
|
e23e5a292d | ||
|
|
1f702c20ae | ||
|
|
9b1b915925 | ||
|
|
11bfefcd04 | ||
|
|
fceefac0ee | ||
|
|
98375f8629 | ||
|
|
d4e0cf7e18 | ||
|
|
62af066234 | ||
|
|
00d368080b | ||
|
|
e43b0d1522 | ||
|
|
400b14cd75 | ||
|
|
f88655e214 | ||
|
|
fc74a000a6 | ||
|
|
eb1a86e5b2 | ||
|
|
2fa3570f85 | ||
|
|
da2cf2acc5 | ||
|
|
f68ffefaa9 | ||
|
|
617fbdf8f7 | ||
|
|
034abb06ad | ||
|
|
46b176fc59 | ||
|
|
506686b11e | ||
|
|
1314cf1c19 | ||
|
|
f1e314fa13 | ||
|
|
3bce7de015 | ||
|
|
1bd035e1ca | ||
|
|
3a9ad5adb8 | ||
|
|
022d5bbd7d | ||
|
|
c905489ea5 | ||
|
|
65377d9236 | ||
|
|
8ead6c59c0 | ||
|
|
ea4e8856c2 | ||
|
|
318cfb5fe2 | ||
|
|
7706126479 | ||
|
|
ba84d0ead3 | ||
|
|
5604c6ece5 | ||
|
|
140f2970c4 | ||
|
|
b2ac98d25e | ||
|
|
1ed37897d5 | ||
|
|
29df3d658b | ||
|
|
576397a042 | ||
|
|
b7c9b84449 | ||
|
|
efbf799218 | ||
|
|
7d578ce363 | ||
|
|
e03e584684 | ||
|
|
e8e6b928cf | ||
|
|
5f6d29a3f8 | ||
|
|
a27d83a5e3 | ||
|
|
ef1154d6f3 | ||
|
|
ae2400fd0b | ||
|
|
fbcd004b7e | ||
|
|
cb307f95ce | ||
|
|
7838cd1a6a | ||
|
|
03dcb7d860 | ||
|
|
62c5470efe | ||
|
|
53b4674da4 | ||
|
|
ff2cf68b08 | ||
|
|
8b55373794 | ||
|
|
dba67d7127 | ||
|
|
762c455b53 | ||
|
|
714d1a36c4 | ||
|
|
0e0ddbbd99 | ||
|
|
497f8cfd1f | ||
|
|
1ff709e6f8 | ||
|
|
ea35871aba | ||
|
|
0e78857645 | ||
|
|
5d7d115910 | ||
|
|
b9384afd5d | ||
|
|
dbe020dc94 | ||
|
|
9673aa7690 | ||
|
|
ec06d30d59 | ||
|
|
356d2e592b | ||
|
|
521c2e7ca6 | ||
|
|
c4014c9333 | ||
|
|
2908a8d8a9 | ||
|
|
c964b98240 | ||
|
|
f7c74b5c96 | ||
|
|
d34c9818b4 | ||
|
|
6414c93116 | ||
|
|
5a12c4a3ce | ||
|
|
97a6ee39e5 | ||
|
|
44db5ab150 | ||
|
|
e9bcd29e36 | ||
|
|
a2ca897fca | ||
|
|
9a34e63d5f | ||
|
|
e501b894c3 | ||
|
|
d97ef84b7e | ||
|
|
0f2dc4d3ba | ||
|
|
49a9eb5460 | ||
|
|
7e7780a754 | ||
|
|
d0770970f0 | ||
|
|
d5a10a5817 | ||
|
|
7b63d8b2ba | ||
|
|
c544de8909 | ||
|
|
89da2ab50f | ||
|
|
a1a6b5967b | ||
|
|
53ffb1b565 | ||
|
|
6efecd123f | ||
|
|
1ec5349a96 | ||
|
|
187abcd10c | ||
|
|
d6ca4429d5 | ||
|
|
86e869ff16 | ||
|
|
dc58f9397f | ||
|
|
97b4ab2f15 | ||
|
|
23a9d02aba | ||
|
|
32ed6c3e97 | ||
|
|
102556dd2a | ||
|
|
2df3f7c56d | ||
|
|
d6fd02ec19 | ||
|
|
c901b4bc06 | ||
|
|
e19c89ccd9 | ||
|
|
a667f1a65e | ||
|
|
9f5829876c | ||
|
|
189d7c4719 | ||
|
|
dfe877c438 | ||
|
|
3a634d7888 | ||
|
|
fd9f3d04d9 | ||
|
|
ac70fc37f6 | ||
|
|
c29aeeee41 | ||
|
|
51c13c7b52 | ||
|
|
fec8c0fe53 | ||
|
|
5da2143212 | ||
|
|
30158ac145 | ||
|
|
ff7eecee55 | ||
|
|
0755a4026a | ||
|
|
9aaf363584 | ||
|
|
0a27cd7403 | ||
|
|
ab8cdd88b9 | ||
|
|
dab4a092d9 | ||
|
|
9d365dbf1e | ||
|
|
16d25fb60d | ||
|
|
50d43a2fc5 | ||
|
|
6d99539730 | ||
|
|
cf43dc8e70 | ||
|
|
95c506c638 | ||
|
|
464b768c55 | ||
|
|
00ca4cc5bd | ||
|
|
bfb81ef60d | ||
|
|
420701bf23 | ||
|
|
88073aaa20 | ||
|
|
8456320884 | ||
|
|
3555b08fe8 | ||
|
|
55ad046e96 | ||
|
|
ea4ddb68f3 | ||
|
|
edde5f8a88 | ||
|
|
de28fd4ca4 | ||
|
|
dd7bbb138a | ||
|
|
4a54eb56a7 | ||
|
|
24cda70cbc | ||
|
|
a0c869d0a1 | ||
|
|
58f85992b0 | ||
|
|
f11f5e1ca4 | ||
|
|
0672698ba7 | ||
|
|
cf37f7c950 | ||
|
|
c7d64554ad | ||
|
|
039021532e | ||
|
|
e39ae170c3 | ||
|
|
8a44fe9d1c | ||
|
|
70649653fb | ||
|
|
db1f241c33 | ||
|
|
c9ed317e62 | ||
|
|
afde26a7ae | ||
|
|
1a2d9ba2b2 | ||
|
|
3943536485 | ||
|
|
e963a4051f | ||
|
|
2b02194a18 | ||
|
|
dd1aec3b60 | ||
|
|
0b05d4d186 | ||
|
|
8f0327604f | ||
|
|
40dddb3316 | ||
|
|
be7ea64c5a | ||
|
|
8eef0e410e | ||
|
|
63f0b20fd6 | ||
|
|
87a685b823 | ||
|
|
4796a494de | ||
|
|
a329ff3796 | ||
|
|
00ab65e720 | ||
|
|
4997e25cdc | ||
|
|
6ce05984d5 | ||
|
|
26e08774b0 | ||
|
|
83ce9fb4c4 | ||
|
|
ac946f4903 | ||
|
|
d6480db609 | ||
|
|
9f9c191240 | ||
|
|
cb781aa4ad | ||
|
|
6a31e88855 | ||
|
|
f862e5ea1b | ||
|
|
b8de3e9867 | ||
|
|
1959160681 | ||
|
|
b665ec5717 | ||
|
|
8ef2c12be1 | ||
|
|
d85e7f0bcb | ||
|
|
544d65c7d0 | ||
|
|
1bd5fc389a | ||
|
|
9149d6de9a | ||
|
|
261d9fcd79 | ||
|
|
5ecac4223d | ||
|
|
215c21ad8a | ||
|
|
5772de888c | ||
|
|
55979e90dc | ||
|
|
5785f5beea | ||
|
|
e8bb256a8d | ||
|
|
729a563545 | ||
|
|
1b38ed5c78 | ||
|
|
483aea5c4f | ||
|
|
a64d493a29 | ||
|
|
2a8d436267 | ||
|
|
7905f82d65 | ||
|
|
dcf82d041a | ||
|
|
9618bd891f | ||
|
|
c63f9de5c5 | ||
|
|
0e6113f0a6 | ||
|
|
71cf85f535 | ||
|
|
2a4f646181 | ||
|
|
0cc326836c | ||
|
|
52b8bc8909 | ||
|
|
844a7b455c | ||
|
|
489a0b6fb8 | ||
|
|
e535133eca | ||
|
|
ea2be7609c | ||
|
|
011d9d639b | ||
|
|
e78e9d8e55 | ||
|
|
89762cad78 | ||
|
|
4788562241 | ||
|
|
5b1ad450d3 | ||
|
|
7b6d8671cf | ||
|
|
01631860f4 | ||
|
|
c65e76bbc3 | ||
|
|
ef35e1cfd9 | ||
|
|
1d535b5d61 | ||
|
|
004cb20132 | ||
|
|
909bdf60e7 | ||
|
|
0cd3bea6bd | ||
|
|
d001a0de15 | ||
|
|
0dca7acee6 | ||
|
|
b5ad7a1721 | ||
|
|
9f23b911c1 | ||
|
|
1db8bb4d13 | ||
|
|
eebafda9e5 | ||
|
|
0fb57a0a2c | ||
|
|
d4c55620f1 | ||
|
|
ef26567850 | ||
|
|
c15c43dba4 | ||
|
|
5d738d99fe | ||
|
|
43120b0017 | ||
|
|
23dc82f042 | ||
|
|
49330536c7 | ||
|
|
781fa7ea1b | ||
|
|
bf17312a5f | ||
|
|
e4da8d4f15 | ||
|
|
cc26bd09e6 | ||
|
|
9dc1c5c9e9 | ||
|
|
442ae94ad5 | ||
|
|
43820f71a3 | ||
|
|
63920e034a | ||
|
|
cddc47305f | ||
|
|
583bebd10c | ||
|
|
7fe9a6c900 | ||
|
|
670e457dc6 | ||
|
|
300d8224ec | ||
|
|
924f5320bb | ||
|
|
dd09fb2283 | ||
|
|
3e18af626b | ||
|
|
89dd5b79d6 | ||
|
|
356f71f13e | ||
|
|
2f5d71a299 | ||
|
|
b581c12edb | ||
|
|
d4d6aeb0b4 | ||
|
|
77114e6cfc | ||
|
|
e14a078440 | ||
|
|
3b38a0d628 | ||
|
|
927d8cb6c9 | ||
|
|
eb8ea2f549 | ||
|
|
208c93bc8f | ||
|
|
93180faa23 | ||
|
|
816858f231 | ||
|
|
fa1d68848f | ||
|
|
bce955a1e1 | ||
|
|
82f70e0ac9 | ||
|
|
fc9b78cfef | ||
|
|
72400a48da | ||
|
|
537b39b3c4 | ||
|
|
47e6e48729 | ||
|
|
d252229777 | ||
|
|
7f4c7f607d | ||
|
|
994ba5dd1a | ||
|
|
e3a06b28dd | ||
|
|
10d512470e | ||
|
|
c1d8040fd5 | ||
|
|
e4b81da386 | ||
|
|
fd7360e6f4 | ||
|
|
62f15e218e | ||
|
|
5c1b91f348 | ||
|
|
378cbd580f | ||
|
|
3994f14010 | ||
|
|
e7f4e743e3 | ||
|
|
b934ce2893 | ||
|
|
5eec076ea2 | ||
|
|
ada36ebff5 | ||
|
|
d9543ca23c | ||
|
|
3871eb3c84 | ||
|
|
4ba696d289 | ||
|
|
2f69e0b96c | ||
|
|
d6b0c56c35 | ||
|
|
d8fe983b9d |
63
.gitattributes
vendored
@@ -1,63 +0,0 @@
|
||||
###############################################################################
|
||||
# Set default behavior to automatically normalize line endings.
|
||||
###############################################################################
|
||||
* text=auto
|
||||
|
||||
###############################################################################
|
||||
# Set default behavior for command prompt diff.
|
||||
#
|
||||
# This is need for earlier builds of msysgit that does not have it on by
|
||||
# default for csharp files.
|
||||
# Note: This is only used by command line
|
||||
###############################################################################
|
||||
#*.cs diff=csharp
|
||||
|
||||
###############################################################################
|
||||
# Set the merge driver for project and solution files
|
||||
#
|
||||
# Merging from the command prompt will add diff markers to the files if there
|
||||
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||
# the diff markers are never inserted). Diff markers may cause the following
|
||||
# file extensions to fail to load in VS. An alternative would be to treat
|
||||
# these files as binary and thus will always conflict and require user
|
||||
# intervention with every merge. To do so, just uncomment the entries below
|
||||
###############################################################################
|
||||
#*.sln merge=binary
|
||||
#*.csproj merge=binary
|
||||
#*.vbproj merge=binary
|
||||
#*.vcxproj merge=binary
|
||||
#*.vcproj merge=binary
|
||||
#*.dbproj merge=binary
|
||||
#*.fsproj merge=binary
|
||||
#*.lsproj merge=binary
|
||||
#*.wixproj merge=binary
|
||||
#*.modelproj merge=binary
|
||||
#*.sqlproj merge=binary
|
||||
#*.wwaproj merge=binary
|
||||
|
||||
###############################################################################
|
||||
# behavior for image files
|
||||
#
|
||||
# image files are treated as binary by default.
|
||||
###############################################################################
|
||||
#*.jpg binary
|
||||
#*.png binary
|
||||
#*.gif binary
|
||||
|
||||
###############################################################################
|
||||
# diff behavior for common document formats
|
||||
#
|
||||
# Convert binary document formats to text before diffing them. This feature
|
||||
# is only available from the command line. Turn it on by uncommenting the
|
||||
# entries below.
|
||||
###############################################################################
|
||||
#*.doc diff=astextplain
|
||||
#*.DOC diff=astextplain
|
||||
#*.docx diff=astextplain
|
||||
#*.DOCX diff=astextplain
|
||||
#*.dot diff=astextplain
|
||||
#*.DOT diff=astextplain
|
||||
#*.pdf diff=astextplain
|
||||
#*.PDF diff=astextplain
|
||||
#*.rtf diff=astextplain
|
||||
#*.RTF diff=astextplain
|
||||
14
.gitignore
vendored
@@ -2,17 +2,11 @@
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
WebFirst/SoEasyPlatform.exe
|
||||
WebFirst/excel
|
||||
WebFirst/wwwroot
|
||||
WebFirst/SoEasyPlatform.exe
|
||||
WebFirst/appsettings.Development.json
|
||||
WebFirst/appsettings.json
|
||||
WebFirst/SoEasyPlatform.pdb
|
||||
WebFirst/SoEasyPlatform.Views.pdb
|
||||
WebFirst/web.config
|
||||
WebFirst/WebFirst.exe
|
||||
dist/
|
||||
|
||||
appsettings.Production.json
|
||||
appsettings.Development.json
|
||||
wwwroot
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
|
||||
214
LICENSE
@@ -1,201 +1,21 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
MIT License
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
Copyright (c) 2022 jacktang
|
||||
|
||||
1. Definitions.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
293
README.md
@@ -1,7 +1,6 @@
|
||||
|
||||
|
||||
<h1 align="center"><img align="left" height="100px" src="https://user-images.githubusercontent.com/68722157/138828506-f58b7c57-5e10-4178-8f7d-5d5e12050113.png"> Yi框架</h1>
|
||||
<h4 align="center">一套与SqlSugar一样爽的.Net6低代码开源框架</h4>
|
||||
<h1 align="center"><img align="left" height="150px" src="https://user-images.githubusercontent.com/68722157/138828506-f58b7c57-5e10-4178-8f7d-5d5e12050113.png"> Yi框架</h1>
|
||||
<h4 align="center">一套以用户体验出发的.Net6 Web开源框架</h4>
|
||||
<h5 align="center">支持原生版本、Furion版本、Abp版本,前端后台接入Ruoyi Vue3.0</h5>
|
||||
<h2 align="center">集大成者,终究轮子</h2>
|
||||
|
||||
[English](README-en.md) | 简体中文
|
||||
@@ -10,29 +9,38 @@
|
||||
|
||||
****
|
||||
### 简介:
|
||||
**中文:意框架**(和他的名字一样“简易”)
|
||||
**中文:意框架**(和他的名字一样“简易”,同时接入Java的Ruoyi Vue3.0前端)
|
||||
|
||||
模块分化较多,可根据业务自行引用或抛弃,集大成者,大而全乎,也许你能从中学习到一些独特见解
|
||||
|
||||
**英文:YiFramework**
|
||||
|
||||
Yi框架-一套与SqlSugar一样爽的.Net6低代码开源框架。
|
||||
Yi框架-一套与SqlSugar一样爽的.Net6开源框架。
|
||||
与Sqlsugar理念一致,以用户体验出发。
|
||||
架构干净整洁、无业务代码、采用微软风格原生框架封装、WebFrist开发。
|
||||
适合.Net6学习、Sqlsugar学习 、项目二次开发。
|
||||
集大成者,终究轮子
|
||||
|
||||
Yi框架最新版本标签:`v1.0.5`,具体版本可以查看标签迭代
|
||||
Yi框架最新版本标签:`v3.0.0`,具体版本可以查看标签迭代
|
||||
|
||||
(项目与Sqlsugar同步更新,但这作者老杰哥代码天天爆肝到凌晨两点,我们也尽量会跟上他的脚步。更新频繁,所以可watching持续关注。)
|
||||
|
||||
————这不仅仅是一个程序,更是一个艺术品,面向艺术的开发!
|
||||
|
||||
**分支**:
|
||||
> 核心特点:简单好用,框架不以打包形式引用,而是直接以项目附带源码给出,自由度拉满,遵循Mit协议,允许随意修改(请注明来源即可)
|
||||
|
||||
(本项目由EFCore版本历经3年不断迭代至Sqlsugar版本,现EFcore版本已弃用,目前sqlsugar不带任何业务,之后会更新业务功能)
|
||||
**分支:**
|
||||
|
||||
**SqlSugar**:.Net6 DDD领域驱动设计 简单分层微服务架构
|
||||
(本项目由EFCore版本历经3年不断迭代至Sqlsugar版本,现EFcore版本已弃用,目前sqlsugar已带业务功能)
|
||||
|
||||
**ec**:EFcore完整电商项目
|
||||
- **Framework**: 框架分支,所有东西都在这里
|
||||
|
||||
- **Furion**: 基于Furion分支,回归开发本质,极度简单,用起来贼爽
|
||||
|
||||
- ~~**SqlSugar**:.Net6 DDD领域驱动设计 简单分层微服务架构~~
|
||||
|
||||
- ~~**SqlSugar-Dev**:为sqlsugar分支的实时开发版本~~
|
||||
|
||||
- ~~**ec**: EFcore完整电商项目~~
|
||||
|
||||
****
|
||||
|
||||
@@ -40,19 +48,13 @@ Yi框架最新版本标签:`v1.0.5`,具体版本可以查看标签迭代
|
||||
|
||||
废话少说直接上地址,**请不要**更改里面的数据
|
||||
|
||||
API服务:~~[yi.ccnetcore.com](http://yi.ccnetcore.com) 管理员账号:admin 、 123~~
|
||||
官网网址:[ccnetcore.com](https://ccnetcore.com) (已上线,欢迎加入)
|
||||
|
||||
网关地址:~~[gate.ccnetcore.com/swagger](http://gate.ccnetcore.com/swagger)~~
|
||||
Bbs社区系统:[ccnetcore.com](https://ccnetcore.com) (已上线,欢迎加入)
|
||||
|
||||
WebFirst开发:所有代码生成器已经配置完成,无需任何操作数据库及任何代码,只需要网页表格上点点点即可
|
||||
Rbac后台管理系统:[yi.ccnetcore.com](http://yi.ccnetcore.com) (已上线)~~管理员账号:cc 、 123456~~
|
||||
|
||||
[https://www.donet5.com/Doc/11](https://www.donet5.com/Doc/11)
|
||||
|
||||
谁能把持的住Sqlsugar作者自己都依赖成瘾的东西呢?这是继DbFirst、CodeFirst下一代的划时代产品!无脑爽!
|
||||
|
||||

|
||||
|
||||
(首次添加实体后,生成代码记得修改对应的路径哦~~)
|
||||
网关地址:~~[gate.ccnetcore.com/swagger](http://gate.ccnetcore.com/swagger)~~(目前使用单体架构)
|
||||
|
||||
### 支持:
|
||||
|
||||
@@ -62,113 +64,158 @@ WebFirst开发:所有代码生成器已经配置完成,无需任何操作数
|
||||
- [ ] 即将支持网格服务架构(我们将在后续版本加入dapr)
|
||||
|
||||
****
|
||||
### 软件架构:
|
||||
### 详细到爆炸的Yi框架教程导航:
|
||||
|
||||
**架构**:后端.NET6(Asp.NetCore 6)、WebFirst代码生成器~~与.NET5(Asp.NetCore 5)、前端Vue(2.0)~~
|
||||
|
||||
**关系型数据库**:mysql、sql server、sqlite、oracle(正在兼容中)
|
||||
|
||||
**操作系统**:Windows、Linux
|
||||
|
||||
**身份验证**:JWT、IdentityServer4
|
||||
|
||||
**组件**:~~EFcore~~SqlSugar、Autofac、Castle、Swagger、Log4Net、Redis、RabbitMq、ES、Quartz.net、~~T4~~
|
||||
|
||||
**分布式**:CAP、Lock
|
||||
|
||||
**微服务**:Consul、Ocelot、IdentityService、Apollo、Docker、Jenkins、Nginx、K8s、ELK、Polly
|
||||
|
||||
**封装**:Json处理模块,滑动验证码模块,base64图片处理模块,异常捕捉模块、邮件处理模块、linq封装模块、随机数模块、统一接口模块、基于策略的jwt验证、过滤器、数据库连接、跨域、初始化种子数据、Base32、Console输出、日期处理、文件传输、html筛选、http请求、ip过滤、md5加密、Rsa加密、序列化、雪花算法、字符串处理、编码处理、地址处理、xml处理、心跳检查。。。
|
||||
1. [框架快速开始](https://ccnetcore.com/article/1641733850189139969)(已完成)
|
||||
2. [框架模块教程](https://ccnetcore.com/article/1641733991574933505)(已完成)
|
||||
3. [应用模块教程](https://ccnetcore.com/article/1641734073091231745)
|
||||
4. [Yi.RBAC后台系统](https://ccnetcore.com/article/1641734171128893441)
|
||||
5. [Yi.BBS社区系统](https://ccnetcore.com/article/1641734308475572225)
|
||||
|
||||
****
|
||||
### 支持模块:
|
||||
### 它的理念:
|
||||
优雅的进行快速开发,通常,简单程度与优雅程度不可兼得,Yi框架并不一昧的追求极致的解耦,会站在用户使用角度上,在使用难易度进行考虑衡量
|
||||
|
||||
大致如图:
|
||||
例如:我们大部分功能紧密贴合Sqlsugar,虽然缺少其他orm的替换性,但在使用程度上降低的使用难度
|
||||
|
||||

|
||||

|
||||

|
||||
例如:我们在应用层中使用属性注入,虽然依赖关系会比较模糊,但是使用起来会减少一定代码量
|
||||
|
||||
(删除线代表已实现功能还未迁移过来)
|
||||
- [x] 支持大致`DDD领域驱动设计`进行分层,支持微服务扩展
|
||||
- [x] 支持采用`异步`开发awit/async
|
||||
- [x] 支持数据库主从`读写分离`
|
||||
- [x] 支持功能替换,无需改动代码,只需配置`json文件`进行装配即可
|
||||
- [x] ~~-支持采用DbFirst开发方式,使用`T4模板代码生成器`,自动映射模型一键生成Service及IService所有代码~~
|
||||
- [x] 支持WebFirst,无需改动代码,自动生成全套代码与数据库,只需点点点
|
||||
- [x] ~~-支持`用户-角色-菜单-接口`以及vue2.0前端全部逻辑代码,下载无需修改直接使用~~
|
||||
- [x] 支持`Aop封装`,FilterAop、IocAop、LogAop、SqlAop
|
||||
- [x] 支持`Log4Net日志`记录,自动生成至bin目录下的logs文件夹
|
||||
- [x] 支持`DbSeed数据库种子数据`接入
|
||||
- [x] 支持主流`数据库随意切换`,Mysql/Sqlite/Sqlserver/Oracle
|
||||
- [x] 支持上海杰哥官方`SqlSugar ORM`封装
|
||||
- [x] 支持新版`SwaggerWebAPI`,jwt身份认证接入
|
||||
- [x] 支持`Cors`跨域
|
||||
- [x] 支持`AutoFac`自动映射依赖注入
|
||||
- [x] 支持`consul`服务器注册与发现
|
||||
- [x] 支持`健康检查`
|
||||
- [x] 支持`RabbitMQ`消息队列
|
||||
- [x] 支持`Redis`多级缓存
|
||||
- [x] 支持`Ocelot`网关,路由、服务聚合、服务发现、认证、鉴权、限流、熔断、缓存、Header头传递
|
||||
- [x] 支持`Apollo`全局配置中心;
|
||||
- [x] 支持`docker`镜像制作
|
||||
- [x] ~~-支持页面`静态化处理`,将动态页面生成静态页面~~
|
||||
- [x] 支持`Quartz.net`任务调度,实现任意接口被调度
|
||||
- [x] 支持`ELK`,log4net+kafka+es+logstach+kibana
|
||||
- [x] 支持`IdentityService4`授权中心
|
||||
- [x] 支持`Es`分词查询
|
||||
- [x] 支持多级`缓存`
|
||||
- [x] 支持`CAP`分布式事务,mysql+rabbitMq
|
||||
- [x] 支持`Docker+k8s`部署
|
||||
- [x] 支持`Jenkins+CI/CD`
|
||||
- [x] 支持`AutoMapper`模块映射
|
||||
- [ ] 支持`微信支付`(没账号)
|
||||
- [x] 支持`单表多租户`常用功能
|
||||
- [x] 支持`逻辑删除`常用功能
|
||||
- [x] 支持`操作日志`常用功能
|
||||
- [x] 支持`自动分表`
|
||||
- [x] 支持 太多了忘了
|
||||
> 一个面向用户的快速开发后端框架
|
||||
|
||||
在真正的使用这,你会明白这一点,极致的简单,也是优雅的一种体现。
|
||||
****
|
||||
|
||||
### 特点:
|
||||
- 面向用户的后端框架,使用简单,适合小型、企业级项目
|
||||
- 项目内置源码,不打包
|
||||
- 开箱即用
|
||||
- 支持模块化
|
||||
- 支持动态Api
|
||||
- 支持属性注入
|
||||
- 内置包含大量通用场景模块
|
||||
- 等等
|
||||
|
||||
### 基础设施简介
|
||||
- Jwt鉴权
|
||||
- 接口级别授权
|
||||
- 对象映射
|
||||
- O/RM
|
||||
- 数据过滤
|
||||
- 多租户
|
||||
- 逻辑删除
|
||||
- 审计日志
|
||||
- 种子数据
|
||||
- 工作单元
|
||||
- 模块化
|
||||
- 动态Api
|
||||
- 属性注入
|
||||
- 自动依赖注入
|
||||
- 当前用户
|
||||
- 仓储
|
||||
- Crud
|
||||
|
||||
|
||||
### 内置模块简介
|
||||
- 后台任务
|
||||
- 本地缓存
|
||||
- 分布式缓存
|
||||
- 事件总线
|
||||
- 字典管理
|
||||
- 文件管理
|
||||
- 图片操作
|
||||
- Excel操作
|
||||
- 操作日志管理
|
||||
- Sms短信
|
||||
- 微信支付
|
||||
- 模板代码生成
|
||||
|
||||
### 业务项目
|
||||
- RABC后台管理系统
|
||||
- BBS社区系统
|
||||
|
||||
> 重复的东西,无需再写一遍,这也是优雅的体现之一
|
||||
|
||||
****
|
||||
### 目录结构:
|
||||
### 核心技术
|
||||
#### 后端
|
||||
C# Asp.NetCore 6.0
|
||||
- [x] 动态Api:Cike.AutoApi
|
||||
- [x] 鉴权授权:Jwt
|
||||
- [x] 日志:Nlog
|
||||
- [x] 模块化:StartupModules
|
||||
- [x] 依赖注入:Autofac
|
||||
- [x] 对象映射:Mapster
|
||||
- [x] ORM:SqlsugarCore
|
||||
- [x] 多租户:Abp
|
||||
- [x] 后台任务:Quartz.Net
|
||||
- [x] 本地缓存:MemortCache
|
||||
- [x] 分布式缓存:CSRedisCore
|
||||
- [x] 事件总线:Cike.EventBus
|
||||
- [x] 图像操作:SixLabors.ImageSharp
|
||||
- [x] Excle操作:ExcelToObject.Npoi
|
||||
|
||||

|
||||

|
||||

|
||||
#### 前端
|
||||
js Vue3.2
|
||||
- [x] 异步请求:axios
|
||||
- [x] 图表:echarts
|
||||
- [x] ui:element-plus
|
||||
- [x] 存储:pinia
|
||||
- [x] 路由:vue-router
|
||||
- [x] 打包:vite
|
||||
|
||||
我们大致依照DDD领域驱动设计分层
|
||||
|
||||
分层如此清晰!什么?还感觉太复杂了?用户只需关注Api、Service其他都是轮子啊!
|
||||
|
||||
~~- BackGround:后台进程(目前可以无视,等待更新)~~
|
||||
- Client:客户端(测试、客户端)
|
||||
- Domain:领域层(Dto、服务接口层、模型层、仓储层、服务层)
|
||||
- Infrastructure:基础实例层(通用工具层、核心层、定时任务Job、国际化、Web扩展层)
|
||||
- MicroServiceInstance:服务层(微服务)
|
||||
#### 运维
|
||||
- [x] 部署:nginx
|
||||
- [x] CICD:gitlab+Jenkins
|
||||
- [x] Docker:harbor
|
||||
|
||||
****
|
||||
### 安装教程:
|
||||
### 业务支持模块:
|
||||
|
||||
我们将在之后更新教程手册!
|
||||
RABC权限管理系统(正在更新)
|
||||
(采用ruoyi前端)
|
||||
- 用户管理
|
||||
- 角色管理
|
||||
- 菜单管理
|
||||
- 部门管理
|
||||
- 岗位管理
|
||||
- 字典管理
|
||||
- 参数管理
|
||||
- 用户在线
|
||||
- 操作日志
|
||||
- 登录日志
|
||||
- 等等
|
||||
|
||||
1. 下载全部源码,默认使用sqlite数据库,已经生成
|
||||
2. 直接点击sln文件运行即可,没有其他依赖
|
||||
**演示截图:**
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
****
|
||||
### 使用说明:
|
||||
BBS论坛系统(持续迭代)
|
||||
- 文章管理
|
||||
- 评论管理
|
||||
- 主题管理
|
||||
- 板块管理
|
||||
- 点赞管理
|
||||
- 等等
|
||||
|
||||
1. 导入使用仓库中的WebFirst数据库
|
||||
2. 使用WebFirst添加实体、同步实体、修改模板生成路径并生成方案
|
||||
ERP进销存系统(正在更新)
|
||||
- 供货商管理
|
||||
- 等等
|
||||
|
||||
没了,恭喜你已经成功完成了项目,并且已经具备大部分通用场景业务
|
||||
是不是一个字?爽!
|
||||
到此为止,你无需写任何一个代码!
|
||||
SHOP电商系统(持续迭代)
|
||||
- SPU管理
|
||||
- SKU管理
|
||||
- 商品规格
|
||||
- 商品分类
|
||||
- 等等
|
||||
|
||||
**爽点**:
|
||||
|
||||
新人,看这里,项目下载之后直接可以启动,无任何依赖,之后你可以查看`Test控制器`,迫不及待的快来爽一爽!
|
||||
|
||||
我们将使用说明转移至我们的官方论坛中,正在制作中,尽情期待!
|
||||
|
||||
****
|
||||
### 感谢:
|
||||
@@ -196,29 +243,11 @@ QQ交流群:官方一群(已满)、官方二群(已满)、官方三群
|
||||
|
||||
联系作者,这里人人都是顾问
|
||||
|
||||
官方网址:正在建设
|
||||
官方网址留言区:[ccnetcore.com](https://ccnetcore.com)
|
||||
|
||||
****
|
||||
### FQA:
|
||||
|
||||
问1:为什么不采用EFcore?
|
||||
前往官网查看留言区
|
||||
|
||||
答1:别问,问就是Sqlsugar,和本框架一样爽!
|
||||
|
||||
问2:以后会持续更新下去吗?
|
||||
|
||||
答2:一定会的,我们的标题是 一个和Sqlsugar一样爽的.Net6开源框架 ,只要Sqlsugar在,我们将一直更新下去。
|
||||
|
||||
问3:这个框架的针对人群是哪些人?适合所有人吗?
|
||||
|
||||
答3:并不是适合所有人,应该算适合需要有一定基础的开发人员,当然,如果你是大神,你完全可以将这个框架二次开发!
|
||||
|
||||
问4:花如此多的精力制作这个框架,是为了什么?是为了赚钱吗?和目前主流的abp等框架比,又有什么意义呢?
|
||||
|
||||
答4:我们与Sqlsugar作者理念一致,我们是从用户角度出发,框架是为用户服务,与ABP复杂上手理念完全是相反的。
|
||||
|
||||
问5:为何不出版一个详细的说明书呢?
|
||||
|
||||
答5:暂时不会了,之后可能会,代码都是基于Asp.NetCore框架,适用于新手不用造轮子,整个框架较为简单,阅读源码后,基本能自定义改造使用了,过难也已经封装完毕,别忘了,其意义是为了开发更加简易!建议添加作者好友,这里人人都是顾问。
|
||||
|
||||
我大抵要厌倦了负重前行。
|
||||
[留言区](https://ccnetcore.com/discuss/1641030787056930818)
|
||||
0
Yi.BBS.Vue3/.env
Normal file
2
Yi.BBS.Vue3/.env.development
Normal file
@@ -0,0 +1,2 @@
|
||||
VITE_APP_BASEAPI="/api-dev"
|
||||
VITE_APP_URL="http://localhost:19001/api"
|
||||
1
Yi.BBS.Vue3/.env.production
Normal file
@@ -0,0 +1 @@
|
||||
VITE_APP_BASEAPI="/prod-api"
|
||||
27
Yi.Vue2.x/.gitignore → Yi.BBS.Vue3/.gitignore
vendored
@@ -1,21 +1,26 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
3
Yi.BBS.Vue3/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
|
||||
}
|
||||
29
Yi.BBS.Vue3/README.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# yi-bbs
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
|
||||
|
||||
## Customize configuration
|
||||
|
||||
See [Vite Configuration Reference](https://vitejs.dev/config/).
|
||||
|
||||
## Project Setup
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compile and Hot-Reload for Development
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Compile and Minify for Production
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
14
Yi.BBS.Vue3/index.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>意社区</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
6826
Yi.BBS.Vue3/package-lock.json
generated
Normal file
33
Yi.BBS.Vue3/package.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "yi-bbs",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.1.0",
|
||||
"axios": "^1.3.4",
|
||||
"echarts": "^5.4.2",
|
||||
"element-plus": "^2.2.32",
|
||||
"highlight": "^0.2.4",
|
||||
"marked": "^4.2.12",
|
||||
"mavon-editor": "^3.0.0",
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^2.0.32",
|
||||
"vue": "^3.2.47",
|
||||
"vue-cropper": "1.0.3",
|
||||
"vue-router": "^4.1.6",
|
||||
"yarm": "^0.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"sass": "1.52.1",
|
||||
"unplugin-auto-import": "^0.15.0",
|
||||
"unplugin-vue-components": "^0.24.0",
|
||||
"vite": "^4.1.3"
|
||||
}
|
||||
}
|
||||
BIN
Yi.BBS.Vue3/public/favicon.ico
Normal file
|
After Width: | Height: | Size: 21 KiB |
28
Yi.BBS.Vue3/src/App.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<script setup>
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-config-provider :locale="locale">
|
||||
<RouterView />
|
||||
</el-config-provider>
|
||||
</template>
|
||||
<script setup>
|
||||
import useConfigStore from "@/stores/config";
|
||||
import { ElConfigProvider } from 'element-plus'
|
||||
import {onMounted } from "vue";
|
||||
|
||||
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
|
||||
const locale= zhCn;
|
||||
|
||||
const configStore = useConfigStore();
|
||||
|
||||
//加载全局信息
|
||||
onMounted(async()=>{
|
||||
|
||||
await configStore.getConfig();
|
||||
})
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
79
Yi.BBS.Vue3/src/apis/accountApi.js
Normal file
@@ -0,0 +1,79 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 登录方法
|
||||
export function login(username, password, code, uuid) {
|
||||
const data = {
|
||||
username,
|
||||
password,
|
||||
code,
|
||||
uuid
|
||||
}
|
||||
return request({
|
||||
url: '/account/login',
|
||||
headers: {
|
||||
isToken: false
|
||||
},
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 注册方法
|
||||
export function register(userName,password,phone,code,uuid) {
|
||||
const data = {
|
||||
userName,
|
||||
password,
|
||||
phone,
|
||||
code,
|
||||
uuid
|
||||
}
|
||||
return request({
|
||||
url: '/account/register',
|
||||
headers: {
|
||||
isToken: false
|
||||
},
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 获取用户详细信息
|
||||
export function getInfo() {
|
||||
return request({
|
||||
url: '/account',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 退出方法
|
||||
export function logout() {
|
||||
return request({
|
||||
url: '/account/logout',
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取验证码
|
||||
export function getCodeImg() {
|
||||
return request({
|
||||
url: '/account/captcha-image',
|
||||
headers: {
|
||||
isToken: false
|
||||
},
|
||||
method: 'get',
|
||||
timeout: 20000
|
||||
})
|
||||
}
|
||||
// 获取短信验证码
|
||||
export function getCodePhone(phone) {
|
||||
return request({
|
||||
url: '/account/captcha-phone',
|
||||
headers: {
|
||||
isToken: false
|
||||
},
|
||||
method: 'post',
|
||||
timeout: 20000,
|
||||
data:{phone}
|
||||
})
|
||||
}
|
||||
11
Yi.BBS.Vue3/src/apis/agreeApi.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import myaxios from '@/utils/request'
|
||||
export function operate(discussId){
|
||||
if(discussId==undefined)
|
||||
{
|
||||
return;
|
||||
}
|
||||
return myaxios({
|
||||
url: `/agree/operate/${discussId}`,
|
||||
method: 'post'
|
||||
})
|
||||
};
|
||||
41
Yi.BBS.Vue3/src/apis/articleApi.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import myaxios from '@/utils/request'
|
||||
export function getList(data){
|
||||
return myaxios({
|
||||
url: '/article',
|
||||
method: 'get',
|
||||
params:data
|
||||
})
|
||||
};
|
||||
export function get(id){
|
||||
return myaxios({
|
||||
url: `/article/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
};
|
||||
export function add(data){
|
||||
return myaxios({
|
||||
url: `/article`,
|
||||
method: 'post',
|
||||
data:data
|
||||
})
|
||||
};
|
||||
export function update(id,data){
|
||||
return myaxios({
|
||||
url: `/article/${id}`,
|
||||
method: 'put',
|
||||
data:data
|
||||
})
|
||||
};
|
||||
export function del(ids){
|
||||
return myaxios({
|
||||
url: `/article/${ids}`,
|
||||
method: 'delete'
|
||||
})
|
||||
};
|
||||
export function all(discussId)
|
||||
{
|
||||
return myaxios({
|
||||
url: `/article/all/discuss-id/${discussId}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
8
Yi.BBS.Vue3/src/apis/bannerApi.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import myaxios from '@/utils/request'
|
||||
export function getList(data){
|
||||
return myaxios({
|
||||
url: '/banner',
|
||||
method: 'get',
|
||||
params:data
|
||||
})
|
||||
};
|
||||
22
Yi.BBS.Vue3/src/apis/commentApi.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import myaxios from '@/utils/request'
|
||||
export function getListByDiscussId(discussId,data){
|
||||
return myaxios({
|
||||
url: `/comment/discuss-id/${discussId}`,
|
||||
method: 'get',
|
||||
params:data
|
||||
})
|
||||
};
|
||||
export function add(data){
|
||||
return myaxios({
|
||||
url: `/comment`,
|
||||
method: 'post',
|
||||
data:data
|
||||
})
|
||||
};
|
||||
|
||||
export function del(ids){
|
||||
return myaxios({
|
||||
url: `/comment/${ids}`,
|
||||
method: 'delete'
|
||||
})
|
||||
};
|
||||
9
Yi.BBS.Vue3/src/apis/configApi.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import myaxios from '@/utils/request'
|
||||
|
||||
//获取配置
|
||||
export function getAll(){
|
||||
return myaxios({
|
||||
url: '/config',
|
||||
method: 'get'
|
||||
})
|
||||
};
|
||||
51
Yi.BBS.Vue3/src/apis/discussApi.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import myaxios from '@/utils/request'
|
||||
export function getList(data){
|
||||
return myaxios({
|
||||
url: '/discuss',
|
||||
method: 'get',
|
||||
params:data
|
||||
})
|
||||
};
|
||||
export function getTopList(data){
|
||||
|
||||
if(data==undefined)
|
||||
{
|
||||
data={isTop:true}
|
||||
}
|
||||
else
|
||||
{
|
||||
data["isTop"]=true;
|
||||
}
|
||||
|
||||
return myaxios({
|
||||
url: '/discuss',
|
||||
method: 'get',
|
||||
params:data
|
||||
})
|
||||
};
|
||||
export function get(id){
|
||||
return myaxios({
|
||||
url: `/discuss/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
};
|
||||
export function add(data){
|
||||
return myaxios({
|
||||
url: `/discuss`,
|
||||
method: 'post',
|
||||
data:data
|
||||
})
|
||||
};
|
||||
export function update(id,data){
|
||||
return myaxios({
|
||||
url: `/discuss/${id}`,
|
||||
method: 'put',
|
||||
data:data
|
||||
})
|
||||
};
|
||||
export function del(ids){
|
||||
return myaxios({
|
||||
url: `/discuss/${ids}`,
|
||||
method: 'delete'
|
||||
})
|
||||
};
|
||||
9
Yi.BBS.Vue3/src/apis/fileApi.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import myaxios from '@/utils/request'
|
||||
export function upload(data){
|
||||
return myaxios({
|
||||
url: '/file',
|
||||
method: 'post',
|
||||
data:data,
|
||||
headers: { 'Content-Type': 'multipart/form-data' }
|
||||
})
|
||||
};
|
||||
8
Yi.BBS.Vue3/src/apis/plateApi.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import myaxios from '@/utils/request'
|
||||
export function getList(data){
|
||||
return myaxios({
|
||||
url: '/plate',
|
||||
method: 'get',
|
||||
params:data
|
||||
})
|
||||
};
|
||||
139
Yi.BBS.Vue3/src/apis/userApi.js
Normal file
@@ -0,0 +1,139 @@
|
||||
import myaxios from '@/utils/request'
|
||||
|
||||
// 查询用户列表
|
||||
export function listUser(query) {
|
||||
return myaxios({
|
||||
url: '/user',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询用户详细
|
||||
export function getUser(userId) {
|
||||
return myaxios({
|
||||
url: '/user/' + parseStrEmpty(userId),
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增用户
|
||||
export function addUser(data) {
|
||||
return myaxios({
|
||||
url: '/user',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改用户
|
||||
export function updateUser(id, data) {
|
||||
return myaxios({
|
||||
url: `/user/${id}`,
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除用户
|
||||
export function delUser(userId) {
|
||||
return myaxios({
|
||||
url: `/user/${userId}`,
|
||||
method: 'delete',
|
||||
})
|
||||
}
|
||||
|
||||
// 用户密码重置
|
||||
export function resetUserPwd(id, password) {
|
||||
const data = {
|
||||
password
|
||||
}
|
||||
|
||||
|
||||
return myaxios({
|
||||
url: `/account/rest-password/${id}`,
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 用户状态修改
|
||||
export function changeUserStatus(userId, isDel) {
|
||||
return myaxios({
|
||||
url: `/user/${userId}/${isDel}`,
|
||||
method: 'put'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询用户个人信息
|
||||
export function getUserProfile() {
|
||||
return myaxios({
|
||||
url: '/account',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 修改用户个人信息
|
||||
export function updateUserProfile(data) {
|
||||
return myaxios({
|
||||
url: `/user/profile`,
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
// 只修改用户头像
|
||||
export function updateUserIcon(data) {
|
||||
return myaxios({
|
||||
url: `/account/icon`,
|
||||
method: 'put',
|
||||
data:{icon:data}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 用户密码重置
|
||||
export function updateUserPwd(oldPassword, newPassword) {
|
||||
const data = {
|
||||
oldPassword,
|
||||
newPassword
|
||||
}
|
||||
return myaxios({
|
||||
url: '/account/password',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 用户头像上传
|
||||
export function uploadAvatar(data) {
|
||||
return request({
|
||||
url: '/system/user/profile/avatar',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 查询授权角色
|
||||
export function getAuthRole(userId) {
|
||||
return request({
|
||||
url: '/system/user/authRole/' + userId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 保存授权角色
|
||||
export function updateAuthRole(data) {
|
||||
return request({
|
||||
url: '/system/user/authRole',
|
||||
method: 'put',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// // 查询部门下拉树结构
|
||||
// export function deptTreeSelect() {
|
||||
// return request({
|
||||
// url: '/system/user/deptTree',
|
||||
// method: 'get'
|
||||
// })
|
||||
// }
|
||||
BIN
Yi.BBS.Vue3/src/assets/401_images/401.gif
Normal file
|
After Width: | Height: | Size: 160 KiB |
BIN
Yi.BBS.Vue3/src/assets/404_images/404.png
Normal file
|
After Width: | Height: | Size: 96 KiB |
BIN
Yi.BBS.Vue3/src/assets/404_images/404_cloud.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
0
Yi.BBS.Vue3/src/assets/base.css
Normal file
710
Yi.BBS.Vue3/src/assets/github-markdown.css
Normal file
@@ -0,0 +1,710 @@
|
||||
@font-face {
|
||||
font-family: octicons-link;
|
||||
src: url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAZwABAAAAAACFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEU0lHAAAGaAAAAAgAAAAIAAAAAUdTVUIAAAZcAAAACgAAAAoAAQAAT1MvMgAAAyQAAABJAAAAYFYEU3RjbWFwAAADcAAAAEUAAACAAJThvmN2dCAAAATkAAAABAAAAAQAAAAAZnBnbQAAA7gAAACyAAABCUM+8IhnYXNwAAAGTAAAABAAAAAQABoAI2dseWYAAAFsAAABPAAAAZwcEq9taGVhZAAAAsgAAAA0AAAANgh4a91oaGVhAAADCAAAABoAAAAkCA8DRGhtdHgAAAL8AAAADAAAAAwGAACfbG9jYQAAAsAAAAAIAAAACABiATBtYXhwAAACqAAAABgAAAAgAA8ASm5hbWUAAAToAAABQgAAAlXu73sOcG9zdAAABiwAAAAeAAAAME3QpOBwcmVwAAAEbAAAAHYAAAB/aFGpk3jaTY6xa8JAGMW/O62BDi0tJLYQincXEypYIiGJjSgHniQ6umTsUEyLm5BV6NDBP8Tpts6F0v+k/0an2i+itHDw3v2+9+DBKTzsJNnWJNTgHEy4BgG3EMI9DCEDOGEXzDADU5hBKMIgNPZqoD3SilVaXZCER3/I7AtxEJLtzzuZfI+VVkprxTlXShWKb3TBecG11rwoNlmmn1P2WYcJczl32etSpKnziC7lQyWe1smVPy/Lt7Kc+0vWY/gAgIIEqAN9we0pwKXreiMasxvabDQMM4riO+qxM2ogwDGOZTXxwxDiycQIcoYFBLj5K3EIaSctAq2kTYiw+ymhce7vwM9jSqO8JyVd5RH9gyTt2+J/yUmYlIR0s04n6+7Vm1ozezUeLEaUjhaDSuXHwVRgvLJn1tQ7xiuVv/ocTRF42mNgZGBgYGbwZOBiAAFGJBIMAAizAFoAAABiAGIAznjaY2BkYGAA4in8zwXi+W2+MjCzMIDApSwvXzC97Z4Ig8N/BxYGZgcgl52BCSQKAA3jCV8CAABfAAAAAAQAAEB42mNgZGBg4f3vACQZQABIMjKgAmYAKEgBXgAAeNpjYGY6wTiBgZWBg2kmUxoDA4MPhGZMYzBi1AHygVLYQUCaawqDA4PChxhmh/8ODDEsvAwHgMKMIDnGL0x7gJQCAwMAJd4MFwAAAHjaY2BgYGaA4DAGRgYQkAHyGMF8NgYrIM3JIAGVYYDT+AEjAwuDFpBmA9KMDEwMCh9i/v8H8sH0/4dQc1iAmAkALaUKLgAAAHjaTY9LDsIgEIbtgqHUPpDi3gPoBVyRTmTddOmqTXThEXqrob2gQ1FjwpDvfwCBdmdXC5AVKFu3e5MfNFJ29KTQT48Ob9/lqYwOGZxeUelN2U2R6+cArgtCJpauW7UQBqnFkUsjAY/kOU1cP+DAgvxwn1chZDwUbd6CFimGXwzwF6tPbFIcjEl+vvmM/byA48e6tWrKArm4ZJlCbdsrxksL1AwWn/yBSJKpYbq8AXaaTb8AAHja28jAwOC00ZrBeQNDQOWO//sdBBgYGRiYWYAEELEwMTE4uzo5Zzo5b2BxdnFOcALxNjA6b2ByTswC8jYwg0VlNuoCTWAMqNzMzsoK1rEhNqByEyerg5PMJlYuVueETKcd/89uBpnpvIEVomeHLoMsAAe1Id4AAAAAAAB42oWQT07CQBTGv0JBhagk7HQzKxca2sJCE1hDt4QF+9JOS0nbaaYDCQfwCJ7Au3AHj+LO13FMmm6cl7785vven0kBjHCBhfpYuNa5Ph1c0e2Xu3jEvWG7UdPDLZ4N92nOm+EBXuAbHmIMSRMs+4aUEd4Nd3CHD8NdvOLTsA2GL8M9PODbcL+hD7C1xoaHeLJSEao0FEW14ckxC+TU8TxvsY6X0eLPmRhry2WVioLpkrbp84LLQPGI7c6sOiUzpWIWS5GzlSgUzzLBSikOPFTOXqly7rqx0Z1Q5BAIoZBSFihQYQOOBEdkCOgXTOHA07HAGjGWiIjaPZNW13/+lm6S9FT7rLHFJ6fQbkATOG1j2OFMucKJJsxIVfQORl+9Jyda6Sl1dUYhSCm1dyClfoeDve4qMYdLEbfqHf3O/AdDumsjAAB42mNgYoAAZQYjBmyAGYQZmdhL8zLdDEydARfoAqIAAAABAAMABwAKABMAB///AA8AAQAAAAAAAAAAAAAAAAABAAAAAA==) format('woff');
|
||||
}
|
||||
|
||||
.markdown-body {
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
line-height: 1.5;
|
||||
color: #24292e;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.markdown-body .pl-c {
|
||||
color: #6a737d;
|
||||
}
|
||||
|
||||
.markdown-body .pl-c1,
|
||||
.markdown-body .pl-s .pl-v {
|
||||
color: #005cc5;
|
||||
}
|
||||
|
||||
.markdown-body .pl-e,
|
||||
.markdown-body .pl-en {
|
||||
color: #6f42c1;
|
||||
}
|
||||
|
||||
.markdown-body .pl-smi,
|
||||
.markdown-body .pl-s .pl-s1 {
|
||||
color: #24292e;
|
||||
}
|
||||
|
||||
.markdown-body .pl-ent {
|
||||
color: #22863a;
|
||||
}
|
||||
|
||||
.markdown-body .pl-k {
|
||||
color: #d73a49;
|
||||
}
|
||||
|
||||
.markdown-body .pl-s,
|
||||
.markdown-body .pl-pds,
|
||||
.markdown-body .pl-s .pl-pse .pl-s1,
|
||||
.markdown-body .pl-sr,
|
||||
.markdown-body .pl-sr .pl-cce,
|
||||
.markdown-body .pl-sr .pl-sre,
|
||||
.markdown-body .pl-sr .pl-sra {
|
||||
color: #032f62;
|
||||
}
|
||||
|
||||
.markdown-body .pl-v,
|
||||
.markdown-body .pl-smw {
|
||||
color: #e36209;
|
||||
}
|
||||
|
||||
.markdown-body .pl-bu {
|
||||
color: #b31d28;
|
||||
}
|
||||
|
||||
.markdown-body .pl-ii {
|
||||
color: #fafbfc;
|
||||
background-color: #b31d28;
|
||||
}
|
||||
|
||||
.markdown-body .pl-c2 {
|
||||
color: #fafbfc;
|
||||
background-color: #d73a49;
|
||||
}
|
||||
|
||||
.markdown-body .pl-c2::before {
|
||||
content: "^M";
|
||||
}
|
||||
|
||||
.markdown-body .pl-sr .pl-cce {
|
||||
font-weight: bold;
|
||||
color: #22863a;
|
||||
}
|
||||
|
||||
.markdown-body .pl-ml {
|
||||
color: #735c0f;
|
||||
}
|
||||
|
||||
.markdown-body .pl-mh,
|
||||
.markdown-body .pl-mh .pl-en,
|
||||
.markdown-body .pl-ms {
|
||||
font-weight: bold;
|
||||
color: #005cc5;
|
||||
}
|
||||
|
||||
.markdown-body .pl-mi {
|
||||
font-style: italic;
|
||||
color: #24292e;
|
||||
}
|
||||
|
||||
.markdown-body .pl-mb {
|
||||
font-weight: bold;
|
||||
color: #24292e;
|
||||
}
|
||||
|
||||
.markdown-body .pl-md {
|
||||
color: #b31d28;
|
||||
background-color: #ffeef0;
|
||||
}
|
||||
|
||||
.markdown-body .pl-mi1 {
|
||||
color: #22863a;
|
||||
background-color: #f0fff4;
|
||||
}
|
||||
|
||||
.markdown-body .pl-mc {
|
||||
color: #e36209;
|
||||
background-color: #ffebda;
|
||||
}
|
||||
|
||||
.markdown-body .pl-mi2 {
|
||||
color: #f6f8fa;
|
||||
background-color: #005cc5;
|
||||
}
|
||||
|
||||
.markdown-body .pl-mdr {
|
||||
font-weight: bold;
|
||||
color: #6f42c1;
|
||||
}
|
||||
|
||||
.markdown-body .pl-ba {
|
||||
color: #586069;
|
||||
}
|
||||
|
||||
.markdown-body .pl-sg {
|
||||
color: #959da5;
|
||||
}
|
||||
|
||||
.markdown-body .pl-corl {
|
||||
text-decoration: underline;
|
||||
color: #032f62;
|
||||
}
|
||||
|
||||
.markdown-body .octicon {
|
||||
display: inline-block;
|
||||
vertical-align: text-top;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.markdown-body a {
|
||||
background-color: transparent;
|
||||
-webkit-text-decoration-skip: objects;
|
||||
}
|
||||
|
||||
.markdown-body a:active,
|
||||
.markdown-body a:hover {
|
||||
outline-width: 0;
|
||||
}
|
||||
|
||||
.markdown-body strong {
|
||||
font-weight: inherit;
|
||||
}
|
||||
|
||||
.markdown-body strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
.markdown-body h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
|
||||
.markdown-body img {
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
.markdown-body svg:not(:root) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.markdown-body code,
|
||||
.markdown-body kbd,
|
||||
.markdown-body pre {
|
||||
font-family: monospace, monospace;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.markdown-body hr {
|
||||
box-sizing: content-box;
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.markdown-body input {
|
||||
font: inherit;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.markdown-body input {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.markdown-body [type="checkbox"] {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.markdown-body * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.markdown-body input {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
.markdown-body a {
|
||||
color: #0366d6;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.markdown-body a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.markdown-body strong {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown-body hr {
|
||||
height: 0;
|
||||
margin: 15px 0;
|
||||
overflow: hidden;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
border-bottom: 1px solid #dfe2e5;
|
||||
}
|
||||
|
||||
.markdown-body hr::before {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
|
||||
.markdown-body hr::after {
|
||||
display: table;
|
||||
clear: both;
|
||||
content: "";
|
||||
}
|
||||
|
||||
.markdown-body table {
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.markdown-body td,
|
||||
.markdown-body th {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.markdown-body h1,
|
||||
.markdown-body h2,
|
||||
.markdown-body h3,
|
||||
.markdown-body h4,
|
||||
.markdown-body h5,
|
||||
.markdown-body h6 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.markdown-body h1 {
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown-body h2 {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown-body h3 {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown-body h4 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown-body h5 {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown-body h6 {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown-body p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.markdown-body blockquote {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.markdown-body ul,
|
||||
.markdown-body ol {
|
||||
padding-left: 0;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.markdown-body ol ol,
|
||||
.markdown-body ul ol {
|
||||
list-style-type: lower-roman;
|
||||
}
|
||||
|
||||
.markdown-body ul ul ol,
|
||||
.markdown-body ul ol ol,
|
||||
.markdown-body ol ul ol,
|
||||
.markdown-body ol ol ol {
|
||||
list-style-type: lower-alpha;
|
||||
}
|
||||
|
||||
.markdown-body dd {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.markdown-body code {
|
||||
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.markdown-body pre {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.markdown-body .octicon {
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
.markdown-body .pl-0 {
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
|
||||
.markdown-body .pl-1 {
|
||||
padding-left: 4px !important;
|
||||
}
|
||||
|
||||
.markdown-body .pl-2 {
|
||||
padding-left: 8px !important;
|
||||
}
|
||||
|
||||
.markdown-body .pl-3 {
|
||||
padding-left: 16px !important;
|
||||
}
|
||||
|
||||
.markdown-body .pl-4 {
|
||||
padding-left: 24px !important;
|
||||
}
|
||||
|
||||
.markdown-body .pl-5 {
|
||||
padding-left: 32px !important;
|
||||
}
|
||||
|
||||
.markdown-body .pl-6 {
|
||||
padding-left: 40px !important;
|
||||
}
|
||||
|
||||
.markdown-body::before {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
|
||||
.markdown-body::after {
|
||||
display: table;
|
||||
clear: both;
|
||||
content: "";
|
||||
}
|
||||
|
||||
.markdown-body>*:first-child {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
.markdown-body>*:last-child {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.markdown-body a:not([href]) {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.markdown-body .anchor {
|
||||
float: left;
|
||||
padding-right: 4px;
|
||||
margin-left: -20px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.markdown-body .anchor:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.markdown-body p,
|
||||
.markdown-body blockquote,
|
||||
.markdown-body ul,
|
||||
.markdown-body ol,
|
||||
.markdown-body dl,
|
||||
.markdown-body table,
|
||||
.markdown-body pre {
|
||||
margin-top: 0;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.markdown-body hr {
|
||||
height: 0.25em;
|
||||
padding: 0;
|
||||
margin: 24px 0;
|
||||
background-color: #e1e4e8;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.markdown-body blockquote {
|
||||
padding: 0 1em;
|
||||
color: #6a737d;
|
||||
border-left: 0.25em solid #dfe2e5;
|
||||
}
|
||||
|
||||
.markdown-body blockquote>:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.markdown-body blockquote>:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.markdown-body kbd {
|
||||
display: inline-block;
|
||||
padding: 3px 5px;
|
||||
font-size: 11px;
|
||||
line-height: 10px;
|
||||
color: #444d56;
|
||||
vertical-align: middle;
|
||||
background-color: #fafbfc;
|
||||
border: solid 1px #c6cbd1;
|
||||
border-bottom-color: #959da5;
|
||||
border-radius: 3px;
|
||||
box-shadow: inset 0 -1px 0 #959da5;
|
||||
}
|
||||
|
||||
.markdown-body h1,
|
||||
.markdown-body h2,
|
||||
.markdown-body h3,
|
||||
.markdown-body h4,
|
||||
.markdown-body h5,
|
||||
.markdown-body h6 {
|
||||
margin-top: 24px;
|
||||
margin-bottom: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
.markdown-body h1 .octicon-link,
|
||||
.markdown-body h2 .octicon-link,
|
||||
.markdown-body h3 .octicon-link,
|
||||
.markdown-body h4 .octicon-link,
|
||||
.markdown-body h5 .octicon-link,
|
||||
.markdown-body h6 .octicon-link {
|
||||
color: #1b1f23;
|
||||
vertical-align: middle;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.markdown-body h1:hover .anchor,
|
||||
.markdown-body h2:hover .anchor,
|
||||
.markdown-body h3:hover .anchor,
|
||||
.markdown-body h4:hover .anchor,
|
||||
.markdown-body h5:hover .anchor,
|
||||
.markdown-body h6:hover .anchor {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.markdown-body h1:hover .anchor .octicon-link,
|
||||
.markdown-body h2:hover .anchor .octicon-link,
|
||||
.markdown-body h3:hover .anchor .octicon-link,
|
||||
.markdown-body h4:hover .anchor .octicon-link,
|
||||
.markdown-body h5:hover .anchor .octicon-link,
|
||||
.markdown-body h6:hover .anchor .octicon-link {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.markdown-body h1 {
|
||||
padding-bottom: 0.3em;
|
||||
font-size: 2em;
|
||||
border-bottom: 1px solid #eaecef;
|
||||
}
|
||||
|
||||
.markdown-body h2 {
|
||||
padding-bottom: 0.3em;
|
||||
font-size: 1.5em;
|
||||
border-bottom: 1px solid #eaecef;
|
||||
}
|
||||
|
||||
.markdown-body h3 {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
.markdown-body h4 {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.markdown-body h5 {
|
||||
font-size: 0.875em;
|
||||
}
|
||||
|
||||
.markdown-body h6 {
|
||||
font-size: 0.85em;
|
||||
color: #6a737d;
|
||||
}
|
||||
|
||||
.markdown-body ul,
|
||||
.markdown-body ol {
|
||||
padding-left: 2em;
|
||||
}
|
||||
|
||||
.markdown-body ul ul,
|
||||
.markdown-body ul ol,
|
||||
.markdown-body ol ol,
|
||||
.markdown-body ol ul {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.markdown-body li>p {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.markdown-body li+li {
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
|
||||
.markdown-body dl {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.markdown-body dl dt {
|
||||
padding: 0;
|
||||
margin-top: 16px;
|
||||
font-size: 1em;
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown-body dl dd {
|
||||
padding: 0 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.markdown-body table {
|
||||
display: block;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.markdown-body table th {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown-body table th,
|
||||
.markdown-body table td {
|
||||
padding: 6px 13px;
|
||||
border: 1px solid #dfe2e5;
|
||||
}
|
||||
|
||||
.markdown-body table tr {
|
||||
background-color: #fff;
|
||||
border-top: 1px solid #c6cbd1;
|
||||
}
|
||||
|
||||
.markdown-body table tr:nth-child(2n) {
|
||||
background-color: #f6f8fa;
|
||||
}
|
||||
|
||||
.markdown-body img {
|
||||
max-width: 100%;
|
||||
box-sizing: content-box;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.markdown-body img[align=right] {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.markdown-body img[align=left] {
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.markdown-body code {
|
||||
padding: 0;
|
||||
padding-top: 0.2em;
|
||||
padding-bottom: 0.2em;
|
||||
margin: 0;
|
||||
font-size: 85%;
|
||||
background-color: rgba(27,31,35,0.05);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.markdown-body code::before,
|
||||
.markdown-body code::after {
|
||||
letter-spacing: -0.2em;
|
||||
content: "\00a0";
|
||||
}
|
||||
|
||||
.markdown-body pre {
|
||||
word-wrap: normal;
|
||||
}
|
||||
|
||||
.markdown-body pre>code {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: 100%;
|
||||
word-break: normal;
|
||||
white-space: pre;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.markdown-body .highlight {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.markdown-body .highlight pre {
|
||||
margin-bottom: 0;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
.markdown-body .highlight pre,
|
||||
.markdown-body pre {
|
||||
padding: 16px;
|
||||
overflow: auto;
|
||||
font-size: 85%;
|
||||
line-height: 1.45;
|
||||
/* background-color: #f6f8fa; */
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.markdown-body pre code {
|
||||
display: inline;
|
||||
max-width: auto;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
overflow: visible;
|
||||
line-height: inherit;
|
||||
word-wrap: normal;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.markdown-body pre code::before,
|
||||
.markdown-body pre code::after {
|
||||
content: normal;
|
||||
}
|
||||
|
||||
.markdown-body .full-commit .btn-outline:not(:disabled):hover {
|
||||
color: #005cc5;
|
||||
border-color: #005cc5;
|
||||
}
|
||||
|
||||
.markdown-body kbd {
|
||||
display: inline-block;
|
||||
padding: 3px 5px;
|
||||
font: 11px "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
line-height: 10px;
|
||||
color: #444d56;
|
||||
vertical-align: middle;
|
||||
background-color: #fafbfc;
|
||||
border: solid 1px #d1d5da;
|
||||
border-bottom-color: #c6cbd1;
|
||||
border-radius: 3px;
|
||||
box-shadow: inset 0 -1px 0 #c6cbd1;
|
||||
}
|
||||
|
||||
.markdown-body :checked+.radio-label {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
border-color: #0366d6;
|
||||
}
|
||||
|
||||
.markdown-body .task-list-item {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.markdown-body .task-list-item+.task-list-item {
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.markdown-body .task-list-item input {
|
||||
margin: 0 0.2em 0.25em -1.6em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.markdown-body hr {
|
||||
border-bottom-color: #eee;
|
||||
}
|
||||
|
||||
BIN
Yi.BBS.Vue3/src/assets/login_images/QQ.png
Normal file
|
After Width: | Height: | Size: 9.5 KiB |
BIN
Yi.BBS.Vue3/src/assets/login_images/WeChat.png
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
BIN
Yi.BBS.Vue3/src/assets/login_images/login_bg_phone.png
Normal file
|
After Width: | Height: | Size: 244 KiB |
BIN
Yi.BBS.Vue3/src/assets/login_images/login_two.jpg
Normal file
|
After Width: | Height: | Size: 180 KiB |
BIN
Yi.BBS.Vue3/src/assets/logo.ico
Normal file
|
After Width: | Height: | Size: 21 KiB |
48
Yi.BBS.Vue3/src/assets/main.css
Normal file
@@ -0,0 +1,48 @@
|
||||
/* @import './base.css'; */
|
||||
/* #app {
|
||||
margin: 0 auto;
|
||||
font-weight: normal;
|
||||
} */
|
||||
|
||||
body
|
||||
{
|
||||
margin: 0 auto;
|
||||
font-weight: normal;
|
||||
}
|
||||
a{
|
||||
text-decoration: none;
|
||||
}
|
||||
/*
|
||||
#app {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
a,
|
||||
.green {
|
||||
text-decoration: none;
|
||||
color: hsla(160, 100%, 37%, 1);
|
||||
transition: 0.4s;
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
a:hover {
|
||||
background-color: hsla(160, 100%, 37%, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
body {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
#app {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
padding: 0 2rem;
|
||||
}
|
||||
} */
|
||||
190
Yi.BBS.Vue3/src/assets/styles/index.scss
Normal file
@@ -0,0 +1,190 @@
|
||||
// @import './variables.module.scss';
|
||||
// @import './mixin.scss';
|
||||
// @import './transition.scss';
|
||||
// @import './element-ui.scss';
|
||||
// @import './sidebar.scss';
|
||||
// @import './btn.scss';
|
||||
// @import './ruoyi.scss';
|
||||
|
||||
// body {
|
||||
// height: 100%;
|
||||
// margin: 0;
|
||||
// -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;
|
||||
// }
|
||||
|
||||
// label {
|
||||
// font-weight: 700;
|
||||
// }
|
||||
|
||||
// html {
|
||||
// height: 100%;
|
||||
// box-sizing: border-box;
|
||||
// }
|
||||
|
||||
// #app {
|
||||
// height: 100%;
|
||||
// }
|
||||
|
||||
// *,
|
||||
// *:before,
|
||||
// *:after {
|
||||
// box-sizing: inherit;
|
||||
// }
|
||||
|
||||
// .no-padding {
|
||||
// padding: 0px !important;
|
||||
// }
|
||||
|
||||
// .padding-content {
|
||||
// padding: 4px 0;
|
||||
// }
|
||||
|
||||
// a:focus,
|
||||
// a:active {
|
||||
// outline: none;
|
||||
// }
|
||||
|
||||
// a,
|
||||
// a:focus,
|
||||
// a:hover {
|
||||
// cursor: pointer;
|
||||
// color: inherit;
|
||||
// text-decoration: none;
|
||||
// }
|
||||
|
||||
// div:focus {
|
||||
// outline: none;
|
||||
// }
|
||||
|
||||
// .fr {
|
||||
// float: right;
|
||||
// }
|
||||
|
||||
// .fl {
|
||||
// float: left;
|
||||
// }
|
||||
|
||||
// .pr-5 {
|
||||
// padding-right: 5px;
|
||||
// }
|
||||
|
||||
// .pl-5 {
|
||||
// padding-left: 5px;
|
||||
// }
|
||||
|
||||
// .block {
|
||||
// display: block;
|
||||
// }
|
||||
|
||||
// .pointer {
|
||||
// cursor: pointer;
|
||||
// }
|
||||
|
||||
// .inlineBlock {
|
||||
// display: block;
|
||||
// }
|
||||
|
||||
// .clearfix {
|
||||
// &:after {
|
||||
// visibility: hidden;
|
||||
// display: block;
|
||||
// font-size: 0;
|
||||
// content: " ";
|
||||
// clear: both;
|
||||
// height: 0;
|
||||
// }
|
||||
// }
|
||||
|
||||
// aside {
|
||||
// background: #eef1f6;
|
||||
// padding: 8px 24px;
|
||||
// margin-bottom: 20px;
|
||||
// border-radius: 2px;
|
||||
// display: block;
|
||||
// line-height: 32px;
|
||||
// font-size: 16px;
|
||||
// font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
|
||||
// color: #2c3e50;
|
||||
// -webkit-font-smoothing: antialiased;
|
||||
// -moz-osx-font-smoothing: grayscale;
|
||||
|
||||
// a {
|
||||
// color: #337ab7;
|
||||
// cursor: pointer;
|
||||
|
||||
// &:hover {
|
||||
// color: rgb(32, 160, 255);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// .components-container {
|
||||
// margin: 30px 50px;
|
||||
// position: relative;
|
||||
// }
|
||||
|
||||
// .pagination-container {
|
||||
// margin-top: 30px;
|
||||
// }
|
||||
|
||||
.text-center {
|
||||
text-align: center
|
||||
}
|
||||
|
||||
// .sub-navbar {
|
||||
// height: 50px;
|
||||
// line-height: 50px;
|
||||
// position: relative;
|
||||
// width: 100%;
|
||||
// text-align: right;
|
||||
// padding-right: 20px;
|
||||
// transition: 600ms ease position;
|
||||
// background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%);
|
||||
|
||||
// .subtitle {
|
||||
// font-size: 20px;
|
||||
// color: #fff;
|
||||
// }
|
||||
|
||||
// &.draft {
|
||||
// background: #d0d0d0;
|
||||
// }
|
||||
|
||||
// &.deleted {
|
||||
// background: #d0d0d0;
|
||||
// }
|
||||
// }
|
||||
|
||||
// .link-type,
|
||||
// .link-type:focus {
|
||||
// color: #337ab7;
|
||||
// cursor: pointer;
|
||||
|
||||
// &:hover {
|
||||
// color: rgb(32, 160, 255);
|
||||
// }
|
||||
// }
|
||||
|
||||
// .filter-container {
|
||||
// padding-bottom: 10px;
|
||||
|
||||
// .filter-item {
|
||||
// display: inline-block;
|
||||
// vertical-align: middle;
|
||||
// margin-bottom: 10px;
|
||||
// }
|
||||
// }
|
||||
|
||||
// //refine vue-multiselect plugin
|
||||
// .multiselect {
|
||||
// line-height: 16px;
|
||||
// }
|
||||
|
||||
// .multiselect--active {
|
||||
// z-index: 1000 !important;
|
||||
// }
|
||||
206
Yi.BBS.Vue3/src/assets/styles/login.scss
Normal file
@@ -0,0 +1,206 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
/*公共CSS*/
|
||||
.box {
|
||||
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: rgb(29, 67, 89);
|
||||
.content {
|
||||
box-shadow: 0px 1px 6px #3B4859;
|
||||
.login-wrapper {
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
.login-form {
|
||||
.form-item {
|
||||
margin: 20px 0;
|
||||
span {
|
||||
display: block;
|
||||
margin: 5px 20px;
|
||||
font-weight: 100;
|
||||
}
|
||||
.input-item {
|
||||
width: 100%;
|
||||
border-radius: 40px;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
font-size: 20px;
|
||||
font-weight: 200;
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
.login-btn {
|
||||
width: 100%;
|
||||
border-radius: 40px;
|
||||
color: #fff;
|
||||
border: 0;
|
||||
font-weight: 100;
|
||||
margin-top: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.divider {
|
||||
width: 100%;
|
||||
margin: 20px 0;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
span:nth-child(1) {
|
||||
flex: 1;
|
||||
}
|
||||
span:nth-child(3) {
|
||||
flex: 1;
|
||||
}
|
||||
.line {
|
||||
display: inline-block;
|
||||
max-width: 30%;
|
||||
width: 30%;
|
||||
}
|
||||
.divider-text {
|
||||
vertical-align: middle;
|
||||
margin: 0px 20px;
|
||||
line-height: 0px;
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
.other-login-wrapper {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.other-login-item {
|
||||
border: 1px solid rgb(214, 222, 228);
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*一般大于手机的尺寸CSS*/
|
||||
@media (min-width: 767px) {
|
||||
.box {
|
||||
background-color: #F0F2F5;
|
||||
.content {
|
||||
width: 85vw;
|
||||
height: 90vh;
|
||||
background: url("@/assets/login_images/login_two.jpg") no-repeat;
|
||||
background-size: 90% 100%;
|
||||
position: absolute;
|
||||
right: 15%;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
border-radius: 20px;
|
||||
background-color: #fff;
|
||||
.login-wrapper {
|
||||
width: 25vw;
|
||||
position: absolute;
|
||||
right: 15%;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
h1 {
|
||||
text-align: center;
|
||||
font-size: 45px;
|
||||
color: rgb(81, 100, 115);
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
.login-form {
|
||||
margin: 10px 0;
|
||||
.form-item {
|
||||
span {
|
||||
color: rgb(81, 100, 115);
|
||||
}
|
||||
.input-item {
|
||||
height: 60px;
|
||||
border: 1px solid rgb(214, 222, 228);
|
||||
}
|
||||
}
|
||||
.login-btn {
|
||||
height: 50px;
|
||||
background-color: rgb(59, 72, 89);
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
.divider {
|
||||
.line {
|
||||
border-bottom: 1px solid rgb(214, 222, 228);
|
||||
}
|
||||
}
|
||||
.other-login-item {
|
||||
border-radius: 20px;
|
||||
img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*手机端CSS*/
|
||||
@media (max-width: 768px) {
|
||||
.box {
|
||||
.content {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: url("@/assets/login_images/login_bg_phone.png") no-repeat;
|
||||
background-size: 100% 100%;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
.login-wrapper {
|
||||
width: 70%;
|
||||
height: 60%;
|
||||
padding-top: 15%;
|
||||
h1 {
|
||||
font-size: 30px;
|
||||
color: #fff;
|
||||
}
|
||||
.login-form {
|
||||
.form-item {
|
||||
margin: 10px 0;
|
||||
span {
|
||||
color: rgb(113, 129, 141);
|
||||
}
|
||||
.input-item {
|
||||
height: 30px;
|
||||
border: 1px solid rgb(113, 129, 141);
|
||||
background-color: transparent;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
.login-btn {
|
||||
height: 40px;
|
||||
background-color: rgb(235, 95, 93);
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
.divider {
|
||||
.line {
|
||||
border-bottom: 1px solid #fff;
|
||||
}
|
||||
.divider-text {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
.other-login-item {
|
||||
border-radius: 15px;
|
||||
img {
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
271
Yi.BBS.Vue3/src/assets/styles/ruoyi.scss
Normal file
@@ -0,0 +1,271 @@
|
||||
/**
|
||||
* 通用css样式布局处理
|
||||
* Copyright (c) 2019 ruoyi
|
||||
*/
|
||||
|
||||
/** 基础通用 **/
|
||||
.pt5 {
|
||||
padding-top: 5px;
|
||||
}
|
||||
.pr5 {
|
||||
padding-right: 5px;
|
||||
}
|
||||
.pb5 {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
.mt5 {
|
||||
margin-top: 5px;
|
||||
}
|
||||
.mr5 {
|
||||
margin-right: 5px;
|
||||
}
|
||||
.mb5 {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.mb8 {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.ml5 {
|
||||
margin-left: 5px;
|
||||
}
|
||||
.mt10 {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.mr10 {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.mb10 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.ml10 {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.mt20 {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.mr20 {
|
||||
margin-right: 20px;
|
||||
}
|
||||
.mb20 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.ml20 {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {
|
||||
font-family: inherit;
|
||||
font-weight: 500;
|
||||
line-height: 1.1;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.el-dialog:not(.is-fullscreen) {
|
||||
margin-top: 6vh !important;
|
||||
}
|
||||
|
||||
.el-dialog.scrollbar .el-dialog__body {
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
max-height: 70vh;
|
||||
padding: 10px 20px 0;
|
||||
}
|
||||
|
||||
.el-table {
|
||||
.el-table__header-wrapper, .el-table__fixed-header-wrapper {
|
||||
th {
|
||||
word-break: break-word;
|
||||
background-color: #f8f8f9 !important;
|
||||
color: #515a6e;
|
||||
height: 40px !important;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
.el-table__body-wrapper {
|
||||
.el-button [class*="el-icon-"] + span {
|
||||
margin-left: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 表单布局 **/
|
||||
.form-header {
|
||||
font-size:15px;
|
||||
color:#6379bb;
|
||||
border-bottom:1px solid #ddd;
|
||||
margin:8px 10px 25px 10px;
|
||||
padding-bottom:5px
|
||||
}
|
||||
|
||||
/** 表格布局 **/
|
||||
.pagination-container {
|
||||
// position: relative;
|
||||
height: 25px;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 15px;
|
||||
padding: 10px 20px !important;
|
||||
}
|
||||
|
||||
/* tree border */
|
||||
.tree-border {
|
||||
margin-top: 5px;
|
||||
border: 1px solid #e5e6e7;
|
||||
background: #FFFFFF none;
|
||||
border-radius:4px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pagination-container .el-pagination {
|
||||
right: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
@media ( max-width : 768px) {
|
||||
.pagination-container .el-pagination > .el-pagination__jump {
|
||||
display: none !important;
|
||||
}
|
||||
.pagination-container .el-pagination > .el-pagination__sizes {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-table .fixed-width .el-button--small {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
width: inherit;
|
||||
}
|
||||
|
||||
/** 表格更多操作下拉样式 */
|
||||
.el-table .el-dropdown-link {
|
||||
cursor: pointer;
|
||||
color: #409EFF;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.el-table .el-dropdown, .el-icon-arrow-down {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.el-tree-node__content > .el-checkbox {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.list-group-striped > .list-group-item {
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
border-radius: 0;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.list-group {
|
||||
padding-left: 0px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
border-bottom: 1px solid #e7eaec;
|
||||
border-top: 1px solid #e7eaec;
|
||||
margin-bottom: -1px;
|
||||
padding: 11px 0px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
|
||||
// .el-card__header {
|
||||
// padding: 14px 15px 7px !important;
|
||||
// min-height: 40px;
|
||||
// }
|
||||
|
||||
// .el-card__body {
|
||||
// padding: 15px 20px 20px 20px !important;
|
||||
// }
|
||||
|
||||
.card-box {
|
||||
padding-right: 15px;
|
||||
padding-left: 15px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* button color */
|
||||
.el-button--cyan.is-active,
|
||||
.el-button--cyan:active {
|
||||
background: #20B2AA;
|
||||
border-color: #20B2AA;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.el-button--cyan:focus,
|
||||
.el-button--cyan:hover {
|
||||
background: #48D1CC;
|
||||
border-color: #48D1CC;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.el-button--cyan {
|
||||
background-color: #20B2AA;
|
||||
border-color: #20B2AA;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
/* text color */
|
||||
.text-navy {
|
||||
color: #1ab394;
|
||||
}
|
||||
|
||||
.text-primary {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.text-success {
|
||||
color: #1c84c6;
|
||||
}
|
||||
|
||||
.text-info {
|
||||
color: #23c6c8;
|
||||
}
|
||||
|
||||
.text-warning {
|
||||
color: #f8ac59;
|
||||
}
|
||||
|
||||
.text-danger {
|
||||
color: #ed5565;
|
||||
}
|
||||
|
||||
.text-muted {
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
/* image */
|
||||
.img-circle {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.img-lg {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.avatar-upload-preview {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translate(50%, -50%);
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 4px #ccc;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 拖拽列样式 */
|
||||
.sortable-ghost{
|
||||
opacity: .8;
|
||||
color: #fff!important;
|
||||
background: #42b983!important;
|
||||
}
|
||||
|
||||
/* 表格右侧工具栏样式 */
|
||||
.top-right-btn {
|
||||
margin-left: auto;
|
||||
}
|
||||
55
Yi.BBS.Vue3/src/components/AgreeInfo.vue
Normal file
@@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<el-button text @click="agree">
|
||||
<el-icon v-if="data.isAgree" color="#409EFF">
|
||||
<CircleCheckFilled />
|
||||
</el-icon>
|
||||
<el-icon v-else color="#1E1E1E">
|
||||
<Pointer />
|
||||
</el-icon> 点赞:{{ data.agreeNum ?? 0 }}</el-button>
|
||||
</template>
|
||||
<script setup>
|
||||
import {onMounted,reactive,watch} from 'vue'
|
||||
import { operate } from '@/apis/agreeApi'
|
||||
|
||||
|
||||
//'isAgree','agreeNum','id'
|
||||
const props = defineProps([ 'data'])
|
||||
|
||||
watch(()=>props,(n)=>{
|
||||
data.id=n.data.id;
|
||||
data.isAgree=n.data.isAgree;
|
||||
data.agreeNum=n.data.agreeNum;
|
||||
},{deep:true})
|
||||
|
||||
|
||||
const data=reactive({
|
||||
id:'',
|
||||
isAgree:false,
|
||||
agreeNum:0
|
||||
})
|
||||
// onMounted(()=>{
|
||||
|
||||
// })
|
||||
//点赞操作
|
||||
const agree = async () => {
|
||||
const response = await operate(data.id)
|
||||
const res = response.data;
|
||||
//提示框,颜色区分
|
||||
if (res.isAgree) {
|
||||
data.isAgree = true;
|
||||
data.agreeNum += 1;
|
||||
ElMessage({
|
||||
message: res.message,
|
||||
type: 'success',
|
||||
})
|
||||
}
|
||||
else {
|
||||
data.isAgree = false;
|
||||
data.agreeNum-= 1;
|
||||
ElMessage({
|
||||
message: res.message,
|
||||
type: 'warning',
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
43
Yi.BBS.Vue3/src/components/ArticleContentInfo.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="markdown-body" v-html="outputHtml"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { marked } from 'marked';
|
||||
|
||||
import hljs from "highlight.js";
|
||||
//可以设置加载样式切换主题
|
||||
import 'highlight.js/styles/atom-one-dark.css'
|
||||
import '@/assets/github-markdown.css'
|
||||
import { ref,watch } from 'vue';
|
||||
|
||||
|
||||
|
||||
const outputHtml=ref("")
|
||||
const props = defineProps(['code'])
|
||||
watch(props,(n,o)=>{
|
||||
marked.setOptions({
|
||||
renderer: new marked.Renderer(),
|
||||
highlight: function(code) {
|
||||
return hljs.highlightAuto(code).value;
|
||||
},
|
||||
pedantic: false,
|
||||
gfm: true,//允许 Git Hub标准的markdown
|
||||
tables: true,//支持表格
|
||||
|
||||
breaks: true,
|
||||
sanitize: false,
|
||||
smartLists: true,
|
||||
smartypants: false,
|
||||
xhtml: false,
|
||||
smartLists: true,
|
||||
}
|
||||
);
|
||||
//需要注意代码块样式
|
||||
outputHtml.value = marked(n.code).replace(/<pre>/g, "<pre class='hljs'>")
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
131
Yi.BBS.Vue3/src/components/AvatarInfo.vue
Normal file
@@ -0,0 +1,131 @@
|
||||
<template >
|
||||
<div class="avatar">
|
||||
<div class="avatar-left">
|
||||
<el-avatar :size="props.size" :src="iconUrl" />
|
||||
|
||||
<div v-if="props.isSelf">
|
||||
<div class="nick"> {{ userInfo.nick }}</div>
|
||||
</div>
|
||||
|
||||
<div v-if="!props.isSelf">
|
||||
|
||||
<div class="nick" :class="{ mt_1: props.time != 'undefined' }"> {{ userInfo.nick }}</div>
|
||||
<div class="remarks" v-if="props.time"> {{ props.time }}</div>
|
||||
<div class="remarks">
|
||||
<slot name="bottom" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="info" v-if="!props.isSelf">
|
||||
<el-tag class="ml-2" type="warning">V8</el-tag>
|
||||
<el-tag class="ml-2" type="danger">会员</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<el-button v-if="props.showWatching" type="primary" size="default" icon="Plus">关注</el-button>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import useUserStore from '@/stores/user'
|
||||
import { reactive, watch, onMounted, computed, ref } from 'vue';
|
||||
//userInfo
|
||||
//{icon,name,role,id},根据判断userInfo是否等于未定义,来觉得是当前登录用户信息,还是其他人信息
|
||||
const props = defineProps(['size', 'showWatching', 'time', 'userInfo', 'isSelf'])
|
||||
const userStore = useUserStore();
|
||||
const userInfo = reactive({
|
||||
icon: "",
|
||||
nick: "",
|
||||
role: [],
|
||||
id: ""
|
||||
});
|
||||
const iconUrl=ref('/src/assets/logo.ico');
|
||||
const iconUrlHandler = () => {
|
||||
if (userInfo.icon == null || userInfo.icon == undefined || userInfo.icon == '') {
|
||||
|
||||
return '/src/assets/logo.ico';
|
||||
}
|
||||
return `${import.meta.env.VITE_APP_BASEAPI}/file/${userInfo.icon}`;
|
||||
}
|
||||
|
||||
watch(userStore, (n) => {
|
||||
if (props.userInfo == undefined) {
|
||||
userInfo.nick = n.name;
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
watch(() => props, (n) => {
|
||||
Init();
|
||||
}, { deep: true })
|
||||
|
||||
onMounted(() => {
|
||||
Init();
|
||||
})
|
||||
|
||||
const Init = () => {
|
||||
//使用传入值
|
||||
if (props.userInfo != undefined) {
|
||||
userInfo.icon = props.userInfo.icon;
|
||||
userInfo.nick = props.userInfo.nick;
|
||||
userInfo.role = props.userInfo.role;
|
||||
userInfo.id = props.userInfo.id;
|
||||
iconUrl.value=iconUrlHandler(userInfo.icon)
|
||||
}
|
||||
|
||||
//使用当前登录用户
|
||||
else {
|
||||
|
||||
userInfo.icon = userStore.icon;
|
||||
userInfo.nick = userStore.name;
|
||||
userInfo.role = userStore.role;
|
||||
userInfo.id = userStore.id;
|
||||
iconUrl.value=userInfo.icon;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
.mt_1 {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.nick {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.info {
|
||||
margin-top: 0.6rem;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.info .el-tag {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.el-icon {
|
||||
color: white;
|
||||
|
||||
}
|
||||
|
||||
.avatar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.avatar-left {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.el-avatar {
|
||||
margin-right: 1.2rem;
|
||||
}
|
||||
|
||||
.remarks {
|
||||
padding-top: 0.5rem;
|
||||
color: #8C8C8C;
|
||||
}
|
||||
</style>
|
||||
38
Yi.BBS.Vue3/src/components/BottomInfo.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div class="botton-div">
|
||||
<a><el-icon><UserFilled /></el-icon>站长:{{configStore.author}}</a>
|
||||
<a><el-icon><Search /></el-icon>{{configStore.bottom}}</a>
|
||||
<a><el-icon><View /></el-icon>关于本站</a>
|
||||
<a><el-icon><Message /></el-icon>建议反馈</a>
|
||||
<p></p>
|
||||
<a ><el-icon><Position /></el-icon>{{configStore.icp}}</a>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import useConfigStore from "@/stores/config";
|
||||
const configStore= useConfigStore();
|
||||
</script>
|
||||
<style scoped>
|
||||
.el-icon
|
||||
{margin: 0 0.2rem;}
|
||||
a{
|
||||
margin-right: 2rem;
|
||||
line-height: 1.8rem;
|
||||
}
|
||||
a:hover {
|
||||
color: #40a9ff;
|
||||
cursor:pointer;
|
||||
}
|
||||
.botton-div
|
||||
{
|
||||
background: transparent;
|
||||
color: rgba(0,0,0,.45);
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
height: auto;
|
||||
width: auto;
|
||||
justify-content: center;
|
||||
margin: 0.5rem auto;
|
||||
}
|
||||
</style>
|
||||
204
Yi.BBS.Vue3/src/components/CommentInfo.vue
Normal file
@@ -0,0 +1,204 @@
|
||||
<template>
|
||||
<el-tabs v-model="activeName" @tab-click="handleClick">
|
||||
<el-tab-pane label="评论" name="comment"></el-tab-pane>
|
||||
<el-tab-pane label="相关内容" name="interrelated"></el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<div class="total">
|
||||
<div style="align-self: center;"> 共{{total}}个评论</div>
|
||||
<div> <el-radio-group v-model="selectRadio">
|
||||
<el-radio-button label="new" name="new">最新</el-radio-button>
|
||||
<el-radio-button label="host" name="host">最热</el-radio-button>
|
||||
</el-radio-group></div>
|
||||
</div>
|
||||
|
||||
|
||||
<el-divider />
|
||||
<div v-hasPer="['bbs:comment:add']">
|
||||
<el-input v-model="topContent" placeholder="发表一个友善的评论吧~" :rows="5" type="textarea"></el-input>
|
||||
<el-button @click="addTopComment" type="primary" class="btn-top-comment" >发表评论</el-button>
|
||||
<el-button class="btn-top-comment">其他</el-button>
|
||||
|
||||
<el-divider />
|
||||
</div>
|
||||
|
||||
<!-- 开始评论主体 -->
|
||||
|
||||
<div v-for="item in commentList" :key="item.id" class="comment1">
|
||||
<AvatarInfo :userInfo="item.createUser" />
|
||||
<div class="content">
|
||||
{{ item.content }}
|
||||
</div>
|
||||
<span class="time"> {{ item.creationTime }} </span>
|
||||
<span class="pointer"><el-icon>
|
||||
<Pointer />
|
||||
</el-icon> 0</span>
|
||||
<el-button type="primary" @click="replay(item.createUser.nick, item.id, item.id)" size="large" text v-hasPer="['bbs:comment:add']">回复</el-button>
|
||||
<el-button type="danger" @click="delComment(item.id)" size="large" text v-hasPer="['bbs:comment:remove']">删除</el-button>
|
||||
<div v-show="replayId == item.id" class="input-reply">
|
||||
<el-input v-model="form.content" :placeholder="placeholder" :rows="3" type="textarea"></el-input>
|
||||
<div class="btn-reply">
|
||||
<el-button @click="addComment" type="primary" v-hasPer="['bbs:comment:add']">回复</el-button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 开始子评论主体 -->
|
||||
<div v-for="children in item.children" :key="children.id" class="comment2">
|
||||
|
||||
<div style="display: flex ;">
|
||||
<AvatarInfo :userInfo="children.createUser" />
|
||||
<span style="align-self: center;color:#606266;"> 回复@{{ children.commentedUser.nick }}</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
{{ children.content }}
|
||||
</div>
|
||||
<span class="time">{{ children.creationTime }} </span>
|
||||
<span class="pointer"> <el-icon>
|
||||
<Pointer />
|
||||
</el-icon>0</span>
|
||||
<el-button type="primary" @click="replay(children.createUser.nick, children.id, item.id)" size="large" text v-hasPer="['bbs:comment:add']">回复</el-button>
|
||||
<el-button type="danger" @click="delComment(children.id)" size="large" text v-hasPer="['bbs:comment:remove']">删除</el-button>
|
||||
<div v-show="replayId == children.id" class="input-reply">
|
||||
<el-input v-model="form.content" :placeholder="placeholder" :rows="3" type="textarea"></el-input>
|
||||
<div class="btn-reply">
|
||||
<el-button @click="addComment" type="primary" v-hasPer="['bbs:comment:add']">回复</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<el-divider />
|
||||
|
||||
</div>
|
||||
<el-empty v-show="commentList.length<=0" description="评论空空如也,快来抢占沙发~" />
|
||||
</template>
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { getListByDiscussId, add ,del} from "@/apis/commentApi.js";
|
||||
import AvatarInfo from './AvatarInfo.vue';
|
||||
//数据定义
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const commentList = ref([]);
|
||||
const query = reactive({});
|
||||
const topContent=ref('');
|
||||
//当前回复id
|
||||
const replayId = ref('');
|
||||
//回复文本框
|
||||
const placeholder = ref('')
|
||||
|
||||
//选择类型:评论
|
||||
const activeName = ref('comment');
|
||||
//选择 最新
|
||||
const selectRadio = ref('new');
|
||||
//评论总数
|
||||
const total=ref(0);
|
||||
const form = reactive({
|
||||
content: "",
|
||||
discussId: route.params.discussId,
|
||||
query,
|
||||
parentId: 0,
|
||||
rootId: 0,
|
||||
});
|
||||
onMounted(async () => {
|
||||
await loadComment();
|
||||
});
|
||||
const loadComment = async () => {
|
||||
topContent.value='';
|
||||
form.content = '';
|
||||
const response = await getListByDiscussId(route.params.discussId, query);
|
||||
commentList.value = response.data.items;
|
||||
total.value=response.data.total
|
||||
};
|
||||
const addTopComment = async () => {
|
||||
form.parentId = 0;
|
||||
form.rootId = 0;
|
||||
form.content=topContent.value;
|
||||
await addComment();
|
||||
}
|
||||
const addComment = async () => {
|
||||
if(form.content.length<=0)
|
||||
{
|
||||
ElMessage.error('输入评论不能为空!')
|
||||
return
|
||||
}
|
||||
await add(form);
|
||||
await loadComment();
|
||||
ElMessage({
|
||||
message: '评论发表成功!',
|
||||
type: 'success',
|
||||
})
|
||||
};
|
||||
const delComment=async(ids)=>{
|
||||
ElMessageBox.confirm(`确定是否删除编号[${ids}]的评论吗?`, "警告", {
|
||||
confirmButtonText: "确认",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(async () => {
|
||||
|
||||
await del(ids);
|
||||
await loadComment();
|
||||
ElMessage({
|
||||
message: '评论已删除!',
|
||||
type: 'success',
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
const replay = async (parentUserName, parentId, rootId) => {
|
||||
replayId.value = parentId;
|
||||
form.parentId = parentId;
|
||||
form.rootId = rootId;
|
||||
placeholder.value = `回复@${parentUserName}`;
|
||||
}
|
||||
|
||||
//切换 评论、相关内容
|
||||
const handleClick = () => { }
|
||||
</script>
|
||||
<style scoped>
|
||||
.input-reply {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.btn-reply {
|
||||
margin: 1rem 0;
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
.comment1 .pointer {
|
||||
margin: 0 0 0 1rem;
|
||||
}
|
||||
|
||||
.time {
|
||||
color: #8C8C8C;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.total {
|
||||
display: flex;
|
||||
|
||||
justify-content: space-between;
|
||||
|
||||
}
|
||||
|
||||
.comment2 {
|
||||
margin-left: 3rem;
|
||||
}
|
||||
|
||||
.el-divider {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.btn-top-comment {
|
||||
margin-top: 0.5rem;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
</style>
|
||||
197
Yi.BBS.Vue3/src/components/DisscussCard.vue
Normal file
@@ -0,0 +1,197 @@
|
||||
<template>
|
||||
<el-badge :value="props.badge ?? ''" class="box-card">
|
||||
<el-card shadow="never" :style="{ 'border-color': discuss.color }">
|
||||
<el-row>
|
||||
|
||||
<!-- 头部 -->
|
||||
<el-col :span=24 class="card-header">
|
||||
<AvatarInfo :userInfo="discuss.user" :time="discuss.creationTime" />
|
||||
</el-col>
|
||||
|
||||
|
||||
<!-- 身体 -->
|
||||
|
||||
<el-col :span=18 >
|
||||
<el-row>
|
||||
|
||||
<el-col v-if="discuss.isBan" :span=24 class=" item item-title "> <el-link size="100" :underline="false"
|
||||
style="color:#F56C6C;" >{{ discuss.title }}</el-link></el-col>
|
||||
|
||||
<el-col v-else :span=24 class=" item item-title "> <el-link size="100" :underline="false"
|
||||
@click="enterDiscuss(discuss.id)">{{ discuss.title }}</el-link></el-col>
|
||||
|
||||
|
||||
|
||||
<el-col :span=24 class=" item item-description">{{ discuss.introduction }}</el-col>
|
||||
<el-col :span=24 class=" item item-tag"><el-tag v-for="i in 4" :key="i">教程</el-tag></el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
|
||||
|
||||
<el-col :span=6 style=" display: flex;justify-content: center;">
|
||||
<el-image :preview-src-list="[getUrl(discuss.cover)]" v-if="discuss.cover" :src="getUrl(discuss.cover)" style="width: 100px;height: 100px;" />
|
||||
</el-col>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- 底部 -->
|
||||
<el-col :span=24 class=" item item-bottom " style=" margin-bottom: 0;">
|
||||
<el-space :size="10" :spacer="spacer">
|
||||
<div class="item-description">
|
||||
{{ discuss.creationTime }}
|
||||
</div>
|
||||
<AgreeInfo :data="discuss"/>
|
||||
<!--
|
||||
<el-button text @click="agree">
|
||||
<el-icon v-if="discuss.isAgree" color="#409EFF">
|
||||
<CircleCheckFilled />
|
||||
</el-icon>
|
||||
<el-icon v-else color="#1E1E1E">
|
||||
<Pointer />
|
||||
</el-icon> 点赞:{{ discuss.agreeNum ?? 0 }}</el-button>
|
||||
<el-button icon="Star" text>
|
||||
收藏</el-button> -->
|
||||
|
||||
<el-button icon="View" text>
|
||||
浏览数:{{ discuss.seeNum ?? 0 }}</el-button>
|
||||
|
||||
|
||||
</el-space>
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
|
||||
</el-card>
|
||||
</el-badge>
|
||||
</template>
|
||||
<script setup>
|
||||
import { h, ref, toRef, onMounted ,reactive} from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import AvatarInfo from './AvatarInfo.vue';
|
||||
import AgreeInfo from './AgreeInfo.vue'
|
||||
import { operate } from '@/apis/agreeApi'
|
||||
|
||||
const props = defineProps(['discuss','badge'])
|
||||
const discuss=reactive({
|
||||
id:'',
|
||||
title:"",
|
||||
introduction:"",
|
||||
creationTime:"",
|
||||
user:{},
|
||||
color:"",
|
||||
seeNum:0,
|
||||
agreeNum:0,
|
||||
isAgree:false,
|
||||
cover:"",
|
||||
isBan:false,
|
||||
isAgree:false,
|
||||
agreeNum:0
|
||||
})
|
||||
const router = useRouter()
|
||||
const spacer = h(ElDivider, { direction: 'vertical' })
|
||||
const enterDiscuss = (id) => {
|
||||
router.push(`/article/${id}`)
|
||||
}
|
||||
const getUrl= (str)=>{
|
||||
return `${import.meta.env.VITE_APP_BASEAPI}/file/${str}`
|
||||
}
|
||||
|
||||
//点赞操作
|
||||
const agree = async () => {
|
||||
const response = await operate(discuss.id)
|
||||
const res = response.data;
|
||||
//提示框,颜色区分
|
||||
if (res.isAgree) {
|
||||
discuss.isAgree = true;
|
||||
discuss.agreeNum += 1;
|
||||
ElMessage({
|
||||
message: res.message,
|
||||
type: 'success',
|
||||
})
|
||||
}
|
||||
else {
|
||||
discuss.isAgree = false;
|
||||
discuss.agreeNum-= 1;
|
||||
ElMessage({
|
||||
message: res.message,
|
||||
type: 'warning',
|
||||
})
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
|
||||
// id:'',
|
||||
// title:"",
|
||||
// introduction:"",
|
||||
// creationTime:"",
|
||||
// user:{},
|
||||
// color:"",
|
||||
// seeNum:0,
|
||||
// agreeNum:0,
|
||||
// isAgree:""
|
||||
discuss.id=props.discuss.id;
|
||||
discuss.title=props.discuss.title;
|
||||
discuss.introduction=props.discuss.introduction;
|
||||
discuss.creationTime=props.discuss.creationTime;
|
||||
discuss.user=props.discuss.user;
|
||||
discuss.color=props.discuss.color;
|
||||
discuss.seeNum=props.discuss.seeNum;
|
||||
discuss.isAgree=props.discuss.isAgree;
|
||||
discuss.agreeNum=props.discuss.agreeNum;
|
||||
discuss.isBan=props.discuss.isBan;
|
||||
discuss.cover=props.discuss.cover;
|
||||
discuss.value = props.isAgree;
|
||||
discuss.value = props.agreeNum;
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
.el-card {
|
||||
border: 2px solid white
|
||||
}
|
||||
|
||||
.item-bottom .el-icon {
|
||||
margin-right: 0.4rem;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
margin-bottom: 1.5rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.item {
|
||||
font-size: 14px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.box-card {
|
||||
width: 100%;
|
||||
min-height: 15rem;
|
||||
/* right: calc(1px + var(--el-badge-size)/ 2) !important; */
|
||||
/* top: 0 !important; */
|
||||
|
||||
}
|
||||
|
||||
.item-title {
|
||||
/* font-size: var(--el-font-size-large); */
|
||||
}
|
||||
|
||||
.item-description {
|
||||
font-size: var(--el-font-size-small);
|
||||
color: #8C8C8C;
|
||||
}
|
||||
|
||||
.item .el-tag {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.ml-2 {
|
||||
margin-left: 1.2rem;
|
||||
}
|
||||
|
||||
.el-link {
|
||||
font-size: initial;
|
||||
font-weight: 700;
|
||||
}
|
||||
</style>
|
||||
55
Yi.BBS.Vue3/src/components/InfoCard.vue
Normal file
@@ -0,0 +1,55 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const props = defineProps(['items','header','text','hideDivider'])
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-card class="box-card" shadow="never">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>{{props.header}}</span>
|
||||
<el-link :underline="false" type="primary">{{props.text}}</el-link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<slot name="content" />
|
||||
|
||||
|
||||
<div v-for="(item,i) in props.items " >
|
||||
<div class="text item">
|
||||
<slot name="item" v-bind="item"/>
|
||||
</div>
|
||||
<el-divider v-if="i!=props.items.length-1&&hideDivider==undefined" />
|
||||
</div>
|
||||
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
|
||||
.el-divider
|
||||
{
|
||||
margin: 0.2rem 0;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.item {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.box-card {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
151
Yi.BBS.Vue3/src/components/MavonEdit.vue
Normal file
@@ -0,0 +1,151 @@
|
||||
<template>
|
||||
<mavon-editor ref='md' v-model="text" :subfield="true" :codeStyle="props.codeStyle" :ishljs="true"
|
||||
:style="{ minHeight: props.height, maxHeight: '100%' }" class="edit" @imgAdd="imgAdd">
|
||||
|
||||
<!-- 引用视频链接的自定义按钮 -->
|
||||
<template v-slot:left-toolbar-after>
|
||||
<!--点击按钮触发的事件是打开表单对话框-->
|
||||
|
||||
|
||||
<el-button @click="fileDialogVisible=true" aria-hidden="true" class="op-icon fa" title="上传文件">
|
||||
<el-icon ><FolderChecked /></el-icon>
|
||||
</el-button>
|
||||
|
||||
|
||||
<el-dropdown :hide-on-click='false'>
|
||||
<el-button aria-hidden="true" class="op-icon fa" title="表情包">
|
||||
😊
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu >
|
||||
<el-dropdown-item>
|
||||
<table border="1">
|
||||
<tr>
|
||||
<td @click="text+='😊'">😊</td>
|
||||
<td>😊</td>
|
||||
<td>😊</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>😊</td>
|
||||
<td>😊</td>
|
||||
<td>😊</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>😊</td>
|
||||
<td>😊</td>
|
||||
<td>😊</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</el-dropdown-item>
|
||||
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
|
||||
|
||||
<el-dialog
|
||||
:modal=false
|
||||
:draggable=true
|
||||
|
||||
v-model="fileDialogVisible"
|
||||
title="文件上传"
|
||||
width="30%"
|
||||
:before-close="fileHandleClose"
|
||||
>
|
||||
<span>选择你的文件:</span>
|
||||
|
||||
<el-upload
|
||||
class="upload-demo"
|
||||
drag
|
||||
:action="fileUploadUrl"
|
||||
multiple
|
||||
:on-success="onSuccess"
|
||||
>
|
||||
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||||
<div class="el-upload__text">
|
||||
可将文件拖拽到这里 <em>点击上传</em>
|
||||
</div>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">
|
||||
文件需小于100MB以内
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
<p v-for="(item,i) in fileUrlList" :key="i">{{` ${i+1}: ${getUrl(item)}` }} <el-button></el-button></p>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="fileDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="dialogVisible = false">
|
||||
确认
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
</mavon-editor>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup>
|
||||
import { mavonEditor } from 'mavon-editor'
|
||||
import 'mavon-editor/dist/css/index.css'
|
||||
import { ref, computed, watch, onMounted } from 'vue';
|
||||
import { upload } from '@/apis/fileApi'
|
||||
|
||||
const md = ref(null);
|
||||
const props = defineProps(['height', 'modelValue', "codeStyle"])
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const fileDialogVisible=ref(false)
|
||||
|
||||
//已经上传好的文件列表url
|
||||
const fileUrlList=ref([])
|
||||
|
||||
const fileUploadUrl=`${import.meta.env.VITE_APP_BASEAPI}/file`
|
||||
// //v-model传值出去
|
||||
const text = computed({
|
||||
get() {
|
||||
return props.modelValue
|
||||
},
|
||||
set(value) {
|
||||
emit('update:modelValue', value)
|
||||
}
|
||||
})
|
||||
|
||||
const getUrl= (str)=>{
|
||||
return `${import.meta.env.VITE_APP_BASEAPI}/file/${str}`
|
||||
}
|
||||
|
||||
//关闭文件上传弹窗
|
||||
const fileHandleClose=()=>{
|
||||
fileDialogVisible.value=false;
|
||||
}
|
||||
//文件上传成功后
|
||||
const onSuccess=(response)=>{
|
||||
fileUrlList.value.push(response[0].id)
|
||||
|
||||
}
|
||||
//图片上传
|
||||
const imgAdd = async (pos, $file) => {
|
||||
// 第一步.将图片上传到服务器.
|
||||
var formdata = new FormData();
|
||||
formdata.append('file', $file);
|
||||
const response = await upload(formdata)
|
||||
const url = `${import.meta.env.VITE_APP_BASEAPI}/file/${response.data[0].id}/true`;
|
||||
console.log(url)
|
||||
md.value.$img2Url(pos, url);
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
.edit {
|
||||
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
49
Yi.BBS.Vue3/src/components/PlateCard.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<el-row>
|
||||
<el-col
|
||||
>
|
||||
<el-card :body-style="{ padding: '0px' }" shadow="never">
|
||||
<img
|
||||
src=""
|
||||
class="image"
|
||||
/>
|
||||
<div style="padding: 14px">
|
||||
<span>{{props.name}}</span>
|
||||
<div class="bottom">
|
||||
<time class="remarks">{{ props.introduction }}</time>
|
||||
<RouterLink :to="`/discuss/${props.id}`"> <el-button text class="button" type="primary">进入<el-icon><CaretRight /></el-icon></el-button> </RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted } from 'vue';
|
||||
|
||||
const props = defineProps(['name','introduction','id'])
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.remarks {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.bottom {
|
||||
margin-top: 13px;
|
||||
line-height: 12px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.image {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
28
Yi.BBS.Vue3/src/components/ScrollbarInfo.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<el-scrollbar>
|
||||
<div class="scrollbar-flex-content">
|
||||
<p v-for="item in 50" :key="item" class="scrollbar-item">
|
||||
推荐
|
||||
</p>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
<style scoped>
|
||||
|
||||
.scrollbar-flex-content {
|
||||
display: flex;
|
||||
}
|
||||
.scrollbar-item {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 4rem;
|
||||
height: 2.6rem;
|
||||
margin: 0 0.2rem ;
|
||||
text-align: center;
|
||||
border-radius: 4px;
|
||||
background-color:#FAFAFA;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
67
Yi.BBS.Vue3/src/components/TreeArticleInfo.vue
Normal file
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<el-tree
|
||||
:data="props.data==''?[]:props.data"
|
||||
:props="defaultProps"
|
||||
@node-click="handleNodeClick"
|
||||
:expand-on-click-node="false"
|
||||
node-key="id"
|
||||
:default-expand-all='true'
|
||||
:highlight-current="true"
|
||||
:current-node-key="currentNodeKey"
|
||||
>
|
||||
|
||||
<template #default="{ node, data }">
|
||||
<span class="custom-tree-node">
|
||||
<span>{{data.name}}</span>
|
||||
<span>
|
||||
|
||||
<a style="color: #409EFF; margin-left: 8px" @click="$emit('create',node, data)" v-hasPer="['bbs:article:add']"
|
||||
|
||||
> + </a>
|
||||
<a style="color: #409EFF; margin-left: 8px" @click="$emit('update',node, data)" v-hasPer="['bbs:article:edit']"
|
||||
|
||||
> 编辑 </a>
|
||||
<a style="color: #f56c6c; margin-left: 8px" @click="$emit('remove',node, data)" v-hasPer="['bbs:article:remove']"
|
||||
|
||||
> 删除 </a>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
|
||||
const props = defineProps(['data',"currentNodeKey"])
|
||||
const emits= defineEmits(["handleNodeClick"])
|
||||
|
||||
const currentNodeKey=props.currentNodeKey;
|
||||
//数据定义
|
||||
//子文章数据
|
||||
// const articleData =ref([]);
|
||||
//树形子文章选项
|
||||
const defaultProps = {
|
||||
children: "children",
|
||||
label: "name",
|
||||
};
|
||||
// //子文章初始化
|
||||
// const loadArticleData=async()=>
|
||||
// {
|
||||
// articleData.value= await articleall(route.params.discussId);
|
||||
// }
|
||||
//点击事件
|
||||
const handleNodeClick = (data) => {
|
||||
emits('handleNodeClick',data)
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.custom-tree-node {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 14px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
</style>
|
||||
129
Yi.BBS.Vue3/src/components/UserSelectInfo.vue
Normal file
@@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<el-select
|
||||
style="width: 600px;"
|
||||
v-model="value"
|
||||
multiple
|
||||
filterable
|
||||
remote
|
||||
reserve-keyword
|
||||
placeholder="请输入用户账号(可多选)"
|
||||
remote-show-suffix
|
||||
:remote-method="remoteMethod"
|
||||
:loading="loading"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref,computed } from 'vue'
|
||||
import {listUser} from '@/apis/userApi'
|
||||
const props = defineProps(['modelValue'])
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
|
||||
//这个为可选择的列表,{value,label},value为用户id,label为账号名称(不可重复)
|
||||
const options = ref([])
|
||||
|
||||
const value = computed({
|
||||
get() {
|
||||
return props.modelValue
|
||||
},
|
||||
set(value) {
|
||||
emit('update:modelValue', value)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
onMounted( async()=>{
|
||||
|
||||
const response= await listUser({ids:value.value.join()});
|
||||
const res=response.data.items;
|
||||
//下拉列表
|
||||
options.value = res
|
||||
.map((item) => {
|
||||
return { value: `${item.id}`, label: `用户:${item.userName}` }
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
const loadUser=async(query)=>{
|
||||
const response= await listUser({userName:query});
|
||||
const res=response.data.items;
|
||||
//下拉列表
|
||||
options.value = res
|
||||
.map((item) => {
|
||||
return { value: `${item.id}`, label: `用户:${item.userName}` }
|
||||
})
|
||||
}
|
||||
|
||||
const remoteMethod =async (query) => {
|
||||
if (query) {
|
||||
loading.value = true
|
||||
await loadUser(query);
|
||||
loading.value = false
|
||||
} else {
|
||||
options.value = []
|
||||
}
|
||||
}
|
||||
|
||||
const states = [
|
||||
'Alabama',
|
||||
'Alaska',
|
||||
'Arizona',
|
||||
'Arkansas',
|
||||
'California',
|
||||
'Colorado',
|
||||
'Connecticut',
|
||||
'Delaware',
|
||||
'Florida',
|
||||
'Georgia',
|
||||
'Hawaii',
|
||||
'Idaho',
|
||||
'Illinois',
|
||||
'Indiana',
|
||||
'Iowa',
|
||||
'Kansas',
|
||||
'Kentucky',
|
||||
'Louisiana',
|
||||
'Maine',
|
||||
'Maryland',
|
||||
'Massachusetts',
|
||||
'Michigan',
|
||||
'Minnesota',
|
||||
'Mississippi',
|
||||
'Missouri',
|
||||
'Montana',
|
||||
'Nebraska',
|
||||
'Nevada',
|
||||
'New Hampshire',
|
||||
'New Jersey',
|
||||
'New Mexico',
|
||||
'New York',
|
||||
'North Carolina',
|
||||
'North Dakota',
|
||||
'Ohio',
|
||||
'Oklahoma',
|
||||
'Oregon',
|
||||
'Pennsylvania',
|
||||
'Rhode Island',
|
||||
'South Carolina',
|
||||
'South Dakota',
|
||||
'Tennessee',
|
||||
'Texas',
|
||||
'Utah',
|
||||
'Vermont',
|
||||
'Virginia',
|
||||
'Washington',
|
||||
'West Virginia',
|
||||
'Wisconsin',
|
||||
'Wyoming',
|
||||
]
|
||||
</script>
|
||||
51
Yi.BBS.Vue3/src/components/echars/VisitsLineChart.vue
Normal file
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<div ref="VisitsLineChart"></div>
|
||||
</template>
|
||||
<script setup>
|
||||
import * as echarts from 'echarts/core';
|
||||
import { GridComponent } from 'echarts/components';
|
||||
import { LineChart } from 'echarts/charts';
|
||||
import { UniversalTransition } from 'echarts/features';
|
||||
import { CanvasRenderer } from 'echarts/renderers';
|
||||
import { ref ,onMounted} from 'vue';
|
||||
|
||||
echarts.use([GridComponent, LineChart, CanvasRenderer, UniversalTransition]);
|
||||
|
||||
const VisitsLineChart=ref(null);
|
||||
|
||||
onMounted(()=>{
|
||||
var myChart = echarts.init(VisitsLineChart.value, null, {
|
||||
width: 320,
|
||||
height: 230
|
||||
});
|
||||
var option;
|
||||
|
||||
option = {
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: [82, 93, 90, 93, 129, 133, 132],
|
||||
type: 'line',
|
||||
areaStyle: {}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
option && myChart.setOption(option);
|
||||
|
||||
window.addEventListener('resize', function() {
|
||||
myChart.resize();
|
||||
});
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
9
Yi.BBS.Vue3/src/directive/index.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import hasRole from './permission/hasRole'
|
||||
import hasPermi from './permission/hasPermi'
|
||||
// import copyText from './common/copyText'
|
||||
|
||||
export default function directive(app){
|
||||
app.directive('hasRole', hasRole)
|
||||
app.directive('hasPer', hasPermi)
|
||||
// app.directive('copyText', copyText)
|
||||
}
|
||||
29
Yi.BBS.Vue3/src/directive/permission/hasDiscussPermi.js
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* v-hasDiscussPermi 操作权限处理
|
||||
*/
|
||||
|
||||
import useUserStore from '@/stores/user'
|
||||
|
||||
//传一个值,一个主题id的创建者id,判断当前主题是否为自己创建,拥有*:*:*,直接跳过
|
||||
export default {
|
||||
mounted(el, binding, vnode) {
|
||||
const { value } = binding
|
||||
const all_permission = "*:*:*";
|
||||
const permissions = useUserStore().permissions
|
||||
const userId = useUserStore().id
|
||||
if (value && value instanceof Array && value.length > 0) {
|
||||
const permissionFlag = value
|
||||
|
||||
const hasPermissions = permissions.some(permission => {
|
||||
return all_permission === permission || permissionFlag==userId
|
||||
})
|
||||
|
||||
if (!hasPermissions) {
|
||||
el.parentNode && el.parentNode.removeChild(el)
|
||||
}
|
||||
} else {
|
||||
throw new Error(`请设置操作主题用户签值`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
27
Yi.BBS.Vue3/src/directive/permission/hasPermi.js
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* v-hasPermi 操作权限处理
|
||||
* Copyright (c) 2019 ruoyi
|
||||
*/
|
||||
|
||||
import useUserStore from '@/stores/user'
|
||||
|
||||
export default {
|
||||
mounted(el, binding, vnode) {
|
||||
const { value } = binding
|
||||
const all_permission = "*:*:*";
|
||||
const permissions = useUserStore().permissions
|
||||
if (value && value instanceof Array && value.length > 0) {
|
||||
const permissionFlag = value
|
||||
|
||||
const hasPermissions = permissions.some(permission => {
|
||||
return all_permission === permission || permissionFlag.includes(permission)
|
||||
})
|
||||
|
||||
if (!hasPermissions) {
|
||||
el.parentNode && el.parentNode.removeChild(el)
|
||||
}
|
||||
} else {
|
||||
throw new Error(`请设置操作权限标签值`)
|
||||
}
|
||||
}
|
||||
}
|
||||
28
Yi.BBS.Vue3/src/directive/permission/hasRole.js
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* v-hasRole 角色权限处理
|
||||
* Copyright (c) 2019 ruoyi
|
||||
*/
|
||||
|
||||
import useUserStore from '@/stores/user'
|
||||
|
||||
export default {
|
||||
mounted(el, binding, vnode) {
|
||||
const { value } = binding
|
||||
const super_admin = "admin";
|
||||
const roles = useUserStore().roles
|
||||
|
||||
if (value && value instanceof Array && value.length > 0) {
|
||||
const roleFlag = value
|
||||
|
||||
const hasRole = roles.some(role => {
|
||||
return super_admin === role || roleFlag.includes(role)
|
||||
})
|
||||
|
||||
if (!hasRole) {
|
||||
el.parentNode && el.parentNode.removeChild(el)
|
||||
}
|
||||
} else {
|
||||
throw new Error(`请设置角色权限标签值"`)
|
||||
}
|
||||
}
|
||||
}
|
||||
24
Yi.BBS.Vue3/src/layout/AppBody.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
<template class="back-color">
|
||||
|
||||
<div class="content-main">
|
||||
<RouterView />
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.body-main{
|
||||
min-height: 10rem;
|
||||
min-width: 10rem;
|
||||
background-color:#F0F2F5;
|
||||
|
||||
}
|
||||
.content-main{
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
min-height: 1150px;
|
||||
}
|
||||
</style>
|
||||
112
Yi.BBS.Vue3/src/layout/AppHeader.vue
Normal file
@@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<el-menu
|
||||
:default-active="activeIndex"
|
||||
class="el-menu-demo"
|
||||
mode="horizontal"
|
||||
:ellipsis="false"
|
||||
@select="handleSelect"
|
||||
|
||||
>
|
||||
<el-menu-item class="logo" index="" @click="enterIndex" >
|
||||
<img class="img-icon" style="width: 35px; height: 35px" src="@/assets/logo.ico" />{{configStore.name}}</el-menu-item>
|
||||
<el-menu-item index="1" @click="enterIndex">主页</el-menu-item>
|
||||
<el-sub-menu index="2">
|
||||
<template #title>学习</template>
|
||||
<el-menu-item index="2-1">学习 one</el-menu-item>
|
||||
<el-menu-item index="2-2">学习 two</el-menu-item>
|
||||
<el-menu-item index="2-3">学习 three</el-menu-item>
|
||||
</el-sub-menu>
|
||||
<el-sub-menu index="3">
|
||||
<template #title>资源</template>
|
||||
<el-menu-item index="3-1">资源 one</el-menu-item>
|
||||
<el-menu-item index="3-2">资源 two</el-menu-item>
|
||||
<el-menu-item index="3-3">资源 three</el-menu-item>
|
||||
</el-sub-menu>
|
||||
<el-sub-menu index="4">
|
||||
<template #title>问答</template>
|
||||
<el-menu-item index="4-1">问答 one</el-menu-item>
|
||||
<el-menu-item index="4-2">问答 two</el-menu-item>
|
||||
<el-menu-item index="4-3">问答 three</el-menu-item>
|
||||
</el-sub-menu>
|
||||
|
||||
<div class="flex-grow" />
|
||||
|
||||
<el-menu-item index="5">
|
||||
<div style="width: 350px;">
|
||||
<el-input style="width: 300px;" v-model="searchText" placeholder="全站搜索" clearable prefix-icon="Search" />
|
||||
<el-button type="primary" plain @click="search">搜索</el-button>
|
||||
</div>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item index="6" @click="enterProfile" >
|
||||
<AvatarInfo :size='30' :isSelf="true" />
|
||||
</el-menu-item>
|
||||
<el-sub-menu index="6">
|
||||
<template #title>个人中心</template>
|
||||
<el-menu-item index="6-1" @click="enterProfile">进入个人中心</el-menu-item>
|
||||
<el-menu-item index="6-2" @click="enterProfile">其他</el-menu-item>
|
||||
<el-menu-item index="6-3" @click="logout">登出</el-menu-item>
|
||||
</el-sub-menu>
|
||||
|
||||
</el-menu>
|
||||
|
||||
</template>
|
||||
<script setup>
|
||||
import AvatarInfo from '@/components/AvatarInfo.vue'
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import useUserStore from '@/stores/user.js'
|
||||
import useConfigStore from "@/stores/config";
|
||||
const configStore= useConfigStore();
|
||||
const router = useRouter()
|
||||
const userStore =useUserStore();
|
||||
const activeIndex = ref('1')
|
||||
const searchText=ref('')
|
||||
const handleSelect = (key, keyPath) => {
|
||||
console.log(key, keyPath)
|
||||
}
|
||||
const logout= async()=>{
|
||||
ElMessageBox.confirm(`确定登出系统吗?`, "警告", {
|
||||
confirmButtonText: "确认",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then( async() => {
|
||||
//异步
|
||||
await userStore.logOut();
|
||||
//删除成功后,跳转到主页
|
||||
router.push("/login");
|
||||
ElMessage({
|
||||
type: "success",
|
||||
message: "登出成功",
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
const enterIndex=()=>{
|
||||
router.push("/index");
|
||||
};
|
||||
const enterProfile=()=>{
|
||||
router.push("/profile");}
|
||||
|
||||
const search=()=>{
|
||||
var routerPer = { path: `/discuss`,query:{q:searchText.value} };
|
||||
searchText.value='';
|
||||
router.push(routerPer)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.logo{
|
||||
min-width: 14rem;
|
||||
font-size: large;
|
||||
font-weight: 600;
|
||||
}
|
||||
.flex-grow {
|
||||
flex-grow: 1;
|
||||
}
|
||||
.img-icon
|
||||
{
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
27
Yi.BBS.Vue3/src/layout/Index.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<div class="common-layout">
|
||||
<el-container>
|
||||
<el-header>
|
||||
|
||||
<AppHeader />
|
||||
|
||||
</el-header>
|
||||
<el-main>
|
||||
<AppBody />
|
||||
</el-main>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import AppHeader from "./AppHeader.vue"
|
||||
import AppBody from "./AppBody.vue"
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-main{
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 10rem;
|
||||
background-color: #F0F2F5;
|
||||
}
|
||||
</style>
|
||||
8
Yi.BBS.Vue3/src/layout/LoginLayout.vue
Normal file
@@ -0,0 +1,8 @@
|
||||
<template>
|
||||
<div class="box">
|
||||
<div class="content">
|
||||
<RouterView/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style src="@/assets/styles/login.scss" scoped></style>
|
||||
23
Yi.BBS.Vue3/src/main.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
import './assets/main.css'
|
||||
import '@/assets/styles/index.scss' // global css
|
||||
|
||||
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
|
||||
import directive from './directive' // directive
|
||||
|
||||
import "./permission"
|
||||
|
||||
const app = createApp(App)
|
||||
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
|
||||
app.component(key, component)
|
||||
}
|
||||
|
||||
app.use(createPinia())
|
||||
directive(app);
|
||||
app.use(router)
|
||||
app.mount('#app')
|
||||
65
Yi.BBS.Vue3/src/permission.js
Normal file
@@ -0,0 +1,65 @@
|
||||
import router from './router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import NProgress from 'nprogress'
|
||||
import 'nprogress/nprogress.css'
|
||||
import { getToken } from '@/utils/auth'
|
||||
import { isRelogin } from '@/utils/request'
|
||||
import useUserStore from '@/stores/user'
|
||||
|
||||
|
||||
NProgress.configure({ showSpinner: false });
|
||||
|
||||
const whiteList = ['/login', '/auth-redirect', '/bind', '/register'];
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
NProgress.start()
|
||||
if (getToken()) {
|
||||
// to.meta.title && useSettingsStore().setTitle(to.meta.title)
|
||||
/* has token*/
|
||||
if (to.path === '/login') {
|
||||
next({ path: '/' })
|
||||
NProgress.done()
|
||||
} else {
|
||||
|
||||
if (useUserStore().roles.length === 0)
|
||||
{
|
||||
isRelogin.show = true
|
||||
// 判断当前用户是否已拉取完user_info信息
|
||||
useUserStore().getInfo().then(() => {
|
||||
isRelogin.show = false
|
||||
//这里不需要动态路由
|
||||
// usePermissionStore().generateRoutes().then(accessRoutes => {
|
||||
// // 根据roles权限生成可访问的路由表
|
||||
// accessRoutes.forEach(route => {
|
||||
// if (!isHttp(route.path)) {
|
||||
// router.addRoute(route) // 动态添加可访问路由表
|
||||
// }
|
||||
// })
|
||||
// next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
|
||||
// })
|
||||
next({ ...to, replace: true })
|
||||
}).catch(err => {
|
||||
useUserStore().logOut().then(() => {
|
||||
ElMessage.error(err)
|
||||
next({ path: '/' })
|
||||
})
|
||||
})
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 没有token
|
||||
if (whiteList.indexOf(to.path) !== -1) {
|
||||
// 在免登录白名单,直接进入
|
||||
next()
|
||||
} else {
|
||||
next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
|
||||
NProgress.done()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
router.afterEach(() => {
|
||||
NProgress.done()
|
||||
})
|
||||
76
Yi.BBS.Vue3/src/router/index.js
Normal file
@@ -0,0 +1,76 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import Layout from '../layout/Index.vue'
|
||||
import NotFound from '../views/error/404.vue'
|
||||
import LoginLayout from '../layout/LoginLayout.vue'
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
scrollBehavior(to, from, savedPosition) {
|
||||
// 始终滚动到顶部
|
||||
return { top: 0 }
|
||||
},
|
||||
routes: [
|
||||
{
|
||||
name:'test',
|
||||
path: '/test',
|
||||
component: () => import('../views/Test.vue')
|
||||
},
|
||||
{
|
||||
|
||||
path: '/loginLayout',
|
||||
name: 'loginLayout',
|
||||
component: LoginLayout,
|
||||
redirect: '/login' ,
|
||||
children :[
|
||||
{
|
||||
name:'login',
|
||||
path: '/login',
|
||||
component: () => import('../views/Login.vue')
|
||||
},
|
||||
{
|
||||
name:'register',
|
||||
path: '/register',
|
||||
component: () => import('../views/Register.vue')
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
name: 'layout',
|
||||
component: Layout,
|
||||
redirect: '/index' ,
|
||||
children :[
|
||||
{
|
||||
name:'index',
|
||||
path: '/index',
|
||||
component: () => import('../views/Index.vue')
|
||||
},
|
||||
{
|
||||
name:'article',
|
||||
path: '/article/:discussId/:articleId?',
|
||||
component: () => import('../views/Article.vue')
|
||||
},
|
||||
{
|
||||
name:'discuss',
|
||||
path: '/discuss/:plateId?',
|
||||
component: () => import('../views/Discuss.vue')
|
||||
},
|
||||
{
|
||||
//artType:discuss主题、article文章
|
||||
//operType:create创建、update更新
|
||||
name:'editArt',
|
||||
path:'/editArt',
|
||||
component:()=>import('../views/EditArticle.vue')
|
||||
},
|
||||
{
|
||||
name:'profile',
|
||||
path:'/profile',
|
||||
component:()=>import('../views/profile/Index.vue')
|
||||
|
||||
}
|
||||
]
|
||||
},
|
||||
{ path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
|
||||
]
|
||||
})
|
||||
|
||||
export default router
|
||||
22
Yi.BBS.Vue3/src/stores/config.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import {getAll} from '@/apis/configApi'
|
||||
import { defineStore } from 'pinia'
|
||||
const useConfigStore = defineStore('config',
|
||||
{
|
||||
state: () => ({
|
||||
data: []
|
||||
}),
|
||||
getters: {
|
||||
name:(state)=>state.data.filter(s=> s.configKey=='bbs.site.name').map(x=>x.configValue)[0],
|
||||
author:(state)=>state.data.filter(s=> s.configKey=='bbs.site.author').map(x=>x.configValue)[0],
|
||||
icp:(state)=>state.data.filter(s=> s.configKey=='bbs.site.icp').map(x=>x.configValue)[0],
|
||||
bottom:(state)=>state.data.filter(s=>s.configKey=='bbs.site.bottom').map(x=>x.configValue)[0]
|
||||
},
|
||||
actions: {
|
||||
// 登录
|
||||
async getConfig() {
|
||||
const response = await getAll();
|
||||
this.data = response.data.items;
|
||||
},
|
||||
},
|
||||
})
|
||||
export default useConfigStore;
|
||||
98
Yi.BBS.Vue3/src/stores/user.js
Normal file
@@ -0,0 +1,98 @@
|
||||
import { login, logout, getInfo,register } from '@/apis/accountApi'
|
||||
import { getToken, setToken, removeToken } from '@/utils/auth'
|
||||
import { defineStore } from 'pinia'
|
||||
const useUserStore = defineStore('user',
|
||||
{
|
||||
state: () => ({
|
||||
id:'',
|
||||
token: getToken(),
|
||||
name: '游客',
|
||||
userName:'',
|
||||
icon: null,
|
||||
roles: [],
|
||||
permissions: []
|
||||
}),
|
||||
getters: {
|
||||
},
|
||||
actions: {
|
||||
// 登录
|
||||
login(userInfo) {
|
||||
const userName = userInfo.userName.trim()
|
||||
const password = userInfo.password
|
||||
const code = userInfo.code
|
||||
const uuid = userInfo.uuid
|
||||
return new Promise((resolve, reject) => {
|
||||
login(userName, password, code, uuid).then(response => {
|
||||
const res=response.data;
|
||||
setToken(res.token);
|
||||
this.token = res.token;
|
||||
resolve(response);
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
// 获取用户信息
|
||||
getInfo() {
|
||||
return new Promise((resolve, reject) => {
|
||||
getInfo().then(response => {
|
||||
const res=response.data;
|
||||
const user = res.user
|
||||
const avatar = (user.icon == "" || user.icon == null) ? "/src/assets/logo.ico" : import.meta.env.VITE_APP_BASEAPI + "/file/"+user.icon;
|
||||
|
||||
if (res.roleCodes && res.roleCodes.length > 0) { // 验证返回的roles是否是一个非空数组
|
||||
this.roles = res.roleCodes
|
||||
this.permissions = res.permissionCodes
|
||||
// this.roles = ["admin"];
|
||||
// this.permissions=["*:*:*"]
|
||||
|
||||
} else {
|
||||
this.roles = ['ROLE_DEFAULT']
|
||||
}
|
||||
// this.roles = ["admin"];
|
||||
// this.permissions=["*:*:*"]
|
||||
this.name = user.nick
|
||||
this.icon = avatar;
|
||||
|
||||
this.userName=user.userName;
|
||||
this.id=user.id;
|
||||
resolve(res)
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
// 退出系统
|
||||
logOut() {
|
||||
return new Promise((resolve, reject) => {
|
||||
logout().then(() => {
|
||||
this.token = ''
|
||||
this.roles = []
|
||||
this.permissions = []
|
||||
removeToken()
|
||||
resolve()
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
// 注册
|
||||
register(userInfo) {
|
||||
const userName = userInfo.userName.trim()
|
||||
const password = userInfo.password.trim()
|
||||
const phone = userInfo.phone;
|
||||
const uuid = userInfo.uuid;
|
||||
const code=userInfo.code;
|
||||
return new Promise((resolve, reject) => {
|
||||
register(userName,password,phone,code,uuid).then(response => {
|
||||
resolve(response);
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
},
|
||||
})
|
||||
export default useUserStore;
|
||||
|
||||
13
Yi.BBS.Vue3/src/utils/auth.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const TokenKey = 'Admin-Token'
|
||||
|
||||
export function getToken() {
|
||||
return localStorage.getItem(TokenKey)
|
||||
}
|
||||
|
||||
export function setToken(token) {
|
||||
return localStorage.setItem(TokenKey, token)
|
||||
}
|
||||
|
||||
export function removeToken() {
|
||||
return localStorage.removeItem (TokenKey)
|
||||
}
|
||||
81
Yi.BBS.Vue3/src/utils/hljsData.js
Normal file
@@ -0,0 +1,81 @@
|
||||
export default {
|
||||
'agate': 1,
|
||||
'androidstudio': 1,
|
||||
'arduino-light': 1,
|
||||
'arta': 1,
|
||||
'ascetic': 1,
|
||||
'atelier-cave-dark': 1,
|
||||
'atelier-cave-light': 1,
|
||||
'atelier-dune-dark': 1,
|
||||
'atelier-dune-light': 1,
|
||||
'atelier-estuary-dark': 1,
|
||||
'atelier-estuary-light': 1,
|
||||
'atelier-forest-dark': 1,
|
||||
'atelier-forest-light': 1,
|
||||
'atelier-heath-dark': 1,
|
||||
'atelier-heath-light': 1,
|
||||
'atelier-lakeside-dark': 1,
|
||||
'atelier-lakeside-light': 1,
|
||||
'atelier-plateau-dark': 1,
|
||||
'atelier-plateau-light': 1,
|
||||
'atelier-savanna-dark': 1,
|
||||
'atelier-savanna-light': 1,
|
||||
'atelier-seaside-dark': 1,
|
||||
'atelier-seaside-light': 1,
|
||||
'atelier-sulphurpool-dark': 1,
|
||||
'atelier-sulphurpool-light': 1,
|
||||
'atom-one-dark': 1,
|
||||
'atom-one-light': 1,
|
||||
'brown-paper': 1,
|
||||
'codepen-embed': 1,
|
||||
'color-brewer': 1,
|
||||
'darcula': 1,
|
||||
'dark': 1,
|
||||
'darkula': 1,
|
||||
'default': 1,
|
||||
'docco': 1,
|
||||
'dracula': 1,
|
||||
'far': 1,
|
||||
'foundation': 1,
|
||||
'github-gist': 1,
|
||||
'github': 1,
|
||||
'googlecode': 1,
|
||||
'grayscale': 1,
|
||||
'gruvbox-dark': 1,
|
||||
'gruvbox-light': 1,
|
||||
'hopscotch': 1,
|
||||
'hybrid': 1,
|
||||
'idea': 1,
|
||||
'ir-black': 1,
|
||||
'kimbie.dark': 1,
|
||||
'kimbie.light': 1,
|
||||
'magula': 1,
|
||||
'mono-blue': 1,
|
||||
'monokai-sublime': 1,
|
||||
'monokai': 1,
|
||||
'obsidian': 1,
|
||||
'ocean': 1,
|
||||
'paraiso-dark': 1,
|
||||
'paraiso-light': 1,
|
||||
'pojoaque': 1,
|
||||
'purebasic': 1,
|
||||
'qtcreator_dark': 1,
|
||||
'qtcreator_light': 1,
|
||||
'railscasts': 1,
|
||||
'rainbow': 1,
|
||||
'routeros': 1,
|
||||
'school-book': 1,
|
||||
'solarized-dark': 1,
|
||||
'solarized-light': 1,
|
||||
'sunburst': 1,
|
||||
'tomorrow-night-blue': 1,
|
||||
'tomorrow-night-bright': 1,
|
||||
'tomorrow-night-eighties': 1,
|
||||
'tomorrow-night': 1,
|
||||
'tomorrow': 1,
|
||||
'vs': 1,
|
||||
'vs2015': 1,
|
||||
'xcode': 1,
|
||||
'xt256': 1,
|
||||
'zenburn': 1
|
||||
};
|
||||
59
Yi.BBS.Vue3/src/utils/request.js
Normal file
@@ -0,0 +1,59 @@
|
||||
|
||||
import axios from 'axios';
|
||||
import { getToken } from '@/utils/auth'
|
||||
export let isRelogin = { show: false };
|
||||
// import JsonBig from 'json-bigint'
|
||||
const myaxios = axios.create({
|
||||
baseURL:import.meta.env.VITE_APP_BASEAPI,
|
||||
timeout: 50000,
|
||||
// transformResponse: [data => {
|
||||
// try {
|
||||
// const json = JsonBig({
|
||||
// storeAsString: true
|
||||
// })
|
||||
// return json.parse(data)
|
||||
// } catch (err) {
|
||||
// // 如果转换失败,则包装为统一数据格式并返回
|
||||
// return {
|
||||
// data
|
||||
// }
|
||||
// }
|
||||
// }],
|
||||
})
|
||||
|
||||
|
||||
|
||||
// 请求拦截器
|
||||
myaxios.interceptors.request.use(function (config) {
|
||||
if (getToken()) {
|
||||
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
|
||||
}
|
||||
|
||||
return config;
|
||||
}, function (error) {
|
||||
return Promise.reject(error);
|
||||
});
|
||||
|
||||
// 响应拦截器
|
||||
myaxios.interceptors.response.use(function (response) {
|
||||
|
||||
return response;
|
||||
}, function (error) {
|
||||
const response=error.response.data;
|
||||
//业务异常+应用异常,统一处理
|
||||
switch(response.code)
|
||||
{
|
||||
case 401:
|
||||
ElMessage.error('登录已过期')
|
||||
break;
|
||||
case 403:
|
||||
ElMessage.error(response.message)
|
||||
break;
|
||||
case 500:
|
||||
ElMessage.error(response.message)
|
||||
break;
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
});
|
||||
export default myaxios
|
||||
386
Yi.BBS.Vue3/src/views/Article.vue
Normal file
@@ -0,0 +1,386 @@
|
||||
<template>
|
||||
<div style="width: 90%; min-width: 1200px">
|
||||
<!-- <div style="width: 1200px;"> -->
|
||||
<el-row :gutter="20" class="top-div">
|
||||
<el-col :span="5">
|
||||
<el-row class="art-info-left">
|
||||
<el-col :span="24">
|
||||
<InfoCard header="文章信息" text="展开" hideDivider="true">
|
||||
<template #content>
|
||||
<el-button style="width: 100%; margin-bottom: 0.8rem" @click="loadDiscuss(true)">首页</el-button>
|
||||
<el-button v-hasPer="['bbs:article:add']" @click="addArticle(0)" type="primary"
|
||||
style="width: 100%; margin-bottom: 0.8rem; margin-left: 0">添加子文章</el-button>
|
||||
<!--目录在这里 -->
|
||||
<TreeArticleInfo :data="articleData" @remove="delArticle" @update="updateArticle" @create="addNextArticle"
|
||||
@handleNodeClick="handleNodeClick" :currentNodeKey="currentNodeKey"/>
|
||||
</template>
|
||||
</InfoCard>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<InfoCard :items="items" header="作者分享" text="更多">
|
||||
<template #item="temp">
|
||||
<AvatarInfo />
|
||||
</template>
|
||||
</InfoCard>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<InfoCard :items="items" header="内容推荐" text="更多">
|
||||
<template #item="temp">
|
||||
<AvatarInfo />
|
||||
</template>
|
||||
</InfoCard>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="14">
|
||||
<el-row class="left-div">
|
||||
<el-col :span="24">
|
||||
<!-- {{ discuss.user }} -->
|
||||
<AvatarInfo :size="50" :showWatching="true" :time="discuss.creationTime" :userInfo="discuss.user"></AvatarInfo>
|
||||
<!-- :userInfo="{nick:'qwe'} -->
|
||||
<el-divider />
|
||||
<h2>{{ discuss.title }}</h2>
|
||||
<el-image :preview-src-list="[getUrl(discuss.cover)]" v-if="discuss.cover" :src="getUrl(discuss.cover)" style="width: 150px;height: 150px;" />
|
||||
|
||||
|
||||
<ArticleContentInfo :code="discuss.content??''"></ArticleContentInfo>
|
||||
|
||||
<el-divider class="tab-divider" />
|
||||
|
||||
<el-space :size="10" :spacer="spacer">
|
||||
<AgreeInfo :data="discuss"/>
|
||||
<el-button icon="Star" text> 0</el-button>
|
||||
<el-button icon="Share" text> 分享</el-button>
|
||||
<el-button icon="Operation" text> 操作</el-button>
|
||||
</el-space>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24" class="comment">
|
||||
|
||||
<CommentInfo/>
|
||||
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
<BottomInfo/>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="5">
|
||||
<el-row class="right-div">
|
||||
<el-col :span="24">
|
||||
<InfoCard class="art-info-right" header="主题信息" text="更多" hideDivider="true">
|
||||
<template #content>
|
||||
<div>
|
||||
<ul class="art-info-ul">
|
||||
|
||||
<li>
|
||||
<el-button type="primary" size="default"
|
||||
v-hasPer="['bbs:discuss:edit']"
|
||||
@click="updateHander(route.params.discussId)">编辑</el-button>
|
||||
<el-button style="margin-left: 1rem" type="danger"
|
||||
v-hasPer="['bbs:discuss:remove']"
|
||||
@click="delHander(route.params.discussId)">删除</el-button>
|
||||
</li>
|
||||
<li>分类: <span>主题</span></li>
|
||||
标签:
|
||||
<el-tag type="success">主题</el-tag>
|
||||
<el-tag type="info">资源</el-tag>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
</InfoCard>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<InfoCard class="art-info-right" header="目录" hideDivider="true">
|
||||
<template #content>
|
||||
<div>
|
||||
<el-empty :image-size="100" style="padding: 20px 0;" v-if="catalogueData.length==0" description="无目录" />
|
||||
<ul v-else class="art-info-ul">
|
||||
<li v-for="(item, i) in catalogueData" :key="i">
|
||||
<el-button style="width: 100%; justify-content: left" type="primary" text>{{ `${i + 1} : ${item}`
|
||||
}}</el-button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
</InfoCard>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<InfoCard :items="items" header="其他" text="更多">
|
||||
<template #item="temp">
|
||||
<AvatarInfo />
|
||||
</template>
|
||||
</InfoCard>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<InfoCard :items="items" header="其他" text="更多">
|
||||
<template #item="temp">
|
||||
<AvatarInfo />
|
||||
</template>
|
||||
</InfoCard>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { h, ref, onMounted } from "vue";
|
||||
import AvatarInfo from "@/components/AvatarInfo.vue";
|
||||
import InfoCard from "@/components/InfoCard.vue";
|
||||
import ArticleContentInfo from "@/components/ArticleContentInfo.vue";
|
||||
import CommentInfo from "@/components/CommentInfo.vue";
|
||||
import BottomInfo from '@/components/BottomInfo.vue'
|
||||
import TreeArticleInfo from "@/components/TreeArticleInfo.vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import AgreeInfo from '@/components/AgreeInfo.vue'
|
||||
import { get as discussGet, del as discussDel } from "@/apis/discussApi.js";
|
||||
import { all as articleall, del as articleDel, get as articleGet } from "@/apis/articleApi.js";
|
||||
//数据定义
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const spacer = h(ElDivider, { direction: "vertical" });
|
||||
const items = [{ user: "用户1" }, { user: "用户2" }, { user: "用户3" }];
|
||||
|
||||
|
||||
//子文章数据
|
||||
const articleData = ref([]);
|
||||
//主题内容
|
||||
const discuss = ref({});
|
||||
//封面url
|
||||
const getUrl= (str)=>{
|
||||
return `${import.meta.env.VITE_APP_BASEAPI}/file/${str}`
|
||||
}
|
||||
|
||||
//当前默认选择的子文章
|
||||
const currentNodeKey=route.params.articleId;
|
||||
//目录数据
|
||||
const catalogueData = ref([]);
|
||||
|
||||
//子文章初始化
|
||||
const loadArticleData = async () => {
|
||||
const response= await articleall(route.params.discussId)
|
||||
articleData.value = response.data;
|
||||
}
|
||||
|
||||
//主题初始化
|
||||
const loadDiscuss = async (isRewrite) => {
|
||||
|
||||
if (isRewrite) {
|
||||
//跳转路由
|
||||
router.push(`/article/${route.params.discussId}`);
|
||||
}
|
||||
discuss.value = (await discussGet(route.params.discussId)).data;
|
||||
if (route.params.articleId != "") {
|
||||
const response = await articleGet(route.params.articleId);
|
||||
discuss.value.content = response.data.content;
|
||||
}
|
||||
ContentHander();
|
||||
};
|
||||
//加载文章及目录
|
||||
const ContentHander = () => {
|
||||
//加载目录
|
||||
var reg = /(#{1,6})\s(.*)/g;
|
||||
|
||||
if(discuss.value.content)
|
||||
{
|
||||
var myArray = discuss.value.content.match(reg);
|
||||
if (myArray != null) {
|
||||
catalogueData.value = myArray.map((x) => {
|
||||
return x.replace(/#/g, "").replace(/\s/g, "");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
//添加树型子文章
|
||||
const addArticle = (parentArticleId) => {
|
||||
//跳转路由
|
||||
var routerPer = {
|
||||
path: "/editArt",
|
||||
query: {
|
||||
operType: "create",
|
||||
artType: "article",
|
||||
discussId: route.params.discussId,
|
||||
parentArticleId: parentArticleId,
|
||||
},
|
||||
};
|
||||
router.push(routerPer);
|
||||
};
|
||||
|
||||
//删除主题
|
||||
const delHander = async (ids) => {
|
||||
ElMessageBox.confirm(`确定是否删除编号[${ids}]的主题吗?`, "警告", {
|
||||
confirmButtonText: "确认",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(async () => {
|
||||
const response = await discussDel(ids);
|
||||
//删除成功后,跳转到主页
|
||||
router.push("/index");
|
||||
ElMessage({
|
||||
type: "success",
|
||||
message: "删除成功",
|
||||
});
|
||||
});
|
||||
};
|
||||
//更新操作
|
||||
const updateHander = (discussId) => {
|
||||
//跳转路由
|
||||
var routerPer = {
|
||||
path: "/editArt",
|
||||
query: {
|
||||
operType: "update",
|
||||
artType: "discuss",
|
||||
discussId: discussId,
|
||||
},
|
||||
};
|
||||
router.push(routerPer);
|
||||
};
|
||||
|
||||
//跳转添加子菜单
|
||||
const addNextArticle = (node, data) => {
|
||||
//跳转路由
|
||||
var routerPer = {
|
||||
path: "/editArt",
|
||||
query: {
|
||||
operType: "create",
|
||||
artType: "article",
|
||||
discussId: data.discussId,
|
||||
parentArticleId: data.id,
|
||||
},
|
||||
};
|
||||
router.push(routerPer);
|
||||
}
|
||||
|
||||
//跳转更新子菜单
|
||||
const updateArticle = (node, data) => {
|
||||
//跳转路由
|
||||
var routerPer = {
|
||||
path: "/editArt",
|
||||
query: {
|
||||
operType: "update",
|
||||
artType: "article",
|
||||
discussId: data.discussId,
|
||||
parentArticleId: data.parentId,
|
||||
articleId: data.id
|
||||
},
|
||||
};
|
||||
router.push(routerPer);
|
||||
}
|
||||
//单机节点
|
||||
const handleNodeClick = async(data) => {
|
||||
|
||||
//跳转路由
|
||||
|
||||
router.push(`/article/${route.params.discussId}/${data.id}`);
|
||||
|
||||
const response=await articleGet(data.id);
|
||||
discuss.value.content = response.data.content;
|
||||
ContentHander();
|
||||
}
|
||||
//删除子文章
|
||||
const delArticle = (node, data) => {
|
||||
ElMessageBox.confirm(`确定是否删除编号[${data.id}]的子文章吗?`, "警告", {
|
||||
confirmButtonText: "确认",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(async () => {
|
||||
|
||||
await articleDel(data.id);
|
||||
await loadArticleData();
|
||||
|
||||
ElMessage({
|
||||
type: "success",
|
||||
message: "删除成功",
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
onMounted(async () => {
|
||||
await loadDiscuss();
|
||||
await loadArticleData();
|
||||
});
|
||||
</script>
|
||||
<style scoped >
|
||||
.comment {
|
||||
min-height: 40rem;
|
||||
}
|
||||
|
||||
.art-info-left .el-col {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.art-info-ul span {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.art-info-ul .el-tag {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.art-info-ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.art-info-right {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.left-div .el-col {
|
||||
background-color: #ffffff;
|
||||
min-height: 12rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.right-div .el-col {
|
||||
background-color: #ffffff;
|
||||
min-height: 10rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.left-top-div .el-col {
|
||||
min-height: 2rem;
|
||||
background-color: #fafafa;
|
||||
margin-bottom: 1rem;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.top-div {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.left-top-div {
|
||||
font-size: small;
|
||||
text-align: center;
|
||||
line-height: 2rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-bottom: 0.5em;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
font-weight: 600;
|
||||
font-size: 30px;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.left-div .el-col {
|
||||
padding: 1.4rem 1.4rem 0.5rem 1.4rem;
|
||||
}
|
||||
|
||||
.el-space {
|
||||
display: flex;
|
||||
vertical-align: top;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.tab-divider {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
221
Yi.BBS.Vue3/src/views/Discuss.vue
Normal file
@@ -0,0 +1,221 @@
|
||||
<template>
|
||||
<div style="width: 1200px;" class="body-div">
|
||||
<div class="header">
|
||||
<el-form :inline="true" >
|
||||
<el-form-item label="标题:" >
|
||||
<el-input v-model="query.title" placeholder="请输入标题"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="标签:">
|
||||
<el-input
|
||||
placeholder="搜索当下分类下的标签"
|
||||
|
||||
/>
|
||||
</el-form-item>
|
||||
<div class="form-right">
|
||||
|
||||
<el-button>重置</el-button>
|
||||
<el-button type="primary" @click="async()=>{ await loadDiscussList();}">查询</el-button>
|
||||
<el-button @click="enterEditArticle" type="primary" v-hasPer="['bbs:discuss:add']">分享</el-button>
|
||||
<el-dropdown>
|
||||
<span class="el-dropdown-link">
|
||||
展开
|
||||
<el-icon class="el-icon--right">
|
||||
<arrow-down />
|
||||
</el-icon>
|
||||
</span>
|
||||
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item>Action 1</el-dropdown-item>
|
||||
<el-dropdown-item>Action 2</el-dropdown-item>
|
||||
<el-dropdown-item>Action 3</el-dropdown-item>
|
||||
<el-dropdown-item disabled>Action 4</el-dropdown-item>
|
||||
<el-dropdown-item divided>Action 5</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
|
||||
<el-tabs v-model="activeName" @tab-change="handleClick">
|
||||
<el-tab-pane label="最新" name="new"> </el-tab-pane>
|
||||
<el-tab-pane label="推荐" name="suggest"> </el-tab-pane>
|
||||
<el-tab-pane label="最热" name="host"> </el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<el-collapse class="collapse-list" style="background-color: #F0F2F5;" >
|
||||
<el-collapse-item >
|
||||
<template #title>
|
||||
<div class="collapse-top">
|
||||
已置顶主题<el-icon class="header-icon">
|
||||
<info-filled />
|
||||
</el-icon>
|
||||
</div>
|
||||
</template>
|
||||
<div class="div-item" v-for="i in topDiscussList" >
|
||||
<DisscussCard :discuss="i" badge="置顶"/>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
<el-divider v-show="topDiscussList.length>0" />
|
||||
|
||||
<div class="div-item" v-for="i in discussList" >
|
||||
<DisscussCard :discuss="i"/>
|
||||
</div>
|
||||
<div>
|
||||
<el-pagination
|
||||
v-model:current-page="query.pageNum"
|
||||
v-model:page-size="query.pageSize"
|
||||
:page-sizes="[10, 20, 30, 50]"
|
||||
:background="true"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="total"
|
||||
@size-change="async(val)=>{ await loadDiscussList();}"
|
||||
@current-change="async(val)=>{ await loadDiscussList();}"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<el-empty v-if="discussList.length==0" description="空空如也" />
|
||||
<BottomInfo/>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
|
||||
<script setup>
|
||||
import DisscussCard from '@/components/DisscussCard.vue'
|
||||
import {getList,getTopList} from '@/apis/discussApi.js'
|
||||
import { onMounted, ref,reactive } from 'vue'
|
||||
import { useRoute,useRouter } from 'vue-router'
|
||||
import BottomInfo from '@/components/BottomInfo.vue'
|
||||
//数据定义
|
||||
const route=useRoute()
|
||||
const router=useRouter()
|
||||
const activeName = ref('new')
|
||||
//主题内容
|
||||
const discussList=ref([]);
|
||||
//置顶主题内容
|
||||
const topDiscussList = ref([]);
|
||||
const total=ref(100)
|
||||
const query=reactive({
|
||||
pageNum:1,
|
||||
pageSize:10,
|
||||
title:'',
|
||||
plateId:route.params.plateId,
|
||||
type:activeName.value
|
||||
})
|
||||
|
||||
const handleClick =async (tab, event) => {
|
||||
query.type=activeName.value ;
|
||||
await loadDiscussList();
|
||||
}
|
||||
|
||||
onMounted(async()=>{
|
||||
|
||||
if(route.query.q !=undefined)
|
||||
{
|
||||
query.title=route.query.q??''
|
||||
router.push("/discuss")
|
||||
}
|
||||
await loadDiscussList();
|
||||
})
|
||||
|
||||
//加载discuss
|
||||
const loadDiscussList=async()=>{
|
||||
const response= await getList(query);
|
||||
discussList.value=response.data.items;
|
||||
total.value=Number( response.data.total);
|
||||
|
||||
//全查,无需参数
|
||||
const topResponse=await getTopList();
|
||||
topDiscussList.value=topResponse.data.items;
|
||||
}
|
||||
|
||||
//进入添加主题页面
|
||||
const enterEditArticle=()=>{
|
||||
//跳转路由
|
||||
var routerPer= { path: '/editArt', query: {
|
||||
operType: 'create',
|
||||
artType:'discuss',
|
||||
plateId:route.params.plateId,
|
||||
}}
|
||||
router.push(routerPer);
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.el-pagination
|
||||
{margin: 2rem 0rem 2rem 0rem;justify-content: right;}
|
||||
.body-div{
|
||||
min-height: 1000px;
|
||||
}
|
||||
.el-dropdown-link {
|
||||
cursor: pointer;
|
||||
color: var(--el-color-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.header{
|
||||
background-color: #FFFFFF;
|
||||
padding: 1rem;
|
||||
margin: 1rem 0rem ;
|
||||
}
|
||||
.collapse-top
|
||||
{
|
||||
padding-left: 2rem;
|
||||
}
|
||||
.header .el-input
|
||||
{
|
||||
|
||||
}
|
||||
.el-tabs
|
||||
{
|
||||
background-color: #FFFFFF;
|
||||
padding-left: 2rem;
|
||||
}
|
||||
.el-tabs >>> .el-tabs__header
|
||||
{
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.div-item
|
||||
{
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.el-form {
|
||||
--el-form-label-font-size: var(--el-font-size-base);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.el-form-item
|
||||
{padding-top: 0.8rem;}
|
||||
.form-right
|
||||
{
|
||||
align-items: center;
|
||||
display: flex;
|
||||
margin-left: auto;
|
||||
}
|
||||
.form-right .el-button
|
||||
{
|
||||
margin-right: 0.6rem;
|
||||
}
|
||||
.header .el-input
|
||||
{
|
||||
width:20rem;
|
||||
}
|
||||
.collapse-list >>> .el-collapse-item__header {
|
||||
border-bottom-color: #F0F2F5 !important;
|
||||
}
|
||||
|
||||
.el-divider
|
||||
{
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
</style>
|
||||
294
Yi.BBS.Vue3/src/views/EditArticle.vue
Normal file
@@ -0,0 +1,294 @@
|
||||
<template>
|
||||
<div style="width: 100%">
|
||||
<div class="body-div">
|
||||
<el-form label-width="120px" :model="editForm" label-position="left" :rules="rules" ref="ruleFormRef">
|
||||
<el-form-item label="类型:">
|
||||
<el-radio-group v-model="radio">
|
||||
<el-radio-button label="discuss">主题</el-radio-button>
|
||||
<el-radio-button label="article">文章</el-radio-button>
|
||||
<el-radio-button label="plate">板块</el-radio-button>
|
||||
<el-radio-button label="orther">其他</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="权限:" v-if="route.query.artType == 'discuss'">
|
||||
<el-radio-group v-model="perRadio">
|
||||
<el-radio-button label="Public">公开</el-radio-button>
|
||||
<el-radio-button label="Oneself">仅自己可见</el-radio-button>
|
||||
<el-radio-button label="User">部分用户可见</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="可见用户:" v-if="route.query.artType == 'discuss' && perRadio=='User'">
|
||||
<UserSelectInfo v-model="editForm.permissionUserIds"/>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
|
||||
<el-form-item v-if="route.query.artType == 'article'" label="子文章名称:" prop="name">
|
||||
<el-input placeholder="请输入" v-model="editForm.name" />
|
||||
</el-form-item>
|
||||
<el-form-item v-else label="标题:" prop="title">
|
||||
<el-input placeholder="请输入" v-model="editForm.title" />
|
||||
</el-form-item>
|
||||
<el-form-item label="描述:" prop="introduction">
|
||||
<el-input placeholder="请输入" v-model="editForm.introduction" />
|
||||
</el-form-item>
|
||||
<el-form-item label="内容:" prop="content">
|
||||
<MavonEdit height="30rem" v-model="editForm.content" :codeStyle="codeStyle" />
|
||||
</el-form-item>
|
||||
<el-form-item label="封面:" v-if="route.query.artType == 'discuss'">
|
||||
|
||||
<!-- 主题封面选择 -->
|
||||
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
:action="fileUploadUrl"
|
||||
:show-file-list="false"
|
||||
:on-success="onSuccess"
|
||||
>
|
||||
<el-image v-if="dialogImageUrl" :src="getUrl(dialogImageUrl)" style="width: 178px;height: 178px;" class="avatar" />
|
||||
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
|
||||
</el-upload>
|
||||
|
||||
|
||||
</el-form-item>
|
||||
<el-form-item label="标签:" prop="types">
|
||||
<el-input placeholder="请输入" v-model="editForm.types" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="submit(ruleFormRef)" class="submit-btn" type="primary">提交</el-button></el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import MavonEdit from "@/components/MavonEdit.vue";
|
||||
import UserSelectInfo from '@/components/UserSelectInfo.vue'
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
|
||||
import {
|
||||
add as discussAdd,
|
||||
update as discussUpdate,
|
||||
get as discussGet,
|
||||
} from "@/apis/discussApi.js";
|
||||
|
||||
import {
|
||||
add as articleAdd,
|
||||
update as articleUpdate,
|
||||
get as articleGet,
|
||||
} from "@/apis/articleApi.js";
|
||||
|
||||
//数据定义
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const perRadio=ref("Public");
|
||||
const radio = ref(route.query.artType);
|
||||
const codeStyle = "atom-one-dark";
|
||||
|
||||
//封面完整显示的url
|
||||
const fileUploadUrl=`${import.meta.env.VITE_APP_BASEAPI}/file`
|
||||
//封面的url
|
||||
const dialogImageUrl = ref('')
|
||||
|
||||
//文件上传成功后
|
||||
const onSuccess=(response)=>{
|
||||
dialogImageUrl.value=response[0].id
|
||||
}
|
||||
//封面url
|
||||
const getUrl= (str)=>{
|
||||
return `${import.meta.env.VITE_APP_BASEAPI}/file/${str}`
|
||||
}
|
||||
|
||||
|
||||
//整个页面上的表单
|
||||
const editForm = reactive({
|
||||
title: "",
|
||||
types: "",
|
||||
introduction: "",
|
||||
content: "",
|
||||
name: "",
|
||||
permissionUserIds:[]
|
||||
});
|
||||
|
||||
//组装主题内容: 需要更新主题信息
|
||||
const discuss = {
|
||||
|
||||
};
|
||||
|
||||
//组装文章内容:需要添加的文章信息
|
||||
const article = {
|
||||
};
|
||||
|
||||
//定义效验规则
|
||||
const ruleFormRef = ref(null);
|
||||
const rules = reactive({
|
||||
title: [
|
||||
{ required: true, message: "请输入标题", trigger: "blur" },
|
||||
{ min: 3, max: 40, message: "长度 3 到 20", trigger: "blur" },
|
||||
],
|
||||
name: [
|
||||
{ required: true, message: "请输入子文章名称", trigger: "blur" },
|
||||
],
|
||||
content: [
|
||||
{ required: true, message: "请输入内容", trigger: "blur" },
|
||||
{ min: 10, message: "长度 大于 10", trigger: "blur" },
|
||||
],
|
||||
});
|
||||
//提交按钮,需要区分操作类型
|
||||
const submit = async (formEl) => {
|
||||
if (!formEl) return;
|
||||
await formEl.validate(async (valid, fields) => {
|
||||
if (valid) {
|
||||
//dicuss主题处理
|
||||
if (route.query.artType == "discuss") {
|
||||
|
||||
|
||||
discuss.title = editForm.title;
|
||||
discuss.types = editForm.types;
|
||||
discuss.introduction = editForm.introduction;
|
||||
discuss.content = editForm.content;
|
||||
discuss.plateId = discuss.plateId ?? route.query.plateId
|
||||
discuss.cover=dialogImageUrl.value;
|
||||
discuss.permissionType=perRadio.value;
|
||||
|
||||
discuss.permissionUserIds=editForm.permissionUserIds;
|
||||
//主题创建
|
||||
if (route.query.operType == "create") {
|
||||
const response = await discussAdd(discuss);
|
||||
|
||||
ElMessage({
|
||||
message: `[${discuss.title}]主题创建成功!`,
|
||||
type: 'success',
|
||||
})
|
||||
var routerPer = { path: `/article/${response.data.id}` };
|
||||
router.push(routerPer);
|
||||
}
|
||||
//主题更新
|
||||
else if (route.query.operType == "update") {
|
||||
await discussUpdate(route.query.discussId, discuss);
|
||||
|
||||
ElMessage({
|
||||
message: `[${discuss.title}]主题更新成功!`,
|
||||
type: 'success',
|
||||
})
|
||||
var routerPer = { path: `/article/${route.query.discussId}` };
|
||||
router.push(routerPer);
|
||||
}
|
||||
}
|
||||
|
||||
//artcle文章处理
|
||||
else if (route.query.artType == "article") {
|
||||
//组装文章内容:需要添加的文章信息
|
||||
article.content = editForm.content;
|
||||
article.name = editForm.name;
|
||||
article.discussId = route.query.discussId;
|
||||
article.parentId = route.query.parentArticleId
|
||||
//文章创建
|
||||
if (route.query.operType == "create") {
|
||||
const response = await articleAdd(article);
|
||||
ElMessage({
|
||||
message: `[${article.name}]文章创建成功!`,
|
||||
type: 'success',
|
||||
})
|
||||
var routerPer = { path: `/article/${route.query.discussId}/${response.data.id}` };
|
||||
router.push(routerPer);
|
||||
}
|
||||
//文章更新
|
||||
else if (route.query.operType == "update") {
|
||||
await articleUpdate(route.query.articleId, article);
|
||||
ElMessage({
|
||||
message: `[${article.name}]文章更新成功!`,
|
||||
type: 'success',
|
||||
})
|
||||
var routerPer = { path: `/article/${route.query.discussId}/${route.query.articleId}` };
|
||||
router.push(routerPer);
|
||||
}
|
||||
}
|
||||
//添加成功后跳转到该页面
|
||||
// var routerPer = { path: `/discuss/${discuss.plateId}` };
|
||||
// router.push(routerPer);
|
||||
// ruleFormRef.value.resetFields();
|
||||
// discuss.plateId = route.query.plateId;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
//如果是更新操作,需要先查询
|
||||
if (route.query.operType == "update") {
|
||||
|
||||
//更新主题
|
||||
if (route.query.artType == "discuss") {
|
||||
await loadDiscuss();
|
||||
|
||||
//更新文章
|
||||
} else if (route.query.artType == "article") {
|
||||
await loadArticle();
|
||||
}
|
||||
}
|
||||
});
|
||||
//加载主题
|
||||
const loadDiscuss = async () => {
|
||||
const response = await discussGet(route.query.discussId);
|
||||
const res = response.data
|
||||
editForm.content = res.content;
|
||||
editForm.title = res.title;
|
||||
editForm.types = res.types;
|
||||
editForm.introduction = res.introduction;
|
||||
discuss.plateId = res.plateId;
|
||||
dialogImageUrl.value= res.cover;
|
||||
perRadio.value=res.permissionType;
|
||||
editForm.permissionUserIds=res.permissionUserIds;
|
||||
};
|
||||
//加载文章
|
||||
const loadArticle = async () => {
|
||||
const response = await articleGet(route.query.articleId);
|
||||
const res = response.data
|
||||
editForm.content = res.content;
|
||||
editForm.name = res.name;
|
||||
editForm.discussId = res.discussId;
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.submit-btn {
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
.body-div {
|
||||
min-height: 1000px;
|
||||
background-color: #fff;
|
||||
margin: 1.5rem;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.avatar-uploader >>>.el-upload {
|
||||
border: 1px dashed var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
|
||||
}
|
||||
|
||||
.avatar-uploader >>>.el-upload:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-icon.avatar-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
text-align: center;
|
||||
}
|
||||
.el-upload
|
||||
{
|
||||
|
||||
|
||||
|
||||
}
|
||||
</style>
|
||||
175
Yi.BBS.Vue3/src/views/Index.vue
Normal file
@@ -0,0 +1,175 @@
|
||||
<template >
|
||||
<div style="width: 1200px;">
|
||||
<el-row :gutter="20" class="top-div">
|
||||
|
||||
<el-col :span="17">
|
||||
<div class="scrollbar">
|
||||
<ScrollbarInfo />
|
||||
</div>
|
||||
|
||||
|
||||
<el-row class="left-div">
|
||||
<el-col :span="8" v-for="i in plateList" class="plate"
|
||||
:style="{ 'padding-left': i % 3 == 1 ? 0 : 0.2 + 'rem', 'padding-right': i % 3 == 0 ? 0 : 0.2 + 'rem' }">
|
||||
<PlateCard :name="i.name" :introduction="i.introduction" :id="i.id" />
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24" v-for="i in discussList">
|
||||
<DisscussCard :discuss="i" />
|
||||
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-empty v-show="discussList.length <= 0" description="推荐位置,空空如也" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
</el-col>
|
||||
|
||||
<el-col :span="7">
|
||||
<el-row class="right-div">
|
||||
<el-col :span="24">
|
||||
|
||||
|
||||
<el-carousel trigger="click" height="150px">
|
||||
<el-carousel-item v-for="item in bannerList">
|
||||
<div class="carousel-font" :style="{ color: item.color }">{{ item.name }}</div>
|
||||
<el-image style="width: 100%; height: 100%" :src="item.logo" fit="cover" />
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24" >
|
||||
<InfoCard header="访问统计" class="VisitsLineChart" text="详情">
|
||||
<template #content>
|
||||
<VisitsLineChart />
|
||||
|
||||
</template>
|
||||
</InfoCard>
|
||||
</el-col>
|
||||
|
||||
|
||||
|
||||
<el-col :span="24">
|
||||
<InfoCard header="简介" text="详情">
|
||||
<template #content>
|
||||
<div class="introduce">
|
||||
|
||||
没有什么能够阻挡,人类对代码<span style="color: #1890ff;">优雅</span>的追求
|
||||
</div>
|
||||
|
||||
</template>
|
||||
</InfoCard>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24">
|
||||
<InfoCard :items=items header="本月排行" text="更多">
|
||||
<template #item="temp">
|
||||
<AvatarInfo>
|
||||
<template #bottom>
|
||||
本月积分:680
|
||||
</template>
|
||||
|
||||
</AvatarInfo>
|
||||
</template>
|
||||
</InfoCard>
|
||||
</el-col>
|
||||
|
||||
|
||||
<el-col :span="24">
|
||||
<InfoCard :items=items header="推荐好友" text="更多">
|
||||
<template #item="temp">
|
||||
<AvatarInfo />
|
||||
</template>
|
||||
</InfoCard>
|
||||
</el-col>
|
||||
|
||||
|
||||
<el-col :span="24" style=" background: transparent;">
|
||||
<BottomInfo />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import DisscussCard from '@/components/DisscussCard.vue'
|
||||
import InfoCard from '@/components/InfoCard.vue'
|
||||
import PlateCard from '@/components/PlateCard.vue'
|
||||
import ScrollbarInfo from '@/components/ScrollbarInfo.vue'
|
||||
import AvatarInfo from '@/components/AvatarInfo.vue'
|
||||
import BottomInfo from '@/components/BottomInfo.vue'
|
||||
import VisitsLineChart from '@/components/echars/VisitsLineChart.vue'
|
||||
|
||||
import { getList } from '@/apis/plateApi.js'
|
||||
import { getList as bannerGetList } from '@/apis/bannerApi.js'
|
||||
import { getList as discussGetList } from '@/apis/discussApi.js'
|
||||
import { onMounted, ref, reactive } from 'vue'
|
||||
var plateList = ref([]);
|
||||
var discussList = ref([]);
|
||||
var bannerList = ref([]);
|
||||
|
||||
const items = [{ user: "用户1" }, { user: "用户2" }, { user: "用户3" }]
|
||||
//主题查询参数
|
||||
const query = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
isTop: true
|
||||
});
|
||||
|
||||
//初始化
|
||||
onMounted(async () => {
|
||||
plateList.value = (await getList()).data.items;
|
||||
discussList.value = (await discussGetList(query)).data.items;
|
||||
bannerList.value = (await bannerGetList()).data.items
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
<style scoped >
|
||||
.introduce {
|
||||
color: rgba(0, 0, 0, .45);
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
.plate {
|
||||
background: transparent !important;
|
||||
|
||||
}
|
||||
|
||||
.left-div .el-col {
|
||||
background-color: #FFFFFF;
|
||||
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.right-div .el-col {
|
||||
background-color: #FFFFFF;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.carousel-font {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 10%;
|
||||
left: 10%;
|
||||
}
|
||||
|
||||
|
||||
.top-div {
|
||||
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
|
||||
.scrollbar {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.VisitsLineChart >>> .el-card__body
|
||||
{
|
||||
|
||||
padding: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
101
Yi.BBS.Vue3/src/views/Login.vue
Normal file
@@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<div class="login-wrapper">
|
||||
<h1>{{configStore.name}}-登录</h1>
|
||||
<div class="login-form">
|
||||
|
||||
<el-form ref="loginFormRef" v-model="loginForm" :rules="rules" >
|
||||
|
||||
<div class="username form-item">
|
||||
<el-form-item prop="userName">
|
||||
<span>使用账号</span>
|
||||
<input type="text" class="input-item" v-model="loginForm.userName">
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<div class="password form-item">
|
||||
<el-form-item prop="password">
|
||||
<span>密码</span>
|
||||
<input type="password" class="input-item" v-model="loginForm.password">
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
<RouterLink to="/register" > 没有账号?前往注册</RouterLink>
|
||||
<button class="login-btn" @click="login">登 录</button>
|
||||
<button class="login-btn" @click="guestlogin">游客临时登录</button>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="divider">
|
||||
<span class="line"></span>
|
||||
<span class="divider-text">其他方式登录</span>
|
||||
<span class="line"></span>
|
||||
</div>
|
||||
|
||||
<div class="other-login-wrapper">
|
||||
<div class="other-login-item">
|
||||
<img src="@/assets/login_images/QQ.png" alt="">
|
||||
</div>
|
||||
<div class="other-login-item">
|
||||
<img src="@/assets/login_images/WeChat.png" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <h2> 登录-欢迎</h2>
|
||||
<el-input v-model="loginForm.userName" placeholder="用户名" />
|
||||
<el-input v-model="loginForm.password" placeholder="密码" show-password />
|
||||
<el-button class="login-btn" type="primary" @click="login">登录</el-button>
|
||||
<br>
|
||||
<el-button class="login-btn" type="primary" @click="guestlogin">游客临时登录</el-button> -->
|
||||
</template>
|
||||
<script setup>
|
||||
import { reactive } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import useUserStore from '@/stores/user.js'
|
||||
import useConfigStore from "@/stores/config";
|
||||
const configStore= useConfigStore();
|
||||
const userStore = useUserStore();
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
||||
const rules = reactive({
|
||||
userName: [
|
||||
{ required: true, message: '请输入账号名', trigger: 'blur' },
|
||||
{ min: 3, message: '至少大于等于3位', trigger: 'blur' },
|
||||
],
|
||||
password:[
|
||||
{ required: true, message: '请输入密码', trigger: 'blur' },
|
||||
{ min: 3, message: '至少大于等于6位', trigger: 'blur' },
|
||||
|
||||
]})
|
||||
const loginForm = reactive({
|
||||
userName: "",
|
||||
password: "",
|
||||
uuid: "",
|
||||
code: ""
|
||||
})
|
||||
const guestlogin = async () => {
|
||||
loginForm.userName = "guest";
|
||||
loginForm.password = "123456"
|
||||
await userStore.login(loginForm);
|
||||
const redirect = route.query?.redirect ?? '/index'
|
||||
router.push(redirect)
|
||||
}
|
||||
const login = async () => {
|
||||
const response = await userStore.login(loginForm).catch((e) => {
|
||||
loginForm.password = "";
|
||||
});
|
||||
if (response!=undefined) {
|
||||
ElMessage({
|
||||
message: `您好${loginForm.userName},登录成功!`,
|
||||
type: 'success',
|
||||
})
|
||||
|
||||
const redirect = route.query?.redirect ?? '/index'
|
||||
router.push(redirect)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
<style src="@/assets/styles/login.scss" scoped></style>
|
||||
4
Yi.BBS.Vue3/src/views/NotFound.vue
Normal file
@@ -0,0 +1,4 @@
|
||||
<template>
|
||||
404
|
||||
|
||||
</template>
|
||||
108
Yi.BBS.Vue3/src/views/Register.vue
Normal file
@@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<div class="login-wrapper">
|
||||
<h1>{{configStore.name}}-注册</h1>
|
||||
<div class="login-form">
|
||||
<div class="username form-item">
|
||||
<span>登录账号</span>
|
||||
<input type="text" class="input-item" v-model="registerForm.userName">
|
||||
</div>
|
||||
<div class="username form-item">
|
||||
<span>手机号</span>
|
||||
<input style="width: 70%;" type="text" class="input-item" v-model="registerForm.phone">
|
||||
<button v-if="!isSendCaptcha" style="width: 30%;background-color: #C14949;" class="login-btn" @click="captcha" >验证码</button>
|
||||
<button v-else style="width: 30%;background-color:#F0F2F5;" class="login-btn" >已发送</button>
|
||||
</div>
|
||||
<div class="username form-item" v-show="isSendCaptcha">
|
||||
<span>手机短信验证码</span>
|
||||
<input type="text" class="input-item" v-model="registerForm.code">
|
||||
</div>
|
||||
|
||||
|
||||
<div class="password form-item">
|
||||
<span>密码</span>
|
||||
<input type="password" class="input-item" v-model="registerForm.password">
|
||||
</div>
|
||||
<div class="password form-item">
|
||||
<span>确认密码</span>
|
||||
<input type="password" class="input-item" v-model="passwordConfirm">
|
||||
</div>
|
||||
<RouterLink to="/login" > 已有账号,前往登录</RouterLink>
|
||||
<button class="login-btn" @click="register">注册</button>
|
||||
</div>
|
||||
<div class="divider">
|
||||
<span class="line"></span>
|
||||
<span class="divider-text">其他方式注册</span>
|
||||
<span class="line"></span>
|
||||
</div>
|
||||
<div class="other-login-wrapper">
|
||||
<div class="other-login-item">
|
||||
<img src="@/assets/login_images/QQ.png" alt="">
|
||||
</div>
|
||||
<div class="other-login-item">
|
||||
<img src="@/assets/login_images/WeChat.png" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { reactive ,ref} from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import {getCodePhone} from '@/apis/accountApi'
|
||||
import useUserStore from '@/stores/user.js'
|
||||
import useConfigStore from "@/stores/config";
|
||||
const configStore= useConfigStore();
|
||||
const userStore = useUserStore();
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const passwordConfirm=ref('');
|
||||
const registerForm = reactive({
|
||||
userName: "",
|
||||
password: "",
|
||||
uuid: "",
|
||||
code: "",
|
||||
phone:""
|
||||
})
|
||||
|
||||
const isSendCaptcha=ref(false)
|
||||
|
||||
//验证码
|
||||
const captcha=async()=>{
|
||||
isSendCaptcha.value=true;
|
||||
const response= await getCodePhone(registerForm.phone);
|
||||
ElMessage({
|
||||
message: `已向${registerForm.phone}发送验证码,请注意查收`,
|
||||
type: 'success',
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
|
||||
const register = async () => {
|
||||
|
||||
if(registerForm.password!=passwordConfirm.value)
|
||||
{
|
||||
ElMessage.error('两次密码输入不一致')
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const response = await userStore.register(registerForm).catch((e) => {
|
||||
registerForm.password="";
|
||||
passwordConfirm.value="";
|
||||
});
|
||||
|
||||
//成功
|
||||
if (response!=undefined) {
|
||||
ElMessage({
|
||||
message: `恭喜!${registerForm.userName},注册成功!请登录!`,
|
||||
type: 'success',
|
||||
})
|
||||
|
||||
const redirect = route.query?.redirect ?? '/login'
|
||||
router.push(redirect)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
<style src="@/assets/styles/login.scss" scoped></style>
|
||||
1
Yi.BBS.Vue3/src/views/Test.vue
Normal file
@@ -0,0 +1 @@
|
||||
<template></template>
|
||||
85
Yi.BBS.Vue3/src/views/error/401.vue
Normal file
@@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<div class="errPage-container">
|
||||
<el-button icon="arrow-left" class="pan-back-btn" @click="back">
|
||||
返回
|
||||
</el-button>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<h1 class="text-jumbo text-ginormous">
|
||||
401错误!
|
||||
</h1>
|
||||
<h2>您没有访问权限!</h2>
|
||||
<h6>对不起,您没有访问权限,请不要进行非法操作!您可以返回主页面</h6>
|
||||
<ul class="list-unstyled">
|
||||
<li class="link-type">
|
||||
<router-link to="/">
|
||||
回首页
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<img :src="errGif" width="313" height="428" alt="Girl has dropped her ice cream.">
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
import errImage from "@/assets/401_images/401.gif";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
let { proxy } = getCurrentInstance();
|
||||
const router=useRouter();
|
||||
|
||||
const errGif = ref(errImage + "?" + +new Date());
|
||||
|
||||
function back() {
|
||||
if (proxy.$route.query.noGoBack) {
|
||||
router.push({ path: "/" });
|
||||
} else {
|
||||
router.go(-1);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.errPage-container {
|
||||
width: 800px;
|
||||
max-width: 100%;
|
||||
margin: 100px auto;
|
||||
.pan-back-btn {
|
||||
background: #008489;
|
||||
color: #fff;
|
||||
border: none !important;
|
||||
}
|
||||
.pan-gif {
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
.pan-img {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
}
|
||||
.text-jumbo {
|
||||
font-size: 60px;
|
||||
font-weight: 700;
|
||||
color: #484848;
|
||||
}
|
||||
.list-unstyled {
|
||||
font-size: 14px;
|
||||
li {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
a {
|
||||
color: #008489;
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
228
Yi.BBS.Vue3/src/views/error/404.vue
Normal file
@@ -0,0 +1,228 @@
|
||||
<template>
|
||||
<div class="wscn-http404-container">
|
||||
<div class="wscn-http404">
|
||||
<div class="pic-404">
|
||||
<img class="pic-404__parent" src="@/assets/404_images/404.png" alt="404">
|
||||
<img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404">
|
||||
<img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404">
|
||||
<img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404">
|
||||
</div>
|
||||
<div class="bullshit">
|
||||
<div class="bullshit__oops">
|
||||
404错误!
|
||||
</div>
|
||||
<div class="bullshit__headline">
|
||||
{{ message }}
|
||||
</div>
|
||||
<div class="bullshit__info">
|
||||
对不起,您正在寻找的页面不存在。尝试检查URL的错误,然后按浏览器上的刷新按钮或尝试在我们的应用程序中找到其他内容。
|
||||
</div>
|
||||
<router-link to="/index" class="bullshit__return-home">
|
||||
返回首页
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {computed} from 'vue'
|
||||
let message = computed(() => {
|
||||
return '找不到网页!'
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wscn-http404-container{
|
||||
transform: translate(-50%,-50%);
|
||||
position: absolute;
|
||||
top: 40%;
|
||||
left: 50%;
|
||||
}
|
||||
.wscn-http404 {
|
||||
position: relative;
|
||||
width: 1200px;
|
||||
padding: 0 50px;
|
||||
overflow: hidden;
|
||||
.pic-404 {
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 600px;
|
||||
overflow: hidden;
|
||||
&__parent {
|
||||
width: 100%;
|
||||
}
|
||||
&__child {
|
||||
position: absolute;
|
||||
&.left {
|
||||
width: 80px;
|
||||
top: 17px;
|
||||
left: 220px;
|
||||
opacity: 0;
|
||||
animation-name: cloudLeft;
|
||||
animation-duration: 2s;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
animation-delay: 1s;
|
||||
}
|
||||
&.mid {
|
||||
width: 46px;
|
||||
top: 10px;
|
||||
left: 420px;
|
||||
opacity: 0;
|
||||
animation-name: cloudMid;
|
||||
animation-duration: 2s;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
animation-delay: 1.2s;
|
||||
}
|
||||
&.right {
|
||||
width: 62px;
|
||||
top: 100px;
|
||||
left: 500px;
|
||||
opacity: 0;
|
||||
animation-name: cloudRight;
|
||||
animation-duration: 2s;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
animation-delay: 1s;
|
||||
}
|
||||
@keyframes cloudLeft {
|
||||
0% {
|
||||
top: 17px;
|
||||
left: 220px;
|
||||
opacity: 0;
|
||||
}
|
||||
20% {
|
||||
top: 33px;
|
||||
left: 188px;
|
||||
opacity: 1;
|
||||
}
|
||||
80% {
|
||||
top: 81px;
|
||||
left: 92px;
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
top: 97px;
|
||||
left: 60px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@keyframes cloudMid {
|
||||
0% {
|
||||
top: 10px;
|
||||
left: 420px;
|
||||
opacity: 0;
|
||||
}
|
||||
20% {
|
||||
top: 40px;
|
||||
left: 360px;
|
||||
opacity: 1;
|
||||
}
|
||||
70% {
|
||||
top: 130px;
|
||||
left: 180px;
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
top: 160px;
|
||||
left: 120px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@keyframes cloudRight {
|
||||
0% {
|
||||
top: 100px;
|
||||
left: 500px;
|
||||
opacity: 0;
|
||||
}
|
||||
20% {
|
||||
top: 120px;
|
||||
left: 460px;
|
||||
opacity: 1;
|
||||
}
|
||||
80% {
|
||||
top: 180px;
|
||||
left: 340px;
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
top: 200px;
|
||||
left: 300px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.bullshit {
|
||||
position: relative;
|
||||
float: left;
|
||||
width: 300px;
|
||||
padding: 30px 0;
|
||||
overflow: hidden;
|
||||
&__oops {
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
line-height: 40px;
|
||||
color: #1482f0;
|
||||
opacity: 0;
|
||||
margin-bottom: 20px;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
&__headline {
|
||||
font-size: 20px;
|
||||
line-height: 24px;
|
||||
color: #222;
|
||||
font-weight: bold;
|
||||
opacity: 0;
|
||||
margin-bottom: 10px;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-delay: 0.1s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
&__info {
|
||||
font-size: 13px;
|
||||
line-height: 21px;
|
||||
color: grey;
|
||||
opacity: 0;
|
||||
margin-bottom: 30px;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-delay: 0.2s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
&__return-home {
|
||||
display: block;
|
||||
float: left;
|
||||
width: 110px;
|
||||
height: 36px;
|
||||
background: #1482f0;
|
||||
border-radius: 100px;
|
||||
text-align: center;
|
||||
color: #ffffff;
|
||||
opacity: 0;
|
||||
font-size: 14px;
|
||||
line-height: 36px;
|
||||
cursor: pointer;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-delay: 0.3s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
@keyframes slideUp {
|
||||
0% {
|
||||
transform: translateY(60px);
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
124
Yi.BBS.Vue3/src/views/profile/Index.vue
Normal file
@@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<div class="app-container" style="width: 1400px;">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6" :xs="24">
|
||||
<el-card class="box-card">
|
||||
<template v-slot:header>
|
||||
<div class="clearfix">
|
||||
<span>个人信息</span>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<div class="text-center">
|
||||
<userAvatar :user="state.user" />
|
||||
</div>
|
||||
<ul class="list-group list-group-striped">
|
||||
<li class="list-group-item">
|
||||
<el-icon><User /></el-icon> 用户名称
|
||||
<div class="pull-right">{{ state.user.userName }}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<el-icon><Phone /></el-icon> 手机号码
|
||||
<div class="pull-right">{{ state.user.phone }}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<el-icon><Message /></el-icon> 用户邮箱
|
||||
<div class="pull-right">{{ state.user.email }}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<el-icon><HelpFilled /></el-icon> 所属部门
|
||||
<div class="pull-right" v-if="state.dept">{{ state.dept.deptName }}</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<el-icon><Avatar /></el-icon> 所属角色
|
||||
<div class="pull-right"><span v-for="role in state.roles" :key="role.id">{{ role.roleName }} /</span></div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<el-icon><Stopwatch /></el-icon> 创建日期
|
||||
<div class="pull-right">{{ state.user.creationTime }}</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="18" :xs="24">
|
||||
<el-card>
|
||||
<template v-slot:header>
|
||||
<div class="clearfix">
|
||||
<span>基本资料</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-tabs v-model="activeTab">
|
||||
<el-tab-pane label="基本资料" name="userinfo">
|
||||
<userInfo :user="state.user" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="修改密码" name="resetPwd">
|
||||
<resetPwd />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Profile">
|
||||
import userAvatar from "./UserAvatar.vue";
|
||||
import userInfo from "./UserInfo.vue";
|
||||
import resetPwd from "./ResetPwd.vue";
|
||||
import { getUserProfile } from "@/apis/userApi.js";
|
||||
import { onMounted ,ref,reactive} from "vue";
|
||||
|
||||
const activeTab = ref("userinfo");
|
||||
const state = reactive({
|
||||
user: {},
|
||||
dept: {},
|
||||
roles: [],
|
||||
roleGroup: {},
|
||||
postGroup: {}
|
||||
});
|
||||
|
||||
function getUser() {
|
||||
getUserProfile().then(response => {
|
||||
const res=response.data;
|
||||
state.user = res.user;
|
||||
state.dept=res.dept;
|
||||
state.roles=res.roles;
|
||||
state.roleGroup = res.roleGroup;
|
||||
state.postGroup = res.postGroup;
|
||||
});
|
||||
};
|
||||
onMounted(()=>{
|
||||
getUser();
|
||||
})
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
.pull-right {
|
||||
float: right !important;
|
||||
}
|
||||
.list-group-striped > .list-group-item {
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
border-radius: 0;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.list-group {
|
||||
padding-left: 0px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
border-bottom: 1px solid #e7eaec;
|
||||
border-top: 1px solid #e7eaec;
|
||||
margin-bottom: -1px;
|
||||
padding: 11px 0px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.app-container {
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
66
Yi.BBS.Vue3/src/views/profile/ResetPwd.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<el-form ref="pwdRef" :model="user" :rules="rules" label-width="80px">
|
||||
<el-form-item label="旧密码" prop="oldPassword">
|
||||
<el-input v-model="user.oldPassword" placeholder="请输入旧密码" type="password" show-password />
|
||||
</el-form-item>
|
||||
<el-form-item label="新密码" prop="newPassword">
|
||||
<el-input v-model="user.newPassword" placeholder="请输入新密码" type="password" show-password />
|
||||
</el-form-item>
|
||||
<el-form-item label="确认密码" prop="confirmPassword">
|
||||
<el-input v-model="user.confirmPassword" placeholder="请确认新密码" type="password" show-password/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submit">保存</el-button>
|
||||
<el-button type="danger" @click="close">关闭</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { updateUserPwd } from "@/apis/userApi";
|
||||
import { ref,reactive } from "vue";
|
||||
|
||||
const pwdRef=ref(null);
|
||||
|
||||
const user = reactive({
|
||||
oldPassword: undefined,
|
||||
newPassword: undefined,
|
||||
confirmPassword: undefined
|
||||
});
|
||||
|
||||
const equalToPassword = (rule, value, callback) => {
|
||||
if (user.newPassword !== value) {
|
||||
callback(new Error("两次输入的密码不一致"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
const rules = ref({
|
||||
oldPassword: [{ required: true, message: "旧密码不能为空", trigger: "blur" }],
|
||||
newPassword: [{ required: true, message: "新密码不能为空", trigger: "blur" }, { min: 6, max: 20, message: "长度在 6 到 20 个字符", trigger: "blur" }],
|
||||
confirmPassword: [{ required: true, message: "确认密码不能为空", trigger: "blur" }, { required: true, validator: equalToPassword, trigger: "blur" }]
|
||||
});
|
||||
|
||||
/** 提交按钮 */
|
||||
function submit() {
|
||||
pwdRef.value.validate(async valid => {
|
||||
if (valid) {
|
||||
const response=await updateUserPwd(user.oldPassword, user.newPassword)
|
||||
|
||||
if(response.status==200)
|
||||
{
|
||||
ElMessage({
|
||||
type: "success",
|
||||
message: "更新密码成功",
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
};
|
||||
/** 关闭按钮 */
|
||||
function close() {
|
||||
|
||||
// proxy.$tab.closePage();
|
||||
};
|
||||
</script>
|
||||
187
Yi.BBS.Vue3/src/views/profile/UserAvatar.vue
Normal file
@@ -0,0 +1,187 @@
|
||||
<template>
|
||||
<div class="user-info-head" @click="editCropper()"><img :src="options.img" title="点击上传头像" class="img-circle img-lg" />
|
||||
</div>
|
||||
<el-dialog :title="title" v-model="open" width="800px" append-to-body @opened="modalOpened" @close="closeDialog">
|
||||
<el-row>
|
||||
<el-col :xs="24" :md="12" :style="{ height: '350px' }">
|
||||
<vue-cropper ref="cropper" :img="options.img" :info="true" :autoCrop="options.autoCrop"
|
||||
:autoCropWidth="options.autoCropWidth" :autoCropHeight="options.autoCropHeight" :fixedBox="options.fixedBox"
|
||||
@realTime="realTime" v-if="visible" />
|
||||
</el-col>
|
||||
<el-col :xs="24" :md="12" :style="{ height: '350px' }">
|
||||
<div class="avatar-upload-preview">
|
||||
<img :src="options.previews.url" :style="options.previews.img" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<br />
|
||||
<el-row>
|
||||
<el-col :lg="2" :md="2">
|
||||
<el-upload action="#" :http-request="requestUpload" :show-file-list="false" :before-upload="beforeUpload">
|
||||
<el-button>
|
||||
选择
|
||||
<el-icon class="el-icon--right">
|
||||
<Upload />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</el-upload>
|
||||
</el-col>
|
||||
<el-col :lg="{ span: 1, offset: 2 }" :md="2">
|
||||
<el-button icon="Plus" @click="changeScale(1)"></el-button>
|
||||
</el-col>
|
||||
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
|
||||
<el-button icon="Minus" @click="changeScale(-1)"></el-button>
|
||||
</el-col>
|
||||
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
|
||||
<el-button icon="RefreshLeft" @click="rotateLeft()"></el-button>
|
||||
</el-col>
|
||||
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
|
||||
<el-button icon="RefreshRight" @click="rotateRight()"></el-button>
|
||||
</el-col>
|
||||
<el-col :lg="{ span: 2, offset: 6 }" :md="2">
|
||||
<el-button type="primary" @click="uploadImg()">上传</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import "vue-cropper/dist/index.css";
|
||||
import { VueCropper } from "vue-cropper";
|
||||
import { upload } from "@/apis/fileApi";
|
||||
import { updateUserIcon } from "@/apis/userApi";
|
||||
import useUserStore from '@/stores/user'
|
||||
import { ref, reactive } from "vue";
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
const cropper = ref(null);
|
||||
|
||||
const open = ref(false);
|
||||
const visible = ref(false);
|
||||
const title = ref("修改头像");
|
||||
|
||||
//图片裁剪数据
|
||||
const options = reactive({
|
||||
img: userStore.icon, // 裁剪图片的地址
|
||||
autoCrop: true, // 是否默认生成截图框
|
||||
autoCropWidth: 200, // 默认生成截图框宽度
|
||||
autoCropHeight: 200, // 默认生成截图框高度
|
||||
fixedBox: true, // 固定截图框大小 不允许改变
|
||||
previews: {} //预览数据
|
||||
});
|
||||
|
||||
/** 编辑头像 */
|
||||
function editCropper() {
|
||||
open.value = true;
|
||||
};
|
||||
/** 打开弹出层结束时的回调 */
|
||||
function modalOpened() {
|
||||
visible.value = true;
|
||||
};
|
||||
/** 覆盖默认上传行为 */
|
||||
function requestUpload() {
|
||||
};
|
||||
/** 向左旋转 */
|
||||
function rotateLeft() {
|
||||
cropper.value.rotateLeft();
|
||||
};
|
||||
/** 向右旋转 */
|
||||
function rotateRight() {
|
||||
cropper.value.rotateRight();
|
||||
};
|
||||
/** 图片缩放 */
|
||||
function changeScale(num) {
|
||||
num = num || 1;
|
||||
cropper.value.changeScale(num);
|
||||
};
|
||||
/** 上传预处理 */
|
||||
function beforeUpload(file) {
|
||||
if (file.type.indexOf("image/") == -1) {
|
||||
ElMessage.error("文件格式错误,请上传图片类型,如:JPG,PNG后缀的文件。")
|
||||
} else {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => {
|
||||
options.img = reader.result;
|
||||
};
|
||||
|
||||
}
|
||||
};
|
||||
/** 上传图片 */
|
||||
async function uploadImg() {
|
||||
await cropper.value.getCropBlob(async data => {
|
||||
let formData = new FormData();
|
||||
formData.append("file", data);
|
||||
const response = await upload(formData)
|
||||
open.value = false;
|
||||
options.img = import.meta.env.VITE_APP_BASEAPI + "/file/" + response.data[0].id;
|
||||
userStore.icon = options.img;
|
||||
const iconResponse= await updateUserIcon(response.data[0].id);
|
||||
if(iconResponse.status==200)
|
||||
{
|
||||
ElMessage({
|
||||
type: "success",
|
||||
message: "头像更新成功",
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
/** 实时预览 */
|
||||
function realTime(data) {
|
||||
options.previews = data;
|
||||
};
|
||||
/** 关闭窗口 */
|
||||
function closeDialog() {
|
||||
options.img = userStore.icon;
|
||||
options.visible = false;
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.user-info-head {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.user-info-head:hover:after {
|
||||
content: "+";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
color: #eee;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
font-size: 24px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
cursor: pointer;
|
||||
line-height: 110px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.img-circle {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.img-lg {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.avatar-upload-preview {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translate(50%, -50%);
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 4px #ccc;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
66
Yi.BBS.Vue3/src/views/profile/UserInfo.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<el-form ref="userRef" :model="user" :rules="rules" label-width="80px">
|
||||
<el-form-item label="账号" prop="userName">
|
||||
<el-input v-model="user.userName" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="用户昵称" prop="nick">
|
||||
<el-input v-model="user.nick" maxlength="30" />
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="phone">
|
||||
<el-input v-model="user.phone" maxlength="11" />
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input v-model="user.email" maxlength="50" />
|
||||
</el-form-item>
|
||||
<el-form-item label="性别">
|
||||
<el-radio-group v-model="user.sex">
|
||||
<el-radio :label="'Male'">男</el-radio>
|
||||
<el-radio :label="'Woman'">女</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submit">保存</el-button>
|
||||
<el-button type="danger" @click="close">关闭</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { updateUserProfile } from "@/apis/userApi";
|
||||
import useUserStore from "@/stores/user"
|
||||
import { ref } from "vue";
|
||||
const userStore = useUserStore();
|
||||
const props = defineProps({
|
||||
user: {
|
||||
type: Object
|
||||
}
|
||||
});
|
||||
|
||||
const userRef = ref(null);
|
||||
|
||||
const rules = ref({
|
||||
nick: [{ required: true, message: "用户昵称不能为空", trigger: "blur" }],
|
||||
email: [{ required: true, message: "邮箱地址不能为空", trigger: "blur" }, { type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] }],
|
||||
phone: [{ required: true, message: "手机号码不能为空", trigger: "blur" }, { pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur" }],
|
||||
});
|
||||
|
||||
/** 提交按钮 */
|
||||
function submit() {
|
||||
userRef.value.validate(async valid => {
|
||||
if (valid) {
|
||||
const response = await updateUserProfile(props.user);
|
||||
if (response.status == 200) {
|
||||
ElMessage({
|
||||
type: "success",
|
||||
message: "用户信息修改成功",
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
/** 关闭按钮 */
|
||||
function close() {
|
||||
// proxy.$tab.closePage();
|
||||
};
|
||||
</script>
|
||||
45
Yi.BBS.Vue3/vite.config.js
Normal file
@@ -0,0 +1,45 @@
|
||||
import { fileURLToPath, URL } from 'node:url'
|
||||
|
||||
import { defineConfig, loadEnv } from 'vite';
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import AutoImport from 'unplugin-auto-import/vite'
|
||||
import Components from 'unplugin-vue-components/vite'
|
||||
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
|
||||
|
||||
var CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
/** @type {import('vite').UserConfig} */
|
||||
export default defineConfig(({ command, mode })=>{
|
||||
const env = loadEnv(mode, process.cwd(), '')
|
||||
return {
|
||||
// envDir: 'env',
|
||||
plugins: [
|
||||
vue(),
|
||||
AutoImport({
|
||||
resolvers: [ElementPlusResolver()],
|
||||
}),
|
||||
Components({
|
||||
resolvers: [ElementPlusResolver()],
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
}
|
||||
},
|
||||
server:{
|
||||
port:18001,
|
||||
open:true,
|
||||
proxy:{
|
||||
|
||||
[env.VITE_APP_BASEAPI]: {
|
||||
target: env.VITE_APP_URL,
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/api-dev/, ''),
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
**/.classpath
|
||||
**/.dockerignore
|
||||
**/.env
|
||||
**/.git
|
||||
**/.gitignore
|
||||
**/.project
|
||||
**/.settings
|
||||
**/.toolstarget
|
||||
**/.vs
|
||||
**/.vscode
|
||||
**/*.*proj.user
|
||||
**/*.dbmdl
|
||||
**/*.jfm
|
||||
**/azds.yaml
|
||||
**/bin
|
||||
**/charts
|
||||
**/docker-compose*
|
||||
**/Dockerfile*
|
||||
**/node_modules
|
||||
**/npm-debug.log
|
||||
**/obj
|
||||
**/secrets.dev.yaml
|
||||
**/values.dev.yaml
|
||||
LICENSE
|
||||
README.md
|
||||
@@ -1,4 +0,0 @@
|
||||
[*.cs]
|
||||
|
||||
# SYSLIB0014: 类型或成员已过时
|
||||
dotnet_diagnostic.SYSLIB0014.severity = none
|
||||
3
Yi.Framework.Net6/Build.props
Normal file
@@ -0,0 +1,3 @@
|
||||
<Project>
|
||||
|
||||
</Project>
|
||||
90
Yi.Framework.Net6/Version.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyCompany("YiFramework")]
|
||||
[assembly: AssemblyFileVersion(VersionInfo.FullVersion)]
|
||||
[assembly: AssemblyInformationalVersion(VersionInfo.FullVersion)]
|
||||
[assembly: AssemblyVersion(VersionInfo.FullVersion)]
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
#pragma warning disable IDE1006 // 命名样式
|
||||
internal static class VersionInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 主版本: 项目进行了重大修改,导致不再向下兼容,主版本号加1,其它版本号清0;
|
||||
/// </summary>
|
||||
public const string Major = "1";
|
||||
|
||||
/// <summary>
|
||||
/// 次版本:项目进行了重构,增加了功能,但向下兼容,子版本号加1,主版本号不变, 修正版本号清0;
|
||||
/// </summary>
|
||||
public const string Minor = "1";
|
||||
|
||||
/// <summary>
|
||||
/// 生成号
|
||||
/// </summary>
|
||||
public const string Build = "0";
|
||||
|
||||
/// <summary>
|
||||
/// 修订号:项目进行了Bug修复,向下兼容,修正版本号加1,主版本号、子版本号不 变;
|
||||
/// </summary>
|
||||
public const string Revision = "0";
|
||||
|
||||
/// <summary>
|
||||
/// 版本名称
|
||||
/// </summary>
|
||||
public const string VersionName = null;
|
||||
|
||||
/// <summary>
|
||||
/// 版本
|
||||
/// </summary>
|
||||
public const string FullVersion = Major + "." + Minor + "." + Build + "." + Revision;
|
||||
#pragma warning disable CS1570 // XML 注释出现 XML 格式错误
|
||||
/// <summary>
|
||||
/// 比较 a,b两个版本
|
||||
/// 当 a > b 时,返回 1
|
||||
/// 当 a = b 时,返回 0
|
||||
/// 当 a < b 时,返回 -1
|
||||
/// </summary>
|
||||
#pragma warning restore CS1570 // XML 注释出现 XML 格式错误
|
||||
public static int Compare(this Version a, Version b)
|
||||
|
||||
{
|
||||
// 比较主版本
|
||||
if (a.Major != b.Major)
|
||||
{
|
||||
return a.Major > b.Major ? 1 : -1;
|
||||
}
|
||||
|
||||
// 比较次版本
|
||||
if (a.Minor != b.Minor)
|
||||
{
|
||||
return a.Minor > b.Minor ? 1 : -1;
|
||||
}
|
||||
|
||||
// 比较生成号
|
||||
if (a.Build < 0 || b.Build < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (a.Build != b.Build)
|
||||
{
|
||||
return a.Build > b.Build ? 1 : -1;
|
||||
}
|
||||
|
||||
// 比较修订号
|
||||
if (a.Revision < 0 || b.Revision < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (a.Revision != b.Revision)
|
||||
{
|
||||
return a.Revision > b.Revision ? 1 : -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#pragma warning restore IDE1006 // 命名样式
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<log4net>
|
||||
<!-- 将日志以回滚文件的形式写到文件中 -->
|
||||
<!-- 按日期切分日志文件,并将日期作为日志文件的名字 -->
|
||||
<!--Error-->
|
||||
<appender name="ErrorLog" type="log4net.Appender.RollingFileAppender">
|
||||
<!--不加utf-8编码格式,中文字符将显示成乱码-->
|
||||
<param name="Encoding" value="utf-8" />
|
||||
<file value="log/"/>
|
||||
<appendToFile value="true" />
|
||||
<rollingStyle value="Date" />
|
||||
<!--日期的格式,每天换一个文件记录,如不设置则永远只记录一天的日志,需设置-->
|
||||
<datePattern value=""GlobalExceptionLogs_"yyyyMMdd".log"" />
|
||||
<!--日志文件名是否为静态-->
|
||||
<StaticLogFileName value="false"/>
|
||||
<!--多线程时采用最小锁定-->
|
||||
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
|
||||
<!--布局(向用户显示最后经过格式化的输出信息)-->
|
||||
<layout type="log4net.Layout.PatternLayout">
|
||||
<conversionPattern value="%date| %-5level %newline%message%newline--------------------------------%newline" />
|
||||
</layout>
|
||||
<filter type="log4net.Filter.LevelRangeFilter">
|
||||
<levelMin value="ERROR" />
|
||||
<levelMax value="FATAL" />
|
||||
</filter>
|
||||
</appender>
|
||||
<!--Error-->
|
||||
|
||||
|
||||
|
||||
<!--Info-->
|
||||
<appender name="InfoLog" type="log4net.Appender.RollingFileAppender">
|
||||
<!--不加utf-8编码格式,中文字符将显示成乱码-->
|
||||
<param name="Encoding" value="utf-8" />
|
||||
<!--定义文件存放位置-->
|
||||
<file value="log/"/>
|
||||
<appendToFile value="true" />
|
||||
<rollingStyle value="Date" />
|
||||
<!--日志文件名是否为静态-->
|
||||
<StaticLogFileName value="false"/>
|
||||
<!--日期的格式,每天换一个文件记录,如不设置则永远只记录一天的日志,需设置-->
|
||||
<datePattern value=""GlobalInfoLogs_"yyyyMMdd".log"" />
|
||||
<!--多线程时采用最小锁定-->
|
||||
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
|
||||
<!--布局(向用户显示最后经过格式化的输出信息)-->
|
||||
<layout type="log4net.Layout.PatternLayout">
|
||||
<conversionPattern value="%date| %-5level%c %newline%message%newline--------------------------------%newline" />
|
||||
</layout>
|
||||
<filter type="log4net.Filter.LevelRangeFilter">
|
||||
<levelMin value="DEBUG" />
|
||||
<levelMax value="WARN" />
|
||||
</filter>
|
||||
</appender>
|
||||
<!--Info-->
|
||||
|
||||
<root>
|
||||
<!-- 控制级别,由低到高:ALL|DEBUG|INFO|WARN|ERROR|FATAL|OFF -->
|
||||
<!-- 比如定义级别为INFO,则INFO级别向下的级别,比如DEBUG日志将不会被记录 -->
|
||||
<!-- 如果没有定义LEVEL的值,则缺省为DEBUG -->
|
||||
<level value="ALL" />
|
||||
<!-- 按日期切分日志文件,并将日期作为日志文件的名字 -->
|
||||
<appender-ref ref="ErrorLog" />
|
||||
<appender-ref ref="InfoLog" />
|
||||
</root>
|
||||
</log4net>
|
||||
@@ -1,185 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<doc>
|
||||
<assembly>
|
||||
<name>Yi.Framework.ApiMicroservice</name>
|
||||
</assembly>
|
||||
<members>
|
||||
<member name="T:Yi.Framework.ApiMicroservice.Controllers.AccountController">
|
||||
<summary>
|
||||
账户管理
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Yi.Framework.ApiMicroservice.Controllers.BaseCrudController`1">
|
||||
<summary>
|
||||
Json To Sql 类比模式,通用模型
|
||||
</summary>
|
||||
<typeparam name="T"></typeparam>
|
||||
</member>
|
||||
<member name="M:Yi.Framework.ApiMicroservice.Controllers.BaseCrudController`1.GetById(System.Int64)">
|
||||
<summary>
|
||||
主键查询
|
||||
</summary>
|
||||
<param name="id"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:Yi.Framework.ApiMicroservice.Controllers.BaseCrudController`1.GetList(Yi.Framework.Model.Query.QueryCondition)">
|
||||
<summary>
|
||||
列表查询
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:Yi.Framework.ApiMicroservice.Controllers.BaseCrudController`1.PageList(Yi.Framework.Model.Query.QueryPageCondition)">
|
||||
<summary>
|
||||
条件分页查询
|
||||
</summary>
|
||||
<param name="queryCondition"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:Yi.Framework.ApiMicroservice.Controllers.BaseCrudController`1.Add(`0)">
|
||||
<summary>
|
||||
添加
|
||||
</summary>
|
||||
<param name="entity"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:Yi.Framework.ApiMicroservice.Controllers.BaseCrudController`1.Update(`0)">
|
||||
<summary>
|
||||
修改
|
||||
</summary>
|
||||
<param name="entity"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:Yi.Framework.ApiMicroservice.Controllers.BaseCrudController`1.DeleteList(System.Collections.Generic.List{System.Int64})">
|
||||
<summary>
|
||||
列表删除
|
||||
</summary>
|
||||
<param name="ids"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:Yi.Framework.ApiMicroservice.Controllers.LogController.Add">
|
||||
<summary>
|
||||
自动分表,日志添加
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:Yi.Framework.ApiMicroservice.Controllers.LogController.GetList">
|
||||
<summary>
|
||||
查询近20年与21年的日志表
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:Yi.Framework.ApiMicroservice.Controllers.MenuController">
|
||||
<summary>
|
||||
菜单管理
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Yi.Framework.ApiMicroservice.Controllers.MenuController.GetMenuTree">
|
||||
<summary>
|
||||
得到树形菜单
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:Yi.Framework.ApiMicroservice.Controllers.RoleController">
|
||||
<summary>
|
||||
角色管理
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Yi.Framework.ApiMicroservice.Controllers.RoleController.GiveRoleSetMenu(Yi.Framework.DTOModel.GiveRoleSetMenuDto)">
|
||||
<summary>
|
||||
给多用户设置多角色
|
||||
</summary>
|
||||
<param name="giveRoleSetMenuDto"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:Yi.Framework.ApiMicroservice.Controllers.RoleController.GetInMenuByRoleId(System.Int64)">
|
||||
<summary>
|
||||
通过角色id来获取菜单列表
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:Yi.Framework.ApiMicroservice.Controllers.TestController">
|
||||
<summary>
|
||||
测试控制器
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Yi.Framework.ApiMicroservice.Controllers.TestController.DbTest">
|
||||
<summary>
|
||||
仓储上下文对象测试
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:Yi.Framework.ApiMicroservice.Controllers.TestController.SqlTest">
|
||||
<summary>
|
||||
执行Sql返回
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:Yi.Framework.ApiMicroservice.Controllers.TestController.LocalTest">
|
||||
<summary>
|
||||
国际化测试
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:Yi.Framework.ApiMicroservice.Controllers.TestController.PermissionTest">
|
||||
<summary>
|
||||
权限测试
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:Yi.Framework.ApiMicroservice.Controllers.TestController.AuthTest">
|
||||
<summary>
|
||||
策略授权测试
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:Yi.Framework.ApiMicroservice.Controllers.TestController.TranTest">
|
||||
<summary>
|
||||
异步事务测试
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:Yi.Framework.ApiMicroservice.Controllers.TestController.IncludeTest">
|
||||
<summary>
|
||||
极爽导航属性
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:Yi.Framework.ApiMicroservice.Controllers.TestController.JobTest">
|
||||
<summary>
|
||||
启动一个定时任务
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:Yi.Framework.ApiMicroservice.Controllers.TestController.stopJob">
|
||||
<summary>
|
||||
停止任务
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:Yi.Framework.ApiMicroservice.Controllers.UserController">
|
||||
<summary>
|
||||
用户管理
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Yi.Framework.ApiMicroservice.Controllers.UserController.Add(Yi.Framework.Model.Models.UserEntity)">
|
||||
<summary>
|
||||
添加用户,去重,密码加密
|
||||
</summary>
|
||||
<param name="entity"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:Yi.Framework.ApiMicroservice.Controllers.UserController.GiveUserSetRole(Yi.Framework.DTOModel.GiveUserSetRoleDto)">
|
||||
<summary>
|
||||
给多用户设置多角色
|
||||
</summary>
|
||||
<param name="giveUserSetRoleDto"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:Yi.Framework.ApiMicroservice.Controllers.UserController.GetRoleListByUserId(System.Int64)">
|
||||
<summary>
|
||||
通过用户id得到角色列表
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"apollo": {
|
||||
"AppId": "ApiMicroservice",
|
||||
"Env": "DEV",
|
||||
"MetaServer": "http://[xxxx]:18080",
|
||||
"ConfigServer": [ "http://[xxxx]:18080" ]
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Yi.Framework.Common.Models;
|
||||
using Yi.Framework.Core;
|
||||
using Yi.Framework.DTOModel;
|
||||
using Yi.Framework.Interface;
|
||||
using Yi.Framework.Model.Models;
|
||||
using Yi.Framework.Repository;
|
||||
using Yi.Framework.WebCore;
|
||||
using Yi.Framework.WebCore.AttributeExtend;
|
||||
using Yi.Framework.WebCore.AuthorizationPolicy;
|
||||
|
||||
namespace Yi.Framework.ApiMicroservice.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// 账户管理
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/[controller]/[action]")]
|
||||
public class AccountController :ControllerBase
|
||||
{
|
||||
private IUserService _iUserService;
|
||||
private JwtInvoker _jwtInvoker;
|
||||
public AccountController(ILogger<UserEntity> logger, IUserService iUserService, JwtInvoker jwtInvoker)
|
||||
{
|
||||
_iUserService = iUserService;
|
||||
_jwtInvoker = jwtInvoker;
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpPost]
|
||||
public async Task<Result> Login(LoginDto loginDto)
|
||||
{
|
||||
UserEntity user=new();
|
||||
if (await _iUserService.Login(loginDto.UserName, loginDto.Password,o=> user=o))
|
||||
{
|
||||
return Result.Success("登录成功!").SetData(new { user, token = _jwtInvoker.GetAccessToken(user)});
|
||||
}
|
||||
return Result.SuccessError("登录失败!用户名或者密码错误!");
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpPost]
|
||||
public async Task<Result> Register(RegisterDto registerDto)
|
||||
{
|
||||
UserEntity user = new();
|
||||
if (await _iUserService.Register(WebCore.Mapper.MapperHelper.Map<UserEntity, RegisterDto>(registerDto), o => user = o))
|
||||
{
|
||||
return Result.Success("注册成功!").SetData(user);
|
||||
}
|
||||
return Result.SuccessError("注册失败!用户名已存在!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Yi.Framework.Common.Models;
|
||||
using Yi.Framework.Interface;
|
||||
using Yi.Framework.Language;
|
||||
using Yi.Framework.Model.Models;
|
||||
using Yi.Framework.Model.Query;
|
||||
using Yi.Framework.Repository;
|
||||
using Yi.Framework.WebCore.AttributeExtend;
|
||||
|
||||
namespace Yi.Framework.ApiMicroservice.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Json To Sql 类比模式,通用模型
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
[ApiController]
|
||||
public class BaseCrudController<T> : ControllerBase where T : class, IBaseModelEntity,new()
|
||||
{
|
||||
private readonly ILogger<T> _logger;
|
||||
private IBaseService<T> _baseService;
|
||||
private IRepository<T> _repository;
|
||||
public BaseCrudController(ILogger<T> logger, IBaseService<T> iBaseService)
|
||||
{
|
||||
_logger = logger;
|
||||
_baseService = iBaseService;
|
||||
_repository = iBaseService._repository;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 主键查询
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
[Permission($"{nameof(T)}:get:one")]
|
||||
[HttpGet]
|
||||
public virtual async Task<Result> GetById(long id)
|
||||
{
|
||||
return Result.Success().SetData(await _repository.GetByIdAsync(id));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 列表查询
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Permission($"{nameof(T)}:get:list")]
|
||||
[HttpPost]
|
||||
public virtual async Task<Result> GetList(QueryCondition queryCondition)
|
||||
{
|
||||
return Result.Success().SetData(await _repository.GetListAsync(queryCondition));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 条件分页查询
|
||||
/// </summary>
|
||||
/// <param name="queryCondition"></param>
|
||||
/// <returns></returns>
|
||||
[Permission($"{nameof(T)}:get:page")]
|
||||
[HttpPost]
|
||||
public virtual async Task<Result> PageList(QueryPageCondition queryCondition)
|
||||
{
|
||||
return Result.Success().SetData(await _repository.CommonPageAsync(queryCondition));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
[Permission($"{nameof(T)}:add")]
|
||||
[HttpPost]
|
||||
public virtual async Task<Result> Add(T entity)
|
||||
{
|
||||
return Result.Success().SetData(await _repository.InsertReturnSnowflakeIdAsync(entity));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 修改
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
[Permission($"{nameof(T)}:update")]
|
||||
[HttpPut]
|
||||
public virtual async Task<Result> Update(T entity)
|
||||
{
|
||||
return Result.Success().SetStatus(await _repository.UpdateIgnoreNullAsync(entity));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 列表删除
|
||||
/// </summary>
|
||||
/// <param name="ids"></param>
|
||||
/// <returns></returns>
|
||||
[Permission($"{nameof(T)}:delete:list")]
|
||||
[HttpDelete]
|
||||
public virtual async Task<Result> DeleteList(List<long> ids)
|
||||
{
|
||||
return Result.Success().SetStatus(await _repository.DeleteByLogicAsync(ids));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Yi.Framework.Common.Models;
|
||||
using Yi.Framework.Interface;
|
||||
using Yi.Framework.Model.Models;
|
||||
using Yi.Framework.Repository;
|
||||
using Yi.Framework.WebCore;
|
||||
using Yi.Framework.WebCore.AttributeExtend;
|
||||
using Yi.Framework.WebCore.AuthorizationPolicy;
|
||||
|
||||
namespace Yi.Framework.ApiMicroservice.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("api/[controller]/[action]")]
|
||||
public class LogController : ControllerBase
|
||||
{
|
||||
private ILogService _iLogService;
|
||||
//大量日志,将采用自动分表形式,默认1年分一次表
|
||||
public LogController(ILogger<LogEntity> logger, ILogService iLogService)
|
||||
{
|
||||
_iLogService = iLogService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 自动分表,日志添加
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
public async Task<Result> Add()
|
||||
{
|
||||
Random random = new Random();
|
||||
var logList = new List<LogEntity>() {
|
||||
new LogEntity() { LogCreateTime = Convert.ToDateTime("2019-12-1"), Message = "jack"+random.Next() } ,
|
||||
new LogEntity() { LogCreateTime = Convert.ToDateTime("2022-02-1"), Message = "jack"+random.Next() },
|
||||
new LogEntity() { LogCreateTime = Convert.ToDateTime("2020-02-1"), Message = "jack"+random.Next() },
|
||||
new LogEntity() { LogCreateTime = Convert.ToDateTime("2021-12-1"), Message = "jack"+random.Next() } };
|
||||
return Result.Success().SetData(await _iLogService.AddListTest(logList));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询近20年与21年的日志表
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public async Task<Result> GetList()
|
||||
{
|
||||
return Result.Success().SetData(await _iLogService.GetListTest());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Yi.Framework.Common.Models;
|
||||
using Yi.Framework.Interface;
|
||||
using Yi.Framework.Model.Models;
|
||||
using Yi.Framework.Repository;
|
||||
using Yi.Framework.WebCore;
|
||||
using Yi.Framework.WebCore.AttributeExtend;
|
||||
using Yi.Framework.WebCore.AuthorizationPolicy;
|
||||
|
||||
namespace Yi.Framework.ApiMicroservice.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// 菜单管理
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/[controller]/[action]")]
|
||||
public class MenuController : BaseCrudController<MenuEntity>
|
||||
{
|
||||
private IMenuService _iMenuService;
|
||||
public MenuController(ILogger<MenuEntity> logger, IMenuService iMenuService) : base(logger, iMenuService)
|
||||
{
|
||||
_iMenuService = iMenuService;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 得到树形菜单
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
//暂未制作逻辑删除与多租户的过滤
|
||||
public async Task<Result> GetMenuTree()
|
||||
{
|
||||
return Result.Success().SetData(await _iMenuService. GetMenuTreeAsync());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Yi.Framework.Common.Models;
|
||||
using Yi.Framework.DTOModel;
|
||||
using Yi.Framework.Interface;
|
||||
using Yi.Framework.Model.Models;
|
||||
using Yi.Framework.Repository;
|
||||
using Yi.Framework.WebCore;
|
||||
using Yi.Framework.WebCore.AttributeExtend;
|
||||
using Yi.Framework.WebCore.AuthorizationPolicy;
|
||||
|
||||
namespace Yi.Framework.ApiMicroservice.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// 角色管理
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/[controller]/[action]")]
|
||||
public class RoleController : BaseCrudController<RoleEntity>
|
||||
{
|
||||
private IRoleService _iRoleService;
|
||||
public RoleController(ILogger<RoleEntity> logger, IRoleService iRoleService) : base(logger, iRoleService)
|
||||
{
|
||||
_iRoleService = iRoleService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 给多用户设置多角色
|
||||
/// </summary>
|
||||
/// <param name="giveRoleSetMenuDto"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPut]
|
||||
public async Task<Result> GiveRoleSetMenu(GiveRoleSetMenuDto giveRoleSetMenuDto)
|
||||
{
|
||||
return Result.Success().SetStatus(await _iRoleService.GiveRoleSetMenu(giveRoleSetMenuDto.RoleIds, giveRoleSetMenuDto.MenuIds));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过角色id来获取菜单列表
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public async Task<Result> GetInMenuByRoleId(long RoleId)
|
||||
{
|
||||
return Result.Success().SetData(await _iRoleService.GetInMenuByRoleId(RoleId));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Yi.Framework.Common.Const;
|
||||
using Yi.Framework.Common.Models;
|
||||
using Yi.Framework.Core;
|
||||
using Yi.Framework.Interface;
|
||||
using Yi.Framework.Language;
|
||||
using Yi.Framework.Model.Models;
|
||||
using Yi.Framework.Repository;
|
||||
using Yi.Framework.WebCore;
|
||||
using Yi.Framework.WebCore.AttributeExtend;
|
||||
using Yi.Framework.WebCore.AuthorizationPolicy;
|
||||
|
||||
namespace Yi.Framework.ApiMicroservice.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// 测试控制器
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/[controller]/[action]")]
|
||||
public class TestController : ControllerBase
|
||||
{
|
||||
private IStringLocalizer<LocalLanguage> _local;
|
||||
private IUserService _iUserService;
|
||||
private IRoleService _iRoleService;
|
||||
private QuartzInvoker _quartzInvoker;
|
||||
//你可以依赖注入服务层各各接口,也可以注入其他仓储层,怎么爽怎么来!
|
||||
public TestController(ILogger<UserEntity> logger, IRoleService iRoleService, IUserService iUserService, IStringLocalizer<LocalLanguage> local, QuartzInvoker quartzInvoker)
|
||||
{
|
||||
_local = local;
|
||||
_iUserService = iUserService;
|
||||
_iRoleService = iRoleService;
|
||||
_quartzInvoker = quartzInvoker;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 仓储上下文对象测试
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
// 特点:化繁为简!意框架仓储代理上下文对象,用起来就是爽,但最好按规范来爽!
|
||||
// 规范:控制器不建议使用切换仓储方法、控制器严禁使用DB上下文对象,其它怎么爽怎么来!
|
||||
public async Task<Result> DbTest()
|
||||
{
|
||||
//非常好,使用UserService的特有方法
|
||||
await _iUserService.DbTest();
|
||||
|
||||
//非常好,依赖注入使用其他Service的特有方法
|
||||
await _iRoleService.DbTest();
|
||||
|
||||
//很核理,使用仓储的通用方法
|
||||
await _iUserService._repository.GetListAsync();
|
||||
|
||||
//挺不错,依赖注入其他仓储
|
||||
await _iRoleService._repository.GetListAsync();
|
||||
|
||||
//不建议操作,直接切换其他仓储
|
||||
await _iUserService._repository.ChangeRepository<Repository<RoleEntity>>().GetListAsync();
|
||||
|
||||
//最好不要直接操作Db对象
|
||||
await _iUserService._repository._Db.Queryable<UserEntity>().ToListAsync();
|
||||
|
||||
return Result.Success().SetData(await _iUserService.DbTest());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行Sql返回
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
//简单语句不推荐!
|
||||
public async Task<Result> SqlTest()
|
||||
{
|
||||
return Result.Success().SetData(await _iUserService._repository.UseSqlAsync<UserEntity>("select * from User"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 国际化测试
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
//根据浏览器语言设置来切换输出
|
||||
public Result LocalTest()
|
||||
{
|
||||
return Result.Success().SetData(_local["succeed"]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 权限测试
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
[Permission("user:get:test")]
|
||||
public Result PermissionTest()
|
||||
{
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 策略授权测试
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
[Authorize(PolicyName.Sid)]
|
||||
public Result AuthTest()
|
||||
{
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步事务测试
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
//注册一个用户获取它的信息之后再更新它,但是这个年龄可能会报错
|
||||
//如果一个事务中有任何一个错误,将会把所有执行过的操作进行回滚,确保数据的原子性
|
||||
public async Task<Result> TranTest()
|
||||
{
|
||||
UserEntity user = new() { UserName = $"杰哥{DateTime.Now}", Password = "5201314", Age = 99 };
|
||||
|
||||
var res = await _iUserService._repository.UseTranAsync(async () =>
|
||||
{
|
||||
await _iUserService.Register(user, (o) => user = o);
|
||||
user.Age = 18 / (new Random().Next(0, 2));
|
||||
await _iUserService._repository.UpdateAsync(user);
|
||||
});
|
||||
if (res)
|
||||
{
|
||||
return Result.Success("执行成功!");
|
||||
}
|
||||
else
|
||||
{
|
||||
return Result.Error("发生错误,插入已回滚!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 极爽导航属性
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
//Sqlsugar精髓之一!必学!最新版本
|
||||
public async Task<Result> IncludeTest()
|
||||
{
|
||||
return Result.Success().SetData(await _iUserService.GetListInRole());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动一个定时任务
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
//每5秒访问一次百度,可查看控制台
|
||||
public async Task<Result> JobTest()
|
||||
{
|
||||
Dictionary<string, object> data = new Dictionary<string, object>()
|
||||
{
|
||||
{JobConst.method,"get" },
|
||||
{JobConst.url,"https://www.baidu.com" }
|
||||
};
|
||||
await _quartzInvoker.start("*/5 * * * * ?", new Quartz.JobKey("test", "my"), "Yi.Framework.Job", "HttpJob", data: data);
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止任务
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPut]
|
||||
public async Task<Result> stopJob()
|
||||
{
|
||||
await _quartzInvoker.Stop(new Quartz.JobKey("test", "my"));
|
||||
return Result.Success();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Yi.Framework.Common.Models;
|
||||
using Yi.Framework.DTOModel;
|
||||
using Yi.Framework.Interface;
|
||||
using Yi.Framework.Model.Models;
|
||||
using Yi.Framework.Repository;
|
||||
using Yi.Framework.WebCore;
|
||||
using Yi.Framework.WebCore.AttributeExtend;
|
||||
using Yi.Framework.WebCore.AuthorizationPolicy;
|
||||
|
||||
namespace Yi.Framework.ApiMicroservice.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// 用户管理
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/[controller]/[action]")]
|
||||
public class UserController : BaseCrudController<UserEntity>
|
||||
{
|
||||
private IUserService _iUserService;
|
||||
public UserController(ILogger<UserEntity> logger, IUserService iUserService) : base(logger, iUserService)
|
||||
{
|
||||
_iUserService = iUserService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加用户,去重,密码加密
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
[Permission($"{nameof(UserEntity)}:add")]
|
||||
[HttpPost]
|
||||
public override async Task<Result> Add(UserEntity entity)
|
||||
{
|
||||
if (!await _iUserService.Exist(entity.UserName))
|
||||
{
|
||||
entity.BuildPassword();
|
||||
return Result.Success().SetData(await _iUserService._repository.InsertReturnSnowflakeIdAsync(entity));
|
||||
}
|
||||
return Result.SuccessError("用户已存在");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 给多用户设置多角色
|
||||
/// </summary>
|
||||
/// <param name="giveUserSetRoleDto"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPut]
|
||||
public async Task<Result> GiveUserSetRole(GiveUserSetRoleDto giveUserSetRoleDto)
|
||||
{
|
||||
return Result.Success().SetStatus(await _iUserService.GiveUserSetRole(giveUserSetRoleDto.UserIds, giveUserSetRoleDto.RoleIds));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 通过用户id得到角色列表
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public async Task<Result> GetRoleListByUserId(long userId)
|
||||
{
|
||||
return Result.Success().SetData(await _iUserService.GetRoleListByUserId(userId));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
using Autofac.Extensions.DependencyInjection;
|
||||
using Yi.Framework.WebCore.BuilderExtend;
|
||||
using Yi.Framework.Core;
|
||||
using Yi.Framework.WebCore.MiddlewareExtend;
|
||||
using Yi.Framework.WebCore.Utility;
|
||||
using Autofac;
|
||||
using Yi.Framework.Common.Models;
|
||||
using Yi.Framework.Language;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Yi.Framework.WebCore.AttributeExtend;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
builder.Configuration.AddCommandLine(args);
|
||||
builder.WebHost.UseUrls(builder.Configuration.GetValue<string>("StartUrl"));
|
||||
builder.Host.ConfigureAppConfiguration((hostBuilderContext, configurationBuilder) =>
|
||||
{
|
||||
configurationBuilder.AddCommandLine(args);
|
||||
configurationBuilder.AddJsonFileService();
|
||||
#region
|
||||
//Apollo配置
|
||||
#endregion
|
||||
configurationBuilder.AddApolloService("Yi");
|
||||
});
|
||||
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
|
||||
builder.Host.ConfigureContainer<ContainerBuilder>(containerBuilder =>
|
||||
{
|
||||
#region
|
||||
//交由Module依赖注入
|
||||
#endregion
|
||||
containerBuilder.RegisterModule<CustomAutofacModule>();
|
||||
});
|
||||
builder.Host.ConfigureLogging(loggingBuilder =>
|
||||
{
|
||||
loggingBuilder.AddFilter("System", Microsoft.Extensions.Logging.LogLevel.Warning);
|
||||
loggingBuilder.AddFilter("Microsoft", Microsoft.Extensions.Logging.LogLevel.Warning);
|
||||
loggingBuilder.AddLog4Net("./Config/Log4net.config");
|
||||
|
||||
});
|
||||
#region
|
||||
//配置类配置
|
||||
//builder.Host.ConfigureWebHostDefaults(webBuilder =>
|
||||
// {
|
||||
// //webBuilder.UseStartup<Startup>();
|
||||
// });
|
||||
#endregion
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
#region
|
||||
//Ioc配置
|
||||
#endregion
|
||||
builder.Services.AddIocService(builder.Configuration);
|
||||
#region
|
||||
//Sqlsugar上下文注入
|
||||
#endregion
|
||||
builder.Services.AddSqlsugarServer();
|
||||
#region
|
||||
//Quartz任务调度配置
|
||||
#endregion
|
||||
builder.Services.AddQuartzService();
|
||||
#region
|
||||
//AutoMapper注入
|
||||
#endregion
|
||||
builder.Services.AddAutoMapperService();
|
||||
#region
|
||||
//控制器+过滤器配置
|
||||
#endregion
|
||||
builder.Services.AddControllers(optios => {
|
||||
//optios.Filters.Add<PermissionAttribute>();
|
||||
}).AddJsonFileService();
|
||||
#region
|
||||
//权限过滤器
|
||||
#endregion
|
||||
builder.Services.AddSingleton<PermissionAttribute>();
|
||||
#region
|
||||
//Swagger服务配置
|
||||
#endregion
|
||||
builder.Services.AddSwaggerService<Program>();
|
||||
#region
|
||||
//跨域服务配置
|
||||
#endregion
|
||||
builder.Services.AddCorsService();
|
||||
#region
|
||||
//Jwt鉴权配置
|
||||
#endregion
|
||||
builder.Services.AddJwtService();
|
||||
#region
|
||||
//授权配置
|
||||
#endregion
|
||||
builder.Services.AddAuthorizationService();
|
||||
#region
|
||||
//Redis服务配置
|
||||
#endregion
|
||||
builder.Services.AddRedisService();
|
||||
#region
|
||||
//RabbitMQ服务配置
|
||||
#endregion
|
||||
builder.Services.AddRabbitMQService();
|
||||
#region
|
||||
//ElasticSeach服务配置
|
||||
#endregion
|
||||
builder.Services.AddElasticSeachService();
|
||||
#region
|
||||
//短信服务配置
|
||||
#endregion
|
||||
builder.Services.AddSMSService();
|
||||
#region
|
||||
//CAP服务配置
|
||||
#endregion
|
||||
builder.Services.AddCAPService();
|
||||
#region
|
||||
//国际化配置
|
||||
#endregion
|
||||
builder.Services.AddLocalizerService();
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
var app = builder.Build();
|
||||
#region
|
||||
//服务容器
|
||||
#endregion
|
||||
ServiceLocator.Instance = app.Services;
|
||||
//if (app.Environment.IsDevelopment())
|
||||
{
|
||||
#region
|
||||
//测试页面注入
|
||||
#endregion
|
||||
app.UseDeveloperExceptionPage();
|
||||
#region
|
||||
//Swagger服务注入
|
||||
#endregion
|
||||
app.UseSwaggerService();
|
||||
}
|
||||
#region
|
||||
//错误抓取反馈注入
|
||||
#endregion
|
||||
app.UseErrorHandlingService();
|
||||
#region
|
||||
//静态文件注入
|
||||
#endregion
|
||||
app.UseStaticFiles();
|
||||
#region
|
||||
//多语言国际化注入
|
||||
#endregion
|
||||
app.UseLocalizerService();
|
||||
#region
|
||||
//HttpsRedirection注入
|
||||
#endregion
|
||||
app.UseHttpsRedirection();
|
||||
#region
|
||||
//路由注入
|
||||
#endregion
|
||||
app.UseRouting();
|
||||
#region
|
||||
//跨域服务注入
|
||||
#endregion
|
||||
app.UseCorsService();
|
||||
#region
|
||||
//健康检查注入
|
||||
#endregion
|
||||
app.UseHealthCheckService();
|
||||
#region
|
||||
//鉴权注入
|
||||
#endregion
|
||||
app.UseAuthentication();
|
||||
#region
|
||||
//授权注入
|
||||
#endregion
|
||||
app.UseAuthorization();
|
||||
#region
|
||||
//Consul服务注入
|
||||
#endregion
|
||||
app.UseConsulService();
|
||||
#region
|
||||
//redis种子注入
|
||||
#endregion
|
||||
app.UseRedisSeedInitService();
|
||||
#region
|
||||
//Endpoints注入
|
||||
#endregion
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllers();
|
||||
});
|
||||
app.Run();
|
||||
@@ -1,36 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<UserSecretsId>f5ce4739-9524-4330-9aea-cfcdb41501de</UserSecretsId>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DocumentationFile>./Config/SwaggerDoc.xml</DocumentationFile>
|
||||
<NoWarn>1701;1702;CS1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="wwwrooot\**" />
|
||||
<Content Remove="wwwrooot\**" />
|
||||
<EmbeddedResource Remove="wwwrooot\**" />
|
||||
<None Remove="wwwrooot\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Yi.Framework.DTOModel\Yi.Framework.DTOModel.csproj" />
|
||||
<ProjectReference Include="..\Yi.Framework.Interface\Yi.Framework.Interface.csproj" />
|
||||
<ProjectReference Include="..\Yi.Framework.Model\Yi.Framework.Model.csproj" />
|
||||
<ProjectReference Include="..\Yi.Framework.Service\Yi.Framework.Service.csproj" />
|
||||
<ProjectReference Include="..\Yi.Framework.WebCore\Yi.Framework.WebCore.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="wwwroot\file\" />
|
||||
<Folder Include="wwwroot\image\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||