Compare commits

..

37 Commits

Author SHA1 Message Date
caishi cf1905bc4b personal link 2021-08-09 10:58:27 +08:00
caishi fc5c957bf1 头部头像跳转到个人中心链接错误 2021-08-05 17:55:27 +08:00
caishi 8401a55e49 .json 2021-05-12 09:32:33 +08:00
caishi ee6961d02e 接口加上后缀.json 2021-05-11 18:12:12 +08:00
caishi 57ab5d1199 取消置顶接口名错误 2021-05-11 17:48:58 +08:00
caishi 6d9af5c906 未登录点击弹出登录框 2021-05-11 15:26:27 +08:00
caishi e38508ea59 未登录点击写点什么没有反应 2021-05-11 14:27:14 +08:00
caishi 68d1b69a09 a->link 2021-05-11 10:44:00 +08:00
caishi aef1451455 页面跳转时未滚动到顶部 2021-05-10 18:03:23 +08:00
caishi 191e79e05a d 2021-05-10 17:52:27 +08:00
caishi ea8b5ece1a 板块管理-背景色 2021-05-10 16:31:10 +08:00
caishi 41e9326174 router 2021-05-10 09:41:39 +08:00
caishi 8dad3b231c 底部信息 2021-05-08 17:20:52 +08:00
caishi 0703fb1bb6 antd-img-crop 2021-05-08 16:58:14 +08:00
caishi 7a96f7021a build 2020-10-28 15:37:00 +08:00
caishi 54fc16ddad Merge branch 'dev_wenba' of https://git.trustie.net/jasder/forgeplus-react into dev_wenba 2020-10-19 11:46:34 +08:00
caishi c04b2ed5f6 ismanager 2020-10-19 11:46:30 +08:00
sylor_huang@126.com 107b6ed95e Change NewHeader active 2020-10-16 17:12:28 +08:00
sylor_huang@126.com 67ccc2ebe5 Change List Count 2020-10-16 14:46:31 +08:00
sylor_huang@126.com c00933b0ca change2 2020-10-16 11:36:01 +08:00
sylor_huang@126.com 4f67719dac change children_forum_id 2020-10-15 15:48:01 +08:00
sylor_huang@126.com 5be45528cf Change page bugs 2020-10-15 14:38:36 +08:00
sylor_huang@126.com c3b2f8f6ad Change forums url /project to 404 2020-10-15 14:18:31 +08:00
sylor_huang@126.com 6950c1a820 Change Forum Url 2020-10-15 11:27:25 +08:00
sylor_huang@126.com b5881d5162 Change ForumUser Page1 2020-10-15 10:11:51 +08:00
sylor_huang@126.com 4de25d36a8 Merge branch 'dev_wenba' of http://git.trustie.net/jasder/forgeplus-react into dev_wenba 2020-10-15 10:01:32 +08:00
sylor_huang@126.com 5992e83c60 Change ForumUser Page 2020-10-15 10:01:21 +08:00
caishi c740dcc6f8 Merge branch 'dev_wenba' of https://git.trustie.net/jasder/forgeplus-react into dev_wenba 2020-10-15 09:50:05 +08:00
caishi 9922b109f8 spining 2020-10-15 09:50:02 +08:00
sylor_huang@126.com 7b097149eb Change ForumUser 2020-10-14 19:19:16 +08:00
caishi f7fa5291c6 exchange-板块管理 2020-10-14 18:15:36 +08:00
caishi 8ebac42d42 Merge branch 'dev_wenba' of https://git.trustie.net/jasder/forgeplus-react into dev_wenba 2020-10-14 17:07:52 +08:00
caishi 10db5271d7 编辑 2020-10-14 17:07:49 +08:00
sylor_huang@126.com 4596eac895 Change ForumSection Watch APi 2020-10-14 16:43:55 +08:00
caishi d1232be557 upload 2020-10-14 15:55:20 +08:00
sylor_huang@126.com 79c943d31b Change Memo Url 2020-10-13 14:39:22 +08:00
caishi 6d8834403a 问吧 2020-10-12 17:48:42 +08:00
496 changed files with 24782 additions and 32997 deletions

View File

@ -1,16 +0,0 @@
{
"presets": [
"es2015",
"react",
"stage-2"
],
"plugins": [[
"transform-runtime",
{
"helpers": false,
"polyfill": false,
"regenerator": true,
"moduleName": "babel-runtime"
}
]]
}

View File

@ -1,20 +1,294 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="9830c5da-176a-4c72-a301-9f6ce98c82fe" name="Default Changelist" comment="色值修改">
<change beforePath="$PROJECT_DIR$/src/forge/Branch/branch.css" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Branch/branch.css" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/forge/Component/Component.css" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Component/Component.css" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/forge/Main/Index.css" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Main/Index.css" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/forge/Main/list.css" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Main/list.css" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/forge/Main/sub/sub.css" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Main/sub/sub.css" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/forge/Wiki/Index.css" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Wiki/Index.css" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/forge/Wiki/components/Login/index.css" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Wiki/components/Login/index.css" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/forge/Wiki/components/ModalFun/index.css" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/Wiki/components/ModalFun/index.css" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/forge/css/index.css" beforeDir="false" afterPath="$PROJECT_DIR$/src/forge/css/index.css" afterDir="false" />
<list default="true" id="9830c5da-176a-4c72-a301-9f6ce98c82fe" name="Default Changelist" comment="">
<change beforePath="$PROJECT_DIR$/build/LICENSE" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/README.md" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/asset-manifest.json" beforeDir="false" afterPath="$PROJECT_DIR$/build/asset-manifest.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/build/index.html" beforeDir="false" afterPath="$PROJECT_DIR$/build/index.html" afterDir="false" />
<change beforePath="$PROJECT_DIR$/build/service-worker.js" beforeDir="false" afterPath="$PROJECT_DIR$/build/service-worker.js" afterDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/0.b4e2ef10.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/1.68949f00.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/10.1be71b42.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/100.41f2a409.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/101.8c9ebcb9.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/102.ae5e7930.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/103.71e1260c.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/104.6010b0d8.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/105.d97473e3.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/106.a18d9295.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/107.e4fa8da8.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/108.cb5fa18c.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/109.728021f4.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/11.1e6e3f2f.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/110.4d60f060.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/111.00847641.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/112.dff32e36.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/113.9321d650.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/114.ab44c57c.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/115.16bc2b0d.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/116.acb34db4.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/117.c12032ae.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/118.06ac0c97.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/119.09fe7ab0.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/12.c388554b.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/120.712692c0.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/121.5542a234.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/122.5434414a.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/123.56b5aa50.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/124.4fa3c025.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/125.e90a0e54.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/126.6ac902e8.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/127.382109ca.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/128.f5cab6ce.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/129.a97defb5.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/13.5f9dd754.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/130.dc6cfef3.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/131.e36c51f6.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/132.e86e6598.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/133.cd5b0e41.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/134.a45157f4.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/135.219cca2d.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/136.792455ab.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/137.d98f1f26.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/138.537d528f.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/139.f968f8da.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/14.ae36f994.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/140.d2937c5d.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/141.41414563.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/142.085bcaac.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/143.7c7bdefd.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/144.2734eeac.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/145.0567a7a7.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/146.6f118df0.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/147.8ad6802f.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/148.1509b466.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/149.5a32ad42.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/15.63726bf5.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/150.67b85412.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/151.80c1ff31.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/152.a8ce322f.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/153.eb7d58a7.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/154.4378c4dc.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/155.fd685811.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/156.2b59a7a3.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/157.2d1b2b40.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/158.809809fb.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/159.6b94727b.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/16.cd292edc.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/160.5c01b4fe.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/161.2e3c0cf9.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/162.87f4e888.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/163.0a540ffa.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/164.b22433aa.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/165.2670f91b.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/166.ba688344.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/167.c636b766.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/168.96ee0643.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/169.59c6e595.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/17.9b37107f.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/170.a0e40916.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/171.c5e14617.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/172.aa2c6247.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/173.f234f2ec.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/174.23eb263b.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/175.978237cd.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/176.6a70c7ca.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/177.babfb8a0.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/178.ca78bfb7.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/179.d209043e.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/18.59166b96.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/180.34e55d7e.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/181.dd0e9427.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/182.c133a270.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/183.f43e4007.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/184.3abba332.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/185.c61d56af.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/186.5ca9f085.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/187.99889df3.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/188.9a0c959d.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/189.98f6f431.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/19.e9122038.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/190.a972b289.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/191.91c8d4dd.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/192.09b656bb.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/193.2db797e4.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/194.570ec7dc.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/195.8bafb8cc.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/196.ebf790e4.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/197.e98bbe5e.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/198.59eec2cd.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/199.f7a6cf5d.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/2.2225a575.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/20.ea1b83ca.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/200.64c98b62.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/201.4dcc10b4.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/202.4e8c6091.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/203.d93d18f4.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/204.0919cf31.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/205.1d636166.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/206.5c119f09.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/207.147ba6db.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/208.25081b68.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/209.784c19f9.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/21.66ea1441.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/210.ebc5386b.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/211.ca304f55.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/212.b5fc6346.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/213.9e5a8f54.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/214.471a7c4b.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/215.cf8d392b.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/216.8ce08d4e.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/217.03e5cde4.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/218.c3b4a63d.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/219.df96a008.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/22.69d33500.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/220.83a4b3b7.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/221.0e7c7b15.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/222.ae23590f.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/223.6e1a4ddc.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/224.66c696e0.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/225.86f6de74.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/226.334eacdf.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/227.fded1c54.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/228.081121a3.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/229.fbecdb8f.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/23.f3d3a190.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/230.54214923.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/231.78c978ac.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/232.65715f0f.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/233.892f3048.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/234.9aa182e7.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/235.d81ddb83.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/236.69cf4cf6.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/237.71ab4f1f.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/238.8572225b.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/239.fcae5847.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/24.2f9ea72c.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/240.e2238e45.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/241.83f72690.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/242.d3cb57dd.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/243.99af0ef6.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/244.38603408.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/245.2d35733a.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/246.e8bec9f4.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/247.c9b742e0.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/248.9f3a7f5e.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/249.a235bfb8.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/25.3253bb14.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/250.89a0ec2e.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/251.f94f4795.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/252.614475d7.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/253.16b76954.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/254.64d033a9.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/255.d2823d40.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/256.1eca44ce.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/257.7dd534ae.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/258.03522780.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/259.073dfc4a.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/26.9902b511.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/260.35044602.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/261.617c2c29.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/262.031490eb.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/263.0713aea5.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/264.72a7fa9b.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/265.8136f3c3.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/266.5c84b920.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/267.56ebb248.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/268.21a4574f.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/269.28226efe.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/27.4304d4c5.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/270.c5d4fa4b.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/271.78c89e20.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/272.c46f7455.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/273.aa76ad67.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/274.42a38d5c.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/275.eab35d6d.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/276.ba152567.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/277.8ad1fd18.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/278.82ce41b4.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/279.92f86639.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/28.3836fc1f.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/280.d787fd18.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/281.8bdfd9de.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/282.2f9ff948.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/283.40367bdd.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/29.b7147766.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/3.31787e19.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/30.a574d03f.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/31.df24e028.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/32.c4d7bbea.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/33.a4469f83.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/34.a525a713.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/35.991c6b3a.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/36.ca30f366.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/37.b67239f9.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/38.d6220035.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/39.8bbe7e4b.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/4.3c8412d9.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/42.b16613be.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/46.41f6ac73.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/47.b0fb496a.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/48.0f00b572.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/49.8b9d29ee.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/5.2b2c8a26.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/50.b42a5752.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/51.37761187.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/52.550fc073.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/53.86605c6d.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/54.e91a419f.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/55.c6646d9f.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/56.b701b846.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/57.7796d2c0.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/58.865fc1eb.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/59.b58d70cd.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/6.625bd454.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/60.f785cede.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/61.f63d91e7.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/62.b83b97dc.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/63.5d9d4d43.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/64.d1514b70.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/65.67458d6c.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/66.ab5a4b68.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/67.24cbd43e.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/68.26be36d2.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/69.d34e03a8.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/7.45c119ae.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/70.71f3f5e9.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/71.3e0e22a5.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/72.58d589d7.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/73.38beeb0e.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/74.f0ac65fe.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/75.9f0bfe3e.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/76.7bf08411.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/77.cc859ed7.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/78.1e7b969f.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/79.d68cadb4.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/8.ce3f8347.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/80.3633279d.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/81.13af2c38.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/82.dfb68271.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/83.a2cc3c51.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/84.284aff4a.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/85.41f3159a.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/86.4e688009.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/87.90d54fab.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/88.03076a1d.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/89.0d2f2ee8.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/9.562a7877.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/90.3b84e0a5.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/91.ad92675e.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/92.6abb40b8.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/93.e5666aea.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/94.149f3765.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/95.36a069b7.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/96.c09dc12e.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/97.8ff3af84.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/98.cd3f899c.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/99.07307f5a.chunk.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/build/static/js/main.dda56577.js" beforeDir="false" />
</list>
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
@ -251,7 +525,6 @@
<option name="width" value="1440" />
<option name="height" value="900" />
</component>
<component name="ProjectId" id="1xHteQnKsYu4IEUm8tEsYIEEC5E" />
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
<component name="ProjectView">
<navigator proportions="" version="1">
@ -277,30 +550,18 @@
<pane id="Scope" />
</panes>
</component>
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="last_opened_file_path" value="$PROJECT_DIR$/src/forge/Main" />
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
<property name="node.js.detected.package.eslint" value="true" />
<property name="node.js.path.for.package.eslint" value="project" />
<property name="node.js.path.for.package.tslint" value="project" />
<property name="node.js.selected.package.eslint" value="E:\file\forgeplus-react\node_modules\eslint" />
<property name="node.js.selected.package.tslint" value="(autodetect)" />
<property name="node.js.selected.package.eslint" value="$PROJECT_DIR$/node_modules/eslint" />
<property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" />
<property name="nodejs_npm_path_reset_for_default_project" value="true" />
<property name="nodejs_package_manager_path" value="npm" />
<property name="ts.external.directory.path" value="$APPLICATION_HOME_DIR$/plugins/JavaScriptLanguage/jsLanguageServicesImpl/external" />
<property name="vue.rearranger.settings.migration" value="true" />
</component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="E:\file\forgeplus-react\src\forge\Main" />
</key>
<key name="MoveFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$" />
<recent name="$PROJECT_DIR$/src/modules/user/usersInfo" />
@ -315,7 +576,18 @@
</list>
</option>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="RunDashboard">
<option name="ruleStates">
<list>
<RuleState>
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
</RuleState>
<RuleState>
<option name="name" value="StatusDashboardGroupingRule" />
</RuleState>
</list>
</option>
</component>
<component name="SpringUtil" SPRING_PRE_LOADER_OPTION="true" />
<component name="SvnConfiguration">
<configuration />
@ -364,8 +636,6 @@
<workItem from="1587870344748" duration="138000" />
<workItem from="1587882941476" duration="4365000" />
<workItem from="1587891008299" duration="390000" />
<workItem from="1630023942152" duration="459000" />
<workItem from="1630024424307" duration="17223000" />
</task>
<servers />
</component>
@ -398,7 +668,7 @@
</layout>
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
<option name="version" value="1" />
</component>
<component name="Vcs.Log.History.Properties">
<option name="COLUMN_ORDER">
@ -415,7 +685,22 @@
<map>
<entry key="MAIN">
<value>
<State />
<State>
<option name="RECENTLY_FILTERED_USER_GROUPS">
<collection />
</option>
<option name="RECENTLY_FILTERED_BRANCH_GROUPS">
<collection />
</option>
<option name="COLUMN_ORDER">
<list>
<option value="0" />
<option value="1" />
<option value="2" />
<option value="3" />
</list>
</option>
</State>
</value>
</entry>
</map>
@ -434,19 +719,6 @@
</entry>
</map>
</option>
<option name="oldMeFiltersMigrated" value="true" />
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
<breakpoints>
<line-breakpoint enabled="true" type="javascript">
<url>file://$PROJECT_DIR$/src/forge/Main/CoderDepot.jsx</url>
<line>409</line>
<properties lambdaOrdinal="-1" />
<option name="timeStamp" value="1" />
</line-breakpoint>
</breakpoints>
</breakpoint-manager>
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/src/modules/user/usersInfo/usersInfo.css">

View File

@ -1,16 +0,0 @@
<h3>前端react环境安装</h3>
<p>1、 安装node v6.9.x此安装包含了node和npm。</p>
<p>2、 安装cnpm命令行 npm install -g cnpm --registry=https://registry.npm.taobao.org</p>
<p>3、 安装依赖的js库public/react目录下<即项目package.json所在目录>,开启命令行): cnpm install</p>
<p>4、 如果你的ruby服务使用的是3000端口则需要在package.json中修改"port"参数的值</p>
<p>5、 启动服务(命令行-目录同3 npm start</p>
<p>6、 build初始化 npm run build</p>
<h3>分支信息:</h3>
<p>相关代码提交到对应分支能上线的代码先提交到develop分支上测试版测试通过后合并提交到master分支上线正式版</p>
<p>master:开发环境(正式环境)</p>
<p>develop:测试环境</p>
<p>dev_local:本地版本</p>
<p>dev_chain:含有区块链相关内容的分支</p>
<p>PS:新增加的需求功能先新建新分支开发在测试版测试没问题后再分别合并到develop和master分支</p>

View File

@ -17,7 +17,16 @@ const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const paths = require("./paths");
const getClientEnvironment = require("./env");
// Some apps do not use client-side routing with pushState.
// For these, "homepage" can be set to "." to enable relative asset paths.
let publicPath = "/react/build/";
// let nodeEnv = process.env.NODE_ENV
// if (nodeEnv === 'testBuild') {
// publicPath = 'https://testforgeplus.trustie.net/react/build/';
// }
// if (nodeEnv === 'production') {
// publicPath = 'https://forgeplus.trustie.net/react/build/';
// }
const publicUrl = publicPath.slice(0, -1);
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== "false";
const env = getClientEnvironment(publicPath);
@ -132,6 +141,7 @@ module.exports = {
name: "static/media/[name].[hash:8].[ext]",
},
},
// Process JS with Babel.
{
test: /\.(js|jsx|mjs)$/,
include: paths.appSrc,
@ -151,8 +161,10 @@ module.exports = {
],
},
},
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,

