├── .gitignore
├── .prettierrc
├── LICENSE
├── README.md
├── babel.config.js
├── jsconfig.json
├── package-lock.json
├── package.json
├── public
├── favicon.ico
└── index.html
├── src
├── App.vue
├── assets
│ ├── css
│ │ ├── main.css
│ │ └── main.less
│ ├── font
│ │ ├── font.css
│ │ └── hamburgserial-xbold.ttf
│ ├── icon
│ │ ├── iconfont.css
│ │ ├── iconfont.js
│ │ ├── iconfont.json
│ │ ├── iconfont.ttf
│ │ ├── iconfont.woff
│ │ └── iconfont.woff2
│ ├── img
│ │ ├── 404.svg
│ │ ├── about-nsbbs.png
│ │ ├── avatar-bg.png
│ │ ├── badge.svg
│ │ ├── default-avatar.png
│ │ ├── default_avatar.png
│ │ ├── level
│ │ │ ├── Lv1.svg
│ │ │ ├── Lv2.svg
│ │ │ ├── Lv3.svg
│ │ │ ├── Lv4.svg
│ │ │ ├── Lv5.svg
│ │ │ ├── Lv6.svg
│ │ │ ├── badge-Lv1.svg
│ │ │ ├── badge-Lv2.svg
│ │ │ ├── badge-Lv3.svg
│ │ │ ├── badge-Lv4.svg
│ │ │ ├── badge-Lv5.svg
│ │ │ └── badge-Lv6.svg
│ │ ├── logo-lanse.png
│ │ ├── logo-lanse222.png
│ │ ├── logo002.png
│ │ ├── message
│ │ │ ├── header-message.svg
│ │ │ └── notification.svg
│ │ └── nan.jpg
│ └── json
│ │ └── nav.js
├── components
│ ├── article
│ │ ├── ArticleBasicInfo.vue
│ │ ├── ArticleCheck.vue
│ │ ├── ArticleDetail.vue
│ │ ├── FrontPageArticle.vue
│ │ ├── LeftButtons.vue
│ │ ├── UploadImage.vue
│ │ └── WriteArticle.vue
│ ├── books
│ │ └── NanShengValue.vue
│ ├── comment
│ │ ├── ArticleComment.vue
│ │ ├── ChildComment.vue
│ │ └── CreateComment.vue
│ ├── concern
│ │ └── SlideShow.vue
│ ├── errorPage
│ │ ├── NotFound.vue
│ │ └── ServerError.vue
│ ├── index
│ │ ├── About.vue
│ │ ├── ArticleDetailIndex.vue
│ │ ├── AuthorsListIndex.vue
│ │ ├── Book.vue
│ │ ├── CommentDonateIndex.vue
│ │ ├── Index.vue
│ │ ├── LabelIndex.vue
│ │ ├── LabelToArticleIndex.vue
│ │ ├── ResourceIndex.vue
│ │ ├── SetUpIndex.vue
│ │ ├── UserCenterIndex.vue
│ │ ├── WriteArticleIndex.vue
│ │ ├── head
│ │ │ ├── IndexHeader.vue
│ │ │ ├── IndexMenu.vue
│ │ │ └── IndexSider.vue
│ │ └── messages
│ │ │ ├── Message.vue
│ │ │ ├── MessageBox.vue
│ │ │ └── Notification.vue
│ ├── label
│ │ ├── ImageUpload.vue
│ │ ├── LabelContent.vue
│ │ └── LabelCreate.vue
│ ├── login
│ │ ├── EmailResetPassword.vue
│ │ ├── Login.vue
│ │ ├── MobileResetPassword.vue
│ │ └── Register.vue
│ ├── resource
│ │ ├── ImageUpload.vue
│ │ ├── ResourceContent.vue
│ │ └── ResourceCreate.vue
│ ├── right
│ │ ├── AuthorBlock.vue
│ │ ├── AuthorsList.vue
│ │ ├── FilingInfo.vue
│ │ ├── FollowCount.vue
│ │ ├── FriendDonate.vue
│ │ ├── LatestComment.vue
│ │ ├── MarkdownToc.vue
│ │ ├── PersonalAchievement.vue
│ │ ├── ProjectIntro.vue
│ │ └── RelatArticle.vue
│ ├── user
│ │ ├── AccountSettings.vue
│ │ ├── AuthorsListContent.vue
│ │ ├── BindEmail.vue
│ │ ├── BindPhone.vue
│ │ ├── ChangePassword.vue
│ │ ├── Dynamic.vue
│ │ ├── FollowAuthorsListContent.vue
│ │ ├── FollowTabs.vue
│ │ ├── PersonalInfoDisplay.vue
│ │ ├── ProfileContent.vue
│ │ ├── SetUpMenu.vue
│ │ ├── UploadModal.vue
│ │ └── UserTabs.vue
│ └── utils
│ │ ├── BackTop.vue
│ │ ├── CustomEmpty.vue
│ │ ├── FooterButtons.vue
│ │ └── Spin.vue
├── config
│ ├── config.js
│ └── utils.js
├── i18n
│ ├── en
│ │ └── common.js
│ ├── en_US.js
│ ├── zh
│ │ └── common.js
│ └── zh_CN.js
├── main.js
├── router
│ └── index.js
├── service
│ ├── articleService.js
│ ├── axios.js
│ ├── carouselService.js
│ ├── commentService.js
│ ├── dynamicService.js
│ ├── labelService.js
│ ├── loginService.js
│ ├── messageService.js
│ ├── resourceService.js
│ └── userService.js
├── store
│ └── index.js
└── utils
│ └── utils.js
├── vue.config.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 | dist.zip
5 |
6 |
7 | # local env files
8 | .env.local
9 | .env.*.local
10 |
11 | # Log files
12 | npm-debug.log*
13 | yarn-debug.log*
14 | yarn-error.log*
15 | pnpm-debug.log*
16 |
17 | # Editor directories and files
18 | .idea
19 | .vscode
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 180,
3 | "semi": true
4 | }
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | // presets: ["@vue/app"],
3 | presets: [
4 | '@vue/cli-plugin-babel/preset'
5 | ],
6 | plugins: [
7 | // [
8 | // "import",
9 | // {libraryName: "ant-design-vue", libraryDirectory: "es", style: true}
10 | // ]
11 | // ["import", { "libraryName": "ant-design-vue", "libraryDirectory": "es", "style": "css" }] // `style: true` 会加载 less 文件
12 | ]
13 | };
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "esnext",
5 | "baseUrl": "./",
6 | "moduleResolution": "node",
7 | "paths": {
8 | "@/*": [
9 | "src/*"
10 | ]
11 | },
12 | "lib": [
13 | "esnext",
14 | "dom",
15 | "dom.iterable",
16 | "scripthost"
17 | ]
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ns-bbs-ui",
3 | "version": "1.0.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "dev": "vue-cli-service serve",
8 | "build": "vue-cli-service build"
9 | },
10 | "dependencies": {
11 | "ant-design-vue": "2.2.8",
12 | "axios": "^0.21.0",
13 | "compression-webpack-plugin": "5.0.0",
14 | "core-js": "^3.6.5",
15 | "echarts": "^5.1.2",
16 | "less": "2.7.2",
17 | "less-loader": "^7.0.2",
18 | "mavon-editor": "3.0.1",
19 | "moment": "^2.29.1",
20 | "qs": "^6.10.3",
21 | "serve": "^11.3.2",
22 | "uglifyjs-webpack-plugin": "^2.2.0",
23 | "vue": "3.3.4",
24 | "vue-clipboard2": "^0.3.3",
25 | "vue-clipboard3": "^2.0.0",
26 | "vue-i18n": "^8.22.1",
27 | "vue-router": "^4.0.13",
28 | "vuex": "^4.0.2",
29 | "wangeditor": "^4.7.4",
30 | "webpack-theme-color-replacer": "1.3.22"
31 | },
32 | "devDependencies": {
33 | "@vue/cli-plugin-babel": "~4.5.0",
34 | "@vue/cli-plugin-router": "~4.5.0",
35 | "@vue/cli-plugin-vuex": "~4.5.0",
36 | "@vue/cli-service": "~4.5.0",
37 | "babel-plugin-import": "^1.13.3",
38 | "vue-template-compiler": "^2.6.11"
39 | },
40 | "browserslist": [
41 | "> 1%",
42 | "last 2 versions",
43 | "not dead"
44 | ]
45 | }
46 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maliangnansheng/bbs-vue3-ui/501bc1a74ef7601a18a983d6c1013a4d77ce8975/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | 南生论坛
10 |
11 |
12 |
13 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
105 |
106 |
124 |
--------------------------------------------------------------------------------
/src/assets/css/main.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | width: 100%;
4 | height: 100%;
5 | margin: 0;
6 | padding: 0;
7 | /*全局置灰*/
8 | /*filter:grayscale(100%)*/
9 | }
10 |
11 | .main-header .iconfont {
12 | font-size: 24px !important;
13 | }
14 |
15 | .ant-badge-count {
16 | top: 2px !important;
17 | right: 5px;
18 | }
19 |
20 | .ant-layout-footer {
21 | text-align: center;
22 | background: #e7e7e7!important;
23 | height: 10px
24 | }
25 |
26 | /* 修改浏览器自带的滚动条样式 */
27 | ::-webkit-scrollbar {
28 | width: 0.35rem;
29 | height: 0.25rem;
30 | background-image: linear-gradient(#ffffff 100%, #ffffff 100%);
31 | }
32 |
33 | ::-webkit-scrollbar-track {
34 | border-radius: 0;
35 | }
36 |
37 | ::-webkit-scrollbar-thumb {
38 | background-image: linear-gradient(#3798e8 100%, #3798e8 100%);
39 | transition: all .2s;
40 | }
41 |
42 | ::-webkit-scrollbar-thumb:hover {
43 | background-color: rgba(95, 95, 95, 0.7);
44 | }
--------------------------------------------------------------------------------
/src/assets/css/main.less:
--------------------------------------------------------------------------------
1 | // 引入antd的less文件,然后最后只需在入口文件里面引入当前文件即可
2 | @import "~ant-design-vue/dist/antd.less";
3 |
4 | @colors: #fa541c #3eaf7c #13c2c2 #1869ff #722ed1 #eb2f96;
5 | #home,
6 | #components-layout-demo-custom-trigger .trigger:hover {
7 | color: @primary-color !important;
8 | }
9 |
10 | .feedback-icon-container,
11 | .components-back-top-demo-custom .ant-back-top-inner {
12 | background: @primary-color;
13 | }
14 |
15 | .ant-tree li .ant-tree-node-content-wrapper:hover .org-tree-title {
16 | background: fade(@primary-color, 50%);
17 | }
18 |
19 | .dashboard-user-map .icon-fullscreen,
20 | .map-popup-count,
21 | .selected-stastics .anticon {
22 | color: @primary-color !important;
23 | }
24 |
25 | //声明map-icons为6 int map-icons = 6
26 | .map-icons(6);
27 | //循环 for(int i = 1; i <= n; i++)
28 | .map-icons(@n, @i:1) when (@i <= @n) {
29 | //.map-icon1{}
30 | //.map-icon2{}
31 | // ...
32 | .map-icon@{i} {
33 | //background-color 从@colors数组中取第i位的值,然后用fade方法淡化至50%
34 | background-color: fade(extract(@colors, @i), 50%);
35 | }
36 | .map-icons(@n, (@i + 1));
37 | }
38 |
39 | .map-icon {
40 | .text-container {
41 | background-color: @primary-color;
42 | }
43 | }
44 |
45 | .selected-stastics {
46 | background-color: fade(@primary-color, 20%);
47 | border: 1px solid fade(@primary-color, 50%);
48 | margin-bottom: 10px;
49 | }
50 |
51 | .ant-dropdown-menu-item,
52 | .ant-dropdown-menu-submenu-title {
53 | color: @primary-color !important;
54 | }
55 |
56 | .org-container {
57 | .iconfont {
58 | color: @primary-color;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/assets/font/font.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'HamburgSerial-Xbold';
3 | src: url('./hamburgserial-xbold.ttf');
4 | font-weight: normal;
5 | font-style: normal;
6 | }
--------------------------------------------------------------------------------
/src/assets/font/hamburgserial-xbold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maliangnansheng/bbs-vue3-ui/501bc1a74ef7601a18a983d6c1013a4d77ce8975/src/assets/font/hamburgserial-xbold.ttf
--------------------------------------------------------------------------------
/src/assets/icon/iconfont.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: "iconfont"; /* Project id 2533087 */
3 | src: url('iconfont.woff2?t=1677467908743') format('woff2'),
4 | url('iconfont.woff?t=1677467908743') format('woff'),
5 | url('iconfont.ttf?t=1677467908743') format('truetype');
6 | }
7 |
8 | .iconfont {
9 | font-family: "iconfont" !important;
10 | font-size: 16px;
11 | font-style: normal;
12 | -webkit-font-smoothing: antialiased;
13 | -moz-osx-font-smoothing: grayscale;
14 | }
15 |
16 | .icon-copy:before {
17 | content: "\e706";
18 | }
19 |
20 | .icon-NotFound:before {
21 | content: "\e61d";
22 | }
23 |
24 | .icon-gitee:before {
25 | content: "\e60c";
26 | }
27 |
28 | .icon-applyManage:before {
29 | content: "\e642";
30 | }
31 |
32 | .icon-rightsApply:before {
33 | content: "\e9a7";
34 | }
35 |
36 | .icon-dynamic:before {
37 | content: "\e636";
38 | }
39 |
40 | .icon-pendingReview:before {
41 | content: "\e609";
42 | }
43 |
44 | .icon-reviewRejected:before {
45 | content: "\e605";
46 | }
47 |
48 | .icon-donate:before {
49 | content: "\e657";
50 | }
51 |
52 | .icon-qianbi:before {
53 | content: "\e60a";
54 | }
55 |
56 | .icon-right-triangle:before {
57 | content: "\e652";
58 | }
59 |
60 | .icon-bug:before {
61 | content: "\e8e8";
62 | }
63 |
64 | .icon-chat:before {
65 | content: "\e635";
66 | }
67 |
68 | .icon-Lv6:before {
69 | content: "\e774";
70 | }
71 |
72 | .icon-Lv4:before {
73 | content: "\e76f";
74 | }
75 |
76 | .icon-Lv3:before {
77 | content: "\e770";
78 | }
79 |
80 | .icon-Lv1:before {
81 | content: "\e772";
82 | }
83 |
84 | .icon-Lv2:before {
85 | content: "\e773";
86 | }
87 |
88 | .icon-Lv5:before {
89 | content: "\e775";
90 | }
91 |
92 | .icon-about:before {
93 | content: "\e671";
94 | }
95 |
96 | .icon-setUp:before {
97 | content: "\e606";
98 | }
99 |
100 | .icon-quit:before {
101 | content: "\e607";
102 | }
103 |
104 | .icon-user-picture:before {
105 | content: "\e681";
106 | }
107 |
108 | .icon-writeArticle:before {
109 | content: "\e608";
110 | }
111 |
112 | .icon-follow:before {
113 | content: "\e64f";
114 | }
115 |
116 | .icon-personal-center:before {
117 | content: "\e604";
118 | }
119 |
120 | .icon-intro:before {
121 | content: "\e6b8";
122 | }
123 |
124 | .icon-office:before {
125 | content: "\e8b7";
126 | }
127 |
128 | .icon-GitHub:before {
129 | content: "\e8c6";
130 | }
131 |
132 | .icon-achievement:before {
133 | content: "\e627";
134 | }
135 |
136 | .icon-rise:before {
137 | content: "\e602";
138 | }
139 |
140 | .icon-relat-article:before {
141 | content: "\ec64";
142 | }
143 |
144 | .icon-eye:before {
145 | content: "\e68f";
146 | }
147 |
148 | .icon-comment:before {
149 | content: "\e60e";
150 | }
151 |
152 | .icon-like:before {
153 | content: "\e61c";
154 | }
155 |
156 | .icon-backTop:before {
157 | content: "\e685";
158 | }
159 |
160 | .icon-reject:before {
161 | content: "\e67c";
162 | }
163 |
164 | .icon-feed-back:before {
165 | content: "\e699";
166 | }
167 |
168 | .icon-resolve:before {
169 | content: "\e696";
170 | }
171 |
172 | .icon-feedbacks:before {
173 | content: "\e62f";
174 | }
175 |
176 | .icon-fullscreen:before {
177 | content: "\e6e5";
178 | }
179 |
180 | .icon-visitRecord:before {
181 | content: "\e61b";
182 | }
183 |
184 | .icon-home:before {
185 | content: "\e617";
186 | }
187 |
188 | .icon-packUp:before {
189 | content: "\e62e";
190 | }
191 |
192 | .icon-theme:before {
193 | content: "\e65b";
194 | }
195 |
196 | .icon-logOut:before {
197 | content: "\e63b";
198 | }
199 |
200 | .icon-profile:before {
201 | content: "\e601";
202 | }
203 |
204 | .icon-bell:before {
205 | content: "\e600";
206 | }
207 |
208 | .icon-message:before {
209 | content: "\e603";
210 | }
211 |
212 | .icon-disable:before {
213 | content: "\e663";
214 | }
215 |
216 | .icon-enable:before {
217 | content: "\e69e";
218 | }
219 |
220 | .icon-minus1:before {
221 | content: "\e828";
222 | }
223 |
224 | .icon-role:before {
225 | content: "\e74e";
226 | }
227 |
228 | .icon-add:before {
229 | content: "\e61e";
230 | }
231 |
232 | .icon-delete:before {
233 | content: "\e684";
234 | }
235 |
236 | .icon-rights:before {
237 | content: "\e611";
238 | }
239 |
240 | .icon-expand:before {
241 | content: "\e655";
242 | }
243 |
244 | .icon-edit:before {
245 | content: "\e8c3";
246 | }
247 |
248 | .icon-user:before {
249 | content: "\f2dc";
250 | }
251 |
252 | .icon-rightsRole:before {
253 | content: "\e637";
254 | }
255 |
256 | .icon-organization:before {
257 | content: "\e62a";
258 | }
259 |
260 | .icon-dashboard:before {
261 | content: "\e714";
262 | }
263 |
264 | .icon-projectManage:before {
265 | content: "\e610";
266 | }
267 |
268 |
--------------------------------------------------------------------------------
/src/assets/icon/iconfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maliangnansheng/bbs-vue3-ui/501bc1a74ef7601a18a983d6c1013a4d77ce8975/src/assets/icon/iconfont.ttf
--------------------------------------------------------------------------------
/src/assets/icon/iconfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maliangnansheng/bbs-vue3-ui/501bc1a74ef7601a18a983d6c1013a4d77ce8975/src/assets/icon/iconfont.woff
--------------------------------------------------------------------------------
/src/assets/icon/iconfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maliangnansheng/bbs-vue3-ui/501bc1a74ef7601a18a983d6c1013a4d77ce8975/src/assets/icon/iconfont.woff2
--------------------------------------------------------------------------------
/src/assets/img/about-nsbbs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maliangnansheng/bbs-vue3-ui/501bc1a74ef7601a18a983d6c1013a4d77ce8975/src/assets/img/about-nsbbs.png
--------------------------------------------------------------------------------
/src/assets/img/avatar-bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maliangnansheng/bbs-vue3-ui/501bc1a74ef7601a18a983d6c1013a4d77ce8975/src/assets/img/avatar-bg.png
--------------------------------------------------------------------------------
/src/assets/img/badge.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/img/default-avatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maliangnansheng/bbs-vue3-ui/501bc1a74ef7601a18a983d6c1013a4d77ce8975/src/assets/img/default-avatar.png
--------------------------------------------------------------------------------
/src/assets/img/default_avatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maliangnansheng/bbs-vue3-ui/501bc1a74ef7601a18a983d6c1013a4d77ce8975/src/assets/img/default_avatar.png
--------------------------------------------------------------------------------
/src/assets/img/level/Lv1.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/assets/img/level/Lv2.svg:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/src/assets/img/level/Lv3.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/assets/img/level/Lv4.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/assets/img/level/Lv5.svg:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/src/assets/img/level/Lv6.svg:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/src/assets/img/level/badge-Lv1.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/img/level/badge-Lv2.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/img/level/badge-Lv3.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/img/level/badge-Lv4.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/img/level/badge-Lv5.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/img/level/badge-Lv6.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/img/logo-lanse.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maliangnansheng/bbs-vue3-ui/501bc1a74ef7601a18a983d6c1013a4d77ce8975/src/assets/img/logo-lanse.png
--------------------------------------------------------------------------------
/src/assets/img/logo-lanse222.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maliangnansheng/bbs-vue3-ui/501bc1a74ef7601a18a983d6c1013a4d77ce8975/src/assets/img/logo-lanse222.png
--------------------------------------------------------------------------------
/src/assets/img/logo002.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maliangnansheng/bbs-vue3-ui/501bc1a74ef7601a18a983d6c1013a4d77ce8975/src/assets/img/logo002.png
--------------------------------------------------------------------------------
/src/assets/img/message/header-message.svg:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/src/assets/img/message/notification.svg:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/src/assets/img/nan.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maliangnansheng/bbs-vue3-ui/501bc1a74ef7601a18a983d6c1013a4d77ce8975/src/assets/img/nan.jpg
--------------------------------------------------------------------------------
/src/assets/json/nav.js:
--------------------------------------------------------------------------------
1 | export default {
2 | menu: [
3 | {
4 | name: "dashboard",
5 | path: "/dashboard"
6 | },
7 | {
8 | name: "organization",
9 | path: "/organization"
10 | },
11 | {
12 | name: "projectManage",
13 | path: "/projectManage"
14 | },
15 | {
16 | name: "rightsRole",
17 | path: "/rightsRole",
18 | children: [
19 | {name: "rights", path: "/rightsRole/rights"},
20 | {name: "role", path: "/rightsRole/role"}
21 | ]
22 | },
23 | {
24 | name: "user",
25 | path: "/user"
26 | },
27 | {
28 | name: "feedbacks",
29 | path: "/feedbacks"
30 | },
31 | {
32 | name: "visitRecord",
33 | path: "/visitRecord"
34 | }
35 | ]
36 | }
--------------------------------------------------------------------------------
/src/components/article/ArticleCheck.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
19 |
32 |
33 |
34 |
47 |
48 |
49 |
50 |
51 |
91 |
92 |
126 |
--------------------------------------------------------------------------------
/src/components/article/LeftButtons.vue:
--------------------------------------------------------------------------------
1 |
2 |
34 |
35 |
36 |
79 |
80 |
116 |
--------------------------------------------------------------------------------
/src/components/article/UploadImage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
19 |
20 |
21 |
22 |
44 |
45 |
--------------------------------------------------------------------------------
/src/components/comment/ArticleComment.vue:
--------------------------------------------------------------------------------
1 |
2 |
4 |
{{ $t('common.comment') }}
5 |
7 |
8 |
22 |
23 |
24 |
30 |
31 |
32 |
33 |
88 |
89 |
--------------------------------------------------------------------------------
/src/components/comment/CreateComment.vue:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
95 |
96 |
--------------------------------------------------------------------------------
/src/components/concern/SlideShow.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
61 |
62 |
79 |
--------------------------------------------------------------------------------
/src/components/errorPage/NotFound.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |

9 |
10 |
11 | {{ $t("common.backHome") }}
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
37 |
38 |
69 |
--------------------------------------------------------------------------------
/src/components/errorPage/ServerError.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | {{ $t("common.backHome") }}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
38 |
39 |
70 |
--------------------------------------------------------------------------------
/src/components/index/AuthorsListIndex.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/src/components/index/Book.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/components/index/CommentDonateIndex.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
39 |
40 |
--------------------------------------------------------------------------------
/src/components/index/LabelIndex.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/src/components/index/ResourceIndex.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/src/components/index/SetUpIndex.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | {{ $t('common.backPersonalHomepage') }}
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/src/components/index/UserCenterIndex.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
10 |
14 |
15 |
16 |
17 |
20 |
21 |
22 |
23 |
26 |
27 |
28 |
29 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/src/components/index/WriteArticleIndex.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
15 |
16 |
--------------------------------------------------------------------------------
/src/components/index/head/IndexSider.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
15 |
16 |
17 |
18 |
19 |
20 |
51 |
52 |
55 |
--------------------------------------------------------------------------------
/src/components/index/messages/Message.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |

7 |
8 |
9 |
{{
10 | item.message
11 | }}
12 |
13 |
{{ item.message }}
14 |
15 | {{ $moment(item.createTime).format("YYYY-MM-DD HH:mm:ss") }}
16 |
17 |
18 |
19 |
{{ $t("common.noAgain") }}
20 |
21 |
24 |
32 |
33 |
34 |
35 |
118 |
--------------------------------------------------------------------------------
/src/components/index/messages/MessageBox.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | tabClick(e, pane.key)">{{ pane.title }}
8 | {{ messageNum[pane.key] }}
9 |
10 |
11 |
12 |
14 |
15 |
16 |
17 |
18 |
19 |
76 |
77 |
200 |
--------------------------------------------------------------------------------
/src/components/index/messages/Notification.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |

7 |
8 |
9 |
{{
10 | item.message
11 | }}
12 |
13 |
{{ item.message }}
14 |
15 | {{ $moment(item.createTime).format("YYYY-MM-DD HH:mm:ss") }}
16 |
17 |
18 |
19 |
20 |
23 |
31 |
32 |
33 |
34 |
116 |
--------------------------------------------------------------------------------
/src/components/label/ImageUpload.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 |
13 |
14 |
15 | Upload
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
118 |
130 |
--------------------------------------------------------------------------------
/src/components/label/LabelCreate.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 |
15 |
16 |
17 |
18 | {{ $t('common.sureAndAdd') }}
19 |
20 |
21 |
22 |
23 |
131 |
132 |
141 |
--------------------------------------------------------------------------------
/src/components/login/Login.vue:
--------------------------------------------------------------------------------
1 |
2 |
48 |
49 |
50 |
122 |
123 |
--------------------------------------------------------------------------------
/src/components/resource/ImageUpload.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 |
13 |
14 |
15 | Upload
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
119 |
131 |
--------------------------------------------------------------------------------
/src/components/right/AuthorBlock.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
{{ data.name }}
10 |
![]()
11 |
{{ data.intro }}
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
{{ $t("common.getLikes") + ' ' + data.likeCount }}
20 |
21 |
22 |
23 |
24 |
25 |
{{ $t("common.articleRead") + ' ' + data.readCount }}
26 |
27 |
28 |
29 |
30 |
81 |
82 |
--------------------------------------------------------------------------------
/src/components/right/AuthorsList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | {{ item.name }}
12 |
13 |
14 |
15 |
16 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
31 |
32 |
33 |
34 |
86 |
87 |
130 |
--------------------------------------------------------------------------------
/src/components/right/FilingInfo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{ $t("common.getTheWord") }}
7 |
8 | {{ $t("common.designDocument") }}
9 |
10 |
11 | {{ $t("common.userGuidance") }}
12 |
13 |
14 | {{ $t("common.links") }}
15 |
16 | {{ $t("common.about") }}
17 |
18 |
19 | 版权 © 南生论坛 丨
20 | 蜀ICP备19014736号-1
21 |
22 |
23 |
24 |
25 |
55 |
56 |
69 |
--------------------------------------------------------------------------------
/src/components/right/FollowCount.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ $t("common.follow") }}
5 | {{ data.followCount }}
6 |
7 |
8 |
9 | {{ $t("common.fan") }}
10 | {{ data.fanCount }}
11 |
12 |
13 |
14 |
15 |
50 |
51 |
--------------------------------------------------------------------------------
/src/components/right/FriendDonate.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{ $t("common.donateContent1") }}
7 |
8 | {{ $t("common.donateContent2") }}
9 |
10 |
11 |
12 |
Where can I send money?
13 |
Alipay Cooperative overseas remittance platform:
14 |
https://remit.alipay.com
15 |
What information should I fill in at the money transfer platform?
16 |
Alipay ID:
17 |
m15680982093_1@163.com
18 |
Last Name:
19 |
MA
20 |
First Name:
21 |
LIANG
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |

