Compare commits

...

170 Commits

Author SHA1 Message Date
caishi 11d3d90def 本地版-非定制不需要头部下拉menu 2022-01-25 18:13:21 +08:00
caishi c3fcf4a1bb 本地版头部menu更新 2021-07-15 15:04:24 +08:00
caishi 97b246d608 质量分析:描述修改 2021-07-15 09:49:05 +08:00
caishi 83a5a5111f 本地版:顶部导航和质量分析 2021-07-14 10:00:03 +08:00
caishi 3b7766bd71 详情:复刻按钮不能连续点击 2021-07-13 10:07:47 +08:00
caishi d1086c2051 新建合并请求:切换目标分支或者源分支时不用调用check_can_merge接口 2021-07-13 09:35:47 +08:00
caishi 1e3ce12d93 页面切换时滚动条没有返回到顶部 2021-07-12 17:00:40 +08:00
caishi 0566d32268 小改 2021-07-12 16:09:06 +08:00
caishi da9e643b47 邀请码位置,页面整体的加载 2021-07-09 17:54:20 +08:00
caishi c8eb745a4e 代码库:增加文件类型submodule 不允许点击 2021-07-09 11:33:30 +08:00
caishi 31a14d7868 本地版:replaceAll无替换时报错 2021-07-08 15:24:56 +08:00
caishi a89485f09a 新建项目跳转地址login 2021-07-07 15:50:48 +08:00
caishi 68a0e8d225 针对本地版本的一些修改 2021-07-07 15:13:05 +08:00
caishi 3ebef52e63 代码库根目录:点击readme文件的编辑按钮时要显示编辑状态 2021-07-07 11:19:35 +08:00
caishi 357039e4eb 甘特图+代码质量分析 2021-07-06 18:10:49 +08:00
caishi 8b1b61031a permission有为空的情况 2021-07-06 14:39:01 +08:00
caishi 3b31b5c47b /默认跳转地址 2021-07-06 13:59:54 +08:00
caishi 7da0c7f6ab 新建:拥有者默认选中当前登录用户,默认路由 2021-07-06 11:55:43 +08:00
caishi d9effff762 数据为0时不显示 2021-07-05 20:38:16 +08:00
caishi f162443756 同上 update 2021-07-05 17:54:04 +08:00
caishi 4979d30a8a 项目列表:banner部分样式版本更新 2021-07-05 17:14:31 +08:00
caishi a8981c48f3 合并请求详情:发布合并请求者跳转链接错误 2021-07-05 15:12:13 +08:00
caishi cdc43a4ac5 工作流-构建列表-头像地址错误 2021-07-02 18:48:18 +08:00
caishi 4f6482597b 镜像地址增加跳转链接 2021-07-02 18:48:07 +08:00
caishi b4c773b58e index.html lang="zh-cN"禁止浏览器翻译 2021-07-01 16:49:39 +08:00
caishi a7384907cb update 2021-07-01 16:49:30 +08:00
caishi 0e1281b3b9 易修批量修改:非项目协作者不能操作 2021-07-01 16:49:19 +08:00
caishi 54cb27343d update issue 2021-07-01 16:49:05 +08:00
caishi a363e62542 merge:fork项目不能修改可见性(公有私有) 2021-07-01 16:48:50 +08:00
caishi d8b2a20568 项目详情:顶部信息排版调整,修改显示bug 2021-07-01 16:47:31 +08:00
caishi 145bbefe57 educoder forge:可以修改密码 2021-06-28 09:40:11 +08:00
caishi bdd2a4c30f 合并请求:冲突文件的显示换行 2021-06-25 18:36:24 +08:00
caishi 3c585cb06d 权限:易修和合并请求的新建修改等操作权限 2021-06-25 18:25:44 +08:00
caishi 29437494f8 路由:tree后面的参数branch内容含有/ 2021-06-25 18:25:28 +08:00
caishi f415151f89 易修:批量删除和批量修改issue状态要更新数量 2021-06-25 18:25:17 +08:00
caishi 12828b0fdf 头部的nav字符串内容中有标签要渲染 2021-06-24 10:42:35 +08:00
caishi 7c9f7857e9 代码库:提交信息的显示和隐藏 2021-06-21 18:35:17 +08:00
caishi 8a1e5328de 代码库:从文件详情切换到文件列表时,上传文件和新建文件的按钮没有显示 2021-06-18 17:45:24 +08:00
caishi 2dbb1b1eb2 上传文件显示: ADD file via upload
创建新文件显示: Add 文件名
修改文件: Update 文件名
2021-06-18 17:45:13 +08:00
caishi 873fccc7b3 合并请求默认选中开启中的 2021-06-18 17:44:55 +08:00
caishi 0057c90995 复刻自,如果是组织要跳转到组织界面 2021-06-17 16:29:02 +08:00
caishi 1d7275fcd7 current fix update 2021-06-17 14:12:48 +08:00
caishi 4fd89c4fc3 个人中心性别icon不显示 2021-06-17 14:00:10 +08:00
caishi 3d5ceb21f2 详情banner数量,显示开启中的数量,且执行关闭或者拒绝等修改状态的操作时要更新数量 2021-06-17 13:59:59 +08:00
caishi 9d387e9a81 项目详情和个人中心的项目列表,增加私有标识 2021-06-17 13:59:47 +08:00
caishi 3c76c3f228 右侧按钮-帮助弹框 2021-06-17 13:59:33 +08:00
caishi 00e5b53e86 forked from 文案 2021-06-17 13:59:19 +08:00
caishi 410a51093b 添加成员--随便输入不存在的用户时的提示 2021-06-17 13:58:56 +08:00
caishi 47a5f84ed4 头部导航栏-搜索小改 2021-06-17 13:58:46 +08:00
caishi 814aa775c7 项目邀请码-第一版测试版上线 2021-06-17 13:58:28 +08:00
caishi 67fcb49342 详情+邀请码的显示 2021-06-17 13:54:20 +08:00
caishi 01d915385c 小修 2021-06-17 13:54:03 +08:00
caishi fee812c63b 添加成员后要清除选中的成员 2021-06-17 13:53:44 +08:00
caishi b948fe9459 issue 2021-06-17 13:53:34 +08:00
caishi f4136f87b4 issue+新建组织-未登录没有弹出登录框 2021-06-17 13:53:23 +08:00
caishi 875b52eb92 修改-个人主页 2021-06-17 13:53:11 +08:00
caishi 94d5040e84 数据统计三个时间筛选参数值必须是时间戳 2021-06-17 13:52:52 +08:00
caishi d450d77c2c 同上+样式 2021-06-17 13:52:34 +08:00
caishi 8c4151fdb7 bug 2021-06-17 13:52:25 +08:00
caishi 9feeaafaa2 修改资料+密码管理 2021-06-17 13:52:02 +08:00
caishi 8a968fbf04 同上=issue修改 2021-06-17 13:51:15 +08:00
caishi 6105df55a0 git cherry-pick 1432afebfd 2021-06-17 11:59:35 +08:00
caishi c2a718ef4d git cherry-pick a9285f37e7 2021-06-17 11:56:13 +08:00
caishi d1bbe72857 git cherry-pick d41dce26b1 2021-06-17 11:54:46 +08:00
caishi 35a03e9f99 同上+样式 2021-06-17 11:54:14 +08:00
caishi 475c48c69d bug 2021-06-17 11:54:05 +08:00
caishi 4da91c11eb 修改资料+密码管理 2021-06-17 11:53:54 +08:00
caishi bca1102b58 git cherry-pick 975d49 2021-06-17 11:53:30 +08:00
caishi 6f8a8e099c 同上-修改细节 2021-06-17 11:52:13 +08:00
caishi 691ace31df 个人中心整体上线 2021-06-17 11:52:02 +08:00
caishi 22cd220aa5 概览页数据绑定完成,只差数据统计页 2021-06-17 11:51:47 +08:00
caishi 37553fcf19 year 2021-06-17 11:51:35 +08:00
caishi 6fa2fd24fb cherry-pick develop 个人中心第一版 2021-06-17 11:50:58 +08:00
caishi cf5ac359ee eudocderforge头像url 2021-06-16 09:47:52 +08:00
caishi 5cc7f51505 邮箱弹框点击事件 2021-06-11 17:21:11 +08:00
caishi 585cf95840 2021-05-28同步develop分支 2021-05-28 16:37:47 +08:00
caishi 05aab13502 上一版+update 2021-05-28 16:29:46 +08:00
caishi dbdd736bf6 上一版-update 2021-05-28 16:29:33 +08:00
caishi 2d5b5217c5 代码库-无默认分支时调用entries接口未传ref参数,+个人主页的三个echart 2021-05-28 16:29:17 +08:00
caishi ed1194a90f markdown定位问题 2021-05-28 16:28:13 +08:00
caishi 18dbf76743 readme下拉 2021-05-28 16:28:03 +08:00
caishi c8dfe9fdff issue 2021-05-28 16:27:53 +08:00
caishi fe9e565e33 git merge a1bd41795a 2021-05-28 16:26:48 +08:00
caishi d3ed2727ef 右侧按钮-帮助列表超出没有隐藏 2021-05-28 16:26:06 +08:00
caishi fccb96ebb5 git merge 70af7dae5f 2021-05-28 16:24:44 +08:00
caishi 712de65d40 隐藏资源库 2021-05-21 19:03:58 +08:00
caishi 325f84ce80 merge d029840874 2021-05-14 11:36:23 +08:00
caishi 85bd0ffb62 merge develop 2021-05-08 17:22:18 +08:00
caishi 0f59544d10 编辑合并请求-url地址错误 2021-05-08 14:35:32 +08:00
caishi 57c8f256fa detail+同步镜像type=2的才不需要合并请求 2021-05-07 17:40:44 +08:00
caishi 0470da643f 导航-字体颜色 2021-05-07 16:33:32 +08:00
caishi 878ff76b00 merge 2021-05-07 16:29:32 +08:00
caishi d6e7666607 统一项目简介、项目概览 2021-05-07 16:27:33 +08:00
caishi 138db0b0cb merge 2021-05-06 17:24:24 +08:00
caishi ba81f51e76 设置所有标签的marginbottom为0 2021-05-06 17:23:15 +08:00
caishi 3ca1ce6c1b merge 2021-05-06 17:23:10 +08:00
caishi d4550d44e9 04-28休假期修改issue --需合并至其它分支(合并请求相关) 2021-04-28 20:24:14 +08:00
caishi 0138eb2f1e 头部不设置默认logo 2021-04-28 13:50:14 +08:00
caishi e1aef30b9d 组织团队-无数据显示错误 2021-04-27 09:46:49 +08:00
caishi 277ba72f91 代码库编辑文件-切换到其它目录后要重新将编辑状态改为显示状态 2021-04-25 17:51:43 +08:00
caishi 6a58a468f9 样式覆盖 2021-04-23 18:04:23 +08:00
caishi 14486724fa +上 关注后不需要提示 2021-04-23 16:14:32 +08:00
caishi 3201b7265b +上 贡献者悬浮框里增加的跳转链接不要新开页 2021-04-23 16:14:22 +08:00
caishi f774ce974d 贡献者-悬浮内容增加跳转链接 2021-04-23 16:14:06 +08:00
caishi fdca717eeb imageUrl + / 2021-04-23 16:13:55 +08:00
caishi 4b304ead34 个人中心关注-关注或者取消关注未更新状态 2021-04-23 16:13:45 +08:00
caishi 67d914ddb6 贡献者-悬浮卡片-测试版1 2021-04-23 16:13:35 +08:00
caishi e6020c3e13 发布评论者头像路径错误 2021-04-23 16:13:18 +08:00
caishi af2feeb34f 新建组织-组织账号正则 2021-04-23 16:12:58 +08:00
caishi 40918bf1d7 merge develop 2021-04-23 16:12:35 +08:00
caishi 8a5a7a0647 md文件要用Markdown渲染 2021-04-20 15:36:40 +08:00
caishi 93ba9c6a98 合并请求-提出申请者头像显示问题 2021-04-20 11:18:24 +08:00
caishi 12322f8785 合并--组织团队设置、团队项目列表的跳转login和显示的name 2021-04-20 11:03:41 +08:00
caishi 8c1bebcfdc 切换左侧目录,选择不同的文件时,文件详情没有更新 2021-04-20 10:31:19 +08:00
caishi 04bdbd7c30 all-默认头像(首字母加背景颜色) 2021-04-20 09:46:19 +08:00
caishi f57ee7fd99 个人主页头像url 2021-04-20 09:46:07 +08:00
caishi 1e0f522f4a 上线后的getImageURL也不用在前面加/ + 一些小样式修改 2021-04-20 09:45:56 +08:00
caishi 798d919447 imageurl接口统一返回带iamges的字段 2021-04-20 09:44:58 +08:00
caishi 1f6a4bda6c readme-文件增加一个目录下拉icon 2021-04-20 09:42:47 +08:00
caishi 0d546f4789 资源库数量显示错误 2021-04-20 09:40:57 +08:00
caishi 44996b5dea 组织团队-新增一个团队标识 2021-04-20 09:40:45 +08:00
caishi 134d79faa3 新建团队板块无边框 2021-04-20 09:40:32 +08:00
caishi 2677efec83 合并冲突 2021-04-20 09:40:13 +08:00
caishi 7482d1184c 资源库不需要按引用次数排序 2021-04-20 09:39:09 +08:00
caishi 58fb71b324 组织-项目-切换协作者管理和团队管理时会重复调用增加成员接口 2021-04-13 17:14:32 +08:00
caishi 5517b28062 去掉右侧悬浮手册按钮 2021-04-09 14:00:57 +08:00
caishi 0bd7f7d900 项目详情右侧增加实践课程链接的显示 2021-04-08 15:38:41 +08:00
caishi 9cfe2c186e entries接口传的branch值为undefined 2021-04-07 18:47:26 +08:00
caishi e4c54622b8 账号同步框提示文字错误 2021-04-07 15:00:09 +08:00
caishi 21902543f4 新增组织账号,组织名称增加新的check条件 2021-04-06 18:40:59 +08:00
caishi e541e91a06 仓库设置页面增加资源库配置项 2021-04-06 17:35:13 +08:00
caishi b6dc01b0be 资源库模块测试版第一次上线 2021-04-06 17:17:40 +08:00
caishi f0858e7ecc 列表的type字段为头像跳转到个人中心或者组织的判断依据 2021-04-06 11:45:37 +08:00
caishi f9f79e0365 资源库部分接口调试 2021-04-06 11:37:17 +08:00
caishi 8903363695 合并请求分页 2021-04-02 18:57:10 +08:00
caishi b9a8becec3 合并资源库 2021-04-02 17:32:20 +08:00
caishi 2fcd2fd066 冲突 2021-04-02 14:10:32 +08:00
caishi 898ad15343 构建列表 2021-04-02 12:01:37 +08:00
caishi 1cf7655e63 tips(易修、复刻) 2021-04-02 10:00:48 +08:00
caishi 2383710c54 通知、消息的地址,trustie改为educoder 2021-04-01 15:46:51 +08:00
caishi 3b3d6dc8b1 工作流author 2021-03-31 17:13:10 +08:00
caishi c74c40b73e 冲突 2021-03-31 14:06:13 +08:00
caishi c5bbcd9c1d color 2021-03-31 11:40:19 +08:00
caishi 74deb640a9 合并 2021-03-31 11:37:05 +08:00
caishi 534da2115a 头部 active 2021-03-30 13:49:38 +08:00
caishi f2ef3183ea toubu 2021-03-30 13:46:52 +08:00
caishi 1182a45cdc Merge branch 'develop' into develop_educoder 2021-03-30 11:51:06 +08:00
caishi 29f37b9760 代码冲突 2021-03-26 10:04:01 +08:00
caishi f5fa45e1ce 新建项目 2021-03-25 22:05:27 +08:00
caishi 97bf4a5a46 readme 2021-03-25 14:41:08 +08:00
caishi 5e4d3a92a1 header 2021-03-24 14:14:00 +08:00
caishi 29f25a585d Merge branch 'develop_new' into develop_educoder
# Conflicts:
#	src/forge/Head/Header.js
#	src/modules/tpm/TPMIndex.css
#	src/modules/tpm/TPMIndexHOC.js
2021-03-24 14:11:12 +08:00
caishi 6bd57d8877 style 2021-03-23 17:57:51 +08:00
caishi e7aa871b53 condition 2021-03-23 17:42:57 +08:00
caishi 9c22457249 delete 2021-03-23 17:00:09 +08:00
caishi 68b1296652 Merge branch 'develop_new' into develop_educoder
# Conflicts:
#	src/modules/tpm/NewHeader.js
2021-03-23 16:13:36 +08:00
caishi 0f7c8a4b5d style 2021-03-22 16:54:41 +08:00
caishi 98289b05aa 未登录不弹框 2021-03-22 14:19:50 +08:00
caishi 83086e7d61 导航栏和底部 2021-03-22 13:51:02 +08:00
caishi 76c88a659a Merge branch 'develop_new' into develop_educoder 2021-03-22 11:24:23 +08:00
caishi ae8ece2695 Merge branch 'develop_new' into develop_educoder 2021-03-22 09:45:46 +08:00
caishi 93994cb785 Merge branch 'develop_new' into develop_educoder 2021-03-19 17:22:14 +08:00
caishi 917958c880 Merge branch 'develop_new' into develop_educoder 2021-03-19 16:31:51 +08:00
caishi 65b906f49f update 2021-03-19 16:31:46 +08:00
caishi 90be5ad793 Merge branch 'develop_new' into develop_educoder 2021-03-19 15:54:10 +08:00
caishi eeaefc5810 隐藏操作手册 2021-03-19 15:48:00 +08:00
caishi 284bf67f82 educoder和forge用户的密码是否一致 2021-03-19 13:44:02 +08:00
caishi fbdde52651 弹框修改接口 2021-03-19 11:52:04 +08:00
caishi 64dcdce51b Merge branch 'develop_new' into develop_educoder 2021-03-19 11:35:20 +08:00
caishi 741a461f7e 邮箱绑定 2021-03-19 09:33:22 +08:00
187 changed files with 6414 additions and 3834 deletions

30
package-lock.json generated
View File

@ -5058,13 +5058,18 @@
}
},
"echarts": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/echarts/-/echarts-4.7.0.tgz",
"integrity": "sha512-NlOTdUcAsIyCCG+N4uh0ZEvXtrPW2jvcuqf03RyqYeCKzyPbiOQ4I3MdKXMhxG3lBdqQNdNXVT71SB4KTQjN0A==",
"version": "4.9.0",
"resolved": "https://registry.nlark.com/echarts/download/echarts-4.9.0.tgz",
"integrity": "sha1-qbm6oD8Doqcx5jQMVb77V6nhNH0=",
"requires": {
"zrender": "4.3.0"
"zrender": "4.3.2"
}
},
"echarts-wordcloud": {
"version": "2.0.0",
"resolved": "https://registry.npm.taobao.org/echarts-wordcloud/download/echarts-wordcloud-2.0.0.tgz?cache=0&sync_timestamp=1610779172014&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fecharts-wordcloud%2Fdownload%2Fecharts-wordcloud-2.0.0.tgz",
"integrity": "sha1-Uu+BeJWAH/6emd0brKt2hrLewEo="
},
"editor.md": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/editor.md/-/editor.md-1.5.0.tgz",
@ -9888,6 +9893,11 @@
"esprima": "^4.0.0"
}
},
"js2wordcloud": {
"version": "1.1.12",
"resolved": "https://registry.npm.taobao.org/js2wordcloud/download/js2wordcloud-1.1.12.tgz",
"integrity": "sha1-8BdC2k5qyzAAsoY1dmQWjvn8o64="
},
"jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
@ -14024,9 +14034,9 @@
"integrity": "sha1-ys6GOG9ZoNuAUPqQ2baw6IoeNk8="
},
"qrcode.react": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-1.0.0.tgz",
"integrity": "sha512-jBXleohRTwvGBe1ngV+62QvEZ/9IZqQivdwzo9pJM4LQMoCM2VnvNBnKdjvGnKyDZ/l0nCDgsPod19RzlPvm/Q==",
"version": "1.0.1",
"resolved": "https://registry.npm.taobao.org/qrcode.react/download/qrcode.react-1.0.1.tgz",
"integrity": "sha1-KDS7UOXidf/lr2kG7/FTkf6eOKU=",
"requires": {
"loose-envify": "^1.4.0",
"prop-types": "^15.6.0",
@ -20517,9 +20527,9 @@
}
},
"zrender": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/zrender/-/zrender-4.3.0.tgz",
"integrity": "sha512-Dii6j2bDsPkxQayuVf2DXJeruIB/mKVxxcGRZQ9GExiBd4c3w7+oBuvo1O/JGHeFeA1nCmSDVDs/S7yKZG1nrA=="
"version": "4.3.2",
"resolved": "https://registry.npm.taobao.org/zrender/download/zrender-4.3.2.tgz",
"integrity": "sha1-7HQy+UFcgsc1hLa3uMR+GwFiCcY="
}
}
}

View File

@ -29,7 +29,8 @@
"dompurify": "^2.0.15",
"dotenv": "4.0.0",
"dotenv-expand": "4.2.0",
"echarts": "^4.7.0",
"echarts": "^4.9.0",
"echarts-wordcloud": "^2.0.0",
"editor.md": "^1.5.0",
"eslint": "4.10.0",
"eslint-config-react-app": "^2.1.0",
@ -46,6 +47,7 @@
"install": "^0.12.2",
"jest": "20.0.4",
"js-base64": "^2.5.2",
"js2wordcloud": "^1.1.12",
"katex": "^0.11.1",
"lodash": "^4.17.15",
"loglevel": "^1.6.8",
@ -62,7 +64,7 @@
"postcss-loader": "2.0.8",
"promise": "8.0.1",
"prop-types": "^15.6.1",
"qrcode.react": "^1.0.0",
"qrcode.react": "^1.0.1",
"qs": "^6.9.3",
"quill": "^1.3.7",
"quill-delta-to-html": "^0.11.0",

File diff suppressed because one or more lines are too long

View File