778
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +1,21 @@
{
"name": "forge",
"version": "3.0.0",
"name": "educoder",
"version": "0.1.0",
"private": true,
"dependencies": {
"@monaco-editor/react": "^2.3.0",
"@novnc/novnc": "^1.1.0",
"actioncable": "^5.2.4-3",
"antd": "^3.26.15",
"antd-img-crop": "^3.14.1",
"array-flatten": "^2.1.2",
"autoprefixer": "7.1.6",
"axios": "^0.18.1",
"babel-core": "6.26.0",
"babel-eslint": "7.2.3",
"babel-jest": "20.0.3",
"babel-loader": "7.1.2",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"babel-polyfill": "^6.26.0",
"babel-preset-react-app": "^3.1.1",
"babel-runtime": "6.26.0",
"bizcharts": "^3.5.8",
@ -27,11 +28,9 @@
"codemirror": "^5.53.0",
"connected-react-router": "4.4.1",
"css-loader": "^3.5.2",
"dompurify": "^2.0.15",
"dotenv": "4.0.0",
"dotenv-expand": "4.2.0",
"echarts": "^4.9.0",
"echarts-wordcloud": "^2.0.0",
"echarts": "^4.7.0",
"editor.md": "^1.5.0",
"eslint": "4.10.0",
"eslint-config-react-app": "^2.1.0",
@ -48,7 +47,6 @@
"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",
@ -65,7 +63,7 @@
"postcss-loader": "2.0.8",
"promise": "8.0.1",
"prop-types": "^15.6.1",
"qrcode.react": "^1.0.1",
"qrcode.react": "^1.0.0",
"qs": "^6.9.3",
"quill": "^1.3.7",
"quill-delta-to-html": "^0.11.0",
@ -95,7 +93,6 @@
"react-resizable": "^1.10.1",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"react-slick": "^0.28.1",
"react-split-pane": "^0.1.91",
"react-url-query": "^1.5.0",
"react-zmage": "^0.8.5-beta.31",
@ -106,7 +103,6 @@
"scroll-into-view": "^1.14.2",
"showdown": "^1.9.1",
"showdown-katex": "^0.8.0",
"slick-carousel": "^1.8.1",
"store": "^2.0.12",
"style-loader": "0.19.0",
"styled-components": "^4.4.1",
@ -116,9 +112,7 @@
"webpack-dev-server": "^3.10.3",
"webpack-manifest-plugin": "^2.2.0",
"whatwg-fetch": "2.0.3",
"wrap-md-editor": "^0.2.20",
"xterm": "4.8.1",
"xterm-addon-fit": "0.4.0"
"wrap-md-editor": "^0.2.20"
},
"scripts": {
"start": "node --max_old_space_size=15360 scripts/start.js",
@ -189,14 +183,7 @@
"port": "3007",
"devDependencies": {
"@babel/runtime": "7.0.0-beta.51",
"babel-cli": "^6.26.0",
"babel-core": "^6.26.0",
"babel-plugin-import": "^1.13.0",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-polyfill": "^6.26.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-2": "^6.24.1",
"compression-webpack-plugin": "^1.1.12",
"concat": "^1.0.3",
"happypack": "^5.0.1",
@ -204,7 +191,6 @@
"node-sass": "^4.12.0",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"purgecss": "^2.1.2",
"react-json-view": "^1.21.3",
"reqwest": "^2.0.5",
"resize-observer-polyfill": "^1.5.1",
"terser-webpack-plugin": "^2.3.5",

File diff suppressed because one or more lines are too long

View File

@ -38,6 +38,78 @@
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

@ -114,6 +114,14 @@ a:visited {
color: #898989;
}
a:hover {
color: #FF7500;
}
a:hover.fa {
color: #FF7500;
}
input,
textarea,
select {

View File

@ -97,6 +97,10 @@ a:visited {
color: #05101a;
}
a:hover {
color: #459be5;
}
ol,
ul,
li {

View File

@ -1363,9 +1363,9 @@ a:visited {
color: #05101a;
}
/*a:hover {*/
/* color: #2A61FF!important;*/
/*}*/
a:hover {
color: #459be5;
}
ol,
ul,
@ -1473,7 +1473,7 @@ a.edu-txt-w80,
/*隐藏*/
.none {
display: none!important;
display: none
}
.block {
@ -1609,7 +1609,7 @@ a.decoration {
}
.mt6 {
margin-top: 3px;
margin-top: 6px;
}
.mt7 {
@ -1753,7 +1753,7 @@ a.decoration {
}
.mb15 {
margin-bottom: 15px!important;
margin-bottom: 15px;
}
.mb16 {
@ -2325,9 +2325,6 @@ input::-ms-clear {
background-color: #F5F5F5;
}
.ant-modal-close{
top:8px!important;
}
.newContainer {
min-height: 100%;
@ -2347,6 +2344,10 @@ input::-ms-clear {
.newMain {
margin: 0 auto;
min-width: 1200px;
height: 100%;
min-height: 100%;
padding-top: 70px;
background-color: #fafafa;
}
@ -2424,19 +2425,13 @@ input::-ms-clear {
.color-grey-c {
color: #ccc !important;
}
a.hoverLine:hover{
text-decoration: underline;
}
.color-grey-cd {
color: #cdcdcd !important;
}
.color-grey-d {
color: #ddd;
}
.color-grey-9 {
color: #333333 !important;
color: #999999 !important;
}
.color-grey-98 {
@ -2471,24 +2466,33 @@ a.hoverLine:hover{
a.color-grey-name:hover,
a.color-dark:hover,
a.color-grey-6:hover,
a.color-grey-3:hover,a.color-ooo span:hover{
color: #466AFF !important;
a.color-grey-3:hover {
color: #4cacff !important;
}
a.color-grey-9:hover,
a.color-grey-8:hover,
a.color-grey-c:hover {
color: #111C24 !important;
}
/*蓝色*/
.color-blue, .color-blue-file{
.color-blue {
color: #4CACFF !important;
}
.color-blue {
color: #2A61FF !important;
}
/* 绿色 */
.color-green-file{
color: #28BD6C;
}
/*主*/
.color-blue_4C {
color: #4CACFF !important;
}
a.color-blue:hover,
a.color-blue_4C:hover {
color: #459BE6 !important;
}
/*橙色*/
.color-orange {
@ -2657,7 +2661,7 @@ a.color-green:hover {
/*百分比宽度*/
.width100 {
width: 100% !important;
width: 100%;
}
.width89 {
@ -3406,7 +3410,7 @@ a.user_bluebg_btn {
}
.cdefault {
cursor: default!important;
cursor: default
}
@ -3581,6 +3585,42 @@ 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 ******/
/*****载入中******/
@ -3905,21 +3945,11 @@ 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;
@ -3941,7 +3971,7 @@ html>body #ajax-indicator {
cursor: pointer;
position: relative;
font-size: 16px;
padding-right:40px;
padding:0px 20px;
}
.head-nav ul#header-nav li a {
@ -4078,6 +4108,8 @@ em.vertical-line {
/* 右侧内容宽度变化的话需要调整posi-search right的值*/
/*底部*/
.footercon {
border-bottom: 1px solid #47494d;
@ -6673,19 +6705,4 @@ 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;
}
.ant-input, .ant-input .ant-input-suffix{
background-color: #fff!important;
}
.has-error .ant-input{
background-color: #FEF1F0!important;
}

File diff suppressed because one or more lines are too long

BIN
public/css/iconfont.eot Normal file

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

1526
public/css/iconfont.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 584 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,111 +0,0 @@
.CodeMirror-merge {
position: relative;
white-space: pre;
}
.CodeMirror-merge, .CodeMirror-merge .CodeMirror {
min-height:50px;
}
.CodeMirror-merge-2pane .CodeMirror-merge-pane { width: 48%; }
.CodeMirror-merge-2pane .CodeMirror-merge-gap { width: 4%; }
.CodeMirror-merge-3pane .CodeMirror-merge-pane { width: 31%; }
.CodeMirror-merge-3pane .CodeMirror-merge-gap { width: 3.5%; }
.CodeMirror-merge-pane {
display: inline-block;
white-space: normal;
vertical-align: top;
}
.CodeMirror-merge-pane-rightmost {
position: absolute;
right: 0px;
z-index: 1;
}
.CodeMirror-merge-gap {
z-index: 2;
display: inline-block;
height: 100%;
-moz-box-sizing: border-box;
box-sizing: border-box;
overflow: hidden;
position: relative;
background: #515151;
}
.CodeMirror-merge-scrolllock-wrap {
position: absolute;
bottom: 0; left: 50%;
}
.CodeMirror-merge-scrolllock {
position: relative;
left: -50%;
cursor: pointer;
color: #d8d8d8;
line-height: 1;
}
.CodeMirror-merge-copybuttons-left, .CodeMirror-merge-copybuttons-right {
position: absolute;
left: 0; top: 0;
right: 0; bottom: 0;
line-height: 1;
}
.CodeMirror-merge-copy {
position: absolute;
cursor: pointer;
color: #ce374b;
z-index: 3;
}
.CodeMirror-merge-copy-reverse {
position: absolute;
cursor: pointer;
color: #44c;
}
.CodeMirror-merge-copybuttons-left .CodeMirror-merge-copy { left: 2px; }
.CodeMirror-merge-copybuttons-right .CodeMirror-merge-copy { right: 2px; }
.CodeMirror-merge-r-inserted, .CodeMirror-merge-l-inserted {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12MwuCXy3+CWyH8GBgYGJgYkAABZbAQ9ELXurwAAAABJRU5ErkJggg==);
background-position: bottom left;
background-repeat: repeat-x;
}
.CodeMirror-merge-r-deleted, .CodeMirror-merge-l-deleted {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12M4Kyb2/6yY2H8GBgYGJgYkAABURgPz6Ks7wQAAAABJRU5ErkJggg==);
background-position: bottom left;
background-repeat: repeat-x;
}
.CodeMirror-merge-r-chunk { background: #9a6868; }
.CodeMirror-merge-r-chunk-start { /*border-top: 1px solid #ee8; */}
.CodeMirror-merge-r-chunk-end {/* border-bottom: 1px solid #ee8; */}
.CodeMirror-merge-r-connect { fill:#9a6868;}
.CodeMirror-merge-l-chunk { background: #eef; }
.CodeMirror-merge-l-chunk-start { border-top: 1px solid #88e; }
.CodeMirror-merge-l-chunk-end { border-bottom: 1px solid #88e; }
.CodeMirror-merge-l-connect { fill: #eef; stroke: #88e; stroke-width: 1px; }
.CodeMirror-merge-l-chunk.CodeMirror-merge-r-chunk { background: #dfd; }
.CodeMirror-merge-l-chunk-start.CodeMirror-merge-r-chunk-start { border-top: 1px solid #4e4; }
.CodeMirror-merge-l-chunk-end.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #4e4; }
.CodeMirror-merge-collapsed-widget:before {
content: "(...)";
}
.CodeMirror-merge-collapsed-widget {
cursor: pointer;
color: #88b;
background: #eef;
border: 1px solid #ddf;
font-size: 90%;
padding: 0 3px;
border-radius: 4px;
}
.CodeMirror-merge-collapsed-line .CodeMirror-gutter-elt { display: none; }

BIN
public/favicon.ico Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

@ -1,16 +1,15 @@
<!DOCTYPE html>
<html lang="zh-CN" class="notranslate translated-ltr" translate="no">
<html lang="en">
<head>
<meta charset="utf-8">
<meta name=”Keywords” Content=”trustie,trustieforge,forge,确实让创建更美好,协同开发平台″>
<meta name=”Keywords” Content=”TrustieOpenSourceProject″>
<meta name=”Keywords” Content=”issue,bug,tracker,软件工程,课程实践″>
<meta name=”Description” Content=持续构建协同、共享、可信的软件创建生态开源创作与软件生产相结合,支持大规模群体开展软件协同创新活动>
<meta name=”Keywords” Content="trustie,trustieforge,forge,确实让创建更美好,协同开发平台">
<meta name=”Keywords” Content="TrustieOpenSourceProject">
<meta name=”Keywords” Content="issue,bug,tracker,软件工程,课程实践">
<meta name=”Description” Content="持续构建协同、共享、可信的软件创建生态开源创作与软件生产相结合,支持大规模群体开展软件协同创新活动">
<meta name="theme-color" content="#000000">
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/iconfont.css">
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/edu-purge.css">
<link rel=" stylesheet" type="text/css" href="%PUBLIC_URL%css/iconfont.css">
<link rel=" stylesheet" type="text/css" href="%PUBLIC_URL%css/edu-purge.css">
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/editormd.min.css">
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/merge.css">
<%= htmlWebpackPlugin.tags.headTags %>
@ -21,7 +20,6 @@
<div id="root" class="page -layout-v -fit widthunit"></div>
<div id="picture_display" style="display: none;"></div>
<script src="%PUBLIC_URL%js/jquery-1.8.3.min.js"></script>
<script src="%PUBLIC_URL%js/js_min_all.js"></script>
<script src="%PUBLIC_URL%js/codemirror/codemirror.js"></script>
<script src="%PUBLIC_URL%js/editormd/editormd.min.js"></script>
<script src="%PUBLIC_URL%js/codemirror/merge/merge.js"></script>

View File

@ -60,13 +60,12 @@ 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;
}
@ -88,10 +87,6 @@ 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

@ -5,11 +5,18 @@ import zhCN from 'antd/lib/locale-provider/zh_CN';
import {
BrowserRouter as Router,
Route,
Switch
Switch,
Redirect
} from 'react-router-dom';
import axios from 'axios';
import LoginDialog from './modules/login/LoginDialog';
import 'babel-polyfill';
import Notcompletedysl from './modules/user/Notcompletedysl';
import Trialapplicationysl from './modules/login/Trialapplicationysl';
import Trialapplicationreview from './modules/user/Trialapplicationreview';
import AccountProfile from "./modules/user/AccountProfile";
import Accountnewprofile from './modules/user/Accountnewprofile';
import Certifiedprofessional from './modules/modals/Certifiedprofessional';
import Loading from './Loading'
import Loadable from 'react-loadable';
@ -17,7 +24,8 @@ import marked from './common/marked';
import moment from 'moment'
import { MuiThemeProvider, createMuiTheme } from 'material-ui/styles';
import SiderBar from './forge/Component/SiderBar'
import history from './history';
import { SnackbarHOC } from 'educoder'
import { initAxiosInterceptors } from './AppConfig'
@ -35,15 +43,10 @@ const theme = createMuiTheme({
},
});
//forge项目
const Projects = Loadable({
loader: () => import('./forge/Index'),
loading: Loading,
})
//forge安全设置
const Security = Loadable({
loader: () => import('./forge/SecuritySetting/Index'),
loading: Loading,
})
// const Projects = Loadable({
// loader: () => import('./forge/Index'),
// loading: Loading,
// })
//forge项目-devOps详情
const OpsDetail = Loadable({
loader: () => import('./forge/DevOps/opsDetail'),
@ -59,38 +62,24 @@ const Shixunnopage = Loadable({
loader: () => import('./modules/404/Shixunnopage'),
loading: Loading,
})
const ExchangeForums = Loadable({
loader: () => import("./exchange/index"),
loading: Loading,
});
// 新版论坛交流
const Forums = Loadable({
loader: () => import("./forums/Index"),
loading: Loading,
});
const Account = Loadable({
loader: () => import("./user_info/Index"),
loading: Loading,
});
//500页面
const http500 = Loadable({
loader: () => import('./modules/500/http500'),
loading: Loading,
})
const InfosIndex = Loadable({
loader: () => import('./forge/users/Index'),
loading: Loading,
})
// 组织
const OrganizeIndex = Loadable({
loader: () => import('./forge/Team/Index'),
loading: Loading,
})
const EducoderLogin = Loadable({
loader: () => import('./modules/login/EducoderLogin'),
loading: Loading,
})
const Search = Loadable({
loader: () => import('./modules/search/'),
loading: Loading,
})
const WikiPreview = Loadable({
loader: () => import('./forge/Wiki/Preview'),
loading: Loading,
})
const ProjectIndex = Loadable({
loader: () => import("./forge/Index"),
loading: Loading,
});
class App extends Component {
constructor(props) {
super(props);
@ -116,9 +105,36 @@ 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();
@ -196,93 +212,47 @@ 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>
<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"}
render={
(props) => {
return (<OpsDetail {...this.props} {...props} {...this.state} />)
}
}>
</Route>
path="/accounts/:login"
render={(props) => (
<Account {...this.props} {...this.state} {...props} />
)}
></Route>
{/* 论坛交流板块管理 */}
<Route
path={"/settings"}
render={
(props) => {
return (<Security {...this.props} {...props} {...this.state} />)
}
}>
</Route>
{/*项目*/}
path="/forums/manage"
render={(props) => (
<ExchangeForums {...this.props} {...this.state} {...props} />
)}
></Route>
{/* 问吧、论坛交流 */}
<Route
path={"/projects"}
render={
(props) => {
return (<Projects {...this.props} {...props} {...this.state} />)
}
}>
</Route>
<Route
path="/register"
render={
(props) => {
return (<EducoderLogin {...this.props} {...props} {...this.state} />)
}
}
/>
path="/forums"
render={(props) => (
<Forums {...this.props} {...this.state} {...props} />
)}
></Route>
{/*403*/}
<Route path="/403" component={Shixunauthority} />
<Route path="/500" component={http500} />
<Route path={"/organize"}
render={
(props) => {
return (<OrganizeIndex {...props} {...this.props} {...this.state} />)
}
}>
</Route>
{/*404*/}
<Route path="/nopage" component={Shixunnopage} />
{/* 查询 */}
<Route path="/search" component={Search} />
<Redirect from="/projects" to="/nopage"/>
{/* 个人主页 */}
<Route path="/users/:username"
render={
(props) => {
return (<InfosIndex {...this.props} {...this.state} />)
}
}></Route>
<Route exact path="/"
render={
(props) => (
personal && personal.length > 0 ?
<InfosIndex {...this.props} {...props} />
:
<ProjectIndex {...this.props} {...props} />
<Forums {...this.props} {...this.state} {...props} />
)
}
/>
<Route component={Shixunnopage} />
</Switch>
</Router>
</MuiThemeProvider>

View File

@ -2,17 +2,19 @@ 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;
broadcastChannelOnmessage('refreshPage', () => {
window.location.reload();
window.location.reload()
})
function locationurl(list) {
if (window.location.port !== "3007") {
window.location.href = list
if (window.location.port === "3007") {
} else {
window.location.href = list
}
}
// TODO 开发期多个身份切换
@ -24,22 +26,56 @@ if (isDev) {
parsed = queryString.parse(_search);
}
debugType = window.location.search.indexOf('debug=t') !== -1 ? 'teacher' :
window.location.search.indexOf('debug=s') !== -1 ? 'student' :
window.location.search.indexOf('debug=a') !== -1 ? 'admin' : parsed.debug || 'admin'
}
window.location.search.indexOf('debug=s') !== -1 ? 'student' :
window.location.search.indexOf('debug=a') !== -1 ? 'admin' : parsed.debug || 'admin'
}
function clearAllCookie() {
cookie.remove('_educoder_session', { path: '/' });
cookie.remove('autologin_trustie', { path: '/' });
setpostcookie()
}
clearAllCookie();
function setpostcookie() {
const str = window.location.pathname;
if (str.indexOf("/wxcode") !== -1) {
console.log("123")
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();
initOnlineOfflineListener()
// TODO 避免重复的请求 https://github.com/axios/axios#cancellation
var
proxy = "http://localhost:3000"
proxy = "https://forum.trustie.net"
var proxy = "https://testforgeplus.trustie.net";
const requestMap = {};
window.setfalseInRequestMap = function (keyName) {
requestMap[keyName] = false;
}
//响应前的设置
axios.interceptors.request.use(
config => {
if(config.url.indexOf("http") !== -1) {
setpostcookie()
clearAllCookie()
if (config.url.indexOf(proxy) !== -1 || config.url.indexOf(':') !== -1) {
return config
}
requestProxy(config);
requestProxy(config)
let url = `/api${config.url}`;
if (`${config[0]}` !== `true`) {
@ -53,12 +89,18 @@ 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;
},
err => {
return Promise.reject(err);
});
});
axios.interceptors.response.use(function (response) {
if (response === undefined) {
@ -107,6 +149,8 @@ export function initAxiosInterceptors(props) {
message501 = false
}, 2000);
}
requestMap[response.config.url] = false;
setpostcookie();
return response;
}, function (error) {
return Promise.reject(error);

View File

@ -1,4 +1,7 @@
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import { Spin } from 'antd';
class Loading extends Component {
@ -9,7 +12,21 @@ class Loading extends Component {
}
render() {
return ""
// Loading
return (
<div className="App" style={{ minHeight: '800px', width: "100%" }}>
<style>
{
`
.margintop{
margin-top:20%;
}
`
}
</style>
<Spin size="large" className={"margintop"} />
</div>
);
}
}

View File

@ -1,5 +1,5 @@
import React, { useEffect , useState } from 'react';
import './header.scss';
import './Index.scss';
function Footer(){
const [ value , setValue ] = useState(undefined);
@ -19,7 +19,7 @@ function Footer(){
return(
<div>
<div style={{height:"497px"}}></div>
<div style={{height:"483px"}}></div>
<div className="newFooter edu-txt-center">
{value && showhtml(value)}
{/* <div className="footerInfos">

View File

@ -1,6 +1,7 @@
.dropdownFlex{
display:flex;
padding:5px;
background:#fff;
border-radius: 3px;
.ant-menu-vertical > .ant-menu-item{
@ -8,57 +9,11 @@
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;
@ -110,13 +65,4 @@
}
}
}
}
.inviteForm{
.ant-form-item{
margin-right: 0px;
}
.ant-form-item-label{
width: 110px;
text-align: right;
}
}

View File

@ -218,7 +218,7 @@ a:hover {
}
.color-blue {
color: #2A61FF;
color: #4CACFF;
}
.color-huang {

View File

@ -1,5 +1,4 @@
import moment from "moment";
import { number } from "prop-types";
// 处理整点 半点
// 取传入时间往后的第一个半点
@ -98,40 +97,3 @@ 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 + "秒前";
}
}

32
src/common/FileList.js Normal file
View File

@ -0,0 +1,32 @@
import React, { PureComponent } from 'react';
function bytesToSize(bytes) {
if (bytes === 0) return '0 B';
let k = 1024,
sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
i = Math.floor(Math.log(bytes) / Math.log(k));
return (bytes / Math.pow(k, i)). toFixed(2) + ' ' + sizes[i];
}
class FileList extends PureComponent{
render(){
let { list , className } = this.props;
const listMap = list && list.map((item)=>{
return(
<li>
<i className="iconfont icon-fujian color-blue font-16 mr8"></i>
<a href={item.url} className="color-blue" target="_self">{item.filename}</a>
<span className="color-grey-9 ml10">{bytesToSize(`${item.filesize}`)}</span>
</li>
)
})
return(
<ul className={className}>
{ listMap }
</ul>
)
}
}
export default FileList;

343
src/common/MDEditor.js Normal file
View File

@ -0,0 +1,343 @@
import React, {Component} from 'react';
// import "antd/dist/antd.css";
import { getUrl } from 'educoder';
import './mdEditor.css';
require('codemirror/lib/codemirror.css');
let path = '/editormd/lib/'
path = getUrl("/editormd/lib/")
const $ = window.$;
// 保存数据
function md_add_data(k,mdu,d){
window.sessionStorage.setItem(k+mdu,d);
}
// 清空保存的数据
function md_clear_data(k,mdu,id){
window.sessionStorage.removeItem(k+mdu);
var id1 = "#e_tip_"+id;
var id2 = "#e_tips_"+id;
if(k == 'content'){
$(id2).html(" ");
}else{
$(id1).html(" ");
}
}
window.md_clear_data = md_clear_data
// editor 存在了jquery对象上应用不需要自己写md_rec_data方法了
function md_rec_data(k, mdu, id) {
if (window.sessionStorage.getItem(k + mdu) !== null) {
var editor = $("#e_tips_" + id).data('editor');
editor.setValue(window.sessionStorage.getItem(k + mdu));
// debugger;
// /shixuns/b5hjq9zm/challenges/3977/tab=3 setValue可能导致editor样式问题
md_clear_data(k, mdu, id);
}
}
window.md_rec_data = md_rec_data;
function md_elocalStorage(editor,mdu,id){
if (window.sessionStorage){
var oc = window.sessionStorage.getItem('content'+mdu);
if(oc !== null && oc != editor.getValue()){
console.log("#e_tips_"+id)
$("#e_tips_"+id).data('editor', editor);
var h = '您上次有已保存的数据,是否<a style="cursor: pointer;" class="link-color-blue" onclick="md_rec_data(\'content\',\''+ mdu + '\',\'' + id + '\')">恢复</a> ? / <a style="cursor: pointer;" class="link-color-blue" onclick="md_clear_data(\'content\',\''+ mdu + '\',\'' + id + '\')">不恢复</a>';
$("#e_tips_"+id).html(h);
}
setInterval(function() {
var d = new Date();
var h = d.getHours();
var m = d.getMinutes();
var s = d.getSeconds();
h = h < 10 ? '0' + h : h;
m = m < 10 ? '0' + m : m;
s = s < 10 ? '0' + s : s;
if(editor.getValue().trim() != ""){
md_add_data("content",mdu,editor.getValue());
var id1 = "#e_tip_"+id;
var id2 = "#e_tips_"+id;
var textStart = " 数据已于 "
var text = textStart + h + ':' + m + ':' + s +" 保存 ";
// 占位符
var oldHtml = $(id2).html();
if (oldHtml && oldHtml != ' ' && oldHtml.startsWith(textStart) == false) {
$(id2).html( oldHtml.split(' (')[0] + ` (${text})`);
} else {
$(id2).html(text);
}
// $(id2).html("");
}
},10000);
}else{
$("#e_tip_"+id).after('您的浏览器不支持localStorage.无法开启自动保存草稿服务,请升级浏览器!');
}
}
function create_editorMD(id, width, high, placeholder, imageUrl, callback, initValue,
onchange, watch, { noStorage, showNullButton, emoji }, that) {
// 还是出现了setting只有一份被共用的问题
var editorName = window.editormd(id, {
width: width,
height: high===undefined?400:high,
path: path, // "/editormd/lib/"
markdown : initValue,
dialogLockScreen: false,
watch:watch===undefined?true:watch,
syncScrolling: "single",
tex: true,
tocm: true,
emoji: !!emoji ,
taskList: true,
codeFold: true,
searchReplace: true,
htmlDecode: "style,script,iframe",
sequenceDiagram: true,
autoFocus: false,
// mine
toolbarIcons: function (mdEditor) {
let react_id = `react_${mdEditor.id}`;
const __that = window[react_id]
// Or return editormd.toolbarModes[name]; // full, simple, mini
// Using "||" set icons align right.
const icons = ["bold", "italic", "|", "list-ul", "list-ol", "|", "code", "code-block", "link", "|", "testIcon", "testIcon1", '|', "image", "table", '|', "watch", "clear"];
// 试卷处用到的填空题新增按钮
if (__that.props.showNullButton) {
icons.push('nullBtton')
}
return icons
},
toolbarCustomIcons: {
testIcon: "<a type=\"inline\" class=\"latex\" ><div class='zbg'></div></a>",
testIcon1: "<a type=\"latex\" class=\"latex\" ><div class='zbg_latex'></div></a>",
nullBtton: "<a type=\"nullBtton\" class='pr' title='增加填空'><div class='border-left'><span></span></div><span class='fillTip'>点击插入填空项</span><i class=\"iconfont icon-edit font-16\"></i></a>",
},
//这个配置在simple.html中并没有但是为了能够提交表单使用这个配置可以让构造出来的HTML代码直接在第二个隐藏的textarea域中方便post提交表单。
saveHTMLToTextarea: true,
// 用于增加自定义工具栏的功能可以直接插入HTML标签不使用默认的元素创建图标
dialogMaskOpacity: 0.6,
placeholder: placeholder,
imageUpload: true,
imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp", "JPG", "JPEG", "GIF", "PNG", "BMP", "WEBP"],
imageUploadURL: imageUrl,//url
onchange: onchange,
onload: function() {
let _id = this.id // 如果要使用this这里不能使用箭头函数
let _editorName = this;
let react_id = `react_${_editorName.id}`;
const __that = window[react_id]
// this.previewing();
// let _id = id;
$("#" + _id + " [type=\"latex\"]").bind("click", function () {
_editorName.cm.replaceSelection("```latex");
_editorName.cm.replaceSelection("\n");
_editorName.cm.replaceSelection("\n");
_editorName.cm.replaceSelection("```");
var __Cursor = _editorName.cm.getDoc().getCursor();
_editorName.cm.setCursor(__Cursor.line - 1, 0);
});
$("#" + _id + " [type=\"inline\"]").bind("click", function () {
_editorName.cm.replaceSelection("`$$$$`");
var __Cursor = _editorName.cm.getDoc().getCursor();
_editorName.cm.setCursor(__Cursor.line, __Cursor.ch - 3);
_editorName.cm.focus();
});
$("[type=\"inline\"]").attr("title", "行内公式");
$("[type=\"latex\"]").attr("title", "多行公式");
if (__that.props.showNullButton) {
const NULL_CH = '▁'
// const NULL_CH = ''
// const NULL_CH = '🈳'
$("#" + _id + " [type=\"nullBtton\"]").bind("click", function () {
_editorName.cm.replaceSelection(NULL_CH);
// var __Cursor = _editorName.cm.getDoc().getCursor();
// _editorName.cm.setCursor(__Cursor.line - 1, 0);
});
}
if (noStorage == true) {
} else {
md_elocalStorage(_editorName, `MDEditor__${_id}`, _id);
}
callback && callback(_editorName)
}
});
return editorName;
}
export default class MDEditor extends Component {
constructor(props) {
super(props)
this.state = {
initValue: ''
}
}
componentDidUpdate(prevProps, prevState) {
// 不能加,影响了试卷填空题
// if (this.props.initValue != prevProps.initValue) {
// this.answers_editormd.setValue(this.props.initValue)
// }
}
// react_mdEditor_
componentDidMount = () => {
const { mdID, initValue, placeholder, showNullButton} = this.props;
let _id = `mdEditor_${mdID}`
this.contentChanged = false;
const _placeholder = placeholder || "";
// amp;
// 编辑时要传memoId
const imageUrl = `/upload_with_markdown?container_id=${mdID || ''}&container_type=Memo`;
// 创建editorMd
let react_id = `react_${_id}`;
// 将实例存到了window
window[react_id] = this
const answers_editormd = create_editorMD(_id, '100%', this.props.height, _placeholder, imageUrl, (_editorName) => {
const __editorName = _editorName;
react_id = `react_${__editorName.id}`;
const that = window[react_id]
// 一个延迟的recreate或resize不加这段代码md初始化可能会出现样式问题
setTimeout(() => {
if (that.props.needRecreate == true) {
__editorName.recreate() // 注意 必须在setValue之前触发不然会清空
} else {
__editorName.resize()
}
console.log('timeout', __editorName.id)
__editorName.cm && __editorName.cm.refresh()
}, that.props.refreshTimeout || 500)
if (this.props.noSetValueOnInit) {
that.onEditorChange()
} else {
if (that.props.initValue != undefined && that.props.initValue != '') {
__editorName.setValue(that.props.initValue)
}
if (that.state.initValue) {
__editorName.setValue(that.state.initValue)
}
}
__editorName.cm.on("change", (_cm, changeObj) => {
that.contentChanged = true;
if (that.state.showError) {
that.setState({showError: false})
}
that.onEditorChange()
})
that.props.onCMBlur && __editorName.cm.on('blur', () => {
that.props.onCMBlur()
})
that.props.onCMBeforeChange && __editorName.cm.on('beforeChange', (cm,change) => {
that.props.onCMBeforeChange(cm,change)
})
that.answers_editormd = __editorName;
// 这里应该可以去掉了,方便调试加的
window[__editorName.id+'_'] = __editorName;
}, initValue, this.onEditorChange,this.props.watch, {
noStorage: this.props.noStorage,
showNullButton: this.props.showNullButton,
emoji: this.props.emoji
}, this);
}
// 用在form里时validate失败时出现一个红色边框
showError = () => {
this.setState({showError: true})
}
onEditorChange = () => {
if (!this.answers_editormd) return;
const val = this.answers_editormd.getValue();
//console.log('onEditorChange', this.props.id, val)
try {
this.props.onChange && this.props.onChange(val)
} catch(e) {
// http://localhost:3007/courses/1309/common_homeworks/6566/setting
// 从这个页面,跳转到编辑页面,再在编辑页面点击返回的时候,这里会报错
console.error('出错')
console.error(e)
}
}
resize = () => {
if (!this.answers_editormd) { // 还未初始化
return;
}
this.answers_editormd.resize()
this.answers_editormd.cm && this.answers_editormd.cm.refresh()
this.answers_editormd.cm.focus()
}
getValue = () => {
try {
return this.answers_editormd.getValue()
} catch (e) {
return ''
}
}
setValue = (val) => {
try {
this.answers_editormd.setValue(val)
} catch (e) {
// TODO 这里多实例的时候前一个实例的state会被后面这个覆盖 参考NewWork.js http://localhost:3007/courses/1309/homework/9300/edit/1
// 未初始化
this.setState({ initValue: val })
}
}
render() {
let {
showError
} = this.state;
let { mdID, className, noStorage, imageExpand } = this.props;
let _style = {}
if (showError) {
_style.border = '1px solid red'
}
return (
<React.Fragment>
<div className={`df ${className} ${imageExpand && 'editormd-image-click-expand' }`} >
{/* padding10-20 */}
<div className="edu-back-greyf5 radius4" id={`mdEditor_${mdID}`} style={{..._style}}>
<textarea style={{display: 'none'}} id={`mdEditors_${mdID}`} name="content"></textarea>
<div className="CodeMirror cm-s-defualt">
</div>
</div>
</div>
{
noStorage == true ? '' :
<div className={"fr rememberTip"}>
<p id={`e_tips_mdEditor_${mdID}`} className="edu-txt-right color-grey-cd font-12"> </p>
{/* {noStorage == true ? ' ' : <p id={`e_tips_mdEditor_${mdID}`} className="edu-txt-right color-grey-cd font-12"> </p>} */}
</div>
}
</React.Fragment>
)
}
}

View File

@ -11,43 +11,11 @@ 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.trustie.net/'
if (isDev) {
return `${local}/${path}`
}
return `${path}`;
}
export function numFormat(num, digits){
let d = digits || 1;
var si = [
{ value: 1, symbol: "" },
{ value: 1E3, symbol: "k" },
{ value: 1E4, symbol: "W" }
];
var rx = /\.0+$|(\.[0-9]*[1-9])0+$/;
var i;
for (i = si.length - 1; i > 0; i--) {
if (num >= si[i].value) {
break;
}
}
return (num / si[i].value).toFixed(d).replace(rx, "$1") + si[i].symbol;
}
export function getImage(path) {
// https://www.educoder.net
// https://testbdweb.trustie.net
// const local = 'http://localhost:3000'
const local = 'https://testforgeplus.trustie.net/';
if(path.indexOf("http://")===-1){
if (isDev) {
return `${local}/images/${path}`
}
return `/${path}`;
}else{
return path;
}
return `/${path}`;
}
export function getcdnImageUrl(path) {
@ -179,28 +147,28 @@ export function getmyUrl(geturl) {
}
export function getUploadActionUrl(path, goTest) {
return `${getUrl()}/api/attachments.json${isDev ?`${isDev ?`?debug=${window._debugType || 'admin'}` : ""}` : ""}`;
return `${getUrl()}/api/attachments.json?debug=${window._debugType || 'admin'}`;
}
export function getUploadLogoActionUrl() {
return `${getUrl()}/api/resumes/logo.json${isDev ?`?debug=${window._debugType || 'admin'}` : ""}`;
return `${getUrl()}/api/resumes/logo.json?debug=${window._debugType || 'admin'}`;
}
export function getUploadActionUrltwo(id) {
return `${getUrlmys()}/api/shixuns/${id}/upload_data_sets.json${isDev ?`?debug=${window._debugType || 'admin'}` : ""}`
return `${getUrlmys()}/api/shixuns/${id}/upload_data_sets.json?debug=${window._debugType || 'admin'}`
}
export function getUploadActionUrlthree() {
return `${getUrlmys()}/api/jupyters/import_with_tpm.json${isDev ?`?debug=${window._debugType || 'admin'}` : ""}`
return `${getUrlmys()}/api/jupyters/import_with_tpm.json?debug=${window._debugType || 'admin'}`
}
export function getupload_git_file(id) {
return `${getUrlmys()}/api/shixuns/${id}/upload_git_file.json${isDev ?`?debug=${window._debugType || 'admin'}` : ""}`
return `${getUrlmys()}/api/shixuns/${id}/upload_git_file.json?debug=${window._debugType || 'admin'}`
}
export function getUploadActionUrlOfAuth(id) {
return `${getUrl()}/api/users/accounts/${id}/auth_attachment.json${isDev ?`?debug=${window._debugType || 'admin'}` : ""}`
return `${getUrl()}/api/users/accounts/${id}/auth_attachment.json?debug=${window._debugType || 'admin'}`
}
export function getRandomNumber(type) {

View File

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

View File

@ -38,11 +38,13 @@ export const formatDelta = (deltas) => {
alt="${alt}"
/>
`;
// text = "<img src="+url+" width='60px' height='30px' onclick='' alt="+alt+"/>";
}
}
formatted.push(text);
});
console.log(formatted);
return formatted.join('');
}

View File

@ -3,8 +3,8 @@
// export { default as OrderStateUtil } from '../routes/Order/components/OrderStateUtil';
export {
getUploadLogoActionUrl as getUploadLogoActionUrl,numFormat as numFormat,
getImageUrl as getImageUrl,getImage as getImage, getmyUrl as getmyUrl, getRandomNumber as getRandomNumber, getUrl as getUrl, publicSearchs as publicSearchs, getRandomcode as getRandomcode, getUrlmys as getUrlmys, getUrl2 as getUrl2, setImagesUrl as setImagesUrl
getUploadLogoActionUrl as getUploadLogoActionUrl,
getImageUrl as getImageUrl, getmyUrl as getmyUrl, getRandomNumber as getRandomNumber, getUrl as getUrl, publicSearchs as publicSearchs, getRandomcode as getRandomcode, getUrlmys as getUrlmys, getUrl2 as getUrl2, setImagesUrl as setImagesUrl
, getUploadActionUrl as getUploadActionUrl, getUploadActionUrltwo as getUploadActionUrltwo, getUploadActionUrlthree as getUploadActionUrlthree, getUploadActionUrlOfAuth as getUploadActionUrlOfAuth
, getTaskUrlById as getTaskUrlById, TEST_HOST, htmlEncode as htmlEncode, getupload_git_file as getupload_git_file, getcdnImageUrl as getcdnImageUrl
} from './UrlTool';
@ -27,7 +27,7 @@ export {
markdownToHTML, uploadNameSizeSeperator, appendFileSizeToUploadFile, appendFileSizeToUploadFileAll, isImageExtension,
downloadFile, sortDirections, validateLength, mdJSONParse, exportMdtoHtml
} from './TextUtil'
export { handleDateString, getNextHalfHourOfMoment, formatDuring, formatSeconds ,timeAgo} from './DateUtil'
export { handleDateString, getNextHalfHourOfMoment, formatDuring, formatSeconds } from './DateUtil'
export { configShareForIndex, configShareForPaths, configShareForShixuns, configShareForCourses, configShareForCustom } from './util/ShareUtil'
@ -76,4 +76,10 @@ export { default as AliyunUploader } from './components/media/AliyunUploader'
export { default as ImageLayer2 } from './hooks/ImageLayer2'
// 外部
export { default as CBreadcrumb } from '../modules/courses/common/CBreadcrumb'
export { CNotificationHOC as CNotificationHOC } from '../modules/courses/common/CNotificationHOC'
export { default as ModalWrapper } from '../modules/courses/common/ModalWrapper'
export { default as NoneData } from '../modules/courses/coursesPublic/NoneData'
export { default as WordNumberTextarea } from '../modules/modals/WordNumberTextarea'

View File

@ -48,15 +48,15 @@ function buildToc(coll, k, level, ctx) {
});
ctx.push("</ul>")
}
ctx.push("</li>");
ctx.push("</li>")
k = buildToc(coll, k, level, ctx)
return k
}
export function getTocContent() {
buildToc(toc, 0, 0, ctx);
ctx.push("</ul>");
return ctx.join("");
buildToc(toc, 0, 0, ctx)
ctx.push("</ul>")
return ctx.join("")
}
const tokenizer = {
@ -157,6 +157,7 @@ renderer.heading = function (text, level, raw) {
})
return '<h' + level + ' id="' + anchor + '">' + text + '</h' + level + '>'
}
marked.setOptions({
silent: true,
smartypants: true,

269
src/common/mdEditor.css Normal file
View File

@ -0,0 +1,269 @@
.CodeMirror-scroll {
overflow: auto !important;
margin-bottom: -30px;
margin-right: -30px;
padding-bottom: 30px;
height: 100%;
outline: none;
position: relative;
}
a.white-btn.orange-btn:hover {
border: 1px solid #F06200;
color: #FFF !important;
}
.flex1 a.white-btn.orange-btn:hover {
border: 1px solid #F06200;
color: #FFF !important;
}
/*.challenge_nav li a{*/
/*color:#000 !important;*/
/*}*/
.questionli{
width: 95%;
margin-left: 37px;
}
#directory_file{
height:200px;
overflow-y:auto;
background:#f5f5f5;
padding:10px;
}
.directory_filepath{
width:120px;
text-align:left;
}
a{
text-decoration: none;
color: #05101a;
}
.repository_url_tippostion{
position: absolute;
left: 22%;
width: 500px;
top: 100%;
}
.top-black-trangleft {
display: block;
border-width: 8px;
position: absolute;
top: -16px;
/* right: 4px; */
border-style: dashed solid dashed dashed;
border-color: transparent transparent rgba(5,16,26,0.6) transparent;
font-size: 0;
line-height: 0;
}
#exercisememoMD .CodeMirror {
margin-top: 31px !important;
height: 370px !important;
/*width: 579px !important;*/
}
#exercisememoMD .editormd-preview {
top: 40px !important;
height: 370px !important;
width: 578px !important;
}
#exercisememoMD{
/*height: 700px !important;*/
}
#questioMD{
/*width: 95% !important;*/
height: 417px !important;
margin-left: 0% !important;
}
#questioMD .CodeMirror {
/*width: 550.5px !important;*/
margin-top: 31px !important;
height: 374px !important;
}
#questioMD .editormd-preview {
top: 40px !important;
height: 375px !important;
width: 550px !important;
}
#newquestioMD .CodeMirror {
/*width: 549px !important;*/
margin-top: 31px !important;
height: 364px !important;
}
#newquestioMD .editormd-preview {
top: 40px !important;
height: 364px !important;
width: 578px !important;
}
#challenge_choose_answer .CodeMirror {
margin-top: 31px !important;
height: 364px !important;
/*width: 578px !important;*/
}
#challenge_choose_answer .editormd-preview {
top: 40px !important;
height: 364px !important;
width: 578px !important;
}
#neweditanswer .CodeMirror {
margin-top: 31px !important;
height: 364px !important;
/*width: 549.5px !important;*/
}
#neweditanswer .editormd-preview {
top: 40px !important;
height: 364px !important;
width: 551px !important;
}
#repository_url_tip {
top: 30px !important;
left: 249px !important;
width: 292px !important;
}
#editanswers .CodeMirror{
/*width: 548px !important;*/
height: 358px !important;
margin-top: 30px !important;
}
#editanswers .editormd-preview{
width: 578px !important;
height: 358px !important;
}
#newquestioMDs .CodeMirror{
/*width: 510px !important;*/
height: 358px !important;
margin-top: 30px !important;
}
#newquestioMDs .editormd-preview{
width: 578px !important;
height: 358px !important;
}
.choose_names{
width: 80px;
margin-left: 20px;
}
#answerMD .CodeMirror{
/*width: 569px !important;*/
height: 600px !important;
margin-top: 30px !important;
}
#answerMD .editormd-preview{
width: 578px !important;
height: 600px !important;
}
#answerMD {
height: 600px !important;
}
.textareavalue{
width: 100%;
padding: 5px;
box-sizing: border-box;
}
.greyInput{
width: 107%;
}
.greyInpus{
width: 100%;
}
.pdr20{
padding-right:20px;
}
.winput-240-40s {
background-color: #F5F5F5;
}
.winput-240-40s:focus{
background-color: #fff;
}
.input-100-45{
background-color: #F5F5F5;
}
.input-100-45:focus{
background-color: #fff;
}
.wind100{
width:100% !important;
}
.color-bule-tip {
color: #5485f7 !important;
}
.martopf4{
margin-top:-4px;
}
.headdfgf{
display: block;
width: 100px;
height: 30px;
line-height: 30px;
float: left;
}
.color979797{
color: #979797 !important;
}
.border-left{
width: 0;
height: 0;
border-bottom: 6px solid transparent;
border-right: 6px solid #cccbcb;
border-top: 6px solid transparent;
position: absolute;
left: 30px;
top: 12px;
}
.border-left span{
display: block;
width: 0;
height: 0;
border-bottom: 6px solid transparent;
border-right: 6px solid #fff;
border-top: 6px solid transparent;
position: absolute;
left: 1px;
top: -6px;
z-index: 10;
}
.fillTip{
position: absolute;
left: 36px;
top: 2px;
width: 125px;
font-size: 12px;
display: block;
padding: 5px;
border: 1px solid #eaeaea;
border-radius: 5px;
box-sizing: border-box;
height: 32px;
line-height: 20px;
font-family: "微软雅黑","宋体";
}

View File

@ -1,67 +1,46 @@
import React, { useEffect, useRef, useMemo } from 'react'
import 'katex/dist/katex.min.css'
import marked, { getTocContent, cleanToc, getMathExpressions, resetMathExpressions } from '../common/marked';
import React, { useEffect, useRef, useMemo } from "react";
import "katex/dist/katex.min.css";
import { renderToString } from 'katex';
import marked, { getTocContent, cleanToc, getMathExpressions, resetMathExpressions } from "../common/marked";
import 'code-prettify'
import dompurify from 'dompurify';
import { renderToString } from 'katex'
const preRegex = /<pre[^>]*>/g
function _unescape(str) {
let div = document.createElement('div')
div.innerHTML = str
return div.childNodes.length === 0 ? "" : div.childNodes[0].nodeValue
return div.childNodes.length === 0 ? "" : div.childNodes[0].nodeValue;
}
export default ({ value = '', className, style = {} }) => {
let str = String(value)
export default ({
value = '',
className,
style = {},
url
}) => {
let str = String(value);
const html = useMemo(() => {
let rs = marked(str);
const math_expressions = getMathExpressions();
let rs = marked(str)
const math_expressions = getMathExpressions()
if (str.match(/\[TOC\]/)) {
rs = rs.replace("<p>[TOC]</p>", getTocContent())
cleanToc()
}
rs = rs.replace(/(__special_katext_id_\d+__)/g, (_match, capture) => {
const { type, expression } = math_expressions[capture]
return renderToString(_unescape(expression) || '', { displayMode: type === 'block', throwOnError: false, output: 'html' })
return renderToString(_unescape(expression), { displayMode: type === 'block', throwOnError: false, output: 'html' })
})
rs = rs.replace(/▁/g, "▁▁▁")
resetMathExpressions()
return dompurify.sanitize(rs)
}, [str]);
return rs
}, [str])
// #id
useEffect(()=>{
if(url && url.hash && html){
let u = url.hash;
if(u){
let id = decodeURIComponent(u.split("#")[1]);
let ele = document.getElementById(id);
if(ele){
window.scrollTo(0, ele.offsetTop + 120);
}
}
}
},[url,html])
const el = useRef();
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 && ancher.startsWith('#')) {
let ancher = target.getAttribute('href')
if (ancher.startsWith('#')) {
e.preventDefault()
let viewEl = document.getElementById(ancher.replace('#', ''))
if (viewEl) {
viewEl.scrollIntoView(true)
viewEl.parentNode.scrollTop = viewEl.offsetTop
}
}
}
@ -82,12 +61,6 @@ export default ({
}
}
}, [html, el.current, onAncherHandler])
return (
<div
ref={el}
style={style}
className={`${className ? className : ''} markdown-body`}
dangerouslySetInnerHTML={{ __html: html }}
></div>
)
return (<div ref={el} style={style} className={`${className ? className : ''} markdown-body`} dangerouslySetInnerHTML={{ __html: html }}></div>)
}

View File

@ -0,0 +1,40 @@
import React, { PureComponent } from 'react';
import { Popconfirm } from 'antd';
import './comment.css';
import RenderHtml from "../../components/render-html";
class CommentsItem extends PureComponent{
cancelEvent=()=>{}
render(){
const { username , time , content , id , admin , deleteReplyEvent } = this.props
// 当前用户是否是管理员或者版主true为有权限删除评论
const adminDelete = (
admin &&
<Popconfirm
title="确定删除这条评论?"
onConfirm={()=>deleteReplyEvent(id)}
onCancel={this.cancelEvent}
okText="确定"
cancelText="取消"
>
<a className="fr c_point color-grey-9">删除</a>
</Popconfirm>
)
return(
<React.Fragment>
<p className="mt3 mb10 clearfix">
<span className="color-grey3 font-16 mr20">{username}</span>
<span className="color-grey9">{time}</span>
{ adminDelete }
</p>
<RenderHtml className="forumsDetailHtml" value={content} />
</React.Fragment>
)
}
}
export default CommentsItem;

View File

@ -0,0 +1,17 @@
import React, { PureComponent } from 'react';
import { getImageUrl } from 'educoder';
import './comment.css';
class CommentsItemImg extends PureComponent{
render(){
const { image_url } = this.props
return(
<img alt="用户头像" src={getImageUrl(`images/${image_url}`)} width="64" height="64" className="radius mr20"></img>
)
}
}
export default CommentsItemImg;

View File

@ -0,0 +1,72 @@
import React, { Component } from 'react';
import MDEditor from '../../common/MDEditor';
import { Form , Button } from "antd";
import ItemImg from './CommentsItemImg'
import '../exchange.css'
import axios from 'axios';
class CommentsSend extends Component{
constructor(props){
super(props);
this.contentMdRef = React.createRef();
}
// 发送评论
handleSubmit=()=>{
this.props.form.validateFieldsAndScroll((err, values) => {
if(!err){
let { id , refresh } = this.props;
const url = `/memos/${id}/reply`;
axios.post(url,{
content:values.replyContent,
parent_id:id
}).then(result=>{
if(result){
this.contentMdRef.current.setValue(' ');
refresh();
}
}).catch(error=>{
})
}
})
}
render(){
const { getFieldDecorator } = this.props.form;
// 唯一键
const { unique , image_url } = this.props;
const imgWrap = (
image_url && <ItemImg image_url={image_url} ></ItemImg>
)
return(
<div className="df mt15">
{ imgWrap }
<div className="flex1">
<Form.Item label="" className="editorFromItem">
{getFieldDecorator('replyContent', {
rules: [{
required: true, message: '请输入评论内容',
},{
max: 5000 , message:'最大限制5000个字符'
}],
})(
<MDEditor ref={this.contentMdRef} placeholder="请输入评论内容最大限制5000字符" mdID={`replyContent${unique}`} refreshTimeout={1500}
className="CommentSendMD" height={150} watch={false} noStorage={true}></MDEditor>
)}
</Form.Item>
<p className="clearfix pb10 pt10">
<Button type="primary" onClick={this.handleSubmit} className="small-default-btn small-blue-btn fr">发送</Button>
</p>
</div>
</div>
)
}
}
const WrappedCommentsSendForm = Form.create({ name: 'CommentsSend' })(CommentsSend);
export default WrappedCommentsSendForm;

View File

View File

@ -0,0 +1,17 @@
import React, { PureComponent } from 'react';
import './custom.css'
class Index extends PureComponent {
render() {
let { img_url , name ,hrefUrl} = this.props;
return (
<span {...this.props}>
<a target="_blank" rel="noopener noreferrer" href={hrefUrl} ><img alt="用户头像" src={img_url} className="radius custom-img"></img></a>
<a target="_blank" rel="noopener noreferrer" href={hrefUrl} ><span className="wrap-name">{name}</span></a>
</span>
)
}
}
export default Index;

View File

@ -0,0 +1,34 @@
/* 论坛主页 */
.custom-wrap{
display: flex;
align-items: center
}
.custom-wrap .wrap-name{
color:#333;
font-size: 16px;
}
.custom-wrap .custom-img{
width:36px;
height:36px;
margin-right: 8px;
}
/* 版块主页 */
.moderatorInfo{
width: 82px;
text-align: center;
display: flex;
flex-direction:column;
align-items: center;
margin:10px 2px 20px 2px;
}
.moderatorInfo .custom-img{
height: 48px;
width: 48px;
}
.moderatorInfo .wrap-name{
display: block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
max-width: 80px;
}

15
src/exchange/Empty.js Normal file
View File

@ -0,0 +1,15 @@
import React, { PureComponent } from 'react';
import nodata from './images/nodata.png';
class Empty extends PureComponent{
render(){
return(
<div style={{textAlign:'center',fontSize:"20px"}}>
<img alt="暂无数据" src={nodata} style={{marginBottom:"20px"}}></img>
<p>暂无数据</p>
</div>
)
}
}
export default Empty;

View File

@ -0,0 +1,164 @@
import React, { Component } from 'react';
import { Menu, Dropdown , Pagination ,Spin} from 'antd';
import { Link } from 'react-router-dom'
import ExchangeItem from './ExchangeItem';
import ExchangeRight from './ExchangeRight'
import './exchange.css';
import Empty from './Empty'
import axios from 'axios';
class ExchangeIndex extends Component {
constructor(props){
super(props);
this.state={
// 热门话题
hottest_memos:undefined,
// 版主推荐
recommend_memos:undefined,
// 列表数据
memos:undefined,
// 列表总数量
memos_count:0,
// 板块导航
forum_sections:undefined,
page:1,
current_user:undefined,
// 默认一页数据
pageSize:15,
search:undefined,
//是否出现加载中的样式
loading:false
}
}
componentDidMount = () =>{
this.InitData();
}
// 数据加载
InitData=(page,search)=>{
let url = `/memos`;
axios.get((url),{params:{
page,search
}}).then((result)=>{
if(result){
this.setState({
loading:false,
hottest_memos:result.data.hottest_memos,
recommend_memos:result.data.recommend_memos,
forum_sections:result.data.forum_sections,
memos:result.data.memos,
memos_count:result.data.memos_count,
current_user:result.data.current_user
})
}
}).catch((error)=>{
})
}
// 搜索
searchEvent=(search)=>{
this.setState({
loading:true,
search,
page:1
})
this.InitData(0,search);
}
// 分页
changePageEvent = (pageNumber) =>{
this.setState({
page:pageNumber
})
const { search } = this.state;
this.InitData(pageNumber,search);
}
render(){
let { hottest_memos , recommend_memos , memos , memos_count , forum_sections , page , pageSize , current_user } = this.state;
// 板块导航dropdown显示内容
const menu = (
<div className="platePanel">
{
forum_sections && forum_sections.map(item => {
return(
<div className="plateItem">
<span className="plateItem_h"><Link to={`/forums/theme/${item.id}`}>{item.name}</Link></span>
<ul className="plateUl">
{
item.children_tags && item.children_tags.map(i=>{
return(<li><Link to={`/forums/theme/${i.id}`}>{i.title}</Link></li>)
})
}
</ul>
</div>
)
})
}
</div>
);
// 数据列表和暂无数据
const dataList = memos && memos.length > 0 ?
<ExchangeItem memos = {memos} {...this.props} {...this.state} refresh={this.InitData} current_user={current_user} page={page}/>
:
<div className="pt50 pb50"><Empty /></div>
return(
<div className="clearfix F_panel">
<div className="fl with76 pr20">
<div className="back-color-white">
<div className="f_left_head">
<ul>
<li className="active">
<a>论坛首页</a>
</li>
{ forum_sections &&
<li>
<Dropdown overlay={menu}>
<a>板块导航<i className="iconfont icon-xiajiantou font-16 ml10 color-dark-grey"></i></a>
</Dropdown>
</li>
}
</ul>
<ul>
<li><Link to='/forums/MyTopic'>我的话题</Link></li>
<li><Link to='/forums/MyEnshrine'>我的收藏</Link></li>
<li><Link to='/forums/MyInteresting'>我感兴趣的论坛</Link></li>
</ul>
</div>
<Spin spinning={this.state.loading} >
{ dataList }
</Spin>
</div>
{
memos_count > pageSize &&
<div className="pt30 pb50 edu-txt-center"><Pagination showQuickJumper current={page} total={memos_count} onChange={this.changePageEvent} pageSize={pageSize} /></div>
}
</div>
<ExchangeRight
{...this.props}
{...this.state}
searchEvent={this.searchEvent}
hottest_memos={hottest_memos}
recommend_memos={recommend_memos}
loading={this.setState.loading}
/>
</div>
)
}
}
export default ExchangeIndex;

View File

@ -0,0 +1,198 @@
import React, { Component, memo } from 'react';
import { Dropdown , Menu } from 'antd';
import {Link} from 'react-router-dom'
import { getImageUrl } from 'educoder';
import Tags from './TagComponent/Index';
import Infos from './InfoComponent/Index';
import Custom from './CustomComponent/Index';
import "./exchange.css"
import axios from 'axios';
class ExchangeItem extends Component {
// 右侧dropdown
InitItemMenu = (id,sticky,is_fine,memo_watched) => {
const { current_user , detail }= this.props;
if(current_user){
if(current_user.admin || current_user.banned_permission){
return(
<Dropdown className="fr c_point" overlay={ this.InitlistMenu(id,sticky,is_fine,memo_watched) } placement="bottomCenter">
<span className="addheight"></span>
</Dropdown>
)
}else if(detail){
return(
<span className="fr c_point color-blue" style={{lineHeight:'16px'}} onClick={()=>this.enShrineEvent(id,memo_watched)}>
{ memo_watched ? '取消收藏' : '收藏' }
</span>
)
}
}
}
// 右侧dropdown的选项
InitlistMenu = (id,sticky,is_fine,memo_watched) => (
<Menu className="edu-txt-center" style={{minWidth:"100px"}}>
<Menu.Item onClick={()=>this.topEvent(sticky,id)} > { sticky ? '取消置顶' : '置顶' } </Menu.Item>
<Menu.Item onClick={()=>this.bestEvent(is_fine,id)}> { is_fine ? '取消推荐' : '推荐' } </Menu.Item>
<Menu.Item onClick={()=>this.enShrineEvent(id,memo_watched)}> { memo_watched ? '取消收藏' : '收藏' } </Menu.Item>
<Menu.Item onClick={()=>this.editEvent(id)}> 编辑 </Menu.Item>
<Menu.Item onClick={()=>this.deleteEvent(id)}> 删除 </Menu.Item>
</Menu>
)
// 取消收藏+收藏
enShrineEvent=(id,memo_watched)=>{
const { refresh } = this.props;
const url = `/memos/${id}/watch_memo`;
// is_watch:1为添加关注
axios.post(url,{
is_watch:memo_watched ? 0 : 1
}).then((result)=>{
if(result){
this.props.showNotification(result.data.message);
refresh();
}
}).catch((error)=>{
})
}
// 取消置顶+置顶
topEvent = (sticky,id) =>{
const { refresh } = this.props;
const url =`/memos/${id}/set_top_or_down.json`;
axios.get(url,{params:{
sticky:sticky ? 0 : 1
}}).then((result)=>{
if(result){
// 调用父级方法刷新
this.props.showNotification(result.data.message);
refresh();
}
}).catch((error)=>{
})
}
// 取消推荐+推荐
bestEvent = (is_fine,id) =>{
const { refresh } = this.props;
const url =`/memos/${id}/is_fine.json`;
axios.post(url,{
is_fine:is_fine ? 0 : 1
}).then((result)=>{
if(result){
// 调用父级方法刷新
this.props.showNotification(result.data.message);
refresh();
}
}).catch((error)=>{
})
}
// 编辑
editEvent=(id)=>{
this.props.history.push(`/forums/${id}/edit`);
}
// 删除
deleteEvent = (id) =>{
this.props.confirm({
content: '确认删除帖子?',
onOk: () => {
const url =`/memos/${id}/destroy.json`;
axios.post(url).then((result)=>{
if(result){
if(result.data.status === 0){
this.props.showNotification(result.data.message);
const { page , refresh } = this.props;
if(page){
refresh(page);
}else{
this.props.history.push("/forums");
}
}
}
}).catch((error)=>{
})
},
onCancel() {
console.log('Cancel');
},
});
}
// 跳转页面
turnToEvent=(id,detail)=>{
{/*传了enshine就代表底部右侧操作按钮只有收藏或者取消收藏否则就是置顶、推荐等 */}
const { enShrine} = this.props;
if(enShrine){return}
if(detail!=true){
const w= window.open('about:blank');
w.location.href=`/forums/${id}`;
}
}
render(){
/**detail:true为详情否则为列表 */
const { memos , current_user , detail} = this.props;
console.log(memos)
const ListItem = memos && memos.map(item=>{
return(
<li>
<div>
<div className="tabulation_infos flex-align-top mb15 ">
<Tags bestClass={item.is_fine ? "mr15" : undefined } topClass={item.sticky ? "mr15" : undefined}></Tags>
<p onClick={()=>this.turnToEvent(item.id,detail)} className={ detail ? "exchangeItem-subject task-hide justify color_black" : "exchangeItem-subject task-hide justify c_point is_onclick" }>
{item.subject}
</p>
{/* 右侧的下拉操作菜单项:登录后才能有右侧信息 */}
{ current_user && this.InitItemMenu(item.id,item.sticky,item.is_fine,item.memo_watched) }
</div>
</div>
<div className="flex-align-center">
{/* 判断用户是否登录 */}
<Custom className="custom-wrap fl" hrefUrl={`/users/${item.user_login}`} img_url = {getImageUrl(`images/${item.image_url}`)} name={item.username}></Custom>
{ item.forum_section_title && <span className="sendPoint">发表在<Link className="color-blue c_point" to={`/forums/theme/${item.forum_section_id}`}>{item.forum_section_title}</Link></span> }
{ item.time && <span className="sendPoint">{item.time}</span> }
{
item.new_reply && item.new_reply.username &&
<span className="fl ml30 pl10 sendPoint">
<span className="color-grey-9 font-14">最新回复
<span className="color-grey-3">{item.new_reply.username}</span>
<span className="ml10 font-12">{item.new_reply.time}</span>
</span>
</span>
}
<span className="flex1"></span>
{/* 论坛首页和论坛详情公用,不同情况下左浮动或者有浮动 */}
<span className="fr">
<Infos className="icon-wrap" icon={"iconfont icon-liulan font-16"} count={item.viewed_count} />
<Infos className="icon-wrap" icon={"iconfont icon-dianzan font-16"} count={item.praises_count} />
<Infos className="icon-wrap" icon={"iconfont icon-pinglun font-16"} count={item.replies_count} />
</span>
</div>
</li>
)
})
return(
<div className="plateTabulation">
{ ListItem }
</div>
)
}
}
export default ExchangeItem;

View File

@ -0,0 +1,68 @@
import React, { PureComponent } from 'react';
import { Link } from "react-router-dom"
import { getImageUrl } from 'educoder';
import ExchangeRightSearch from './ExchangeRightSearch'
import './exchange.css'
class ExchangeRight extends PureComponent {
render(){
const { searchEvent , hottest_memos , recommend_memos , hideSearchPanel} = this.props;
// 热门话题
const hotList = hottest_memos && hottest_memos.length > 0 && (
<div className="bc-white mb20">
<p className="clearfix r_part_title">
<img src={getImageUrl("images/plate/hot.png")} width="13px" className="mr10 fl mt7" alt=""/>
<span className="color-grey3 font-16 fl">热门话题</span>
</p>
<ul className="r_part_list">
{
hottest_memos.map((apply)=>{
return(
<li><a target="_blank" href={`/forums/${apply.id}`}>{apply.subject}</a></li>
)
})
}
</ul>
</div>
)
// 版主推荐
const moderatorList = recommend_memos && recommend_memos.length > 0 && (
<div className="bc-white">
<p className="clearfix r_part_title">
<img src={getImageUrl("images/plate/point.png")} width="16px" className="mr10 fl mt7" alt=""/>
<span className="color-grey3 font-16 fl">版主推荐</span>
</p>
<ul className="r_part_list">
{
recommend_memos.map((apply)=>{
return(
<li><a target="_blank" href={`/forums/${apply.id}`}>{apply.subject}</a></li>
)
})
}
</ul>
</div>
)
return(
<div className="fl with24">
{
!hideSearchPanel && <ExchangeRightSearch {...this.props} {...this.state} searchEvent={searchEvent}></ExchangeRightSearch>
}
{/* 热门话题 */}
{hotList}
{/* 版主推荐 */}
{ moderatorList }
</div>
)
}
}
export default ExchangeRight;

View File

@ -0,0 +1,64 @@
import React, { Component } from 'react';
import { Input } from 'antd'
import { Link } from 'react-router-dom'
const Search = Input.Search;
class ExchangeRightSearch extends Component {
constructor(props) {
super(props)
this.state={
searchDefault:true
}
}
// 有关搜索部分
activeSearch =(e)=>{
this.props.searchEvent(e);
if(!e){
this.setState({
searchDefault:true
})
}
}
showSearchPanel = () =>{
this.setState({
searchDefault:false
})
}
render(){
let { searchDefault } = this.state;
const { current_user } = this.props;
const sendBtn =()=> {
if(current_user){
return(<Link to={'/forums/new'} className="send_btn">发布话题</Link>)
}else{
return(<a href="/login" className="send_btn">发布话题</a>)
}
}
return(
<div className="top_Operate">
{sendBtn()}
{
searchDefault ?
<Search
className="searchfrom"
placeholder="请输入您想搜索的内容"
size="large"
onSearch={this.showSearchPanel}
/>
:
<Search
className="searchfrom active"
placeholder="请输入您想搜索的内容"
size="large"
onSearch={this.activeSearch}
/>
}
</div>
)
}
}
export default ExchangeRightSearch;

View File

@ -0,0 +1,19 @@
import React, { PureComponent } from 'react';
class Index extends PureComponent {
render() {
const { icon , count = 0 , ...props } = this.props;
return (
<span {...props}>
<i className={icon}></i>
<span className={'span-text'}>
{count}
</span>
</span>
)
}
}
export default Index;

View File

@ -0,0 +1,171 @@
import React, { Component } from 'react';
import { Modal , Input , Table ,Pagination } from 'antd'
import './manage.css'
import '../exchange.css';
import axios from 'axios';
const Search = Input.Search;
class AddModeratorModal extends Component {
constructor(props){
super(props);
this.state={
data:undefined,
page:1,
user_name:undefined,
limit:10,
total:undefined,
selectedRowKeys:undefined
}
}
componentDidUpdate=(prevState)=>{
if(prevState.operationPlateId !== this.props.operationPlateId && this.props.visible){
this.setState({
user_name:undefined,
page:1
})
this.getTabData(undefined , 1);
}
}
getTabData=(user_name,page)=>{
const { operationPlateId } = this.props;
const url =`/forum_sections/${operationPlateId}/search_users.json`;
axios.get(url,{
params:{
user_name,
page
}
}).then(result=>{
if(result){
const user_lists = result.data.user_lists;
let array = [];
for(var i = 0;i<user_lists.length;i++){
array.push({
key:user_lists[i].id,
username:user_lists[i].username,
nickname:user_lists[i].nickname,
})
}
this.setState({
data:array,
limit:result.data.limit,
total:result.data.users_count
})
}
}).catch(error=>{
})
}
getSelectKeys=(selectedRowKeys,selectedRows)=>{
this.setState({
selectedRowKeys
})
}
// 搜索
searchEvent=(value)=>{
this.setState({
user_name:value,
page:1
})
this.getTabData(value,1);
}
// 翻页
changePageEvent=(page)=>{
const { user_name } = this.state;
this.setState({
page
})
this.getTabData(user_name,page);
}
// 取消
cancel=()=>{
const { hideAddBox }=this.props;
this.setState({
user_name:undefined,
page:1
})
hideAddBox();
}
// 确定
modalSave=()=>{
const { selectedRowKeys } = this.state;
console.log(selectedRowKeys);
const { plateId } = this.props.match.params;
const { operationPlateId , hideAddBox , getSubModerator } = this.props;
const url = `/forum_sections/${plateId}/add_users.json`;
axios.post(url,{
user_ids:selectedRowKeys,
children_section_id:operationPlateId
}).then(result=>{
if(result){
this.props.showNotification(result.data.message);
hideAddBox();
getSubModerator();
}
}).catch(error=>{
})
}
render(){
const { visible } = this.props;
let { data , total , page , limit } = this.state;
const rowSelection = {
onChange: (selectedRowKeys, selectedRows) => this.getSelectKeys(selectedRowKeys,selectedRows)
};
const columns = [{
title:"姓名",
dataIndex:"username"
},{
title:"昵称",
dataIndex:"nickname"
}];
return(
<Modal
keyboard={false}
title={"添加版主"}
visible={ visible }
closable={false}
footer={null}
destroyOnClose={true}
centered={true}
width="700px"
className="addPlateModal"
>
<div>
<div className="edu-txt-right clearfix mb20 pr30">
<div style={{width:"400px"}} className="fr">
<Search
placeholder="请输入用户名进行搜索"
enterButton="搜索"
onSearch={(value) => this.searchEvent(value)}
/>
</div>
</div>
<Table rowSelection={rowSelection} columns={columns} dataSource={data} size={"small"} pagination={false}/>
{
total && total > limit ?
<div className="edu-txt-center mt10">
<Pagination current={page} size={"small"} total={total} pageSize={limit} onChange={this.changePageEvent}></Pagination>
</div>:""
}
<div className="clearfix mt30 edu-txt-center">
<a className="task-btn mr30" onClick={this.cancel}>取消</a>
<a className="task-btn task-btn-orange" onClick={this.modalSave}>确定</a>
</div>
</div>
</Modal>
)
}
}
export default AddModeratorModal;

View File

@ -0,0 +1,54 @@
import React, { Component } from 'react';
import MenuWraps from '../MenuComponent/Menu';
import SendItem from './SubSendItem';
import CheckItem from './SubCheckItem'
import CheckReplyItem from './SubCheckReplyItem'
const menu_nav = [
{
name:"待审查帖子",
key:`checkPost`,
content:CheckItem
},
{
name:"待审查回复",
key:`checkReply`,
content:CheckReplyItem
},
{
name:"已发布的帖子",
key:`sendPost`,
content:SendItem
}
]
class CheckPublic extends Component {
constructor(props){
super(props);
this.state={
activeKey:undefined
}
}
changeTab=(activeKey)=>{
this.setState({
activeKey
})
}
render(){
return(
<div className="edu-back-white mb20">
<MenuWraps
{...this.props}
{...this.state}
className="plate-left-Menu moderatorMenu"
menu_nav={menu_nav}
defaultUrlKey={"checkPost"}
changeTab={this.changeTab}/>
</div>
)
}
}
export default CheckPublic;

View File

@ -0,0 +1,40 @@
import React, { Component } from 'react';
import {Link} from 'react-router-dom';
import { getImageUrl } from 'educoder';
import RenderHtml from "../../components/render-html";
import Customers from '../CustomComponent/Index'
import "./manage.css"
class ItemLeft extends Component {
render(){
// user_url,username , time , image_url :用户链接,用户名,时间,头像,
// memo_title , forum_title待审查帖子以及已发布帖子帖子名称发表的论坛名
// source_title , reply_content待审查回复帖子来源回复内容
//memo_id,forum_id,source_id 待审查帖子id发表在版块id,来源版块id
const {user_url,id, memo_id,username , time , image_url , memo_title , forum_title ,source_title , reply_content,forum_id,source_id} = this.props;
const title_Url= id || memo_id;
return(
<div className="flex1">
<p className="flex-align-center mb15">
<Customers className="ItemsHeadPhoto flex-align-center" hrefUrl={user_url} img_url={getImageUrl(`images/${image_url}`)} name={username}></Customers>
<span className="sendPoint">{time}</span>
{ forum_title && <span className="sendPoint">发表在 <Link to={`/forums/theme/${forum_id}`}><span className="green">{forum_title}</span></Link></span> }
{ source_title && <span className="sendPoint">来源 <Link to={`/forums/theme/${source_id}`}><span className="green">{source_title}</span></Link></span> }
</p>
{ memo_title && <p><Link to={`/forums/${title_Url}`}>{memo_title}</Link></p>}
{
reply_content &&
<Link to={`/forums/${title_Url}/detail`}>
<RenderHtml className="reply_manage_content" value={reply_content} />
</Link>
}
</div>
)
}
}
export default ItemLeft;

View File

@ -0,0 +1,87 @@
import React, { Component } from 'react';
import { Dropdown } from 'antd';
import { Link } from 'react-router-dom';
import Nav from '../NavComponent/Index';
class ModeratorNav extends Component {
render(){
const { current_user , bread_crumb }=this.props;
const forum_tag = bread_crumb && bread_crumb.forum_tag;
const forum = bread_crumb && bread_crumb.forum;
// {
// name:current_user && current_user.username,
// url:`/users/${current_user && current_user.login}`
// },
const routerMap = [
{
name: forum && forum.title,
url:"/forums"
},
{
url:`/forums/theme/${forum_tag && forum_tag.id}`,
name:forum_tag && forum_tag.title
},
{
url:`/forums/theme/${forum_tag && forum_tag.children_bread_crumb && forum_tag.children_bread_crumb.id}`,
name:forum_tag && forum_tag.children_bread_crumb && forum_tag.children_bread_crumb.title
},{
name:"版主管理"
}
];
// 板块导航dropdown显示内容
const menu =(item)=> {
// console.log("item",item);
if(item){
return(
<div className="platePanel">
<div className="plateItem">
<ul className="plateUl ">
{
item.map((i,key)=>{
return(<li key={key}><Link to={`/forums/manage/${i.id}`}>{i.title}</Link></li>)
})
}
</ul>
</div>
</div>
)
}
}
const titleFlag = () => {
if (forum_tag ) {
console.log(forum_tag.children_bread_crumb);
if (bread_crumb && bread_crumb.is_children) {
return (<div className="padding20-30 edu-back-white font-22 mb20">{forum_tag.children_bread_crumb && forum_tag.children_bread_crumb.title}</div>)
} else {
return (
<div className="padding20-30 edu-back-white font-22 mb20">
{ forum_tag.children_bread_crumb ?
<Dropdown overlay={menu(forum_tag.children_bread_crumb) } >
<div>{forum_tag.title}<i className="iconfont icon-xiajiantou font-16 ml10 color-dark-grey"></i></div>
</Dropdown>
:
<span>{forum_tag.title}</span>
}
</div>
)
}
}
}
return(
<div>
<Nav className="mt20 mb30" NavMap = {routerMap} ></Nav>
{titleFlag()}
</div>
)
}
}
export default ModeratorNav;

View File

@ -0,0 +1,35 @@
import React, { Component } from 'react';
import '../exchange.css';
import './manage.css';
import axios from 'axios'
class PassItem extends Component {
passEvent=(checked)=>{
const { id , refresh ,page } = this.props;
const url = `/memos/${id}/memo_hidden`;
axios.post(url,{
checked
}).then((result)=>{
if(result){
this.props.showNotification(result.data.message);
refresh(page);
}
})
}
render(){
return(
<p className="ml50">
<span className="middle-default-btn small-blue-btn c_point" onClick={()=>this.passEvent(true)}>通过</span>
<span className="middle-default-btn ml20 c_point" onClick={()=>this.passEvent(false)}>不通过</span>
</p>
)
}
}
export default PassItem;

View File

@ -0,0 +1,122 @@
import React, { Component } from 'react';
import { getImageUrl } from 'educoder';
import axios from 'axios';
import './manage.css'
import { Link } from 'react-router-dom';
class PreApplyPlate extends Component {
constructor(props){
super(props);
this.state={
applylist:undefined
}
}
componentDidMount=()=>{
const { plateId } = this.props.match.params;
this.getApplyInfo(plateId);
}
getApplyInfo=(id)=>{
const url=`/forum_sections/${id}/applied_forums`;
axios.get(url).then((result)=>{
if(result){
this.setState({
applylist:result.data.applied_moderators
})
}
}).catch(error=>{
})
}
// 通过,拒绝
passApplyEvent=(flag,applyId)=>{
this.props.confirm({
content: `确认${flag?"通过":"拒绝"}版主申请?`,
onOk: () => {
const { plateId } = this.props.match.params;
const url = `/forum_sections/${plateId}/deal_applies/${applyId}`
axios.post(url,{
deal_type:flag?1:2
}).then((result)=>{
if(result){
this.props.showNotification(result.data.message);
this.getApplyInfo(plateId);
}
}).catch((error)=>{
})
},
onCancel() {
console.log('Cancel');
}
})
}
render(){
const { applylist } = this.state;
// const applylist = [
// {
// apply_id: 5,
// username: "OpenGCC",
// login: "innov",
// image_url: "avatars/User/girl.jpg",
// user_url: "/users/innov",
// user_ip: null,
// user_ip_address: "--",
// time: "14小时前",
// forum_title: "MAC安全",
// forum_id: 9,
// forum_url: "/memos/forum_memos/9",
// parent_forum: {
// forum_title: "网络安全网络安全333",
// forum_id: 6,
// forum_url: "/memos/forum_memos/6"
// }
// }
// ]
const listItem= () => {
if(applylist && applylist.length > 0){
return(
<div className="mt20">
<p className="font-16 mb15 color-grey3">版主申请</p>
<div className="applyList">
{ applylist.map((item,key)=>{
return(
<div>
<a href={`/users/${item.login}`} > <img alt="用户头像" src={getImageUrl(`images/${item.image_url}`)} width="36" height="36" className="radius mr15"></img></a>
<div className="flex1">
<p className="mb10">
<a href={`/users/${item.login}`} > <span className="color-blue mr15">{item.username}</span></a>
{ item.user_ip && <span className="color-grey9">IP{item.user_ip}{item.user_ip_address}</span> }
</p>
<p>申请成为<Link to={`/forums/theme/${item.forum_id}`}><span className="color-blue">{item.forum_title}</span></Link></p>
</div>
<div className="edu-txt-right">
<p className="color-grey9 mb10">{item.time}</p>
<p>
<span className="middle-default-btn small-blue-btn c_point" onClick={()=>this.passApplyEvent(true,item.apply_id)}>通过</span>
<span className="middle-default-btn ml20 c_point" onClick={()=>this.passApplyEvent(false,item.apply_id)}>拒绝</span>
</p>
</div>
</div>
)
})
}
</div>
</div>
)
}
}
return(
<React.Fragment>
{listItem()}
</React.Fragment>
)
}
}
export default PreApplyPlate;

View File

@ -0,0 +1,88 @@
import React, { Component } from 'react';
import { Modal , Form , Input } from 'antd';
import '../exchange.css';
import axios from 'axios';
class PreCreate extends Component {
componentDidUpdate=(preState)=>{
const { subId , subName } = this.props;
if(preState.subId !== subId){
if(subId){
this.props.form.setFieldsValue({
title:subName
})
}
}
}
// 确定
modalSave=()=>{
this.props.form.validateFieldsAndScroll((err, values) => {
if(!err){
// subId存在就是编辑否则就是新增
const { refresh , subId } = this.props;
const { plateId } = this.props.match.params;
const url = `/forum_sections${subId ? "/rename":""}.json`;
axios.post(url,{
title:values.title,
children_section_id:subId,
id: plateId
}).then((result)=>{
if(result){
refresh();
this.props.showNotification(result.data.message);
}
}).catch((error)=>{
})
}
})
}
modalCancel=()=>{
this.props.close();
}
render(){
const { getFieldDecorator } = this.props.form;
const { title , visible } = this.props
return(
<Modal
keyboard={false}
title={title}
visible={ visible }
closable={false}
footer={null}
destroyOnClose={true}
centered={true}
width="530px"
>
<div className="task-popup-content">
<Form className="formInline">
<Form.Item
label="标题"
>
{getFieldDecorator('title', {
rules: [{
required: true, message: '请输入板块名称',
},{
max: 5000 , message:'最大限制20个字符'
}],
})(
<Input placeholder="请输入名称最大限制20个字符" maxLength="60" style={{height:"40px"}}/>
)}
</Form.Item>
<div className="clearfix mt30 edu-txt-center">
<a className="task-btn mr30" onClick={this.modalCancel}>取消</a>
<a className="task-btn task-btn-orange" onClick={this.modalSave}>确定</a>
</div>
</Form>
</div>
</Modal>
)
}
}
const WrappedPreCreate = Form.create({ name: 'PreCreate' })(PreCreate);
export default WrappedPreCreate;

View File

@ -0,0 +1,76 @@
import React, { Component } from 'react';
import CheckPublic from './CheckPublic'
import ModeratorNav from './ModeratorNav';
import ApplyPlate from './PreApplyPlate';
import PrePlateManage from './PrePlateManage'
import axios from 'axios';
class PreModerator extends Component {
constructor(props){
super(props);
this.state={
activeKey:undefined,
bread_crumb:undefined,
is_children:false,
}
}
changeTab=(activeKey)=>{
this.setState({
activeKey
})
}
componentDidMount=()=>{
const { plateId } = this.props.match.params;
this.getPlateInfo(plateId);
}
componentDidUpdate=(prevState)=>{
let prePlateId = prevState.match.params.plateId;
const { plateId } = this.props.match.params;
if(prePlateId !== prePlateId){
this.getPlateInfo(plateId);
}
}
getPlateInfo=(plateId)=>{
const url = `/forum_sections/${plateId}/forum_section_header`;
axios.get(url).then((result)=>{
if(result){
this.setState({
bread_crumb:result.data.bread_crumb,
is_children:result.data.bread_crumb && result.data.bread_crumb.is_children
})
}
}).catch(error=>{
})
}
render(){
const { bread_crumb , is_children } = this.state;
const parentManage =()=> {
if(!is_children){
return(
<React.Fragment>
<PrePlateManage {...this.props} {...this.state} getPlateInfo={this.getPlateInfo}/>
{/* 板块申请 */}
<ApplyPlate {...this.props} {...this.state}/>
</React.Fragment>
)
}
}
return(
<div className="newMain">
<div className="educontent">
<ModeratorNav {...this.props} {...this.state}/>
{parentManage()}
{/* 审批(二级版主只有这一块) */}
<CheckPublic {...this.props} {...this.state} bread_crumb={bread_crumb}/>
</div>
</div>
)
}
}
export default PreModerator;

View File

@ -0,0 +1,286 @@
import React, { Component } from 'react';
import { getImageUrl} from 'educoder';
import PreCreate from './PreCreate';
import './manage.css'
import '../exchange.css'
import {Link} from 'react-router-dom'
import axios from 'axios';
import update from 'immutability-helper'
import AddModeratorModal from './AddModeratorModal'
class PrePlateManage extends Component {
constructor(props){
super(props);
this.state={
visible:false,
children_tags:undefined,
subId:undefined,
subName:undefined,
// 新增版主有关
addVisible:undefined,
operationPlateId:undefined
}
}
componentDidMount=()=>{
this.getSubModerator();
}
// 获取所有二级板块
getSubModerator=()=>{
const { plateId } = this.props.match.params;
const url = `/forum_sections/${plateId}/managements`;
axios.get(url).then((result)=>{
if(result){
this.setState({
children_tags:result.data.forum_tag && result.data.forum_tag.children_tags
})
}
}).catch((error)=>{
})
}
// 新建板块和重命名板块后的刷新方法
reSetInfo=()=>{
this.setState({
visible:false,
subId:undefined,
subName:undefined
})
this.getSubModerator();
const { plateId } = this.props.match.params;
const { getPlateInfo } = this.props;
getPlateInfo(plateId);
}
// 展开
expandEvent=(index,flag)=>{
this.setState(
(prevState) => ({
children_tags : update(prevState.children_tags, {[index]: { expand: {$set: flag} }}),
})
)
}
// 显示删除版主的按钮
deleteManageEvent=(index,flag)=>{
console.log(index)
this.setState(
(prevState) => ({
children_tags : update(prevState.children_tags, {[index]: { isDeleting: {$set: flag} }}),
})
)
}
// 新建二级板块
createSubPlateEvent=()=>{
this.setState({
visible:true,
subId:undefined,
subName:undefined
})
}
// 二级板块重命名
RenamneSubPlateEvent=(id,name)=>{
this.setState({
visible:true,
subId:id,
subName:name
})
}
// 删除二级板块
DeleteSubPlateEvent=(id)=>{
this.props.confirm({
content: '确认删除二级板块?',
onOk: () => {
const { plateId } = this.props.match.params;
const url=`/forum_sections/destroy_forum.json`
axios.post(url,{
children_section_id:id,
id: plateId
}).then(result=>{
if(result){
this.props.showNotification(result.data.message);
this.getSubModerator();
const { getPlateInfo } = this.props;
getPlateInfo(plateId);
}
}).catch((error)=>{
})
},
onCancel() {
console.log('Cancel');
}
})
}
// 关闭新建弹框
colseModalEvent=()=>{
this.setState({
visible:false
})
}
// 删除版主
deletePlateEvent=(id,name,index,key)=>{
console.log(index,'dddd',key);
this.props.confirm({
content: `是否确认解除“${name}”的二级版主权限?`,
onOk: () => {
const { plateId } = this.props.match.params;
const url=`/forum_sections/${plateId}/destroy_moderator/${id}`
axios.post(url).then(result=>{
if(result){
this.props.showNotification(result.data.message);
const { children_tags } = this.state;
// 去掉当前选中的版主
let tempObj = children_tags[index].forum_moderators;
tempObj = tempObj.filter((_, i) => i !== key) ;
children_tags[index].forum_moderators = tempObj;
this.setState({ children_tags })
}
}).catch((error)=>{
})
},
onCancel() {
console.log('Cancel');
}
})
}
// 新增版主弹框
showAddBox=(flag,plateId)=>{
this.setState({
addVisible:true,
operationPlateId:plateId
})
}
hideAddBox=()=>{
this.setState({
addVisible:false,
operationPlateId:undefined
})
}
render(){
const { visible ,children_tags , subId , subName , addVisible , operationPlateId } = this.state;
// console.log("child",children_tags);
const forumTagList = ()=>{
if(children_tags && children_tags.length>0){
return(
<div className="subPlateList">
{
children_tags.map((item,key)=>{
return(
<div>
<div className={item.expand ? "subPlateItem active":"subPlateItem"}>
<p className="subPlateItem_head">
<span>{item.title}</span>
<span>
<span className="c_point mr10 color-green font-12" onClick={()=>this.RenamneSubPlateEvent(item.id,item.title)}>重命名</span>
<span className="c_point color-grey-9 sendPoint pl10" onClick={()=>this.DeleteSubPlateEvent(item.id)}>删除板块</span>
</span>
</p>
<p className="mt10 mb10 color-grey3">二级版主</p>
<div className="plateManager">
{ renderSubList(item.forum_moderators,key,item.isDeleting) }
<span className="fr">
{/* 展开 */}
{
item.forum_moderators && item.forum_moderators.length > 5 && !item.expand &&
<span className="c_point mr30" onClick={()=>this.expandEvent(key,true)}><i className="iconfont icon-gengduo1 font-36 color-grey-9"></i></span>
}
{/* 收起 */}
{
item.forum_moderators && item.forum_moderators.length > 5 && item.expand &&
<span className="c_point mr30" onClick={()=>this.expandEvent(key,false)}><i className="iconfont icon-shangjiantou-tianchong font-36 color-grey-9"></i></span>
}
{/* 新增版主 */}
<span className="c_point mr30" onClick={()=>this.showAddBox(true,item.id)}><i className="iconfont icon-roundaddfill color-green font-36"></i></span>
{/* 删除版主 */}
{
item.forum_moderators && item.forum_moderators.length > 0 && !item.isDeleting &&
<span className="c_point mr30" onClick={()=>this.deleteManageEvent(key,true)}><i className="iconfont icon-default color-grey-9 font-36"></i></span>
}
{
item.isDeleting && <span className="c_point mr30 completeIcon" onClick={()=>this.deleteManageEvent(key,false)}>完成</span>
}
</span>
</div>
</div>
</div>
)
})
}
</div>
)
}
}
const renderSubList = (item,index,deletingFlag) =>{
if(item){
return(
<ul>
{
item.map((i,key)=>{
return(
<React.Fragment>
{
!i.isDelete &&
<li>
<a href={`/users/${i.login}`}><img alt="" src={getImageUrl(`images/${i.image_url}`)} width="39" height="39" className="radius mb3"/></a>
{
deletingFlag &&
<i className="iconfont icon-htmal5icon19 deletePlateIcon" onClick={()=>this.deletePlateEvent(`${i.moderator_id}`,`${i.username}`,index,key)}></i>
}
<a href={`/users/${i.login}`}><span className="task-hide" style={{width:"39px",display:"block"}}>{i.username}</span></a>
</li>
}
</React.Fragment>
)
})
}
</ul>
)
}
}
return(
<div>
<div className="padding20-30 edu-back-white font-22 mb20 clearfix">
<PreCreate
{...this.props}
{...this.state}
visible={visible}
title="新建"
refresh={this.reSetInfo}
close={this.colseModalEvent}
subId={subId}
subName={subName}
/>
<AddModeratorModal
{...this.props}
{...this.state}
visible = {addVisible}
operationPlateId={operationPlateId}
hideAddBox={this.hideAddBox}
getSubModerator={this.getSubModerator}
/>
<span className="fl color-grey3 font-16">二级板块管理</span>
<a onClick={this.createSubPlateEvent} className="fr middle-default-btn small-green-btn">新建板块</a>
</div>
{forumTagList()}
</div>
)
}
}
export default PrePlateManage;

View File

@ -0,0 +1,146 @@
import React, { Component } from 'react';
import { Spin , Pagination } from 'antd';
import '../exchange.css';
import './manage.css';
import Empty from '../Empty'
import ItemLeft from './ItemLeft';
import PassItem from './PassItem';
import axios from 'axios'
class SubCheckItem extends Component {
constructor(props){
super(props);
this.state={
page:1,
data:undefined,
isSpin:true,
limit:10
}
}
componentDidMount=()=>{
this.getList(1);
}
getList = (page) =>{
this.setState({
isSpin:true
})
const { plateId } = this.props.match.params;
const url = `/forum_sections/${plateId}/unchecked_memos.json`;
axios.get(url,{params:{
page
}}).then((result)=>{
if(result){
this.setState({
data:result.data,
isSpin:false
})
}
}).catch((error)=>{
})
}
// 禁言
stopEvent=(id,banned,user_id)=>{
const { current_user } = this.props;
const url =`/memos/${id}/banned_user.json`
axios.post((url),{
user_id:user_id,
banned
}).then((result)=>{
if(result){
const { page } = this.state;
this.props.showNotification(result.data.message);
this.getList(page);
}
}).catch((error)=>{
})
console.log("banned",banned);
}
// 分页
changePageEvent=(page)=>{
this.setState({
page
})
this.getList(page);
}
render(){
const { data , page , isSpin , limit } = this.state;
const { current_user } = this.props;
const pageCom = (
data && data.memos_count > limit &&
<div className="edu-txt-center pt30 pb30">
<Pagination showQuickJumper current={page} total={data.memos_count} pageSize={limit} onChange={this.changePageEvent}/>
</div>
)
const dataList = (
data && data.memos_lists && data.memos_lists.length > 0 ?
<div className="pl30 pr30">
{
data.memos_lists.map((item,key)=>{
const userInfo = {
forum_id:item.forum_id,
memo_id:item.memo_id,
user_url:item.user_url,
username:item.username,
time:item.time,
image_url:item.image_url,
memo_title:item.memo_title,
forum_title:item.forum_title,
user_id:item.user_id
}
const passItem = {
refresh:this.getList,
page,
id:item.memo_id
}
return(
<div className="moderatorItems">
<div className="df">
<ItemLeft {...userInfo}/>
<div className="flex-align-bottom between_">
{
current_user && current_user.admin &&
( item.is_banned ?
<p className="edu-txt-right color-red mb15 mt12 c_point" onClick={()=>this.stopEvent(item.memo_id,0,item.user_id)}><i className="iconfont icon-jinzhi font-16 mr10"></i></p>
:
<p className="edu-txt-right color-grey-9 mb15 mt12 c_point" onClick={()=>this.stopEvent(item.memo_id,1,item.user_id)}><i className="iconfont icon-jinzhi font-16 mr10"></i></p>
)
}
<PassItem {...this.props} {...this.state} {...passItem} ></PassItem>
</div>
</div>
</div>
)
})
}
{ pageCom }
</div>
:
<div className="edu-back-white pt50 pb50">
<Empty></Empty>
</div>
)
return(
<Spin spinning={isSpin}>
{ dataList }
</Spin>
)
}
}
export default SubCheckItem;

View File

@ -0,0 +1,148 @@
import React, { Component } from 'react';
import { Spin , Pagination } from 'antd';
import '../exchange.css';
import './manage.css';
import Empty from '../Empty'
import ItemLeft from './ItemLeft';
import PassItem from './PassItem';
import axios from 'axios'
class SubCheckReplyItem extends Component {
constructor(props){
super(props);
this.state={
page:1,
data:undefined,
isSpin:true,
limit:10
}
}
componentDidMount=()=>{
this.getList(1);
}
getList = (page) =>{
this.setState({
isSpin:true
})
const { plateId } = this.props.match.params;
const url = `/forum_sections/${plateId}/unchecked_replies.json`;
axios.get(url,{params:{
page
}}).then((result)=>{
if(result){
this.setState({
data:result.data,
isSpin:false
})
}
}).catch((error)=>{
})
}
// 禁言
stopEvent=(id,banned,user_id)=>{
const { current_user } = this.props;
const url =`/memos/${id}/banned_user.json`
axios.post((url),{
user_id:user_id,
banned
}).then((result)=>{
if(result){
const { page } = this.state;
this.props.showNotification(result.data.message);
this.getList(page);
}
}).catch((error)=>{
})
}
// 分页
changePageEvent=(page)=>{
this.setState({
page
})
this.getList(page);
}
render(){
const { data , page , isSpin , limit } = this.state;
const { current_user } = this.props;
const pageCom = (
data && data.memos_count > limit &&
<div className="edu-txt-center pt30 pb30">
<Pagination showQuickJumper current={page} total={data.memos_count} pageSize={limit} onChange={this.changePageEvent}/>
</div>
)
const dataList = (
data && data.replies_lists && data.replies_lists.length > 0 ?
<div className="pl30 pr30">
{
data.replies_lists.map((item,key)=>{
const userInfo = {
source_id:item.source_id,
user_url:item.user_url,
username:item.username,
time:item.time,
image_url:item.image_url,
reply_content:item.reply_content,
source_title:item.source_title,
user_id:item.user_id,
id:item.reply_id
}
const passItem = {
refresh:this.getList,
page,
id:item.reply_id
}
return(
<div className="moderatorItems">
<div className="df">
<ItemLeft {...userInfo}/>
<div className="flex-align-bottom between_">
{
current_user && current_user.admin &&
( item.is_banned ?
<p className="edu-txt-right color-red mb15 mt12 c_point" onClick={()=>this.stopEvent(item.reply_id,0,item.user_id)}><i className="iconfont icon-jinzhi font-16 mr10"></i></p>
:
<p className="edu-txt-right color-grey-9 mb15 mt12 c_point" onClick={()=>this.stopEvent(item.reply_id,1,item.user_id)}><i className="iconfont icon-jinzhi font-16 mr10"></i></p>
)
}
<PassItem {...this.props} {...this.state} {...passItem} ></PassItem>
</div>
</div>
</div>
)
})
}
{ pageCom }
</div>
:
<div className="edu-back-white pt50 pb50">
<Empty></Empty>
</div>
)
return(
<Spin spinning={isSpin}>
{ dataList }
</Spin>
)
}
}
export default SubCheckReplyItem;

View File

@ -0,0 +1,174 @@
import React, { Component } from 'react';
import { Spin , Pagination } from 'antd';
import Tags from '../TagComponent/Index';
import '../exchange.css';
import './manage.css';
import Empty from '../Empty'
import ItemLeft from './ItemLeft';
import PassItem from './PassItem';
import axios from 'axios'
class SubSendItem extends Component {
constructor(props){
super(props);
this.state={
page:1,
data:undefined,
isSpin:true,
limit:10
}
}
componentDidMount=()=>{
this.getList(1);
}
getList = (page) =>{
this.setState({
isSpin:true
})
const { plateId } = this.props.match.params;
const url = `/forum_sections/${plateId}/checked_memos.json`;
axios.get(url,{params:{
page
}}).then((result)=>{
if(result){
this.setState({
data:result.data,
isSpin:false
})
}
}).catch((error)=>{
})
}
// 删除-已发布的帖子
deleteEvent=(id)=>{
const { page } = this.state;
const url = `/memos/${id}/destroy.json`;
axios.post(url).then(result=>{
if(result){
this.props.showNotification(result.data.message);
this.getList(page);
}
}).catch(error=>{
})
}
// 取消推荐/推荐-已发布的帖子
isFineEvent=(id,flag)=>{
const { page } = this.state;
const url = `/memos/${id}/is_fine.json`;
axios.post(url,{
is_fine:flag ? 0 : 1
}).then(result=>{
if(result){
this.props.showNotification(result.data.message);
this.getList(page);
}
}).catch(error=>{
})
}
// 取消置顶/置顶-已发布的帖子
stickyEvent=(id,flag)=>{
const { page } = this.state;
const url = `/memos/${id}/set_top_or_down.json`;
axios.get(url,{params:{
sticky:flag ? 0 : 1
}}).then(result=>{
if(result){
this.props.showNotification(result.data.message);
this.getList(page);
}
}).catch(error=>{
})
}
// 禁言
stopEvent=(id,banned)=>{
const { current_user } = this.props;
const url =`/memos/${id}/banned_user.json`
axios.post((url),{
user_id:current_user && current_user.user_id,
banned
}).then((result)=>{
if(result){
const { page } = this.state;
this.props.showNotification(result.data.message);
this.getList(page);
}
}).catch((error)=>{
})
}
// 分页
changePageEvent=(page)=>{
this.setState({
page
})
this.getList(page);
}
render(){
const { data , page , isSpin , limit } = this.state;
const pageCom = (
data && data.memos_count > limit &&
<div className="edu-txt-center pt30 pb30">
<Pagination showQuickJumper current={page} total={data.memos_count} pageSize={limit} onChange={this.changePageEvent}/>
</div>
)
const dataList = (
data && data.memos_lists && data.memos_lists.length > 0 ?
<div className="pl30 pr30">
{
data.memos_lists.map((item,key)=>{
const userInfo = {
forum_id:item.forum_id,
memo_id:item.memo_id,
user_url:item.user_url,
username:item.username,
time:item.time,
image_url:item.image_url,
memo_title:item.memo_title,
forum_title:item.forum_title
}
return(
<div className="moderatorItems">
<div className="df">
<ItemLeft {...userInfo}/>
<div>
<p className="edu-txt-right color-red mb15 mt12" style={{height:"33px"}}>
<Tags bestClass={item.is_fine ? "mb5 ml15" : undefined} topClass={item.sticky ? "mb10 ml15" : undefined}></Tags>
</p>
<p className="ml50 color-grey-9">
<span className="c_point" onClick={()=>this.stickyEvent(item.memo_id,item.sticky)}>{item.sticky?"取消置顶":"置顶"}</span>
<span className="sendPoint c_point font-14" onClick={()=>this.isFineEvent(item.memo_id,item.is_fine)}>{item.is_fine?"取消推荐":"推荐"}</span>
<span className="sendPoint c_point font-14" onClick={()=>this.deleteEvent(item.memo_id)}>删除</span>
</p>
</div>
</div>
</div>
)
})
}
{ pageCom }
</div>
:
<div className="edu-back-white pt50 pb50">
<Empty></Empty>
</div>
)
return(
<Spin spinning={isSpin}>
{ dataList }
</Spin>
)
}
}
export default SubSendItem;

View File

@ -0,0 +1,134 @@
.moderatorMenu .ant-tabs-ink-bar{
display: none!important;
}
.moderatorItems{
padding:20px 0px;
border-bottom: 1px solid #eaeaea;
}
.moderatorItems:last-child{
border-bottom: none;
}
.ItemsHeadPhoto .custom-img{
width:36px;
height: 36px;
margin-right: 15px;
}
.reply_manage_content img{
width: 30px
}
/* 一级板块管理-版主申请 */
.applyList{
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between
}
.applyList > div{
background: #fff;
width: 585px;
display: flex;
padding:20px 30px;
box-sizing: border-box;
margin-bottom: 20px;
}
/* 表单form---标题和内容在统一行 */
.formInline .ant-row.ant-form-item{
display: flex
}
.formInline .ant-col.ant-form-item-control-wrapper{
flex: 1;
}
/* 二级板块管理 */
.subPlateList{
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content:space-between;
}
.subPlateList > div{
width:585px;
height: 180px;
background: #fff;
position: relative;
margin-bottom: 20px;
}
.subPlateList > div .subPlateItem{
position: absolute;
width: 100%;
left: 0px;
top:0px;
padding-left:30px;
box-sizing: border-box;
background: #fff;
}
.subPlateList > div .subPlateItem.active{
box-shadow: 0px 5px 26px rgba(0,0,0,0.1);
z-index: 1;
}
.subPlateItem_head{
display: flex;
justify-content: space-between;
height: 50px;
line-height: 50px;
border-bottom: 1px solid #f4f4f4;
margin-right: 30px;
}
.subPlateItem.active .plateManager > ul{
width: auto;
height: auto;
display: inline
}
.plateManager > ul{
display: inline-block;
width: 345px;
height: 60px;
overflow: hidden;
}
.font-36{font-size: 36px!important;}
.plateManager > ul > li{
display: flex;
flex-direction: column;
font-size: 12px;
align-items: center;
float: left;
margin-right: 30px;
text-align: center;
margin-bottom: 20px;
position: relative;
}
.deletePlateIcon{
position: absolute;
top: -11px;
right: -8px;
font-size: 18px;
color: #FF5555;
cursor: pointer;
}
.completeIcon{
background: #5091FF;
width: 36px;
height: 36px;
line-height: 36px;
font-size: 12px;
color: #fff;
text-align: center;
float: right;
border-radius: 50%;
margin-top: 18px;
}
/* 添加版主弹框 */
.addPlateModal .ant-modal-body{
padding:30px 0px!important;
}
/* .addPlateModal .ant-table-tbody > tr > td{
padding:10px 5px;
} */
/*禁言两端对齐*/
.between_{
flex-direction: column;
justify-content: space-between;
}

View File

@ -0,0 +1,18 @@
.plate-left-Menu .ant-tabs-tab{
height: 80px;
line-height: 80px;
font-size: 16px;
margin-left: 30px;
margin-right: 0px;
padding:0px;
}
.plate-left-Menu .ant-tabs-bar{
margin-bottom: 0px;
border-bottom: 1px solid #f4f4f4;
background: #fff;
}
.plate-left-Menu .ant-tabs-extra-content{
margin-top: 18px;
margin-right: 30px;
}

View File

@ -0,0 +1,51 @@
import React, { Component } from 'react';
import { Tabs } from 'antd';
import './Menu.css';
const { TabPane } = Tabs;
class MenuCom extends Component{
constructor(props){
super(props);
this.state={
activeKey: ''
}
}
componentDidMount () {
const { defaultUrlKey } = this.props;
this.setState({
activeKey: defaultUrlKey
})
}
changeTabs=(activeKey)=>{
this.setState({
activeKey:activeKey
})
const { changeTab } = this.props;
changeTab(activeKey);
}
render(){
const { menu_nav , btn , ...props } = this.props;
const tabs = menu_nav.map((tab)=>{
const Content = tab.content;
return(
<TabPane tab={tab.name} key={tab.key}>
<Content condition={tab.key} {...this.props} {...this.state}/>
</TabPane>
)
})
let { activeKey } = this.state;
return(
<Tabs { ...props } onChange={ this.changeTabs } activeKey={ activeKey } animated={false} tabBarExtraContent={ btn }>
{ tabs }
</Tabs>
)
}
}
export default MenuCom;

View File

@ -0,0 +1,115 @@
import React, { Component } from 'react';
import ExchangeRight from '../ExchangeRight'
import '../exchange.css';
import './myExchange.css';
import Nav from '../NavComponent/Index'
import MyTopic from './MyTopic';
import MyInteresting from './MyInteresting';
import MyEnshrine from './MyEnshrine';
import MenuWraps from '../MenuComponent/Menu'
import axios from 'axios';
const menu_nav = [
{
name:"我的话题",
key:`MyTopic`,
content:MyTopic
},
{
name:"我的收藏",
key:`MyEnshrine`,
content:MyEnshrine
},
{
name:"我感兴趣的论坛",
key:`MyInteresting`,
content:MyInteresting
}
]
class Index extends Component {
constructor(props){
super(props);
this.state={
activeKey:"MyTopic",
data:undefined
}
}
componentDidMount=()=>{
this.getMain();
}
getMain=()=>{
const url =`/my_memos/recommend_memos`;
axios.get(url).then(result=>{
if(result){
this.setState({
data:result.data
})
}
}).catch(error=>{
})
}
// 切换第一个nav
changeTab=(activeKey)=>{
// console.log('1--',activeKey);
this.setState({
activeKey
})
}
render(){
let pathname = this.props.location.pathname;
let urllength = pathname.split("/").length;
let urlLast = pathname.split("/")[urllength-1];
const { current_user } = this.props;
const { activeKey , data } = this.state;
const NavMap=[
{
url:current_user && current_user.user_url,
name:current_user && current_user.username
},
{
url:"/forums",
name:"论坛交流"
},
{
name:activeKey === "MyTopic" ? "我的话题" :activeKey === "MyEnshrine" ? "我的收藏" : "我感兴趣的论坛"
}
]
return(
<div className="educontent">
<Nav className="mt30" NavMap = {NavMap}></Nav>
<div className="clearfix F_panel" style={{marginTop:"10px"}}>
<div className="fl with76 pr20">
<div>
<MenuWraps
className="plate-left-Menu mainNav-style"
{...this.props}
{...this.state}
menu_nav={menu_nav}
defaultUrlKey={urlLast}
changeTab={this.changeTab}/>
</div>
</div>
<ExchangeRight
hideSearchPanel={true}
hottest_memos={data && data.hottest_memos}
recommend_memos={data && data.recommend_memos}
></ExchangeRight>
</div>
</div>
)
}
}
export default Index;

View File

@ -0,0 +1,17 @@
import React, { Component } from 'react';
import MyTopicItem from './MyTopicItem';
class MyEnshrine extends Component {
render(){
return(
<div>
<MyTopicItem {...this.props} {...this.state} urlName={'my_memos/my_watched'}></MyTopicItem>
</div>
)
}
}
export default MyEnshrine;

View File

@ -0,0 +1,101 @@
import React, { Component } from 'react';
import { Spin } from 'antd';
import { Link } from 'react-router-dom'
import Empty from '../Empty';
import './myExchange.css';
import box1 from '../images/box1.png';
import box2 from '../images/box2.png';
import box3 from '../images/box3.png';
import box4 from '../images/box4.png';
import box5 from '../images/box5.png';
import box6 from '../images/box6.png';
import axios from 'axios';
const backImgArray = [box1,box2,box3,box4,box5,box6];
class MyInteresting extends Component {
constructor(props){
super(props);
this.state={
forum_details:undefined,
isSpin:true
}
}
componentDidMount = () =>{
this.getInfo();
}
getInfo = () =>{
this.setState({
isSpin:true
})
const url = `/my_memos/my_interested`;
axios.get(url).then((result)=>{
if(result){
this.setState({
forum_details:result.data.forum_details,
isSpin:false
})
}
}).catch(error=>{
})
}
// 取消收藏
cancelEnshrineEvent=(id)=>{
const url = `/memos/forum_memos/${id}/is_watch.json`;
axios.post((url),{
is_watch:0
}).then(result=>{
if(result){
this.getInfo();
}
}).catch(error=>{
})
}
render(){
const { forum_details , isSpin } = this.state;
const divList = ( forum_details && forum_details.length > 0 ?
<div className="ForumList">
{
forum_details.map((item,key) => {
const index = Number(Number((key)%6)+1)-1;
// console.log(index);
return(
<div className="interestItem">
<div className="interestingUpper" style={{backgroundImage:`url(${backImgArray[index]})`}}>
<div>
<p className="font-20 color-white mb10">{item.title}</p>
<p className="color-white font-12 edu-txt-center">{item.memos_count} 个话题</p>
</div>
</div>
<span className="interestingOperate">
<span className="operateBtn c_point" onClick={()=>this.cancelEnshrineEvent(`${item.id}`)}>取消收藏</span>
<Link className="operateBtn color-blue" to={`/forums/theme/${item.id}`}>查看</Link>
</span>
</div>
)
})
}
</div>
:
<div className="pt50 pb50 edu-back-white"><Empty/></div>
)
return(
<Spin spinning={isSpin}>
<div style={{backgroundColor:"#fafafa"}}>
{ divList }
</div>
</Spin>
)
}
}
export default MyInteresting;

View File

@ -0,0 +1,59 @@
import React, { Component } from 'react';
import MenuWraps from '../MenuComponent/Menu';
import MyTopicItem from './MyTopicItem';
import './myExchange.css';
const menu_nav = [
{
name:"发表的话题",
key:`published`,
content:MyTopicItem
},
{
name:"回复的话题",
key:`replied`,
content:MyTopicItem
},
{
name:"看过的话题",
key:`watched`,
content:MyTopicItem
}
]
class MyTopic extends Component {
constructor(props){
super(props);
this.state={
activeKey:"published"
}
}
// 切换第2个nav发表的话题、回复的话题、看过的话题
changeTab=(activeKey)=>{
this.setState({
activeKey
})
}
render(){
let { activeKey } = this.state;
return(
<div>
<MenuWraps
{...this.props}
{...this.state}
className="plate-left-Menu"
subActiveKey={activeKey}
menu_nav={menu_nav}
defaultUrlKey={activeKey}
changeTab={this.changeTab}
urlName={'my_memos'}
/>
</div>
)
}
}
export default MyTopic;

View File

@ -0,0 +1,239 @@
import React, { Component } from 'react';
import { Dropdown , Menu , Icon , DatePicker , Pagination , Spin } from 'antd';
import { Link } from 'react-router-dom'
import moment from 'moment'
import Empty from '../Empty'
import ExchangeItems from '../ExchangeItem';
import './myExchange.css'
import '../exchange.css'
import axios from 'axios';
const { SubMenu } = Menu;
const dateFormat = 'YYYY-MM-DD';
class MyTopicItem extends Component {
constructor(props){
super(props);
this.state={
// 筛选条件
memo_type:undefined,
forum_section_id:undefined,
is_hidden:undefined,
is_hidden_name:undefined,
start_time:undefined,
end_time:undefined,
page:1,
plateName:undefined,
// 分页
pageSize:15,
// 数据
data:undefined,
isSpin:true
}
}
// 搜索
searchEvent=()=>{}
// 清除
clearEvent=()=>{}
componentDidMount=()=>{
this.refresh();
}
// 我的话题和我的收藏公用当前组件但接口不同根据url判断调用哪个接口
getMytopicInfo = (forum_section_id,is_hidden,start_time,end_time,page) =>{
this.setState({
isSpin:true
})
const { subActiveKey , urlName } = this.props;
const url = `/${urlName}`;
axios.get(url,{params:{
memo_type:subActiveKey,
forum_section_id,
is_hidden,
start_time,
end_time,
page
}}).then(result=>{
if(result){
this.setState({
data:result.data,
isSpin:false
})
}
}).catch(error=>{
})
}
// 刷新时需要用到
refresh=()=>{
const {forum_section_id,is_hidden,start_time,end_time } = this.state;
this.getMytopicInfo(forum_section_id,is_hidden,start_time,end_time,1);
}
// 切换分页
changePageEvent = (page) =>{
this.setState({
page
})
const {forum_section_id,is_hidden,start_time,end_time } = this.state;
this.getMytopicInfo(forum_section_id,is_hidden,start_time,end_time,page);
}
// 选择板块
changeForumId=(plateId,name)=>{
this.setState({
forum_section_id:plateId,
plateName:name
})
const {is_hidden,start_time,end_time , page } = this.state;
this.getMytopicInfo(plateId,is_hidden,start_time,end_time,page);
}
// 选择帖子状态
forumType=(type)=>{
this.setState({
is_hidden:type,
is_hidden_name:type === "show" ? "已发布的话题":"待审查的话题"
})
const {forum_section_id,start_time,end_time , page } = this.state;
this.getMytopicInfo(forum_section_id,type,start_time,end_time,page);
}
// 清除搜索条件
clearEvent=()=>{
this.setState({
forum_section_id:undefined,
plateName:undefined,
is_hidden:undefined,
is_hidden_name:undefined,
start_time:undefined,
end_time:undefined
})
this.getMytopicInfo(undefined,undefined,undefined,undefined,1);
}
// 选择开始时间
changeBeginEvent=(e,dateString)=>{
this.setState({
start_time:dateString
})
}
// 选择结束时间
changeEndEvent=(e,dateString)=>{
this.setState({
end_time:dateString
})
}
// 搜索
searchEvent=()=>{
this.setState({
page:1
})
const {forum_section_id,is_hidden,start_time,end_time } = this.state;
this.getMytopicInfo(forum_section_id , is_hidden , start_time , end_time , 1);
}
render(){
const { data , pageSize , page , plateName , is_hidden_name , start_time , end_time , isSpin } = this.state;
// 选择板块
const menuList =(
<div className="platePanel">
{
data && data.forum_sections && data.forum_sections.map(item => {
return(
<div className="plateItem">
<span className="plateItem_h"><a onClick={()=>this.changeForumId(item.id,item.name)}>{item.name}</a></span>
<ul className="plateUl">
{
item.children_tags && item.children_tags.map(i=>{
return(<li><a onClick={()=>this.changeForumId(i.id,i.title)}>{i.title}</a></li>)
})
}
</ul>
</div>
)
})
}
</div>
)
// 全部帖子
const forumList = (
<Menu>
<Menu.Item onClick={()=>this.forumType('hidden')}>待审查的话题</Menu.Item>
<Menu.Item onClick={()=>this.forumType('show')}>已发布的话题</Menu.Item>
</Menu>
)
const pagination = (
data && data.memos && data.memos_count > pageSize &&
<div className="edu-txt-center pt30 pb50">
<Pagination current={page} pageSize={pageSize} total={data.memos_count} showQuickJumper onChange={this.changePageEvent}></Pagination>
</div>
)
const dataList = (
<Spin spinning={isSpin}>
<div className="MyTopicSearch">
<Dropdown overlay={menuList}>
<a className="ant-dropdown-link">
{plateName || "选择板块"} <Icon type="down" />
</a>
</Dropdown>
<Dropdown overlay={forumList}>
<a className="ant-dropdown-link">
{ is_hidden_name || '全部帖子' } <Icon type="down" />
</a>
</Dropdown>
<div>
<span className="mr10">开始日期</span>
<DatePicker style={{width:"145px"}}
placeholder={'请选择开始时间'}
format={dateFormat}
value={start_time && moment(start_time,dateFormat)}
onChange={this.changeBeginEvent}
/>
<span className="ml15 mr10">结束日期</span>
<DatePicker style={{width:"145px"}}
placeholder={'请选择结束时间'}
format={dateFormat}
value={end_time && moment(end_time,dateFormat)}
onChange={this.changeEndEvent}
/>
</div>
<span className="df">
<a onClick={this.searchEvent} className="small-default-btn small-blue-btn mr15">搜索</a>
<a onClick={this.clearEvent} className="small-default-btn">清除</a>
</span>
<span className="color-grey9 font-12"><span className="color-blue">{ data && data.memos_count }</span></span>
</div>
{
data && data.memos && data.memos.length>0 ?
<div>
<ExchangeItems memos = { data && data.memos } {...this.props} {...this.state} refresh={this.refresh}></ExchangeItems>
{ pagination }
</div>
:
<div className="pt50 pb50 edu-back-white">
<Empty/>
</div>
}
</Spin>
)
return( dataList )
}
}
export default MyTopicItem;

View File

@ -0,0 +1,56 @@
.MyTopicSearch{
display: flex;
border-bottom: 1px solid #f4f4f4;
padding:20px 30px;
align-items: center;
justify-content: space-between;
background: #fff;
}
/* 我感兴趣的论坛 */
.ForumList{
display: flex;
flex-flow: row;
flex-wrap:wrap;
justify-content: space-between;
}
.interestItem{
width: 280px;
margin:20px 0px;
background: #FFFFFF;
}
.interestItem .interestingUpper{
height: 150px;
background-repeat:no-repeat;
background-size:100% 100%;
background-color:#fff ;
display: flex;
flex-flow: row;
align-items: center;
justify-content: center;
}
.interestingOperate{
display: flex;
height: 50px;
line-height: 50px;
}
.interestingOperate .operateBtn{
width: 50%;
display: block;
text-align: center;
position: relative;
color: #666666;
}
.interestingOperate .operateBtn:first-child::after{
height: 24px;
width:1px;
top:13px;
position: absolute;
right: 0px;
content: '';
background: #eee;
}
.mainNav-style > .ant-tabs-bar .ant-tabs-ink-bar{
display: none!important;
}

View File

@ -0,0 +1,28 @@
import React , { PureComponent } from "react";
import { Link } from 'react-router-dom';
import './nav.css'
class Index extends PureComponent{
render(){
const { NavMap , ...props } = this.props;
let navRouter = NavMap && NavMap.map((item) => {
return(
<React.Fragment>
{
item.name ? (item.url ?
<Link to={item.url} className="color-grey-9 nav_Link" >{ item.name }</Link>
:
<span className="color-grey3">{ item.name }</span>)
:""
}
</React.Fragment>
)
})
return(
<p {...props}>{ navRouter }</p>
)
}
}
export default Index;

View File

@ -0,0 +1,13 @@
.nav_Link{
position: relative;
margin-right: 20px;
}
.nav_Link::after{
position: absolute;
right: -15px;
content: '>';
top:0px;
color: #999;
height: 20px;
line-height: 18px;
}

36
src/exchange/Plate/All.js Normal file
View File

@ -0,0 +1,36 @@
import React, { PureComponent } from 'react';
import ExchangeItem from '../ExchangeItem'
import Empty from '../Empty'
import '../exchange.css'
import './plate.css'
class All extends PureComponent{
state = {
condition:"all"
}
componentDidMount = () =>{
const { condition } = this.props;
this.setState({
condition
})
}
render(){
const { memos } = this.props;
const memosList = (
memos && memos.length > 0 ?
<ExchangeItem memos={ memos } {...this.props} {...this.state}></ExchangeItem>
:
<div className="pt50 pb50">
<Empty/>
</div>
)
return(
memosList
)
}
}
export default All;

263
src/exchange/Plate/Index.js Normal file
View File

@ -0,0 +1,263 @@
import React, { Component } from 'react';
import { Pagination , Spin } from 'antd'
import {Link} from 'react-router-dom'
import '../exchange.css';
import './plate.css'
import Nav from '../NavComponent/Index';
import MenuWraps from '../MenuComponent/Menu';
import PlateRight from './PlateRight'
import axios from 'axios';
import All from './All'
const menu_nav = [
{
name:"全部",
key:`all`,
content:All
},
{
name:"推荐精华",
key:`is_fine`,
content:All
},
{
name:"我的话题",
key:`my_memos`,
content:All
},
{
name:"我参与的话题",
key:`my_topics`,
content:All
}
]
class Index extends Component {
constructor(props){
super(props);
this.state = {
collectFlag:false,
// 列表搜索相关
page:1,
search:undefined,
pageSize:10,
select_type:"all",
memos:undefined,
memos_count:0,
// 接口返回的所有数据
data:undefined,
forum_tag :undefined,
forum_moders:undefined,
forum_sections:undefined,
bread_crumb:undefined,
current_user:undefined,
isSpin:true
}
}
componentDidMount () {
this.InitData();
}
InitData=()=>{
this.setState({
isSpin:true
})
const { plateid } = this.props.match.params;
const { page , search , select_type} = this.state;
this.getInfos(plateid , page , search , select_type);
}
getInfos = (plateid , page , search , select_type) =>{
const url= `/memos/forum_memos/${plateid}.json`;
axios.get(url,{params:{
page,
search,
select_type
}}).then(result=>{
if(result){
this.setState({
data:result.data,
memos:result.data.memos,
memos_count:result.data.memos_count,
collectFlag:result.data.watched,
forum_tag :result.data.bread_crumb && result.data.bread_crumb.forum_tag,
forum_moders:result.data.forum_moders,
forum_sections:result.data.forum_sections,
bread_crumb:result.data.bread_crumb,
current_user:result.data.current_user,
isSpin:false
})
}
}).catch(error=>{
})
}
// 点击收藏
colectPlate = () =>{
const { collectFlag } = this.state;
const { plateid } = this.props.match.params;
const url = `/memos/forum_memos/${plateid}/is_watch.json`;
// is_watch:1为添加关注
axios.post(url,{
is_watch: collectFlag ? 0 : 1
}).then((result)=>{
if(result){
if(result.data.status === 0){
this.setState({
collectFlag:!collectFlag
})
this.props.showNotification(result.data.message);
}
}
}).catch((error)=>{
})
}
// 搜索
searchEvent=(e)=>{
this.setState({
search:e
})
const { plateid } = this.props.match.params;
const { select_type } = this.state;
this.getInfos(plateid , 1 , e , select_type);
}
// 切换菜单项
changeTabEvent = (key) =>{
this.setState({
select_type:key,
isSpin:true
})
const { plateid } = this.props.match.params;
const { search} = this.state;
this.getInfos(plateid , 1 , search , key);
}
// 切换分页
changePageEvent=(pageNum)=>{
this.setState({
page:pageNum
})
const { plateid } = this.props.match.params;
const { search , select_type} = this.state;
this.getInfos(plateid , pageNum , search , select_type);
}
render(){
let {
title,
collectFlag ,
memos ,
forum_tag ,
select_type,
forum_moders ,
forum_sections ,
page,
data,
pageSize,
bread_crumb ,
current_user,
isSpin } = this.state;
const { plateid } = this.props.match.params;
let pathname = this.props.location.pathname;
let urlLastLength = pathname.split("/").length;
let urlLast = pathname.split("/")[urlLastLength-1];
const children_bread_crumb = forum_tag && forum_tag.children_bread_crumb;
//获取当前版块标题
const title_post=forum_tag && ((children_bread_crumb && children_bread_crumb.title) || forum_tag.title);
// 顶部导航栏
const routerMap = [
{
name:current_user && current_user.username,
url:current_user && current_user.user_url
},
{
name:bread_crumb && bread_crumb.forum && bread_crumb.forum.title,
url:"/forums"
},
{
name:forum_tag && ((children_bread_crumb && children_bread_crumb.title) || forum_tag.title)
}
];
let collectMap = current_user? <span className="mt2 collecting" onClick={() => this.colectPlate()}>{collectFlag ?'取消收藏':'收藏'}</span>:<span className="mt2 collecting" ><a href={`/login`}> </a> </span>;
// 菜单行右侧按钮
//只有登录管理员可见
const btn = (current_user && current_user.admin_permission) ?<Link to={`/forums/manage/${plateid}`} className={"color-grey-6"}>版块管理</Link> : undefined;
console.log(btn);
// 操作列表item需要刷新页面以及切换菜单项需要重新调用参数
const commonEvent = {
refresh:this.InitData,
changeTab:this.changeTabEvent
}
// 列表分页
const pageination = (
data && data.memos_count > pageSize &&
<div className="mb50 edu-txt-center">
<Pagination showQuickJumper current={page} total={data && data.memos_count} pageSize={pageSize} onChange={this.changePageEvent}/>
</div>
)
return(
<div className="clearfix educontent pt20">
<Spin spinning={isSpin}>
<Nav NavMap = {routerMap} ></Nav>
<p className="mt30 mb10 clearfix">
<span className="font-22 color-grey3 mr20 fl lineh-25 task-hide" style = {{ maxWidth : "900px"}}>{title_post}</span>
{ collectMap }
</p>
<div className="clearfix">
<div className="fl with76 pr20">
<div className={"edu-back-white mb30"}>
<MenuWraps
{...this.props}
{...this.state}
{...commonEvent}
className="plate-left-Menu"
menu_nav={menu_nav}
btn={btn}
defaultUrlKey={urlLast}
{...this.state}/>
</div>
{ pageination }
</div>
<PlateRight
forum_sections={forum_sections}
forum_moders={forum_moders}
searchEvent={this.searchEvent}
{...this.props}
{...this.state}
></PlateRight>
</div>
</Spin>
</div>
)
}
}
export default Index;

View File

@ -0,0 +1,93 @@
import React, { PureComponent } from 'react';
import { getImageUrl } from 'educoder';
import { Link } from 'react-router-dom';
import '../exchange.css';
import './plate.css'
import ExchangeRightSearch from '../ExchangeRightSearch'
import Custom from '../CustomComponent/Index';
import axios from 'axios';
class PlateRight extends PureComponent {
// 申请版主
applyModerator = () =>{
this.props.confirm({
content: '是否确认申请版主?',
onOk: () => {
const { plateid } = this.props.match.params;
const url = `/forum_sections/${plateid}/user_apply`;
axios.post(url).then(result=>{
if(result){
this.props.showNotification(result.data.message);
}
}).catch(error=>{
})
},
onCancel() {
console.log('Cancel');
},
});
}
render(){
const { forum_sections , forum_moders , searchEvent , current_user }= this.props;
// admin_permission:是否是版主
const moderatorInfo = ( forum_moders && forum_moders.length > 0 &&
<div className="bc-white mb20">
<p className="clearfix r_part_title">
<img src={getImageUrl("images/plate/person.png")} width="18px" className="mr10 fl mt7" alt=""/>
<span className="color-grey3 font-16 fl">版主</span>
{
current_user && current_user.admin_permission === false && <a onClick={this.applyModerator} className="applyBtn fr mt3">申请版主</a>
}
</p>
<div className="moderatorPanel">
{ forum_moders.map((item,key)=>{
return(
<Custom className="moderatorInfo" hrefUrl={`/users/${item.user_login}`} img_url={getImageUrl(`images/${item.image_url}`)} name={item.username}></Custom>
)
}) }
</div>
</div>
)
const choiceInfo = ( forum_sections && forum_sections.length > 0 &&
<div className="bc-white mb20">
<p className="clearfix r_part_title">
<img src={getImageUrl("images/plate/plate.png")} width="18px" className="mr10 fl mt7" alt=""/>
<span className="color-grey3 font-16 fl">精选版块</span>
</p>
<ul className="choicePlate">
{
forum_sections.map((item,key) => {
return(
<li className="clearfix">
<span className="fl"><a href={`/forums/theme/${item.id}`}>{item.title}</a></span>
<span className="fr"><a href={`/forums/theme/${item.id}`} className="color-blue">{item.memo_size}</a></span>
</li>
)
})
}
</ul>
</div>
)
return(
<div className="fl with24">
<ExchangeRightSearch {...this.props} {...this.state} searchEvent={searchEvent}></ExchangeRightSearch>
{ moderatorInfo }
{ choiceInfo }
</div>
)
}
}
export default PlateRight;

View File

@ -0,0 +1,45 @@
/* plate */
.collecting{
height: 22px;
padding:0px 11px;
border: 1px solid #5091FF;
color: #5091FF;
border-radius: 4px;
line-height: 22px;
font-size: 12px;
float: left;
cursor: pointer;
}
.collected{
color: #999999;
border:1px solid #999999;
}
.applyBtn{
border:1px solid #5091FF;
color: #5091FF;
height: 26px;
line-height: 26px;
padding:0px 8px;
border-radius: 4px;
}
/* 版主 */
.moderatorPanel{
display: flex;
flex-wrap:wrap;
padding: 10px 15px 0px;
}
/* 精选版块 */
.choicePlate{
padding:10px 20px 0px 20px;
}
.choicePlate > li{
padding: 10px 0px;
}
.searchForHide .ant-tabs-bar.ant-tabs-top-bar{
display: none;
}

View File

@ -0,0 +1,155 @@
import React, { PureComponent } from 'react';
import './post.css'
import Item from '../Comments/CommentsItem'
import ItemImg from '../Comments/CommentsItemImg'
import axios from 'axios';
import CommentsSend from '../Comments/CommentsSend';
class CommentsIndex extends PureComponent{
// 调用父组件的刷新方法
refreshReply=()=>{
let { refresh , page} = this.props;
refresh(page);
}
// 删除评论
deleteReplyEvent=(id)=>{
const url = `/memos/${id}/destroy.json`
axios.post(url).then((result)=>{
if(result){
this.props.showNotification(result.data.message);
this.refreshReply();
}
}).catch((error)=>{
})
}
commentReplyEvent=(index,flag)=>{
console.log("1",flag);
this.props.showCommentEvent(index,true);
}
// 点赞评论
praiseEvent=(id)=>{
const url = `/discusses/${id}/plus.json`;
axios.post(url,{
container_type:'Memo',
type:1
}).then(result=>{
if(result){
this.refreshReply();
}
}).catch(error=>{
})
}
parseCommentContent = (oldContent) => {
if (oldContent && oldContent.startsWith('<') && oldContent.endsWith('>')) {
} else if (window.$('#md_div').length) { // 有这个临时处理md内容的dom
window.$('#md_div').html('')
// markdown to html
try {
var markdwonParser = window.editormd.markdownToHTML("md_div", {
markdown: oldContent,
emoji: true,
htmlDecode: "style,script,iframe", // you can filter tags decode
taskList: true,
tex: true, // 默认不解析
flowChart: true, // 默认不解析
sequenceDiagram: true // 默认不解析
});
oldContent = window.$('#md_div').html()
} catch (e) {
// TODO 可能公式parse时报错了
console.error(e)
}
}
return oldContent;
}
render(){
// 评论列表,当前用户信息
const { repliesData , current_user , page } = this.props;
const renderNum = (arrs,flag) =>{
return(<p>展开其余1条评论</p>)
}
//二级评论
const renderChild = (arrs) => {
return(
<div className="">
{
arrs.map((child)=>{
return(
<div className="commentsItem_infos">
<ItemImg image_url={child.image_url} ></ItemImg>
<div className="flex1">
<Item image_url={child.image_url} username={child.username} time={child.time} content={child.content} id={child.id}></Item>
</div>
</div>
)
})
}
</div>
)
}
const replyInfoWrap = (i,index)=> (
<React.Fragment>
<p className="edu-txt-right color-grey-6">
<span className={ i.user_praise ? "ml30":"ml30 c_point"} onClick={ i.user_praise ? undefined : ()=>this.praiseEvent(i.id)}>
<i className={ i.user_praise ? "iconfont icon-dianzan color-grey-9 font-16 mr5" : "iconfont icon-dianzan-xian color-grey3 font-16 mr5"}></i>
<span>{i.praise_count}</span>
</span>
{
current_user && (current_user.is_banned === false || current_user.admin) &&
<span className="ml30 c_point" onClick={()=>this.commentReplyEvent(index,i.commentsFlag)}>
<i className="iconfont icon-pinglun color-grey-9 font-16 mr5"></i><span>{i.replies_count}</span>
</span>
}
</p>
{
i.commentsFlag &&
<CommentsSend unique={`sub_${i.id}`} id={i.id} image_url={current_user && current_user.image_url} refresh={this.refreshReply}/>
}
</React.Fragment>
)
// 判断是否是管理员或者版主
const manage = current_user && (current_user.admin || current_user.banned_permission);
const itemMap = repliesData && repliesData.map((i,index)=>{
let _content = this.parseCommentContent(i.content);
return(
<div className="pre_stage" key={index}>
<div className="commentsItem_infos">
<ItemImg image_url={i.image_url} ></ItemImg>
<div className="flex1">
<Item username={i.username} time={i.time} content={_content} id={i.id} admin={manage} deleteReplyEvent={this.deleteReplyEvent}></Item>
{
i.children && i.children.length > 0 &&
<div className="sub_stage">
{renderChild(i.children)}
{/* { i.children.length > 1 && renderNum(i.children)} */}
</div>
}
{replyInfoWrap(i,index)}
</div>
</div>
</div>
)
})
return( itemMap )
}
}
export default CommentsIndex;

View File

@ -0,0 +1,128 @@
import React, { Component } from 'react';
import moment from 'moment'
import Select, {Option, OptGroup} from 'rc-select';
import 'rc-select/assets/index.css';
import { Upload, Icon, Modal, message } from 'antd';
import 'antd/lib/upload/style/index.css'
import 'antd/lib/modal/style/index.css'
import 'antd/lib/style/index.css'
import 'antd/lib/message/style/index.css'
import './MemoNew.css'
// -------------------------------------------
let uploadValidateFailed = false;
function beforeUpload(file) {
const isJPG = file.type === 'image/jpeg';
const isPNG = file.type === 'image/png';
const isGIF = file.type === 'image/gif';
if (!isJPG && !isPNG && !isGIF) {
message.error('只能上传图片文件!(jpg、png或gif)');
uploadValidateFailed = true;
return false;
}
const isLt2M = (file.size / 1024 / 1024) < 2;
if (!isLt2M) {
uploadValidateFailed = true;
message.error('图片大小必须小于 2MB!');
return false;
}
uploadValidateFailed = false;
return true;
}
class ImageUpload extends Component {
state = {
previewVisible: false,
previewImage: '',
fileList: [
// {
// uid: -1,
// name: 'xxx.png',
// status: 'done',
// url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
// }
],
};
handleCancel = () => {
this.setState({ previewVisible: false })
}
handlePreview = (file) => {
this.setState({
previewImage: file.url || file.thumbUrl,
previewVisible: true,
});
}
handleRemove = () => {
const { onImageUploadRemove } = this.props;
onImageUploadRemove()
}
componentWillReceiveProps(newProps, oldProps) {
// 初始化值
if (newProps.fileList && this.props.fileList.length != newProps.fileList.length) {
this.setState({
fileList: newProps.fileList
})
}
}
handleChange = ({ fileList }) => {
const { onImageUploadDone } = this.props;
if (fileList.length && fileList[0].status === 'done') {
onImageUploadDone(fileList[0])
}
if (!uploadValidateFailed) {
this.setState({ fileList })
}
}
render() {
const { previewVisible, previewImage, fileList } = this.state;
const { currentMemoId } = this.props;
const uploadButton = (
<div className="antuploadName">
<div className="antIconName"><Icon type="plus" /></div>
<div className="ant-upload-text">点击上传封面</div>
<div className="ant-upload-text">只支持JPGPNGJPEG大小不超过2M</div>
<div className="ant-upload-text">建议尺寸4:3</div>
</div>
);
return (
<div className="clearfix">
<Upload
action={"http://123.59.135.93/upload_memo_heads"}
listType="picture-card"
fileList={fileList}
onPreview={this.handlePreview}
onChange={this.handleChange}
onRemove={this.handleRemove}
beforeUpload={beforeUpload}
data={{
container_type: 'Memo',
container_id: currentMemoId ? currentMemoId : ''
}}
>
{fileList.length >= 1 ? null : uploadButton}
</Upload>
<Modal visible={previewVisible} footer={null} onCancel={this.handleCancel}>
<img alt="example" style={{ width: '100%' }} src={previewImage} />
</Modal>
</div>
);
}
}
export default ImageUpload;

248
src/exchange/Post/Index.js Normal file
View File

@ -0,0 +1,248 @@
import React, { Component } from 'react';
import { Pagination , Spin } from 'antd';
import '../exchange.css'
import './post.css'
import Nav from '../NavComponent/Index'
import Parse from './Parse';
import ExchangeItem from '../ExchangeItem'
import CommentsIndex from './CommentsIndex';
import CommentsSend from '../Comments/CommentsSend';
import RenderHtml from "../../components/render-html";
import FileList from '../../common/FileList';
import update from 'immutability-helper'
import axios from 'axios';
class Index extends Component{
constructor(props){
super(props);
this.state={
// 当前用户是否点赞
judge:true,
parseNum:0,
// 帖子用户信息
author_info:undefined,
// 当前用户信息
current_user:undefined,
// 面包屑
bread_crumb:undefined,
// 当前帖子详情
memos:undefined,
// 帖子评论
repliesData:undefined,
// 帖子评论分页相关
replyPage:1,
replyPageSize:10,
replyCount:0,
is_Spin:true
}
}
componentDidMount = () =>{
this.getInfo();
}
getInfo=()=>{
this.InitDetailInfo();
this.getReply(1);
}
InitDetailInfo = ()=>{
let postid = this.props.match.params.postid;
const url = `/memos/${postid}.json`;
axios.get(url).then((result)=>{
if(result.data && result.data.status === -1){
this.props.history.push("/forums");
}
if(result){
this.setState({
judge:result.data.memo.user_praise,
parseNum:result.data.memo && result.data.memo.praises_count,
author_info:result.data.author_info,
current_user:result.data.current_user,
bread_crumb:result.data.bread_crumb,
memos:result.data.memo,
is_Spin:false
})
}
}).catch((error)=>{
})
}
getReply = (replyPage) =>{
let postid = this.props.match.params.postid;
const url = `/memos/${postid}/more_reply.json`;
axios.get(url,{params:{
page:replyPage
}}).then((result)=>{
if(result){
this.setState({
repliesData:result.data.memo_replies,
replyCount:result.data.memos_count,
is_Spin:false
})
}
}).catch((error)=>{
})
}
// 点赞
thumbForum = () =>{
let postid = this.props.match.params.postid;
const url = `/discusses/${postid}/plus.json`
axios.post(url,{
container_type:'Memo',
type:1
}).then((result)=>{
if(result){
this.InitDetailInfo();
}
}).catch((error)=>{
})
}
// 切换评论分页
changeReplyEvent=(pageNumber)=>{
this.setState({
replyPage:pageNumber
})
this.getReply(pageNumber);
}
// 点击显示评论输入框
commentReplyEvent=(index,flag)=>{
console.log("2",flag);
this.setState(
(prevState) => ({
repliesData : update(prevState.repliesData, {[index]: { commentsFlag: {$set: flag} }}),
})
)
}
// 删除评论
deleteReplyEvent=(id)=>{
const url = `/memos/${id}/destroy.json`
axios.post(url).then((result)=>{
if(result){
this.props.showNotification(result.data.message);
let{ replyPage } = this.state;
this.getReply(replyPage);
}
}).catch((error)=>{
})
}
render(){
let {
judge , parseNum , author_info , current_user , bread_crumb , memos , repliesData ,
replyPage,
replyPageSize,
replyCount,
is_Spin
} = this.state;
let postid = this.props.match.params.postid;
const NavMap=[
{
url:`/users/${current_user && current_user.login}`,
name:current_user && current_user.username
},
{
url:"/forums",
name:bread_crumb && bread_crumb.forum && bread_crumb.forum.title
},
{
url:`/forums/theme/${bread_crumb && bread_crumb.forum_tag && bread_crumb.forum_tag.id}`,
name:bread_crumb && bread_crumb.forum_tag && bread_crumb.forum_tag.title
},
{
name:"帖子详情"
}
]
//插入字段从author_info表内选取
const memos_list = [{
...memos,
image_url:author_info && author_info.image_url,
username:author_info && author_info.username,
user_login:author_info && author_info.login
}];
// 评论列表
const memo_replies = repliesData;
const repliesList = (
memo_replies && memo_replies.length > 0 &&
<div className="commentsForm">
<p className="replyTitle">
<span className="font-24 color-grey-3 mr20">全部回复</span>
<span className="color-grey9 font-20">{replyCount}</span>
</p>
<CommentsIndex {...this.props} {...this.state} refresh={this.getReply} page={replyPage} showCommentEvent={this.commentReplyEvent}></CommentsIndex>
</div>
)
// 附件显示
const fileListCom = ( memos && memos.attachment_url && <FileList list = { memos.attachment_url } className="fileTeam"></FileList> )
return(
<Spin spinning={is_Spin}>
<div className="educontent">
<Nav className="mt20 mb20" NavMap = {NavMap}></Nav>
<div className="educontent-min bc-white mb30">
{/* 调用和首页共用的列表item传了enshine就代表底部右侧操作按钮只有收藏或者取消收藏否则就是置顶、推荐等 */}
<ExchangeItem
{...this.props}
{...this.state}
memos={memos_list}
current_user = { current_user }
detail={true}
refresh={this.getInfo}
></ExchangeItem>
<div className="postContent">
{/* 帖子详情 */}
<RenderHtml className="postDetail" value={memos && memos.content} />
{/* 文件列表 */}
{ fileListCom }
{/* 点赞 */}
<div className="edu-txt-center pb40">
<Parse judge={!judge} current_user = { current_user } num = {parseNum} clickEvent = { this.thumbForum }></Parse>
</div>
{/* 发送评论部分 */}
{
current_user && (current_user.is_banned === false || current_user.admin) &&
<CommentsSend unique={`main_Send`} id={postid} refresh={()=>this.getReply(replyPage)}/>
}
{/* 评论列表 */}
{ repliesList }
</div>
</div>
{
replyCount && replyCount > replyPageSize ?
<div className="edu-txt-center mb50">
<Pagination showQuickJumper current={replyPage} pageSize={replyPageSize} total={replyCount} onChange={this.changeReplyEvent}></Pagination>
</div>:""
}
</div>
</Spin>
)
}
}
export default Index;

View File

@ -0,0 +1,128 @@
.compilegoback{
float:right;
color:#676767 !important;
cursor: pointer;
}
.compilegodell{
float:right;
margin-right:20px;
color:#dadada;
cursor: pointer;
}
.Releasethetitle{
height: 48px !important;
padding-left: 20px !important;
background:#f4f4f4 !important;
}
.Releasethetitle:focus{
background:#FFF !important;
}
.Releasethetitle::-webkit-input-placeholder{
color:#999999;
}
.Releasethetitle:-moz-placeholder{
color:#999999;
}
.Releasethetitle::-moz-placeholder{
color:#999999;
}
.Releasethetitle:-ms-input-placeholder{
color:#999999;
}
.ecSelectbox{
margin-top: 30px;
}
.ant-select-selection__placeholder{
color:#999999;
}
.antuploadName{
width: 280px;
height: 170px;
}
.antuploadName div:nth-child(2){
color:#999898;
font-size:16px;
}
.antuploadName div:nth-child(3){
margin-top: 10px;
color:#d2d2d2;
}
.antuploadName div:nth-child(4){
margin-top: 10px;
color:#d2d2d2;
}
.antIconName{
width: 30px;
height: 30px;
background: #21B351;
border: 1px solid #21B351;
border-radius: 50%;
color: #FFF;
text-align: center;
line-height: 25px;
margin: 40px auto 12px;
font-size: 20px;
position: relative;
}
.antIconName i{
position: absolute;
top: 4px;
left: 3.8px;
}
.ReleasTopic{
color:#333;
font-size:18px;
}
.ant-calendar-picker-input::-webkit-input-placeholder{
color:#999999;
}
.ant-calendar-picker-input:-moz-placeholder{
color:#999999;
}
.ant-calendar-picker-input::-moz-placeholder{
color:#999999;
}
.ant-calendar-picker-input:-ms-input-placeholder{
color:#999999;
}
.ml57{
margin-left:57px;
}
.newdefalutSubmitbtn{
width: 120px !important;
height: 38px !important;
line-height: 38px !important;
margin-top: -5px;
}
.newdefalutCancelbtn{
width: 120px !important;
height: 38px !important;
line-height: 38px !important;
margin-top: -5px;
border: 1px solid #CDCDCD;
}
.ecSelect{
background: #f4f4f4 !important;
padding-left: 4px !important;
height: 48px;
overflow: hidden;
width: 526px;
position: relative;
}
.newLeftgrey{
margin-left: 18px;
}
.uploadBtnclick{
margin-left: 34px;
}
.ant-select-selection--multiple{
height:100%;
border: 1px solid transparent;
}

View File

@ -0,0 +1,811 @@
import React, { Component } from 'react';
import axios from 'axios';
import { Select } from 'antd';
import 'antd/lib/select/style/index.css'
import 'antd/lib/date-picker/style/index.css'
import zhCN from 'antd/lib/date-picker/locale/zh_CN';
import './MemoNew.css'
import './post.css'
import '../exchange.css'
import ImageUpload from './ImageUpload'
const Option = Select.Option;
const $ = window.$;
const isDevServer = window.location.port === "3007";
let origin = window.location.origin;
let path = "/editormd/lib/" // origin + '/react/build/js/editormd/lib/'
if (isDevServer) {
origin = 'http://localhost:3000'
path = 'http://localhost:3000/editormd/lib/'
}
// load
if (!window.postUpMsg) {
$.getScript(
`${origin}/javascripts/attachments.js`,
(data, textStatus, jqxhr) => {
});
}
// editorMD to create
/**
*
* @param id 渲染DOM的id
* @param width 宽度
* @param high 高度
* @param placeholder
* @param imageUrl 上传图片的url
* @returns {*} 返回一个editorMD实例
*/
function create_editorMD(id, width, high, placeholder, imageUrl, callback){
var editorName = window.editormd(id, {
width : width,
height : high,
syncScrolling : "single",
//你的lib目录的路径我这边用JSP做测试的
path : path , // "/editormd/lib/"
tex : true,
tocm : true,
emoji : true,
taskList : true,
codeFold : true,
searchReplace : true,
htmlDecode : "style,script,iframe",
sequenceDiagram : true,
autoFocus: false,
toolbarIcons : function() {
// Or return editormd.toolbarModes[name]; // full, simple, mini
// Using "||" set icons align right.
return ["bold", "italic", "|", "list-ul", "list-ol", "|", "code", "code-block", "|", "testIcon", "testIcon1", '|', "image", "table", '|', "watch", "clear" ]
},
toolbarCustomIcons : {
testIcon : "<a type=\"inline\" class=\"latex\" ><div class='zbg'></div></a>",
testIcon1 : "<a type=\"latex\" class=\"latex\" ><div class='zbg_latex'></div></a>"
},
//这个配置在simple.html中并没有但是为了能够提交表单使用这个配置可以让构造出来的HTML代码直接在第二个隐藏的textarea域中方便post提交表单。
saveHTMLToTextarea : true,
// 用于增加自定义工具栏的功能可以直接插入HTML标签不使用默认的元素创建图标
dialogMaskOpacity : 0.6,
placeholder: placeholder,
imageUpload : true,
imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp", "JPG", "JPEG", "GIF", "PNG", "BMP", "WEBP"],
imageUploadURL : imageUrl,//url
onload: function(){
// this.previewing();
$("#"+ id +" [type=\"latex\"]").bind("click", function(){
editorName.cm.replaceSelection("```latex");
editorName.cm.replaceSelection("\n");
editorName.cm.replaceSelection("\n");
editorName.cm.replaceSelection("```");
var __Cursor = editorName.cm.getDoc().getCursor();
editorName.cm.setCursor(__Cursor.line-1, 0);
});
$("#"+ id +" [type=\"inline\"]").bind("click", function(){
editorName.cm.replaceSelection("$$$$");
var __Cursor = editorName.cm.getDoc().getCursor();
editorName.cm.setCursor(__Cursor.line, __Cursor.ch-2);
editorName.cm.focus();
});
$("[type=\"inline\"]").attr("title", "行内公式");
$("[type=\"latex\"]").attr("title", "多行公式");
callback && callback()
}
});
return editorName;
}
class MemoNew extends Component {
constructor(props) {
super(props)
this.state = {
memoSubject: '',
memoContent: '',
memoType: undefined,
memoTime: undefined,
memoRepertoire: '',
memoLanguage: [],
fileList: [],
memoSubjectLength:undefined,
repertoires: [],
currentSelectRepertoiresIndex: -1,
repertoiresTagMap: {},
// 主题板块
smallOption:undefined,
bigPlateId:undefined,
smallPlateId:undefined,
}
}
onCommit() {
const { memoSubject , memoLanguage, currentMemoId, attachmentData , bigPlateId , smallPlateId} = this.state;
const { showSnackbar } = this.props;
this.setState({
memoSubjectLength : memoSubject.length
})
if (!memoSubject) {
showSnackbar('请先输入标题')
return
}
let mdVal;
try {
mdVal = this.taskpass_editormd.getValue()
} catch (e) {
showSnackbar('编辑器还未加载完毕,请稍后')
return
}
if (!mdVal) {
showSnackbar('请输入话题内容')
return
}
if (!bigPlateId) {
showSnackbar('请选择一级板块');
return
}
/*
<meta content="authenticity_token" name="csrf-param" />
<meta content="G7peAyb1T37RvdwxnVUKmTXuL8T7FaBze5mK0j6MCKs=" name="csrf-token" />
http://localhost:3000/attachments/download/185790/Git-2.17.1.2-32-bit.exe
https://www.educoder.net/attachments/205112.js?attachment_id=1
*/
// collect attachments
const $ = window.$;
const attachmentsMap = {};
let lastIndex = 0;
$('#attachments_fields .attachment').each(( index, item ) => {
const filename = $(item).find('.upload_filename').val();
// $($('#attachments_fields .attachment')[0]).find('input:nth-child(6)').val()
const token = $(item).find('input:nth-child(6)').val()
const attachment_id = $(item).find('input:nth-child(7)').val()
attachmentsMap[index] = {
filename,
token,
attachment_id
}
lastIndex = index;
})
if (attachmentData) {
const { response } = attachmentData
lastIndex++;
attachmentsMap[lastIndex] = {
filename: attachmentData.name,
token: '',
attachment_id: response.attachment_id
}
}
if (currentMemoId) {
this.updateMemo(attachmentsMap)
} else {
this.newMemo(attachmentsMap)
}
}
onCancel() {
const { currentMemoId } = this.state;
if (currentMemoId) { // 编辑
this.props.history.push(`/forums/${currentMemoId}`)
} else { // 新建
this.props.history.push(`/forums`)
}
}
onOkTime(value, dateString){
this.setState({
memoTime:dateString
})
}
updateMemo(attachmentsMap) {
const { memoSubject , currentMemoId, content, attachmentData , smallPlateId , bigPlateId} = this.state;
const mdVal = this.taskpass_editormd.getValue()
console.log('isContentEdit: ', mdVal === content);
const newMemoUrl = `/memos/${currentMemoId}/update`
const params = {
content_changed: this.contentChanged,
memo: {
subject: memoSubject ,
content: mdVal,
},
forum_id:bigPlateId,
attachments: attachmentsMap,
children_forum_id:smallPlateId
}
if (attachmentData) {
const attachment_id = attachmentData.response.attachment_id
if (attachment_id) {
params.attachment_id = attachment_id;
}
}
axios.post(newMemoUrl, params)
.then((response) => {
const { status, message } = response.data;
if (status === 0) {
window.$("html,body").animate({"scrollTop":0})
this.props.history.push(`/forums/${currentMemoId}`)
} else {
if (message.indexOf("Couldn't find Attachment with") !== -1) {
this.props.showSnackbar('附件不存在或正在被删除中,请稍后再试。。。')
} else {
this.props.showSnackbar(message)
}
}
}).catch((error) => {
console.log(error)
})
}
newMemo(attachmentsMap) {
const { memoSubject , attachmentData , smallPlateId , bigPlateId } = this.state;
const mdVal = this.taskpass_editormd.getValue()
const newMemoUrl = `/memos/create`
const params = {
memo: {
subject: memoSubject ,
content: mdVal
},
forum_id:bigPlateId,
attachments: attachmentsMap,
children_forum_id:smallPlateId,
}
if (attachmentData) {
const attachment_id = attachmentData.response.attachment_id
if (attachment_id) {
params.attachment_id = attachment_id;
// params.filename = attachmentData.name // 服务端处理 服务端使用默认的origin_filename
}
}
axios.post(newMemoUrl, params).then((response) => {
const { status, message, memo_id } = response.data;
if (status === 0) {
window.$("html,body").animate({"scrollTop":0})
this.props.history.push(`/forums/${memo_id}`)
} else {
if (message.indexOf("Couldn't find Attachment with") !== -1) {
this.props.showSnackbar('附件不存在或正在被删除中,请稍后再试。。。')
} else {
this.props.showNotification(message)
}
}
}).catch((error) => {
console.log(error)
})
}
onMemoDelete(memo) {
const deleteUrl = `/memos/${memo.id}`;
// 获取memo list
axios.delete(deleteUrl, {
withCredentials: true,
})
.then((response) => {
const status = response.data.status
if (status === 0) {
this.props.showSnackbar('删除成功');
this.props.history.push(`/forums`)
}
}).catch((error) => {
console.log(error)
})
}
componentWillUnmount() {
$('body>#root').off('onMemoDelete')
}
componentDidMount(){
$('body>#root').on('onMemoDelete', (event) => {
// const val = $('body>#root').data('onMemoDelete')
const val = window.onMemoDelete ;
this.onMemoDelete( JSON.parse(decodeURIComponent(val)) )
})
const newMemoUrl = `/memos/new`
axios.get(newMemoUrl,{
// withCredentials: true,
})
.then((response) => {
const data = response.data;
if ( data.current_user ) {
this.setState({
memo_tag: data.memo_type,
memoTime:""
})
const user = response.data.current_user;
user.tidding_count = response.data.tidding_count;
// this.props.initCommonState(user)
// 初始化 csrf meta
const $ = window.$
$('head').append( $('<meta content="authenticity_token" name="csrf-param" />') )
$('head').append( $(`<meta content="${response.data.csrf_token}" name="csrf-token" />`) )
}
}).catch((error) => {
console.log(error)
})
// 如果是编辑
const { match } = this.props
const memoId = match.params.memoId;
if (memoId) {
const memoUrl = `/memos/${match.params.memoId}/edit`;
axios.get(memoUrl).then((response) => {
const current_user = response.data.current_user;
if (current_user) {
const { content, forum_section, id, subject , attachments_url, memo_image_info,
memo_type, published_at , children_forum_section} = response.data;
let date;
let date_value;
this.initMD(content, memoId);
if(published_at){
date = new Date(published_at);
date_value=date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate() + ' ' + date.getHours() + ':' + date.getMinutes() + ':' + date.getSeconds();
}
const newState = {
currentMemoId: id,
attachments_url,
memoSubject: subject,
memo_tag: memo_type,
memoLanguage: forum_section.forum_id,
memoTime:date_value,
content,
bigPlateId: forum_section.forum_id,
smallPlateId: children_forum_section.children_forum_id
}
// 编辑,初始化小板块的下拉选项(如果大板块有值)
this.getChildPlate(memo_type,forum_section && forum_section.forum_id);
if (memo_image_info && memo_image_info.id) {
newState.fileList = [{
uid: memo_image_info.id,
name: memo_image_info.filename,
status: 'done',
url: memo_image_info.url,
}]
newState.attachmentData = { // 初始化
name: memo_image_info.filename,
response: {
attachment_id: memo_image_info.id,
}
}
}
this.setState({...newState})
// 加载完后滚动条滚动
window.$("html,body").animate({"scrollTop":0})
// this.props.initForumState({
// current_user,
// tag_list
// })
}
}).catch((error) => {
console.log(error)
})
} else {
this.initMD();
}
}
initMD(initValue, memoId) {
this.contentChanged = false;
const placeholder = "";
// amp;
// 编辑时要传memoId
const imageUrl = `/upload_with_markdown?container_id=${memoId?memoId:''}&container_type=Memo`;
// 创建editorMd
setTimeout(()=>{
const taskpass_editormd = create_editorMD("memoMD", '100%', 400, placeholder, imageUrl, () => {
setTimeout(()=>{
taskpass_editormd.resize()
taskpass_editormd.cm && taskpass_editormd.cm.refresh()
}, 500)
if (initValue) {
taskpass_editormd.setValue(initValue)
}
taskpass_editormd.cm.on("change", (_cm, changeObj) =>{
console.log('....contentChanged')
this.contentChanged = true;
})
});
this.taskpass_editormd = taskpass_editormd;
window.taskpass_editormd = taskpass_editormd;
}, 300)
}
renderOptions(array) {
const elementArray = [];
array.forEach(( item, index ) => {
elementArray.push(
<Option key={index} value={item}>{item}</Option>
)
})
return elementArray
}
onRepertoiresChange(value) {
const index = this.state.repertoires.indexOf(value)
this.setState({
currentSelectRepertoiresIndex: index,
memoRepertoire: value,
memoLanguage: []
});
};
onTagChange(value) {
if (value && value.length > 3) {
this.props.showSnackbar(`最多选择3个技术标签`)
return;
}
this.setState({
memoLanguage: value
})
}
onTypeChange(value) {
this.setState({
memoType: value
})
}
onMemoNameChange(e) {
this.setState({
memoSubject: e.target.value
})
}
renderMemoType() {
const { memo_type } = this.state;
if (!memo_type || memo_type.length === 0) {
return ''
}
const result = []
// memo_type.map((item, index) => <Option value={item.id} key={index} >{item.name}</Option> )
memo_type.forEach((item, index) => {
result.push(<Option value={item.id} key={index} >{item.name}</Option>)
})
return result;
}
renderTag() {
let { memo_tag } = this.state;
if (!memo_tag || memo_tag.length === 0) {
return ''
}
const result = []
memo_tag.forEach((item, index) => {
result.push(<Option value={item.id+''} key={index} >{item.name}</Option>)
})
return result;
}
renderAttachment() {
const { attachments_url } = this.state;
const attachments = []
attachments_url.forEach((item, index) => {
const ar = item.url.split('/')
attachments.push(
<React.Fragment>
<span id={`attachments_10${index}`} className="attachment">
<i className="fa fa-folder mr5 color-light-grey newLeftgrey" aria-hidden="true"></i>
<input type="text" className="upload_filename readonly hidden" name="attachments[2][filename]" readonly="readonly"
style={{border:'none', width:'220px',whiteSpace: 'nowrap', textOverflow:'ellipsis',fontFamily: 'Consolas'}}
size="8" value={item.filename}></input>
<a href={`/attachments/${item.id}.js?attachment_id=10${index}`} className="remove-upload"
style={{verticalAlign: 'top', display: 'inlineBlock'}} data-remote="true"
data-method="delete">
<i className="fa fa-trash-o mr5"></i>
</a>
<div className="div_attachments" name="div_attachments_xx"></div>
<input type="hidden" name="attachments[xx][token]" value="185811.24305bb2c4912f715629aa3615cdbabc"></input>
<input type="hidden" name="attachments[xx][attachment_id]" value={item.id}></input>
</span>
<div className="cl"></div>
</React.Fragment>
)
})
return attachments;
}
_findById(id, arg_items) {
if (!arg_items) {
return undefined
}
const items = arg_items;
for(let i = 0; i < items.length; i++) {
if (id === items[i].id) {
return i;
}
}
}
// 是否是技术动态吧
_isTechShare() {
const { memoType, memo_type } = this.state
const index = this._findById(memoType, memo_type)
return !!index && memo_type[index] && memo_type[index].name.indexOf('技术问答') !== -1
}
onImageUploadDone = (data) => {
this.setState({
attachmentData: data
})
}
onImageUploadRemove = () => {
const { attachment_id } = this.state.attachmentData.response;
const deleteUrl = `/attachments/${attachment_id}.js?attachment_id=1`;
// 获取memo list
axios.delete(deleteUrl, {
withCredentials: true,
})
.then((response) => {
const data = response.data
if (data) {
this.setState({
attachmentData: undefined
})
this.props.showSnackbar('删除成功');
}
}).catch((error) => {
console.log(error)
})
}
// 切换大板块
changeLargeOption=(value)=>{
const { memo_tag } = this.state;
this.getChildPlate(memo_tag,value);
}
getChildPlate =(memo_tag,value)=>{
let listFilter = memo_tag && memo_tag.length > 0 && memo_tag.filter((item=> item.id === value))[0];
let list = listFilter && listFilter.children_tags;
this.setState({
smallOption:list && list.map(item=>{
return(
<Option value={item.id}>{item.title}</Option>
)
}),
bigPlateId:value,
smallPlateId: list && list.length > 0 ? (list[0].id || undefined) : undefined
})
}
// 切换二级板块
changeSmallOption=(value)=>{
this.setState({
smallPlateId:value
})
}
render() {
const { match } = this.props
const {
memoSubject ,
attachments_url,
currentMemoId,
fileList,
memoSubjectLength ,
memo_tag,
smallOption,
bigPlateId,
smallPlateId
} = this.state;
const memoId = match.params.memoId;
console.log("bigPlateId",bigPlateId);
return (
<div className="educontent-min postNewform">
<p className="font-22 color-grey3 mt30 mb10">{ memoId ? '编辑' : '新建'}</p>
<div className="edu-back-white mb10 clearfix" id="memoSubject" style={{position:'relative'}}>
<div className="padding30">
<div className="df">
<span className="mr20 new_label"><span className="color-orange">*</span></span>
<div className="flex1">
<input type="text" className="input-100-45" maxlength="50"
value={memoSubject} onChange={(val)=>this.onMemoNameChange(val)} placeholder="请输入发布标题最大限制50字符">
</input>
</div>
<div style={{width: '57px'}}>
<span className="color-orange mt8 fl" style={memoSubjectLength==0?{display:'block'}:{display:'none'}}>
<i className="fa fa-exclamation-circle mr3"></i>
</span>
</div>
</div>
{/* 发布时间 */}
{/* <div className="df ecSelectbox">
<span className="mr30 color-orange pt10"></span>
<div className="flex1 mr20">
<DatePicker
disabledDate={disabledDate}
showTime
format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择发布时间"
locale={zhCN}
placeholder={"请选择时间"}
onChange={(value, dateString)=>this.onOkTime(value, dateString)}
/>
</div>
<div style={{width: '57px'}}>
<span className="color-orange mt8 fl none" >
<i className="fa fa-exclamation-circle mr3"></i>
</span>
</div>
</div> */}
{/* <p className="color-grey-6 font-16 mb30">内容</p> */}
<div className="df mt20">
<span className="mr20 new_label"><span className="color-orange">*</span></span>
<div className="flex1">
<div className="break_word new_li" id="memoMD">
<textarea style={{'display':'none'}}></textarea>
</div>
</div>
<div style={{width: '57px'}}>
<span className="color-orange mt8 fl none" >
<i className="fa fa-exclamation-circle mr3"></i>
</span>
</div>
</div>
<form className="newForm" style={{marginLeft:"70px"}}>
<span id={`attachments_fields`} className="attachments_fields newforgeattachment" xmlns="http://www.w3.org/1999/html">
{ attachments_url && !!attachments_url.length &&
this.renderAttachment()
}
</span>
<span className="add_attachment">
<input className="file_selector" data-are-you-sure="您确定要删除吗?"
data-delete-all-files="您确定要删除所有文件吗" data-description-placeholder="可选的描述"
data-field-is-public="公开" data-file-count="个文件已上传"
data-lebel-file-uploding="个文件正在上传" data-max-concurrent-uploads="2"
data-max-file-size-message="该文件无法上传。超过文件大小限制 (50 MB)建议上传到百度云等其他共享工具里然后在txt文档里给出链接以及共享密码并上传"
data-max-file-size="52428800" data-upload-path="/uploads.js" id="_file"
multiple="multiple" name="attachments[dummy][file]"
onChange={()=>{debugger;window.addInputFiles( window.$('.file_selector')[0] ) }}
style={{'display':'none'}} type="file">
</input>
</span>
</form>
{/* 请求status 422 X-CSRF-Token: eVo38laEF880o3cwZ/0F9kH01q4jMkriuVRemIBq06Y= */}
<div className="df uploadBtn uploadBtnclick mb20" >
<a className="fl" onClick={()=>window.$('#_file').click()}>
<span style={{color: '#21B351', fontSize: "14px", marginLeft: "62px"}}>上传附件</span>
<span style={{color: '#CDCDCD', fontSize: "14px"}}>(单个文件50M以内)</span>
</a>
</div>
{/* 选择了技术问答才显示树标签 */}
{/* this._isTechShare() &&
<div className="edu-back-white mb10 clearfix">
<div className="padding30-20">
<p className="color-grey-6 font-16 mb30">技术标签</p>
<div className="df">
<span className="mr30 color-orange pt10">*</span>
<div className="flex1 mr20">
<Select
className="ecSelect"
value={memoLanguage}
placeholder="请选择技术标签"
onChange={(e) => this.onTagChange(e)}
dropdownStyle={{'maxHeight': '500px', 'overflow': 'auto'}}
mode="multiple"
tokenSeparators={[';']} >
{this.renderTag()}
</Select>
</div>
<div style={{width: '57px'}}>
<span className="color-orange mt8 fl none" >
<i className="fa fa-exclamation-circle mr3"></i>
</span>
</div>
</div>
</div>
</div> */}
<div className="df ecSelectbox mb20">
<span className="mr20 new_label"><span className="color-orange">*</span></span>
<div className="flex1">
{/* <Select className="ecSelect"
value={memoType}
placeholder="请选择技术标签"
onChange={(val)=>this.onTypeChange(val)}>
{ this.renderMemoType()}
</Select> */}
{/* <Select
mode="multiple"
className="ecSelect"
value={memoLanguage}
placeholder="请选择技术标签"
onChange={(e) => this.onTagChange(e)}
dropdownStyle={{'maxHeight': '500px', 'overflow': 'auto'}}
>
{this.renderTag()}
</Select> */}
<Select className="selectItem" value={bigPlateId} onChange={this.changeLargeOption}>
{
memo_tag && memo_tag.length > 0 && memo_tag.map((item,key)=>{
return(
<Option value={item.id} key={key}>{item.name}</Option>
)
})
}
</Select>
<Select className="selectItem" value={smallPlateId} onChange={this.changeSmallOption}>
{ smallOption }
</Select>
</div>
<div style={{width: '57px'}}>
<span className="color-orange mt8 fl none" >
<i className="fa fa-exclamation-circle mr3"></i>
</span>
</div>
</div>
{/* */}
<div className="df">
<span className="mr20 new_label">上传封面</span>
<div className="flex1 mr20">
<ImageUpload
fileList={fileList}
currentMemoId={currentMemoId}
onImageUploadRemove={this.onImageUploadRemove}
onImageUploadDone={this.onImageUploadDone}
></ImageUpload>
</div>
<div style={{width: '57px'}}>
<span className="color-orange mt8 fl none" >
<i className="fa fa-exclamation-circle mr3"></i>
</span>
</div>
</div>
</div>
</div>
<div className="clearfix mt30 mb50 edu-txt-center">
<span className="inline">
<a onClick={()=>{ this.onCancel() }} className="defalutCancelbtn mr20 fl newdefalutCancelbtn">返回</a>
<a className="defalutSubmitbtn fl newdefalutSubmitbtn" onClick={()=>{this.onCommit()}}>提交</a>
</span>
</div>
</div>
);
}
}
const children = [];
for (let i = 10; i < 36; i++) {
children.push(<Option key={i.toString(36) + i}>{i.toString(36) + i}</Option>);
}
export default MemoNew;

277
src/exchange/Post/New.js Normal file
View File

@ -0,0 +1,277 @@
import React, { PureComponent } from 'react';
import {appendFileSizeToUploadFileAll ,appendFileSizeToUploadFile} from 'educoder';
import { Form , Input , Upload ,Button , Select , Icon , message } from "antd";
import './post.css'
import '../exchange.css'
import axios from 'axios';
import MDEditor from '../../common/MDEditor';
const Option = Select.Option;
const uploadButton = (
<div>
<i className="iconfont icon-tianjia mb30"></i>
<div className="font-16 mb10">点击上传封面</div>
<p className="font-12">只支持JPGPNGJPEG大小不超过2M建议尺寸43</p>
</div>
)
function getBase64(img, callback) {
debugger;
const reader = new FileReader();
reader.addEventListener('load', () => callback(reader.result));
reader.readAsDataURL(img);
}
class New extends PureComponent{
constructor(props){
super(props);
this.contentMdRef = React.createRef();
this.state={
// 封面图
imageUrl:undefined,
// 大板块下拉选项
memo_type:undefined,
// 小板块
smallOption:undefined,
// 附件列表
contentFileList:undefined,
}
}
componentDidMount = () =>{
this.getInfo();
}
// 获取信息
getInfo = () =>{
let url = '/memos/new';
axios.get((url)).then((result)=>{
if(result){
this.setState({
memo_type:result.data.memo_type
})
}
}).catch((error)=>{
})
}
changeUploadImage =(info)=>{
if (info.file.status === 'done') {
// Get this url from response in real world.
getBase64(info.file.originFileObj, imageUrl =>
this.setState({
imageUrl
}),
);
}
}
// 切换大板块
changeLargeOption=(value)=>{
// console.log(value);
const { memo_type } = this.state;
let list = memo_type.filter((item=> item.id === value))[0].children_tags;
// console.log(list);
this.setState({
smallOption:list && list.map(item=>{
return(
<Option value={item.id}>{item.title}</Option>
)
})
})
}
// 提交
handleSubmit=()=>{
this.props.form.validateFieldsAndScroll((err, values) => {
console.log(values);
if(!err){
let url=`/memos.json`;
axios.post((url),{
forum_id:values.bigPlate,
children_forum_id:values.smallPlate,
memo:{
subject:values.title,
content:values.description
}
}).then(result=>{
if(result){
this.props.showNotification(result.data.message);
this.props.history.push(`/forums/${result.data && result.data.memo_id}`);
}
}).catch(error=>{
})
}
})
}
// 上传附件
handleContentUploadChange = (info) => {
console.log("changeFIle==================>",info);
if (info.file.status === 'done' || info.file.status === 'uploading' || info.file.status === 'removed') {
let contentFileList = info.fileList;
this.setState({ contentFileList: appendFileSizeToUploadFileAll(contentFileList)});
let list = appendFileSizeToUploadFileAll(contentFileList);
let arr = list.map(item=>{
return ( item.response && item.response.id )
})
this.setState({
filesID:arr,
checkFile:arr.length > 0 ? false : true
})
}
}
// 上传附件-删除确认框
onAttachmentRemove = (file, stateName) => {
if(!file.percent || file.percent == 100){
this.props.confirm({
content: '是否确认删除?',
onOk: () => {
this.deleteAttachment(file, stateName)
},
onCancel() {
console.log('Cancel');
},
});
return false;
}
}
deleteAttachment=(file,list)=>{
console.log("delete===============>",file)
}
render(){
const { getFieldDecorator } = this.props.form;
/** 上传封面图 */
let { imageUrl , memo_type , smallOption , contentFileList } = this.state;
const largeOption = memo_type && memo_type.length > 0 && memo_type.map(item=>{
return(
<Option value={item.id}>{item.name}</Option>
)
})
let uploadImg = ( imageUrl ? <img src={imageUrl} alt="avatar" style={{ width: '100%' }} /> : uploadButton )
// 上传封面图
const uploadCover = {
listType:"picture-card",
className:"avatar-uploader",
showUploadList:false,
action:`/uploads.js`,
data: { attachment_id: 1 },
onChange:this.changeUploadImage,
}
/**上传附件 */
const uploadProps = {
width: 600,
fileList: contentFileList,
multiple: true,
action: 'http://127.0.0.1:3000/uploads.js',
onChange: this.handleContentUploadChange,
onRemove: (file) => this.onAttachmentRemove(file, 'contentFileList'),
beforeUpload: (file) => {
console.log('beforeUpload', file.name);
const isLt150M = file.size / 1024 / 1024 < 150;
if (!isLt150M) {
this.props.showNotification('文件大小必须小于150MB!');
}
return isLt150M;
},
};
return(
<div className="educontent-min">
<p className="font-22 color-grey3 mt30 mb10">新建</p>
<div className="bc-white padding30">
<Form className="postNewform">
<Form.Item
label="标题"
>
{getFieldDecorator('title', {
rules: [{
required: true, message: '请输入帖子标题',
},{
max: 5000 , message:'最大限制60个字符'
}],
})(
<Input placeholder="请输入标题最大限制60个字符" maxLength="60" style={{height:"40px"}}/>
)}
</Form.Item>
<Form.Item label="内容" className="editorFromItem">
{getFieldDecorator('description', {
rules: [{
required: true, message: '请输入帖子内容',
},{
max: 5000 , message:'最大限制5000个字符'
}],
})(
<MDEditor ref={this.contentMdRef} placeholder="请输入内容最大限制5000字符" mdID={'courseContentMD'} refreshTimeout={1000}
initValue={""} className="courseMessageMD" ></MDEditor>
)}
</Form.Item>
<Upload {...uploadProps} className="upload_1 newPostUpload">
<Button className="uploadBtn">
<i className="iconfont icon-fabu font-22 color-blue fl mr10"></i>
</Button>
</Upload>
<div className="df mt10">
<Form.Item label="主题板块">
{
getFieldDecorator('bigPlate',{
rules:[{
required:true,message:'请选择大板块'
}]
})(
<Select className="selectItem" onChange={this.changeLargeOption}>
{ largeOption }
</Select>
)
}
</Form.Item>
<Form.Item label="">
{
getFieldDecorator('smallPlate',{
rules:[{
required:true,message:'请选择小板块'
}]
})(
<Select className="selectItem">
{ smallOption }
</Select>
)
}
</Form.Item>
</div>
<div className="df uploadImageBox">
<label className="new_label">上传封面</label>
<Upload {...uploadCover}>
{uploadImg}
</Upload>
</div>
</Form>
</div>
<p className="clearfix mt20 mb50 edu-txt-center">
<span className="inline">
<a className="defalutCancelbtn fl mr20">取消</a>
<Button type="primary" onClick={this.handleSubmit} className="defalutSubmitbtn fl mr20">提交</Button>
</span>
</p>
</div>
)
}
}
const WrappedNewPostForm = Form.create({ name: 'New' })(New);
export default WrappedNewPostForm;

View File

@ -0,0 +1,33 @@
import React, { PureComponent } from 'react';
class Parse extends PureComponent{
render(){
let { judge , num , clickEvent ,current_user} = this.props;
const info = (
current_user? (judge ?
<a className={`forumParse`} onClick={clickEvent}>
<i className="iconfont icon-dianzan font-24" style={{height:"30px",lineHeight:"40px"}}></i>
<span>{num}</span>
</a>
:
<span className={`forumParse parsed`} >
<i className="iconfont icon-dianzan font-24" style={{height:"30px",lineHeight:"40px"}}></i>
<span>{num}</span>
</span>
):
<a className={`forumParse`} href="/login">
<i className="iconfont icon-dianzan font-24" style={{height:"30px",lineHeight:"40px"}}></i>
<span>{num}</span>
</a>
)
return(
<React.Fragment>
{ info }
</React.Fragment>
)
}
}
export default Parse;

157
src/exchange/Post/post.css Normal file
View File

@ -0,0 +1,157 @@
.postDetail{
padding:20px 0px;
}
.postContent{
margin:0px 30px;
border-top: 1px solid #f4f4f4;
}
.fileTeam{
padding-bottom: 40px;
}
.fileTeam > li {
height: 18px;
line-height: 18px;
margin-bottom: 10px;
}
/* parse.js */
.forumParse{
width: 72px;
height: 72px;
display: inline-flex;
align-items: center;
flex-flow: column;
background: #5091FF;
color: #fff!important;
border-radius: 50%;
padding-top: 7px;
}
.forumParse.parsed{
background:#999999;
}
/* 评论 */
.replyTitle{
line-height: 30px;
padding:20px;
border-bottom: 1px solid #f4f4f4;
}
.pre_stage{
border-bottom: 1px solid #f4f4f4;
padding : 10px 0px;
}
.pre_stage:last-child{
border-bottom: none;
}
.pre_stage .sub_stage{
padding: 10px 20px;
box-sizing: border-box;
background: #fafafa;
position: relative;
margin-top: 20px;
border-radius:4px;
}
.pre_stage .sub_stage::before{
position: absolute;
content: "";
top:-20px;
z-index: 1;
left: 20px;
width: 0;
height: 0;
border-width: 10px;
border-style: solid;
border-color: transparent transparent #fafafa transparent;
}
.commentsItem_infos{
display: flex;
padding:10px 0px 10px;
}
.commentsItem_infos .markdown-body{
padding:0px;
}
.commentsItem_infos .editormd-html-preview,.commentsItem_infos .editormd-preview-container{
background-color: unset;
}
/* 新建 */
.postNewform.ant-form .ant-row{
display: flex;
align-items: top;
}
.postNewform.ant-form .ant-form-item-control-wrapper{
flex: 1;
}
.postNewform.ant-form .ant-form-item-label,.postNewform .new_label{
width: 90px;
text-align: right;
margin-right: 8px;
height: 40px;
line-height: 40px;
margin-top: 3px;
font-size: 16px
}
.postNewform.ant-form label{
font-size: 16px;
color: #333;
}
.editorFromItem {
align-items: flex-start!important;
margin-bottom: 0px!important;
}
.editorFromItem .editormd.editormd-vertical{
margin-bottom: 0px;
}
.editorFromItem .rememberTip{
height: 20px;
line-height: 20px;
}
.newPostUpload{
margin-left: 84px;
}
.newPostUpload .uploadBtn{
border:none;
box-shadow: none;
color:#5091FF;
line-height: 30px;
}
.newPostUpload .ant-upload-list{
padding-left: 100px;
}
.newPostUpload .ant-upload-list-item-info{
padding:0px;
font-size: 15px;
height: 24px;
line-height: 24px;
max-width: 500px;
position: relative;
}
.selectItem .ant-select-selection--single{
width: 200px;
margin-right: 20px;
height: 40px;
}
.selectItem .ant-select-selection__rendered{
line-height: 40px;
}
/* 上传封面 */
.uploadImageBox .ant-upload-picture-card-wrapper{
width: 340px;
height: 248px;
border:none;
background: #fafafa;
display: flex;
flex-direction: column;
padding:0px 60px;
color: #999;
}
.uploadImageBox .ant-upload.ant-upload-select-picture-card{
width: 100%;
height: 100%;
border: none;
}
.uploadImageBox .ant-upload.ant-upload-select-picture-card i{
font-size: 44px!important;
color:#999;
}

View File

@ -0,0 +1,16 @@
import React, { PureComponent } from 'react';
class Index extends PureComponent {
render(){
const {count, returnEvent} = this.props;
return(
<p className="clearfix pl30 pr30 mb10">
<span className="fl color-grey3 font-16">共找到相关结果<span className="color-blue">{count}</span></span>
<span onClick={returnEvent} className="color-grey-6 fr mt2 c_point">返回</span>
</p>
)
}
}
export default Index;

View File

@ -0,0 +1,18 @@
import React, { PureComponent } from 'react';
import { getImageUrl } from 'educoder';
class Index extends PureComponent {
render(){
let { topClass , bestClass } = this.props;
return(
<React.Fragment>
{ topClass && <img alt="" src={getImageUrl("images/plate/top.png")} width="42px" className={ topClass }></img> }
{ bestClass && <img alt="" src={getImageUrl("images/plate/best.png")} width="28px" className={ bestClass }></img> }
</React.Fragment>
)
}
}
export default Index;

289
src/exchange/exchange.css Normal file
View File

@ -0,0 +1,289 @@
.newContainer{
padding:60px 0px 120px 0px;
}
ul,p{
margin: 0px;
}
.flex1{
flex: 1;
width: 0
}
.ant-tabs-nav .ant-tabs-tab-active,.ant-tabs-nav .ant-tabs-tab:hover{
color:#21B350!important;
}
/* shownotification的z-index */
.ant-notification{
z-index: 100000;
}
/* 小按钮 */
.small-default-btn{
height: 24px;
line-height: 24px;
padding: 0px 15px;
font-size: 12px;
background:rgba(244,244,244,1);
border-radius:2px;
color: #999;
display: inline-block;
}
.middle-default-btn{
height: 30px;
line-height: 30px;
padding: 0px 15px;
font-size: 12px;
background:rgba(244,244,244,1);
border-radius:2px;
color: #999;
display: inline-block;
}
.small-blue-btn{
background:rgba(80,145,255,1);
color:rgba(255,255,255,1);
}
.small-green-btn{
background:#21B350;
color:rgba(255,255,255,1)!important;
}
.educontent{
width: 1200px;
margin:0px auto;
}
.educontent-min{
width: 960px;
margin:0px auto;
}
.F_panel{
width: 1200px;
display: flex;
margin:30px auto;
}
.f_left_head{
display: flex;
justify-content:space-between;
border-bottom: 1px solid #eaeaea;
padding-left:30px;
background: #fff;
}
.f_left_head > ul{
display: flex;
align-items: center;
height:80px;
margin-bottom: 0px;
}
.f_left_head > ul >li{
margin-right: 30px;
color: #666666
}
.f_left_head > ul >li a{
color: #666666
}
.f_left_head > ul:first-child > li{
font-size: 16px;
position: relative;
color: #333;
}
.f_left_head > ul:first-child > li.active a{
color: #5091FF;
}
.f_left_head > ul:first-child > li.active a:after{
position: absolute;
width: 100%;
bottom: -25px;
height: 2px;
background: #5091FF;
content: '';
left: 0px;
}
.platePanel{
top: 24px;
background: #fff;
left: 0px;
box-shadow:0px 6px 16px 0px rgba(7,12,70,0.2);
border-radius:4px;
max-width: 770px;
max-height: 500px;
padding:10px 10px 0px 10px;
overflow: auto;
box-sizing: border-box;
}
.platePanel .plateItem{
display: flex;
padding-bottom:10px;
box-sizing: border-box;
}
.platePanel .plateItem .plateItem_h{
color: #333;
margin:5px 20px 0px 10px;
}
.plateUl{
flex: 1;
}
.plateUl li{
position: relative;
margin-right: 11px;
color: #666;
float: left;
padding:5px 0px;
box-sizing: border-box;
margin-left: 10px;
}
.plateUl li:after{
width: 1px;
position: absolute;
right: -12px;
height:10px;
background: #ccc;
top:11px;
content: ''
}
.plateUl li:last-child:after{
display:none;
}
.radius{
border-radius: 50%;
}
.flex1{
flex: 1;
}
.color-blue {
color: #5091FF!important;
}
.flex-align-center{
display: flex;
align-items: center;
}
.flex-align-top{
display: flex;
align-items: flex-start;
}
.flex-align-bottom{
display: flex;
align-items: flex-end;
}
/* 列表 */
.plateTabulation{
padding: 0px 30px;
box-sizing: border-box;
background: #fff;
}
.plateTabulation > li{
border-bottom: 1px solid #f4f4f4;
padding:20px 0px;
}
.plateTabulation > li:last-child{
border-bottom: none;
}
.exchangeItem-subject{
font-size: 16px;
color: #333;
flex:1;
height: 20px;
line-height: 20px;
margin-right: 20px;
}
.flex_h{
display: flex;
align-items: center
}
.sendPoint{
font-size: 12px;
color: #999;
position: relative;
margin-left: 20px;
height: 18px;
line-height: 18px;
}
.sendPoint::before{
position: absolute;
left: -10px;
top:3px;
height:12px;
width:1px ;
content: '';
background: #ccc;
}
p{
margin:0px;padding:0px
}
.edu-txt-center .ant-dropdown-menu-item,.edu-txt-center .ant-dropdown-menu-submenu-title{
text-align: center;
}
.c_point{
cursor: pointer;
}
.top_Operate{
position: relative;
background: #fff;
padding:20px;
margin-bottom: 20px;
}
.top_Operate .send_btn{
width: 188px;
height: 40px;
background: #5091ff;
text-align: center;
color: #fff;
font-size: 16px;
line-height: 40px;
display: block;
border-radius: 4px;
}
.top_Operate .searchfrom{
position: absolute;
width: 40px;
right:20px;
top:20px;
}
.top_Operate .searchfrom.ant-input-affix-wrapper .ant-input-suffix{
right:-2px
}
.top_Operate .searchfrom .ant-input-search-icon svg{
width: 3em;
height: 1.3em;
}
.top_Operate .searchfrom.active{
width: 250px;
z-index: 10;
}
.r_part_title{
border-bottom: 1px solid #F4F4F4;
padding:20px;
}
.r_part_list{
padding:20px;
}
.r_part_list li{
margin-bottom: 20px;
color: #333;
}
.r_part_list li:last-child{
margin-bottom: 0px;
}
/* InfoComponent */
.icon-wrap{
margin-left: 30px;
color: #ccc;
float: left;
line-height: 20px;
}
.icon-wrap > i{
margin-right: 5px;
float: left;
}
/*給下拉列表的……添加高宽*/
.addheight{
background: url(./images/more.png) no-repeat right;
width: 30px;
height: 20px;
}
/*帖子列表标题点击变颜色*/
.is_onclick:hover{
color: #5091FF;
}
/*帖子详情标题颜色不变*/
.color_black{
color: black;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

98
src/exchange/index.js Normal file
View File

@ -0,0 +1,98 @@
import React, { Component } from 'react';
import { Route , Switch } from 'react-router-dom';
import { TPMIndexHOC } from '../modules/tpm/TPMIndexHOC';
import { SnackbarHOC , CNotificationHOC } from 'educoder';
import ExchangeIndex from './ExchangeIndex';
import Post from './Post/Index'
import PostNew from './Post/MemoNew';
import PlateIndex from './Plate/Index';
import MyExchange from './MyExchange/Index'
import PreManager from './Manage/PreModerator'
import axios from 'axios';
import './exchange.css';
class Index extends Component{
constructor(props){
super(props);
this.state={
current_user:undefined
}
}
componentDidMount=()=>{
const url = `/users/get_user_info.json`;
axios.get(url).then(result=>{
if(result){
this.setState({
current_user:result.data.current_user
})
// 给NewHeader传值当前用户的信息
if(result.data.current_user && result.data.current_user.login){
this.props.initCommonState(result.data.current_user);
}
}
}).catch(error=>{
})
}
componentDidUpdate=()=>{
this.props.history.listen(()=>{
if (document.body.scrollTop || document.documentElement.scrollTop > 0) {
window.scrollTo(0, 0)
}
})
}
render(){
return(
<Switch {...this.props}>
{/* 帖子列表 */}
<Route path='/forums/plates/:plateid/all' render={
(props) => (<PlateIndex {...this.props} {...this.state} {...props} />)
} ></Route>
{/* 编辑帖子 */}
<Route path={`/forums/:memoId/edit`} render={
(props) => {
return (<PostNew {...this.props} {...this.state} {...props}></PostNew>)}
}></Route>
{/* 新建帖子 */}
<Route path={`/forums/new`} render={
(props) => {
return (<PostNew {...this.props} {...this.state} {...props}></PostNew>)}
}></Route>
{/* 版主管理 */}
<Route path={`/forums/manage/:plateId`} render={
(props) => {
return (<PreManager {...this.props} {...this.state} {...props}></PreManager>)}
}></Route>
{/* 我的话题 */}
<Route path={`/forums/MyTopic`} render={
(props) => {
return (<MyExchange {...this.props} {...this.state} {...props}></MyExchange>)}
}></Route>
<Route path={`/forums/MyEnshrine`} render={
(props) => {
return (<MyExchange {...this.props} {...this.state} {...props}></MyExchange>)}
}></Route>
<Route path={`/forums/MyInteresting`} render={
(props) => {
return (<MyExchange {...this.props} {...this.state} {...props}></MyExchange>)}
}></Route>
{/* 帖子详情 */}
<Route path={`/forums/:postid`} render={
(props) => {
return (<Post {...this.props} {...this.state} {...props}></Post>)}
}></Route>
{/* 论坛首页 */}
<Route path={`/forums`} render={
(props) => (<ExchangeIndex {...this.props} {...this.state} {...props} />)
}></Route>
</Switch>
)
}
}
export default SnackbarHOC() (CNotificationHOC() (TPMIndexHOC ( Index ))) ;

View File

@ -67,17 +67,18 @@ function Index(props){
}
return(
<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>
{ editOpration && !edit && <a onClick={editContent} className="color-blue">编辑</a> }
<AlignCenterBetween style={{padding:"14px 0px"}}>
<span className="font-16"><i className="iconfont icon-xiangmujianjie mr5 font-16 color-blue"></i>项目简介</span>
{ editOpration && <a onClick={editContent} className="color-blue">编辑</a> }
</AlignCenterBetween>
{
edit ?
<div className="padding20">
<div>
<MDEditor
placeholder={"请输入描述信息"}
height={500}
height={200}
mdID={"order-new-description"}
initValue={content}
onChange={onContentChange}
@ -113,11 +114,11 @@ function Index(props){
</div>
</div>
:
<div className="padding20">
<div style={{padding:"20px 0px"}}>
{content ?
<RenderHtml className="break_word_comments imageLayerParent" value={content} url={props.history.location}/>
<RenderHtml className="break_word_comments imageLayerParent" value={content} />
:
<div>暂无概览~</div>
<div>暂无简介~</div>
}
{attachments && attachments.length > 0 &&
<Attachments

View File

@ -4,6 +4,7 @@
.aboutContent{
border-radius: 2px;
border: 1px solid #EEEEEE;
padding:0px 30px;
width:100%;
background-color: #fff;
margin-top:20px;

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