Compare commits

...

23 Commits

Author SHA1 Message Date
caishi 114cda0830 上线后修改 2021-05-20 15:31:07 +08:00
caishi 9d387511e0 活动模块-1 2021-05-20 09:56:17 +08:00
caishi 54b9bd4535 活动模块第一版测试 2021-05-19 18:16:22 +08:00
caishi 088fd09fa6 save 2021-05-17 16:09:46 +08:00
caishi 3a46d81e98 开源活动模块 2021-05-17 10:01:08 +08:00
caishi 3321dad83e 首页查询参数 2021-05-14 17:17:05 +08:00
caishi fdf342da42 首页增加最新动态模块 2021-05-14 16:47:08 +08:00
caishi b89789ac98 首页新增的两部分已完成,TOC还需等接口,下一步是活动首页的页面 2021-05-14 11:11:51 +08:00
caishi 4d28abaaf1 首页新增两个部分的显示 2021-05-13 14:13:00 +08:00
caishi edf55d5466 update 2021-05-12 17:23:34 +08:00
caishi f2ef8d4843 合作伙伴logo没有换行 2021-05-12 10:23:54 +08:00
caishi 523c8b36e4 顶部链接-外链 2021-04-26 18:20:04 +08:00
caishi 0717ec8fbb 新增关于我们页面 2021-04-26 17:14:14 +08:00
caishi 941a89cecc 增加后台管理、修改faq页面的table问题 2021-04-26 16:30:01 +08:00
caishi 1b3284cd1d url 2021-04-17 13:55:55 +08:00
caishi bea187c1f9 木兰第一版 2021-04-16 17:53:08 +08:00
caishi ccb1dd8216 登录注册按钮以及弹框样式等(未进行数据绑定) 2021-04-12 15:23:23 +08:00
caishi 8544c7df86 style 2021-03-29 09:32:35 +08:00
caishi 88f56c7160 link 2021-03-27 13:09:43 +08:00
caishi f05c3f322f data 2021-03-26 18:24:29 +08:00
caishi 087d089705 首页 2021-03-26 17:22:37 +08:00
caishi d86b07ae4a page 2021-03-25 21:47:22 +08:00
caishi 3f01ac4ccb 许可证 2021-03-25 14:36:57 +08:00
76 changed files with 3274 additions and 201 deletions

21
package-lock.json generated
View File

