├── .gitignore ├── LICENSE ├── README.md ├── crawler ├── jue_jin_article.py └── jue_jin_list.py ├── image ├── article.png ├── article__01.png ├── cloud__off.png ├── cloud__on.png ├── comm.png ├── dia.png ├── eye.png ├── fire.png ├── index.png ├── inform-logo-off.png ├── inform-logo-on.png ├── input-1.png ├── input.png ├── juejin-logo.png ├── juejinVip.png ├── jy.png ├── lv-2.png ├── panelFa.png ├── panelLiu.png ├── panelQuan.png ├── panelStar.png ├── panelZan.png ├── paneljing.png ├── pencil.png ├── ping.png ├── user.png ├── userEye.png ├── userZan.png ├── zan__off.png └── zan__on.png ├── juejin-app ├── babel.config.js ├── jsconfig.json ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ └── index.html ├── src │ ├── App.vue │ ├── api │ │ └── article.js │ ├── assets │ │ ├── css │ │ │ ├── article.css │ │ │ ├── header.css │ │ │ └── main.css │ │ ├── img │ │ │ ├── article__01.png │ │ │ ├── cloud__off.png │ │ │ ├── cloud__on.png │ │ │ ├── comm.png │ │ │ ├── day.png │ │ │ ├── dia.png │ │ │ ├── eye.png │ │ │ ├── fire.png │ │ │ ├── inform-logo-off.png │ │ │ ├── inform-logo-on.png │ │ │ ├── input-1.png │ │ │ ├── input.png │ │ │ ├── juejin-logo.png │ │ │ ├── juejinVip.png │ │ │ ├── jy.png │ │ │ ├── lv-2.png │ │ │ ├── panelFa.png │ │ │ ├── panelLiu.png │ │ │ ├── panelQuan.png │ │ │ ├── panelStar.png │ │ │ ├── panelZan__off.png │ │ │ ├── panelZan__on.png │ │ │ ├── paneljing.png │ │ │ ├── pencil.png │ │ │ ├── ping.png │ │ │ ├── top.png │ │ │ ├── user-top.png │ │ │ ├── user.png │ │ │ ├── userEye.png │ │ │ ├── userZan.png │ │ │ ├── zan__off.png │ │ │ └── zan__on.png │ │ └── js │ │ │ ├── EventBus.js │ │ │ └── vue.js │ ├── components │ │ └── Header.vue │ ├── main.js │ ├── pages │ │ ├── Detail.vue │ │ └── Index.vue │ ├── router │ │ └── index.js │ └── utils │ │ ├── request.js │ │ └── timeDispose.js └── vue.config.js ├── juejin-server ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── jue_jin.sql ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── edu │ │ │ └── gdut │ │ │ └── juejinserver │ │ │ ├── JuejinServerApplication.java │ │ │ ├── config │ │ │ ├── MybatisPlusConfig.java │ │ │ ├── RedisConfig.java │ │ │ └── SwaggerConfig.java │ │ │ ├── controller │ │ │ ├── ArticleContentController.java │ │ │ └── ArticleInfoController.java │ │ │ ├── mapper │ │ │ ├── ArticleContentMapper.java │ │ │ ├── ArticleInfoMapper.java │ │ │ ├── AuthorUserMapper.java │ │ │ ├── TagsMapper.java │ │ │ └── xml │ │ │ │ ├── ArticleContentMapper.xml │ │ │ │ ├── ArticleInfoMapper.xml │ │ │ │ ├── AuthorUserMapper.xml │ │ │ │ └── TagsMapper.xml │ │ │ ├── pojo │ │ │ ├── ArticleContent.java │ │ │ ├── ArticleInfo.java │ │ │ ├── AuthorUser.java │ │ │ └── Tags.java │ │ │ ├── service │ │ │ ├── ArticleContentService.java │ │ │ ├── ArticleInfoService.java │ │ │ ├── AuthorUserService.java │ │ │ ├── TagsService.java │ │ │ └── impl │ │ │ │ ├── ArticleContentServiceImpl.java │ │ │ │ ├── ArticleInfoServiceImpl.java │ │ │ │ ├── AuthorUserServiceImpl.java │ │ │ │ └── TagsServiceImpl.java │ │ │ ├── utils │ │ │ ├── JuejinStringUtils.java │ │ │ ├── Result.java │ │ │ └── ResultCode.java │ │ │ └── vo │ │ │ ├── ArticleDetailVo.java │ │ │ └── ArticleInfoVo.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── edu │ └── gdut │ └── juejinserver │ └── CodeGenerator.java └── project ├── article.css ├── article.html ├── index.html ├── page__header.css └── page__main.css /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /juejin-app/dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | *.iml 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 仿掘金官网 2 | 3 | ## 食用 4 | 5 | #### 进入`juejin-app`目录 6 | 7 | ``` 8 | cd juejin-app 9 | ``` 10 | 11 | #### 安装依赖 12 | 13 | ``` 14 | npm install 15 | ``` 16 | 17 | #### 启动服务器 18 | 19 | ``` 20 | npm run serve 21 | ``` 22 | 23 | #### 访问URL 24 | 25 | http://localhost:8080 26 | 27 | ## 演示 28 | 29 | 在线地址:http://106.14.212.78:8080 30 | 31 | - 主页 32 | ![](./image/index.png) 33 | - 文章页 34 | ![](./image/article.png) 35 | 36 | -------------------------------------------------------------------------------- /crawler/jue_jin_article.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import re 3 | import pymysql 4 | 5 | 6 | def get_detail_content(article_id): 7 | """ 8 | 爬取掘金文章内容 9 | :param article_id: 文章ID 10 | :return: 文章内容字符串 11 | """ 12 | 13 | url = "https://juejin.cn/post/" + article_id 14 | 15 | headers = { 16 | "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36" 17 | } 18 | 19 | resp = requests.get(url, headers=headers) 20 | 21 | text = resp.text 22 | 23 | resp.close() 24 | 25 | li = re.findall(r'article_info:{.*display_count:.*?}', text) 26 | 27 | s = "7016520448204603423" # 如果没有该文章id对应的数据就用这个 28 | if len(li) > 0: 29 | s = li[0] 30 | 31 | # 查找开始位置 32 | start_idx = s.find("mark_content") 33 | 34 | start_idx = start_idx + 14 35 | 36 | # 查找结束位置 37 | end_idx = s.find("display_count") 38 | 39 | end_idx = end_idx - 2 40 | 41 | result = s[start_idx:end_idx] 42 | 43 | return result 44 | 45 | 46 | # test 47 | 48 | # aid = "7130812569043861511" 49 | # 50 | # print(get_detail_content(aid)) 51 | 52 | # 连接数据库 53 | host = "....." 54 | port = 3306 55 | user = "...." 56 | password = "....." 57 | charset = "utf8mb4" 58 | db = "jue_jin" 59 | # 返回连接对象 60 | conn = pymysql.connect(host=host, port=port, user=user, password=password, charset=charset, db=db) 61 | # 创建操作对象 62 | cur = conn.cursor() 63 | # 执行sql语句查出所有文章id记录 64 | sql = "select article_id from article_info" 65 | cur.execute(sql) 66 | # 返回执行结果 67 | tup = cur.fetchall() 68 | 69 | for ele in tup: 70 | article_id = ele[0] 71 | # 根据id查询文章内容 72 | article_content = get_detail_content(article_id=article_id) 73 | sql = "insert into article_content(article_id,mark_content) values(%s,%s)" 74 | cur.execute(sql, (article_id, article_content)) 75 | conn.commit() 76 | 77 | print("finish") 78 | 79 | # 释放资源 80 | cur.close() 81 | conn.close() 82 | -------------------------------------------------------------------------------- /crawler/jue_jin_list.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | import pandas as pd 4 | import xlwt 5 | import requests 6 | 7 | 8 | # 处理结果 9 | # 拿到一个list,list里面每个元素是一个dict字典,字典里的article_info里的数据就是需要的 10 | def get_article_info(json_data_list): 11 | result_list = [] 12 | for dit in json_data_list: 13 | _dit = dit["item_info"] 14 | 15 | # # article_info 16 | # if "article_info" in _dit: 17 | # result_list.append(_dit["article_info"]) 18 | # # author_user_info 19 | 20 | if "author_user_info" in _dit: 21 | result_list.append(_dit["author_user_info"]) 22 | # # tags 23 | # if "tags" in _dit: 24 | # # 取到的是一个数组 25 | # temp_list = _dit["tags"] 26 | # if len(temp_list) > 0: 27 | # result_list.append(temp_list[0]) 28 | 29 | return result_list 30 | 31 | 32 | # 爬数据 33 | def crawler_jue_jin(current_page): 34 | url = "https://api.juejin.cn/recommend_api/v1/article/recommend_all_feed?aid=2608&uuid=7123759892783040000" 35 | 36 | headers = { 37 | "content-type": "application/json;charset=utf-8", 38 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36', 39 | "origin": "https://juejin.cn", 40 | "referer": "https://juejin.cn/", 41 | "cookie": "_ga=GA1.2.1069960429.1658629609; __tea_cookie_tokens_2608=%257B%2522web_id%2522%253A%25227123759892783040000%2522%252C%2522user_unique_id%2522%253A%25227123759892783040000%2522%252C%2522timestamp%2522%253A1658629609298%257D; _tea_utm_cache_6587={%22utm_source%22:%22jjhd_2021_qxybj%22}; _tea_utm_cache_2608={%22utm_source%22:%22gold_browser_extension%22}; MONITOR_WEB_ID=efc1dda2-8e66-4b1a-95c6-039cdd100710; _gid=GA1.2.164141089.1659063255" 42 | } 43 | 44 | payload = { 45 | "client_type": 2608, 46 | "cursor": str(current_page), 47 | "id_type": 2, 48 | 'limit': 20, 49 | "sort_type": 200 50 | } 51 | 52 | resp = requests.post(url=url, data=json.dumps(payload), headers=headers) 53 | 54 | json_data = resp.json()["data"] 55 | 56 | result = get_article_info(json_data) 57 | 58 | resp.close() 59 | 60 | return result 61 | 62 | 63 | index = 0 64 | data_list = [] 65 | while index < 52: 66 | temp = crawler_jue_jin(index) 67 | data_list.extend(temp) 68 | index += 1 69 | 70 | 71 | # 写文件到excel 或 直接存到数据库 72 | def export_excel(export): 73 | # 将字典列表转换为DataFrame 74 | pf = pd.DataFrame(list(export)) 75 | 76 | # 生成的excel 77 | file_path = pd.ExcelWriter("juejin_data_users.xlsx") 78 | 79 | pf.to_excel(file_path, encoding="utf-8", index=False) 80 | file_path.save() 81 | 82 | 83 | print("start write data to excel...") 84 | export_excel(data_list) 85 | print("finish!") 86 | -------------------------------------------------------------------------------- /image/article.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/article.png -------------------------------------------------------------------------------- /image/article__01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/article__01.png -------------------------------------------------------------------------------- /image/cloud__off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/cloud__off.png -------------------------------------------------------------------------------- /image/cloud__on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/cloud__on.png -------------------------------------------------------------------------------- /image/comm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/comm.png -------------------------------------------------------------------------------- /image/dia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/dia.png -------------------------------------------------------------------------------- /image/eye.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/eye.png -------------------------------------------------------------------------------- /image/fire.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/fire.png -------------------------------------------------------------------------------- /image/index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/index.png -------------------------------------------------------------------------------- /image/inform-logo-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/inform-logo-off.png -------------------------------------------------------------------------------- /image/inform-logo-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/inform-logo-on.png -------------------------------------------------------------------------------- /image/input-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/input-1.png -------------------------------------------------------------------------------- /image/input.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/input.png -------------------------------------------------------------------------------- /image/juejin-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/juejin-logo.png -------------------------------------------------------------------------------- /image/juejinVip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/juejinVip.png -------------------------------------------------------------------------------- /image/jy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/jy.png -------------------------------------------------------------------------------- /image/lv-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/lv-2.png -------------------------------------------------------------------------------- /image/panelFa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/panelFa.png -------------------------------------------------------------------------------- /image/panelLiu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/panelLiu.png -------------------------------------------------------------------------------- /image/panelQuan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/panelQuan.png -------------------------------------------------------------------------------- /image/panelStar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/panelStar.png -------------------------------------------------------------------------------- /image/panelZan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/panelZan.png -------------------------------------------------------------------------------- /image/paneljing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/paneljing.png -------------------------------------------------------------------------------- /image/pencil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/pencil.png -------------------------------------------------------------------------------- /image/ping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/ping.png -------------------------------------------------------------------------------- /image/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/user.png -------------------------------------------------------------------------------- /image/userEye.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/userEye.png -------------------------------------------------------------------------------- /image/userZan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/userZan.png -------------------------------------------------------------------------------- /image/zan__off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/zan__off.png -------------------------------------------------------------------------------- /image/zan__on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/image/zan__on.png -------------------------------------------------------------------------------- /juejin-app/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /juejin-app/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 | -------------------------------------------------------------------------------- /juejin-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "juejin-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "@bytemd/plugin-frontmatter": "^1.17.2", 12 | "@bytemd/vue": "^1.17.2", 13 | "axios": "^0.27.2", 14 | "bytemd": "^1.17.2", 15 | "core-js": "^3.8.3", 16 | "element-ui": "^2.15.9", 17 | "juejin-markdown-themes": "^1.29.3", 18 | "tb-skeleton": "^0.3.6", 19 | "vue": "^2.6.14", 20 | "vue-loading-skeleton": "^1.1.9", 21 | "vue-router": "^3.5.4", 22 | "vue-wechat-title": "^2.0.7" 23 | }, 24 | "devDependencies": { 25 | "@babel/core": "^7.12.16", 26 | "@babel/eslint-parser": "^7.12.16", 27 | "@vue/cli-plugin-babel": "~5.0.0", 28 | "@vue/cli-plugin-eslint": "~5.0.0", 29 | "@vue/cli-service": "~5.0.0", 30 | "babel-plugin-component": "^1.1.1", 31 | "eslint": "^7.32.0", 32 | "eslint-plugin-vue": "^8.0.3", 33 | "vue-template-compiler": "^2.6.14" 34 | }, 35 | "eslintConfig": { 36 | "root": true, 37 | "env": { 38 | "node": true 39 | }, 40 | "extends": [ 41 | "plugin:vue/essential", 42 | "eslint:recommended" 43 | ], 44 | "parserOptions": { 45 | "parser": "@babel/eslint-parser" 46 | }, 47 | "rules": {} 48 | }, 49 | "browserslist": [ 50 | "> 1%", 51 | "last 2 versions", 52 | "not dead" 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /juejin-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/public/favicon.ico -------------------------------------------------------------------------------- /juejin-app/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /juejin-app/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 30 | 31 | -------------------------------------------------------------------------------- /juejin-app/src/api/article.js: -------------------------------------------------------------------------------- 1 | import request from "../utils/request" 2 | 3 | export default { 4 | getArticleInfo(current, limit) { 5 | return request({ 6 | url: `/juejinserver/index_data/find_article/${current}/${limit}`, 7 | method: "get" 8 | }) 9 | }, 10 | 11 | getArticleDetailById(articleId) { 12 | return request({ 13 | url: `/juejinserver/index_data/findArticleDetail/${articleId}`, 14 | method: "get" 15 | }) 16 | }, 17 | 18 | getArticleTagsById(articleId) { 19 | return request({ 20 | url: `/juejinserver/index_data/findArticleTags/${articleId}`, 21 | method: "get" 22 | }) 23 | }, 24 | 25 | getArticleContentById(articleId) { 26 | return request({ 27 | url: `/juejinserver/article_content/getArticleContent/${articleId}`, 28 | method: "get" 29 | }) 30 | } 31 | } -------------------------------------------------------------------------------- /juejin-app/src/assets/css/article.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | background-color: #f4f5f5; 4 | } 5 | 6 | .page{ 7 | width: 98.93vw; 8 | } 9 | 10 | .page__header{ 11 | width: 100%; 12 | height: 60px; 13 | } 14 | 15 | .page__main{ 16 | max-width: 1140px; 17 | width: 100%; 18 | margin: 0 auto; 19 | position: relative; 20 | margin-top: 81px; 21 | margin-bottom: 80px; 22 | } 23 | .article-main{ 24 | width: 100%; 25 | margin: 0 auto; 26 | display: flex; 27 | 28 | } 29 | 30 | .article-area{ 31 | width: 820px; 32 | margin-right: 21px; 33 | } 34 | .article{ 35 | padding: 32px; 36 | box-sizing: border-box; 37 | background: #fff; 38 | border-radius: 4px; 39 | } 40 | .article>div{ 41 | margin-bottom: 20px; 42 | } 43 | .article .article-title{ 44 | font-size: 1.9rem; 45 | font-weight: 600; 46 | color: #252933; 47 | word-wrap: break-word; 48 | } 49 | .article .article-info-box{ 50 | width: 100%; 51 | height: 48px; 52 | /* background: rgba(0, 0, 0, 0.13); */ 53 | display: flex; 54 | align-items: center; 55 | } 56 | .user-photo{ 57 | width: 40px; 58 | height: 40px; 59 | border-radius: 50%; 60 | margin-right: 8px; 61 | overflow: hidden; 62 | display: flex; 63 | justify-content: center; 64 | align-items: center; 65 | } 66 | .user-photo img{ 67 | height: 100%; 68 | } 69 | .author-info{ 70 | flex: 1px; 71 | } 72 | .author-info .author-name{ 73 | font-size: 1rem; 74 | font-weight: 500; 75 | color: #515767; 76 | line-height: 2rem; 77 | display: flex; 78 | } 79 | .author-name span{ 80 | max-width: 20%; 81 | text-overflow: ellipsis; 82 | overflow: hidden; 83 | white-space: nowrap; 84 | display: block; 85 | margin-right: 5px; 86 | } 87 | .author-name img{ 88 | width: 35px; 89 | height: 16px; 90 | } 91 | .author-info .meta-box{ 92 | font-size: .9rem; 93 | color: #8a919f; 94 | line-height: 20px; 95 | display: flex; 96 | } 97 | .meta-box span{ 98 | margin-right: 15px; 99 | position: relative; 100 | } 101 | .meta-box span:not(:last-child)::after{ 102 | content: '·'; 103 | position: absolute; 104 | right: -8px; 105 | } 106 | .follow-btn{ 107 | width: 70px; 108 | height: 34px; 109 | font-size: 14px; 110 | color: #1e80ff; 111 | background: rgba(30, 128, 255, .05); 112 | border: 1px solid rgba(30, 128, 255, .3); 113 | border-radius: 4px; 114 | } 115 | .article-img{ 116 | width: 100%; 117 | height: 425px; 118 | overflow: hidden; 119 | display: flex; 120 | align-items: center; 121 | } 122 | .article-img img{ 123 | width: 100%; 124 | } 125 | 126 | 127 | .tag-list-box{ 128 | width: 100%; 129 | height: 32px; 130 | display: flex; 131 | /* background: rgba(0, 0, 0, 0.089); */ 132 | font-size: .9rem; 133 | line-height: 32px; 134 | color: #515767d8; 135 | } 136 | .tag-list-box .list{ 137 | margin-right: 20px; 138 | display: flex; 139 | } 140 | .tag-list-box .list>span{ 141 | display: block; 142 | color: #1e80ff; 143 | background: #eaf2ff; 144 | padding: 0 12px; 145 | border-radius: 4px; 146 | margin-right: 5px; 147 | } 148 | 149 | .comment{ 150 | width: 100%; 151 | /* height: 176px; */ 152 | background: #fff; 153 | margin-top: 21px; 154 | border-radius: 4px; 155 | padding: 0px 32px; 156 | box-sizing: border-box; 157 | /* margin-bottom: 60px; */ 158 | } 159 | .comment>div{ 160 | padding-top: 24px; 161 | } 162 | .comment-form .form-header{ 163 | font-size: 18px; 164 | line-height: 30px; 165 | font-weight: 600; 166 | color: #252933; 167 | margin-bottom: 24px; 168 | } 169 | .comment-form .form-content{ 170 | display: flex; 171 | /* height: 74px; */ 172 | } 173 | .form-userimg{ 174 | width: 40px; 175 | height: 40px; 176 | border-radius: 50%; 177 | overflow: hidden; 178 | display: flex; 179 | justify-content: center; 180 | align-items: center; 181 | margin-right: 16px; 182 | } 183 | .form-userimg>img{ 184 | height: 100%; 185 | } 186 | .form-box>textarea{ 187 | border: none; 188 | background: #f2f3f5; 189 | border-radius: 4px; 190 | padding: 8px 12px; 191 | box-sizing: border-box; 192 | color: #252933; 193 | line-height: 22px; 194 | font-size: 14px; 195 | } 196 | .form-box>textarea:focus-within{ 197 | outline: 1px solid var(--juejin-brand-1-normal); 198 | } 199 | .form-box:focus-within .action-box{ 200 | display: flex; 201 | } 202 | .action-box{ 203 | height: 36px; 204 | 205 | align-items: center; 206 | margin-top: 8px; 207 | font-size: 13px; 208 | color: #515767; 209 | /* display: flex; */ 210 | display: none; 211 | } 212 | 213 | .image-btn{ 214 | margin-left: 24px; 215 | } 216 | .submit-box{ 217 | flex: 1; 218 | display: flex; 219 | justify-content: flex-end; 220 | font-size: 14px; 221 | color: #86909c; 222 | line-height: 36px; 223 | } 224 | .submit-box .submit{ 225 | width: 92px; 226 | height: 36px; 227 | border: none; 228 | font-size: 14px; 229 | color: #fff; 230 | background: #1e80ff; 231 | border-radius: 4px; 232 | margin-left: 16px; 233 | } 234 | .hot-list{ 235 | /* height: 600px; */ 236 | } 237 | .title{ 238 | font-size: 18px; 239 | line-height: 30px; 240 | font-weight: 600; 241 | color: #252933; 242 | margin-bottom: 24px; 243 | display: flex; 244 | align-items: center; 245 | } 246 | .title>img{ 247 | width: 20px; 248 | height: 20px; 249 | } 250 | 251 | 252 | /* 一条评论的样式 开始 */ 253 | .comment-item{ 254 | width: 100%; 255 | display: flex; 256 | padding: 16px 0px; 257 | } 258 | .comment-userImg .userImg{ 259 | width: 40px; 260 | height: 40px; 261 | border-radius: 50%; 262 | overflow: hidden; 263 | display: flex; 264 | justify-content: center; 265 | align-items: center; 266 | } 267 | .comment-userImg .userImg>img{ 268 | height: 100%; 269 | } 270 | .comment-content{ 271 | flex: 1; 272 | margin-left: 16px; 273 | } 274 | .comment-main>div{ 275 | margin-bottom: 8px; 276 | } 277 | .user-box span:not(:last-child){ 278 | margin-right: 7px; 279 | } 280 | .user-box{ 281 | width: 100%; 282 | height: 26px; 283 | display: flex; 284 | align-items: center; 285 | } 286 | .user-box .name{ 287 | max-width: 128px; 288 | font-size: 15px; 289 | color: #252933; 290 | line-height: 26px; 291 | 292 | text-overflow: ellipsis; 293 | overflow: hidden; 294 | white-space: nowrap; 295 | } 296 | .user-box .level{ 297 | width: 45px; 298 | height: 16px; 299 | } 300 | .user-box .jueyou-level{ 301 | width: 45px; 302 | height: 16px; 303 | } 304 | .user-box img{ 305 | width: 45px; 306 | height: 16px; 307 | } 308 | .user-box .position{ 309 | max-width: 150px; 310 | font-size: 14px; 311 | color: #8a919f; 312 | 313 | text-overflow: ellipsis; 314 | overflow: hidden; 315 | white-space: nowrap; 316 | 317 | margin-left: 10px; 318 | } 319 | .user-box .time{ 320 | flex: 1; 321 | font-size: 14px; 322 | color: #8a919f; 323 | text-align: right; 324 | } 325 | .content-main{ 326 | font-size: 14px; 327 | line-height: 24px; 328 | color: #515767; 329 | } 330 | .comment-action-box{ 331 | display: flex; 332 | font-size: 14px; 333 | color: #8a919f; 334 | } 335 | .comment-action-box div{ 336 | display: flex; 337 | align-items: center; 338 | margin-right: 15px; 339 | } 340 | .comment-action-box img{ 341 | width: 16px; 342 | height: 16px; 343 | margin-right: 5px; 344 | } 345 | /* 一条评论的样式 结束 */ 346 | 347 | 348 | 349 | 350 | /* 侧边栏 开始 */ 351 | .sidebar{ 352 | position: absolute; 353 | top: 0; 354 | right: 0; 355 | width: 300px; 356 | height: 600px; 357 | flex: 1; 358 | /* background: rgba(146, 231, 77, 0.63); */ 359 | /* background: #fff; */ 360 | 361 | } 362 | .sidebar>div{ 363 | /* width: 100%; */ 364 | /* padding: 20px; */ 365 | margin-bottom: 20px; 366 | box-sizing: border-box; 367 | border-radius: 4px; 368 | background: #fff; 369 | } 370 | 371 | /* 作者信息栏 */ 372 | .author-block{ 373 | /* background: #fff; */ 374 | padding: 20px; 375 | } 376 | .user-item{ 377 | display: flex; 378 | padding-bottom: 17px; 379 | position: relative; 380 | } 381 | .userimg{ 382 | width: 48px; 383 | height: 48px; 384 | border-radius: 50%; 385 | overflow: hidden; 386 | display: flex; 387 | justify-content: center; 388 | align-items: center; 389 | } 390 | .userimg>img{ 391 | height: 48px; 392 | /* margin-left: 5px; */ 393 | } 394 | .info-box{ 395 | margin-left: 16px; 396 | } 397 | .info-box>div{ 398 | max-width: 196px; 399 | height: 24px; 400 | font-size: 16px; 401 | font-weight: 400; 402 | overflow: hidden; 403 | text-overflow: ellipsis; 404 | white-space: nowrap; 405 | } 406 | .info-box .userName{ 407 | color: #252933; 408 | display: flex; 409 | align-items: center; 410 | } 411 | .userName>span{ 412 | max-width: 155px; 413 | overflow: hidden; 414 | text-overflow: ellipsis; 415 | white-space: nowrap; 416 | } 417 | .userName>img{ 418 | width: 35px; 419 | height: 16px; 420 | } 421 | .info-box .position{ 422 | color: #515767; 423 | } 424 | 425 | .stat-item{ 426 | margin-top: 17px; 427 | display: flex; 428 | font-size: 15px; 429 | line-height: 25px; 430 | color: #252933; 431 | } 432 | .stat-item>div{ 433 | width: 25px; 434 | height: 25px; 435 | border-radius: 50%; 436 | background: #e1efff; 437 | margin-right: 12px; 438 | display: flex; 439 | justify-content: center; 440 | align-items: center; 441 | } 442 | .stat-item img{ 443 | width: 16px; 444 | height: 16px; 445 | } 446 | .user-item::after{ 447 | content: ''; 448 | position: absolute; 449 | bottom: 0; 450 | width: 100%; 451 | height: 1px; 452 | background: rgba(0, 0, 0, 0.178); 453 | transform: scaleY(.5); 454 | } 455 | 456 | 457 | /* 文章目录 */ 458 | ul{ 459 | list-style: none; 460 | } 461 | a{ 462 | text-decoration: none; 463 | color: #515767; 464 | } 465 | .sticky-block-box{ 466 | width: 300px; 467 | position: relative; 468 | /* margin-top: 5px; */ 469 | } 470 | .activeBox{ 471 | transition: all .3s linear; 472 | } 473 | .stickyBoxStatus{ 474 | position: fixed; 475 | top: 20px; 476 | } 477 | .sticky-title{ 478 | position: relative; 479 | margin: 0 20px; 480 | padding: 16px 0; 481 | } 482 | .sticky-title::after{ 483 | content: ''; 484 | position: absolute; 485 | bottom: 0px; 486 | left: 0px; 487 | width: 100%; 488 | height: 1px; 489 | background: rgba(0, 0, 0, 0.178); 490 | transform: scaleY(.5); 491 | } 492 | .sticky-content{ 493 | width: 100%; 494 | max-height: 460px; 495 | overflow-y: scroll; 496 | padding: 20px 5px; 497 | box-sizing: border-box; 498 | } 499 | 500 | /* 目录滑动条样式 */ 501 | .sticky-content::-webkit-scrollbar{ 502 | width: 6px; 503 | } 504 | .sticky-content::-webkit-scrollbar-thumb{ 505 | border-radius: 10px; 506 | background: #c2c8d19b; 507 | margin-left: 10px; 508 | } 509 | 510 | .sticky-list{ 511 | width: 100%; 512 | padding-left: 20px; 513 | box-sizing: border-box; 514 | position: relative; 515 | color: #515767; 516 | } 517 | .a-container{ 518 | height: 44px; 519 | border-radius: 4px; 520 | font-size: 14px; 521 | /* color: #515767; */ 522 | line-height: 44px; 523 | padding-left: 5px; 524 | box-sizing: border-box; 525 | 526 | text-overflow: ellipsis; 527 | overflow: hidden; 528 | white-space: nowrap; 529 | 530 | } 531 | .a-container:hover{ 532 | background: #f7f8fa; 533 | } 534 | 535 | .first{ 536 | color: #1e80ff; 537 | } 538 | .first::after{ 539 | content: ''; 540 | /* border-left: 2 #1e80ff; */ 541 | position: absolute; 542 | margin-top: -32px; 543 | /* top: 13px; */ 544 | left: 0; 545 | width: 5px; 546 | height: 18px; 547 | background: #1e80ff; 548 | border-radius: 3px; 549 | margin-left: 5px; 550 | } 551 | /* 侧边栏 结束 */ 552 | 553 | /* 左侧功能栏 */ 554 | .article-suspended-panel{ 555 | width: 48px; 556 | height: 300px; 557 | /* background: rgba(0, 0, 0, 0.157); */ 558 | position: fixed; 559 | top: 140px; 560 | left: 100px; 561 | } 562 | .panel-btn{ 563 | width: 100%; 564 | height: 48px; 565 | border-radius: 50%; 566 | background: #fff; 567 | margin-bottom: 20px; 568 | box-shadow: 1px 1px 20px #dadddd; 569 | 570 | display: flex; 571 | justify-content: center; 572 | align-items: center; 573 | position: relative; 574 | 575 | } 576 | .panel-btn>img{ 577 | width: 17px; 578 | height: 17px; 579 | } 580 | .panel-btn:nth-child(4){ 581 | margin-bottom: 40px; 582 | position: relative; 583 | } 584 | .panel-btn:nth-child(4)::after{ 585 | content: ''; 586 | position: absolute; 587 | bottom: -20px; 588 | width: 30px; 589 | height: 1px; 590 | background: rgba(0, 0, 0, 0.157); 591 | transform: scaleY(.5); 592 | } 593 | 594 | .btn-num{ 595 | position: absolute; 596 | top: 0; 597 | left: 75%; 598 | height: 17px; 599 | line-height: 17px; 600 | padding: 0 5px; 601 | border-radius: 9px; 602 | font-size: 11px; 603 | text-align: center; 604 | white-space: nowrap; 605 | background-color: #c2c8d1; 606 | color: #fff; 607 | } 608 | -------------------------------------------------------------------------------- /juejin-app/src/assets/css/header.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | background-color: #f4f5f5; 4 | } 5 | 6 | a { 7 | text-decoration: none; 8 | color: var(--juejin-font-3); 9 | } 10 | a:visited { 11 | color: #86909c; 12 | } 13 | 14 | ul { 15 | list-style: none; 16 | } 17 | 18 | .list__contents a:hover { 19 | color: var(--juejin-font-1); 20 | } 21 | .juejin__page { 22 | width: 100%; 23 | height: 100%; 24 | } 25 | 26 | /* 导航栏部分 */ 27 | .page__header { 28 | width: 100%; 29 | height: 106px; 30 | position: fixed; 31 | top: 0; 32 | 33 | z-index: 5; 34 | } 35 | .page__header__box{ 36 | width: 100%; 37 | height: 106px; 38 | position: relative; 39 | top: 0px; 40 | } 41 | .activeBox{ 42 | transition: all .3s linear; 43 | } 44 | 45 | .page__header__nav { 46 | width: 100%; 47 | height: 60px; 48 | display: flex; 49 | justify-content: center; 50 | position: relative; 51 | top: 0px; 52 | background: rgb(255, 255, 255); 53 | } 54 | 55 | .page__header__nav::after { 56 | content: ''; 57 | position: absolute; 58 | bottom: 0; 59 | width: 100%; 60 | height: 1px; 61 | background-color: var(--juejin-font-4); 62 | transform: scaleY(0.5); 63 | } 64 | 65 | /* 标签页面 */ 66 | .page__header__tag{ 67 | width: 100%; 68 | height: 46px; 69 | background: #fff; 70 | position: relative; 71 | top: 0; 72 | z-index: 3; 73 | } 74 | .page__header__tag::after { 75 | content: ''; 76 | position: absolute; 77 | bottom: 0; 78 | width: 100%; 79 | height: 1px; 80 | background-color: var(--juejin-font-4); 81 | transform: scaleY(0.5); 82 | } 83 | .tag__contents{ 84 | height: 100%; 85 | /* background: rgba(239, 24, 24, 0.199); */ 86 | width: 960px; 87 | margin: 0 auto; 88 | display: flex; 89 | justify-content: space-between; 90 | /* overflow-x: scroll; */ 91 | } 92 | .tag__list{ 93 | display: flex; 94 | height: 100%; 95 | } 96 | .tag__manage{ 97 | display: flex; 98 | align-items: center; 99 | } 100 | .tag__manage > span{ 101 | font-size: .9rem; 102 | color: var(--juejin-font-2); 103 | } 104 | .page__header__tag a:hover{ 105 | color: var(--juejin-brand-1-normal); 106 | } 107 | .tag__list .list__contents{ 108 | margin-left: 0; 109 | } 110 | .tag__list li:nth-child(1){ 111 | margin-left: 0; 112 | } 113 | 114 | 115 | 116 | /* 掘金图标 */ 117 | .nav__sign { 118 | display: flex; 119 | } 120 | 121 | .logo__img { 122 | width: 107px; 123 | height: 22px; 124 | margin-left: 24px; 125 | margin-top: 20px; 126 | } 127 | 128 | /* 导航按钮 */ 129 | .nav__main-list { 130 | height: 100%; 131 | display: flex; 132 | } 133 | 134 | .list-left { 135 | width: 37vw; 136 | } 137 | 138 | .list-left, 139 | .list-right { 140 | display: flex; 141 | } 142 | 143 | .list__contents { 144 | height: 100%; 145 | display: flex; 146 | margin-left: 10px; 147 | } 148 | 149 | .list__contents>li { 150 | display: flex; 151 | font-size: .9rem; 152 | align-items: center; 153 | margin: 0 .65rem; 154 | } 155 | 156 | .list__contents li a { 157 | display: inline-block; 158 | } 159 | 160 | /* 开放社区下拉框 */ 161 | li>.community::after { 162 | content: ''; 163 | /* 注意这里的伪类要为块级元素才能旋转 */ 164 | display: inline-block; 165 | width: 0px; 166 | height: 0px; 167 | border: 4px solid #515761; 168 | border-left-color: transparent; 169 | border-right-color: transparent; 170 | border-bottom-color: transparent; 171 | position: relative; 172 | left: 2px; 173 | transform-origin: 50% 25%; 174 | transition: transform .5s ease-in-out; 175 | } 176 | 177 | .list__contents>li:nth-child(7):hover .community { 178 | color: var(--juejin-brand-1-normal); 179 | } 180 | 181 | .list__contents>li:nth-child(7):hover .community::after { 182 | border: 4px solid var(--juejin-brand-1-normal); 183 | border-left-color: transparent; 184 | border-right-color: transparent; 185 | border-bottom-color: transparent; 186 | transform: rotate(-180deg); 187 | } 188 | 189 | .list__contents>li:nth-child(7):hover .community__list { 190 | display: block; 191 | } 192 | .community__list{ 193 | display: none; 194 | position: absolute; 195 | /* width: 200px; */ 196 | padding: 5px; 197 | background: #fff; 198 | border-radius: 5px; 199 | box-shadow: 4px 0 70px #8a919f2f, -4px 0 70px #8a919f25, 0 4px 70px #8a919f3c; 200 | top: 52px; 201 | z-index: 10; 202 | } 203 | .community__list li { 204 | display: inline-block; 205 | /* width: 90px; */ 206 | height: 26px; 207 | color: var(--juejin-font-2); 208 | line-height: 26px; 209 | padding: 6px 20px 5px 9px; 210 | } 211 | 212 | .community__list li:hover { 213 | background-color: #f2f3f5; 214 | border-radius: 3px; 215 | } 216 | 217 | .community__list span::before { 218 | content: ''; 219 | display: inline-block; 220 | position: relative; 221 | top: 4px; 222 | width: 18px; 223 | height: 18px; 224 | margin: 0 12px 0 2px; 225 | } 226 | 227 | .community__list li:nth-child(1) span::before { 228 | background-image: url('../img/comm.png'); 229 | background-size: 18px 18px; 230 | } 231 | 232 | 233 | /* 搜索框 */ 234 | .input__frame{ 235 | height: 36px; 236 | border: 1px solid #86909ccf; 237 | border-radius: 3px; 238 | display: flex; 239 | align-items: center; 240 | } 241 | 242 | .input:focus{ 243 | outline: none; 244 | border-radius: 3px; 245 | } 246 | .input__frame:focus-within{ 247 | /* width: 400px; */ 248 | transform-origin: 0 0; 249 | /* transform: scaleX(1.32); */ 250 | outline: 1px solid var(--juejin-brand-1-normal); 251 | } 252 | .input__frame:focus-within .input__sign{ 253 | background: var(--juejin-brand-5-light); 254 | } 255 | .input__frame:focus-within .input__typehead{ 256 | display: block; 257 | } 258 | 259 | .input__frame .input { 260 | flex: 1; 261 | width: 316px; 262 | height: 12px; 263 | border: none; 264 | padding: 12px 7px; 265 | -webkit-text-fill-color: var(--juejin-font-3); 266 | } 267 | 268 | .input__frame .input__sign { 269 | width: 44px; 270 | height: 30px; 271 | background: #86909c23; 272 | border-radius: 3px; 273 | margin-right: 3px; 274 | /* margin-left: 140px; */ 275 | } 276 | 277 | .input__sign .input__img{ 278 | width: 16px; 279 | height: 16px; 280 | background-image: url('../img/input.png'); 281 | background-size: 16px 16px; 282 | margin: 7px 15px; 283 | } 284 | .input__frame .input__typehead{ 285 | display: none; 286 | position: absolute; 287 | top: 52px; 288 | width: auto; 289 | background: #fff; 290 | border: 1px solid #5157613d; 291 | border-radius: 2px; 292 | z-index: 10; 293 | font-size: .8rem; 294 | color: var(--juejin-font-3); 295 | } 296 | .input__typehead .title{ 297 | width: 100%; 298 | display: flex; 299 | justify-content: space-between; 300 | position: relative; 301 | } 302 | .input__typehead>.title::after{ 303 | content: ''; 304 | position: absolute; 305 | width: 100%; 306 | height: 1px; 307 | bottom: 0; 308 | background-color: var(--juejin-font-4); 309 | transform: scaleY(.5); 310 | } 311 | .title>span{ 312 | display: inline-block; 313 | margin: 10px; 314 | } 315 | .input__typehead .list .list-item{ 316 | width: 342px; 317 | display: flex; 318 | padding: 10px; 319 | } 320 | .list-item:hover{ 321 | background-color: #eff2f5; 322 | } 323 | 324 | 325 | /* 创作者中心 */ 326 | .creator { 327 | /* width: 115px; */ 328 | height: 36px; 329 | display: flex; 330 | } 331 | 332 | .creator .crea-btn { 333 | width: 80px; 334 | height: 100%; 335 | font-size: .8rem; 336 | color: #fff; 337 | background-color: var(--juejin-brand-1-normal); 338 | border: none; 339 | border-radius: 3px 0 0 3px; 340 | position: relative; 341 | } 342 | 343 | .crea-btn::after { 344 | content: ''; 345 | position: absolute; 346 | top: 0; 347 | right: 0; 348 | width: 1px; 349 | height: 36px; 350 | background-color: var(--juejin-brand-4-disable); 351 | transform: scaleX(0.3); 352 | } 353 | 354 | .creator .more { 355 | width: 20px; 356 | height: 100%; 357 | background-color: var(--juejin-brand-1-normal); 358 | border-radius: 0 3px 3px 0; 359 | } 360 | 361 | .more:hover, 362 | .crea-btn:hover { 363 | background-color: var(--juejin-brand-2-hover); 364 | } 365 | 366 | #dianj { 367 | display: none; 368 | } 369 | 370 | input:checked~ul { 371 | display: block; 372 | } 373 | 374 | input:checked~label .more__tri { 375 | transform-origin: 55% 75%; 376 | transform: rotateZ(225deg); 377 | } 378 | 379 | .more__tri { 380 | width: 0px; 381 | height: 0px; 382 | border: 4px solid #fff; 383 | border-left-color: transparent; 384 | border-top-color: transparent; 385 | transform: rotateZ(45deg); 386 | position: relative; 387 | top: 12px; 388 | left: 6px; 389 | } 390 | 391 | .more__list { 392 | display: none; 393 | position: absolute; 394 | width: 100px; 395 | padding: 5px; 396 | background: #fff; 397 | border-radius: 5px; 398 | box-shadow: 4px 0 70px #8a919f2f, -4px 0 70px #8a919f25, 0 4px 70px #8a919f3c; 399 | top: 52px; 400 | z-index: 10; 401 | } 402 | 403 | .more__list li { 404 | display: inline-block; 405 | width: 90px; 406 | height: 26px; 407 | color: var(--juejin-font-2); 408 | line-height: 26px; 409 | padding: 6px 1px 5px 9px; 410 | } 411 | 412 | .more__list li:hover { 413 | background-color: #f2f3f5; 414 | border-radius: 3px; 415 | } 416 | 417 | .more__list span::before { 418 | content: ''; 419 | display: inline-block; 420 | position: relative; 421 | top: 2px; 422 | width: 18px; 423 | height: 18px; 424 | margin: 0 12px 0 2px; 425 | } 426 | 427 | .more__list li:nth-child(1) span::before { 428 | background-image: url('../img/pencil.png'); 429 | background-size: 18px 18px; 430 | } 431 | 432 | .more__list li:nth-child(2) span::before { 433 | background-image: url('../img/dia.png'); 434 | background-size: 18px 18px; 435 | } 436 | 437 | 438 | 439 | /* 会员 */ 440 | .vip__title { 441 | height: 100%; 442 | display: flex; 443 | align-items: center; 444 | color: var(--juejin-font-3); 445 | } 446 | 447 | .vip__title .vip__img { 448 | width: 24px; 449 | height: 24px; 450 | margin-right: 5px; 451 | } 452 | 453 | /* 通知 */ 454 | .inform { 455 | height: 100%; 456 | display: flex; 457 | align-items: center; 458 | } 459 | 460 | .inform__img { 461 | width: 24px; 462 | height: 24px; 463 | background-image: url('../img/inform-logo-off.png'); 464 | background-size: 24px 24px; 465 | } 466 | 467 | .inform__img:hover { 468 | background-image: url('../img/inform-logo-on.png'); 469 | } 470 | 471 | /* 用户头像 */ 472 | .user { 473 | height: 100%; 474 | display: flex; 475 | align-items: center; 476 | } 477 | 478 | .user__img { 479 | width: 36px; 480 | /* transform: scale(1); */ 481 | height: 36px; 482 | object-fit: cover; 483 | border-radius: 50%; 484 | } 485 | 486 | 487 | /* 一键回到顶部 */ 488 | .page__header__side{ 489 | width: 38px; 490 | height: 38px; 491 | background: #fff; 492 | border-radius: 50%; 493 | position: fixed; 494 | bottom: 50px; 495 | right: 50px; 496 | box-shadow: 1px 1px 10px #5157673f; 497 | } 498 | .page__header__side:hover{ 499 | box-shadow: 2px 2px 10px #515767; 500 | } 501 | .backTop{ 502 | width: 100%; 503 | height: 100%; 504 | display: flex; 505 | 506 | justify-content: center; 507 | align-items: center; 508 | } 509 | .backTop img{ 510 | width: 12px; 511 | height: 18px; 512 | margin-top: 4px; 513 | } 514 | -------------------------------------------------------------------------------- /juejin-app/src/assets/css/main.css: -------------------------------------------------------------------------------- 1 | 2 | .page__main{ 3 | width: 98.93vw; 4 | } 5 | .page__main__contents{ 6 | /* position: absolute; */ 7 | width: 960px; 8 | margin: 0 auto; 9 | /* background: rgba(50, 241, 16, 0.215); */ 10 | margin-top: 130px; 11 | position: relative; 12 | } 13 | .contents__list{ 14 | width: 700px; 15 | /* height: 100%; */ 16 | /* background: rgba(17, 0, 255, 0.142); */ 17 | margin-right: 260px; 18 | } 19 | .list__header{ 20 | width: 676px; 21 | /* height: 47px; */ 22 | background: #fff; 23 | padding: 17px 12px; 24 | position: relative; 25 | } 26 | .list__content{ 27 | width: 100%; 28 | /* height: 100vh; 29 | overflow: scroll; */ 30 | background: #fff; 31 | } 32 | .article__list{ 33 | width: 100%; 34 | } 35 | .list__item{ 36 | /* width: 660px; 37 | height: 129px; */ 38 | padding: 12px 20px 0 20px; 39 | position: relative; 40 | } 41 | .list__item:hover{ 42 | background: #fafafa; 43 | } 44 | .list__header::after{ 45 | content: ''; 46 | position: absolute; 47 | bottom: 0; 48 | left: 0; 49 | width: 100%; 50 | height: 1px; 51 | background: var(--juejin-font-4); 52 | transform: scaleY(.5); 53 | } 54 | .list__item::after{ 55 | content: ''; 56 | position: absolute; 57 | bottom: 0; 58 | left: 21px; 59 | width: 94%; 60 | height: 1px; 61 | background: var(--juejin-font-4); 62 | transform: scaleY(.5); 63 | } 64 | 65 | /* 内容头部标签 */ 66 | .nav__list{ 67 | display: flex; 68 | height: 19px; 69 | } 70 | .nav__list>ul{ 71 | display: flex; 72 | } 73 | .nav__list li{ 74 | font-size: .9rem; 75 | margin: 0 13px; 76 | position: relative; 77 | } 78 | .nav__list li:not(:last-child)::after{ 79 | content: ''; 80 | position: absolute; 81 | right: -13px; 82 | top: -5px; 83 | width: 1px; 84 | height: 155%; 85 | background: var(--juejin-font-4); 86 | transform: scaleY(.5); 87 | } 88 | 89 | .tag__a{ 90 | display: flex; 91 | } 92 | .tag__name{ 93 | position: relative; 94 | margin-right: 16px; 95 | } 96 | .tag__name:not(:last-child)::after{ 97 | content: '·'; 98 | position: absolute; 99 | right: -8px; 100 | top: 0px; 101 | transform: scale(2); 102 | color: #4e59699a; 103 | } 104 | 105 | .nav__list a:hover{ 106 | color: var(--juejin-brand-1-normal); 107 | } 108 | 109 | 110 | /* 文章列表 */ 111 | .list__item>.entry{ 112 | display: flex; 113 | width: 100%; 114 | height: 100%; 115 | flex-wrap: wrap; 116 | margin: 5px 0; 117 | } 118 | .article__inform{ 119 | width: 100%; 120 | height: 22px; 121 | display: flex; 122 | } 123 | .article__content{ 124 | width: 100%; 125 | height: 97px; 126 | display: flex; 127 | } 128 | .article__inform>.nav__list li{ 129 | font-size: .7rem; 130 | } 131 | .article__inform>.nav__list li:nth-child(1){ 132 | margin-left: 0; 133 | } 134 | 135 | .content-main{ 136 | width: 200px; 137 | flex: 1 0 auto; 138 | } 139 | 140 | .content-main .title{ 141 | font-size: .98rem; 142 | 143 | font-weight: bold; 144 | margin: 8px 0 12px 0; 145 | text-overflow: ellipsis; 146 | overflow: hidden; 147 | white-space: nowrap; 148 | } 149 | .titleColor{ 150 | color: #000; 151 | } 152 | 153 | .content-main .abstract{ 154 | /* width: 100px; */ 155 | font-size: .82rem; 156 | margin-bottom: 10px; 157 | text-overflow: ellipsis; 158 | -webkit-box-orient: vertical; 159 | /* -webkit-line-clamp: 1 */ 160 | overflow: hidden; 161 | white-space: nowrap; 162 | } 163 | .action-list{ 164 | display: flex; 165 | font-size: 0.8em; 166 | color: var(--juejin-font-2); 167 | 168 | } 169 | .action-list .item{ 170 | display: inline-block; 171 | margin-right: 20px; 172 | display: flex; 173 | } 174 | .action-list>li>i{ 175 | display: block; 176 | width: 16px; 177 | height: 16px; 178 | background-size: 16px 16px; 179 | margin-right: 4px; 180 | } 181 | .action-list .eye{ 182 | background-image: url("../img/eye.png"); 183 | } 184 | .action-list .zan{ 185 | background-image: url("../img/zan__off.png"); 186 | } 187 | .action-list .cloud{ 188 | margin-top: 2px; 189 | background-image: url("../img/cloud__off.png"); 190 | } 191 | .action-list .zan:hover{ 192 | background-image: url("../img/zan__on.png"); 193 | } 194 | .action-list .cloud:hover{ 195 | /* margin-top: 2px; */ 196 | background-image: url("../img/cloud__on.png"); 197 | } 198 | .content-img{ 199 | width: 120px; 200 | height: 80px; 201 | margin-left: 24px; 202 | flex: 0 0 auto; 203 | border-radius: 2px; 204 | overflow: hidden; 205 | display: flex; 206 | align-items: center; 207 | } 208 | .article__content img{ 209 | display: block; 210 | /* transform: translate(0,-50%); */ 211 | width: 120px; 212 | /* height: 160px; */ 213 | /* margin-left: 24px; 214 | flex: 0 0 auto; */ 215 | border-radius: 2px; 216 | transform: scale(1.2); 217 | } 218 | 219 | .sidebar{ 220 | width: 240px; 221 | position: absolute; 222 | top: 0; 223 | right: 0; 224 | } 225 | .sidebar>div{ 226 | margin-bottom: 16px; 227 | border-radius: 3px; 228 | } 229 | .signin__top{ 230 | width: 100%; 231 | height: 96px; 232 | padding: 16px; 233 | box-sizing: border-box; 234 | background: #fff; 235 | display: flex; 236 | flex-direction: column; 237 | justify-content: space-between; 238 | } 239 | .first__line{ 240 | display: flex; 241 | justify-content: space-between; 242 | 243 | } 244 | .icon__text{ 245 | display: flex; 246 | align-items: center; 247 | font-size: 18px; 248 | font-weight: 500; 249 | } 250 | .icon__text>img{ 251 | width: 20px; 252 | height: 20px; 253 | margin-right: 10px; 254 | } 255 | .signedin__btn{ 256 | width: 72px; 257 | height: 32px; 258 | border: none; 259 | border-radius: 20px; 260 | font-size: 14px; 261 | color: #1e80ff; 262 | background: #e8f3ff; 263 | 264 | } 265 | .second__line{ 266 | font-size: 14px; 267 | display: flex; 268 | justify-content: center; 269 | color: #86909c; 270 | } 271 | 272 | 273 | 274 | .sidebar__block{ 275 | width: 100%; 276 | /* height: 200px; */ 277 | overflow: hidden; 278 | position: relative; 279 | background: #fff; 280 | } 281 | .sidebar__block>img{ 282 | width: 100%; 283 | } 284 | .sidebar__block:not(:last-child)::after{ 285 | content: '广告'; 286 | position: absolute; 287 | bottom: 10px; 288 | right: 10px; 289 | width: 36px; 290 | height: 19px; 291 | background: rgba(0, 0, 0, 0.151); 292 | border: 1px solid rgb(199, 199, 199); 293 | border-radius: 3px; 294 | 295 | font-size: 8px; 296 | color: #fff; 297 | text-align: center; 298 | line-height: 19px; 299 | } 300 | .block-body{ 301 | width: 100%; 302 | padding: 16px; 303 | box-sizing: border-box; 304 | background: #fff; 305 | display: flex; 306 | } 307 | .block-body>img{ 308 | width: 50px; 309 | height: 50px; 310 | margin-right: 12px; 311 | } 312 | .block-body-text{ 313 | display: flex; 314 | flex-direction: column; 315 | justify-content: space-between; 316 | } 317 | .block-body-text>:first-child{ 318 | font-size: 14px; 319 | } 320 | .block-body-text>:last-child{ 321 | font-size: 12px; 322 | line-height: 20px; 323 | color: #86909c; 324 | } 325 | .header-block{ 326 | padding: 12px 15.6px; 327 | box-sizing: border-box; 328 | font-size: 14px; 329 | color: #333333; 330 | } 331 | .author-list{ 332 | font-size: 14px; 333 | color: #007fff; 334 | text-align: center; 335 | padding: 12px 0; 336 | } 337 | 338 | .user-top>div{ 339 | position: relative; 340 | } 341 | .user-top>div:not(:last-child)::after{ 342 | content: ''; 343 | position: absolute; 344 | bottom: 0; 345 | left: 0; 346 | width: 100%; 347 | height: 1px; 348 | background: #e8f3ff; 349 | } 350 | .user-list>.item{ 351 | padding: 12px 15.6px; 352 | box-sizing: border-box; 353 | display: flex; 354 | } 355 | .item__img-box{ 356 | width: 45.6px; 357 | height: 45.6px; 358 | border-radius: 50%; 359 | overflow: hidden; 360 | display: flex; 361 | align-items: center; 362 | justify-content: center; 363 | margin-right: 6px; 364 | } 365 | .item__img-box>img{ 366 | width: 100%; 367 | } 368 | .item__user-info{ 369 | height: 100%; 370 | font-size: 14px; 371 | color: #333333; 372 | line-height: 45.6px; 373 | display: flex; 374 | align-items: center; 375 | } 376 | .item__user-info>img{ 377 | width: 35px; 378 | height: 16px; 379 | margin-left: 6px; 380 | } 381 | .user-body{ 382 | background: #fff; 383 | } -------------------------------------------------------------------------------- /juejin-app/src/assets/img/article__01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/article__01.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/cloud__off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/cloud__off.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/cloud__on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/cloud__on.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/comm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/comm.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/day.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/dia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/dia.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/eye.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/eye.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/fire.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/fire.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/inform-logo-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/inform-logo-off.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/inform-logo-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/inform-logo-on.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/input-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/input-1.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/input.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/input.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/juejin-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/juejin-logo.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/juejinVip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/juejinVip.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/jy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/jy.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/lv-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/lv-2.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/panelFa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/panelFa.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/panelLiu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/panelLiu.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/panelQuan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/panelQuan.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/panelStar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/panelStar.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/panelZan__off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/panelZan__off.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/panelZan__on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/panelZan__on.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/paneljing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/paneljing.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/pencil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/pencil.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/ping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/ping.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/top.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/user-top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/user-top.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/user.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/userEye.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/userEye.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/userZan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/userZan.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/zan__off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/zan__off.png -------------------------------------------------------------------------------- /juejin-app/src/assets/img/zan__on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-app/src/assets/img/zan__on.png -------------------------------------------------------------------------------- /juejin-app/src/assets/js/EventBus.js: -------------------------------------------------------------------------------- 1 | import Vue from './vue' 2 | export default new Vue() -------------------------------------------------------------------------------- /juejin-app/src/components/Header.vue: -------------------------------------------------------------------------------- 1 | 138 | 139 | 240 | 241 | -------------------------------------------------------------------------------- /juejin-app/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import ElementUI from 'element-ui'; 3 | import 'element-ui/lib/theme-chalk/index.css'; 4 | import App from './App.vue' 5 | // 引入路由器 6 | import router from './router' 7 | // 设置标题 8 | import VueWechatTitle from 'vue-wechat-title' 9 | 10 | Vue.use(ElementUI); 11 | Vue.use(VueWechatTitle) 12 | Vue.config.productionTip = false 13 | 14 | new Vue({ 15 | render: h => h(App), 16 | router, 17 | }).$mount('#app') 18 | -------------------------------------------------------------------------------- /juejin-app/src/pages/Index.vue: -------------------------------------------------------------------------------- 1 | 201 | 202 | 348 | 349 | -------------------------------------------------------------------------------- /juejin-app/src/router/index.js: -------------------------------------------------------------------------------- 1 | // 路由配置 2 | import VueRouter from "vue-router"; 3 | import Vue from "vue"; 4 | 5 | Vue.use(VueRouter); 6 | 7 | export default new VueRouter({ 8 | routes: [{ 9 | path: "/", 10 | component: () => import("../pages/Index"), 11 | meta: { 12 | title: '掘金' 13 | } 14 | }, { 15 | path: "/detail/:articleId", 16 | component: () => import("../pages/Detail"), 17 | meta: { 18 | title: '文章详情' 19 | } 20 | }] 21 | }); -------------------------------------------------------------------------------- /juejin-app/src/utils/request.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | 3 | const service = axios.create({ 4 | baseURL: 'http://106.14.212.78:9088', 5 | timeout: 18000 6 | }) 7 | 8 | export default service -------------------------------------------------------------------------------- /juejin-app/src/utils/timeDispose.js: -------------------------------------------------------------------------------- 1 | // 时间戳转为距今多久 2 | let timeInterval = function(dateTime){ 3 | // 如果为null,则格式化当前时间 4 | if (!dateTime) dateTime = Number(new Date()); 5 | // 如果dateTime长度为10或者13,则为秒和毫秒的时间戳,如果超过13位,则为其他的时间格式 6 | if (dateTime.toString().length == 10) dateTime *= 1000; 7 | let timestamp = +new Date(Number(dateTime)); 8 | 9 | let timer = (Number(new Date()) - timestamp) / 1000; 10 | // 如果小于5分钟,则返回"刚刚",其他以此类推 11 | let tips = ''; 12 | switch (true) { 13 | case timer < 300: 14 | tips = '刚刚'; 15 | break; 16 | case timer >= 300 && timer < 3600: 17 | tips = parseInt(timer / 60) + '分钟前'; 18 | break; 19 | case timer >= 3600 && timer < 86400: 20 | tips = parseInt(timer / 3600) + '小时前'; 21 | break; 22 | case timer >= 86400 && timer < 2592000: 23 | tips = parseInt(timer / 86400) + '天前'; 24 | break; 25 | default: 26 | if (timer >= 2592000 && timer < 365 * 86400) { 27 | tips = parseInt(timer / (86400 * 30)) + '个月前'; 28 | } else { 29 | tips = parseInt(timer / (86400 * 365)) + '年前'; 30 | } 31 | } 32 | return tips; 33 | } 34 | 35 | // 显示上午好 下午好 晚上好 36 | let newTime = function(){ 37 | let date = new Date(); 38 | let h = date.getHours(); 39 | if(h < 12 && h>=6){ 40 | return '上午好' 41 | }else if(h < 18 && h>=12){ 42 | return '下午好' 43 | }else{ 44 | return '晚上好' 45 | } 46 | } 47 | 48 | // 格式化文章时间 49 | let setArticleTiem = function(time){ 50 | let date = new Date(time); 51 | let Y = date.getFullYear(); 52 | let M = (date.getMonth()+1 < 10 ? '0'+(date.getMonth()+1) : date.getMonth()+1); 53 | let D = date.getDate(); 54 | let h = date.getHours() + ':'; 55 | let m = date.getMinutes(); 56 | return `${Y}年${M}月${D}日 ${h}${m}`; 57 | } 58 | 59 | module.exports = { 60 | timeInterval, 61 | setArticleTiem, 62 | newTime 63 | } -------------------------------------------------------------------------------- /juejin-app/vue.config.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require('@vue/cli-service') 2 | module.exports = defineConfig({ 3 | transpileDependencies: true, 4 | lintOnSave: false 5 | }) 6 | -------------------------------------------------------------------------------- /juejin-server/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /juejin-server/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IamTrust/juejin/0bad1b791b5e592ae08bf96c9a27be861ff7924a/juejin-server/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /juejin-server/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar 3 | -------------------------------------------------------------------------------- /juejin-server/jue_jin.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat Premium Data Transfer 3 | 4 | Source Server : aliyun 5 | Source Server Type : MySQL 6 | Source Server Version : 80018 7 | Source Host : rm-wz9mc28o4x3thxw56bo.mysql.rds.aliyuncs.com:3306 8 | Source Schema : jue_jin 9 | 10 | Target Server Type : MySQL 11 | Target Server Version : 80018 12 | File Encoding : 65001 13 | 14 | Date: 26/08/2022 10:26:15 15 | */ 16 | 17 | SET NAMES utf8mb4; 18 | SET FOREIGN_KEY_CHECKS = 0; 19 | 20 | -- ---------------------------- 21 | -- Table structure for article_content 22 | -- ---------------------------- 23 | DROP TABLE IF EXISTS `article_content`; 24 | CREATE TABLE `article_content` ( 25 | `article_id` varchar(255) CHARACTER SET utf8 NOT NULL, 26 | `mark_content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, 27 | PRIMARY KEY (`article_id`) 28 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; 29 | 30 | -- ---------------------------- 31 | -- Table structure for article_info 32 | -- ---------------------------- 33 | DROP TABLE IF EXISTS `article_info`; 34 | CREATE TABLE `article_info` ( 35 | `article_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, 36 | `user_id` varchar(255) DEFAULT NULL, 37 | `category_id` varchar(255) DEFAULT NULL, 38 | `tag_ids` varchar(255) DEFAULT NULL, 39 | `visible_level` int(11) DEFAULT NULL, 40 | `link_url` varchar(255) DEFAULT NULL, 41 | `cover_image` varchar(255) DEFAULT NULL, 42 | `is_gfw` smallint(6) DEFAULT NULL, 43 | `title` varchar(255) DEFAULT NULL, 44 | `brief_content` varchar(255) DEFAULT NULL, 45 | `is_english` smallint(6) DEFAULT NULL, 46 | `is_original` smallint(6) DEFAULT NULL, 47 | `user_index` double DEFAULT NULL, 48 | `original_type` smallint(6) DEFAULT NULL, 49 | `original_author` varchar(255) DEFAULT NULL, 50 | `content` varchar(255) DEFAULT NULL, 51 | `ctime` varchar(255) DEFAULT NULL, 52 | `mtime` varchar(255) DEFAULT NULL, 53 | `rtime` varchar(255) DEFAULT NULL, 54 | `draft_id` varchar(255) DEFAULT NULL, 55 | `view_count` int(11) DEFAULT NULL, 56 | `collect_count` int(11) DEFAULT NULL, 57 | `digg_count` int(11) DEFAULT NULL, 58 | `comment_count` int(11) DEFAULT NULL, 59 | `hot_index` int(11) DEFAULT NULL, 60 | `is_hot` smallint(6) DEFAULT NULL, 61 | `rank_index` double DEFAULT NULL, 62 | `status` smallint(6) DEFAULT NULL, 63 | `verify_status` smallint(6) DEFAULT NULL, 64 | `audit_status` smallint(6) DEFAULT NULL, 65 | `mark_content` varchar(255) DEFAULT NULL, 66 | `display_count` smallint(6) DEFAULT NULL, 67 | PRIMARY KEY (`article_id`) 68 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 69 | 70 | -- ---------------------------- 71 | -- Table structure for author_user 72 | -- ---------------------------- 73 | DROP TABLE IF EXISTS `author_user`; 74 | CREATE TABLE `author_user` ( 75 | `user_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, 76 | `user_name` varchar(255) DEFAULT NULL, 77 | `company` varchar(255) DEFAULT NULL, 78 | `job_title` varchar(255) DEFAULT NULL, 79 | `avatar_large` varchar(255) DEFAULT NULL, 80 | `level` int(11) DEFAULT NULL, 81 | `description` varchar(255) DEFAULT NULL, 82 | `followee_count` int(11) DEFAULT NULL, 83 | `follower_count` int(11) DEFAULT NULL, 84 | `post_article_count` int(11) DEFAULT NULL, 85 | `digg_article_count` int(11) DEFAULT NULL, 86 | `got_digg_count` int(11) DEFAULT NULL, 87 | `got_view_count` int(11) DEFAULT NULL, 88 | `post_shortmsg_count` int(11) DEFAULT NULL, 89 | `digg_shortmsg_count` int(11) DEFAULT NULL, 90 | `isfollowed` varchar(255) DEFAULT NULL, 91 | `favorable_author` int(11) DEFAULT NULL, 92 | `power` int(11) DEFAULT NULL, 93 | `study_point` int(11) DEFAULT NULL, 94 | `university` varchar(255) DEFAULT NULL, 95 | `major` varchar(255) DEFAULT NULL, 96 | `student_status` int(11) DEFAULT NULL, 97 | `select_event_count` int(11) DEFAULT NULL, 98 | `select_online_course_count` int(11) DEFAULT NULL, 99 | `identity` int(11) DEFAULT NULL, 100 | `is_select_annual` varchar(255) DEFAULT NULL, 101 | `select_annual_rank` int(11) DEFAULT NULL, 102 | `annual_list_type` int(11) DEFAULT NULL, 103 | `extraMap` varchar(255) DEFAULT NULL, 104 | `is_logout` int(11) DEFAULT NULL, 105 | `annual_info` varchar(255) DEFAULT NULL, 106 | `account_amount` int(11) DEFAULT NULL, 107 | `user_growth_info` varchar(255) DEFAULT NULL, 108 | `is_vip` varchar(255) DEFAULT NULL, 109 | PRIMARY KEY (`user_id`) 110 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 111 | 112 | -- ---------------------------- 113 | -- Table structure for tags 114 | -- ---------------------------- 115 | DROP TABLE IF EXISTS `tags`; 116 | CREATE TABLE `tags` ( 117 | `id` varchar(255) NOT NULL, 118 | `tag_id` varchar(255) DEFAULT NULL, 119 | `tag_name` varchar(255) DEFAULT NULL, 120 | `color` varchar(255) DEFAULT NULL, 121 | `icon` varchar(255) DEFAULT NULL, 122 | `back_ground` varchar(255) DEFAULT NULL, 123 | `show_navi` smallint(6) DEFAULT NULL, 124 | `ctime` varchar(255) DEFAULT NULL, 125 | `mtime` varchar(255) DEFAULT NULL, 126 | `id_type` int(11) DEFAULT NULL, 127 | `tag_alias` varchar(255) DEFAULT NULL, 128 | `post_article_count` varchar(255) DEFAULT NULL, 129 | `concern_user_count` varchar(255) DEFAULT NULL, 130 | PRIMARY KEY (`id`) 131 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 132 | 133 | SET FOREIGN_KEY_CHECKS = 1; 134 | -------------------------------------------------------------------------------- /juejin-server/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /usr/local/etc/mavenrc ] ; then 40 | . /usr/local/etc/mavenrc 41 | fi 42 | 43 | if [ -f /etc/mavenrc ] ; then 44 | . /etc/mavenrc 45 | fi 46 | 47 | if [ -f "$HOME/.mavenrc" ] ; then 48 | . "$HOME/.mavenrc" 49 | fi 50 | 51 | fi 52 | 53 | # OS specific support. $var _must_ be set to either true or false. 54 | cygwin=false; 55 | darwin=false; 56 | mingw=false 57 | case "`uname`" in 58 | CYGWIN*) cygwin=true ;; 59 | MINGW*) mingw=true;; 60 | Darwin*) darwin=true 61 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 62 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 63 | if [ -z "$JAVA_HOME" ]; then 64 | if [ -x "/usr/libexec/java_home" ]; then 65 | export JAVA_HOME="`/usr/libexec/java_home`" 66 | else 67 | export JAVA_HOME="/Library/Java/Home" 68 | fi 69 | fi 70 | ;; 71 | esac 72 | 73 | if [ -z "$JAVA_HOME" ] ; then 74 | if [ -r /etc/gentoo-release ] ; then 75 | JAVA_HOME=`java-config --jre-home` 76 | fi 77 | fi 78 | 79 | if [ -z "$M2_HOME" ] ; then 80 | ## resolve links - $0 may be a link to maven's home 81 | PRG="$0" 82 | 83 | # need this for relative symlinks 84 | while [ -h "$PRG" ] ; do 85 | ls=`ls -ld "$PRG"` 86 | link=`expr "$ls" : '.*-> \(.*\)$'` 87 | if expr "$link" : '/.*' > /dev/null; then 88 | PRG="$link" 89 | else 90 | PRG="`dirname "$PRG"`/$link" 91 | fi 92 | done 93 | 94 | saveddir=`pwd` 95 | 96 | M2_HOME=`dirname "$PRG"`/.. 97 | 98 | # make it fully qualified 99 | M2_HOME=`cd "$M2_HOME" && pwd` 100 | 101 | cd "$saveddir" 102 | # echo Using m2 at $M2_HOME 103 | fi 104 | 105 | # For Cygwin, ensure paths are in UNIX format before anything is touched 106 | if $cygwin ; then 107 | [ -n "$M2_HOME" ] && 108 | M2_HOME=`cygpath --unix "$M2_HOME"` 109 | [ -n "$JAVA_HOME" ] && 110 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 111 | [ -n "$CLASSPATH" ] && 112 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 113 | fi 114 | 115 | # For Mingw, ensure paths are in UNIX format before anything is touched 116 | if $mingw ; then 117 | [ -n "$M2_HOME" ] && 118 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 119 | [ -n "$JAVA_HOME" ] && 120 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 121 | fi 122 | 123 | if [ -z "$JAVA_HOME" ]; then 124 | javaExecutable="`which javac`" 125 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 126 | # readlink(1) is not available as standard on Solaris 10. 127 | readLink=`which readlink` 128 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 129 | if $darwin ; then 130 | javaHome="`dirname \"$javaExecutable\"`" 131 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 132 | else 133 | javaExecutable="`readlink -f \"$javaExecutable\"`" 134 | fi 135 | javaHome="`dirname \"$javaExecutable\"`" 136 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 137 | JAVA_HOME="$javaHome" 138 | export JAVA_HOME 139 | fi 140 | fi 141 | fi 142 | 143 | if [ -z "$JAVACMD" ] ; then 144 | if [ -n "$JAVA_HOME" ] ; then 145 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 146 | # IBM's JDK on AIX uses strange locations for the executables 147 | JAVACMD="$JAVA_HOME/jre/sh/java" 148 | else 149 | JAVACMD="$JAVA_HOME/bin/java" 150 | fi 151 | else 152 | JAVACMD="`\\unset -f command; \\command -v java`" 153 | fi 154 | fi 155 | 156 | if [ ! -x "$JAVACMD" ] ; then 157 | echo "Error: JAVA_HOME is not defined correctly." >&2 158 | echo " We cannot execute $JAVACMD" >&2 159 | exit 1 160 | fi 161 | 162 | if [ -z "$JAVA_HOME" ] ; then 163 | echo "Warning: JAVA_HOME environment variable is not set." 164 | fi 165 | 166 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 167 | 168 | # traverses directory structure from process work directory to filesystem root 169 | # first directory with .mvn subdirectory is considered project base directory 170 | find_maven_basedir() { 171 | 172 | if [ -z "$1" ] 173 | then 174 | echo "Path not specified to find_maven_basedir" 175 | return 1 176 | fi 177 | 178 | basedir="$1" 179 | wdir="$1" 180 | while [ "$wdir" != '/' ] ; do 181 | if [ -d "$wdir"/.mvn ] ; then 182 | basedir=$wdir 183 | break 184 | fi 185 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 186 | if [ -d "${wdir}" ]; then 187 | wdir=`cd "$wdir/.."; pwd` 188 | fi 189 | # end of workaround 190 | done 191 | echo "${basedir}" 192 | } 193 | 194 | # concatenates all lines of a file 195 | concat_lines() { 196 | if [ -f "$1" ]; then 197 | echo "$(tr -s '\n' ' ' < "$1")" 198 | fi 199 | } 200 | 201 | BASE_DIR=`find_maven_basedir "$(pwd)"` 202 | if [ -z "$BASE_DIR" ]; then 203 | exit 1; 204 | fi 205 | 206 | ########################################################################################## 207 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 208 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 209 | ########################################################################################## 210 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 211 | if [ "$MVNW_VERBOSE" = true ]; then 212 | echo "Found .mvn/wrapper/maven-wrapper.jar" 213 | fi 214 | else 215 | if [ "$MVNW_VERBOSE" = true ]; then 216 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 217 | fi 218 | if [ -n "$MVNW_REPOURL" ]; then 219 | jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 220 | else 221 | jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 222 | fi 223 | while IFS="=" read key value; do 224 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 225 | esac 226 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 227 | if [ "$MVNW_VERBOSE" = true ]; then 228 | echo "Downloading from: $jarUrl" 229 | fi 230 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 231 | if $cygwin; then 232 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` 233 | fi 234 | 235 | if command -v wget > /dev/null; then 236 | if [ "$MVNW_VERBOSE" = true ]; then 237 | echo "Found wget ... using wget" 238 | fi 239 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 240 | wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" 241 | else 242 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" 243 | fi 244 | elif command -v curl > /dev/null; then 245 | if [ "$MVNW_VERBOSE" = true ]; then 246 | echo "Found curl ... using curl" 247 | fi 248 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 249 | curl -o "$wrapperJarPath" "$jarUrl" -f 250 | else 251 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f 252 | fi 253 | 254 | else 255 | if [ "$MVNW_VERBOSE" = true ]; then 256 | echo "Falling back to using Java to download" 257 | fi 258 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 259 | # For Cygwin, switch paths to Windows format before running javac 260 | if $cygwin; then 261 | javaClass=`cygpath --path --windows "$javaClass"` 262 | fi 263 | if [ -e "$javaClass" ]; then 264 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 265 | if [ "$MVNW_VERBOSE" = true ]; then 266 | echo " - Compiling MavenWrapperDownloader.java ..." 267 | fi 268 | # Compiling the Java class 269 | ("$JAVA_HOME/bin/javac" "$javaClass") 270 | fi 271 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 272 | # Running the downloader 273 | if [ "$MVNW_VERBOSE" = true ]; then 274 | echo " - Running MavenWrapperDownloader.java ..." 275 | fi 276 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 277 | fi 278 | fi 279 | fi 280 | fi 281 | ########################################################################################## 282 | # End of extension 283 | ########################################################################################## 284 | 285 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 286 | if [ "$MVNW_VERBOSE" = true ]; then 287 | echo $MAVEN_PROJECTBASEDIR 288 | fi 289 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 290 | 291 | # For Cygwin, switch paths to Windows format before running java 292 | if $cygwin; then 293 | [ -n "$M2_HOME" ] && 294 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 295 | [ -n "$JAVA_HOME" ] && 296 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 297 | [ -n "$CLASSPATH" ] && 298 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 299 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 300 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 301 | fi 302 | 303 | # Provide a "standardized" way to retrieve the CLI args that will 304 | # work with both Windows and non-Windows executions. 305 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 306 | export MAVEN_CMD_LINE_ARGS 307 | 308 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 309 | 310 | exec "$JAVACMD" \ 311 | $MAVEN_OPTS \ 312 | $MAVEN_DEBUG_OPTS \ 313 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 314 | "-Dmaven.home=${M2_HOME}" \ 315 | "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 316 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 317 | -------------------------------------------------------------------------------- /juejin-server/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* 50 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 124 | 125 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% ^ 162 | %JVM_CONFIG_MAVEN_PROPS% ^ 163 | %MAVEN_OPTS% ^ 164 | %MAVEN_DEBUG_OPTS% ^ 165 | -classpath %WRAPPER_JAR% ^ 166 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ 167 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 168 | if ERRORLEVEL 1 goto error 169 | goto end 170 | 171 | :error 172 | set ERROR_CODE=1 173 | 174 | :end 175 | @endlocal & set ERROR_CODE=%ERROR_CODE% 176 | 177 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost 178 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 179 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" 180 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" 181 | :skipRcPost 182 | 183 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 184 | if "%MAVEN_BATCH_PAUSE%"=="on" pause 185 | 186 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% 187 | 188 | cmd /C exit /B %ERROR_CODE% 189 | -------------------------------------------------------------------------------- /juejin-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.7.2 9 | 10 | 11 | edu.gdut 12 | juejin-server 13 | 0.0.1-SNAPSHOT 14 | juejin-server 15 | juejin-server 16 | 17 | 1.8 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-data-jpa 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-jdbc 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | 34 | com.baomidou 35 | mybatis-plus-boot-starter 36 | 3.0.5 37 | 38 | 39 | 40 | org.apache.velocity 41 | velocity-engine-core 42 | 2.0 43 | 44 | 45 | 46 | io.springfox 47 | springfox-swagger2 48 | 3.0.0 49 | 50 | 51 | junit 52 | junit 53 | test 54 | 55 | 56 | 57 | org.springframework.boot 58 | spring-boot-starter-data-redis 59 | 60 | 61 | 62 | org.apache.commons 63 | commons-pool2 64 | 65 | 66 | 67 | mysql 68 | mysql-connector-java 69 | runtime 70 | 71 | 72 | org.projectlombok 73 | lombok 74 | true 75 | 76 | 77 | org.springframework.boot 78 | spring-boot-starter-test 79 | test 80 | 81 | 82 | 83 | 84 | 85 | 86 | org.springframework.boot 87 | spring-boot-maven-plugin 88 | 2.7.2 89 | 90 | 91 | 92 | org.projectlombok 93 | lombok 94 | 95 | 96 | edu.gdut.juejinserver.JuejinServerApplication 97 | 98 | 99 | 100 | 101 | 102 | 103 | src/main/java 104 | 105 | 106 | **/*.xml 107 | **/*.properties 108 | 109 | 110 | 111 | 112 | src/main/resources 113 | 114 | 115 | **/*.xml 116 | **/*.properties 117 | 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/JuejinServerApplication.java: -------------------------------------------------------------------------------- 1 | package edu.gdut.juejinserver; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.context.annotation.ComponentScan; 6 | 7 | @SpringBootApplication 8 | @ComponentScan("edu.gdut") 9 | public class JuejinServerApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(JuejinServerApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/config/MybatisPlusConfig.java: -------------------------------------------------------------------------------- 1 | package edu.gdut.juejinserver.config; 2 | 3 | import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; 4 | import org.mybatis.spring.annotation.MapperScan; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | @Configuration 9 | @MapperScan("edu.gdut.juejinserver.mapper") 10 | public class MybatisPlusConfig { 11 | /** 12 | * 13 | * 分页查询插件 14 | * 15 | */ 16 | @Bean 17 | public PaginationInterceptor paginationInterceptor() { 18 | return new PaginationInterceptor(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/config/RedisConfig.java: -------------------------------------------------------------------------------- 1 | package edu.gdut.juejinserver.config; 2 | 3 | import com.fasterxml.jackson.annotation.JsonAutoDetect; 4 | import com.fasterxml.jackson.annotation.PropertyAccessor; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import org.springframework.cache.CacheManager; 7 | import org.springframework.cache.annotation.CachingConfigurerSupport; 8 | import org.springframework.cache.annotation.EnableCaching; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.data.redis.cache.RedisCacheConfiguration; 12 | import org.springframework.data.redis.cache.RedisCacheManager; 13 | import org.springframework.data.redis.connection.RedisConnectionFactory; 14 | import org.springframework.data.redis.core.RedisTemplate; 15 | import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; 16 | import org.springframework.data.redis.serializer.RedisSerializationContext; 17 | import org.springframework.data.redis.serializer.RedisSerializer; 18 | import org.springframework.data.redis.serializer.StringRedisSerializer; 19 | 20 | import java.time.Duration; 21 | 22 | /** 23 | * 配置SpringBoot集成Redis 24 | */ 25 | @EnableCaching 26 | @Configuration 27 | public class RedisConfig extends CachingConfigurerSupport { 28 | /** 29 | * 30 | * redis模版插件,对redis缓存做相关操作 31 | * 32 | */ 33 | @Bean 34 | public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { 35 | RedisTemplate redisTemplate = new RedisTemplate<>(); 36 | RedisSerializer redisSerializer = new StringRedisSerializer(); 37 | Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); 38 | ObjectMapper om = new ObjectMapper(); 39 | om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); 40 | om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); 41 | jackson2JsonRedisSerializer.setObjectMapper(om); 42 | redisTemplate.setConnectionFactory(redisConnectionFactory); 43 | // Key序列化方式 44 | redisTemplate.setKeySerializer(redisSerializer); 45 | // value序列化 46 | redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); 47 | // value hashmap序列化 48 | redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); 49 | return redisTemplate; 50 | } 51 | 52 | /** 53 | * 54 | * 针对缓存的管理 55 | * 56 | */ 57 | @Bean 58 | public CacheManager cacheManager(RedisConnectionFactory factory) { 59 | RedisSerializer redisSerializer = new StringRedisSerializer(); 60 | Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); 61 | // 解决查询缓存转换异常问题 62 | ObjectMapper om = new ObjectMapper(); 63 | om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); 64 | om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); 65 | jackson2JsonRedisSerializer.setObjectMapper(om); 66 | // 配置序列化,解决乱码问题,设置过期时间,单位秒 67 | RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() 68 | .entryTtl(Duration.ofSeconds(600)) 69 | .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)) 70 | .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)) 71 | .disableCachingNullValues(); 72 | RedisCacheManager manager = RedisCacheManager.builder(factory).cacheDefaults(config).build(); 73 | return manager; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/config/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | package edu.gdut.juejinserver.config; 2 | 3 | public class SwaggerConfig { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/controller/ArticleContentController.java: -------------------------------------------------------------------------------- 1 | package edu.gdut.juejinserver.controller; 2 | 3 | 4 | import edu.gdut.juejinserver.service.ArticleContentService; 5 | import edu.gdut.juejinserver.utils.Result; 6 | import org.springframework.web.bind.annotation.*; 7 | 8 | import javax.annotation.Resource; 9 | 10 | /** 11 | *

