Compare commits

...

210 Commits

Author SHA1 Message Date
helinfeng 39fa2610df fix 2025-06-27 11:36:45 +08:00
helinfeng 7a42daa920 fix 2025-06-27 10:54:54 +08:00
helinfeng 28f44121ec fix 2025-06-27 10:35:55 +08:00
helinfeng e88c747eca issue:11677 2025-06-26 15:59:14 +08:00
helinfeng 1b7924df22 fix 2025-06-26 15:10:34 +08:00
helinfeng 690f3e58ca issue:11588 2025-06-24 16:27:16 +08:00
helinfeng 60f460bace issue:11588 2025-06-24 16:15:45 +08:00
helinfeng 5f21c58832 Merge remote-tracking branch 'origin/dev_edu_local' into dev_edu_local_suzhoudaxue 2025-06-24 09:08:54 +08:00
helinfeng 3d56307bd5 issue:11627 2025-06-23 18:32:41 +08:00
helinfeng 45650ad50c issue:11627 2025-06-23 17:50:24 +08:00
helinfeng fe0c5f3981 issue:11627 2025-06-23 17:46:28 +08:00
helinfeng 1a9de5bd13 Merge remote-tracking branch 'origin/dev_edu_local' into dev_edu_local_suzhoudaxue 2025-06-23 17:21:38 +08:00
helinfeng c255a36c9d issue:11627 2025-06-23 17:18:25 +08:00
helinfeng 4fd9750f30 issue:11588 2025-06-23 10:53:36 +08:00
向明星 0ce695ba08 质量分析功能迁移 2025-04-18 16:49:20 +08:00
向明星 55e5abf48c Merge remote-tracking branch 'fork/dev_edu_local' into dev_edu_local 2025-04-14 09:42:54 +08:00
向明星 b98b2f917d fix:顶部导航显示 2025-04-14 09:38:05 +08:00
ZhangRuining 26bdafa6c9 fix 2025-03-12 18:06:21 +08:00
ZhangRuining c68be919d0 fix 2025-03-12 13:38:50 +08:00
ZhangRuining ad3c20ec41 fix10277 2025-03-12 13:21:56 +08:00
ZhangRuining e21627767d fix传参错误 2025-03-12 11:44:54 +08:00
ZhangRuining 04ffef47da fix 审核管理 2025-03-11 21:58:26 +08:00
ZhangRuining be24551549 fix 2025-03-11 18:05:21 +08:00
ZhangRuining 1ddc705ea8 添加提示 2025-03-11 16:48:08 +08:00
ZhangRuining 5706710933 fix审核管理 2025-03-11 15:50:55 +08:00
ZhangRuining 6898b87d9d fix 2025-03-11 12:22:42 +08:00
ZhangRuining 69bb64c4de 10197 2025-03-11 11:43:22 +08:00
ZhangRuining 61eea3c3f7 test 2025-03-10 14:40:46 +08:00
向明星 8ab777d114 统一认证登录 2024-12-11 11:00:36 +08:00
linlu 7fa80109e8 fix 2024-12-10 18:24:31 +08:00
向明星 115c194091 统一认证登录 2024-12-10 14:47:31 +08:00
向明星 c1a745b873 fix 2024-11-10 18:22:44 +08:00
向明星 e3fe1f147e fix 2024-11-10 18:06:46 +08:00
向明星 ef83677ac6 提交 2024-11-10 17:57:35 +08:00
向明星 40f507f9b3 build问题 2024-11-01 18:31:19 +08:00
jasder duan 5ebf6795ba FIX code bug 2024-11-01 18:04:44 +08:00
jasder duan 7bb56bd5ed FIX code bug 2024-11-01 18:03:59 +08:00
jasder duan c57c26ceca FIX 跳转到头歌平台导航地址问题 2024-11-01 17:16:32 +08:00
向明星 0ee36d59a6 navbar路径跳转问题 2024-11-01 17:05:36 +08:00
caishi f309d8619c issue评论高度影响样式 2023-09-12 10:15:14 +08:00
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
219 changed files with 22009 additions and 13428 deletions

7
docker/.dockerignore Normal file
View File

@ -0,0 +1,7 @@
node_modules
.git
.DS_Store
dist
build
*.log
.env

12
docker/docker-compose.yml Normal file
View File

@ -0,0 +1,12 @@
version: '3.8'
services:
forgeplus-react:
image: node:12-alpine
volumes:
- ../../forgeplus-react:/app
working_dir: /app
ports:
- "3007:3007"
- "3000:3000"
command: sh -c "npm install && npm run start"