30 |
31 |
32 |
33 |
34 |
35 |

36 |
37 |
38 |
39 |
40 |
41 |

42 |
43 |
44 |
45 |
46 |
47 |

48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
58 |
59 |
86 |
--------------------------------------------------------------------------------
/src/components/right/LatestComment.vue:
--------------------------------------------------------------------------------
1 |
2 |
22 |
23 |
24 |
59 |
60 |
104 |
--------------------------------------------------------------------------------
/src/components/right/MarkdownToc.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ $t("common.toc") }}
5 |
6 |
7 |
8 |
9 |
10 |
11 |
55 |
56 |
--------------------------------------------------------------------------------
/src/components/right/PersonalAchievement.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
10 |
11 |
12 |
{{ $t("common.getLikes") + ' ' + data.likeCount }}
13 |
14 |
15 |
16 |
17 |
18 |
{{ $t("common.articleRead") + ' ' + data.readCount }}
19 |
20 |
21 |
22 |
23 |
24 |
{{ $t("common.nanshengValue") + ' ' + data.points }}
25 |
26 |
27 |
28 |
29 |
48 |
49 |
--------------------------------------------------------------------------------
/src/components/right/ProjectIntro.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | {{ $t('common.projectIntro') }}
13 |
14 |
15 | {{ $t("common.commonCount") }}
16 |
17 |
18 | {{ $t("common.article") }}
19 |
21 |
22 |
23 | {{ $t("common.comment") }}
24 |
26 |
27 |
28 | {{ $t("common.visit") }}
29 |
31 |
32 |
33 | {{ $t("common.carousel") }}
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
81 |
82 |
--------------------------------------------------------------------------------
/src/components/right/RelatArticle.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ $t("common.relatArtilce") }}
6 |
7 |
8 |
9 |
10 |
11 |
14 |
15 | {{ item.title }}
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
69 |
70 |
122 |
--------------------------------------------------------------------------------
/src/components/user/AuthorsListContent.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
![]()
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | {{ $t("common.get") }}
16 | {{ item.likeCount + ' ' + $t("common.praise") }}
17 | ·
18 | {{ item.readCount + ' ' + $t("common.read") }}
19 |
20 |
21 |
23 | {{ $t("common.follow") }}
24 |
25 |
26 | {{ $t("common.haveFollowed") }}
27 |
28 |
29 |
30 |
31 |
{{ $t("common.noAgain") }}
32 |
33 |
34 |
35 |
75 |
76 |
--------------------------------------------------------------------------------
/src/components/user/ChangePassword.vue:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
35 |
36 |
37 |
53 |
54 |
55 |
71 |
72 |
73 |
74 |
75 |
76 |
149 |
150 |
167 |
--------------------------------------------------------------------------------
/src/components/user/Dynamic.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
{{ item.userName }}
10 |
11 |
12 | {{ $t("common.dynamicWritten") }}
13 | {{ $t("common.dynamicLiked") }}
14 | {{ $t("common.dynamicLiked") }}
15 | {{ $t("common.dynamicCommented") }}
16 | {{ $t("common.dynamicReply") }}
17 | {{ $t("common.dynamicFollowed") }}
18 |
19 |
20 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | {{ $t("common.noAgain") }}
36 |
37 |
38 |
39 |
40 |
75 |
76 |
--------------------------------------------------------------------------------
/src/components/user/FollowAuthorsListContent.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
![]()
9 |
10 |
12 |
13 |
14 |
15 |
16 |
17 | {{ $t("common.get") }}
18 | {{ item.likeCount + ' ' + $t("common.praise") }}
19 | ·
20 | {{ item.readCount + ' ' + $t("common.read") }}
21 |
22 |
23 |
26 | {{ $t("common.follow") }}
27 |
28 |
30 | {{ $t("common.haveFollowed") }}
31 |
32 |
33 |
34 |
35 |
{{ $t("common.noAgain") }}
36 |
37 |
38 |
39 |
71 |
72 |
--------------------------------------------------------------------------------
/src/components/user/FollowTabs.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{ $t("common.follow") + ' ' + followedTotal }}
8 |
9 |
10 |
17 |
18 |
19 |
20 |
21 | {{ $t("common.fan") + ' ' + fanTotal }}
22 |
23 |
24 |
31 |
32 |
33 |
34 |
35 |
36 |
147 |
148 |
--------------------------------------------------------------------------------
/src/components/user/SetUpMenu.vue:
--------------------------------------------------------------------------------
1 |
2 |
21 |
22 |
73 |
74 |
--------------------------------------------------------------------------------
/src/components/user/UploadModal.vue:
--------------------------------------------------------------------------------
1 |
2 |
12 | {{ $t("common.editAvatar") }}
13 | {{ $t("common.avatarTip")[0] }}
14 | {{ $t("common.avatarTip")[1] }}
15 |
18 |
19 |
28 |
36 |
37 |
43 |
44 |
45 |
46 |
121 |
122 |
162 |
--------------------------------------------------------------------------------
/src/components/utils/BackTop.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{ $t("common.backToTop") }}
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
27 |
28 |
--------------------------------------------------------------------------------
/src/components/utils/CustomEmpty.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
21 |
22 |
23 |
24 |
25 |
26 |
31 |
32 |
--------------------------------------------------------------------------------
/src/components/utils/FooterButtons.vue:
--------------------------------------------------------------------------------
1 |
2 |
37 |
38 |
39 |
71 |
72 |
97 |
--------------------------------------------------------------------------------
/src/components/utils/Spin.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
16 |
17 |
--------------------------------------------------------------------------------
/src/config/config.js:
--------------------------------------------------------------------------------
1 | // 按照教程配置动态切换主题方法
2 | const ThemeColorReplacer = require('webpack-theme-color-replacer')
3 | const {generate} = require('@ant-design/colors/dist/index')
4 |
5 | const getAntdSerials = (color) => {
6 | // 淡化(即less的tint)
7 | const lightens = new Array(9).fill().map((t, i) => {
8 | return ThemeColorReplacer.varyColor.lighten(color, i / 10)
9 | })
10 | const colorPalettes = generate(color)
11 | const rgb = ThemeColorReplacer.varyColor.toNum3(color.replace('#', '')).join(',')
12 | return lightens.concat(colorPalettes).concat(rgb)
13 | }
14 |
15 | const themePluginOption = {
16 | fileName: 'css/theme-colors-[contenthash:8].css',
17 | matchColors: getAntdSerials('#1890ff'), // 主色系列
18 | // 改变样式选择器,解决样式覆盖问题
19 | changeSelector(selector) {
20 | switch (selector) {
21 | case '.ant-calendar-today .ant-calendar-date':
22 | return ':not(.ant-calendar-selected-date):not(.ant-calendar-selected-day)' + selector
23 | case '.ant-btn:focus,.ant-btn:hover':
24 | return '.ant-btn:focus:not(.ant-btn-primary):not(.ant-btn-danger),.ant-btn:hover:not(.ant-btn-primary):not(.ant-btn-danger)'
25 | case '.ant-btn.active,.ant-btn:active':
26 | return '.ant-btn.active:not(.ant-btn-primary):not(.ant-btn-danger),.ant-btn:active:not(.ant-btn-primary):not(.ant-btn-danger)'
27 | case '.ant-steps-item-process .ant-steps-item-icon > .ant-steps-icon':
28 | case '.ant-steps-item-process .ant-steps-item-icon>.ant-steps-icon':
29 | return ':not(.ant-steps-item-process)' + selector
30 | case '.ant-menu-horizontal>.ant-menu-item-active,.ant-menu-horizontal>.ant-menu-item-open,.ant-menu-horizontal>.ant-menu-item-selected,.ant-menu-horizontal>.ant-menu-item:hover,.ant-menu-horizontal>.ant-menu-submenu-active,.ant-menu-horizontal>.ant-menu-submenu-open,.ant-menu-horizontal>.ant-menu-submenu-selected,.ant-menu-horizontal>.ant-menu-submenu:hover':
31 | case '.ant-menu-horizontal > .ant-menu-item-active,.ant-menu-horizontal > .ant-menu-item-open,.ant-menu-horizontal > .ant-menu-item-selected,.ant-menu-horizontal > .ant-menu-item:hover,.ant-menu-horizontal > .ant-menu-submenu-active,.ant-menu-horizontal > .ant-menu-submenu-open,.ant-menu-horizontal > .ant-menu-submenu-selected,.ant-menu-horizontal > .ant-menu-submenu:hover':
32 | return '.ant-menu-horizontal > .ant-menu-item-active,.ant-menu-horizontal > .ant-menu-item-open,.ant-menu-horizontal > .ant-menu-item-selected,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-item:hover,.ant-menu-horizontal > .ant-menu-submenu-active,.ant-menu-horizontal > .ant-menu-submenu-open,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu-selected,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu:hover'
33 | case '.ant-menu-horizontal > .ant-menu-item-selected > a':
34 | case '.ant-menu-horizontal>.ant-menu-item-selected>a':
35 | return '.ant-menu-horizontal:not(ant-menu-light):not(.ant-menu-dark) > .ant-menu-item-selected > a'
36 | case '.ant-menu-horizontal > .ant-menu-item > a:hover':
37 | case '.ant-menu-horizontal>.ant-menu-item>a:hover':
38 | return '.ant-menu-horizontal:not(ant-menu-light):not(.ant-menu-dark) > .ant-menu-item > a:hover'
39 | default :
40 | return selector
41 | }
42 | }
43 | }
44 |
45 | const createThemeColorReplacerPlugin = () => new ThemeColorReplacer(themePluginOption)
46 |
47 | module.exports = createThemeColorReplacerPlugin
48 |
49 |
--------------------------------------------------------------------------------
/src/config/utils.js:
--------------------------------------------------------------------------------
1 | // 按照教程配置动态切换主题方法 https://blog.csdn.net/Joey_Tribiani/article/details/117420207?spm=1001.2014.3001.5501
2 | import client from "webpack-theme-color-replacer/client";
3 | import {generate} from "@ant-design/colors/dist/index";
4 | // import {generate} = require('@ant-design/colors/dist/index').default
5 |
6 | function getAntdSerials(color) {
7 | // 淡化(即less的tint)
8 | const lightens = new Array(9).fill().map((t, i) => {
9 | return client.varyColor.lighten(color, i / 10);
10 | });
11 | // colorPalette变换得到颜色值
12 | const colorPalettes = generate(color);
13 | const rgb = client.varyColor.toNum3(color.replace("#", "")).join(",");
14 | return lightens.concat(colorPalettes).concat(rgb);
15 | }
16 |
17 | function changeColor(newColor) {
18 | var options = {
19 | newColors: getAntdSerials(newColor), // new colors array, one-to-one corresponde with `matchColors`
20 | changeUrl(cssUrl) {
21 | return `/${cssUrl}`; // while router is not `hash` mode, it needs absolute path
22 | },
23 | };
24 | return client.changer.changeColor(options, Promise);
25 | }
26 |
27 | export default {
28 | updateTheme: (newPrimaryColor) => {
29 | changeColor(newPrimaryColor)
30 | },
31 | };
32 |
33 |
--------------------------------------------------------------------------------
/src/i18n/en_US.js:
--------------------------------------------------------------------------------
1 | import common from "./en/common";
2 |
3 | export default {
4 | menu: {
5 | dashboard: "Dashboard",
6 | organization: "Organization",
7 | projectManage: "Project Manage",
8 | rightsRole: "Rights and Role",
9 | rights: "Rights Manage",
10 | role: "Role Manage",
11 | user: "User Manage",
12 | visitRecord: "Visit Record",
13 | feedbacks: "Feedback Manage",
14 | editRole: "Edit Role",
15 | },
16 | common,
17 | };
18 |
--------------------------------------------------------------------------------
/src/i18n/zh_CN.js:
--------------------------------------------------------------------------------
1 | import common from "./zh/common";
2 |
3 | export default {
4 | menu: {
5 | dashboard: "仪表盘",
6 | organization: "组织架构",
7 | projectManage: "系统管理",
8 | rightsRole: "权限角色",
9 | rights: "权限管理",
10 | role: "角色管理",
11 | user: "用户管理",
12 | visitRecord: "访问记录",
13 | feedbacks: "反馈管理",
14 | editRole: "编辑角色",
15 | },
16 | common,
17 | };
18 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue';
2 | import App from './App';
3 | import Antd from 'ant-design-vue';
4 | import 'ant-design-vue/dist/antd.css';
5 |
6 | import router from './router'
7 | import store from './store'
8 | import "@/assets/css/main.css"
9 | // 引入自定义全局字体样式表
10 | import '@/assets/font/font.css'
11 | // 引入iconfont的样式(iconfont的使用参照iconfont官网)
12 | import '@/assets/icon/iconfont.css'
13 | // 引入axios 初始化axios里面的配置
14 | import "@/service/axios"
15 | import moment from "moment"
16 | import "./assets/css/main.less"
17 | import utils from "./utils/utils"
18 | import mavonEditor from 'mavon-editor'
19 | import 'mavon-editor/dist/css/index.css'
20 | // import routerHistory from '@/router/vueRouterHistory';
21 |
22 | // import VueClipboards from 'vue-clipboard2'
23 |
24 | // Vue.config.productionTip = false;
25 |
26 | // // 引入自定义工具模块
27 | // Vue.prototype.$utils = utils
28 | // Vue.prototype.$moment = moment
29 | // // 将国际化添加为Vue的原型上的方法
30 | // Vue.prototype.$t = store.state.translate.bind(store.state)
31 |
32 | // Vue.use(Antd);
33 | // Vue.use(mavonEditor)
34 | // Vue.use(VueClipboards);
35 |
36 | // export default new Vue({
37 | // router,
38 | // store,
39 | // render: h => h(App)
40 | // }).$mount('#app')
41 |
42 |
43 |
44 | const app = createApp(App);
45 | app.config.globalProperties.$utils = utils;
46 | app.config.globalProperties.$moment = moment;
47 | app.config.globalProperties.$t = store.state.translate.bind(store.state);
48 |
49 | app
50 | .use(Antd)
51 | .use(store)
52 | .use(router)
53 | .use(mavonEditor)
54 | .mount('#app');
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | // import Vue from "vue";
2 | // import VueRouter from "vue-router";
3 | import { createRouter, RouteRecordRaw, createWebHistory } from 'vue-router';
4 |
5 | // 每次调用Vue.$router.push方法跳转路由的时候先判断是不是已经在目标路由,避免重复跳转(Vue会有警告)
6 | // const originalPush = VueRouter.prototype.push;
7 | // VueRouter.prototype.push = function push(location) {
8 | // return originalPush.call(this, location).catch(err => err);
9 | // };
10 |
11 | // Vue.use(VueRouter);
12 |
13 | const routes = [
14 | {
15 | path: "/",
16 | name: "home",
17 | component: () => import("@/components/index/Index"),
18 | },
19 | {
20 | path: "/search",
21 | component: () => import("@/components/index/Index"),
22 | },
23 | {
24 | path: "/recommended",
25 | name: "recommended",
26 | component: () => import("@/components/index/AuthorsListIndex"),
27 | },
28 | {
29 | path: "/write",
30 | component: () => import("@/components/index/WriteArticleIndex"),
31 | },
32 | {
33 | path: "/edit/:id",
34 | component: () => import("@/components/index/WriteArticleIndex"),
35 | },
36 | {
37 | path: "/detail/:id",
38 | name: "detail",
39 | component: () => import("@/components/index/ArticleDetailIndex"),
40 | },
41 | {
42 | path: "/empty",
43 | component: () => import("@/components/utils/CustomEmpty"),
44 | },
45 | {
46 | path: "/user/:id",
47 | component: () => import("@/components/index/UserCenterIndex"),
48 | children: [
49 | {
50 | path: ":userCenterTab",
51 | component: () => import("@/components/index/UserCenterIndex")
52 | },
53 | ]
54 | },
55 | {
56 | path: "/label",
57 | name: "label",
58 | component: () => import("@/components/index/LabelIndex"),
59 | },
60 | {
61 | path: "/label/:id",
62 | component: () => import("@/components/index/LabelToArticleIndex"),
63 | },
64 | {
65 | path: "/settings",
66 | component: () => import("@/components/index/SetUpIndex"),
67 | children: [
68 | {
69 | path: "",
70 | redirect: "profile"
71 | },
72 | {
73 | path: "profile",
74 | name: "profile",
75 | component: () => import("@/components/user/ProfileContent")
76 | },
77 | {
78 | path: "account",
79 | name: "account",
80 | component: () => import("@/components/user/AccountSettings")
81 | },
82 | ]
83 | },
84 | {
85 | path: "/resource",
86 | name: "resource",
87 | component: () => import("@/components/index/ResourceIndex"),
88 | },
89 | {
90 | path: "/book",
91 | name: "book",
92 | component: () => import("@/components/index/Book"),
93 | },
94 | {
95 | path: "/about",
96 | name: "about",
97 | component: () => import("@/components/index/About"),
98 | },
99 | {
100 | path: "/commentDonate",
101 | name: "commentDonate",
102 | component: () => import("@/components/index/CommentDonateIndex"),
103 | },
104 | {
105 | path: "/500",
106 | name: '500',
107 | component: () => import("@/components/errorPage/ServerError")
108 | },
109 | {
110 | // 将匹配所有内容并将其放在 `$route.params.pathMatch` 下
111 | path: "/:pathMatch(.*)*",
112 | name: '404',
113 | component: () => import("@/components/errorPage/NotFound")
114 | }
115 | ];
116 |
117 | // const router = new VueRouter({
118 | // mode: "history",
119 | // base: process.env.BASE_URL,
120 | // routes
121 | // });
122 |
123 | const router = createRouter({
124 | // history: createWebHashHistory(),
125 | history: createWebHistory(),
126 | routes,
127 | });
128 |
129 | export default router;
130 |
--------------------------------------------------------------------------------
/src/service/articleService.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 |
3 | export default {
4 | // 获取文章
5 | getArticleList(params) {
6 | return new Promise((resolve, reject) => {
7 | axios.get("/api/bbs/article/getList", {params})
8 | .then((res) => resolve(res))
9 | .catch((err) => reject(err));
10 | });
11 | },
12 | // 获取个人发布的文章(所有)
13 | getPersonalArticles(params) {
14 | return new Promise((resolve, reject) => {
15 | axios.get("/api/bbs/article/getPersonalArticles", {params})
16 | .then((res) => resolve(res))
17 | .catch((err) => reject(err));
18 | });
19 | },
20 | // 获取待审核的文章
21 | getPendingReviewArticles(params) {
22 | return new Promise((resolve, reject) => {
23 | axios.get("/api/bbs/article/getPendingReviewArticles", {params})
24 | .then((res) => resolve(res))
25 | .catch((err) => reject(err));
26 | });
27 | },
28 | // 获取禁用的文章
29 | getDisabledArticles(params) {
30 | return new Promise((resolve, reject) => {
31 | axios.get("/api/bbs/article/getDisabledArticles", {params})
32 | .then((res) => resolve(res))
33 | .catch((err) => reject(err));
34 | });
35 | },
36 | // 修改文章审批状态
37 | updateState(data) {
38 | return new Promise((resolve, reject) => {
39 | axios.post("/api/bbs/article/updateState", data)
40 | .then((res) => resolve(res))
41 | .catch((err) => reject(err));
42 | });
43 | },
44 | // 获取点赞过的文章
45 | getLikesArticle(params) {
46 | return new Promise((resolve, reject) => {
47 | axios.get("/api/bbs/article/getLikesArticle", {params})
48 | .then((res) => resolve(res))
49 | .catch((err) => reject(err));
50 | });
51 | },
52 | // 获取文章评论访问总数
53 | getArticleCommentVisitTotal(params) {
54 | return new Promise((resolve, reject) => {
55 | axios.get("/api/bbs/article/getArticleCommentVisitTotal", {params})
56 | .then((res) => resolve(res))
57 | .catch((err) => reject(err));
58 | });
59 | },
60 | // 上传图片(一张)
61 | uploadPicture(data) {
62 | return new Promise((resolve, reject) => {
63 | axios.post("/api/bbs/article/uploadPicture", data)
64 | .then((res) => resolve(res))
65 | .catch((err) => reject(err));
66 | });
67 | },
68 | // 写文章
69 | articleCreate(data) {
70 | return new Promise((resolve, reject) => {
71 | axios.post("/api/bbs/article/create", data)
72 | .then((res) => resolve(res))
73 | .catch((err) => reject(err));
74 | });
75 | },
76 | // 更新文章
77 | articleUpdate(data) {
78 | return new Promise((resolve, reject) => {
79 | axios.post("/api/bbs/article/update", data)
80 | .then((res) => resolve(res))
81 | .catch((err) => reject(err));
82 | });
83 | },
84 | // 获取文章详情
85 | getArticleById(params) {
86 | return new Promise((resolve, reject) => {
87 | axios.get("/api/bbs/article/getById", {params})
88 | .then((res) => resolve(res))
89 | .catch((err) => reject(err));
90 | });
91 | },
92 | // 获取文章一些统计数据
93 | getArticleCountById(params) {
94 | return new Promise((resolve, reject) => {
95 | axios.get("/api/bbs/article/getCountById", {params})
96 | .then((res) => resolve(res))
97 | .catch((err) => reject(err));
98 | });
99 | },
100 | // 文章置顶/取消置顶
101 | articleTop(params) {
102 | return new Promise((resolve, reject) => {
103 | axios.get("/api/bbs/article/articleTop", {params})
104 | .then((res) => resolve(res))
105 | .catch((err) => reject(err));
106 | });
107 | },
108 | //删除文章
109 | articleDelete(data) {
110 | return new Promise((resolve, reject) => {
111 | axios.post("/api/bbs/article/delete/" + data)
112 | .then((res) => resolve(res))
113 | .catch((err) => reject(err));
114 | });
115 | },
116 | // 文章审核数据量
117 | getArticleCheckCount(params) {
118 | return new Promise((resolve, reject) => {
119 | axios.get("/api/bbs/article/getArticleCheckCount", {params})
120 | .then((res) => resolve(res))
121 | .catch((err) => reject(err));
122 | });
123 | },
124 | };
125 |
--------------------------------------------------------------------------------
/src/service/axios.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import store from "@/store/index";
3 | // 设置xhr请求超时时间和baseURL(毫秒)
4 | axios.defaults.timeout = 15000;
5 | if (process.env.NODE_ENV === "production") {
6 | // axios.defaults.baseURL = "http://bbs.nansin.top";
7 | }
8 | axios.defaults.withCredentials = true; //允许axios请求携带cookie等凭证
9 | export default (() => {
10 |
11 | // 每次请求前处理
12 | axios.interceptors.request.use(
13 | function (config) {
14 | return config;
15 | },
16 | function (error) {
17 | return Promise.reject(error);
18 | }
19 | );
20 |
21 | // 每次请求回来的处理
22 | axios.interceptors.response.use(
23 | function (response) {
24 | // 后台会在响应头带上用户头像链接,每次和存在store中的比较,不同就替换,实现头像更新
25 | if (response.headers["x-user-picture"]) {
26 | store.state.picture = response.headers["x-user-picture"];
27 | }
28 | // 后台会在响应头带上用户任务提醒和消息通知的数量,存在store里面,
29 | if (response.headers["x-system-notify-count"]) {
30 | store.state.systemNotifyCount = response.headers["x-system-notify-count"];
31 | }
32 | if (response.headers["x-task-notify-count"]) {
33 | store.state.taskNotifyCount = response.headers["x-task-notify-count"];
34 | }
35 | // 和后台约定好响应码为200且响应体的code字段为0的时候才算成功
36 | if (response.status === 200) {
37 | if (response.data) {
38 | if (response.data.code === 0) {
39 | return Promise.resolve(response.data);
40 |
41 | // 如果code是302,代表需要跳转到切页面
42 | } else if (response.data.code === 302 && response.config.url !== '/api/bbs/user/getCurrentUserRights') {
43 | // window.location.href = response.data.data.target;
44 | store.state.isLogin = false;
45 | store.state.loginVisible = true;
46 | return Promise.reject(response.data);
47 | } else {
48 | throw response.data;
49 | }
50 | } else {
51 | throw response;
52 | }
53 | } else {
54 | // 响应码不是200则返回一个失败的Promise
55 | return Promise.reject(response);
56 | }
57 | },
58 | function (error) {
59 | return Promise.reject(error);
60 | }
61 | );
62 | })();
63 |
--------------------------------------------------------------------------------
/src/service/carouselService.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 |
3 | export default {
4 | // 获取走马灯
5 | getCarouselList(params) {
6 | return new Promise((resolve, reject) => {
7 | axios.get("/api/bbs/carousel/getList", {params})
8 | .then((res) => resolve(res))
9 | .catch((err) => reject(err));
10 | });
11 | },
12 |
13 | };
14 |
--------------------------------------------------------------------------------
/src/service/commentService.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 |
3 | export default {
4 | // 获取文章的评论信息
5 | getCommentByArticleId(params) {
6 | return new Promise((resolve, reject) => {
7 | axios.get("/api/bbs/comment/getCommentByArticleId", {params})
8 | .then((res) => resolve(res))
9 | .catch((err) => reject(err));
10 | });
11 | },
12 | // 获取最新评论信息
13 | getLatestComment(params) {
14 | return new Promise((resolve, reject) => {
15 | axios.get("/api/bbs/comment/getLatestComment", {params})
16 | .then((res) => resolve(res))
17 | .catch((err) => reject(err));
18 | });
19 | },
20 | // 创建评论
21 | createComment(data) {
22 | return new Promise((resolve, reject) => {
23 | axios.post("/api/bbs/comment/create", data)
24 | .then((res) => resolve(res))
25 | .catch((err) => reject(err));
26 | });
27 | },
28 | // 删除评论
29 | deleteComment(data) {
30 | return new Promise((resolve, reject) => {
31 | axios.post("/api/bbs/comment/delete/" + data)
32 | .then((res) => resolve(res))
33 | .catch((err) => reject(err));
34 | });
35 | },
36 |
37 | };
38 |
--------------------------------------------------------------------------------
/src/service/dynamicService.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 |
3 | export default {
4 | // 获取动态
5 | getDynamicList(params) {
6 | return new Promise((resolve, reject) => {
7 | axios.get("/api/bbs/dynamic/getList", {params})
8 | .then((res) => resolve(res))
9 | .catch((err) => reject(err));
10 | });
11 | },
12 |
13 | };
14 |
--------------------------------------------------------------------------------
/src/service/labelService.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 |
3 | export default {
4 | // 获取标签
5 | getLabelList(params) {
6 | return new Promise((resolve, reject) => {
7 | axios.get("/api/bbs/label/getList", {params})
8 | .then((res) => resolve(res))
9 | .catch((err) => reject(err));
10 | });
11 | },
12 | // 上传标签logo
13 | uploadLabelLogo(data) {
14 | return new Promise((resolve, reject) => {
15 | axios.post("/api/bbs/label/uploadLabelLogo", data)
16 | .then((res) => resolve(res))
17 | .catch((err) => reject(err));
18 | });
19 | },
20 | // 新增标签
21 | labelCreate(data) {
22 | return new Promise((resolve, reject) => {
23 | axios.post("/api/bbs/label/create", data)
24 | .then((res) => resolve(res))
25 | .catch((err) => reject(err));
26 | });
27 | },
28 | // 更新标签
29 | labelUpdate(data) {
30 | return new Promise((resolve, reject) => {
31 | axios.post("/api/bbs/label/update", data)
32 | .then((res) => resolve(res))
33 | .catch((err) => reject(err));
34 | });
35 | },
36 | // 删除标签
37 | labelDelete(data) {
38 | return new Promise((resolve, reject) => {
39 | axios.post("/api/bbs/label/delete/" + data)
40 | .then((res) => resolve(res))
41 | .catch((err) => reject(err));
42 | });
43 | },
44 |
45 | };
46 |
--------------------------------------------------------------------------------
/src/service/loginService.js:
--------------------------------------------------------------------------------
1 | import axios from "axios"
2 |
3 | export default {
4 | // 注册
5 | register(data) {
6 | return new Promise((resolve, reject) => {
7 | axios.post("/api/bbs/sso/register", data)
8 | .then(res => resolve(res))
9 | .catch(err => reject(err));
10 | });
11 | },
12 | // 登录
13 | login(data) {
14 | return new Promise((resolve, reject) => {
15 | axios.post("/api/bbs/sso/login", data)
16 | .then(res => resolve(res))
17 | .catch(err => reject(err));
18 | });
19 | },
20 | // 退出登录
21 | logout(data) {
22 | return new Promise((resolve, reject) => {
23 | axios.get("/api/bbs/sso/logout", data)
24 | .then(res => resolve(res))
25 | .catch(err => reject(err))
26 | })
27 | }
28 | }
--------------------------------------------------------------------------------
/src/service/messageService.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 |
3 | export default {
4 | // 分页获取通知信息
5 | getMessageList(params) {
6 | return new Promise((resolve, reject) => {
7 | axios.get("/api/bbs/notify/getList", {params})
8 | .then(res => resolve(res))
9 | .catch(err => reject(err));
10 | });
11 | },
12 | // 全部已读
13 | makeAllRead(params) {
14 | return new Promise((resolve, reject) => {
15 | axios.post("/api/bbs/notify/haveRead", null, {params})
16 | .then(res => resolve(res))
17 | .catch(err => reject(err));
18 | });
19 | },
20 | // 标记已读
21 | markRead(data) {
22 | return new Promise((resolve, reject) => {
23 | axios.post("/api/bbs/notify/markRead", data)
24 | .then(res => resolve(res))
25 | .catch(err => reject(err));
26 | });
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/src/service/resourceService.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 |
3 | export default {
4 | // 获取资源导航
5 | getResourceList(params) {
6 | return new Promise((resolve, reject) => {
7 | axios.get("/api/bbs/resource/getList", {params})
8 | .then((res) => resolve(res))
9 | .catch((err) => reject(err));
10 | });
11 | },
12 | // 获取资源导航
13 | getCategorys(params) {
14 | return new Promise((resolve, reject) => {
15 | axios.get("/api/bbs/resource/getCategorys", {params})
16 | .then((res) => resolve(res))
17 | .catch((err) => reject(err));
18 | });
19 | },
20 | // 上传资源导航logo
21 | uploadResourceLogo(data) {
22 | return new Promise((resolve, reject) => {
23 | axios.post("/api/bbs/resource/uploadResourceLogo", data)
24 | .then((res) => resolve(res))
25 | .catch((err) => reject(err));
26 | });
27 | },
28 | // 新增资源导航
29 | resourceCreate(data) {
30 | return new Promise((resolve, reject) => {
31 | axios.post("/api/bbs/resource/create", data)
32 | .then((res) => resolve(res))
33 | .catch((err) => reject(err));
34 | });
35 | },
36 | // 更新资源导航
37 | resourceUpdate(data) {
38 | return new Promise((resolve, reject) => {
39 | axios.post("/api/bbs/resource/update", data)
40 | .then((res) => resolve(res))
41 | .catch((err) => reject(err));
42 | });
43 | },
44 | // 删除资源导航
45 | resourceDelete(data) {
46 | return new Promise((resolve, reject) => {
47 | axios.post("/api/bbs/resource/delete/" + data)
48 | .then((res) => resolve(res))
49 | .catch((err) => reject(err));
50 | });
51 | },
52 |
53 | };
54 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | // import Vue from "vue";
2 | // import Vuex from "vuex";
3 | import zh_CN from "@/i18n/zh_CN";
4 | import en_US from "@/i18n/en_US";
5 | import userService from "@/service/userService";
6 | import utils from "@/config/utils";
7 | import { createStore } from 'vuex';
8 |
9 | const langs = {zh_CN, en_US};
10 |
11 | // Vue.use(Vuex);
12 |
13 | const state = {
14 | // 判断用户是否已经登录
15 | isLogin: false,
16 | // 登录modal是否可见
17 | loginVisible: false,
18 | // 注册modal是否可见
19 | registerVisible: false,
20 | // 手机找回密码modal是否可见
21 | mobileResetPasswordVisible: false,
22 | // 邮箱找回密码modal是否可见
23 | emailResetPasswordVisible: false,
24 | // 登录用户id
25 | userId: "",
26 | // 用户头像
27 | picture: "",
28 | // 判断用户是否是管理员
29 | isManage: false,
30 | // 文章审核(enable、disabled、pendingReview)
31 | articleCheck: "enable",
32 | // 用户名称长度限制
33 | userMaxLength: 10,
34 | // 主题色
35 | colorOptions: ["#000000", "#3eaf7c", "#13c2c2", "#1869ff", "#722ed1", "#eb2f96"],
36 | // 当前使能的主题色
37 | themeColor: "#13c2c2",
38 | // 是否启用跑马灯(1:启用,0:禁止)
39 | isCarousel: 1,
40 | // 菜单是否收缩
41 | collapsed: false,
42 | // 比collapsed大一点
43 | collapsedMax: false,
44 | // 用户屏幕宽度
45 | width: 0,
46 | // 用户屏幕高度
47 | height: 0,
48 | // 语言
49 | locale: "zh_CN",
50 | // 系统通知数量
51 | systemNotifyCount: 0,
52 | // 任务提醒数量
53 | taskNotifyCount: 0,
54 | // 南生运营域名
55 | manageDomain: 'http://manage-test.nansin.top',
56 | // 国际化方法
57 | translate: function (val) {
58 | // 国际化方法
59 | if (!val) {
60 | return "";
61 | }
62 | const arr = val.split(".");
63 | let l = arr.length;
64 | let re;
65 | try {
66 | re = langs[this.locale];
67 | for (let i = 0; i < l; i++) {
68 | re = re[arr[i]];
69 | }
70 | } catch (err) {
71 | re = arr[l - 1];
72 | }
73 | return re || arr[l - 1];
74 | }
75 | };
76 | const getters = {
77 | formCol(state) {
78 | if (state.width >= 500) {
79 | return {label: 6, wrapper: 16};
80 | }
81 | return {label: 8, wrapper: 16};
82 | },
83 | // 自动计算屏幕的内容区域(减取padding、margin和菜单栏的宽度)
84 | contentWidth(state) {
85 | if (state.collapsed) {
86 | return state.width - 120;
87 | }
88 | return state.width - 314;
89 | },
90 | isPc(state) {
91 | // 根据用户屏幕宽度判断是pc访问还是移动设备访问
92 | if (state.width > 750) {
93 | return true;
94 | }
95 | return false;
96 | },
97 | // 状态枚举,项目中多出用到,所以存到store里面使用
98 | stateList(state) {
99 | return [
100 | {title: state.translate("common.enabled"), value: 1},
101 | {title: state.translate("common.disabled"), value: 0}
102 | ];
103 | }
104 | };
105 | const mutations = {
106 | // 更改主题色,同时将配置存在localStorage
107 | changeColor(state, color) {
108 | utils.updateTheme(color);
109 | window.localStorage.themeColor = color;
110 | state.themeColor = color;
111 | }
112 | };
113 | export default createStore({
114 | state,
115 | mutations,
116 | getters,
117 | });
118 |
--------------------------------------------------------------------------------
/src/utils/utils.js:
--------------------------------------------------------------------------------
1 | export default {
2 | isClamp(event) {
3 | /**
4 | * @param {*} event 事件对象
5 | * @returns {Boolean} true: 说明超出元素宽度显示 false: 说明并未超出显示
6 | */
7 | try {
8 | const width = event.target.clientWidth;
9 | const originWidth = event.target.nextElementSibling.clientWidth;
10 | return originWidth > width;
11 | } catch (error) {
12 | return false;
13 | }
14 | },
15 |
16 | // 元素onscroll事件的回调函数,用于滚动加载
17 | scroll(el) {
18 | /**
19 | * @this {VM} this指向的是Vue实例对象,因为是在vue实例(组件)里面通过call绑定this后调用的,
20 | * 这样hasNext、finish等参数就不需要传过来,直接在this身上取
21 | * @argument {DomElement} el 需要监听滚动事件的dom元素(容器)
22 | * @argument {Number} height 高度
23 | */
24 |
25 |
26 | el.onscroll = ({ target }) => {
27 | /**
28 | * scrollTop:滚动条滚动距离;
29 | * scrollHeight:文档内容实际高度(包括超出视窗的溢出部分);
30 | * clientHeight:窗口可视范围高度
31 | */
32 | const {scrollTop, scrollHeight, clientHeight} = target;
33 | /**
34 | * @property hasNext 列表是否还有没加载完的(是否已经加载完最后一项),是后台返回的结果
35 | * @property finish 上个请求是否完成,完成后才可以进行下个请求
36 | * @function loadMore 加载下一页的方法
37 | * 100 可不加
38 | */
39 | if (this.hasNext && this.finish && scrollHeight - scrollTop <= clientHeight + 100) {
40 | this.loadMore();
41 | }
42 | };
43 | },
44 |
45 | // 按照创建时间和当前时间显示操作(刚刚,几小时/分/天/周/月/年前)
46 | showtime(time) {
47 | let date = typeof time === "number" ? new Date(time) : new Date((time || "").replace(/-/g, "/"));
48 | // 秒
49 | let secondTime = (new Date().getTime() - date.getTime()) / 1000;
50 | // 天
51 | let dayTime = Math.floor(secondTime / 86400);
52 |
53 | let isValidDate = Object.prototype.toString.call(date) === "[object Date]" && !isNaN(date.getTime());
54 |
55 | if (!isValidDate) {
56 | window.console.error("不是有效日期格式");
57 | }
58 | const formatDate = function (date) {
59 | let today = new Date(date);
60 | let year = today.getFullYear();
61 | let month = ("0" + (today.getMonth() + 1)).slice(-2);
62 | let day = ("0" + today.getDate()).slice(-2);
63 | let hour = today.getHours();
64 | let minute = today.getMinutes();
65 | let second = today.getSeconds();
66 | return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
67 | };
68 |
69 | // 异常情况
70 | if (isNaN(dayTime) || dayTime < 0) {
71 | return formatDate(date);
72 | }
73 |
74 | return (
75 | (dayTime === 0 && ((secondTime < 60 && "刚刚") ||
76 | (secondTime < 120 && "1分钟前") ||
77 | (secondTime < 3600 && Math.floor(secondTime / 60) + "分钟前") ||
78 | (secondTime < 7200 && "1小时前") ||
79 | (secondTime < 86400 && Math.floor(secondTime / 3600) + "小时前"))) ||
80 | (dayTime === 1 && "昨天") ||
81 | (dayTime < 7 && dayTime + "天前") ||
82 | (dayTime < 30 && Math.floor(dayTime / 7) + "周前") ||
83 | (dayTime < 365 && Math.floor(dayTime / 30) + "月前") ||
84 | (dayTime >= 365 && Math.floor(dayTime / 365) + "年前")
85 | );
86 | },
87 |
88 | // 将h标签转化为ul>li的形式
89 | toToc(data) {
90 | // 过滤出标签
91 | data = data.match(/<[hH][1-6]>.*?<\/[hH][1-6]>/g);
92 | if (!data) {
93 | return null;
94 | }
95 | const levelStack = [];
96 | let result = '';
97 | const addStartUL = function () {
98 | result += '';
99 | };
100 | const addEndUL = function () {
101 | result += '
\n';
102 | };
103 | const addLI = function (index, itemText) {
104 | result += '' + itemText + "\n";
105 | };
106 | data.forEach(function (item, index) {
107 | // 获取a标签的id值
108 | let a_id = item.replace(//g, '');
109 | // 匹配h标签的文字
110 | const itemText = item.replace(/<[^>]+>/g, '');
111 | // 匹配h?标签
112 | const itemLabel = item.match(/<\w+?>/)[0];
113 | // 判断数组里有无
114 | let levelIndex = levelStack.indexOf(itemLabel);
115 | // 没有找到相应标签,则将新增ul、li
116 | if (levelIndex === -1) {
117 | levelStack.unshift(itemLabel);
118 | addStartUL();
119 | addLI(a_id, itemText);
120 | }
121 | // 找到了相应标签,并且在栈顶的位置则直接将li放在此ul下
122 | else if (levelIndex === 0) {
123 | addLI(a_id, itemText);
124 | }
125 | // 找到了相应标签,但是不在栈顶位置,需要将之前的所有出栈并且打上闭合标签,最后新增li
126 | else {
127 | while (levelIndex--) {
128 | levelStack.shift();
129 | addEndUL();
130 | }
131 | addLI(a_id, itemText);
132 | }
133 | });
134 | // 如果栈中还有,全部出栈打上闭合标签
135 | while (levelStack.length) {
136 | levelStack.shift();
137 | addEndUL();
138 | }
139 |
140 | // 去掉回车换行
141 | return result.replace(/\r?\n|\r/g, "");
142 | },
143 |
144 | };
145 |
--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
1 | const UglyfyJsPlugin = require("uglifyjs-webpack-plugin");
2 | const CompressionPlugin = require("compression-webpack-plugin");
3 | const createThemeColorReplacerPlugin = require("./src/config/config");
4 | const vueConfig = {
5 | // 开发环境的跨域配置
6 | devServer: {
7 | // proxy: "http://bbs.localhost.com",
8 | disableHostCheck: true,
9 | port: 8082,
10 | },
11 | // css样式配置(为了实现动态切换主题),教程 https://blog.csdn.net/Joey_Tribiani/article/details/117420207?spm=1001.2014.3001.5501
12 | css: {
13 | loaderOptions: {
14 | less: {
15 | lessOptions: {
16 | modifyVars: {},
17 | javascriptEnabled: true
18 | }
19 | }
20 | }
21 | },
22 | assetsDir: "static",
23 | productionSourceMap: false,
24 | // configureWebpack里面是webpack配置项
25 | configureWebpack: {
26 | optimization: {
27 | minimizer: [
28 | // 压缩js文件
29 | new UglyfyJsPlugin({
30 | test: /\.js(\?.*)?$/i
31 | })
32 | ]
33 | },
34 | plugins: [
35 | // 使用gzip压缩
36 | new CompressionPlugin({
37 | algorithm: "gzip",
38 | test: /\.js$|\.html$|\.css$/, // 匹配文件名
39 | filename: "[path].gz[query]", // 压缩后的文件名(保持原文件名,后缀加.gz)
40 | minRatio: 1, // 压缩率小于1才会压缩
41 | threshold: 10240, // 对超过10k的数据压缩
42 | deleteOriginalAssets: false // 是否删除未压缩的源文件,谨慎设置,如果希望提供非gzip的资源,可不设置或者设置为false(比如删除打包后的gz后还可以加载到原始资源文件)
43 | }),
44 | // 动态切换主题插件
45 | createThemeColorReplacerPlugin()
46 | ],
47 | // 外链cdn引入
48 | externals: {
49 | // vue: "Vue",
50 | "vue2-leaflet": "Vue2Leaflet",
51 | leaflet: "L"
52 | }
53 | }
54 | };
55 | module.exports = vueConfig;
56 |
--------------------------------------------------------------------------------