@ -38,78 +38,6 @@
box-sizing: border-box;
}
.head-nav ul#header-nav li {
float: left;
height: 60px;
line-height: 60px;
margin-right: 30px;
cursor: pointer;
position: relative;
font-size: 16px
}
.head-nav ul#header-nav li a {
display: block;
height: 100%;
width: 100%;
color: #fff
}
.head-nav ul#header-nav li a:hover {
color: #cccccc;
}
.head-nav ul#header-nav li:last-child {
margin-right: 0px
}
.head-nav ul#header-nav li.active a {
color: #459be5 !important;
}
.head-nav ul#header-nav li.active p {
color: #459be5 !important;
}
.head-nav ul#header-nav li p:hover {
color: #cccccc;
}
.head-nav ul#header-nav li p {
display: block;
height: 100%;
width: 100%;
color: #fff
}
.head-nav ul#header-nav li.active div ul li a {
color: #000 !important;
}
.head-nav ul#header-nav li.active div ul li a:hover {
color: #FFF !important;
}
.head-nav ul#header-nav li.active ul li a {
color: #000 !important;
}
.head-nav ul#header-nav li.active ul li a:hover {
color: #FFF !important;
}
.head-nav ul#header-nav li.active:after {
content: '';
position: absolute;
left: 0px;
top: auto;
bottom: 10px;
right: auto;
height: 2px;
width: 14px;
background-color: #459be5;
}
.nav-img {
position: absolute;
top: 2px;

View File

@ -25,9 +25,6 @@ html {
min-width: 1200px
}
.newFooter {
max-height: 110px;
}
.newFooter {
position: absolute;

View File

@ -1307,6 +1307,7 @@ td,
span {
margin: 0;
padding: 0;
margin-bottom: 0px!important;
}
table,
@ -2346,7 +2347,6 @@ input::-ms-clear {
/*中间部分宽度固定为1200*/
.newMain {
margin: 0 auto;
padding-bottom: 110px;
min-width: 1200px;
}
@ -3410,7 +3410,7 @@ a.user_bluebg_btn {
}
.cdefault {
cursor: default
cursor: default!important;
}
@ -3585,43 +3585,6 @@ a.user_bluebg_btn {
margin-right: 5px;
}
/*-------------------个人主页:右侧提示区域--------------------------*/
.-task-sidebar {
position: fixed;
width: 40px;
height: 180px;
right: 0;
bottom: 80px;
z-index: 10;
}
.-task-sidebar div {
height: 40px;
line-height: 40px;
box-sizing: border-box;
width: 40px;
background: #4CACFF;
color: #fff;
font-size: 20px;
text-align: center;
margin-bottom: 5px;
border-radius: 4px;
}
.-task-sidebar div i {
color: #fff;
}
.-task-sidebar div i:hover {
color: #fff !important;
}
.gotop {
background-color: rgba(208, 207, 207, 0.5) !important;
padding: 0px !important;
}
/***** loading ******/
/*****载入中******/
#ajax-indicator {
@ -3945,11 +3908,21 @@ html>body #ajax-indicator {
max-height: 340px;
}/*头部导航条样式---2018-03-19--by-cs*/
.privateTag{
display: block;
padding:0px 6px;
border-radius: 12px;
border:1px solid #2FC25B;
height: 18px;
line-height: 18px;
font-size: 12px;
margin-left: 10px;
color: #2FC25B;
}
.head-nav {
text-align: center;
height: 70px;
box-sizing: border-box;
min-width: 780px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@ -3971,14 +3944,14 @@ html>body #ajax-indicator {
cursor: pointer;
position: relative;
font-size: 16px;
padding:0px 20px;
padding-right:40px;
}
.head-nav ul#header-nav li a {
display: block;
height: 100%;
width: 100%;
color: #333;
color: #fff;
font-size: 16px;
}
@ -4108,21 +4081,6 @@ em.vertical-line {
/* 右侧内容宽度变化的话需要调整posi-search right的值*/
/*底部*/
.newFooter {
max-height: 110px;
}
.newFooter {
position: absolute;
bottom: 0;
width: 100%;
background: #323232;
clear: both;
min-width: 1200px;
z-index: 8;
left: 0px;
}
.footercon {
border-bottom: 1px solid #47494d;
@ -6718,4 +6676,13 @@ ul.count_ul li:not(:last-child):after {
}
input.ant-input-lg::placeholder{
font-size: 14px !important;
}
p{
margin-bottom: 0px!important;
}
.toprightNum{
position: absolute;
right: 0px;
top:4px;
color: #999;
}

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,139 @@
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "17575494",
"name": "file-submodule",
"font_class": "file-submodule",
"unicode": "e866",
"unicode_decimal": 59494
},
{
"icon_id": "7539612",
"name": "nv",
"font_class": "nv1",
"unicode": "e864",
"unicode_decimal": 59492
},
{
"icon_id": "7539613",
"name": "nan",
"font_class": "nan1",
"unicode": "e865",
"unicode_decimal": 59493
},
{
"icon_id": "21936935",
"name": "邮箱",
"font_class": "youxiang",
"unicode": "e8b2",
"unicode_decimal": 59570
},
{
"icon_id": "21936924",
"name": "单位",
"font_class": "danwei",
"unicode": "e8a7",
"unicode_decimal": 59559
},
{
"icon_id": "21936925",
"name": "待办事项",
"font_class": "daibanshixiang",
"unicode": "e8a8",
"unicode_decimal": 59560
},
{
"icon_id": "21936928",
"name": "概览",
"font_class": "gailan",
"unicode": "e8ab",
"unicode_decimal": 59563
},
{
"icon_id": "21936929",
"name": "男",
"font_class": "nan",
"unicode": "e8ac",
"unicode_decimal": 59564
},
{
"icon_id": "21936930",
"name": "女",
"font_class": "nv",
"unicode": "e8ad",
"unicode_decimal": 59565
},
{
"icon_id": "21936931",
"name": "工作流",
"font_class": "gongzuoliu1",
"unicode": "e8ae",
"unicode_decimal": 59566
},
{
"icon_id": "21936934",
"name": "数据统计",
"font_class": "shujutongji",
"unicode": "e8b1",
"unicode_decimal": 59569
},
{
"icon_id": "21936936",
"name": "项目",
"font_class": "xiangmu",
"unicode": "e8b3",
"unicode_decimal": 59571
},
{
"icon_id": "21936937",
"name": "组织",
"font_class": "zuzhi",
"unicode": "e8b4",
"unicode_decimal": 59572
},
{
"icon_id": "14835599",
"name": "右箭头",
"font_class": "arrowRight",
"unicode": "e863",
"unicode_decimal": 59491
},
{
"icon_id": "21151489",
"name": "箭头镂空-左",
"font_class": "jiantouloukong-zuo",
"unicode": "e861",
"unicode_decimal": 59489
},
{
"icon_id": "21151557",
"name": "箭头镂空-右",
"font_class": "jiantouloukong-you",
"unicode": "e862",
"unicode_decimal": 59490
},
{
"icon_id": "21568989",
"name": "分享",
"font_class": "fenxiang1",
"unicode": "e89c",
"unicode_decimal": 59548
},
{
"icon_id": "21568990",
"name": "回到顶部",
"font_class": "huidaodingbu1",
"unicode": "e89d",
"unicode_decimal": 59549
},
{
"icon_id": "21568993",
"name": "帮助",
"font_class": "bangzhu",
"unicode": "e8a0",
"unicode_decimal": 59552
},
{
"icon_id": "991344",
"name": "提交",
@ -4236,7 +4369,7 @@
{
"icon_id": "1004630",
"name": "点赞2",
"font_class": "dianzan1",
"font_class": "dianzaned",
"unicode": "e639",
"unicode_decimal": 58937
},

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 733 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html lang="zh-CN" class="notranslate translated-ltr" translate="no">
<head>
<meta charset="utf-8">
<meta name=”Keywords” Content=”trustie,trustieforge,forge,确实让创建更美好,协同开发平台″>

View File

@ -17,8 +17,7 @@ import marked from './common/marked';
import moment from 'moment'
import { MuiThemeProvider, createMuiTheme } from 'material-ui/styles';
import history from './history';
import SiderBar from './forge/Component/SiderBar'
import { SnackbarHOC } from 'educoder'
import { initAxiosInterceptors } from './AppConfig'
@ -75,6 +74,10 @@ const EducoderLogin = Loadable({
loading: Loading,
})
const ProjectIndex = Loadable({
loader: () => import("./forge/Main/Index"),
loading: Loading,
});
class App extends Component {
constructor(props) {
super(props);
@ -86,7 +89,7 @@ class App extends Component {
mygetHelmetapi: null,
}
}
HideAddcoursestypess = (i) => {
this.setState({
Addcoursestype: false,
@ -100,39 +103,11 @@ class App extends Component {
Addcoursestypes: false
})
};
ModalCancelsy = () => {
this.setState({
mydisplay: false,
})
window.location.href = "/";
};
ModalshowCancelsy = () => {
this.setState({
mydisplay: true,
})
};
disableVideoContextMenu = () => {
window.$("body").on("mousedown", "video", function (event) {
if (event.which === 3) {
window.$('video').bind('contextmenu', function () { return false; });
} else {
window.$('video').unbind('contextmenu');
}
});
}
componentDidMount() {
document.title = "loading...";
this.disableVideoContextMenu();
history.listen(() => {
this.forceUpdate()
const $ = window.$
$("html").animate({ scrollTop: $('html').scrollTop() - 0 })
});
initAxiosInterceptors(this.props);
this.getAppdata();
window.addEventListener('error', (event) => {
const msg = `${event.type}: ${event.message}`;
});
@ -207,11 +182,13 @@ class App extends Component {
};
render() {
const { mygetHelmetapi } = this.state;
return (
<Provider store={store}>
<ConfigProvider locale={zhCN}>
<MuiThemeProvider theme={theme}>
<LoginDialog {...this.props} {...this.state} Modifyloginvalue={() => this.Modifyloginvalue()}></LoginDialog>
<SiderBar/>
<Router>
<Switch>
{/*项目*/}
@ -224,14 +201,6 @@ class App extends Component {
}>
</Route>
{/*项目*/}
<Route
path={"/projects"}
render={
(props) => {
return (<Projects {...this.props} {...props} {...this.state} />)
}
}>
</Route>
<Route
path="/register"
render={
@ -260,10 +229,18 @@ class App extends Component {
return (<InfosIndex {...this.props} {...this.state} />)
}
}></Route>
<Route
path={"/projects"}
render={
(props) => {
return (<Projects {...this.props} {...props} {...this.state} />)
}
}>
</Route>
<Route exact path="/"
render={
(props) => (
<Projects {...this.props} {...props} {...this.state}></Projects>
<Projects {...this.props} {...props} {...this.state} />
)
}
/>

View File

@ -2,7 +2,7 @@ import axios from 'axios';
import { requestProxy } from "./indexEduplus2RequestProxy";
import { broadcastChannelOnmessage, isDev, queryString } from 'educoder';
import { notification } from 'antd';
import cookie from 'react-cookies';
import './index.css';
let message501 = false;
@ -11,10 +11,8 @@ broadcastChannelOnmessage('refreshPage', () => {
})
function locationurl(list) {
if (window.location.port === "3007") {
} else {
window.location.href = list
if (window.location.port !== "3007") {
window.location.href = list
}
}
// TODO 开发期多个身份切换
@ -26,78 +24,41 @@ if (isDev) {
parsed = queryString.parse(_search);
}
debugType = window.location.search.indexOf('debug=t') !== -1 ? 'teacher' :
window.location.search.indexOf('debug=s') !== -1 ? 'student' :
window.location.search.indexOf('debug=a') !== -1 ? 'admin' : parsed.debug || 'admin'
window.location.search.indexOf('debug=s') !== -1 ? 'student' :
window.location.search.indexOf('debug=a') !== -1 ? 'admin' : parsed.debug || 'admin'
}
function clearAllCookie() {
cookie.remove('_educoder_session', { path: '/' });
cookie.remove('autologin_trustie', { path: '/' });
setpostcookie()
}
clearAllCookie();
function setpostcookie() {
const str = window.location.pathname;
if (str.indexOf("/wxcode") !== -1) {
cookie.remove('_educoder_session', { path: '/' });
cookie.remove('autologin_trustie', { path: '/' });
const _params = window.location.search;
if (_params) {
let _search = _params.split('?')[1];
let _educoder_sessions = _search.split('&')[0].split('=');
cookie.save('_educoder_session', _educoder_sessions[1], { domain: '.educoder.net', path: '/' });
let autologin_trusties = _search.split('&')[1].split('=');
cookie.save('autologin_trustie', autologin_trusties[1], { domain: '.educoder.net', path: '/' });
}
}
}
setpostcookie();
window._debugType = debugType;
export function initAxiosInterceptors(props) {
// 判断网络是否连接
initOnlineOfflineListener();
var proxy = "http://localhost:3000";
proxy = "https://testforgeplus.trustie.net";
const requestMap = {};
window.setfalseInRequestMap = function (keyName) {
requestMap[keyName] = false;
}
var proxy = "http://106.75.178.228:81";
//响应前的设置
axios.interceptors.request.use(
config => {
setpostcookie()
clearAllCookie()
if (config.url.indexOf(proxy) !== -1) {
if(config.url.indexOf("http") !== -1) {
return config
}
requestProxy(config)
requestProxy(config);
let url = `/api${config.url}`;
if (`${config[0]}` !== `true`) {
if (window.location.port === "3007") {
config.url = `${proxy}${url}`;
if (config.url.indexOf('?') === -1) {
config.url = `${config.url}?debug=${debugType}`;
} else {
config.url = `${config.url}&debug=${debugType}`;
}
// if (config.url.indexOf('?') === -1) {
// config.url = `${config.url}?debug=${debugType}`;
// } else {
// config.url = `${config.url}&debug=${debugType}`;
// }
} else {
config.url = url;
}
setpostcookie();
}
if (config.url.indexOf('update_file') === -1) {
requestMap[config.url] = true;
window.setTimeout("setfalseInRequestMap('" + config.url + "')", 900)
}
return config;
},
err => {
return Promise.reject(err);
});
});
axios.interceptors.response.use(function (response) {
if (response === undefined) {
@ -146,8 +107,6 @@ export function initAxiosInterceptors(props) {
message501 = false
}, 2000);
}
requestMap[response.config.url] = false;
setpostcookie();
return response;
}, function (error) {
return Promise.reject(error);

View File

@ -9,20 +9,7 @@ class Loading extends Component {
}
render() {
return (
<div className="App" style={{ minHeight: '800px', width: "100%" }}>
<style>
{
`
.margintop{
margin-top:20%;
}
`
}
</style>
<Spin size="large" className={"margintop"} />
</div>
);
return ""
}
}

View File

@ -8,14 +8,17 @@ const isDev = window.location.port == 3007;
const isdev2= window.location.hostname ==='www.educoder.net'
export const TEST_HOST = "https://testforgeplus.trustie.net/"
export function getImageUrl(path) {
// https://www.educoder.net
// https://testbdweb.trustie.net
// const local = 'http://localhost:3000'
const local = 'https://testforgeplus.trustie.net';
if (isDev) {
return `${local}/${path}`
const local = 'https://testforgeplus.educoder.net';
const normal = 'https://ali-cdn.educoder.net/images';
if(path.substr(0, 7) !== "/system"){
if (isDev) {
return `${local}/${path}`
}
if(path.substr(0, 7) !== "/images"){
return `${normal}${path}`;
}
}
return `/${path}`;
return `${path}`;
}
export function getImage(path) {
@ -162,7 +165,7 @@ export function getmyUrl(geturl) {
}
export function getUploadActionUrl(path, goTest) {
return `${getUrl()}/api/attachments.json?debug=${window._debugType || 'admin'}`;
return `${getUrl()}/api/attachments.json`;
}
export function getUploadLogoActionUrl() {

View File

@ -64,7 +64,7 @@ function CommentItem({
const commentAvatar = (author) => (
<img
className="item-flex flex-image"
src={author.image_url ? getImageUrl(`images/${author.image_url}`) : 'https://b-ssl.duitang.com/uploads/item/201511/13/20151113110434_kyReJ.jpeg'}
src={author.image_url ? getImageUrl(`/${author.image_url}`) : 'https://b-ssl.duitang.com/uploads/item/201511/13/20151113110434_kyReJ.jpeg'}
alt=""
/>
);

View File

@ -46,18 +46,18 @@ export default ({
let id = decodeURIComponent(u.split("#")[1]);
let ele = document.getElementById(id);
if(ele){
window.scrollTo(0, ele.offsetTop + 220);
window.scrollTo(0, ele.offsetTop + 120);
}
}
}
},[url])
},[url,html])
const el = useRef();
function onAncherHandler(e) {
let target = e.target
let target = e.target;
if (target.tagName.toUpperCase() === 'A') {
let ancher = target.getAttribute('href')
if (ancher.startsWith('#')) {
let ancher = target.getAttribute('href');
if (ancher && ancher.startsWith('#')) {
e.preventDefault()
let viewEl = document.getElementById(ancher.replace('#', ''))
if (viewEl) {

View File

@ -69,7 +69,7 @@ function Index(props){
<div className="aboutPanels">
<div className="aboutContent">
<AlignCenterBetween style={{padding:"14px 20px"}}>
<span className="font-16"><i className="iconfont icon-xiangmujianjie mr5 font-16 color-blue"></i>项目简介</span>
<span className="font-16"><i className="iconfont icon-xiangmujianjie mr5 font-16 color-blue"></i>项目概览</span>
{ editOpration && !edit && <a onClick={editContent} className="color-blue">编辑</a> }
</AlignCenterBetween>
{
@ -117,7 +117,7 @@ function Index(props){
{content ?
<RenderHtml className="break_word_comments imageLayerParent" value={content} url={props.history.location}/>
:
<div>暂无简介~</div>
<div>暂无概览~</div>
}
{attachments && attachments.length > 0 &&
<Attachments

View File

@ -33,7 +33,7 @@ class ActivityItem extends Component {
}
<p className="itemLine mt10">
<Link to={`/users/${item && item.user_login}`} className="show-user-link">
<img alt="" src={getImageUrl(`images/${item.user_avatar}`)} className="createImage" />
<img alt="" src={getImageUrl(`/${item.user_avatar}`)} className="createImage" />
<span className="mr20">{item.user_name}</span>
</Link>
{item.created_at && <span className="color-grey-9">创建于<span className="ml2 color-grey-6">{item.created_at}</span></span>}

View File

@ -4,7 +4,7 @@ import axios from 'axios';
import { getImageUrl } from 'educoder';
const { Option } = AutoComplete;
function AddMember({getID,login}){
function AddMember({getID,login,showNotification}){
const [ id , setID ] = useState(undefined);
const [ source , setSource ] = useState(undefined);
const [ searchKey , setSearchKey ] = useState(undefined);
@ -42,10 +42,10 @@ function AddMember({getID,login}){
className="user_img radius"
width="28"
height="28"
src={getImageUrl(`images/${item && item.image_url}`)}
src={getImageUrl(`/${item && item.image_url}`)}
alt=""
/>
<span className="ml10" style={{ "vertical-align": "middle" }}>
<span className="ml10" style={{ verticalAlign: "middle" }}>
{item.username}
<span className="color-grey ml10">({item.login})</span>
</span>
@ -66,7 +66,12 @@ function AddMember({getID,login}){
};
function addCollaborator(){
getID && getID(id);
if(source && source.length>0){
getID && getID(id);
setSearchKey(undefined);
}else{
showNotification("请选择存在的用户!");
}
}
return(

View File

@ -6,7 +6,7 @@ import './Component.scss';
function Cards({img , title, desc , rightBtn , src}){
return(
<div className="cards">
{img &&<div className="img"><img src={getImageUrl(`images/${img}`)} alt=""/></div>}
{img &&<div className="img"><img src={getImageUrl(`/${img}`)} alt=""/></div>}
<div className="content">
<p className="titles">
<Link to={src}>{title}</Link>

View File

@ -112,6 +112,9 @@ li.ant-menu-item{
right:240px;
z-index: 10000;
}
.laterest{
color: #05690d;
}
@media screen and (max-width: 1800px){
.handleBox{
@ -151,4 +154,210 @@ li.ant-menu-item{
.ant-tree{
margin:0px 20px!important;
}
}
}
.menuPanels{
width: 240px;
height: 180px;
.ant-popover-content,.ant-popover-inner{
height: 100%;
width: 100%;
}
}
.halfs{
margin-top: 24px;
padding:24px 0px 0px 0px;
border-top: 1px solid #e8e8e8;
.attrPerson{
padding-bottom: 24px;
}
}
.menuinfos{
padding:15px 0px;
&>a{
display: flex;
flex-direction: column;
align-items: center;
border-right: 1px solid #eee;
flex: 1;
& >span:first-child{
font-size: 18px;
font-weight: 400;
color: #333;
}
& >span:last-child{
color: #666;
}
&:last-child{
border-right: none;
}
}
}
/*-------------------个人主页:右侧提示区域--------------------------*/
.-task-sidebar {
position: fixed;
width: 40px;
right: 0;
bottom: 80px;
z-index: 10;
}
@media screen and (max-width: 1920px){
.-task-sidebar{
right:220px;
}
}
@media screen and (max-width: 1750px){
.-task-sidebar{
right:160px;
}
}
@media screen and (max-width: 1650px){
.-task-sidebar{
right:115px;
}
}
@media screen and (max-width: 1550px){
.-task-sidebar{
right:90px;
}
}
@media screen and (max-width: 1450px){
.-task-sidebar{
right:45px;
}
}
@media screen and (max-width: 1200px){
.-task-sidebar{
right:0px;
display: none;
}
}
.-task-sidebar>div {
height: 40px;
line-height: 40px;
box-sizing: border-box;
width: 40px;
color: #999;
font-size: 20px;
text-align: center;
margin-bottom: 20px;
border-radius: 50%;
background: #FFFFFF;
box-shadow: 0px 0px 10px 1px #F1F1F1;
}
.-task-sidebar>div i {
color: #999;
}
.-task-sidebar>div:hover i {
color: #fff !important;
}
.-task-sidebar>div:hover{
background: #1890FF;
box-shadow: 0px 0px 10px 2px #B6D0FC;
}
.helpBox{
width: 260px;
z-index: 103;
&.shareContent{
width: 200px;
}
.ant-popover-inner-content{
padding:0px;
}
p.titlecontent{
font-size: 18px;
color: #333;
line-height: 20px;
padding:15px 20px;
}
.faqUl{
padding:0px 20px 10px;
max-height: 230px;
overflow-y: auto;
li{
background: #F5F5F5;
border-radius: 20px;
padding:0px 20px;
color: #333;
height: 34px;
line-height: 34px;
margin-bottom: 10px;
a{
display: block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
&:hover{
background-color: #D1E9FF;
a{
color: #333!important;
}
}
}
}
.shareUl{
padding:10px 0px;
display: flex;
align-items: center;
.titlecontent{
margin-right: 20px;
}
li > i{
font-size: 32px!important;
}
}
}
.-task-desc {
background: #494949;
width: 90px;
line-height: 36px;
text-align: center;
position: absolute;
color: #fff;
font-size: 13px;
z-index: 999999;
opacity: 0;
}
.-task-desc div {
position: absolute;
top: 10px;
right: -7px;
height: 13px;
}
.-task-desc div img {
float: left
}
.-task-sidebar .scan_ewm {
position: absolute !important;
right: 45px !important;
bottom: 0px !important;
background-color: #494949 !important;
-webkit-box-sizing: border-box !important;
box-sizing: border-box !important;
font-size: 14px !important;
line-height: 16px !important;
display: none;
height: 213px !important;
}
.trangle_right {
position: absolute;
right: -5px;
bottom: 15px;
width: 0;
height: 0px;
border-top: 6px solid transparent;
border-left: 5px solid #494949;
border-bottom: 6px solid transparent
}

View File

@ -1,21 +1,154 @@
import React from 'react';
import React, { useEffect, useState } from 'react';
import { AlignCenter , FlexAJ } from '../Component/layout';
import { Link } from 'react-router-dom';
import { Popover , Spin } from 'antd';
import { getImageUrl } from 'educoder';
import './Component.scss';
import { getUser } from '../GetData/getData';
import axios from 'axios';
function Contributors({contributors,owner,projectsId}){
const [ menuList ,setMenuList ]= useState([]);
const [ list , setList ]= useState(undefined);
const [ total , setTotal ]= useState(0);
const [ menu , setMenu ] = useState("");
const [ login , setLogin ] = useState(undefined);
const [ isSpin , setIsSpin ] = useState(false);
useEffect(()=>{
if(contributors && contributors.total_count>0){
setTotal(contributors.total_count);
setList(contributors.list);
}
},[contributors])
useEffect(()=>{
if(login){
getUsers(login);
}else{
setMenu(undefined);
}
},[login])
async function getUsers(login){
setIsSpin(true);
let a = menuList && menuList.filter(i=>i.login === login);
if(a.length === 0){
let result = await getUser(login);
let arr = menuList;
arr.push({...result});
setMenuList(arr);
setMenusFunc(result);
setIsSpin(false);
}else{
setMenusFunc(a[0]);
setIsSpin(false);
}
}
function setMenusFunc(data){
if(data){
let ele = (
<Spin spinning={isSpin}>
<FlexAJ>
<AlignCenter>
<Link to={`/users/${data.login}`}><img src={getImageUrl(`/${data.image_url}`)} alt="" className="radius" width="38px" height="38px"/></Link>
<Link to={`/users/${data.login}`} className="ml10">{data.name}</Link>
</AlignCenter>
{
data.is_watch ? <a className="color-grey-9" onClick={()=>FocusFunc(false,data.login)}>取消关注</a>:<a className="color-blue" onClick={()=>FocusFunc(true,data.login)}>关注</a>
}
</FlexAJ>
<AlignCenter className="menuinfos">
<a href={data.projects_url}>
<span>{data.projects_count}</span>
<span>项目数</span>
</a>
<a href={data.followers_url}>
<span>{data.followers_count}</span>
<span>粉丝数</span>
</a>
<a href={data.following_url}>
<span>{data.following_count}</span>
<span>关注数</span>
</a>
</AlignCenter>
{
data.organizations && data.organizations.length > 0 ?
<AlignCenter className="font-12 pt4 pb4">
<span>所属组织</span>
<div className="task-hide flex1">
{renderArray(data.organizations)}
</div>
</AlignCenter>
:""
}
{
data.location && <AlignCenter className="font-12 pt4 pb4"><span>所在地址:</span><span className="ml5">{data.location}</span></AlignCenter>
}
</Spin>
)
setMenu(ele);
}
}
function FocusFunc(flag,login){
axios({
method: flag ? 'post' : 'delete',
url: `/watchers/${flag ? 'follow' : 'unfollow'}.json`,
params: {target_type: "user",id:login}
}).then(result => {
if (result && (result.data.status === 0 || result.data.status === 2)) {
let a = menuList && menuList.filter(i=>i.login === login);
if(a){
a[0].is_watch = flag;
}
setMenusFunc(a[0]);
}
})
.catch(error => {
console.log(error);
});
}
function renderArray(array){
let str = "";
for(var i = 0;i<array.length;i++){
str += array[i].name +"、";
}
let substr = str.substr(0,str.length-1);
return (<span title={substr}>{substr}</span>)
}
function setVisibleFunc(flag,l,index){
if(l !== login){
setLogin(l);
}
var lx = list.concat();
lx.map(i=>i.visible =false);
if(flag){
lx[index].visible = flag;
}
lx.splice();
setList(lx);
}
return(
<div>
<div className="halfs">
<FlexAJ>
<AlignCenter><span className="font-16 color-grey-6">贡献者</span>{ contributors && contributors.total_count > 0 && <span className="infoCount">{contributors.total_count}</span>}</AlignCenter>
<Link className="font-12 color-grey-9" to={`/projects/${owner}/${projectsId}/contribute`}>全部</Link>
</FlexAJ>
<div className="attrPerson">
<div className="attrPerson" onMouseLeave={()=>setVisibleFunc(false)}>
{
contributors && contributors.total_count > 0 ?
contributors.list.map((item,key)=>{
total > 0 ?
list.map((item,key)=>{
return(
<Link key={key} to={`/users/${item.login}`}><img src={getImageUrl(`images/${item.image_url}`)} alt=""/></Link>
<Popover content={menu} visible={item.visible} overlayClassName="menuPanels" placement="top">
<Link key={key} to={`/users/${item.login}`}>
<img src={getImageUrl(`/${item.image_url}`)} alt="" onMouseOver={()=>setVisibleFunc(true,item.login,key)}/>
</Link>
</Popover>
)
})
:""

View File

@ -4,6 +4,12 @@ import './Component.scss';
import axios from 'axios';
const { TreeNode , DirectoryTree } = Tree;
function turnbar(str){
if(str && str.length>0 && str.indexOf("/")>-1){
return str.replaceAll('/','%2F');
}
return str;
}
function DrawerPanel({visible,onClose,branch,owner,projectsId,history, name , list}){
const [ treeData , setTreeData ] = useState(undefined);
const [ isSpin , setIsSpin ] = useState(true);
@ -71,7 +77,8 @@ function DrawerPanel({visible,onClose,branch,owner,projectsId,history, name , li
let dataref = event.node.props.dataRef;
if(dataref.type==="file"){
onClose();
history.push(`/projects/${owner}/${projectsId}/tree/${branch}/${dataref.path}`);
let value = turnbar(branch);
history.push(`/projects/${owner}/${projectsId}/tree/${value}/${dataref.path}`);
}
}

View File

@ -0,0 +1,9 @@
.ant-modal-mask{
z-index: 10000;
}
.ant-modal-wrap{
z-index: 10001;
.ant-form-explain{
position: absolute;
}
}

View File

@ -0,0 +1,65 @@
import React , {forwardRef, useEffect} from 'react';
import { Modal , Form , Input , Button } from 'antd';
import './EAccount.scss';
function EducoderAccount({form , visible , onOk , email}){
const { getFieldDecorator, validateFields , setFieldsValue } = form;
useEffect(()=>{
if(email){
setFieldsValue({email})
}
},[email])
function onSure(){
validateFields((error,values)=>{
if(!error){
onOk(values);
}
})
}
const layout = {
labelCol: { span: 5 },
wrapperCol: { span: 18 },
};
return(
<Modal
visible={visible}
title="提示"
width="500px"
closable={false}
footer={
<Button type="primary" onClick={onSure}>确定</Button>
}
centered
>
<div>
<p className="mb15 edu-txt-center" style={{maxWidth:"350px",margin:"0px auto"}}>
{
email ?
`平台已检测到您已绑定邮箱${email},请您确认如下操作`
:
"平台已检测到您未绑定邮箱,为不影响使用协同开发功能,请先绑定邮箱"
}
</p>
<Form {...layout}>
<Form.Item label="邮箱">
{getFieldDecorator("email",{
rules:[{required:true,message:"请输入邮箱账号"}]
})(
<Input placeholder="请输入您的邮箱账号" width="220px" disabled={email}/>
)}
</Form.Item>
<Form.Item label="密码">
{getFieldDecorator("password",{
rules:[{required:true,message:"请输入您的平台密码"}]
})(
<Input.Password placeholder="请输入您的平台密码" width="220px"/>
)}
</Form.Item>
</Form>
</div>
</Modal>
)
}
export default Form.create()(forwardRef(EducoderAccount));

View File

@ -45,7 +45,7 @@ const Div = styled.div`{
export default (({ user , img, name, time, focusStatus, is_current_user, login , successFunc }) => {
return (
<Div>
<Link to={`/users/${user && user.login}`}><Img src={getImageUrl(`images/${img}`)} /></Link>
<Link to={`/users/${user && user.login}`}><Img src={getImageUrl(`/${img}`)} /></Link>
<div className="m-infos">
<Link to={`/users/${user && user.login}`}><Name>{name}</Name></Link>
<Time><I className="iconfont icon-shijian"></I>加入时间:{time}</Time>

View File

@ -23,7 +23,10 @@ function Releases({owner,projectsId,releaseVersions}){
<AlignTop className="mt10">
<i className="iconfont icon-biaoqian3 color-grey-6 font-18 mr10"></i>
<div>
<p className="font-16 color-grey-6"><Link to={`/projects/${owner}/${projectsId}/releases/8/update`}>{item.name}</Link></p>
<p className="font-16 color-grey-6">
<Link to={`/projects/${owner}/${projectsId}/releases`}>{item.name}</Link>
<span className="font-12 laterest ml5">最新</span>
</p>
<p className="color-grey-9 font-13">{item.created_at}</p>
</div>
</AlignTop>

View File

@ -44,7 +44,7 @@ export default ({ getUser })=>{
className="user_img radius"
width="28"
height="28"
src={getImageUrl(`images/${item && item.image_url}`)}
src={getImageUrl(`/${item && item.image_url}`)}
alt=""
/>
<span className="ml10" style={{ "vertical-align": "middle" }}>

View File

@ -0,0 +1,91 @@
import React, { useEffect, useState } from 'react';
import { Popover , Tooltip } from 'antd';
import './Component.scss';
import axios from 'axios';
import ShareModal from './SiderBarShareModal';
const $ = window.$;
$(window).scroll(function () {
if ($(".gotop").length > 0) {
if ($(document).scrollTop() > 0) {
$(".-task-sidebar .gotop").show();
$(".gotop").click(function () {
$("html,body").scrollTop(0);
});
}
if ($(document).scrollTop() === 0) {
$(".-task-sidebar .gotop").hide();
}
}
});
function SiderBar() {
const [ data , setData ] = useState([]);
const [ visible , setVisible ] = useState(false);
useEffect(()=>{
getFAQ();
},[])
function getFAQ(){
const url = `/faqs.json`;
axios.get(url).then(result=>{
if(result && result.data){
setData(result.data);
}
}).catch(error=>{})
}
function content(list){
return <div>
<p className="titlecontent">帮助</p>
<ul className="faqUl">
{
list && list.map((i,k)=>{
return(
<li><a href={`${i.url}`} title={i.question} target="_blank">{i.question}</a></li>
)
})
}
</ul>
</div>
}
function shareContent(){
return <div>
<ul className="shareUl">
<p className="titlecontent">分享到</p>
<li onClick={()=>setVisible(true)}><i className="iconfont icon-weixin2" style={{color:"#62b900"}}></i></li>
</ul>
</div>
}
return (
<div className={"-task-sidebar"} >
<ShareModal visible={visible} urlValue={window.location.href} onCancel={()=>setVisible(false)}/>
{
data && data.length > 0 && (data[0] && data[0].question) ?
<Popover content={content(data)} overlayClassName="helpBox" placement={"left"}>
<div className="feedback">
<i className="iconfont icon-bangzhu font-22"></i>
</div>
</Popover>
:""
}
{/* <div className="scan pr" title="">
<span className="inline erweima"><i className="iconfont icon-erweima color-white font-22 fl"></i></span>
</div>*/}
<Popover content={shareContent()} overlayClassName="helpBox shareContent" placement={"left"}>
<div className="consult">
<i className="iconfont icon-fenxiang1"></i>
</div>
</Popover>
<div className="gotop">
<Tooltip title="返回顶部" placement={"right"}>
<a><i className="iconfont icon-huidaodingbu1"></i></a>
</Tooltip>
</div>
</div>
)
}
export default SiderBar;

View File

@ -0,0 +1,27 @@
import React from 'react';
import { Modal } from 'antd';
import QRCode from 'qrcode.react';
function SiderBarShareModal({visible,urlValue,onCancel}) {
return(
<Modal
title={"分享到微信"}
visible={visible}
width="500px"
closable={true}
footer={false}
onCancel={onCancel}
>
<div style={{textAlign:"center"}}>
{urlValue &&<QRCode
value={urlValue}
size={200}
fgColor="#000000"
style={{margin:"20px"}}
/>}
<p>打开微信扫一扫,点击右上角菜单即可将网页分享至朋友圈</p>
</div>
</Modal>
)
}
export default SiderBarShareModal;

View File

@ -28,6 +28,11 @@ export const AlignTop = styled.div`{
display:flex;
align-items: flex-start;
}`
export const AlignAJBottom = styled.div`{
display:flex;
justify-content: space-between;
align-items: flex-end;
}`
//
export const Box = styled.div`{
display:flex;

View File

@ -37,7 +37,7 @@ function About(props, ref) {
const [ typeFlag, setTypeFlag] = useState(false);
const AuthorLogin = props.author && props.author.login;
const AuthorLogin = props.projectDetail && props.projectDetail.author && props.projectDetail.author.login;
const CurrentLogin = props.current_user && props.current_user.login;
useEffect(()=>{
if(CurrentLogin === AuthorLogin){
@ -176,12 +176,12 @@ function About(props, ref) {
CurrentLogin !== AuthorLogin ?
<div className="noOperation">DevOps开启功能暂未对项目创建者以外的角色开放可以联系项目创建者进行开启开启后便可查看构建信息</div>:""
}
<a href={"https://forum.trustie.net/forums/3110/detail"} target="_blank" style={{ color: "#5091FF"}}>
{/* <a href={"https://forum.trustie.net/forums/3110/detail"} target="_blank" style={{ color: "#5091FF"}}>
了解什么是DevOps
</a>
<a href={"https://forum.trustie.net/forums/3080/detail"} target="_blank" style={{ color: "#5091FF"}}>
如何使用DevOps
</a>
</a> */}
{
AuthorLogin === CurrentLogin ?
<React.Fragment>
@ -233,7 +233,7 @@ function About(props, ref) {
step === 1 ?
<div>
<AlignCenter style={{justifyContent:"center",marginTop:"20px"}}>
<span style={{marginBottom:"42px"}}>密码</span>
<span className="mb40">密码</span>
<div>
<Input.Password value={authorityVal} className={authorityValFlag===true && "flags"} onChange={(e)=>setAuthorityVal(e.target.value)} style={{width:"220px"}}></Input.Password>
<p className="color-grey-9" style={{textAlign:"left",lineHeight:'21px'}}>您已保存相关服务器信息请输入密码<br/>确认授权DevOps应用</p>

View File

@ -10,6 +10,12 @@ import { Link } from 'react-router-dom';
// killed:"",
// pending:""
// }
function turnbar(str){
if(str && str.length>0 && str.indexOf("/")>-1){
return str.replaceAll('/','%2F');
}
return str;
}
function renderTableStatus(status) {
switch (status) {
case "running":
@ -65,8 +71,9 @@ function List({ list, operate , projectsId , owner , showModal , deleteFunc }){
width:"15%",
ellipsis:true,
render:(value,item)=>{
let v = turnbar(item.branch);
return(
<Link to={`/projects/${owner}/${projectsId}/tree/${item.branch}/${value}`} className="color-blue">{value}</Link>
<Link to={`/projects/${owner}/${projectsId}/tree/${v}/${value}`} className="color-blue">{value}</Link>
)
}
},

View File

@ -12,7 +12,7 @@ function ServiceModal({sureModal}){
<div className="mt30" style={{textAlign:"center"}}>
<Radio.Group value={type} onChange={changeType}>
<Radio value={1}>自有服务器</Radio>
<Radio value={2}>Trustie服务器</Radio>
<Radio value={2}>平台服务器</Radio>
</Radio.Group>
<p className="mt30"><Button type="primary" onClick={()=>sureModal(type)}>下一步</Button></p>
</div>

View File

@ -2,7 +2,7 @@ import React, { useState, useEffect , useImperativeHandle ,forwardRef } from "re
import { FlexAJ, AlignCenter , Banner } from "../Component/layout";
import { Table, Pagination, Popconfirm } from "antd";
import { truncateCommitId } from "../common/util";
import {getUrl} from 'educoder';
import { getImageUrl } from 'educoder';
import axios from "axios";
import { Link } from 'react-router-dom';
@ -58,6 +58,7 @@ function Structure(props,ref){
return {
...item,
author:item.author && item.author.name,
image_url:item.author && item.author.image_url,
message: {
branch: item.branch_target,
message: item.message,
@ -244,7 +245,7 @@ function Structure(props,ref){
{meg.sha && <span className="color-orange">{meg.sha}</span>}
</div>
<AlignCenter>
<img style={{borderRadius:"50%",marginRight:"10px",width:"25px",height:"25px"}} src={`${current_user && getUrl(`/images/${current_user.image_url}`)}`} />
<img style={{borderRadius:"50%",marginRight:"10px",width:"25px",height:"25px"}} alt="" src={`${item.image_url && getImageUrl(`/${item.image_url}`)}`} />
<div className="task-hide ml5" style={{ maxWidth: "300px" }}>
{meg.message}
</div>

View File

@ -0,0 +1,174 @@
import React ,{ forwardRef, useEffect, useState } from 'react';
import { Modal , Form , Input , Radio , Select } from 'antd';
import SearchUser from '../Component/SearchUser';
import './Index.scss';
import Axios from 'axios';
const { Option } = Select;
function DivertModal({form , visible , onSuccess , onCancel,owner,repo}){
const { getFieldDecorator, validateFields , setFieldsValue } = form;
const [ cate , setCate ] = useState(0);
const [ value , setValue ] = useState(undefined);
const [ organizations , setOrganizations ] = useState(undefined);
useEffect(()=>{
setFieldsValue({goal:cate})
},[])
useEffect(()=>{
if(owner && repo && visible===true){
getTeam();
}
if(!visible){
setFieldsValue({
owner_name:undefined,
identifier:undefined
})
setValue(undefined)
}
},[repo,owner,visible])
function getTeam(){
const url = `/${owner}/${repo}/applied_transfer_projects/organizations.json`;
Axios.get(url).then(result=>{
if(result){
setOrganizations(result.data.organizations);
}
}).catch(error=>{})
}
//
function onOk(){
validateFields((error,values)=>{
console.log(...values);
if(!error){
const url = `/${owner}/${repo}/applied_transfer_projects.json`;
Axios.post(url,{
...values
}).then(result=>{
if(result){
onSuccess(result.data && result.data.owner);
}
}).catch(error=>{})
}
})
}
function changeType(e){
setCate(e.target.value);
setFieldsValue({
owner_name:undefined
})
}
function checkIdentifier(rule, value, callback){
if(!value){
callback();
}
if (repo && value !== repo) {
callback("请输入当前项目的标识!");
}
callback();
}
const layout = {
labelCol: { span: 5 },
wrapperCol: { span: 18 },
};
function getUser(id){
setValue(id);
setFieldsValue({
owner_name:id
})
}
return(
<Modal
width="620px"
visible={visible}
title="转移仓库"
onCancel={onCancel}
onOk={onOk}
okText="确认转移"
cancelText={"取消"}
centered
>
<div className="diverModal">
{
cate === 0 ?
<ul className="descUl">
<li>转移需对方确认接受转移成功后你将被移出仓库其他已有成员权限不变</li>
<li>转移成功后仓库的地址将变更至目标用户的命名空间下</li>
<li>已有成员如需继续操作仓库需更新本地仓库的remote使之指向新的地址</li>
</ul>
:
<ul className="descUl">
<li>仓库仅可以转移到您已经加入的组织中不可以转移到未加入的组织中</li>
<li>涉及到仓库改名操作请提前做好仓库备份并且在转移后对本地仓库的remote进行修改</li>
<li>转移仓库到组织后你和组织创建者/管理员同时拥有对该仓库的管理操作</li>
</ul>
}
<Form {...layout} colon={false} layout={"horizontal"}>
<Form.Item label="转移给:" style={{marginBottom:"0px"}}>
{getFieldDecorator("goal",{
rules:[]
})(
<Radio.Group onChange={changeType}>
<Radio value={0}>个人</Radio>
<Radio value={1}>组织</Radio>
</Radio.Group>
)}
</Form.Item>
{
cate === 0 &&
<Form.Item label=" ">
{getFieldDecorator("owner_name",{
rules:[{required:true,message:"请输入目标用户名"}]
})(
// <Input placeholder="" autoComplete={"off"}/>
<SearchUser getUser={getUser} width={"100%"} placeholder="请输入目标用户" value={value}/>
)}
</Form.Item>
}
{
cate === 1 &&
<Form.Item label=" ">
{getFieldDecorator("owner_name",
{rules:[{required:true,message:"请选择目标组织"}]}
)(
<Select placeholder="请选择目标组织" getPopupContainer={trigger => trigger.parentNode}>
{
organizations && organizations.length > 0 ?
organizations.map((i,k)=>{
return(
<Option value={i.name}>{i.nickname}</Option>
)
})
:""
}
</Select>
)}
</Form.Item>
}
<Form.Item label="仓库名称:">
{getFieldDecorator("identifier",
{
rules:[
{required:true,message:"请输入仓库名称"},
{
validator:checkIdentifier
}
]
}
)(
<Input placeholder="请输入仓库名称" autoComplete={"off"}/>
)}
</Form.Item>
</Form>
</div>
</Modal>
)
}
export default Form.create()(forwardRef(DivertModal));

107
src/forge/Gantt/Index.jsx Normal file
View File

@ -0,0 +1,107 @@
import React, { useEffect } from 'react';
import * as echarts from 'echarts';
import './index.scss';
function Index(props) {
useEffect(()=>{
Init();
},[])
function Init() {
var myCharts = document.getElementById('main');
var myEcharts = echarts.init(myCharts);
var option = {
title: {
text: '项目实施进度表',
left: 10
},
legend: {
y: 'bottom',
data: ['计划时间', '实际时间'] //1
},
grid: {
containLabel: true,
left: 20
},
xAxis: {
type: 'time'
},
yAxis: {
data: ['任务一', '任务二', '任务三', '任务四', '任务五', '任务六', '任务七']
},
tooltip: {
trigger: 'axis',
formatter: function(params) {
var res = params[0].name + "</br>"
var date0 = params[0].data;
var date1 = params[1].data;
var date2 = params[2].data;
var date3 = params[3].data;
date0 = date0.getFullYear() + "-" + (date0.getMonth() + 1) + "-" + date0.getDate();
date1 = date1.getFullYear() + "-" + (date1.getMonth() + 1) + "-" + date1.getDate();
date2 = date2.getFullYear() + "-" + (date2.getMonth() + 1) + "-" + date2.getDate();
date3 = date3.getFullYear() + "-" + (date3.getMonth() + 1) + "-" + date3.getDate();
res += params[0].seriesName + "~" + params[1].seriesName + ":</br>" + date0 + "~" + date1 + "</br>"
res += params[2].seriesName + "~" + params[3].seriesName + ":</br>" + date2 + "~" + date3 + "</br>"
console.log(params[0]);
return res;
}
},
series: [
{
name: '计划开始时间',
type: 'bar',
stack: 'test1',
itemStyle: {
normal: {
color: 'rgba(0,0,0,0)'
}
},
data: []
},
{
name: '计划时间',
type: 'bar',
stack: 'test1',
//2
itemStyle: {
normal: {
color: '#F98563'
}
},
data: []
},
{
name: '实际开始时间',
type: 'bar',
stack: 'test2',
itemStyle: {
normal: {
color: 'rgba(0,0,0,0)'
}
},
data: []
},
{
name: '实际时间',
type: 'bar',
stack: 'test2',
//3
itemStyle: {
normal: {
color: '#A2E068'
}
},
data: []
}
]
};
myEcharts.setOption(option);
}
return(
<div className="ganttBox">
<div id="main" style={{height:"500px"}}></div>
</div>
)
}
export default Index;

View File

@ -0,0 +1,5 @@
.ganttBox{
width: 1200px;
margin:0px auto;
padding:22px 0px;
}

View File

@ -14,4 +14,8 @@ export const getHooks = async (id,params)=>{
//
export const getSubEntries = async (owner,projectsId,params)=>{
return (await axios.get(`/${owner}/${projectsId}/sub_entries.json`,{params})).data;
}
//
export const getUser = async (login)=>{
return (await axios.get(`/users/${login}/hovercard.json`)).data;
}

View File

@ -0,0 +1,84 @@
import React, { useState , forwardRef, useEffect } from 'react';
import { Form , Modal , Input , Radio } from 'antd';
import Axios from 'axios';
export default Form.create()(
forwardRef((props)=>{
const { getFieldDecorator, validateFields , setFieldsValue } = props && props.form;
const [ visible , setVisible ] = useState(false);
useEffect(()=>{
if(!visible){
setFieldsValue({
code:undefined,
role:"developer"
})
}
},[visible])
function onOk() {
validateFields((error,values)=>{
if(!error){
const url = `/applied_projects.json`;
Axios.post(url,{
applied_project:{
...values
}
}).then(result=>{
if(result && result.data){
setVisible(false);
props.showNotification("申请加入项目成功,等待审核!");
}
}).catch(error=>{})
}
})
}
function checkValue(rule, value, callback){
if(!value){
callback();
}
if(value.length < 6 || value.length > 6){
callback("请输入6位数的邀请码");
}
callback();
}
return(
<React.Fragment>
<Modal
title="加入项目"
width="480px"
visible={visible}
centered={true}
onOk={onOk}
onCancel={()=>setVisible(false)}
>
<Form layout={'inline'} className="inviteForm">
<Form.Item label="项目邀请码">
{getFieldDecorator("code",{
rules:[
{required:true,message:"请输入6位项目邀请码"},
{validator:checkValue}
]
})(
<Input placeholder="请输入6位项目邀请码" autoComplete={"off"} maxLength="6" style={{width:"300px"}}/>
)}
</Form.Item>
<Form.Item label="选择角色">
{getFieldDecorator("role",{
rules:[{required:true,message:"请选择角色"}]
})(
<Radio.Group defaultValue={"developer"}>
<Radio value="manager">管理员</Radio>
<Radio value="developer">开发者</Radio>
<Radio value="reporter">报告者</Radio>
</Radio.Group>
)}
</Form.Item>
</Form>
</Modal>
<a onClick={()=>setVisible(true)}>加入项目</a>
</React.Fragment>
)
})
)

66
src/forge/Head/Footer.jsx Normal file
View File

@ -0,0 +1,66 @@
import React, { useEffect , useState } from 'react';
import './header.scss';
function Footer(){
const [ value , setValue ] = useState(undefined);
useEffect(()=>{
try {
var chromesettingArray = JSON.parse(localStorage.getItem('chromesetting'));
setValue(chromesettingArray.footer);
} catch (e) {
}
},[])
function showhtml(htmlString){
var html = {__html:htmlString};
return <div dangerouslySetInnerHTML={html}></div> ;
}
return(
<div>
<div style={{height:"810px"}}></div>
<div className="newFooter edu-txt-center">
{value && showhtml(value)}
{/* <div className="footerInfos">
<ul>
<li>社区</li>
<li><a href={`/`} target="_blank">网站首页</a></li>
<li><a href={`https://www.trustie.net/agreement`} target="_blank">服务协议</a></li>
<li><a href={`https://forum.trustie.net/forums/1168/detail`} target="_blank">帮助中心</a></li>
<li><a href={`https://forum.trustie.net/`} target="_blank">问吧交流</a></li>
<li><a href={`https://www.trustie.net/cooperation`} target="_blank">合作伙伴</a></li>
</ul>
<ul>
<li>支持与服务</li>
<li><a href={`https://forgeplus.trustie.net/docs/api`} target="_blank">API文档</a></li>
<li><a href={`https://forum.trustie.net/forums/1168/detail`} target="_blank">帮助中心</a></li>
<li><a href={`https://git-scm.com`} target="_blank">Git常用命令</a></li>
<li><a href={`https://forum.trustie.net/forums/3080/detail`} target="_blank">DevOps使用文档</a></li>
<li><a href={`https://forgeplus.trustie.net/projects/jasder/forgeplus/tree/master/CHANGELOG.md`} target="_blank">日志更新</a></li>
</ul>
<ul>
<li>合作伙伴</li>
<li><a href={`http://www.sei.pku.edu.cn`} target="_blank">北京大学</a></li>
<li><a href={`http://scse.buaa.edu.cn`} target="_blank">北京航空航天大学</a></li>
<li><a href={`https://www.nju.edu.cn`} target="_blank">南京大学</a></li>
<li><a href={`https://www.xtu.edu.cn`} target="_blank">湘潭大学</a></li>
<li><a href={`http://www.iscas.ac.cn`} target="_blank">ISCAS</a></li>
<li><a href={`https://www.ucloud.cn`} target="_blank">UCloud优刻得</a></li>
<li><a href={`http://www.inforbus.com`} target="_blank">中创软件</a></li>
<li><a href={`https://www.inspur.com`} target="_blank">浪潮集团</a></li>
<li><a href={`http://www.copu.org.cn`} target="_blank">中国开源软件推进联盟</a></li>
<li><a href={`https://www.sjtu.edu.cn`} target="_blank">上海交通大学</a></li>
</ul>
<ul>
<li>合作伙伴</li>
<li><span>热线</span></li>
<li><span>QQ群1071514693</span></li>
</ul>
</div>
<p className="footerCopy">© Copyright 2007~2021 国防科技大学Trustie团队 & IntelliDE <a href="https://beian.miit.gov.cn">湘ICP备 17009477</a></p> */}
</div>
</div>
)
}
export default Footer;

View File

@ -2,16 +2,11 @@ import React, { Component } from 'react';
import AccountProfile from "../../modules/user/AccountProfile";
import { getImageUrl } from 'educoder'
import axios from 'axios';
import { Modal, Input, message, notification , Dropdown , Menu ,Divider } from 'antd';
import { Input , notification , Dropdown , Menu } from 'antd';
import LoginDialog from '../../modules/login/LoginDialog';
import GotoQQgroup from '../../modal/GotoQQgroup'
// import 'antd/lib/modal/style/index.css';
// import 'antd/lib/checkbox/style/index.css';
// import 'antd/lib/radio/style/index.css';
// import 'antd/lib/input/style/index.css';
import AddProjectModal from './AddProjectModal';
import '../../modules/tpm/TPMIndex.css';
import logo from '../../modules/tpm/images/logo.png';
import './header.scss';
const $ = window.$
// TODO 这部分脚本从公共脚本中直接调用
@ -35,11 +30,9 @@ class NewHeader extends Component {
Checkboxteachertype: false,
Checkboxteachingtype: false,
code_notice: false,
checked_notice: false,
RadioGroupvalue: undefined,
submitapplications: false,
isRender: false,
showSearchOpentype: false,
showTrial: false,
setevaluatinghides: false,
occupation: 0,
@ -47,13 +40,11 @@ class NewHeader extends Component {
headtypesonClickbool: false,
headtypess: "/",
settings: null,
goshowqqgtounp: false,
visiblemyss: false,
openSearch:false,
}
}
componentDidMount() {
// this.getAppdata();
this.geturlsdata();
window._header_componentHandler = this;
@ -96,22 +87,23 @@ class NewHeader extends Component {
}, 300)
}}
>
<Search placeholder="实践课程/教学课堂/实践项目/交流问答"
<Search placeholder="请输入搜索关键字"
className={`search-input mr20`}
onSearch={(value)=>this.onGlobalSearch(value,item)}
autoFocus={true}
style={{width:"260px"}}
/>
</div>
)
}else{
return <i className="iconfont icon-sousuo font-18 color-grey-6 ml30" onClick={() => {
return <i className="iconfont icon-sousuo font-18 color-white ml30" onClick={() => {
this.setState({openSearch:true})
}} />
}
}
onGlobalSearch=(value,item)=>{
window.location.href=`${item && item.url}?value=` + value;
window.location.href=`${item}?value=` + value;
}
openNotification = (messge) => {
@ -132,158 +124,8 @@ class NewHeader extends Component {
old_url = newProps.Headertop.old_url
}
}
getCookie = (key) => {
var arr, reg = RegExp('(^| )' + key + '=([^;]+)(;|$)');
if (arr === document.cookie.match(reg))
return decodeURIComponent(arr[2]);
else
return null;
}
delCookie = (name) => {
var exp = new Date();
exp.setTime(exp.getTime() - 1);
var cval = this.getCookie(name);
if (cval != null) {
document.cookie = name + "=" + cval + ";expires=" + exp.toGMTString();
}
}
onLogout = () => {
const url = `/accounts/logout.json`
this.delCookie("autologin_trustie")
axios.get(url, {
}).then((response) => {
if (response.data.status === 1) {
this.setState({
user: undefined
})
window.location.href = "/login"
message.success('退出成功');
}
});
}
tojoinclass = () => {
let { user } = this.state;
if (user === undefined) {
this.setState({
isRender: true
})
return
}
if (user && user.login === "") {
this.setState({
isRender: true
})
return;
}
if (user && user.profile_completed === false) {
this.setState({
AccountProfiletype: true
})
return;
}
this.setState({
Addcoursestypes: true,
})
}
tojoinitem = () => {
if (this.props.user && this.props.user.email === undefined || this.props.user && this.props.user.email === null || this.props.user && this.props.user.email === "") {
this.openNotification("请先绑定邮箱,谢谢");
return
}
let { user } = this.state;
if (user === undefined) {
this.setState({
isRender: true
})
return
}
if (user && user.login === "") {
this.setState({
isRender: true
})
return;
}
if (user && user.profile_completed === false) {
this.setState({
AccountProfiletype: true
})
return;
}
this.setState({
tojoinitemtype: true
})
}
submitstatevalue = (sum, value, data) => {
this.setState({
Addcoursestypes: false,
tojoinitemtype: false,
tojoinclasstitle: undefined,
rolearr: ["", ""],
Checkboxteacherchecked: false,
Checkboxstudentchecked: false,
Checkboxteachingchecked: false,
Checkboxteachertype: false,
Checkboxteachingtype: false,
code_notice: false,
checked_notice: false,
submitapplicationssum: sum,
submitapplications: true,
submitapplicationsvalue: value,
submitapplicationsvaluedata: data,
RadioGroupvalue: undefined
})
}
onChangeRadioGroup = (e) => {
this.setState({
RadioGroupvalue: e.target.value,
});
}
submitsubmitapplications = () => {
let {
submitapplicationssum,
submitapplicationsvaluedata
} = this.state;
this.setState({
submitapplications: false,
RadioGroupvalue: undefined
})
if (submitapplicationssum === 0) {
if (submitapplicationsvaluedata !== undefined) {
window.location.href = "/courses/" + submitapplicationsvaluedata;
}
} else if (submitapplicationssum === 1) {
if (submitapplicationsvaluedata !== undefined) {
window.location.href = "/projects/" + submitapplicationsvaluedata;
}
}
}
hidesubmitapplications = () => {
this.setState({
Addcoursestypes: false,
tojoinitemtype: false,
tojoinclasstitle: undefined,
rolearr: ["", ""],
Checkboxteacherchecked: false,
Checkboxstudentchecked: false,
Checkboxteachingchecked: false,
Checkboxteachertype: false,
Checkboxteachingtype: false,
code_notice: false,
checked_notice: false,
submitapplications: false,
RadioGroupvalue: undefined
})
}
educoderlogin = () => {
//登录账号
this.setState({
@ -322,23 +164,6 @@ class NewHeader extends Component {
};
hidetojoinclass = () => {
this.setState({
tojoinclasstype: false,
tojoinitemtype: false,
tojoinclasstitle: undefined,
rolearr: ["", ""],
Checkboxteacherchecked: false,
Checkboxstudentchecked: false,
Checkboxteachingchecked: false,
Checkboxteachertype: false,
Checkboxteachingtype: false,
code_notice: false,
checked_notice: false,
RadioGroupvalue: undefined
})
}
// 关闭
cancelModulationModels = () => {
this.setState({ isRenders: false })
@ -404,39 +229,13 @@ class NewHeader extends Component {
})
}
getAppdata = () => {
try {
var chromesettingArray = JSON.parse(localStorage.getItem('chromesetting'));
var chromesettingresponseArray = JSON.parse(localStorage.getItem('chromesettingresponse'));
this.setState({
settings: chromesettingArray
});
if (chromesettingArray.tab_logo_url) {
this.gettablogourldata(chromesettingresponseArray);
} else {
this.gettablogourlnull();
}
} catch (e) {
this.geturlsdata();
}
};
geturlsdata = () => {
let url = "/setting.json";
axios.get(url).then((response) => {
if (response && response.data) {
this.setState({ settings: response.data.setting });
// localStorage.setItem('chromesetting', JSON.stringify(response.data.setting));
// localStorage.setItem('chromesettingresponse', JSON.stringify(response));
try {
if (response.data.setting.tab_logo_url) {
this.gettablogourldata(response);
} else {
this.gettablogourlnull();
}
} catch (e) {
this.gettablogourlnull();
}
localStorage.setItem('chromesetting', JSON.stringify(response.data.setting));
localStorage.setItem('chromesettingresponse', JSON.stringify(response));
} else {
this.gettablogourlnull();
}
@ -456,18 +255,10 @@ class NewHeader extends Component {
}
}
// 处理弹框
setgoshowqqgtounp = (bool) => {
this.setState({
goshowqqgtounp: bool
})
}
addMenu=(list)=>{
return(
list && list.length >0 &&
<div className="dropdownFlex">
<div className="dropdownFlex double">
<Menu>
{
list.map((item,key)=>{
@ -476,10 +267,29 @@ class NewHeader extends Component {
)
})
}
<Menu.Item><AddProjectModal showNotification={this.props.showNotification}/></Menu.Item>
</Menu>
</div>
)
}
renderMenu=(personal)=>{
const { current_user } = this.props;
return(
<Menu className="currentMenu">
<Menu.Item>
<span title={current_user && current_user.username}>{current_user && current_user.username}</span>
</Menu.Item>
{
personal && personal.length > 0 && personal.map((item,key)=>{
return(
<li key={key}><a href={item.url} target="_blank">{item.name}</a></li>
)
})
}
<Menu.Item><a onClick={() => this.educoderloginysl()}>退出</a></Menu.Item>
</Menu>
)
}
render() {
const { match} = this.props;
@ -488,17 +298,12 @@ class NewHeader extends Component {
tojoinitemtype,
tojoinclasstitle,
code_notice,
checked_notice,
AccountProfiletype,
submitapplications,
submitapplicationsvalue,
user,
isRender,
showSearchOpentype,
headtypesonClickbool,
headtypess,
settings,
goshowqqgtounp,
openSearch,
} = this.state;
/*用户名称 用户头像url*/
@ -557,7 +362,7 @@ class NewHeader extends Component {
let shixun = "/shixuns";
let paths = "/paths";
let courses = "/courses";
this.props.mygetHelmetapi.navbar.map((item, key) => {
this.props.mygetHelmetapi && this.props.mygetHelmetapi.navbar && this.props.mygetHelmetapi.navbar.map((item, key) => {
var reg = RegExp(item.link);
if (shixun.match(reg)) {
if (item.hidden === true) {
@ -577,8 +382,8 @@ class NewHeader extends Component {
})
}
let search_url = settings && settings.common && settings.common.length> 0 && settings.common.filter(item=>item.name==="搜索");
let notice_url = settings && settings.common && settings.common.length> 0 && settings.common.filter(item=>item.name==="通知");
let search_url = settings && settings.common && settings.common.search;
let notice_url = settings && settings.common && settings.common.notice;
return (
<div className="newHeaders" id="nHeader">
<div className="headerContent">
@ -595,19 +400,14 @@ class NewHeader extends Component {
{...this.props}
{...this.state}
/> : ""}
{
goshowqqgtounp === true ?
<GotoQQgroup {...this.state} {...this.props} setgoshowqqgtounp={(bool) => this.setgoshowqqgtounp(bool)}></GotoQQgroup>
:""
}
<a href={settings && settings.new_course.default_url} className={"fl mr30"} style={{minWidth:"45px"}}>
{
settings && settings.nav_logo_url ?
<img alt="可控开源社区" className="logoimg" style={{ heigth: "40px" }} src={getImageUrl(settings.nav_logo_url)}></img>
<a href={settings && settings.new_course.default_url} className={"fl mr50"} style={{minWidth:"45px"}}>
<img alt="可控开源社区" className="logoimg" style={{ heigth: "40px" }} src={getImageUrl(`/${settings.nav_logo_url}`)}></img>
</a>
:
<img alt="可控开源社区" className="logoimg" style={{ heigth: "40px" }} src={logo}></img>
""
}
</a>
<div className="head-nav pr" id={"head-navpre1"}>
{
settings && settings.navbar && settings.navbar.length > 0 ?
@ -636,7 +436,7 @@ class NewHeader extends Component {
var wl = waiLian && waiLian.length>0;
return (
<li key={key} onClick={() => this.headtypesonClick(item.link, true)} className={`${this.matchpaths(item.link) === true ? 'pr active' : 'pr'}`} style={!is_hidden ? { display: 'flex' } : { display: 'none' }}>
<a href={new_link} target={wl ? "_self":"_blank"}>{item.name}</a>
<a href={new_link} target={wl ? "_self":"_blank"}><div dangerouslySetInnerHTML={{ __html: item.name }}></div></a>
</li>
)
})
@ -646,81 +446,40 @@ class NewHeader extends Component {
}
</div>
<div className="head-right">
{search_url && search_url.length>0 ? this.SearchInput(openSearch,search_url[0]):""}
{search_url ? this.SearchInput(openSearch,search_url):""}
{
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"></i>
<i className="iconfont icon-tianjiafangda color-white ml30"></i>
</Dropdown>:""
}
{this.props.user && this.props.user.login && (notice_url && notice_url.length>0) ?
{this.props.user && this.props.user.login && notice_url ?
<div className="ml30 edu-menu-panel">
{user && user.login &&
<a href={`${notice_url[0].url}`} style={{ position: 'relative' }}>
<i className="iconfont icon-xiaoxilingdang color-grey-6"></i>
<a href={`${notice_url}`} style={{ position: 'relative' }}>
<i className="iconfont icon-xiaoxilingdang color-white"></i>
<span className="newslight" style={{ display: this.props.Headertop === undefined ? "none" : this.props.Headertop.new_message === true ? "block" : "none" }}>
</span>
</a>
}
</div>:""
}
<Modal
keyboard={false}
title="提示"
visible={submitapplications}
closable={false}
footer={null}
>
<div className="task_popup_con ml30">
<div className="mr15">
<ul>
<div className="task-popup-content">
<p className="task-popup-text-center font-16">
{submitapplicationsvalue}
</p>
</div>
<li className="clearfix mt10 edu-txt-center">
<a className="task-btn mr10"
onClick={this.hidesubmitapplications}>取消</a>
<a
className="task-btn task-btn-orange ml20"
onClick={this.submitsubmitapplications}>确定</a>
</li>
</ul>
</div>
</div>
</Modal>
</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.new_course && settings.new_course.register_url &&
<span><em className="vertical-line"></em><a className="ml5 color-grey-6" href={`${settings.new_course.register_url}`} target="_blank"></a></span>
settings && settings.common && settings.common.register &&
<span><em className="vertical-line"></em><a className="ml5 color-white" href={`${settings.common.register}`} target="_blank"></a></span>
}
</span>
:
<div className="ml30 edu-menu-panel" style={{ height: "70px", lineHeight: "70px" }}>
<a href={`/users/${this.props.current_user === undefined ? "" : this.props.current_user.login}/courses`}>
<img alt="头像" className="radius" height="34" id="nh_user_logo" name="avatar_image"
src={getImageUrl(`images/` + user.image_url)} width="34">
</img>
<Dropdown placement={`bottomRight`} overlay={this.renderMenu(settings && settings.personal)}>
<a href={`/users/${this.props.current_user && this.props.current_user.login}`}>
<img alt="头像" src={getImageUrl(`/${user.image_url}`)} className="currentImg"></img>
</a>
<ul className="edu-menu-list" style={{ top: '60px', textAlign: 'center' }}>
<li className="bor-bottom-greyE" style={{cursor:"default",background:"#fff"}}>{this.props.current_user.username}</li>
{
settings && settings.personal && settings.personal.length > 0 && settings.personal.map((item,key)=>{
return(
<li key={key}><a href={item.url} target="_blank">{item.name}</a></li>
)
})
}
<li className="bor-top-greyE">
<a onClick={() => this.educoderloginysl()}>退出</a>
</li>
</ul>
</div>
</Dropdown>
}
</div>
</div>

View File

@ -1,10 +1,155 @@
.dropdownFlex.double{
width: 232px;
.ant-menu{
height: 145px;
.ant-menu-item:nth-child(-n+4){
border-right: 1px solid #eee;
}
.ant-menu-item{
padding:0px 5px;
}
.ant-menu-item:hover a{
color: #fff!important;
background-color: #1890ff;
}
}
}
.headmenus{
.ant-dropdown-menu-item:hover{
background-color: #f5f5f5;
}
}
.dropdownFlex{
display:flex;
padding:5px;
background:#fff;
border-radius: 3px;
.ant-menu-vertical > .ant-menu-item{
border:none
padding:5px 0px;
box-sizing: border-box;
.ant-menu{
display: flex;
flex-direction: column;
line-height: 45px;
flex-wrap: wrap;
}
.ant-menu > .ant-menu-item{
border:none;
height: 35px;
line-height: 35px;
margin:0px;
width: 116px;
&.ant-menu-item-selected{
background-color: #fff;
a{color: rgba(0, 0, 0, 0.65)!important;}
}
a{
width: 100%;
text-align: center;
}
&.ant-menu-item-active{
a{color: #4cacff!important;}
}
}
.ant-menu-vertical{
border:none;
}
}
.currentImg{
width: 34px;
height: 34px;
border-radius: 50%;
margin-left: 30px;
}
.currentMenu{
width: 120px;
text-align: center;
padding:0px;
li{
height: 40px;
line-height: 40px;
padding:0px!important;
cursor: default;
&:hover{
background-color: #fff;
}
&:first-child{
border-bottom: 1px solid #eee;
}
&:last-child{
border-top: 1px solid #eee;
a{
border-radius: 0px 0px 4px 4px;
}
}
a{
padding:0px;
margin:0px;
display: block;
color: #666;
&:hover{
color: #fff;
background: #4CACFF;
}
}
}
}
.newFooter {
position: absolute;
bottom: 0;
width: 100%;
background: #323232;
clear: both;
min-width: 1200px;
z-index: 8;
left: 0px;
p {
margin-top: 0;
margin-bottom:0px !important;
}
.footerInfos{
display: flex;
max-width: 1200px;
margin:0px auto;
justify-content: space-around;
padding:60px 0px;
& >ul{
padding:0px 40px;
box-sizing: border-box;
max-width: 25%;
text-align: left;
li{
color: #fff;
font-weight: 300;
&:first-child{
font-size: 17px;
}
&>a,&>span{
color: #bbb;
}
&>a:hover{
color: #4cacff;
}
}
}
}
.footerCopy{
color: #bbb;
border-top: 1px solid #4e4e4e;
padding:10px 0px;
a{
color: #bbb;
&:hover{
color: #4cacff;
}
}
}
}
.inviteForm{
.ant-form-item{
margin-right: 0px;
}
.ant-form-item-label{
width: 110px;
text-align: right;
}
}

BIN
src/forge/Images/banner.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

BIN
src/forge/Images/sonar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -6,7 +6,6 @@ import { withRouter } from "react-router";
import { SnackbarHOC } from "educoder";
import { CNotificationHOC } from "../modules/courses/common/CNotificationHOC";
import { TPMIndexHOC } from "../modules/tpm/TPMIndexHOC";
import Handbook from './Component/Handbook';
import "./css/index.scss";
import Loadable from "react-loadable";
@ -27,15 +26,18 @@ const ProjectDetail = Loadable({
loading: Loading,
});
const Infos = Loadable({
loader: () => import("./users/Infos"),
loading: Loading,
});
class Index extends Component {
componentDidUpdate=(preProps)=>{
this.props.history.listen(()=>{
if (document.body.scrollTop || document.documentElement.scrollTop > 0) {
window.scrollTo(0, 0);
}
})
}
render() {
return (
<div className="newMain clearfix">
<Handbook />
<Switch {...this.props}>
<Route
path="/projects/:projectsType/new/:OIdentifier"
@ -68,12 +70,8 @@ class Index extends Component {
)}
></Route>
<Route
exact
path="/"
render={(props) => (
this.props.current_user && this.props.current_user.login ?
<Infos {...this.props} {...props} />
:
<ProjectIndex {...this.props} {...props} />
)}
></Route>

View File

@ -19,10 +19,27 @@ import LanguagePower from '../Component/LanguagePower';
import DrawerPanel from '../Component/DrawerPanel';
import UpdateDescModal from './sub/UpdateDescModal';
import Nodata from '../Nodata';
import Invite from './sub/Invite';
/**
* projectDetail.type:0是托管项目1是镜像项目2是同步镜像项目(为2时不支持在线创建在线上传在线修改在线删除创建合并请求等功能)
*/
function turnbar(str){
if(str && str.length>0 && str.indexOf("/")>-1){
return str.replaceAll('/','%2F');
}
return str;
}
function returnbar(str){
if(str && str.length>0 && str.indexOf("%2F")>-1){
return str.replaceAll('%2F','/');
}
return str;
}
function CoderDepot(props){
const [ projectDetail , setProjectDetail ]= useState(undefined);
const [ inviteCode , setInviteCode ] = useState(undefined);
const [ treeValue , setTreeValue ] = useState(undefined);
const [ treeValuePath , setTreeValuePath ] = useState(undefined);
const [ lastCommit,setLastCommit ] = useState(undefined);
@ -42,19 +59,28 @@ function CoderDepot(props){
const [ openModal , setOpenModal ] = useState(false);
const [ desc , setDesc ] = useState(undefined);
const [ website , setWebsite ] = useState(undefined);
const [ lesson_url , setLessonUrl ] = useState(undefined);
const [ readme , setReadme ] = useState(undefined);
const [ defaultBranch , setDefaultBranch ] = useState(undefined);
const [ editReadme , setEditReadme ] = useState(false);
const owner = props.match.params.owner;
const projectsId = props.match.params.projectsId;
const branchName = props.match.params.branchName;
let branchName = props.match.params.branchName;
branchName = returnbar(branchName);
const details = props.projectDetail;
let pathname = props.history.location.pathname;
useEffect(()=>{
if(props.projectDetail){
setProjectDetail(props.projectDetail);
setDesc(props.projectDetail.description);
setWebsite(props.projectDetail.website);
if(details){
setProjectDetail(details);
setDesc(details.description);
setWebsite(details.website);
setLessonUrl(details.lesson_url);
setDefaultBranch(details.default_branch);
setInviteCode(details.invite_code);
}
},[props])
},[details])
useEffect(()=>{
if(treeValue){
@ -64,23 +90,28 @@ function CoderDepot(props){
}
},[treeValue])
useEffect(()=>{
if (pathname){
if(pathname.indexOf(`/projects/${owner}/${projectsId}`) > -1 && pathname.indexOf(`/tree/${branchName}/`) > -1) {
let url = pathname.split(`/tree/${branchName}/`)[1];
if (projectsId && owner && defaultBranch){
let b = turnbar(branchName) ;
if(pathname.indexOf(`/projects/${owner}/${projectsId}`) > -1 && pathname.indexOf(`/tree/${b}/`) > -1) {
let url = pathname.split(`/tree/${b}/`)[1];
setTreeValue(url);
getFileInfo(url,branchName);
setType("file");
}else{
setTreeValue(undefined);
getDirInfo(branchName ||(projectDetail && projectDetail.default_branch));
getDirInfo(branchName || defaultBranch);
setType("dir");
}
}
},[pathname])
},[projectsId,owner,pathname,defaultBranch])
//
function getDirInfo(branch){
setIsSpin(true);
const url = `/${owner}/${projectsId}/entries.json`;
axios.get(url, {
params: { ref: branch }
}).then((result) => {
@ -92,8 +123,12 @@ function CoderDepot(props){
setZip_url(result.data.zip_url);
let c = result.data.last_commit
setLastCommit(c && c.commit);
setLastCommitAuthor(c && (c.author || (c.commit && c.commit.author)));
setLastCommitAuthor(c && c.committer);
setMainFlag(true);
setReadOnly(true);
setReadme(result.data.readme);
setEditReadme(false);
setHide(true);
}
setTimeout(function(){setIsSpin(false);},500);
}).catch(error=>{setIsSpin(false);})
@ -105,7 +140,7 @@ function CoderDepot(props){
let ele = document.getElementById("ptxt");
if(ele){
let h = ele.offsetHeight;
if( h > 18 ) setHideBtn(true)
if( h > 18 ) setHideBtn(true);
}
}
},[projectDetail,lastCommit])
@ -133,8 +168,10 @@ function CoderDepot(props){
}
let c = result.data.last_commit
setLastCommit(c && c.commit);
setLastCommitAuthor(c && (c.author || (c.commit && c.commit.author)));
setLastCommitAuthor(c && c.committer);
setMainFlag(false);
setReadOnly(!editReadme);
setHide(true);
}
setTimeout(function(){setIsSpin(false);},500)
}).catch(error=>{setIsSpin(false);})
@ -142,17 +179,22 @@ function CoderDepot(props){
//
function changeBranch(value){
let url = `/projects/${owner}/${projectsId}${value && `/tree/${value}`}${treeValue ? `/${treeValue}`:""}`;
let checkvalue = turnbar(value);
let url = `/projects/${owner}/${projectsId}${value && `/tree/${checkvalue}`}${treeValue ? `/${treeValue}`:""}`;
props.history.push(url);
}
//
const fileMenu =(
function fileMenu(){
let b = branchName || defaultBranch;
let checkvalue = turnbar(b);
return (
<Menu>
<Menu.Item><a onClick={()=>urlLink(`/projects/${owner}/${projectsId}/${branchName || (projectDetail && projectDetail.default_branch)}/uploadfile${treeValue === undefined ? "" : `/${treeValue}`}`)}>上传文件</a></Menu.Item>
<Menu.Item><a onClick={()=>urlLink(`/projects/${owner}/${projectsId}/${branchName || (projectDetail && projectDetail.default_branch)}/newfile${treeValue === undefined ? "" : `/${treeValue}`}`)}>新建文件</a></Menu.Item>
<Menu.Item><a onClick={()=>urlLink(`/projects/${owner}/${projectsId}/${checkvalue}/uploadfile${treeValue === undefined ? "" : `/${treeValue}`}`)}>上传文件</a></Menu.Item>
<Menu.Item><a onClick={()=>urlLink(`/projects/${owner}/${projectsId}/${checkvalue}/newfile${treeValue === undefined ? "" : `/${treeValue}`}`)}>新建文件</a></Menu.Item>
</Menu>
)
)
}
function getPathUrl(array,index){
if(array && array.length>0 && index){
@ -166,27 +208,36 @@ function CoderDepot(props){
//
function returnMain(){
setTreeValue(undefined);
let branch = branchName || (projectDetail && projectDetail.default_branch);
props.history.push(`/projects/${owner}/${projectsId}/tree/${branch}`);
let branch = branchName || defaultBranch;
let checkvalue = turnbar(branch);
props.history.push(`/projects/${owner}/${projectsId}/tree/${checkvalue}`);
};
//
function returnUlr(url){
props.history.push(`/projects/${owner}/${projectsId}/tree${branchName?`/${branchName}`:""}/${url}`);
let enBranch = turnbar(branchName);
props.history.push(`/projects/${owner}/${projectsId}/tree${enBranch?`/${enBranch}`:""}/${url}`);
}
//
function goToSubRoot(path,type,filename){
setType(type);
props.history.push(`/projects/${owner}/${projectsId}${`/tree/${branchName || (projectDetail && projectDetail.default_branch)}`}${path?`/${path}`:""}`);
if(type!=="submodule"){
let enBranch = branchName || defaultBranch;
let checkvalue = turnbar(enBranch);
setType(type);
props.history.push(`/projects/${owner}/${projectsId}${`/tree/${checkvalue}`}${path?`/${path}`:""}`);
}
}
function onEdit(readOnly){
setReadOnly(readOnly);
setEditReadme(false);
}
function ChangeFile(path, readOnly){
//
props.history.push(`/projects/${owner}/${projectsId}/tree/${branchName || (projectDetail && projectDetail.default_branch)}/${path}`);
let enBranch = branchName || defaultBranch;
let checkvalue = turnbar(enBranch);
props.history.push(`/projects/${owner}/${projectsId}/tree/${checkvalue}/${path}`);
setType("file");
setReadOnly(readOnly);
setEditReadme(true);
};
function changeHide(hide){
@ -214,20 +265,27 @@ function CoderDepot(props){
</Menu>
</div>
)
function okUpdate(d,w){
// website
function okUpdate(d,w,l){
const url = `/${owner}/${projectsId}.json`;
axios.put(url,{
description:d,website:w
description:d,website:w,lesson_url:l
}).then(result=>{
if(result && result.data && result.data.id){
setDesc(result.data.description);
setWebsite(result.data.website);
setLessonUrl(result.data.lesson_url);
}
})
}
let n = fileInfo && fileInfo.name;
const mdFlag = n && n.substring(n.length-3,n.length) === ".md";
const { current_user } = props;
const fileOperate = type === "dir" && projectDetail && projectDetail.type !== 2 && ((projectDetail.permission && projectDetail.permission !=="Reporter") || (current_user && current_user.admin));
return(
<WhiteBack>
<UpdateDescModal desc={desc} website={website} visible={openModal} onCancel={()=>setOpenModal(false)} onOk={okUpdate}/>
<UpdateDescModal desc={desc} website={website} lesson_url={lesson_url} visible={openModal} onCancel={()=>setOpenModal(false)} onOk={okUpdate}/>
<Spin spinning={isSpin}>
{
(dirInfo || fileInfo) &&
@ -237,7 +295,7 @@ function CoderDepot(props){
owner={owner}
projectsId={projectsId}
name={projectDetail && projectDetail.name}
branch={branchName || (projectDetail && projectDetail.default_branch)}
branch={branchName || defaultBranch}
visible={visible}
onClose={()=>setVisible(false)}
list = {mainFlag ? dirInfo : undefined}
@ -262,14 +320,14 @@ function CoderDepot(props){
<SelectBranch
repo_id={projectDetail && projectDetail.repo_id}
projectsId={projectsId}
branch={branchName || (projectDetail && projectDetail.default_branch)}
branch={branchName || defaultBranch}
changeBranch={changeBranch}
owner={owner}
history={props.history}
branchList={projectDetail && projectDetail.branches && projectDetail.branches.list}
></SelectBranch>
:
<span>分支<span className="color-grey-6">{branchName || (projectDetail && projectDetail.default_branch)}</span></span>
<span>分支<span className="color-grey-6">{branchName || defaultBranch}</span></span>
}
</div>
<AlignCenter className="mr20">
@ -287,15 +345,19 @@ function CoderDepot(props){
</AlignCenter>
<AlignCenter>
<div className="mr20 addOptionBtn">
<a onClick={()=>urlLink(`/projects/${owner}/${projectsId}/pulls/new`)} >+ 合并请求</a>
{
projectDetail.type !== 2 &&
<a onClick={()=>urlLink(`/projects/${owner}/${projectsId}/pulls/new`)} >+ 合并请求</a>
}
<a onClick={()=>urlLink(`/projects/${owner}/${projectsId}/issues/new`)} >+ 任务</a>
</div>
{ type === "dir" &&
<Dropdown overlay={fileMenu} className="mr20">
{ fileOperate &&
<Dropdown overlay={fileMenu()} className="mr20" trigger={['click']}>
<Button type="default">文件 <i className="iconfont icon-sanjiaoxing-down ml3 font-14 color-grey-9"></i></Button>
</Dropdown>
}
<Dropdown overlay={downloadMenu} placement="bottomRight">
<Dropdown overlay={downloadMenu} placement="bottomRight" trigger={['click']}>
<Button type={'primary'}>下载 <i className="iconfont icon-sanjiaoxing-down ml3 font-14 color-white"></i></Button>
</Dropdown>
</AlignCenter>
@ -306,8 +368,10 @@ function CoderDepot(props){
{
lastCommit &&
<div className="listtablehead">
<User url={getImageUrl(`images/${lastCommitAuthor && lastCommitAuthor.image_url}`)} name={lastCommitAuthor && lastCommitAuthor.name} />
<div className={hideBtn && hide ? "ellipsistxt hide" :"ellipsistxt"}><p id="ptxt">{lastCommit && lastCommit.message}</p></div>
<User url={getImageUrl(`/${lastCommitAuthor && lastCommitAuthor.image_url}`)} name={lastCommitAuthor && lastCommitAuthor.name} id={lastCommitAuthor && lastCommitAuthor.id} login={lastCommitAuthor && lastCommitAuthor.login}/>
<div className={hideBtn && hide ? "ellipsistxt hidetxt" :"ellipsistxt"}>
<pre id="ptxt">{lastCommit && lastCommit.message}</pre>
</div>
{ hideBtn && <span className="ellipsis" onClick={()=>changeHide(hide)}><i className="iconfont icon-shenglvehao"></i></span> }
<span className="ml12 color-grey-9 mt3">{lastCommit && lastCommit.time_from_now}</span>
@ -344,8 +408,10 @@ function CoderDepot(props){
{...props}
detail={fileInfo}
readOnly={readOnly}
md={mdFlag}
onEdit={onEdit}
currentBranch={branchName || (projectDetail && projectDetail.default_branch)}
currentBranch={branchName || defaultBranch}
type={projectDetail.type}
></CoderRootFileDetail>
}
</ul>
@ -356,7 +422,7 @@ function CoderDepot(props){
(dirInfo && dirInfo.length === 0) && (fileInfo && fileInfo.length === 0) ? <Nodata _html="暂未发现文件"/> :""
}
{/* readme文件显示(显示文件详情时不显示readme文件) */}
{ dirInfo && (projectDetail && projectDetail.readme) ? <ReadMe ChangeFile={ChangeFile} readme={projectDetail && projectDetail.readme} operate={props && (props.isManager || props.isDeveloper)} history={props.history} /> :"" }
{ dirInfo && (readme && readme.content) ? <ReadMe ChangeFile={ChangeFile} readme={readme} operate={props && (props.isManager || props.isDeveloper) && projectDetail.type !==2 } history={props.history} /> :"" }
</div>
</LongWidth>
{
@ -372,7 +438,7 @@ function CoderDepot(props){
website &&
<p className="color-grey-6 df">
<i className="iconfont icon-lianjie2 font-15 mr10 color-grey-9"></i>
<span style={{wordBreak:"break-all",lineHeight:"20px",marginTop:"5px"}}>{website}</span>
<a href={website} target="_blank" style={{wordBreak:"break-all",lineHeight:"20px",marginTop:"5px",textDecoration:"underline"}}>{website}</a>
</p>
}
<p>
@ -391,6 +457,21 @@ function CoderDepot(props){
</p>
}
</div>
{
inviteCode &&
<div>
<Divider />
<Invite code={inviteCode} className={"detailsCode"}/>
</div>
}
{
lesson_url &&
<div>
<Divider />
<p className="font-16 color-grey-6">实践课程</p>
<a href={lesson_url} target="_blank" className="color-grey-6" style={{textDecoration:"underline"}}>{lesson_url}</a>
</div>
}
{/* 发布 */}
{
projectDetail && projectDetail.release_versions &&
@ -402,10 +483,7 @@ function CoderDepot(props){
{/* 贡献者 */}
{
projectDetail && projectDetail.contributors &&
<React.Fragment>
<Divider />
<Contributors contributors={projectDetail && projectDetail.contributors} owner={owner} projectsId={projectsId}/>
</React.Fragment>
<Contributors contributors={projectDetail && projectDetail.contributors} owner={owner} projectsId={projectsId} />
}
{/* 语言 */}
{ projectDetail && projectDetail.languages &&

View File

@ -2,12 +2,18 @@ import React from 'react';
import { Link } from 'react-router-dom';
import { truncateCommitId } from '../common/util';
const typeIco = {
"submodule":"icon-file-submodule font-17",
"file":'icon-wenjia font-15',
"dir":"icon-wenjianjia1 font-15"
}
function CoderDepotCatalogue({item , goToSubRoot , owner , projectsId }){
return(
<li>
<span>
<a onClick={()=>goToSubRoot(item.path,item.type,item.name)}>
<i className={item.type === 'dir' ? "iconfont icon-wenjianjia1 color-green-file font-15 mr5":"iconfont icon-wenjia color-green-file font-15 mr5"}></i>{item.name}
<a onClick={()=>goToSubRoot(item.path,item.type,item.name)} className={item.type === "submodule" && "submoduleStyle"}>
<i className={`iconfont ${typeIco[`${item.type}`]} color-green-file mr5`}></i>{item.name}
</a>
</span>
<span title="init project">

View File

@ -1,14 +1,63 @@
import React from 'react';
import React, { useEffect, useState } from 'react';
import RenderHtml from '../../components/render-html';
import { AlignCenter } from '../Component/layout';
import { Dropdown , Menu , Spin } from 'antd';
import { Link } from 'react-router-dom';
const $ = window.$;
function CoderDepotReadme({ operate , history , readme , ChangeFile }){
const [ menuList ,setMenuList ] = useState(undefined);
const [ content ,setContent ] = useState(undefined);
useEffect(()=>{
if(readme && readme.content){
setContent(readme.content);
}else{
setContent(undefined);
}
},[readme])
useEffect(()=>{
let path = history.location.pathname;
const items = $.map($("#readme").find("h1,h2,h3,h4,h5,h6"), function (el, _) {
const anchor = el.id;
const level = el.tagName.replace("H", "");
const href = `#${anchor}`;
return { href:`${path}${href}`,text:el.textContent , level:level }
});
setMenuList(items);
},[content])
function menu(){
if(menuList && menuList.length > 0){
let hash = history.location.hash;
return(
<Menu className="menuslist">
{
menuList.map((item,key)=>{
return(
<Menu.Item key={item.id} className={decodeURI(hash).indexOf(item.text)>-1 ?"active":""}><Link to={`${item.href}`} style={{paddingLeft:`${item.level *10}px`}} title={item.text}>{item.text}</Link></Menu.Item>
)
})
}
</Menu>
)
}else{
return <Spin />
}
}
return(
<div className="commonBox" id="readme">
<div className="commonBox-title">
<span className="mr10">
<i className="iconfont icon-wenjian1 font-16 color-grey-9 fl mt3"></i>
</span>
<span className="commonBox-title-read">README.md</span>
<div className="commonBox-title boxTitle">
<AlignCenter>
<Dropdown overlay={menu()}>
<span className="catelogue">
<i className="iconfont icon-zhangjie1 font-14 mr5"></i>
<span>目录</span>
</span>
</Dropdown>
<span className="commonBox-title-read">README.md</span>
</AlignCenter>
{
operate ?
<a className="ml20 pull-right" onClick={() =>ChangeFile(readme && readme.path, false)}>
@ -17,9 +66,12 @@ function CoderDepotReadme({ operate , history , readme , ChangeFile }){
:""
}
</div>
<div className="commonBox-info">
<RenderHtml className="break_word_comments imageLayerParent" value={readme && readme.content} url={history.location}/>
</div>
{
content &&
<div className="commonBox-info">
<RenderHtml className="break_word_comments imageLayerParent" value={content} url={history.location}/>
</div>
}
</div>
)
}

View File

@ -6,6 +6,12 @@ import { getBranch } from '../GetData/getData';
import Nodata from '../Nodata';
import './list.css';
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);
@ -32,7 +38,7 @@ export default ((props)=>{
return(
<li key={key}>
<div>
<Link to={`/projects/${owner}/${projectsId}/tree/${item.name}`} className="color-blue font-15" style={{"maxWidth":"100px"}}>{item.name}</Link>
<Link to={`/projects/${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={`/projects/${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>
@ -40,7 +46,7 @@ export default ((props)=>{
</p>
</div>
<span>
<Link to={`/projects/${owner}/${projectsId}/pulls/new`} className="mr20 color-blue mr30">创建合并请求</Link>
<Link to={`/projects/${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>

View File

@ -2,6 +2,7 @@ import React , { Component } from 'react';
import { Spin , Pagination } from 'antd';
import { getImageUrl } from 'educoder';
import { truncateCommitId } from '../common/util';
import { AlignTop } from '../Component/layout';
import SelectBranch from '../Branch/Select';
import Nodata from '../Nodata';
@ -74,6 +75,7 @@ class CoderRootCommit extends Component{
array.push({
name:item.author && item.author.name,
login: item.author && item.author.login,
id: item.author && item.author.id,
image_url:item.author && item.author.image_url,
sha:item.sha,
time_from_now:item.time_from_now,
@ -131,15 +133,23 @@ class CoderRootCommit extends Component{
commitDatas && commitDatas.length > 0 && commitDatas.map((item,k)=>{
return(
<div key={k}>
<p className="f-wrap-alignCenter">
<Link to={`/projects/${owner}/${projectsId}/commits/${truncateCommitId(`${item.sha}`)}`} className="commitKey" style={{marginLeft:0}}>{truncateCommitId(`${item.sha}`)}</Link>
<Link to={`/projects/${owner}/${projectsId}/commits/${truncateCommitId(`${item.sha}`)}`} className="flex1 ml20 font-16 color-grey-3">{item.message}</Link>
</p>
<AlignTop>
<Link to={`/projects/${owner}/${projectsId}/commits/${truncateCommitId(`${item.sha}`)}`} className="commitKey" style={{marginLeft:0,marginTop:"3px"}}>{truncateCommitId(`${item.sha}`)}</Link>
<Link to={`/projects/${owner}/${projectsId}/commits/${truncateCommitId(`${item.sha}`)}`} className="commitDesc">{item.message}</Link>
</AlignTop>
<p className="f-wrap-alignCenter mt15">
<Link to={`/users/${item.login}`} className="show-user-link">
{item.image_url?<img src={getImageUrl(`images/${item.image_url}`)} alt="" width="28px" height="28px" className="mr15 radius"/>:""}
<label className="font-14 color-grey-6" style={{verticalAlign:'middle'}}>{item.name ?`${item.name}:`:""}提交于 {item.time_from_now}</label>
</Link>
{
item.id ?
<Link to={`/users/${item.login}`} className="show-user-link">
{item.image_url?<img src={getImageUrl(`/${item.image_url}`)} alt="" width="28px" height="28px" className="mr15 radius"/>:""}
<label className="font-14 color-grey-6" style={{verticalAlign:'middle'}}>{item.name ?`${item.name}:`:""}提交于 {item.time_from_now}</label>
</Link>:
<span className="show-user-link">
{item.image_url?<img src={getImageUrl(`/${item.image_url}`)} alt="" width="28px" height="28px" className="mr15 radius"/>:""}
<label className="font-14 color-grey-6" style={{verticalAlign:'middle'}}>{item.name ?`${item.name}:`:""}提交于 {item.time_from_now}</label>
</span>
}
</p>
</div>
)

View File

@ -31,6 +31,18 @@ class CoderRootFileDetail extends Component {
this.languages_total();
};
componentDidUpdate=(prevProps)=>{
const { content } = this.props && this.props.detail;
const prevcontent = prevProps.detail && prevProps.detail.content;
if (content && prevcontent) {
if (prevcontent !== content){
this.setState({
description: content
});
}
}
}
languages_total = () => {
const { detail } = this.props;
const file_name = detail.path.split("/").pop().split(".").pop();
@ -164,9 +176,9 @@ class CoderRootFileDetail extends Component {
current_user,
isManager,
isDeveloper,
md,
currentBranch,
platform
platform,
md
} = this.props;
const { language, languages, description } = this.state;
let flag = current_user && current_user.login && (isManager || isDeveloper);
@ -245,11 +257,7 @@ class CoderRootFileDetail extends Component {
<div>
{detail.image_type ? (
<div className="edu-txt-center pt20 pb20">
<img
alt=""
src={detail.download_url}
style={{ maxWidth: "80%" }}
/>
<img alt="" src={detail.download_url} style={{ maxWidth: "80%" }} />
</div>
) : detail.direct_download ? (
<div className="mt20 text-center">
@ -268,10 +276,11 @@ class CoderRootFileDetail extends Component {
{...this.state}
language={language ? language : "javascript"}
filepath={`/${detail.path}`}
content={detail.content}
content={description}
readOnly={readOnly}
editorType="update"
currentBranch={currentBranch}
descName={detail && `Update ${detail.name}`}
></Meditor>
)}
</div>

View File

@ -26,7 +26,7 @@ export default (( props, { projectDetail }) => {
}, [owner, projectsId]);
return (
<div className="main">
<div className="main" style={{padding:"0px",border:"none"}}>
<Spin spinning={isSpin}>
<div style={{minHeight:"400px"}}>
{

View File

@ -1,7 +1,7 @@
import React, { Component } from 'react';
import { Spin, Tooltip } from 'antd';
import { Spin, Tooltip , Button } from 'antd';
import { Link, Route, Switch } from 'react-router-dom';
import { Content } from '../Component/layout';
import { Content , AlignTop } from '../Component/layout';
import DetailBanner from './sub/DetailBanner';
import '../css/index.scss'
import './list.css';
@ -101,6 +101,14 @@ const Contribute = Loadable({
loader: () => import('./sub/Contribute'),
loading: Loading,
})
const Gantt = Loadable({
loader: () => import('../Gantt/Index'),
loading: Loading,
})
const Sonar = Loadable({
loader: () => import('../Sonar/Index'),
loading: Loading,
})
const CoderRootCommit = Loadable({
loader: () => import('./CoderRootCommit'),
@ -151,6 +159,10 @@ function checkPathname(projectsId,owner,pathname){
name="devops"
}else if(url.indexOf(`/source`)>-1){
name="source"
}else if(url.indexOf(`/gantt`)>-1){
name="gantt"
}else if(url.indexOf(`/sonar`)>-1){
name="sonar"
}
}
return name;
@ -176,6 +188,7 @@ class Detail extends Component {
firstSync:false,
secondSync:false,
open_devops:false,
forkSpin:false,
// 默认分支
defaultBranch:undefined,
@ -358,6 +371,9 @@ class Detail extends Component {
forkFunc = () => {
const { platform } = this.state;
if(!platform)return;
this.setState({
forkSpin:true
})
const { current_user } = this.props
const { projectsId , owner } = this.props.match.params;
const url = `/${owner}/${projectsId}/forks.json`;
@ -366,8 +382,13 @@ class Detail extends Component {
this.props.history.push(`/projects/${current_user && current_user.login}/${result.data.identifier}`);
this.props.showNotification(result.data.message);
}
this.setState({
forkSpin:false
})
}).catch(error => {
console.log(error);
this.setState({
forkSpin:false
})
})
}
@ -389,32 +410,33 @@ class Detail extends Component {
})
}
textFunc = (forked_from_project_id,fork_info)=>{
let type = fork_info && fork_info.fork_project_user_type;
return forked_from_project_id && fork_info ?
<div className="color-grey-9 df">
<span>复刻自</span>
<Link to={`${type ==="Organization" ? "/organize":'/users'}/${fork_info.fork_project_user_login}`} className="show-user-link color-grey-6 ml5">{fork_info.fork_project_user_name}</Link>
<span> / </span>
<Link to={`/projects/${fork_info.fork_project_user_login}/${fork_info.fork_project_identifier}`} className="color-grey-6 task-hide flex1" style={{maxWidth:"400px"}} title={fork_info.fork_form_name}>{fork_info.fork_form_name}</Link>
</div> : ""
}
render() {
const { projectDetail, watchers_count, praises_count,
forked_count, firstSync , secondSync ,
isManager, watched, praised,
project , open_devops , platform , defaultBranch , bannerList } = this.state;
project , open_devops , platform , defaultBranch , bannerList , forkSpin } = this.state;
const url = this.props.history.location.pathname;
const urlArr = url.split("/");
const urlFlag = (urlArr.length === 3);
const { projectsId , owner } = this.props.match.params;
const { current_user } = this.props;
let pathname = checkPathname(projectsId,owner,url);
const { state } = this.props.history.location;
const text = (
projectDetail && projectDetail.forked_from_project_id && projectDetail.fork_info ?
<React.Fragment>
<span>forked from </span>
<Link to={`/users/${projectDetail.fork_info.fork_project_user_login}`} className="show-user-link color-grey-ccc">{projectDetail.fork_info.fork_project_user_name}</Link>
<span> / </span>
<Link to={`/projects/${projectDetail.fork_info.fork_project_user_login}/${projectDetail.fork_info.fork_project_identifier}`} className="color-grey-ccc">{projectDetail.fork_info.fork_form_name}</Link>
</React.Fragment> : ""
);
const common = {
getDetail: this.getDetail,
changeOpenDevops:this.changeOpenDevops,
@ -424,98 +446,95 @@ class Detail extends Component {
<div>
<div className="detailHeader-wrapper">
<div className="normal">
<div className="f-wrap-between pb15" style={{ position: "relative" }}>
<p className="font-22 df flex-1 lineH2 mt15" style={{ alignItems: "center" }}>
{project && project.author &&
<Link to={`${project.author.type ==="Organization" ? "/organize":'/users'}/${project.author.login}`} className="show-user-link">
{project.author.name}
</Link>
}
<span className="ml5 mr5">/</span>
<span className="hide-1 flex-1 df">
<Link to={`/projects/${owner}/${projectsId}`} className="font-22">{project && project.name}</Link>
<AlignTop style={{padding:"20px 0px 10px",justifyContent:"space-between"}}>
<div>
<AlignTop>
<div className="projectallName">
{project && project.author &&
<Link to={`${project.author.type ==="Organization" ? "/organize":'/users'}/${project.author.login}`}>{project.author.name}</Link>
}
<span className="ml5 mr5">/</span>
<Link to={`/projects/${owner}/${projectsId}`} className="projectN mt6">{project && project.name}</Link>
</div>
{ projectDetail && projectDetail.private && <span className="privateTag mt6">私有</span>}
</AlignTop>
<div className="mt8">
{
projectDetail && projectDetail.forked_from_project_id && projectDetail.fork_info ?
<Tooltip placement={'right'} title={text}>
<Link to={`/projects/${projectDetail.fork_info.fork_project_user_login}/${projectDetail.fork_info.fork_project_identifier}`}
className="ml10" >
<i className="iconfont icon-fork font-18 fl mt6" style={{ color: "#8D90E3" }}></i>
</Link>
</Tooltip> : ""
this.textFunc(projectDetail.forked_from_project_id,projectDetail.fork_info)
:""
}
{
projectDetail && projectDetail.type && projectDetail.type !== 0 ?
projectDetail.type === 2 ?
<Tooltip title={"镜像自: " + projectDetail.mirror_url} className="ml5" placement={'right'}>
<i className="iconfont icon-banbenku font-18 mt6" style={{ color: "#8D90E3" }}/>
</Tooltip>
:
<Tooltip title={"镜像自: " + projectDetail.mirror_url} className="ml5" placement={'right'}>
<i className="iconfont icon-jingxiang font-18 color-green mt6" />
</Tooltip>
<span className="color-grey-9">镜像自 <a className="color-grey-6" target="_blank" href={projectDetail.mirror_url}>{projectDetail.mirror_url}</a></span>
:""
}
</span>
</p>
{
firstSync ? "":
<span className="df mt25">
{
projectDetail && projectDetail.type && projectDetail.type === 2 ?
</div>
</div>
<div>
{
firstSync ? "":
<span className="df">
{
((current_user && current_user.admin) || isManager) && (projectDetail && projectDetail.type && projectDetail.type === 2) ?
<a className="synchronism ml30" onClick={this.synchronismMirror}>同步镜像</a> : ""
}
<span className="detail_tag_btn">
<a className="detail_tag_btn_name" style={{cursor:platform?"pointer":"default"}} onClick={() => this.focusFunc(watched)}>
<i className={watched ? "iconfont icon-shixing color-orange font-16 mr3":"iconfont icon-kongxing color-grey-9 font-16 mr3"}></i>
<span>{watched ? '取消关注' : '关注'}</span>
</a>
{
watchers_count > 0 ?
platform ?
<Link className="detail_tag_btn_count" style={{color:`${watched?"#2878FF":"#666"}`}} to={platform?{ pathname: `/projects/${owner}/${projectsId}/watchers`, state }:""}>
{watchers_count}
</Link>
:
<span className="detail_tag_btn_count">{watchers_count}</span>
:""
}
<Button className="detail_tag_btn">
<a className="detail_tag_btn_name" style={{cursor:platform?"pointer":"default"}} onClick={() => this.focusFunc(watched)}>
<i className={watched ? "iconfont icon-shixing color-orange font-16 mr3":"iconfont icon-kongxing color-grey-9 font-16 mr3"}></i>
<span>{watched ? '取消关注' : '关注'}</span>
</a>
{
watchers_count > 0 ?
platform ?
<Link className="detail_tag_btn_count" style={{color:`${watched?"#2878FF":"#666"}`}} to={platform?{ pathname: `/projects/${owner}/${projectsId}/watchers`, state }:""}>
{watchers_count}
</Link>
:
<span className="detail_tag_btn_count">{watchers_count}</span>
:""
}
</Button>
<Button className="detail_tag_btn">
<a className="detail_tag_btn_name" style={{cursor:platform?"pointer":"default"}} onClick={() => this.pariseFunc(praised)}>
<i className={praised ? "iconfont icon-weibiaoti105 color-orange font-14 mr3":"iconfont icon-guanzhu color-grey-9 font-14 mr3"}></i>
<span>{praised ? '取消点赞' : '点赞'}</span>
</a>
{
praises_count > 0 ?
platform ?
<Link className="detail_tag_btn_count" style={{color:`${praised?"#2878FF":"#666"}`}} to={{ pathname: `/projects/${owner}/${projectsId}/stargazers`, state }}>
{praises_count}
</Link>:
<span className="detail_tag_btn_count">{praises_count}</span>
:""
}
</Button>
<Button className="detail_tag_btn" loading={forkSpin}>
<Tooltip title="复刻是fork的中文名即复制代码仓库" placement="bottom">
<a className="detail_tag_btn_name" style={{cursor:platform?"pointer":"default"}} onClick={this.forkFunc}>
<i className="iconfont icon-fork color-grey-9 mr3"></i>
</a>
</Tooltip>
{
forked_count > 0 ?
platform ?
<Link className="detail_tag_btn_count" to={{ pathname: `/projects/${owner}/${projectsId}/fork_users`, state }}>
{forked_count}
</Link>
:
<span className="detail_tag_btn_count">{forked_count}</span>
:""
}
</Button>
</span>
<span className="detail_tag_btn">
<a className="detail_tag_btn_name" style={{cursor:platform?"pointer":"default"}} onClick={() => this.pariseFunc(praised)}>
<i className={praised ? "iconfont icon-weibiaoti105 color-orange font-14 mr3":"iconfont icon-guanzhu color-grey-9 font-14 mr3"}></i>
<span>{praised ? '取消点赞' : '点赞'}</span>
</a>
{
praises_count > 0 ?
platform ?
<Link className="detail_tag_btn_count" style={{color:`${praised?"#2878FF":"#666"}`}} to={{ pathname: `/projects/${owner}/${projectsId}/stargazers`, state }}>
{praises_count}
</Link>:
<span className="detail_tag_btn_count">{praises_count}</span>
:""
}
</span>
<span className="detail_tag_btn">
<a className="detail_tag_btn_name" style={{cursor:platform?"pointer":"default"}} onClick={this.forkFunc}>
<i className="iconfont icon-fork color-grey-9 mr3"></i> (Fork)
</a>
{
forked_count > 0 ?
platform ?
<Link className="detail_tag_btn_count" to={{ pathname: `/projects/${owner}/${projectsId}/fork_users`, state }}>
{forked_count}
</Link>
:
<span className="detail_tag_btn_count">{forked_count}</span>
:""
}
</span>
</span>
}
</div>
}
</div>
</AlignTop>
{
firstSync ? "" :
<DetailBanner
history={this.props.history}
list={bannerList}
owner={owner}
projectsId={projectsId}
@ -635,6 +654,11 @@ class Detail extends Component {
}
></Route>
{/* 新建合并请求 */}
<Route path="/projects/:owner/:projectsId/pulls/new/:branch"
render={
(props) => (<CreateMerge {...this.props} {...props} {...this.state} {...common} is_fork={true} />)
}
></Route>
<Route path="/projects/:owner/:projectsId/pulls/new"
render={
(props) => (<CreateMerge {...this.props} {...props} {...this.state} {...common} is_fork={true} />)
@ -661,6 +685,20 @@ class Detail extends Component {
(props) => (<MergeIndexDetail {...this.props} {...props} {...this.state} {...common} />)
}
></Route>
{/* 甘特图 */}
<Route path="/projects/:owner/:projectsId/gantt"
render={
(props) => (<Gantt {...this.props} {...props} {...this.state} {...common} />)
}
></Route>
{/* 甘特图 */}
<Route path="/projects/:owner/:projectsId/sonar"
render={
(props) => (<Sonar {...this.props} {...props} {...this.state} {...common} />)
}
></Route>
<Route path="/projects/:owner/:projectsId/watchers"
render={
(props) => (<WatchUsers {...this.props} {...props} {...this.state} {...common} />)

View File

@ -51,7 +51,7 @@ export default ({ match , history }) => {
}
}, [projectsId , owner, sha]);
return (
<div className="main">
<div className="main" style={{padding:"0px",border:"none"}}>
<Spin spinning={isSpin}>
<Infos>
<div className="commitinfos">
@ -65,7 +65,7 @@ export default ({ match , history }) => {
<div className="f-wrap-between" style={{ alignItems: "center" }}>
<ul className="df">
<User
url={(committer && getImageUrl(`images/${committer.image_url}`))|| "https://dss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3025493530,1989042357&fm=26&gp=0.jpg"}
url={(committer && getImageUrl(`/${committer.image_url}`))|| "https://dss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3025493530,1989042357&fm=26&gp=0.jpg"}
name={committer && committer.name}
/>
{committer && committer.time_from_now && <li className="ml20 mt2">{committer.time_from_now}</li>}

View File

@ -1,7 +1,7 @@
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { Menu, Input , Spin, Pagination , Popover , Select } from 'antd';
import { getUrl } from 'educoder';
import { Menu, Input , Spin, Pagination , Popover , Select , Button } from 'antd';
import { getImageUrl } from 'educoder';
import '../css/index.scss'
import './list.css';
import './Index.scss';
@ -9,7 +9,8 @@ import ListItem from './IndexItem'
import axios from 'axios';
import img_new from '../Images/new.png';
import img_array from '../Images/array.png';
import banner from '../Images/banner_list.jpg';
import banner from '../Images/banner.png';
import AddProjectModal from '../Head/AddProjectModal';
const Search = Input.Search;
class Index extends Component {
@ -260,9 +261,24 @@ class Index extends Component {
return (
<div>
<p className="t_project_banner">
<img src={banner} width="100%" alt=""/>
</p>
<div className="subjectBanner">
<img src={banner} alt=""/>
<div className="bannerBox">
<div class="subjectleft">
<span>开源社区</span>
<span>面向高校的教学开源</span>
</div>
<Button type={"primary"} size={"large"}>
<Link to={`/projects/deposit/new`}>新建项目</Link>
</Button>
<Button type={"primary"} size={"large"}>
<AddProjectModal showNotification={this.props.showNotification}/>
</Button>
{/* <Button type={"primary"} size={"large"} style={{backgroundColor:"rgb(47, 163, 79)",borderColor:"rgb(47, 163, 79)"}}>
<a href={`https://data.educoder.net/api/attachments/1955244?disposition=inline`} target="_blank">新手指引</a>
</Button> */}
</div>
</div>
{
recommendList && recommendList.length>0 &&
<div className="recommandProjects">
@ -271,7 +287,7 @@ class Index extends Component {
return(
<div onClick={()=>this.getoDetail(item.author && item.author.login,item.identifier)}>
<div className="mainInfo">
<img src={getUrl(`/images/${item.author && item.author.image_url}`)} alt=""/>
<img src={getImageUrl(`/${item.author && item.author.image_url}`)} alt=""/>
<p className="school">{item.name}</p>
<p className="name">{item.author && item.author.name}</p>
</div>

View File

@ -73,7 +73,6 @@
}
}
}
// coderDepot
.Panels{
max-width: 1200px;
@ -129,9 +128,7 @@
height: 7px;
margin-top: 12px;
span{
border-left: 1px solid #fff;
&:first-child{
border-left: none;
border-radius: 10px 0px 0px 10px;
}
&:last-child{
@ -177,27 +174,33 @@
border-radius: 4px 4px 0px 0px;
background-color: #FAFBFC;
.ellipsistxt{
margin-top: 6px;
#ptxt{
margin-bottom: 0px;
word-break: break-all;
overflow: unset;
white-space:pre-wrap; /* css3.0 */
white-space:-moz-pre-wrap; /* Firefox */
white-space:-pre-wrap; /* Opera 4-6 */
white-space:-o-pre-wrap; /* Opera 7 */
word-wrap:break-word;
}
margin-left: 13px;
line-height:18px;
margin-top:6px;
flex:1;
width: 0;
color: #666;
&>p{
word-break:break-all;
}
&.hide{
&.hidetxt{
height: 18px;
overflow: hidden;
position: relative;
padding-right:8px;
}
&.hide::after{
position: absolute;
right: 0px;
bottom: 0px;
content:"...";
&::after{
position: absolute;
right: 0px;
bottom: 0px;
content:"...";
}
}
}
.ellipsis{
@ -220,8 +223,11 @@
.listtablebody{
li.listtablepath{
a{color: #40a9ff;}
p{
margin-bottom: 0px!important;
}
}
li{
& > li{
height: 42px;
display: flex;
justify-content: space-between;
@ -283,4 +289,52 @@
.downMenu{
box-shadow: 0px 0px 9px rgba(134, 134, 134,0.4);
background-color: #fff;
.ant-menu-vertical .ant-menu-item:hover{
background-color: #e6f7ff;
}
}
.menuslist{
max-height: 200px;
overflow-y: auto;
padding:10px 15px;
border-radius: 4px;
.ant-dropdown-menu-item{
border-radius: 8px;
text-align: left!important;
a{
width: 350px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
.ant-dropdown-menu-item.active{
background-color: #e6f7ff;
}
}
.catelogue{
border:1px solid rgb(211, 211, 211);
font-size: 15px;
font-weight: normal;
border-radius: 5px;
margin-right: 10px;
padding:0px 10px;
height: 30px;
line-height: 30px;
color: #666!important;
display: flex;
align-items: center;
span{
margin-top: 1px;
}
}
.submoduleStyle{
cursor: default;
i{
cursor: default;
}
&:hover{
color: #05101a;
}
}

View File

@ -1,6 +1,7 @@
import React, { Component } from 'react';
import { Tooltip } from 'antd';
import { getImageUrl } from 'educoder';
import { AlignCenter } from '../Component/layout';
import { Link } from 'react-router-dom';
import '../css/index.scss';
import Nodata from '../Nodata';
@ -17,7 +18,7 @@ class IndexItem extends Component {
render() {
const { projects } = this.props;
return (
<div className="project-list minH-670">
<div className="project-list minH-670" style={{padding:"0px 20px"}}>
{ projects && projects.length > 0 ? projects.map((item, key) => {
return (
<div className="p-r-Item" key={key}>
@ -27,14 +28,17 @@ class IndexItem extends Component {
<img className="p-r-photo" alt="" src={item.author && item.author.image_url} ></img>
</a>
:
<Link to={`/users/${item.author.login}`} className="show-user-link">
<img className="p-r-photo" alt="" src={getImageUrl(`${item.author && item.author.image_url}`)} ></img>
<Link to={item.author && (item.author.type === "Organization" ? `/organize/${item.author.login}`:`/users/${item.author.login}`)} className="show-user-link">
<img className="p-r-photo" alt="" src={getImageUrl(`/${item.author && item.author.image_url}`)} ></img>
</Link>
}
<div className="p-r-Infos">
<div className="p-r-name">
<Link to={`/projects/${item.author.login}/${item.identifier}`} className="hide-1 color-grey-3 font-18 task-hide " style={{ whiteSpace: "wrap", display: 'flex', width: 400 }}>
{item.author.name}/{item.name}
<AlignCenter>
<Link to={`/projects/${item.author.login}/${item.identifier}`} title={`${item.author.name}/${item.name}`} className="color-grey-3 font-18 task-hide " style={{maxWidth: 470 }}>
{item.author.name}/{item.name}
</Link>
{ !item.is_public && <span className="privateTag">私有</span> }
{
item.forked_from_project_id ?
<span className="ml5">
@ -52,10 +56,20 @@ class IndexItem extends Component {
<i className="iconfont icon-jingxiang font-18 color-green" />
</span>:""
}
</Link>
</AlignCenter>
<span className="p-r-tags">
<span className="pariseTag"><img src={img_parise} alt="" className="pariseImg" /> {item.praises_count}</span>
<span><i className="iconfont icon-fork mr3 font-16" style={{ color: "#1B8FFF" }} />fork {item.forked_count}</span>
{
item.praises_count && item.praises_count>0 ?
<span className="pariseTag">
<img src={img_parise} alt="" className="pariseImg" /> {item.praises_count}
</span>:""
}
{
item.forked_count && item.forked_count>0 ?
<span>
<i className="iconfont icon-fork mr3 font-16" style={{ color: "#1B8FFF" }} />fork {item.forked_count}
</span>:""
}
</span>
</div>
<p className="break_word task-hide-2 mt10" style={{ maxHeight: "44px",lineHeight:"22px" }}>{item.description}</p>

View File

@ -1,10 +1,51 @@
.lineH2{line-height:2}
.t_project_banner {
/* height: 260px;
background: url(../Images/banner_list.jpg) no-repeat center; */
.subjectBanner {
height: 160px;
position: relative;
overflow: hidden;
background-color: #050d34;
}
.subjectleft{
margin-top: 40px;
display: -ms-flexbox;
display: flex;
-ms-flex-align: center;
align-items: center;
}
.bannerBox{
width: 1200px;
position: absolute;
z-index: 2;
left: 50%;
top: 0;
transform: translateX(-50%);
}
.subjectleft span:first-child{
width: 140px;
height: 30px;
font-size: 30px;
font-weight: 600;
color: #fff;
line-height: 30px;
letter-spacing: 4px;
}
.subjectleft span:last-child{
height: 16px;
font-size: 16px;
color: #fff;
line-height: 16px;
letter-spacing: 4px;
margin-left: 16px;
}
.bannerBox .ant-btn{
margin-top: 30px;
height: 40px;
font-size: 16px;
width: 120px;
border-radius: 4px;
margin-right: 40px;
}
.ProjectListIndex{
width: 1200px;
margin:20px auto;
@ -91,6 +132,7 @@
display: flex;
border-bottom:1px solid rgba(238,238,238,1);
padding:22px 0px;
justify-content: flex-start;
}
.boxShandow{
box-shadow:0px 2px 20px 10px rgba(0,0,0,0.03);
@ -100,6 +142,7 @@
height: 60px;
border-radius: 50%;
margin-right: 22px;
margin-top: 8px;
}
.p-r-Infos{
flex: 1;
@ -108,6 +151,7 @@
.p-r-name{
display: flex;
justify-content: space-between;
align-items: center;
}
.p-r-name > p{
flex: 1;
@ -261,7 +305,10 @@
border:1px solid #f1f1f1;
display: flex;
align-items: center;
margin-left: 30px
margin-left: 30px;
padding:0px;
background-color: transparent;
box-shadow: none;
}
.ant-tooltip {
max-width: fit-content!important;
@ -281,7 +328,6 @@
height:100%;
}
.files-md{
border:1px solid #eee;
padding:20px;
}
/* 详情-代码 */
@ -437,9 +483,6 @@
font-size: 16px;
border-bottom: 1px solid #d9d9d9;
}
.branchUl{
padding:0px 30px;
}
.branchUl li{
display: flex;
flex-wrap: wrap;
@ -565,6 +608,10 @@
border-bottom: 1px solid #d9d9d9;
border-radius: 4px 4px 0px 0px;
}
.commonBox .commonBox-title.boxTitle{
display: flex;
justify-content: space-between;
}
.synchronism{
display: block;
height: 26px;

View File

@ -50,7 +50,7 @@ function Contribute(props){
list.map((item,key)=>{
return(
<AlignCenter>
<img alt="" style={{borderRadius:"50%",marginRight:"10px"}} src={getImageUrl(`images/${item.image_url}`)} width="50px" height="50px"/>
<img alt="" style={{borderRadius:"50%",marginRight:"10px"}} src={getImageUrl(`/${item.image_url}`)} width="50px" height="50px"/>
<div>
<Link to={`/users/${item.login}`} className="font-16">{item.name}</Link>
<p className="font-12 color-grey-9">提交{item.contributions}</p>

View File

@ -1,18 +1,25 @@
import React, { useEffect, useState } from 'react';
import { Skeleton } from 'antd';
import { Skeleton , Tooltip } from 'antd';
import { Link } from 'react-router-dom';
function DetailBanner({ list , owner , projectsId , isManager , url , pathname , state , urlFlag , projectDetail , platform ,open_devops }){
function DetailBanner({ history,list , owner , projectsId , isManager , url , pathname , state , urlFlag , projectDetail , platform ,open_devops }){
const [ menuName , setMenuName ] = useState(undefined);
useEffect(()=>{
if(list){
// banner
if(pathname && pathname==="source"){
let a = list.filter(item=>item.menu_name === "resources");
if(a && a.length === 0){
history.push(`/projects/${owner}/${projectsId}`);
}
}
setMenuName(list);
}
},[list]);
return(
<div className="f-wrap-between mt15">
{
menuName && projectDetail ?
menuName && menuName.length> 0 && projectDetail ?
<ul className="headerMenu-wrapper">
{
menuName.map((item,key)=>{
@ -32,18 +39,20 @@ function DetailBanner({ list , owner , projectsId , isManager , url , pathname ,
<li className={(pathname==="" || urlFlag) ? "active" : ""}>
<Link to={{ pathname: `/projects/${owner}/${projectsId}`, state }}>
<i className={(pathname==="" || urlFlag) ? "iconfont icon-daimaku color-grey-3 mr5 font-14":"iconfont icon-daimaku color-grey-6 font-14 mr5"}></i>
<span>代码</span>
<span>代码</span>
</Link>
</li>
}
{
item.menu_name === "issues" &&
<li className={pathname==="issues" ? "active" : ""}>
<Link to={{ pathname: `/projects/${owner}/${projectsId}/issues`, state }}>
<i className={pathname==="issues" ? "iconfont icon-renwu color-grey-3 mr5 font-14":"iconfont icon-renwu color-grey-6 font-14 mr5"}></i>
<span>易修 (Issue)</span>
{projectDetail && projectDetail.issues_count ? <span className="num">{projectDetail.issues_count}</span> : ""}
</Link>
<Tooltip title="易修是Issue的中文名即问题列表" placement="bottom">
<Link to={{ pathname: `/projects/${owner}/${projectsId}/issues`, state }}>
<i className={pathname==="issues" ? "iconfont icon-renwu color-grey-3 mr5 font-14":"iconfont icon-renwu color-grey-6 font-14 mr5"}></i>
<span>易修</span>
{projectDetail && projectDetail.issues_count ? <span className="num">{projectDetail.issues_count}</span> : ""}
</Link>
</Tooltip>
</li>
}
{
@ -60,14 +69,14 @@ function DetailBanner({ list , owner , projectsId , isManager , url , pathname ,
item.menu_name === "devops" && platform ?
<li className={pathname==="devops" ? "active" : ""}>
<Link to={{ pathname: `/projects/${owner}/${projectsId}/devops${open_devops ? `/dispose`:""}`, state }}>
<i className="iconfont icon-gongzuoliu font-13 mr8"></i>工作流(beta版)
<i className="iconfont icon-gongzuoliu font-13 mr8"></i>工作流
{projectDetail && projectDetail.ops_count ? <span>{projectDetail.ops_count}</span> : ""}
</Link>
</li>
:""
}
{
item.menu_name === "source" &&
{/* {
item.menu_name === "resources" &&
<li className={pathname==="source" ? "active" : ""}>
<Link to={{ pathname: `/projects/${owner}/${projectsId}/source`, state }}>
<i className={pathname==="source" ? "iconfont icon-ziyuanpaihanghetuijian color-grey-3 mr5 font-14":"iconfont icon-ziyuanpaihanghetuijian color-grey-6 font-14 mr5"}></i>
@ -75,7 +84,7 @@ function DetailBanner({ list , owner , projectsId , isManager , url , pathname ,
{projectDetail && projectDetail.source_count ? <span className="num">{projectDetail.source_count}</span> :""}
</Link>
</li>
}
} */}
{
item.menu_name === "versions" &&
<li className={pathname==="milestones" ? "active" : ""}>
@ -95,19 +104,37 @@ function DetailBanner({ list , owner , projectsId , isManager , url , pathname ,
</Link>
</li>
}
{
item.menu_name === "setting" &&
<li className={pathname === "setting" ? "active" : ""}>
<Link to={`/projects/${owner}/${projectsId}/setting`}>
<i className={url && url.indexOf("/setting") > 0 ? "iconfont icon-cangku color-grey-3 mr5 font-14":"iconfont icon-cangku color-grey-6 font-14 mr5"}></i>
<span>设置</span>
</Link>
</li>
}
{
item.menu_name === "gantt" &&
<li className={pathname === "gantt" ? "active" : ""}>
<Link to={`/projects/${owner}/${projectsId}/gantt`}>
<i className={url && url.indexOf("/gantt") > 0 ? "iconfont icon-zuzhi color-grey-3 mr5 font-14":"iconfont icon-zuzhi color-grey-6 font-14 mr5"}></i>
<span>甘特图</span>
</Link>
</li>
}
{
item.menu_name === "sonar" &&
<li className={pathname === "sonar" ? "active" : ""}>
<Link to={`/projects/${owner}/${projectsId}/sonar`}>
<i className={url && url.indexOf("/sonar") > 0 ? "iconfont icon-tijiao color-grey-3 mr5 font-18":"iconfont icon-tijiao color-grey-6 font-18 mr5"}></i>
<span>质量分析/检测</span>
</Link>
</li>
}
</React.Fragment>
)
})
}
{
isManager && platform ?
<li className={pathname === "setting" ? "active" : ""}>
<Link to={`/projects/${owner}/${projectsId}/setting`}>
<i className={url && url.indexOf("/setting") > 0 ? "iconfont icon-cangku color-grey-3 mr5 font-14":"iconfont icon-cangku color-grey-6 font-14 mr5"}></i>
<span>仓库设置</span>
</Link>
</li>:""
}
</ul>
:
<Skeleton paragraph={false} active={true}/>

View File

@ -0,0 +1,28 @@
import React from 'react';
import { Tooltip , message } from 'antd';
import './sub.scss';
function Invite({code,className}) {
function jsCopy(id) {
const copyEle = document.querySelector(id); //
const range = document.createRange(); // range
window.getSelection().removeAllRanges(); //selection
range.selectNode(copyEle); //
window.getSelection().addRange(range); //
document.execCommand("Copy"); // copy
message.success('复制成功');
}
return(
<div className={className}>
<span className="font-16 color-grey-6">邀请码</span>
<div>
<span id="devitecode">{code}</span>
<Tooltip title={<p className="edu-txt-center">可以通过邀请码邀请成员加入项目<br/>点击复制邀请码</p>} placement={"bottom"}>
<i className="iconfont icon-fuzhi2 font-16 color-blue ml8" onClick={()=>jsCopy("#devitecode")}></i>
</Tooltip>
</div>
</div>
)
}
export default Invite;

View File

@ -2,13 +2,13 @@ import React , { forwardRef, useEffect } from 'react';
import {Form , Modal , Input } from 'antd';
import "./sub.scss";
const { TextArea } = Input;
function UpdateDescModal({form , visible , onCancel , onOk,desc,website}){
function UpdateDescModal({form , visible , onCancel , onOk,desc,website,lesson_url}){
const { getFieldDecorator, validateFields , setFieldsValue } = form;
useEffect(()=>{
if(desc || website){
setFieldsValue({
website,desc
website,desc,lesson_url
})
}
},[desc,website])
@ -17,7 +17,7 @@ function UpdateDescModal({form , visible , onCancel , onOk,desc,website}){
validateFields((err,values)=>{
if(!err){
onCancel();
onOk(values.desc,values.website)
onOk(values.desc,values.website,values.lesson_url)
}
})
}
@ -35,11 +35,11 @@ function UpdateDescModal({form , visible , onCancel , onOk,desc,website}){
className={"descmodal"}
>
<Form>
<Form.Item label="仓库描述">
<Form.Item label="项目简介">
{getFieldDecorator("desc",{
rules:[]
})(
<TextArea placeholder="仓库描述" rows={4} maxLength={200}/>
<TextArea placeholder="请输入项目简介" rows={4} maxLength={200}/>
)}
</Form.Item>
<Form.Item label="website">
@ -49,6 +49,13 @@ function UpdateDescModal({form , visible , onCancel , onOk,desc,website}){
<Input placeholder="website链接"/>
)}
</Form.Item>
<Form.Item label="实践课程">
{getFieldDecorator("lesson_url",{
rules:[]
})(
<Input placeholder="实践课程链接"/>
)}
</Form.Item>
</Form>
</Modal>
)

View File

@ -20,4 +20,9 @@
height: 20px;
line-height: 20px;
}
}
.detailsCode{
display: flex;
justify-content: space-between;
}

View File

@ -22,7 +22,7 @@ function Commits({ commits , projectsId , owner }){
<Link to={`/projects/${owner}/${projectsId}/commits/${truncateCommitId(item.sha)}`} className="color-blue">浏览代码</Link>
</FlexAJ>
<AlignCenter className="mt15">
<User url={getImageUrl(`images/${item.committer && item.committer.image_url}`)} name={`${item.committer && item.committer.name}`}></User><span>提交于{item.time_from_now}</span>
<User url={getImageUrl(`/${item.committer && item.committer.image_url}`)} name={`${item.committer && item.committer.name}`}></User><span>提交于{item.time_from_now}</span>
</AlignCenter>
</div>
</div>

View File

@ -0,0 +1,21 @@
.i_open{
color: #28BD6C!important;
}
.i_merged{
color: #4C9ED3!important;
}
.i_closed{
color: #FA6400!important;
}
.pr_tags_open{
border:1px solid #28BD6C;
color: #28BD6C;
}
.pr_tags_merged{
border:1px solid #4C9ED3;
color: #4C9ED3;
}
.pr_tags_closed{
border:1px solid #FA6400;
color: #FA6400;
}

View File

@ -1,9 +1,16 @@
import React, { Component } from "react";
import { Link } from "react-router-dom";
import { Popconfirm, Tag } from "antd";
import { Tag } from "antd";
import { AlignCenter } from '../Component/layout';
import { getImageUrl } from "educoder";
import "./merge.css";
function turnbar(str){
if(str && str.length>0 && str.indexOf("/")>-1){
return str.replaceAll('/','%2F');
}
return str;
}
class MergeItem extends Component {
constructor(props) {
super(props);
@ -42,16 +49,18 @@ class MergeItem extends Component {
};
render() {
const { issues, project_name, project_author_name } = this.props;
const { issues, project_name, project_author_name , user_admin_or_member} = this.props;
const { projectsId , owner } = this.props.match.params;
const { current_user } = this.props;
const renderList = () => {
if (issues && issues.length > 0) {
return issues.map((item, key) => {
let status = item.pull_request_staus;
return (
<div className="issueItem">
<div className="flex-1">
<p className="mb15 df" style={{ alignItems: "center" }}>
<i className={`iconfont icon-hebingqingqiu1 font-14 mr3 i_${status}`}></i>
<Link
to={`/projects/${owner}/${projectsId}/pulls/${item.pull_request_id}/Messagecount`}
className="hide-1 font-15 color-grey-3 fwb lineh-30 mr10"
@ -59,10 +68,10 @@ class MergeItem extends Component {
>
{item.name}
</Link>
<Tag className={`pr_tags_${item.pull_request_staus}`}>
{item.pull_request_staus === "merged"
<Tag className={`pr_tags_${status}`}>
{status === "merged"
? "已合并"
: item.pull_request_staus === "closed"
: status === "closed"
? "已拒绝"
: "开启的"}
</Tag>
@ -74,13 +83,13 @@ class MergeItem extends Component {
>
<img
className="radius"
src={getImageUrl(`images/${item && item.avatar_url}`)}
src={getImageUrl(`/${item && item.avatar_url}`)}
alt=""
width="24"
height="24"
/>
</Link>
<span>
<AlignCenter>
<Link
to={`/users/${item && item.author_login}`}
className="show-user-link color-grey-8 ml5"
@ -96,35 +105,45 @@ class MergeItem extends Component {
</span>
<span className="color-grey-8">{item.pr_time}</span>
<span className="ml15">
<Tag className="pr-branch-tag">
<Link
to={`/projects/${item.is_original ? item.fork_project_user : owner}/${ item.is_original ? item.fork_project_identifier : projectsId }/tree/${item.pull_request_head}`}
className="maxW200px hide-1 ver-middle"
>
{item.is_original
? item.fork_project_user
: project_author_name}
:{item.pull_request_head}
</Link>
</Tag>
<span className="mr8 ver-middle">
<i
className={
"iconfont icon-youjiang color-grey-c font-16"
}
></i>
</span>
<Tag className="pr-branch-tag">
<Link
to={`/projects/${owner}/${projectsId}/tree/${item.pull_request_base}`}
className="maxW200px hide-1 ver-middle"
>
{/* {item.is_fork ? item.pull_request_base : `${item.author_name}:${item.pull_request_base}`} */}
{project_author_name}:{item.pull_request_base}
</Link>
</Tag>
{
item.pull_request_head &&
<Tag className="pr-branch-tag">
<Link
to={`/projects/${item.is_original ? item.fork_project_user : owner}/${ item.is_original ? item.fork_project_identifier : projectsId }/tree/${turnbar(item.pull_request_head)}`}
className="maxW200px hide-1 ver-middle"
>
{item.is_original
? item.fork_project_user
: project_author_name}
:{item.pull_request_head}
</Link>
</Tag>
}
{
item.pull_request_base &&
<span className="mr8 ver-middle">
<i
className={
"iconfont icon-youjiang color-grey-c font-16"
}
></i>
</span>
}
{
item.pull_request_base &&
<Tag className="pr-branch-tag">
<Link
to={`/projects/${owner}/${projectsId}/tree/${turnbar(item.pull_request_base)}`}
className="maxW200px hide-1 ver-middle"
>
{/* {item.is_fork ? item.pull_request_base : `${item.author_name}:${item.pull_request_base}`} */}
{project_author_name}:{item.pull_request_base}
</Link>
</Tag>
}
</span>
</span>
</AlignCenter>
</p>
</div>
<ul
@ -164,7 +183,7 @@ class MergeItem extends Component {
) : (
""
)}
{current_user && current_user.login ? (
{user_admin_or_member ? (
<div
className="milepostleft"
style={{
@ -177,7 +196,7 @@ class MergeItem extends Component {
>
<div className="grid-item mr15 color-grey-9">
<Link
to={`/projects/${owner}/${projectsId}/merge/${item.pull_request_id}/updatemerge`}
to={`/projects/${owner}/${projectsId}/pulls/${item.pull_request_id}/updatemerge`}
className="color-grey-9"
>
<i className="iconfont icon-bianji3 font-14 mr5"></i>

View File

@ -101,7 +101,7 @@ class MergeSubmit extends Component{
render: (text,item) => (
<span className="f-wrap-alignCenter">
<Link to={`/users/${item.login}`} className="show-user-link">
<img src={getImageUrl(`images/${item.image_url}`)} alt="" width="28px" height="28px" className="mr3 radius"/>
<img src={getImageUrl(`/${item.image_url}`)} alt="" width="28px" height="28px" className="mr3 radius"/>
<label className="hide-1" style={{maxWidth:"75px",'vertical-align':'middle'}}>{text}</label>
</Link>
</span>

View File

@ -24,6 +24,12 @@ import MergeFooter from "./merge_footer";
const Option = Select.Option;
const TextArea = Input.TextArea;
function turnbar(str){
if(str && str.length>0 && str.indexOf("/")>-1){
return str.replaceAll('/','%2F');
}
return str;
}
class MessageCount extends Component {
constructor(props) {
super(props);
@ -41,6 +47,7 @@ class MessageCount extends Component {
edit_spin: false,
pr_status: undefined,
pull_request:undefined,
conflict_files:[],
copyVisible:false,
};
@ -75,7 +82,8 @@ class MessageCount extends Component {
data: result.data,
SpinFlag: false,
pr_status: result.data.pull_request && result.data.pull_request.status,
pull_request:result.data.pull_request
pull_request:result.data.pull_request,
conflict_files:result.data.conflict_files
});
} else {
this.setState({ SpinFlag: false });
@ -102,6 +110,8 @@ class MessageCount extends Component {
isSpin: false,
pr_status: 2,
});
const { getDetail } = this.props;
getDetail && getDetail();
} else {
this.setState({
isSpin: false,
@ -138,6 +148,8 @@ class MessageCount extends Component {
SpinMerge: false,
pr_status: 1,
});
const { getDetail } = this.props;
getDetail && getDetail();
} else {
this.setState({ SpinMerge: false });
}
@ -241,6 +253,49 @@ class MessageCount extends Component {
)
}
// 点击按钮复制功能
jsCopy = () => {
const copyEle = document.querySelector('#descContent') // 获取要复制的节点
const range = document.createRange(); // 创造range
window.getSelection().removeAllRanges(); //清除页面中已有的selection
range.selectNode(copyEle); // 选中需要复制的节点
window.getSelection().addRange(range); // 执行选中元素
document.execCommand("Copy"); // 执行copy操作
}
mergeabledMes=()=>{
return(
<div className="clearfix">
<p className="fl">该分支存在冲突无法自动合并你可以尝试通过如下命令手动合并</p>
<i className="iconfont icon-fuzhi font-16 fr" onClick={()=>this.jsCopy()}></i>
</div>
)
}
mergeabledDesc=(base,head,conflict_files)=>{
return(
<div>
<ul id="descContent">
<li>git fetch origin</li>
<li>git checkout -b {`${base}`} origin/{`${base}`}</li>
<li>git merge {`${head}`}</li>
</ul>
{
conflict_files && conflict_files.length>0 &&
<div>
<p className="mt10 font-16 pt10" style={{borderTop:"1px solid #f9d7d5"}}>如下文件有代码冲突</p>
<p>
{
conflict_files.map((i,k)=>{
return <p>{i}</p>
})
}
</p>
</div>
}
</div>
)
}
render() {
const { projectsId, mergeId , owner } = this.props.match.params;
@ -252,7 +307,8 @@ class MessageCount extends Component {
ismesrge,
SpinFlag,
copyVisible,
pull_request
pull_request,
conflict_files
} = this.state;
const { current_user, projectDetail } = this.props;
const menu = (
@ -305,10 +361,10 @@ class MessageCount extends Component {
<div className="mt15">
<Tag className="pr-branch-tag">
<Link
to={`/projects/${owner}/${data.pull_request.is_original?data.project_identifier:projectsId}/tree/${data.pull_request.head}`}
to={`/projects/${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)}`}
className="ver-middle"
>
{data.pull_request.is_original ? data.pull_request.fork_project_user : data.issue.project_author_name}:{data.pull_request.head}
{data.pull_request.is_original ? data.pull_request.fork_project_user : data.issue.project_author_name}: {turnbar(data.pull_request && data.pull_request.head)}
</Link>
</Tag>
<span className="mr8 ver-middle">
@ -323,7 +379,6 @@ class MessageCount extends Component {
to={`/projects/${owner}/${projectsId}/tree/${data.pull_request.base}`}
className="ver-middle"
>
{/* {data.pull_request.is_fork ? data.pull_request.base : `${data.pull_request.pull_request_user}:${data.pull_request.base}`} */}
{data.issue.project_author_name}:{data.pull_request.base}
</Link>
</Tag>
@ -331,26 +386,14 @@ class MessageCount extends Component {
}
<div className="mt15">
<Link
to={`/users/${data.issue.author_login}`}
className="show-user-link"
>
<img
className="mr5"
src={getImageUrl(
`images/${data.issue.author_picture}`
)}
alt=""
width="24"
height="24"
<Link to={`/users/${data.issue.author_login}`} className="show-user-link">
<img className="mr5" src={getImageUrl(`/${data.issue.author_picture}`)}
alt="" width="24" height="24" style={{borderRadius:"50%"}}
/>
</Link>
<span className="ver-middle">
<span className="color-grey-8 mr5"></span>
<Link
to={`/users/${data.issue.author_login}`}
className="show-user-link color-blue"
>
<Link to={`/users/${data.issue.author_login}`} className="show-user-link color-blue">
{data.issue.author_name}
</Link>
<span className="ml5 color-grey-8">
@ -400,12 +443,6 @@ class MessageCount extends Component {
</div>
<div className="ml10">
<div className="mt15 text-right" style={{display:"flex",justifyContent:"flex-end"}}>
{/* <span className="composeButton">
<Dropdown overlay={this.copyItem()} visible={copyVisible} onClick={(e)=>this.setCopyVisible(e)}>
<span>复制</span>
</Dropdown>
<span>下载为<i className="iconfont icon-sanjiaoxing-down color-blue"></i></span>
</span> */}
{operate && (
<Button
type="green"
@ -452,35 +489,40 @@ class MessageCount extends Component {
type="success"
/>
)}
{pr_status === 0 && projectDetail && projectDetail.permission !=="Reporter" && (
{operate && (
<Spin spinning={SpinFlag}>
<div
style={{
display:
this.state.mergekey === "rebase"
? this.state.buttonshow === "none"
? "block"
: "none"
: !ismesrge
? "block"
: "none",
? this.state.buttonshow === "none" ? "block" : "none"
: !ismesrge ? "block" : "none",
}}
>
<p className="mb15">
<Dropdown.Button
overlay={menu}
type="primary"
onClick={this.submitmerge}
icon={<Icon type="caret-down" />}
>
{this.state.mergename}
</Dropdown.Button>
</p>
<Alert
message="该合并请求可以进行自动合并操作"
type="success"
showIcon
/>
<Dropdown.Button
overlay={menu}
type="primary"
onClick={this.submitmerge}
className="mb15"
icon={<Icon type="caret-down" />}
disabled={!pull_request || (pull_request && !pull_request.mergeable) }
>
{this.state.mergename}
</Dropdown.Button>
{pull_request && pull_request.mergeable
?
<Alert
message="该合并请求可以进行自动合并操作"
type="success"
showIcon
/>:
<Alert
message={this.mergeabledMes()}
type="error"
description={this.mergeabledDesc(pull_request.base,pull_request.head,conflict_files)}
showIcon
/>
}
</div>
<div>
<div

View File

@ -9,13 +9,14 @@ const Option = Select.Option;
class NewMerge extends Component {
constructor(props) {
super(props);
const { branch } = this.props.match.params;
this.state = {
data: undefined,
branches: undefined,
merge_branches: undefined,
merge_projects: undefined,
merge: "master",
pull: "master",
pull: branch,
id: undefined,
is_fork: false,
projects_names: undefined,
@ -104,6 +105,18 @@ class NewMerge extends Component {
}
axios.get(url).then(result=>{
if(result){
if (result.data.status === 0) {
this.setState({
isSpin: false,
show_message: false,
});
} else {
this.setState({
isSpin: false,
show_message: true,
default_message: result.data.message,
});
}
this.setState({
comparesData:result.data
})
@ -113,16 +126,19 @@ class NewMerge extends Component {
}
set_default_pull = (branches) => {
if(branches && branches.length>0){
let default_pull = branches.filter((e) => e.name === "master")
if (default_pull.length > 0){
this.setState({
pull:default_pull[0].name
})
}else{
this.setState({
pull:"master"
})
const { branch } = this.props.match.params;
if(!branch){
if(branches && branches.length>0){
let default_pull = branches.filter((e) => e.name === "master")
if (default_pull.length > 0){
this.setState({
pull:default_pull[0].name
})
}else{
this.setState({
pull:"master"
})
}
}
}
}
@ -139,7 +155,7 @@ class NewMerge extends Component {
merge:"master"
})
}
this.ischeckmerge();
// this.ischeckmerge();
}
}
@ -163,10 +179,12 @@ class NewMerge extends Component {
};
selectBrach = (type, value) => {
const { projectsId , owner } = this.props.match.params;
this.state[type] = value;
this.ischeckmerge();
// this.ischeckmerge();
let { id ,merge , pull } = this.state;
if(type==="pull"){
this.props.history.push(`/projects/${owner}/${projectsId}/pulls/new/${pull}`)
this.compareProject(id,value,merge);
}else{
this.compareProject(id,pull,value);
@ -178,7 +196,7 @@ class NewMerge extends Component {
let arr = projects_names && projects_names.filter(item=>item.id===value);
let identifier = arr && arr[0].project_id;
let login = arr && arr[0].project_user_login;
let is_fork_id = parseInt(value) !== parseInt(id)
let is_fork_id = parseInt(value) !== parseInt(id);
this.setState({
isSpin: true,
merge_head: is_fork_id,
@ -193,7 +211,6 @@ class NewMerge extends Component {
};
//判断2分支是否可以合并
ischeckmerge = () => {
this.setState({ isSpin: true });
const { projectsId , owner } = this.props.match.params;

View File

@ -168,6 +168,7 @@ form .ant-cascader-picker, form .ant-select {
}
.linesContent > p{
flex:1;
word-break: break-all;
}
.linesContent .lines{
display: flex;

View File

@ -5,6 +5,7 @@ import "../Order/order.css";
import "../Order/index.scss";
import NoneData from "./no_data";
import OrderItem from "./MergeItem";
import './Index.scss';
import axios from "axios";
@ -37,7 +38,7 @@ class merge extends Component {
// page: 1,
search_count: undefined,
issue_type: undefined,
status_type: undefined,
status_type: "1",
//设置选择高亮
openselect: 1,
closeselect: undefined,
@ -47,7 +48,7 @@ class merge extends Component {
paix: "排序",
priority_ids: "优先级",
select_params: {
status_type: undefined, //开启中和关闭中,默认为开启中的
status_type: "1", //开启中和关闭中,默认为开启中的
assigned_to_id: undefined, // 指派人
fixed_version_id: undefined,
priority_id: undefined,
@ -154,7 +155,7 @@ class merge extends Component {
renderMenu = (array, name, id) => {
return (
<Menu>
<Menu className="orderCondition">
<Menu.Item key={"all"} onClick={(e) => this.getOption(e, id, name)}>
{name}
</Menu.Item>
@ -206,23 +207,16 @@ class merge extends Component {
paix: "排序",
priority_ids: "优先级",
});
this.state.select_params = {
status_type: type,
search: undefined,
page: 1,
limit: 15,
};
this.state.select_params.status_type = type;
this.state.select_params.page=1;
this.state.select_params.limit=15;
this.getIssueList();
};
islogin() {
checkOperation() {
const { projectsId,owner } = this.props.match.params;
if (this.props.checkIfLogin() === false) {
this.props.showLoginDialog();
return;
} else {
this.props.history.push(`/projects/${owner}/${projectsId}/pulls/new`);
}
this.props.history.push(`/projects/${owner}/${projectsId}/pulls/new`);
}
render() {
const { projectsId , owner } = this.props.match.params;
@ -253,24 +247,6 @@ class merge extends Component {
</Menu.Item>
</Menu>
);
const Paginations = (
<React.Fragment>
{search_count > limit ? (
<div className="mt30 mb50 edu-txt-center">
<Pagination
simple
defaultCurrent={page}
total={search_count}
pageSize={limit}
onChange={this.ChangePage}
></Pagination>
</div>
) : (
""
)}
</React.Fragment>
);
return (
<div className="main" style={{padding:"0px"}}>
<div className="topWrapper" style={{borderBottom:"none",padding:"20px"}}>
@ -282,9 +258,12 @@ class merge extends Component {
style={{ width: 300 }}
/>
</div>
<a className="topWrapper_btn ml10" onClick={() => this.islogin()}>
+&nbsp;新建合并请求
</a>
{
data && data.user_admin_or_member &&
<a className="topWrapper_btn ml10" onClick={() => this.checkOperation()}>
+&nbsp;新建合并请求
</a>
}
</div>
<div className="f-wrap-between screenWrap">
<div className="df">
@ -300,6 +279,7 @@ class merge extends Component {
className={status_type === "1" ? "active" : ""}
onClick={() => this.openorder("1")}
>
<i className="iconfont icon-hebingqingqiu1 font-14 mr3 i_open"></i>
<label>开启的</label>
<span>{data && data.open_count}</span>
</li>
@ -307,6 +287,7 @@ class merge extends Component {
className={status_type === "11" ? "active" : ""}
onClick={() => this.openorder("11")}
>
<i className="iconfont icon-hebingqingqiu1 font-14 mr3 i_merged"></i>
<label>已合并</label>
<span>{data && data.merged_issues_size}</span>
</li>
@ -314,6 +295,7 @@ class merge extends Component {
className={status_type === "2" ? "active" : ""}
onClick={() => this.openorder("2")}
>
<i className="iconfont icon-hebingqingqiu1 font-14 mr3 i_closed"></i>
<label>已拒绝</label>
<span>{data && data.close_count}</span>
</li>
@ -418,10 +400,23 @@ class merge extends Component {
project_author_name={data.project_author_name}
{...this.props}
{...this.state}
user_admin_or_member={data && data.user_admin_or_member}
></OrderItem>
{Paginations}
</div>
):""}
{search_count > select_params.limit ? (
<div className="mt30 mb50 edu-txt-center">
<Pagination
simple
current={select_params.page}
total={search_count}
pageSize={select_params.limit}
onChange={this.ChangePage}
></Pagination>
</div>
) : (
""
)}
{ data && data.issues && data.issues.length === 0 ? <NoneData _html="暂时还没有相关数据!" projectsId={projectsId} owner={owner} /> :""}
</Spin>
</div>

View File

@ -34,17 +34,19 @@ class MergeForm extends Component {
this.set_defatul();
};
componentDidUpdate=(prevPros)=>{
const { projectsId ,owner } = this.props.match.params;
const pId = prevPros.match.params.projectsId;
const oId = prevPros.match.params.owner;
if(pId !== projectsId || oId !== owner ){
// console.log("切换了项目分支···········");
this.get_default_selects();
}
if(prevPros && this.props && !this.props.checkIfLogin()){
this.props.history.push("/403")
return
}
}
// check_is_login =() =>{
// if(!this.props.checkIfLogin()){
// this.props.history.push("/403")
// return
// }
// };
get_default_selects = () => {
const { projectsId ,owner } = this.props.match.params;
this.setState({ isSpin: true });

View File

@ -43,7 +43,8 @@ class Index extends Component {
project_language_name: undefined,
project_category_name: undefined,
license_name: undefined,
ignore_name: undefined
ignore_name: undefined,
descNum:0
}
}
componentDidMount = () => {
@ -67,24 +68,30 @@ class Index extends Component {
getOwner=()=>{
const { OIdentifier } = this.props.match.params;
const { user_id } = this.props && this.props.current_user;
const url = `/owners.json`;
axios.get(url).then(result=>{
if(result && result.data){
let owner = result.data.owners;
this.setState({
OwnerList: owner,
})
if(OIdentifier){
owner = owner.filter(item=>item.name === OIdentifier);
this.props.form.setFieldsValue({
user_id:OIdentifier
})
owner && this.setState({
owners_id:owner[0].id,
owners_name:owner[0].name
}else if(user_id){
owner = owner.filter(item=>item.id === user_id);
this.props.form.setFieldsValue({
user_id:owner && owner[0].name
})
}
this.setOptionsList(owner, 'owners');
this.setState({
OwnerList: owner,
owner && this.setState({
owners_id:owner[0].id,
owners_name:owner[0].name
})
this.setOptionsList(owner, 'owners');
}
}).catch(error=>{})
}
@ -180,7 +187,7 @@ class Index extends Component {
isSpin: false
})
this.props.showNotification(`${projectsType && projectsType === "mirror" ? "镜像" : "托管"}项目创建成功!`);
this.props.history.push(`/projects/${owners_name}/${result.data.identifier}`);
this.props.history.push(`/projects/${result.data.login}/${result.data.identifier}`);
}
}
}).catch((error) => {
@ -254,6 +261,13 @@ class Index extends Component {
}
}
changeDesc=(e)=>{
let value = e.target.value;
this.setState({
descNum:value ? value.length :0
})
}
render() {
const { getFieldDecorator } = this.props.form;
// 项目类型deposit-托管项目mirror-镜像项目
@ -273,7 +287,9 @@ class Index extends Component {
license_list,
ignore_list,
mirrorCheck
mirrorCheck,
descNum
} = this.state;
return (
<div className="main back-white" style={{padding:"0px",border:"none"}}>
@ -361,21 +377,23 @@ class Index extends Component {
required: true, message: '请填写项目名称'
}],
})(
<Input placeholder="例如:团队协作方法与研究" />
)}
</Form.Item>
<Form.Item
label="项目简介"
>
{getFieldDecorator('description', {
rules: [{
required: true, message: '请填写项目简介'
}],
})(
<Input.TextArea placeholder="项目的介绍" autoSize={{ minRows: 2, maxRows: 6 }} />
<Input placeholder="例如:团队协作方法与研究" maxLength={50}/>
)}
</Form.Item>
<div className="pr">
<span className="toprightNum">{descNum}/200</span>
<Form.Item
label="项目简介"
>
{getFieldDecorator('description', {
rules: [{
required: true, message: '请填写项目简介'
}],
})(
<Input.TextArea maxLength={200} placeholder="项目的介绍" autoSize={{ minRows: 2, maxRows: 6 }} onChange={this.changeDesc}/>
)}
</Form.Item>
</div>
<Form.Item
label="仓库名称"
>
@ -384,7 +402,7 @@ class Index extends Component {
required: true, message: '请填写仓库名称'
}],
})(
<Input placeholder="仓库名称请使用与项目相关的英文关键字" />
<Input placeholder="仓库名称请使用与项目相关的英文关键字" maxLength={100} />
)}
</Form.Item>
<Form.Item

View File

@ -73,6 +73,7 @@ class Index extends Component {
content={undefined}
readOnly={false}
editorType="new"
descName={filename && `Add ${filename}`}
></Meditor>
</div>
</div>

View File

@ -6,6 +6,20 @@ import "./index.css";
import axios from "axios";
const TextArea = Input.TextArea;
function turnbar(str){
if(str && str.length>0 && str.indexOf("/")>-1){
return str.replaceAll('/','%2F');
}
return str;
}
function returnbar(str){
if(str && str.length>0 && str.indexOf("%2F")>-1){
return str.replaceAll('%2F','/');
}
return str;
}
class UserSubmitComponent extends Component {
constructor(props) {
super(props);
@ -16,6 +30,24 @@ class UserSubmitComponent extends Component {
};
}
componentDidMount=()=>{
const { descName } = this.props;
if(descName){
this.props.form.setFieldsValue({
desc:descName
})
}
}
componentDidUpdate=(preProps)=>{
const { descName } = this.props;
if(preProps && descName && preProps.descName !== descName ){
this.props.form.setFieldsValue({
desc:descName
})
}
}
changeSubmittype = (e) => {
this.setState({
submitType: e.target.value,
@ -41,7 +73,7 @@ class UserSubmitComponent extends Component {
const url = `/${owner}/${projectsId}/create_file.json`;
axios.post(url, {
filepath: filename ? filename : path,
branch: branch,
branch: returnbar(branch),
new_branch: submitType === "1" ? values.branchname : undefined,
content,
message: values.desc,
@ -54,7 +86,7 @@ class UserSubmitComponent extends Component {
const { getTopCount } = this.props;
getTopCount && getTopCount(values.branchname);
}
let url = `/projects/${owner}/${projectsId}${values.branchname ? `/tree/${values.branchname}`: (branch ? `/tree/${branch}` : "")}`;
let url = `/projects/${owner}/${projectsId}${values.branchname ? `/tree/${turnbar(values.branchname)}`: (branch ? `/tree/${turnbar(branch)}` : "")}`;
this.props.history.push(url);
}
})
@ -75,12 +107,13 @@ class UserSubmitComponent extends Component {
const { projectsId , owner } = this.props.match.params;
const { submitType } = this.state;
const url = `/${owner}/${projectsId}/update_file.json`;
let b = currentBranch || branch;
this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) {
axios
.put(url, {
filepath: detail.path,
branch: submitType === "1" ? undefined : (currentBranch || branch),
branch: submitType === "1" ? undefined : returnbar(b),
new_branch: submitType === "1" ? values.branchname : undefined,
content: content,
sha: detail.sha,
@ -89,7 +122,8 @@ class UserSubmitComponent extends Component {
.then((result) => {
this.setState({ isSpin: false });
if (result.data && result.data.status === 1) {
let url = `/projects/${owner}/${projectsId}${(values.branchname ? `/tree/${values.branchname}` : ((currentBranch || branch) ? `/tree/${currentBranch || branch}`:""))}`;
let b = currentBranch || branch;
let url = `/projects/${owner}/${projectsId}${(values.branchname ? `/tree/${turnbar(values.branchname)}` : (b ? `/tree/${turnbar(b)}`:""))}`;
this.props.history.push(url);
this.props.showNotification("文件修改成功!");
}
@ -112,12 +146,13 @@ class UserSubmitComponent extends Component {
const { current_user, filepath, projectDetail , currentBranch } = this.props;
const { editor_type } = this.props;
let b = currentBranch || branch;
return (
<div>
<span className="df" style={{ alignItems: "center" }}>
<Link to={`/users/${current_user && current_user.login}`} className="show-user-link" >
<img
src={getImageUrl(`images/${current_user && current_user.image_url}`)}
src={getImageUrl(`/${current_user && current_user.image_url}`)}
alt=""
className="screwImg"
/>
@ -170,7 +205,7 @@ class UserSubmitComponent extends Component {
>
<Radio value="0" className="mb10">
<i className="iconfont icon-banbenku font-16 mr5"></i>
直接提交至<span className="color-orange">{currentBranch || branch}</span>
直接提交至<span className="color-orange">{returnbar(b)}</span>
</Radio>
<Radio value="1">
<Icon type="pull-request" className="mr5" />

View File

@ -12,6 +12,13 @@ class m_editor extends Component {
editorValue: this.props.content,
};
}
componentDidUpdate=(prevProps)=>{
if(prevProps && this.props && this.props.content !== prevProps.content){
this.setState({
editorValue:this.props.content
})
}
}
changeEditor = (editorValue) => {
this.setState({
editorValue,
@ -20,7 +27,7 @@ class m_editor extends Component {
render() {
const { editorValue } = this.state;
const { readOnly, editorType, language , currentBranch } = this.props;
const { readOnly, editorType, language , currentBranch , descName } = this.props;
const editor_options = {
lineNumbers: "on",
wordWrap: true, //强制换行
@ -44,7 +51,7 @@ class m_editor extends Component {
return (
<React.Fragment>
<div>
<div className="branchTable">
<div className="branchTable" style={{border:"1px solid #eee"}}>
<Editor
height="400px"
language={language ? language : "plaintext"}
@ -57,7 +64,7 @@ class m_editor extends Component {
/>
</div>
{!readOnly && (
<div style={{padding:"20px",marginTop:"20px",borderTop:"1px solid #d9d9d9"}}>
<div style={{marginTop:"20px",padding:"20px"}}>
<UserSubmitComponent
{...this.props}
{...this.state}
@ -65,6 +72,7 @@ class m_editor extends Component {
content={editorValue}
editor_type={editorType}
currentBranch={currentBranch}
descName={descName}
></UserSubmitComponent>
</div>
)}

View File

@ -60,6 +60,7 @@ class UploadFile extends Component {
filepath={file_path}
content={editorValue}
editor_type={"upload"}
descName={`ADD file via upload`}
></UserSubmitComponent>
</div>
</div>

114
src/forge/Notice/Apply.jsx Normal file
View File

@ -0,0 +1,114 @@
import React, { useEffect , useState } from 'react';
import Axios from 'axios';
import { Pagination , Spin , Popconfirm }from 'antd';
import { Link } from 'react-router-dom';
import { getImageUrl } from 'educoder';
import Nodata from '../Nodata';
import { FlexAJ } from '../Component/layout';
const limit = 15;
function Apply(props) {
const username = props.match.params.username;
const [ list , setList ] = useState(undefined);
const [ page , setPage ] = useState(1);
const [ total , setTotal ] = useState(0);
const [ isSpin , setIsSpin ] = useState(true);
useEffect(()=>{
if(username){
setIsSpin(true);
getList();
}
},[username])
function getList() {
const url = `/users/${username}/applied_projects.json`;
Axios.get(url).then(result=>{
if(result){
setList(result.data.applied_projects);
setTotal(result.data.total_count);
setIsSpin(false);
}
}).catch(error=>{})
}
//
function acceptDivert(id){
const url = `/users/${username}/applied_projects/${id}/accept.json`;
Axios.post(url).then(result=>{
if(result && result.data){
getList();
props && props.deleteEvent("apply",1);
}
}).catch(error=>{})
}
//
function revertDivert(id){
const url = `/users/${username}/applied_projects/${id}/refuse.json`;
Axios.post(url).then(result=>{
if(result && result.data){
getList();
props && props.deleteEvent("apply",1);
}
}).catch(error=>{})
}
return(
<div>
<Spin spinning={isSpin}>
<div style={{minHeight:"400px"}}>
{
list && list.length > 0 ?
<ul className="notifyList">
{
list.map((i,k)=>{
return(
<li>
<Link to={`/users/${i.user && i.user.login}`}><img src={getImageUrl(`/${i.user && i.user.image_url}`)} alt="" className="notifyImg"/></Link>
<div className="notifyFlex">
<p className="notifyInfos">
<Link to={`/users/${i.user && i.user.login}`} className="font-15 mr20">{i.user && i.user.name}</Link>
<span className="color-grey-9">{i.time_ago}</span>
</p>
<FlexAJ>
<p>申请以{i.role === "developer" ?"开发者":i.role === "manager" ? "管理者":"报告者"}身份加入{i.project && i.project.name}项目是否同意</p>
{
i.status === "common" &&
<span>
<Popconfirm title={`确定同意${i.user && i.user.name}加入【${i.project && i.project.name}】项目?`} okText="确定" cancelText="取消" onConfirm={()=>acceptDivert(i.id)}>
<a className="color-blue">同意</a>
</Popconfirm>
<Popconfirm title={`确定拒绝${i.user && i.user.name}加入【${i.project && i.project.name}】项目?`} okText="确定" cancelText="取消" onConfirm={()=>revertDivert(i.id)}>
<a className="color-red ml20">拒绝</a>
</Popconfirm>
</span>
}
{
i.status === "accepted" && <span className="color-grey-9">已接受</span>
}
{
i.status === "refused" && <span className="color-grey-9">已拒绝</span>
}
</FlexAJ>
</div>
</li>
)
})
}
</ul>
:
""
}
{list && list.length === 0 && <Nodata _html="暂无成员申请" />}
{
total > limit &&
<div className="edu-txt-center pt20 pb20">
<Pagination simple pageSize={limit} total={total} current={page} onChange={(p)=>{setPage(p)}}/>
</div>
}
</div>
</Spin>
</div>
)
}
export default Apply;

128
src/forge/Notice/Index.jsx Normal file
View File

@ -0,0 +1,128 @@
import React, { useEffect, useState } from "react";
import { Link } from 'react-router-dom';
import './Index.scss';
import Loadable from "react-loadable";
import Loading from "../../Loading";
import { Route, Switch } from "react-router-dom";
const Apply = Loadable({
loader: () => import("./Apply"),
loading: Loading,
});
const Notify = Loadable({
loader: () => import("./Notify"),
loading: Loading,
});
const UndoEvent = Loadable({
loader: () => import("./UndoEvent"),
loading: Loading,
});
function Index(props){
const username = props.match.params.username;
const pathname = props.history.location.pathname;
const user = props.user;
const [ menu , setMenu ] = useState("notify");
const [ messagesCount , setMessagesCount ] = useState(0);
const [ transferCount , setTransferCount ] = useState(0);
const [ applyCount , setApplyCount ] = useState(0);
const [ flag , setFlag ] = useState(true);
const { current_user } = props;
useEffect(()=>{
if((username && current_user && (current_user.login !== username))){
props.history.push(`/users/${username}`);
}
},[current_user,username])
useEffect(()=>{
if(user){
setTransferCount(user.undo_transfer_projects);
setApplyCount(user.undo_join_projects);
setMessagesCount(user.undo_messages);
}
},[user])
useEffect(()=>{
if(pathname && username){
if(pathname === `/users/${username}/notice`){
setMenu("notify");
changeNum(user.undo_messages);
}
if(pathname === `/users/${username}/notice/undo`){
setMenu("undo");
}
if(pathname === `/users/${username}/notice/apply`){
setMenu("apply");
}
}
},[pathname,user])
function changeNum(){
if(flag){
messagesCount && props.deleteUndoEvent(messagesCount);
setFlag(false);
}
}
function deleteEvent(type,count) {
let c = count;
if(type==="apply"){
setApplyCount(applyCount-count);
}else if(type==="undo"){
setTransferCount(applyCount-count);
}else{
setMessagesCount(0);
c = messagesCount;
}
(c || c===0) && props.deleteUndoEvent(c);
}
return (
<div>
<ul className="noticeMenu">
<li className={menu === "notify" ? "active":""}>
<Link to={`/users/${username}/notice`} onClick={changeNum}>
<span>通知</span>
{messagesCount ? <span className="unNum">{messagesCount}</span>:""}
</Link>
</li>
<li className={menu === "undo" ? "active":""}>
<Link to={`/users/${username}/notice/undo`}>
<span>接收仓库</span>
{transferCount ? <span className="unNum">{transferCount}</span>:""}
</Link>
</li>
<li className={menu === "apply" ? "active":""}>
<Link to={`/users/${username}/notice/apply`}>
<span>成员申请</span>
{applyCount ? <span className="unNum">{applyCount}</span>:""}
</Link>
</li>
</ul>
<Switch>
<Route
path="/users/:username/notice/apply"
render={(p) => {
return <Apply {...props} {...p} deleteEvent={deleteEvent}/>;
}}
></Route>
<Route
path="/users/:username/notice/undo"
render={(p) => {
return <UndoEvent {...props} {...p} deleteEvent={deleteEvent}/>;
}}
></Route>
<Route
path="/users/:username/notice"
render={(p) => {
return <Notify {...props} {...p} deleteEvent={deleteEvent}/>;
}}
></Route>
</Switch>
</div>
);
}
export default Index;

View File

@ -0,0 +1,60 @@
.noticeMenu{
padding:0px 30px;
display: flex;
border-bottom: 1px solid #eee;
li{
font-size: 16px;
padding:0px;
margin-right:30px;
height: 70px;
line-height: 70px;
position: relative;
transform: none;
a{
display: flex;
}
&.active a span{
color: #1890ff;
}
.unNum{
color: #d38900;
font-size: 12px;
border-radius: 13px;
height: 16px;
line-height: 16px;
padding:0px 4px;
min-width: 23px;
text-align: center;
background-color: #ffe4b3;
margin-top: 27px;
margin-left: 10px;
display: block;
}
}
}
.notifyList{
padding:0px 30px;
li{
display: flex;
border-bottom: 1px solid #eee;
padding:20px 0px;
.notifyImg{
width: 48px;
height: 48px;
border-radius: 50%;
margin-right: 15px;
}
.notifyFlex{
flex:1;
p{
margin:0px;
}
.notifyInfos{
margin-bottom: 8px;
}
}
&:last-child{
border-bottom: none;
}
}
}

113
src/forge/Notice/Notify.jsx Normal file
View File

@ -0,0 +1,113 @@
import React, { useEffect, useState } from "react";
import Nodata from '../Nodata';
import { Pagination , Spin } from 'antd';
import { Link } from 'react-router-dom';
import { getImageUrl } from 'educoder';
import Axios from "axios";
const limit = 15;
function Notify(props){
const username = props.match.params.username;
const [ list , setList ] = useState(undefined);
const [ page , setPage ] = useState(1);
const [ total , setTotal ] = useState(0);
const [ isSpin , setIsSpin ] = useState(true);
useEffect(()=>{
props && props.deleteEvent("notify",0);
},[])
useEffect(()=>{
if(username){
setIsSpin(true);
getList();
}
},[username,page])
function getList(){
const url = `/users/${username}/applied_messages.json`;
Axios.get(url,{
params:{
page,per_page:limit
}
}).then(result=>{
if(result){
setList(result.data.applied_messages);
setTotal(result.data.total_count);
setIsSpin(false);
}
}).catch(error=>{})
}
function renderStatus(status,applied){
let { project , owner} = applied
if(status){
switch(status){
case 'canceled':
return <p>取消转移<Link to={`/projects/${project && project.owner && project.owner.login}/${project && project.identifier}`}>{project && project.name}</Link>仓库</p>
case 'common':
return <p>正在将<Link to={`/projects/${project && project.owner && project.owner.login}/${project && project.identifier}`}>{project && project.name}</Link>仓库转移给<Link to={`/users/${owner && owner.login}`}>{owner && owner.name}</Link></p>
case 'successed':
return <p><Link to={`/projects/${project && project.owner && project.owner.login}/${project && project.identifier}`}>{project && project.name}</Link>仓库成功转移给<Link to={`/users/${owner && owner.login}`}>{owner && owner.name}</Link></p>
default:
return <p>拒绝转移<Link to={`/projects/${project && project.owner && project.owner.login}/${project && project.identifier}`}>{project && project.name}</Link>仓库</p>
}
}else{
return ""
}
}
function renderApplyStatus(status,applied) {
let { project } = applied;
if(status){
switch(status){
case 'successed':
return <p>已通过你加入<Link to={`/projects/${project && project.owner && project.owner.login}/${project && project.identifier}`}>{project && project.name}</Link>项目的申请</p>
default:
return <p>已拒绝你加入<Link to={`/projects/${project && project.owner && project.owner.login}/${project && project.identifier}`}>{project && project.name}</Link>项目的申请</p>
}
}else{
return ""
}
}
return(
<div>
<Spin spinning={isSpin}>
<div style={{minHeight:"400px"}}>
{
list && list.length > 0 ?
<ul className="notifyList">
{
list.map((i,k)=>{
return(
<li>
<Link to={`/users/${i.login}`}><img src={getImageUrl(`/${i.applied_user && i.applied_user.image_url}`)} alt="" className="notifyImg"/></Link>
<div className="notifyFlex">
<p className="notifyInfos">
<Link to={`/users/${i.applied_user && i.applied_user.login}`} className="font-15 mr20">{i.applied_user && i.applied_user.name}</Link>
<span className="color-grey-9">{i.time_ago}</span>
</p>
{ i.applied_type === "AppliedProject" ? renderApplyStatus(i.status,i.applied):renderStatus(i.status,i.applied)}
</div>
</li>
)
})
}
</ul>
:
""
}
{list && list.length === 0 && <Nodata _html="暂无通知" />}
{
total > limit &&
<div className="edu-txt-center pt20 pb20">
<Pagination simple pageSize={limit} total={total} current={page} onChange={(p)=>{setPage(p)}}/>
</div>
}
</div>
</Spin>
</div>
)
}
export default Notify;

View File

@ -0,0 +1,123 @@
import React, { useEffect, useState } from "react";
import Nodata from '../Nodata';
import { FlexAJ } from '../Component/layout';
import { Pagination , Popconfirm , Spin } from 'antd';
import { Link } from 'react-router-dom';
import { getImageUrl } from 'educoder';
import Axios from 'axios';
const limit = 15;
function UndoEvent(props){
const username = props.match.params.username;
const [ list , setList ] = useState(undefined);
const [ page , setPage ] = useState(1);
const [ total , setTotal ] = useState(0);
const [ isSpin , setIsSpin ] = useState(true);
useEffect(()=>{
if(username){
setIsSpin(true);
getList();
}
},[username,page])
function getList(){
const url = `/users/${username}/applied_transfer_projects.json`;
Axios.get(url,{
params:{
page,per_page:limit
}
}).then(result=>{
if(result){
setList(result.data.applied_transfer_projects);
setTotal(result.data.total_count);
setIsSpin(false);
}
}).catch(error=>{})
}
//
function acceptDivert(id){
const url = `/users/${username}/applied_transfer_projects/${id}/accept.json`;
Axios.post(url).then(result=>{
if(result && result.data){
getList();
props && props.deleteEvent("undo",1);
}
}).catch(error=>{})
}
//
function revertDivert(id){
const url = `/users/${username}/applied_transfer_projects/${id}/refuse.json`;
Axios.post(url).then(result=>{
if(result && result.data){
getList();
props && props.deleteEvent("undo",1);
}
}).catch(error=>{})
}
return(
<div>
<Spin spinning={isSpin}>
<div style={{minHeight:"400px"}}>
{
list && list.length > 0 ?
<ul className="notifyList">
{
list.map((i,k)=>{
return(
<li>
<Link to={`/users/${i.user && i.user.login}`}><img src={getImageUrl(`/${i.user && i.user.image_url}`)} alt="" className="notifyImg"/></Link>
<div className="notifyFlex">
<p className="notifyInfos">
<Link to={`/users/${i.login}`} className="font-15 mr20">{i.user && i.user.name}</Link>
<span className="color-grey-9">{i.time_ago}</span>
</p>
<FlexAJ>
<p className="color-grey-6">请求将仓库<Link to={`/projects/${i.project && i.project.owner && i.project.owner.login}/${i.project && i.project.identifier}`}>{i.project && i.project.name}</Link>
转移给<Link to={`/users/${i.owner && i.owner.login}`}>{i.owner && i.owner.name}</Link>是否接受</p>
{
i.status === "common" &&
<span>
<Popconfirm title={`确定接受仓库${i.project && i.project.name}`} okText="确定" cancelText="取消" onConfirm={()=>acceptDivert(i.id)}>
<a className="color-blue">接受</a>
</Popconfirm>
<Popconfirm title={`确定拒绝接受仓库${i.project && i.project.name}`} okText="确定" cancelText="取消" onConfirm={()=>revertDivert(i.id)}>
<a className="color-red ml20">拒绝</a>
</Popconfirm>
</span>
}
{
i.status === "canceled" && <span className="color-grey-9">对方已取消转移</span>
}
{
i.status === "accepted" && <span className="color-grey-9">已接受</span>
}
{
i.status === "refused" && <span className="color-grey-9">已拒绝</span>
}
</FlexAJ>
</div>
</li>
)
})
}
</ul>
:
""
}
</div>
</Spin>
{list && list.length === 0 && <Nodata _html="暂无接收信息" />}
{
total > limit &&
<div className="edu-txt-center pt20 pb20">
<Pagination simple pageSize={limit} total={total} current={page} onChange={(p)=>{setPage(p)}}/>
</div>
}
</div>
)
}
export default UndoEvent;

View File

@ -212,7 +212,7 @@ class Detail extends Component {
>
<img
className="user_img"
src={getImageUrl(`images/${data && data.author_picture}`)}
src={getImageUrl(`/${data && data.author_picture}`)}
alt=""
width="50"
height="50"
@ -249,7 +249,7 @@ class Detail extends Component {
添加于 {data && data.created_at}
</span>
{data && data.user_permission ? (
<span className="pull-right">
<span className="pull-right 123123">
<a className="color-blue fr" onClick={this.copydetail}>
复制
</a>

View File

@ -89,8 +89,9 @@ class Milepost extends Component {
closeselect: status === "closed" ? current_user.user_id : undefined,
openselect: status === "closed" ? undefined : current_user.user_id
})
this.getList(1, status, 'desc')
this.getList(1, status, 'desc');
const { getDetail } = this.props;
getDetail && getDetail();
}
}).catch(error => {
console.log(error);
@ -107,7 +108,9 @@ class Milepost extends Component {
}
}).then((result) => {
if (result) {
this.getList(1, this.state.status, 'desc')
this.getList(1, this.state.status, 'desc');
const { getDetail } = this.props;
getDetail && getDetail();
}
}).catch((error) => {
console.log(error);
@ -150,7 +153,7 @@ class Milepost extends Component {
const { data, limit, page, openselect, closeselect, spinings } = this.state;
const { projectsId , owner } = this.props.match.params;
const menu = (
<Menu onClick={this.arrayList}>
<Menu className="orderCondition" onClick={this.arrayList}>
<Menu.Item key={'created_on'} value="desc">到期日从近到远</Menu.Item>
<Menu.Item key={'created_on'} value="asc">到期日从远到近</Menu.Item>
<Menu.Item key={'percent'} value="desc">完成度从低到高</Menu.Item>
@ -228,8 +231,8 @@ class Milepost extends Component {
<Link to={`/projects/${owner}/${projectsId}/milestones/${item.id}/edit`} className="color-grey-9">编辑</Link>
</div>
<div className="grid-item ml15 color-grey-9">
<i className="iconfont icon-yiguanbi1 font-14 mr5"></i>
<a onClick={() => this.updatestatusemile(this.state.status === "closed" ? "open" : "closed", item)} className="color-grey-9">{this.state.status === "closed" ? "开启" : "关闭"}</a>
<i className={item.status === "closed" ? "iconfont icon-gouxuan font-14 mr5":"iconfont icon-yiguanbi1 font-14 mr5"}></i>
<a onClick={() => this.updatestatusemile(item.status === "closed" ? "open" : "closed", item)} className="color-grey-9">{this.state.status === "closed" ? "开启" : "关闭"}</a>
</div>
<div className="grid-item ml15 color-grey-9">
<i className="iconfont icon-lajitong font-14 mr5" ></i>

View File

@ -69,6 +69,27 @@ class MilepostDetail extends Component {
})
}
deletedetail = (id) => {
const { projectsId , owner } = this.props.match.params;
const url = `/${owner}/${projectsId}/issues/${id}.json`;
axios.delete(url, {
data: {
project_id: projectsId,
id: id,
},
})
.then((result) => {
if (result) {
const { page } = this.state;
this.getIssueList(page);
const { getDetail } = this.props;
getDetail && getDetail();
}
})
.catch((error) => {
console.log(error);
});
};
// 获取列表数据
getIssueList = ( page , item , value , update , updateValue , type ) => {
const { projectsId, meilid , owner } = this.props.match.params;
@ -141,7 +162,7 @@ class MilepostDetail extends Component {
renderMenu = (array, name, id) => {
return (
<Menu>
<Menu className="orderCondition">
<Menu.Item key={"all"} onClick={(e) => this.getOption(e, id, name)}>{name}</Menu.Item>
{
array && array.length > 0 && array.map((item, key) => {
@ -218,7 +239,7 @@ class MilepostDetail extends Component {
</span>
<div className="milepostdiv">
<Link to={`/projects/${owner}/${projectsId}/milestones/${meilid}/edit`} className="topWrapper_btn" style={{ marginRight: 15 }} >编辑里程碑</Link>
<Link to={`/projects/${owner}/${projectsId}/issues/${meilid}/new`} className="topWrapper_btn">创建任务</Link>
<Link to={`/projects/${owner}/${projectsId}/issues/${meilid}/new`} className="topWrapper_btn">创建易修</Link>
</div>
</FlexAJ>
</div>
@ -275,7 +296,15 @@ class MilepostDetail extends Component {
:
issues && issues.length>0 && issues.map((item,key)=>{
return(
<OrderItem key={key} mile item={item} search_count={search_count} page={page} limit={limit} {...this.props} {...this.state}></OrderItem>
<OrderItem
key={key} mile
item={item}
search_count={search_count}
page={page}
limit={limit}
{...this.props} {...this.state}
deletedetail={this.deletedetail}
></OrderItem>
)
})
}

View File

@ -42,10 +42,9 @@ class OrderItem extends Component {
})
}
render() {
const { item , checkbox , mile } = this.props;
const { item , checkbox , mile , user_admin_or_member } = this.props;
const { projectsId , owner } = this.props.match.params;
const { current_user } = this.props
const { current_user } = this.props;
return (
item &&
<div className="issueItem">
@ -91,13 +90,13 @@ class OrderItem extends Component {
<div className="milepostleft">
<Link to={`/projects/${owner}/${projectsId}/issues/${item.id}/detail`}><i className="iconfont icon-pinglun1 mr3 font-16"></i>{item.journals_count}</Link>
{
current_user && current_user.login ?
<div style={{ display: this.state.orderid === item.id && this.state.isdisplay ? 'flex' : 'none' }}>
user_admin_or_member ?
<div id="hoverBox" style={{ display: this.state.orderid === item.id && this.state.isdisplay ? 'flex' : 'none' }}>
<div className="mr8 ml8 color-grey-9">
<Link to={`/projects/${owner}/${projectsId}/issues/${item.id}/updatedetail`} className="color-grey-9"><i className="iconfont icon-bianji3 font-14 mr5"></i></Link>
</div>
<div className="color-grey-9">
<Popconfirm placement="bottom" title={'您确定要删除吗'} okText="是" cancelText="否" onConfirm={() => this.deletedetail(item.id)}>
<Popconfirm placement="bottom" overlayClassName="overlayBox" getPopupContainer={()=>document.getElementById("hoverBox")} title={'您确定要删除当前易修'} okText="是" cancelText="否" onConfirm={() => this.deletedetail(item.id)}>
<i className="iconfont icon-yiguanbi1 font-14"></i>
</Popconfirm>
</div>

View File

@ -64,6 +64,8 @@ class NewMilepost extends Component {
if (result) {
this.setState({ isSpin: false })
this.props.history.push(`/projects/${owner}/${projectsId}/milestones`);
const { getDetail } = this.props;
getDetail && getDetail();
}
}).catch(error => {
this.setState({ isSpin: false })

View File

@ -17,7 +17,9 @@
line-height: 18px;
color: red;
}
.overlayBox{
width: 230px;
}
.topmilepost {
box-sizing: border-box;
display: flex;
@ -590,7 +592,7 @@ a.issue-type-button.active:hover {
}
/* 发布人、指派人数量过多时要出现滚动条 */
.ant-dropdown-menu {
.ant-dropdown-menu.orderCondition {
max-height: 350px;
overflow-y: auto;
}

View File

@ -201,7 +201,7 @@ class order extends Component {
renderMenu = (array, name, id, toGet) => {
return (
<Menu>
<Menu className="orderCondition">
<Menu.Item key={"all"} onClick={(e) => this.getOption(e, id, name, toGet)}>
{name}
</Menu.Item>
@ -268,11 +268,8 @@ class order extends Component {
checkedValue:[],
all:undefined
});
this.state.select_params = {
search: undefined,
page: 1,
limit: 15,
};
this.state.select_params.page = 1;
this.state.select_params.limit = 15;
this.getIssueList(type);
};
@ -337,6 +334,8 @@ class order extends Component {
const { status_type } = this.state;
this.getIssueList(status_type);
const { getDetail } = this.props;
getDetail && getDetail();
}
})
.catch((error) => {
@ -348,17 +347,14 @@ class order extends Component {
this.props.showLoginDialog();
}
renderNew =()=>{
const { projectsId , owner } = this.props.match.params;
if (this.props.checkIfLogin()) {
const { data } = this.state;
if(data && data.user_admin_or_member){
const { projectsId , owner } = this.props.match.params;
return(
<Link className="topWrapper_btn ml10" target="_blank" to={`/projects/${owner}/${projectsId}/issues/new`}>
+&nbsp;创建任务
+&nbsp;创建易修
</Link>
)
}else{
return(
<a className="topWrapper_btn ml10" onClick={this.islogin}>+&nbsp;创建任务</a>
)
}
}
@ -438,6 +434,8 @@ class order extends Component {
status_id: select_params.update_status_id
}).then(result => {
if (result) {
const { getDetail } = this.props;
(select_params && select_params.update_status_id) && getDetail && getDetail();
this.props.showNotification("修改成功!");
this.successFunc();
}
@ -451,7 +449,6 @@ class order extends Component {
const { status_type } = this.state;
this.getIssueList(status_type);
}
resetSelectParams = () => {
let select_params = this.state.select_params;
select_params.update_author_id = undefined;
@ -482,6 +479,8 @@ class order extends Component {
ids: checkedValue
}).then(result => {
if (result) {
const { getDetail } = this.props;
getDetail && getDetail();
this.props.showNotification("删除成功!");
this.successFunc();
}
@ -553,7 +552,7 @@ class order extends Component {
<div className="topWrapper" style={{borderBottom:"none"}}>
<div className="target-detail-search">
<Search
placeholder="输入issue名称进行搜索"
placeholder="输入关键字搜索易修"
enterButton
onSearch={this.searchFunc}
style={{ width: 300 }}
@ -574,7 +573,7 @@ class order extends Component {
<div className="f-wrap-between screenWrap">
<div className="df">
{
current_user && current_user.login ?
((current_user && current_user.login) && (data && data.user_admin_or_member)) ?
<Checkbox value="0" style={{ lineHeight: "50px", marginRight: "15px" }} checked={all} onChange={this.changeAll}></Checkbox>
: ""
}
@ -819,13 +818,14 @@ class order extends Component {
<OrderItem
key={key}
item={item}
checkbox={current_user ? <Checkbox value={item.id} key={item.id} style={{ margin: '4px 15px 0px 0px' }}></Checkbox> : ""}
checkbox={current_user &&(data && data.user_admin_or_member) ? <Checkbox value={item.id} key={item.id} style={{ margin: '4px 15px 0px 0px' }}></Checkbox> : ""}
search_count={search_count}
page={select_params.page}
limit={select_params.limit}
{...this.props}
{...this.state}
deletedetail={this.deletedetail}
user_admin_or_member={data && data.user_admin_or_member}
></OrderItem>
)
})}
@ -834,7 +834,7 @@ class order extends Component {
)}
{
search_count > select_params.limit ?
<div className="mt30 mb10 edu-txt-center">
<div className="mt30 mb30 edu-txt-center">
<Pagination
simple
defaultCurrent={select_params.page}

View File

@ -193,13 +193,14 @@ class order_form extends Component {
if (result) {
this.props.history.push(`/projects/${owner}/${projectsId}/issues/${orderId}/detail`);
this.props.showNotification("任务更新成功!");
const { getDetail } = this.props;
getDetail && getDetail();
}
})
.catch((error) => {
this.setState({
isSpin: false,
});
console.log(error);
});
}
}
@ -310,17 +311,17 @@ class order_form extends Component {
<div className="list-right">
<div className="pd20">
<h3 className="mb15">
{form_type === "new" ? "新建" :( form_type === "copy" ? "复制" : "编辑")}任务
{form_type === "new" ? "新建" :( form_type === "copy" ? "复制" : "编辑")}易修
</h3>
<Form.Item>
{getFieldDecorator("subject", {
rules: [
{
required: true,
message: "请填写任务标题",
message: "请填写易修标题",
},
]
})(<Input placeholder="标题" size="large" />)}
})(<Input placeholder="标题" size="large" maxLength={80}/>)}
</Form.Item>
<div className="quillContent">
<MDEditor

View File

@ -26,15 +26,15 @@ function Collaborator(props){
{
author && author.type === "Organization" ?
<span>
<span style={{cursor:"pointer"}} className={nav === "1" ? "font-18 text-black color-blue":"font-18 text-black"} onClick={()=>setNav("1")}>协作者管理</span>
<span style={{cursor:"pointer"}} className={nav === "2" ? "font-18 text-black ml30 color-blue":"font-18 text-black ml30"} onClick={()=>setNav("2")}>团队管理</span>
<span style={{cursor:"pointer"}} className={nav === "1" ? "font-18 text-black color-blue":"font-18 text-black"} onClick={()=>{setNav("1");setNewId(undefined)}}>协作者管理</span>
<span style={{cursor:"pointer"}} className={nav === "2" ? "font-18 text-black ml30 color-blue":"font-18 text-black ml30"} onClick={()=>{setNav("2");setNewId(undefined)}}>团队管理</span>
</span>
:
<span className="font-18 text-black">协作者管理</span>
}
{
nav === "1" ?
<AddMember getID={getID} login/>
<AddMember getID={getID} login showNotification={props.showNotification}/>
:
<AddGroup getGroupID={getGroupID} organizeId={owner}/>
}
@ -42,7 +42,7 @@ function Collaborator(props){
<div>
{
nav === "1" ?
<Member newId={newId} projectsId={projectsId} owner={owner} project_id={props.project_id} author={props.author} showNotification={props.showNotification}/>
<Member newId={newId} projectsId={projectsId} owner={owner} project_id={props.project_id} author={props.projectDetail && props.projectDetail.author} showNotification={props.showNotification}/>
:
<Group owner={owner} projectsId={projectsId} newGroupId={newGroupId}/>
}

View File

@ -20,8 +20,6 @@ function CollaboratorMember({projectsId,owner,project_id,author,showNotification
const [ role , setRole ] = useState(undefined);
const [ listData , setListData ] = useState(undefined);
const [ total , setTotal ] = useState(0);
useEffect(()=>{
if(newId){
addCollaborator(newId);
@ -188,7 +186,7 @@ function CollaboratorMember({projectsId,owner,project_id,author,showNotification
className="show-user-link"
>
<img
src={getImageUrl(`images/${text}`)}
src={getImageUrl(`/${text}`)}
alt=""
width="32px"
height="32px"

View File

@ -13,6 +13,7 @@ const menu = [
{name:"易修 (Issue)",index:"issues"},
{name:"合并请求",index:"pulls"},
{name:"工作流(beta版)",index:"devops"},
// {name:"资源库",index:"resources"},
{name:"里程碑",index:"versions"},
{name:"动态",index:"activity"},
]
@ -24,14 +25,18 @@ class Setting extends Component {
LanguageList: undefined,
private_check: undefined,
loading:true,
project_units:['home',"activity","code"]
project_units:['home',"activity","code"],
divertVisible:false,
is_transfering:undefined,
projectName:undefined
};
}
componentDidUpdate=(prevPros)=>{
if(prevPros && this.props && !this.props.checkIfLogin()){
this.props.history.push("/403")
return
return;
}
}
componentDidMount = () => {
@ -70,6 +75,7 @@ class Setting extends Component {
project_units:units
});
this.setState({
projectName:result.data.project_name,
private_check: result.data.private,
loading:false,
project_units:units
@ -162,10 +168,11 @@ class Setting extends Component {
// 删除本仓库
deleteProject = () => {
const { projectsId , owner } = this.props.match.params;
const { projectName } = this.state;
this.props.confirm({
content: "删除后无法恢复,是否确认删除本仓库?",
content: <span style={{display:"block",textAlign:"left"}}>该操作无法撤销且将会一并删除相关的易修合并请求工作流里程碑动态等数据<br/>是否确认删除 <span style={{fontWeight:"bold"}}>{owner}/{projectName}({projectsId})</span></span>,
onOk: () => {
const { projectsId , owner } = this.props.match.params;
const url = `/${owner}/${projectsId}.json`;
axios
.delete(url)
@ -187,8 +194,12 @@ class Setting extends Component {
render() {
const { getFieldDecorator } = this.props.form;
const { projectDetail } = this.props;
const { CategoryList, LanguageList, private_check ,loading } = this.state;
let mirror = projectDetail && projectDetail.mirror;
let type = projectDetail && projectDetail.type;
const forked_from_project_id = this.props && this.props.projectDetail && this.props.projectDetail.forked_from_project_id;
return (
<div>
<Spin spinning={loading}>
@ -214,18 +225,22 @@ class Setting extends Component {
<Checkbox
checked={private_check}
onChange={this.changePrivate}
disabled={forked_from_project_id}
>
将仓库设为私有
<span className="color-grey-9">将仓库设为私有</span>
<span className="color-grey-6">
{ forked_from_project_id ?`Fork仓库的可见性实时同步自源仓库不支持直接修改`:`修改仓库的可见性将会影响到该仓库下所有Fork仓库的可见性`}
</span>
</Checkbox>
)}
</Form.Item>
</div>
<Form.Item label="仓库描述">
<Form.Item label="项目简介">
{getFieldDecorator("project_description", {
rules: [],
})(
<TextArea
placeholder="请输入仓库描述"
placeholder="请输入项目简介"
style={{ height: "80px" }} maxLength={200}
/>
)}
@ -261,7 +276,7 @@ class Setting extends Component {
<Checkbox
key={key}
value={item.index}
disabled={item.index === "home" || item.index === "activity" || item.index === "code"}
disabled={item.index === "home" || item.index === "activity" || item.index === "code" || (mirror && (type && type===2) && item.index === "pulls")}
>{item.name}</Checkbox>
)
})

82
src/forge/Sonar/Index.jsx Normal file
View File

@ -0,0 +1,82 @@
import React, { useEffect, useState } from 'react';
import sonar from '../Images/sonar.png';
import { FlexAJ } from '../Component/layout';
import { Button } from 'antd';
import './index.scss';
import axios from 'axios';
function Index(props) {
const [ spining , setSpining ] = useState(false);
const [ data , setData ] = useState(null);
const permission = props.projectDetail && props.projectDetail.permission;
const { owner , projectsId } = props.match.params;
useEffect(()=>{
if(permission && permission!=="Reporter"){
run();
}
},[permission])
function startCheck() {
setSpining(true);
run();
}
function run() {
const url = `/${owner}/${projectsId}/sonarqube.json`;
axios.get(url).then(result=>{
if(result){
setSpining(false);
setData(result.data);
}
}).catch(error=>{setSpining(false);})
}
return(
<div className="sonarBox">
<FlexAJ>
<img src={sonar} alt=""/>
<Button type={"primary"} loading={spining} onClick={startCheck} disabled={permission !== "Admin" && permission !== "Owner" && permission !== "Manager"}>{spining ? "分析中":"质量分析"}</Button>
</FlexAJ>
<div className="sonarDesc">
<p>1. 基于 Sonar 的代码质量分析支持对项目进行文件数重复率统计代码逻辑缺陷扫描等功能并给出对应解决报告;</p>
<p>2. 目前支持 JavaJavaScriptC++GoPythonPHPRubyGroovyLuaTypeScriptWebXML</p>
<p>3. 默认支持代码文本格式全为 UTF-8其他编码可能会产生乱码</p>
<p>4. 仅保存最近一次分析结果;</p>
</div>
{
data && data.length>0 && data.map((i,k)=>{
return(
<div className="sonarData">
<p className="color-grey-9 font-16">分支<span className="color-grey-6">{i.branch_name}</span></p>
<div>
<span className="color-grey-9 mr30">开始时间<span className="color-grey-6">{i.created_at}</span></span>
<span className="color-grey-9">结束时间<span className="color-grey-6">{i.updated_at}</span></span>
</div>
<ul className="sonarList">
<li>
<span>{i.bug_num || "0"}</span>
<span>bugs</span>
</li>
<li>
<span>{i.loopholes || "0"}</span>
<span>漏洞</span>
</li>
<li>
<span>{i.repetition_rate || "--"}</span>
<span>重复</span>
</li>
<li>
<span>{i.file_num || "0"}</span>
<span>文件数</span>
</li>
</ul>
</div>
)
})
}
</div>
)
}
export default Index;

Some files were not shown because too many files have changed in this diff Show More