Compare commits

...

124 Commits

Author SHA1 Message Date
Eeeros 40ec067139 124125
5125125
2024-09-20 15:40:54 +08:00
xxq250 801c544f65 Merge pull request '前端页面优化' (#282) from hc1913847458/forgeplus-react:testdev into develop 2021-11-25 09:43:19 +08:00
hucong 5a1fcb1443 403,404登录验证 2021-11-24 18:31:46 +08:00
hucong 33297226ea 页面优化 2021-11-23 18:00:05 +08:00
hucong fd4046af04 issue 2021-11-23 15:04:34 +08:00
hucong 36683ad69b 团队介绍根据不同团队权限显示不同内容 2021-11-23 15:00:06 +08:00
hucong 2262508777 前端403,404,500页面优化 2021-11-23 14:39:44 +08:00
hucong 9db8455de8 项目导航菜单悬停优化 2021-11-22 15:40:59 +08:00
jasder 7579dc8a59 Merge pull request 'ADD @功能' (#266) from feature_notification_v2 into develop 2021-11-18 10:51:43 +08:00
jasder 89773b5036 Merge pull request '文案调整' (#264) from hc1913847458/forgeplus-react:testdev into develop 2021-11-17 14:31:12 +08:00
hucong 2617831a9f 文案调整 2021-11-16 18:29:55 +08:00
hucong f0747eab05 文案调整 2021-11-16 18:28:30 +08:00
jasder d1743b350d Merge pull request '项目tab栏wiki添加中文' (#258) from hc1913847458/forgeplus-react:testdev into develop 2021-11-15 11:01:37 +08:00
baladiwei 7fd790fe7d Merge pull request '@我功能' (#261) from durian/forgeplus-react:feature_notification_v2 into feature_notification_v2 2021-11-12 14:21:39 +08:00
谢思 c48e3584c3 删除之前atwo.js的引入 2021-11-12 14:17:06 +08:00
谢思 d0ca09e780 Merge branch 'feature_notification_v2' of https://git.trustie.net/Gitlink/forgeplus-react into feature_notification_v2 2021-11-12 14:08:56 +08:00
baladiwei ed3bf55f90 Merge pull request 'merge old version' (#260) from feature_noti_v2 into feature_notification_v2 2021-11-12 14:07:07 +08:00
谢思 7ff29fec70 未及时更新评论内容 2021-11-12 13:50:35 +08:00
hucong fbfd5f175d 项目tab栏合并请求增加英文 2021-11-12 10:34:56 +08:00
hucong 07101f5770 项目tab栏wiki增加中文 2021-11-12 10:29:53 +08:00
谢思 a94d02bb8a atwo markdown渲染 2021-11-11 11:31:42 +08:00
谢思 cc2dd35ff4 编辑合并请求最后一行@弹框被遮挡 2021-11-10 20:45:23 +08:00
谢思 f003a99779 @我功能。 2021-11-10 16:47:22 +08:00
谢思 b1ead191a6 Merge branch 'pre_develop' of https://git.trustie.net/Gitlink/forgeplus-react into feature_notification_v2 2021-11-10 10:28:23 +08:00
谢思 55a6f93fe3 atwho 2021-11-08 16:04:19 +08:00
谢思 ac4488a907 atwho 2021-11-08 15:14:04 +08:00
jasder ea979b78ee Merge pull request '目录跳转问题' (#248) from hc1913847458/forgeplus-react:testdev into develop 2021-11-08 14:34:11 +08:00
hucong b1775e5158 目录跳转问题 2021-11-08 14:05:39 +08:00
hucong 3678b8eb72 issue 2021-11-08 13:58:46 +08:00
hucong e04fb11194 目录跳转问题 2021-11-05 19:03:24 +08:00
hucong 010edf3cb2 目录跳转问题 2021-11-05 18:31:14 +08:00
jasder 6d39e25667 Merge pull request 'pr中的用户名称显示问题' (#244) from hc1913847458/forgeplus-react:testdev into develop 2021-11-03 18:46:12 +08:00
hucong 190200d1e6 Merge branch 'pre_develop_dev' of https://git.trustie.net/Gitlink/forgeplus-react into testdev 2021-11-01 14:41:27 +08:00
hucong b124364203 pr中用户名称显示问题 2021-11-01 14:40:41 +08:00
谢思 2078a044b6 at我列表 2021-11-01 09:31:08 +08:00
jasder e785e9a358 Merge pull request 'ADD 项目首页功能并入开发分支' (#238) from featrue_project_homepage into develop 2021-10-28 17:53:05 +08:00
jasder b3b95e9856 Merge pull request 'merge' (#237) from caishi/forgeplus-react:featrue_project_homepage into featrue_project_homepage 2021-10-28 17:51:56 +08:00
caishi 5be8ead356 Merge branch 'develop' of https://git.trustie.net/Gitlink/forgeplus-react into featrue_project_homepage 2021-10-28 17:48:56 +08:00
jasder 7d78919c4a Merge pull request 'FIX 解决冲突' (#236) from pre_develop into develop 2021-10-28 17:48:45 +08:00
caishi f259f05cdc Merge branch 'develop' of https://git.trustie.net/Gitlink/forgeplus-react into featrue_project_homepage 2021-10-28 17:46:55 +08:00
jasder 95cd289373 Merge pull request '项目首页改版更新' (#232) from caishi/forgeplus-react:featrue_project_homepage into featrue_project_homepage 2021-10-28 17:43:43 +08:00
caishi c79be9a597 Merge branch 'pre_develop_dev' of https://git.trustie.net/Gitlink/forgeplus-react into featrue_project_homepage 2021-10-28 17:41:54 +08:00
jasder fb03565a25 Merge pull request '并入开发分支' (#230) from pre_develop_dev into develop 2021-10-28 17:39:40 +08:00
caishi d5f989bbe2 update 2021-10-28 17:08:36 +08:00
caishi 1c24baf3b9 数据绑定 2021-10-28 09:31:08 +08:00
caishi 3d3ef19d4a 项目首页 banner 2021-10-26 09:40:35 +08:00
caishi d37709b1a5 首页文件夹 2021-10-25 11:24:21 +08:00
谢思 1c46617144 Merge branch 'pre_develop' of https://git.trustie.net/Gitlink/forgeplus-react into feature_notification_v2 2021-10-23 19:52:58 +08:00
谢思 ec133a6880 添加todo 2021-10-23 19:50:17 +08:00
谢思 bcdb631c20 删除引入的js文件 2021-10-23 19:43:58 +08:00
谢思 8021d96cd8 atwho列表 2021-10-23 19:39:54 +08:00
jasder 3bf9de2d4c Merge pull request 'FIX 合并主干开发分支功能' (#220) from develop into master 2021-10-22 16:55:27 +08:00
jasder 6be0d12e8c Merge pull request 'cherry pick' (#219) from jasder/forgeplus-react:develop into develop 2021-10-22 16:54:51 +08:00
caishi b000fc11f3 iconfont + style 2021-10-22 16:52:27 +08:00
jasder a0fa4eccf0 Merge pull request 'FIX bug' (#209) from jasder/forgeplus-react:develop into develop 2021-10-22 10:09:16 +08:00
jasder d81b40969a FIX 项目路由改造的bug 2021-10-22 10:05:24 +08:00
jasder 9f4069fc6a Merge pull request 'Add license 协议' (#207) from master into develop 2021-10-22 09:58:00 +08:00
jasder 257519f98b Merge branch 'master' of https://git.trustie.net/Gitlink/forgeplus-react 2021-10-22 09:56:37 +08:00
jasder 0c180ace32 ADD MulanPSL-2.0 LICENSE 2021-10-22 09:55:53 +08:00
jasder b703c27d44 Merge pull request '并入master分支' (#205) from develop into master 2021-10-22 09:47:53 +08:00
jasder fe2318021e Merge pull request '合并最新功能代码' (#206) from jasder/forgeplus-react:master into master 2021-10-22 09:47:39 +08:00
jasder 8f9e9b0152 解决代码冲突 2021-10-22 09:41:50 +08:00
jasder cde55c1b3c Merge pull request 'WIP' (#204) from jasder/forgeplus-react:develop into develop 2021-10-21 18:38:47 +08:00
jasder 0f0130742e FIX 解决文件冲突 2021-10-21 18:35:47 +08:00
谢思 3498390974 at 2021-10-21 14:12:15 +08:00
谢思 4f3e385f78 at谁列表(缺弹框的上下,enter键监听) 2021-10-21 10:51:40 +08:00
谢思 18b289b84c @who(缺优化) 2021-10-21 09:41:17 +08:00
谢思 e3f49db5c0 at谁列表 2021-10-18 14:19:32 +08:00
谢思 4add266047 at谁列表 2021-10-15 11:36:51 +08:00
谢思 4a6012bfa9 at谁列表 2021-10-14 15:04:17 +08:00
谢思 7fe88aa4d9 at列表 2021-10-14 09:11:37 +08:00
谢思 bc6ef583fb atwho 2021-10-13 16:01:38 +08:00
谢思 b268b540d8 @我列表 2021-10-13 14:53:13 +08:00
谢思 7e69c64c9c @who列表 2021-10-13 11:52:54 +08:00
谢思 85ef26f70e @who 2021-10-13 10:07:38 +08:00
谢思 8c7865d2c1 Merge branch 'pre_develop' of https://git.trustie.net/Gitlink/forgeplus-react into feature_notification_v2 2021-10-13 09:54:04 +08:00
谢思 999a15761d md@谁列表 2021-10-12 14:24:18 +08:00
谢思 5c2330512e @我:在markdown编辑器中输入@我弹出列表需要引入的js 2021-10-11 13:48:50 +08:00
caishi 0d0b164370 Merge branch 'develop' of https://git.trustie.net/jasder/forgeplus-react into develop 2021-08-23 10:15:55 +08:00
caishi ec84ed56b0 reset 2021-08-23 10:15:32 +08:00
caishi 466ed1fe1b Merge branch 'pre_develop' into develop
# Conflicts:
#	public/css/iconfont.css
#	public/css/iconfont.js
#	public/css/iconfont.ttf
#	public/css/iconfont.woff
#	public/css/iconfont.woff2
2021-08-23 10:09:00 +08:00
caishi 9b101403a1 里程碑-切换分页需要带上状态参数 2021-08-23 10:08:04 +08:00
caishi 6beb4c2c8a 修复镜像项目时的展示问题
修复镜像项目时的展示问题
2021-08-18 14:43:28 +08:00
何童崇 e8dadb187b 紧急修复镜像bug 2021-08-18 14:38:11 +08:00
caishi 3a1fd14e7c 代码库-样式 2021-08-13 17:12:56 +08:00
caishi 20f64bdf81 md fullscreen 2021-08-12 17:36:37 +08:00
caishi 2347d04710 同上-细节修改 2021-08-12 15:24:45 +08:00
caishi d13d94e9cf 代码库样式整改 2021-08-12 11:45:56 +08:00
caishi 00402ef13d Merge branch 'master' of https://git.trustie.net/jasder/forgeplus-react
# Conflicts:
#	src/forge/Main/sub/UpdateDescModal.jsx
2021-08-09 10:37:23 +08:00
caishi 8f2df90555 代码style 2021-08-09 10:36:15 +08:00
caishi 7530d3f9c4 Merge branch 'develop' 2021-08-06 17:07:54 +08:00
hc1913847458 b7869e5203 Merge branch 'develop' of https://git.trustie.net/jasder/forgeplus-react 2021-08-05 10:48:43 +08:00
hc1913847458 b238bb51ca 限制字符 2021-08-05 10:47:22 +08:00
hc1913847458 ad7ab189b1 限制字符 2021-08-04 14:42:08 +08:00
caishi c9802c26a4 Merge branch 'develop' 2021-08-02 10:35:49 +08:00
caishi 4ec840f252 Merge branch 'develop' 2021-07-30 20:13:49 +08:00
caishi 44a8f806e0 Merge branch 'develop' 2021-07-30 17:44:17 +08:00
caishi 9408ad7dcb Merge branch 'develop' 2021-07-30 17:03:19 +08:00
caishi 810ea37bed Merge branch 'develop' 2021-07-26 09:38:44 +08:00
caishi 6679fe8a48 Merge branch 'develop' 2021-07-22 17:44:30 +08:00
caishi d31b932efe Merge branch 'develop' 2021-07-21 10:10:53 +08:00
caishi 07852b204c Merge branch 'develop' 2021-07-05 20:35:59 +08:00
caishi 934ac61b77 Merge branch 'develop' 2021-07-02 18:19:02 +08:00
caishi 95091d0e08 Merge branch 'develop' 2021-07-02 15:37:43 +08:00
caishi e31ff9bdf3 Merge branch 'develop' 2021-07-01 17:00:43 +08:00
caishi eba6ed0876 搜索的接口改用正式版对应的服务器地址 2021-06-28 10:44:42 +08:00
caishi db682ace86 Merge branch 'develop' 2021-06-25 18:34:19 +08:00
caishi 777dbd0f43 Merge branch 'develop' 2021-06-25 18:21:48 +08:00
caishi b44b6530e5 Merge branch 'develop' 2021-06-25 09:14:50 +08:00
caishi b808bcee99 Merge branch 'develop' 2021-06-18 16:56:18 +08:00
caishi c0f7c489fb Merge branch 'develop' 2021-06-11 18:11:43 +08:00
caishi bc64cbb415 Merge branch 'develop' 2021-06-08 14:56:20 +08:00
caishi fa169b27cd Merge branch 'develop' 2021-05-26 16:48:35 +08:00
caishi 3e0814344f Merge branch 'develop' 2021-05-25 11:25:04 +08:00
caishi eb58f91dbf Merge branch 'develop' 2021-05-21 19:21:32 +08:00
caishi 513255428c Merge branch 'develop'
# Conflicts:
#	package-lock.json
2021-05-14 17:04:57 +08:00
caishi 8baf2cb49c install babel-polyfill 2021-05-08 17:28:14 +08:00
caishi 705bfb07bd Merge branch 'develop' 2021-05-08 16:53:26 +08:00
caishi d783d998fc 编辑合并请求-url地址错误 2021-05-08 14:36:12 +08:00
caishi 51925542b8 Merge branch 'develop'
# Conflicts:
#	public/css/edu-purge.css
2021-05-07 16:37:21 +08:00
caishi ff06efe109 merge develop 2021-05-06 17:15:43 +08:00
caishi c33561c212 新建项目-maxlength 2021-05-06 17:14:42 +08:00
caishi 2deb6f8f9d 设置所有标签的marginbottom为0 2021-05-06 17:14:30 +08:00
caishi 63d69a955e 代码库-提交描述未换行 2021-05-06 17:14:19 +08:00
61 changed files with 1815 additions and 98 deletions

124
LICENSE Normal file
View File

@ -0,0 +1,124 @@
木兰宽松许可证, 第2版
2020年1月 http://license.coscl.org.cn/MulanPSL2
您对“软件”的复制、使用、修改及分发受木兰宽松许可证第2版“本许可证”的如下条款的约束
0. 定义
“软件” 是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。
“贡献” 是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。
“贡献者” 是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。
“法人实体” 是指提交贡献的机构及其“关联实体”。
“关联实体” 是指对“本许可证”下的行为方而言控制、受控制或与其共同受控制的机构此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。
1. 授予版权许可
每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。
2. 授予专利许可
每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。
3. 无商标许可
“本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可但您为满足第4条规定的声明义务而必须使用除外。
4. 分发限制
您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。
5. 免责声明与责任限制
“软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。
6. 语言
“本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。
条款结束
如何将木兰宽松许可证第2版应用到您的软件
如果您希望将木兰宽松许可证第2版应用到您的新软件为了方便接收者查阅建议您完成如下三步
1 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字;
2 请您在软件包的一级目录下创建以“LICENSE”为名的文件将整个许可证文本放入该文件中
3 请将如下声明文本放入每个源文件的头部注释中。
Copyright (c) [Year] [name of copyright holder]
[Software Name] is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
Mulan Permissive Software LicenseVersion 2
Mulan Permissive Software LicenseVersion 2 (Mulan PSL v2)
January 2020 http://license.coscl.org.cn/MulanPSL2
Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions:
0. Definition
Software means the program and related documents which are licensed under this License and comprise all Contribution(s).
Contribution means the copyrightable work licensed by a particular Contributor under this License.
Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License.
Legal Entity means the entity making a Contribution and all its Affiliates.
Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, control means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity.
1. Grant of Copyright License
Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not.
2. Grant of Patent License
Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken.
3. No Trademark License
No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in section 4.
4. Distribution Restriction
You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software.
5. Disclaimer of Warranty and Limitation of Liability
THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO MATTER HOW ITS CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
6. Language
THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL.
END OF THE TERMS AND CONDITIONS
How to Apply the Mulan Permissive Software LicenseVersion 2 (Mulan PSL v2) to Your Software
To apply the Mulan PSL v2 to your work, for easy identification by recipients, you are suggested to complete following three steps:
Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner;
Create a file named "LICENSE" which contains the whole context of this License in the first directory of your software package;
Attach the statement to the appropriate annotated syntax at the beginning of each source file.
Copyright (c) [Year] [name of copyright holder]
[Software Name] is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.

View File

@ -3971,9 +3971,12 @@ html>body #ajax-indicator {
display: block;
height: 100%;
width: 100%;
color: #333;
color: #fff;
font-size: 16px;
}
.head-right i{
color: #fff!important;
}
.head-nav ul#header-nav li a:hover,.head-nav ul#header-nav li.active a {
color: #5091FF;

View File

@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 2340181 */
src: url('iconfont.woff2?t=1632964996877') format('woff2'),
url('iconfont.woff?t=1632964996877') format('woff'),
url('iconfont.ttf?t=1632964996877') format('truetype');
src: url('iconfont.woff2?t=1634881729644') format('woff2'),
url('iconfont.woff?t=1634881729644') format('woff'),
url('iconfont.ttf?t=1634881729644') format('truetype');
}
.iconfont {

View File

@ -322,6 +322,13 @@ class App extends Component {
{/* 查询 */}
<Route path="/search" component={Search} />
<Route exact path="/explore/all"
render={
(props) => (
<ProjectIndex {...this.props} {...props} />
)
}
/>
<Route exact path="/explore"
render={
(props) => (

View File

@ -32,7 +32,7 @@ export default ({history}) => {
/>
</div>
:
<i className="iconfont icon-sousuo font-18 color-grey-6 ml30" onClick={() => {
<i className="iconfont icon-sousuo font-18 ml30" onClick={() => {
setOpenSearch(true)
}} />
}

View File

@ -19,7 +19,7 @@ function Footer(){
return(
<div>
<div style={{height:"543px"}}></div>
<div style={{height:"483px"}}></div>
<div className="newFooter edu-txt-center">
{value && showhtml(value)}
{/* <div className="footerInfos">

View File

@ -433,7 +433,7 @@ class NewHeader extends Component {
{
current_user && (current_user.main_site || current_user.login) && (settings && settings.add && settings.add.length>0)?
<Dropdown overlay={this.addMenu(settings && settings.add)} placement="bottomRight">
<i className="iconfont icon-tianjiafangda color-grey-6 ml30 mr15"></i>
<i className="iconfont icon-tianjiafangda ml30 mr15"></i>
</Dropdown>:""
}
@ -448,7 +448,7 @@ class NewHeader extends Component {
>
<Link to={"/settings/notice"} className="message-icon">
{current_user && <Badge count={current_user.message_unread_total}>
<i className="iconfont icon-xiaoxilingdang color-grey-6 ml15 mr15"></i>
<i className="iconfont icon-xiaoxilingdang ml15 mr15"></i>
</Badge>}
</Link>
</Popover>
@ -457,10 +457,10 @@ class NewHeader extends Component {
</div>
{!user || (user && !user.login) ?
<span className="font-15 ml30">
<a onClick={() => this.educoderlogin()} className="mr5 color-grey-6">登录</a>
<a onClick={() => this.educoderlogin()} className="mr5 color-white">登录</a>
{
settings && settings.common && settings.common.register &&
<span><em className="vertical-line"></em><a className="ml5 color-grey-6" href={`${settings.common.register}`} target="_blank"></a></span>
<span><em className="vertical-line"></em><a className="ml5 color-white" href={`${settings.common.register}`} target="_blank"></a></span>
}
</span>
:

View File

@ -224,7 +224,7 @@ function NoticeContent({ visible, showNotification, resetUserInfo, current_user:
<Badge color="#FA2020" />
</span>
<div className="noticeCont-text">
<span className="content-span atme-cont-span" dangerouslySetInnerHTML={{ __html: "<b>" + (item.sender ? item.sender.name : '') + "</b>&nbsp;&nbsp;&nbsp;" + item.content + " 中@我" }}></span>
<span className="content-span atme-cont-span" dangerouslySetInnerHTML={{ __html: item.content }}></span>
<span className="timeSpan">{item.time_ago}</span>
</div>
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

@ -22,6 +22,11 @@ const ProjectIndex = Loadable({
loading: Loading,
});
const ProjectHome = Loadable({
loader: () => import("./Main/projecthome/Index"),
loading: Loading,
});
// 项目详情放在用户和组织下作为二级菜单存在
// const ProjectDetail = Loadable({
// loader: () => import("./Main/Detail"),
@ -67,16 +72,22 @@ class Index extends Component {
)}
></Route> */}
<Route
path="/explore"
path="/explore/all"
render={(props) => (
<ProjectIndex {...this.props} {...props} />
)}
></Route>
<Route
path="/explore"
render={(props) => (
<ProjectHome {...this.props} {...props} />
)}
></Route>
<Route
path="/"
render={(props) => (
<ProjectIndex {...this.props} {...props} />
<ProjectHome {...this.props} {...props} />
)}
></Route>
</Switch>

View File

@ -138,7 +138,7 @@ function CoderDepot(props){
if(result && result.data){
const release = {
"list":result.data.releases,
"total_count":result.data.releases.length
"total_count":result.data.releases && result.data.releases.length
}
setReleaseVersions(release);
}

View File

@ -41,7 +41,7 @@ function CoderDepotReadme({ operate , history , readme , ChangeFile }){
return(
<div className="commonBox readBox" id="readme">
<Anchor offsetTop={70} targetOffset={160}>
<Anchor offsetTop={70}>
<div className="commonBox-title boxTitle">
<AlignCenter>
<Dropdown overlay={menu()} trigger={['hover']} overlayClassName="menuslist">

View File

@ -0,0 +1,92 @@
import React , { useState, useEffect } from 'react';
import { Link } from "react-router-dom";
import { Dropdown , Menu , Icon , Tooltip , Spin } from 'antd';
import { truncateCommitId } from '../common/util';
import { getBranch } from '../GetData/getData';
import Nodata from '../Nodata';
import './list.scss';
function turnbar(str){
if(str && str.length>0 && str.indexOf("/")>-1){
return str.replaceAll('/','%2F');
}
return str;
}
export default ((props)=>{
const [ data , setData ] =useState(undefined);
const [ isSpin , setIsSpin ] =useState(true);
const { projectsId , owner } = props.match.params;
const { isManager , isDeveloper , projectDetail } = props;
useEffect(()=>{
getBranchs(projectsId, owner);
},[projectsId])
async function getBranchs(id,owner){
let result = await getBranch(id,owner);
setData(result);
setIsSpin(false);
}
const list =()=>{
if(data && data.length>0){
return(
<React.Fragment>
<ul className="branchUl">
{
data.map((item,key)=>{
return(
<li key={key}>
<div>
<Link to={`/${owner}/${projectsId}/tree/${turnbar(item.name)}`} className="color-blue font-15" style={{"maxWidth":"100px"}}>{item.name}</Link>
<p className="f-wrap-alignCenter mt15">
<Link to={`/${owner}/${projectsId}/commits/${truncateCommitId(`${item.last_commit.sha}`)}`} className="mr5 commitKey" style={{marginLeft:0}}>{item.last_commit && truncateCommitId(item.last_commit.sha)}</Link>
<span className="color-grey-3 hide-1 messages leftPoint">{item.last_commit && item.last_commit.message}</span>
<span className="color-grey-8 ml30">最后更新于{item.last_commit && item.last_commit.time_from_now}</span>
</p>
</div>
<span>
{
(isManager || isDeveloper) && (projectDetail && projectDetail.type!==2) &&
<Link to={`/${owner}/${projectsId}/pulls/new/${item.name}`} className="mr20 color-blue mr30">创建合并请求</Link>
}
<Dropdown overlay={menu(item.zip_url,item.tar_url)} trigger={['click']} placement="bottomRight" className="color-green-file">
<a className="ant-dropdown-link">
<Tooltip title={`下载分支${item.name}`}><Icon type="cloud-download" className="font-18"/></Tooltip>
</a>
</Dropdown>
</span>
</li>
)
})
}
</ul>
</React.Fragment>
)
}else if(data && data.length === 0){
return ( <Nodata _html="暂无数据"/>)
}
}
const menu =(zip_url,tar_url)=> (
<Menu>
<Menu.Item key={'0'}><a href={zip_url}>ZIP</a></Menu.Item>
<Menu.Item key={'1'}><a href={tar_url}>TAR.GZ</a></Menu.Item>
</Menu>
)
return(
<React.Fragment>
<div className="main">
<Spin spinning={isSpin}>
<div className="branchTable">
<p className="branchTitle bor-bottom-greyE">分支列表</p>
<div style={{minHeight:"400px"}}>{list()}</div>
</div>
</Spin>
</div>
</React.Fragment>
)
})

View File

@ -3,6 +3,7 @@ import { Spin, Tooltip } from 'antd';
import { Link, Route, Switch } from 'react-router-dom';
import { Content, AlignTop } from '../Component/layout';
import DetailBanner from './sub/DetailBanner';
import { numFormat } from 'educoder';
import '../css/index.scss'
import './list.scss';
@ -570,6 +571,18 @@ class Detail extends Component {
}
</div>
</AlignTop>
{/* <div className="mt6" style={{minHeight:"20px"}}>
{
projectDetail && projectDetail.forked_from_project_id && projectDetail.fork_info ?
this.textFunc(projectDetail.forked_from_project_id,projectDetail.fork_info)
:""
}
{
projectDetail && projectDetail.type && projectDetail.type !== 0 ?
<span className="color-grey-9">镜像自 <a className="color-blue hoverLine" target="_blank" href={projectDetail.mirror_url}>{projectDetail.mirror_url}</a></span>
:""
}
</div> */}
{
firstSync ? "" :
<DetailBanner

View File

@ -50,7 +50,7 @@ class Index extends Component {
this.getCategory();
this.getRecommand();
// this.getRecommand();
this.getLanguage();
@ -304,9 +304,9 @@ class Index extends Component {
return (
<div>
<p className="t_project_banner">
{/* <p className="t_project_banner">
<img src={banner} width="100%" alt=""/>
</p>
</p> */}
{/* {
recommendOriList && recommendOriList.length>0?
<Slider {...settings} className="recommandOri">
@ -320,7 +320,7 @@ class Index extends Component {
</Slider>
:""
} */}
{
{/* {
recommendList && recommendList.length>0 &&
<Slider {...setting} className={recommendList.length>5 ? "recommandProjects":"recommandProjects mb20"}>
{
@ -341,7 +341,7 @@ class Index extends Component {
})
}
</Slider>
}
} */}
<div className="ProjectListIndex">
<div className="list-left">
<ul className="list-l-Menu">

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 463 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 969 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,126 @@
import React , { useEffect , useState } from 'react';
import './Index.scss';
import SubBanner from './SubBanner';
import SubUnitBanner from './SubUnitBanner';
import Icon from '../img/index/icon.png';
import { Link } from 'react-router-dom';
import { Spin } from 'antd';
import SubList from './SubList';
import more from '../img/index/more.png';
import axios from 'axios';
import { getImageUrl } from 'educoder';
import Nodata from '../../Nodata';
const LIMIT = 20;
function Index() {
const [ cateList , setCateList ] = useState(undefined);
const [ projectsList , setProjectsList ] = useState(undefined);
const [ cateID, setCateID ] = useState(undefined);
const [ isSpin, setIsSpin ] = useState(true);
useEffect(()=>{
getCate();
},[])
useEffect(()=>{
setIsSpin(true);
getProject();
},[cateID])
function getCate() {
const url = `/project_categories/pinned_index.json`;
axios.get(url).then(result=>{
if(result && result.data){
setCateList(result.data.project_categories);
}
}).catch(error=>{})
}
function getProject() {
const url = `/projects.json`;
axios.get(url,{
params:{
pinned:"d",
category_id:cateID,
limit:LIMIT
}
}).then(result=>{
if(result && result.data){
setProjectsList(result.data.projects);
setIsSpin(false);
}
}).catch(error=>{})
}
return(
<div>
<SubBanner />
<SubUnitBanner />
<div className="dataPanel">
<div className="left">
<ul className="leftTypes">
<a className={cateID ? "" : "active"} onClick={()=>setCateID(undefined)}><img src={Icon} alt="" /><span>最近更新</span></a>
{
cateList && cateList.length>0?
cateList.map((i,k)=>{
return(
<a className={cateID === i.id ?"active":""} onClick={()=>setCateID(i.id)}><img src={i.logo_url || Icon} alt="" /><span>{i.name}</span></a>
)
})
:""
}
</ul>
<div className="leftLists">
<div className="leftTitles">
<span>开源项目</span>
<Link to={`/explore/all`}>更多<i className="iconfont icon-triangle font-12"></i></Link>
</div>
<Spin spinning={isSpin}>
<div style={{minHeight:"400px"}}>
{
projectsList && projectsList.length > 0 ?
<div className="leftlistItem">
{
projectsList.map((i,k)=>{
return(
<li>
<Link to={`/${i.author && i.author.login}`}><img src={getImageUrl(`/${i.author && i.author.image_url}`)} alt="" /></Link>
<div className="itemTitle">
<div className="item-title-infos">
<Link to={`/${i.author && i.author.login}/${i.identifier}`} className="infotitle">{i.author && i.author.name}/{i.name}</Link>
{i.praises_count > 0 ? <span><i className="iconfont icon-dianzan11 mr3 font-16"></i>{i.praises_count}</span> :"" }
{i.forked_count > 0 ? <span><i className="iconfont icon-fork2 mr3 font-13"></i>{i.forked_count}</span>:""}
</div>
<div className="item-desc task-hide-2">
{i.description}
</div>
<div className="item-data">
{i.category && i.category.id ? <span className="category">{i.category.name}</span> :"" }
{i.language && i.language.id ? <span className="language mr30">{i.language.name}</span> :""}
<span style={{lineHeight:"15px",display:"flex"}}><i className="iconfont icon-shijian font-15 mr5"></i>更新于{i.time_ago}</span>
</div>
</div>
</li>
)
})
}
</div>
:""
}
{
projectsList && projectsList.length === 0 && <Nodata _html="暂无数据" />
}
</div>
</Spin>
<div className="left-bottom-btn">
<Link to={`/explore/all`}>查看更多开源项目<img src={more} alt="" /></Link>
</div>
</div>
</div>
<SubList />
</div>
</div>
)
}
export default Index;

View File

@ -0,0 +1,553 @@
.banners{
background: url('../img/index/banner.png') no-repeat top;
min-height: 516px;
background-size: cover;
background-color: #0C2A5B;
.bannersCenter{
padding-top: 30px;
max-width: 1200px;
margin: 0px auto;
position: relative;
height: 516px;
}
.bTitle{
height: 30px;
line-height: 30px;
font-size: 30px;
font-weight: 500;
color: #FFFFFF;
margin-bottom: 20px!important;
text-align: center;
span{
background: linear-gradient(to right,#289AF6 0%, #9E84FF 40%, #FFB03B 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
}
.bSubTitle{
text-align: center;
height: 30px;
line-height: 30px;
font-size: 16px;
font-weight: 400;
color: #FFFFFF;
}
.bannerBox{
background: url('../img/index/box.png') no-repeat;
background-size: 100% 100%;
height: 240px;
width: 380px;
margin:0px auto;
padding:25px 30px 34px;
.bannersProject{
position: relative;
.slick-list{
width: 100%;
overflow: hidden;
}
.slick-track{
display: flex;
li{
padding:15px 20px;
.projectinfos{
display: flex;
align-items: center;
width: 100%;
margin-bottom: 13px;
img{
width: 32px;
height: 32px;
margin-right: 7px;
border-radius: 50%;
}
.name{
height: 28px;
line-height: 28px;
font-size: 20px;
font-weight: 500;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
max-width: 250px;
color: #25EBFF;
a{
color: #25EBFF!important;
}
}
.company{
height: 20px;
line-height: 20px;
font-size: 14px;
font-weight: 400;
color: #FFFFFF;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
max-width: 250px;
a{
color: #FFFFFF!important;
}
}
}
.desc{
height: 40px;
font-size: 13px;
font-weight: 400;
color: #FFFFFF;
line-height: 20px;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
.infoData{
display: flex;
margin-top: 16px;
justify-content: space-around;
&>span{
display: flex;
align-items: center;
&>span{
color: #FFB729;
font-size: 15px;
font-weight: 400;
}
}
img{
width: 16px;
margin-right: 4px;
}
}
}
}
}
}
.airBubble{
&>div{
position: absolute;
height: 410px;
width: 415px;
bottom: 0px;
&.right{
right: 0px;
}
&.left{
left: 0px;
}
&.right div, &.left div{
position: relative;
width: 100%;
height: 100%;
span{
position: absolute;
border-radius: 50%;
background: linear-gradient(124deg, rgba(255, 255, 255, 0.1) 0%, rgba(5, 200, 220, 0.04) 50%, rgba(5, 200, 220, 0.03) 100%);
box-shadow: 0px 0px 7px 3px rgba(0, 154, 255, 0.19);
border: 1px solid rgba(0, 154, 255, 0.19);
font-size: 14px;
font-weight: 400;
line-height: 17px;
display: flex;
align-items: center;
word-break: break-all;
text-align: center;
padding:5px;
animation: zoomin 1.3s infinite alternate;
&:hover{
animation:none
}
}
}
&.right span:nth-child(4) a,&.left span:nth-child(1) a,&.left span:nth-child(2) a{
color: #25EBFF!important;
}
&.right span:nth-child(1) a,&.left span:nth-child(5) a{
color: #CD8AFF!important;
}
&.right span:nth-child(3) a,&.right span:nth-child(5) a,&.left span:nth-child(6) a{
color: #FF6125!important;
}
&.left span:nth-child(3) a{
color: #FF8425!important;
}
&.right span:nth-child(2) a,&.left span:nth-child(4) a{
color: #8CC2FF!important;
}
&.right span:nth-child(1){
right: 0px;
height: 120px;
width: 120px;
}
&.right span:nth-child(2){
right: 160px;
height: 72px;
width: 72px;
bottom: 220px;
padding:0px;
animation-delay: 0.2s;
}
&.right span:nth-child(3){
right: 300px;
height: 94px;
width: 94px;
bottom: 99px;
padding:8px;
animation-delay: 0.3s;
}
&.right span:nth-child(4){
right: 156px;
height: 100px;
width: 100px;
bottom: 39px;
font-size: 12px;
animation-delay: 0.2s;
}
&.right span:nth-child(5){
right: 10px;
height: 94px;
width: 94px;
bottom: 97px;
animation-delay: 0.4s;
}
&.left span:nth-child(1){
left: 80px;
height: 110px;
width: 110px;
padding:8px;
animation-delay: 0.1s;
}
&.left span:nth-child(2){
right: 60px;
height: 96px;
width: 96px;
top: 85px;
font-size: 13px;
animation-delay: 0.2s;
}
&.left span:nth-child(3){
left: 0px;
height: 86px;
width: 86px;
top: 145px;
font-size: 13px;
animation-delay: 0.3s;
}
&.left span:nth-child(4){
left: 164px;
height: 70px;
width: 70px;
bottom: 158px;
font-size: 12px;
font-size: 12px;
animation-delay: 0.3s;
}
&.left span:nth-child(5){
left: 35px;
height: 102px;
width: 102px;
bottom: 32px;
font-size: 13px;
animation-delay: 0.4s;
}
&.left span:nth-child(6){
right: 10px;
height: 114px;
width: 114px;
bottom: 26px;
padding:8px;
}
}
}
}
@keyframes zoomin{
0%{-webkit-transform:scale(1.05);transform:scale(1.05)}
100%{-webkit-transform:scale(0.8);transform:scale(0.8)}
}
@-webkit-keyframes zoomin{
0%{-webkit-transform:scale(1.05);transform:scale(1.05)}
100%{-webkit-transform:scale(0.8);transform:scale(0.8)}
}
.unitBanner{
padding:29px 0px;
background: #F9F9F9;
.unitContent{
max-width: 1200px;
margin:0px auto;
.unitTitle{
height: 28px;
line-height: 28px;
font-size: 20px;
font-weight: 500;
color: #1E1E1E;
display: flex;
align-items: center;
img{
margin-left: 9px;
}
}
.unitSlider{
padding:30px 0px 10px;
.slick-list{
width: 100%;
height: 56px;
overflow: hidden;
.slickline{
display: flex!important;
align-items: center;
justify-content: space-between;
}
}
}
}
}
.dataPanel{
width: 1200px;
display: flex;
justify-content: space-between;
padding:30px 0px 60px;
margin:0px auto;
.left{
width: 850px;
box-shadow: 0px 0px 4px 5px rgba(0, 0, 0, 0.02);
display: flex;
.leftTypes{
width: 220px;
height: 1576px;
background: url('../img/index/typebg.png');
background-size: 100% 100%;
box-shadow: 0px 0px 4px 5px rgba(0, 0, 0, 0.02);
padding:12px 0px;
a{
padding:0px 20px;
height: 44px;
margin-top: 10px;
font-size: 16px;
font-weight: 500;
color: #333333;
display: flex;
align-items: center;
cursor: pointer;
&:hover{
background-color: #F5F5F5;
}
&.active{
background: linear-gradient(to right,#07228F , #466AFF);
color: #fff!important;
}
img{
margin-right: 12px;
width: 28px;
}
}
}
.leftLists{
flex:1;
.leftTitles{
height: 60px;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid rgba(153, 153, 153, 0.16);
padding:0px 20px;
span{
font-size: 18px;
font-weight: 500;
color: #000000;
}
a{
color: #466AFF!important;
}
}
.leftlistItem{
padding:0px 20px;
li{
border-bottom: 1px solid rgba(153, 153, 153, 0.16);
display: flex;
align-items: flex-start;
padding:20px 0px ;
&>a img{
width: 32px;
height: 32px;
border-radius: 50%;
margin-right: 10px;
}
.itemTitle{
flex:1;
.item-title-infos{
height: 22px;
display: flex;
align-items: center;
.infotitle{
flex:1;
height: 21px;
font-size: 15px;
font-weight: 600;
line-height: 21px;
color: #333!important;
&:hover{
color: #466AFF!important;
}
}
span{
margin-left:20px ;
color: #333;
i{
color: #666;
}
}
}
.item-desc{
font-size: 14px;
font-weight: 400;
color: #414141;
line-height: 24px;
max-width: 548px;
margin: 5px 0px;
}
.item-data{
font-size: 13px;
font-weight: 400;
color: #7D7D7D;
height: 20px;
line-height: 20px;
display: flex;
align-items: flex-end;
.category{
position: relative;
padding-left: 11px;
margin-right: 10px;
&::before{
position: absolute;
content: "";
width: 6px;
height: 6px;
border-radius: 50%;
background-color: #466AFF;
top: 7px;
left: 0px;
}
}
.language{
position: relative;
padding-left: 11px;
&::before{
position: absolute;
content: "";
width: 1px;
height: 10px;
border-radius: 50%;
border-left: 1px solid #9e9e9e;
bottom: 4px;
left: 0px;
}
}
}
}
}
}
}
.left-bottom-btn{
display: flex;
justify-content: center;
padding:18px 0px;
a{
height: 38px;
line-height: 36px;
border-radius: 8px;
border: 1px solid #466AFF;
color: #466AFF;
font-size: 16px;
padding:0px 20px;
img{
height: 16px;
margin-top: -1px;
margin-left: 8px;
}
}
}
}
.right{
width: 330px;
&>div{
box-shadow: 0px 0px 4px 5px rgba(0, 0, 0, 0.02);
margin-bottom: 20px;
}
.partTitle{
padding:15px;
background: #F6F9FF;
font-size: 18px;
color: #000000;
font-weight: 500;
margin-bottom: 10px;
img{
width: 33px;
margin-right: 6px;
}
}
.righthotAuthor{
padding-bottom: 15px;
li{
padding-left: 15px;
&>div{
display: flex;
align-items: flex-start;
padding:10px 15px 10px 0px;
border-bottom: 1px solid rgba(153, 153, 153, 0.1);
img{
width: 32px;
height: 32px;
border-radius: 50%;
margin-right: 8px;
margin-top: 3px;
}
}
&:hover{
background-color: #F3F3F3;
}
&:last-child > div{
border-bottom: none;
}
}
}
.hotProjects{
li{
padding:10px 15px;
margin-bottom: 10px;
&:hover{
background-color: #F3F3F3;
}
.mInfos{
display: flex;
align-items: center;
height: 20px;
.num{
width: 18px;
height: 18px;
background: #466AFF;
border-radius: 2px;
color: #fff;
text-align: center;
line-height: 18px;
margin-right: 8px;
}
.name{
flex:1;
font-size: 15px;
}
}
.sInfos{
background: #F7F8F9;
padding:5px;
font-size: 13px;
font-weight: 400;
color: #666666;
line-height: 22px;
word-break: break-all;
margin-top: 10px;
}
}
}
}
}

View File

@ -0,0 +1,115 @@
import React , { useEffect , useState } from 'react';
import Slider from 'react-slick';
import { Link } from 'react-router-dom';
import { getImageUrl } from 'educoder';
import Eye from '../img/index/eye.png';
import Data from '../img/index/data.png';
import Earth from '../img/index/earth.png';
import Imgs from '../img/tree.png';
import axios from 'axios';
// const list =[
// {img:Imgs,name:"XiUOS",company:"",desc:"",look:432,cloud:"",type:"C++"},
// {img:Imgs,name:"openGauss-operator",company:"",desc:"",look:432,cloud:"",type:"C++"},
// {img:Imgs,name:"PaddleDetection 2.0",company:"TrustieMirrors",desc:"",look:432,cloud:"",type:"C++"},
// {img:Imgs,name:"skyline",company:"",desc:"",look:432,cloud:"",type:"C++"},
// {img:Imgs,name:"BitXHub",company:"",desc:"",look:432,cloud:"",type:"C++"},
// {img:Imgs,name:"Device Model",company:"openDACS",desc:"",look:432,cloud:"",type:"C++"},
// {img:Imgs,name:"Gitlink",company:"Gitlink",desc:"",look:432,cloud:"",type:"C++"},
// {img:Imgs,name:"openEuler-datenlord",company:"",desc:"",look:432,cloud:"",type:"C++"},
// {img:Imgs,name:"OpenAtom XuperChain",company:"",desc:"",look:432,cloud:"",type:"C++"},
// {img:Imgs,name:"xuos-web",company:"",desc:"",look:432,cloud:"",type:"C++"},
// {img:Imgs,name:"CrowdOS",company:"西",desc:"",look:432,cloud:"",type:"C++"},
// ]
const settings={
dots: false,
infinite: true,
speed: 1000,
slidesToShow: 1,
slidesToScroll: 1,
autoplay:true,
arrows:false,
adaptiveHeight:true
}
function SubBanner() {
const [ list , setList ] = useState(undefined);
useEffect(()=>{
getList();
},[])
function getList() {
const url = `/projects/banner_recommend.json`;
axios.get(url).then(result=>{
if(result && result.data){
setList(result.data.projects);
}
}).catch(error=>{})
}
return(
<div className="banners">
<div className="bannersCenter">
<p className="bTitle"><span>新一代开源创新服务平台</span></p>
<p className="bSubTitle">Gitlink确实开源-CCF官网指定的产学研融合面向软件开源创新的开源社区</p>
<div className="bannerBox">
{
list && list.length > 0 ?
<Slider {...settings} className="bannersProject">
{
list.map((i,k)=>{
return(
<li>
<div className="projectinfos">
<Link to={`/${i.author && i.author.login}`}><img src={getImageUrl(`/${i.author && i.author.image_url}`)} alt="" /></Link>
<div>
<p className="name"><Link to={`/${i.author && i.author.login}/${i.identifier}`}>{i.name}</Link></p>
<p className="company"><Link to={`/${i.author && i.author.login}`}>{i.author && i.author.name}</Link></p>
</div>
</div>
<p className="desc">{i.description}</p>
<div className="infoData">
<span><img src={Eye} alt="" /><span>{i.visits}</span></span>
{i.category && i.category.id && <span><img src={Data} alt="" /><span>{i.category.name}</span></span>}
{i.language && i.language.id && <span><img src={Earth} alt="" /><span>{i.language.name}</span></span>}
</div>
</li>
)
})
}
</Slider>
:""
}
</div>
<div className="airBubble">
<div className="left">
<div>
{
list && list.length > 0 && list.map((i,k)=>{
return(
k%2 === 0? <span><Link to={`/${i.author && i.author.login}/${i.identifier}`}>{i.author && i.author.name}/{i.name}</Link></span> : ""
)
})
}
</div>
</div>
<div className="right">
<div>
{
list && list.length > 0 && list.map((i,k)=>{
return(
k%2 > 0 ? <span><Link to={`/${i.author && i.author.login}/${i.identifier}`}>{i.author && i.author.name}/{i.name}</Link></span> : ""
)
})
}
</div>
</div>
</div>
</div>
</div>
)
}
export default SubBanner

View File

@ -0,0 +1,138 @@
import React , { useEffect , useState } from 'react';
import hotAuthor from '../img/index/hotAuthor.png';
import week from '../img/index/week.png';
import month from '../img/index/month.png';
import { Link } from 'react-router-dom';
import axios from 'axios';
import { getImageUrl } from 'educoder';
function SubList() {
const [ weekList ,setWeekList ] = useState(undefined);
const [ monthList ,setMonthList ] = useState(undefined);
const [ authorList ,setAuthorList ] = useState(undefined);
useEffect(()=>{
getList(7);
getList(30);
getAuthorList(7);
},[])
function getList(time){
const url = `/project_rank.json`;
axios.get(url,{
params:{
time
}
}).then(result=>{
if(result && result.data){
time === 7 ? setWeekList(result.data.projects) : setMonthList(result.data.projects);
}
}).catch(error=>{})
}
function getAuthorList(time){
const url = `/user_rank.json`;
axios.get(url,{
params:{
time
}
}).then(result=>{
if(result && result.data){
setAuthorList(result.data.users);
}
}).catch(error=>{})
}
return(
<div className="right">
{
authorList && authorList.length > 0?
<div>
<div className="partTitle"><img src={hotAuthor} alt="" /><span>本周热门开发者</span></div>
<div className="righthotAuthor">
{
authorList.map((i,k)=>{
return(
<li>
<div>
<Link to={`/${i.login}`}><img src={getImageUrl(`/${i.avatar_url}`)} alt=""/></Link>
<div>
<Link to={`/${i.login}`} className="font-15">{i.name}</Link>
<p className="task-hide" style={{maxWidth:"260px"}}>
<Link to={`/${i.login}/${i.project && i.project.identifier}`}><i className="iconfont icon-daimakuicon1 font-14 mr8"></i>{i.project && i.project.name}</Link>
</p>
</div>
</div>
</li>
)
})
}
</div>
</div>
:""
}
{
weekList && weekList.length > 0 ?
<div>
<div className="partTitle"><img src={week} alt="" /><span>本周热门项目</span></div>
<div className="hotProjects">
{
weekList.map((i,k)=>{
return(
<li>
<div className="mInfos">
<span className="num">{k+1}</span>
<Link to={`/${i.owner && i.owner.login}/${i.identifier}`} className="name task-hide">{i.owner && i.owner.name}/{i.name}</Link>
<span>
<i className="iconfont icon-dianzan11 font-16 mr4"></i>{i.praises}
</span>
</div>
{i.description &&
<div className="sInfos task-hide-2">
{i.description}
</div>
}
</li>
)
})
}
</div>
</div>
:""
}
{
monthList && monthList.length > 0?
<div>
<div className="partTitle"><img src={month} alt="" /><span>本月热门项目</span></div>
<div className="hotProjects">
{
monthList.map((i,k)=>{
return(
<li>
<div className="mInfos">
<span className="num">{k+1}</span>
<Link to={`/${i.owner && i.owner.login}/${i.identifier}`} className="name task-hide">{i.owner && i.owner.name}/{i.name}</Link>
<span>
<i className="iconfont icon-dianzan11 font-16 mr4"></i>{i.praises}
</span>
</div>
{i.description &&
<div className="sInfos task-hide-2">
{i.description}
</div>
}
</li>
)
})
}
</div>
</div>
:""
}
</div>
)
}
export default SubList;

View File

@ -0,0 +1,84 @@
import React , { useEffect , useState } from 'react';
import Hot from '../img/index/hot.png';
import Slider from "react-slick";
import huawei from '../img/index/unit/huawei.png';
import langchao from '../img/index/unit/langchao.png';
import jijinhui from '../img/index/unit/jijinhui.png';
import mulan from '../img/index/unit/mulan.png';
import xigongye from '../img/index/unit/xigongye.png';
import feiteng from '../img/index/unit/feiteng.png';
import xiuos from '../img/index/unit/xiuos.png';
import huake from '../img/index/unit/huake.png';
import axios from 'axios';
import { Link } from 'react-router-dom';
import { getImageUrl } from 'educoder';
const settings = {
dots: false,
infinite: true,
slidesToShow: 1,
slidesToScroll: 1,
vertical: true,
verticalSwiping: true,
autoplay:true,
arrows:false
};
const group_size = 6;
function SubUnitBanner() {
const [ list , setlist ] = useState(undefined);
useEffect(()=>{
getRecommandOrz();
},[])
function getRecommandOrz(params) {
const url = `/organizations/recommend.json`;
axios.get(url,{
params:{
group_size
}
}).then(result=>{
if(result){
setlist(result.data.organizations)
}
}).catch(error=>{})
}
return(
list && list.length > 0 ?
<div className="unitBanner">
<div className="unitContent">
<div className="unitTitle">
<span>精选开源组织</span>
<img src={Hot} alt="" width="47px"/>
</div>
<Slider {...settings} className="unitSlider">
{
list.map((i,k)=>{
return(
<div className="slickline">
{
i.map((j,k1)=>{
return(
<Link to={j.name}><img src={getImageUrl(`/${j.avatar_url}`)} alt="" height="56px" style={{maxWidth:"180px"}}/></Link>
)
})
}
</div>
)
})
}
{/* <div className="slickline">
<a href="https://www.nwpu.edu.cn/" target="_blank"><img src={xigongye} alt="" height="56px"/></a>
<a href="https://www.phytium.com.cn/" target="_blank"><img src={feiteng} alt="" height="56px"/></a>
<a href="http://xuos.io" target="_blank"><img src={xiuos} alt="" height="56px"/></a>
<a href="https://www.hust.edu.cn/" target="_blank"><img src={huake} alt="" height="56px"/></a>
</div> */}
</Slider>
</div>
</div>
:""
)
}
export default SubUnitBanner;

View File

@ -48,7 +48,7 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa
item.menu_name === "issues" &&
<li className={pathname==="issues" ? "active" : ""}>
<Link to={{ pathname: `/${owner}/${projectsId}/issues`, state }}>
<Tooltip title="易修是Issue的中文名即问题列表" placement="bottom">
<Tooltip placement="bottom">
<i className={"iconfont icon-yixiuicon1 color-grey-3 mr5 font-14"}></i>
<span>易修(Issue)</span>
</Tooltip>
@ -61,7 +61,7 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa
<li className={pathname==="pulls" ? "active" : ""}>
<Link to={{ pathname: `/${owner}/${projectsId}/pulls`, state }}>
<i className={"iconfont icon-hebingqingqiu1 color-grey-3 mr5 font-14"}></i>
<span>合并请求</span>
<span>合并请求(PR)</span>
{projectDetail && projectDetail.pull_requests_count ? <span className="num">{numFormat(projectDetail.pull_requests_count)}</span> : ""}
</Link>
</li>:""
@ -71,7 +71,7 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa
<li className={pathname === "wiki" ? "active" : ""}>
<Link to={{ pathname: `/${owner}/${projectsId}/wiki`, state }}>
<i className={"iconfont icon-a-wikiicon1 color-grey-3 mr5 font-14"}></i>
<span>Wiki</span>
<span>维基(Wiki)</span>
</Link>
</li>
}
@ -80,7 +80,7 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa
<li className={pathname==="devops" ? "active" : ""}>
{/* <Link to={{ pathname: `/${owner}/${projectsId}/devops${open_devops ? `/dispose`:""}`, state }}> */}
<Link to={{ pathname: `/${owner}/${projectsId}/devops`, state:{...state,open_devops} }}>
<i className="iconfont icon-gongzuoliuicon font-13 mr5 color-grey-3"></i>工作流(beta版)
<i className="iconfont icon-gongzuoliuicon font-13 mr5 color-grey-3"></i>引擎(Engine)
{projectDetail && projectDetail.ops_count ? <span>{projectDetail.ops_count}</span> : ""}
</Link>
</li>

View File

@ -36,7 +36,7 @@ function ReadmeCatelogue({ menuList , hash }) {
{
menu && menu.length>0?
<div className="anchorBox">
<Anchor affix={false} onChange={onChange}>
<Anchor affix={false} onChange={onChange} offsetTop={140}>
{
menu.map((item,key)=>{
return(

View File

@ -18,7 +18,7 @@ function Files({ data,history,owner,projectsId , parentsSha }){
useEffect(()=>{
document.addEventListener('click',()=>{setIsOpen(false)})
})
},[])
function showDown(flag,index,isBin){
if(!isBin){

View File

@ -112,9 +112,9 @@ class MergeItem extends Component {
to={`/${item.is_original ? item.fork_project_user : owner}/${ item.is_original ? item.fork_project_identifier : projectsId }/tree/${turnbar(item.pull_request_head)}`}
className="maxW200px task-hide ver-middle" style={{maxWidth:"200px"}}
>
{item.is_original
? item.fork_project_user
: project_author_name}
{item.is_fork
? item.pull_request_base
: item.author_name}
:{item.pull_request_head}
</Link>
</Tag>
@ -136,8 +136,8 @@ class MergeItem extends Component {
to={`/${owner}/${projectsId}/tree/${turnbar(item.pull_request_base)}`}
className="maxW200px task-hide ver-middle" style={{maxWidth:"200px"}}
>
{/* {item.is_fork ? item.pull_request_base : `${item.author_name}:${item.pull_request_base}`} */}
{project_author_name}:{item.pull_request_base}
{item.is_fork ? item.pull_request_base : `${item.author_name}:${item.pull_request_base}`}
{/* {project_author_name}:{item.pull_request_base} */}
</Link>
</Tag>
}

View File

@ -365,10 +365,10 @@ class MessageCount extends Component {
<div className="mt15">
<Tag className="pr-branch-tag">
<Link
to={`/${data.pull_request.is_original ? data.pull_request.fork_project_user : data.issue.project_author_name}/${data.pull_request.is_original?data.project_identifier:projectsId}/tree/${turnbar(data.pull_request && data.pull_request.head)}`}
to={`/${data.pull_request.is_fork ? data.pull_request_base.fork_project_user : data.issue.author_name}/${data.pull_request.is_original?data.project_identifier:projectsId}/tree/${turnbar(data.pull_request && data.pull_request.head)}`}
className="ver-middle task-hide" style={{maxWidth:"200px"}} title={`${data.pull_request.is_original ? data.pull_request.fork_project_user : data.issue.project_author_name}: ${data.pull_request && data.pull_request.head}`}
>
{data.pull_request.is_original ? data.pull_request.fork_project_user : data.issue.project_author_name}: {data.pull_request && data.pull_request.head}
{data.pull_request.is_fork ? data.pull_request_base.fork_project_user : data.issue.author_name}: {data.pull_request && data.pull_request.head}
</Link>
</Tag>
<span className="mr8 ver-middle">
@ -383,7 +383,7 @@ class MessageCount extends Component {
to={`/${owner}/${projectsId}/tree/${data.pull_request.base}`}
className="ver-middle task-hide" style={{maxWidth:"200px"}} title={`${data.issue.project_author_name}:${data.pull_request.base}`}
>
{data.issue.project_author_name}:{data.pull_request.base}
{data.issue.author_name}:{data.pull_request.base}
</Link>
</Tag>
</div>

View File

@ -54,7 +54,7 @@ class UpdateMerge extends Component {
const { data, isSpin, pull, merge } = this.state;
return (
<div>
<div className="main">
<div className="main updateMerge">
<Spin spinning={isSpin}>
{" "}
{data ? (

View File

@ -222,4 +222,8 @@ form .ant-cascader-picker, form .ant-select {
.overlihide li{
max-width: 450px;
}
/* 距离底部加大 @列表被遮挡 */
.updateMerge{
margin: 30px auto 60px;
}

View File

@ -25,6 +25,7 @@ class MergeForm extends Component {
issue_tags: undefined,
issue_versions: undefined,
issue_priories: undefined,
atWhoLoginList:undefined
};
}
@ -146,7 +147,7 @@ class MergeForm extends Component {
} else {
values.issue_tag_ids = [];
}
const { desc } = this.state;
const { desc , atWhoLoginList } = this.state;
if (merge_type === "new") {
let url = `/${owner}/${projectsId}/pulls.json`;
axios.post(url, {
@ -158,7 +159,8 @@ class MergeForm extends Component {
fork_project_id: data && data.fork_project_id,
merge_user_login: data && data.merge_user_login,
files_count,
commits_count
commits_count,
receivers_login:atWhoLoginList,
})
.then((result) => {
if (result) {
@ -189,6 +191,7 @@ class MergeForm extends Component {
body: desc,
head: pull,
base: merge,
receivers_login:atWhoLoginList,
})
.then((result) => {
if (result) {
@ -225,6 +228,13 @@ class MergeForm extends Component {
});
};
//合并请求中at谁列表存储login
changeAtWhoLoginList = (loginList) =>{
this.setState({
atWhoLoginList:loginList,
});
};
render() {
const { merge_type } = this.props;
const { getFieldDecorator } = this.props.form;
@ -273,6 +283,10 @@ class MergeForm extends Component {
mdID={"merge-new-description"}
initValue={desc}
onChange={this.onContentChange}
isCanAtme = {true}
changeAtWhoLoginList = {this.changeAtWhoLoginList}
owner = {owner}
projectsId = {projectsId}
></MDEditor>
<p className="clearfix mt20">
<Button

View File

@ -35,7 +35,8 @@ class order_form extends Component {
get_attachments: undefined,
show_token: false,
cannot_edit: false,
issue_current_user: true
issue_current_user: true,
atWhoLoginList:undefined
};
}
componentDidUpdate=(prevPros)=>{
@ -152,7 +153,7 @@ class order_form extends Component {
if (values.issue_tag_ids.length > 0) {
values.issue_tag_ids = [values.issue_tag_ids];
}
const { description, start_date, due_date, issue_type } = this.state;
const { description, start_date, due_date, issue_type , atWhoLoginList } = this.state;
if (form_type !== "edit") {
const url = `/${owner}/${projectsId}/issues.json`;
axios.post(url, {
@ -162,6 +163,7 @@ class order_form extends Component {
start_date: start_date,
due_date: due_date,
issue_type: issue_type,
receivers_login:atWhoLoginList,
}).then((result) => {
if (result && result.data.id) {
this.props.showNotification("任务创建成功!");
@ -188,6 +190,7 @@ class order_form extends Component {
due_date: due_date,
issue_type: issue_type,
...values,
receivers_login:atWhoLoginList,
}).then((result) => {
if (result) {
this.props.history.push(`/${owner}/${projectsId}/issues/${orderId}`);
@ -221,6 +224,14 @@ class order_form extends Component {
description: value,
});
};
//issue中at谁列表存储login
changeAtWhoLoginList = (loginList) =>{
this.setState({
atWhoLoginList:loginList,
});
};
// 修改开始时间
changeBeginTime = (start_date, value) => {
this.setState({
@ -329,6 +340,10 @@ class order_form extends Component {
mdID={"order-new-description"}
initValue={description}
onChange={this.onContentChange}
isCanAtme = {true}
changeAtWhoLoginList = {this.changeAtWhoLoginList}
owner = {owner}
projectsId = {projectsId}
></MDEditor>
</div>
{get_attachments && get_attachments.length > 0 ? (

View File

@ -17,7 +17,7 @@ function MyNotice(props) {
const [selectedNum, setSelectedNum] = useState(0);//@
const [isBatchDelete, setIsBatchDelete] = useState(false);//@
const [batchDeleteCheckedAll, setBatchDeleteCheckAll] = useState(false);//@--
const [messageType, setMessageType] = useState(undefined);
const [noticeUnreadCount, setNoticeUnreadCount] = useState();//
// const [letterUnreadCount, setLetterUnreadCount] = useState(0);//
const [atUnreadCount, setAtUnreadCount] = useState();//@
@ -65,6 +65,7 @@ function MyNotice(props) {
setAtUnreadCount(response.data.unread_atme);
setMessageList(response.data.messages);
setMessTotalCount(response.data.total_count);
setMessageType(response.data.type);
}
});
}
@ -203,7 +204,46 @@ function MyNotice(props) {
</div>
</div>}
{messageList && messageList.map(item => {
{/* 系统消息 */}
{messageType === "notification" && messageList && messageList.map(item =>{
return (
<div className="mynotice-content vertical-center-style" key={item.id}>
<div className="mynotice-cont stretch-style">
{item.status === 1 ? <Badge color="#FA2020" /> : <span className="system-notice-blank"></span>}
<i className={"iconfont "+noticeSourceType[item.source]}></i>
<span className={`sysNotice-length ${item.notification_url?'highlightSpan':''}`} onClick={() => {turnToMess(item)}} dangerouslySetInnerHTML={{__html: item.content}}></span>
</div>
<div className="mynotice-cont vertical-center-style float-left-little">
<span className={item.status === 1?"timeSpan":""}>{item.time_ago}</span>
{item.status === 1 && <span className="invisable-read" onClick={()=>readNotice([item.id])}>标记为已读</span>}
</div>
</div>
)
})}
{/* @我消息 */}
{messageType === "atme" && messageList && messageList.map(item =>{
return (
<div className={`mynotice-content vertical-center-style ${isBatchDelete?'batchDel':''}`} key={item.id}>
<div className="mynotice-cont vertical-center-style">
<Checkbox value={item.id} className={isBatchDelete ? 'visible-checkbox' : 'invisible-checkbox'} onChange={onChange} checked={item.checkedBatch}></Checkbox>
{item.sender && <img src={`https://testforgeplus.trustie.net//${item.sender.image_url}`} className="currentImg" onClick={()=>{window.open(`/${item.sender && item.sender.login}`);}}/>}
<div className={`atme-notice-text stretch-style ${item.notification_url && 'highlightSpan'}`} onClick={() => {turnToMess(item)}}>
{item.status === 1 ? <Badge color="#FA2020"/> : <span className="system-notice-blank"></span>}
{item.sender && <span className="atme-length" dangerouslySetInnerHTML={{__html: item.content}}></span>}
</div>
</div>
<div className="mynotice-cont vertical-center-style">
<span className={!isBatchDelete && item.status === 1?"timeSpan":""}>{item.time_ago}</span>
{!isBatchDelete && item.status === 1 && <span className="invisable-read" onClick={()=>readNotice([item.id])}>标记为已读</span>}&nbsp;&nbsp;&nbsp;
{!isBatchDelete && <span className="invisable-read float-left-little" onClick={()=>deleteNotice([item.id])}>删除</span>}
</div>
</div>
)
})}
{false && messageList && messageList.map(item => {
console.log('item',item);
//
if (noticeType === "0") {
//
@ -229,7 +269,7 @@ function MyNotice(props) {
{item.sender && <img src={`https://testforgeplus.trustie.net//${item.sender.image_url}`} className="currentImg" onClick={()=>{window.open(`/${item.sender && item.sender.login}`);}}/>}
<div className={`atme-notice-text stretch-style ${item.notification_url && 'highlightSpan'}`} onClick={() => {turnToMess(item)}}>
{item.status === 1 ? <Badge color="#FA2020"/> : <span className="system-notice-blank"></span>}
{item.sender && <span className="atme-length" dangerouslySetInnerHTML={{__html: "<b class = 'atme-notice-name'>" + item.sender.name+ "</b> "+ item.content +" 中@我"}}></span>}
{item.sender && <span className="atme-length" dangerouslySetInnerHTML={{__html: "<b class = 'atme-notice-name'>" + item.sender.name+ "</b> "+ item.content}}></span>}
</div>
</div>
<div className="mynotice-cont vertical-center-style">

View File

@ -13,9 +13,9 @@ const menu = [
{name:"主页",index:"home"},
{name:"代码库",index:"code"},
{name:"易修 (Issue)",index:"issues"},
{name:"合并请求",index:"pulls"},
{name:"Wiki",index:"wiki"},
{name:"工作流(beta版)",index:"devops"},
{name:"合并请求 (PR)",index:"pulls"},
{name:"维基 (Wiki)",index:"wiki"},
{name:"引擎 (Engine)",index:"devops"},
// {name:"资源库",index:"resources"},
{name:"里程碑",index:"versions"},
{name:"动态",index:"activity"},

View File

@ -117,8 +117,8 @@ export default ((props) => {
</AlignCenterBetween>
<div className="g-desc">{group.description ? group.description : "暂无描述"}</div>
<div className="g-tip">
<p>管理员团队对 <span>所有仓库</span> 具有操作权限且对组织具有 <span>管理员权限</span> </p>
<p>此外该团队拥有了 <span>创建仓库</span> 的权限成员可以在组织中创建新的仓库 </p>
<p>组织的Owner团队拥有更改组织设置在组织内新建仓库新建组织团队等权限</p>
<p>此外Owner团队成员对当前组织下所有项目均具有仓库管理员权限组织下其他团队的权限可由Owner团队成员自由制定</p>
{group.is_admin ? <Button type="primary" onClick={()=>props.history.push(`/${OIdentifier}/teams/${groupId}/setting`)}><span className="color-white">团队设置</span></Button> : ""}
</div>
</div>

View File

@ -27,7 +27,7 @@ class ForkUsers extends Component {
});
const { projectsId , owner } = this.props.match.params;
const url = `/${owner}/${projectsId}/members.json`;
const url = `/${owner}/${projectsId}/forks.json`;
axios
.get(url, {
params: {

View File

@ -30,6 +30,7 @@ class comments extends Component {
reply_id: undefined,
reply_content: undefined,
new_journal_id: undefined,
atWhoLoginList:undefined
};
}
@ -51,6 +52,7 @@ class comments extends Component {
});
return;
}
this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) {
const {
@ -60,10 +62,12 @@ class comments extends Component {
orderId,
reply_id,
is_reply,
atWhoLoginList,
} = this.state;
const url = `/issues/${orderId}/journals.json`;
const url = `/issues/${orderId}/journals.json`;
axios
.post(url, {
...values,
@ -71,6 +75,7 @@ class comments extends Component {
issue_id: orderId,
attachment_ids: fileList,
parent_id: reply_id,
receivers_login:atWhoLoginList,
})
.then((result) => {
if (result && result.data.status === 0) {
@ -248,18 +253,29 @@ class comments extends Component {
onContentChange = (value) => {
if (value) {
this.setState({
content: value,
quillFlag: false,
});
}
this.setState({
content: value,
});
};
replyContentChange = (value) => {
if (value) {
this.setState({
reply_content: value,
quillFlag: false,
});
}
this.setState({
reply_content: value,
});
};
//评论中at谁列表存储login
changeAtWhoLoginList = (loginList) =>{
this.setState({
atWhoLoginList:loginList,
});
};
onRef = (ref) => {
@ -308,6 +324,7 @@ class comments extends Component {
new_journal_id,
} = this.state;
const { current_user, only_show_content } = this.props;
const { projectsId ,owner } = this.props.match.params;
const new_comment = (is_reply, item_id) => {
return (
@ -339,6 +356,10 @@ class comments extends Component {
onChange={
is_reply ? this.replyContentChange : this.onContentChange
}
isCanAtme = {true}
changeAtWhoLoginList = {this.changeAtWhoLoginList}
owner = {owner}
projectsId = {projectsId}
></MDEditor>
<p className="quillFlag">
{quillFlag && <span className="">请输入评论内容</span>}

View File

@ -4,6 +4,13 @@ ul,ol,dl{
.newMain{
background-color: #fff;
}
.newTable{
background-color: #F0F5FF;
.newImages{
background-image: url('../../forge/Images/taskbar.png');
height: 364px;
}
}
.color-black{
color: #333;
}

View File

@ -1,4 +1,4 @@
import React, { Component } from 'react';
import React, { Component, useReducer } from 'react';
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
@ -11,20 +11,29 @@ import { TPMIndexHOC } from '../tpm/TPMIndexHOC';
import { SnackbarHOC,getImageUrl } from 'educoder';
class Shixunauthority extends Component {
constructor(props) {
super(props);
}
render() {
console.log(this.props);
const { current_user } = this.props;
return (
<div className="newMain clearfix">
<div className=" edu-txt-center mt60 mb60">
<div className="newMain newTable clearfix">
<div className="edu-txt-center mt60 mb60">
{/*mt100 mb100*/}
<img src={getImageUrl("images/warn/pic_403.jpg")} />
<p className="font-18 mt40">
您可以稍后尝试&nbsp;<a href="/"
className="color-blue">返回首页</a>
或者&nbsp;
<p className="font-16 mt56 newImages">
<p>您尚未被授权访问此页面请先获取相关权限</p>
您可尝试
{(!current_user || !current_user.login) && (
<p style={{display:"inline"}}>您可尝试<a href="//testforgeplus.trustie.net/login" className="color-blue">登录</a></p>
)}
<a href="/"
className="color-blue">返回首页</a>
<a target="_blank"
href="//shang.qq.com/wpa/qunwpa?idkey=2f2043d88c1bd61d182b98bf1e061c6185e23055bec832c07d8148fe11c5a6cd"
className="color-blue">QQ反馈&gt;&gt;</a>
</p>
href="https://qm.qq.com/cgi-bin/qm/qr?k=YVGUhY7uK8ovpyd7tG_lHe2qGZ63LOij&jump_from=webapi"
className="color-blue">QQ</a>
</p>
</div>
{/*<div style="clear:both;"></div>*/}
{/*<div id="ajax-indicator" style="display:none;"><span>载入中...</span></div>*/}
@ -33,5 +42,4 @@ class Shixunauthority extends Component {
);
}
}
export default SnackbarHOC() (TPMIndexHOC ( Shixunauthority ));

View File

@ -11,19 +11,29 @@ import { TPMIndexHOC } from '../tpm/TPMIndexHOC';
import { SnackbarHOC,getImageUrl } from 'educoder';
class http500 extends Component {
constructor(props) {
super(props);
}
render() {
console.log(this.props);
const { current_user } = this.props;
return (
<div className="newMain clearfix">
<div className=" edu-txt-center mt60 mb60">
<div className="newMain newTable clearfix">
<div className=" edu-txt-center mt60">
{/*mt100 mb100*/}
<img src={getImageUrl("/images/warn/pic_404.jpg")} />
<p className="font-18 mt40">
您可以稍后尝试&nbsp;<a href="/"
<p className="font-16 mt56 newImages">
<p>你访问的页面不存在</p>
您可尝试
{(!current_user || !current_user.login) && (
<p style={{display:"inline"}}>您可尝试<a href="//testforgeplus.trustie.net/login" className="color-blue">登录</a></p>
)}
<a href="/"
className="color-blue">返回首页</a>
或者&nbsp;
也可通过
<a target="_blank"
href="//shang.qq.com/wpa/qunwpa?idkey=2f2043d88c1bd61d182b98bf1e061c6185e23055bec832c07d8148fe11c5a6cd"
className="color-blue">QQ反馈&gt;&gt;</a>
href="https://qm.qq.com/cgi-bin/qm/qr?k=YVGUhY7uK8ovpyd7tG_lHe2qGZ63LOij&jump_from=webapi"
className="color-blue">QQ</a>
</p>
</div>
{/*<div style="clear:both;"></div>*/}

View File

@ -13,17 +13,18 @@ import { SnackbarHOC,getImageUrl } from 'educoder';
class Shixunnopage extends Component {
render() {
return (
<div className="newMain clearfix">
<div className="newMain newTable clearfix">
<div className=" edu-txt-center mt60 mb60">
{/*mt100 mb100*/}
<img src={getImageUrl("images/warn/pic_500.jpg")} />
<p className="font-18 mt40">
您可以稍后尝试&nbsp;<a href="/"
<p className="font-16 mt56 newImages">
<p>服务器异常请稍后重试</p>
您可尝试<a href="javascript:location.reload();" className="color-blue">刷新页面</a>或<a href="/"
className="color-blue">返回首页</a>
或者&nbsp;
也可以通过
<a target="_blank"
href="//shang.qq.com/wpa/qunwpa?idkey=2f2043d88c1bd61d182b98bf1e061c6185e23055bec832c07d8148fe11c5a6cd"
className="color-blue">QQ反馈&gt;&gt;</a>
href="https://qm.qq.com/cgi-bin/qm/qr?k=YVGUhY7uK8ovpyd7tG_lHe2qGZ63LOij&jump_from=webapi"
className="color-blue">QQ</a>
</p>
</div>
{/*<div style="clear:both;"></div>*/}

View File

@ -28,13 +28,14 @@ body>.-task-title {
height:70px;
min-width: 1200px;
z-index: 1000;
background:rgba(255,255,255,1);
box-shadow: 0px 2px 4px 0px rgb(0 0 0 / 50%);
position: fixed;
box-shadow: 0px 0px 14px rgb(0 0 0 / 10%);
background-image: linear-gradient(to right, #0F141F, #0C2A5B,#101417);
color: #fff;
}
.headerContent{
width:1200px;
margin:0px auto;
padding:0px 30px;
display: flex;
align-items: center;
height: 100%;

View File

@ -4,4 +4,39 @@
.Permanentban{
color:#5091FF !important;
border-color: #5091FF !important;
}
/*md编辑器中输入@弹出可选人列表样式*/
.at_who_list{
position: absolute;
z-index: 100;
width: 180px;
max-height: 160px;
background: #FFFFFF;
box-shadow: 0px 4px 8px 2px rgba(212, 212, 212, 0.5);
border-radius: 4px;
overflow-y: scroll;
cursor: pointer;
}
.at_who{
height: 40px;
display: flex;
flex-direction: row;
align-items: center;
border-bottom: 1px solid rgba(212, 212, 212, 0.5);
padding: 0 4px;
}
.at_who.active{
background: #F3F4F6;
}
.at_who img{
width:30px;
height:30px;
border-radius:50%;
margin-right: 10px;
}
.at_who span{
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

View File

@ -1,11 +1,12 @@
import React, { Fragment, useEffect, useRef, useState } from 'react';
import { getUploadActionUrl, getUrl } from 'educoder';
import ResizeObserver from 'resize-observer-polyfill';
import { getImageUrl } from 'educoder';
import axios from 'axios';
import '../../courses/css/Courses.css';
import './css/TPMchallengesnew.css';
import 'codemirror/lib/codemirror.css';
import './css/newquestion.css';
const $ = window.$
const mdIcons = ["bold", "italic", "|", "list-ul", "list-ol", "|", "code", "code-block", "link", "|", "inline-latex", "latex", '|', "image", "table", '|', "line-break", "watch", "clear"];
@ -39,7 +40,6 @@ function md_rec_data(k, mdu, id) {
}
window.md_rec_data = md_rec_data;
function md_elocalStorage(editor, mdu, id) {
let oc = window.sessionStorage.getItem('content' + mdu)
if (oc !== null && oc !== editor.getValue()) {
@ -74,16 +74,38 @@ function md_elocalStorage(editor, mdu, id) {
return tid
}
export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, className = '', noStorage = false, imageExpand = true, placeholder = '', width = '100%', height = 400, initValue = '', emoji, watch, showNullButton = false, showResizeBar = false, startInit = true , forMember = true }) => {
export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, className = '', noStorage = false, imageExpand = true, placeholder = '', width = '100%', height = 400, initValue = '', emoji, watch, showNullButton = false, showResizeBar = false, startInit = true , forMember = true , isCanAtme = false , changeAtWhoLoginList, owner, projectsId }) => {
const editorEl = useRef();
const resizeBarEl = useRef();
const [editorInstance, setEditorInstance] = useState();
const [atWhoVisible, setAtWhoVisible] = useState(false);
const [atWhoLoginListState, setAtWhoLoginListState] = useState([]);
//调用member.json接口获取到的用户列表
const [users, setUsers] = useState([]);
//可以@的全部用户
const [allUsers, setAllUsers] = useState([]);
const atWhoLoginList = useRef([]);
const atWhoVisibleRef = useRef(false);
const containerId = `mdEditor_${mdID}`;
const editorBodyId = `mdEditors_${mdID}`;
const tipId = `e_tips_mdEditor_${mdID}`;
useEffect(()=>{
//请求members接口获取全部可@列表
isCanAtme && axios.get(`/${owner}/${projectsId}/members.json`).then(response=>{
if(response.data.total_count !== 0){
setAllUsers(response.data.users);
setUsers(response.data.users);
}
})
//点击其他地方关闭弹框
document.addEventListener('click',()=>{
atWhoVisibleRef.current = false;
setAtWhoVisible(false);
})
},[])
function onLayout() {
let ro;
if (editorEl.current) {
@ -101,6 +123,96 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
return ro;
}
function selectAtWho(username){
atWhoVisibleRef.current = false;
setAtWhoVisible(false);
const cm = editorInstance.cm;
//获取鼠标所在行的行数和ch
const cursor = cm.doc.getCursor();
const line = cursor.line;//行
const ch = cursor.ch;//列
const startIndex = cm.getRange({line,ch:0},{line,ch}).lastIndexOf("@");
let selectUserLogin = undefined;
users.map((item)=>{
item.username === username && (selectUserLogin = item.login);
})
//替换内容
cm.replaceRange("[@"+username+"]"+`(/${selectUserLogin}) `,{line,ch:startIndex},{line,ch});
//鼠标聚焦
cm.focus();
//将此user的login存储到atWhoLoginList集合中
const list = new Set(atWhoLoginList.current);
list.add(selectUserLogin);
atWhoLoginList.current = Array.from(list);
setAtWhoLoginListState(Array.from(list));
}
function onMouseOver(key){
document.getElementsByClassName("at_who active")[0] && (document.getElementsByClassName("at_who active")[0].className="at_who");
document.getElementsByClassName("at_who")[key] && (document.getElementsByClassName("at_who")[key].className="at_who active");
}
//markdown编辑器中输入的键盘监听事件
function mdKeyDown(e){
if (e.shiftKey && e.code === "Digit2") {
// 输入@键后在对应的位置显示可选的项目成员
atWhoVisibleRef.current = true;
setAtWhoVisible(true);
//获取光标位置
const cssStyle = document.getElementsByClassName("CodeMirror cm-s-default CodeMirror-wrap")[0].firstChild.style;
//设置弹框位置
const newTop = placeholder === "添加评论..." ? 159: placeholder === "请输入合并请求的描述..." ? 172:62;
const newLeft = placeholder === "添加评论..." ? 80: 20;
document.getElementById("at_who_list").style.top = parseInt(cssStyle.getPropertyValue("top").replace("px","")) + newTop +"px";
document.getElementById("at_who_list").style.left = parseInt(cssStyle.getPropertyValue("left").replace("px",""))+newLeft+"px";
}
//处理本来@了某人 -> 删掉 -> 撤回 的情况
if(e.ctrlKey && e.code === "KeyZ" && allUsers.length != 0){
const codemirror = editorInstance.cm;
let value = codemirror.getValue();
//处理初始内容就自带@谁的情况
if(initValue){
const del = [];
allUsers.map(item=>{
if(initValue.indexOf(item.username)!=-1 && initValue.charAt(initValue.indexOf(item.username)-1) === "@" && initValue.indexOf(`@${item.username}`)===value.indexOf(`@${item.username}`)){
//初始内容中有符合@+名字的格式并且当前内容未删除初始内容
del[del.length] = `[@${item.username}](/${item.login})`;
}
})
del.length!=0 && del.map(str=>{
value = value.replace(str,"");
})
}
//判断value是否包含@符号
value.indexOf("@") != -1 && allUsers.map(item =>{
if(value.indexOf(item.username)!=-1 && value.charAt(value.indexOf(item.username)-1) ==="@"){
//将此user的login存储到atWhoLoginList集合中
const list = new Set(atWhoLoginList.current);
list.add(item.login);
atWhoLoginList.current = Array.from(list);
setAtWhoLoginListState(Array.from(list));
}
})
}
}
useEffect(()=>{
changeAtWhoLoginList && changeAtWhoLoginList(atWhoLoginListState);
},[atWhoLoginListState])
const atWhoList = (
<div className="at_who_list" id="at_who_list" >
{users && users.map((item,key)=>{
return(
<div key={key} className={`at_who ${key===0 && `active`}`} onClick={()=>{selectAtWho(item.username)}} onMouseOver={()=>{onMouseOver(key)}}>
{item.image_url && <img src={getImageUrl(`/${item.image_url}`)}></img>}
<span>{item.username}</span>
</div>
)
})}
</div>
)
useEffect(() => {
if (editorInstance) {
return
@ -183,6 +295,69 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
const cmEl = editorInstance && editorInstance.cm
useEffect(()=>{
if(atWhoVisibleRef.current){
// 添加上下键、enter键监听事件
cmEl.addKeyMap({
'Up':()=>{
const atWhoListDiv = document.getElementById("at_who_list");
const atWhoDivs = document.getElementsByClassName("at_who");
let index;
for(let i = 0; i<atWhoDivs.length;i++){
atWhoDivs[i].className === "at_who active" && (index = i);
}
if(index>0){
index <=atWhoDivs.length-4 && (atWhoListDiv.scrollTop -=40)
atWhoDivs[index].className = "at_who";
atWhoDivs[index-1].className = "at_who active";
}
},
'Down':()=>{
const atWhoListDiv = document.getElementById("at_who_list");
const atWhoDivs = document.getElementsByClassName("at_who");
let index;
for(let i = 0; i<atWhoDivs.length;i++){
atWhoDivs[i].className === "at_who active" && (index = i);
}
if(index<atWhoDivs.length-1){
index >=3 && (atWhoListDiv.scrollTop +=40)
atWhoDivs[index].className = "at_who";
atWhoDivs[index+1].className = "at_who active";
}
},
'Enter':()=>{
//找到classname为at_who active的div执行click事件
if(document.getElementsByClassName("at_who active")[0]){
document.getElementsByClassName("at_who active")[0].click()
}else{
const cm = editorInstance.cm;
const cursor = cm.doc.getCursor();
const line = cursor.line;//行
const ch = cursor.ch;//列
//添加换行
cm.replaceRange("\n",{line,ch},{line,ch});
setAtWhoVisible(false);
atWhoVisibleRef.current = false;
}
}
})
} else {
//移除上下、enter键监听
cmEl && cmEl.removeKeyMap();
}
},[atWhoVisible])
useEffect(()=>{
//当users数组发生变化时改变框的位置
if(atWhoVisibleRef.current && users){
//获取光标位置
const cssStyle = document.getElementsByClassName("CodeMirror cm-s-default CodeMirror-wrap")[0].firstChild.style;
//设置弹框位置
const newLeft = placeholder === "添加评论..."? 80: 10;
document.getElementById("at_who_list").style.left = (parseInt(cssStyle.getPropertyValue("left").replace("px",""))+newLeft)+"px";
}
},[users])
useEffect(() => {
if (cmEl) {
let tid = null
@ -198,19 +373,138 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
if (!noStorage) {
tid = md_elocalStorage(editorInstance, `MDEditor__${containerId}`, containerId)
}
if (onChange) {
editorInstance.cm.on('change', (cm) => {
// if(forMember){
// document.onkeydown = (e) => {
// if (e.key === "@") {
// // 输入@键后在对应的位置显示可选的项目成员
// }
// };
// }
onChange(cm.getValue())
})
}
//isCanAtme:只有issue和合并请求以及评论部分可以@他人操作
//绑定@事件
isCanAtme && editorInstance.cm.on("focus", () => {
document.addEventListener("keydown", mdKeyDown);
});
isCanAtme && editorInstance.cm.on("blur", () => {
document.removeEventListener("keydown",mdKeyDown);
});
editorInstance.cm.on("change", (cm) => {
//调用父组件的onchange方法将输入内容传入父级组件
onChange && onChange(cm.getValue());
if(atWhoVisibleRef.current){
//搜索用户(弹框之后用户输入用户名信息)
const cur = cm.doc.getCursor();
const line = cur.line;
const ch = cur.ch;
let rangeCont = cmEl.getRange({line,ch:0},{line,ch});
//处理已经弹出列表框,但用户删除@符号
if(rangeCont.indexOf("@")===-1){
setAtWhoVisible(false);
atWhoVisibleRef.current = false;
}else{
rangeCont = rangeCont.substring(rangeCont.lastIndexOf("@")+1);
rangeCont ? axios.get(`/${owner}/${projectsId}/members.json`,{
params: {
search: rangeCont,
},
}).then(response=>{
if(response && response.data && response.data.total_count !== 0){
setUsers(response.data.users);
}else{
setUsers(undefined);
}
}):setUsers(allUsers)
}
}
//当内容发生改变并且有已@列表时
if(atWhoLoginList.current.length != 0){
const codemirror = editorInstance.cm;
//startValue触发change方法时的内容value处理了初始内容带@用户的情况
let startValue = codemirror.getValue();
let value = codemirror.getValue();
//处理初始内容就自带@谁的情况
if(initValue){
const del = [];
allUsers.map(item=>{
if(initValue.indexOf(item.username)!=-1 && initValue.charAt(initValue.indexOf(item.username)-1) === "@" && initValue.indexOf(`@${item.username}`)===value.indexOf(`@${item.username}`)){
//初始内容中有符合@+名字的格式并且当前内容未删除初始内容
del[del.length] = `[@${item.username}](/${item.login})`;
}
})
del.length!=0 && del.map(str=>{
value = value.replace(str,"");
})
}
//以username为主键login为value的map集合
let atWhoMap = new Map();
Array.from(atWhoLoginList.current).map(item=>{
allUsers.map(i=>{
if(i.login === item){
atWhoMap.set(i.username,i.login);
}
})
});
const cursor = codemirror.doc.getCursor();
const line = cursor.line;
const ch = cursor.ch;
//处理全部内容中不包含“@”的情况
if(value.indexOf("@") === -1){
//markdown嵌套的链接删掉
// Array.from(atWhoMap.keys()).map(username=>{
// startValue = startValue.replaceAll(`[${username}](/${atWhoMap.get(username)}) `,username);
// })
//替换全部内容
// codemirror.setValue(startValue);
//全部内容已经有要@的列表,但是没有@符号 -> 清空@集合
atWhoLoginList.current = [];
setAtWhoLoginListState([]);
}
//截取第一个字符到光标的内容
const curAfterCont = codemirror.getRange({line,ch:0},{line,ch});
const content = codemirror.getLine(line);
//处理光标所在行 有“@”的情况
if(content && content.indexOf("@") !== -1){
Array.from(atWhoMap.keys()).map(username=>{
//判断content是不是以列表中的某个username结尾
const userCont = `[@${username}](/${atWhoMap.get(username)})`;
//删除空格->选中@用户区域
if(curAfterCont.endsWith(userCont)){
codemirror.setSelection({line,ch:curAfterCont.lastIndexOf("@")-1},{line,ch});
}
//处理已经有@列表但是value中不包含完整[@用户名](/login)的情况
if(value.indexOf(userCont)===-1){
// //markdown嵌套的链接删掉,删[]、()的情况不用处理markdown会自动认为不是链接
// //找到[和)的index将区域内容替换成[]包裹的内容
// //光标之后的内容
// const curLeterCont = codemirror.getRange({line,ch},{line,ch:content.length});
// console.log('光标之后的内容curLeterCont',curLeterCont);
// //删除用户名 -> ]在curLeterCont中
// //删除login -> ]在curAfterCont中
// const a = curAfterCont.lastIndexOf('[');
// const b = curLeterCont.indexOf(')')
// const c = curLeterCont.indexOf(']') === -1 ? curAfterCont.lastIndexOf(']') : curLeterCont.indexOf(']')+curAfterCont.length;
// console.log('[',a,')',b,']',c);
// const newCont = codemirror.getRange({line,ch:a+1},{line,ch:c});
// console.log('newCont',newCont);
// codemirror.replaceRange(newCont,{line,ch:a-1},{line,ch:b+curAfterCont.length+1})
//符合情况->踢掉这个人 不给他发消息
const list = new Set(atWhoLoginList.current);
list.delete(atWhoMap.get(username));
atWhoLoginList.current = Array.from(list);
setAtWhoLoginListState(Array.from(list));
}
})
}else{
//处理所在行没有“@”的情况
Array.from(atWhoMap.keys()).map(username=>{
const userCont = `[@${username}](/${atWhoMap.get(username)})`;
if(value.indexOf(userCont)===-1){
//符合情况->踢掉这个人 不给他发消息
const list = new Set(atWhoLoginList.current);
list.delete(atWhoMap.get(username));
atWhoLoginList.current = Array.from(list);
setAtWhoLoginListState(Array.from(list));
}
})
}
}
});
ro = onLayout()
return () => {
if (!noStorage) {
@ -271,7 +565,8 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
return (
<Fragment>
<div ref={editorEl} className={`df ${className} ${imageExpand && 'editormd-image-click-expand'} `} >
{atWhoVisible && atWhoList}
<div ref={editorEl} className={`df ${className} ${imageExpand && 'editormd-image-click-expand'} `}>
<div className={`edu-back-greyf5 radius4 editormd ${error ? 'error' : ''}`} id={containerId} >
<textarea style={{ display: 'none' }} id={editorBodyId} name="content"></textarea>
<div className="CodeMirror cm-s-defualt"></div>