12 | * 前端控制器 13 | *

14 | * 15 | * @author Trust会长 16 | * @since 2022-08-14 17 | */ 18 | @RestController 19 | @RequestMapping("/juejinserver/article_content") 20 | @CrossOrigin 21 | public class ArticleContentController { 22 | 23 | @Resource 24 | private ArticleContentService articleContentService; 25 | 26 | /** 27 | * 根据文章id查询文章内容 28 | * @param articleId 文章id 29 | * @return 结果 30 | */ 31 | @GetMapping("/getArticleContent/{articleId}") 32 | public Result getArticleContent(@PathVariable String articleId) { 33 | return Result.success().data("articleContent", articleContentService.getById(articleId).getMarkContent()); 34 | } 35 | 36 | } 37 | 38 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/controller/ArticleInfoController.java: -------------------------------------------------------------------------------- 1 | package edu.gdut.juejinserver.controller; 2 | 3 | import edu.gdut.juejinserver.service.ArticleInfoService; 4 | import edu.gdut.juejinserver.utils.Result; 5 | import org.springframework.web.bind.annotation.*; 6 | 7 | import javax.annotation.Resource; 8 | 9 | /** 10 | *

11 | * 前端控制器 12 | *

13 | * 14 | * @author Trust会长 15 | * @since 2022-07-30 16 | */ 17 | @RestController 18 | @RequestMapping("/juejinserver/index_data") 19 | @CrossOrigin 20 | public class ArticleInfoController { 21 | 22 | @Resource 23 | private ArticleInfoService articleInfoService; 24 | 25 | /** 26 | * 分页查询首页文章 27 | * @param current 当前页 28 | * @param limit 每页记录数 29 | * @return 统一返回结果 30 | */ 31 | @GetMapping("/find_article/{current}/{limit}") 32 | public Result findIndexArticle(@PathVariable Integer current, @PathVariable Integer limit) { 33 | return Result.success().data("article_info", articleInfoService.queryArticleInfoList(current, limit)); 34 | } 35 | 36 | @GetMapping("/findArticleDetail/{articleId}") 37 | public Result findArticleDetailById(@PathVariable String articleId) { 38 | return Result.success().data("articleDetail", articleInfoService.getArticleDetailById(articleId)); 39 | } 40 | 41 | @GetMapping("/findArticleTags/{articleId}") 42 | public Result findArticleTagsById(@PathVariable String articleId) { 43 | return Result.success().data("tags", articleInfoService.getArticleTagsById(articleId)); 44 | } 45 | 46 | } 47 | 48 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/mapper/ArticleContentMapper.java: -------------------------------------------------------------------------------- 1 | package edu.gdut.juejinserver.mapper; 2 | 3 | import edu.gdut.juejinserver.pojo.ArticleContent; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | *

8 | * Mapper 接口 9 | *

10 | * 11 | * @author Trust会长 12 | * @since 2022-08-14 13 | */ 14 | public interface ArticleContentMapper extends BaseMapper { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/mapper/ArticleInfoMapper.java: -------------------------------------------------------------------------------- 1 | package edu.gdut.juejinserver.mapper; 2 | 3 | import edu.gdut.juejinserver.pojo.ArticleInfo; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import edu.gdut.juejinserver.vo.ArticleDetailVo; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | *

11 | * Mapper 接口 12 | *

13 | * 14 | * @author Trust会长 15 | * @since 2022-07-30 16 | */ 17 | public interface ArticleInfoMapper extends BaseMapper { 18 | 19 | ArticleDetailVo queryArticleInfoById(String articleId); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/mapper/AuthorUserMapper.java: -------------------------------------------------------------------------------- 1 | package edu.gdut.juejinserver.mapper; 2 | 3 | import edu.gdut.juejinserver.pojo.AuthorUser; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | *

8 | * Mapper 接口 9 | *

10 | * 11 | * @author Trust会长 12 | * @since 2022-08-01 13 | */ 14 | public interface AuthorUserMapper extends BaseMapper { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/mapper/TagsMapper.java: -------------------------------------------------------------------------------- 1 | package edu.gdut.juejinserver.mapper; 2 | 3 | import edu.gdut.juejinserver.pojo.Tags; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | *

8 | * Mapper 接口 9 | *

10 | * 11 | * @author Trust会长 12 | * @since 2022-08-01 13 | */ 14 | public interface TagsMapper extends BaseMapper { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/mapper/xml/ArticleContentMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/mapper/xml/ArticleInfoMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 28 | 29 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/mapper/xml/AuthorUserMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/mapper/xml/TagsMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/pojo/ArticleContent.java: -------------------------------------------------------------------------------- 1 | package edu.gdut.juejinserver.pojo; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import java.io.Serializable; 6 | import io.swagger.annotations.ApiModel; 7 | import io.swagger.annotations.ApiModelProperty; 8 | import lombok.Data; 9 | import lombok.EqualsAndHashCode; 10 | import lombok.experimental.Accessors; 11 | 12 | /** 13 | *

14 | * 15 | *

16 | * 17 | * @author Trust会长 18 | * @since 2022-08-14 19 | */ 20 | @Data 21 | @EqualsAndHashCode(callSuper = false) 22 | @Accessors(chain = true) 23 | @ApiModel(value="ArticleContent对象", description="") 24 | public class ArticleContent implements Serializable { 25 | 26 | private static final long serialVersionUID = 1L; 27 | 28 | @TableId(value = "article_id", type = IdType.ID_WORKER_STR) 29 | private String articleId; 30 | 31 | private String markContent; 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/pojo/ArticleInfo.java: -------------------------------------------------------------------------------- 1 | package edu.gdut.juejinserver.pojo; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import java.io.Serializable; 6 | import io.swagger.annotations.ApiModel; 7 | import io.swagger.annotations.ApiModelProperty; 8 | import lombok.Data; 9 | import lombok.EqualsAndHashCode; 10 | import lombok.experimental.Accessors; 11 | 12 | /** 13 | *

14 | * 15 | *

16 | * 17 | * @author Trust会长 18 | * @since 2022-07-30 19 | */ 20 | @Data 21 | @EqualsAndHashCode(callSuper = false) 22 | @Accessors(chain = true) 23 | @ApiModel(value="ArticleInfo对象", description="") 24 | public class ArticleInfo implements Serializable { 25 | 26 | private static final long serialVersionUID = 1L; 27 | 28 | @TableId(value = "article_id", type = IdType.ID_WORKER_STR) 29 | private String articleId; 30 | 31 | private String userId; 32 | 33 | private String categoryId; 34 | 35 | private String tagIds; 36 | 37 | private Integer visibleLevel; 38 | 39 | private String linkUrl; 40 | 41 | private String coverImage; 42 | 43 | private Integer isGfw; 44 | 45 | private String title; 46 | 47 | private String briefContent; 48 | 49 | private Integer isEnglish; 50 | 51 | private Integer isOriginal; 52 | 53 | private Double userIndex; 54 | 55 | private Integer originalType; 56 | 57 | private String originalAuthor; 58 | 59 | private String content; 60 | 61 | private String ctime; 62 | 63 | private String mtime; 64 | 65 | private String rtime; 66 | 67 | private String draftId; 68 | 69 | private Integer viewCount; 70 | 71 | private Integer collectCount; 72 | 73 | private Integer diggCount; 74 | 75 | private Integer commentCount; 76 | 77 | private Integer hotIndex; 78 | 79 | private Integer isHot; 80 | 81 | private Double rankIndex; 82 | 83 | private Integer status; 84 | 85 | private Integer verifyStatus; 86 | 87 | private Integer auditStatus; 88 | 89 | private String markContent; 90 | 91 | private Integer displayCount; 92 | 93 | 94 | } 95 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/pojo/AuthorUser.java: -------------------------------------------------------------------------------- 1 | package edu.gdut.juejinserver.pojo; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.baomidou.mybatisplus.annotation.TableField; 6 | import java.io.Serializable; 7 | import io.swagger.annotations.ApiModel; 8 | import io.swagger.annotations.ApiModelProperty; 9 | import lombok.Data; 10 | import lombok.EqualsAndHashCode; 11 | import lombok.experimental.Accessors; 12 | 13 | /** 14 | *

15 | * 16 | *

17 | * 18 | * @author Trust会长 19 | * @since 2022-08-01 20 | */ 21 | @Data 22 | @EqualsAndHashCode(callSuper = false) 23 | @Accessors(chain = true) 24 | @ApiModel(value="AuthorUser对象", description="") 25 | public class AuthorUser implements Serializable { 26 | 27 | private static final long serialVersionUID = 1L; 28 | 29 | @TableId(value = "user_id", type = IdType.ID_WORKER_STR) 30 | private String userId; 31 | 32 | private String userName; 33 | 34 | private String company; 35 | 36 | private String jobTitle; 37 | 38 | private String avatarLarge; 39 | 40 | private Integer level; 41 | 42 | private String description; 43 | 44 | private Integer followeeCount; 45 | 46 | private Integer followerCount; 47 | 48 | private Integer postArticleCount; 49 | 50 | private Integer diggArticleCount; 51 | 52 | private Integer gotDiggCount; 53 | 54 | private Integer gotViewCount; 55 | 56 | private Integer postShortmsgCount; 57 | 58 | private Integer diggShortmsgCount; 59 | 60 | private String isfollowed; 61 | 62 | private Integer favorableAuthor; 63 | 64 | private Integer power; 65 | 66 | private Integer studyPoint; 67 | 68 | private String university; 69 | 70 | private String major; 71 | 72 | private Integer studentStatus; 73 | 74 | private Integer selectEventCount; 75 | 76 | private Integer selectOnlineCourseCount; 77 | 78 | private Integer identity; 79 | 80 | private String isSelectAnnual; 81 | 82 | private Integer selectAnnualRank; 83 | 84 | private Integer annualListType; 85 | 86 | @TableField("extraMap") 87 | private String extraMap; 88 | 89 | private Integer isLogout; 90 | 91 | private String annualInfo; 92 | 93 | private Integer accountAmount; 94 | 95 | private String userGrowthInfo; 96 | 97 | private String isVip; 98 | 99 | 100 | } 101 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/pojo/Tags.java: -------------------------------------------------------------------------------- 1 | package edu.gdut.juejinserver.pojo; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import java.io.Serializable; 6 | import io.swagger.annotations.ApiModel; 7 | import io.swagger.annotations.ApiModelProperty; 8 | import lombok.Data; 9 | import lombok.EqualsAndHashCode; 10 | import lombok.experimental.Accessors; 11 | 12 | /** 13 | *

14 | * 15 | *

16 | * 17 | * @author Trust会长 18 | * @since 2022-08-01 19 | */ 20 | @Data 21 | @EqualsAndHashCode(callSuper = false) 22 | @Accessors(chain = true) 23 | @ApiModel(value="Tags对象", description="") 24 | public class Tags implements Serializable { 25 | 26 | private static final long serialVersionUID = 1L; 27 | 28 | @TableId(value = "id", type = IdType.ID_WORKER_STR) 29 | private String id; 30 | 31 | private String tagId; 32 | 33 | private String tagName; 34 | 35 | private String color; 36 | 37 | private String icon; 38 | 39 | private String backGround; 40 | 41 | private Integer showNavi; 42 | 43 | private String ctime; 44 | 45 | private String mtime; 46 | 47 | private Integer idType; 48 | 49 | private String tagAlias; 50 | 51 | private String postArticleCount; 52 | 53 | private String concernUserCount; 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/service/ArticleContentService.java: -------------------------------------------------------------------------------- 1 | package edu.gdut.juejinserver.service; 2 | 3 | import edu.gdut.juejinserver.pojo.ArticleContent; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | /** 7 | *

8 | * 服务类 9 | *

10 | * 11 | * @author Trust会长 12 | * @since 2022-08-14 13 | */ 14 | public interface ArticleContentService extends IService { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/service/ArticleInfoService.java: -------------------------------------------------------------------------------- 1 | package edu.gdut.juejinserver.service; 2 | 3 | import edu.gdut.juejinserver.pojo.ArticleInfo; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | import edu.gdut.juejinserver.vo.ArticleDetailVo; 6 | import edu.gdut.juejinserver.vo.ArticleInfoVo; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | *

12 | * 服务类 13 | *

14 | * 15 | * @author Trust会长 16 | * @since 2022-07-30 17 | */ 18 | public interface ArticleInfoService extends IService { 19 | 20 | /** 21 | * 分页查询数据 22 | * @param current 当前页 23 | * @param limit 每页记录数 24 | * @return 25 | */ 26 | List queryArticleInfoList(int current, int limit); 27 | 28 | /** 29 | * 查询文章和作者详情 30 | * @param articleId 文章id 31 | * @return 文章详情 32 | */ 33 | ArticleDetailVo getArticleDetailById(String articleId); 34 | 35 | List getArticleTagsById(String articleId); 36 | } 37 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/service/AuthorUserService.java: -------------------------------------------------------------------------------- 1 | package edu.gdut.juejinserver.service; 2 | 3 | import edu.gdut.juejinserver.pojo.AuthorUser; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | /** 7 | *

8 | * 服务类 9 | *

10 | * 11 | * @author Trust会长 12 | * @since 2022-08-01 13 | */ 14 | public interface AuthorUserService extends IService { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/service/TagsService.java: -------------------------------------------------------------------------------- 1 | package edu.gdut.juejinserver.service; 2 | 3 | import edu.gdut.juejinserver.pojo.Tags; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | /** 7 | *

8 | * 服务类 9 | *

10 | * 11 | * @author Trust会长 12 | * @since 2022-08-01 13 | */ 14 | public interface TagsService extends IService { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/service/impl/ArticleContentServiceImpl.java: -------------------------------------------------------------------------------- 1 | package edu.gdut.juejinserver.service.impl; 2 | 3 | import edu.gdut.juejinserver.pojo.ArticleContent; 4 | import edu.gdut.juejinserver.mapper.ArticleContentMapper; 5 | import edu.gdut.juejinserver.service.ArticleContentService; 6 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 7 | import org.springframework.stereotype.Service; 8 | 9 | /** 10 | *

11 | * 服务实现类 12 | *

13 | * 14 | * @author Trust会长 15 | * @since 2022-08-14 16 | */ 17 | @Service 18 | public class ArticleContentServiceImpl extends ServiceImpl implements ArticleContentService { 19 | 20 | } 21 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/service/impl/ArticleInfoServiceImpl.java: -------------------------------------------------------------------------------- 1 | package edu.gdut.juejinserver.service.impl; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 4 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 5 | import edu.gdut.juejinserver.mapper.AuthorUserMapper; 6 | import edu.gdut.juejinserver.mapper.TagsMapper; 7 | import edu.gdut.juejinserver.pojo.ArticleInfo; 8 | import edu.gdut.juejinserver.mapper.ArticleInfoMapper; 9 | import edu.gdut.juejinserver.pojo.AuthorUser; 10 | import edu.gdut.juejinserver.pojo.Tags; 11 | import edu.gdut.juejinserver.service.ArticleInfoService; 12 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 13 | import edu.gdut.juejinserver.utils.JuejinStringUtils; 14 | import edu.gdut.juejinserver.vo.ArticleDetailVo; 15 | import edu.gdut.juejinserver.vo.ArticleInfoVo; 16 | import org.springframework.cache.annotation.Cacheable; 17 | import org.springframework.stereotype.Service; 18 | 19 | import javax.annotation.Resource; 20 | import java.util.ArrayList; 21 | import java.util.Arrays; 22 | import java.util.List; 23 | 24 | /** 25 | *

26 | * 服务实现类 27 | *

28 | * 29 | * @author Trust会长 30 | * @since 2022-07-30 31 | */ 32 | @Service 33 | public class ArticleInfoServiceImpl extends ServiceImpl implements ArticleInfoService { 34 | 35 | @Resource 36 | private ArticleInfoMapper articleInfoMapper; 37 | @Resource 38 | private AuthorUserMapper authorUserMapper; 39 | @Resource 40 | private TagsMapper tagsMapper; 41 | 42 | @Override 43 | @Cacheable(value = "juejin", key = "'articleList' + #current + 'Current' + #limit + 'Limit'") 44 | public List queryArticleInfoList(int current, int limit) { 45 | List list = new ArrayList<>(); 46 | // 分页查询article数据 47 | Page page = new Page<>(current, limit); 48 | articleInfoMapper.selectPage(page, null); 49 | List articleInfos = null; 50 | if (page.getRecords().size() < limit) { 51 | // 不足limit条记录的时候 52 | // 随机查连续limit条记录 53 | QueryWrapper wrapper = new QueryWrapper<>(); 54 | Integer count = articleInfoMapper.selectCount(null); 55 | // 生成一个随机数。范围为 0 —— (count - limit) 56 | int random = (int)(Math.random() * (count - limit)); 57 | wrapper.last("limit "+ random +"," + limit); 58 | articleInfos = articleInfoMapper.selectList(wrapper); 59 | } else { 60 | // 足够 61 | articleInfos = page.getRecords(); 62 | } 63 | // 根据userId查作者 64 | // 根据tagId查tag 65 | for (ArticleInfo article : articleInfos) { 66 | ArticleInfoVo vo = new ArticleInfoVo(); 67 | // 封装数据 68 | vo.setArticleId(article.getArticleId()); 69 | vo.setBriefContent(article.getBriefContent()); 70 | vo.setCollectCount(article.getCollectCount()); 71 | vo.setCommentCount(article.getCommentCount()); 72 | vo.setCtime(article.getCtime()); 73 | vo.setCoverImage(article.getCoverImage()); 74 | vo.setDiggCount(article.getDiggCount()); 75 | vo.setMtime(article.getMtime()); 76 | vo.setRtime(article.getRtime()); 77 | vo.setTitle(article.getTitle()); 78 | vo.setUserId(article.getUserId()); 79 | vo.setViewCount(article.getViewCount()); 80 | 81 | // 二次处理的数据 82 | AuthorUser user = authorUserMapper.selectById(article.getUserId()); 83 | vo.setUserName(user.getUserName()); 84 | 85 | // tagIds和tagNames 86 | String tagIds = article.getTagIds(); 87 | String[] tagIdsArr = JuejinStringUtils.getTagIdsArray(tagIds); 88 | vo.setTagsIds(tagIdsArr); 89 | // 根据tagId查tagName 90 | List tagNames = new ArrayList<>(); 91 | for (int i = 0; i < tagIdsArr.length; i++) { 92 | QueryWrapper wrapper = new QueryWrapper<>(); 93 | wrapper.eq("tag_id", tagIdsArr[i]); 94 | Tags tag = tagsMapper.selectOne(wrapper); 95 | if (tag != null) tagNames.add(tag.getTagName()); 96 | } 97 | // 设置tagNames 98 | vo.setTagNames(tagNames); 99 | list.add(vo); 100 | } 101 | return list; 102 | } 103 | 104 | @Override 105 | public ArticleDetailVo getArticleDetailById(String articleId) { 106 | return articleInfoMapper.queryArticleInfoById(articleId); 107 | } 108 | 109 | @Override 110 | public List getArticleTagsById(String articleId) { 111 | ArticleInfo articleInfo = articleInfoMapper.selectById(articleId); 112 | String tagIds = articleInfo.getTagIds(); 113 | String[] tagIdsArray = JuejinStringUtils.getTagIdsArray(tagIds); 114 | List tags = new ArrayList<>(); 115 | // 根据tagId查tagName 116 | for (String s : tagIdsArray) { 117 | QueryWrapper wrapper = new QueryWrapper<>(); 118 | wrapper.eq("tag_id", s); 119 | Tags tag = tagsMapper.selectOne(wrapper); 120 | if (tag != null) tags.add(tag.getTagName()); 121 | } 122 | return tags; 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/service/impl/AuthorUserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package edu.gdut.juejinserver.service.impl; 2 | 3 | import edu.gdut.juejinserver.pojo.AuthorUser; 4 | import edu.gdut.juejinserver.mapper.AuthorUserMapper; 5 | import edu.gdut.juejinserver.service.AuthorUserService; 6 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 7 | import org.springframework.stereotype.Service; 8 | 9 | /** 10 | *

11 | * 服务实现类 12 | *

13 | * 14 | * @author Trust会长 15 | * @since 2022-08-01 16 | */ 17 | @Service 18 | public class AuthorUserServiceImpl extends ServiceImpl implements AuthorUserService { 19 | 20 | } 21 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/service/impl/TagsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package edu.gdut.juejinserver.service.impl; 2 | 3 | import edu.gdut.juejinserver.pojo.Tags; 4 | import edu.gdut.juejinserver.mapper.TagsMapper; 5 | import edu.gdut.juejinserver.service.TagsService; 6 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 7 | import org.springframework.stereotype.Service; 8 | 9 | /** 10 | *

11 | * 服务实现类 12 | *

13 | * 14 | * @author Trust会长 15 | * @since 2022-08-01 16 | */ 17 | @Service 18 | public class TagsServiceImpl extends ServiceImpl implements TagsService { 19 | 20 | } 21 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/utils/JuejinStringUtils.java: -------------------------------------------------------------------------------- 1 | package edu.gdut.juejinserver.utils; 2 | 3 | public class JuejinStringUtils { 4 | /** 5 | * 解析tagids的字符串变成字符串数组 6 | * @param tagIds [123,456,789] 或 [123] 7 | * @return 字符串数组 8 | */ 9 | public static String[] getTagIdsArray(String tagIds) { 10 | // tagIds: [123, 456, 789, 2142] 11 | // 特殊情况:只有一个tag的:[123] 12 | tagIds = tagIds.substring(1, tagIds.length() - 1); // 去掉中括号 13 | // tagIds = tagIds.trim(); // 这个方法只能去除首尾空格 14 | tagIds = tagIds.replace(" ", ""); 15 | // 处理特殊情况:[123], 根据有没有逗号来判断 16 | String[] tagIdsArr = null; 17 | if (tagIds.contains(",")) { 18 | tagIdsArr = tagIds.split(","); 19 | } else { 20 | tagIdsArr = new String[]{tagIds}; 21 | } 22 | return tagIdsArr; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/utils/Result.java: -------------------------------------------------------------------------------- 1 | package edu.gdut.juejinserver.utils; 2 | 3 | 4 | import lombok.Data; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | @Data 10 | public class Result { 11 | 12 | private Boolean isSuccess; 13 | 14 | private Integer code; 15 | 16 | private String message; 17 | 18 | private Map data; 19 | 20 | /*构造方法私有化*/ 21 | private Result() { 22 | data = new HashMap<>(); 23 | } 24 | 25 | public static Result success() { 26 | Result result = new Result(); 27 | result.isSuccess = true; 28 | result.code = ResultCode.SUCCESS; 29 | return result; 30 | } 31 | 32 | public static Result error() { 33 | Result result = new Result(); 34 | result.isSuccess = false; 35 | result.code = ResultCode.ERROR; 36 | return result; 37 | } 38 | 39 | public Result message(String message) { 40 | this.message = message; 41 | return this; 42 | } 43 | 44 | public Result data(String key, Object value) { 45 | this.data.put(key, value); 46 | return this; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/utils/ResultCode.java: -------------------------------------------------------------------------------- 1 | package edu.gdut.juejinserver.utils; 2 | 3 | 4 | public class ResultCode { 5 | 6 | public static final int SUCCESS = 2000; 7 | 8 | public static final int ERROR = 2001; 9 | 10 | } 11 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/vo/ArticleDetailVo.java: -------------------------------------------------------------------------------- 1 | package edu.gdut.juejinserver.vo; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class ArticleDetailVo { 7 | 8 | private String articleId; 9 | 10 | private String title; 11 | 12 | private String coverImage; 13 | 14 | private String ctime; 15 | 16 | private String mtime; 17 | 18 | private String rtime; 19 | 20 | private Integer viewCount; 21 | 22 | private Integer collectCount; 23 | 24 | private Integer diggCount; 25 | 26 | private Integer commentCount; 27 | 28 | private String userId; 29 | 30 | private String userName; 31 | 32 | private String company; 33 | 34 | private String jobTitle; 35 | 36 | private String avatarLarge; 37 | 38 | } 39 | -------------------------------------------------------------------------------- /juejin-server/src/main/java/edu/gdut/juejinserver/vo/ArticleInfoVo.java: -------------------------------------------------------------------------------- 1 | package edu.gdut.juejinserver.vo; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * 文章数据 9 | */ 10 | @Data 11 | public class ArticleInfoVo { 12 | 13 | private String articleId; 14 | 15 | private String userId; 16 | 17 | private String userName; 18 | 19 | private String[] tagsIds; 20 | 21 | private List tagNames; 22 | 23 | private String ctime; 24 | 25 | private String mtime; 26 | 27 | private String rtime; 28 | 29 | private String title; 30 | 31 | private String coverImage; 32 | 33 | private String briefContent; 34 | 35 | private Integer viewCount; 36 | 37 | private Integer collectCount; 38 | 39 | private Integer diggCount; 40 | 41 | private Integer commentCount; 42 | 43 | } 44 | -------------------------------------------------------------------------------- /juejin-server/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # 端口 2 | server.port=9088 3 | # 服务名 4 | spring.application.name=juejin-server 5 | # 数据库信息 6 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 7 | spring.datasource.url=自己数据库的url 8 | spring.datasource.username=自己数据库的username 9 | spring.datasource.password=自己数据库的password 10 | # mybatis-plus 11 | mybatis-plus.mapper-locations=classpath:edu/gdut/juejinserver/mapper/xml/*.xml 12 | 13 | # 这是我的本地的虚拟机上的redis地址 14 | # IP redis 15 | spring.redis.host=10.211.55.5 16 | # redis端口号 17 | spring.redis.port=6379 18 | # redis数据库 19 | spring.redis.database=0 20 | # redis密码 21 | spring.redis.password=123456 22 | # redis超时时间 23 | spring.redis.timeout=180000 24 | -------------------------------------------------------------------------------- /juejin-server/src/test/java/edu/gdut/juejinserver/CodeGenerator.java: -------------------------------------------------------------------------------- 1 | package edu.gdut.juejinserver; 2 | 3 | import com.baomidou.mybatisplus.annotation.DbType; 4 | import com.baomidou.mybatisplus.annotation.IdType; 5 | import com.baomidou.mybatisplus.generator.AutoGenerator; 6 | import com.baomidou.mybatisplus.generator.config.DataSourceConfig; 7 | import com.baomidou.mybatisplus.generator.config.GlobalConfig; 8 | import com.baomidou.mybatisplus.generator.config.PackageConfig; 9 | import com.baomidou.mybatisplus.generator.config.StrategyConfig; 10 | import com.baomidou.mybatisplus.generator.config.rules.DateType; 11 | import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; 12 | import org.junit.Test; 13 | 14 | /** 15 | * @author 16 | * @since 2018/12/13 17 | */ 18 | public class CodeGenerator { 19 | 20 | @Test 21 | public void run() { 22 | 23 | // 1、创建代码生成器 24 | AutoGenerator mpg = new AutoGenerator(); 25 | 26 | // 2、全局配置 27 | GlobalConfig gc = new GlobalConfig(); 28 | String projectPath = System.getProperty("user.dir"); 29 | gc.setOutputDir("/Users/luorui/dev/projects/juejin/juejin-server/src/main/java/edu/gdut/juejinserver"); 30 | gc.setAuthor("Trust会长"); 31 | gc.setOpen(false); //生成后是否打开资源管理器 32 | gc.setFileOverride(false); //重新生成时文件是否覆盖 33 | gc.setServiceName("%sService"); //去掉Service接口的首字母I 34 | gc.setIdType(IdType.ID_WORKER_STR); //主键策略 35 | gc.setDateType(DateType.ONLY_DATE);//定义生成的实体类中日期类型 36 | gc.setSwagger2(true);//开启Swagger2模式 37 | 38 | mpg.setGlobalConfig(gc); 39 | 40 | // 3、数据源配置 41 | DataSourceConfig dsc = new DataSourceConfig(); 42 | dsc.setUrl("jdbc:mysql://rm-wz9mc28o4x3thxw56bo.mysql.rds.aliyuncs.com:3306/jue_jin"); 43 | dsc.setDriverName("com.mysql.cj.jdbc.Driver"); 44 | dsc.setUsername("trust"); 45 | dsc.setPassword("Trust666"); 46 | dsc.setDbType(DbType.MYSQL); 47 | mpg.setDataSource(dsc); 48 | 49 | // 4、包配置 50 | PackageConfig pc = new PackageConfig(); 51 | pc.setModuleName("juejinserver"); //模块名 52 | pc.setParent("edu.gdut"); 53 | pc.setController("controller"); 54 | pc.setEntity("pojo"); 55 | pc.setService("service"); 56 | pc.setMapper("mapper"); 57 | mpg.setPackageInfo(pc); 58 | 59 | // 5、策略配置 60 | StrategyConfig strategy = new StrategyConfig(); 61 | strategy.setInclude("article_content"); 62 | strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略 63 | strategy.setTablePrefix(pc.getModuleName() + "_"); //生成实体时去掉表前缀 64 | 65 | strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略 66 | strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作 67 | 68 | strategy.setRestControllerStyle(true); //restful api风格控制器 69 | strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符 70 | 71 | mpg.setStrategy(strategy); 72 | 73 | 74 | // 6、执行 75 | mpg.execute(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /project/article.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --juejin-brand-1-normal: #1e80ff; 3 | --juejin-brand-2-hover: #1171ee; 4 | --juejin-brand-3-click: #0060dd; 5 | --juejin-brand-4-disable: #abcdff; 6 | --juejin-brand-5-light: #eaf2ff; 7 | --juejin-font-1: #252933; 8 | --juejin-font-2: #515767; 9 | --juejin-font-3: #8a919f; 10 | --juejin-font-4: #c2c8d1; 11 | } 12 | 13 | * { 14 | margin: 0; 15 | padding: 0; 16 | } 17 | 18 | html, 19 | body { 20 | background-color: #f4f5f5; 21 | } 22 | 23 | .page{ 24 | width: 98.93vw; 25 | } 26 | 27 | .page__header{ 28 | width: 100%; 29 | height: 60px; 30 | background: rgba(144, 243, 78, 0.171); 31 | } 32 | 33 | .page__main{ 34 | max-width: 1140px; 35 | width: 100%; 36 | height: 800px; 37 | margin: 0 auto; 38 | position: relative; 39 | margin-top: 21px; 40 | } 41 | .article-main{ 42 | width: 100%; 43 | height: 600px; 44 | /* background: rgba(61, 155, 231, 0.219); */ 45 | margin: 0 auto; 46 | display: flex; 47 | 48 | } 49 | 50 | .article-area{ 51 | /* flex: 1; */ 52 | width: 820px; 53 | margin-right: 21px; 54 | } 55 | .article{ 56 | /* width: 100%; */ 57 | /* height: 400px; */ 58 | padding: 32px; 59 | box-sizing: border-box; 60 | background: #fff; 61 | border-radius: 4px; 62 | } 63 | .article>div{ 64 | margin-bottom: 20px; 65 | } 66 | .article .article-title{ 67 | font-size: 1.9rem; 68 | font-weight: 600; 69 | color: #252933; 70 | word-wrap: break-word; 71 | } 72 | .article .article-info-box{ 73 | width: 100%; 74 | height: 48px; 75 | /* background: rgba(0, 0, 0, 0.13); */ 76 | display: flex; 77 | align-items: center; 78 | } 79 | .user-photo{ 80 | width: 40px; 81 | height: 40px; 82 | border-radius: 50%; 83 | margin-right: 8px; 84 | overflow: hidden; 85 | display: flex; 86 | justify-content: center; 87 | align-items: center; 88 | } 89 | .user-photo img{ 90 | height: 100%; 91 | } 92 | .author-info{ 93 | flex: 1px; 94 | } 95 | .author-info .author-name{ 96 | font-size: 1rem; 97 | font-weight: 500; 98 | color: #515767; 99 | line-height: 2rem; 100 | display: flex; 101 | } 102 | .author-name span{ 103 | width: 20%; 104 | text-overflow: ellipsis; 105 | overflow: hidden; 106 | white-space: nowrap; 107 | display: block; 108 | margin-right: 5px; 109 | } 110 | .author-name img{ 111 | width: 35px; 112 | height: 16px; 113 | } 114 | .author-info .meta-box{ 115 | font-size: .9rem; 116 | color: #8a919f; 117 | line-height: 20px; 118 | display: flex; 119 | } 120 | .meta-box span{ 121 | margin-right: 15px; 122 | position: relative; 123 | } 124 | .meta-box span:not(:last-child)::after{ 125 | content: '·'; 126 | position: absolute; 127 | right: -8px; 128 | } 129 | .follow-btn{ 130 | width: 70px; 131 | height: 34px; 132 | font-size: 14px; 133 | color: #1e80ff; 134 | background: rgba(30, 128, 255, .05); 135 | border: 1px solid rgba(30, 128, 255, .3); 136 | border-radius: 4px; 137 | } 138 | .article-img{ 139 | width: 100%; 140 | height: 425px; 141 | overflow: hidden; 142 | display: flex; 143 | align-items: center; 144 | } 145 | .article-img img{ 146 | width: 100%; 147 | } 148 | 149 | 150 | .tag-list-box{ 151 | width: 100%; 152 | height: 32px; 153 | display: flex; 154 | /* background: rgba(0, 0, 0, 0.089); */ 155 | font-size: .9rem; 156 | line-height: 32px; 157 | color: #515767d8; 158 | } 159 | .tag-list-box .list{ 160 | margin-right: 20px; 161 | display: flex; 162 | } 163 | .tag-list-box .list>span{ 164 | display: block; 165 | color: #1e80ff; 166 | background: #eaf2ff; 167 | padding: 0 12px; 168 | border-radius: 4px; 169 | margin-right: 5px; 170 | } 171 | 172 | .comment{ 173 | width: 100%; 174 | /* height: 176px; */ 175 | background: #fff; 176 | margin-top: 21px; 177 | border-radius: 4px; 178 | padding: 0px 32px; 179 | box-sizing: border-box; 180 | } 181 | .comment>div{ 182 | padding-top: 24px; 183 | } 184 | .comment-form .form-header{ 185 | font-size: 18px; 186 | line-height: 30px; 187 | font-weight: 600; 188 | color: #252933; 189 | margin-bottom: 24px; 190 | } 191 | .comment-form .form-content{ 192 | display: flex; 193 | /* height: 74px; */ 194 | } 195 | .form-userimg{ 196 | width: 40px; 197 | height: 40px; 198 | border-radius: 50%; 199 | overflow: hidden; 200 | display: flex; 201 | justify-content: center; 202 | align-items: center; 203 | margin-right: 16px; 204 | } 205 | .form-userimg>img{ 206 | height: 100%; 207 | } 208 | .form-box>textarea{ 209 | border: none; 210 | background: #f2f3f5; 211 | border-radius: 4px; 212 | padding: 8px 12px; 213 | box-sizing: border-box; 214 | color: #252933; 215 | line-height: 22px; 216 | font-size: 14px; 217 | } 218 | .form-box>textarea:focus-within{ 219 | outline: 1px solid var(--juejin-brand-1-normal); 220 | } 221 | .form-box:focus-within .action-box{ 222 | display: flex; 223 | } 224 | .action-box{ 225 | height: 36px; 226 | 227 | align-items: center; 228 | margin-top: 8px; 229 | font-size: 13px; 230 | color: #515767; 231 | /* display: flex; */ 232 | display: none; 233 | } 234 | 235 | .image-btn{ 236 | margin-left: 24px; 237 | } 238 | .submit-box{ 239 | flex: 1; 240 | display: flex; 241 | justify-content: flex-end; 242 | font-size: 14px; 243 | color: #86909c; 244 | line-height: 36px; 245 | } 246 | .submit-box .submit{ 247 | width: 92px; 248 | height: 36px; 249 | border: none; 250 | font-size: 14px; 251 | color: #fff; 252 | background: #1e80ff; 253 | border-radius: 4px; 254 | margin-left: 16px; 255 | } 256 | .hot-list{ 257 | /* height: 600px; */ 258 | } 259 | .title{ 260 | font-size: 18px; 261 | line-height: 30px; 262 | font-weight: 600; 263 | color: #252933; 264 | margin-bottom: 24px; 265 | display: flex; 266 | align-items: center; 267 | } 268 | .title>img{ 269 | width: 20px; 270 | height: 20px; 271 | } 272 | 273 | 274 | /* 一条评论的样式 开始 */ 275 | .comment-item{ 276 | width: 100%; 277 | display: flex; 278 | padding: 16px 0px; 279 | } 280 | .comment-userImg .userImg{ 281 | width: 40px; 282 | height: 40px; 283 | border-radius: 50%; 284 | overflow: hidden; 285 | display: flex; 286 | justify-content: center; 287 | align-items: center; 288 | } 289 | .comment-userImg .userImg>img{ 290 | height: 100%; 291 | } 292 | .comment-content{ 293 | flex: 1; 294 | margin-left: 16px; 295 | } 296 | .comment-main>div{ 297 | margin-bottom: 8px; 298 | } 299 | .user-box span:not(:last-child){ 300 | margin-right: 7px; 301 | } 302 | .user-box{ 303 | width: 100%; 304 | height: 26px; 305 | display: flex; 306 | align-items: center; 307 | } 308 | .user-box .name{ 309 | max-width: 128px; 310 | font-size: 15px; 311 | color: #252933; 312 | line-height: 26px; 313 | 314 | text-overflow: ellipsis; 315 | overflow: hidden; 316 | white-space: nowrap; 317 | } 318 | .user-box .level{ 319 | width: 45px; 320 | height: 16px; 321 | } 322 | .user-box .jueyou-level{ 323 | width: 45px; 324 | height: 16px; 325 | } 326 | .user-box img{ 327 | width: 45px; 328 | height: 16px; 329 | } 330 | .user-box .position{ 331 | max-width: 150px; 332 | font-size: 14px; 333 | color: #8a919f; 334 | 335 | text-overflow: ellipsis; 336 | overflow: hidden; 337 | white-space: nowrap; 338 | 339 | margin-left: 10px; 340 | } 341 | .user-box .time{ 342 | flex: 1; 343 | font-size: 14px; 344 | color: #8a919f; 345 | text-align: right; 346 | } 347 | .content-main{ 348 | font-size: 14px; 349 | line-height: 24px; 350 | color: #515767; 351 | } 352 | .comment-action-box{ 353 | display: flex; 354 | font-size: 14px; 355 | color: #8a919f; 356 | } 357 | .comment-action-box div{ 358 | display: flex; 359 | align-items: center; 360 | margin-right: 15px; 361 | } 362 | .comment-action-box img{ 363 | width: 16px; 364 | height: 16px; 365 | margin-right: 5px; 366 | } 367 | /* 一条评论的样式 结束 */ 368 | 369 | 370 | 371 | 372 | /* 侧边栏 开始 */ 373 | .sidebar{ 374 | position: absolute; 375 | top: 0; 376 | right: 0; 377 | width: 300px; 378 | height: 600px; 379 | flex: 1; 380 | /* background: rgba(146, 231, 77, 0.63); */ 381 | /* background: #fff; */ 382 | 383 | } 384 | .sidebar>div{ 385 | width: 100%; 386 | /* padding: 20px; */ 387 | margin-bottom: 20px; 388 | box-sizing: border-box; 389 | border-radius: 4px; 390 | background: #fff; 391 | } 392 | 393 | /* 作者信息栏 */ 394 | .author-block{ 395 | /* background: #fff; */ 396 | padding: 20px; 397 | } 398 | .user-item{ 399 | display: flex; 400 | padding-bottom: 17px; 401 | position: relative; 402 | } 403 | .userimg{ 404 | width: 48px; 405 | height: 48px; 406 | border-radius: 50%; 407 | overflow: hidden; 408 | display: flex; 409 | justify-content: center; 410 | align-items: center; 411 | } 412 | .userimg>img{ 413 | height: 48px; 414 | margin-left: 5px; 415 | } 416 | .info-box{ 417 | margin-left: 16px; 418 | } 419 | .info-box>div{ 420 | max-width: 196px; 421 | height: 24px; 422 | font-size: 16px; 423 | font-weight: 400; 424 | overflow: hidden; 425 | text-overflow: ellipsis; 426 | white-space: nowrap; 427 | } 428 | .info-box .userName{ 429 | color: #252933; 430 | display: flex; 431 | align-items: center; 432 | } 433 | .userName>span{ 434 | max-width: 155px; 435 | overflow: hidden; 436 | text-overflow: ellipsis; 437 | white-space: nowrap; 438 | } 439 | .userName>img{ 440 | width: 35px; 441 | height: 16px; 442 | } 443 | .info-box .position{ 444 | color: #515767; 445 | } 446 | 447 | .stat-item{ 448 | margin-top: 17px; 449 | display: flex; 450 | font-size: 15px; 451 | line-height: 25px; 452 | color: #252933; 453 | } 454 | .stat-item>div{ 455 | width: 25px; 456 | height: 25px; 457 | border-radius: 50%; 458 | background: #e1efff; 459 | margin-right: 12px; 460 | display: flex; 461 | justify-content: center; 462 | align-items: center; 463 | } 464 | .stat-item img{ 465 | width: 16px; 466 | height: 16px; 467 | } 468 | .user-item::after{ 469 | content: ''; 470 | position: absolute; 471 | bottom: 0; 472 | width: 100%; 473 | height: 1px; 474 | background: rgba(0, 0, 0, 0.178); 475 | transform: scaleY(.5); 476 | } 477 | 478 | 479 | /* 文章目录 */ 480 | ul{ 481 | list-style: none; 482 | } 483 | a{ 484 | text-decoration: none; 485 | color: #515767; 486 | } 487 | .sticky-block-box{ 488 | /* height: 524px; */ 489 | } 490 | .sticky-title{ 491 | position: relative; 492 | margin: 0 20px; 493 | padding: 16px 0; 494 | } 495 | .sticky-title::after{ 496 | content: ''; 497 | position: absolute; 498 | bottom: 0px; 499 | left: 0px; 500 | width: 100%; 501 | height: 1px; 502 | background: rgba(0, 0, 0, 0.178); 503 | transform: scaleY(.5); 504 | } 505 | 506 | .sticky-content{ 507 | width: 100%; 508 | max-height: 460px; 509 | overflow-y: scroll; 510 | padding: 20px 5px; 511 | box-sizing: border-box; 512 | } 513 | 514 | /* 目录滑动条样式 */ 515 | .sticky-content::-webkit-scrollbar{ 516 | width: 6px; 517 | } 518 | .sticky-content::-webkit-scrollbar-thumb{ 519 | border-radius: 10px; 520 | background: #c2c8d19b; 521 | margin-left: 10px; 522 | } 523 | 524 | .sticky-list{ 525 | width: 100%; 526 | padding-left: 20px; 527 | box-sizing: border-box; 528 | position: relative; 529 | 530 | } 531 | .a-container{ 532 | height: 44px; 533 | border-radius: 4px; 534 | font-size: 14px; 535 | color: #515767; 536 | line-height: 44px; 537 | padding-left: 5px; 538 | box-sizing: border-box; 539 | 540 | } 541 | .a-container:hover{ 542 | background: #f7f8fa; 543 | } 544 | 545 | .first::after{ 546 | content: ''; 547 | position: absolute; 548 | top: 13px; 549 | /* 距离下一个标签 top增加53px */ 550 | left: 0; 551 | width: 5px; 552 | height: 18px; 553 | background: #1e80ff; 554 | border-radius: 3px; 555 | } 556 | 557 | /* 侧边栏 结束 */ 558 | 559 | 560 | /* 左侧功能栏 */ 561 | .article-suspended-panel{ 562 | width: 48px; 563 | height: 300px; 564 | /* background: rgba(0, 0, 0, 0.157); */ 565 | position: fixed; 566 | top: 140px; 567 | left: 100px; 568 | } 569 | .panel-btn{ 570 | width: 100%; 571 | height: 48px; 572 | border-radius: 50%; 573 | background: #fff; 574 | margin-bottom: 20px; 575 | box-shadow: 1px 1px 20px #dadddd; 576 | 577 | display: flex; 578 | justify-content: center; 579 | align-items: center; 580 | 581 | } 582 | .panel-btn>img{ 583 | width: 17px; 584 | height: 17px; 585 | } 586 | .panel-btn:nth-child(4){ 587 | margin-bottom: 40px; 588 | position: relative; 589 | } 590 | .panel-btn:nth-child(4)::after{ 591 | content: ''; 592 | position: absolute; 593 | bottom: -20px; 594 | width: 30px; 595 | height: 1px; 596 | background: rgba(0, 0, 0, 0.157); 597 | transform: scaleY(.5); 598 | } -------------------------------------------------------------------------------- /project/article.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 | 10 |
11 | 12 |
13 | 14 |
15 | 16 |
17 | 18 |
19 | 20 |
分布式架构下的几个关键问题分布式架构下的几个关键问题分布式架构下的几个关键问题
21 | 22 | 23 | 39 | 40 | 41 |
42 | 43 |
44 | 45 | 46 |
47 | 48 | 49 |
50 |
分类: 51 | 后端 52 |
53 |
标签: 54 | 后端 55 | 后端 56 | 后端 57 |
58 |
59 |
60 | 61 | 62 |
63 | 64 |
65 |
评论
66 |
67 |
68 | 69 |
70 |
71 | 72 |
73 |
😀表情
74 |
🖼图片
75 |
76 | Ctrl + Enter 77 | 78 |
79 |
80 |
81 |
82 |
83 | 84 | 85 |
86 |
87 | 热门评论 88 | 89 |
90 |
91 | 92 |
93 |
94 | 95 | 96 | 97 |
98 |
99 |
100 |
101 | 青山绿水长流 102 | 103 | 104 | 移动端开发 105 | 1个月 106 |
107 |
hi,想问你一个问题,flutter如何做长截屏?
108 |
109 |
110 | 点赞 111 |
112 |
113 | 回复 114 |
115 |
116 |
117 |
118 |
119 |
120 | 121 |
122 |
123 | 124 | 125 | 126 |
127 |
128 |
129 |
130 | 青山绿水长流 131 | 132 | 133 | 移动端开发 134 | 1个月 135 |
136 |
hi,想问你一个问题,flutter如何做长截屏?
137 |
138 |
139 | 点赞 140 |
141 |
142 | 回复 143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 | 152 | 153 |
154 |
155 | 全部评论 156 |   11 157 |
158 |
159 |
160 |
161 | 162 | 163 | 164 |
165 |
166 |
167 |
168 | 青山绿水长流 169 | 170 | 171 | 移动端开发 172 | 1个月 173 |
174 |
hi,想问你一个问题,flutter如何做长截屏?
175 |
176 |
177 | 点赞 178 |
179 |
180 | 回复 181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 | 192 | 193 | 303 |
304 | 305 | 306 |
307 |
308 | 309 |
310 |
311 | 312 |
313 |
314 | 315 |
316 |
317 | 318 |
319 |
320 | 321 |
322 |
323 | 324 |
325 |
326 |
327 |
328 | 329 | -------------------------------------------------------------------------------- /project/page__header.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --juejin-brand-1-normal: #1e80ff; 3 | --juejin-brand-2-hover: #1171ee; 4 | --juejin-brand-3-click: #0060dd; 5 | --juejin-brand-4-disable: #abcdff; 6 | --juejin-brand-5-light: #eaf2ff; 7 | --juejin-font-1: #252933; 8 | --juejin-font-2: #515767; 9 | --juejin-font-3: #8a919f; 10 | --juejin-font-4: #c2c8d1; 11 | } 12 | 13 | * { 14 | margin: 0; 15 | padding: 0; 16 | } 17 | 18 | html, 19 | body { 20 | background-color: #d6d4cb48; 21 | } 22 | 23 | a { 24 | text-decoration: none; 25 | color: var(--juejin-font-3); 26 | } 27 | 28 | ul { 29 | list-style: none; 30 | } 31 | 32 | .list__contents a:hover { 33 | color: var(--juejin-font-1); 34 | } 35 | 36 | .juejin__page { 37 | width: 100%; 38 | height: 100%; 39 | } 40 | 41 | /* 导航栏部分 */ 42 | .page__header { 43 | width: 100%; 44 | height: 106px; 45 | position: fixed; 46 | background: rgb(255, 255, 255); 47 | z-index: 5; 48 | } 49 | 50 | .page__header__nav { 51 | width: 100%; 52 | height: 60px; 53 | display: flex; 54 | justify-content: center; 55 | position: relative; 56 | } 57 | 58 | .page__header__nav::after { 59 | content: ''; 60 | position: absolute; 61 | bottom: 0; 62 | width: 100%; 63 | height: 1px; 64 | background-color: var(--juejin-font-4); 65 | transform: scaleY(0.5); 66 | } 67 | 68 | /* 掘金图标 */ 69 | .nav__sign { 70 | display: flex; 71 | } 72 | 73 | .logo__img { 74 | width: 107px; 75 | height: 22px; 76 | margin-left: 24px; 77 | margin-top: 20px; 78 | } 79 | 80 | /* 导航按钮 */ 81 | .nav__main-list { 82 | height: 100%; 83 | display: flex; 84 | } 85 | 86 | .list-left { 87 | width: 37vw; 88 | } 89 | 90 | .list-left, 91 | .list-right { 92 | display: flex; 93 | } 94 | 95 | .list__contents { 96 | height: 100%; 97 | display: flex; 98 | margin-left: 10px; 99 | } 100 | 101 | .list__contents>li { 102 | display: flex; 103 | font-size: .9rem; 104 | align-items: center; 105 | margin: 0 .65rem; 106 | } 107 | 108 | .list__contents li a { 109 | display: inline-block; 110 | } 111 | 112 | /* 开放社区下拉框 */ 113 | li>.community::after { 114 | content: ''; 115 | /* 注意这里的伪类要为块级元素才能旋转 */ 116 | display: inline-block; 117 | width: 0px; 118 | height: 0px; 119 | border: 4px solid #515761; 120 | border-left-color: transparent; 121 | border-right-color: transparent; 122 | border-bottom-color: transparent; 123 | position: relative; 124 | left: 2px; 125 | transform-origin: 50% 25%; 126 | transition: transform .5s ease-in-out; 127 | } 128 | 129 | .list__contents>li:nth-child(7):hover .community { 130 | color: var(--juejin-brand-1-normal); 131 | } 132 | 133 | .list__contents>li:nth-child(7):hover .community::after { 134 | border: 4px solid var(--juejin-brand-1-normal); 135 | border-left-color: transparent; 136 | border-right-color: transparent; 137 | border-bottom-color: transparent; 138 | transform: rotate(-180deg); 139 | } 140 | 141 | .list__contents>li:nth-child(7):hover .community__list { 142 | display: block; 143 | } 144 | .community__list{ 145 | display: none; 146 | position: absolute; 147 | /* width: 200px; */ 148 | padding: 5px; 149 | background: #fff; 150 | border-radius: 5px; 151 | box-shadow: 4px 0 70px #8a919f2f, -4px 0 70px #8a919f25, 0 4px 70px #8a919f3c; 152 | top: 52px; 153 | z-index: 10; 154 | } 155 | .community__list li { 156 | display: inline-block; 157 | /* width: 90px; */ 158 | height: 26px; 159 | color: var(--juejin-font-2); 160 | line-height: 26px; 161 | padding: 6px 20px 5px 9px; 162 | } 163 | 164 | .community__list li:hover { 165 | background-color: #f2f3f5; 166 | border-radius: 3px; 167 | } 168 | 169 | .community__list span::before { 170 | content: ''; 171 | display: inline-block; 172 | position: relative; 173 | top: 4px; 174 | width: 18px; 175 | height: 18px; 176 | margin: 0 12px 0 2px; 177 | } 178 | 179 | .community__list li:nth-child(1) span::before { 180 | background-image: url('../image/comm.png'); 181 | background-size: 18px 18px; 182 | } 183 | 184 | 185 | /* 搜索框 */ 186 | .input__frame{ 187 | height: 36px; 188 | border: 1px solid #86909ccf; 189 | border-radius: 3px; 190 | display: flex; 191 | align-items: center; 192 | } 193 | 194 | .input:focus{ 195 | outline: none; 196 | border-radius: 3px; 197 | } 198 | .input__frame:focus-within{ 199 | /* width: 400px; */ 200 | transform-origin: 0 0; 201 | /* transform: scaleX(1.32); */ 202 | outline: 1px solid var(--juejin-brand-1-normal); 203 | } 204 | .input__frame:focus-within .input__sign{ 205 | background: var(--juejin-brand-5-light); 206 | } 207 | .input__frame:focus-within .input__typehead{ 208 | display: block; 209 | } 210 | 211 | .input__frame .input { 212 | flex: 1; 213 | height: 12px; 214 | border: none; 215 | padding: 12px 7px; 216 | -webkit-text-fill-color: var(--juejin-font-3); 217 | } 218 | 219 | .input__frame .input__sign { 220 | width: 44px; 221 | height: 30px; 222 | background: #86909c23; 223 | border-radius: 3px; 224 | margin-right: 3px; 225 | margin-left: 140px; 226 | } 227 | 228 | .input__sign .input__img{ 229 | width: 16px; 230 | height: 16px; 231 | background-image: url('../image/input.png'); 232 | background-size: 16px 16px; 233 | margin: 7px 15px; 234 | } 235 | .input__frame .input__typehead{ 236 | display: none; 237 | position: absolute; 238 | top: 52px; 239 | width: auto; 240 | background: #fff; 241 | border: 1px solid #5157613d; 242 | border-radius: 2px; 243 | z-index: 10; 244 | font-size: .8rem; 245 | color: var(--juejin-font-3); 246 | } 247 | .input__typehead .title{ 248 | width: 100%; 249 | display: flex; 250 | justify-content: space-between; 251 | position: relative; 252 | } 253 | .input__typehead>.title::after{ 254 | content: ''; 255 | position: absolute; 256 | width: 100%; 257 | height: 1px; 258 | bottom: 0; 259 | background-color: var(--juejin-font-4); 260 | transform: scaleY(.5); 261 | } 262 | .title>span{ 263 | display: inline-block; 264 | margin: 10px; 265 | } 266 | .input__typehead .list .list-item{ 267 | width: 342px; 268 | display: flex; 269 | padding: 10px; 270 | } 271 | .list-item:hover{ 272 | background-color: #eff2f5; 273 | } 274 | 275 | 276 | /* 创作者中心 */ 277 | .creator { 278 | /* width: 115px; */ 279 | height: 36px; 280 | display: flex; 281 | } 282 | 283 | .creator .crea-btn { 284 | width: 80px; 285 | height: 100%; 286 | font-size: .8rem; 287 | color: #fff; 288 | background-color: var(--juejin-brand-1-normal); 289 | border: none; 290 | border-radius: 3px 0 0 3px; 291 | position: relative; 292 | } 293 | 294 | .crea-btn::after { 295 | content: ''; 296 | position: absolute; 297 | top: 0; 298 | right: 0; 299 | width: 1px; 300 | height: 36px; 301 | background-color: var(--juejin-brand-4-disable); 302 | transform: scaleX(0.3); 303 | } 304 | 305 | .creator .more { 306 | width: 20px; 307 | height: 100%; 308 | background-color: var(--juejin-brand-1-normal); 309 | border-radius: 0 3px 3px 0; 310 | } 311 | 312 | .more:hover, 313 | .crea-btn:hover { 314 | background-color: var(--juejin-brand-2-hover); 315 | } 316 | 317 | #dianj { 318 | display: none; 319 | } 320 | 321 | input:checked~ul { 322 | display: block; 323 | } 324 | 325 | input:checked~label .more__tri { 326 | transform-origin: 55% 75%; 327 | transform: rotateZ(225deg); 328 | } 329 | 330 | .more__tri { 331 | width: 0px; 332 | height: 0px; 333 | border: 4px solid #fff; 334 | border-left-color: transparent; 335 | border-top-color: transparent; 336 | transform: rotateZ(45deg); 337 | position: relative; 338 | top: 12px; 339 | left: 6px; 340 | } 341 | 342 | .more__list { 343 | display: none; 344 | position: absolute; 345 | width: 100px; 346 | padding: 5px; 347 | background: #fff; 348 | border-radius: 5px; 349 | box-shadow: 4px 0 70px #8a919f2f, -4px 0 70px #8a919f25, 0 4px 70px #8a919f3c; 350 | top: 52px; 351 | z-index: 10; 352 | } 353 | 354 | .more__list li { 355 | display: inline-block; 356 | width: 90px; 357 | height: 26px; 358 | color: var(--juejin-font-2); 359 | line-height: 26px; 360 | padding: 6px 1px 5px 9px; 361 | } 362 | 363 | .more__list li:hover { 364 | background-color: #f2f3f5; 365 | border-radius: 3px; 366 | } 367 | 368 | .more__list span::before { 369 | content: ''; 370 | display: inline-block; 371 | position: relative; 372 | top: 2px; 373 | width: 18px; 374 | height: 18px; 375 | margin: 0 12px 0 2px; 376 | } 377 | 378 | .more__list li:nth-child(1) span::before { 379 | background-image: url('../image/pencil.png'); 380 | background-size: 18px 18px; 381 | } 382 | 383 | .more__list li:nth-child(2) span::before { 384 | background-image: url('../image/dia.png'); 385 | background-size: 18px 18px; 386 | } 387 | 388 | 389 | 390 | /* 会员 */ 391 | .vip__title { 392 | height: 100%; 393 | display: flex; 394 | align-items: center; 395 | color: var(--juejin-font-3); 396 | } 397 | 398 | .vip__title .vip__img { 399 | width: 24px; 400 | height: 24px; 401 | margin-right: 5px; 402 | } 403 | 404 | /* 通知 */ 405 | .inform { 406 | height: 100%; 407 | display: flex; 408 | align-items: center; 409 | } 410 | 411 | .inform__img { 412 | width: 24px; 413 | height: 24px; 414 | background-image: url('../image/inform-logo-off.png'); 415 | background-size: 24px 24px; 416 | } 417 | 418 | .inform__img:hover { 419 | background-image: url('../image/inform-logo-on.png'); 420 | } 421 | 422 | /* 用户头像 */ 423 | .user { 424 | height: 100%; 425 | display: flex; 426 | align-items: center; 427 | } 428 | 429 | .user__img { 430 | width: 36px; 431 | /* transform: scale(1); */ 432 | height: 36px; 433 | object-fit: cover; 434 | border-radius: 50%; 435 | } 436 | 437 | /* 标签页面 */ 438 | .page__header__tag{ 439 | width: 100%; 440 | height: 46px; 441 | /* background: rgba(17, 244, 96, 0.228); */ 442 | } 443 | .page__header__tag::after { 444 | content: ''; 445 | position: absolute; 446 | bottom: 0; 447 | width: 100%; 448 | height: 1px; 449 | background-color: var(--juejin-font-4); 450 | transform: scaleY(0.5); 451 | } 452 | .tag__contents{ 453 | height: 100%; 454 | /* background: rgba(239, 24, 24, 0.199); */ 455 | margin: 0 280px; 456 | display: flex; 457 | justify-content: space-between; 458 | } 459 | .tag__list{ 460 | display: flex; 461 | height: 100%; 462 | } 463 | .tag__manage{ 464 | display: flex; 465 | align-items: center; 466 | } 467 | .tag__manage > span{ 468 | font-size: .9rem; 469 | color: var(--juejin-font-2); 470 | } 471 | .page__header__tag a:hover{ 472 | color: var(--juejin-brand-1-normal); 473 | } 474 | .tag__list .list__contents{ 475 | margin-left: 0; 476 | } 477 | .tag__list li:nth-child(1){ 478 | margin-left: 0; 479 | } 480 | -------------------------------------------------------------------------------- /project/page__main.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --juejin-brand-1-normal: #1e80ff; 3 | --juejin-brand-2-hover: #1171ee; 4 | --juejin-brand-3-click: #0060dd; 5 | --juejin-brand-4-disable: #abcdff; 6 | --juejin-brand-5-light: #eaf2ff; 7 | --juejin-font-1: #252933; 8 | --juejin-font-2: #515767; 9 | --juejin-font-3: #8a919f; 10 | --juejin-font-4: #c2c8d1; 11 | } 12 | .page__main{ 13 | width: 98.93vw; 14 | /* height: 800px; */ 15 | display: flex; 16 | /* background: rgba(0, 0, 0, 0.13); */ 17 | } 18 | .page__main__contents{ 19 | width: 960px; 20 | margin: 0 auto; 21 | /* background: rgba(50, 241, 16, 0.215); */ 22 | margin-top: 115px; 23 | } 24 | .contents__list{ 25 | width: 700px; 26 | /* height: 100%; */ 27 | /* background: rgba(17, 0, 255, 0.142); */ 28 | margin-right: 260px; 29 | } 30 | .list__header{ 31 | width: 676px; 32 | /* height: 47px; */ 33 | background: #fff; 34 | padding: 17px 12px; 35 | position: relative; 36 | } 37 | .list__content{ 38 | width: 100%; 39 | /* height: 700px;s */ 40 | background: #fff; 41 | } 42 | .article__list{ 43 | width: 100%; 44 | } 45 | .list__item{ 46 | /* width: 660px; 47 | height: 129px; */ 48 | padding: 12px 20px 0 20px; 49 | position: relative; 50 | } 51 | .list__header::after{ 52 | content: ''; 53 | position: absolute; 54 | bottom: 0; 55 | left: 0; 56 | width: 100%; 57 | height: 1px; 58 | background: var(--juejin-font-4); 59 | transform: scaleY(.5); 60 | } 61 | .list__item::after{ 62 | content: ''; 63 | position: absolute; 64 | bottom: 0; 65 | left: 21px; 66 | width: 94%; 67 | height: 1px; 68 | background: var(--juejin-font-4); 69 | transform: scaleY(.5); 70 | } 71 | 72 | /* 内容头部标签 */ 73 | .nav__list{ 74 | display: flex; 75 | height: 19px; 76 | } 77 | .nav__list>ul{ 78 | display: flex; 79 | } 80 | .nav__list li{ 81 | font-size: .9rem; 82 | margin: 0 13px; 83 | position: relative; 84 | } 85 | .nav__list li:not(:last-child)::after{ 86 | content: ''; 87 | position: absolute; 88 | right: -13px; 89 | top: -5px; 90 | width: 1px; 91 | height: 155%; 92 | background: var(--juejin-font-4); 93 | transform: scaleY(.5); 94 | } 95 | .nav__list a:hover{ 96 | color: var(--juejin-brand-1-normal); 97 | } 98 | 99 | 100 | /* 文章列表 */ 101 | .list__item>.entry{ 102 | display: flex; 103 | width: 100%; 104 | height: 100%; 105 | flex-wrap: wrap; 106 | } 107 | .article__inform{ 108 | width: 100%; 109 | height: 22px; 110 | display: flex; 111 | } 112 | .article__content{ 113 | width: 100%; 114 | height: 97px; 115 | display: flex; 116 | /* flex-direction: column; */ 117 | } 118 | .article__inform>.nav__list li{ 119 | font-size: .7rem; 120 | } 121 | .article__inform>.nav__list li:nth-child(1){ 122 | margin-left: 0; 123 | } 124 | 125 | .content-main{ 126 | width: 200px; 127 | flex: 1 0 auto; 128 | } 129 | 130 | .content-main .title{ 131 | font-size: .9rem; 132 | color: #000; 133 | font-weight: bold; 134 | margin: 10px 0; 135 | } 136 | 137 | .content-main .abstract{ 138 | /* width: 100px; */ 139 | font-size: .8rem; 140 | color: red; 141 | margin-bottom: 10px; 142 | text-overflow: ellipsis; 143 | -webkit-box-orient: vertical; 144 | /* -webkit-line-clamp: 1 */ 145 | overflow: hidden; 146 | white-space: nowrap; 147 | } 148 | .action-list{ 149 | display: flex; 150 | font-size: 0.8em; 151 | color: var(--juejin-font-2); 152 | 153 | } 154 | .action-list .item{ 155 | display: inline-block; 156 | margin-right: 20px; 157 | display: flex; 158 | } 159 | .action-list>li>i{ 160 | display: block; 161 | width: 16px; 162 | height: 16px; 163 | background-size: 16px 16px; 164 | margin-right: 4px; 165 | } 166 | .action-list .eye{ 167 | background-image: url("../image/eye.png"); 168 | } 169 | .action-list .zan{ 170 | background-image: url("../image/zan__off.png"); 171 | } 172 | .action-list .cloud{ 173 | margin-top: 2px; 174 | background-image: url("../image/cloud__off.png"); 175 | } 176 | .action-list .zan:hover{ 177 | background-image: url("../image/zan__on.png"); 178 | } 179 | .action-list .cloud:hover{ 180 | /* margin-top: 2px; */ 181 | background-image: url("../image/cloud__on.png"); 182 | } 183 | 184 | 185 | 186 | .article__content>img{ 187 | width: 120px; 188 | height: 80px; 189 | margin-left: 24px; 190 | flex: 0 0 auto; 191 | border-radius: 2px; 192 | } --------------------------------------------------------------------------------