From 6d8834403a90bed762f4a78861524d3eac937ce7 Mon Sep 17 00:00:00 2001
From: caishi <1149225589@qq.com>
Date: Mon, 12 Oct 2020 17:48:42 +0800
Subject: [PATCH] =?UTF-8?q?=E9=97=AE=E5=90=A7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
package-lock.json | 39 ++
package.json | 1 +
public/index.html | 18 +-
src/App.js | 23 +-
src/forge/Upload/Cover.js | 70 ++++
src/forums/Component/Announcement.jsx | 130 +++++++
src/forums/Component/AuthorCard.jsx | 51 +++
src/forums/Component/AuthorItem.jsx | 23 ++
src/forums/Component/BestItem.jsx | 27 ++
src/forums/Component/BestModalItem.jsx | 54 +++
src/forums/Component/DoubleItem.jsx | 66 ++++
src/forums/Component/ItemDropDown.jsx | 114 ++++++
src/forums/Component/ItemType.jsx | 19 +
src/forums/Component/ListItem.jsx | 89 +++++
src/forums/Component/ListSearch.jsx | 23 ++
src/forums/Component/Paginations.jsx | 17 +
src/forums/Component/Prise.jsx | 45 +++
src/forums/Component/Title.jsx | 19 +
src/forums/Component/Top.jsx | 152 ++++++++
src/forums/Component/UrlItem.jsx | 42 +++
src/forums/Component/User.jsx | 24 ++
src/forums/Component/UserInfo.jsx | 29 ++
src/forums/Forums.js | 197 ++++++++++
src/forums/ForumsDetail.jsx | 248 ++++++++++++
src/forums/Index.jsx | 58 +++
src/forums/New.jsx | 374 +++++++++++++++++++
src/forums/Nodata.js | 18 +
src/forums/Theme.js | 156 ++++++++
src/forums/ThemeRight.jsx | 52 +++
src/forums/css/All.scss | 285 ++++++++++++++
src/forums/css/Index.css | 84 +++++
src/forums/css/Theme.scss | 25 ++
src/forums/css/layout.jsx | 207 ++++++++++
src/forums/image/nodata.png | Bin 0 -> 5877 bytes
src/forums/image/original.png | Bin 0 -> 6629 bytes
src/forums/image/radius.png | Bin 0 -> 1623 bytes
src/forums/new_comments/Title.jsx | 19 +
src/forums/new_comments/children_journals.js | 26 ++
src/forums/new_comments/comment.scss | 59 +++
src/forums/new_comments/comment_list.js | 241 ++++++++++++
src/forums/new_comments/comments.js | 146 ++++++++
src/forums/new_comments/head_reply_button.js | 19 +
src/forums/new_comments/new_comment.js | 67 ++++
src/modules/tpm/TPMIndex.css | 2 +-
src/user_info/Index.jsx | 84 +++++
src/user_info/Memo/Memos.js | 101 +++++
src/user_info/Memo/Replies.js | 37 ++
src/user_info/Memo/ReplyItem.js | 10 +
src/user_info/Memo/SectionItem.js | 62 +++
src/user_info/Memo/Sections.js | 70 ++++
src/user_info/Memo/SelectTitle.js | 119 ++++++
src/user_info/Memo/select_title.scss | 27 ++
src/user_info/Modal/modal.scss | 18 +
src/user_info/Modal/tip_modal.js | 61 +++
src/user_info/User/Account.js | 50 +++
src/user_info/User/BlockItem.js | 36 ++
src/user_info/User/BlockUsers.js | 72 ++++
src/user_info/User/Blocks.js | 0
src/user_info/User/HeadUser.js | 68 ++++
src/user_info/User/Menu.js | 46 +++
src/user_info/User/ProjectItem.js | 53 +++
src/user_info/User/Projects.js | 118 ++++++
src/user_info/User/StarUser.js | 169 +++++++++
src/user_info/User/UserAvatar.js | 106 ++++++
src/user_info/User/UserBrief.js | 81 ++++
src/user_info/User/UserIdentify.js | 109 ++++++
src/user_info/User/menu.scss | 45 +++
src/user_info/User/user.scss | 192 ++++++++++
src/user_info/css/head_user.jsx | 0
src/user_info/css/layout.jsx | 149 ++++++++
src/user_info/css/layout.scss | 3 +
src/user_info/css/projects.jsx | 14 +
src/user_info/images/userhead.jpg | Bin 0 -> 11343 bytes
73 files changed, 5242 insertions(+), 16 deletions(-)
create mode 100644 src/forge/Upload/Cover.js
create mode 100644 src/forums/Component/Announcement.jsx
create mode 100644 src/forums/Component/AuthorCard.jsx
create mode 100644 src/forums/Component/AuthorItem.jsx
create mode 100644 src/forums/Component/BestItem.jsx
create mode 100644 src/forums/Component/BestModalItem.jsx
create mode 100644 src/forums/Component/DoubleItem.jsx
create mode 100644 src/forums/Component/ItemDropDown.jsx
create mode 100644 src/forums/Component/ItemType.jsx
create mode 100644 src/forums/Component/ListItem.jsx
create mode 100644 src/forums/Component/ListSearch.jsx
create mode 100644 src/forums/Component/Paginations.jsx
create mode 100644 src/forums/Component/Prise.jsx
create mode 100644 src/forums/Component/Title.jsx
create mode 100644 src/forums/Component/Top.jsx
create mode 100644 src/forums/Component/UrlItem.jsx
create mode 100644 src/forums/Component/User.jsx
create mode 100644 src/forums/Component/UserInfo.jsx
create mode 100644 src/forums/Forums.js
create mode 100644 src/forums/ForumsDetail.jsx
create mode 100644 src/forums/Index.jsx
create mode 100644 src/forums/New.jsx
create mode 100644 src/forums/Nodata.js
create mode 100644 src/forums/Theme.js
create mode 100644 src/forums/ThemeRight.jsx
create mode 100644 src/forums/css/All.scss
create mode 100644 src/forums/css/Index.css
create mode 100644 src/forums/css/Theme.scss
create mode 100644 src/forums/css/layout.jsx
create mode 100644 src/forums/image/nodata.png
create mode 100755 src/forums/image/original.png
create mode 100755 src/forums/image/radius.png
create mode 100644 src/forums/new_comments/Title.jsx
create mode 100644 src/forums/new_comments/children_journals.js
create mode 100644 src/forums/new_comments/comment.scss
create mode 100644 src/forums/new_comments/comment_list.js
create mode 100644 src/forums/new_comments/comments.js
create mode 100644 src/forums/new_comments/head_reply_button.js
create mode 100644 src/forums/new_comments/new_comment.js
create mode 100644 src/user_info/Index.jsx
create mode 100644 src/user_info/Memo/Memos.js
create mode 100644 src/user_info/Memo/Replies.js
create mode 100644 src/user_info/Memo/ReplyItem.js
create mode 100644 src/user_info/Memo/SectionItem.js
create mode 100644 src/user_info/Memo/Sections.js
create mode 100644 src/user_info/Memo/SelectTitle.js
create mode 100644 src/user_info/Memo/select_title.scss
create mode 100644 src/user_info/Modal/modal.scss
create mode 100644 src/user_info/Modal/tip_modal.js
create mode 100644 src/user_info/User/Account.js
create mode 100644 src/user_info/User/BlockItem.js
create mode 100644 src/user_info/User/BlockUsers.js
create mode 100644 src/user_info/User/Blocks.js
create mode 100644 src/user_info/User/HeadUser.js
create mode 100644 src/user_info/User/Menu.js
create mode 100644 src/user_info/User/ProjectItem.js
create mode 100644 src/user_info/User/Projects.js
create mode 100644 src/user_info/User/StarUser.js
create mode 100644 src/user_info/User/UserAvatar.js
create mode 100644 src/user_info/User/UserBrief.js
create mode 100644 src/user_info/User/UserIdentify.js
create mode 100644 src/user_info/User/menu.scss
create mode 100644 src/user_info/User/user.scss
create mode 100644 src/user_info/css/head_user.jsx
create mode 100644 src/user_info/css/layout.jsx
create mode 100644 src/user_info/css/layout.scss
create mode 100644 src/user_info/css/projects.jsx
create mode 100644 src/user_info/images/userhead.jpg
diff --git a/package-lock.json b/package-lock.json
index 2c5f2c41..3a6d5752 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -988,6 +988,30 @@
}
}
},
+ "antd-img-crop": {
+ "version": "3.12.0",
+ "resolved": "https://registry.npm.taobao.org/antd-img-crop/download/antd-img-crop-3.12.0.tgz",
+ "integrity": "sha1-6S95QUkhNSwffjv7uu/Cc9pQFtU=",
+ "requires": {
+ "@babel/runtime": "^7.11.2",
+ "react-easy-crop": "^3.1.1"
+ },
+ "dependencies": {
+ "@babel/runtime": {
+ "version": "7.11.2",
+ "resolved": "https://registry.npm.taobao.org/@babel/runtime/download/@babel/runtime-7.11.2.tgz?cache=0&sync_timestamp=1596637887062&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40babel%2Fruntime%2Fdownload%2F%40babel%2Fruntime-7.11.2.tgz",
+ "integrity": "sha1-9UnBPHVMxAuHZEufqfCaapX+BzY=",
+ "requires": {
+ "regenerator-runtime": "^0.13.4"
+ }
+ },
+ "regenerator-runtime": {
+ "version": "0.13.7",
+ "resolved": "https://registry.npm.taobao.org/regenerator-runtime/download/regenerator-runtime-0.13.7.tgz?cache=0&sync_timestamp=1595456367497&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fregenerator-runtime%2Fdownload%2Fregenerator-runtime-0.13.7.tgz",
+ "integrity": "sha1-ysLazIoepnX+qrrriugziYrkb1U="
+ }
+ }
+ },
"anymatch": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz",
@@ -14809,6 +14833,21 @@
"prop-types": "^15.6.0"
}
},
+ "react-easy-crop": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npm.taobao.org/react-easy-crop/download/react-easy-crop-3.2.0.tgz",
+ "integrity": "sha1-HGaM4eTIV8CdryXdrzmKwkdfRTw=",
+ "requires": {
+ "tslib": "2.0.1"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npm.taobao.org/tslib/download/tslib-2.0.1.tgz?cache=0&sync_timestamp=1602286603545&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ftslib%2Fdownload%2Ftslib-2.0.1.tgz",
+ "integrity": "sha1-QQ6w0RPltjVkkO7HSWA3JbAhtD4="
+ }
+ }
+ },
"react-error-overlay": {
"version": "6.1.0-next.80",
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.1.0-next.80.tgz",
diff --git a/package.json b/package.json
index 0b17601d..3d1b2407 100644
--- a/package.json
+++ b/package.json
@@ -7,6 +7,7 @@
"@novnc/novnc": "^1.1.0",
"actioncable": "^5.2.4-3",
"antd": "^3.26.15",
+ "antd-img-crop": "^3.9.0",
"array-flatten": "^2.1.2",
"autoprefixer": "7.1.6",
"axios": "^0.18.1",
diff --git a/public/index.html b/public/index.html
index 1969fa0e..07c2e378 100755
--- a/public/index.html
+++ b/public/index.html
@@ -2,22 +2,12 @@
-
-
-
-
+
+
+
+
-
diff --git a/src/App.js b/src/App.js
index 28ec0d5f..b8d91742 100644
--- a/src/App.js
+++ b/src/App.js
@@ -61,7 +61,15 @@ const Shixunnopage = Loadable({
loader: () => import('./modules/404/Shixunnopage'),
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'),
@@ -231,6 +239,19 @@ class App extends Component {
}
}>
+ (
+
+ )}
+ >
+ {/* 问吧、论坛交流 */}
+ (
+
+ )}
+ >
{/*项目*/}
{
+ const [loading, setLoading] = useState(false);
+ const [url, setUrl] = useState(undefined);
+
+ useEffect(() => {
+ if (imageUrl) {
+ setUrl(imageUrl);
+ }
+ }, [imageUrl])
+
+ function getBase64(img, callback) {
+ const reader = new FileReader();
+ reader.addEventListener('load', () => callback(reader.result));
+ reader.readAsDataURL(img);
+ }
+
+ function handleChange(info) {
+ if (info.file.status === 'uploading') {
+ setLoading(true);
+ return;
+ }
+ if (info.file.status === 'done') {
+ let filelist = info.fileList && info.fileList.length>0 && info.fileList[info.fileList.length-1];
+ if(filelist && filelist.response && filelist.response.status === -1){
+ setLoading(false)
+ setUrl(null)
+ message.error(filelist.response.message)
+ }else{
+ getBase64(info.file.originFileObj, imageUrl =>{
+ setLoading(false),
+ getImageUrl(filelist && filelist.response),
+ setUrl(imageUrl)
+ });
+ }
+ }
+ };
+ function beforeUpload(file) {
+ const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
+ if (!isJpgOrPng) {
+ message.error('只能上传图片!');
+ }
+ const isLt2M = file.size / 1024 / 1024 < 2;
+ if (!isLt2M) {
+ message.error('图片大小必须小于2MB!');
+ }
+ return isJpgOrPng && isLt2M;
+ }
+ const uploadButton = (
+
+ {loading ? : }
+
+ );
+ return (
+
+ {url ?
: uploadButton}
+
+ )
+})
\ No newline at end of file
diff --git a/src/forums/Component/Announcement.jsx b/src/forums/Component/Announcement.jsx
new file mode 100644
index 00000000..6ecce112
--- /dev/null
+++ b/src/forums/Component/Announcement.jsx
@@ -0,0 +1,130 @@
+import React, { useState, useEffect } from "react";
+import { WhiteBack } from "../css/layout";
+import Title from "./Title";
+import { Modal, Button, Input , notification, message } from "antd";
+import Nodata from "../Nodata";
+import axios from 'axios';
+
+const TextArea = Input.TextArea;
+export default (({ content , operation ,plateId }) => {
+ const [word, setWord] = useState(undefined);
+ const [editWord, setEditWord] = useState(undefined);
+ const [show, setShow] = useState(false);
+ const [visible, setVisible] = useState(false);
+ const [AnnModalType, setAnnModalType] = useState(1); //默认为1:查看,否则为编辑
+
+ let child = document.getElementById("annWords") && document.getElementById("annWords").offsetHeight;
+ useEffect(() => {
+ if (content) {
+ setWord(content.notice);
+ }
+ }, [content]);
+
+ useEffect(() => {
+ changeShow();
+ }, [word, child , content]);
+
+ // 保存公告
+ function saveAnn() {
+ setWord(editWord);
+ setVisible(false);
+ // 调用保存接口
+ if(editWord){
+ const url = `/v1/forum_sections/${plateId}/edit_notice.json`;
+ axios.post(url,{
+ content:editWord
+ }).then(result=>{
+ if(result && result.data){
+ notification.open({message:"提示",description:result.data.message});
+ changeShow();
+ }
+ }).catch(error=>{
+ console.log(error);
+ })
+ }
+ }
+
+ function changeShow(){
+ let p = document.getElementById("annContent")&& document.getElementById("annContent").offsetHeight;
+ let c = document.getElementById("annWords") &&document.getElementById("annWords").offsetHeight;
+ if (c > p) {
+ setShow(true);
+ }else{
+ setShow(false);
+ }
+ }
+ // 取消编辑公告
+ function cancelAnn() {
+ setEditWord(word);
+ setVisible(false);
+ }
+
+ // 查看(1)或者编辑(2)
+ function changeAnnModalType(type) {
+ setAnnModalType(type);
+ setVisible(true);
+ setEditWord(word);
+ }
+
+ function changeText(e) {
+ setEditWord(e.target.value);
+ }
+ return (
+
+ setVisible(false)}
+ footer={
+ AnnModalType === 1 ? false :
+
+
+
+ }
+ >
+ {AnnModalType === 1 ? (
+ {word}
+ ) : (
+
+ )}
+
+
+ 公告
+ {
+ operation ?
+ changeAnnModalType(2)}>
+
+ :""
+ }
+
+
+
+ );
+});
diff --git a/src/forums/Component/AuthorCard.jsx b/src/forums/Component/AuthorCard.jsx
new file mode 100644
index 00000000..bd151fe3
--- /dev/null
+++ b/src/forums/Component/AuthorCard.jsx
@@ -0,0 +1,51 @@
+import React , { useState, useEffect } from 'react';
+import { WhiteBack , Greenline , UDStructure , Grid } from '../css/layout';
+import UserInfo from './UserInfo';
+import { getUrl } from "educoder";
+import StarUser from "../../user_info/User/StarUser";
+export default (({user})=>{
+ useEffect(() => {
+ if(user){
+ setFansCount(user.watchers_count)
+ }
+ },[]);
+ const [fansCount, setFansCount] = useState(0)
+ const fans_count = (count) => {
+ let new_fans_count = fansCount + count
+ setFansCount(new_fans_count)
+ }
+
+ return(
+
+
+ {user && user.description ? user.description : "这家伙太懒了,还未填写个人描述!"}
+ {user && !user.is_current_user && (
+
+ )}
+
+
+ {user && user.memos_count}
+ 文章数
+
+
+ {user && user.replies_count}
+ 评论数
+
+
+ {fansCount}
+ 关注者
+
+
+
+ )
+})
\ No newline at end of file
diff --git a/src/forums/Component/AuthorItem.jsx b/src/forums/Component/AuthorItem.jsx
new file mode 100644
index 00000000..ad0f3fa6
--- /dev/null
+++ b/src/forums/Component/AuthorItem.jsx
@@ -0,0 +1,23 @@
+import React, { useState } from "react";
+import { WhiteBack } from "../css/layout";
+import Title from './Title';
+import UserInfo from "./UserInfo";
+import { getUrl } from 'educoder';
+export default (({ author }) => {
+ return (
+
+ 版块活跃作者
+
+ {
+ author && author.length > 0 ?
+ author.map((item,key)=>{
+ return(
+
+ )
+ })
+ :""
+ }
+
+
+ );
+});
diff --git a/src/forums/Component/BestItem.jsx b/src/forums/Component/BestItem.jsx
new file mode 100644
index 00000000..74e6c8e0
--- /dev/null
+++ b/src/forums/Component/BestItem.jsx
@@ -0,0 +1,27 @@
+import React,{useEffect,useState} from 'react';
+import {Link} from 'react-router-dom';
+import Nodata from '../Nodata';
+
+export default (({ memos , selectKey })=>{
+ const [list, setList] = useState(undefined);
+ const [ keys , setKeys ] = useState(undefined);
+
+ useEffect(() => {
+ if (memos) {
+ setList(memos);
+ setKeys(selectKey[0]);
+ }
+ }, [memos , selectKey]);
+ return(
+
+ {
+ list && list.length>0 ? list.map((item,key)=>{
+ return(
+ - {item.subject}
+ )
+ }):
+
+ }
+
+ )
+})
\ No newline at end of file
diff --git a/src/forums/Component/BestModalItem.jsx b/src/forums/Component/BestModalItem.jsx
new file mode 100644
index 00000000..8f01295a
--- /dev/null
+++ b/src/forums/Component/BestModalItem.jsx
@@ -0,0 +1,54 @@
+import React, { useState } from "react";
+import { WhiteBack, AlignCenter } from "../css/layout";
+import { Link } from 'react-router-dom';
+import Title from "./Title";
+import ring from "../image/radius.png";
+import styled from "styled-components";
+
+const Img = styled.img`
+ {
+ width: 40px;
+ height: 40px;
+ margin-right: 14px;
+ border-radius: 50%;
+ }
+`;
+const Prag = styled.p`
+ {
+ color: #999;
+ font-size: 14px;
+ line-height: 18px;
+ margin: 4px 0 !important;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: -webkit-box;
+ -webkit-line-clamp: 1;
+ -webkit-box-orient: vertical;
+ word-break: break-all;
+ }
+`;
+
+export default ({ recommand }) => {
+ return (
+
+ 精选板块
+
+ {recommand && recommand.length > 0
+ ? recommand.map((item, key) => {
+ return (
+
+
+
+
{item.title}
+
{item.description || "暂无描述~"}
+ {/*
{item.description}
*/}
+
{item.watchers_count}人收藏{item.memos_count}个话题
+
+
+ );
+ })
+ : ""}
+
+
+ );
+};
diff --git a/src/forums/Component/DoubleItem.jsx b/src/forums/Component/DoubleItem.jsx
new file mode 100644
index 00000000..c7a8df9e
--- /dev/null
+++ b/src/forums/Component/DoubleItem.jsx
@@ -0,0 +1,66 @@
+import React, { useState, useEffect } from "react";
+import logo from "../image/radius.png";
+import { FlexAJ, P } from "../css/layout";
+import { Link } from "react-router-dom";
+import styled from "styled-components";
+
+const Img = styled.img`
+ {
+ width: 60px;
+ height: 60px;
+ border-radius: 50%;
+ margin-right: 25px;
+ }
+`;
+
+const Prag = styled.p`
+ {
+ color: #333;
+ font-size: 14px;
+ line-height: 18px;
+ margin-bottom: 10px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: -webkit-box;
+ -webkit-line-clamp: 1;
+ -webkit-box-orient: vertical;
+ word-break: break-all;
+ height:18px;
+ }
+`;
+
+export default (props) => {
+ const list = props.plate;
+
+ return (
+
+ {list && list.length > 0
+ ? list.map((item, key) => {
+ return (
+
+

+
+
+
{item.title}
+
帖子数:{item.memos_count}
+
+
{item.description || 暂无描述~}
+
+ 版主:{item.user_name}
+
+
+
+ );
+ })
+ : ""}
+
+ );
+};
diff --git a/src/forums/Component/ItemDropDown.jsx b/src/forums/Component/ItemDropDown.jsx
new file mode 100644
index 00000000..8d5406ea
--- /dev/null
+++ b/src/forums/Component/ItemDropDown.jsx
@@ -0,0 +1,114 @@
+import React from "react";
+import { Menu , Dropdown , notification } from "antd";
+import {Link} from 'react-router-dom';
+import axios from 'axios';
+
+// permission = {
+// admin://管理员
+// banned_permission://管理员、且有置顶、推荐权限
+// is_currentUser: true, #是否为当前用户,判断是否显示编辑/删除,并隐藏其他的
+// is_fine: true, #是否精华帖子
+// sticky: true, #是否置顶
+// memo_watched: true, #是否收藏
+// is_deleted:true#是否已经申请删除
+// }
+export default ({ id , permission , calbackFunc , confirm }) => {
+ // 置顶、取消置顶
+ function changeSticky(s){
+ let sticky = s ? 0 : 1;//1为置顶,0为取消置顶
+ const url = `/v1/memos/${id}/set-top-or-down.json`;
+ axios.get(url,{
+ params:{
+ sticky
+ }
+ }).then(result=>{
+ if(result){
+ notification.open({message:"提示",description:result.data.message});
+ calbackFunc && calbackFunc();
+ }
+ })
+ }
+ // 推荐、取消推荐
+ function changeFine(f){
+ let is_fine = f ? 0 : 1;//1表示加精,0表示取消加精
+ const url = `/v1/memos/${id}/is_fine.json`;
+ axios.post(url,{
+ is_fine
+ }).then(result=>{
+ if(result){
+ notification.open({message:"提示",description:result.data.message});
+ calbackFunc && calbackFunc();
+ }
+ })
+ }
+
+ // 关注、取消关注
+ function changeMemoWatched(m){
+ let is_watch = m ? 0 : 1;//1为添加关注,0为取消关注
+ const url = `/v1/memos/${id}/watch_memo.json`;
+ axios.post(url,{
+ is_watch
+ }).then(result=>{
+ if(result){
+ notification.open({message:"提示",description:result.data.message});
+ calbackFunc && calbackFunc();
+ }
+ })
+ }
+ // 管理员直接删除帖子
+ function deleteForum(){
+ confirm && confirm({
+ content: '确认删除帖子?',
+ onOk:()=>{
+ const url = `/v1/memos/${id}/destroy.json`;
+ axios.post(url).then(result=>{
+ if(result){
+ notification.open({message:"提示",description:result.data.message});
+ calbackFunc && calbackFunc();
+ window.location.href="/forums"
+ }
+ })
+ }
+ })
+ }
+
+ // 发布者申请删除、撤销申请删除
+ function sendDeleteForum(d){
+ let is_apply = d ? 0 : 1;//1为申请删除,0为撤销申请删除
+ confirm && confirm({
+ content: '确认申请删帖?',
+ onOk:()=>{
+ const url = `/v1/memos/${id}/confirm_delete.json`;
+ axios.post(url,{
+ is_apply
+ }).then(result=>{
+ if(result){
+ notification.open({message:"提示",description:result.data.message});
+ calbackFunc && calbackFunc();
+ }
+ })
+ }
+ })
+ }
+
+ const menu=(
+ permission &&
+
+ )
+ return (
+
+
+
+ );
+};
diff --git a/src/forums/Component/ItemType.jsx b/src/forums/Component/ItemType.jsx
new file mode 100644
index 00000000..d52014bc
--- /dev/null
+++ b/src/forums/Component/ItemType.jsx
@@ -0,0 +1,19 @@
+import React from 'react';
+
+export function Type(value){
+ if(value === "交流"){
+ return (【交流】)
+ }else if(value === "求助"){
+ return (【求助】)
+ }
+}
+
+export function Tag(value){
+ if(value === "置顶"){
+ return (置顶)
+ }else if(value === "精华"){
+ return (精华)
+ }else if(value === "原创"){
+ return (原创)
+ }
+}
\ No newline at end of file
diff --git a/src/forums/Component/ListItem.jsx b/src/forums/Component/ListItem.jsx
new file mode 100644
index 00000000..ca400732
--- /dev/null
+++ b/src/forums/Component/ListItem.jsx
@@ -0,0 +1,89 @@
+import React, { useState, useEffect } from "react";
+import { FlexAJ, AlignCenter, LeftLine } from "../css/layout";
+import { Link } from "react-router-dom";
+import { getUrl } from 'educoder';
+import { Type, Tag } from "./ItemType";
+import User from "./User";
+import Drop from "./ItemDropDown";
+import Nodata from '../Nodata';
+
+export default ({ memos , current_user , calbackFunc , confirm }) => {
+ const [list, setList] = useState(undefined);
+
+ useEffect(() => {
+ if (memos) {
+ setList(memos);
+ }
+ }, [memos]);
+
+ return (
+
+ {list && list.length > 0 ? (
+ list.map((item, key) => {
+ let permission = {
+ index:key,
+ admin:current_user && current_user.admin,
+ banned_permission:item.banned_permission,
+ login:current_user && current_user.login,
+ is_currentUser:current_user && (item.user_login === current_user.login),
+ is_fine:item.is_fine,
+ sticky:item.sticky,
+ memo_watched:item.memo_watched,
+ user_banned_permission:item.banned_permission,
+ is_deleted:item.apply_destroy
+ }
+ return (
+ -
+
+
+ {Type(item.tag_name)}
+
+ {item.subject}
+
+ { item.sticky === true ? {Tag("置顶")} : "" }
+ { item.is_original === true ? {Tag("原创")} : "" }
+ { item.is_fine === true ? {Tag("精华")} : "" }
+
+
+ { item.apply_destroy ? 已申请删帖 : "" }
+ {
+ current_user && current_user.login ?
+
+ :""
+ }
+
+
+
+
+
+ {item.forum_section_title ? {item.forum_section_title} : "" }
+ {item.published_time ? {item.published_time} : "" }
+
+
+
+
+ {item.replies_count}
+
+
+
+ {item.praises_count}
+
+
+
+ {item.replies_count}
+
+
+
+
+ );
+ })
+ ) :
+
+ }
+
+ );
+};
diff --git a/src/forums/Component/ListSearch.jsx b/src/forums/Component/ListSearch.jsx
new file mode 100644
index 00000000..ee13f269
--- /dev/null
+++ b/src/forums/Component/ListSearch.jsx
@@ -0,0 +1,23 @@
+import React from 'react';
+import { Input } from 'antd';
+import { Greenback } from '../css/layout';
+import { Link } from 'react-router-dom';
+
+const Search = Input.Search;
+export default (({ onSearch , current_user })=>{
+ return(
+
+
+ {
+ current_user && current_user.login ?
+
+ 写点什么
+
+ :
+
+ 写点什么
+
+ }
+
+ )
+})
\ No newline at end of file
diff --git a/src/forums/Component/Paginations.jsx b/src/forums/Component/Paginations.jsx
new file mode 100644
index 00000000..cc19dd43
--- /dev/null
+++ b/src/forums/Component/Paginations.jsx
@@ -0,0 +1,17 @@
+import React from "react";
+import { Pagination } from "antd";
+
+export default (({page,total,changePage , pageSize}) => {
+ return (
+ total > pageSize ?
+ :""
+ );
+});
diff --git a/src/forums/Component/Prise.jsx b/src/forums/Component/Prise.jsx
new file mode 100644
index 00000000..394f01b4
--- /dev/null
+++ b/src/forums/Component/Prise.jsx
@@ -0,0 +1,45 @@
+import React, { useState, useEffect } from "react";
+import axios from "axios";
+import { message } from "antd";
+export default ({ isPrised, num, memo_id, container_type, current_login }) => {
+ const [flag, setFlag] = useState(undefined);
+ const [number, setNumber] = useState(undefined);
+
+ useEffect(() => {
+ setFlag(isPrised);
+ setNumber(num);
+ }, []);
+
+ function priseForums() {
+ if (current_login) {
+ axios
+ .post(`/v1/discusses/${memo_id}/plus.json`, {
+ container_type: container_type,
+ id: memo_id,
+ type: flag ? 0 : 1,
+ })
+ .then((result) => {
+ setNumber(result.data.praise_count);
+ setFlag(!flag);
+ message.success(flag ? "取消点赞" : "已点赞")
+ })
+ .catch((error) => {
+ message.error(error);
+ });
+ } else {
+ window.open("/login", "_blank");
+ }
+ }
+ return (
+
+
+
+
+ {number}
+
+ );
+};
diff --git a/src/forums/Component/Title.jsx b/src/forums/Component/Title.jsx
new file mode 100644
index 00000000..c31adbd1
--- /dev/null
+++ b/src/forums/Component/Title.jsx
@@ -0,0 +1,19 @@
+import React from 'react';
+import styled from 'styled-components';
+
+const Nav = styled.div`{
+ background-color:#fff;
+ padding:20px 30px;
+ border-bottom:1px solid #eee;
+ font-size:16px;
+ color:#333;
+ display:flex;
+ justify-content: space-between;
+ align-items:center;
+}`
+
+export default (({children,className})=>{
+ return(
+
+ )
+})
\ No newline at end of file
diff --git a/src/forums/Component/Top.jsx b/src/forums/Component/Top.jsx
new file mode 100644
index 00000000..203aa822
--- /dev/null
+++ b/src/forums/Component/Top.jsx
@@ -0,0 +1,152 @@
+import React, { useEffect , useState } from "react";
+import { Button , notification } from "antd";
+import { WhiteBack, FlexStart, P, FlexAJ, AlignCenter } from "../css/layout";
+import Radius from "../image/radius.png";
+import styled from "styled-components";
+import axios from "axios";
+import {Link} from 'react-router-dom';
+
+const Img = styled.img`
+ {
+ width: 60px;
+ height: 60px;
+ border-radius: 50%;
+ margin-right: 15px;
+ }
+`;
+const Prag = styled.p`
+ {
+ color: #333;
+ font-size: 14px;
+ line-height: 18px;
+ margin: 10px 0px;
+ text-align: justify;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: -webkit-box;
+ -webkit-line-clamp: 3;
+ -webkit-box-orient: vertical;
+ word-break: break-all;
+ max-height: 54px;
+ }
+`;
+const Spanleft = styled.span`
+ {
+ margin-right: 30px;
+ color: #888;
+ font-size: 12px;
+ }
+`;
+const Spanright = styled.span`
+ {
+ margin-left: 30px;
+ color: #888;
+ font-size: 12px;
+ & > label {
+ color: #333;
+ }
+ }
+`;
+export default ({ headData, title , operation , history }) => {
+ const [section, setSection] = useState(undefined);
+ const [sectionUser, setSectionUser] = useState(undefined);
+ const [forumModers, setForumModers] = useState(undefined);
+ const [ watched , setWacth ] = useState(headData && headData.watched);
+
+ useEffect(() => {
+ if (headData) {
+ setSection(headData.forum_section);
+ setSectionUser(headData.forum_section_user);
+ setForumModers(headData.forum_moders);
+ setWacth(headData.watched);
+ }
+ }, [headData]);
+
+ // 收藏、取消收藏
+ function saveForum(id){
+ if(id){
+ const url = `/v1/memos/forum_memos/${id}/is_watch.json`;
+ axios.post(url,{
+ is_watch:watched?0:1
+ }).then(result=>{
+ if(result && result.data && result.data.status!=-1){
+ setWacth(!watched);
+ notification.open({message:"提示",description:result.data.message});
+ }
+ }).catch(error=>{
+ console.log(error);
+ })
+ }
+ }
+
+ function toManage(id){
+ history.push(`/forums/manage/${section && section.id}`);
+ }
+
+ return (
+
+
+
+
+
+ {section && section.title}
+
+ {
+ operation ?
+
+ :""
+ }
+
+
+
+
+ {section && section.description ? (
+
{section.description}
+ ) : (
+ ""
+ )}
+
+
+ 版主:{sectionUser && sectionUser.username}
+ {forumModers && forumModers.length > 0 ? (
+
+ 管理员:
+ {forumModers.map((item, key) => {
+ return key < forumModers.length - 1
+ ? {item.username}、
+ : {item.username};
+ })}
+
+ ) : (
+ ""
+ )}
+
+
+
+ 版块主题:
+
+ {section && section.publish_today_coun ? (
+
+ 今日发帖:
+
+ ) : (
+ ""
+ )}
+ {section && section.replies_today_count ? (
+
+ 今日回帖:
+
+ ) : (
+ ""
+ )}
+
+
+
+
+
+ );
+};
diff --git a/src/forums/Component/UrlItem.jsx b/src/forums/Component/UrlItem.jsx
new file mode 100644
index 00000000..e20480a1
--- /dev/null
+++ b/src/forums/Component/UrlItem.jsx
@@ -0,0 +1,42 @@
+import React , { useState , useEffect } from "react";
+import { Link } from 'react-router-dom';
+
+export default ({user}) => {
+
+ return (
+
+ );
+};
diff --git a/src/forums/Component/User.jsx b/src/forums/Component/User.jsx
new file mode 100644
index 00000000..a8345826
--- /dev/null
+++ b/src/forums/Component/User.jsx
@@ -0,0 +1,24 @@
+import React from 'react';
+import { AlignCenter } from '../css/layout';
+import styled from 'styled-components';
+import { Link } from 'react-router-dom';
+
+const Img = styled.img`{
+ width:20px;
+ height:20px;
+ border-radius:50%;
+ margin-right:10px;
+}`
+const Span = styled.span`{
+ color:#999;
+}`
+export default (({name,url,login})=>{
+ return(
+
+
+
+ {name}
+
+
+ )
+})
\ No newline at end of file
diff --git a/src/forums/Component/UserInfo.jsx b/src/forums/Component/UserInfo.jsx
new file mode 100644
index 00000000..07464039
--- /dev/null
+++ b/src/forums/Component/UserInfo.jsx
@@ -0,0 +1,29 @@
+import React from 'react';
+import styled from 'styled-components';
+import { Link } from 'react-router-dom';
+
+export default ({ url , name , column , login })=>{
+ const Img = styled.span`
+ display:flex;
+ ${column && "flex-direction: column;text-align:center;"}
+ align-items: center;
+ & img{
+ width:30px;
+ height:30px;
+ border-radius:50%;
+ }
+ ${!column && `
+ & span{
+ margin-left:8px;
+ }`
+ }
+ `;
+ return(
+
+
+
+ {name}
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/forums/Forums.js b/src/forums/Forums.js
new file mode 100644
index 00000000..fb86d5cb
--- /dev/null
+++ b/src/forums/Forums.js
@@ -0,0 +1,197 @@
+import React, { useEffect, useState } from "react";
+import { Pagination, Menu, Spin } from "antd";
+
+import {
+ Box,
+ Long,
+ Short,
+ Gap,
+ WhiteBack,
+ LeftLine,
+ AlignCenter,
+} from "./css/layout";
+import Title from "./Component/Title";
+import ListItem from "./Component/ListItem";
+import "./css/All.scss";
+import ListSearch from "./Component/ListSearch";
+import UrlItem from "./Component/UrlItem";
+import BestItem from "./Component/BestItem";
+import DoubleItem from "./Component/DoubleItem";
+import "./css/Theme.scss";
+import { Link } from "react-router-dom";
+import axios from "axios";
+
+const PAGESIZE = 10; // 首页帖子每页只展示5条
+function aa(props) {
+ const current_user = props.current_user;
+ const [data, setData] = useState(undefined); //接口所有数据
+ const [memos, setMemos] = useState([]); //帖子列表
+ const [sort, setSort] = useState("published_at"); //最新最热
+ const [listSpin, setListSpin] = useState(true);
+ const [memosCount, setMemosCount] = useState([]); //帖子数量
+
+ const [page, setPage] = useState(1); //分页
+ const [search, setSearch] = useState(undefined); //搜索内容
+ const [selectKey, setSelectKey] = useState(["hot"]);
+
+ const [hotMemos, setHotMemos] = useState([]); //热门推荐、版主推荐
+ const [forumSections, setForumSections] = useState([]); //二级模块
+
+ const [plate, setPlate] = useState(undefined); // 所有版块
+ useEffect(() => {
+ init();
+ }, [page, search, sort]);
+
+ async function init() {
+ setListSpin(true);
+ let url = `/v1/memos.json`;
+ axios
+ .get(url, {
+ params: { page, search, sort , limit:PAGESIZE },
+ })
+ .then((result) => {
+ if (result) {
+ setData(result.data);
+ setMemos(result.data.memos);
+ setListSpin(false);
+ setMemosCount(result.data.memos_count);
+ setHotMemos(result.data.hottest_memos);
+ setForumSections(result.data.forum_sections);
+ }
+ })
+ .catch((e) => {
+ console.log(e);
+ });
+ }
+
+ useEffect(() => {
+ async function init() {
+ let url = `/v1/forum_sections.json`;
+ axios
+ .get(url, {
+ params: { is_detail: true },
+ })
+ .then((result) => {
+ if (result) {
+ setPlate(result.data.forum_sections);
+ }
+ })
+ .catch((e) => {
+ console.log(e);
+ });
+ }
+ init();
+ }, []);
+ // 翻页
+ function changePage(page) {
+ setPage(page);
+ }
+ // 搜索
+ function onSearch(e) {
+ setSearch(e);
+ }
+
+ function changeMenu(e) {
+ setSelectKey(e.key);
+ if (e.key === "hot") {
+ setHotMemos(data.hottest_memos);
+ } else {
+ setHotMemos(data.recommend_memos);
+ }
+ }
+
+ function changeSort(value) {
+ setSort(value);
+ }
+ return (
+
+
+
+
+
+ 论坛首页
+
+ changeSort("published_at")}
+ className={
+ sort === "published_at" ? "green cPointer" : "cPointer"
+ }
+ >
+ 最新
+
+ changeSort("replies_count")}
+ className={
+ sort === "published_at" ? "cPointer" : "green cPointer"
+ }
+ style={{ fontSize: "16px" }}
+ >
+ 最热
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 热门话题
+
+
+
+ 版主推荐
+
+
+
+
+
+ {plate && plate.length > 0
+ ? plate.map((item, key) => {
+ return item.children_tags && item.children_tags.length > 0 ? (
+
+
+ {/* {item.name} */}
+
+
+ {item.name}
+
+
+
+
+
+ ) : (
+ ""
+ );
+ })
+ : ""}
+
+ );
+}
+export default aa;
diff --git a/src/forums/ForumsDetail.jsx b/src/forums/ForumsDetail.jsx
new file mode 100644
index 00000000..a8ee79da
--- /dev/null
+++ b/src/forums/ForumsDetail.jsx
@@ -0,0 +1,248 @@
+import React, { useEffect, useState } from "react";
+import axios from "axios";
+import { getUrl } from "educoder";
+import { Breadcrumb, Spin, Empty } from "antd";
+import { Link } from "react-router-dom";
+import {
+ WhiteBack,
+ Box,
+ Long,
+ Short,
+ Gap,
+ FlexAJ,
+ AlignCenter,
+ P,
+} from "./css/layout";
+import Title from "./Component/Title";
+import RenderHtml from "../components/render-html";
+import { Type, Tag } from "./Component/ItemType";
+import Drop from "./Component/ItemDropDown";
+import UserInfo from "./Component/UserInfo";
+import Prise from "./Component/Prise";
+import ListItem from "./Component/ListItem";
+import AuthorCard from "./Component/AuthorCard";
+import BestItem from "./Component/BestItem";
+import Original from "./image/original.png";
+import Comments from "./new_comments/comments";
+import Attachments from '../forge/Upload/attachment';
+function memo_show(props) {
+ const memo_id = props.match.params.id;
+ const current_user = props.current_user;
+
+ const [memo, getMemo] = useState(null);
+ const [memoUser, getMemoUser] = useState(null);
+ const [replies, setReplies] = useState([]);
+ const [memoImage, setMemoImage] = useState(null);
+ const [recent_memos, setRecentmemos] = useState([]);
+ const [bannedPermission, setPermission] = useState(undefined);
+ const [isBanned, setIsBanned] = useState(false);
+ const [page, setListPage] = useState(1);
+ const [limit, setLimitType] = useState(5);
+ const [isSpin, setSpinType] = useState(false);
+ const [dropPermission, setDropPermission] = useState({});
+ useEffect(() => {
+ init();
+ related_memos();
+ }, [memo_id]);
+
+ async function init() {
+ setSpinType(true);
+ let url = `/v1/memos/${memo_id}.json`;
+ axios
+ .get(url)
+ .then((result) => {
+ if (result) {
+ let per = {
+ admin:result.data && result.data.is_current_admin,
+ login: result.data && result.data.current_login,
+ banned_permission:result.data.banned_permission,
+ is_currentUser: result.data.author_info ? result.data.author_info.is_current_user : undefined,
+ is_fine: result.data.memo && result.data.memo.is_fine,
+ sticky: result.data.memo && result.data.memo.sticky,
+ memo_watched: result.data.memo && result.data.memo.memo_watched,
+ is_deleted:result.data.memo && result.data.memo.apply_destroy
+ }
+ setPermission(per);
+
+ getMemo(result.data.memo);
+ setMemoImage(result.data.memo_image_info); //封面信息
+ setRecentmemos(result.data.recent_memos);
+ setIsBanned(result.data.is_banned);
+ getMemoUser(result.data.author_info);
+ }
+ setSpinType(false);
+ })
+ .catch((e) => {
+ setSpinType(false);
+ console.log(e);
+ });
+ }
+
+ function related_memos() {
+ let url = `/v1/memos/${memo_id}/related_memos.json`;
+ axios
+ .get(url)
+ .then((result) => {
+ if (result) {
+ setReplies(result.data.memos);
+ }
+ setSpinType(false);
+ })
+ .catch((e) => {
+ setSpinType(false);
+ console.log(e);
+ });
+ }
+
+ return (
+
+
+ {memo && memoUser ? (
+
+
+
+ 论坛
+
+ {memo && memo.forum_tag && memo.forum_tag.id && (
+
+
+ {memo && memo.forum_tag.title}
+
+
+ )}
+
+ {memo ? memo.subject : "帖子详情"}
+
+
+
+
+
+
+
+
+ {memo && Type(`${memo.tag_name}`)}
+
+ {memo && memo.subject}
+
+ {memo && memo.is_fine && Tag("精华")}
+ {memo && memo.sticky && Tag("置顶")}
+
+
+ {current_user && current_user.login ? (
+
+ ):""}
+
+ {memo && memo.is_original && (
+
+ )}
+
+
+
+
+
+
+
+ {memo && memo.viewed_count}
+
+
+
+
+
+ {memo && memo.replies_count}
+
+
+
+ {memo && memo.published_time}
+
+ {memo && memo.apply_destroy && (
+ 已申请删帖
+ )}
+
+
+
+ {memoImage && (
+
+

+
+ )}
+
+
+ {memo.attachment_url && memo.attachment_url.length > 0 &&
+
}
+
+ {memo.reprint_link && (
+
转载自:{memo.reprint_link}
+ )}
+
+
+
+
+
+
+ {
+ replies && replies.length > 0 &&
+
+
+ 相关推荐
+
+
+
+ }
+
+
+
+
+
+
+ 作者最近文章
+
+ 更多
+
+
+
+
+
+
+
+
+
+
+
+
+ ) : (
+
+ )}
+
+
+ );
+}
+
+export default memo_show;
diff --git a/src/forums/Index.jsx b/src/forums/Index.jsx
new file mode 100644
index 00000000..fd087b0b
--- /dev/null
+++ b/src/forums/Index.jsx
@@ -0,0 +1,58 @@
+import React, { Component } from "react";
+import { TPMIndexHOC } from "../modules/tpm/TPMIndexHOC";
+import { SnackbarHOC } from "educoder";
+import { CNotificationHOC } from "../modules/courses/common/CNotificationHOC";
+import { Route, Switch } from "react-router-dom";
+import Loading from "../Loading";
+import Loadable from "react-loadable";
+import "./css/Index.css";
+import "./css/All.scss";
+
+const Detail = Loadable({
+ loader: () => import("./ForumsDetail"),
+ loading: Loading,
+});
+const Theme = Loadable({
+ loader: () => import("./Theme"),
+ loading: Loading,
+});
+const Forums = Loadable({
+ loader: () => import("./Forums"),
+ loading: Loading,
+});
+const New = Loadable({
+ loader: () => import("./New"),
+ loading: Loading,
+});
+class Index extends Component {
+ render() {
+ return (
+
+
+ {/* 主题全部 */}
+ (
+
+ )}
+ >
+ {/* 详情 */}
+ }
+ >
+ }
+ >
+ }
+ >
+ }>
+
+
+ );
+ }
+}
+export default CNotificationHOC()(SnackbarHOC()(TPMIndexHOC(Index)));
diff --git a/src/forums/New.jsx b/src/forums/New.jsx
new file mode 100644
index 00000000..74345629
--- /dev/null
+++ b/src/forums/New.jsx
@@ -0,0 +1,374 @@
+import React, { useCallback, useState, useEffect, forwardRef } from "react";
+import { Banner, WhiteBack, AlignCenter, Greenback } from "./css/layout";
+import { Form, Input, Select, Radio, Button, notification, Spin } from "antd";
+import MDEditor from "../modules/tpm/challengesnew/tpm-md-editor";
+import Uplaod from "../forge/Upload/Index";
+import UplaodCover from "../forge/Upload/Cover";
+import Attachments from '../forge/Upload/attachment';
+import "./css/All.scss";
+import axios from "axios";
+
+const TextArea = Input.TextArea;
+const Option = Select.Option;
+
+// forum_id: values.forum_id, //大主题版块的id, int类型 (必填)
+// attachment_id: ImgInfo.attachment_id, //上传的封面id
+// children_forum_id: values.children_forum_id, //小主题版块的id,int类型
+// attachments, //上传的附件id数组
+// memo: {
+// //帖子的相关内容
+// subject: values.subject, // 帖子的标题 (必填)
+// content, //帖子的内容 (必填)
+// tag_id: values.tag_id, //帖子的标签,1为交流,2为求助(必填)
+// is_original: values.is_original, //是否原创,默认为true,(必填)
+// reprint_link: values.reprint_link, //转载链接,仅当is_original为false时,出现
+// }
+function New(props, ref) {
+ const {
+ form: { getFieldDecorator, validateFields, setFieldsValue },
+ } = props;
+ const [parentPlate, setParentPlate] = useState(undefined); //父版块
+ const [childPlate, setChildPlate] = useState(undefined); //子版块
+ const [content, setContent] = useState(""); //内容
+ const [ImgInfo, setImgInfo] = useState(undefined); //封面图信息
+ const [attachments, setAttachments] = useState(undefined); //上传附件的id数组
+ const [spinning, setSpining] = useState(false);
+
+ const [ fileList ,setFileList ] = useState(undefined);
+
+ const [ originalType , setOriginalType ] = useState(2);
+
+ let forumId = props.match.params.forumId;
+
+ useEffect(() => {
+ setFieldsValue({
+ tag_id: 1,
+ is_original: 2,
+ });
+ getPlate();
+ }, []);
+
+ function getDetail(plate){
+ if(forumId){
+ const url = `/v1/memos/${forumId}/edit.json`;
+ axios.get(url).then(result=>{
+ if(result){
+ setContent(result.data.content);
+
+ let forum_id = result.data.forum_section && result.data.forum_section.forum_id;
+ let child = plate && plate.filter((item) => item.id === forum_id);
+ let memo_original = result.data.is_original ? 1 : 2
+ setChildPlate(child && child.length>0 && child[0].children_tags);
+ setOriginalType(memo_original)
+ setFieldsValue({
+ ...result.data,
+ forum_id,
+ tag_id: result.data.tag_id,
+ children_forum_id:result.data.forum_section_id,
+ is_original: memo_original
+ });
+ let attachments_url = result.data.attachments_url && result.data.attachments_url.map((item,key)=>{
+ return({
+ title:item.title,
+ ...item
+ })
+ });
+ let array = result.data.attachments_url && result.data.attachments_url.map((item,key)=>{
+ return item.id
+ })
+ setAttachments(array);
+ setFileList(attachments_url);
+ setImgInfo(result.data.memo_image_info);
+ }
+ }).catch(error=>{
+ console.log(error);
+ })
+ }
+ }
+
+ // 获取所有版块
+ function getPlate() {
+ setSpining(true);
+
+ const url = `/v1/forum_sections.json`;
+ axios.get(url)
+ .then((result) => {
+ if (result && result.data) {
+ setParentPlate(result.data.forum_sections);
+ setFieldsValue({
+ forum_id: 0,
+ });
+ if(forumId){
+ getDetail(result.data.forum_sections);
+ }
+ setSpining(false);
+
+ }
+ })
+ .catch((error) => {
+ console.log(error);
+ });
+ }
+ // 切换主题版块
+ function changeParent(e) {
+ let child = parentPlate.filter((item) => item.id === e);
+ if (child && child.length === 1) {
+ setChildPlate(child[0].children_tags);
+ setFieldsValue({
+ children_forum_id:
+ child[0].children_tags && child[0].children_tags.length > 0
+ ? child[0].children_tags[0].id
+ : "",
+ });
+ } else {
+ setChildPlate(undefined);
+ setFieldsValue({
+ children_forum_id: "",
+ });
+ }
+ }
+
+ const helper = useCallback(
+ (label, name, rules, widget, isRequired) => (
+
+ {label}
+
+ {getFieldDecorator(name, { rules, validateFirst: true })(widget)}
+
+
+ ),
+ []
+ );
+
+ // 修改内容
+ function onContentChange(e) {
+ setContent(e);
+ }
+
+ // 上传的图片
+ function UploadFunc(e) {
+ attachments && attachments.length>0 && attachments.map((item,key)=>{
+ return e.push(item.id)
+ });
+ setAttachments(e);
+ }
+
+ // 获取封面图片
+ function getImageUrl(imgId) {
+ setImgInfo(imgId);
+ }
+
+ // 返回
+ function returnNew() {
+ props.history.goBack();
+ }
+ // 提交
+ function sureNew() {
+ setSpining(true);
+ // console.log("dfasdfasdfad")
+ validateFields((error, values) => {
+ if (!error) {
+ let params = {
+ forum_id: values.forum_id,
+ attachment_id: ImgInfo && ImgInfo.attachment_id,
+ children_forum_id: values.children_forum_id,
+ attachments,
+ memo: {
+ subject: values.subject,
+ content,
+ tag_id: values.tag_id,
+ is_original: values.is_original,
+ reprint_link: values.reprint_link,
+ }
+ }
+ if(forumId){
+ // 编辑保存
+ const url = `/v1/memos/${forumId}/update.json`;
+ axios.post(url, params).then((result) => {
+ if (result && result.data) {
+ notification.open({
+ message: "提示",
+ description: result.data.message,
+ });
+ if(result.data.status === 1){
+ props.history.push(`/forums/${forumId}/detail`);
+ }
+ setSpining(false);
+ }
+ })
+ .catch((error) => {
+ setSpining(true);
+ console.log(error);
+ });
+ }else{
+ // 新建提交
+ const url = `/v1/memos/create.json`;
+ axios.post(url, params).then((result) => {
+ if (result && result.data) {
+ notification.open({
+ message: "提示",
+ description: result.data.message,
+ });
+
+ if(result.data.status === 1){
+ props.history.push(`/forums/${result.data.memo_id}/detail`);
+ }
+ setSpining(false);
+ }
+ })
+ .catch((error) => {
+ setSpining(false);
+ console.log(error);
+ });
+ }
+ }else{
+ console.log(error)
+ }
+ });
+ }
+
+ function showNotification(description){
+ notification.open({
+ message:"提示",
+ description
+ })
+ }
+
+ function changeOriginal(e){
+ setOriginalType(e.target.value);
+ }
+ // 编辑删除附件时获取删除后剩余的数组
+ function deleteFunc(id){
+ let team = attachments && attachments.length>0 && attachments.filter(item=>item!==id);
+ setAttachments(team);
+ }
+ return (
+
+
+ { forumId ? "编辑" : "新建"}
+
+
+
+ 主题板块
+
+
+ {helper(
+ "",
+ "forum_id",
+ [{ required: true, message: "请选择主题板块" }],
+
+ )}
+ {helper(
+ "",
+ "children_forum_id",
+ [],
+
+ )}
+
+
+ 上传封面:
+
+ (只支持JPG、PNG、JPEG大小不超过2M建议尺寸4:3)
+
+
+
+ {helper(
+ "帖子标签",
+ "tag_id",
+ [{ required: true, message: "请选择帖子标签" }],
+ ,
+ true
+ )}
+ {helper(
+ "是否原创",
+ "is_original",
+ [],
+
+ 是
+ 否
+ ,
+ true
+ )}
+ { originalType !==1? helper(
+ "转载自",
+ "reprint_link",
+ [],
+
+ ) :""}
+
+
+
+
+
+
+ { forumId ? "保存" : "提交"}
+
+
+
+ );
+}
+export default Form.create()(forwardRef(New));
diff --git a/src/forums/Nodata.js b/src/forums/Nodata.js
new file mode 100644
index 00000000..1e09608f
--- /dev/null
+++ b/src/forums/Nodata.js
@@ -0,0 +1,18 @@
+import React , { Component } from 'react';
+
+import nodata from './image/nodata.png';
+
+class Nodata extends Component{
+ render(){
+ const { _html } = this.props;
+ return(
+
+
+

+
{_html}
+
+
+ )
+ }
+}
+export default Nodata;
\ No newline at end of file
diff --git a/src/forums/Theme.js b/src/forums/Theme.js
new file mode 100644
index 00000000..aa47a549
--- /dev/null
+++ b/src/forums/Theme.js
@@ -0,0 +1,156 @@
+//主题页
+import React, { useState, useEffect } from "react";
+import { Breadcrumb, Menu, Spin } from "antd";
+import { Link } from "react-router-dom";
+import ListSearch from './Component/ListSearch';
+import { Box, Long, Short, Gap, WhiteBack, LeftLine, FlexAJ, AlignCenter } from './css/layout';
+import axios from 'axios';
+import Tops from './Component/Top';
+import ListItem from './Component/ListItem';
+import Paginations from './Component/Paginations';
+import ThemeRight from './ThemeRight';
+import Nodata from './Nodata';
+import './css/Theme.scss';
+
+function theme(props) {
+ const [ operation , setOperation ]= useState(undefined);
+ const [menuKey, setMenuKey] = useState("all");//tab
+ const [search, setSearch] = useState(undefined);//搜索内容
+ const [listSpin, setListSpin] = useState(true);
+ const [sort, setSort] = useState("published_at");//排序
+ const [pageSize, setPageSize] = useState(0);//每页条数
+ const [page, setPage] = useState(1);
+ const [total, setTotal] = useState(0);
+ const [memos, setMemos] = useState(undefined);//帖子列表
+ const [ breadCrumb , setBreadCrumb ] = useState(undefined);
+ const [ headData , setHeadData] = useState(undefined);// 头部信息
+
+ let plateMainId = props.match.params.plateMainId;
+ let current_user = props.current_user;
+
+ // 获取列表
+ useEffect(() => {
+ if (plateMainId) {
+ InitList();
+ }
+ }, [plateMainId, page, search, menuKey, sort])
+
+ async function InitList() {
+ setListSpin(true);
+ const url = `/v1/memos/forum_memos/${plateMainId}.json`;
+ axios.get(url, {
+ params: {
+ page, search, sort, select_type: menuKey
+ }
+ }).then(result => {
+ if (result) {
+ setMemos(result.data.memos);
+ setTotal(result.data.memos_count);
+ setListSpin(false);
+ setPageSize(result.data.limit);
+ }
+ }).catch(error => {
+ console.log(error);
+ })
+ }
+
+ useEffect(()=>{
+ async function getTopInfo(){
+ const url = `/v1/memos/forum_memos_head/${plateMainId}.json`;
+ axios.get(url).then(result=>{
+ if(result && result.data){
+ setBreadCrumb(result.data.bread_crumb.forum_tag);
+ setHeadData(result.data);
+ filterUsers(current_user,result.data.forum_moders,result.data.forum_section_user);
+ }
+ }).catch(error=>{
+ console.log(error);
+ })
+ }
+ if(plateMainId && current_user) getTopInfo();
+ },[plateMainId, current_user])
+
+ let pathname = props.location.pathname;
+ useEffect(() => {
+ if (pathname) {
+ if (pathname === "/forums/involvedTopics") {
+ setMenuKey("my_topics");
+ } else if (pathname === "/forums/topic") {
+ setMenuKey("my_memos");
+ } else if (pathname === "/forums/recommend") {
+ setMenuKey("is_fine");
+ } else {
+ setMenuKey("all");
+ }
+ }
+ }, [pathname])
+ // 判断当前用户是否是版主或者管理员
+ function filterUsers(currentUser,moders,users){
+ if(currentUser){
+ let admin = moders && moders.filter(item=>item.user_login === current_user.login);//管理员
+ let creater = users && users.user_login === current_user.login;//版主
+ setOperation((admin && admin.length>0)|| creater);
+ }
+ }
+ // 搜索
+ function onSearch(e) {
+ setSearch(e);
+ }
+ // 翻页
+ function changePage(page) {
+ setPage(page);
+ }
+
+ function changeMenu(e) {
+ setMenuKey(e.key);
+ }
+ return (
+
+
+ 论坛交流
+ { breadCrumb && breadCrumb.title ? {breadCrumb.title}:"" }
+ { breadCrumb && breadCrumb.children_bread_crumb ?{breadCrumb.children_bread_crumb && breadCrumb.children_bread_crumb.title}:""}
+
+
+
+
+
+
+
+
+ setSort("published_at")} className={sort === "published_at" ? "green cPointer" : "cPointer"}>最新
+ setSort("replies_count")} className={sort === "published_at" ? "cPointer" : "green cPointer"} style={{ fontSize: '14px' }}>最热
+
+
+
+ {
+ memos && memos.length > 0 ?
+
+
+
+ :
+
+
+
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+} export default theme
\ No newline at end of file
diff --git a/src/forums/ThemeRight.jsx b/src/forums/ThemeRight.jsx
new file mode 100644
index 00000000..4028baf7
--- /dev/null
+++ b/src/forums/ThemeRight.jsx
@@ -0,0 +1,52 @@
+import React, { useEffect, useState } from "react";
+import axios from "axios";
+import AuthorItem from './Component/AuthorItem';
+import BestModalItem from './Component/BestModalItem';
+import Announcement from './Component/Announcement';
+
+export default (({ plateId , operation })=> {
+ const [content, setContent] = useState(undefined);//公告
+ const [ recommand , setRecommand ]= useState(undefined);//推荐版块
+ const [ author , setAuthor ]= useState(undefined);//推荐作者
+
+ useEffect(()=>{
+ async function Init(){
+ const url = `/v1/memos/forum_memos_right/${plateId}.json`;
+ axios.get(url).then(result=>{
+ if(result){
+ setContent({notice:result.data.notice,login:result.data.user_login,name:result.data.username});
+ setRecommand(result.data.recommend_forum_sections);
+ setAuthor(result.data.active_users);
+ }
+ }).catch(error=>{
+ console.log(error);
+ })
+ }
+ if(plateId){
+ Init();
+ }
+ },[plateId])
+
+ return (
+
+ {
+ content &&
+
+ }
+ {
+ author && author.length > 0 &&
+
+ }
+ {
+ recommand && recommand.length > 0 &&
+
+
+
+ }
+
+ );
+});
diff --git a/src/forums/css/All.scss b/src/forums/css/All.scss
new file mode 100644
index 00000000..4420a261
--- /dev/null
+++ b/src/forums/css/All.scss
@@ -0,0 +1,285 @@
+// page:UrlItem
+.urllist {
+ & > a {
+ display: flex;
+ justify-content: space-between;
+ padding: 12px 0px;
+ color: #333;
+ align-items: center;
+ border-bottom: 1px solid #eee;
+ i {
+ font-size: 14px;
+ color: #ccc;
+ }
+ }
+ & > a:last-child {
+ border-bottom: none;
+ }
+}
+.memo-detail-ul{
+ .BestUl{height: auto !important;}
+}
+.BestUl {
+ padding: 15px 25px;
+ height: 288px;
+ li {
+ padding: 6px 0px 5px 13px;
+ position: relative;
+ &::before {
+ position: absolute;
+ width: 4px;
+ height: 4px;
+ content: "";
+ border-radius: 50%;
+ background-color: #21b350;
+ left: 0px;
+ top: 14px;
+ }
+ }
+}
+.doubleItems {
+ padding: 0px 30px;
+ display: flex;
+ flex-wrap: wrap;
+ align-items: flex-start;
+
+ & > a {
+ padding: 20px 0px;
+ width: 50%;
+ box-sizing: border-box;
+ border-bottom: 1px solid #eee;
+ display: flex;
+ align-items: flex-start;
+ text-align: justify;
+ cursor: pointer;
+ &:nth-child(odd) {
+ padding-right: 30px;
+ }
+ &:nth-child(even) {
+ padding-left: 30px;
+ }
+ &:last-child {
+ border-bottom: none;
+ }
+ }
+}
+.unlow {
+ border: none !important;
+}
+.authorUl {
+ display: grid;
+ grid-template-columns: repeat(3, 33.33%);
+ grid-template-rows: 65px;
+ padding: 20px 30px;
+ & > span >img{
+ width:40px;
+ height:40px;
+ }
+ & > span:nth-child(3n + 1),
+ & > span:first-child {
+ align-items: flex-start;
+ img{margin-left: 10px;}
+ }
+ & > span:nth-child(3n) {
+ align-items: flex-end;
+ img{margin-right: 10px;}
+ }
+}
+.BestModalUl {
+ padding: 10px 30px;
+ & > div {
+ padding: 10px 0px;
+ }
+}
+.annContent {
+ margin-bottom: 10px;
+ overflow: hidden;
+ height: 100px;
+ line-height: 20px;
+ position: relative;
+ .annWords {
+ margin-bottom: 0px;
+ word-break: break-all;
+ }
+}
+.annBtn {
+ position: absolute;
+ right: 27px;
+ top: 90px;
+ color: #888;
+ background-color: #fff;
+}
+.none_panels{
+ text-align: center;
+ width: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding:20px 0px;
+ img{
+ margin-bottom: 15px;
+ width:60%;
+ }
+ .none_p_title{
+ font-size: 14px;
+ color: #999;
+ }
+}
+.formStyle{
+ padding:20px 30px!important;
+ .lables{
+ position: relative;
+ color:#333;
+ margin-bottom: 5px;
+ display: block;
+ font-size: 16px;
+ }
+ .lables.must::before{
+ content: "*";
+ color: #F73030;
+ font-size: 18px;
+ position: absolute;
+ left: -15px;
+ height: 100%;
+ }
+}
+.UploadStyle{
+ border:none!important;
+ text-align: left!important;
+ background-color: #fff!important;
+ height: 20px!important;
+ line-height: 20px!important;
+ .ant-upload{
+ padding:0px!important;
+ }
+}
+.ant-upload-list-item-card-actions .anticon.anticon-download{
+ display:none!important;
+}
+.inlineEnd{
+ display: flex;
+ align-items: center;
+ .ant-row.ant-form-item{
+ margin-bottom: 0px;
+ width:260px;
+ margin-right: 20px;
+ }
+}
+.headerInfo{
+ padding:20px 0px!important;
+ position: relative;
+ align-items:flex-start!important;
+ .headerInfoLeft{
+ display:flex;
+ flex:1;
+ align-items:flex-start;
+ width:0;
+ }
+ .originalTag{
+ position: absolute;
+ right: 50px;
+ top:20px;
+ z-index: 10;
+ }
+ .tag{
+ margin-top:3px;
+ }
+}
+.icon-wrap {
+ margin-left: 30px;
+ color: #ccc;
+ float: left;
+ line-height: 20px;
+ display: flex;
+ align-items: center;
+ .span-text{
+ margin-left: 3px;
+ }
+}
+.tag{
+ display: inline-block;
+ height: 18px;
+ line-height: 18px;
+ padding:0px 8px;
+ font-size: 12px;
+ color:#fff;
+ margin-left: 8px;
+ border-radius: 12px;
+ &.tagRed{
+ background-color: #FA4A4A;
+ }
+ &.tagBlue{
+ background-color: #50C7FF;
+ }
+ &.tagOrange{
+ background-color: #FA6400;
+ }
+}
+.greenLiftLine{
+ font-size: 16px;
+ color: #333;
+ height: 22px;
+ line-height: 22px;
+ margin-left: 14px;
+ position: relative;
+ &::before{
+ position: absolute;
+ left: -14px;
+ height: 100%;
+ width:6px;
+ content: "";
+ top:0px;
+ background-color: #21B350;
+ }
+}
+.priseBox{
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding:20px 0px;
+ span:first-child{
+ height: 70px;
+ width: 70px;
+ line-height: 70px;
+ border-radius: 50%;
+ background-color:#F3F7F5 ;
+ text-align: center;
+ cursor: pointer;
+ i{
+ font-size: 27px !important;
+ color: #21B350;
+ }
+ }
+ span:last-child{
+ margin-top: 10px;
+ color:#888888 ;
+ };
+}
+.authorCard{
+ padding:40px 35px;
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ img{
+ height: 60px!important;
+ width:60px!important;
+ margin-bottom: 10px;
+ }
+ img + span{
+ color: #333;
+ font-size: 16px;
+ }
+}
+.tip_tag{
+ font-size: 12px;
+ background:rgba(238,238,238,1);
+border-radius:10px;
+color: #888;
+margin-left: 8px;
+padding: 1px 8px;
+}
+.b-bottom-none{border-bottom: none !important;}
+.edu-text-center{text-align: center;}
+.pd510{padding: 5px 10px;}
+.fwt-600{font-weight: 600;}
\ No newline at end of file
diff --git a/src/forums/css/Index.css b/src/forums/css/Index.css
new file mode 100644
index 00000000..d62d9c7a
--- /dev/null
+++ b/src/forums/css/Index.css
@@ -0,0 +1,84 @@
+
+ul,ol,dl{
+ margin:0px;
+}
+p{
+ margin-bottom: 8px;
+}
+.ml4{
+ margin-left: 4px;
+}
+.ml8{
+ margin-left: 8px;
+}
+.mt8{
+ margin-top: 8px;
+}
+.grey-3{
+ color:#333!important;
+}.grey-8{
+ color:#888!important;
+}.grey-9{
+ color:#999!important;
+}
+a.grey-9:hover{
+ color:#5091ff!important
+}
+.green{
+ color: #21B350!important;
+}
+.blue{
+ color: #5091FF!important;
+}
+.orange{
+ color: #FA6400!important;
+}
+.flex1{
+ flex:1;
+}
+.cPointer{
+ cursor: pointer;
+}
+.bigNoData .none_panels>div{
+ width:30%;
+}
+.markdown-body p{
+ white-space: pre-wrap;
+}
+/*超过隐藏*/
+.task-hide {
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+}
+.task-hide2 {
+ overflow:-moz-hidden-unscrollable; white-space: nowrap; text-overflow:ellipsis;
+}
+.center{
+ text-align: center;
+}
+.greenbtn{
+ height:30px;
+ line-height:30px;
+ border-radius:2px;
+ background-color:#28BD6C;
+ color:#fff!important;
+ padding:0px 12px;
+ display:inline-block;
+ min-width:80px;
+ text-align:center;
+}
+.forumList{
+ padding:0px 30px;
+}
+.forumList > li{
+ border-bottom: 1px solid #eee;
+ padding:15px 0px;
+}
+.forumList > li:last-child{
+ border-bottom: none;
+}
+.pd200{padding: 200px 0;}
+.minH400{
+ min-height: 400px;
+}
diff --git a/src/forums/css/Theme.scss b/src/forums/css/Theme.scss
new file mode 100644
index 00000000..05c9a4ec
--- /dev/null
+++ b/src/forums/css/Theme.scss
@@ -0,0 +1,25 @@
+
+.newMenu .ant-menu-item, .ant-menu-submenu-title{
+ margin: 2px 0px 2px 30px!important;
+ padding:0px!important;
+ font-size: 16px;
+}
+.newMenu.ant-menu-horizontal > .ant-menu-item:hover,
+.newMenu.ant-menu-horizontal > .ant-menu-item-selected,
+.newMenu.ant-menu-horizontal > .ant-menu-submenu:hover{
+ color: #21B350!important;
+ border-bottom:2px solid #fff!important;
+}
+.newMenu.ant-menu-horizontal > .ant-menu-item-selected{
+ color: #21B350!important;
+ position:relative;
+}
+.newMenu.ant-menu-horizontal > .ant-menu-item-selected:after{
+ position:absolute;
+ width:100%;
+ height:2px;
+ background:#28BD6C;
+ content:"";
+ left:0px;
+ bottom:-4px;
+}
\ No newline at end of file
diff --git a/src/forums/css/layout.jsx b/src/forums/css/layout.jsx
new file mode 100644
index 00000000..87c7dfc4
--- /dev/null
+++ b/src/forums/css/layout.jsx
@@ -0,0 +1,207 @@
+import styled from 'styled-components';
+
+export const Banner = styled.div`{
+ padding:20px 30px;
+ color:#333;
+ font-size:18px;
+ border-bottom:1px solid #eee;
+ background-color:#fff;
+ border-radius:5px 5px 0px 0px;
+}`
+export const AlignCenterBetween = styled.div`{
+ display:flex;
+ align-items: center;
+ padding: 14px 14px 14px 20px;
+ justify-content: space-between;
+ border-bottom:1px solid #eee;
+}`
+export const FlexAJ = styled.div`{
+ display:flex;
+ align-items: center;
+ justify-content: space-between;
+}`
+export const AlignCenter = styled.div`{
+ display:flex;
+ align-items: center;
+}`
+export const FlexStart = styled.div`{
+ display:flex;
+ align-items: flex-start;
+}`
+export const P = styled.p`{
+ color:#333;
+ font-size:18px;
+ height:24px;
+ line-height:24px;
+ margin-bottom:8px;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+}`
+// 左右结构
+export const Box = styled.div`{
+ display:flex;
+ align-item:flex-start;
+}`
+export const Long = styled.div`{
+ width:72%;
+ border-radius:5px;
+ margin-bottom:15px;
+}`
+export const Short = styled.div`{
+ width:28%;
+ border-radius:5px;
+ margin-bottom:15px;
+}`
+export const Gap = styled.div`{
+ padding-left:15px;
+ box-sizing:border-box;
+}`
+export const WhiteBack = styled.div`{
+ background-color:#fff;
+ border-radius:5px;
+}`
+export const Blueline = styled.a`{
+ height:30px;
+ line-height:28px;
+ border-radius:2px;
+ border:1px solid rgba(80,145,255,1);
+ color:rgba(80,145,255,1);
+ padding:0px 12px;
+ display:inline-block;
+}`
+export const Redline = styled.a`{
+ height:30px;
+ line-height:28px;
+ border-radius:2px;
+ border:1px solid #F73030;
+ color:#F73030;
+ padding:0px 12px;
+ display:inline-block;
+ min-width:80px;
+ text-align:center;
+}`
+export const Greenline = styled.a`{
+ height:30px;
+ line-height:28px;
+ border-radius:2px;
+ border:1px solid #28BD6C;
+ color:#28BD6C!important;
+ padding:0px 12px;
+ display:inline-block;
+ min-width:80px;
+ text-align:center;
+}`
+export const Greenback = styled.a`{
+ height:30px;
+ line-height:30px;
+ border-radius:2px;
+ background-color:#28BD6C;
+ color:#fff!important;
+ padding:0px 12px;
+ display:inline-block;
+ min-width:80px;
+ text-align:center;
+}`
+export const Blueback = styled.a`{
+ height:30px;
+ line-height:30px;
+ border-radius:2px;
+ background-color:rgba(80,145,255,1);
+ color:#fff;
+ padding:0px 12px;
+ display:inline-block;
+ min-width:80px;
+ text-align:center;
+}`
+export const Redback = styled.a`{
+ height:30px;
+ line-height:30px;
+ border-radius:2px;
+ background-color:#F73030;
+ color:#fff;
+ padding:0px 12px;
+ display:inline-block;
+ min-width:80px;
+ text-align:center;
+}`
+export const NumUl = styled.ul`{
+ padding-left: 20px;
+ & > li{
+ list-style-type: decimal;
+ color:#888;
+ height:24px;
+ line-height:24px;
+ }
+}`
+export const GreenUnder = styled.a`{
+ color:#28BD6C!important;
+ position:relative;
+ &:after{
+ position:absolute;
+ bottom:-2px;
+ left:0px;
+ width:100%;
+ height:1px;
+ content:'';
+ background:#28BD6C;
+ }
+}`
+export const Cancel = styled.a`{
+ height:32px;
+ line-height:32px;
+ border-radius:2px;
+ background-color:#BBBBBB;
+ color:#fff;
+ padding:0px 12px;
+ display:inline-block;
+ min-width:64px;
+ text-align:center;
+ letter-spacing: 4px;
+}`
+export const Content = styled.div`{
+ width:1200px;
+ margin:20px auto;
+ text-align:center;
+ display:flex;
+ align-Items:center;
+ background-color:#fff;
+ justify-content: center;
+}`
+export const LeftLine = styled.span`{
+ font-size: 12px;
+ color: #999;
+ position: relative;
+ margin-left: 20px;
+ height: 18px;
+ line-height: 18px;
+ &:before{
+ position: absolute;
+ left: -10px;
+ top: 50%;
+ margin-top:-6px;
+ height: 12px;
+ width: 1px;
+ content: "";
+ background: #ccc;
+ }
+}`
+export const Grid = styled.div`{
+ display:grid;
+ grid-template-columns: repeat(3, 33.33%);
+ grid-template-rows: 65px;
+ width: 100%;
+}`
+export const UDStructure = styled.span`{
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ span:first-child{
+ color:#333;
+ font-size:20px;
+ margin-bottom:6px;
+ }
+ span:last-child{
+ font-size:14px;
+ }
+}`
diff --git a/src/forums/image/nodata.png b/src/forums/image/nodata.png
new file mode 100644
index 0000000000000000000000000000000000000000..ad4c76b97b4100d34558aa4a36838e650d1dbc36
GIT binary patch
literal 5877
zcmVPy0uSrBfRCod1oC$bTRhGxADwTzVkc4a`BxEH40ttHvOGp4&Y!H`L5XB9rM;$-A
z)t>R|Z>DGb?6Ga_aqRACmu?kA6hx49WM9G}hFumRkc})r0s#VKC#jm>Df2RAQI)D!
zON_qnyWf59zI)F-_y5j4_ndQYl}7UuzZNZ8H1xv{Kg{nA!_;>NCOpLhAdfc~42$)8
z{ezzBLk(S>n#w<>x(gRB6o3~RjmFuJKKkf^?!fy`PO?A${By6HH*da%L{ISb^_@6>
z{``yG0chdjCj~ar9DC!&jb&P`_C$1abk3c3-r3w;fEGTq-H91jctJlug35Rq8ZSNh
z2$w+
z{PD+^x(bG;+k1Js19#I$f_44+^_$NkxEx77&muS%YvYi+`|$+5AB|*-l~Gq$_W)r3
z1|Uv)dwc)uBab{%>MAHKs=t9|E9&QuuF|sCzT4~Uk3ar6;MT2MBFVpEFoytU9t+?}
zdjoUiJwQJVy_pda5qCIJ-_<(Z7VHl`_+T(<;;ELFmRaO$NOwN*#1kd14p?cA_3|r#
zo*Nh#IBnLfSq)0LC-ZIvHd35`6wd*KIe>eM^mnB0U)%`>v~@`I8-SWYy5oTd9=PdA
zfX!+DpgQ}|p+nx^eDh5f3{53X2V7s$XXxYQcz#9ZjLQ9|pMDzB)YK%BKAgwB#5^{l
zx9ctI*?Mkozl`@~85tSRjvP7imaV#;DDP6Rk*IKh8iidvn!!jC=@LD>8Q?ZQ_Sj>^
zmV;!vN7H6Db?znQ@V;XI*^u@cGQ}5Em^W88C`vk08HIX
zq3!4Ar|I9nf907oXJUMOd``}sIWw%Ns7O~>SQv5r`gOsqmAbpgSM%Z4U=E&_!u9Ld
z`(L_r=@n>9=5fBe7FVII_Hql@NZ$bl(~rT{!oU?$4XKLfV#*68_3G8D7|Cn2#h}EU
z+uGXhp^bn%MCu`}Z7C@!rGPB=i4^IUEnB7m%)I;WzduLn$c1XDE-5K#<3mJWy?Rvx
z4I{-8>DvOW2YLPkwefpvA6D9T)#VnjonaObbc+`+&H`Xj5BESLLFfbp1vRIprj{lr
zC;Rm1(L>6<$D=i{rJZs$H#gVp*|Vp$tgNh$QU=&3>0=eMX*HhaH%fU|=ieKMr#qJg
z@%ZuMlTa}N?%kwF=`$oGq<+w#L8bBW@qws|I4QGrbpp1bzP^6%u3fu){r&y@va+)L
z05=@aYtNoN+xNnS3%)`V;Lnk*BCSSu@8j9PeV1Gw2fN)Vna*fLJL^*J9buozD(~jl0|vclimJjB%6!>ZUN5spLJRO`kq}ss|4qd_E>7
zCJL#O1yuDVU<1x>Y;WDMW5-&N*hKVkWkNzi3^e;WLXeqtlOWc`f`Wp`^78T^o~4Ai9VKicuGMP#_U&7S)p40%em@2hp{BiA-8KLzK!PuA
z+_*6pDQyDaBLfEx3_!1^Q_rDsn9=LFg>J*;%a_XmKD4y7G#uKZY8*BxI|w;&;}DZr
z3PWJxxC8WrQB8b&{8<35#rqR;ueT%Z%<8o8QLC^pDv{co=-i&w)zxufVPUmI`)(dM
za3BlQFK^DAIXPx++;!W;2R(4|j{gEuR5#iZO#zRKLBbf!axz+Sp^X;?!;LnR%U
z78%QqV4{s_dcw~-
z2tNY)9s{Z?BpX}3?9{1KEu>_TXx4=$7WR|a|2>K2gr7WlG6+E#j?I3;92oKP!_VJ^*n6=jt
zo(q5(kKT?0Oqp?Kh71|fgrhZxL70q#(W6J#fq|Zn@D7rm-L6_38lx((Su}o$`I
z=EO57lXMnq>ebDgHxGvCStK3xs>hEXf1brnECyA5nRnp9`!?MBk`pIR1T(g901v`?
zs^SPl0cNT!?zhcQ%6d+mIPt=!O`FCu%O8Qa#g6jluo{u*8w*tdF$poZM6cSq%316d
z5dey2HGi0|--7^s9Uk2o^z%TN^|#f~k#cva5v1}Wag^q7zx_78v9VD+*oO4<^kNqI
z2qfC%)!O3)p=!oCx`D@=`p`oUU9(qT-m8(e|AL`FfVM}sUvO~ne-MwZW>9aeUAtC~
z)$xlhTeggVu~u~YF=jz5i>X?Z&|bftzDH{70p}u)P%Nfo6yq@B!4{&2y}6I4v8u&A
zH#fH)D=nOl^(t+j>9q6qmkvob(b|TdVzI^=jR7ATQe~Q
z?e6gF3FyZHz+0rDbZ!+$zzSnwFggLWj4uQ!{P)}Pj9#cC9YXXj_+C>k-lP}!2Z{M4^$n$U*?gJPv0J(yd
za|MY@u|vn|YsS!o$61XON0W#3?%lf$OQ{sTgIH@sWjRIHYG>_*fUBQ0X;RG>UwknV
zq5TEL?{!+Ah3^K826k3y_UzfM0Io*AcScTm*UHUO%*K!R?%jJA_Y2@701Ku8(96Xd
zpzO9jE!7AP#|7-(Kt7r{MjCW*752UXJ*-MSDNH$&BREZ%yp7D7RKm8GZPD-oNKqeY
zwkjYQ8r*QJ>cv(Y2g`+K2H-x71Xd#1zeK_d2@7P9B>B%QwRC>+C@$vQCIQoJP<|g9Rg7W5Gn{8n`Xj5CgSZ
zMN%!x8eofF{UNSwI^B%^dmfW&3XFcCFu`ow)*XQs{s_ihQ24aSbqLJwZnHB{D-lfL
zRd{?l7R3%-Y0OYRqnX+HqBZ@ZL01k(x?g2+nvSKQL6QuZHC9!Q2L_>#T(e@u
z3OT)60l;FT%ST_HkPshLT6!)Uy}cLEu{k2o
z<`-&DeiBi@p`j~Q!B#3`gOBI_RSw8xqmRU~QC@XLKADf6&H&!R=H6aaRV9n$@)x($!cS`kI=W`{7Acg>09tCB4JivcnM`It5!H0O(V>
z|4(%ID69cvWMrg6?UeRT@Q33eyR(^WMM%sI
z20X%vKI}CSk9h|+{~>hoPym)w0?N4GpwBv{*)^Gt1>xbL{~H?{I|V04=zZJ2fAV0~k$8@Md`XZ$i#W?J
zyhfRFie8s`u3R<5Ru&<}ySXN?;ruf|me}?lQZ0lfuJRCP1}oKeqH6J@gEgE6?F|DT
zR#a5TF)_d4!-u;YbcqdrLfXyXj2RijtS&q$Dg86y;S7f+AB3N`IPCMh+QD1Y-;JfS
zgDVxs>K_2uhyHJ(bN@DK)Tq+ly@T)Duwm1;v~ha(Nfp@U%uSq_U0GgUv6ksRhpnR+
zl0*gFA?Y8Wt260q1iS2cK0e+uea*Wz-BT9MF3?e|fkdQGe#EpMb5N`THJ5~cm!gxZ
zIWuw}G+&m+fP9L8*y60L%s^BXF&j-(I}3&F#J8hb=v`wv2!zAhlWe`1zY
z1_b!auY}(RXnWn-d{6saUU~*>vtGvy7?3)S=Y6M77nA_{eGI$@3#nNZySIO#hQva&
zLx*OF(~@ql1$84POqg&GRZ`E+_%j5j{>H3(+N|ASKEyJ%=Yp_e9;f`N_IG(i3l|e9
z?BnI7lV6SXBbhaG`gWhKmM%-S6~bc&4jeER#`7`!D;fA~)QdOA8mv_PUeO7Dxllm6
zhdFmrj{ThD=-~Bwub&aaevc3Q6!GpsoafjL<+Ff)0BINd{56s{QY^9izw$x0#KpyO
zqE;&^%iqy{6mFrjlY3El-e#!*I-;|;DD^CJPk?Re1BS+=r47=+z<#Fg6{Khyi>Mb~
zsU;7}^ZyVkc#a1jz&J&_>B%0UC_~^&}^^oCDYq
z9_-ceIM@mUfIAB7MRr*Z1G+DhQ-Fl3~Xs>I)?Ld9ANw~
zANhrs*41MMVjGX{Za@pRswErgYeD~NSn1Dmva=P?;#~PiUS9eP%sfp-MtT{vvjE8z
za-ME0o~_JErSH7jNu*io1l@GC!4IId6;-tarfbNuwscHe?C_x}Wdm>-P*mj=ti;GJ>
zCFc7iQkj9-CWpokVFgJ1S9Vw2c5U3aNobkk*={3hgAgF!;%uOIMMb3{IJnmaJYj>k
zw~vHcy#TsgulG*l_d;@&wKnbOwQwpRHy0S9d~
zfV?^yOsq>tt0
z?Mmw1JLDkokz1&lv7NN<@>e)mZJd$uI(M$j2*@irbM41r0{OcQTOhad*eMP+$Ds;r
zB=-V<%8G3Bg3)jvAKyPrn>O|I<;ykC@_Y5WuU@@2b^G=mM$X}F9x@~?LZ|Z@W|Ke{
zyuf-g5J%AgavLD8%FWI3M-XK5KufaAf#6`bl9dXoz=jv%7e>)nb}yJ77#0@#4jZ&v
z3AsLV?%cU&ILbSX-`zB_OTQA=eF#KS?Gkch1$YAFb2@;&g>|u--6lDN`E$x00$E|@
zI~;Q35Rlc3L+WCU)X;V?0kvw?ZPhlYu0B5n2Wma-
z{M5FE(K>^n?Je}VH-3VIQ~imR6tIZyEgz*reBMXGKQ#-DXkl(4*=vD^xi?z;=~!IkzDW^>TZq%KG|-KXO!HIIm<6P{)SB
zuCZgs6tNZdAQ;{Di!zm91M+z!xuiiBK7K>PI-zw1WElaw{@OVlQQj&K&RkA`EghkI
zIlEz43^3WwbS!O<^g}of?{UC=9&{#AWMD^U<=C-V&FE4Ay<<&r1Z?OT5d={ebwY<9
zNJRniG-vr!+Ci6N6}m@R;Lj;#&g4#mt#rnshh!RG0hoBDOl{6!Rug;J2v9FTX8_NQ
zoW5F!xyb*L=;Tjfw4H%1AoKCHMMcFo`OW2u^z^~8fWz!_?Sk$(MB?B1Wx*2HD1F!3
zWw3<|V0c$oUzp$4W)wZ_)FBqkDcMxd
zh|M~kPX8GPIrEircjj&f+nhOo9}MubDZ2~shXY)P4mPi6su$)Oz65nlm#9RQzDO+;k_kAiK^3t;uYPR~GEM>5cn
ztc9FB)efZG%e!iS=ZJx{j~w?X^fu3^mhnz
z^AL9OG0GclweduGmw|1Kc?RxFq%f8bK*+X@Lo)J90L=%-gVkmP+rbGxmP*BRb>5)0NSYW&+Z
z1rGoIuo+u2i>)Ah=p%
zN)f7{q)H(FBnkkI1z_#KIqblhb--$$1E0U^bm%Deh#D^~e=!7O?-s
zoc?x2ax;O}NV;^V8QCDFR4K8Q$$#^%Jn1+hw<1N!>}Cb7v|(&$!OJv7Z%RnIbha+XsVO^|i2R?^tpB
z;bK}*!L4YV)d&vt6X5i@0Ae`^t2QoD&yo8l*zTPMU>(5OEkH3MV5&9-Vf7}wr^=c2
zU^)4V-Ilp~4b2*6224$V%DXYoimybG>N#D92#wgD6t$ERb_u2plI6tjxeghy#Igswj1g11}0?e
z$4dH@Bg5?K7mL7V>>>!R)-zz-hE%>nl#3$s6T6HOw;Z%w68VwD)rDboFTi^HC{O}1
z2I%I>3^-69iQogW$D>VE^+X+DV|qGLUwk08Hh2MZJZmw?MyPl2)Zu)S%+F`V2=)
ztw_!Ms2P$zQ)h`9cmFr&iI)s$KP?x21N;3<;PM}WwA(lkHjMDt*7*B;u-!SiJaF>P
zk9-YKDn)AMhQ~;HPFgQ&8C|OOo`gc5(*E!&usQd@@!|n+hJ0X68sb9|+bfa+t*k`c
zB&Nz6WMxs30AF(?zjIa-`jTMD+zsN^gFxKw9iO{}HROSQ#b}mRR&1*HaP<{$1Np58
zZDQBW@
z$bF3Os~=ClJ94r>w{E;i5D-#J@lo>g;`Ml~NX_>>7X*B;dJ-K})o(fQfG})LAlp~g
zZ(6ypLPyShFr_XgC%iN&9t5>o_$?#f2B!Usz;-=ty=wCAQGP2@J-b6)al2vHB;6;7
z_zqZDkF)4Ozj-PMD%S^Tk4a^+Q^m)|)83r12CUc9LDKns5I64!;0IQT{h_g>F4GOA_T=jjT2Y>`!lisD5Xkf*b$%9yp^KA?+lm^
zEFpfNr2BYaD%9fH(nB?l1NFC&&g2>Z!2}dz*Lv@hb%1X5hrn43@2TRqZYr$obw%bU
zM$A>((CVqz5cl_5%}A!>8kor?ctkdQiN-tX%RkY~DjK9b8JH9clORAgotpF_cP~
z=pSONZ9VswYbdDR1Oj3f`uyS-XDoSENmD~QW%0d
zZHmHeZaQZ3AA@eqSeq!gQeD{xYqLGBNcH@l%ajwhjiba)P{4XE4Fq*s1C0rL4+UKj
z{iaVqIc`I#-5h)NB`3Tklz=n};jQ}K^}`jUT}m0bAefa9{uWG3nbMYngLCXn*w|0WK)j$xp8|D{qVZF2$Oovv0a$It$cLCoyy4J(Ikmv|fP(^i0>+M6KhhVT~uRe^_u<WhyoH+leQ;eTGy>2)$@C3DJzjH!zO>{gKWm`D`qt02DzLqSQ07+|nIMS(79r1Jx)DuaH*
z6i~*lh0=tvzQ)Gi2_SNM3s_EV1xg^gZ8ce2R{VUAuCT3JMd~+BxhNn0V;ELq4f%ut
zhW?|cf=mYOlA)lSyxXgLg4^Gmz7p(rPlKfEhad_=c*{qouV7F->`W;?0;Q+`Z2m(K
zRBHn0_u~DGL3
z8E3q6XxMS(qbV>S-$1sQG-51CALuts21VRDvN~B9^l|!G3T-SilzD`cDJDEhYxvPk+ZvWlMej)ps0~EO=-E5LUJkI
zP;mXGPeIat7zk>&BHO@xWGz^)9U>^WsKwj7RtNfW=EJK=YM?|Ci*9t##UAtyJ3YT|ak6p4&CbLtYHRh0?=k(!=O+5NL%
z-2B;)z-j5*JSsAGc6cwvkZ)3iZ#WSiOl~1(#~UeBw{{%Jdc*@}6U|4~fv8~wNV*`r
z4BUgd#)={VZY~ByFi-!W(Eg)*%pQLAXYhno#0$n6e1&L3_eGAb|CIJ9R
zcJW9Txa0dcp5FuG?%5szUGu5UAa2&Lh`GUBx^Ckq!-A)$ztnySKa#}RJ*Hd~w{mmp
z;n%#pk0=A7CUP(Y@Q1f-cTRv|%QR4oT|@FErr-Zdj5~t0ND@U`iR?AYx!s_MOZE!+
z+Yr{127-V`Mv~P$bpj%fZ@2&=&d-E0Su=Nm{2e!0R*nPHv1Dgj*ClYU-8=%Ooilo?
z64Lj2T#+y4bgL@wI`SbJeJqX_S->F_Z!r?*6c{1MqT)*p+8Q=bCDDlpx75ZdAd8*>
zmQ$O734I;J9flK{9iG6LI}z6J$j4H6-Z?z@K9;lFfz8bZY0t?e`azN`r+-m`j*Xsy
z?ZBeOifKo#c~m4fGhAS5+gC5`{U5>|L0BCDRt}ALL>x&tl_imdCodxCicw35!ff|5
zAz=6qz`n==)4}DyRH_RICSz!dhYmToc!rTLHGzQ0)dM6P^~Luw?wJdMn$1bX#kXK^
zpg+1z5jrbf?@^KXgMQ=Wi?Si#hT%%FoY_vCTXB1Yw~7+r`3_vX90=aNJ}A}-*t@xmDiyoG?s
zy|W+*|Bx5o2hEmY^XJYJ+Yf|Yn>1p-YTSk*?AjuV)UF!UkbsY;Zkf
zuoCj(wLcC1CTQB>nXZk9B8|&NR<&6)KLAY+m2@6M5!l{eyToJCu2nEAsa-e_6tU~T
z_}jN24(kQB`)2`tQ#^pY!FGlHkkgaETXjI%%}>4$%L3Wf182ECIB|tr?;Hn7vwlTp
zMj*8vU-eZ@%lM;+BJ+Rj_crBlY!NkoOY|xjV&3$NK!mtOKSDc^510kOFhEPuPI|v4
zhrS<@vXFp)kyL5;K1jO4m|QV(iCYmd{3L#At)gIydEWwy@x=bnkXb1&AvElO%3n36
zCarmLJK%$rLQl7}+>PWfZ^m68h8;6WdI9}NK83(r2#DM`Ok(oV@O{WQ91FVDV+kJM
zE*e9(2i+nP8oJyW!gku_@1>|d-dy5{CtF*yaKId_NPhwnucr(R2
zC$Y65&u6ZN@O?7(kl47ad>@nn<7?|h1c?{Bov$PRA=tCdJ3o|5!O+mKapKs(g!EO#
z8xlp{pBXL*4iMjy4VhO3eJWn1G`tyJ9T%x(V
zes6(KN=k7VF)Xy~U({Ch<&o1q6=_^GvW-ozIjM+CrpQ|oK1ykMGj6*j@Ky?#2)xy<
z;5r+BK*Xo4mr@9)GGNp)pyj^l+OGYfE!VFZdr}$uv&RfziO5E?AUavts9Ovlhqx@f
znW9D<`96z*5=!~;eSB%H*zX90m^0RbeE5&Rgt&jf0F{MXAMbpZSdE#xz`So!_rU3g
z4tg58gd+7ThgGtx)mZ`WFOx78Jm)P6J1i}42HU^n`#eOxPaFtIp2#POn7^UhDPmtM^2s~nC~$<19;uRd*LVFiTJ
zG;+yp*?<|f{K?(;oZw5oPZFV>5J1EazK_rTu>y{9WgnQ*lAL4&4ihB>n*-R
z=*`}WSA(Sfp>LX`=w{LF#e-BZAtH8)Tt%{2m9BXUGe}0|hgR
z7&;2;;o-{9)|R*oUm$@gb+OfwxvgDLLdJPMLU|oczxMqmc6IJW#n^R}Hyh(_AijJb
z1R(GZq3uQn!32?+#=O>$P+JfOMaD30O@y=%^5XP)gp}{dxl6)0u$f7`jN7>IGAEyj
zd#hu;@EaKa`0tpY=^1PI_T}@lss9(9B&^>dR@Ub;zI~D{RvNw!QP88SV1IIxJnq5a
z8)#+&V607^sF5mxmIVP_p$4JRAR7(;`UA?k!x&vBh8aS`mTBJyPTk9Y!hF`hdg<^W
z&XD&&5wl29{JDbSn#KG!-h)B#QxY|y+*o0}zo3_j$fKC
zQswR##P?B4UWxNZJ*l
zKLU-piP{KzV%zkcD#@SkP;r?yKK;7Z;iX9O!d^hHYqGvm-zf411U|TNevena53U4!
zZK}Ml6!V!VJ_v$59eegg!j?mqt9a@F`@x$O#vL=ZD)TkNDG&FteT~OUk=TiT*@&L3
z!;%sZyPm>Z{`kNKL8-ORj{_|@BknLPUflNG67*Yij4hYhM-1Hdg=e*6+f`k$o`7
z?YN)$804z|sK#&kg>3)Ch=EL4ul16s3G(8X%`qmoKfDC?XLm`MhL|Vc7Y9l+G0W9d
z0G0(Ij^n}MN3X)1y2NBVzbnp_!&92^u#I1e)U18K9+&(45+xANe@>ed3-93-4vw#r
zLWP>^q&=s!amOe_Cl^WecqyIFg?XQoLMDX6TH>`7RFbzK0_~w`-=gb|C%0k(r>9|Z
zrp(1JMdE1OqTXVv;3A{D(YY-{81a+6DZzXNtiN^PVJkj*X$0^|0U*=j&_WbNA!PAdi;yVw%^*1M~yXu!8n-C6i{vH
z;OgxJmFl;o6cs`ctiU(W2$`P|IPpY^cG^z_E=(F=HO~+^%5mgAxZ=pky2NnyG=+*z
zqsIS5wJQ$=@$KUuRixWfe`Z@{J|nKlFiZ`WV}pU>gcL`MSc+vB8q@%EE&!KnF;m&0
jvR`VZMqhUhn?_K|p&tIFLF<00000NkvXXu0mjf0=}^n
literal 0
HcmV?d00001
diff --git a/src/forums/image/radius.png b/src/forums/image/radius.png
new file mode 100755
index 0000000000000000000000000000000000000000..a4da037fd7bc73d486b9861edb258e11f6280a27
GIT binary patch
literal 1623
zcmV-d2B`UoP)7%Q6vzK>{O!h0?2t6ELz7A^NZb${K$NzsMAb@w)B{443nwJtLZxy5i4zhRDwPW$
zae)g(h(ixWYT;1P4?Ye^y&*xX#tC-fG>z@Vjz8D?&}?GMNgVI&dTY1x+$_J3`R&Zk
zy!U37@POLF!a}eX9<%c
zR(ud$BPWff^Xc)Drkxf{r*!Dg5y)%DK7*?Q;!mCUV!-XbFcF%#XU9%E9Y=uYvkQkA
ze3qF{0&qLkxlKD75nU$GxqXq?^=@r<1^i!j@o1gvp8z=2ZATRKZ@S3!@#B%nUlrwd
zh}i;8EGH&~$8(MuZ!ohh8@4-4#6RGyF{
z9dA#MPW@=bt}-w)d*`yL^F;JcuSaz^AR=OB{%SfBKSM-S6>ufs+bg%jUJ<;=%*RxW
z_F9IBu2%Kpse_{j*UWsI0^dpB8FzdAR{%V1W~|Tu-_ok(69*;^blk!^0?U#0P(ZGT
z4?Aq*S>LL0>1d~s-W6EBv-7!y%Ma@`?(kk;-8T|Dx!XIvD{vx{`~twceV(Rv-G4a~
zoqA6>(vn*b1pV-^tj1QU0QcUvIZ%?HFMhRKV^2yYKrffIZ#-yeQi-6o9*0Y~lvJ1*%j+4G
zYUQ@_DzJ8;1&Je#)UpYj$RsZTcv-3U0EYb`j0D1};)a}j57YVO9XeKEZ9@Mv6PKeeZQc;BY`l&ext-?tF4yv$dycU1~B2w{>aql2Jl=ab&bKz7hA=%Q^4`z3CP9I
zZrklO*=~Jm{nl6a2uf|k!e}1
zX*yHPAzxWHb=3~Kdst&i-={=2Sq5WgYGT?gz~}ZF@3Kd@JA&8sjpSjiQc$qEAtJ-p
zuy4q)D~VQQRE=?d7?L6-tD?36)CP1m}#w5cwE?M`WDdeB=d#+07RrtUKH
z{@%Lok2fNkwY@F%r_0@WHMS+YfroVMRK-KC^D<+SHLOmR6PX4UYb9d=w9|~h!)|El
zWW{4_PZU9KK$)RXS;tB-r!3zP+z2gmsh3#iU#x07Rq>FSlFXQ_w&ZFWa=WwAjO3x;
z>7n24!`_hPxCe`C_ms0F`B0kaq5RR$0zj{Tn*{&~+|LqEuYk9dc=}ny>k;tQBAy&&
zt7TfJ^5I?p6VavVNc>gFX}b{rC;_)E#BYwW@=?np1blNQI`v{>`?7tQ_9y_G9;V5s
zpn6>DRK-J{|4xR-t;7$8Y-)80nn~JddR%HuYsCX`Co3Kkp)sSFpxVl{3YH4@JBzCx
zh#P^3;*|%dDjqHpM&-os@k4Yd8Og)&QnidsX|?0QxTPx(jmI`nms*V2bI`6WN$7#P
znAVuu19kPV0X#a?}i_dm#J
V+<}otg}wj)002ovPDHLkV1kSeCx`$5
literal 0
HcmV?d00001
diff --git a/src/forums/new_comments/Title.jsx b/src/forums/new_comments/Title.jsx
new file mode 100644
index 00000000..c31adbd1
--- /dev/null
+++ b/src/forums/new_comments/Title.jsx
@@ -0,0 +1,19 @@
+import React from 'react';
+import styled from 'styled-components';
+
+const Nav = styled.div`{
+ background-color:#fff;
+ padding:20px 30px;
+ border-bottom:1px solid #eee;
+ font-size:16px;
+ color:#333;
+ display:flex;
+ justify-content: space-between;
+ align-items:center;
+}`
+
+export default (({children,className})=>{
+ return(
+
+ )
+})
\ No newline at end of file
diff --git a/src/forums/new_comments/children_journals.js b/src/forums/new_comments/children_journals.js
new file mode 100644
index 00000000..68a1ad6f
--- /dev/null
+++ b/src/forums/new_comments/children_journals.js
@@ -0,0 +1,26 @@
+import React, { useState, useEffect } from "react";
+import { List } from "antd";
+import CommentList from "./comment_list";
+function children_journals({ replies, user_image, current_login }) {
+
+ return (
+
+ (
+
+
+
+ )}
+ />
+
+ );
+}
+export default children_journals;
diff --git a/src/forums/new_comments/comment.scss b/src/forums/new_comments/comment.scss
new file mode 100644
index 00000000..a092c3fd
--- /dev/null
+++ b/src/forums/new_comments/comment.scss
@@ -0,0 +1,59 @@
+.comments-lists{
+ padding: 10px 30px 15px 30px;
+ .ant-list-item{padding: 16px 20px !important;}
+ .new-comment-head{
+ background-color: #fafafa;
+ }
+ .user-image{
+ width: 30px;
+ height: 30px;
+ border-radius: 50%;
+ }
+ .add_reply_button{
+ background-color: #fff!important;
+ color: #bbb;
+ width: 100%;
+ text-align: left;
+ border: 1px solid #eee;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+ }
+}
+.grid-item{
+ display: grid;
+ grid-template-columns: max-content 1fr;
+ align-items: center;
+}
+.grid-item-3{
+ display: grid;
+ grid-template-columns: max-content max-content 1fr;
+ align-items: center;
+}
+.grid-item-left{
+ display: grid;
+ grid-template-columns: 1fr max-content;
+ align-items: center;
+}
+.ver-middle{vertical-align: middle;}
+.grid-item-top {
+ display: grid;
+ align-items: center;
+ grid-template-columns: max-content 1fr;
+ align-items: flex-start;
+}
+.pd20{padding: 20px;}
+.width100{width: 100%;}
+.color-grey-8{color: #888;}
+.children-memo-item{
+ // padding: 0 20px ;
+ background-color: #fafafa;
+ // margin-left: 40px;
+ margin-top: 10px;
+}
+.color-green{
+ color: #21B350;
+}
+.user-is-star{
+ background-color: #21B350 !important;
+ color: #fff !important;
+}
diff --git a/src/forums/new_comments/comment_list.js b/src/forums/new_comments/comment_list.js
new file mode 100644
index 00000000..b0354306
--- /dev/null
+++ b/src/forums/new_comments/comment_list.js
@@ -0,0 +1,241 @@
+import React, { useState, useEffect } from "react";
+import RenderHtml from "../../components/render-html";
+import { Popconfirm, Button, message } from "antd";
+import { Link } from "react-router-dom";
+import NewComment from "./new_comment";
+import { getUrl } from "educoder";
+import axios from "axios";
+import ChildrenJournals from "./children_journals";
+function comment_list({
+ item,
+ user_image,
+ current_login,
+ is_children,
+ target_type,
+ commet_destroy,
+}) {
+ const [is_reply, setIsReply] = useState(false);
+ const [is_delete, setIsDelete] = useState(false);
+ const [deleteSpin, setDeleteSpin] = useState(false);
+ const [subComments, setSubComments] = useState([]);
+ const [subCommentsCount, setSubCommentsCount] = useState(0);
+ const [page, setChangePage] = useState(1);
+ const [is_praise, setIsPraise] = useState(false);
+ const [praisesCont, setPraisesCount] = useState(0);
+ const [isSpin, setIsSpin] = useState(false);
+ const [limit, setLimit] = useState(5);
+ useEffect(() => {
+ setSubComments(item.children);
+ if (item.children) {
+ setSubCommentsCount(item.children.length);
+ }else{
+ setSubCommentsCount(0);
+ }
+ setIsPraise(item.user_praise);
+ setPraisesCount(item.praise_count);
+ }, [item]);
+ const is_reply_click = (reply_boolean) => {
+ if (current_login) {
+ setIsReply(reply_boolean);
+ } else {
+ window.open("/login", "_blank");
+ }
+ };
+
+ const praise_reply = (id) => {
+ if (current_login) {
+ axios
+ .post(`/v1/discusses/${id}/plus.json`, {
+ container_type: "Memo",
+ id: id,
+ type: is_praise ? 0 : 1,
+ })
+ .then((result) => {
+ setPraisesCount(result.data.praise_count);
+ setIsPraise(!is_praise);
+ message.success(is_praise ? "取消点赞" : "已点赞")
+ })
+ .catch((error) => {
+ message.error(error);
+ });
+ } else {
+ window.open("/login", "_blank");
+ }
+ };
+
+ function deleteorder(id) {
+ setDeleteSpin(true);
+ let url = `/v1/memos/${id}/destroy.json`;
+ axios
+ .post(url)
+ .then((result) => {
+ if (result) {
+ if (result.data.status === 0) {
+ setIsDelete(true);
+ if (is_children) {
+ setSubCommentsCount(subCommentsCount - 1);
+ } else {
+ commet_destroy();
+ }
+ message.success(result.data.message);
+ } else {
+ message.error(result.data.message);
+ }
+ }
+ setDeleteSpin(false);
+ })
+ .catch((e) => {
+ setDeleteSpin(false);
+ message.error(e);
+ });
+ }
+ const create_new_children = (reply) => {
+ if (subComments && subComments.length > 0) {
+ setSubComments([reply, ...subComments]);
+ } else {
+ setSubComments([reply]);
+ }
+ setSubCommentsCount(subCommentsCount + 1);
+ setIsReply(false);
+ };
+
+ function get_more_reply(id) {
+ let new_page = page;
+ if (subCommentsCount < limit) {
+ setChangePage(1);
+ new_page = 1;
+ } else {
+ new_page = page + 1;
+ setChangePage(new_page);
+ }
+ setIsSpin(true);
+ let url = `/v1/${target_type}/${id}/more_reply.json`;
+ axios
+ .get(url, {
+ params: { page: new_page, limit },
+ })
+ .then((result) => {
+ if (result && result.data.memo_replies) {
+ setSubComments(subComments.concat(result.data.memo_replies));
+ }
+ setIsSpin(false);
+ })
+ .catch((e) => {
+ setIsSpin(false);
+ message.error(e);
+ });
+ }
+
+ return (
+
+ {is_delete ? (
+
已删除
+ ) : (
+
+
+
+
})
+
+
+ {item && item.username}
+
+
+
+ {item.content && (
+
+ )}
+
+
{item.time}
+
+ {item.admin || current_login === item.user_login ? (
+
deleteorder(item.id)}
+ >
+
+
+ ) : (
+ ""
+ )}
+ {!is_children && (
+
+ )}
+ {!is_children && (
+
+ )}
+
+
+ {is_reply && !is_children && (
+
+ )}
+ {subComments && subCommentsCount > 0 && (
+
+ )}
+ {item &&
+ subComments &&
+ item.replies_count > subCommentsCount &&
+ item.replies_count >= page * limit && (
+
+
+
+ )}
+
+
+ )}
+
+ );
+}
+export default comment_list;
diff --git a/src/forums/new_comments/comments.js b/src/forums/new_comments/comments.js
new file mode 100644
index 00000000..d4cf6caa
--- /dev/null
+++ b/src/forums/new_comments/comments.js
@@ -0,0 +1,146 @@
+import React, { useState, useEffect } from "react";
+
+import axios from "axios";
+
+import { List, Pagination, message, Spin } from "antd";
+
+import Title from "./Title";
+import NewComment from "./new_comment";
+import CommentList from "./comment_list";
+import ClickReply from "./head_reply_button";
+import "./comment.scss";
+
+function comments({
+ target_id,
+ target_type,
+ current_user_image,
+ current_login,
+}) {
+ const [currentImage, setCurrentImage] = useState(null);
+ const [replies, setReplies] = useState([]);
+ const [replies_count, setRepliesCount] = useState(0);
+
+ const [isSpin, setIsSpin] = useState(false);
+ const [page, setListPage] = useState(1);
+ const [limit, setLimitType] = useState(10);
+ const [isClick, setIsClick] = useState(false);
+ useEffect(() => {
+ async function init() {
+ setIsSpin(true);
+ let url = `/v1/${target_type}/${target_id}/more_reply.json`;
+ axios
+ .get(url, {
+ params: { page, limit },
+ })
+ .then((result) => {
+ if (result && result.data.memo_replies) {
+ setReplies(result.data.memo_replies);
+ setRepliesCount(result.data.memos_count);
+ }
+ setIsSpin(false);
+ })
+ .catch((e) => {
+ setIsSpin(false);
+ message.error(e);
+ });
+ }
+ init();
+ }, [page, target_id]);
+
+ useEffect(() => {
+ if (current_user_image) {
+ setCurrentImage(current_user_image);
+ } else {
+ setCurrentImage("/images/avatars/User/boy.jpg");
+ }
+ }, []);
+ // 翻页
+ function changePage(page) {
+ setListPage(page);
+ }
+
+ const click_reply = (reply_boolean) => {
+ if(current_login){
+ setIsClick(reply_boolean);
+ }else{
+ window.open("/login", "_blank")
+ }
+ };
+
+ const create_new_reply = (reply) => {
+ let new_memos_count = replies_count + 1
+ setRepliesCount(new_memos_count)
+ setReplies([reply, ...replies]);
+ setIsClick(false);
+ };
+
+ const success_delete_reply = (reply) => {
+ let new_memos_count = replies_count - 1
+ setRepliesCount(new_memos_count)
+ };
+
+ return (
+
+
+
+ 评论
+ {replies_count > 0 && (
+ {replies_count}
+ )}
+
+
+
+
+
+ {isClick ? (
+
+ ) : (
+
+ )}
+
+ {replies && replies.length > 0 && (
+
(
+
+
+
+ )}
+ />
+ )}
+ {replies_count > limit && (
+
+ )}
+
+
+
+ );
+}
+
+export default comments;
diff --git a/src/forums/new_comments/head_reply_button.js b/src/forums/new_comments/head_reply_button.js
new file mode 100644
index 00000000..46b36d51
--- /dev/null
+++ b/src/forums/new_comments/head_reply_button.js
@@ -0,0 +1,19 @@
+import React from "react";
+import { Button } from "antd";
+import { getUrl } from "educoder";
+function reply_click({ user_image, click_button }) {
+ const click_reply_to = () =>{
+ click_button(true)
+ }
+ return (
+
+
})
+
+
+
+
+ );
+}
+export default reply_click;
diff --git a/src/forums/new_comments/new_comment.js b/src/forums/new_comments/new_comment.js
new file mode 100644
index 00000000..2021791b
--- /dev/null
+++ b/src/forums/new_comments/new_comment.js
@@ -0,0 +1,67 @@
+import React, { useState, useEffect } from "react";
+import { getUrl } from "educoder";
+import { Button, message } from "antd";
+import MDEditor from "../../modules/tpm/challengesnew/tpm-md-editor";
+import axios from "axios";
+function new_comment({ memo_id, user_image, click_button, new_reply }) {
+ const [journal_spin, setJournalSpin] = useState(false);
+ const [content, setContent] = useState("");
+
+ function change_input(value) {
+ setContent(value);
+ }
+
+ function add_reply() {
+ setJournalSpin(true);
+ let url = `/v1/memos/${memo_id}/reply.json`;
+ axios
+ .post(url, {parent_id: memo_id, content: content})
+ .then((result) => {
+ if (result) {
+ if (result.data.status === 0) {
+ new_reply(result.data.reply)
+ } else {
+ message.error(result.data.message);
+ }
+ }
+ setJournalSpin(false);
+ })
+ .catch((e) => {
+ setJournalSpin(false);
+ message.error(e);
+ });
+ }
+ function click_reply_cancel() {
+ click_button(false);
+ }
+
+ return (
+
+
})
+
+
+ );
+}
+export default new_comment;
diff --git a/src/modules/tpm/TPMIndex.css b/src/modules/tpm/TPMIndex.css
index 8ee4561d..094e41f0 100644
--- a/src/modules/tpm/TPMIndex.css
+++ b/src/modules/tpm/TPMIndex.css
@@ -95,7 +95,7 @@ body>.-task-title {
}
.newContainer {
- background: #EAEBEC !important;
+ background: #f5f5f5;
}
.ant-modal-title {
diff --git a/src/user_info/Index.jsx b/src/user_info/Index.jsx
new file mode 100644
index 00000000..c4ffa246
--- /dev/null
+++ b/src/user_info/Index.jsx
@@ -0,0 +1,84 @@
+import React from "react";
+import { TPMIndexHOC } from "../modules/tpm/TPMIndexHOC";
+import { SnackbarHOC } from "educoder";
+import { CNotificationHOC } from "../modules/courses/common/CNotificationHOC";
+import { Route, Switch } from "react-router-dom";
+import Loading from "../Loading";
+import Loadable from "react-loadable";
+import UserInfo from "./User/Account";
+import { MainContent, MinH400 } from "./css/projects";
+import '../forums/css/All.scss';
+import '../forums/css/Index.css';
+
+const Projects = Loadable({
+ loader: () => import("./User/Projects"),
+ loading: Loading,
+});
+
+const Memos = Loadable({
+ loader: () => import("./Memo/Memos"),
+ loading: Loading,
+});
+
+const Sections = Loadable({
+ loader: () => import("./Memo/Sections"),
+ loading: Loading,
+});
+
+const Blocks = Loadable({
+ loader: () => import("./User/BlockUsers"),
+ loading: Loading,
+});
+
+function Index(props) {
+ return (
+
+
+
+
+
+ }
+ >
+ }
+ >
+ }
+ >
+ }
+ >
+ }
+ >
+ }
+ >
+ }
+ >
+ }
+ >
+
+ }
+ >
+
+
+
+
+
+ );
+}
+export default CNotificationHOC()(SnackbarHOC()(TPMIndexHOC(Index)));
diff --git a/src/user_info/Memo/Memos.js b/src/user_info/Memo/Memos.js
new file mode 100644
index 00000000..7e04731b
--- /dev/null
+++ b/src/user_info/Memo/Memos.js
@@ -0,0 +1,101 @@
+import React, { useEffect, useState } from "react";
+import axios from "axios";
+import { PagenationDiv } from "../css/projects";
+import { Spin, Empty, Pagination } from "antd";
+import ForumSections from "./SelectTitle";
+import { SelectSection } from "../css/layout";
+import ListItem from "../../forums/Component/ListItem";
+import RepliesMemos from "./Replies";
+
+function memos(props) {
+ const user_login = props.match.params.login;
+ const [is_current_uesr, setIsCurrentUser] = useState(false);
+ const [memos, getMemos] = useState([]);
+ const [memosSize, setMemosSize] = useState(0);
+ const [page, setListPage] = useState(1);
+ const [memo_type, setMemoType] = useState(null);
+ const [limit, setLimitType] = useState(15);
+ const [isSpin, setSpinType] = useState(false);
+ const [common_select, setCommonSelect] = useState(null);
+ useEffect(() => {
+ let memos_type = props.location.pathname.split("/")
+ setMemoType(memos_type.pop());
+ }, [props.location]);
+
+ useEffect(() => {
+ if(memo_type){
+ init();
+ }
+ }, [memo_type, common_select, page]);
+
+ async function init() {
+ setSpinType(true);
+
+ let url = `/v1/my_memos/${user_login}/memos.json`;
+ axios.get(url, {
+ params: {
+ ...common_select,
+ limit,
+ memo_type,
+ page,
+ },
+ }).then((result) => {
+ if (result) {
+ getMemos(result.data.memos);
+ setMemosSize(result.data.memos_count);
+ setIsCurrentUser(result.data.is_current_user);
+ }
+ setSpinType(false);
+ })
+ .catch((e) => {
+ setSpinType(false);
+ console.log(e);
+ });
+ }
+
+ function select_memos(target_params) {
+ setCommonSelect(target_params);
+ }
+ const handleChangePage = (page) => {
+ setListPage(page);
+ };
+
+ return (
+
+ {is_current_uesr && (
+
+ )}
+
+
+ {memos && memos.length > 0 ? (
+
+ {memo_type === "replies" ?
+ :
+ }
+
+ ) : (
+
+ )}
+
+
+ {memosSize > limit && (
+
+
+
+ )}
+
+ );
+}
+export default memos;
diff --git a/src/user_info/Memo/Replies.js b/src/user_info/Memo/Replies.js
new file mode 100644
index 00000000..1d6e093c
--- /dev/null
+++ b/src/user_info/Memo/Replies.js
@@ -0,0 +1,37 @@
+import React from "react";
+import { FlexAJ, AlignCenter } from "../../forums/css/layout";
+import "./select_title.scss";
+import Drop from "../../forums/Component/ItemDropDown";
+import { Link } from "react-router-dom";
+function replies_memos({ memos }) {
+ return (
+
+ {memos.map((item, key) => {
+ return (
+ -
+
+
+ 评论文章:
+ {item.subject}
+
+
+
+
+
+ {item && item.new_reply && (
+
+
{item.new_reply.content}
+
+ {item.new_reply.published_time}
+
+
+ )}
+ 查看
+
+
+ );
+ })}
+
+ );
+}
+export default replies_memos;
diff --git a/src/user_info/Memo/ReplyItem.js b/src/user_info/Memo/ReplyItem.js
new file mode 100644
index 00000000..b72b067d
--- /dev/null
+++ b/src/user_info/Memo/ReplyItem.js
@@ -0,0 +1,10 @@
+import React, { useEffect, useState } from "react";
+
+function reply_item({item}){
+ return (
+
+
+
+ )
+
+}
\ No newline at end of file
diff --git a/src/user_info/Memo/SectionItem.js b/src/user_info/Memo/SectionItem.js
new file mode 100644
index 00000000..315d4722
--- /dev/null
+++ b/src/user_info/Memo/SectionItem.js
@@ -0,0 +1,62 @@
+import React, { useEffect, useState } from "react";
+import { Card, Col, Button, message } from "antd";
+import "../User/user.scss";
+import axios from "axios";
+function section_item({ forum_section, key }) {
+ const [isStar, setIsStar] = useState(true)
+ const [isLoading, setIsLoading] = useState(false)
+
+ function rendomColor() {
+ let r_a = Math.floor(Math.random() * 255);
+ let r_g = Math.floor(Math.random() * 255);
+ let r_b = Math.floor(Math.random() * 255);
+ let radom_color = "rgba(" + r_a + "," + r_g + "," + r_b + ",0.8)";
+ return radom_color;
+ }
+
+ function handleClick(){
+ setIsLoading(true);
+ let url = `/v1/memos/forum_memos/${forum_section.id}/is_watch.json`;
+ axios.post(url, {
+ is_watch: isStar ? 0 : 1
+ })
+ .then((result) => {
+ if(result.data.status === 0){
+ message.success(result.data.message);
+ setIsStar(!isStar)
+ }else{
+ message.error(result.data.message);
+ }
+ setIsLoading(false);
+ })
+ .catch((e) => {
+ setIsLoading(false);
+ console.log(e);
+ });
+ }
+ return (
+
+ handleClick()} loading={isLoading}>
+ {isStar ? "取消收藏" : "收藏"}
+ ,
+ ,
+ ]}
+ bodyStyle={{ background: rendomColor() }}
+ >
+
+
{forum_section.title}
+
+ {forum_section.memos_count}个话题
+
+
+
+
+ );
+}
+export default section_item;
diff --git a/src/user_info/Memo/Sections.js b/src/user_info/Memo/Sections.js
new file mode 100644
index 00000000..9a9b70e5
--- /dev/null
+++ b/src/user_info/Memo/Sections.js
@@ -0,0 +1,70 @@
+import React, { useEffect, useState } from "react";
+import axios from "axios";
+import { Spin, Empty, Row, Pagination } from "antd";
+import { PagenationDiv } from "../css/projects";
+import SectionItem from "./SectionItem";
+function sections(props) {
+ const user_login = props.match.params.login;
+ const [isSpin, setSpinType] = useState(false);
+ const [forum_sections, getSections] = useState([]);
+ const [page, setListPage] = useState(1);
+ const [limit, setLimitType] = useState(32);
+ const [sectionsSize, setSectinsSize] = useState(0);
+ useEffect(() => {
+ async function init() {
+ setSpinType(true);
+ let url = `/v1/my_memos/${user_login}/my_interested.json`;
+ axios
+ .get(url, {
+ params: {
+ limit,
+ page,
+ },
+ })
+ .then((result) => {
+ if (result) {
+ getSections(result.data.forum_details);
+ setSectinsSize(result.data.count);
+ }
+ setSpinType(false);
+ })
+ .catch((e) => {
+ setSpinType(false);
+ console.log(e);
+ });
+ }
+ init();
+ }, [page]);
+
+ const handleChangePage = (page) => {
+ setListPage(page);
+ };
+
+ return (
+
+
+ {forum_sections && forum_sections.length > 0 ? (
+
+ {forum_sections.map((item, key) => {
+ return ;
+ })}
+
+ ) : (
+
+ )}
+
+ {sectionsSize > limit && (
+
+
+
+ )}
+
+ );
+}
+export default sections;
diff --git a/src/user_info/Memo/SelectTitle.js b/src/user_info/Memo/SelectTitle.js
new file mode 100644
index 00000000..35c8675e
--- /dev/null
+++ b/src/user_info/Memo/SelectTitle.js
@@ -0,0 +1,119 @@
+import React, { useEffect, useState } from "react";
+import axios from "axios";
+import { Cascader,DatePicker, Button } from "antd";
+import "./select_title.scss";
+import { SelectSection, GridItem2 } from "../css/layout";
+
+const SectionOptions = [
+ {
+ title: "待审查的话题",
+ id: "hidden",
+ },
+ {
+ title: "已发布的话题",
+ id: "show",
+ },
+];
+
+const InputSize = "large"
+function select_title({section_params, select_memos}) {
+ const [isLoading, setLoadingType] = useState(false);
+ const [forumSections, setSectionsType] = useState([]);
+ const [sectionParams, setSectionParams] = useState([]);
+ useEffect(() => {
+ async function init() {
+ setLoadingType(true);
+ let url = `/v1/forum_sections/select_sections.json`;
+ axios
+ .get(url)
+ .then((result) => {
+ if (result) {
+ setSectionsType(result.data);
+ }
+ setLoadingType(false);
+ })
+ .catch((e) => {
+ setLoadingType(false);
+ console.log(e);
+ });
+ }
+ init();
+ }, []);
+
+ function onChange(value, selectedOptions) {
+ let new_section_params = {...sectionParams, forum_section_id: value[value.length-1]}
+ setSectionParams(new_section_params)
+ select_memos(new_section_params)
+ }
+
+ function isHiddenChange(value, selectedOptions) {
+ let new_section_params = {...sectionParams, is_hidden: value[0]}
+ setSectionParams(new_section_params)
+ select_memos(new_section_params)
+ }
+
+ function selectBeginTime(date,dateString) {
+ setSectionParams({...sectionParams, start_time: dateString})
+ }
+
+ function selectEndTime(date,dateString) {
+ setSectionParams({...sectionParams, end_time: dateString})
+ }
+
+ function SectionSearch(){
+ select_memos(sectionParams)
+ }
+
+ function filter(inputValue, path) {
+ return path.some(
+ (option) =>
+ option.title.toLowerCase().indexOf(inputValue.toLowerCase()) > -1
+ );
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default select_title;
diff --git a/src/user_info/Memo/select_title.scss b/src/user_info/Memo/select_title.scss
new file mode 100644
index 00000000..720416d0
--- /dev/null
+++ b/src/user_info/Memo/select_title.scss
@@ -0,0 +1,27 @@
+.d-inline-block{display: inline-block;}
+.section-cascader{
+ width: 120px;
+
+}
+.ant-cascader-menu{
+ min-height: 20px;
+ max-height: 180px;
+ height: auto;
+}
+.forumList{
+ padding:0px 30px;
+}
+.forumList > li{
+ border-bottom: 1px solid #eee;
+ padding:15px 0px;
+}
+.forumList > li:last-child{
+ border-bottom: none;
+}
+.color-gray-8{color: #888;}
+.reply-link{
+ padding: 2px 16px;
+ color: #21B350 !important;
+ border: 1px solid #21B350;
+ border-radius: 20px;
+}
\ No newline at end of file
diff --git a/src/user_info/Modal/modal.scss b/src/user_info/Modal/modal.scss
new file mode 100644
index 00000000..7795594d
--- /dev/null
+++ b/src/user_info/Modal/modal.scss
@@ -0,0 +1,18 @@
+.fs16{font-size: 16px;}
+.lineH2{line-height: 2;}
+
+.tips-modal-confirm{
+ .ant-modal-body{
+ width: 100%;
+ color: #333333;
+ height: 180px;
+ display: table-cell;
+ vertical-align: middle;
+ .modal-content-padding{
+ padding: 10px 25px;
+ }
+ }
+}
+.tips-modal {
+ .ant-modal-footer{border-top: none !important}
+}
\ No newline at end of file
diff --git a/src/user_info/Modal/tip_modal.js b/src/user_info/Modal/tip_modal.js
new file mode 100644
index 00000000..4629d815
--- /dev/null
+++ b/src/user_info/Modal/tip_modal.js
@@ -0,0 +1,61 @@
+import React, { useEffect, useState } from "react";
+import { Modal, Button } from "antd";
+import "./modal.scss";
+function tip_modal({ data, okClick, cancelClick }) {
+ const [isShow, setShowModal] = useState(false);
+ useEffect(() => {
+ setShowModal(true);
+ });
+ const ok_click = () => {
+ setShowModal(false);
+ okClick(data.modal_type);
+ };
+ const cancel_click = () => {
+ setShowModal(false);
+ cancelClick(false);
+ };
+ return (
+
+ {data &&
+ (data.modal_type === "confirm" ? (
+
cancel_click()}
+ footer={[
+
+
+
,
+ ]}
+ >
+
+
+ ) : (
+
ok_click()}
+ onCancel={() => cancel_click()}
+ okText="确认"
+ cancelText="取消"
+ wrapClassName="tips-modal-confirm"
+ >
+
+ {data.head && (
+
+ {data.head}
+
+ )}
+ {data.content &&
{data.content}
}
+
+
+ ))}
+
+ );
+}
+export default tip_modal;
diff --git a/src/user_info/User/Account.js b/src/user_info/User/Account.js
new file mode 100644
index 00000000..4c163186
--- /dev/null
+++ b/src/user_info/User/Account.js
@@ -0,0 +1,50 @@
+import React, { useEffect, useState } from "react";
+import axios from "axios";
+import HeadUser from "./HeadUser";
+import { MainUser, HeadImage, HeadCon } from "../css/layout.jsx";
+import { Spin } from "antd";
+import UserMenu from "./Menu";
+import "../css/layout.scss"
+function account(props) {
+ const [UserInfo, setUserInfo] = useState(null); //用户的内容
+ const [isSpin, setSpin] = useState(false); //用户的内容
+ const user_login = props.match.params.login;
+ useEffect(() => {
+ async function init(login) {
+ setSpin(true);
+ let url = `/v1/users/${login}/user_info.json`;
+ axios
+ .get(url)
+ .then((result) => {
+ if (result) {
+ setUserInfo(result.data.user);
+ }
+ setSpin(false);
+ })
+ .catch((e) => {
+ setSpin(false);
+ console.log(e);
+ });
+ }
+ if (user_login) {
+ init(user_login);
+ }
+ }, []);
+
+ return (
+
+
+
+
+
+
+
+
+
+ );
+}
+export default account;
diff --git a/src/user_info/User/BlockItem.js b/src/user_info/User/BlockItem.js
new file mode 100644
index 00000000..18f418e6
--- /dev/null
+++ b/src/user_info/User/BlockItem.js
@@ -0,0 +1,36 @@
+import React from "react";
+import { Card, Col, Avatar } from "antd";
+import { getUrl } from "educoder";
+import "./user.scss";
+
+function block_item({ item, key }) {
+
+ return (
+
+
+
+
+
+
+ {item.username}
+
+
+
+ {item.brief ? item.brief : "这家伙太懒了,还未填写个人描述!"}
+
+
+ {/*
+
+
*/}
+
+
+ );
+}
+export default block_item;
diff --git a/src/user_info/User/BlockUsers.js b/src/user_info/User/BlockUsers.js
new file mode 100644
index 00000000..3516e2a8
--- /dev/null
+++ b/src/user_info/User/BlockUsers.js
@@ -0,0 +1,72 @@
+import React, { useEffect, useState } from "react";
+import axios from "axios";
+import { Spin, Empty, Row, Pagination } from "antd";
+import { PagenationDiv } from "../css/projects";
+import BlockItem from "./BlockItem";
+
+function block_users(props) {
+ const user_login = props.match.params.login;
+ const [isSpin, setSpinType] = useState(false);
+ const [users, setUsers] = useState([]);
+ const [page, setListPage] = useState(1);
+ const [limit, setLimitType] = useState(30);
+ const [usersSize, setSectinsSize] = useState(0);
+ useEffect(() => {
+ async function init() {
+ setSpinType(true);
+ let url = `/v1/users/${user_login}/block_user_lists.json`;
+ axios
+ .get(url, {
+ params: {
+ limit,
+ page,
+ },
+ })
+ .then((result) => {
+ if (result) {
+ setUsers(result.data.users);
+ setSectinsSize(result.data.users_size);
+ }
+ setSpinType(false);
+ })
+ .catch((e) => {
+ setSpinType(false);
+ console.log(e);
+ });
+ }
+ init();
+ }, [page]);
+
+
+ const handleChangePage = (page) => {
+ setListPage(page);
+ };
+
+ return (
+
+
+ {users && users.length > 0 ? (
+
+ {users.map((item, key) => {
+ return ;
+ })}
+
+ ) : (
+
+ )}
+
+ {usersSize > limit && (
+
+
+
+ )}
+
+ );
+}
+export default block_users;
diff --git a/src/user_info/User/Blocks.js b/src/user_info/User/Blocks.js
new file mode 100644
index 00000000..e69de29b
diff --git a/src/user_info/User/HeadUser.js b/src/user_info/User/HeadUser.js
new file mode 100644
index 00000000..8e6abb81
--- /dev/null
+++ b/src/user_info/User/HeadUser.js
@@ -0,0 +1,68 @@
+import React, { useState } from "react";
+import { Pt80, HeadTab, ShowName, DivInline } from "../css/layout.jsx";
+import UserIdentify from "./UserIdentify";
+import UserAvatar from "./UserAvatar";
+import UserBrief from "./UserBrief";
+import StarUser from "./StarUser";
+
+
+function head_user({ UserInfo }) {
+ const [fansCount, setFansCount] = useState(0)
+ const fans_count = (count) => {
+ let new_fans_count = UserInfo.fans_count + count
+ setFansCount(new_fans_count)
+ }
+ return (
+
+ );
+}
+export default head_user;
diff --git a/src/user_info/User/Menu.js b/src/user_info/User/Menu.js
new file mode 100644
index 00000000..1f7a0180
--- /dev/null
+++ b/src/user_info/User/Menu.js
@@ -0,0 +1,46 @@
+import React, { useEffect, useState } from "react";
+import { Menu } from "antd";
+import "./menu.scss";
+
+function user_menu({ is_current_user, login, props }) {
+ const [defaultMenu, setDefaultMenu] = useState("p_project");
+ const handleClick = (e) => {
+ setDefaultMenu(e.key);
+ props.history.push(`/accounts/${login}/${e.key}`);
+ };
+ useEffect(() => {
+ const location_path = props.location.pathname.split("/");
+ const current_path = location_path[location_path.length - 1];
+ if (location_path.length > 3 && current_path) {
+ setDefaultMenu(current_path);
+ } else {
+ setDefaultMenu("p_project");
+ }
+ }, [props.location]);
+ const show_name = is_current_user ? "我" : "TA";
+ return (
+
+
+
+ );
+}
+export default user_menu;
diff --git a/src/user_info/User/ProjectItem.js b/src/user_info/User/ProjectItem.js
new file mode 100644
index 00000000..cf01e887
--- /dev/null
+++ b/src/user_info/User/ProjectItem.js
@@ -0,0 +1,53 @@
+import React from "react";
+import { Card, Col, Avatar, Tooltip } from "antd";
+import { getUrl } from "educoder";
+import "./user.scss";
+
+function project_item({ project, key }) {
+ const item_bottom = () => {
+ return (
+
+
+
+ {project.members_count}
+
+ {project.commits_count > 0 && (
+
+
+ {project.commits_count}
+
+ )}
+
+ );
+ };
+ return (
+
+
+ {project.is_public && (
+
+
+
+ 公开
+
+ )}
+
+
+
+
+
+
+
{project.school_name}
+
+
+
+ );
+}
+export default project_item;
diff --git a/src/user_info/User/Projects.js b/src/user_info/User/Projects.js
new file mode 100644
index 00000000..52d61f41
--- /dev/null
+++ b/src/user_info/User/Projects.js
@@ -0,0 +1,118 @@
+import React, { useEffect, useState } from "react";
+import axios from "axios";
+import { Spin, Menu, Button, Empty, Row, Pagination } from "antd";
+import "./menu.scss";
+import "./user.scss";
+import ProjectItem from "./ProjectItem";
+import { PagenationDiv } from "../css/projects";
+
+function user_projects(props) {
+ const user_login = props.match.params.login;
+ const [is_current_uesr, setIsCurrentUser] = useState(false);
+ const [projects, getProjectsList] = useState([]);
+ const [projectsSize, setProjectsSize] = useState(0);
+ const [page, setListPage] = useState(1);
+ const [type, setListType] = useState("");
+ const [p, setSelectType] = useState("a");
+ const [order, setOrderType] = useState("desc");
+ const [limit, setLimitType] = useState(16);
+ const [isSpin, setSpinType] = useState(false);
+ useEffect(() => {
+ if (props.location.pathname.indexOf("l_projects") > -1) {
+ setListType("l_projects");
+ } else {
+ setListType("p_projects");
+ }
+ }, [props.location]);
+
+ useEffect(() => {
+ async function init() {
+ setSpinType(true);
+ let url = `/v1/users/${user_login}/user_projects.json`;
+ axios
+ .get(url, {
+ params: { page, type, p, order, limit },
+ })
+ .then((result) => {
+ if (result) {
+ getProjectsList(result.data.projects);
+ setProjectsSize(result.data.projects_size);
+ setIsCurrentUser(result.data.is_current_user);
+ }
+ setSpinType(false);
+ })
+ .catch((e) => {
+ setSpinType(false);
+ console.log(e);
+ });
+ }
+ if(type.length > 0){
+ init();
+ }
+
+ }, [page, type, p, order]);
+
+ const select_project = (e) => {
+ setSelectType(e.key);
+ };
+ const select_order = () => {
+ setOrderType(order === "desc" ? "asc" : "desc");
+ };
+
+ const handleChangePage = (page) => {
+ setListPage(page);
+ };
+
+ return (
+
+ {is_current_uesr && (
+
+
+
+ )}
+
+ 共{projectsSize}个项目
+
+
+
+ {projects && projects.length > 0 ? (
+
+ {projects.map((item, key) => {
+ return ;
+ })}
+
+ ) : (
+
+ )}
+
+ {projectsSize > limit && (
+
+
+
+ )}
+
+ );
+}
+export default user_projects;
diff --git a/src/user_info/User/StarUser.js b/src/user_info/User/StarUser.js
new file mode 100644
index 00000000..ddf3b6de
--- /dev/null
+++ b/src/user_info/User/StarUser.js
@@ -0,0 +1,169 @@
+import React, { useEffect, useState } from "react";
+import { Button, message} from "antd";
+import axios from "axios";
+import { DivInline } from "../css/layout.jsx";
+import "./user.scss";
+import TipModal from "../Modal/tip_modal";
+
+function star_user({
+ is_watched,
+ login,
+ current_login,
+ user_id,
+ set_fans_count,
+ is_blocked,
+ is_blocked_by,
+ user_name,
+ show_block
+}) {
+ const [isStar, setStarStatus] = useState(false);
+ const [isBlocked, setBlockStatus] = useState(false);
+ const [showModal, setModalStatus] = useState(false);
+ const [isLoading, setLoadinStatus] = useState(false);
+ const [isBlockLoading, setBlockLoadingStatus] = useState(false);
+ const [modalData, setDataModal] = useState({});
+ useEffect(() => {
+ if (is_watched) {
+ setStarStatus(is_watched);
+ }
+ if (is_blocked) {
+ setBlockStatus(is_blocked);
+ }
+ },[is_blocked, is_watched]);
+ const to_star = (star_type) => {
+ if(current_login){
+ setLoadinStatus(true);
+ let url = `/v1/users/${login}/watch_user.json`;
+ if (is_blocked_by) {
+ setModalStatus(true)
+ setLoadinStatus(false);
+ setDataModal({
+ title: "提示",
+ modal_type: "confirm",
+ head: null,
+ content: "对方已将你加入黑名单,你不能关注对方。"
+ });
+ } else {
+ axios
+ .post(url, { watch: star_type, login: login })
+ .then((result) => {
+ if (result && result.data.status >= 0) {
+ message.success(result.data.message);
+ setStarStatus(!isStar);
+ if (star_type === "watch") {
+ set_fans_count(1);
+ } else {
+ set_fans_count(-1);
+ }
+ } else {
+ message.error(result.data.message);
+ }
+ setLoadinStatus(false);
+ })
+ .catch((e) => {
+ setLoadinStatus(false);
+ console.log(e);
+ });
+ }
+ }else{
+ window.open("/login", "_blank")
+ }
+
+ };
+ const to_block = (block_type) => {
+ setModalStatus(false);
+ setBlockLoadingStatus(true);
+ let url = `/v1/users/${login}/block_user.json`;
+ axios
+ .post(url, { block: block_type })
+ .then((result) => {
+ if (result && result.data.status >= 0) {
+ message.success(result.data.message);
+ setBlockStatus(!isBlocked);
+ if (block_type === "block" && isStar) {
+ //如果是已关注的状态,则取消关注状态
+ setStarStatus(false);
+ set_fans_count(-1);
+ }
+ } else {
+ message.error(result.data.message);
+ }
+ setBlockLoadingStatus(false);
+ })
+ .catch((e) => {
+ setBlockLoadingStatus(false);
+ console.log(e);
+ });
+ };
+ const confirm_block = () => {
+
+ if (isBlocked) {
+ setDataModal({
+ title: "移出黑名单",
+ modal_type: "cancel",
+ head: null,
+ content: "移出黑名单后,对方可以关注你,向你发私信,评论你的帖子。"
+ });
+ } else {
+ setDataModal({
+ title: "加入黑名单",
+ modal_type: "block",
+ head: `确定要将${user_name}加入黑名单?`,
+ content:
+ " 加入黑名单后,已有关注关系将被解除,对方不能再关注你,向你发私信,评论你的帖子等。但仍可以查看你的公开信息。"
+ });
+ }
+ setModalStatus(true);
+ };
+ const cancel_click = () => {
+ setModalStatus(false)
+ setBlockLoadingStatus(false)
+ }
+ return (
+
+
+
+
+ {show_block && current_login && }
+
+
+ {showModal && (
+
+ )}
+
+ );
+}
+export default star_user;
diff --git a/src/user_info/User/UserAvatar.js b/src/user_info/User/UserAvatar.js
new file mode 100644
index 00000000..7f9777b8
--- /dev/null
+++ b/src/user_info/User/UserAvatar.js
@@ -0,0 +1,106 @@
+import React, { useEffect, useState } from "react";
+import { Avatar, Upload, message } from "antd";
+import { getUrl } from "educoder";
+import "antd/dist/antd.css";
+import { HeadPhoto, HeadPhotoHover } from "../css/layout.jsx";
+import ImgCrop from "antd-img-crop";
+import axios from "axios";
+function user_avatar({ avatar, is_current_user, user_id, login }) {
+ const current_route = window.location.protocol + "//" + window.location.host;
+ const [isHover, setIsHover] = useState(false);
+ const [isClick, setIsClick] = useState(false);
+ const [userAvatar, setUserAvatar] = useState("");
+ const [isLoading, setLoadinStatus] = useState(false);
+ useEffect(() => {
+ setUserAvatar(avatar);
+ }, []);
+ const 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)");
+ setIsHover(false);
+ setIsClick(false);
+ return false;
+ }
+ const isLt2M = file.size / 1024 / 1024 < 1;
+ if (!isLt2M) {
+ setIsHover(false);
+ setIsClick(false);
+ message.error("图片大小必须小于 2MB!");
+ return false;
+ }
+ return true;
+ };
+
+ function sync_upload_picture(file) {
+ setLoadinStatus(true);
+ let base64Url = "";
+ let reader = new FileReader();
+ reader.readAsDataURL(file.file);
+ reader.onloadend = function () {
+ base64Url = reader.result;
+ if (base64Url && base64Url.length > 0) {
+ handleUpload(base64Url)
+ } else {
+ setLoadinStatus(false);
+ message.error("上传失败,请稍后重试");
+ }
+ };
+ }
+
+ function handleUpload(base64Url) {
+ var formData = new FormData();
+ formData.append("img", base64Url);
+ formData.append("source_type", "User");
+ formData.append("source_id", user_id);
+ formData.append("is_direct", 0);
+ axios.post(`/upload_avatar.json`, formData)
+ .then((result) => {
+ if (result && result.data.status === 0) {
+ setUserAvatar(result.data.url)
+
+ } else {
+ message.error(result.data.message);
+ }
+ setIsHover(false);
+ setIsClick(false);
+ setLoadinStatus(false);
+ })
+ .catch((e) => {
+ setIsHover(false);
+ setIsClick(false);
+ setLoadinStatus(false);
+ console.log(e);
+ });
+ }
+
+ return (
+
+ setIsHover(true)}
+ onMouseLeave={() => setIsHover(isClick)}
+ >
+
+ {is_current_user && (isHover || isClick) && (
+ setIsClick(true)}>
+
+
+ {isLoading?"上传中..":"修改头像"}
+
+
+
+ )}
+
+
+ );
+}
+
+export default user_avatar;
diff --git a/src/user_info/User/UserBrief.js b/src/user_info/User/UserBrief.js
new file mode 100644
index 00000000..a33e7b05
--- /dev/null
+++ b/src/user_info/User/UserBrief.js
@@ -0,0 +1,81 @@
+import React, {useState } from "react";
+import { Form, Input, Icon, Button, message } from "antd";
+import axios from "axios";
+const { Item } = Form;
+
+function user_brief({ brief, login, is_current_user }) {
+ const [editBrief, setEditBrief] = useState(null);
+ const [isEdit, setEditStatus] = useState(false);
+ const [isLoading, setLoadingStatus] = useState(false);
+ const changeInput = (e) => {
+ setEditBrief(e.target.value)
+ };
+ const edit_brief = () => {
+ setLoadingStatus(true);
+ let url = `/v1/users/${login}/edit_brief.json`;
+ axios
+ .post(url, { content: editBrief })
+ .then((result) => {
+ if (result && result.data.status >= 0) {
+ message.success(result.data.message);
+ setEditStatus(false);
+ } else {
+ message.error(result.data.message);
+ }
+ setLoadingStatus(false);
+ })
+ .catch((e) => {
+ setLoadingStatus(false);
+ console.log(e);
+ });
+ };
+ return (
+
+ {!isEdit && (
+
+ {editBrief
+ ? editBrief
+ : brief
+ ? brief
+ : "这家伙很懒,什么都没留下~"}
+ {is_current_user && (
+ setEditStatus(true)}>
+
+
+ )}
+
+ )}
+ {isEdit && (
+
+ )}
+
+ );
+}
+export default user_brief;
diff --git a/src/user_info/User/UserIdentify.js b/src/user_info/User/UserIdentify.js
new file mode 100644
index 00000000..b90f0d07
--- /dev/null
+++ b/src/user_info/User/UserIdentify.js
@@ -0,0 +1,109 @@
+import React from "react";
+import { Tooltip } from "antd";
+import { getImageUrl } from "educoder";
+import {
+ DivInline,
+ GridItem5,
+ IdentifyName,
+ IdentifyA,
+} from "../css/layout.jsx";
+function user_identify({ user_info }) {
+ const identityImage = getImageUrl(
+ "images/educoder/icon/auto-identityed.svg"
+ );
+ const identityIng = getImageUrl("images/educoder/icon/auto-identity.svg");
+ const autoPosted = getImageUrl("images/educoder/icon/auto-posted.svg");
+ const autoPost = getImageUrl("images/educoder/icon/auto-post.svg");
+ const autoPhone = getImageUrl("images/educoder/icon/auto-phone.svg");
+ const autoPhoned = getImageUrl("images/educoder/icon/auto-phoneed.svg");
+ const autoMailed = getImageUrl("images/educoder/icon/auto-emailed.svg");
+ const autoMail = getImageUrl("images/educoder/icon/auto-email.svg");
+
+ return (
+
+ );
+}
+
+export default user_identify;
diff --git a/src/user_info/User/menu.scss b/src/user_info/User/menu.scss
new file mode 100644
index 00000000..10472bc3
--- /dev/null
+++ b/src/user_info/User/menu.scss
@@ -0,0 +1,45 @@
+.user-menu, .user-submenu {
+ .ant-menu-horizontal {
+ border-bottom: none !important;
+ padding: 15px !important;
+ text-align: left !important;
+ }
+ .ant-menu-item{
+ border-bottom: none !important;
+ font-size: 16px !important;
+ margin: 0 10px !important;
+ }
+}
+.user-menu{
+ .ant-menu-item-selected{
+ color: #21B350 !important;
+ border: 1px solid #21B350 !important;
+ border-radius: 24px;
+ &::after{
+ content: none !important
+ }
+ }
+ .ant-menu-item:hover{
+ color: #21B350 !important;
+ &::after{
+ content: '';
+ position: absolute;
+ left: 30px;
+ bottom: 0;
+ right: auto;
+ height: 2px;
+ width: 15px;
+ background-color: #21B350;
+ }
+ }
+}
+.user-submenu{
+ margin-top: 15px;
+ .ant-menu-item-selected, .ant-menu-item:hover{
+ color: #21B350 !important;
+ }
+ .ant-menu-horizontal {
+ padding: 8px 15px !important;
+ }
+}
+
diff --git a/src/user_info/User/user.scss b/src/user_info/User/user.scss
new file mode 100644
index 00000000..ca4e47b0
--- /dev/null
+++ b/src/user_info/User/user.scss
@@ -0,0 +1,192 @@
+.pt80 {
+ padding-top: 80px;
+}
+.pr {
+ position: relative;
+}
+.width120 {
+ width: 120px;
+}
+.ant-btn-success {
+ color: #fff;
+ background-color: #21b350;
+ border-color: #21b350 !important;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.12);
+ -webkit-box-shadow: 0 2px 0 rgba(0, 0, 0, 0.045);
+ box-shadow: 0 2px 0 rgba(0, 0, 0, 0.045);
+}
+.ant-btn-success:hover {
+ color: #fff;
+ background-color: #21b350;
+ border-color: #21b350;
+ opacity: 0.8;
+}
+.ant-btn-success.ant-btn-background-ghost {
+ color: #21b350 !important;
+}
+.ant-btn-success:focus {
+ color: #fff;
+ background-color: #21b350;
+ border-color: #21b350 !important;
+}
+.fs12 {
+ font-size: 12px;
+}
+
+.black-color {
+ color: rgba(51, 51, 51, 1);
+}
+.ant-btn-background-ghost.ant-btn-danger {
+ color: #fa4a4a;
+ border-color: #fa4a4a;
+}
+.ant-btn-danger {
+ border-color: #fa4a4a;
+ background-color: #fa4a4a;
+}
+.grid-item-left {
+ display: grid;
+ grid-template-columns: 1fr max-content;
+ align-items: center;
+}
+.user-project-size {
+ padding: 16px 25px;
+}
+.text-right {
+ text-align: right;
+}
+.color-grey {
+ color: #999 !important;
+}
+.color-black {
+ color: #333;
+}
+.color-green {
+ color: #21b351 !important;
+}
+.minH300 {
+ min-height: 300px;
+}
+.pd100 {
+ padding: 100px 20px;
+}
+.project-card-item {
+ margin-bottom: 20px;
+ position: relative;
+ .project-public-tip {
+ position: absolute;
+ left: 0;
+ top: 0;
+ }
+ .project-item-user {
+ text-align: center;
+ margin-top: 16px;
+ }
+ .ant-card-hoverable:hover {
+ bottom: 3px;
+ box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.3);
+ }
+ .project-user-school {
+ color: #999;
+ margin: 16px 0 10px 0;
+ font-size: 16px;
+ height: 32px;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ }
+ .project-user-bottom {
+ color: #666 !important;
+ }
+ .ant-card {
+ padding: 16px 0;
+ }
+ .ant-card-head {
+ border-bottom: none;
+ text-align: center;
+ padding: 0 20px;
+ }
+ .ant-card-head-wrapper {
+ padding: 16px 0 0 0;
+ }
+ .ant-card-head-title {
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 2;
+ overflow: hidden;
+ height: 45px;
+ white-space: initial;
+ padding: 0;
+ a {
+ font-size: 16px;
+ color: #1a0b00;
+ }
+
+ a:hover {
+ color: #21b351;
+ }
+ }
+ .publicpart {
+ position: absolute;
+ left: 0px;
+ top: 0px;
+ width: 0;
+ height: 0;
+ border-left: 80px solid #21b351;
+ border-bottom: 80px solid transparent;
+ z-index: 1;
+ }
+ .smalltrangle {
+ display: block;
+ position: absolute;
+ left: 0px;
+ top: 0px;
+ border-left: 25px solid #fff;
+ border-bottom: 25px solid transparent;
+ z-index: 2;
+ }
+ .publicword {
+ transform: rotate(-45deg);
+ text-align: center;
+ color: #fff;
+ font-size: 14px;
+ display: block;
+ position: absolute;
+ width: 50px;
+ left: 0px;
+ z-index: 3;
+ top: 16px;
+ }
+ .ant-card-body {
+ padding: 0 24px;
+ }
+ .ant-card-actions {
+ background: #fff;
+ li {
+ margin: 16px 0 0 0;
+ }
+ }
+}
+.position-absolute {
+ position: absolute;
+}
+
+.forum-section-card{
+ .ant-card{border-radius: 8px;}
+ .intresting-forum-section{
+ text-align: center;
+ height: 166px;
+ overflow: hidden;
+ display: flex;
+ flex-flow: column;
+ align-items: center;
+ justify-content: center;
+ }
+ .ant-card-body{margin-top: -16px; border-top-left-radius: 8px; border-top-right-radius: 8px;}
+}
+.block-item-user{
+ text-align: center;
+ padding: 50px 0 40px 0;
+ width: 100%;
+ display: inline-block;
+}
\ No newline at end of file
diff --git a/src/user_info/css/head_user.jsx b/src/user_info/css/head_user.jsx
new file mode 100644
index 00000000..e69de29b
diff --git a/src/user_info/css/layout.jsx b/src/user_info/css/layout.jsx
new file mode 100644
index 00000000..b09e5adc
--- /dev/null
+++ b/src/user_info/css/layout.jsx
@@ -0,0 +1,149 @@
+import styled from 'styled-components';
+import imgs from '../images/userhead.jpg';
+
+export const NewUserMain = styled.div`{
+ margin: 0 auto;
+ min-width: 1200px;
+ padding-top: 60px;
+ padding-bottom: 117px;
+}`
+
+export const SelectSection = styled.div`{
+ padding: 15px;
+ margin-top: 15px;
+ background: #fff;
+ input, button{height: 36px; line-height: 36px;}
+ input::placeholder{font-size: 14px;}
+}`
+
+export const MainUser = styled.div`{
+ width: 100%;
+ background: #fff;
+ margin-bottom: 20px;
+ position: relative;
+}`
+
+export const HeadImage = styled.div`{
+ width: 100%;
+ height: 160px;
+ background-image: url('${imgs}');
+ position: absolute;
+ width: 100%;
+ left: 0px;
+ top: 0px;
+}`
+
+export const HeadCon = styled.div`{
+ position: absolute;
+ width: 100%;
+ left: 0px;
+ top: 0px;
+ min-height: 356px;
+}`
+
+export const IdentifyA = styled.a`{
+ width: 20px;
+ height: 20px;
+ line-height: 20px;
+ text-align: center;
+ border-radius: 50%;
+ background-color: #F4FAFF;
+ margin-right: 5px;
+}`
+
+export const RelativePos = styled.div`{
+ position: relative;
+}`
+
+export const DivInline = styled.div`{
+ display: inline-block;
+ width: auto;
+ line-height: 26px;
+ font-size: 16px;
+}`
+
+export const IdentifyName = styled.span`{
+ color: #686868;
+ font-size: 14px;
+ margin-right: 10px;
+}`
+
+export const GridItem5 = styled.div`{
+ display: grid;
+ grid-template-columns: max-content max-content max-content max-content max-content;
+ align-items: center;
+}`
+
+export const GridItem2 = styled.div`{
+ display: grid;
+ grid-template-columns: max-content 1fr;
+ align-items: center;
+}`
+export const Pt80 = styled.div`{
+ padding-top: 80px;
+}`
+
+export const ShowName = styled.span`{
+ display: block;
+ width: auto;
+ color: #05101A;
+ font-size: 24px;
+ height: 28px;
+ line-height: 28px;
+ margin-top: 5px;
+ clear: both;
+}`
+
+export const HeadPhoto = styled.div`{
+ margin-top: 14px;
+ text-align: center;
+ background: #FFFFff;
+ width: 115px;
+ height: 115px;
+ padding: 3px;
+ border-radius: 50%;
+ position: relative;
+ float: left;
+ margin-top: 19px;
+ box-sizing: border-box;
+}`
+
+export const HeadPhotoHover = styled.div`{
+ display: block;
+ cursor: pointer;
+ position: absolute;
+ top: 3px;
+ left: 3px;
+ width: 109px;
+ height: 109px;
+ text-align: center;
+ line-height: 112px;
+ border-radius: 50%;
+ background-color: rgba(0,0,0,0.5);
+ color: #fff;
+ .upload-edit-word{
+ color: #fff !important;
+ }
+}`
+
+export const HeadTab = styled.div`{
+ width: 188px;
+ height: 60px;
+ text-align: center;
+ span {
+ color: #989898;
+ font-size: 14px;
+ };
+ a {
+ color: #fff;
+ font-size: 24px;
+ line-height: 2;
+ };
+ span, a{
+ {
+ display: block;
+ width: 100%;
+ text-align: center;
+ }
+ }
+}`
\ No newline at end of file
diff --git a/src/user_info/css/layout.scss b/src/user_info/css/layout.scss
new file mode 100644
index 00000000..1fd43739
--- /dev/null
+++ b/src/user_info/css/layout.scss
@@ -0,0 +1,3 @@
+a:hover {
+ color: #21b351;
+}
\ No newline at end of file
diff --git a/src/user_info/css/projects.jsx b/src/user_info/css/projects.jsx
new file mode 100644
index 00000000..37797aa7
--- /dev/null
+++ b/src/user_info/css/projects.jsx
@@ -0,0 +1,14 @@
+import styled from 'styled-components';
+export const MainContent = styled.div`{
+ width: 1200px;
+ margin: 0 auto;
+ zoom: 1;
+}`
+export const PagenationDiv = styled.div`{
+ text-align: center;
+ padding: 20px;
+}`
+
+export const MinH400 = styled.div`{
+ min-height: 400px;
+}`
\ No newline at end of file
diff --git a/src/user_info/images/userhead.jpg b/src/user_info/images/userhead.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..0be793deba1158a354671bf06bc2a30a50946789
GIT binary patch
literal 11343
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