Compare commits

...

13 Commits

Author SHA1 Message Date
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
52 changed files with 1768 additions and 162 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": {

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,19 @@ 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 About = Loadable({
loader: () => import('./mulan/About/Index'),
loading: Loading,
})
//403页面
@ -55,26 +60,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 +125,7 @@ class App extends Component {
});
initAxiosInterceptors(this.props);
this.getAppdata();
this.getSetting();
window.addEventListener('error', (event) => {
const msg = `${event.type}: ${event.message}`;
@ -144,67 +138,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 +171,38 @@ 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="/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

@ -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
*/

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

@ -0,0 +1,30 @@
import React, { useEffect, useState } from 'react';
import { HomeHoc } from '../HOC/HomeHoc';
import'./Index.scss';
import axios from 'axios';
function Index(){
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 className="aboutBox">
<p className="aboutTitle">关于我们</p>
<div className="desc">
{about && about.content}
</div>
</div>
)
}
export default HomeHoc(Index);

View File

@ -0,0 +1,21 @@
.aboutBox{
max-width: 1200px;
margin:20px auto;
border:1px solid #eee;
border-radius: 4px;
.aboutTitle{
font-size: 20px;
padding:10px 0px;
text-align: center;
border-bottom:1px solid #eee;
margin-bottom: 0px;
}
.desc{
line-height: 22px;
font-size: 16px;
color: #333;
text-indent: 2em;
text-align: justify;
padding:40px;
}
}

View File

@ -0,0 +1,37 @@
.loginModal{
.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, useEffect } 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="请输入登录账号" onChange={changeInput}/>
)}
</Form.Item>
<Form.Item>
{getFieldDecorator("password",{
rules:[{required:true,message:"请输入登录密码"}]
})(
<Input.Password placeholder="请输入登录密码" 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"}>
<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, successFunc , quitFunc , match }){
return(
<div className="headsnav">
<Nav
match={match}
users={users}
headData={headData}
successFunc={successFunc}
quitFunc={quitFunc}
/>
</div>
)
}
export default Header;

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

@ -0,0 +1,68 @@
import React, { Component, useState } from 'react';
import Header from './Header';
import Footer from './Footer';
import './Index.scss';
import axios from 'axios';
export function HomeHoc(Sub){
return class II extends Component {
constructor(props){
super(props);
this.state={
users:undefined
}
}
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)=> {
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=>{})
}
render(){
const { users } = this.state;
const { headData } = this.props;
console.log(this.props);
return(
<div style={{height:"100%",overflow:"auto",position:"relative"}}>
<Header {...this.props} {...this.state} users={users} headData={headData} successFunc={this.successFunc} quitFunc={this.quitFunc}/>
<div className="homebody">
<Sub {...this.props} {...this.state} users={users}/>
<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>
)
}
}
}

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

@ -0,0 +1,100 @@
.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;
}
.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 , successFunc , 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.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={()=>setVisible(true)}>登录/注册</a>
}
</div>
</div>
)
}
export default Nav;

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

@ -0,0 +1,213 @@
import React, { useEffect, useState } 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 } from 'antd';
import { getUrlHead } from './Data/getUrl';
import axios from 'axios';
function Index(props){
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 [ cooperaters ,setCooperaters ] =useState(undefined);
useEffect(()=>{
getRecommand();
getAbout();
getNews();
getActivity();
getCooperaters();
},[])
//
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:{type:"activity"}
}).then(result=>{
if(result && result.data){
setNews(result.data.data);
}
}).catch(error=>{})
}
//
function getActivity(){
const url = `/topics.json`;
axios.get(url,{
params:{type:"news"}
}).then(result=>{
if(result && result.data){
setActivity(result.data.data);
}
}).catch(error=>{})
}
//
function getCooperaters(){
const url = `/cooperaters.json`;
axios.get(url).then(result=>{
if(result && result.data){
setCooperaters(result.data.data);
}
}).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>
<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} 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>
{
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>
}
</div>
)
}
export default HomeHoc(Index);

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

@ -0,0 +1,254 @@
.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;
}
}
}
}
}
}
.homeFooterParter{
padding:30px 0px;
width: 1200px;
margin:0px auto!important;
}
.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;
}
}
}
.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;
}
}
}
}

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 && item.project_tags.length>0 &&
<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,149 @@
.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;
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;
}
.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>
<p className="name task-hide">{i.project_full_name}</p>
<span className="num">{i.commits}</span>
</li>
)
})
}
</ul>
:<Skeleton />
}
</div>
)
}
export default Ranking;

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

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,45 @@
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));
console.log(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;