Compare commits

...

203 Commits

Author SHA1 Message Date
jasder b6f53575c9 Merge pull request '将功能合并至educoder forge(隐藏wiki)' (#32) from caishi/forgeplus-react:develop_educoder into develop_educoder 2021-09-03 13:43:52 +08:00
caishi 20e1d2c793 merge 转移仓库完成 2021-08-30 10:29:07 +08:00
caishi a2e89f09b0 merge 93a53e2c7f 2021-08-30 10:25:26 +08:00
caishi bb3d9d8bbd appconfig 2021-08-30 10:23:42 +08:00
caishi 12be5d17fd merge 4e48b0b363 2021-08-30 10:21:08 +08:00
caishi 8d08058e96 搜索用户 2021-08-30 10:10:54 +08:00
caishi b7d22f1233 merge 09065383c1 2021-08-30 10:06:16 +08:00
caishi 35ecf20e34 hide 2021-08-27 15:44:26 +08:00
caishi e9763ab932 项目列表头部button需要更新样式 2021-08-27 14:27:50 +08:00
caishi ff66526d88 合并请求-判断条件改成user_admin_or_member 2021-08-27 14:08:55 +08:00
caishi f74d4c140d 转移到组织-提示文案 2021-08-27 14:08:46 +08:00
caishi bade9a84fb 组织团队权限 2021-08-27 14:06:56 +08:00
caishi 24e7e3c70f 权限问题测试版-第二版 2021-08-27 14:06:40 +08:00
caishi 269964cc70 团队权限分支 2021-08-27 14:05:35 +08:00
何童崇 148b01ed34 merge 52e08e77e5 2021-08-27 13:49:29 +08:00
caishi 7838c7a1cb 组织团队设置:增加可选访问单元wiki 2021-08-27 13:46:15 +08:00
caishi 17bdd757ff 组织团队设置:可以设置版本库权限,允许设置访问项目单元 2021-08-27 13:46:04 +08:00
何童崇 c0774d123c 优化删除函数组件 2021-08-27 13:45:43 +08:00
何童崇 bc21da941c 修改头像,增加复制toolTip组件,优化组件代码 2021-08-27 13:45:36 +08:00
何童崇 81ccdae6e0 修改指向路径 2021-08-27 13:45:07 +08:00
何童崇 5a8e5df6bb 兼容页面刷新时的id新增 2021-08-27 13:44:47 +08:00
何童崇 ffc8ffdaf8 merge ff75065a0a 2021-08-27 11:39:03 +08:00
何童崇 fb9798becb 紧急修复镜像bug 2021-08-27 11:36:11 +08:00
caishi 6dbd2c8b1e merge 3294d99516 2021-08-27 11:36:01 +08:00
caishi 00cb2ad8f3 merge pre_develop 001cb8da53 2021-08-27 11:35:26 +08:00
caishi cde741f296 issue 2021-08-27 11:34:59 +08:00
caishi 61c55276de issue-同步镜像项目不能对仓库进行操作(新建合并请求、新建发行版等) 2021-08-27 11:34:38 +08:00
hc1913847458 ababcbfdd6 限制字符 2021-08-27 11:34:03 +08:00
hc1913847458 75f50c6742 merge pre_develop 2aad9c67f5 2021-08-27 11:33:47 +08:00
hc1913847458 f44c66d2f0 限制字符 2021-08-27 11:33:02 +08:00
caishi 1b2f107e3b markdown-issue 2021-08-27 11:32:52 +08:00
caishi 44dc67d9fa issue 2021-08-27 11:32:41 +08:00
caishi 9e8f2ff569 merge pre_develop 5140bfbe77 2021-08-27 11:32:27 +08:00
caishi 63cec25557 z-index 2021-08-03 10:40:01 +08:00
caishi 107b916b97 修改流水线名称弹框z-index 2021-08-03 10:32:40 +08:00
caishi 1155d7f7c6 密码同步弹框出现条件 2021-08-02 18:22:15 +08:00
caishi e4d4d93e5e 小需求 2021-07-15 18:22:23 +08:00
caishi c2f46a6a06 详情:复刻按钮不能连续点击 2021-07-13 09:59:41 +08:00
caishi 7bc795b501 appconfig 2021-07-13 09:34:48 +08:00
caishi 0a7e1bf1a3 新建合并请求:切换目标分支或者源分支时不用调用check_can_merge接口 2021-07-12 18:24:18 +08:00
caishi dea3b71539 邀请码位置,页面整体的加载 2021-07-09 17:51:57 +08:00
caishi 77fc84bdae 代码库:增加文件类型submodule 不允许点击 2021-07-09 11:30:05 +08:00
caishi 0942bc9c21 本地版:replaceAll无替换时报错 2021-07-09 09:36:13 +08:00
caishi c8f006284d 新建项目跳转地址login 2021-07-07 15:52:49 +08:00
caishi 92eb0b2d3c banner 2021-07-07 15:35:41 +08:00
caishi 96dba53ef9 代码库根目录:点击readme文件的编辑按钮时要显示编辑状态 2021-07-07 11:17:30 +08:00
caishi 6032550938 列表页:描述内容样式 2021-07-07 10:35:26 +08:00
caishi f4497533dc permission有为空的情况 2021-07-06 14:39:49 +08:00
caishi 3ac00c3f86 /默认跳转地址 2021-07-06 14:06:42 +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
216 changed files with 10517 additions and 4349 deletions

539
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

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

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,713 @@
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "23655968",
"name": "新手指引",
"font_class": "xinshouzhiyin",
"unicode": "e8e4",
"unicode_decimal": 59620
},
{
"icon_id": "23655969",
"name": "新建项目",
"font_class": "xinjianxiangmu",
"unicode": "e8e6",
"unicode_decimal": 59622
},
{
"icon_id": "23658111",
"name": "加入课堂",
"font_class": "jiaruketang1",
"unicode": "e8e9",
"unicode_decimal": 59625
},
{
"icon_id": "23791639",
"name": "项目公告",
"font_class": "xiangmugonggao",
"unicode": "e8c2",
"unicode_decimal": 59586
},
{
"icon_id": "23791640",
"name": "成果",
"font_class": "chengguo",
"unicode": "e8c3",
"unicode_decimal": 59587
},
{
"icon_id": "23791410",
"name": "成交公告",
"font_class": "chengjiaogonggao",
"unicode": "e8c0",
"unicode_decimal": 59584
},
{
"icon_id": "23791411",
"name": "技术资产",
"font_class": "jishuzichan",
"unicode": "e8c1",
"unicode_decimal": 59585
},
{
"icon_id": "23790928",
"name": "废标公告",
"font_class": "feibiaogonggao",
"unicode": "e8bc",
"unicode_decimal": 59580
},
{
"icon_id": "23790929",
"name": "中标公告",
"font_class": "zhongbiaogonggao",
"unicode": "e8bd",
"unicode_decimal": 59581
},
{
"icon_id": "23790930",
"name": "更正公告",
"font_class": "gengzhenggonggao",
"unicode": "e8be",
"unicode_decimal": 59582
},
{
"icon_id": "23790931",
"name": "招标公告",
"font_class": "zhaobiaogonggao",
"unicode": "e8bf",
"unicode_decimal": 59583
},
{
"icon_id": "23732532",
"name": "文件",
"font_class": "wenjian6",
"unicode": "e8ba",
"unicode_decimal": 59578
},
{
"icon_id": "23732533",
"name": "文件夹",
"font_class": "wenjianjia4",
"unicode": "e8bb",
"unicode_decimal": 59579
},
{
"icon_id": "23642443",
"name": "取消关注",
"font_class": "quxiaoguanzhu",
"unicode": "e89a",
"unicode_decimal": 59546
},
{
"icon_id": "23642444",
"name": "点赞_icon",
"font_class": "dianzan_icon",
"unicode": "e8a2",
"unicode_decimal": 59554
},
{
"icon_id": "23639530",
"name": "文件",
"font_class": "wenjian5",
"unicode": "e896",
"unicode_decimal": 59542
},
{
"icon_id": "23639533",
"name": "文件夹",
"font_class": "wenjianjia3",
"unicode": "e8a9",
"unicode_decimal": 59561
},
{
"icon_id": "23639440",
"name": "复制icon",
"font_class": "fuzhiicon",
"unicode": "e886",
"unicode_decimal": 59526
},
{
"icon_id": "23639422",
"name": "主页-fill",
"font_class": "zhuye-fill",
"unicode": "e876",
"unicode_decimal": 59510
},
{
"icon_id": "23639423",
"name": "代码库icon",
"font_class": "daimakuicon",
"unicode": "e884",
"unicode_decimal": 59524
},
{
"icon_id": "23572260",
"name": "新建",
"font_class": "xinjian2",
"unicode": "e8b0",
"unicode_decimal": 59568
},
{
"icon_id": "23567674",
"name": "协议icon",
"font_class": "xieyiicon",
"unicode": "e870",
"unicode_decimal": 59504
},
{
"icon_id": "23567675",
"name": "内存icon",
"font_class": "neicunicon",
"unicode": "e891",
"unicode_decimal": 59537
},
{
"icon_id": "23567676",
"name": "自述文件_icon",
"font_class": "zishuwenjian_icon",
"unicode": "e8a6",
"unicode_decimal": 59558
},
{
"icon_id": "23472253",
"name": "标签icon",
"font_class": "biaoqianicon",
"unicode": "e882",
"unicode_decimal": 59522
},
{
"icon_id": "23472254",
"name": "编 辑",
"font_class": "a-bianji",
"unicode": "e883",
"unicode_decimal": 59523
},
{
"icon_id": "23472256",
"name": "仓库设置icon",
"font_class": "cangkushezhiicon",
"unicode": "e885",
"unicode_decimal": 59525
},
{
"icon_id": "23472258",
"name": "链接icon",
"font_class": "lianjieicon",
"unicode": "e887",
"unicode_decimal": 59527
},
{
"icon_id": "23472259",
"name": "合并请求icon",
"font_class": "hebingqingqiuicon",
"unicode": "e888",
"unicode_decimal": 59528
},
{
"icon_id": "23472260",
"name": "里程碑icon",
"font_class": "lichengbeiicon",
"unicode": "e889",
"unicode_decimal": 59529
},
{
"icon_id": "23472261",
"name": "工作流icon",
"font_class": "gongzuoliuicon",
"unicode": "e88a",
"unicode_decimal": 59530
},
{
"icon_id": "23472262",
"name": "动态icon",
"font_class": "dongtaiicon",
"unicode": "e88b",
"unicode_decimal": 59531
},
{
"icon_id": "23472263",
"name": "默认点赞_icon",
"font_class": "morendianzan_icon",
"unicode": "e88e",
"unicode_decimal": 59534
},
{
"icon_id": "23472265",
"name": "目录icon",
"font_class": "muluicon",
"unicode": "e894",
"unicode_decimal": 59540
},
{
"icon_id": "23472267",
"name": "设 置",
"font_class": "a-shezhi",
"unicode": "e899",
"unicode_decimal": 59545
},
{
"icon_id": "23472269",
"name": "提交icon",
"font_class": "tijiaoicon",
"unicode": "e89e",
"unicode_decimal": 59550
},
{
"icon_id": "23472270",
"name": "默认关注_ICON",
"font_class": "morenguanzhu_ICON",
"unicode": "e89f",
"unicode_decimal": 59551
},
{
"icon_id": "23472272",
"name": "下拉按钮",
"font_class": "xialaanniu1",
"unicode": "e8a4",
"unicode_decimal": 59556
},
{
"icon_id": "23472276",
"name": "左滑icon",
"font_class": "zuohuaicon",
"unicode": "e8b5",
"unicode_decimal": 59573
},
{
"icon_id": "23472277",
"name": "master_icon",
"font_class": "master_icon",
"unicode": "e8b6",
"unicode_decimal": 59574
},
{
"icon_id": "23472278",
"name": "默认复刻_icon",
"font_class": "morenfuke_icon",
"unicode": "e8b7",
"unicode_decimal": 59575
},
{
"icon_id": "23472279",
"name": "wiki icon",
"font_class": "a-wikiicon",
"unicode": "e8b8",
"unicode_decimal": 59576
},
{
"icon_id": "23472280",
"name": "易修icon",
"font_class": "yixiuicon",
"unicode": "e8b9",
"unicode_decimal": 59577
},
{
"icon_id": "23436350",
"name": "缩放",
"font_class": "suofang",
"unicode": "e87f",
"unicode_decimal": 59519
},
{
"icon_id": "23436351",
"name": "放大icon",
"font_class": "fangdaicon",
"unicode": "e881",
"unicode_decimal": 59521
},
{
"icon_id": "23384231",
"name": "搜索_icon",
"font_class": "sousuo_icon1",
"unicode": "e873",
"unicode_decimal": 59507
},
{
"icon_id": "23384232",
"name": "欢迎_icon",
"font_class": "huanying_icon",
"unicode": "e878",
"unicode_decimal": 59512
},
{
"icon_id": "23384233",
"name": "文件夹",
"font_class": "wenjianjia2",
"unicode": "e879",
"unicode_decimal": 59513
},
{
"icon_id": "23384234",
"name": "删除icon",
"font_class": "shanchuicon1",
"unicode": "e87a",
"unicode_decimal": 59514
},
{
"icon_id": "23261798",
"name": "请求icon",
"font_class": "qingqiuicon",
"unicode": "e871",
"unicode_decimal": 59505
},
{
"icon_id": "23261799",
"name": "响应icon",
"font_class": "xiangyingicon",
"unicode": "e87c",
"unicode_decimal": 59516
},
{
"icon_id": "23144143",
"name": "多选选中",
"font_class": "duoxuanxuanzhong",
"unicode": "e88f",
"unicode_decimal": 59535
},
{
"icon_id": "23144144",
"name": "错误icon",
"font_class": "cuowuicon",
"unicode": "e890",
"unicode_decimal": 59536
},
{
"icon_id": "23144146",
"name": "成功icon",
"font_class": "chenggongicon",
"unicode": "e892",
"unicode_decimal": 59538
},
{
"icon_id": "23144147",
"name": "未选中响应icon",
"font_class": "weixuanzhongxiangyingicon",
"unicode": "e893",
"unicode_decimal": 59539
},
{
"icon_id": "23144149",
"name": "必填icon",
"font_class": "bitianicon",
"unicode": "e895",
"unicode_decimal": 59541
},
{
"icon_id": "23144151",
"name": "未选中项目标签icon",
"font_class": "weixuanzhongxiangmubiaoqianicon",
"unicode": "e897",
"unicode_decimal": 59543
},
{
"icon_id": "23144152",
"name": "列表icon",
"font_class": "liebiaoicon",
"unicode": "e898",
"unicode_decimal": 59544
},
{
"icon_id": "23144155",
"name": "未选中请求icon",
"font_class": "weixuanzhongqingqiuicon",
"unicode": "e89b",
"unicode_decimal": 59547
},
{
"icon_id": "23144158",
"name": "协作者管理icon",
"font_class": "xiezuozheguanliicon",
"unicode": "e8a1",
"unicode_decimal": 59553
},
{
"icon_id": "23144160",
"name": "选中分支icon",
"font_class": "xuanzhongfenzhiicon",
"unicode": "e8a3",
"unicode_decimal": 59555
},
{
"icon_id": "23144162",
"name": "选中基本设置icon",
"font_class": "xuanzhongjibenshezhiicon",
"unicode": "e8a5",
"unicode_decimal": 59557
},
{
"icon_id": "23144165",
"name": "选中项目标签icon",
"font_class": "xuanzhongxiangmubiaoqianicon",
"unicode": "e8aa",
"unicode_decimal": 59562
},
{
"icon_id": "23144167",
"name": "选中webhook icon",
"font_class": "a-xuanzhongwebhookicon",
"unicode": "e8af",
"unicode_decimal": 59567
},
{
"icon_id": "23046290",
"name": "shanchu_tc_icon",
"font_class": "shanchu_tc_icon",
"unicode": "e88c",
"unicode_decimal": 59532
},
{
"icon_id": "23046293",
"name": "wiki_icon",
"font_class": "wiki_icon",
"unicode": "e88d",
"unicode_decimal": 59533
},
{
"icon_id": "23046244",
"name": "导入模版_icon",
"font_class": "daorumoban_icon",
"unicode": "e86f",
"unicode_decimal": 59503
},
{
"icon_id": "23046252",
"name": "错误",
"font_class": "cuowu",
"unicode": "e872",
"unicode_decimal": 59506
},
{
"icon_id": "23046255",
"name": "更多_icon",
"font_class": "gengduo_icon",
"unicode": "e874",
"unicode_decimal": 59508
},
{
"icon_id": "23046258",
"name": "复层关闭_icon",
"font_class": "fucengguanbi_icon",
"unicode": "e875",
"unicode_decimal": 59509
},
{
"icon_id": "23046268",
"name": "删除icon",
"font_class": "shanchuicon",
"unicode": "e877",
"unicode_decimal": 59511
},
{
"icon_id": "23046273",
"name": "搜索_删除icon",
"font_class": "sousuo_shanchuicon",
"unicode": "e87b",
"unicode_decimal": 59515
},
{
"icon_id": "23046275",
"name": "搜索_icon",
"font_class": "sousuo_icon",
"unicode": "e87d",
"unicode_decimal": 59517
},
{
"icon_id": "23046276",
"name": "文档预览_icon",
"font_class": "wendangyulan_icon",
"unicode": "e87e",
"unicode_decimal": 59518
},
{
"icon_id": "23046278",
"name": "下拉按钮",
"font_class": "xialaanniu",
"unicode": "e880",
"unicode_decimal": 59520
},
{
"icon_id": "22906287",
"name": "二次确认_icon",
"font_class": "erciqueren_icon",
"unicode": "e867",
"unicode_decimal": 59495
},
{
"icon_id": "22906288",
"name": "选中ssh_icon",
"font_class": "xuanzhongssh_icon",
"unicode": "e868",
"unicode_decimal": 59496
},
{
"icon_id": "22906289",
"name": "未选中安全设置_icon",
"font_class": "weixuanzhonganquanshezhi_icon",
"unicode": "e869",
"unicode_decimal": 59497
},
{
"icon_id": "22906290",
"name": "未选中ssh_icon",
"font_class": "weixuanzhongssh_icon",
"unicode": "e86a",
"unicode_decimal": 59498
},
{
"icon_id": "22906291",
"name": "选中安全设置_icon",
"font_class": "xuanzhonganquanshezhi_icon",
"unicode": "e86b",
"unicode_decimal": 59499
},
{
"icon_id": "22906292",
"name": "删除_icon",
"font_class": "shanchu_icon",
"unicode": "e86c",
"unicode_decimal": 59500
},
{
"icon_id": "22906293",
"name": "列表ssh_icon",
"font_class": "liebiaossh_icon",
"unicode": "e86e",
"unicode_decimal": 59502
},
{
"icon_id": "17575494",
"name": "file-submodule",
"font_class": "file-submodule",
"unicode": "e866",
"unicode_decimal": 59494
},
{
"icon_id": "7539612",
"name": "nv",
"font_class": "nv1",
"unicode": "e864",
"unicode_decimal": 59492
},
{
"icon_id": "7539613",
"name": "nan",
"font_class": "nan1",
"unicode": "e865",
"unicode_decimal": 59493
},
{
"icon_id": "21936935",
"name": "邮箱",
"font_class": "youxiang",
"unicode": "e8b2",
"unicode_decimal": 59570
},
{
"icon_id": "21936924",
"name": "单位",
"font_class": "danwei",
"unicode": "e8a7",
"unicode_decimal": 59559
},
{
"icon_id": "21936925",
"name": "待办事项",
"font_class": "daibanshixiang",
"unicode": "e8a8",
"unicode_decimal": 59560
},
{
"icon_id": "21936928",
"name": "概览",
"font_class": "gailan",
"unicode": "e8ab",
"unicode_decimal": 59563
},
{
"icon_id": "21936929",
"name": "男",
"font_class": "nan",
"unicode": "e8ac",
"unicode_decimal": 59564
},
{
"icon_id": "21936930",
"name": "女",
"font_class": "nv",
"unicode": "e8ad",
"unicode_decimal": 59565
},
{
"icon_id": "21936931",
"name": "工作流",
"font_class": "gongzuoliu1",
"unicode": "e8ae",
"unicode_decimal": 59566
},
{
"icon_id": "21936934",
"name": "数据统计",
"font_class": "shujutongji",
"unicode": "e8b1",
"unicode_decimal": 59569
},
{
"icon_id": "21936936",
"name": "项目",
"font_class": "xiangmu",
"unicode": "e8b3",
"unicode_decimal": 59571
},
{
"icon_id": "21936937",
"name": "组织",
"font_class": "zuzhi",
"unicode": "e8b4",
"unicode_decimal": 59572
},
{
"icon_id": "14835599",
"name": "右箭头",
"font_class": "arrowRight",
"unicode": "e863",
"unicode_decimal": 59491
},
{
"icon_id": "21151489",
"name": "箭头镂空-左",
"font_class": "jiantouloukong-zuo",
"unicode": "e861",
"unicode_decimal": 59489
},
{
"icon_id": "21151557",
"name": "箭头镂空-右",
"font_class": "jiantouloukong-you",
"unicode": "e862",
"unicode_decimal": 59490
},
{
"icon_id": "21568989",
"name": "分享",
"font_class": "fenxiang1",
"unicode": "e89c",
"unicode_decimal": 59548
},
{
"icon_id": "21568990",
"name": "回到顶部",
"font_class": "huidaodingbu1",
"unicode": "e89d",
"unicode_decimal": 59549
},
{
"icon_id": "21568993",
"name": "帮助",
"font_class": "bangzhu",
"unicode": "e8a0",
"unicode_decimal": 59552
},
{
"icon_id": "991344",
"name": "提交",
@ -4236,7 +4943,7 @@
{
"icon_id": "1004630",
"name": "点赞2",
"font_class": "dianzan1",
"font_class": "dianzaned",
"unicode": "e639",
"unicode_decimal": 58937
},

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 733 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

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

View File

@ -60,12 +60,13 @@ body {
.ant-progress-textno {
color: #f5222d;
}
.CodeMirror pre.CodeMirror-line{
font-size: 16px!important;
}
/* md多空格 */
.markdown-body p {
margin:10px 0px!important;
font-size: 16px !important;
line-height: 2 !important;
white-space: pre-wrap;
}
@ -87,6 +88,10 @@ body {
border-left: 1px solid rgb(221, 221, 221);
/* 某些情况下被cm盖住了 */
z-index: 99;
padding:8px 8px 50px;
}
.editormd-preview .markdown-body{
padding:0px !important;
}
/* 图片点击放大的场景,隐藏图片链接 */

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'
@ -74,6 +73,15 @@ const EducoderLogin = Loadable({
loader: () => import('./modules/login/EducoderLogin'),
loading: Loading,
})
// const Search = Loadable({
// loader: () => import('./search/SearchPage'),
// loading: Loading,
// })
// const WikiPreview = Loadable({
// loader: () => import('./forge/Wiki/Preview'),
// loading: Loading,
// })
class App extends Component {
constructor(props) {
@ -100,36 +108,9 @@ 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();
@ -207,13 +188,23 @@ class App extends Component {
};
render() {
const { mygetHelmetapi } = this.state;
let personal = mygetHelmetapi && mygetHelmetapi.personal;
return (
<Provider store={store}>
<ConfigProvider locale={zhCN}>
<MuiThemeProvider theme={theme}>
<LoginDialog {...this.props} {...this.state} Modifyloginvalue={() => this.Modifyloginvalue()}></LoginDialog>
<SiderBar />
<Router>
<Switch>
{/* wiki预览 */}
{/* <Route path="/projects/:owner/:projectsId/wiki/preview/:projectName/:projectId" render={
(props) => {
return (<WikiPreview {...this.props} {...props} {...this.state} />)
}
} /> */}
{/*项目*/}
<Route
path={"/projects/:owner/:projectId/devops/:opsId/detail"}
@ -224,14 +215,6 @@ class App extends Component {
}>
</Route>
{/*项目*/}
<Route
path={"/projects"}
render={
(props) => {
return (<Projects {...this.props} {...props} {...this.state} />)
}
}>
</Route>
<Route
path="/register"
render={
@ -253,6 +236,11 @@ class App extends Component {
</Route>
{/*404*/}
<Route path="/nopage" component={Shixunnopage} />
{/* 查询 */}
{/* <Route path="/search" component={Search} /> */}
{/* 个人主页 */}
<Route path="/users/:username"
render={
@ -260,10 +248,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
}
}
@ -29,50 +27,19 @@ if (isDev) {
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 = "https://testforgeplus.educoder.net";
//响应前的设置
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`) {
@ -86,12 +53,6 @@ export function initAxiosInterceptors(props) {
} 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 +107,6 @@ export function initAxiosInterceptors(props) {
message501 = false
}, 2000);
}
requestMap[response.config.url] = false;
setpostcookie();
return response;
}, function (error) {
return Promise.reject(error);

View File

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

View File

@ -1,4 +1,5 @@
import moment from "moment";
import { number } from "prop-types";
// 处理整点 半点
// 取传入时间往后的第一个半点
@ -97,3 +98,40 @@ export function formatDuring(mss){
}
return days + "天" + hours + "小时" + minutes + "分";
}
/*
返回多久以前
backDate以前的某个日期
*/
export function timeAgo(backDate) {
try {
moment(backDate);
} catch (e) {
return;
}
if(typeof backDate ==='number'){
backDate=backDate*1000
}else{
backDate= moment(backDate);
}
let time = new Date() - backDate;
var days = Math.floor(time / (1000 * 60 * 60 * 24));
var hours = Math.floor((time % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
var minutes = Math.floor((time % (1000 * 60 * 60)) / (1000 * 60));
var seconds = Math.floor((time % (1000 * 60 * 60)) / 1000);
if (time <= 0) {
return "刚刚";
}
if (days) {
return days + "天前";
}
if (hours) {
return hours + "小时前";
}
if (minutes) {
return minutes + "分前";
}
if (seconds) {
return seconds + "秒前";
}
}

View File

@ -8,14 +8,20 @@ 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';
const local = 'https://testforgeplus.educoder.net';
const normal = 'https://ali-cdn.educoder.net/images';
const normalend = 'https://ali-cdn.educoder.net';
if(path.substr(0, 7) !== "/system"){
if (isDev) {
return `${local}/${path}`
}
return `/${path}`;
if(path.substr(0, 7) !== "/images"){
return `${normal}${path}`;
}else{
return `${normalend}${path}`;
}
}
return `${path}`;
}
export function getImage(path) {
@ -162,7 +168,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

@ -27,7 +27,7 @@ export {
markdownToHTML, uploadNameSizeSeperator, appendFileSizeToUploadFile, appendFileSizeToUploadFileAll, isImageExtension,
downloadFile, sortDirections, validateLength, mdJSONParse, exportMdtoHtml
} from './TextUtil'
export { handleDateString, getNextHalfHourOfMoment, formatDuring, formatSeconds } from './DateUtil'
export { handleDateString, getNextHalfHourOfMoment, formatDuring, formatSeconds ,timeAgo} from './DateUtil'
export { configShareForIndex, configShareForPaths, configShareForShixuns, configShareForCourses, configShareForCustom } from './util/ShareUtil'

View File

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

View File

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

View File

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

View File

@ -55,6 +55,7 @@ function AddGroup({organizeId,getGroupID}){
function addCollaborator(){
getGroupID && getGroupID(id);
setID(undefined);
}
return(

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(){
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{
@ -152,3 +155,209 @@ li.ant-menu-item{
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,44 @@
import React, { useState, useCallback, memo } from 'react';
import { Tooltip } from 'antd';
CopyTool.defaultProps = {
beforeText: '复制', //
afterText: '复制成功', //
className: '', //svgclass
inputId: 'copyText', //ID
};
function CopyTool({ beforeText, afterText, className,inputId }) {
const [title, setTitle] = useState(() => {
return beforeText;
});
//
const copyUrl = useCallback(() => {
let inputDom = document.getElementById(inputId);
if (!inputDom) {
console.error("您的CopyTool未设置正确的inputId");
return;
}
inputDom.select();
if (document.execCommand('copy')) {
document.execCommand('copy');
}
setTitle(afterText);
inputDom.blur();
}, []);
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

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

View File

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

View File

@ -0,0 +1,72 @@
import React , {forwardRef, useEffect} from 'react';
import { Modal , Form , Input , Button } from 'antd';
import './EAccount.scss';
function EducoderAccount({form , visible , onOk , email , onCancel , match , unReload}){
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 },
};
function Cancel() {
onCancel();
}
return(
<Modal
visible={visible}
title="提示"
width="500px"
closable={false}
footer={
<div>
<Button onClick={Cancel}>暂不绑定随便看看</Button>
<Button type="primary" onClick={onSure}>绑定邮箱</Button>
</div>
}
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

@ -2,7 +2,7 @@ import React from 'react';
import { AlignCenter , AlignTop , FlexAJ } from '../Component/layout';
import { Link } from 'react-router-dom';
function Releases({owner,projectsId,releaseVersions}){
function Releases({owner,projectsId,releaseVersions , baseOperate , projectType}){
return(
<div>
@ -10,10 +10,10 @@ function Releases({owner,projectsId,releaseVersions}){
<AlignCenter><span className="font-16 color-grey-6">发行版</span>
{ releaseVersions && releaseVersions.total_count > 0 && <span className="infoCount">{releaseVersions.total_count}</span>}
</AlignCenter>
{ releaseVersions && releaseVersions.total_count > 0 ?
{ (releaseVersions && releaseVersions.total_count > 0) || projectType ===2 ?
<Link className="font-12 color-grey-9" to={`/projects/${owner}/${projectsId}/releases`}>全部</Link>
:
<Link className="font-12 color-blue" to={`/projects/${owner}/${projectsId}/releases/new`}>新建</Link>
baseOperate && <Link className="font-12 color-blue" to={`/projects/${owner}/${projectsId}/releases/new`}>新建</Link>
}
</FlexAJ>
{
@ -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

@ -1,24 +1,33 @@
import React , { useState } from 'react';
import React , { useState , useEffect } from 'react';
import { AutoComplete } from 'antd';
import { getImageUrl } from "educoder";
import axios from 'axios';
const Option = AutoComplete.Option;
export default ({ getUser })=>{
export default ({ getUser , placeholder, width ,value })=>{
const [ source , setSource ] = useState(undefined);
const [ searchKey , setSearchKey ] = useState(undefined);
const [ userDataSource , setUserDataSource ] = useState(undefined);
useEffect(()=>{
if(!value){
setSearchKey(undefined);
}
},[value])
useEffect(()=>{
getUserList();
},[searchKey])
function getUserList(e){
const url = `/users/list.json`;
axios.get(url, {
params: {
search: e,
search: searchKey,
},
})
.then((result) => {
}).then((result) => {
if (result) {
setUserDataSource(result.data.users);
sourceOptions(result.data.users);
}
})
.catch((error) => {
@ -26,25 +35,20 @@ export default ({ getUser })=>{
});
};
function changeInputUser(value){
setSearchKey(value);
getUserList(value);
}
function selectInputUser(id, option){
setSearchKey(option.props.value);
getUserList(option.props.value);
getUser && getUser(id);
}
const source =
userDataSource && userDataSource.map((item, key) => {
function sourceOptions(userDataSource){
const s = userDataSource && userDataSource.map((item, key) => {
return (
<Option key={key} value={`${item.login}`}>
<Option
key={key}
value={`${item.user_id}`}
login={`${item.login}`}
name={item.username}
>
<img
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" }}>
@ -54,14 +58,31 @@ export default ({ getUser })=>{
</Option>
);
});
setSource(s);
}
function changeInputUser(e){
setSearchKey(e);
};
//
function selectInputUser(e, option){
setSearchKey(option.props.name);
getUser(option.props.login);
};
return(
<div className="addPanel">
<AutoComplete
getPopupContainer={trigger => trigger.parentNode}
dataSource={source}
value={searchKey}
style={{ width: 300 }}
style={{ width: width || 300 }}
onChange={changeInputUser}
onSelect={selectInputUser}
placeholder="搜索需要添加的用户..."
placeholder={placeholder || "搜索需要添加的用户..."}
allowClear
/>
</div>
)
}

View File

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

View File

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

View File

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

View File

@ -37,7 +37,7 @@ function About(props, ref) {
const [ typeFlag, setTypeFlag] = useState(false);
const AuthorLogin = props.author && props.author.login;
const AuthorLogin = props.projectDetail && props.projectDetail.author && props.projectDetail.author.login;
const CurrentLogin = props.current_user && props.current_user.login;
useEffect(()=>{
if(CurrentLogin === AuthorLogin){

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

@ -42,7 +42,7 @@ function PipelineName({visible,onCancel,onOk,value ,branchList}){
</div>
<div className="choosenList mt20">
<span>触发条件:</span>
<Select value={branchValue} style={{width:"150px"}} onChange={(e)=>setBranchValue(e)}>
<Select value={branchValue} style={{width:"150px"}} dropdownClassName="chooseCon" onChange={(e)=>setBranchValue(e)}>
{
branchList && branchList.length>0 && branchList.map((item,key)=>{
return(
@ -51,7 +51,7 @@ function PipelineName({visible,onCancel,onOk,value ,branchList}){
})
}
</Select>
<Select mode="multiple" allowClear value={eventValue} style={{width:"180px",marginLeft:"10px"}} onChange={(e)=>{console.log(e);setEventValue(e)}}>
<Select mode="multiple" allowClear value={eventValue} dropdownClassName="chooseCon" style={{width:"180px",marginLeft:"10px"}} onChange={(e)=>{console.log(e);setEventValue(e)}}>
{
EVENT.map((item,key)=>{
return(

View File

@ -33,6 +33,14 @@ const Params = Loadable({
export default ((props)=>{
useEffect(()=>{
const { projectsId , owner } = props.match.params;
const { giteaVisible , showEABox } = props;
if(giteaVisible){
showEABox && showEABox(`/projects/${owner}/${projectsId}`);
}
},[])
return(
<WhiteBack className="opsPanel">
<Switch {...props}>

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}>EduCoder服务器</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

@ -194,6 +194,18 @@
&.rightSection{
width:100%;
background-color: #081930;
.devopsNav{
background-color: #111c24;
border-bottom: none;
.ant-menu-item{
color: #ccc;
padding:0px;
margin:0px 20px!important;
}
.ant-menu-item.ant-menu-item-selected{
color: #1890ff;
}
}
.rightMainContent{
padding:24px 30px;
height:100vh;
@ -379,6 +391,9 @@
}
}
}
.chooseCon.ant-select-dropdown{
z-index: 100001;
}
.choosenList{
display: flex;

View File

@ -0,0 +1,167 @@
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();
}
},[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();
}
}).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:"请输入目标用户名"}]
})(
<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>
<span className="color-grey-9" style={{marginLeft:"120px"}}>请输入当前项目的标识<span className="ml5 mr5 color-grey-3">{repo}</span>进行确认</span>
</Form>
</div>
</Modal>
)
}
export default Form.create()(forwardRef(DivertModal));

View File

@ -0,0 +1,12 @@
.diverModal{
.descUl{
background-color: #fffae6;
border-radius: 4px;
padding:10px 15px;
color: #efc16b;
border:1px solid #efc16b;
}
.ant-form-item-required::before{
content: "";
}
}

View File

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

View File

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

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

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

View File

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

View File

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

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

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,10 @@ const ProjectDetail = Loadable({
loading: Loading,
});
const Infos = Loadable({
loader: () => import("./users/Infos"),
loading: Loading,
});
class Index extends Component {
render() {
return (
<div className="newMain clearfix">
<Handbook />
<Switch {...this.props}>
<Route
path="/projects/:projectsType/new/:OIdentifier"
@ -68,12 +62,8 @@ class Index extends Component {
)}
></Route>
<Route
exact
path="/"
render={(props) => (
this.props.current_user && this.props.current_user.login ?
<Infos {...this.props} {...props} />
:
<ProjectIndex {...this.props} {...props} />
)}
></Route>

View File

@ -19,10 +19,27 @@ import LanguagePower from '../Component/LanguagePower';
import DrawerPanel from '../Component/DrawerPanel';
import UpdateDescModal from './sub/UpdateDescModal';
import Nodata from '../Nodata';
import Invite from './sub/Invite';
/**
* projectDetail.type:0是托管项目1是镜像项目2是同步镜像项目(为2时不支持在线创建在线上传在线修改在线删除创建合并请求等功能)
*/
function turnbar(str){
if(str && str.length>0 && str.indexOf("/")>-1){
return str.replaceAll('/','%2F');
}
return str;
}
function returnbar(str){
if(str && str.length>0 && str.indexOf("%2F")>-1){
return str.replaceAll('%2F','/');
}
return str;
}
function CoderDepot(props){
const [ projectDetail , setProjectDetail ]= useState(undefined);
const [ inviteCode , setInviteCode ] = useState(undefined);
const [ treeValue , setTreeValue ] = useState(undefined);
const [ treeValuePath , setTreeValuePath ] = useState(undefined);
const [ lastCommit,setLastCommit ] = useState(undefined);
@ -42,19 +59,46 @@ function CoderDepot(props){
const [ openModal , setOpenModal ] = useState(false);
const [ desc , setDesc ] = useState(undefined);
const [ website , setWebsite ] = useState(undefined);
const [ lesson_url , setLessonUrl ] = useState(undefined);
const [ readme , setReadme ] = useState(undefined);
const [ defaultBranch , setDefaultBranch ] = useState(undefined);
const [ editReadme , setEditReadme ] = useState(false);
const [ pullsFlag , setPullsFlag ] = useState(true);
const [ issuesFlag , setIssuesFlag ] = useState(true);
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;
const { bannerList } = props;
useEffect(()=>{
if(props.projectDetail){
setProjectDetail(props.projectDetail);
setDesc(props.projectDetail.description);
setWebsite(props.projectDetail.website);
if(bannerList && bannerList.length>0){
let a = bannerList.filter(i=>i.menu_name === "pulls");
let i = bannerList.filter(i=>i.menu_name === "issues");
if(a && a.length === 0){
setPullsFlag(false);
}
},[props])
if(i && i.length === 0){
setIssuesFlag(false);
}
}
},[bannerList])
useEffect(()=>{
if(details){
setProjectDetail(details);
setDesc(details.description);
setWebsite(details.website);
setLessonUrl(details.lesson_url);
setDefaultBranch(details.default_branch);
setInviteCode(details.invite_code);
}
},[details])
useEffect(()=>{
if(treeValue){
@ -64,23 +108,28 @@ function CoderDepot(props){
}
},[treeValue])
useEffect(()=>{
if (pathname){
if(pathname.indexOf(`/projects/${owner}/${projectsId}`) > -1 && pathname.indexOf(`/tree/${branchName}/`) > -1) {
let url = pathname.split(`/tree/${branchName}/`)[1];
if (projectsId && owner && defaultBranch){
let b = turnbar(branchName) ;
if(pathname.indexOf(`/projects/${owner}/${projectsId}`) > -1 && pathname.indexOf(`/tree/${b}/`) > -1) {
let url = pathname.split(`/tree/${b}/`)[1];
setTreeValue(url);
getFileInfo(url,branchName);
setType("file");
}else{
setTreeValue(undefined);
getDirInfo(branchName ||(projectDetail && projectDetail.default_branch));
getDirInfo(branchName || defaultBranch);
setType("dir");
}
}
},[pathname])
},[projectsId,owner,pathname,defaultBranch])
//
function getDirInfo(branch){
setIsSpin(true);
const url = `/${owner}/${projectsId}/entries.json`;
axios.get(url, {
params: { ref: branch }
}).then((result) => {
@ -92,8 +141,13 @@ 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);
console.log("dddd:",result.data.entries);
}
setTimeout(function(){setIsSpin(false);},500);
}).catch(error=>{setIsSpin(false);})
@ -105,7 +159,7 @@ function CoderDepot(props){
let ele = document.getElementById("ptxt");
if(ele){
let h = ele.offsetHeight;
if( h > 18 ) setHideBtn(true)
if( h > 18 ) setHideBtn(true);
}
}
},[projectDetail,lastCommit])
@ -133,8 +187,10 @@ function CoderDepot(props){
}
let c = result.data.last_commit
setLastCommit(c && c.commit);
setLastCommitAuthor(c && (c.author || (c.commit && c.commit.author)));
setLastCommitAuthor(c && c.committer);
setMainFlag(false);
setReadOnly(!editReadme);
setHide(true);
}
setTimeout(function(){setIsSpin(false);},500)
}).catch(error=>{setIsSpin(false);})
@ -142,17 +198,22 @@ function CoderDepot(props){
//
function changeBranch(value){
let url = `/projects/${owner}/${projectsId}${value && `/tree/${value}`}${treeValue ? `/${treeValue}`:""}`;
let checkvalue = turnbar(value);
let url = `/projects/${owner}/${projectsId}${value && `/tree/${checkvalue}`}${treeValue ? `/${treeValue}`:""}`;
props.history.push(url);
}
//
const fileMenu =(
function fileMenu(){
let b = branchName || defaultBranch;
let checkvalue = turnbar(b);
return (
<Menu>
<Menu.Item><a onClick={()=>urlLink(`/projects/${owner}/${projectsId}/${branchName || (projectDetail && projectDetail.default_branch)}/uploadfile${treeValue === undefined ? "" : `/${treeValue}`}`)}>上传文件</a></Menu.Item>
<Menu.Item><a onClick={()=>urlLink(`/projects/${owner}/${projectsId}/${branchName || (projectDetail && projectDetail.default_branch)}/newfile${treeValue === undefined ? "" : `/${treeValue}`}`)}>新建文件</a></Menu.Item>
<Menu.Item><a onClick={()=>urlLink(`/projects/${owner}/${projectsId}/${checkvalue}/uploadfile${treeValue === undefined ? "" : `/${treeValue}`}`)}>上传文件</a></Menu.Item>
<Menu.Item><a onClick={()=>urlLink(`/projects/${owner}/${projectsId}/${checkvalue}/newfile${treeValue === undefined ? "" : `/${treeValue}`}`)}>新建文件</a></Menu.Item>
</Menu>
)
}
function getPathUrl(array,index){
if(array && array.length>0 && index){
@ -166,27 +227,36 @@ function CoderDepot(props){
//
function returnMain(){
setTreeValue(undefined);
let branch = branchName || (projectDetail && projectDetail.default_branch);
props.history.push(`/projects/${owner}/${projectsId}/tree/${branch}`);
let branch = branchName || defaultBranch;
let checkvalue = turnbar(branch);
props.history.push(`/projects/${owner}/${projectsId}/tree/${checkvalue}`);
};
//
function returnUlr(url){
props.history.push(`/projects/${owner}/${projectsId}/tree${branchName?`/${branchName}`:""}/${url}`);
let enBranch = turnbar(branchName);
props.history.push(`/projects/${owner}/${projectsId}/tree${enBranch?`/${enBranch}`:""}/${url}`);
}
//
function goToSubRoot(path,type,filename){
if(type!=="submodule"){
let enBranch = branchName || defaultBranch;
let checkvalue = turnbar(enBranch);
setType(type);
props.history.push(`/projects/${owner}/${projectsId}${`/tree/${branchName || (projectDetail && projectDetail.default_branch)}`}${path?`/${path}`:""}`);
props.history.push(`/projects/${owner}/${projectsId}${`/tree/${checkvalue}`}${path?`/${path}`:""}`);
}
}
function onEdit(readOnly){
setReadOnly(readOnly);
setEditReadme(false);
}
function ChangeFile(path, readOnly){
//
props.history.push(`/projects/${owner}/${projectsId}/tree/${branchName || (projectDetail && projectDetail.default_branch)}/${path}`);
let enBranch = branchName || defaultBranch;
let checkvalue = turnbar(enBranch);
props.history.push(`/projects/${owner}/${projectsId}/tree/${checkvalue}/${path}`);
setType("file");
setReadOnly(readOnly);
setEditReadme(true);
};
function changeHide(hide){
@ -214,20 +284,28 @@ function CoderDepot(props){
</Menu>
</div>
)
function okUpdate(d,w){
// website
function okUpdate(d,w,l){
const url = `/${owner}/${projectsId}.json`;
axios.put(url,{
description:d,website:w
description:d,website:w,lesson_url:l
}).then(result=>{
if(result && result.data && result.data.id){
setDesc(result.data.description);
setWebsite(result.data.website);
setLessonUrl(result.data.lesson_url);
}
})
}
let n = fileInfo && fileInfo.name;
const mdFlag = n && n.substring(n.length-3,n.length) === ".md";
const { current_user } = props;
const baseOperate = projectDetail && projectDetail.permission && projectDetail.permission !=="Reporter";
const fileOperate = type === "dir" && projectDetail && projectDetail.type !== 2 && ((projectDetail.permission && projectDetail.permission !=="Reporter") || (current_user && current_user.admin));
return(
<WhiteBack>
<UpdateDescModal desc={desc} website={website} visible={openModal} onCancel={()=>setOpenModal(false)} onOk={okUpdate}/>
<UpdateDescModal desc={desc} website={website} lesson_url={lesson_url} visible={openModal} onCancel={()=>setOpenModal(false)} onOk={okUpdate}/>
<Spin spinning={isSpin}>
{
(dirInfo || fileInfo) &&
@ -237,7 +315,7 @@ function CoderDepot(props){
owner={owner}
projectsId={projectsId}
name={projectDetail && projectDetail.name}
branch={branchName || (projectDetail && projectDetail.default_branch)}
branch={branchName || defaultBranch}
visible={visible}
onClose={()=>setVisible(false)}
list = {mainFlag ? dirInfo : undefined}
@ -262,14 +340,14 @@ function CoderDepot(props){
<SelectBranch
repo_id={projectDetail && projectDetail.repo_id}
projectsId={projectsId}
branch={branchName || (projectDetail && projectDetail.default_branch)}
branch={branchName || defaultBranch}
changeBranch={changeBranch}
owner={owner}
history={props.history}
branchList={projectDetail && projectDetail.branches && projectDetail.branches.list}
></SelectBranch>
:
<span>分支<span className="color-grey-6">{branchName || (projectDetail && projectDetail.default_branch)}</span></span>
<span>分支<span className="color-grey-6">{branchName || defaultBranch}</span></span>
}
</div>
<AlignCenter className="mr20">
@ -286,32 +364,46 @@ function CoderDepot(props){
</AlignCenter>
</AlignCenter>
<AlignCenter>
{
baseOperate && ((projectDetail.type !== 2 && pullsFlag) || issuesFlag )&&
<div className="mr20 addOptionBtn">
{
projectDetail.type !== 2 && pullsFlag &&
<a onClick={()=>urlLink(`/projects/${owner}/${projectsId}/pulls/new`)} >+ 合并请求</a>
}
{
issuesFlag &&
<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 ?
(dirInfo && dirInfo.length>0) || 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>
<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>:"" }
{ commitCount ? <Link to={`/projects/${owner}/${projectsId}/commits/branch/${turnbar(branchName || defaultBranch)}`} className="ml12 color-grey-9">
<i className="iconfont icon-tijiao mr3 font-17 color-grey-9"></i>{commitCount}次提交
</Link>:"" }
</div>
}
<ul className="listtablebody">
@ -344,8 +436,10 @@ function CoderDepot(props){
{...props}
detail={fileInfo}
readOnly={readOnly}
md={mdFlag}
onEdit={onEdit}
currentBranch={branchName || (projectDetail && projectDetail.default_branch)}
currentBranch={branchName || defaultBranch}
type={projectDetail.type}
></CoderRootFileDetail>
}
</ul>
@ -353,10 +447,10 @@ function CoderDepot(props){
: ""
}
{
(dirInfo && dirInfo.length === 0) && (fileInfo && fileInfo.length === 0) ? <Nodata _html="暂未发现文件"/> :""
(dirInfo && dirInfo.length === 0) && !fileInfo ? <Nodata _html="暂未发现文件"/> :""
}
{/* readme文件显示(显示文件详情时不显示readme文件) */}
{ dirInfo && (projectDetail && projectDetail.readme) ? <ReadMe ChangeFile={ChangeFile} readme={projectDetail && projectDetail.readme} operate={props && (props.isManager || props.isDeveloper)} history={props.history} /> :"" }
{ dirInfo && (readme && readme.content) ? <ReadMe ChangeFile={ChangeFile} readme={readme} operate={props && (props.isManager || props.isDeveloper) && projectDetail.type !==2 } history={props.history} /> :"" }
</div>
</LongWidth>
{
@ -372,7 +466,7 @@ function CoderDepot(props){
website &&
<p className="color-grey-6 df">
<i className="iconfont icon-lianjie2 font-15 mr10 color-grey-9"></i>
<span style={{wordBreak:"break-all",lineHeight:"20px",marginTop:"5px"}}>{website}</span>
<a href={website} target="_blank" style={{wordBreak:"break-all",lineHeight:"20px",marginTop:"5px",textDecoration:"underline"}}>{website}</a>
</p>
}
<p>
@ -391,24 +485,43 @@ 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",wordBreak:"break-all"}}>{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}
baseOperate={baseOperate}
projectType={projectDetail.type}
/>
</React.Fragment>
}
{/* 贡献者 */}
{
projectDetail && projectDetail.contributors &&
<React.Fragment>
<Divider />
<Contributors contributors={projectDetail && projectDetail.contributors} owner={owner} projectsId={projectsId}/>
</React.Fragment>
projectDetail && projectDetail.contributors && projectDetail.contributors.length >0 &&
<Contributors contributors={projectDetail && projectDetail.contributors} owner={owner} projectsId={projectsId} />
}
{/* 语言 */}
{ projectDetail && projectDetail.languages &&
{ projectDetail && projectDetail.languages && projectDetail.languages.length >0 &&
<React.Fragment>
<Divider />
<LanguagePower languages={projectDetail.languages}/>

View File

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

View File

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

View File

@ -6,12 +6,18 @@ 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);
const { projectsId , owner } = props.match.params;
const { isManager , isDeveloper , projectDetail } = props;
useEffect(()=>{
getBranchs(projectsId, owner);
},[projectsId])
@ -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,10 @@ export default ((props)=>{
</p>
</div>
<span>
<Link to={`/projects/${owner}/${projectsId}/pulls/new`} className="mr20 color-blue mr30">创建合并请求</Link>
{
(isManager || isDeveloper) && (projectDetail && projectDetail.type!==2) &&
<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>
@ -64,6 +73,7 @@ export default ((props)=>{
<Menu.Item key={'1'}><a href={tar_url}>TAR.GZ</a></Menu.Item>
</Menu>
)
return(
<React.Fragment>
<div className="main">

View File

@ -2,12 +2,19 @@ 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';
import axios from 'axios';
import {Link} from "react-router-dom";
function returnbar(str){
if(str && str.length>0 && str.indexOf("%2F")>-1){
return str.replaceAll('%2F','/');
}
return str;
}
class CoderRootCommit extends Component{
constructor(props){
super(props)
@ -56,11 +63,12 @@ class CoderRootCommit extends Component{
this.setState({
isSpining:true
})
console.log(returnbar(branch));
const { projectsId , owner } = this.props.match.params;
const url = `/${owner}/${projectsId}/commits.json`;
axios.get(url,{
params:{
sha:branch,
sha:returnbar(branch),
page,
limit
}
@ -74,6 +82,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,
@ -104,7 +113,7 @@ class CoderRootCommit extends Component{
const { commitDatas , dataCount , limit , page , isSpining , branchList } = this.state;
const { projectDetail, commit_class , defaultBranch } = this.props;
const { projectsId , owner , branchName } = this.props.match.params;
let branch = branchName || defaultBranch;
let branch = returnbar(branchName || defaultBranch);
return(
<React.Fragment>
<div className={"main"}>
@ -131,15 +140,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">
{
item.id ?
<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"/>:""}
{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>
</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

@ -102,6 +102,11 @@ class CoderRootIndex extends Component{
(props) => (<FileNew {...this.props} {...props} {...this.state} getTopCount={this.getTopCount} />)
}
></Route>
<Route path="/projects/:owner/:projectsId/commits/branch/:branchName"
render={
() => (<CoderRootCommit {...this.props} {...this.state} commit_class="main" getTopCount={this.getTopCount} />)
}
></Route>
<Route path="/projects/:owner/:projectsId/commits/:sha"
render={
(props) => (<Diff {...this.props} {...props} {...this.state}/>)

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';
@ -127,30 +127,40 @@ const Source = Loadable({
const DevIndex = Loadable({
loader: () => import('../DevOps/Index'),
loading: Loading,
})
});
const Wiki = Loadable({
loader: () => import('../Wiki/Index'),
loading: Loading,
});
const WikiEdit = Loadable({
loader: () => import('../Wiki/EditWiki'),
loading: Loading,
});
/**
* permissionManager:管理员Reporter报告人员(只有读取权限)Developer开发人员除不能设置仓库信息外
*/
function checkPathname(projectsId,owner,pathname){
function checkPathname(projectsId, owner, pathname) {
let name = "";
if(pathname && pathname !== `/projects/${owner}/${projectsId}`){
if (pathname && pathname !== `/projects/${owner}/${projectsId}`) {
let url = pathname.split(`/projects/${owner}/${projectsId}`)[1];
if(url.indexOf("/about")>-1){
name="about"
}else if(url.indexOf("/issues")>-1 ||url.indexOf("Milepost") > 0){
if (url.indexOf("/about") > -1) {
name = "about"
} else if (url.indexOf("/issues") > -1 || url.indexOf("Milepost") > 0) {
name = "issues";
}else if(url.indexOf("/pulls")>-1){
name="pulls"
}else if(url.indexOf("/milestones")>-1){
name="milestones"
}else if(url.indexOf("/activity")>-1){
name="activity"
}else if(url.indexOf("/setting")>-1){
name="setting"
}else if(url.indexOf(`/devops`)>-1){
name="devops"
}else if(url.indexOf(`/source`)>-1){
name="source"
} else if (url.indexOf("/pulls") > -1) {
name = "pulls"
} else if (url.indexOf("/milestones") > -1) {
name = "milestones"
} else if (url.indexOf("/activity") > -1) {
name = "activity"
} else if (url.indexOf("/setting") > -1) {
name = "setting"
} else if (url.indexOf(`/devops`) > -1) {
name = "devops"
} else if (url.indexOf(`/source`) > -1) {
name = "source"
} else if (url.indexOf(`/wiki`) > -1) {
name = "wiki"
}
}
return name;
@ -173,14 +183,15 @@ class Detail extends Component {
branchs: undefined,
branchList: undefined,
project: null,
firstSync:false,
secondSync:false,
open_devops:false,
firstSync: false,
secondSync: false,
open_devops: false,
forkSpin: false,
// 默认分支
defaultBranch:undefined,
defaultBranch: undefined,
// 非本平台项目
platform:false
platform: false
}
}
@ -197,36 +208,38 @@ class Detail extends Component {
}
getProject = (num) => {
const { projectsId , owner } = this.props.match.params;
const { projectsId, owner } = this.props.match.params;
const url = `/${owner}/${projectsId}/simple.json`;
axios.get(url).then((result) => {
if (result && result.data) {
this.setState({
project: result.data,
open_devops:result.data.open_devops,
platform:result.data.platform && result.data.platform !== 'educoder'
open_devops: result.data.open_devops,
platform: result.data.platform && result.data.platform !== 'educoder'
})
if (result.data.type !== 0 && result.data.mirror_status === 1) {
console.log("--------start channel --------");
// 是镜像项目,且未完成迁移
this.canvasChannel();
if(num){
if (num) {
this.setState({
secondSync:true,
firsrtSync:false
secondSync: true,
firstSync: false
})
}else{
} else {
this.setState({
firstSync:true,
secondSync:false
firstSync: true,
secondSync: false
})
}
}else{
} else if (result.data.mirror_status === 2) {
this.deleteProjectBack();
} else {
this.getDetail();
this.setState({
firsrtSync:false,
secondSync:false
firstSync: false,
secondSync: false
})
}
}
@ -234,9 +247,9 @@ class Detail extends Component {
}
// 工作流激活后修改状态
changeOpenDevops=(flag)=>{
changeOpenDevops = (flag) => {
this.setState({
open_devops:flag
open_devops: flag
})
}
canvasChannel = () => {
@ -246,7 +259,7 @@ class Detail extends Component {
var cable = actioncable.createConsumer(`wss://${name}/cable`);
this.canvasChannel1 = cable.subscriptions.create({
channel: `MirrorProjectChannel`,
id: project && project.identifier
id: project && project.id
}, {
connected: () => {
console.log("###### channel connected! ######");
@ -254,16 +267,41 @@ class Detail extends Component {
disconnected: () => { },
received: data => {
console.log(`###### ---received data--- ######`);
console.log(data);
if (data) {
if ( data.project && data.project.mirror_status === 2) {
this.deleteProjectBack();
}
this.getDetail();
this.setState({
firstSync: false,
secondSync: false
});
cable.subscriptions.consumer.disconnect();
}
}
})
}
deleteProjectBack = () => {
const { history } = this.props;
const { projectsId, owner } = this.props.match.params;
axios.delete(`/${owner}/${projectsId}.json`).then(res => {
let hash = '/projects/mirror/new';
if (res && res.data) {
history.push({
pathname: hash,
mirror_status: 2
});
}
else {
window.location.hash = hash;
}
});
}
getDetail = () => {
const { projectsId , owner } = this.props.match.params;
const { projectsId, owner } = this.props.match.params;
this.getBanner();
const url = `/${owner}/${projectsId}/detail.json`;
axios.get(url).then((result) => {
@ -280,29 +318,29 @@ class Detail extends Component {
watchers_count: result.data.watchers_count,
praises_count: result.data.praises_count,
forked_count: result.data.forked_count,
defaultBranch:result.data.default_branch
defaultBranch: result.data.default_branch
})
}
}).catch((error) => { })
}
// 获取动态导航栏菜单
getBanner(){
const { projectsId , owner } = this.props.match.params;
getBanner() {
const { projectsId, owner } = this.props.match.params;
const url = `/${owner}/${projectsId}/menu_list.json`;
axios.get(url).then(result=>{
if(result){
axios.get(url).then(result => {
if (result) {
this.setState({
bannerList:result.data
bannerList: result.data
})
}
}).catch(error=>{})
}).catch(error => { })
}
// 关注和取消关注
focusFunc = (flag) => {
const { platform } = this.state;
if(!platform)return;
if (!platform) return;
const { project_id } = this.state;
axios({
@ -325,7 +363,7 @@ class Detail extends Component {
// 点赞和取消点赞
pariseFunc = (flag) => {
const { platform } = this.state;
if(!platform)return;
if (!platform) return;
const { project_id } = this.state;
axios({
method: flag ? 'delete' : 'post',
@ -358,7 +396,14 @@ class Detail extends Component {
forkFunc = () => {
const { platform } = this.state;
if(!platform)return;
const { current_user } = this.props
const { current_user , giteaVisible , showEABox } = this.props;
if(giteaVisible){
showEABox && showEABox();
return;
}
this.setState({
forkSpin: true
})
const { projectsId , owner } = this.props.match.params;
const url = `/${owner}/${projectsId}/forks.json`;
axios.post(url).then(result => {
@ -366,16 +411,21 @@ 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
})
})
}
// 同步镜像
synchronismMirror = () => {
const { platform } = this.state;
if(!platform)return;
const { projectsId , owner } = this.props.match.params;
if (!platform) return;
const { projectsId, owner } = this.props.match.params;
const url = `/${owner}/${projectsId}/sync_mirror.json`;
axios.post(url).then(result => {
if (result && result.data && result.data.status === 0) {
@ -389,116 +439,112 @@ 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 ,
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;
let pathname = checkPathname(projectsId,owner,url);
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,
changeOpenDevops: this.changeOpenDevops,
defaultBranch
}
return (
<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" }}>
<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}`} className="show-user-link">
{project.author.name}
</Link>
<Link to={`${project.author.type === "Organization" ? "/organize" : '/users'}/${project.author.login}`}>{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>
<Link to={`/projects/${owner}/${projectsId}`} className="projectN mt6">{projectDetail && projectDetail.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>
</div>
</div>
<div>
{
firstSync ? "":
<span className="df mt25">
firstSync ? "" :
<span className="df">
{
projectDetail && projectDetail.type && projectDetail.type === 2 ?
((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>
<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 }:""}>
<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>
:""
: ""
}
</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>
</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 }}>
<Link className="detail_tag_btn_count" style={{ color: `${praised ? "#2878FF" : "#666"}` }} to={{ pathname: `/projects/${owner}/${projectsId}/stargazers`, state }}>
{praises_count}
</Link>:
</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)
</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 ?
@ -507,15 +553,17 @@ class Detail extends Component {
</Link>
:
<span className="detail_tag_btn_count">{forked_count}</span>
:""
: ""
}
</span>
</Button>
</span>
}
</div>
</AlignTop>
{
firstSync ? "" :
<DetailBanner
history={this.props.history}
list={bannerList}
owner={owner}
projectsId={projectsId}
@ -550,6 +598,24 @@ class Detail extends Component {
() => (<DevAbout {...this.props} {...this.state} {...common} />)
}
></Route>
{/* wiki新增文件 */}
<Route path="/projects/:owner/:projectsId/wiki/add"
render={
() => (<WikiEdit {...this.props} {...this.state} {...common} />)
}
></Route>
{/* wiki编辑文件 */}
<Route path="/projects/:owner/:projectsId/wiki/edit/:wikiName"
render={
() => (<WikiEdit {...this.props} {...this.state} {...common} />)
}
></Route>
{/* wiki */}
<Route path="/projects/:owner/:projectsId/wiki"
render={
() => (<Wiki {...this.props} {...this.state} {...common} />)
}
></Route>
{/* 工作流 */}
<Route path="/projects/:owner/:projectsId/devops"
render={
@ -635,6 +701,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} />)
@ -686,22 +757,22 @@ class Detail extends Component {
{/* 代码库----详情页面 */}
<Route path="/projects/:owner/:projectsId/commits/branch/:branchName"
render={
(props) => (<CoderRootCommit {...this.props} {...props} {...this.state} {...common}/>)
(props) => (<CoderRootCommit {...this.props} {...props} {...this.state} {...common} />)
}
></Route>
<Route path="/projects/:owner/:projectsId/tree/:branchName"
render={
(props) => (<CoderDepot {...this.props} {...props} {...this.state} {...common}/>)
(props) => (<CoderDepot {...this.props} {...props} {...this.state} {...common} />)
}
></Route>
<Route path="/projects/:owner/:projectsId/:subIndex"
render={
(props) => (<CoderRootIndex {...this.props} {...props} {...this.state} {...common}/>)
(props) => (<CoderRootIndex {...this.props} {...props} {...this.state} {...common} />)
}
></Route>
<Route path="/projects/:owner/:projectsId"
render={
(props) => (<CoderDepot {...this.props} {...props} {...this.state} {...common}/>)
(props) => (<CoderDepot {...this.props} {...props} {...this.state} {...common} />)
}
></Route>
</Switch>

View File

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

View File

@ -1,7 +1,7 @@
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { Menu, Input , Spin, Pagination , Popover , Select } from 'antd';
import { getUrl } from 'educoder';
import { Menu, Input , Spin, Pagination , Popover , Select , Button } from 'antd';
import { getImageUrl } from 'educoder';
import '../css/index.scss'
import './list.css';
import './Index.scss';
@ -9,7 +9,8 @@ import ListItem from './IndexItem'
import axios from 'axios';
import img_new from '../Images/new.png';
import img_array from '../Images/array.png';
import banner from '../Images/banner_list.jpg';
import banner from '../Images/banner.png';
import AddProjectModal from '../Head/AddProjectModal';
const Search = Input.Search;
class Index extends Component {
@ -257,12 +258,38 @@ class Index extends Component {
const { projectsList , recommendList , languageList , languageId ,
isSpin, total, search, limit, page, typeList, categoryList } = this.state;
let userflag = (current_user && ( current_user.login && current_user.login === "pns5oi9af" )) ? true : false;
return (
<div>
<p className="t_project_banner">
<img src={banner} width="100%" alt=""/>
</p>
<div className="subjectBanner">
<img src={banner} alt=""/>
<div className="bannerBox">
<div class="subjectleft">
<span>头歌开源</span>
<span>
<span>
{userflag ?
<span className="words">让师姐开心的玩开源</span>:
<span className="words">让大学绽放开源之花</span>
}
<p className="font-13" style={{color:"#ccc"}}>Powered by Trustie</p>
</span>
</span>
</div>
<Link to={`/projects/deposit/new`}>
<i class="iconfont icon-xinjianxiangmu"></i>
<a>新建项目</a>
</Link>
<a>
<i class="iconfont icon-jiaruketang1"></i>
<AddProjectModal showNotification={this.props.showNotification}/>
</a>
<a href={`https://data.educoder.net/api/attachments/1955244?disposition=inline`} target="_blank">
<i class="iconfont icon-xinshouzhiyin"></i>
新手指引
</a>
</div>
</div>
{
recommendList && recommendList.length>0 &&
<div className="recommandProjects">
@ -271,7 +298,7 @@ class Index extends Component {
return(
<div onClick={()=>this.getoDetail(item.author && item.author.login,item.identifier)}>
<div className="mainInfo">
<img src={getUrl(`/images/${item.author && item.author.image_url}`)} alt=""/>
<img src={getImageUrl(`/${item.author && item.author.image_url}`)} alt=""/>
<p className="school">{item.name}</p>
<p className="name">{item.author && item.author.name}</p>
</div>

View File

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

View File

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

View File

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

View File

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

View File

@ -1,23 +1,30 @@
import React, { useEffect, useState } from 'react';
import { Skeleton } from 'antd';
import { Skeleton , Tooltip } from 'antd';
import { Link } from 'react-router-dom';
function DetailBanner({ list , owner , projectsId , isManager , url , pathname , state , urlFlag , projectDetail , platform ,open_devops }){
function DetailBanner({ history,list , owner , projectsId , isManager , url , pathname , state , urlFlag , projectDetail , platform ,open_devops }){
const [ menuName , setMenuName ] = useState(undefined);
useEffect(()=>{
if(list){
// banner
if(pathname && pathname==="source"){
let a = list.filter(item=>item.menu_name === "resources");
if(a && a.length === 0){
history.push(`/projects/${owner}/${projectsId}`);
}
}
setMenuName(list);
}
},[list]);
return(
<div className="f-wrap-between mt15">
{
menuName && projectDetail ?
menuName && menuName.length> 0 && projectDetail ?
<ul className="headerMenu-wrapper">
{
menuName.map((item,key)=>{
Array.isArray(menuName)&& menuName.map((item,key)=>{
return(
<React.Fragment>
<React.Fragment key={item.menu_name}>
{
item.menu_name === "home" &&
<li className={pathname==="about" ? "active" : ""}>
@ -39,11 +46,13 @@ function DetailBanner({ list , owner , projectsId , isManager , url , pathname ,
{
item.menu_name === "issues" &&
<li className={pathname==="issues" ? "active" : ""}>
<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>易修 (Issue)</span>
<span>易修(Issue)</span>
{projectDetail && projectDetail.issues_count ? <span className="num">{projectDetail.issues_count}</span> : ""}
</Link>
</Tooltip>
</li>
}
{
@ -56,6 +65,15 @@ function DetailBanner({ list , owner , projectsId , isManager , url , pathname ,
</Link>
</li>:""
}
{/* {
item.menu_name === "wiki" &&
<li className={pathname === "wiki" ? "active" : ""}>
<Link to={{ pathname: `/projects/${owner}/${projectsId}/wiki`, state }}>
<i className={pathname==="wiki" ? "iconfont icon-wiki_icon color-grey-3 mr5 font-14":"iconfont icon-wiki_icon color-grey-6 font-14 mr5"}></i>
<span>Wiki</span>
</Link>
</li>
} */}
{
item.menu_name === "devops" && platform ?
<li className={pathname==="devops" ? "active" : ""}>
@ -66,8 +84,8 @@ function DetailBanner({ list , owner , projectsId , isManager , url , pathname ,
</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 +93,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,18 +113,18 @@ function DetailBanner({ list , owner , projectsId , isManager , url , pathname ,
</Link>
</li>
}
</React.Fragment>
)
})
}
{
isManager && platform ?
item.menu_name === "setting" &&
<li className={pathname === "setting" ? "active" : ""}>
<Link to={`/projects/${owner}/${projectsId}/setting`}>
<i className={url && url.indexOf("/setting") > 0 ? "iconfont icon-cangku color-grey-3 mr5 font-14":"iconfont icon-cangku color-grey-6 font-14 mr5"}></i>
<span>仓库设置</span>
</Link>
</li>:""
</li>
}
</React.Fragment>
)
})
}
</ul>
:

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

@ -21,3 +21,8 @@
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_developer} = 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,9 +105,11 @@ class MergeItem extends Component {
</span>
<span className="color-grey-8">{item.pr_time}</span>
<span className="ml15">
{
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/${item.pull_request_head}`}
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
@ -107,6 +118,9 @@ class MergeItem extends Component {
:{item.pull_request_head}
</Link>
</Tag>
}
{
item.pull_request_base &&
<span className="mr8 ver-middle">
<i
className={
@ -114,17 +128,22 @@ class MergeItem extends Component {
}
></i>
</span>
}
{
item.pull_request_base &&
<Tag className="pr-branch-tag">
<Link
to={`/projects/${owner}/${projectsId}/tree/${item.pull_request_base}`}
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_developer && item.pull_request_status === 0 ? (
<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,8 +101,8 @@ 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"/>
<label className="hide-1" style={{maxWidth:"75px",'vertical-align':'middle'}}>{text}</label>
<img src={getImageUrl(`/${item.image_url}`)} alt="" width="28px" height="28px" className="mr3 radius"/>
<label className="hide-1" style={{maxWidth:"75px",verticalAlign:'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}
className="mb15"
icon={<Icon type="caret-down" />}
disabled={!pull_request || (pull_request && !pull_request.mergeable) }
>
{this.state.mergename}
</Dropdown.Button>
</p>
{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,
@ -31,12 +32,16 @@ class NewMerge extends Component {
}
componentDidMount = () => {
const { projectsId } = this.props.match.params;
const { projectsId , owner } = this.props.match.params;
this.getmergelist(projectsId);
// 监听回退事件
if (window.history && window.history.pushState) {
window.addEventListener('popstate', this.handleBack, false);
}
const { giteaVisible , showEABox } = this.props;
if(giteaVisible){
showEABox && showEABox(`/projects/${owner}/${projectsId}/pulls`);
}
};
componentDidUpdate=(preProps)=>{
@ -104,6 +109,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,6 +130,8 @@ class NewMerge extends Component {
}
set_default_pull = (branches) => {
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){
@ -126,6 +145,7 @@ class NewMerge extends Component {
}
}
}
}
set_default_merge = (merge_branches) => {
if(merge_branches && merge_branches.length){
@ -139,7 +159,7 @@ class NewMerge extends Component {
merge:"master"
})
}
this.ischeckmerge();
// this.ischeckmerge();
}
}
@ -163,10 +183,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 +200,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 +215,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

@ -4,7 +4,8 @@ import "./merge.css";
import "../Order/order.css";
import "../Order/index.scss";
import NoneData from "./no_data";
import OrderItem from "./MergeItem";
import MergeItem 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,18 +155,14 @@ 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>
{array &&
array.length > 0 &&
array.map((item, key) => {
{array && array.length > 0 && array.map((item, key) => {
return (
<Menu.Item
key={item.id}
onClick={(e) => this.getOption(e, id, item.name)}
>
((!item.permission) || (item.permission && item.permission !== "Reporter")) &&
<Menu.Item key={item.id} onClick={(e) => this.getOption(e, id, item.name)}>
{item.name}
</Menu.Item>
);
@ -206,24 +203,17 @@ 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`);
}
}
render() {
const { projectsId , owner } = this.props.match.params;
const {
@ -253,24 +243,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 +254,12 @@ class merge extends Component {
style={{ width: 300 }}
/>
</div>
<a className="topWrapper_btn ml10" onClick={() => this.islogin()}>
{
data && data.user_admin_or_developer &&
<a className="topWrapper_btn ml10" onClick={() => this.checkOperation()}>
+&nbsp;新建合并请求
</a>
}
</div>
<div className="f-wrap-between screenWrap">
<div className="df">
@ -300,6 +275,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 +283,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 +291,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>
@ -409,7 +387,7 @@ class merge extends Component {
<Spin spinning={isSpin}>
{data && data.search_count && data.search_count > 0 ? (
<div>
<OrderItem
<MergeItem
issues={issues}
search_count={search_count}
page={select_params.page}
@ -418,10 +396,23 @@ class merge extends Component {
project_author_name={data.project_author_name}
{...this.props}
{...this.state}
></OrderItem>
{Paginations}
user_admin_or_developer={data && data.user_admin_or_developer}
></MergeItem>
</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 });
@ -262,7 +264,7 @@ class MergeForm extends Component {
},
],
initialValue: title,
})(<Input placeholder="标题" />)}
})(<Input placeholder="标题" maxLength={50} />)}
</Form.Item>
<MDEditor
placeholder={"请输入合并请求的描述..."}
@ -344,7 +346,8 @@ class MergeForm extends Component {
</Select>
)}
</Form.Item>
<Form.Item name="checkbox-group" label="其他">
{/* <Form.Item label="">
{getFieldDecorator("checkbox-group")(
<Checkbox.Group>
<div>
<Checkbox value="A">必须审查代码</Checkbox>
@ -356,7 +359,8 @@ class MergeForm extends Component {
<Checkbox value="C">合并后关闭提到的任务</Checkbox>
</div>
</Checkbox.Group>
</Form.Item>
)}
</Form.Item> */}
</div>
</div>
</div>

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, Modal } from 'antd';
import { Base64 } from 'js-base64';
import '../css/index.scss';
@ -43,10 +43,15 @@ class Index extends Component {
project_language_name: undefined,
project_category_name: undefined,
license_name: undefined,
ignore_name: undefined
ignore_name: undefined,
descNum:0
}
}
componentDidMount = () => {
const { giteaVisible , showEABox } = this.props;
if(giteaVisible){
showEABox && showEABox('/projects');
}
// 获取拥有者列表
this.getOwner();
// 获取项目类别
@ -57,6 +62,8 @@ class Index extends Component {
this.getGitignore();
// 获取开源许可证
this.getLicenses();
//判断是否为删除新建项目失败后返回,并执行对应逻辑
this.isDeleteProjectBack();
}
componentDidUpdate=(prevPros)=>{
if(prevPros && this.props && !this.props.checkIfLogin()){
@ -67,24 +74,28 @@ class Index extends Component {
getOwner=()=>{
const { OIdentifier } = this.props.match.params;
const { user_id } = this.props && this.props.current_user;
const url = `/owners.json`;
axios.get(url).then(result=>{
if(result && result.data){
let owner = result.data.owners;
this.setState({
OwnerList: owner,
})
if(OIdentifier){
owner = owner.filter(item=>item.name === OIdentifier);
owner = owner.filter(item=>item.login === OIdentifier);
}else if(user_id){
owner = owner.filter(item=>item.id === user_id);
}
this.props.form.setFieldsValue({
user_id:OIdentifier
user_id:owner && owner[0].name
})
owner && this.setState({
owners_id:owner[0].id,
owners_name:owner[0].name
})
}
this.setOptionsList(owner, 'owners');
this.setState({
OwnerList: owner,
})
}
}).catch(error=>{})
}
@ -137,6 +148,31 @@ class Index extends Component {
}).catch((error) => { })
}
isDeleteProjectBack = () => {
let mirror_status = this.props.history.location.mirror_status;
if (mirror_status === 2 && sessionStorage.newProjectValue) {
Modal.warning({
title: '警告',
content: '镜像项目创建失败!请按操作规范重新创建项目!',
});
let newProjectValue = JSON.parse(sessionStorage.newProjectValue);
if (newProjectValue) {
this.setState({
project_language_id: newProjectValue.project_language_id,
project_category_id: newProjectValue.project_category_id,
license_id: newProjectValue.license_id,
ignore_id: newProjectValue.ignore_id
});
delete newProjectValue.project_language_id;
delete newProjectValue.project_category_id;
delete newProjectValue.license_id;
delete newProjectValue.ignore_id;
this.props.form.setFieldsValue(newProjectValue);
}
}
}
// 设置option
setOptionsList = (data, _head, name) => {
if (data && data.length > 0) {
@ -165,6 +201,8 @@ class Index extends Component {
const { project_language_id, project_category_id, license_id, ignore_id , owners_id , owners_name } = this.state;
const decoderPass = Base64.encode(values.password);
const url = (projectsType && projectsType === "mirror") ? "/projects/migrate.json" : "/projects.json";
// 新建项目的时候,暂存数据,如果失败,返回的时候可以重新赋值
sessionStorage.newProjectValue=JSON.stringify({...values,project_language_id,project_category_id,license_id,ignore_id});
axios.post(url, {
...values,
auth_password:decoderPass,
@ -174,14 +212,12 @@ class Index extends Component {
ignore_id,
user_id:owners_id
}).then((result) => {
if (result) {
if (result.data.id) {
if (result && result.data.id) {
this.setState({
isSpin: false
})
this.props.showNotification(`${projectsType && projectsType === "mirror" ? "镜像" : "托管"}项目创建成功!`);
this.props.history.push(`/projects/${owners_name}/${result.data.identifier}`);
}
projectsType && projectsType !== "mirror" && this.props.showNotification(`托管项目创建成功!`);
this.props.history.push(`/projects/${result.data.login}/${result.data.identifier}`);
}
}).catch((error) => {
this.setState({
@ -254,11 +290,17 @@ class Index extends Component {
}
}
changeDesc=(e)=>{
let value = e.target.value;
this.setState({
descNum:value ? value.length :0
})
}
render() {
const { getFieldDecorator } = this.props.form;
// 项目类型deposit-托管项目mirror-镜像项目
const { projectsType } = this.props.match.params;
const {
CategoryList,
LanguageList,
@ -273,7 +315,9 @@ class Index extends Component {
license_list,
ignore_list,
mirrorCheck
mirrorCheck,
descNum
} = this.state;
return (
<div className="main back-white" style={{padding:"0px",border:"none"}}>
@ -323,7 +367,9 @@ 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>
<span className="ml20 font-12 color-red">如果源项目为公有仓库禁止填写用户名密码如果源项目为私有仓库则必须填写正确的用户名和密码!</span></p>
{
mirrorCheck &&
<div className="df mb20" style={{alignItems:'center'}}>
@ -361,10 +407,11 @@ class Index extends Component {
required: true, message: '请填写项目名称'
}],
})(
<Input placeholder="例如:团队协作方法与研究" />
<Input placeholder="例如:团队协作方法与研究" maxLength={50}/>
)}
</Form.Item>
<div className="pr">
<span className="toprightNum">{descNum}/200</span>
<Form.Item
label="项目简介"
>
@ -373,9 +420,10 @@ class Index extends Component {
required: true, message: '请填写项目简介'
}],
})(
<Input.TextArea placeholder="项目的介绍" autoSize={{ minRows: 2, maxRows: 6 }} />
<Input.TextArea maxLength={200} placeholder="项目的介绍" autoSize={{ minRows: 2, maxRows: 6 }} onChange={this.changeDesc}/>
)}
</Form.Item>
</div>
<Form.Item
label="仓库名称"
>
@ -384,7 +432,7 @@ class Index extends Component {
required: true, message: '请填写仓库名称'
}],
})(
<Input placeholder="仓库名称请使用与项目相关的英文关键字" />
<Input placeholder="仓库名称请使用与项目相关的英文关键字" maxLength={100} />
)}
</Form.Item>
<Form.Item

View File

@ -14,6 +14,14 @@ class Index extends Component {
};
}
componentDidMount=()=>{
const { projectsId , owner } = this.props.match.params;
const { giteaVisible , showEABox } = this.props;
if(giteaVisible){
showEABox && showEABox(`/projects/${owner}/${projectsId}`);
}
}
// 命名文件
changeFileName = (e) => {
this.setState({
@ -73,6 +81,7 @@ class Index extends Component {
content={undefined}
readOnly={false}
editorType="new"
descName={filename && `Add ${filename}`}
></Meditor>
</div>
</div>

View File

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

View File

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

View File

@ -15,6 +15,14 @@ class UploadFile extends Component {
};
}
componentDidMount=()=>{
const { projectsId , owner } = this.props.match.params;
const { giteaVisible , showEABox } = this.props;
if(giteaVisible){
showEABox && showEABox(`/projects/${owner}/${projectsId}`);
}
}
// 获取上传后的文件id数组
UploadFunc = (fileList) => {
this.setState({
@ -60,6 +68,7 @@ class UploadFile extends Component {
filepath={file_path}
content={editorValue}
editor_type={"upload"}
descName={`ADD file via upload`}
></UserSubmitComponent>
</div>
</div>

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

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

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

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -180,8 +180,8 @@ class UpdateMilepost extends Component {
<Row type="flex" justify="space-between">
<Col>
<Group size="small" onChange={e => onTypeChange(e.target.value)} value={type}>
<Button value="month">月份</Button>
<Button value="year"></Button>
<Button value="month">日期</Button>
<Button value="year"></Button>
</Group>
</Col>
<Col>

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