@ -1,5 +1,5 @@
{
"name": "educoder",
"name": "forge",
"version": "0.1.0",
"lockfileVersion": 1,
"requires": true,
@ -3888,11 +3888,6 @@
"randomfill": "^1.0.3"
}
},
"crypto-js": {
"version": "4.0.0",
"resolved": "https://registry.npm.taobao.org/crypto-js/download/crypto-js-4.0.0.tgz",
"integrity": "sha1-KQSrJnep0EKFai6i74DekuSjbcw="
},
"crypto-random-string": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz",
@ -4852,7 +4847,7 @@
},
"dom-closest": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/dom-closest/-/dom-closest-0.2.0.tgz",
"resolved": "https://registry.npm.taobao.org/dom-closest/download/dom-closest-0.2.0.tgz",
"integrity": "sha1-69n5HRvyLo1vR3h2u80+yQIWwM8=",
"requires": {
"dom-matches": ">=1.0.1"
@ -4896,7 +4891,7 @@
},
"dom-matches": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dom-matches/-/dom-matches-2.0.0.tgz",
"resolved": "https://registry.npm.taobao.org/dom-matches/download/dom-matches-2.0.0.tgz",
"integrity": "sha1-0nKLQWqHUzmA6wibhI0lPPI6dYw="
},
"dom-scroll-into-view": {
@ -5149,7 +5144,7 @@
},
"enquire.js": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/enquire.js/-/enquire.js-2.1.6.tgz",
"resolved": "https://registry.npm.taobao.org/enquire.js/download/enquire.js-2.1.6.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fenquire.js%2Fdownload%2Fenquire.js-2.1.6.tgz",
"integrity": "sha1-PoeAybi4NQhMP2DhZtvDwqPImBQ="
},
"entities": {
@ -5668,7 +5663,7 @@
},
"eventlistener": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/eventlistener/-/eventlistener-0.0.1.tgz",
"resolved": "https://registry.npm.taobao.org/eventlistener/download/eventlistener-0.0.1.tgz",
"integrity": "sha1-7Suqu4UiJ68rz4iRUscsY8pTLrg="
},
"events": {
@ -7925,7 +7920,7 @@
},
"hammerjs": {
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz",
"resolved": "https://registry.npm.taobao.org/hammerjs/download/hammerjs-2.0.8.tgz",
"integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE="
},
"handle-thing": {
@ -8766,7 +8761,7 @@
},
"immutable": {
"version": "3.7.6",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz",
"resolved": "https://registry.npm.taobao.org/immutable/download/immutable-3.7.6.tgz",
"integrity": "sha1-E7TTyxK++hVIKib+Gy665kAHHks="
},
"import-fresh": {
@ -10354,7 +10349,7 @@
},
"lodash.throttle": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
"resolved": "https://registry.npm.taobao.org/lodash.throttle/download/lodash.throttle-4.1.1.tgz",
"integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
},
"lodash.uniq": {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,48 @@
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "14835599",
"name": "右箭头",
"font_class": "arrowRight",
"unicode": "e863",
"unicode_decimal": 59491
},
{
"icon_id": "21151489",
"name": "箭头镂空-左",
"font_class": "jiantouloukong-zuo",
"unicode": "e861",
"unicode_decimal": 59489
},
{
"icon_id": "21151557",
"name": "箭头镂空-右",
"font_class": "jiantouloukong-you",
"unicode": "e862",
"unicode_decimal": 59490
},
{
"icon_id": "21568989",
"name": "分享",
"font_class": "fenxiang1",
"unicode": "e89c",
"unicode_decimal": 59548
},
{
"icon_id": "21568990",
"name": "回到顶部",
"font_class": "huidaodingbu1",
"unicode": "e89d",
"unicode_decimal": 59549
},
{
"icon_id": "21568993",
"name": "帮助",
"font_class": "bangzhu",
"unicode": "e8a0",
"unicode_decimal": 59552
},
{
"icon_id": "991344",
"name": "提交",

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/favicon-1.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

BIN
public/favicon.ico Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 66 KiB

View File

@ -35,14 +35,24 @@ const theme = createMuiTheme({
secondary: { main: '#4CACFF' }, // #11cb5f This is just green.A700 as hex.
},
});
//forge项目
const Projects = Loadable({
loader: () => import('./forge/Index'),
//首页
const Index = Loadable({
loader: () => import('./mulan/Index'),
loading: Loading,
})
//forge项目-devOps详情
const OpsDetail = Loadable({
loader: () => import('./forge/DevOps/opsDetail'),
//首页
const License = Loadable({
loader: () => import('./mulan/license/Index'),
loading: Loading,
})
//开源活动
const Activity = Loadable({
loader: () => import('./mulan/Activity/Index'),
loading: Loading,
})
//关于我们
const About = Loadable({
loader: () => import('./mulan/About/Index'),
loading: Loading,
})
//403页面
@ -55,26 +65,15 @@ const Shixunnopage = Loadable({
loader: () => import('./modules/404/Shixunnopage'),
loading: Loading,
})
const Projects =Loadable({
loader: () => import('./mulan/Projects/Index'),
loading: Loading,
})
//500页面
const http500 = Loadable({
loader: () => import('./modules/500/http500'),
loading: Loading,
})
const InfosIndex = Loadable({
loader: () => import('./forge/users/Index'),
loading: Loading,
})
// 组织
const OrganizeIndex = Loadable({
loader: () => import('./forge/Team/Index'),
loading: Loading,
})
const EducoderLogin = Loadable({
loader: () => import('./modules/login/EducoderLogin'),
loading: Loading,
})
class App extends Component {
constructor(props) {
super(props);
@ -131,7 +130,7 @@ class App extends Component {
});
initAxiosInterceptors(this.props);
this.getAppdata();
this.getSetting();
window.addEventListener('error', (event) => {
const msg = `${event.type}: ${event.message}`;
@ -144,67 +143,30 @@ class App extends Component {
})
};
//获取数据为空的时候
gettablogourlnull = () => {
this.setState({
mygetHelmetapi: undefined
});
document.title = "Forge";
var link = document.createElement('link'),
oldLink = document.getElementById('dynamic-favicon');
link.id = 'dynamic-favicon';
link.rel = 'shortcut icon';
link.href = "/react/build/./favicon.ico";
if (oldLink) {
document.head.removeChild(oldLink);
}
document.head.appendChild(link);
};
//获取数据的时候
getSetting=()=>{
const url = `/setting.json`;
axios.get(url).then(result=>{
if(result){
this.setState({
headData:result.data.data
})
this.gettablogourldata(result.data.data)
}
}).catch(error=>{})
}
gettablogourldata = (response) => {
document.title = response.data.setting.name;
var link = document.createElement('link'),
oldLink = document.getElementById('dynamic-favicon');
document.title = response.name;
var link = document.createElement('link'),oldLink = document.getElementById('dynamic-favicon');
link.id = 'dynamic-favicon';
link.rel = 'shortcut icon';
link.href = '/' + response.data.setting.tab_logo_url;
link.href = '/' + response.tab_logo_url;
if (oldLink) {
document.head.removeChild(oldLink);
}
document.head.appendChild(link);
}
//获取当前定制信息
getAppdata = () => {
let url = "/setting.json";
axios.get(url).then((response) => {
if (response) {
if (response.data) {
this.setState({
mygetHelmetapi: response.data.setting
});
//存储配置到游览器
localStorage.setItem('chromesetting', JSON.stringify(response.data.setting));
localStorage.setItem('chromesettingresponse', JSON.stringify(response));
try {
if (response.data.setting.tab_logo_url) {
this.gettablogourldata(response);
} else {
this.gettablogourlnull();
}
} catch (e) {
this.gettablogourlnull();
}
} else {
this.gettablogourlnull();
}
} else {
this.gettablogourlnull();
}
}).catch((error) => {
this.gettablogourlnull();
});
};
render() {
return (
@ -214,60 +176,45 @@ class App extends Component {
<LoginDialog {...this.props} {...this.state} Modifyloginvalue={() => this.Modifyloginvalue()}></LoginDialog>
<Router>
<Switch>
{/*项目*/}
<Route
path={"/projects/:owner/:projectId/devops/:opsId/detail"}
render={
(props) => {
return (<OpsDetail {...this.props} {...props} {...this.state} />)
}
}>
</Route>
{/*项目*/}
<Route
path={"/projects"}
render={
(props) => {
return (<Projects {...this.props} {...props} {...this.state} />)
}
}>
</Route>
<Route
path="/register"
render={
(props) => {
return (<EducoderLogin {...this.props} {...props} {...this.state} />)
}
}
/>
{/*403*/}
<Route path="/403" component={Shixunauthority} />
<Route path="/500" component={http500} />
<Route path={"/organize"}
render={
(props) => {
return (<OrganizeIndex {...props} {...this.props} {...this.state} />)
}
}>
</Route>
{/*404*/}
<Route path="/nopage" component={Shixunnopage} />
{/* 个人主页 */}
<Route path="/users/:username"
render={
(props) => {
return (<InfosIndex {...this.props} {...this.state} />)
}
}></Route>
<Route exact path="/"
<Route path="/projects"
render={
(props) => (
<Projects {...this.props} {...props} {...this.state}></Projects>
)
}
/>
<Route component={Shixunnopage} />
<Route path="/activity"
render={
(props) => (
<Activity {...this.props} {...props} {...this.state}></Activity>
)
}
/>
<Route path="/about"
render={
(props) => (
<About {...this.props} {...props} {...this.state}></About>
)
}
/>
<Route path="/license"
render={
(props) => (
<License {...this.props} {...props} {...this.state}></License>
)
}
/>
<Route exact path="/"
render={
(props) => (
<Index {...this.props} {...props} {...this.state}></Index>
)
}
/>
</Switch>
</Router>
</MuiThemeProvider>

View File

@ -34,7 +34,7 @@ function clearAllCookie() {
cookie.remove('autologin_trustie', { path: '/' });
setpostcookie()
}
clearAllCookie();
// clearAllCookie();
function setpostcookie() {
const str = window.location.pathname;
if (str.indexOf("/wxcode") !== -1) {
@ -50,13 +50,13 @@ function setpostcookie() {
}
}
}
setpostcookie();
// setpostcookie();
window._debugType = debugType;
export function initAxiosInterceptors(props) {
initOnlineOfflineListener();
var proxy = "http://localhost:3000";
proxy = "https://testforgeplus.trustie.net";
proxy = "https://mulan.trustie.net";
const requestMap = {};
window.setfalseInRequestMap = function (keyName) {
@ -65,10 +65,10 @@ export function initAxiosInterceptors(props) {
//响应前的设置
axios.interceptors.request.use(
config => {
setpostcookie()
clearAllCookie()
// clearAllCookie()
// setpostcookie()
if (config.url.indexOf(proxy) !== -1) {
if (config.url.indexOf("http") !== -1) {
return config
}
requestProxy(config)
@ -79,20 +79,20 @@ export function initAxiosInterceptors(props) {
if (window.location.port === "3007") {
config.url = `${proxy}${url}`;
if (config.url.indexOf('?') === -1) {
config.url = `${config.url}?debug=${debugType}`;
config.url = `${config.url}`;
} else {
config.url = `${config.url}&debug=${debugType}`;
config.url = `${config.url}`;
}
} else {
config.url = url;
}
setpostcookie();
// setpostcookie();
}
if (config.url.indexOf('update_file') === -1) {
requestMap[config.url] = true;
// if (config.url.indexOf('update_file') === -1) {
// requestMap[config.url] = true;
window.setTimeout("setfalseInRequestMap('" + config.url + "')", 900)
}
// window.setTimeout("setfalseInRequestMap('" + config.url + "')", 900)
// }
return config;
},
err => {
@ -147,7 +147,7 @@ export function initAxiosInterceptors(props) {
}, 2000);
}
requestMap[response.config.url] = false;
setpostcookie();
// setpostcookie();
return response;
}, function (error) {
return Promise.reject(error);

View File

@ -6,12 +6,12 @@ const { Search } = Input;
const $ = window.$;
const isDev = window.location.port == 3007;
const isdev2= window.location.hostname ==='www.educoder.net'
export const TEST_HOST = "https://testforgeplus.trustie.net/"
export const TEST_HOST = "https://mulan.trustie.net/"
export function getImageUrl(path) {
// https://www.educoder.net
// https://testbdweb.trustie.net
// const local = 'http://localhost:3000'
const local = 'https://testforgeplus.trustie.net';
const local = 'https://mulan.trustie.net';
if (isDev) {
return `${local}/${path}`
}
@ -19,10 +19,7 @@ export function getImageUrl(path) {
}
export function getImage(path) {
// https://www.educoder.net
// https://testbdweb.trustie.net
// const local = 'http://localhost:3000'
const local = 'https://testforgeplus.trustie.net/';
const local = 'https://mulan.trustie.net/';
if(path.indexOf("http://")===-1){
if (isDev) {
return `${local}/images/${path}`
@ -34,9 +31,6 @@ export function getImage(path) {
}
export function getcdnImageUrl(path) {
// https://www.educoder.net
// https://testbdweb.trustie.net
// const local = 'http://localhost:3000'
const testlocal = 'https://testali-cdn.educoder.net'
const local='https://ali-cdn.educoder.net'
let firstStr=path.substr(0,1);
@ -93,7 +87,7 @@ export function setImagesUrl(path){
}
export function getUrl(path, goTest) {
const local = 'https://testforgeplus.trustie.net'
const local = 'https://mulan.trustie.net'
if (isDev) {
return `${local}${path?path:''}`
}

View File

@ -44,6 +44,11 @@ export const Long = styled.div`{
border-radius:5px;
margin-bottom:30px;
}`
export const Longdata = styled.div`{
width:69%;
border-radius:5px;
margin-bottom:30px;
}`
export const ShortWidth = styled.div`{
width:300px;
border-radius:5px;

View File

@ -1,6 +1,7 @@
import React , { Component } from 'react';
import nodata from './Images/nodata.png';
import './css/nodata.scss';
class Nodata extends Component{
render(){

View File

@ -5,7 +5,7 @@ import axios from "axios";
import Upload from "../Upload/Index";
import UploadImg from "../Images/upload.png";
import { getImageUrl } from "educoder";
import { List, Popconfirm, Pagination, Button, Tabs, Avatar } from "antd";
import { List, Popconfirm, Pagination, Button, Tabs } from "antd";
import Attachments from "../Upload/attachment";
import MDEditor from "../../modules/tpm/challengesnew/tpm-md-editor";
import RenderHtml from "../../components/render-html";

View File

@ -84,26 +84,6 @@ ul,ol,dl{
background-color: #f4f4f4;
line-height: 18px;
}
.none_panels{
text-align: center;
height: 400px;
display: flex;
justify-content: center;
align-items: center;
&.small{
height: 120px;
img{
width:50px;
}
}
img{
margin-bottom: 15px;
}
}
.none_p_title{
font-size: 16px;
color: #999;
}
form.ant-form{
color:#333;
}

21
src/forge/css/nodata.scss Normal file
View File

@ -0,0 +1,21 @@
.none_panels{
text-align: center;
height: 400px;
display: flex;
justify-content: center;
align-items: center;
&.small{
height: 120px;
img{
width:50px;
}
}
img{
margin-bottom: 15px;
}
.none_p_title{
font-size: 16px;
color: #999;
}
}

View File

@ -66,19 +66,3 @@ export function requestProxy(config) {
}
return config;
}
/**
('/api/v1/careers/qweqw/edit/').match(/\/api\/v1\/careers\/(\w*)\/edit/i)
0: "/api/v1/careers/qweqw/edit"
1: "qweqw"
example:
`/api/v1/games/${this.props.game.identifier}/answer_grade` ->
`/tasks/${this.props.game.identifier}/answer_grade.json`
https://testeduplus2.educoder.net/api/v1/games/feguz4tiqpvx/rep_content
?path=src/step2/CLnkQueue.cpp&shixun_gpid=2791&status=0&retry=0 ->
http://testeduplus2.educoder.net/tasks/tonblikwzj78/rep_content.json
?path=1-4.py&shixun_gpid=2448&status=0
*/

31
src/mulan/About/About.jsx Normal file
View File

@ -0,0 +1,31 @@
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import { Banner } from '../../forge/Component/layout';
import { Skeleton } from 'antd';
function About(props){
const [ about , setAbout ] = useState(undefined);
useEffect(()=>{
getAbout();
},[])
function getAbout(){
const url = `/helps/about.json`;
axios.get(url).then(result=>{
if(result && result.data){
setAbout(result.data.data);
}
}).catch(error=>{})
}
return(
<div>
<Banner>关于我们</Banner>
<div className="desc">
{about ? about.content : <Skeleton />}
</div>
</div>
)
}
export default About;

67
src/mulan/About/Index.jsx Normal file
View File

@ -0,0 +1,67 @@
import React , { useEffect , useState } from 'react';
import { HomeHoc } from '../HOC/HomeHoc';
import {Route, Switch , Link } from 'react-router-dom';
import Loadable from 'react-loadable';
import Loading from '../../Loading';
import { Box , Long , Short , Gap } from '../../forge/Component/layout';
import './Index.scss';
import { Menu } from 'antd';
import'./Index.scss';
const About = Loadable({
loader: () => import('./About'),
loading: Loading,
})
const TOC = Loadable({
loader: () => import('./TOC'),
loading: Loading,
})
function Index(props){
const [ nav , setNav ] = useState("0");
const pathname = props.history.location.pathname;
useEffect(()=>{
if(pathname){
if(pathname === `/about`){
setNav("0");
}
if(pathname === `/about/TOC`){
setNav("1");
}
}
},[pathname])
return(
<div className="aboutboxs">
<Box>
<Short>
<Menu selectedKeys={[nav]} mode="inline" className="menus">
<Menu.Item key="0"><Link to={`/about`}>关于我们</Link></Menu.Item>
<Menu.Item key="1"><Link to={`/about/TOC`}>TOC</Link></Menu.Item>
</Menu>
</Short>
<Long>
<Gap>
<div className="boxshadow">
<Switch {...props}>
<Route
path="/about/TOC"
render={(p) => (
<TOC {...props} {...p} />
)}
></Route>
<Route
path="/about"
render={(p) => (
<About {...props} {...p} />
)}
></Route>
</Switch>
</div>
</Gap>
</Long>
</Box>
</div>
)
}
export default HomeHoc(Index);

View File

@ -0,0 +1,56 @@
.aboutboxs{
width: 1200px;
margin:20px auto;
.menus.ant-menu-inline {
border-right: none;
box-shadow: 0px 0px 8px 0px #f1f1f1;
}
.boxshadow {
box-shadow: 0px 0px 10px rgba(0,0,0,0.1);
}
.desc{
line-height: 26px;
font-size: 16px;
color: #333;
text-indent: 2em;
text-align: justify;
padding:40px;
}
}
.descTOC{
padding:20px;
.descUl{
text-indent: 0px;
margin-left: 20px;
padding-left: 0px;
li{
list-style-type: disc;
margin-left:20px;
}
}
.descPerson{
display: flex;
flex-wrap: wrap;
padding:10px 0px;
align-items: flex-start;
li{
width: 25%;
display: flex;
align-items: center;
flex-direction: column;
justify-content: center;
margin:20px 4%!important;
text-align: center;
img{
margin-bottom: 15px;
height: 100px;
width: 100px;
border-radius: 50%;
cursor: default;
}
span.school{
color: #888;
}
}
}
}

68
src/mulan/About/TOC.jsx Normal file
View File

@ -0,0 +1,68 @@
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import { Banner } from '../../forge/Component/layout';
import { Skeleton } from 'antd';
import RenderHtml from '../../components/render-html';
function TOC(props){
const [ data , setData ] = useState(undefined);
useEffect(()=>{
getAbout();
},[])
function getAbout(){
const url = `/helps/toc.json`;
axios.get(url).then(result=>{
if(result && result.data){
setData(result.data.data);
}
}).catch(error=>{})
}
return(
<div>
<Banner>木兰开源社区技术委员会</Banner>
{
data ?
<RenderHtml className="break_word_comments imageLayerParent" value={data.content} url={props.history.location}/>
// <div className="descTOC">
// <ul className="descUl">
// <li></li>
// <li></li>
// <li></li>
// </ul>
// <ul className="descPerson">
// <li>
// <img src="" alt="" />
// <span className="name"></span>
// <span className="school"></span>
// </li>
// <li>
// <img src="" alt="" />
// <span className="name"></span>
// <span className="school"></span>
// </li>
// <li>
// <img src="" alt="" />
// <span className="name"></span>
// <span className="school"></span>
// </li>
// <li>
// <img src="" alt="" />
// <span className="name"></span>
// <span className="school"></span>
// </li>
// <li>
// <img src="" alt="" />
// <span className="name"></span>
// <span className="school"></span>
// </li>
// </ul>
// </div>
:<Skeleton />
}
</div>
)
}
export default TOC;

View File

@ -0,0 +1,115 @@
import React, { useEffect, useState } from 'react';
import { Skeleton } from 'antd';
import { Box , Longdata , Short , Gap , AlignCenter } from '../../forge/Component/layout';
import RenderHtml from '../../components/render-html';
import Axios from 'axios';
import { getUrlHead } from '../Data/getUrl';
import Comments from '../comments/Index';
function Detail(props){
const id = props.match.params.id;
const [ data , setData ] = useState(undefined);
useEffect(()=>{
if(id){
getDetail();
}
},[id])
function getDetail(){
const url = `/topics/${id}.json`;
Axios.get(url).then(result=>{
if(result && result.data){
setData(result.data.data);
}
}).catch(error=>{})
}
return(
<div className="detailpanels">
<div className="panels">
{
data ?
<div className="detailinfo">
<img src={getUrlHead(data.cover)} className="cover" alt=""/>
<div className="detailvalue">
<p className="task-hide-2 name">{data.title}</p>
{ data.created_at && <p><span className="tag">时间</span><span className="value">{data.created_at}</span></p>}
{ data.city && <p><span className="tag">地点</span><span className="value">{data.city}</span></p>}
{ data.viewers_count && <p><i className="iconfont icon-dianjiliang"></i><span className="value" style={{color:"#999"}}>{data.viewers_count}</span></p>}
</div>
</div>
:<Skeleton />
}
<Box>
<Longdata>
{
data ?
<div className="detailContents">
<h2>活动详情</h2>
<RenderHtml className="break_word_comments imageLayerParent" value={data.content} url={props.history.location}/>
{
data.attachments && data.attachments.length>0&&
<ul className="mt20">
{
data.attachments.map((i,k)=>{
return(
<li><a href={getUrlHead(i.url)}><i className="iconfont icon-wenjia mr5 font-14 color-grey-9"></i>{i.filename}</a></li>
)
})
}
</ul>
}
</div>
:<Skeleton />
}
<Comments id={id} users={props.users} mainShowLogin={props && props.mainShowLogin}/>
</Longdata>
<Short>
<Gap>
{
data && data.author ?
<div className="detailAuthor">
<h2>发布者</h2>
<AlignCenter>
<img src={getUrlHead(data.author.image_url)} alt="" className="authorImg"/>
<span className="authorName">{data.author.name}</span>
</AlignCenter>
</div>
:""
}
{
data && data.recommend_topics && data.recommend_topics.length>0 ?
<div className="detailRecommand">
<h2>推荐活动</h2>
<ul className="recommandslist">
{
data.recommend_topics.map((i,k)=>{
return(
<li onClick={()=>{props.history.push(`/activity/1`)}}>
<img src={getUrlHead(i.cover)} alt="" />
<div className="numInfo">
<p className="task-hide-2 name">{i.title}</p>
<div className="number">
{ i.created_at && <span><i className="iconfont icon-timefill"></i><span>{i.created_at}</span></span> }
{ i.city && <span><i className="iconfont icon-weizhi"></i><span>{i.city}</span></span> }
</div>
</div>
</li>
)
})
}
</ul>
</div>
:""
}
</Gap>
</Short>
</Box>
</div>
</div>
)
}
export default Detail;

View File

@ -0,0 +1,36 @@
import React from 'react';
import { HomeHoc } from '../HOC/HomeHoc';
import {Route, Switch } from 'react-router-dom';
import Loadable from 'react-loadable';
import Loading from '../../Loading';
import './Index.scss';
import'./Index.scss';
const Detail = Loadable({
loader: () => import('./Detail'),
loading: Loading,
})
const List = Loadable({
loader: () => import('./List'),
loading: Loading,
})
function Index(props){
return(
<Switch {...props}>
<Route
path="/activity/:id"
render={(p) => (
<Detail {...props} {...p} />
)}
></Route>
<Route
path="/activity"
render={(p) => (
<List {...props} {...p} />
)}
></Route>
</Switch>
)
}
export default HomeHoc(Index);

View File

@ -0,0 +1,226 @@
.panels{
width: 1200px;
margin:0px auto;
.sortPanel{
border-radius: 2px;
border: 1px solid #EEEEEE;
padding:25px 0px 0px 35px;
margin:30px 0px 20px;
& > div{
display: flex;
font-size: 16px;
color: #333;
margin-bottom: 10px;
& > span{
display: block;
height: 30px;
line-height: 30px;
padding-right: 30px;
}
& >ul {
flex: 1;
display: flex;
flex-wrap: wrap;
margin-bottom: 0px;
& > li{
margin-bottom: 10px;
margin-right: 20px;
padding:0px 14px;
height: 30px;
line-height: 30px;
cursor: pointer;
&.active{
background-color: #1890FF;
color: #fff;
border-radius: 3px;
}
}
}
}
}
.menus{
margin-bottom: 10px;
.ant-menu-item{
padding:10px 0px;
margin:0px 40px 0px 0px;
font-size: 18px;
position: relative;
&.ant-menu-item-selected,&.ant-menu-item-active,&:hover{
border-bottom-color: transparent;
}
&.ant-menu-item-selected::after{
position: absolute;
width: 40px;
left: 50%;
margin-left: -20px;
bottom: -2px;
content: "";
background-color: #1890ff;
height: 2px;
}
}
}
.lists{
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
align-items: flex-start;
min-height: 400px;
li{
cursor: pointer;
width: 270px;
margin:20px 40px 20px 0px;
background: #FFFFFF;
box-shadow: 0px 0px 8px 0px #F1F1F1;
border-radius: 0px 0px 8px 8px;
&:nth-child(4n){
margin-right: 0px;
}
.imgCover{
width: 100%;
height: 160px;
border-radius: 8px 8px 0px 0px;
}
.infos{
padding:20px 16px;
.infoName{
color: #666;
line-height: 22px;
margin-bottom: 15px;
}
.infosItem{
display: flex;
justify-content: space-between;
font-size: 12px;
color: #888888;
height: 18px;
line-height: 18px;
&>span{
display: flex;
align-items: center;
i{
font-size: 15px!important;
margin-right: 7px;
color: #B9DDFF;
}
}
}
}
}
}
}
.detailpanels{
padding:40px 0px;
background-color: #fafafa;
.detailinfo{
margin-bottom: 30px;
background-color: #fff;
display: flex;
align-items: center;
border-radius:4px;
.cover{
width: 500px;
border-radius: 4px 0px 0px 4px;
}
.detailvalue{
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
padding:30px 20px;
flex: 1;
word-break: break-all;
& > p{
margin-bottom: 10px;
line-height: 22px;
.tag{
color: #888888;
}
.value{
color: #666;
}
&>i{
font-size: 12px!important;
color: #888888;
margin-right: 5px;
}
}
.name{
font-size: 22px;
color: #333;
line-height: 30px;
margin-bottom: 18px;
}
}
}
.detailContents{
background-color: #fff;
padding:38px;
}
.detailAuthor{
background-color: #fff;
padding:20px 20px 30px;
.authorImg{
width: 100px;
height: 100px;
border-radius: 50%;
margin-right: 50px;
margin-top: 15px;
}
.authorName{
font-size: 16px;
color: #333;
}
}
.detailRecommand{
margin-top: 24px;
background-color: #fff;
padding:20px;
.recommandslist{
li{
display: flex;
margin-top:28px ;
cursor: pointer;
img{
max-width: 150px;
height: 90px;
margin-right: 15px;
}
.name{
color: #333;
line-height: 18px;
margin-bottom: 8px;
word-break: break-all;
}
.numInfo{
flex:1;
width: 0;
display: flex;
justify-content: center;
flex-direction: column;
.number{
& > span{
display: flex;
line-height: 18px;
font-size: 12px;
height: 18px;
align-items: center;
span{
color: #888;
}
i{
margin-right: 8px;
font-size: 14px!important;
color: #B9DDFF;
}
}
}
}
}
}
}
}

169
src/mulan/Activity/List.jsx Normal file
View File

@ -0,0 +1,169 @@
import React, { useState , useEffect } from 'react';
import { Menu , Pagination , Spin } from 'antd';
import { getUrlHead } from '../Data/getUrl';
import Cards from '../images/cards.png';
import Axios from 'axios';
import Nodata from '../../forge/Nodata';
const timeArray=[
{name:"今天",id:"today"},
{name:"明天",id:"tomorrow"},
{name:"本周",id:"toweek"},
{name:"本月",id:"tomonth"}
]
const cityArray=[
{name:"北京",id:"北京"},
{name:"上海",id:"上海"},
{name:"广州",id:"广州"},
{name:"深圳",id:"深圳"},
{name:"杭州",id:"杭州"},
{name:"成都",id:"成都"},
{name:"南京",id:"南京"},
{name:"苏州",id:"苏州"},
{name:"武汉",id:"武汉"},
{name:"天津",id:"天津"},
{name:"重庆",id:"重庆"},
{name:"西安",id:"西安"},
{name:"厦门",id:"厦门"},
{name:"宁波",id:"宁波"},
{name:"郑州",id:"郑州"},
{name:"青岛",id:"青岛"},
{name:"东莞",id:"东莞"},
{name:"佛山",id:"佛山"},
{name:"长沙",id:"长沙"},
{name:"石家庄",id:"石家庄"},
{name:"昆明",id:"昆明"},
];
const limit = 16;
function List(props){
const [ sort , setSort ] = useState(undefined);
const [ sortArray ,setSortArray ] = useState(undefined);
const [ time , setTime ] = useState(undefined);
const [ city , setCity ] = useState(undefined);
const [ menu , setMenu ] = useState("start_time");
const [ isSpin , setIsSpin ] =useState(false);
const [ page ,setPage ]= useState(1);
const [ total , setTotal ] = useState(0);
const [ list , setList ] = useState(undefined);
useEffect(()=>{
getSort();
},[])
function getSort(){
const url = `/topic_categories.json`
Axios.get(url,{
params:{
topic_type:"activity"
}
}).then(result=>{
if(result && result.data){
setSortArray(result.data.topic_categories)
}
}).catch(error=>{})
}
useEffect(()=>{
setIsSpin(true);
getList();
},[sort,time,city,menu])
function getList(){
const url = `/topics.json`;
Axios.get(url,{
params:{
topic_type:"activity",
time_type:time,
city,
sort_by:menu,
sort_direction:"desc",
topic_category_id:sort
}
}).then(result=>{
if(result && result.data){
setList(result.data.data);
setTotal(result.data.total_count);
setIsSpin(false);
}
}).catch(error=>{})
}
function renderMenu(data,func,name){
return(
data.map((i,k)=>{
return(
<li onClick={()=>func(i.id)} className={name===i.id?"active":""}>{i.name}</li>
)
})
)
}
return(
<div className="panels">
<div className="sortPanel">
<div>
<span>分类</span>
<ul>
<li className={!sort ? "active":""} onClick={()=>setSort(undefined)}>全部</li>
{ sortArray && renderMenu(sortArray,setSort,sort)}
</ul>
</div>
<div>
<span>时间</span>
<ul>
<li className={!time ? "active":""} onClick={()=>setTime(undefined)}>全部</li>
{ renderMenu(timeArray,setTime,time)}
</ul>
</div>
<div>
<span>城市</span>
<ul>
<li className={!city ? "active":""} onClick={()=>setCity(undefined)}>全部</li>
{ renderMenu(cityArray,setCity,city)}
</ul>
</div>
</div>
<div>
<Menu selectedKeys={[menu]} mode={"horizontal"} className="menus" onSelect={(e)=>{setIsSpin(true);setMenu(e.key)}}>
<Menu.Item key="start_time">最新发布</Menu.Item>
<Menu.Item key="viewers_count">热门活动</Menu.Item>
</Menu>
<Spin spinning={isSpin}>
{
list && list.length > 0 ?
<ul className="lists">
{
list.map((i,k)=>{
return(
<li onClick={()=>{props.history.push(`/activity/${i.id}`)}}>
<img src={getUrlHead(i.cover)} className="imgCover"/>
<div className="infos">
<p className="task-hide-2 infoName">{i.title}</p>
<div className="infosItem">
{ i.created_at && <span><i className="iconfont icon-timefill"></i><span>{i.created_at}</span></span> }
{ i.city && <span><i className="iconfont icon-weizhi"></i><span>{i.city}</span></span> }
</div>
</div>
</li>
)
})
}
</ul>:""
}
{
list && list.length === 0 && <Nodata _html="暂无活动~"/>
}
</Spin>
{
total > limit &&
<div>
<Pagination showQuickJumper pageSize={limit} current={page} total={total} onChange={e=>{setPage(e)}}/>
</div>
}
</div>
</div>
)
}
export default List;

View File

@ -0,0 +1,45 @@
.loginModal{
.homeHOCLoginmenu{
.ant-menu-item{
padding:0px;
margin:0px 30px;
height: 58px;
line-height: 58px;
}
}
.ant-modal-close{
top:0px!important;
height: 48px;
line-height: 48px;
display: flex;
align-items: center;
}
.ant-modal-body{
padding:0px;
.ant-menu{
border-radius: 8px 8px 0px 0px;
font-size: 17px;
}
.content{
padding:40px 50px;
.subbtn{
display: block;
height: 40px;
line-height: 40px;
width: 100%;
text-align: center;
border-radius: 5px;
background-color: #ccc;
color: #fff;
margin-bottom: 10px;
font-size:16px;
&.activeBtn{
background-color: #1890ff;
}
}
.saveitem{
margin-bottom: 0px;
}
}
}
}

View File

@ -0,0 +1,74 @@
import React, { useState , forwardRef } from 'react';
import { Form , Input , Checkbox, Button } from 'antd';
import "./Index.scss";
import axios from 'axios';
function LoginModal({form , successFunc , visible}){
const [ active , setActive ] = useState(false);
const [ loading , setLoading ] = useState(false);
const { getFieldDecorator, validateFields , setFieldsValue , getFieldsValue } = form;
function changeInput(){
const { login , password } = getFieldsValue();
if(login && password){
setActive(true);
}else{
setActive(false);
}
}
function loginFunc() {
validateFields((error,values)=>{
if(!error){
setLoading(true);
const url = `/accounts/login.json`;
axios.post(url,{
...values
}).then(result=>{
successFunc(result.data.data);
setLoading(false);
if(!values.savePass){
setFieldsValue({
login:undefined,
password:undefined,
savePass:false
})
}
}).catch(error=>{})
}
})
}
return(
<Form>
<Form.Item>
{getFieldDecorator("login",{
rules:[{required:true,message:"请输入登录账号"}]
})(
<Input placeholder="请输入登录账号" onBlur={changeInput} onChange={changeInput}/>
)}
</Form.Item>
<Form.Item>
{getFieldDecorator("password",{
rules:[{required:true,message:"请输入登录密码"}]
})(
<Input.Password placeholder="请输入登录密码" onBlur={changeInput} onChange={changeInput}/>
)}
</Form.Item>
{
active ?
<Button className="subbtn activeBtn" loading={loading} onClick={loginFunc}>登录</Button>
:
<span className="subbtn">登录</span>
}
<Form.Item className="saveitem">
{getFieldDecorator("savePass",
{rules:[]},{valuePropName:"checked"}
)(
<Checkbox>记住密码</Checkbox>
)}
</Form.Item>
</Form>
)
}
export default Form.create()(forwardRef(LoginModal));

View File

@ -0,0 +1,36 @@
import React, { useState } from 'react';
import { Modal , Menu } from 'antd';
import "./Index.scss";
import Login from './Login';
import Register from './Register';
function LoginModal({visible,onCancel,successFunc}){
const [ n , setN ] = useState("0");
return(
<Modal
visible={visible}
closable={true}
width="440px"
title={false}
footer={false}
className="loginModal"
centered
onCancel={onCancel}
>
<Menu defaultSelectedKeys={[n]} mode={"horizontal"} className="homeHOCLoginmenu">
<Menu.Item key="0" onClick={(e)=>setN(e.key)}>登录</Menu.Item>
<Menu.Item key="1" onClick={(e)=>setN(e.key)}>注册</Menu.Item>
</Menu>
<div className="content">
{
n === "0"?
<Login successFunc={successFunc} visible={visible}/>
:
<Register successFunc={successFunc} visible={visible}/>
}
</div>
</Modal>
)
}
export default LoginModal;

View File

@ -0,0 +1,99 @@
import React, { useState , forwardRef , useEffect } from 'react';
import { Form , Input , Button } from 'antd';
import "./Index.scss";
import axios from 'axios';
function Register({ form , successFunc , visible }){
const [ active , setActive ] = useState(false);
const [ loading , setLoading ] = useState(false);
const { getFieldDecorator, validateFields , setFieldsValue , getFieldsValue } = form;
useEffect(()=>{
if(!visible){
setFieldsValue({
mail:undefined,
password:undefined,
passwordAgain:undefined
})
}
},[visible])
function changeInput(){
const { mail , password , passwordAgain } = getFieldsValue();
if(mail && password && passwordAgain){
setActive(true);
}else{
setActive(false);
}
}
function registerFunc(){
validateFields((error,values)=>{
if(!error){
setLoading(true);
const url = `/accounts/register.json`;
axios.post(url,{
...values
}).then(result=>{
if(result && result.data && result.data.status === 0){
successFunc(result.data.data);
setLoading(false);
setFieldsValue({
mail:undefined,
password:undefined,
passwordAgain:undefined
})
}
}).catch(error=>{})
}
})
}
function checkRepeat(rule, value, callback){
const { password } = getFieldsValue();
if(!value){
callback();
}else if(value !== password){
callback("两次输入的密码不一致!");
}else{
callback();
}
callback();
}
return(
<Form>
<Form.Item>
{getFieldDecorator("mail",{
rules:[{required:true,message:"请输入注册账号"}]
})(
<Input placeholder="请输入邮箱账号" onChange={changeInput}/>
)}
</Form.Item>
<Form.Item >
{getFieldDecorator("password",{
rules:[{required:true,message:"请输入注册密码"}]
})(
<Input.Password placeholder="请输入注册密码" onChange={changeInput}/>
)}
</Form.Item>
<Form.Item >
{getFieldDecorator("passwordAgain",{
rules:[
{required:true,message:"请再次输入密码"},
{validator:checkRepeat}
]
})(
<Input.Password placeholder="请再次输入密码" onChange={changeInput}/>
)}
</Form.Item>
{
active ?
<Button className="subbtn activeBtn" loading={loading} onClick={registerFunc}>注册</Button>
:
<span className="subbtn">注册</span>
}
</Form>
)
}
export default Form.create()(forwardRef(Register));

10
src/mulan/Data/getUrl.jsx Normal file
View File

@ -0,0 +1,10 @@
const local = 'https://mulan.trustie.net';
const isDev = window.location.port === "3007";
export function getUrlHead(path) {
if (isDev) {
return `${local}/${path}`;
}
return `${path}`;
}

20
src/mulan/HOC/Footer.jsx Normal file
View File

@ -0,0 +1,20 @@
import React from 'react';
import { Link } from 'react-router-dom';
function Footer(props){
return(
<div className="homefooterbase">
<ul className="homeFooterUl">
<li><Link to={``}>关于我们</Link></li>
<li><Link to={``}>帮助中心</Link></li>
<li><Link to={``}>合作伙伴</Link></li>
<li><Link to={``}>服务协议</Link></li>
</ul>
<div>
<span>木兰开源社区版权所有 ©2020 技术支持:长沙智擎科技</span>
<a href="http://www.beian.miit.gov.cn/" target="_blank" style={{color:"#666"}}>粤ICP备12009483号</a>
</div>
</div>
)
}
export default Footer;

17
src/mulan/HOC/Header.jsx Normal file
View File

@ -0,0 +1,17 @@
import React from 'react';
import Nav from './Nav';
function Header({ users,headData, mainShowLogin , quitFunc , match }){
return(
<div className="headsnav">
<Nav
match={match}
users={users}
headData={headData}
mainShowLogin={mainShowLogin}
quitFunc={quitFunc}
/>
</div>
)
}
export default Header;

80
src/mulan/HOC/HomeHoc.jsx Normal file
View File

@ -0,0 +1,80 @@
import React, { Component } from 'react';
import Header from './Header';
import Footer from './Footer';
import './Index.scss';
import axios from 'axios';
import LoginModal from '../Components/Login/LoginModal';
export function HomeHoc(Sub){
return class II extends Component {
constructor(props){
super(props);
this.state={
users:undefined,
visible:false
}
}
componentDidMount=()=>{
this.getUser();
}
getUser=()=>{
const url = `/users/get_user_info.json`;
axios.get(url).then(result=>{
if(result){
this.setState({
users:result.data.data
})
}
}).catch(error=>{})
}
successFunc=(data)=> {
this.setState({
visible:false
})
if(data){
this.setState({
users:data
})
}else{
this.getUser();
}
}
quitFunc =()=>{
const url = `/accounts/logout.json`;
axios.get(url).then(result=>{
if(result && result.data){
this.setState({
users:undefined
})
}
}).catch(error=>{})
}
mainShowLogin=()=>{
this.setState({
visible:true
})
}
render(){
const { users , visible } = this.state;
const { headData } = this.props;
return(
<div style={{height:"100%",overflow:"auto",position:"relative"}}>
<LoginModal visible={visible} onCancel={()=>{this.setState({visible:false})}} successFunc={this.successFunc}/>
<Header {...this.props} {...this.state} users={users} headData={headData} mainShowLogin={this.mainShowLogin} quitFunc={this.quitFunc}/>
<div className="homebody">
<Sub {...this.props} {...this.state} users={users} mainShowLogin={this.mainShowLogin}/>
<Footer />
</div>
</div>
)
}
}
}
export default HomeHoc;

18
src/mulan/HOC/Index.jsx Normal file
View File

@ -0,0 +1,18 @@
import React, { Component } from 'react';
import Header from './Header';
import Footer from './Footer';
import './Index.scss';
export function HOC(Sub){
return class II extends Component {
render(){
return(
<div style={{height:"100%",overflow:"auto"}}>
<Header {...this.props} {...this.state}/>
<div className="homebody"><Sub {...this.props} {...this.state}/></div>
<Footer {...this.props} {...this.state}/>
</div>
)
}
}
}

113
src/mulan/HOC/Index.scss Normal file
View File

@ -0,0 +1,113 @@
.headsnav{
position: fixed;
width: 100%;
left: 0px;
top:0px;
height: 60px;
line-height: 60px;
background-color: #fff;
box-shadow: 0px 0px 10px rgba(0,0,0,0.1);
z-index: 100;
}
.homeHeader{
position: fixed;
width: 100%;
left: 0px;
top:0px;
height: 60px;
line-height: 60px;
z-index: 100;
box-shadow: 0px 0px 3px rgba(0,0,0,0.1);
background: #fff;
ul{
li{
a{
color: #333;
}
}
}
}
.headDiv{
margin: 0px auto;
padding:0px 60px;
display: flex;
justify-content: space-between;
.headerNav{
display: flex;
height: 100%;
margin-bottom: 0px;
li{
padding-right:30px;
height: 100%;
font-size: 16px;
}
}
.headUserInfo{
a{
color: #666666;
}
}
}
.homebody{
padding-top: 60px;
padding-bottom: 136px;
min-height: 100%;
position: relative;
}
.footsnav{
position: absolute;
width: 100%;
bottom: 0px;
left: 0px;
text-align: center;
padding-bottom:10px;
background-color: #fff;
color: #999;
}
.homefooterbase{
background: #DEDEDE;
line-height: 38px;
font-size: 12px;
color: #666;
text-align: center;
padding:30px 0px;
position: absolute;
width: 100%;
left: 0px;
bottom: 0px;
.homeFooterUl{
display: flex;
align-items: center;
justify-content: center;
margin:0px;
li{
padding:0px 40px;
a{
color: #666666;
display: block;
}
}
}
}
.menuItems{
.ant-dropdown-menu-item{
text-align: center;
border-bottom: 1px solid #eee;
color: #333;
}
.ant-dropdown-menu-item:last-child{
border-bottom: none;
}
.ant-dropdown-menu-item:first-child:hover{
color: #333;
cursor: default;
}
.ant-dropdown-menu-item:hover{
background-color: #fff;
color: #aac5fd;
}
}

63
src/mulan/HOC/Nav.jsx Normal file
View File

@ -0,0 +1,63 @@
import React, { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import { getUrlHead } from '../Data/getUrl';
import { Dropdown , Menu } from 'antd';
import logo from '../images/logo.png';
// import LoginModal from '../Components/Login/LoginModal';
function Nav({ users, headData , mainShowLogin , quitFunc , match }){
// const [ visible ,setVisible ] = useState(false);
const [ navs , setNavs ] = useState(undefined);
useEffect(()=>{
if(headData){
setNavs(headData.navbar);
}
},[headData])
// function success(data) {
// setVisible(false);
// successFunc(data);
// }
const menus =(
<Menu className="menuItems">
<Menu.Item>{users && users.real_name}</Menu.Item>
<Menu.Item><a href="/admins">后台管理</a></Menu.Item>
<Menu.Item onClick={quitFunc}>退出</Menu.Item>
</Menu>
)
return(
<div className="headDiv">
{/* <LoginModal visible={visible} onCancel={()=>setVisible(false)} successFunc={success}/> */}
<ul className="headerNav">
<li><img src={logo} alt="" width="110px" style={{marginRight:"100px"}}/></li>
{
navs && navs.length>0 && navs.map((i,k)=>{
return(
<li>
{
i.link && i.link.indexOf("http") > -1 ?
<a href={i.link} target="_blank">{i.name}</a>
:
<Link to={i.link} className={i.link===(match && match.url) ? "color-blue":""}>{i.name}</Link>
}
</li>
)
})
}
</ul>
<div className="headUserInfo">
{
users && users.login ?
<Dropdown overlay={menus} placement={"bottomRight"}>
<img src={users && getUrlHead(users.image_url)} alt="" width="48px" height="48px" style={{borderRadius:"50%"}}/>
</Dropdown>
:
<a onClick={mainShowLogin}>登录/注册</a>
}
</div>
</div>
)
}
export default Nav;

341
src/mulan/Index.jsx Normal file
View File

@ -0,0 +1,341 @@
import React, { useEffect, useState , useRef } from 'react';
import { HomeHoc } from './HOC/HomeHoc';
import { Link } from 'react-router-dom';
import './Index.scss';
import main from './images/320.jpg';
import icon1 from './images/icon1.png';
import icon2 from './images/icon2.png';
import active1 from './images/active1.jpg';
import active2 from './images/active2.png';
import DefaultImg from './images/default.png';
import { Carousel , Spin } from 'antd';
import { getUrlHead } from './Data/getUrl';
import axios from 'axios';
function Index(props){
const Carousels = useRef(null);
const [ about ,setAbout ] =useState(undefined);
const [ covers ,setCovers ] =useState(undefined);
const [ recommand ,setRecommand ] =useState(undefined);
const [ news ,setNews ] =useState(undefined);
const [ activity ,setActivity ] =useState(undefined);
const [ newActivity ,setNewActivity ] =useState(undefined);
const [ cooperatersMenus ,setCooperatersMenus ] =useState(undefined);
const [ cooperatersMenusValue ,setCooperatersMenusValue ] =useState(undefined);
const [ cooperaters ,setCooperaters ] =useState(undefined);
const [ lectures ,setLectures ] =useState(undefined);
const [ isSpin ,setIsSpin ] =useState(false);
useEffect(()=>{
getRecommand();
getAbout();
getNews();
getActivity();
getCooperatersMenu();
getLectures();
getNewActivity();
},[])
//
function getLectures(){
const url = `/lectures.json`;
axios.get(url).then(result=>{
if(result && result.data){
setLectures(result.data);
}
}).catch(error=>{})
}
//
function getAbout(){
const url = `/helps/about.json`;
axios.get(url).then(result=>{
if(result && result.data){
setAbout(result.data.data);
setCovers(result.data.data.covers);
}
}).catch(error=>{})
}
//
function getRecommand(){
const url = `/projects/recommend.json`;
axios.get(url).then(result=>{
if(result && result.data){
setRecommand(result.data.data);
}
}).catch(error=>{})
}
//
function getNews(){
const url = `/topics.json`;
axios.get(url,{
params:{topic_type:"activity",page:1,limit:4,order_by:"created_at",order_direction:"desc"}
}).then(result=>{
if(result && result.data){
setNews(result.data.data);
}
}).catch(error=>{})
}
//
function getNewActivity(){
const url = `/topics.json`;
axios.get(url,{
params:{topic_type:"moment",page:1,limit:4,order_by:"created_at",order_direction:"desc"}
}).then(result=>{
if(result && result.data){
setNewActivity(result.data.data);
}
}).catch(error=>{})
}
//
function getActivity(){
const url = `/topics.json`;
axios.get(url,{
params:{topic_type:"news",page:1,limit:6,order_by:"created_at",order_direction:"desc"}
}).then(result=>{
if(result && result.data){
setActivity(result.data.data);
}
}).catch(error=>{})
}
// -menu
function getCooperatersMenu(){
const url = `/cooperater_categories.json`;
axios.get(url).then(result=>{
if(result && result.data){
let data = result.data.data;
if(data && data.length>0){
setCooperatersMenus(data);
setCooperatersMenusValue(data[0].id);
}
}
}).catch(error=>{})
}
useEffect(()=>{
if(cooperatersMenusValue){
setIsSpin(true);
getCooperaters(cooperatersMenusValue);
}
},[cooperatersMenusValue])
//
function getCooperaters(cateId){
const url = `/cooperaters.json`;
axios.get(url,{
params:{
cooperater_category_id:cateId
}
}).then(result=>{
if(result && result.data){
setCooperaters(result.data.data);
setIsSpin(false);
}
}).catch(error=>{})
}
return(
<div>
<div className="homebase">
<Carousel
autoplay
pauseOnDotsHover
>
{
covers && covers.length>0?
covers.map((i,k)=>{
return(
<a href={i.path}><img src={getUrlHead(i.image_url) } alt="" /></a>
)
})
:<img src={main} alt="" />
}
</Carousel>
</div>
{
recommand && recommand.length > 0 &&
<div className="homeproject">
<div class="tit">开源软件推荐</div>
<ul>
{
recommand.map((item,key)=>{
return(
<li>
<a href={item.url}>
<div className="p_head">
<p className="p_name task-hide-2">{item.full_name}</p>
<img src={item.author && item.author.image_url ? getUrlHead(item.author.image_url) : DefaultImg } alt="" style={{maxHeight:"50px"}}/>
</div>
</a>
<p className="p_author">{item.author && item.author.name}</p>
<p className="p_desc task-hide-2">{item.description}</p>
</li>
)
})
}
</ul>
<Link to={`/projects`} className="color-blue">查看更多<i className="iconfont icon-youjiantou ml8 font-12 color-blue"></i></Link>
</div>
}
{
about &&
<div className="homeFirst">
<div className="firstPanel">
<div className="firstPanelBox">
<div class="tit" style={{marginBottom:"30px"}}>{about.name}</div>
<div className="desc">
{about.content}<Link to={`/about`} className="color-blue">查看更多</Link>
</div>
<ul className="aboutul">
<li>
<img src={icon1} alt="" width="186px"/>
<font>开源项目总数</font>
<span>{about.total_projects_count}</span>
</li>
<li>
<img src={icon2} alt="" width="186px"/>
<font>科技项目开源成果</font>
<span>{about.science_projects_count}</span>
</li>
</ul>
</div>
</div>
</div>
}
<div className="sourceAbout">
<div class="tit">开源活动</div>
{
news && news.length>0 ?
<div className="sourceactive">
{
news.map((item,key)=>{
return(
key < 2 &&<a href={item.url} style={{backgroundImage:`url(${getUrlHead(item.cover)})`}}></a>
)
})
}
</div>
:
<div className="sourceactive">
<li style={{backgroundImage:`url(${active1})`}}></li>
<li style={{backgroundImage:`url(${active2})`}}></li>
</div>
}
{/* <Link to={`/projects`} className="color-blue">查看更多<i className="iconfont icon-youjiantou ml8 font-12 color-blue"></i></Link> */}
</div>
{
newActivity && newActivity.length > 0 &&
<div>
<div class="tit">最新动态</div>
<ul className="newActivity">
{
newActivity.map((i,k)=>{
return(
<li>
<a href={i.url}>
<img src={getUrlHead(i.cover)} alt="" />
<div className="infobox">
<span className="time">{i.created_at}</span>
<p className="name">{i.title}</p>
<div className="desc">{i.content}</div>
<a href={i.url} className="color-blue">了解详情<i className="iconfont icon-arrowRight ml5 font-14 color-blue"></i></a>
</div>
</a>
</li>
)
})
}
</ul>
</div>
}
<div className={"homenews"}>
<div class="tit">开源资讯</div>
<div>
{
activity && activity.length> 0 &&
<ul className="newsul">
{
activity.map((item,key)=>{
return(
<li>
<div className="imgBox"><img src={getUrlHead(item.cover)} alt="" width="145px" height="96px"/></div>
<div className="news-info">
<p className="news-title">
<a href={item.url} className="task-hide" style={{display:"block"}} target="_blank">{item.title}</a>
</p>
<div className="news-desc task-hide-2">{item.content}</div>
<div className="news-time">{item.created_at}</div>
</div>
</li>
)
})
}
</ul>
}
</div>
</div>
<div>
<div class="tit">专家寄语</div>
<div className="professional">
<Carousel
ref={Carousels}
autoplay
pauseOnDotsHover
arrows={lectures && lectures.total_count && lectures.total_count > 1}
speed={1500}
prevArrow={<button type='button' className='slick-prev slick-arrow'><i className='iconfont icon-jiantouloukong-zuo'></i></button>}
nextArrow={<button type='button' className='slick-next slick-arrow'><i className='iconfont icon-jiantouloukong-you'></i></button>}
>
{
lectures && lectures.data && lectures.data.length>0 &&
lectures.data.map((i,k)=>{
return(
<div className="professionalItem">
<img src={getUrlHead(i.avatar_url)} alt="" className="professionalImg"/>
<span className="name">{i.expert_name}</span>
<span className="school">{i.expert_title}</span>
<p className="content">{i.content}</p>
</div>
)
})
}
</Carousel>
</div>
</div>
<div>
<div class="tit">合作伙伴</div>
{
cooperatersMenus && cooperatersMenusValue &&
<ul className="cooperatersMenus">
{
cooperatersMenus.map((i,k)=>{
return(
<li className={cooperatersMenusValue === i.id ?"active":""} onClick={()=>setCooperatersMenusValue(i.id)}>{i.name}</li>
)
})
}
</ul>
}
<Spin spinning={isSpin}>
{
cooperaters && cooperaters.length > 0 &&
<ul className="homeFooterUl homeFooterParter">
{
cooperaters.map((i,k)=>{
return(
<li><a href={i.website_url} target="_blank"><img src={getUrlHead(i.logo_url) } alt="" width="200px"/></a></li>
)
})
}
</ul>
}
</Spin>
</div>
</div>
)
}
export default HomeHoc(Index);

404
src/mulan/Index.scss Normal file
View File

@ -0,0 +1,404 @@
.homeFirst{
background:url(./images/bg2.jpg)center center no-repeat;
min-height: 480px;
position: relative;
margin-bottom: 90px;
.firstPanel{
position: absolute;
top:60px;
width: 100%;
.firstPanelBox{
width: 1300px;
margin:0px auto;
background-color: #fff;
box-shadow: 0px 0px 10px rgba(0,0,0,0.1);
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
min-height: 498px;
border-radius: 4px;
padding-top:50px;
background:url(./images/1_1.png) no-repeat;
.desc{
margin:0px auto 50px;
line-height: 22px;
font-size: 16px;
color: #666;
width: 1030px;
text-indent: 2em;
text-align: justify;
}
.aboutul{
display: flex;
justify-content: space-around;
width: 100%;
align-items: center;
li{
display: flex;
flex-direction: column;
align-items: center;
span{
font-size: 28px;
line-height: 42px;
font-weight: bold;
color: #1890FF;
}
font{
color: #666;
}
}
}
}
}
}
.professional{
width: 1300px;
height: 434px;
margin: 0px auto;
background-image: url(./images/professional.png);
background-repeat: no-repeat;
margin-bottom: 60px;
.ant-carousel,.slick-slider,.slick-list,.slick-track,.slick-slide>div{
height: 100%;
}
.slick-slider{
&:hover .slick-arrow{
display: block!important;
}
.slick-arrow{
height: 46px;
width: 46px;
z-index: 10;
display: none!important;
&:hover i{
color: #1890FF;
}
}
.slick-arrow > i{
color: rgb(236, 236, 236);
font-size: 46px!important;
height: 46px;
width: 46px;
line-height: 46px;
}
.slick-prev{
left: 70px;
}
.slick-next{
right: 70px;
}
}
.professionalItem{
display: flex!important;
align-items: center;
justify-content: center;
flex-direction: column;
height: 100%;
.professionalImg{
width: 120px;
height: 120px;
border-radius: 25px;
margin-bottom: 30px;
}
.name{
font-size: 16px;
color: #333333;
line-height: 40px;
display: block;
}
.school{
color: #888;
display: block;
line-height: 30px;
margin-bottom: 20px;
}
.content{
max-width: 1000px;
color: #333;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
}
}
.homeFooterParter{
padding:40px 0px;
width: 1200px;
margin:0px auto!important;
}
.newActivity{
width: 1240px;
margin:40px auto;
display: flex;
align-items: center;
justify-content: center;
li{
width: 280px;
margin:0px 30px;
background: #FFFFFF;
box-shadow: 0px 0px 8px 0px #F1F1F1;
&:hover{
box-shadow: 0px 0px 40px 0px rgba(24, 144, 255, 0.3);
}
& >a img{
max-height: 150px;
width: 100%;
}
.infobox{
padding:20px;
.time{
color:#666666;
height: 18px;
line-height: 18px;
display: block;
margin-bottom: 15px;
}
.name{
color: #333;
font-size: 16px;
height: 20px;
line-height: 20px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.desc{
line-height: 20px;
color: #666;
height: 100px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 5;
margin-bottom: 15px;
}
}
}
}
.homeFooterUl{
display: flex;
align-items: center;
justify-content: center;
margin:0px;
flex-wrap: wrap;
li{
padding:0px 40px 20px 40px;
a{
color: #666666;
display: block;
}
}
}
.homeFooter{
background: #fff;
.homefooterbase{
background: #DEDEDE;
line-height: 38px;
font-size: 12px;
color: #666;
text-align: center;
padding:30px 0px;
}
}
.homebase{
position: relative;
min-height: 480px;
background-size: 100% 100%;
background-repeat: no-repeat;
background-position: center;
color:#fff;
font-size: 60px;
font-weight: bold;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.ant-carousel{
width: 100%;
a{
display: block;
height: 100%;
width: 100%;
img{
height: 480px;
width: 100%;
}
}
}
p{
margin-bottom:20px;
text-align: center;
}
.sub{
font-size: 28px;
margin:0px;
span{
margin:0px 15px;
}
}
}
.homeproject{
padding:60px 0px 100px 0px;
text-align: center;
ul{
display: flex;
flex-wrap: wrap;
width: 1200px;
margin:0px auto;
li{
width: 22%;
margin:0px 1.5%;
border-radius: 6px;
box-shadow: 0px 0px 4px rgba(0,0,0,0.1);
margin-bottom: 40px;
text-align: left;
.p_head{
padding:25px 20px;
display: flex;
align-items: flex-start;
background-image: url('./images/cards.png');
border-radius: 6px 6px 0px 0px;
justify-content: space-between;
}
.p_author{
font-size: 16px;
color: #666;
padding:0px 15px;
margin: 0px;
}
.p_desc{
line-height: 18px;
color: #666;
padding:0px 15px;
margin: 10px 0px 18px 0px;
}
.p_name{
font-size: 18px;
margin-right: 10px;
margin-bottom: 0px;
color: #fff;
line-height: 20px;
margin-top: 5px;
}
}
}
}
.sourceAbout{
padding:60px 0px 100px 0px;
text-align: center;
.sourceactive{
display: flex;
align-items: center;
justify-content: center;
margin:40px 0px;
a{
width: 600px;
height: 300px;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
border-radius: 4px;
margin:0px 40px;
}
}
}
.cooperatersMenus{
display: flex;
align-items: center;
justify-content: center;
li{
padding:0px 50px;
font-size: 18px;
color: #333333;
position: relative;
cursor: pointer;
&.active{
color: #1890FF;
}
&.active::after{
position: absolute;
content: "";
background-color: #1890FF;
width: 50px;
height: 2px;
left: 50%;
margin-left: -25px;
bottom: -5px;
}
}
}
.tit {
font-size: 22px;
color: #444;
font-weight: bold;
margin-bottom: 60px;
text-align: center;
em{
display: block;
width: 50px;
height: 1px;
background: #ccc;
margin: 18px auto 0;
}
}
.homenews{
padding:60px 0px;
.newsul{
width: 1200px;
margin:0px auto;
display: flex;
flex-wrap: wrap;
li{
display:flex;
width: 48%;
margin-right: 2%;
margin-bottom: 30px;
&:nth-child(2n){
margin-right: 0px;
flex-flow: row-reverse;
img{
margin-left: 10px;
}
}
&:nth-child(2n+1){
img{
margin-right: 10px;
}
}
.imgBox{
width: 145px;
height: 90px;
display: flex;
align-items: center;
justify-content: center;
img{
max-width: 100%;
max-height: 100%;
}
}
.news-title{
font-size: 16px;
margin-bottom:5px;
font-weight: bold;
}
.news-time{
font-size: 12px;
color: #999;
}
.news-desc{
line-height: 20px;
}
.news-info{
flex:1;
width: 0;
}
}
}
}

View File

@ -0,0 +1,148 @@
import React, { useEffect, useState } from 'react';
import { HomeHoc } from '../HOC/HomeHoc';
import { Box , Long , Short , Gap } from '../../forge/Component/layout';
import { AlignCenter ,FlexAJ } from '../../forge/Component/layout';
import './Index.scss';
import DefaultImg from '../images/default.png';
import { getUrlHead } from '../Data/getUrl';
import axios from 'axios';
import { Skeleton , Pagination } from 'antd';
import Ranking from './Ranking';
const data =[
{
index:1,
name:"Trustie社区 / Mini-Kernel",
count:123223123
},
{
index:2,
name:"Trustie社区 / Mini-Kernel",
count:123223122
},
{
index:3,
name:"Trustie社区 / Mini-Kernel",
count:123223121
}
]
const limit = 12;
function Index(props){
const [ list , setList ] = useState([]);
const [ date ,setDate ] = useState("week");
const [ page ,setPage ] = useState(1);
const [ total ,setTotal ] = useState(0);
const [ rankList ,setRankList ] = useState([]);
useEffect(()=>{
getData();
},[page])
useEffect(()=>{
getRank();
},[date])
//
function getData() {
const url = `/projects/science.josn`;
axios.get(url,{
params:{page,limit}
}).then(result=>{
if(result){
setList(result.data.data);
setTotal(result.data.total_count);
}
}).catch(error=>{})
}
//
function getRank(){
const url = `/projects/liveness.json`;
axios.get(url,{
params:{time_type:date,type:"commits"}
}).then(result=>{
if(result){
setRankList(result.data.data);
}
}).catch(error=>{})
}
return(
<Box className="panels">
<Long>
<div className="projectsList">
<p className="font-16">基础类开源软件</p>
{
list && list.length > 0 ?
<div className="lists">
{
list.map((item,key)=>{
return(
<a href={item.url} >
<li>
<div className="listinfo">
<p className="listName">{item.full_name}</p>
<div className="listNum">
{/* <span>代码行数20.43K</span> */}
<span>仓库大小{item.size ||"0kB"}</span>
<span>commits数{item.commits_count}</span>
</div>
</div>
<AlignCenter className="listMain">
<p className="listInfoDesc task-hide-2">{item.description}</p>
<img src={(item.author && getUrlHead(item.author.image_url)) || DefaultImg} alt="" width="48px" className="ml10"/>
</AlignCenter>
{
item.project_tags &&
<div className="listTag">
{
item.project_tags.map((i,k)=>{
return(
<span>{i}</span>
)
})
}
</div>
}
<div className="projectNum">
<AlignCenter><i className="iconfont icon-liulanyan font-12 mr2"></i>{item.visits}</AlignCenter>
<AlignCenter><i className="iconfont icon-kongxing font-12 mr2"></i>{item.praises_count}</AlignCenter>
<AlignCenter><i className="iconfont icon-fork font-12 mr2"></i>{item.forked_count}</AlignCenter>
</div>
</li>
</a>
)
})
}
</div>
:""
}
{
list && list.length === 0 && <Skeleton />
}
{
total > limit &&
<div className="edu-txt-center">
<Pagination showQuickJumper current={page} total={total} pageSize={limit} onChange={(p)=>{setPage(p)}}/>
</div>
}
</div>
</Long>
<Short>
<Gap>
<FlexAJ>
<p className="font-16">活跃度排名</p>
<ul>
<a className={date === "week"?"color-blue":"color-grey-9"} onClick={()=>setDate("week")}></a>
<a className={date === "month"?"ml15 color-blue":"ml15 color-grey-9"} onClick={()=>setDate("month")}></a>
<a className={date === "year"?"ml15 color-blue":"ml15 color-grey-9"} onClick={()=>setDate("year")}></a>
</ul>
</FlexAJ>
<Ranking data={rankList}/>
</Gap>
</Short>
</Box>
)
}
export default HomeHoc(Index);

View File

@ -0,0 +1,153 @@
.panels{
width: 1200px;
margin:40px auto 60px;
.projectsList{
.lists{
min-height:500px;
display: flex;
flex-wrap: wrap;
align-items: flex-start;;
& >a{
margin-right: 2%;
border: 1px solid #eee;
width: 32%;
margin-bottom: 25px;
border-radius: 4px;
min-height: 200px;
box-shadow: 0 4px 8px 0 rgba(95,101,105,0.1);
&:hover{
transform: translateY(-2px);
box-shadow: 0 12px 20px 0 rgba(95,101,105,0.15);
}
&:nth-child(3n){
margin-right: 0px;
}
.listinfo{
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding:0px 20px;
height: 90px;
background-image: url('../images/cards.png');
color: #fff;
border-radius: 4px 4px 0px 0px;
.listName{
font-size: 18px;
margin:0px;
height: 22px;
line-height: 22px;
margin-bottom: 10px;
word-break: break-all;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
width:100%
}
.listNum{
display: flex;
flex-wrap: wrap;
width: 100%;
span{
display:block;
width: 50%;
font-size: 12px;
color: #fff;
height: 18px;
line-height: 18px;
&:nth-child(2n){
text-align: right;
}
}
}
}
.listMain{
display: flex;
justify-content: space-between;
padding:10px 20px 0px 20px;
align-items: center;
.listInfoDesc{
line-height: 20px;
margin:0px;
color:#666666;
max-width: 182px;
word-break: break-all;
}
}
.listTag{
padding:10px 0px;
margin:0px 20px;
min-height: 49px;
border-bottom: 1px solid #F1F1F1;
span{
display: inline-block;
margin-right: 5px;
background-color: #B9DDFF;
color: #1890FF;
padding:0px 5px;
border-radius: 4px;
height: 20px;
line-height: 20px;
font-size: 12px;
}
}
.projectNum{
display: flex;
justify-content: space-between;
color:#999999;
font-size: 12px;
padding:10px 20px;
i{
margin-right: 3px;
color: #666666!important;
}
}
}
}
}
}
.arrayList{
padding:20px 20px 20px 10px;
box-shadow: 0px 0px 8px 0px #F1F1F1;
min-height: 400px;
ul {
margin:0px;
&>li{
display: flex;
align-items: center;
height: 36px;
line-height: 36px;
&:first-child .index{
color: #E53333;
}
&:nth-child(2) .index{
color: #FF8C29;
}
&:nth-child(3) .index{
color: #F7B500;
}
.index{
display: block;
width: 20px;
text-align: center;
color: #999999;
}
.name{
margin-left: 10px;
flex:1;
text-align: left;
color: #333;
margin-bottom: 0px;
max-width: 170px;
&:hover{
color: #1890FF;
}
}
.num{
text-align: right;
margin-left: 15px;
color: #999999;
}
}
}
}

View File

@ -0,0 +1,33 @@
import React, { useEffect, useState } from 'react';
import {Skeleton} from 'antd';
function Ranking({data}){
const [list ,setList ]= useState([]);
useEffect(()=>{
setList(data);
},[data])
return(
<div className="arrayList">
{
list && list.length >0?
<ul>
{
list.map((i,k)=>{
return(
<li>
<span className="index">{i.no}</span>
<a href={i.project_url} className="name task-hide">{i.project_full_name}</a>
<span className="num">{i.commits}</span>
</li>
)
})
}
</ul>
:<Skeleton />
}
</div>
)
}
export default Ranking;

View File

@ -0,0 +1,143 @@
import React, { useEffect, useState } from 'react';
import Axios from 'axios';
import './Index.scss';
import ReplayBox from './ReplyBox';
import List from './List';
function Index({id,users,mainShowLogin}){
const [ data , setData ] = useState(undefined);
const [ page , setPage ] = useState(1);
const [ limit , setLimit ] = useState(6);
const [ total , setTotal ] = useState(0);
const [ yu , setYu ] = useState(0);
useEffect(()=>{
if(id){
getAllComments(1,page*limit);
}
},[id,page])
function getAllComments(p,l){
const url = `/comments.json`;
Axios.get(url,{
params:{
commentable_type:"Topic",
commentable_id:id,
page:p,limit:l
}
}).then(result=>{
if(result && result.data){
setData(result.data.data);
setTotal(result.data.total_count);
let size = parseInt(result.data.total_count/limit);
if(result.data.total_count%limit>0){
size++;
}
setYu(size);
}
}).catch(error=>{})
}
// parent_id
function submit(v,pareid){
const params={
comment:{
commentable_type:"Topic",
commentable_id:id,
content:v,
parent_id:pareid
}
}
const url = `/comments.json`;
Axios.post(url,params).then(result=>{
if(result && result.data){
getAllComments(1,page*limit);
}
}).catch(error=>{})
}
//
function commentFunc(flag,index){
var lists = data.concat();
lists.map(i=>i.flag = false);
lists[index].flag = !flag ? true : false;
lists.splice();
setData(lists);
}
//
function pariseFunc(id,is_like,count,index){
let url = '';
const params={
likable_type:"Comment",likable_id:id
}
if(is_like){
url = `/likes/unlike.json`;
}else{
url = `/likes/like.json`;
}
Axios.get(url,{
params
}).then(result=>{
if(result && result.data && result.data.status === 0){
var lists = data.concat();
lists.map(i=>i.flag = false);
lists[index].likers_count = is_like ? count-1 : count+1;
lists[index].is_like = !is_like;
lists.splice();
setData(lists);
}
}).catch(error=>{})
}
function deleteFunc(id,index,subIndex){
const url = `/comments/${id}.json`;
Axios.delete(url).then(result=>{
if(result && result.data && result.data.status === 0){
var lists = data.concat();
if(subIndex>=0){
let chid = lists[index].children
if(chid && chid.length>0){
chid[subIndex].is_delete = true;
lists[index].children_comments_count=lists[index].children_comments_count-1;
if(!chid.find(i=>i.is_delete === undefined)){
lists[index].children = undefined;
}
}
}else{
lists[index].is_delete = true;
setTotal(total-1);
}
lists.splice();
setData(lists);
// getAllComments(1,page*limit);
}
}).catch(error=>{})
}
return(
<div className="replay">
<h3>全部回复{total && total > 0 ? `(${total})`:""}</h3>
<ReplayBox users={users} submit={submit} mainShowLogin={mainShowLogin}/>
{
total && total > 0 ?
<List
users={users}
list={data}
commentFunc={commentFunc}
pariseFunc={pariseFunc}
deleteFunc={deleteFunc}
submit={submit}
mainShowLogin={mainShowLogin}
/>
:""
}
{
total > limit && (page<yu) &&
<p className="edu-txt-center color-grey-8"><a onClick={()=>setPage(page+1)}>查看更多评论</a></p>
}
</div>
)
}
export default Index;

View File

@ -0,0 +1,64 @@
.replay{
padding:30px;
background-color: #fff;
margin-top: 20px;
.replybox{
margin-top: 15px;
}
.replayImg{
width: 50px;
height: 50px;
border-radius: 50%;
border: 1px solid #DDDDDD;
margin-right: 15px;
}
.replyContent{
flex:1;
}
.replylistItem{
margin-top: 30px;
&>div{
padding:38px 0px;
border-top: 1px solid #eee;
}
.replyInfo{
flex: 1;
.replyInfoMain{
height: 18px;
line-height: 18px;
margin-bottom: 8px;
&>div{
&>span{
margin-right: 15px;
color: #333;
}
&>a{
margin-left: 15px;
color:#BBBBBB;
i{
margin-right: 4px;
}
}
}
}
}
}
.subReply{
padding:5px 30px 20px;
background-color: #fafafa;
position: relative;
margin-top: 20px;
&::before{
position: absolute;
top:-10px;
content: "";
width: 0;
height: 0;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-bottom: 10px solid #fafafa;
left: 30px;
}
}
}

View File

@ -0,0 +1,56 @@
import React from 'react';
import { AlignTop , FlexAJ , AlignCenter } from '../../forge/Component/layout';
import { getUrlHead } from '../Data/getUrl';
import RenderHtml from '../../components/render-html';
import ReplayBox from './ReplyBox';
import SubList from './SubList';
import { Popconfirm } from 'antd';
function List({list , deleteFunc , commentFunc , pariseFunc , users , submit , mainShowLogin}){
return(
<div className="replylistItem">
{
list && list.map((i,k)=>{
return(
!i.is_delete && <AlignTop>
<img src={getUrlHead(i.commenter && i.commenter.image_url)} alt='' className="replayImg"/>
<div className="replyInfo">
<FlexAJ className="replyInfoMain">
<AlignCenter>
<span>{i.commenter && i.commenter.name}</span>
<span style={{color:"#888"}}>{i.created_at}</span>
</AlignCenter>
<AlignCenter>
{ users && users.admin &&
<Popconfirm title="是否确定删除此条评论?" okText="确定" cancelText="取消" onConfirm={()=>{deleteFunc(i.id,k)}}>
<a><i className="iconfont icon-lajitong1 font-14"></i></a>
</Popconfirm>
}
<a onClick={()=>{(users && users.login) ? commentFunc(i.flag,k) : mainShowLogin()}}>
<i className="iconfont icon-huifu1 font-16"></i>
{i.children_comments_count && i.children_comments_count > 0 && <span>{i.children_comments_count}</span>}
</a>
<a onClick={()=>{(users && users.login) ? pariseFunc(i.id,i.is_like,i.likers_count,k) : mainShowLogin()}}>
<i className={i.is_like===true ? "iconfont icon-dianzan":"iconfont icon-dianzan-xian"}></i>
{ i.likers_count && i.likers_count > 0 && <span>{i.likers_count}</span>}
</a>
</AlignCenter>
</FlexAJ>
<div>
<RenderHtml className="break_word_comments imageLayerParent" value={i.content}/>
{ i.children && i.children.length > 0 && <SubList children={i.children} users={users} deleteFunc={deleteFunc} prek={k}/> }
{
i.flag && <ReplayBox k={k} users={users} submit={submit} pareid={i.id} commentFunc={commentFunc}/>
}
</div>
</div>
</AlignTop>
)
})
}
</div>
)
}
export default List;

View File

@ -0,0 +1,64 @@
import React, { useEffect, useState } from 'react';
import { AlignTop } from '../../forge/Component/layout';
import { getUrlHead } from '../Data/getUrl';
import MDEditor from "../../modules/tpm/challengesnew/tpm-md-editor";
import { Button, Input } from 'antd';
function ReplayBox({users,submit,pareid,k,commentFunc , mainShowLogin}){
const [ value , setValue ] = useState("");
const [ valueFlag , setValueFlag ] = useState(false);
const [ inputFlag , setInputFlag ] = useState(false);
useEffect(()=>{
if((users && !users.login) || !users){
setValue("");
setValueFlag(false);
setInputFlag(false);
}
},[users])
useEffect(()=>{
if(k!==undefined){
setInputFlag(true);
}
},[k])
function submitPre(){
if(value){
setValueFlag(false);
submit && submit(value,pareid);
setValue("");
}else{
setValueFlag(true);
}
}
return(
<AlignTop className="replybox">
{ users && users.image_url && <img src={getUrlHead(users.image_url)} alt='' className="replayImg"/>}
{
!inputFlag ?
<div className="replyContent" onClick={()=>{users && users.login ?setInputFlag(true) : mainShowLogin()}}>
<Input placeholder="添加评论" disabled style={{marginTop:"9px",cursor:"default"}}/>
</div>
:
<div className="replyContent">
<MDEditor
placeholder={"请输入评论内容"}
height={240}
mdID={`replyContent_${users && users.login}_${k}`}
initValue={value}
onChange={(e)=>setValue(e)}
></MDEditor>
<div className="clearfix" style={{textAlign:"right"}}>
{ valueFlag && <span className="color-red mr20">评论内容不能为空</span> }
<Button type="default" className="mr20" onClick={()=>{commentFunc ? commentFunc(true,k) : setInputFlag(false)}}>取消</Button>
<Button type="primary" onClick={submitPre}>提交</Button>
</div>
</div>
}
</AlignTop>
)
}
export default ReplayBox;

View File

@ -0,0 +1,32 @@
import React from 'react';
import { FlexAJ } from '../../forge/Component/layout';
import RenderHtml from '../../components/render-html';
import { Popconfirm } from 'antd';
function SubList({ children , deleteFunc , users ,prek}){
return(
<div className="subReply">
{
children.map((i,k)=>{
return(
!i.is_delete &&<div className="mt10">
<FlexAJ>
<span>
<span className="font-16 color-grey-3">{i.commenter && i.commenter.name}</span>
<span className="ml10" style={{color:"#888"}}>{i.created_at}</span>
</span>
{ users && users.admin &&
<Popconfirm title="是否确定删除此条评论?" okText="确定" cancelText="取消" onConfirm={()=>{deleteFunc(i.id,prek,k)}}>
<a><i className="iconfont icon-lajitong1 font-14 color-grey-9"></i></a>
</Popconfirm>
}
</FlexAJ>
<RenderHtml className="break_word_comments imageLayerParent" value={i.content}/>
</div>
)
})
}
</div>
)
}
export default SubList;

BIN
src/mulan/images/1_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

BIN
src/mulan/images/1_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

BIN
src/mulan/images/320.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 616 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 824 KiB

BIN
src/mulan/images/back1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

BIN
src/mulan/images/bg2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
src/mulan/images/bg2_1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

BIN
src/mulan/images/bg3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

BIN
src/mulan/images/cards.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
src/mulan/images/coscl.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
src/mulan/images/deno.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
src/mulan/images/go7c.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
src/mulan/images/h.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

BIN
src/mulan/images/icon1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
src/mulan/images/icon2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
src/mulan/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

BIN
src/mulan/images/perl.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

34
src/mulan/license/FAQ.jsx Normal file
View File

@ -0,0 +1,34 @@
import React, { useEffect, useState } from 'react';
import Title from '../../forge/Component/Title';
import RenderHtml from '../../components/render-html';
import { Skeleton } from 'antd';
import Axios from 'axios';
function FAQ(props){
const [ value , setValue ] = useState(undefined);
useEffect(()=>{
const url = `/helps/faq.json`;
Axios.get(url).then(result=>{
if(result){
setValue(result.data.data.content);
}
}).catch(error=>{})
},[])
return(
<div>
<Title>常见问题</Title>
<div class="contents">
<div className="pre">
{
value ?
<RenderHtml className="break_word_comments imageLayerParent" value={value} url={props.history.location}/>
:
<Skeleton />
}
</div>
</div>
</div>
)
}
export default FAQ;

View File

@ -0,0 +1,99 @@
import React, { useEffect, useState } from 'react';
import { HomeHoc } from '../HOC/HomeHoc';
import {Route, Switch , Link } from 'react-router-dom';
import Loadable from 'react-loadable';
import Loading from '../../Loading';
import { Box , Long , Short , Gap } from '../../forge/Component/layout';
import './Index.scss';
import { Menu } from 'antd';
const Preface = Loadable({
loader: () => import('./Preface'),
loading: Loading,
})
const PSL = Loadable({
loader: () => import('./PSL'),
loading: Loading,
})
const Public = Loadable({
loader: () => import('./Public'),
loading: Loading,
})
const FAQ = Loadable({
loader: () => import('./FAQ'),
loading: Loading,
})
function Index(props){
const [ nav , setNav ] = useState("0");
const pathname = props.history.location.pathname;
useEffect(()=>{
if(pathname){
if(pathname === `/license/preface`){
setNav("0");
}
if(pathname === `/license/psl/mulanpsl-v1`){
setNav("1");
}
if(pathname === `/license/psl/mulanpsl-v2`){
setNav("2");
}
if(pathname === `/license/public/MulanPubL-V1`){
setNav("3");
}
if(pathname === `/license/faq`){
setNav("4");
}
}
},[pathname])
return(
<div className="panelsBox">
<Box>
<Short>
<Menu selectedKeys={[nav]} mode="inline" className="menus" defaultOpenKeys={["2_1"]}>
<Menu.Item key="0"><Link to={`/license/preface`}>引言</Link></Menu.Item>
<Menu.SubMenu key="2_1" title={"木兰宽松许可证"}>
<Menu.Item key="1"><Link to={`/license/psl/mulanpsl-v1`}>第一版</Link></Menu.Item>
<Menu.Item key="2"><Link to={`/license/psl/mulanpsl-v2`}>第二版</Link></Menu.Item>
</Menu.SubMenu>
<Menu.Item key="3"><Link to={`/license/public/MulanPubL-V1`}>木兰公共许可证</Link></Menu.Item>
<Menu.Item key="4"><Link to={`/license/faq`}>常见问题</Link></Menu.Item>
</Menu>
</Short>
<Long>
<Gap>
<div className="boxshadow">
<Switch {...props}>
<Route
path="/license/preface"
render={(p) => (
<Preface {...props} {...p} />
)}
></Route>
<Route
path="/license/psl/:id"
render={(p) => (
<PSL {...props} {...p} />
)}
></Route>
<Route
path="/license/public/:id"
render={(p) => (
<Public {...props} {...p} />
)}
></Route>
<Route
path="/license/faq"
render={(p) => (
<FAQ {...props} {...p} />
)}
></Route>
</Switch>
</div>
</Gap>
</Long>
</Box>
</div>
)
}
export default HomeHoc(Index);

View File

@ -0,0 +1,46 @@
.panelsBox{
width: 1200px;
margin: 20px auto;
.menus.ant-menu-inline{
border-right: none;
box-shadow: 0px 0px 8px 0px #F1F1F1;
.ant-menu-submenu-arrow{
display: none;
}
.ant-menu-item-selected{
background-color: #fff;
color: #1890ff;
}
.ant-menu-item::after{
display: none;
}
}
}
.boxshadow{
box-shadow: 0px 0px 10px rgba(0,0,0,0.1);
}
.contents{
padding:15px 30px;
font-size: 15px;
line-height: 24px;
text-indent: 2em;
color: #333;
.pre > pre {
font-family: Menlo,Monaco,Consolas,"Courier New",monospace;
padding: 12px;
margin: 0 0 10px;
font-size: 13px;
line-height: 1.5;
word-break: break-all;
white-space: pre-wrap;
background-color: #f5f5f5;
border-radius: 0;
border: 1px solid #ccc;
}
.markdown-body table td{
word-break: break-word;
}
.markdown-body table{
width: 100%!important;
}
}

46
src/mulan/license/PSL.jsx Normal file
View File

@ -0,0 +1,46 @@
import React, { useEffect, useState } from 'react';
import Title from '../../forge/Component/Title';
import RenderHtml from '../../components/render-html';
import { Button } from 'antd';
import { Skeleton } from 'antd';
import Axios from 'axios';
function PSL(props){
const [value , setValue ] = useState(undefined);
const id = props.match.params.id;
useEffect(()=>{
if(id && id!==value){
setValue(parseInt(id));
}
},[id])
useEffect(()=>{
const url = `/helps/${id}.josn`;
Axios.get(url).then(result=>{
if(result){
setValue(result.data.data.content);
}
}).catch(error=>{})
},[id])
return(
<div>
<Title>木兰宽松许可证
{/* <Button type={"primary"}>下载</Button> */}
</Title>
<div class="contents">
<div className="pre">
{
value ?
<RenderHtml className="break_word_comments imageLayerParent" value={value} url={props.history.location}/>
:
<Skeleton />
}
</div>
</div>
</div>
)
}
export default PSL;

View File

@ -0,0 +1,32 @@
import React,{useState,useEffect} from 'react';
import Title from '../../forge/Component/Title';
import Axios from 'axios';
import RenderHtml from '../../components/render-html';
import { Skeleton } from 'antd';
function Preface(props){
const [ value , setValue ] = useState(undefined);
useEffect(()=>{
const url = `/helps/introduction.json`;
Axios.get(url).then(result=>{
if(result){
setValue(result.data.data.content);
}
}).catch(error=>{})
},[])
return(
<div>
<Title>引言</Title>
<div class="contents">
{
value ?
<RenderHtml className="break_word_comments imageLayerParent" value={value} url={props.history.location}/>
:
<Skeleton />
}
</div>
</div>
)
}
export default Preface;

View File

@ -0,0 +1,44 @@
import React, { useEffect, useState } from 'react';
import Title from '../../forge/Component/Title';
import RenderHtml from '../../components/render-html';
import { Button } from 'antd';
import { Skeleton } from 'antd';
import Axios from 'axios';
function Public(props){
const [value , setValue ] = useState(undefined);
const id = props.match.params.id;
useEffect(()=>{
if(id && id!==value){
setValue(parseInt(id));
}
},[id])
useEffect(()=>{
const url = `/helps/${id}.josn`;
Axios.get(url).then(result=>{
if(result){
setValue(result.data.data.content);
}
}).catch(error=>{})
},[id])
return(
<div>
{/* <Button type={"primary"}>下载</Button> */}
<Title>木兰公共许可证</Title>
<div class="contents">
<div className="pre">
{
value ?
<RenderHtml className="break_word_comments imageLayerParent" value={value} url={props.history.location}/>
:
<Skeleton />
}
</div>
</div>
</div>
)
}
export default Public;