17359
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -25,11 +25,13 @@
"code-prettify": "^0.1.0",
"codemirror": "^5.53.0",
"connected-react-router": "4.4.1",
"cross-env": "^7.0.3",
"css-loader": "^3.5.2",
"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 +48,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 +65,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",
@ -98,6 +101,7 @@
"redux": "^4.0.5",
"redux-thunk": "2.3.0",
"rsuite": "^4.3.4",
"sass": "^1.82.0",
"sass-loader": "7.3.1",
"scroll-into-view": "^1.14.2",
"showdown": "^1.9.1",
@ -115,7 +119,7 @@
},
"scripts": {
"start": "node --max_old_space_size=15360 scripts/start.js",
"build": "NODE_ENV=production node --max_old_space_size=15360 scripts/build.js",
"build": "cross-env NODE_ENV=production node --max_old_space_size=15360 scripts/build.js",
"test-build": "NODE_ENV=testBuild node --max_old_space_size=15360 scripts/build.js",
"pre-build": "NODE_ENV=preBuild node --max_old_space_size=15360 scripts/build.js",
"gen_stats": "NODE_ENV=production webpack --profile --config=./config/webpack.config.prod.js --json > stats.json",
@ -178,7 +182,7 @@
"eslintConfig": {
"extends": "react-app"
},
"proxy": "http://localhost:3000",
"proxy": "http://172.16.100.182:81",
"port": "3007",
"devDependencies": {
"@babel/runtime": "7.0.0-beta.51",
@ -193,9 +197,9 @@
"concat": "^1.0.3",
"happypack": "^5.0.1",
"mockjs": "^1.1.0",
"node-sass": "^4.12.0",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"purgecss": "^2.1.2",
"react-json-view": "^1.12.3",
"reqwest": "^2.0.5",
"resize-observer-polyfill": "^1.5.1",
"terser-webpack-plugin": "^2.3.5",
@ -203,4 +207,4 @@
"webpack": "^4.42.1",
"webpack-bundle-analyzer": "^3.7.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

@ -460,7 +460,9 @@ textarea::-ms-input-placeholder {
.shixun_work_div {
overflow-y: auto;
max-height: 90px;
}html {
}
html {
height: 100%;
}
@ -1275,7 +1277,7 @@ html body {
color: #05101a;
height: 100%;
position: relative;
padding-right: 0px!important;
padding-right: 0px !important;
}
html,
@ -1307,6 +1309,7 @@ td,
span {
margin: 0;
padding: 0;
margin-bottom: 0px !important;
}
table,
@ -1566,10 +1569,14 @@ a.edu-txt-w80,
.font-50 {
font-size: 50px !important;
}
.font-80 {
font-size: 80px !important;
}
.color-grey-b{color: #bbb !important}
.color-grey-b {
color: #bbb !important
}
/*a标签的下划线*/
a.decoration {
@ -2325,8 +2332,8 @@ input::-ms-clear {
background-color: #F5F5F5;
}
.ant-modal-close{
top:8px!important;
.ant-modal-close {
top: 8px !important;
}
.newContainer {
@ -2346,7 +2353,6 @@ input::-ms-clear {
/*中间部分宽度固定为1200*/
.newMain {
margin: 0 auto;
padding-bottom: 110px;
min-width: 1200px;
}
@ -2480,10 +2486,12 @@ a.color-grey-c:hover {
.color-blue {
color: #4CACFF !important;
}
/* 绿色 */
.color-green-file{
.color-green-file {
color: #28BD6C;
}
/*主*/
.color-blue_4C {
color: #4CACFF !important;
@ -2667,18 +2675,23 @@ a.color-green:hover {
.width89 {
width: 89%;
}
.width50 {
width: 50%;
}
.width40 {
width: 40%;
}
.width45 {
width: 45%;
}
.width10 {
width: 10%;
}
.width20 {
width: 20%;
}
@ -2716,10 +2729,12 @@ a.edu-txt-w80,
.minH-560 {
min-height: 560px;
}
.minH-650{
.minH-650 {
min-height: 650px;
}
.minH-670{
.minH-670 {
min-height: 670px;
}
@ -3410,7 +3425,7 @@ a.user_bluebg_btn {
}
.cdefault {
cursor: default
cursor: default !important;
}
@ -3585,43 +3600,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 {
@ -3929,7 +3907,7 @@ html>body #ajax-indicator {
.markdown-body {
text-align: justify;
word-break: break-all;
width:100%;
width: 100%;
}
.RightPaneDrawer .ant-drawer-content {
@ -3943,13 +3921,26 @@ html>body #ajax-indicator {
.RightPaneDrawer .jupyter_data_list {
max-height: 340px;
}/*头部导航条样式---2018-03-19--by-cs*/
}
/*头部导航条样式---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,26 +3962,78 @@ html>body #ajax-indicator {
cursor: pointer;
position: relative;
font-size: 16px;
padding:0px 20px;
padding-right: 40px;
}
.downMenus {
background: #ffffff;
box-shadow: 0px 10px 16px 0px #cecece;
border-radius: 6px;
position: absolute;
z-index: 999;
left: -20px;
top: -15px;
padding: 20px;
line-height: 1.5;
min-width: 105px;
}
.downMenus li {
cursor: pointer;
}
.downMenus li:hover aside {
color: #165dff !important;
}
.namediv {
display: flex;
}
.head-nav ul#header-nav li a {
display: block;
/* display: block;
height: 100%;
width: 100%;
color: #333;
color: #fff;
font-size: 16px; */
display: flex;
align-items: center;
height: 100%;
width: 100%;
color: #fff;
font-size: 16px;
position: relative;
}
.head-nav ul#header-nav li a:hover,.head-nav ul#header-nav li.active a {
color: #5091FF;
#header-nav li .namediv i {
font-size: 14px !important;
display: inline-block;
color: #979797;
margin: 0px 3.58px 0px 3px;
transform: translateY(0.5px);
}
.head-nav ul#header-nav li a:hover,
.head-nav ul#header-nav li.active a {
color: #c5c5c5;
}
#header-nav li.active a::before {
content: '';
width: 100%;
height: 2px;
background: #c5c5c5;
position: absolute;
bottom: 17px;
left: 0;
border-radius: 1px;
}
.head-nav ul#header-nav li:last-child {
margin-right: 0px
}
.head-nav ul#header-nav li.active{
.head-nav ul#header-nav li.active {
/* background-color: #3B3B3B; */
}
@ -4108,21 +4151,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;
@ -6716,6 +6744,18 @@ ul.count_ul li:not(:last-child):after {
.ant-notification {
z-index: 10001 !important;
}
input.ant-input-lg::placeholder{
font-size: 14px !important;
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

File diff suppressed because it is too large Load Diff

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

@ -63,7 +63,7 @@ body {
/* md多空格 */
.markdown-body p {
margin:10px 0px!important;
margin: 10px 0px !important;
font-size: 16px !important;
line-height: 2 !important;
white-space: pre-wrap;

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,9 +11,7 @@ broadcastChannelOnmessage('refreshPage', () => {
})
function locationurl(list) {
if (window.location.port === "3007") {
} else {
if (window.location.port !== "3007") {
window.location.href = list
}
}
@ -28,70 +26,35 @@ if (isDev) {
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'
}
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://172.16.100.220:81";
// var proxy = 'http://172.16.100.130:81'
var proxy = 'http://172.16.100.182: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;
},
@ -146,8 +109,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 ""
}
}

310
src/classnames.scss Normal file
View File

@ -0,0 +1,310 @@
$NumberList: 2, 4, 5, 6, 8, 10, 12, 14, 15, 16, 18, 20, 22, 24, 28, 30, 32, 36, 40, 42, 44, 48, 56, 60, 64;
$BaseRem: 1; // rem基准为16px
@each $value in $NumberList {
.py-#{$value}px {
padding-top: #{$value}px;
padding-bottom: #{$value}px;
}
.px-#{$value}px {
padding-left: #{$value}px;
padding-right: #{$value}px;
}
.py-#{$value}px-rem {
padding-top: calc(#{$value} / #{$BaseRem})rem;
padding-bottom: calc(#{$value} / #{$BaseRem})rem;
}
.px-#{$value}px-rem {
padding-left: calc(#{$value} / #{$BaseRem})rem;
padding-right: calc(#{$value} / #{$BaseRem})rem;
}
}
@mixin generate-number-classes($property, $prefix) {
@each $value in $NumberList {
.#{$prefix}-#{$value}px {
#{$property}: #{$value}px;
}
.#{$prefix}-#{$value}px-rem {
#{$property}: calc(#{$value} / #{$BaseRem})rem;
}
.hover\:#{$prefix}-#{$value}px:hover {
#{$property}: #{$value}px;
}
.hover\:#{$prefix}-#{$value}px-rem:hover {
#{$property}: calc(#{$value} / #{$BaseRem})rem;
}
}
}
@include generate-number-classes('margin-top', 'mt');
@include generate-number-classes('margin-left', 'ml');
@include generate-number-classes('margin-right', 'mr');
@include generate-number-classes('margin-bottom', 'mb');
@include generate-number-classes('margin', 'm');
@include generate-number-classes('border-radius', 'rounded');
@include generate-number-classes('gap', 'gap');
@include generate-number-classes('padding', 'p');
@include generate-number-classes('padding-left', 'pl');
@include generate-number-classes('padding-right', 'pr');
@include generate-number-classes('padding-top', 'pt');
@include generate-number-classes('padding-bottom', 'pb');
@include generate-number-classes('font-size', 'text');
@include generate-number-classes('width', 'w');
@include generate-number-classes('height', 'h');
.w-1200px {
width: 1200px;
}
.bg-white {
background-color: #fff;
}
.ml-auto {
margin-left: auto;
}
.mr-auto {
margin-right: auto;
}
.rounded-full {
border-radius: 999px;
}
.h-full {
height: 100%;
}
.h-screen {
height: 100vh;
}
.w-full {
width: 100%;
}
.w-screen {
width: 100vw;
}
.w-auto {
width: auto;
}
.minW-0 {
min-width: 0;
}
.minH-0 {
min-height: 0;
}
.h-auto {
height: auto;
}
.flex {
display: flex;
}
.grid {
display: grid;
}
.hidden {
display: none;
}
.block {
display: block;
}
.flex-wrap {
flex-wrap: wrap;
}
.flex-1 {
flex: 1;
}
.flex-2 {
flex: 2;
}
.flex-col {
flex-direction: column;
}
.flex-col-reverse {
flex-direction: column-reverse;
}
.justify-start {
justify-content: flex-start;
}
.justify-center {
justify-content: center;
}
.justify-between {
justify-content: space-between;
}
.justify-around {
justify-content: space-around;
}
.justify-end {
justify-content: end;
}
.items-start {
align-items: flex-start;
}
.item-center {
align-items: center;
}
.item-end {
align-items: end;
}
.text-center {
text-align: center;
}
.text-start {
text-align: start;
}
.text-end {
text-align: end;
}
.text-nowrap {
white-space: nowrap;
}
.text-500 {
font-weight: 500;
}
.fixed {
position: fixed;
}
.sticky {
position: sticky;
}
.absolute {
position: absolute;
}
.relative {
position: relative;
}
.opacity-0 {
opacity: 0;
}
.font-thin {
font-weight: 100;
}
.font-extralight {
font-weight: 200;
}
.font-light {
font-weight: 300;
}
.font-normal {
font-weight: 400;
}
.font-medium {
font-weight: 500;
}
.font-semibold {
font-weight: 600;
}
.font-bold {
font-weight: 700;
}
.font-extrabold {
font-weight: 800;
}
.font-black {
font-weight: 900;
}
.pointer-events-auto {
pointer-events: auto;
}
.pointer-events-none {
pointer-events: none;
}
.overflow-hidden {
overflow: hidden;
}
.overflow-auto {
overflow: auto;
}
.group-hover-show {
.hover-div {
display: none;
}
&:hover {
.hover-div {
display: block;
}
.hover-div-hidden {
display: none;
}
}
}
.bg-transparent {
background-color: transparent;
}
.scale-110 {
transform: scale(1.1);
}
.hover\:scale-110:hover {
transform: scale(1.1);
}
.transition-all {
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.cursor-pointer {
cursor: pointer;
}

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

@ -1,7 +1,7 @@
import React , { Component } from 'react';
import { Dropdown , Menu , Icon , Pagination , Spin } from 'antd';
import React, { Component } from 'react';
import { Dropdown, Menu, Icon, Pagination, Spin } from 'antd';
import '../css/index.scss';
import '../Branch/branch.css';
import '../Branch/branch.scss';
import './activity.css';
import NoneData from '../Nodata';
@ -11,126 +11,126 @@ import axios from 'axios';
const LIMIT = 15;
const ARRAY = [
{
id:1,
name:'1天'
id: 1,
name: '1天'
},
{
id:3,
name:'3天'
id: 3,
name: '3天'
},
{
id:7,
name:'1周'
id: 7,
name: '1周'
},
{
id:30,
name:'1个月'
id: 30,
name: '1个月'
}
]
class Activity extends Component{
constructor(props){
class Activity extends Component {
constructor(props) {
super(props);
this.state={
time:'30',
type:undefined,
state:undefined,
page:1,
this.state = {
time: '30',
type: undefined,
state: undefined,
page: 1,
data:undefined,
project_trends:undefined,
data: undefined,
project_trends: undefined,
isSpin:false
isSpin: false
}
}
componentDidMount=()=>{
const { time,type,status,page } = this.state;
componentDidMount = () => {
const { time, type, status, page } = this.state;
this.setState({
isSpin:true
isSpin: true
})
this.getInfo(time,type,status,page);
this.getInfo(time, type, status, page);
}
getInfo =(time,type,status,page)=>{
const { projectsId , owner } = this.props.match.params;
const url = `/${owner}/${projectsId}/activity.json`;
axios.get(url,{
params:{
time,type,status,page
getInfo = (time, type, status, page) => {
const { projectsId, owner } = this.props.match.params;
const url = `/${owner}/${projectsId}/activity.json`;
axios.get(url, {
params: {
time, type, status, page
}
}).then(result=>{
if(result){
}).then(result => {
if (result) {
this.setState({
data:result.data,
project_trends:result.data.project_trends,
isSpin:false
data: result.data,
project_trends: result.data.project_trends,
isSpin: false
})
}
}).catch(error=>{
}).catch(error => {
console.log(error);
})
}
// 切换周期
changeTime=(e)=>{
changeTime = (e) => {
this.setState({
time:e.key,
isSpin:true
time: e.key,
isSpin: true
})
const { type,status,page } = this.state;
this.getInfo(e.key,type,status,page);
const { type, status, page } = this.state;
this.getInfo(e.key, type, status, page);
}
//筛选
changeTrends=(type,status)=>{
//筛选
changeTrends = (type, status) => {
this.setState({
type,status
type, status
})
const {time,page}=this.state;
this.getInfo(time,type,status,page);
}
const { time, page } = this.state;
this.getInfo(time, type, status, page);
}
// 分页
ChangePage=(page)=>{
ChangePage = (page) => {
this.setState({
page
})
const { time,type,status } = this.state;
this.getInfo(time,type,status,page);
const { time, type, status } = this.state;
this.getInfo(time, type, status, page);
}
menu =()=> (
menu = () => (
<Menu>
{
ARRAY && ARRAY.map((item,key)=>{
return(
ARRAY && ARRAY.map((item, key) => {
return (
<Menu.Item key={item.id} onClick={this.changeTime}>{item.name}</Menu.Item>
)
})
}
</Menu>
)
render(){
const { time , data , page , project_trends , isSpin } = this.state;
render() {
const { time, data, page, project_trends, isSpin } = this.state;
let name = time && ARRAY.filter(item=>item.id === parseInt(time)) ;
const second_per = (parseInt(data && data.close_issues_count)/parseInt(data && data.issues_count)*100)+'%';
const third_per = (parseInt(data && data.close_issues_count)/parseInt(data && data.issues_count)*100)+'%';
const fourth_per = (parseInt(data && data.open_issues_count)/parseInt(data && data.issues_count)*100)+'%';
return(
let name = time && ARRAY.filter(item => item.id === parseInt(time));
const second_per = (parseInt(data && data.close_issues_count) / parseInt(data && data.issues_count) * 100) + '%';
const third_per = (parseInt(data && data.close_issues_count) / parseInt(data && data.issues_count) * 100) + '%';
const fourth_per = (parseInt(data && data.open_issues_count) / parseInt(data && data.issues_count) * 100) + '%';
return (
<div className="main">
<div className="normalBox">
<div class="normalBox-title">概览</div>
<div className="orderInfo">
<div>
<div className="percentLine prPercent">
<p className="percent_purple" style={{width:'100%'}}></p>
<p className="percent_green resetStyle" style={{width:`${second_per}`}}></p>
<p className="percent_purple" style={{ width: '100%' }}></p>
<p className="percent_green resetStyle" style={{ width: `${second_per}` }}></p>
</div>
<span>{data && data.pr_all_count}合并请求</span>
</div>
<div>
<div className="percentLine">
<p className="percent_red" style={{width:`${third_per}`}}></p>
<p className="percent_green" style={{width:`${fourth_per}`}}></p>
<p className="percent_red" style={{ width: `${third_per}` }}></p>
<p className="percent_green" style={{ width: `${fourth_per}` }}></p>
</div>
<span>{data && data.issues_count}任务</span>
</div>
@ -138,19 +138,19 @@ class Activity extends Component{
<ul className="percentBox">
<li>
<span className="purple">{data && data.pr_count}</span>
<span className="change" onClick={()=>this.changeTrends("PullRequest","close")}>已处理的合并请求</span>
<span className="change" onClick={() => this.changeTrends("PullRequest", "close")}>已处理的合并请求</span>
</li>
<li>
<span className="green">{data && data.new_pr_count}</span>
<span className="change" onClick={()=>this.changeTrends("PullRequest","create")}>未处理的合并请求</span>
<span className="change" onClick={() => this.changeTrends("PullRequest", "create")}>未处理的合并请求</span>
</li>
<li>
<span className="red">{data && data.close_issues_count}</span>
<span className="change" onClick={()=>this.changeTrends("Issue","close")}>已关闭的任务</span>
<span className="change" onClick={() => this.changeTrends("Issue", "close")}>已关闭的任务</span>
</li>
<li>
<span className="green">{data && data.open_issues_count}</span>
<span className="change" onClick={()=>this.changeTrends("Issue","create")}>未处理的任务</span>
<span className="change" onClick={() => this.changeTrends("Issue", "create")}>未处理的任务</span>
</li>
</ul>
</div>
@ -159,7 +159,7 @@ class Activity extends Component{
<Dropdown overlay={this.menu()} trigger={['click']} placement="bottomLeft">
<a className="ant-dropdown-link">
<span className="color-grey-9 mr3">周期:</span>
{name && name.length>0 && name[0].name} <Icon type="down" />
{name && name.length > 0 && name[0].name} <Icon type="down" />
</a>
</Dropdown>
</div>
@ -167,17 +167,17 @@ class Activity extends Component{
<Spin spinning={isSpin}>
{
project_trends && project_trends.length > 0 ?
<div className="activity_list">
{
project_trends && project_trends.map((item,key)=>{
return(
<ActivityItem item={item} {...this.props} ></ActivityItem>
)
})
}
</div>
:
<NoneData _html="暂时还没有相关数据!" />
<div className="activity_list">
{
project_trends && project_trends.map((item, key) => {
return (
<ActivityItem item={item} {...this.props} ></ActivityItem>
)
})
}
</div>
:
<NoneData _html="暂时还没有相关数据!" />
}
</Spin>
{

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

@ -1,39 +1,27 @@
import React, { Component } from 'react';
import { Dropdown, Icon, Tooltip } from 'antd';
import "./branch.css";
import React, { useState } from 'react';
import { Menu } from 'antd';
import "./branch.scss";
import CopyTool from '../Component/CopyTool';
class CloneAddress extends Component {
// 点击按钮复制功能
jsCopy = () => {
var e = document.getElementById("copy_rep_content");
e.select();
document.execCommand("Copy");
}
render() {
const { http_url, downloadUrl } = this.props;
return (
<div className="gitAddressClone">
{/* <p className="addressTips"><span>版本库地址已变更,请基于新地址提交代码</span></p> */}
{
http_url && <span>HTTP</span>
}
<input type="text" id="copy_rep_content" value={http_url} />
<Tooltip title="复制链接">
<span className="color-blue" onClick={() => this.jsCopy()}><i className="iconfont icon-fuzhi"></i></span>
</Tooltip>
{
downloadUrl &&
<span>
<Dropdown overlay={downloadUrl} trigger={['click']} placement="bottomRight">
<a className="ant-dropdown-link">
<Icon type="cloud-download" className="font-18 fl color-blue" />
</a>
</Dropdown>
</span>
}
function CloneAddress({ http_url, ssh_url, zip_url, tar_url }) {
const [key, setKey] = useState("HTTP");
return (
<div className="downMenu">
<div>
<Menu className="urlMenu" selectedKeys={[key]} mode={"horizontal"}>
<Menu.Item key="HTTP" onClick={(e) => { setKey(e.key) }}>HTTP</Menu.Item>
{ssh_url && <Menu.Item key="SSH" onClick={(e) => { setKey(e.key) }}>SSH</Menu.Item>}
</Menu>
<div className="gitAddressClone">
<input type="text" id="copy_rep_content" value={key === "HTTP" ? http_url : ssh_url} />
<CopyTool inputId="copy_rep_content" className="copytool" />
</div>
</div>
)
}
<Menu className="edu-txt-center">
{zip_url && <Menu.Item style={{ borderTop: "1px solid #eee" }}><a href={zip_url}>下载 ZIP</a></Menu.Item>}
{tar_url && <Menu.Item><a href={tar_url}>下载 TAR.GZ</a></Menu.Item>}
</Menu>
</div>
)
}
export default CloneAddress;

View File

@ -1,73 +1,73 @@
import React , { useState , useEffect } from 'react';
import { Popover , Input , Spin } from 'antd';
import './branch.css';
import { getBranch , getTag } from '../GetData/getData';
import React, { useState, useEffect } from 'react';
import { Popover, Input, Spin } from 'antd';
import './branch.scss';
import { getBranch, getTag } from '../GetData/getData';
export default (({ projectsId , branch , owner , changeBranch , branchList , tagflag = true })=>{
const [ showValue , setShowValue ] = useState(branch);
const [ inputValue , setInputValue] = useState(undefined);
const [ nav , setNav ] = useState(0);
const [ isSpin , setIsSpin ] = useState(true);
const [ flag , setFlag ] = useState(false);
export default (({ projectsId, branch, owner, changeBranch, branchList, tagflag = true }) => {
const [showValue, setShowValue] = useState(branch);
const [inputValue, setInputValue] = useState(undefined);
const [nav, setNav] = useState(0);
const [isSpin, setIsSpin] = useState(true);
const [flag, setFlag] = useState(false);
const [ data , setData ] = useState(undefined);
const [ datas , setDatas ] = useState(undefined);
const [data, setData] = useState(undefined);
const [datas, setDatas] = useState(undefined);
useEffect(()=>{
useEffect(() => {
setShowValue(branch);
},[branch])
}, [branch])
useEffect(()=>{
useEffect(() => {
document.body.addEventListener('click', e => {
let name = e.target.className;
let turn = name === "ant-input OptionsInput" || name === "navli active"|| name === "navli" || name === "padding10 bor-bottom-greyE";
if(turn){
let turn = name === "ant-input OptionsInput" || name === "navli active" || name === "navli" || name === "padding10 bor-bottom-greyE";
if (turn) {
return;
}else{
} else {
setFlag(false);
}
})
})
useEffect(()=>{
if(branchList){
useEffect(() => {
if (branchList) {
setData(branchList);
setDatas(branchList);
setIsSpin(false);
}
},[branchList])
}, [branchList])
async function getBranchs(id,owner){
let result = await getBranch(id,owner);
async function getBranchs(id, owner) {
let result = await getBranch(id, owner);
setData(result);
setDatas(result);
setIsSpin(false);
}
async function getTags(id,owner){
let result = await getTag(id,owner);
async function getTags(id, owner) {
let result = await getTag(id, owner);
setData(result);
setDatas(result);
setIsSpin(false);
}
function changeInputValue(e){
function changeInputValue(e) {
setInputValue(e.target.value);
let filter = e.target.value ? data && data.length>0 && data.filter(item=>item.name.indexOf(e.target.value)>-1) : data;
let filter = e.target.value ? data && data.length > 0 && data.filter(item => item.name.indexOf(e.target.value) > -1) : data;
setDatas(filter);
}
function changeNav(nav){
function changeNav(nav) {
setNav(nav);
setIsSpin(true);
if(nav === 0){
getBranchs(projectsId,owner);
}else{
getTags(projectsId,owner);
if (nav === 0) {
getBranchs(projectsId, owner);
} else {
getTags(projectsId, owner);
}
}
function chooseitem(value){
function chooseitem(value) {
// setShowValue(value);
changeBranch(value);
}
@ -76,33 +76,33 @@ export default (({ projectsId , branch , owner , changeBranch , branchList , tag
const menu = (
<div>
<div className="padding10 bor-bottom-greyE">
<Input
placeholder="请输入分支或标签名称搜索"
autocomplete="off" className="OptionsInput" value={inputValue}
onChange={changeInputValue} style={{width:"220px"}}
<Input
placeholder="请输入分支或标签名称搜索"
autocomplete="off" className="OptionsInput" value={inputValue}
onChange={changeInputValue} style={{ width: "220px" }}
/>
<ul className="navUl">
<li className={nav === 0?"navli active":"navli"} onClick={()=>changeNav(0)}><i className="iconfont icon-fenzhi1 font-14 mr3"></i>分支列表</li>
{ tagflag && <li className={nav === 1?"navli active":"navli"} onClick={()=>changeNav(1)}><i className="iconfont icon-biaoqian3 font-14 mr3"></i>标签列表</li> }
<li className={nav === 0 ? "navli active" : "navli"} onClick={() => changeNav(0)}><i className="iconfont icon-fenzhi1 font-14 mr3"></i>分支列表</li>
{tagflag && <li className={nav === 1 ? "navli active" : "navli"} onClick={() => changeNav(1)}><i className="iconfont icon-biaoqian3 font-14 mr3"></i>标签列表</li>}
</ul>
</div>
<Spin spinning={isSpin}>
<ul className="OptionsUl" id="ul-btn">
{
datas && datas.length>0 ?
datas.map((item,key)=>{
return(
<li key={key} onClick={()=>chooseitem(item.name)}><a className="task-hide ulALink">{item.name}</a></li>
)
}):
<p className="listTips">暂无{inputValue}{nav === 0 ?"分支":"标签"}~</p>
datas && datas.length > 0 ?
datas.map((item, key) => {
return (
<li key={key} onClick={() => chooseitem(item.name)}><a className="task-hide ulALink">{item.name}</a></li>
)
}) :
<p className="listTips">暂无{inputValue}{nav === 0 ? "分支" : "标签"}~</p>
}
</ul>
</Spin>
</div>
);
return(
<Popover placement="bottom" visible={flag} content={menu} onClick={()=>setFlag(!flag)} overlayClassName="branch-tagBox-list">
return (
<Popover placement="bottom" visible={flag} content={menu} onClick={() => setFlag(!flag)} overlayClassName="branch-tagBox-list">
<div className="branch-tagBox">
{/* {nav === 0 ?"分支":"标签"} */}
<span className="color-grey-9 mr3 ml8"><i className="iconfont icon-fenzhi2 font-18"></i></span>

View File

@ -1,84 +0,0 @@
.branchDropdown{
border:1px solid #eee;
border-radius: 4px;
display: flex;
justify-content: center;
height: 40px;
line-height: 40px;
min-width: 220px;
}
.branchDropdown .ant-dropdown-trigger{
width: 100%;
padding:0px 15px;
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
}
.branchOptions{
width: 220px;
box-shadow: 0px 0px 3px 1px rgba(134, 134, 134, 0.4);
border-radius: 3px;
background: #fff;
max-height: 300px;
}
.OptionsUl{
max-height: 220px;
overflow-y: auto;
}
.OptionsUl li{
height: 35px;
line-height: 35px;
cursor: pointer;
padding:0px 10px;
}
.OptionsUl li:hover{
background-color: #F0F0F0;
}
.OptionsUl li a{
display: block;
}
.OptionsInput{
height: 32px;
padding-left: 4px;
line-height: 32px;
width: 100%;
}
.branch-tagBox{
border:1px solid #eee;
border-radius: 3px;
height: 40px;
display: flex;
align-items: center;
cursor: pointer;
min-width: 140px;
}
.branch-tagBox-list .ant-popover-arrow{
display: none;
}
.branch-tagBox-list.ant-popover.ant-popover-placement-bottom{
padding-top:0px;
}
.branch-tagBox .ant-dropdown-link{
display: block;
flex:1;
}
.branch-tagBox-list .ant-popover-inner-content{
padding:0px;
}
.navUl{
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 5px;
}
.navUl li{
cursor: pointer;
}
.navUl li.active{
color:#5091FF;
}
.listTips{
padding:20px 0px;
text-align: center;
}

View File

@ -0,0 +1,131 @@
.branchDropdown {
border: 1px solid #eee;
border-radius: 4px;
display: flex;
justify-content: center;
height: 40px;
line-height: 40px;
min-width: 220px;
}
.branchDropdown .ant-dropdown-trigger {
width: 100%;
padding: 0px 15px;
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
}
.branchOptions {
width: 220px;
box-shadow: 0px 0px 3px 1px rgba(134, 134, 134, 0.4);
border-radius: 3px;
background: #fff;
max-height: 300px;
}
.OptionsUl {
max-height: 220px;
overflow-y: auto;
}
.OptionsUl li {
height: 35px;
line-height: 35px;
cursor: pointer;
padding: 0px 10px;
}
.OptionsUl li:hover {
background-color: #F0F0F0;
}
.OptionsUl li a {
display: block;
}
.OptionsInput {
height: 32px;
padding-left: 4px;
line-height: 32px;
width: 100%;
}
.branch-tagBox {
border: 1px solid #eee;
border-radius: 3px;
height: 40px;
display: flex;
align-items: center;
cursor: pointer;
min-width: 140px;
}
.branch-tagBox-list .ant-popover-arrow {
display: none;
}
.branch-tagBox-list.ant-popover.ant-popover-placement-bottom {
padding-top: 0px;
}
.branch-tagBox .ant-dropdown-link {
display: block;
flex: 1;
}
.branch-tagBox-list .ant-popover-inner-content {
padding: 0px;
}
.navUl {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 5px;
}
.navUl li {
cursor: pointer;
}
.navUl li.active {
color: #5091FF;
}
.listTips {
padding: 20px 0px;
text-align: center;
}
.urlMenu {
line-height: 30px;
margin-bottom: 10px;
padding: 15px 20px 0px 20px;
border-bottom: none;
li.ant-menu-item {
height: 30px;
line-height: 30px;
padding: 0px 5px;
margin-right: 20px !important;
&.ant-menu-item-selected,
&.ant-menu-item-active {
color: #333;
}
&.ant-menu-item-selected {
border-color: #466aff !important;
}
&.ant-menu-item-active {
border-color: transparent;
}
}
}
.copytool {
margin: 0px 10px;
}

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

@ -0,0 +1,51 @@
import React, { useState, useCallback, memo } from 'react';
import { Tooltip } from 'antd';
CopyTool.defaultProps = {
beforeText: '复制链接', //
afterText: '复制成功', //
className: '', //svgclass
inputId: 'copyText', //ID
timeOut: true, //beforeText
};
function CopyTool({ beforeText, afterText, className, inputId, timeOut }) {
const [title, setTitle] = useState(() => {
return beforeText;
});
//
const copyUrl = useCallback(() => {
const copyEle = document.querySelector(`#${inputId}`); //
if (!copyEle) {
console.error("您的CopyTool未设置正确的inputId");
return;
}
copyEle.select(); //
if (document.execCommand('copy')) {
document.execCommand('copy');
}
document.getSelection().removeAllRanges();
setTitle(afterText);
if (timeOut) {
setTimeout(function () {
setTitle(beforeText);
}, 1500)
}
}, []);
return (
<Tooltip
placement="top"
title={title}
onVisibleChange={() => { setTitle(beforeText) }}
>
<i className={`iconfont icon-fuzhiicon ${className}`} style={{ color: '#466aff' }} onClick={copyUrl}></i>
</Tooltip>
);
}
export default memo(CopyTool);

View File

@ -0,0 +1,33 @@
import React from 'react';
import { AlignCenter } from '../layout';
import { Button } from 'antd';
import Modals from '../PublicModal/Index';
function DeleteBox({
visible ,
onCancel ,
onSuccess ,
title ,
subTitle,
content
}) {
return(
<Modals
title={title}
btn={
<div>
<Button size={'large'} onClick={onCancel}>取消</Button>
<Button type={"danger"} size={"large"} onClick={onSuccess}>确认删除</Button>
</div>
}
onCancel={onCancel}
visible={visible}
>
<div className="desc">
<AlignCenter className="descMain"><i className="iconfont icon-shanchu_tc_icon mr10"></i>{content}</AlignCenter>
<p className="task-hide-2" style={{WebkitLineClamp:5}}>删除后未来事件将不会推送至此Webhook地址<span title={subTitle}>{subTitle}</span></p>
</div>
</Modals>
)
}
export default DeleteBox;

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,13 @@
.ant-modal-mask {
z-index: 1031;
}
.ant-modal-wrap {
z-index: 1032;
}
.ant-modal-wrap {
.ant-form-explain {
position: absolute;
}
}

View File

@ -0,0 +1,66 @@
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}
// visible={false}
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

@ -0,0 +1,62 @@
import React, {useEffect, useRef, useState} from 'react';
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js';
import './Component.scss';
function Monaco(props) {
const {
style = { // dom
height: '400px',
},
value = '', //
onChange = () => { //
},
fontSize = 14, //
monacoOptions = {
scrollBeyondLastLine: false,
lineNumbers: "off",
wordWrap: true,
overviewRulerBorder: true,
lineHeight: 24,
readOnly:true
}, // monaco
language = 'html', // js ts sql css json html
} = props;
const editOrRef = useRef();
const ThisEditor = useRef();
useEffect(() => {
ThisEditor.current = monaco.editor.create(editOrRef.current, {
value: value || '',
language,
theme: "vs-grey",
fontSize: fontSize + 'px',
minimap: { //
enabled: false,
},
...monacoOptions,
});
ThisEditor.current.onDidChangeModelContent((e) => {
let newValue = ThisEditor.current.getValue();
onChange(newValue);
});
return () => {
ThisEditor.current.dispose();
ThisEditor.current = undefined; //
}
}, []);
useEffect(() => {
if (ThisEditor.current) {
ThisEditor.current.updateOptions({
fontSize: fontSize + 'px',
})
}
}, [fontSize]);
return (
<div style={style} ref={editOrRef}>
</div>
);
}
export default Monaco;

View File

@ -0,0 +1,20 @@
import React from 'react';
import { Modal } from 'antd';
import './Index.scss';
function Modals({title,children,btn,onCancel,visible}) {
return(
<Modal
visible={visible}
onCancel={onCancel}
title={title}
width={"520px"}
footer={btn}
centered={true}
wrapClassName={"deleteBox"}
>
{children}
</Modal>
)
}
export default Modals;

View File

@ -0,0 +1,75 @@
.deleteBox{
z-index: 1033;
.ant-modal-close-x{
font-size: 17px!important;
}
.ant-modal-header{
background-color: #f8f8f8;
padding:10px 30px;
.ant-modal-title{
text-align: left;
font-size: 16px;
font-weight: bold;
}
}
.ant-modal-close{
top:0px !important;
font-size: 24px !important;
}
.ant-modal-body{
padding:30px 50px;
p{
font-size: 14px;
line-height: 26px;
color:#666;
word-break: break-all;
}
.desc{
.descMain{
align-items: center;
justify-content: center;
font-size: 20px;
margin-bottom: 10px;
i.red{
color:#DF0002;
}
}
}
}
.ant-modal-footer{
border-top: none;
text-align: center;
padding-bottom: 40px;
button,a{
width: 96px;
height: 32px;
margin:0px 20px;
font-weight: 400;
font-size: 14px;
&.ant-btn{
border-color: #D0D0D0;
color: #666;
&:hover,&:active,&:focus{
background: #f3f4f6;
}
}
&.ant-btn-danger{
background-color: #fff;
color: #DF0002;
border-color: #D0D0D0;
&:hover,&:active,&:focus{
border-color: #DF0002;
background-color: #fff;
}
}
&.ant-btn.ant-btn-primary{
background-color: #466AFF;
color: #fff;
border-color: #466AFF;
&:hover,&:focus,&:active{
background-color: rgba(70,106,255,0.85);
}
}
}
}
}

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

@ -7,6 +7,8 @@ export const Banner = styled.div`{
border-bottom:1px solid #eee;
background-color:#fff;
border-radius:5px 5px 0px 0px;
justify-content: space-between;
display: flex;
}`
export const AlignCenterBetween = styled.div`{
display:flex;
@ -28,6 +30,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,270 @@
import React, { useState, forwardRef, useEffect } from 'react';
import { Form, Modal, Input, Radio, Upload, Button, Icon, message } from 'antd';
import Axios from 'axios';
import { getUploadActionUrl, appendFileSizeToUploadFileAll } from 'educoder';
const JoinProjectForm = Form.create({
name: 'join_project_form',
})(
forwardRef((props) => {
const { getFieldDecorator, validateFields } = props.form;
const { onOk, onCancel } = props;
useEffect(() => {
if (props.visible) {
props.form.setFieldsValue({
code: '',
role: 'developer',
});
}
}, [props.visible])
function checkValue(rule, value, callback) {
if (!value) {
callback();
}
if (value.length < 6 || value.length > 6) {
callback('请输入6位数的邀请码');
}
callback();
}
const handleOk = () => {
validateFields((error, values) => {
if (!error) {
onOk(values);
}
});
};
return (
<Modal
title="加入项目"
width="480px"
visible={props.visible}
centered={true}
onOk={handleOk}
onCancel={onCancel}
>
<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>
<Radio value="manager">管理员</Radio>
<Radio value="developer">开发者</Radio>
<Radio value="reporter">报告者</Radio>
</Radio.Group>
)}
</Form.Item>
</Form>
</Modal>
);
})
);
const PersonalInfoForm = Form.create({
name: 'personal_info_form',
})(
forwardRef((props) => {
const { getFieldDecorator, validateFields } = props.form;
const { onOk, onCancel, contentFileList, uploadProps } = props;
useEffect(() => {
if (props.visible) {
props.form.setFieldsValue({
remarks: '',
attachment_id: '',
});
}
}, [props.visible])
const handleOk = () => {
validateFields((error, values) => {
if (!error) {
const attachmentId = contentFileList.map((item) => {
return item.response ? item.response.id : item.id;
});
onOk({ ...values, attachment_id: attachmentId.join(',') });
}
});
};
return (
<Modal
title="填写个人信息"
width="480px"
visible={props.visible}
centered={true}
onOk={handleOk}
onCancel={onCancel}
>
<Form layout="inline" className="inviteForm2">
<Form.Item label="个人情况">
{getFieldDecorator('remarks', {
rules: [{ required: true, message: '请输入个人情况' }],
})(
<Input
placeholder="请输入个人情况"
// autoComplete="off"
maxLength={200}
style={{ width: '300px', pointerEvents: 'auto' }} // pointer-events auto
/>
)}
</Form.Item>
<Form.Item label="个人资料" style={{ flexWrap: 'nowrap' }}>
{getFieldDecorator('attachment_id', {
rules: [],
})(
<Upload {...uploadProps}>
<Button type="default" className='mr5'>
<Icon type="upload" /> 上传附件
</Button>
(单个文件最大20MB)
</Upload>
)}
</Form.Item>
</Form>
</Modal>
);
})
);
export default forwardRef((props) => {
const [visible, setVisible] = useState(false);
const [infoVis, setInfoVis] = useState(false);
const [contentFileList, setContentFileList] = useState([]);
//
const [joinProjectValues, setJoinProjectValues] = useState({});
const uploadProps = {
fileList: contentFileList,
multiple: false,
action: `${getUploadActionUrl()}`,
onChange: (info) => {
let content = info.fileList;
setContentFileList(appendFileSizeToUploadFileAll(content));
},
onRemove: (file) => {
if (file.response != undefined) {
Modal.confirm({
content: '是否确认删除?',
onOk: () => {
deleteAttachment(file);
},
onCancel() {
console.log('Cancel');
},
});
return false;
}
},
beforeUpload: (file) => {
console.log('beforeUpload', file.name);
const isLt150M = file.size / 1024 / 1024 <= 20;
if (!isLt150M) {
message.error('文件大小必须不超过20MB!');
}
return isLt150M;
},
};
const deleteAttachment = (file) => {
const url = `/attachments/${file.response ? file.response.id : file.uid}.json`;
Axios.delete(url, {})
.then((response) => {
if (response.data) {
const { status } = response.data;
if (status === 0) {
setContentFileList([]);
}
}
})
.catch((error) => {
console.log(error);
});
};
const handleJoinProjectOk = (values) => {
const url = `/applied_projects.json`;
setJoinProjectValues({ ...values })
Axios.post(url, {
applied_project: {
alert_remarks: false,
...values,
},
})
.then((result) => {
if (result) {
setVisible(false);
if (result.data.data.need_verify) {
setInfoVis(true);
} else {
props.showNotification('申请已提交,请等待项目管理者审核!');
}
}
})
.catch((error) => {
console.log(error);
});
};
const handlePersonalInfoOk = (values) => {
const url = `/applied_projects.json`;
let attachmentId = contentFileList.map(item => {
return item.response ? item.response.id : item.id
})
Axios.post(url, {
applied_project: {
...joinProjectValues,
alert_remarks: true,
...values,
attachment_id: attachmentId.join(','),
},
})
.then((result) => {
if (result && result.data) {
setInfoVis(false);
setContentFileList([]);
props.showNotification('申请已提交,请等待项目管理者审核!');
}
})
.catch((error) => {
console.log(error);
});
};
return (
<React.Fragment>
<JoinProjectForm
visible={visible}
onOk={handleJoinProjectOk}
onCancel={() => setVisible(false)}
/>
<PersonalInfoForm
visible={infoVis}
onOk={handlePersonalInfoOk}
onCancel={() => setInfoVis(false)}
contentFileList={contentFileList}
uploadProps={uploadProps}
/>
<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, Divider } 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 这部分脚本从公共脚本中直接调用
@ -20,7 +15,7 @@ let old_url;
window._header_componentHandler = null;
// 非trustie链接则新开页跳转
const str = ['www.trustie.net','forgeplus.trustie.net','forum.trustie.net','testforgeplus.trustie.net']
const str = ['www.trustie.net', 'forgeplus.trustie.net', 'forum.trustie.net', 'testforgeplus.trustie.net']
class NewHeader extends Component {
constructor(props) {
super(props)
@ -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,
openSearch: false,
}
}
componentDidMount() {
// this.getAppdata();
this.geturlsdata();
window._header_componentHandler = this;
@ -81,37 +72,38 @@ class NewHeader extends Component {
//获取游览器地址
try {
window.sessionStorage.setItem("yslgeturls", JSON.stringify(window.location.href))
} catch (e) {}
} catch (e) { }
}
SearchInput = (open,item)=>{
if(open){
return(
SearchInput = (open, item) => {
if (open) {
return (
<div
onBlur={() => {
setTimeout(() => {
this.setState({
openSearch:false
openSearch: false
})
}, 300)
}}
>
<Search placeholder="实践课程/教学课堂/实践项目/交流问答"
<Search placeholder="请输入搜索关键字"
className={`search-input mr20`}
onSearch={(value)=>this.onGlobalSearch(value,item)}
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={() => {
this.setState({openSearch:true})
} else {
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;
onGlobalSearch = (value, item) => {
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();
}
@ -447,58 +246,91 @@ class NewHeader extends Component {
matchpaths = (url) => {
const { match } = this.props;
if(url){
if (match.path.indexOf(url) > -1) {
return true
}else {
return false
}
if (url) {
return match.path.includes(url)
}
}
// 处理弹框
setgoshowqqgtounp = (bool) => {
this.setState({
goshowqqgtounp: bool
})
}
addMenu=(list)=>{
return(
list && list.length >0 &&
<div className="dropdownFlex">
<Menu>
addMenu = (list) => {
return (
list && list.length > 0 &&
<div className="dropdownFlex double">
<Menu>
{
list.map((item,key)=>{
return(
(item.name !=="加入课堂" && item.name !=="加入开发项目") && <Menu.Item><a href={item.url}>{item.name}</a></Menu.Item>
list.map((item, key) => {
return (
(item.name !== "加入课堂" && item.name !== "加入开发项目") && <Menu.Item><a href={item.url}>{item.name}</a></Menu.Item>
)
})
}
<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>
)
}
menus = (item) => {
return (
<div className="downMenus">
{
item.map((i, k) => {
return (
!i.hidden && <li onClick={() => { window.location.href = this.checkLink(i.link) }}>
{k !== 0 && <Divider />}
<aside className="font-16 color-ooo">{i.name}</aside>
<div className="font-14" style={{ color: "rgb(159, 167, 179)", whiteSpace: 'nowrap' }}>{i.content}</div>
</li>
)
})
}
</div>
)
}
checkLink = (link) => {
let { settings, } = this.state;
if (link) {
if (link.indexOf("http") > -1) {
return link;
} else {
return settings.new_course.default_url + link;
}
} else {
return ''
}
}
render() {
const { match} = this.props;
const { match } = this.props;
let current_user = this.props.user;
let { Addcoursestypes,
tojoinitemtype,
tojoinclasstitle,
code_notice,
checked_notice,
AccountProfiletype,
submitapplications,
submitapplicationsvalue,
user,
isRender,
showSearchOpentype,
headtypesonClickbool,
headtypess,
settings,
goshowqqgtounp,
openSearch,
} = this.state;
/*用户名称 用户头像url*/
@ -557,7 +389,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 +409,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">
@ -596,131 +428,105 @@ class NewHeader extends Component {
{...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>
settings && settings.nav_logo_url ?
<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 ?
<ul id="header-nav">
{
settings.navbar && settings.navbar.map((item, key) => {
var new_link = item.link;
var user_login = this.props.user && this.props.user.login;
var is_hidden = item.hidden
if (new_link && (new_link.indexOf("courses") > -1 || new_link.indexOf("contests") > -1)) {
if (user_login) {
if (new_link.indexOf("courses") > -1) {
new_link = new_link.replace(/courses/g, "users/" + user_login + "/courses")
} else if (new_link.indexOf("contests") > -1) {
new_link = new_link.replace(/contests/g, "users/" + user_login + "/contests")
<ul id="header-nav">
{
settings.navbar && settings.navbar.map((item, key) => {
// if (!item.link) return;
var new_link = item.link;
var user_login = this.props.user && this.props.user.login;
var is_hidden = item.hidden
if (new_link && (new_link.indexOf("courses") > -1 || new_link.indexOf("contests") > -1)) {
if (user_login) {
if (new_link.indexOf("courses") > -1) {
new_link = new_link.replace(/courses/g, "users/" + user_login + "/courses")
} else if (new_link.indexOf("contests") > -1) {
new_link = new_link.replace(/contests/g, "users/" + user_login + "/contests")
}
} else {
is_hidden = true
}
} else {
is_hidden = true
}
}
if (user_login && (new_link && new_link.indexOf("homes") > -1)) {
new_link = new_link.replace(/homes/g, "users/" + user_login + "/user_activities")
}
var waiLian = (new_link && str.filter(item=>new_link.indexOf(item)>-1) );
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>
</li>
)
})
}
</ul>
: ""
if (user_login && (new_link && new_link.indexOf("homes") > -1)) {
new_link = new_link.replace(/homes/g, "users/" + user_login + "/user_activities")
}
var waiLian = (new_link && str.filter(item => new_link.indexOf(item) > -1));
var wl = waiLian && waiLian.length > 0;
// if(new_link.indexOf("http")<0){
// new_link = settings.new_course.default_url + new_link
// }
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"}><div dangerouslySetInnerHTML={{ __html: item.name }}></div></a>
// </li>
<li key={key} onClick={() => this.headtypesonClick(item.link, true)} className={`${this.matchpaths(item.link.includes("/projects") ? "/projects" : item.link) === true ? 'pr active' : 'pr'}`} style={!is_hidden ? { display: 'flex' } : { display: 'none' }}>
{
item.subitem && item.subitem.length > 0 ?
<Dropdown overlay={this.menus(item.subitem)}>
<div className="namediv">
<a href={this.checkLink(new_link)} target={wl ? "_self" : "_blank"}><div dangerouslySetInnerHTML={{ __html: item.name }}></div> </a>
<i className="iconfont icon-xiala1 font-14 ml3 mt5 mr3" style={{ color: "#979797", fontSize: '14px' }}></i>
</div>
</Dropdown>
:
<a href={this.checkLink(new_link)} ><div dangerouslySetInnerHTML={{ __html: item.name }}></div></a>
}
</li>
)
})
}
</ul>
: ""
}
</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>
</Dropdown>:""
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-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>:""
</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) ?
{!user || (user && !user.login) ?
<span className="font-15 ml30">
<a onClick={() => this.educoderlogin()} className="mr5 color-grey-6">登录</a>
{(settings && settings.sso_login_url) ? <a className="mr5 color-white" href={settings && settings.sso_login_url}>使用学校统一认证登录</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,168 @@
.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;
}
}
.inviteForm2{
.ant-form-item{
margin-right: 0px;
display:flex;
flex-wrap: nowrap;
}
.ant-form-item-label{
width: 92px;
text-align: right;
}
}

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="12px" height="12px" viewBox="0 0 12 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 61 (89581) - https://sketch.com -->
<title>bug-red</title>
<desc>Created with Sketch.</desc>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="质量分析" transform="translate(-424.000000, -445.000000)" fill="#F54128" fill-rule="nonzero">
<g id="bug1" transform="translate(408.000000, 387.000000)">
<g id="编组-4" transform="translate(0.000000, 44.000000)">
<g id="编组-3">
<g id="bug-red" transform="translate(16.000000, 14.000000)">
<path d="M11.8899083,6.2156806 L9.85321101,6.2156806 L9.85321101,4.91378562 C10.9100917,4.91378562 11.766055,4.10400694 11.766055,3.1041516 C11.766055,3.04686822 11.7165138,3 11.6559633,3 L10.8302752,3 C10.7697248,3 10.7201835,3.04686822 10.7201835,3.1041516 C10.7201835,3.55721105 10.3321101,3.92434544 9.85321101,3.92434544 L2.14678899,3.92434544 C1.66788991,3.92434544 1.27981651,3.55721105 1.27981651,3.1041516 C1.27981651,3.04686822 1.23027523,3 1.16972477,3 L0.344036697,3 C0.283486239,3 0.233944954,3.04686822 0.233944954,3.1041516 C0.233944954,4.10400694 1.08990826,4.91378562 2.14678899,4.91378562 L2.14678899,6.2156806 L0.110091743,6.2156806 C0.0495412844,6.2156806 0,6.26254882 0,6.3198322 L0,7.04889339 C0,7.10617677 0.0495412844,7.15304499 0.110091743,7.15304499 L2.14678899,7.15304499 L2.14678899,8.40286417 C2.14678899,8.48748734 2.14954128,8.57211052 2.15642202,8.6541299 C1.21238532,9.03558513 0.550458716,9.92217561 0.550458716,10.9545783 C0.550458716,11.0118617 0.6,11.0587299 0.660550459,11.0587299 L1.43119266,11.0587299 C1.49174312,11.0587299 1.5412844,11.0118617 1.5412844,10.9545783 C1.5412844,10.3791407 1.87018349,9.87530739 2.36146789,9.60451324 C2.4440367,9.82843917 2.54862385,10.0419499 2.67385321,10.2424418 C3.00825688,10.7827282 3.48577982,11.2344858 4.05688073,11.5508462 C4.45458716,11.7708665 4.89633028,11.925792 5.36834862,12 C5.42889908,12 5.47844037,11.9531318 5.47844037,11.8958484 L5.47844037,5.85115001 C5.47844037,5.79386663 5.52798165,5.74699841 5.58853211,5.74699841 L6.41422018,5.74699841 C6.47477064,5.74699841 6.52431193,5.79386663 6.52431193,5.85115001 L6.52431193,11.8945465 C6.52431193,11.9518299 6.57385321,11.9986981 6.63440367,11.9986981 C7.10642202,11.9244901 7.54816514,11.7695646 7.94587156,11.5495443 C8.51697248,11.2331839 8.99449541,10.7814263 9.32889908,10.2411399 C9.45412844,10.0393462 9.5587156,9.82583538 9.6412844,9.60321134 C10.1298165,9.87530739 10.4587156,10.3791407 10.4587156,10.9545783 C10.4587156,11.0118617 10.5082569,11.0587299 10.5688073,11.0587299 L11.3394495,11.0587299 C11.4,11.0587299 11.4495413,11.0118617 11.4495413,10.9545783 C11.4495413,9.92217561 10.7876147,9.03558513 9.84357798,8.6541299 C9.84908257,8.57080862 9.85321101,8.48748734 9.85321101,8.40286417 L9.85321101,7.15304499 L11.8899083,7.15304499 C11.9504587,7.15304499 12,7.10617677 12,7.04889339 L12,6.3198322 C12,6.26254882 11.9504587,6.2156806 11.8899083,6.2156806 Z" id="路径"></path>
<path d="M3.11111111,3 L8.88888889,3 C8.95,3 9,2.95 9,2.88888889 C9,2.33333333 8.87777778,1.82361111 8.64027778,1.3875 C8.40138889,0.95 8.05,0.598611111 7.6125,0.359722222 C7.17638889,0.122222222 6.66666667,0 6.11111111,0 L5.88888889,0 C5.33333333,0 4.82361111,0.122222222 4.3875,0.359722222 C3.95,0.598611111 3.59861111,0.95 3.35972222,1.3875 C3.12222222,1.82361111 3,2.33333333 3,2.88888889 C3,2.95 3.05,3 3.11111111,3 Z" id="路径"></path>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="12px" height="12px" viewBox="0 0 12 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 61 (89581) - https://sketch.com -->
<title>bug-fill</title>
<desc>Created with Sketch.</desc>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="质量分析" transform="translate(-421.000000, -224.000000)" fill="#434D6C" fill-rule="nonzero">
<g id="编组-5" transform="translate(420.000000, 215.000000)">
<g id="bug-fill" transform="translate(1.000000, 9.000000)">
<path d="M11.8899083,6.2156806 L9.85321101,6.2156806 L9.85321101,4.91378562 C10.9100917,4.91378562 11.766055,4.10400694 11.766055,3.1041516 C11.766055,3.04686822 11.7165138,3 11.6559633,3 L10.8302752,3 C10.7697248,3 10.7201835,3.04686822 10.7201835,3.1041516 C10.7201835,3.55721105 10.3321101,3.92434544 9.85321101,3.92434544 L2.14678899,3.92434544 C1.66788991,3.92434544 1.27981651,3.55721105 1.27981651,3.1041516 C1.27981651,3.04686822 1.23027523,3 1.16972477,3 L0.344036697,3 C0.283486239,3 0.233944954,3.04686822 0.233944954,3.1041516 C0.233944954,4.10400694 1.08990826,4.91378562 2.14678899,4.91378562 L2.14678899,6.2156806 L0.110091743,6.2156806 C0.0495412844,6.2156806 0,6.26254882 0,6.3198322 L0,7.04889339 C0,7.10617677 0.0495412844,7.15304499 0.110091743,7.15304499 L2.14678899,7.15304499 L2.14678899,8.40286417 C2.14678899,8.48748734 2.14954128,8.57211052 2.15642202,8.6541299 C1.21238532,9.03558513 0.550458716,9.92217561 0.550458716,10.9545783 C0.550458716,11.0118617 0.6,11.0587299 0.660550459,11.0587299 L1.43119266,11.0587299 C1.49174312,11.0587299 1.5412844,11.0118617 1.5412844,10.9545783 C1.5412844,10.3791407 1.87018349,9.87530739 2.36146789,9.60451324 C2.4440367,9.82843917 2.54862385,10.0419499 2.67385321,10.2424418 C3.00825688,10.7827282 3.48577982,11.2344858 4.05688073,11.5508462 C4.45458716,11.7708665 4.89633028,11.925792 5.36834862,12 C5.42889908,12 5.47844037,11.9531318 5.47844037,11.8958484 L5.47844037,5.85115001 C5.47844037,5.79386663 5.52798165,5.74699841 5.58853211,5.74699841 L6.41422018,5.74699841 C6.47477064,5.74699841 6.52431193,5.79386663 6.52431193,5.85115001 L6.52431193,11.8945465 C6.52431193,11.9518299 6.57385321,11.9986981 6.63440367,11.9986981 C7.10642202,11.9244901 7.54816514,11.7695646 7.94587156,11.5495443 C8.51697248,11.2331839 8.99449541,10.7814263 9.32889908,10.2411399 C9.45412844,10.0393462 9.5587156,9.82583538 9.6412844,9.60321134 C10.1298165,9.87530739 10.4587156,10.3791407 10.4587156,10.9545783 C10.4587156,11.0118617 10.5082569,11.0587299 10.5688073,11.0587299 L11.3394495,11.0587299 C11.4,11.0587299 11.4495413,11.0118617 11.4495413,10.9545783 C11.4495413,9.92217561 10.7876147,9.03558513 9.84357798,8.6541299 C9.84908257,8.57080862 9.85321101,8.48748734 9.85321101,8.40286417 L9.85321101,7.15304499 L11.8899083,7.15304499 C11.9504587,7.15304499 12,7.10617677 12,7.04889339 L12,6.3198322 C12,6.26254882 11.9504587,6.2156806 11.8899083,6.2156806 Z" id="路径"></path>
<path d="M3.11111111,3 L8.88888889,3 C8.95,3 9,2.95 9,2.88888889 C9,2.33333333 8.87777778,1.82361111 8.64027778,1.3875 C8.40138889,0.95 8.05,0.598611111 7.6125,0.359722222 C7.17638889,0.122222222 6.66666667,0 6.11111111,0 L5.88888889,0 C5.33333333,0 4.82361111,0.122222222 4.3875,0.359722222 C3.95,0.598611111 3.59861111,0.95 3.35972222,1.3875 C3.12222222,1.82361111 3,2.33333333 3,2.88888889 C3,2.95 3.05,3 3.11111111,3 Z" id="路径"></path>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="11px" height="11px" viewBox="0 0 11 11" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 61 (89581) - https://sketch.com -->
<title>辐射</title>
<desc>Created with Sketch.</desc>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="质量分析" transform="translate(-653.000000, -225.000000)" fill="#434D6C" fill-rule="nonzero">
<g id="编组-5" transform="translate(420.000000, 215.000000)">
<g id="辐射" transform="translate(233.000000, 10.000000)">
<path d="M11,5.5 C11,7.01863839 10.4637746,8.31629464 9.39009392,9.38928571 C8.31641324,10.4622768 7.01889535,11 5.50122987,11 C3.9835644,11 2.68604651,10.4622768 1.61236583,9.38928571 C0.536225403,8.31629464 0,7.01863839 0,5.5 C0,3.98136161 0.536225403,2.68370536 1.60990608,1.61071429 C2.68481664,0.537723214 3.98110465,0 5.49877013,0 C7.0164356,0 8.31395349,0.537723214 9.38763417,1.61071429 C10.4637746,2.68370536 11,3.98136161 11,5.5 Z M4.74608676,4.20234375 L3.37723614,1.82924107 C2.73647138,2.19631696 2.22730322,2.6984375 1.84973166,3.33805804 C1.47216011,3.97767857 1.28398927,4.68359375 1.28398927,5.45948661 L4.0130814,5.45948661 C4.0130814,4.88370536 4.25782648,4.46506696 4.74608676,4.20234375 L4.74608676,4.20234375 Z M7.57110912,9.15111607 L6.21701699,6.77064732 C6.09033989,6.84799107 5.96612254,6.90200893 5.84559481,6.93024554 C5.72506708,6.95848214 5.58978086,6.97444196 5.44342576,6.97444196 C5.19007156,6.97444196 4.95762522,6.90691964 4.74731664,6.771875 L3.35140877,9.14866071 C4.00201252,9.51573661 4.70919052,9.69866071 5.47663238,9.69866071 C6.23300537,9.69866071 6.93034436,9.51573661 7.57110912,9.15111607 L7.57110912,9.15111607 Z M6.29449911,5.50982143 C6.29449911,5.28024554 6.21701699,5.0875 6.0608229,4.93158482 C5.90585868,4.77566964 5.71399821,4.69709821 5.48647138,4.69709821 C5.26878354,4.69709821 5.07815295,4.77566964 4.91211986,4.93158482 C4.74731664,5.0875 4.66368515,5.28147321 4.66368515,5.50982143 C4.66368515,5.73816964 4.74116726,5.93214286 4.89736136,6.08805804 C5.05232558,6.24397321 5.24910555,6.32254464 5.48647138,6.32254464 C5.70415921,6.32254464 5.89233005,6.24274554 6.05221377,6.08069196 C6.21455725,5.91986607 6.29449911,5.72834821 6.29449911,5.50982143 L6.29449911,5.50982143 Z M9.71601073,5.45948661 C9.71601073,4.71551339 9.52906977,4.021875 9.15641771,3.37734375 C8.78499553,2.73404018 8.26106887,2.21841518 7.58832737,1.82924107 L6.23300537,4.18638393 C6.70896691,4.49084821 6.94633274,4.91439732 6.94633274,5.45948661 L9.71601073,5.45948661 Z" id="形状"></path>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="18px" height="16px" viewBox="0 0 18 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 61 (89581) - https://sketch.com -->
<title>质量分析</title>
<desc>Created with Sketch.</desc>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="质量分析" transform="translate(-1174.000000, -153.000000)" fill="#666666" fill-rule="nonzero">
<g transform="translate(1174.000000, 153.000000)" id="路径">
<path d="M5.49732895,6.59297368 L4.52690789,8.21097368 C4.51069737,8.20928947 4.49132895,8.20928947 4.47522368,8.20928947 C4.38153947,8.20928947 4.29269737,8.22392105 4.20880263,8.24971053 L3.54185526,7.58276316 C3.56862283,7.49648789 3.58224922,7.4066744 3.58227632,7.31634211 C3.58227632,6.82381579 3.18175,6.42339474 2.68922368,6.42339474 C2.19669737,6.42339474 1.79638158,6.82392105 1.79638158,7.31634211 C1.79638158,7.48760526 1.84480263,7.64581579 1.92722368,7.7815 L0.955118421,9.39939474 C0.938907895,9.39781579 0.919539474,9.39781579 0.903434211,9.39781579 C0.410907895,9.39781579 0.0104868421,9.79823684 0.0104868421,10.2907632 C0.0104868421,10.7832895 0.411013158,11.1837105 0.903434211,11.1837105 C1.39585526,11.1837105 1.79638158,10.7831842 1.79638158,10.2907632 C1.79637494,10.1266758 1.75113933,9.96576525 1.66564474,9.82571053 L2.63764474,8.20592105 C2.74513486,8.21210792 2.85286526,8.19902484 2.95575,8.16728947 L3.62269737,8.83423684 C3.59592303,8.92047445 3.58229637,9.01025429 3.58227632,9.10055263 C3.58227632,9.59318421 3.98280263,9.9935 4.47522368,9.9935 C4.96764474,9.9935 5.36659211,9.59307895 5.36659211,9.10055263 C5.36659211,8.92939474 5.31817105,8.77118421 5.23585526,8.63560526 C5.23585526,8.63560526 6.24332895,7.01918421 6.25964474,7.01918421 L5.49732895,6.59297368 L5.49732895,6.59297368 Z"></path>
<path d="M5.54522368,8.17255263 L6.51732895,6.55276316 C6.62478421,6.55894194 6.73247863,6.5458589 6.83532895,6.51413158 L9.54227632,9.22107895 C9.51556014,9.30736601 9.50193494,9.39717168 9.50185526,9.4875 C9.50185526,9.98002632 9.90238158,10.3804474 10.3948026,10.3804474 C10.8874342,10.3804474 11.2862763,9.97992105 11.2862763,9.4875 C11.286347,9.32338758 11.2410666,9.16244729 11.1554342,9.02244737 L12.1275395,7.40434211 C12.1436447,7.40602632 12.1630132,7.40602632 12.1792237,7.40602632 C12.67175,7.40602632 13.0721711,7.0055 13.0721711,6.51307895 C13.0721711,6.02065789 12.6716447,5.62171053 12.1792237,5.62171053 C11.6866974,5.62171053 11.2862763,6.02223684 11.2862763,6.51465789 C11.2862763,6.68581579 11.3346974,6.84413158 11.4170132,6.97971053 L10.4464868,8.59781579 C10.4303816,8.59613158 10.4109079,8.59613158 10.3948026,8.59613158 C10.3011184,8.59613158 10.2123816,8.61065789 10.1284868,8.63644737 L7.42164474,5.92960526 C7.44832524,5.84331276 7.46191494,5.7535072 7.46196053,5.66318421 C7.46196053,5.17065789 7.06143421,4.77023684 6.56901316,4.77023684 C6.07659211,4.77023684 5.67606579,5.17076316 5.67606579,5.66318421 C5.67608478,5.82731735 5.72135774,5.98826764 5.80690789,6.12834211 C5.80690789,6.12834211 4.79922368,7.74455263 4.78301316,7.74455263 L5.54522368,8.17255263 L5.54522368,8.17255263 Z"></path>
<path d="M14.1078553,11.9339211 L16.9774342,14.8130789 C17.2421711,15.0639211 17.2421711,15.4901316 16.9865921,15.7457105 C16.7312237,16.0110789 16.3051184,16.0202368 16.0490132,15.7549737 L13.1604868,12.8711842 C12.6018553,13.3352895 11.9768026,13.7190789 11.2948026,13.9982368 C10.4114359,14.3682316 9.4631566,14.5583599 8.50543421,14.5575 C6.37901316,14.5575 4.35227632,13.6146579 2.96932895,11.9807632 C2.27311842,11.1666579 3.32943421,10.4042368 4.12490789,11.2803421 C5.25227632,12.5163421 6.83406579,13.2169737 8.50543421,13.2169737 C9.31522368,13.2169737 10.0823816,13.0561316 10.7782763,12.7672895 C11.4973368,12.4722803 12.1508287,12.03774 12.7010132,11.4887632 L12.7106974,11.4790789 C13.2553289,10.9297105 13.7005921,10.2766579 14.0035395,9.54707895 C14.2876447,8.86044737 14.4489079,8.08865789 14.4489079,7.27865789 C14.4486837,6.51090865 14.3005825,5.75038895 14.0126974,5.03865789 L14.0034342,5.01023684 C13.7109514,4.30133595 13.2830765,3.65625198 12.74375,3.11107895 L12.7105921,3.07823684 C12.1563816,2.53360526 11.5028026,2.08865789 10.7782763,1.79444737 C10.0873289,1.49644737 9.31543421,1.34044737 8.50543421,1.34044737 C6.08575,1.34044737 4.00217105,2.81771053 3.08301316,4.88686842 C2.96932895,5.13339474 2.74206579,5.28013158 2.47185526,5.28013158 L2.46753947,5.28013158 C2.23069737,5.28013158 2.03659211,5.16623684 1.90848684,4.97728947 C1.78080263,4.78276316 1.76638158,4.5555 1.86132895,4.33739474 C3.00732895,1.76644737 5.62153947,0.00465789474 8.50532895,0.00465789474 C9.48532895,0.00465789474 10.4281711,0.203605263 11.2948026,0.563289474 C12.1851184,0.928236842 12.9898553,1.47297368 13.6529079,2.13560526 L13.6903816,2.16918421 C14.3442763,2.83655263 14.8652237,3.62286842 15.22975,4.49444737 L15.2438553,4.53192105 C15.5942763,5.38476316 15.7839605,6.30286842 15.7839605,7.27855263 C15.7844779,8.2311106 15.5961086,9.17431671 15.22975,10.0536053 C14.951938,10.7329631 14.5737631,11.366791 14.1078553,11.9339211 L14.1078553,11.9339211 Z"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="10px" height="12px" viewBox="0 0 10 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 61 (89581) - https://sketch.com -->
<title>安全</title>
<desc>Created with Sketch.</desc>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="质量分析" transform="translate(-522.000000, -224.000000)" fill="#434D6C" fill-rule="nonzero">
<g id="编组-5" transform="translate(420.000000, 215.000000)">
<g id="安全" transform="translate(102.000000, 9.000000)">
<path d="M10,1.01217391 C10,1.01217391 10,7.12747826 10,7.12747826 C10,7.49426087 9.9165,7.85895653 9.75000001,8.22156521 C9.583,8.58521739 9.36500001,8.93634783 9.095,9.27756521 C8.825,9.61826087 8.5165,9.94121739 8.17,10.2453913 C7.823,10.550087 7.47150001,10.8213913 7.11500001,11.0593043 C6.758,11.2977391 6.41150001,11.496 6.075,11.6535652 C5.7385,11.8111304 5.4435,11.9191304 5.19,11.978087 C5.19,11.978087 5.06999999,12 5.06999999,12 C5.06999999,12 4.96,11.978087 4.96,11.978087 C4.70000001,11.9191304 4.3985,11.8111304 4.055,11.6535652 C3.7115,11.496 3.355,11.2977391 2.985,11.0593043 C2.61499999,10.8213913 2.252,10.550087 1.895,10.2453913 C1.5385,9.94121739 1.22,9.61826087 0.939999994,9.27756521 C0.659999993,8.93634783 0.433500002,8.58521739 0.260499999,8.22156521 C0.0865000015,7.85895653 0,7.49426087 0,7.12747826 C0,7.12747826 0,1.01217391 0,1.01217391 C0,1.01217391 0.409999999,0.945913043 0.409999999,0.945913043 C0.409999999,0.945913043 5.01,0 5.01,0 C5.01,0 9.70000001,0.945913043 9.70000001,0.945913043 C9.70000001,0.945913043 10,1.01217391 10,1.01217391 C10,1.01217391 10,1.01217391 10,1.01217391 Z M5.0115,0.885391301 C5.0115,0.885391301 4.956,0.873391301 4.956,0.873391301 C4.956,0.873391301 1.008,1.61165217 1.008,1.61165217 C1.008,1.61165217 1.008,5.46991304 1.008,5.46991304 C1.008,5.46991304 5.0115,5.46991304 5.0115,5.46991304 C5.0115,5.46991304 5.0115,0.885391301 5.0115,0.885391301 C5.0115,0.885391301 5.0115,0.885391301 5.0115,0.885391301 Z M8.992,5.46991304 C8.992,5.46991304 5.0115,5.46991304 5.0115,5.46991304 C5.0115,5.46991304 5.0115,10.9617391 5.0115,10.9617391 C5.2675,10.8970435 5.546,10.7984348 5.847,10.6653913 C6.1475,10.5323478 6.4445,10.3753043 6.7375,10.1937391 C7.031,10.0121739 7.313,9.81078261 7.58450001,9.58904347 C7.856,9.36730434 8.0955,9.12886957 8.30500001,8.87478261 C8.51400001,8.62121739 8.68050001,8.35721739 8.80500001,8.08278261 C8.93000001,7.80886957 8.992,7.53026087 8.992,7.248 C8.992,7.248 8.992,5.46991304 8.992,5.46991304 C8.992,5.46991304 8.992,5.46991304 8.992,5.46991304 Z" id="形状"></path>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 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,19 @@ 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 +71,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

@ -1,6 +1,6 @@
import React , { useEffect , useState } from 'react';
import { WhiteBack , Box , LongWidth , ShortWidth , Gap , AlignCenter , FlexAJ } from '../Component/layout';
import { Dropdown , Menu , Divider , Spin, Button } from 'antd';
import React, { useEffect, useState } from 'react';
import { WhiteBack, Box, LongWidth, ShortWidth, Gap, AlignCenter, FlexAJ } from '../Component/layout';
import { Dropdown, Menu, Divider, Spin, Button } from 'antd';
import { getImageUrl } from "educoder";
import { Link } from 'react-router-dom';
import CloneAddress from '../Branch/CloneAddress';
@ -19,70 +19,102 @@ 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 [ treeValue , setTreeValue ] = useState(undefined);
const [ treeValuePath , setTreeValuePath ] = useState(undefined);
const [ lastCommit,setLastCommit ] = useState(undefined);
const [ lastCommitAuthor,setLastCommitAuthor ] = useState(undefined);
const [ type ,setType ] = useState('dir');
const [ hide , setHide ] = useState(true);
const [ hideBtn , setHideBtn ] = useState(false);
const [ commitCount ,setCommitCount ] = useState(0);
const [ dirInfo ,setDirInfo ] = useState(undefined);//
const [ fileInfo ,setFileInfo ] = useState(undefined);//
const [ zip_url , setZip_url ] = useState(undefined);
const [ tar_url , setTar_url ] = useState(undefined);
const [ readOnly , setReadOnly] = useState(true);
const [ isSpin , setIsSpin] = useState(true);
const [ visible ,setVisible ] = useState(false);
const [ mainFlag ,setMainFlag ] = useState(false);
const [ openModal , setOpenModal ] = useState(false);
const [ desc , setDesc ] = useState(undefined);
const [ website , setWebsite ] = useState(undefined);
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);
const [lastCommitAuthor, setLastCommitAuthor] = useState(undefined);
const [type, setType] = useState('dir');
const [hide, setHide] = useState(true);
const [hideBtn, setHideBtn] = useState(false);
const [commitCount, setCommitCount] = useState(0);
const [dirInfo, setDirInfo] = useState(undefined);//
const [fileInfo, setFileInfo] = useState(undefined);//
const [zip_url, setZip_url] = useState(undefined);
const [tar_url, setTar_url] = useState(undefined);
const [readOnly, setReadOnly] = useState(true);
const [isSpin, setIsSpin] = useState(true);
const [visible, setVisible] = useState(false);
const [mainFlag, setMainFlag] = useState(false);
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 [downLoadType, setDownLoadType] = useState("http");
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);
useEffect(() => {
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){
useEffect(() => {
if (treeValue) {
setTreeValuePath(treeValue.split('/'));
}else{
} else {
setTreeValuePath(undefined);
}
},[treeValue])
}, [treeValue])
useEffect(()=>{
if (pathname){
if(pathname.indexOf(`/projects/${owner}/${projectsId}`) > -1 && pathname.indexOf(`/tree/${branchName}/`) > -1) {
let url = pathname.split(`/tree/${branchName}/`)[1];
useEffect(() => {
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);
}else{
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){
function getDirInfo(branch) {
setIsSpin(true);
const url = `/${owner}/${projectsId}/entries.json`;
axios.get(url, {
params: { ref: branch }
axios.get(url, {
params: { ref: branch }
}).then((result) => {
if (result) {
setCommitCount(result.data.commits_count);
@ -92,109 +124,128 @@ 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);})
setTimeout(function () { setIsSpin(false); }, 500);
}).catch(error => { setIsSpin(false); })
}
useEffect(()=>{
if(projectDetail && lastCommit)
{
useEffect(() => {
if (projectDetail && lastCommit) {
let ele = document.getElementById("ptxt");
if(ele){
if (ele) {
let h = ele.offsetHeight;
if( h > 18 ) setHideBtn(true)
if (h > 18) setHideBtn(true);
}
}
},[projectDetail,lastCommit])
}, [projectDetail, lastCommit])
//
function getFileInfo(path, ref){
function getFileInfo(path, ref) {
setIsSpin(true);
const url = `/${owner}/${projectsId}/sub_entries.json`;
axios.get(url, {
params:{
filepath:path,
ref:ref || branchName,
axios.get(url, {
params: {
filepath: path,
ref: ref || branchName,
type
}
}).then((result) => {
if (result) {
let en = result.data.entries;
if(en.type){
if (en.type) {
setDirInfo(undefined);
setFileInfo(en);
setType(en.type);
}else{
} else {
setFileInfo(undefined);
setDirInfo(en);
setType("dir");
}
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);})
setTimeout(function () { setIsSpin(false); }, 500)
}).catch(error => { setIsSpin(false); })
}
//
function changeBranch(value){
let url = `/projects/${owner}/${projectsId}${value && `/tree/${value}`}${treeValue ? `/${treeValue}`:""}`;
function changeBranch(value) {
let checkvalue = turnbar(value);
let url = `/projects/${owner}/${projectsId}${value && `/tree/${checkvalue}`}${treeValue ? `/${treeValue}` : ""}`;
props.history.push(url);
}
//
const fileMenu =(
<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>
)
function getPathUrl(array,index){
if(array && array.length>0 && index){
//
function fileMenu() {
let b = branchName || defaultBranch;
let checkvalue = turnbar(b);
return (
<Menu>
<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) {
let str = "";
for(let i=0;i<index;i++){
for (let i = 0; i < index; i++) {
str += `/${array[i]}`;
}
return str.substr(1);
}
}
//
function returnMain(){
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}`);
function returnUlr(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}`:""}`);
function goToSubRoot(path, type, filename) {
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){
function onEdit(readOnly) {
setReadOnly(readOnly);
setEditReadme(false);
}
function ChangeFile(path, readOnly){
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){
function changeHide(hide) {
setHide(!hide);
}
function urlLink(link){
if(props.checkIfLogin()===false){
function urlLink(link) {
if (props.checkIfLogin() === false) {
props.showLoginDialog()
return false;
}
@ -202,54 +253,56 @@ function CoderDepot(props){
}
const downloadMenu = (
<div className="downMenu">
<div style={{padding:"20px",borderBottom:"1px solid #eee"}}>
<CloneAddress
http_url={projectDetail && projectDetail.clone_url}
showNotification={props.showNotification}/>
</div>
<Menu className="edu-txt-center">
<Menu.Item><a href={zip_url}>下载 ZIP</a></Menu.Item>
<Menu.Item><a href={tar_url}>下载 TAR.GZ</a></Menu.Item>
</Menu>
</div>
<CloneAddress
http_url={projectDetail && projectDetail.clone_url}
ssh_url={(projectDetail && props && props.platform) && projectDetail.ssh_url}
zip_url={(props && props.platform) && zip_url}
tar_url={(props && props.platform) && tar_url}
showNotification={props.showNotification} />
)
function okUpdate(d,w){
// website
function okUpdate(d, w, l) {
const url = `/${owner}/${projectsId}.json`;
axios.put(url,{
description:d,website:w
}).then(result=>{
if(result && result.data && result.data.id){
axios.put(url, {
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);
}
})
}
return(
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) &&
(dirInfo || fileInfo) &&
<React.Fragment>
<DrawerPanel
history={props.history}
owner={owner}
projectsId={projectsId}
<DrawerPanel
history={props.history}
owner={owner}
projectsId={projectsId}
name={projectDetail && projectDetail.name}
branch={branchName || (projectDetail && projectDetail.default_branch)}
visible={visible}
onClose={()=>setVisible(false)}
list = {mainFlag ? dirInfo : undefined}
branch={branchName || defaultBranch}
visible={visible}
onClose={() => setVisible(false)}
list={mainFlag ? dirInfo : undefined}
/>
<div className="drawerBtn" onClick={()=>setVisible(true)}>
<div className="drawerBtn" onClick={() => setVisible(true)}>
<i className="iconfont icon-youjiantou font-16"></i>
<span>目录</span>
</div>
</React.Fragment>
}
<div style={{minHeight:"500px"}}>
{
<div style={{ minHeight: "500px" }}>
{
projectDetail &&
<Box className="Panels">
<LongWidth>
@ -259,17 +312,17 @@ function CoderDepot(props){
<div className="mr20">
{
props && props.platform ?
<SelectBranch
repo_id={projectDetail && projectDetail.repo_id}
projectsId={projectsId}
branch={branchName || (projectDetail && projectDetail.default_branch)}
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>
<SelectBranch
repo_id={projectDetail && projectDetail.repo_id}
projectsId={projectsId}
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 || defaultBranch}</span></span>
}
</div>
<AlignCenter className="mr20">
@ -287,92 +340,100 @@ function CoderDepot(props){
</AlignCenter>
<AlignCenter>
<div className="mr20 addOptionBtn">
<a onClick={()=>urlLink(`/projects/${owner}/${projectsId}/pulls/new`)} >+ 合并请求</a>
<a onClick={()=>urlLink(`/projects/${owner}/${projectsId}/issues/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>
</FlexAJ>
{
dirInfo || fileInfo ?
<div className="listtable">
{
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>
{ hideBtn && <span className="ellipsis" onClick={()=>changeHide(hide)}><i className="iconfont icon-shenglvehao"></i></span> }
<div className="listtable">
{
lastCommit &&
<div className="listtablehead">
<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>
{ commitCount ? <Link to={`/projects/${owner}/${projectsId}/commits`} className="ml12 color-grey-9"><i className="iconfont icon-tijiao mr3 font-17 color-grey-9"></i>{commitCount}次提交</Link>:"" }
</div>
}
<ul className="listtablebody">
{
treeValuePath && treeValuePath.length > 0 &&
<Path
identifier={projectDetail && projectDetail.identifier}
treeValuePath={treeValuePath}
returnUlr={returnUlr}
returnMain={returnMain}
getPathUrl={getPathUrl}
/>
<span className="ml12 color-grey-9 mt3">{lastCommit && lastCommit.time_from_now}</span>
{commitCount ? <Link to={`/projects/${owner}/${projectsId}/commits`} className="ml12 color-grey-9"><i className="iconfont icon-tijiao mr3 font-17 color-grey-9"></i>{commitCount}次提交</Link> : ""}
</div>
}
{
dirInfo && dirInfo.length > 0 &&
dirInfo.map((item,key)=>{
return(
<Catalogue
owner={owner}
item={item}
projectsId={projectsId}
goToSubRoot={goToSubRoot}
/>
)
})
}
{
fileInfo &&
<CoderRootFileDetail
{...props}
detail={fileInfo}
readOnly={readOnly}
onEdit={onEdit}
currentBranch={branchName || (projectDetail && projectDetail.default_branch)}
></CoderRootFileDetail>
}
</ul>
</div>
: ""
<ul className="listtablebody">
{
treeValuePath && treeValuePath.length > 0 &&
<Path
identifier={projectDetail && projectDetail.identifier}
treeValuePath={treeValuePath}
returnUlr={returnUlr}
returnMain={returnMain}
getPathUrl={getPathUrl}
/>
}
{
dirInfo && dirInfo.length > 0 &&
dirInfo.map((item, key) => {
return (
<Catalogue
owner={owner}
item={item}
projectsId={projectsId}
goToSubRoot={goToSubRoot}
/>
)
})
}
{
fileInfo &&
<CoderRootFileDetail
{...props}
detail={fileInfo}
readOnly={readOnly}
md={mdFlag}
onEdit={onEdit}
currentBranch={branchName || defaultBranch}
type={projectDetail.type}
></CoderRootFileDetail>
}
</ul>
</div>
: ""
}
{
(dirInfo && dirInfo.length === 0) && (fileInfo && fileInfo.length === 0) ? <Nodata _html="暂未发现文件"/> :""
}
(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>
{
!fileInfo &&
!fileInfo &&
<ShortWidth>
<Gap style={{paddingLeft:"30px"}}>
<Gap style={{ paddingLeft: "30px" }}>
<div className="panelmenu">
<FlexAJ className="font-18 color-grey-6 mb20" style={{lineHeight:"28px"}}>简介
{projectDetail.permission && (projectDetail.permission==="Admin" || projectDetail.permission==="Owner") && <i onClick={()=>setOpenModal(true)} className="iconfont icon-anquanshezhi color-grey-9 font-15"></i>}
<FlexAJ className="font-18 color-grey-6 mb20" style={{ lineHeight: "28px" }}>简介
{projectDetail.permission && (projectDetail.permission === "Admin" || projectDetail.permission === "Owner") && <i onClick={() => setOpenModal(true)} className="iconfont icon-anquanshezhi color-grey-9 font-15"></i>}
</FlexAJ>
{desc && <p className="font-14 color-grey-9 mb15 task-hide-2" style={{lineHeight:"22px",WebkitLineClamp:"4",textAlign:"justify",wordBreak:"break-all"}}>{desc}</p>}
{desc && <p className="font-14 color-grey-9 mb15 task-hide-2" style={{ lineHeight: "22px", WebkitLineClamp: "4", textAlign: "justify", wordBreak: "break-all" }}>{desc}</p>}
{
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,27 +452,39 @@ 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 &&
<React.Fragment>
<Divider />
<Releases owner={owner} projectsId={projectsId} releaseVersions={projectDetail.release_versions} history={props.history}/>
<Releases owner={owner} projectsId={projectsId} releaseVersions={projectDetail.release_versions} history={props.history} />
</React.Fragment>
}
{/* 贡献者 */}
{
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 &&
{projectDetail && projectDetail.languages &&
<React.Fragment>
<Divider />
<LanguagePower languages={projectDetail.languages}/>
<LanguagePower languages={projectDetail.languages} />
</React.Fragment>
}
</Gap>

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';
@ -66,6 +66,12 @@ const MergeIndexDetail = Loadable({
loading: Loading,
})
//合并请求
// const QualityAnalyse = Loadable({
// loader: () => import('../Quality/index'),
// loading: Loading,
// })
const CreateMerge = Loadable({
loader: () => import('../Merge/NewMerge'),
loading: Loading,
@ -101,6 +107,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 +165,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 +194,7 @@ class Detail extends Component {
firstSync:false,
secondSync:false,
open_devops:false,
forkSpin:false,
// 默认分支
defaultBranch:undefined,
@ -358,6 +377,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 +388,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 +416,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 +452,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 +660,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 +691,26 @@ class Detail extends Component {
(props) => (<MergeIndexDetail {...this.props} {...props} {...this.state} {...common} />)
}
></Route>
{/* <Route path="/:owner/:projectsId/sonar"
render={
(props) => (<QualityAnalyse {...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, Tabs } 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 {
@ -28,17 +29,18 @@ class Index extends Component {
typeList: undefined,
categoryList: undefined,
recommendList:undefined,
recommendList: undefined,
languageList:undefined,
languageId:undefined
languageList: undefined,
languageId: undefined,
filter: undefined,
}
}
componentDidMount = () => {
const { page, limit, search, sort, project_type, category_id , languageId } = this.state;
this.getListData(page, limit, search, sort, project_type, category_id , languageId);
const { page, limit, search, sort, project_type, category_id, languageId, filter } = this.state;
this.getListData(page, limit, search, sort, project_type, category_id, languageId, filter);
this.getType();
@ -50,30 +52,30 @@ class Index extends Component {
}
// 获取语言列表
getLanguage=()=>{
getLanguage = () => {
const url = '/project_languages.json';
axios.get(url).then(result=>{
if(result){
axios.get(url).then(result => {
if (result) {
this.setState({
languageList:result.data.project_languages
languageList: result.data.project_languages
})
}
}).catch(error=>{})
}).catch(error => { })
}
// 获取推荐列表
getRecommand=()=>{
getRecommand = () => {
const url = `/projects/recommend.json`;
axios.get(url).then(result=>{
if(result){
axios.get(url).then(result => {
if (result) {
this.setState({
recommendList:result.data
recommendList: result.data
})
}
}).catch(error=>{})
}).catch(error => { })
}
// 获取列表
getListData = (page, limit, search, sort, project_type, category_id,languageId) => {
getListData = (page, limit, search, sort, project_type, category_id, languageId, filter) => {
const { current_user } = this.props;
const url = `/projects.json`;
axios.get(url, {
@ -85,7 +87,8 @@ class Index extends Component {
sort_by: sort,
project_type,
category_id,
language_id:languageId
language_id: languageId,
filter
}
}).then((result) => {
if (result) {
@ -111,12 +114,12 @@ class Index extends Component {
setTypeList = (list, active_type) => {
this.setState({
typeList: list.map((item, key) => {
const acitve = active_type && active_type === item.project_type
return (
<li key={key} className={active_type && active_type === item.project_type ? 'active' : ''} onClick={() => this.changeType(`${item.project_type}`, list)}>
<p>
<span className="font-16">{item.name}</span>
<span className="color-blue">{item.projects_count}</span>
</p>
<li key={key} className={`cursor-pointer px-12px py-4px rounded-2px `}
style={{ background: acitve ? "#F6F7F9" : "", color: acitve ? "#165DFF" : "" }}
onClick={() => this.changeType(`${item.project_type}`, list)}>
{item.name}{item.projects_count}
</li>
)
})
@ -131,8 +134,8 @@ class Index extends Component {
search: undefined
})
this.setTypeList(list, type)
const { page, limit, sort, category_id , languageId } = this.state;
this.getListData(page, limit, undefined, sort, type, category_id , languageId);
const { page, limit, sort, category_id, languageId, filter } = this.state;
this.getListData(page, limit, undefined, sort, type, category_id, languageId, filter);
}
// 获取类型
@ -149,12 +152,12 @@ class Index extends Component {
setCategoryList = (list, active_id) => {
this.setState({
categoryList: list.map((item, key) => {
const acitve = active_id && parseInt(active_id) === item.id
return (
<li key={key} className={active_id && parseInt(active_id) === item.id ? 'active' : ''} onClick={() => this.changeCategory(`${item.id}`, list)}>
<p>
<span className="font-16">{item.name}</span>
<span className="color-blue">{item.projects_count}</span>
</p>
<li key={key} className={`cursor-pointer px-12px py-4px rounded-2px`}
style={{ background: acitve ? "#F6F7F9" : "", color: acitve ? "#165DFF" : "" }}
onClick={() => this.changeCategory(`${item.id}`, list)}>
{item.name}{item.projects_count}
</li>
)
})
@ -167,8 +170,8 @@ class Index extends Component {
page: 1
});
this.setCategoryList(list, id)
const { limit, sort, project_type , languageId } = this.state;
this.getListData(1, limit, undefined, sort, project_type, id , languageId);
const { limit, sort, project_type, languageId, filter } = this.state;
this.getListData(1, limit, undefined, sort, project_type, id, languageId, filter);
}
// 排序
@ -179,8 +182,8 @@ class Index extends Component {
search: undefined,
isSpin: true
})
const { limit, project_type, category_id , languageId } = this.state;
this.getListData(1, limit, undefined, e.key, project_type, category_id , languageId);
const { limit, project_type, category_id, languageId, filter } = this.state;
this.getListData(1, limit, undefined, e.key, project_type, category_id, languageId, filter);
}
// 搜索
@ -192,8 +195,8 @@ class Index extends Component {
project_type: undefined,
sort: "updated_on"
})
const { limit, sort, category_id , languageId } = this.state;
this.getListData(1, limit, value, sort, undefined, category_id , languageId);
const { limit, sort, category_id, languageId, filter } = this.state;
this.getListData(1, limit, value, sort, undefined, category_id, languageId, filter);
}
changeSearchValue = (e) => {
this.setState({
@ -205,26 +208,35 @@ class Index extends Component {
this.setState({
page
})
const { limit, search, sort, project_type, category_id , languageId } = this.state;
this.getListData(page, limit, search, sort, project_type, category_id , languageId);
const { limit, search, sort, project_type, category_id, languageId, filter } = this.state;
this.getListData(page, limit, search, sort, project_type, category_id, languageId, filter);
}
getoDetail=(login,identifier)=>{
getoDetail = (login, identifier) => {
this.props.history.push(`/projects/${login}/${identifier}`);
}
// 选择语言类别
changeLanguage=(e)=>{
changeLanguage = (e) => {
this.setState({
isSpin:true,
languageId:e === 0 ?undefined:e
isSpin: true,
languageId: e === 0 ? undefined : e
})
const { page, limit, sort , project_type , category_id } = this.state;
this.getListData(page, limit, undefined, sort, project_type, category_id ,e === 0 ?undefined:e);
const { page, limit, sort, project_type, category_id, filter } = this.state;
this.getListData(page, limit, undefined, sort, project_type, category_id, e === 0 ? undefined : e, filter);
}
menu =()=> {
return(
changeFilter = (e) => {
this.setState({
isSpin: true,
filter: e
})
const { page, limit, search, sort, project_type, category_id, languageId } = this.state;
this.getListData(page, limit, search, sort, project_type, category_id, languageId, e);
}
menu = () => {
return (
<Menu onClick={this.ChangeSoryBy}>
<Menu.Item key="updated_on">更新时间排序</Menu.Item>
<Menu.Item key="created_on">创建时间排序</Menu.Item>
@ -234,8 +246,8 @@ class Index extends Component {
)
}
newItem = ()=>{
return(
newItem = () => {
return (
<Menu>
<Menu.Item key="created_mirror"><Link to={`/projects/mirror/new`}>新建镜像项目</Link></Menu.Item>
<Menu.Item key="created_deposit"><Link to={`/projects/deposit/new`}>新建托管项目</Link></Menu.Item>
@ -243,35 +255,51 @@ class Index extends Component {
)
}
pagination=(total,limit,page)=>{
return(
pagination = (total, limit, page) => {
return (
total && total > limit ?
<div className="edu-txt-center pt30 mb30 border-top-grey">
<Pagination simple defaultCurrent={page} total={total} pageSize={limit} onChange={this.ChangePage}></Pagination>
</div>:""
<div className="edu-txt-center pt30 mb30 border-top-grey">
<Pagination simple defaultCurrent={page} total={total} pageSize={limit} onChange={this.ChangePage}></Pagination>
</div> : ""
)
}
render() {
const { current_user } = this.props;
const { projectsList , recommendList , languageList , languageId ,
isSpin, total, search, limit, page, typeList, categoryList } = this.state;
const { projectsList, recommendList, languageList, languageId,
isSpin, total, search, limit, page, typeList, categoryList, filter } = this.state;
return (
<div>
<p className="t_project_banner">
<img src={banner} width="100%" alt=""/>
</p>
<div className='pt-20px' style={{ background: "#EEF5FE", lineHeight: "normal" }}>
{/* <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 &&
recommendList && recommendList.length > 0 &&
<div className="recommandProjects">
{
{/* {
recommendList.map((item,key)=>{
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>
@ -282,76 +310,92 @@ class Index extends Component {
</div>
)
})
}
} */}
</div>
}
<div className="ProjectListIndex">
<div className="list-left">
<ul className="list-l-Menu">
<li className="MenuTitle"><i className="iconfont icon-xiangmuleixing color-grey-9 font-15 mr5"></i></li>
<div className="w-1200px ml-auto mr-auto">
<div className="bg-white rounded-8px py-16px px-20px flex flex-col gap-30px">
<ul className="flex flex-wrap gap-20px">
<li style={{ color: "#9096A3" }} className='py-4px rounded-2px'>项目类型</li>
{typeList}
</ul>
<ul className="list-l-Menu">
<li className="MenuTitle"><i className="iconfont icon-xiangmuleibie color-grey-9 font-15 mr5"></i></li>
<ul className="flex flex-wrap gap-20px">
<li style={{ color: "#9096A3" }} className='py-4px rounded-2px'>项目类别</li>
{categoryList}
</ul>
</div>
<div className="list-right boxShandow radius-2" style={{padding:0}}>
<div className="mt-16px bg-white rounded-8px px-20px">
<Spin spinning={isSpin}>
<div className="list-r-operation">
<div>
<Select
showSearch
placeholder="请选择语言"
style={{width:"150px",marginRight:"20px"}}
size={"large"}
onChange={this.changeLanguage}
value={languageId}
allowClear={true}
optionFilterProp="children"
filterOption={(input,option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
>
<Select.Option key={0} value={0}>请选择语言</Select.Option>
{
languageList && languageList.length>0 && languageList.map((item,key)=>{
return(
<Select.Option key={item.id} value={item.id}>
{item.name}
</Select.Option>
)
})
}
</Select>
<Search
placeholder="输入项目名称关键字进行搜索"
enterButton="搜索"
size="large"
onSearch={this.searchFun}
className="list-r-Search"
value={search}
onChange={this.changeSearchValue}
/>
<div className='flex gap-20px py-12px mb-20px' style={{ borderBottom: "1px solid #DBDCE0" }}>
<div className='flex flex-1 gap-40px mr-auto'>
{[
{ label: "全部", key: "undefined" },
{ label: "我管理的", key: "manage" },
{ label: "我学习的", key: "study" },
].map((item) => (<div key={item.key} className={`h-full flex item-center relative cursor-pointer ${(filter || "undefined") == item.key ? "font-medium" : ""}`}
style={{ color: (filter || "undefined") == item.key ? "#232B40" : "#5F6368" }}
onClick={() => { this.changeFilter(item.key) }}>
{item.label}
{(filter || "undefined") == item.key && <div className='w-full h-2px absolute' style={{ background: "#232B40", left: 0, bottom: "-12px" }}></div>}
</div>))}
</div>
<div>
<Button type={"primary"} size={"large"} style={{ background: "#3061D0" }}>
<Link to={`/projects/deposit/new`}>新建项目</Link>
</Button>
<Button type={"primary"} size={"large"} style={{ background: "#3061D0" }}>
<AddProjectModal showNotification={this.props.showNotification} />
</Button>
</div>
{/* <Button size='large' onClick={()=>{this.changeFilter(undefined)}} type={this.state.filter == undefined ? 'primary':'default'}></Button>
<Button size='large' onClick={()=>{this.changeFilter('manage')}} type={this.state.filter == 'manage' ? 'primary':'default'} className='ml10 mr10'>我管理的</Button>
<Button size='large' onClick={()=>{this.changeFilter('study')}} type={this.state.filter == 'study' ? 'primary':'default'}>我学习的</Button> */}
<div className='flex gap-20px item-center mb-8px'>
<Select
showSearch
placeholder="请选择语言"
style={{ width: "150px" }}
size={"large"}
onChange={this.changeLanguage}
value={languageId}
allowClear={true}
optionFilterProp="children"
filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
>
<Select.Option key={0} value={0}>请选择语言</Select.Option>
{
current_user && current_user.login &&
<Popover content={this.newItem()} trigger={["click"]} placement='bottom' className="mr50">
<a className="ant-dropdown-link">
<span className="color-blue font-16"><img src={img_new} alt="" width="13px" /> 新建</span>
</a>
</Popover>
languageList && languageList.length > 0 && languageList.map((item, key) => {
return (
<Select.Option key={item.id} value={item.id}>
{item.name}
</Select.Option>
)
})
}
<Popover content={this.menu()} trigger={['click']} placement='bottom'>
<a className="ant-dropdown-link">
<span className="color-blue font-16">排序 <img src={img_array} alt="" width="10px" /></span>
</Select>
<Search
placeholder="输入项目名称关键字进行搜索"
enterButton="搜索"
size="large"
onSearch={this.searchFun}
className="list-r-Search mr-auto"
value={search}
onChange={this.changeSearchValue}
/>
{current_user && current_user.login &&
<Popover content={this.newItem()} trigger={["click"]} placement='bottom'>
<a style={{ color: "#3061D0" }}>
<span className="color-blue font-16"><img src={img_new} alt="" width="13px" /> 新建</span>
</a>
</Popover>
</div>
}
<Popover content={this.menu()} trigger={['click']} placement='bottom'>
<a style={{ color: "#3061D0" }}>
<span className="color-blue font-16">排序 <img src={img_array} alt="" width="10px" /></span>
</a>
</Popover>
</div>
<ListItem {...this.props} {...this.state} projects={projectsList}></ListItem>
{this.pagination(total,limit,page)}
{this.pagination(total, limit, page)}
</Spin>
</div>
</div>

View File

@ -1,46 +1,53 @@
/* recommandProjects */
.recommandProjects{
.recommandProjects {
display: flex;
max-width: 1200px;
margin:20px auto;
margin: 20px auto;
}
.recommandProjects >div{
.recommandProjects>div {
background-color: #fff;
border-radius: 10px;
width: 220px;
margin-right: 25px;
cursor: pointer;
border: 1px solid #eee;
&:last-child{
&:last-child {
margin-right: 0px;
}
}
.recommandProjects > div:hover{
.recommandProjects>div:hover {
box-shadow: 0px 2px 20px 0px rgba(0, 0, 0, 0.1);
}
.recommandProjects > div .mainInfo{
.recommandProjects>div .mainInfo {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 160px;
border-bottom: 1px solid #eee;
padding:20px;
padding: 20px;
box-sizing: border-box;
img{
img {
height: 50px;
width:50px;
width: 50px;
border-radius: 50%;
}
.name{
.name {
font-size: 13px;
color: #666;
height: 18px;
line-height: 18px;
margin-top:12px;
margin-top: 12px;
}
.school{
margin-top:12px;
.school {
margin-top: 12px;
color: #333;
font-size: 16px;
height: 22px;
@ -48,24 +55,28 @@
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
max-width:100%;
max-width: 100%;
}
}
.recommandProjects{
.baseInfo{
padding:18px 15px;
.recommandProjects {
.baseInfo {
padding: 18px 15px;
display: flex;
font-size: 12px;
color:#888;
.look{
i{
color: #888;
.look {
i {
margin-right: 5px;
}
margin-right: 10px;
}
.type{
flex:1;
width:0;
.type {
flex: 1;
width: 0;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
@ -75,212 +86,321 @@
}
// coderDepot
.Panels{
.Panels {
max-width: 1200px;
margin: 0 auto;
.panelmenu{
padding-top:30px;
.panelmenu {
padding-top: 30px;
}
.addOptionBtn{
.addOptionBtn {
height: 32px;
line-height: 30px;
display: flex;
border:1px solid #d9d9d9;
border: 1px solid #d9d9d9;
border-radius: 2px;
a{
padding:0px 13px;
a {
padding: 0px 13px;
color: rgba(0, 0, 0, 0.65);
cursor: pointer;
}
& > a:first-child{
&>a:first-child {
border-right: 1px solid #d9d9d9;
}
}
.infoCount{
.infoCount {
display: inline-block;
padding:0px 5px;
padding: 0px 5px;
height: 16px;
line-height: 16px;
background-color: #eee;
color:#999;
color: #999;
border-radius: 12px;
margin-left: 10px;
font-size: 12px;
}
.attrPerson{
.attrPerson {
padding-top: 15px;
display: flex;
flex-wrap: wrap;
a{
a {
margin: 10px 10px 0px 0px;
img{
img {
border-radius: 50%;
width: 35px;
height: 35px;
}
&:nth-child(6){
&:nth-child(6) {
margin-right: 0px;
}
}
}
.progress{
.progress {
display: flex;
border-radius: 10px;
height: 7px;
margin-top: 12px;
span{
border-left: 1px solid #fff;
&:first-child{
border-left: none;
span {
&:first-child {
border-radius: 10px 0px 0px 10px;
}
&:last-child{
&:last-child {
border-radius: 0px 10px 10px 0px;
}
}
}
.progresstip{
.progresstip {
margin-top: 15px;
flex-wrap: wrap;
i.zero{
i.zero {
position: absolute;
display: block;
border-radius: 50%;
height: 8px;
width: 8px;
left: 0px;
top:10px
top: 10px
}
&>span{
&>span {
padding-left: 15px;
position: relative;
min-width: 33.5%;
span{
span {
color: #666;
&:last-child{
&:last-child {
color: #999;
margin-left: 5px;
}
}
}
}
.listtable{
.listtable {
margin-top: 20px;
border:1px solid #d9d9d9;
border: 1px solid #d9d9d9;
border-radius: 4px;
.listtablehead{
.listtablehead {
display: flex;
justify-content: space-between;
align-items: flex-start;
border-bottom: 1px solid #d9d9d9;
padding:7px 20px;
padding: 7px 20px;
border-radius: 4px 4px 0px 0px;
background-color: #FAFBFC;
.ellipsistxt{
.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:"...";
padding-right: 8px;
&::after {
position: absolute;
right: 0px;
bottom: 0px;
content: "...";
}
}
}
.ellipsis{
.ellipsis {
margin-left: 8px;
cursor: pointer;
border-radius: 2px;
background-color: #c1c1c1;
padding:0px 4px;
padding: 0px 4px;
height: 14px;
line-height: 14px;
margin-top: 9px;
i{
font-size: 15px!important;
i {
font-size: 15px !important;
color: #fff;
height: 14px;
line-height: 14px;
}
}
}
.listtablebody{
li.listtablepath{
a{color: #40a9ff;}
.listtablebody {
li.listtablepath {
a {
color: #40a9ff;
}
p {
margin-bottom: 0px !important;
}
}
li{
&>li {
height: 42px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #d9d9d9;
padding:0px 20px 0px 24px;
& > span:first-child{
padding: 0px 20px 0px 24px;
&>span:first-child {
width: 30%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
& > span:nth-child(2){
&>span:nth-child(2) {
width: 60%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
& > span:nth-child(3){
&>span:nth-child(3) {
width: 10%;
text-align: right;
}
}
&> li:last-child{
&>li:last-child {
border-bottom: none;
}
}
}
}
.drawerBtn{
.drawerBtn {
position: fixed;
left: -13px;
border:1px solid rgb(207,205,223);
border: 1px solid rgb(207, 205, 223);
width: 34px;
border-radius: 0px 12px 12px 0px;
height: 70px;
top:50%;
top: 50%;
margin-top: -35px;
cursor: pointer;
display: flex;
flex-direction: column;
align-items: flex-end;
justify-content: center;
&:hover{
box-shadow: 1px 0px 7px rgba(0,0,0,0.1);
&:hover {
box-shadow: 1px 0px 7px rgba(0, 0, 0, 0.1);
}
span{
span {
writing-mode: vertical-lr;
color: #202429;
width: 25px;
}
i{
i {
color: #24292e;
height: 18px;
line-height: 18px;
width: 18px;
}
}
.downMenu{
box-shadow: 0px 0px 9px rgba(134, 134, 134,0.4);
.downMenu {
width: 329px;
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,79 +1,289 @@
import React, { Component } from 'react';
import { Tooltip } from 'antd';
import { Tooltip, Badge, Modal, Form, Input, Upload, Button, Icon, Radio, message } 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';
import './list.css';
import img_parise from '../Images/parise.png';
import Axios from 'axios'; // 引入 Axios
import { getUploadActionUrl, appendFileSizeToUploadFileAll } from 'educoder';
class PrivateAccessModal extends Component {
constructor(props) {
super(props);
this.state = {
contentFileList: []
};
}
handleOk = () => {
this.props.form.validateFields((err, values) => {
if (!err) {
const url = `/applied_projects.json`;
let attachmentId = this.state.contentFileList.map(item => {
return item.response ? item.response.id : item.id
})
Axios.post(url, {
applied_project: {
alert_remarks: true,
...values,
attachment_id: attachmentId.join(','),
code: this.props.projectInfo.invite_code
},
})
.then((result) => {
if (result && result.data) {
this.setState({
contentFileList: []
});
message.info('申请已提交,请等待项目管理者审核!');
}
})
.catch((error) => {
console.log(error);
});
this.props.onClose();
this.props.form.resetFields();
}
});
}
handleCancel = () => {
this.props.onClose();
this.props.form.resetFields();
}
uploadProps = () => {
return {
fileList: this.state.contentFileList,
multiple: false,
action: `${getUploadActionUrl()}`,
onChange: (info) => {
let content = info.fileList;
this.setState({
contentFileList: appendFileSizeToUploadFileAll(content)
});
},
onRemove: (file) => {
if (file.response != undefined) {
Modal.confirm({
content: '是否确认删除?',
onOk: () => {
this.deleteAttachment(file);
},
onCancel() {
console.log('Cancel');
},
});
return false;
}
},
beforeUpload: (file) => {
console.log('beforeUpload', file.name);
const isLt150M = file.size / 1024 / 1024 <= 20;
if (!isLt150M) {
message.error('文件大小必须不超过20MB!');
}
return isLt150M;
},
};
}
deleteAttachment = (file) => {
const url = `/attachments/${file.response ? file.response.id : file.uid}.json`;
Axios.delete(url, {})
.then((response) => {
if (response.data) {
const { status } = response.data;
if (status === 0) {
this.setState({
contentFileList: []
});
}
}
})
.catch((error) => {
console.log(error);
});
};
render() {
const { getFieldDecorator } = this.props.form;
return (
<Modal
title="加入项目"
visible={this.props.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
centered={true}
width={500}
>
<Form layout="inline" className="inviteForm2">
<Form.Item label="选择角色">
{getFieldDecorator('role', {
rules: [{ required: true, message: '请选择角色' }],
initialValue: 'developer',
})(
<Radio.Group defaultValue={'developer'}>
<Radio value="manager">管理员</Radio>
<Radio value="developer">开发者</Radio>
<Radio value="reporter">报告者</Radio>
</Radio.Group>
)}
</Form.Item>
<Form.Item label="个人情况">
{getFieldDecorator('remarks', {
rules: [{ required: true, message: '请输入个人情况' }],
})(
<Input
placeholder="请输入个人情况"
// autoComplete="off"
maxLength={200}
style={{ width: '300px', pointerEvents: 'auto' }} // 确保 pointer-events 为 auto
/>
)}
</Form.Item>
<Form.Item label="个人资料" style={{ flexWrap: 'nowrap' }}>
{getFieldDecorator('attachment_id', {
rules: [],
})(
<Upload {...this.uploadProps()}>
<Button type="default" className='mr5'>
<Icon type="upload" /> 上传附件
</Button>
(单个文件最大20MB)
</Upload>
)}
</Form.Item>
</Form>
</Modal>
);
}
}
const WrappedPrivateAccessModal = Form.create()(PrivateAccessModal);
class IndexItem extends Component {
TurnToDetail = (login, url) => {
this.props.history.push({
pathname: url,
state: login
})
});
}
state = {
showModal: false,
projectInfo: null
};
openModal = (projectInfo) => {
this.setState({ showModal: true, projectInfo });
};
closeModal = () => {
this.setState({ showModal: false, projectInfo: null });
};
render() {
const { projects } = this.props;
const { projects, filter } = this.props;
return (
<div className="project-list minH-670">
{ projects && projects.length > 0 ? projects.map((item, key) => {
<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}>
{
item.platform === "educoder" ?
<a href="javascript:void(0)" style={{cursor:"default"}} className="show-user-link">
<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>
item.platform === "educoder" ? (
<a href="javascript:void(0)" style={{ cursor: "default" }} className="show-user-link">
<img className="p-r-photo" alt="" src={item.author && item.author.image_url} />
</a>
) : (
<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}`)} />
</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 onClick={(e) => {
if (item.is_member || item.is_manager || !item.join_project_need_verify) {
return true;
} else {
e.preventDefault();
this.openModal(item);
return false;
}
}} 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 ?
item.forked_from_project_id ? (
<span className="ml5">
<i className="iconfont icon-fork font-18 color-orange" />
</span>
: ""
) : null
}
{
item.type && item.type !== 0 ?
item.type === 2 ?
<Tooltip title="该项目是一个镜像" className="ml5">
<i className="iconfont icon-banbenku font-18 color-green" />
</Tooltip>:
<span className="ml5">
<i className="iconfont icon-jingxiang font-18 color-green" />
</span>:""
item.type && item.type !== 0 ?
item.type === 2 ?
<Tooltip title="该项目是一个镜像" className="ml5">
<i className="iconfont icon-banbenku font-18 color-green" />
</Tooltip> :
<span className="ml5">
<i className="iconfont icon-jingxiang font-18 color-green" />
</span> : null
}
</Link>
<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>
</span>
</AlignCenter>
<AlignCenter>
{
(item.is_manager && item.join_project_need_verify && !!item.pending_verify_count) && <span style={{ position: 'relative', display: 'inline-block', width: '120px', textAlign: 'center', background: 'rgba(250,100,0,0.1)', borderRadius: '2px', border: '1px solid #FFDCC5' }}>
<Badge dot offset={[-90, 7]} style={{ backgroundColor: '#FA6400' }}><span style={{ fontSize: '12px', color: '#FA6400' }}>待审核申请{item.pending_verify_count}</span></Badge>
</span>}
<span className="p-r-tags">
{
item.praises_count && item.praises_count > 0 ?
<span className="pariseTag">
<img src={img_parise} alt="" className="pariseImg" /> {item.praises_count}
</span> : null
}
{
item.forked_count && item.forked_count > 0 ?
<span>
<i className="iconfont icon-fork mr3 font-16" style={{ color: "#1B8FFF" }} />fork {item.forked_count}
</span> : null
}
{filter == "manage" && <div style={{ lineHeight: "normal" }}>
{item.audit_statu == 1 && <div className='rounded-14px px-10px py-2px' style={{ color: "#1CAC70", border: "1px solid #1CAC70", background: "rgba(60,218,152,0.14)" }}>公开审核中</div>}
{item.audit_statu == 3 && <div className='rounded-14px px-10px py-2px' style={{ color: "#EA7C1F", border: "1px solid #EA7C1F", background: "rgba(255,146,54,0.14)" }}>驳回审核中</div>}
</div>}
</span>
</AlignCenter>
</div>
<p className="break_word task-hide-2 mt10" style={{ maxHeight: "44px",lineHeight:"22px" }}>{item.description}</p>
<p className="break_word task-hide-2 mt10" style={{ maxHeight: "44px", lineHeight: "22px" }}>{item.description}</p>
<div className="p-r-about">
<span className="p-r-detail">
{/* <span><label>浏览量:</label>{item.visits}</span> */}
{/* {item.category && item.category.id && <span>{item.category.name}</span>} */}
{item.last_update_time ? <span><label>更新于</label>{item.time_ago}</span> : ""}
{item.language && item.language.id ? <span className="color-grey-3">{item.language.name}</span> : ""}
{item.last_update_time ? <span><label>更新于</label>{item.time_ago}</span> : null}
{item.language && item.language.id ? <span className="color-grey-3">{item.language.name}</span> : null}
</span>
</div>
</div>
</div>
)
}) : <Nodata _html="暂无数据~"></Nodata>}
);
}) : <Nodata _html="暂无数据~" />}
<WrappedPrivateAccessModal
visible={this.state.showModal}
onClose={this.closeModal}
projectInfo={this.state.projectInfo}
/>
</div>
)
);
}
}
export default IndexItem;

File diff suppressed because it is too large Load Diff

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,27 @@
import React, { useEffect, useState } from 'react';
import { Skeleton } from 'antd';
import { Skeleton , Tooltip,Badge } from 'antd';
import { Link } from 'react-router-dom';
import Quality from '../../Images/quality/quality.svg'
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 +41,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 +71,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 +86,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 +106,38 @@ 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>
{projectDetail && projectDetail.pending_verify_count ? <Badge dot offset={[6,-2]}><span>设置</span></Badge> :'设置'}
</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={{ pathname: `/projects/${owner}/${projectsId}/sonar`, state }}>
<i className={url && url.indexOf("/sonar") > 0 ? "iconfont icon-zhiliangfenxi color-grey-3 mr5 font-14":"iconfont icon-zhiliangfenxi color-grey-6 font-14 mr5"}></i>
{/* <img src={Quality} alt=''className='mt5 mr3' style={{width:'16px',height:'16px'}}/> */}
<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

@ -1,6 +1,6 @@
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { Input , Form , Select , Checkbox , Button , Spin , AutoComplete } from 'antd';
import { Input, Form, Select, Checkbox, Button, Spin, AutoComplete } from 'antd';
import { Base64 } from 'js-base64';
import '../css/index.scss';
@ -17,7 +17,7 @@ class Index extends Component {
gitignoreType: "0",
LicensesType: "0",
mirrorCheck:false,
mirrorCheck: false,
CategoryList: undefined,
LanguageList: undefined,
@ -30,20 +30,23 @@ class Index extends Component {
project_category_id: undefined,
license_id: undefined,
ignore_id: undefined,
owners_id:undefined,
owners_name:undefined,
owners_id: undefined,
owners_name: undefined,
project_language_list: undefined,
project_category_list: undefined,
license_list: undefined,
ignore_list: undefined,
owners_list:undefined,
owners_list: undefined,
project_language_name: undefined,
project_category_name: undefined,
license_name: undefined,
ignore_name: undefined
ignore_name: undefined,
descNum: 0,
join_project_need_verify: true,
private: true
}
}
componentDidMount = () => {
@ -58,35 +61,41 @@ class Index extends Component {
// 获取开源许可证
this.getLicenses();
}
componentDidUpdate=(prevPros)=>{
if(prevPros && this.props && !this.props.checkIfLogin()){
componentDidUpdate = (prevPros) => {
if (prevPros && this.props && !this.props.checkIfLogin()) {
this.props.history.push("/403")
return
}
}
getOwner=()=>{
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){
axios.get(url).then(result => {
if (result && result.data) {
let owner = result.data.owners;
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
})
}
this.setOptionsList(owner, 'owners');
this.setState({
OwnerList: owner,
})
if (OIdentifier) {
owner = owner.filter(item => item.name === OIdentifier);
this.props.form.setFieldsValue({
user_id: OIdentifier
})
} else if (user_id) {
owner = owner.filter(item => item.id === user_id);
this.props.form.setFieldsValue({
user_id: owner && owner[0].name
})
}
owner && this.setState({
owners_id: owner[0].id,
owners_name: owner[0].name
})
this.setOptionsList(owner, 'owners');
}
}).catch(error=>{})
}).catch(error => { })
}
getCategory = () => {
@ -162,17 +171,18 @@ class Index extends Component {
isSpin: true
})
const { projectsType } = this.props.match.params;
const { project_language_id, project_category_id, license_id, ignore_id , owners_id , owners_name } = this.state;
const { project_language_id, project_category_id, license_id, ignore_id, owners_id, owners_name, join_project_need_verify, private: isprivate } = this.state;
const decoderPass = Base64.encode(values.password);
const url = (projectsType && projectsType === "mirror") ? "/projects/migrate.json" : "/projects.json";
axios.post(url, {
...values,
auth_password:decoderPass,
auth_password: decoderPass,
project_language_id,
project_category_id,
license_id,
ignore_id,
user_id:owners_id
user_id: owners_id,
join_project_need_verify, isprivate
}).then((result) => {
if (result) {
if (result.data.id) {
@ -180,7 +190,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) => {
@ -221,7 +231,7 @@ class Index extends Component {
checkId = (rule, value, callback, list, title) => {
let filter = list.filter(item => item.name === value);
if(!value){
if (!value) {
callback();
}
if (filter && filter.length > 0) {
@ -232,28 +242,35 @@ class Index extends Component {
callback();
}
changeMirrorCheck=()=>{
changeMirrorCheck = () => {
const { mirrorCheck } = this.state;
this.setState({
mirrorCheck:!mirrorCheck
mirrorCheck: !mirrorCheck
})
}
ChangeAddr=(e)=>{
ChangeAddr = (e) => {
let value = e.target.value;
if(value.indexOf("/") > -1){
if (value.indexOf("/") > -1) {
let arr = value.split("/");
let first = arr[arr.length-1];
if(first.indexOf(".git") > -1){
let first = arr[arr.length - 1];
if (first.indexOf(".git") > -1) {
let second = first.split('.')[0];
if(!second)return;
if (!second) return;
this.props.form.setFieldsValue({
repository_name:second
repository_name: second
})
}
}
}
changeDesc = (e) => {
let value = e.target.value;
this.setState({
descNum: value ? value.length : 0
})
}
render() {
const { getFieldDecorator } = this.props.form;
// 项目类型deposit-托管项目mirror-镜像项目
@ -273,10 +290,13 @@ class Index extends Component {
license_list,
ignore_list,
mirrorCheck
mirrorCheck,
join_project_need_verify,
private: isprivate,
descNum
} = this.state;
return (
<div className="main back-white" style={{padding:"0px",border:"none"}}>
<div className="main back-white" style={{ padding: "0px", border: "none" }}>
<div className="newPanel">
<div className="newPanel_title">创建{projectsType && projectsType === "mirror" ? "镜像" : "托管"}项目</div>
<Spin spinning={isSpin}>
@ -288,16 +308,16 @@ class Index extends Component {
{getFieldDecorator('user_id', {
rules: [{
required: true, message: '请选择拥有者'
},{
validator:(rule, value, callback) => this.checkId(rule, value, callback, OwnerList, '拥有者')
}, {
validator: (rule, value, callback) => this.checkId(rule, value, callback, OwnerList, '拥有者')
}],
})(
<AutoComplete
<AutoComplete
placeholder="请选择拥有者"
onChange={(value, e) => this.ChangePlatform(value, e, 'owners', OwnerList)}
className="plateAutoComplete"
onBlur={(value) => this.blurCategory(value, OwnerList, "owners")}
>
>
{owners_list}
</AutoComplete>
)}
@ -323,10 +343,10 @@ class Index extends Component {
{
projectsType && projectsType === "mirror" &&
<React.Fragment>
<p className="mt10 mb10 color-grey-3 pointer" onClick={this.changeMirrorCheck}>需要授权验证<i className={mirrorCheck?"iconfont icon-xiajiantou font-13 ml10 color-grey-8":"iconfont icon-youjiantou font-13 ml10 color-grey-8"}></i></p>
<p className="mt10 mb10 color-grey-3 pointer" onClick={this.changeMirrorCheck}>需要授权验证<i className={mirrorCheck ? "iconfont icon-xiajiantou font-13 ml10 color-grey-8" : "iconfont icon-youjiantou font-13 ml10 color-grey-8"}></i></p>
{
mirrorCheck &&
<div className="df mb20" style={{alignItems:'center'}}>
<div className="df mb20" style={{ alignItems: 'center' }}>
<span className="mr10">用户名</span>
<Form.Item
style={{ marginBottom: "0px" }}
@ -335,7 +355,7 @@ class Index extends Component {
{getFieldDecorator('auth_username', {
rules: [],
})(
<Input placeholder="请输入对应平台的登录用户名" style={{width:"240px"}} />
<Input placeholder="请输入对应平台的登录用户名" style={{ width: "240px" }} />
)}
</Form.Item>
<span className="mr10">密码</span>
@ -346,7 +366,7 @@ class Index extends Component {
{getFieldDecorator('password', {
rules: [],
})(
<Input placeholder="请输入对应平台的登录密码" type="password" style={{width:"240px"}}/>
<Input placeholder="请输入对应平台的登录密码" type="password" style={{ width: "240px" }} />
)}
</Form.Item>
</div>
@ -361,21 +381,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 +406,7 @@ class Index extends Component {
required: true, message: '请填写仓库名称'
}],
})(
<Input placeholder="仓库名称请使用与项目相关的英文关键字" />
<Input placeholder="仓库名称请使用与项目相关的英文关键字" maxLength={100} />
)}
</Form.Item>
<Form.Item
@ -478,7 +500,7 @@ class Index extends Component {
className="privatePart"
>
{getFieldDecorator('private')(
<Checkbox value="limit">将项目设为私有<span className="ml15 font-13 color-grey-9">(只有项目所有人或拥有权限的项目成员才能看到)</span></Checkbox>
<Checkbox checked={isprivate} value="limit">提交项目审核<span className="ml15 font-13 color-grey-9">(项目需管理员审核通过才可公开展示未通过或待审核期间默认保持私有状态)</span></Checkbox>
)}
</Form.Item >
{
@ -493,6 +515,14 @@ class Index extends Component {
)}
</Form.Item >
}
<Form.Item
label="加入项目设置"
colon={false}
>
{getFieldDecorator('join_project_need_verify')(
<Checkbox checked={join_project_need_verify} onChange={(e) => this.setState({ join_project_need_verify: e.target.checked })}>用户加入项目时需要审核<span className="font-13 color-grey-9">使用邀请码加入也需要审核由项目所有人或管理员审核</span></Checkbox>
)}
</Form.Item >
<div>
<span className="ant-form-item-required"></span>
</div>

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" />

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