Merge pull request '更新代码' (#587) from gitlink_server into pre_gitlink_server
This commit is contained in:
commit
a3e0fdbff0
File diff suppressed because it is too large
Load Diff
|
|
@ -5,6 +5,8 @@
|
|||
"dependencies": {
|
||||
"@monaco-editor/react": "^2.3.0",
|
||||
"@novnc/novnc": "^1.1.0",
|
||||
"@wangeditor/editor": "^5.1.23",
|
||||
"@wangeditor/editor-for-react": "^1.0.6",
|
||||
"actioncable": "^5.2.4-3",
|
||||
"ahooks": "^2.10.14",
|
||||
"antd": "^3.26.15",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ export {
|
|||
getImageUrl as getImageUrl,getImageUrlAbsolute as getImageUrlAbsolute,getImage as getImage, getmyUrl as getmyUrl, getRandomNumber as getRandomNumber, getUrl as getUrl, getZoneUrl as getZoneUrl, publicSearchs as publicSearchs, getRandomcode as getRandomcode, getUrlmys as getUrlmys, getUrl2 as getUrl2, setImagesUrl as setImagesUrl
|
||||
, getUploadActionUrl as getUploadActionUrl, getUploadActionUrltwo as getUploadActionUrltwo, getUploadActionUrlthree as getUploadActionUrlthree, getUploadActionUrlOfAuth as getUploadActionUrlOfAuth
|
||||
, getTaskUrlById as getTaskUrlById, TEST_HOST, htmlEncode as htmlEncode, getupload_git_file as getupload_git_file, getcdnImageUrl as getcdnImageUrl,
|
||||
turnbar,returnbar,setSeoMeta as setSeoMeta , IsPC as IsPC
|
||||
turnbar,returnbar,setSeoMeta as setSeoMeta , IsPC as IsPC, addMeta as addMeta
|
||||
} from './UrlTool';
|
||||
|
||||
export { setmiyah as setmiyah } from './Component';
|
||||
|
|
|
|||
|
|
@ -0,0 +1,125 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { Editor, Toolbar } from '@wangeditor/editor-for-react';
|
||||
import '@wangeditor/editor/dist/css/style.css';
|
||||
import cookie from 'react-cookies';
|
||||
import { message, Spin } from 'antd';
|
||||
import axios from 'axios';
|
||||
import './index.scss';
|
||||
|
||||
// 富文本编辑器
|
||||
function MyEditor(props) {
|
||||
const {value, setValue, uploadUrl, videoUploadUrl} = props;
|
||||
// editor 实例
|
||||
const [editor, setEditor] = useState(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
// 模拟 ajax 请求,异步设置 html
|
||||
useEffect(() => {
|
||||
if(value && setValue){
|
||||
setValue(value);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// 工具栏配置
|
||||
const toolbarConfig = {};
|
||||
// 隐藏 代办
|
||||
toolbarConfig.excludeKeys = [
|
||||
'todo', "fullScreen"
|
||||
]
|
||||
|
||||
// 编辑器配置
|
||||
const editorConfig = {
|
||||
placeholder: '请输入内容...',
|
||||
MENU_CONF:{
|
||||
uploadImage:{
|
||||
server: uploadUrl,
|
||||
headers: {
|
||||
Authorization: cookie.load('autologin_trustie')
|
||||
},
|
||||
withCredentials: true,
|
||||
fieldName: "file",
|
||||
maxFileSize: 20 * 1024 * 1024,
|
||||
allowedFileTypes: ["image/jpeg", "image/png", "image/gif", "image/webp", "image/svg+xml", "image/bmp"],
|
||||
customInsert(res, insertFn){
|
||||
const {url} = res;
|
||||
insertFn(url, "图片", url);
|
||||
},
|
||||
onBeforeUpload(file){
|
||||
const {type, name} = Object.values(file)[0];
|
||||
const typeRange = ["image/jpeg", "image/png", "image/gif", "image/webp", "image/svg+xml", "image/bmp"];
|
||||
const suffix = name && name.split(".")[1];
|
||||
const isType = typeRange.indexOf(type) !== -1 && suffix !== "tiff";
|
||||
if(!isType){
|
||||
message.error("只能上传jpg、jpeg、webp、png、svg、gif、bmp格式的图片!")
|
||||
return false;
|
||||
}
|
||||
return file;
|
||||
},
|
||||
onError(file, err) {
|
||||
message.error(`${file.name} 上传出错, ${err}` )
|
||||
},
|
||||
},
|
||||
uploadVideo:{
|
||||
// 自定义上传视频
|
||||
async customUpload(file, insertFn){
|
||||
if (!file.type.includes("video")) {
|
||||
return message.error("请上传视频格式的文件!");
|
||||
} else if (file.size / 1024 / 1024 > 200) {
|
||||
return message.error("请上传200M以内的文件!");
|
||||
}
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
setLoading(true);
|
||||
axios.post(videoUploadUrl || uploadUrl, formData, {headers: {Authorization: cookie.load('autologin_trustie')}}).then(res=>{
|
||||
setLoading(false);
|
||||
if(res && res.data && res.data.code === 200){
|
||||
insertFn(res.data.url)
|
||||
}else{
|
||||
message.error(res && res.data && res.data.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 及时销毁 editor ,重要!
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (editor === null) return;
|
||||
editor.destroy();
|
||||
setEditor(null);
|
||||
};
|
||||
}, [editor]);
|
||||
|
||||
const editorBoxClass = {
|
||||
border: '1px solid #ccc',
|
||||
zIndex: 100,
|
||||
'video': {
|
||||
maxWidth: '100%'
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={editorBoxClass}>
|
||||
<Spin spinning={loading} tip="上传中,请耐心等待">
|
||||
<Toolbar
|
||||
editor={editor}
|
||||
defaultConfig={toolbarConfig}
|
||||
mode="default"
|
||||
style={{ borderBottom: '1px solid #ccc' }}
|
||||
/>
|
||||
<Editor
|
||||
defaultConfig={editorConfig}
|
||||
value={value}
|
||||
onCreated={setEditor}
|
||||
onChange={(editor) => setValue && setValue(editor.getHtml())}
|
||||
mode="default"
|
||||
style={{ height: '500px', overflowY: 'hidden' }}
|
||||
/>
|
||||
</Spin>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default MyEditor;
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
.w-e-modal{
|
||||
input, button, select, optgroup, textarea{
|
||||
line-height: 1.9;
|
||||
}
|
||||
}
|
||||
|
|
@ -364,12 +364,13 @@ class NewHeader extends Component {
|
|||
onMouseOut={ () => { this.setState( {showSubMenu: false }) } }
|
||||
>
|
||||
<a onClick={ () => { this.setState( {showSubMenu: true }) } }>特色专区</a>
|
||||
<ul className={ `drop-down ${ showSubMenu ? 'drop-down-show' : '' }` } style={{ height : showSubMenu ? `${ zoneList.length * 46}px` : 0 }}>
|
||||
<ul className={ `drop-down ${ showSubMenu ? 'drop-down-show' : '' }` } style={{ height : showSubMenu ? `${ current_user && current_user.login ? ((zoneList.length+1) * 46) : (zoneList.length * 46)}px` : 0 }}>
|
||||
{
|
||||
zoneList.map((e, k) => {
|
||||
return <li className="drop-down-item" key={ k }><a href={ e.link } className="task-hide" title={ e.name } target="_blank">{ e.name }</a></li>
|
||||
})
|
||||
}
|
||||
{current_user && current_user.login && <li className="drop-down-item"><Link to="/zone/apply" target="_blank">+申请创建</Link></li> }
|
||||
</ul>
|
||||
</li>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { tempEnum } from '../tempInfo';
|
||||
import Crown from '../img/crown.png';
|
||||
import Nodata from '../../Nodata';
|
||||
import nodata from '../img/nodata.png';
|
||||
|
||||
function Member(props) {
|
||||
const { vipLists, temp } = props;
|
||||
|
||||
return(
|
||||
<div className="vip_list">
|
||||
{
|
||||
vipLists.map((i,k)=>{
|
||||
return(
|
||||
<div className="vip_list_card">
|
||||
<div className="card_title">
|
||||
<img src={Crown} alt="" width="37px" style={{marginBottom:5,marginRight:5}}/>
|
||||
<div>
|
||||
<div>
|
||||
{i.typeName}
|
||||
</div>
|
||||
{ temp === tempEnum.zone1 && <div className="linear_gradient">{i.typeName}</div> }
|
||||
</div>
|
||||
</div>
|
||||
<p className="card_desc">{i.typeIntroduction}</p>
|
||||
{
|
||||
i.zoneMemberList && i.zoneMemberList.length > 0 ?
|
||||
<ul className="card_ul">
|
||||
{
|
||||
i.zoneMemberList.map((j,key)=>{
|
||||
return(
|
||||
<li className="card_u_li">
|
||||
<div className="card_u_wrap">
|
||||
<Link to={`/${j.login}`} ><img src={j.imageUrl} alt="" /></Link>
|
||||
<div className="card_u_info">
|
||||
<div className="card_u_up"><span className="card_name task-hide">{j.name}</span>{j.memberLevel && <span className="card_tag">{j.memberLevel}</span> }</div>
|
||||
<p className="card_u_down task-hide-2">{j.introduction}</p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
:
|
||||
(
|
||||
temp === tempEnum.zone1 ? <Nodata _html="暂无数据" img={ nodata }/> : <Nodata _html="暂无数据" />
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Member;
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { Form , Input , Row , Col, Button } from 'antd';
|
||||
import '../index.scss';
|
||||
import { postCreateZone } from '../api';
|
||||
import axios from 'axios';
|
||||
const { TextArea } = Input;
|
||||
|
||||
const phonereg = /^([1][3456789])\d{9}$/
|
||||
function Apply(props){
|
||||
const { form , current_user } = props;
|
||||
const { getFieldDecorator , validateFields } = form;
|
||||
useEffect(()=>{
|
||||
document.title = '申请创建专区';
|
||||
},[])
|
||||
|
||||
useEffect(()=>{
|
||||
if(!(current_user && current_user.login)){
|
||||
props.history.push("/403");
|
||||
}
|
||||
},[current_user])
|
||||
|
||||
function submitFunc(){
|
||||
validateFields((error,values)=>{
|
||||
if(!error){
|
||||
const zoneApplication = {...values};
|
||||
postCreateZone(zoneApplication).then(response=>{
|
||||
if(response && response.data && response.data.code===200){
|
||||
props.showNotification("提交成功!");
|
||||
setTimeout(() => {
|
||||
window.location.href="/";
|
||||
}, 70);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function checkPhone(rule, value, callback){
|
||||
if(!value){
|
||||
callback();
|
||||
}
|
||||
if(!phonereg.test(value)){
|
||||
callback("请输入正确的手机号码");
|
||||
}
|
||||
callback();
|
||||
}
|
||||
|
||||
function checkId(rule,value,callback){
|
||||
value ? axios.post(`/accounts/check_keywords`, {
|
||||
text:value
|
||||
}).then(response => {
|
||||
if(response && response.data.data===false){
|
||||
callback("与系统标识重复,请更改标识");
|
||||
}
|
||||
callback();
|
||||
}):callback();
|
||||
}
|
||||
|
||||
|
||||
return(
|
||||
<div className="applyBox">
|
||||
<div className="applyContent">
|
||||
<p className="apply-t">申请创建专区</p>
|
||||
<div className="apply-tips">
|
||||
<h5>申请说明:</h5>
|
||||
<p>1、如果您想要创建自己的专区,请填写下方页面中的信息来提交专区申请。</p>
|
||||
<p>2、管理员在收到您的专区申请后,将会通过您提供的联系方式与您进行沟通,请您留意查收信息。</p>
|
||||
<p>3、如果您的专区建设情况出色,希望在官方专区列表中展示,请发送邮件至官方邮箱 gitlink@ccf.org.cn,并在邮件中注明”推荐专区+您的专区链接”。经官方审核后,您的专区将会被推荐展示。</p>
|
||||
</div>
|
||||
<Form className="applyForm">
|
||||
<Form.Item
|
||||
label="专区名称"
|
||||
>
|
||||
{getFieldDecorator("name",{
|
||||
rules:[{required:true,message:"请输入专区名称"}]
|
||||
})(
|
||||
<Input placeholder="请输入专区名称" maxLength={50} style={{height:"36px"}}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="专区介绍"
|
||||
>
|
||||
{getFieldDecorator("introduction",{
|
||||
rules:[{required:true,message:"请输入专区介绍"}]
|
||||
})(
|
||||
<TextArea placeholder="请输入专区介绍内容" maxLength={200} rows={5}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="专区用途"
|
||||
>
|
||||
{getFieldDecorator("purpose",{
|
||||
rules:[]
|
||||
})(
|
||||
<TextArea placeholder="请输入专区用途内容" maxLength={200} rows={5}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
<Row type="flex" justify="space-between">
|
||||
<Col span={11}>
|
||||
<Form.Item
|
||||
label="专区标识"
|
||||
>
|
||||
{getFieldDecorator("key",{
|
||||
rules:[
|
||||
{ required: true,message:"请输入专区域名标识" },
|
||||
{ pattern: /^[a-zA-Z][a-zA-Z0-9_-]{0,18}[a-zA-Z]$/, message: "长度2-20,只能包含数字、字母、下划线、中划线,必须以字母开头结尾"},
|
||||
{ validator:checkId }
|
||||
],
|
||||
validateTrigger:"onBlur",
|
||||
validateFirst: true,
|
||||
})(
|
||||
<Input placeholder="请输入专区域名标识" maxLength={20} style={{height:"36px"}}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={11}>
|
||||
<Form.Item
|
||||
label="联系人"
|
||||
>
|
||||
{getFieldDecorator("contactPerson",{
|
||||
rules:[{required:true,message:"请输入联系人"}]
|
||||
})(
|
||||
<Input placeholder="请输入联系人" maxLength={50} style={{height:"36px"}}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row type="flex" justify="space-between">
|
||||
<Col span={11}>
|
||||
<Form.Item
|
||||
label="联系人电话"
|
||||
>
|
||||
{getFieldDecorator("contactPhone",{
|
||||
rules:[
|
||||
{required:true,message:"请输入联系人电话"},
|
||||
{
|
||||
validator: (rule, value, callback) => { checkPhone(rule, value, callback) }
|
||||
}
|
||||
],
|
||||
validateTrigger:"onBlur",
|
||||
validateFirst: true,
|
||||
})(
|
||||
<Input placeholder="请输入联系人电话" maxLength={50} style={{height:"36px"}}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={11}>
|
||||
<Form.Item
|
||||
label="联系人微信号"
|
||||
>
|
||||
{getFieldDecorator("contactWechat",{
|
||||
rules:[]
|
||||
})(
|
||||
<Input placeholder="请输入联系人微信号" maxLength={50} style={{height:"36px"}}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Form.Item
|
||||
label="希望平台提供的服务"
|
||||
>
|
||||
{getFieldDecorator("servicesProvided",{
|
||||
rules:[]
|
||||
})(
|
||||
<TextArea placeholder="请输入您希望平台提供的服务" maxLength={200} rows={5}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<div className="mt20">
|
||||
<Button style={{height:"36px",width:"100px"}} type="primary" onClick={submitFunc}>提交</Button>
|
||||
<Button style={{marginLeft:"30px",height:"36px",width:"100px"}} onClick={()=>{window.location.href="/"}}>取消</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Form.create({ name: 'ApplyNew' })(Apply);
|
||||
|
|
@ -4,10 +4,12 @@ import { httpUrl } from '../fetch';
|
|||
import { Link } from 'react-router-dom';
|
||||
import shijian from '../img/shijian.png';
|
||||
import xuexi from '../img/xuexiguanli.png';
|
||||
import { getHomePageList, gethomePageDocList, getAllList, getNewsAllList } from '../api';
|
||||
import { getHomePageList, gethomePageDocList, getAllList, getNewsAllList, getVIPLists } from '../api';
|
||||
import axios from 'axios';
|
||||
import Partner from '../Component/partner';
|
||||
import MemberList from '../Component/memberList';
|
||||
import '../indexZonebyCCF.scss';
|
||||
import '../indexZone1.scss';
|
||||
import "slick-carousel/slick/slick.css";
|
||||
import "slick-carousel/slick/slick-theme.css";
|
||||
import Slider from 'react-slick';
|
||||
|
|
@ -51,11 +53,19 @@ function HeaderPageCCF(props) {
|
|||
}, [id,cateId])
|
||||
|
||||
function getPersonList() {
|
||||
getHomePageList(id).then(result => {
|
||||
if (result) {
|
||||
setPersonList(result.data.rows);
|
||||
}
|
||||
}).catch(error => { })
|
||||
// 不分类
|
||||
// getHomePageList(id).then(result => {
|
||||
// if (result) {
|
||||
// setPersonList(result.data.rows);
|
||||
// }
|
||||
// }).catch(error => { })
|
||||
// 分类获取首页推荐的会员列表
|
||||
getVIPLists(id, {isHomepage: 1}).then(response=>{
|
||||
if(response){
|
||||
const list = response.data.rows && response.data.rows.filter(item=>item.zoneMemberList);
|
||||
setPersonList(list);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getNewsList() {
|
||||
|
|
@ -102,7 +112,7 @@ function HeaderPageCCF(props) {
|
|||
}).catch(error => { })
|
||||
}
|
||||
return (
|
||||
<div className="zone_box">
|
||||
<div className="zone_box pb100">
|
||||
{
|
||||
// 新闻动态
|
||||
data && data.cmsShow === 1 && newsList && newsList.length >= 0 &&
|
||||
|
|
@ -117,7 +127,7 @@ function HeaderPageCCF(props) {
|
|||
return (
|
||||
<div className="regform" key={k}>
|
||||
<div className="newsBannerBox">
|
||||
<img src={i.headImg || img1} alt="" width="785px" style={{objectFit: "cover"}} onClick={()=>{window.location.href=`/zone/${deptId}/newdetail/${i.id}`}} />
|
||||
<img src={i.headImg || img1} alt="" className='newsImg' onClick={()=>{window.location.href=`/zone/${deptId}/newdetail/${i.id}`}} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
@ -234,23 +244,30 @@ function HeaderPageCCF(props) {
|
|||
{
|
||||
// 核心贡献者
|
||||
data && data.memberShow === 1 && personList && personList.length > 0 &&
|
||||
<div className="zone_contributor">
|
||||
<p className="in_title">{data.homepageMemberTitle}</p>
|
||||
{
|
||||
<ul className="boxmain zone_c_lists">
|
||||
<div className={`zone_contributor boxmain ${temp}_VIP_box`}>
|
||||
<p className="in_title mb50">{data.homepageMemberTitle}</p>
|
||||
{/* <MemberList vipLists={personList} temp={temp}/> */}
|
||||
{personList && personList.map(item=>{
|
||||
return <div>
|
||||
<p className='font-18'>{item.typeName}</p>
|
||||
<ul className="boxmain zone_c_lists mt20">
|
||||
{
|
||||
personList.map((i, k) => {
|
||||
item.zoneMemberList.map((i, k) => {
|
||||
return (
|
||||
<li key={k}>
|
||||
<Link to={`/${i.login}`}><img src={i.imageUrl} alt="" /></Link>
|
||||
<span>{i.name}</span>
|
||||
<div className='mb10'>
|
||||
<span>{i.name}</span>
|
||||
{i.memberLevel && <span className="memberLevel_tag ml10">{i.memberLevel}</span> }
|
||||
</div>
|
||||
<p className="task-hide-2" style={{ display: i.introduction ? "" : "flex" }}>{i.introduction || "暂无~"}</p>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
</div>
|
||||
})}
|
||||
</div>
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React , { useState , useEffect } from 'react';
|
||||
import { Menu , Spin , Skeleton } from 'antd';
|
||||
import { Menu , Spin , Skeleton, Button, Dropdown } from 'antd';
|
||||
import Hot from '../Component/hot';
|
||||
import { Box , LongWidth } from '../../Component/layout';
|
||||
import { Box , FlexAJ, LongWidth } from '../../Component/layout';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Lists from './newsList';
|
||||
import guideArrow from '../img/guideArrow.png';
|
||||
|
|
@ -24,7 +24,18 @@ function Main(props){
|
|||
const [ isSpin , setIsSpin ] = useState(true);
|
||||
const [ newCateId , setNewCateId ] = useState(undefined);
|
||||
const { deptId , cateId } = props.match.params;
|
||||
const { id, temp } = props;
|
||||
const { id, temp, role, history } = props;
|
||||
|
||||
const menu = (
|
||||
<Menu>
|
||||
<Menu.Item>
|
||||
<a onClick={()=>{history.push(`/zone/${deptId}/news/add?type=richEditor`)}}>富文本编辑器</a>
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
<a onClick={()=>{history.push(`/zone/${deptId}/news/add?type=mdEditor`)}}>MD编辑器</a>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
);
|
||||
|
||||
|
||||
useEffect(()=>{
|
||||
|
|
@ -34,7 +45,7 @@ function Main(props){
|
|||
getMainList();
|
||||
} else {
|
||||
getCateList();
|
||||
}
|
||||
}
|
||||
getHotList();
|
||||
}
|
||||
},[id])
|
||||
|
|
@ -109,7 +120,17 @@ function Main(props){
|
|||
<div>
|
||||
<div className="main_content">
|
||||
<div className="boxmain">
|
||||
<p className="in_title">{ tempConfig[temp].mainTitle }</p>
|
||||
<div className='titleButBox'>
|
||||
<p className="in_title">{tempConfig[temp].mainTitle}</p>
|
||||
{role && role.role !== "None" && <div className='createButs mt10'>
|
||||
{role.role === "Member" && <Button type="primary" ghost className='mr20'>
|
||||
<Link to={`/zone/${deptId}/news/self`}>我的文章</Link>
|
||||
</Button>}
|
||||
{role.role === "Member" ? <Dropdown overlay={menu}>
|
||||
<Button icon='plus' type="primary" ghost>新建</Button>
|
||||
</Dropdown> : <Button type="primary" ghost onClick={()=>{window.open(role.docManageUrl)}}>管理资讯</Button>}
|
||||
</div>}
|
||||
</div>
|
||||
{
|
||||
mainList && mainList.length>0 &&
|
||||
(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,185 @@
|
|||
import React,{ useState , useEffect } from 'react';
|
||||
import { Breadcrumb, Button, Form, Input, message, Select } from 'antd';
|
||||
import { getZoneUrl } from 'educoder';
|
||||
import MdEditor from '../../../modules/tpm/challengesnew/tpm-md-editor';
|
||||
import RichEditor from '../../Component/RichEditor';
|
||||
import UploadImage from '../../Team/Component/UploadImage';
|
||||
import { tempConfig } from '../tempInfo';
|
||||
import '../index.scss';
|
||||
import { Base64 } from 'js-base64';
|
||||
import { addNewsByDirId, getDirListById, getNewsDetailByAu, updateDoc } from '../api';
|
||||
import { httpUrl } from '../fetch';
|
||||
|
||||
function NewsCreate(props){
|
||||
const {history, id, role, current_user} = props;
|
||||
const temp = props.temp;
|
||||
const { getFieldDecorator, validateFields , setFieldsValue, setFields } = props.form;
|
||||
const { deptId, id: newsId } = props.match.params;
|
||||
const pathname = props.location.pathname;
|
||||
const [type, setType] = useState(new URLSearchParams(props.location.search.substring(1)).get('type'));
|
||||
const [value, setValue] = useState(undefined);
|
||||
const [imageUrl, setImageUrl] = useState(undefined)
|
||||
const [dirList, setDirList] = useState(undefined);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [isEdit, setIsEdit] = useState(false);
|
||||
const layout = {
|
||||
labelCol: { span: 2 },
|
||||
wrapperCol: { span: 14 },
|
||||
};
|
||||
|
||||
useEffect(()=>{
|
||||
if((role && role.role === "None") || (current_user && !current_user.login)){
|
||||
history.push(`/zone/${deptId}`);
|
||||
}
|
||||
}, [role])
|
||||
|
||||
useEffect(()=>{
|
||||
id && getDircList();
|
||||
},[id])
|
||||
|
||||
useEffect(()=>{
|
||||
if(pathname){
|
||||
if(pathname.endsWith("/edit")){
|
||||
setIsEdit(true);
|
||||
getNewsDetail();
|
||||
}else{
|
||||
setIsEdit(false);
|
||||
}
|
||||
}
|
||||
},[pathname])
|
||||
|
||||
function getDircList(){
|
||||
getDirListById(id).then(res=>{
|
||||
if(res){
|
||||
setDirList(res.data.rows);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getNewsDetail() {
|
||||
getNewsDetailByAu(newsId).then(res=>{
|
||||
if(res && res.data){
|
||||
const {name, summary, cmsDir, content, headImg, editorType} = res.data.data;
|
||||
setType(editorType === "rich_editor" ? 'richEditor' : 'mdEditor')
|
||||
setFieldsValue({
|
||||
name,
|
||||
summary,
|
||||
dirId: cmsDir && cmsDir.id,
|
||||
content: Base64.decode(content),
|
||||
headImg
|
||||
});
|
||||
setImageUrl(headImg);
|
||||
setValue(Base64.decode(content));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 新建文章
|
||||
function submit(e){
|
||||
e.preventDefault();
|
||||
validateFields((err, fieldsValue)=>{
|
||||
const hasContent = type === "richEditor" && fieldsValue.content === "<p><br></p>"
|
||||
if(err || hasContent){
|
||||
hasContent && setFields({content: {value:fieldsValue.content,errors:[new Error('请输入内容!')]}});
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
const params = {
|
||||
...fieldsValue,
|
||||
editorType: type === "richEditor" ? "rich_editor" : "markdown_editor",
|
||||
headImg: imageUrl,
|
||||
content: Base64.encode(fieldsValue.content)
|
||||
}
|
||||
if(isEdit){
|
||||
delete params.editorType;
|
||||
return updateDoc(newsId, params).then(res=>{
|
||||
const {data:{code, msg}} = res;
|
||||
setLoading(false);
|
||||
if(code === 200){
|
||||
message.success("提交成功");
|
||||
history.push(`/zone/${deptId}/newdetail/${newsId}`);
|
||||
}else{
|
||||
message.error(msg || '提交失败,请联系管理员!')
|
||||
}
|
||||
}).catch(e=>{
|
||||
setLoading(false);
|
||||
})
|
||||
}
|
||||
addNewsByDirId(fieldsValue.dirId, params).then(res=>{
|
||||
const {data:{code, msg, data}} = res;
|
||||
setLoading(false);
|
||||
if(code === 200){
|
||||
message.success("提交成功");
|
||||
history.push(`/zone/${deptId}/newdetail/${data}`);
|
||||
}else{
|
||||
message.error(msg || '提交失败,请联系管理员!')
|
||||
}
|
||||
}).catch(e=>{
|
||||
setLoading(false);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return(
|
||||
<div className='boxmain pt30 newsCreateBox pb100'>
|
||||
<Breadcrumb separator=">">
|
||||
<Breadcrumb.Item href={`/zone/${deptId}/news`}>{tempConfig[temp].mainTitle}</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>{isEdit ? '编辑' : '新建'}文章</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
<div className='formBox'>
|
||||
<Form {...layout} onSubmit={submit}>
|
||||
<Form.Item label="标题">
|
||||
{getFieldDecorator("name", {
|
||||
rules:[{ required:true,message:"请输入文章标题!" },
|
||||
{type: 'string', max: 200, message: "最大长度200"},
|
||||
{pattern: /^[^<>[\]/\\]+$/, message: "不能输入特殊字符:< > [ ] / \\"}]
|
||||
})(
|
||||
<Input placeholder="请输入文章标题" maxLength={ 200 } width="220px"/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="概览">
|
||||
{getFieldDecorator("summary", {
|
||||
rules:[{ required:true,message:"请输入概览!" },
|
||||
{type: 'string', max: 200, message: "最大长度200"}]
|
||||
})(
|
||||
<Input.TextArea placeholder="请输入概览" maxLength={ 200 } width="220px"/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="栏目">
|
||||
{getFieldDecorator("dirId", {
|
||||
rules:[{ required:true,message:"请选择文章所属栏目!" }]
|
||||
})(
|
||||
<Select placeholder="请选择文章所属栏目">
|
||||
{dirList && dirList.map(item=>{
|
||||
return <Select.Option key={item.id} value={item.id}>{item.name}</Select.Option>
|
||||
})}
|
||||
</Select>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="内容" wrapperCol={{span: 21}}>
|
||||
{getFieldDecorator("content",{
|
||||
rules:[{required:true,message:"请输入内容!"}],
|
||||
})(
|
||||
type === "richEditor" ? <RichEditor uploadUrl={`${httpUrl}/file/common/upload`} videoUploadUrl={`${httpUrl}/file/common/upload?type=cms`} setValue={(value)=>{setFieldsValue({"content": value})}}/> : <MdEditor
|
||||
placeholder={"请输入详细介绍"}
|
||||
height={500}
|
||||
className='editNewsMd'
|
||||
initValue={value}
|
||||
onChange={(value)=>{setFieldsValue({"content": value})}}
|
||||
imageExpand={false}
|
||||
></MdEditor>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="首图" extra="建议图片比例,长:宽为3:2,上传jpg、jpeg、webp、png、svg、gif、bmp格式的图片">
|
||||
<UploadImage maxSize={ 5 } getImageUrl={ (url) => setImageUrl(url) } url={ imageUrl ? imageUrl : undefined} action={ getZoneUrl(`/api/cms/common/upload`) } />
|
||||
</Form.Item>
|
||||
<Form.Item wrapperCol={{ offset: 2 }}>
|
||||
<Button className='mr20 themeBut' onClick={()=>{history.go(-1)}}>取消</Button>
|
||||
<Button type="primary" htmlType="submit" loading={loading} className='themeBut'>提交</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Form.create()(NewsCreate);
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import React,{ useState , useEffect } from 'react';
|
||||
import { Breadcrumb , Divider, Spin } from 'antd';
|
||||
import { Badge, Breadcrumb , Divider, Spin } from 'antd';
|
||||
import liulan from '../img/liulan.png';
|
||||
import RenderHtml from '../../../components/render-html';
|
||||
import { Base64 } from 'js-base64';
|
||||
|
|
@ -7,7 +7,7 @@ import { getNewsDetail } from '../api';
|
|||
import { IsPC } from 'educoder';
|
||||
import { tempConfig } from '../tempInfo';
|
||||
import Acce from '../Component/mobile/accessory';
|
||||
|
||||
import { addMeta, setSeoMeta } from 'educoder';
|
||||
|
||||
|
||||
function NewsDetail(props){
|
||||
|
|
@ -44,6 +44,23 @@ function NewsDetail(props){
|
|||
}
|
||||
},[id])
|
||||
|
||||
useEffect(()=>{
|
||||
// 设置网页标题
|
||||
if(detail){
|
||||
const { name, cmsDir, summary} = detail;
|
||||
document.title = `${ name }/${ cmsDir.name }`;
|
||||
addMeta('Keywords', `${ name },${ cmsDir.name },${ summary }`);
|
||||
}
|
||||
return componentWillUnmount
|
||||
}, [detail])
|
||||
|
||||
function componentWillUnmount() {
|
||||
if (zonedetail) {
|
||||
document.title= zonedetail.name;
|
||||
setSeoMeta(`${zonedetail.name},`, zonedetail.name, zonedetail.subTitle, `/zone/${deptId}`)
|
||||
}
|
||||
}
|
||||
|
||||
function getDetails(){
|
||||
setIsSpin(true)
|
||||
getNewsDetail(id).then(result=>{
|
||||
|
|
@ -90,7 +107,9 @@ function NewsDetail(props){
|
|||
<ul className="i_ul_value">
|
||||
{ cmsDir && <li><a href={ detail.publishUserUrl } ><img src={ detail.publishUserImage } alt="" className="headimg" /> { detail.publishUserNickname }</a></li> }
|
||||
{ cmsDir && <li>{ detail.isFirstPublish ? '创建于' : '更新于' }{detail.publishTime}</li> }
|
||||
{ detail.visits && <li><img src={liulan} alt="" className="mr5" />{detail.visits}</li> }
|
||||
{ !!detail.visits && <li><img src={liulan} alt="" className="mr5" />{detail.visits}</li> }
|
||||
{ detail.auditStatus === '2' && <li><Badge color={temp === "zone" ? "#466aff" : "#089f7f"} text="待审核" className='pass'/></li>}
|
||||
{ detail.auditStatus === '0' && <li><Badge color="#eb1212" text="未通过" className='failed'/></li>}
|
||||
</ul>
|
||||
</div>
|
||||
{ !IsPC() && <Divider dashed={true} /> }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,197 @@
|
|||
import React,{ useState, useEffect } from 'react';
|
||||
import { Breadcrumb , Spin , Pagination , Modal, Button } from 'antd';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { getNewsMyList , getSourceMyList , delNewsMyList , delSourceMyList } from '../api';
|
||||
import moment from 'moment';
|
||||
import nodata from '../img/nodata.png';
|
||||
import { tempEnum } from '../tempInfo';
|
||||
import emptydata from '../../Images/nodata.png';
|
||||
import '../index.scss';
|
||||
|
||||
|
||||
function SelfList(props){
|
||||
const [ status , setStatus ] = useState(1);
|
||||
const [ isSpin , setIsSpin ] = useState(true);
|
||||
const [ source , setSource ] = useState(undefined);
|
||||
const [ list , setList ] = useState(undefined);
|
||||
const [ page , setPage ] = useState(1);
|
||||
const [ total , setTotal ] = useState(0);
|
||||
|
||||
const [ visible ,setVisible ] = useState(false);
|
||||
const [ deleId , setDeleId] = useState(undefined);
|
||||
const pathname = props.location.pathname;
|
||||
const { deptId} = props.match.params;
|
||||
const { id , temp , data } = props;
|
||||
const limit = 15;
|
||||
|
||||
useEffect(()=>{
|
||||
if(id){
|
||||
setIsSpin(true);
|
||||
getFunc(source);
|
||||
}
|
||||
},[id,source,page])
|
||||
|
||||
|
||||
useEffect(()=>{
|
||||
if(page===1){
|
||||
setIsSpin(true);
|
||||
getFunc(source);
|
||||
}else{
|
||||
setPage(1);
|
||||
}
|
||||
},[status])
|
||||
|
||||
function getFunc(s){
|
||||
if(s===true){
|
||||
getSource();
|
||||
}
|
||||
if(s===false){
|
||||
getNew();
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
if(pathname){
|
||||
if(pathname.indexOf("/source/self")>-1){
|
||||
setSource(true);
|
||||
}else{
|
||||
setSource(false);
|
||||
}
|
||||
}
|
||||
},[pathname])
|
||||
|
||||
function getSource(){
|
||||
getSourceMyList({
|
||||
auditStatus:status,
|
||||
zoneId:id,
|
||||
pageSize:limit,
|
||||
pageNum:page
|
||||
}).then(result=>{
|
||||
let rows = result.data && result.data.rows;
|
||||
if(rows && rows.length>0){
|
||||
setList(rows);
|
||||
}else{
|
||||
setList([]);
|
||||
}
|
||||
setTotal(result.data.total);
|
||||
setIsSpin(false);
|
||||
})
|
||||
}
|
||||
|
||||
function getNew(){
|
||||
getNewsMyList(id,{
|
||||
auditStatus:status===0 ? 2 : status === 2 ? 0 : 1,
|
||||
pageSize:limit,
|
||||
pageNum:page
|
||||
}).then(result=>{
|
||||
let rows = result.data && result.data.rows;
|
||||
if(rows && rows.length>0){
|
||||
setList(rows);
|
||||
}else{
|
||||
setList([]);
|
||||
}
|
||||
setTotal(result.data.total);
|
||||
setIsSpin(false);
|
||||
})
|
||||
}
|
||||
|
||||
function cancelFunc(){
|
||||
setVisible(false);
|
||||
setDeleId(undefined);
|
||||
}
|
||||
function sureFunc(){
|
||||
if(source){
|
||||
delSourceMyList(deleId).then(response=>{
|
||||
setVisible(false);
|
||||
props.showNotification("删除成功!");
|
||||
getSource();
|
||||
})
|
||||
}else{
|
||||
delNewsMyList(deleId).then(response=>{
|
||||
setVisible(false);
|
||||
if(status ===1){
|
||||
props.showNotification("提交成功!");
|
||||
}else{
|
||||
props.showNotification("删除成功!");
|
||||
getNew();
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return(
|
||||
<div className="selfBoxContent">
|
||||
<Modal
|
||||
visible={visible}
|
||||
width="456px"
|
||||
title={`申请删除${source?"资源":"文章"}`}
|
||||
onCancel={cancelFunc}
|
||||
footer={null}
|
||||
className="delSModal"
|
||||
>
|
||||
<div className="delBoxCon">
|
||||
<i className="iconfont icon-shanchu_tc_icon font-22 mr15" style={{color:"#ca0002"}}></i>
|
||||
<div>
|
||||
<p className="font-15 mb15 mt5" style={{color:"#202d40"}}>确定删除该{source?"资源":"文章"}?</p>
|
||||
{(source || (!source && status === 1 && (data && data.docNeedAudit===1))) && <p style={{color:"#5f6872"}}>{source?"删除后所有资源文件将被清除,请谨慎操作":"此操作将提交删除申请,管理员同意申请后该文章将被删除"}</p> }
|
||||
</div>
|
||||
</div>
|
||||
<div className="delBoxBtn">
|
||||
<Button onClick={cancelFunc} style={{borderColor:"#d9d9d9",color:"rgba(0, 0, 0, 0.65)"}}>取消</Button>
|
||||
<Button type="primary" onClick={sureFunc} style={{backgroundColor:"#fff"}}>确认</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
<Breadcrumb separator=">" style={{paddingTop:"20px"}}>
|
||||
<Breadcrumb.Item><Link className="primaryColor" to={pathname.replace("/self","")}>{source ? "资源发布": temp === tempEnum.zone1 ? "新闻资讯":"领域资讯"}</Link></Breadcrumb.Item>
|
||||
<Breadcrumb.Item>我的{source ? "资源":"文章"}</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
<ul className="selfMenu">
|
||||
<li onClick={()=>setStatus(1)} className={status===1?"active":""}>已发布</li>
|
||||
<li onClick={()=>setStatus(0)} className={status===0?"active":""}>待审核</li>
|
||||
<li onClick={()=>setStatus(2)} className={status===2?"active":""}>未通过</li>
|
||||
</ul>
|
||||
<Spin spinning={isSpin}>
|
||||
<div style={{backgroundColor:"#fff",borderRadius:"4px"}}>
|
||||
<ul className="selfListPanel">
|
||||
{
|
||||
list && list.length === 0 &&
|
||||
<div style={{display:"flex",justifyContent:"center",alignItems:"center",flexDirection:"column",height:500}}>
|
||||
<img src={ temp === tempEnum.zone1 ? nodata : emptydata } alt="" width="300"/>
|
||||
<p className="font-15 color-grey-6 mt20">暂无数据~</p>
|
||||
</div>
|
||||
}
|
||||
{
|
||||
list && list.length > 0 && list.map((i,k)=>{
|
||||
return(
|
||||
<li>
|
||||
<div>
|
||||
<Link to={`/zone/${deptId}/${source?"source":"newdetail"}/${i.id}`} className="s-name task-hide">{i.name}</Link>
|
||||
<div className="s-info">
|
||||
<span>
|
||||
<span>{i.domainName || i.dirName}</span>
|
||||
<span className="ml10 mr15">|</span>
|
||||
<span><i className="iconfont icon-a-31shijian mr5 font-15"></i>{moment(i.createTime).format("YYYY/MM/DD")}</span>
|
||||
</span>
|
||||
<span className="s-info-right">
|
||||
<a href={`/zone/${deptId}/${source ? 'source' : 'news'}/${i.id}/edit`} style={{color:"#466aff"}}><i className="iconfont icon-a-bianji12 mr5 font-14"></i>编辑</a>
|
||||
<a style={{color:"#d81017",marginLeft:"25px"}}onClick={()=>{setVisible(true);setDeleId(i.id)}}><i className="iconfont icon-fuzhi-shanchu mr5 font-14"></i>删除</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
{
|
||||
total > limit &&
|
||||
<div style={{padding:"20px",textAlign:"right"}}>
|
||||
<Pagination showQuickJumper current={page} total={total} pageSize={limit} onChange={(p)=>{setPage(p);window.scrollTo(0,450)}}/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</Spin>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default SelfList;
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import React , { useEffect , useState } from 'react';
|
||||
import { Input , Pagination, Spin, Tag } from 'antd';
|
||||
import { Button, Input , Pagination, Spin, Tag } from 'antd';
|
||||
import SourceIcon from '../img/sourceIcon.png';
|
||||
import { Link } from 'react-router-dom';
|
||||
import axios from 'axios';
|
||||
|
|
@ -23,8 +23,7 @@ function Source(props){
|
|||
const [ search , setSearch ] = useState(undefined);
|
||||
const [ isSpin , setIsSpin ] = useState(true);
|
||||
const limit = 20;
|
||||
const { id , temp } = props;
|
||||
|
||||
const { id , temp, history, role } = props;
|
||||
|
||||
useEffect(()=>{
|
||||
id && getLists();
|
||||
|
|
@ -70,12 +69,20 @@ function Source(props){
|
|||
setPage(1);
|
||||
}
|
||||
|
||||
function addSource(){
|
||||
if(role && role.role === "Manager"){
|
||||
window.open(role.resourceManageUrl);
|
||||
}else{
|
||||
history.push(`/zone/${deptId}/source/add`)
|
||||
}
|
||||
}
|
||||
|
||||
return(
|
||||
<div className={`${temp}_source_box`}>
|
||||
<div className="source_h">
|
||||
<p className="in_title">资源发布</p>
|
||||
<div style={{marginTop:"30px",textAlign:"center"}}>
|
||||
<Search
|
||||
<div style={{marginTop:"30px",textAlign:"center"}} className='titleButBox'>
|
||||
<Search
|
||||
placeholder="请输入资源名称"
|
||||
onSearch={onSourceSearch}
|
||||
style={{width:'645px',borderColor:"#fff",boxShadow:"0px 0px 10px rgba(61, 85, 183, 0.1)"}}
|
||||
|
|
@ -84,6 +91,12 @@ function Source(props){
|
|||
allowClear
|
||||
enterButton={temp === "zone" ? <span><i className="iconfont icon-sousuo5 font-15"/>搜索</span> :undefined}
|
||||
/>
|
||||
{role && role.role !== "None" && <div className='createButs'>
|
||||
{role.role === "Member" && <Button size='large' type="primary" ghost className='mr20'>
|
||||
<Link to={`/zone/${deptId}/source/self`}>我的资源</Link>
|
||||
</Button>}
|
||||
<Button size='large' icon={role.role === "Member" && 'plus'} type="primary" ghost onClick={addSource}>{role.role === "Member" ? '新建' : '管理资源'}</Button>
|
||||
</div>}
|
||||
</div>
|
||||
</div>
|
||||
<div className="sources">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,241 @@
|
|||
import React,{ useState , useEffect } from 'react';
|
||||
import { Breadcrumb, Button, Form, Icon, Input, message, Select, Upload } from 'antd';
|
||||
import { getZoneUrl } from 'educoder';
|
||||
import '../index.scss';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { getTypeByFileId, getSourceZoneList, addResource, getSourceDetailByAu, updateMyResource } from '../api';
|
||||
|
||||
function SourceCreate(props){
|
||||
const {history, id, role, current_user} = props;
|
||||
const { getFieldDecorator, validateFields , setFieldsValue, setFields } = props.form;
|
||||
const { deptId, sourceid} = props.match.params;
|
||||
const pathname = props.location.pathname;
|
||||
const [ zoneList , setZoneList ] = useState(undefined);
|
||||
const [files, setFiles] = useState([]);
|
||||
const [loadingBut, setLoadingBut] = useState(false);
|
||||
const [isEdit, setIsEdit] = useState(false);
|
||||
|
||||
const layout = {
|
||||
labelCol: { span: 2 },
|
||||
wrapperCol: { span: 14 },
|
||||
};
|
||||
|
||||
useEffect(()=>{
|
||||
if((role && role.role === "None") || (current_user && !current_user.login)){
|
||||
history.push(`/zone/${deptId}`);
|
||||
}
|
||||
}, [role])
|
||||
|
||||
useEffect(()=>{
|
||||
id && getZoneList();
|
||||
},[id])
|
||||
|
||||
useEffect(()=>{
|
||||
if(pathname){
|
||||
if(pathname.endsWith("/edit")){
|
||||
setIsEdit(true);
|
||||
getSourceDetail();
|
||||
}else{
|
||||
setIsEdit(false);
|
||||
}
|
||||
}
|
||||
},[pathname])
|
||||
|
||||
function getSourceDetail() {
|
||||
getSourceDetailByAu(sourceid).then(res=>{
|
||||
if(res && res.data){
|
||||
const {name, domainId, summary, fileList} = res.data.data;
|
||||
const fileMap = fileList.map(item=>{
|
||||
const {fileId, fileSizeInfo, fileOriginName, zoneResourceType: {id, name}} = item;
|
||||
return {
|
||||
uid: fileId,
|
||||
status: "done",
|
||||
response: {fileId: fileId, fileSize: fileSizeInfo},
|
||||
name: fileOriginName,
|
||||
zoneResourceType:{id, name}
|
||||
}
|
||||
})
|
||||
setFiles(fileMap);
|
||||
setFieldsValue({
|
||||
name,
|
||||
domainId,
|
||||
summary,
|
||||
fileList: fileMap
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getZoneList(){
|
||||
getSourceZoneList(id).then(response=>{
|
||||
if(response && response.data){
|
||||
setZoneList(response.data.rows);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
// 新建/编辑资源
|
||||
function submit(e){
|
||||
e.preventDefault();
|
||||
validateFields((err, fieldsValue)=>{
|
||||
if(err || !files.length){
|
||||
!files.length && setFields({fileList: {value:undefined,errors:[new Error('请上传资源附件!')]}});
|
||||
return;
|
||||
}
|
||||
const fileIds = files.map(item=>{return item.response.fileId}).toString();
|
||||
const params = {
|
||||
...fieldsValue,
|
||||
zoneId: id,
|
||||
fileIds
|
||||
}
|
||||
delete params.fileList;
|
||||
if(isEdit){
|
||||
delete params.zoneId;
|
||||
params.id= parseInt(sourceid, 10);
|
||||
updateMyResource(params).then((res)=>{
|
||||
const {data:{code, msg}} = res;
|
||||
if(code === 200){
|
||||
message.success("提交成功");
|
||||
history.push(`/zone/${deptId}/source/${sourceid}`);
|
||||
}else{
|
||||
message.error(msg || '提交失败,请联系管理员!')
|
||||
}
|
||||
})
|
||||
return;
|
||||
}
|
||||
addResource(params).then((res)=>{
|
||||
const {data:{code, msg, data}} = res;
|
||||
if(code === 200){
|
||||
message.success("提交成功");
|
||||
history.push(`/zone/${deptId}/source/${data.id}`);
|
||||
}else{
|
||||
message.error(msg || '提交失败,请联系管理员!')
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function beforeUpload(file){
|
||||
if (file.size > 200 * 1024 * 1024) {
|
||||
message.error('单个文件限制200MB以内');
|
||||
return false;
|
||||
}
|
||||
if(files){
|
||||
const sameNameFIles = files.filter(item=>item.name === file.name);
|
||||
if(sameNameFIles.length >= 1){
|
||||
message.error("请不要重复上传同名文件!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async function onChange({file, fileList: fileListByUpload}){
|
||||
if(file && (file.status === "done" || file.status === 'uploading' || file.status === 'removed')){
|
||||
setLoadingBut(true);
|
||||
const fileList = [...files];
|
||||
const index = fileList.findIndex(item=>item.uid === file.uid);
|
||||
if(index !== -1){
|
||||
fileList[index] = file;
|
||||
}else{
|
||||
fileList.push(file);
|
||||
}
|
||||
setFiles(fileList);
|
||||
if(!file.response) return
|
||||
if(file.response.code !== 200){
|
||||
message.error(file.response.msg || '上传失败,请联系管理员!')
|
||||
fileList.pop()
|
||||
setFiles([...fileList]);
|
||||
setLoadingBut(false);
|
||||
}else{
|
||||
// 获取此附件的资源类别
|
||||
await getTypeByFileId(id, file.response.fileId).then((res)=>{
|
||||
const {data:{code, data: data1, msg}} = res;
|
||||
if(code !== 200){
|
||||
return message.error(msg);
|
||||
}
|
||||
fileList[fileList.length-1].zoneResourceType = {
|
||||
id: data1.id,
|
||||
name: data1.name
|
||||
}
|
||||
})
|
||||
setFiles([...fileList]);
|
||||
setLoadingBut(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function deleteFile(uid){
|
||||
const fileList = files.filter(item=>item.uid !== uid);
|
||||
setFiles(fileList);
|
||||
}
|
||||
|
||||
return(
|
||||
<div className='boxmain pt30 newsCreateBox pb100 sourceCreateBox'>
|
||||
<Breadcrumb separator=">">
|
||||
<Breadcrumb.Item><Link className="primaryColor" to={`/zone/${deptId}/source`}>资源列表</Link></Breadcrumb.Item>
|
||||
<Breadcrumb.Item>{isEdit ? '编辑' : '新建'}资源</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
<div className='formBox'>
|
||||
<Form {...layout} onSubmit={submit}>
|
||||
<Form.Item label="资源名称">
|
||||
{getFieldDecorator("name", {
|
||||
rules:[{ required:true,message:"请输入资源名称!" },
|
||||
{type: 'string', max: 200, message: "最大长度200"}]
|
||||
})(
|
||||
<Input placeholder="请输入资源名称" maxLength={ 200 } width="220px"/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="资源领域">
|
||||
{getFieldDecorator("domainId", {
|
||||
rules:[{ required:true,message:"请选择资源领域!" }]
|
||||
})(
|
||||
<Select placeholder="请选择资源领域">
|
||||
{zoneList && zoneList.map(item =>{
|
||||
return <Select.Option key={item.id} value={item.id}>{item.name}</Select.Option>
|
||||
})}
|
||||
</Select>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="资源简介">
|
||||
{getFieldDecorator("summary", {
|
||||
rules:[{ required:true,message:"请输入资源简介!" },
|
||||
{type: 'string', max: 300, message: "最大长度300"}]
|
||||
})(
|
||||
<Input.TextArea placeholder="请输入资源简介" maxLength={ 200 } width="220px"/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="资源附件" extra={ <span >可上传多个资源文件,单个文件限制200MB以内</span> }>
|
||||
{getFieldDecorator("fileList", {
|
||||
rules:[{ required:true,message:"请上传资源附件!" }]
|
||||
})(
|
||||
<Upload action={getZoneUrl(`/api/file/common/upload?type=resource`)} showUploadList={false} beforeUpload={beforeUpload} onChange={onChange} withCredentials={true} headers={{Authorization: "3e33a5b0a35824f93666b1084488f0bd5f010025"}}>
|
||||
<Button className='hoverThemeColorBut'>
|
||||
<Icon type="upload" />单击上传
|
||||
</Button>
|
||||
</Upload>
|
||||
)}
|
||||
</Form.Item>
|
||||
{/* 附件展示 */}
|
||||
<div className="sourceFilesBox">
|
||||
{files && files.map((item, index)=>{
|
||||
const {uid, status, response, name, zoneResourceType} = item;
|
||||
return <div key={index} className="sourseFileItem mb15">
|
||||
{status === "uploading" ? <Icon type="loading" className='mr10'/> : <Icon type="file" className='mr10'/>}
|
||||
<span className="fileName task-hide">{name}</span>
|
||||
<span className='mr20 ml20' style={{width: '100px'}}>{response && response.fileSize}</span>
|
||||
<span className='mr50'>{zoneResourceType && zoneResourceType.name}</span>
|
||||
<Icon type="delete" onClick={()=>{deleteFile(uid)}}/>
|
||||
</div>
|
||||
})}
|
||||
</div>
|
||||
<Form.Item wrapperCol={{ offset: 2 }}>
|
||||
<Button className='mr20 themeBut' onClick={()=>{history.go(-1)}}>取消</Button>
|
||||
<Button type="primary" htmlType="submit" loading={loadingBut} className='themeBut'>提交</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Form.create()(SourceCreate);
|
||||
|
|
@ -1,19 +1,19 @@
|
|||
import React , { useEffect , useState } from 'react';
|
||||
import { Breadcrumb } from 'antd';
|
||||
import { Badge, Breadcrumb } from 'antd';
|
||||
import SourceFile from '../img/sourceFile.png';
|
||||
import RenderHtml from '../../../components/render-html';
|
||||
import { getSourceDetail } from '../api';
|
||||
import { Link } from 'react-router-dom';
|
||||
import axios from 'axios';
|
||||
import { Base64 } from 'js-base64';
|
||||
import { httpUrl } from '../fetch';
|
||||
import { AlignCenter, FlexAJ } from '../../Component/layout';
|
||||
import SourceIcon from '../img/sourceIcon.png';
|
||||
import { addMeta, setSeoMeta } from 'educoder';
|
||||
|
||||
|
||||
function SourceDetail(props){
|
||||
const { deptId , sourceid } = props.match.params;
|
||||
const [ detail , setDetail ] = useState(undefined);
|
||||
const zonedetail = props.data;
|
||||
const {temp} = props;
|
||||
|
||||
useEffect(()=>{
|
||||
if(sourceid){
|
||||
|
|
@ -21,6 +21,23 @@ function SourceDetail(props){
|
|||
}
|
||||
},[sourceid])
|
||||
|
||||
useEffect(()=>{
|
||||
// 设置网页标题
|
||||
if(detail){
|
||||
const { name, domainName, summary} = detail;
|
||||
document.title = `${ name }/${ domainName }`;
|
||||
addMeta('Keywords', `${ name },${ domainName },${ summary }`);
|
||||
}
|
||||
return componentWillUnmount
|
||||
}, [detail])
|
||||
|
||||
function componentWillUnmount() {
|
||||
if (zonedetail) {
|
||||
document.title= zonedetail.name;
|
||||
setSeoMeta(`${zonedetail.name},`, zonedetail.name, zonedetail.subTitle, `/zone/${deptId}`)
|
||||
}
|
||||
}
|
||||
|
||||
function getDetails(){
|
||||
getSourceDetail(sourceid).then(response=>{
|
||||
if(response){
|
||||
|
|
@ -50,7 +67,9 @@ function SourceDetail(props){
|
|||
</FlexAJ>
|
||||
{detail.createUser && detail.createUser.avatar && <Link to={`/${detail.createUser.userName}`}><img alt='' src={detail.createUser.avatar} className='publicUserAvatar mr10'/></Link>}
|
||||
{detail.createUser && <Link className='mr20' to={`/${detail.createUser.userName}`}>{detail.createUser.nickName}</Link>}
|
||||
<span>发布于{detail.updateTime || detail.createTime}</span>
|
||||
<span className='mr20'>发布于{detail.updateTime || detail.createTime}</span>
|
||||
{detail.auditStatus === '0' && <Badge color={temp === "zone" ? "#466aff" : "#089f7f"} text="待审核" className='pass'/> }
|
||||
{detail.auditStatus === '2' && <Badge color="#eb1212" text="未通过" className='failed'/> }
|
||||
</div>
|
||||
<div className='pb20'>{detail.summary}</div>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { tempConfig, tempEnum } from '../tempInfo'
|
|||
import MemberApply from '../Component/memberApply';
|
||||
import { getVIPLists, getAuditStatus, applyJoin } from '../api';
|
||||
import nodata from '../img/nodata.png';
|
||||
import MemberList from '../Component/memberList';
|
||||
|
||||
import '../indexZone1.scss';
|
||||
|
||||
|
|
@ -43,7 +44,6 @@ function ZoneVIP(props){
|
|||
|
||||
function getMemberStatus() {
|
||||
getAuditStatus(id).then(res => {
|
||||
console.log(res)
|
||||
if (res && res.data && res.data.code === 200) {
|
||||
setMemberStatus(res.data.data)
|
||||
} else {
|
||||
|
|
@ -83,58 +83,9 @@ function ZoneVIP(props){
|
|||
<Spin spinning={isSpin}>
|
||||
<div style={{minHeight:400}}>
|
||||
{
|
||||
vipLists && vipLists.length> 0 &&
|
||||
<div className="vip_list">
|
||||
{
|
||||
vipLists.map((i,k)=>{
|
||||
return(
|
||||
<div className="vip_list_card">
|
||||
<div className="card_title">
|
||||
<img src={Crown} alt="" width="37px" style={{marginBottom:5,marginRight:5}}/>
|
||||
<div>
|
||||
<div>
|
||||
{i.typeName}
|
||||
</div>
|
||||
{ temp === tempEnum.zone1 && <div className="linear_gradient">{i.typeName}</div> }
|
||||
</div>
|
||||
</div>
|
||||
<p className="card_desc">{i.typeIntroduction}</p>
|
||||
{
|
||||
i.zoneMemberList && i.zoneMemberList.length > 0 ?
|
||||
<ul className="card_ul">
|
||||
{
|
||||
i.zoneMemberList.map((j,key)=>{
|
||||
return(
|
||||
|
||||
<li className="card_u_li">
|
||||
<div className="card_u_wrap">
|
||||
<Link to={`/${j.login}`} ><img src={j.imageUrl} alt="" /></Link>
|
||||
<div className="card_u_info">
|
||||
<div className="card_u_up"><span className="card_name task-hide">{j.name}</span>{j.memberLevel && <span className="card_tag">{j.memberLevel}</span> }</div>
|
||||
<p className="card_u_down task-hide-2">{j.introduction}</p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
:
|
||||
(
|
||||
temp === tempEnum.zone1 ? <Nodata _html="暂无数据" img={ nodata }/> : <Nodata _html="暂无数据" />
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
}
|
||||
{
|
||||
vipLists && vipLists.length === 0 &&
|
||||
<div style={{marginTop:30,backgroundColor:"#fff",padding:"20px"}}>
|
||||
vipLists && (vipLists.length > 0 ? <MemberList vipLists={vipLists} temp={temp}/> : <div style={{marginTop:30,backgroundColor:"#fff",padding:"20px"}}>
|
||||
<Nodata _html={"暂无数据"}/>
|
||||
</div>
|
||||
</div>)
|
||||
}
|
||||
</div>
|
||||
</Spin>
|
||||
|
|
|
|||
|
|
@ -17,6 +17,13 @@ export function getCheckZoneRole(id) {
|
|||
});
|
||||
}
|
||||
|
||||
export function getCurrentRole(id) {
|
||||
return fetch({
|
||||
url: `/zone/zoneFront/${id}/checkCurrentRole`,
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
/********首页接口******/
|
||||
export function getHomePageList(id) {
|
||||
return fetch({
|
||||
|
|
@ -60,6 +67,19 @@ export function getNewsDetail(id){
|
|||
method: 'get',
|
||||
})
|
||||
}
|
||||
export function getNewsMyList(id,params){
|
||||
return fetch({
|
||||
url:`/cms/doc/zone/${id}/myDocList`,
|
||||
method:"get",
|
||||
params
|
||||
})
|
||||
}
|
||||
export function delNewsMyList(id){
|
||||
return fetch({
|
||||
url:`/cms/doc/${id}`,
|
||||
method:"delete"
|
||||
})
|
||||
}
|
||||
|
||||
/**开源项目 */
|
||||
export function getProjectsLists(id,params){
|
||||
|
|
@ -76,10 +96,11 @@ export function getProjectsTypeLists(id){
|
|||
})
|
||||
}
|
||||
/**专区会员 */
|
||||
export function getVIPLists(id){
|
||||
export function getVIPLists(id, params){
|
||||
return fetch({
|
||||
url:`/zone/open/${id}/member/overviewList`,
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -101,6 +122,13 @@ export function applyJoin(id,data) {
|
|||
}
|
||||
|
||||
// 资源模块
|
||||
export function addResource(data) {
|
||||
return fetch({
|
||||
url: `/zone/zoneFront/addResource`,
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
export function getSourceTypeList(id){
|
||||
return fetch({
|
||||
url:`/zone/open/${id}/resourceType/list`,
|
||||
|
|
@ -125,4 +153,103 @@ export function getSourceDetail(id){
|
|||
url:`/zone/open/resource/detail/${id}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
export function getSourceMyList(params){
|
||||
return fetch({
|
||||
url:`/zone/zoneFront/myResourceList`,
|
||||
method:"get",
|
||||
params
|
||||
})
|
||||
}
|
||||
export function delSourceMyList(id){
|
||||
return fetch({
|
||||
url:`/zone/zoneFront/removeResource/${id}`,
|
||||
method:"delete"
|
||||
})
|
||||
}
|
||||
|
||||
// 创建专区
|
||||
export function postCreateZone(data) {
|
||||
return fetch({
|
||||
url: `/zone/application`,
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 根据文件id获取专区下资源类别
|
||||
export function getTypeByFileId(zoneId, fileId){
|
||||
return fetch({
|
||||
url:`/zone/resourceType/zone/${zoneId}/getResourceTypeByFileId/${fileId}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增领域
|
||||
export function addSourceType(data) {
|
||||
return fetch({
|
||||
url: `/zone/resourceType`,
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 修改当前文件对应的资源类别
|
||||
export function updateResourceTypeByFileId(data) {
|
||||
return fetch({
|
||||
url: '/zone/resourceType/updateResourceTypeByFileId',
|
||||
method: 'PUT',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 获取文章领域列表
|
||||
export function getDirListById(id){
|
||||
return fetch({
|
||||
url:`/cms/doc/open/zone/${id}/dirList`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增文章
|
||||
export function addNewsByDirId(dirId, data) {
|
||||
return fetch({
|
||||
url: `/cms/doc/dir/${dirId}`,
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 获取文章详细信息(需要用户权限)
|
||||
export function getNewsDetailByAu(id){
|
||||
return fetch({
|
||||
url:`/cms/doc/${id}`,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
// 修改文章详细信息(需要用户权限)
|
||||
export function updateDoc(id, data) {
|
||||
return fetch({
|
||||
url: `/cms/doc/${id}`,
|
||||
method: 'PUT',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 获取资源详细信息(需要用户权限)
|
||||
export function getSourceDetailByAu(id){
|
||||
return fetch({
|
||||
url:`/zone/zoneFront/myResource/${id}`,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
// 修改资源详细信息(需要用户权限)
|
||||
export function updateMyResource(data) {
|
||||
return fetch({
|
||||
url: `/zone/zoneFront/editResource`,
|
||||
method: 'PUT',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@ import { CNotificationHOC } from "../../modules/courses/common/CNotificationHOC"
|
|||
import { TPMIndexHOC } from "../../modules/tpm/TPMIndexHOC";
|
||||
import Loadable from "react-loadable";
|
||||
import Loading from "../../Loading";
|
||||
import { getMainInfos , getCheckZoneRole } from './api';
|
||||
import { getMainInfos , getCheckZoneRole, getCurrentRole } from './api';
|
||||
import PublicBanner from "./Component/publicBanner";
|
||||
import './index.scss';
|
||||
import { IsPC } from 'educoder';
|
||||
|
|
@ -16,19 +16,33 @@ import "slick-carousel/slick/slick.css";
|
|||
import "slick-carousel/slick/slick-theme.css";
|
||||
import { tempEnum } from "./tempInfo";
|
||||
import { ImageLayerOfCommentHOC } from "../../modules/page/layers/ImageLayerOfCommentHOC";
|
||||
import { setSeoMeta } from 'educoder';
|
||||
|
||||
|
||||
const SourceDetail = Loadable({
|
||||
loader: () => import("./Pages/sourceDetail"),
|
||||
loading: Loading,
|
||||
});
|
||||
const SourceCreate = Loadable({
|
||||
loader: () => import("./Pages/sourceCreate"),
|
||||
loading: Loading,
|
||||
});
|
||||
const VIP = Loadable({
|
||||
loader: () => import("./Pages/zoneVIP"),
|
||||
loading: Loading,
|
||||
});
|
||||
const Apply = Loadable({
|
||||
loader: () => import("./Pages/apply"),
|
||||
loading: Loading,
|
||||
});
|
||||
const Source = Loadable({
|
||||
loader: () => import("./Pages/source"),
|
||||
loading: Loading,
|
||||
});
|
||||
const SourceSelfList = Loadable({
|
||||
loader: () => import("./Pages/selfList"),
|
||||
loading: Loading,
|
||||
});
|
||||
const HeaderPage = Loadable({
|
||||
loader: () => import("./Pages/headerPage"),
|
||||
loading: Loading,
|
||||
|
|
@ -56,6 +70,10 @@ const NewsList = Loadable({
|
|||
loader: () => import("./Pages/newsList"),
|
||||
loading: Loading,
|
||||
});
|
||||
const NewsCreate = Loadable({
|
||||
loader: () => import("./Pages/newsCreate"),
|
||||
loading: Loading,
|
||||
});
|
||||
|
||||
const Main = Loadable({
|
||||
loader: () => import("./Pages/main"),
|
||||
|
|
@ -73,11 +91,13 @@ function Index(props){
|
|||
const { deptId } = props.match.params;
|
||||
const [ id , setId ] = useState(undefined);
|
||||
const [ temp , setTemp ] = useState(tempEnum.zone);
|
||||
const [ role , setRole ] = useState(undefined);
|
||||
const { pathname } = props.history.location;
|
||||
const sourcedetail = pathname.indexOf(`/zone/${deptId}/newdetail/`)>-1 && IsPC();
|
||||
const {current_user} = props;
|
||||
|
||||
useEffect(()=>{
|
||||
if(deptId){
|
||||
if(deptId && deptId!=="apply"){
|
||||
getDetail();
|
||||
}
|
||||
},[deptId])
|
||||
|
|
@ -107,7 +127,9 @@ function Index(props){
|
|||
setId(data.id);
|
||||
if(data.id){
|
||||
getAdminUrl(data.id);
|
||||
getRole(data.id)
|
||||
}
|
||||
setSeoMeta(`${data.name},`, data.name, data.subTitle, `/zone/${deptId}`)
|
||||
}
|
||||
}).catch(console.error())
|
||||
}
|
||||
|
|
@ -121,15 +143,41 @@ function Index(props){
|
|||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
console.log(deptId)
|
||||
|
||||
function getRole(id){
|
||||
if(current_user && current_user.login){
|
||||
getCurrentRole(id).then(res=>{
|
||||
setRole(res.data.data);
|
||||
}).catch(error=>{})
|
||||
}
|
||||
}
|
||||
|
||||
return(
|
||||
<div className="information_main">
|
||||
{ !sourcedetail && (id ? <PublicBanner {...props} data={data} temp={ temp } adminUrl={adminUrl}/> : <div style={{ width: '100%', height: '450px' }}></div>)}
|
||||
{ (!sourcedetail && deptId!=="apply") && (id ? <PublicBanner {...props} data={data} temp={ temp } adminUrl={adminUrl}/> : <div style={{ width: '100%', height: '450px' }}></div>)}
|
||||
<Switch>
|
||||
<Route
|
||||
path="/zone/:deptId/news/self"
|
||||
render={(p) => (
|
||||
<SourceSelfList {...props} {...p} id={id} temp={ temp } data={data}/>
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/zone/:deptId/news/add"
|
||||
render={(p) => (
|
||||
<NewsCreate {...props} {...p} id={id} temp={ temp } data={data} role={role}/>
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/zone/:deptId/news/:id/edit"
|
||||
render={(p) => (
|
||||
<NewsCreate {...props} {...p} id={id} temp={ temp } data={data} role={role}/>
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/zone/:deptId/news/:cateId"
|
||||
render={(p) => (
|
||||
<Main {...props} {...p} id={id} temp={ temp }/>
|
||||
<Main {...props} {...p} id={id} temp={ temp } role={role}/>
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
|
|
@ -138,16 +186,34 @@ function Index(props){
|
|||
<NewsDetail {...props} {...p} id={id} temp={ temp } data={data}/>
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/zone/:deptId/source/self"
|
||||
render={(p) => (
|
||||
<SourceSelfList {...props} {...p} id={id} temp={ temp } data={data}/>
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/zone/:deptId/source/add"
|
||||
render={(p) => (
|
||||
<SourceCreate {...props} {...p} id={id} temp={ temp } data={data} role={role}/>
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/zone/:deptId/source/:sourceid/edit"
|
||||
render={(p) => (
|
||||
<SourceCreate {...props} {...p} id={id} temp={ temp } role={role}/>
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/zone/:deptId/source/:sourceid"
|
||||
render={(p) => (
|
||||
<SourceDetail {...props} {...p} id={id} temp={ temp }/>
|
||||
<SourceDetail {...props} {...p} id={id} temp={ temp } data={data}/>
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/zone/:deptId/source"
|
||||
render={(p) => (
|
||||
<Source {...props} {...p} id={id} temp={ temp }/>
|
||||
<Source {...props} {...p} id={id} temp={ temp } role={role}/>
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
|
|
@ -165,7 +231,7 @@ function Index(props){
|
|||
<Route
|
||||
path="/zone/:deptId/news"
|
||||
render={(p) => (
|
||||
<Main {...props} {...p} id={id} temp={ temp }/>
|
||||
<Main {...props} {...p} id={id} temp={ temp } role={role}/>
|
||||
)}
|
||||
></Route>
|
||||
{data && data.helperShow === 1 && <Route
|
||||
|
|
@ -174,6 +240,12 @@ function Index(props){
|
|||
<Help {...props} {...p} id={id} temp={ temp } data={data} adminUrl={adminUrl}/>
|
||||
)}
|
||||
></Route>}
|
||||
<Route
|
||||
path="/zone/apply"
|
||||
render={(p) => (
|
||||
<Apply {...props} {...p} id={id} temp={ temp }/>
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/zone/:deptId"
|
||||
render={(p) => (
|
||||
|
|
|
|||
|
|
@ -15,6 +15,26 @@
|
|||
}
|
||||
}
|
||||
|
||||
// 公共样式
|
||||
.themeBut{
|
||||
border-color: var(--primary-color);
|
||||
color: var(--primary-color);
|
||||
&:hover, &:active, &:focus{
|
||||
border-color: var(--light-color);
|
||||
color: var(--light-color);
|
||||
}
|
||||
&.ant-btn-primary{
|
||||
color: white;
|
||||
background-color: var(--primary-color);
|
||||
}
|
||||
}
|
||||
.hoverThemeColorBut{
|
||||
&:hover, &:active, &:focus{
|
||||
border-color: var(--light-color);
|
||||
color: var(--light-color);
|
||||
}
|
||||
}
|
||||
|
||||
.information_main{
|
||||
background-color:#f3f5f8;
|
||||
min-height: 100vh;
|
||||
|
|
@ -554,7 +574,7 @@
|
|||
align-items: center;
|
||||
height: 20px;
|
||||
overflow: visible;
|
||||
color: #466aff;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
.headimg {
|
||||
width: 26px;
|
||||
|
|
@ -1521,6 +1541,211 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
// 新增文章/资源
|
||||
.titleButBox{
|
||||
width: 1200px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
.createButs{
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
button{
|
||||
border-color: var(--primary-color);
|
||||
color: var(--primary-color);
|
||||
&:hover, &:active, &:focus{
|
||||
border-color: var(--light-color);
|
||||
color: var(--light-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 新增文章
|
||||
.newsCreateBox{
|
||||
.formBox{
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
padding: 30px 35px 50px;
|
||||
}
|
||||
}
|
||||
.editNewsMd .editormd-dialog-header{
|
||||
padding: 5px 20px;
|
||||
}
|
||||
.editNewsMd .number-input{
|
||||
width: 60px !important;
|
||||
}
|
||||
// 新增资源
|
||||
.sourceCreateBox{
|
||||
.sourceFilesBox{
|
||||
margin-left: 93px;
|
||||
}
|
||||
.sourseFileItem{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.fileName{
|
||||
max-width: 400px;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
.primaryColor{
|
||||
color: var(--primary-color)!important;
|
||||
}
|
||||
}
|
||||
|
||||
.failed .ant-badge-status-text{
|
||||
color: #eb1212;
|
||||
}
|
||||
.pass .ant-badge-status-text{
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
// --------------新建专区
|
||||
.applyBox{
|
||||
background-color: #fff;
|
||||
min-height: 100vh;
|
||||
.applyContent{
|
||||
width: 1200px;
|
||||
margin:0px auto;
|
||||
padding:7px 93.5px 50px;
|
||||
.apply-t{
|
||||
padding:18px 0px;
|
||||
border-bottom: 1px solid #e0e6f5;
|
||||
font-weight:700;
|
||||
color:#151d40;
|
||||
font-size:18px;
|
||||
margin-bottom: 30px!important;
|
||||
}
|
||||
.apply-tips{
|
||||
background:rgba(237, 242, 252, 0.72);
|
||||
border:2px solid rgba(70, 106, 255, 0.1);
|
||||
padding:20px;
|
||||
font-size:15px;
|
||||
margin-bottom: 20px;
|
||||
p{
|
||||
color: #424650;
|
||||
margin-top: 14px;
|
||||
line-height: 24px;
|
||||
}
|
||||
h5{
|
||||
color:#000000;
|
||||
font-size:15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.applyForm{
|
||||
.ant-form-explain{
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
}
|
||||
.selfBoxContent{
|
||||
width: 1200px;
|
||||
margin:0px auto;
|
||||
padding-bottom: 40px;
|
||||
border-radius: 4px;
|
||||
.selfMenu{
|
||||
width: 540px;
|
||||
margin:20px auto 25px;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
border-radius: 4px;
|
||||
box-shadow:0px 0px 10px rgba(61, 85, 183, 0.1);
|
||||
li{
|
||||
flex: 1;
|
||||
border-right: 1px solid rgba(141, 149, 172, 0.17);
|
||||
font-size:17px;
|
||||
text-align:center;
|
||||
color:#3d485d;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
cursor: pointer;
|
||||
&:first-child{
|
||||
border-radius: 4px 0px 0px 4px;
|
||||
}
|
||||
&:last-child{
|
||||
border-right: none;
|
||||
border-radius: 0px 4px 4px 0px;
|
||||
}
|
||||
&.active{
|
||||
background-color: var(--primary-color);
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
.selfListPanel{
|
||||
padding:17px 0px;
|
||||
background-color: #fff;
|
||||
margin-top: 26px;
|
||||
min-height: 400px;
|
||||
li{
|
||||
padding:0px 25px;
|
||||
&:hover{
|
||||
background-color: #f3f5f8;
|
||||
.s-info-right{
|
||||
display: block!important;
|
||||
}
|
||||
}
|
||||
&>div{
|
||||
border-bottom: 1px dashed rgba(170, 175, 190, 0.15);
|
||||
padding:15px 0px;
|
||||
.s-name{
|
||||
position: relative;
|
||||
color:#1f2329;
|
||||
font-size:16px;
|
||||
padding-left: 16px;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
display: block;
|
||||
&::before{
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
background-color: var(--primary-color);
|
||||
border-radius: 50%;
|
||||
top:8px;
|
||||
content: "";
|
||||
}
|
||||
&:hover{
|
||||
color: var(--primary-color);
|
||||
}
|
||||
}
|
||||
.s-info{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
margin-top: 14px;
|
||||
height: 20px;
|
||||
color:#4c5876;
|
||||
align-items: center;
|
||||
.s-info-right{
|
||||
display: none;
|
||||
transition: 0.3s;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.delSModal{
|
||||
.ant-modal-title{
|
||||
text-align: left;
|
||||
}
|
||||
.delBoxCon{
|
||||
width:334px;
|
||||
display:flex;
|
||||
margin:0px auto 20px;
|
||||
align-items:flex-start;
|
||||
}
|
||||
.delBoxBtn{
|
||||
text-align: center;
|
||||
.ant-btn{
|
||||
width: 95px;
|
||||
margin:0px 20px;
|
||||
&.ant-btn-primary{
|
||||
color: var(--primary-color);
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,19 +20,17 @@
|
|||
|
||||
div {
|
||||
height: 100%;
|
||||
|
||||
.regform {
|
||||
img {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
.newsBannerBox{
|
||||
height: 595px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
}
|
||||
.newsImg{
|
||||
width: 785px;
|
||||
height: 590px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -82,7 +80,7 @@
|
|||
text-align: left;
|
||||
left: 50%;
|
||||
margin-left: -115px;
|
||||
bottom: 20%;
|
||||
bottom: 12%;
|
||||
position: absolute;
|
||||
display: flex !important;
|
||||
z-index: 2;
|
||||
|
|
@ -158,7 +156,7 @@
|
|||
li {
|
||||
background-color: #fff;
|
||||
width: 385px;
|
||||
padding: 20px !important;
|
||||
padding: 22px 20px 19px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 25px !important;
|
||||
position: relative;
|
||||
|
|
@ -260,4 +258,12 @@
|
|||
font-size: 32px;
|
||||
text-align: center;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.memberLevel_tag{
|
||||
padding: 0px 5px;
|
||||
line-height: 25px;
|
||||
display: inline-block;
|
||||
border: 1px solid var(--primary-color);
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
|
@ -53,6 +53,12 @@ function AllMenus({owner,projectsId,chooseFunc,update,defaultNames,defaultIds,op
|
|||
// 父组件按钮:清除筛选条件,将Ids\names里的字段全部改为undefined
|
||||
setIds({author_id:undefined,issue_tag_ids:undefined,issue_priorities_id:undefined,milestone_id:undefined,sort_by:undefined,status_id:undefined,assigner_id:undefined});
|
||||
setNames({author_name:undefined,issue_tag_name:undefined,issue_priorities_name:undefined,milestone_name:undefined,sortby_name:undefined,status_name:undefined,assigner_name:undefined});
|
||||
},
|
||||
setChooseTag: (info) => {
|
||||
let copy = {...ids,issue_tag_ids:[info.id].join(",")};
|
||||
let copyname = { ...names,issue_tag_name:info.name};
|
||||
setIds(copy);setNames(copyname);
|
||||
chooseFunc(copy,copyname);
|
||||
}
|
||||
}))
|
||||
// 获取发布人列表
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { Tooltip } from 'antd';
|
|||
|
||||
|
||||
// issue列表显示
|
||||
function Datas({checkbox ,item , projectsId,owner}){
|
||||
function Datas({checkbox ,item , projectsId,owner,chooseTagFunc}){
|
||||
|
||||
function statusTag(name){
|
||||
switch (name) {
|
||||
|
|
@ -40,7 +40,7 @@ function Datas({checkbox ,item , projectsId,owner}){
|
|||
item.tags && item.tags.length>0?
|
||||
item.tags.map((i,k)=>{
|
||||
return(
|
||||
<span style={{backgroundColor: `${i.color}`}} className="ml8 tagscolor task-hide" title={i.name}>{i.name}</span>
|
||||
<span onClick={()=>chooseTagFunc(i)} style={{backgroundColor: `${i.color}`,cursor:"pointer"}} className="ml8 tagscolor task-hide" title={i.name}>{i.name}</span>
|
||||
)
|
||||
})
|
||||
:""
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
|
|
@ -340,6 +340,10 @@ function List(props){
|
|||
setBegin(value[0] || '');
|
||||
setEnd(value[1] || '');
|
||||
}
|
||||
|
||||
function chooseTagFunc(taginfo){
|
||||
menuRef.current && menuRef.current.setChooseTag(taginfo);
|
||||
}
|
||||
return(
|
||||
<div>
|
||||
<DelBox visible={visible} onCancel={()=>setVisible(false)} onSuccess={onSuccess}/>
|
||||
|
|
@ -439,6 +443,7 @@ function List(props){
|
|||
item={item}
|
||||
owner={owner}
|
||||
projectsId={projectsId}
|
||||
chooseTagFunc={chooseTagFunc}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -501,22 +501,30 @@ class Detail extends Component {
|
|||
|
||||
// 同步镜像
|
||||
synchronismMirror = () => {
|
||||
const { platform } = this.state;
|
||||
if (!platform) return;
|
||||
const { projectsId, owner } = this.props.match.params;
|
||||
const url = `/${owner}/${projectsId}/sync_mirror.json`;
|
||||
axios.post(url).then(result => {
|
||||
if (result && result.data && result.data.status === 0) {
|
||||
this.setState({
|
||||
secondSync: true
|
||||
})
|
||||
this.canvasChannel(true);
|
||||
} else {
|
||||
this.props.showNotification("镜像同步失败!");
|
||||
}
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
})
|
||||
const { isManager } = this.state;
|
||||
const { current_user , showLoginDialog } = this.props;
|
||||
if(!(current_user && current_user.login)){
|
||||
showLoginDialog();
|
||||
return
|
||||
}
|
||||
if((current_user && current_user.admin) || isManager){
|
||||
const { platform } = this.state;
|
||||
if (!platform) return;
|
||||
const { projectsId, owner } = this.props.match.params;
|
||||
const url = `/${owner}/${projectsId}/sync_mirror.json`;
|
||||
axios.post(url).then(result => {
|
||||
if (result && result.data && result.data.status === 0) {
|
||||
this.setState({
|
||||
secondSync: true
|
||||
})
|
||||
this.canvasChannel(true);
|
||||
} else {
|
||||
this.props.showNotification("镜像同步失败!");
|
||||
}
|
||||
}).catch(error => {
|
||||
console.log(error);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
textFunc = (forked_from_project_id, fork_info) => {
|
||||
|
|
@ -580,7 +588,7 @@ class Detail extends Component {
|
|||
firstSync ? "" :
|
||||
<span className="df">
|
||||
{
|
||||
((current_user && current_user.admin) || isManager) && (projectDetail && projectDetail.type && projectDetail.type === 2) ?
|
||||
(projectDetail && projectDetail.type && projectDetail.type === 2) ?
|
||||
<a className="synchronism ml30" onClick={this.synchronismMirror}>同步镜像</a> : ""
|
||||
}
|
||||
<span className="detail_tag_btn">
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import Datas from '../Issues/Component/datas';
|
|||
import axios from 'axios';
|
||||
import { FlexAJ } from '../Component/layout';
|
||||
import CheckProfile from '../Component/ProfileModal/Profile';
|
||||
import emp from '../../forge/Issues/Img/emp.png';
|
||||
import millestones from '../../forge/Issues/Img/millestones-big.png';
|
||||
import './milepost.scss';
|
||||
import './order.scss';
|
||||
import '../Issues/index.scss';
|
||||
|
|
@ -222,7 +222,12 @@ function MilepostDetail(props){
|
|||
</div>
|
||||
</div>
|
||||
{
|
||||
total === 0 && <div className="milestonesNoDate"><img src={emp} alt=""/></div>
|
||||
total === 0 &&
|
||||
<div className="listempty">
|
||||
<img src={millestones} style={{width:"68px"}} alt="" />
|
||||
<p className="font-22 mt5 mb10">欢迎使用里程碑</p>
|
||||
<p className="font-15">里程碑用于集中归类管理项目的疑修及进度。在使用之前,您可以先<CheckProfile {...props} checklogin sureFunc={()=>{props.history.push(`/${owner}/${projectsId}/issues/${mileId}/new`)}} className="color-blue">创建一个疑修</CheckProfile></p>
|
||||
</div>
|
||||
}
|
||||
{
|
||||
total > 0 &&
|
||||
|
|
|
|||
|
|
@ -70,24 +70,28 @@ const Mysite = Loadable({
|
|||
loader: () => import("./website/mySiteList"),
|
||||
loading: Loading,
|
||||
});
|
||||
const CreateSite = Loadable({
|
||||
loader: () => import("./website/createSite"),
|
||||
loading: Loading,
|
||||
});
|
||||
|
||||
function Index(props){
|
||||
const { current_user,mygetHelmetapi , checkIfLogin } = props;
|
||||
const { pathname } = props.location;
|
||||
const notice_url = mygetHelmetapi && mygetHelmetapi.common && mygetHelmetapi.common.notice;
|
||||
const {id_card_verify, website_permission} = current_user;
|
||||
|
||||
const [ avatarVisible , setAvatarVisible ] = useState(false);
|
||||
const [active, setActive] = useState(1);
|
||||
console.log('active', active);
|
||||
|
||||
useEffect(()=>{
|
||||
if(checkIfLogin() === false){
|
||||
props.history.push('/login');
|
||||
props.history.push(`/login?go_page=${pathname}`);
|
||||
}
|
||||
},[])
|
||||
|
||||
useEffect(()=>{
|
||||
console.log('pathname', pathname, ["/settings/profile"].indexOf(pathname));
|
||||
const pattern = /^\/settings\/installbot\/\d+$/;
|
||||
if(["/settings/profile"].indexOf(pathname) !== -1){
|
||||
setActive(1);
|
||||
}else if(["/settings/emails", "/settings/phone", "/settings/password", "/settings/cancel"].indexOf(pathname) !== -1){
|
||||
|
|
@ -100,11 +104,11 @@ function Index(props){
|
|||
setActive(5);
|
||||
}else if(["/settings/CLA"].indexOf(pathname) !== -1){
|
||||
setActive(6);
|
||||
}else if(["/settings/mybot"].indexOf(pathname) !== -1){
|
||||
}else if(["/settings/mybot", "/settings/mybot/new"].indexOf(pathname) !== -1){
|
||||
setActive(7);
|
||||
}else if(["/settings/installbot"].indexOf(pathname) !== -1){
|
||||
}else if(["/settings/installbot"].indexOf(pathname) !== -1 || pattern.test(pathname)){
|
||||
setActive(8);
|
||||
}else if(["/settings/mysite"].indexOf(pathname) !== -1){
|
||||
}else if(["/settings/mysite", "/settings/mysite/create"].indexOf(pathname) !== -1){
|
||||
setActive(9);
|
||||
}
|
||||
}, [pathname])
|
||||
|
|
@ -154,10 +158,10 @@ function Index(props){
|
|||
<li className={active === 7 ?"active":""}><Link to={`/settings/mybot`}><i className="iconfont icon-kaifabot mr5 font-18"></i><span className="text-shodow-bold">我的Bot</span></Link></li>
|
||||
<li className={active === 8 ?"active":""}><Link to={`/settings/installbot`}><i className="iconfont icon-BOTpeizhi mr5 font-17"></i><span className="text-shodow-bold">Bot安装</span></Link></li>
|
||||
</ul>
|
||||
{/* <ul className="securityUl">
|
||||
<ul className="securityUl">
|
||||
<li>个人建站</li>
|
||||
<li className={active === 9 ?"active":""}><Link to={`/settings/mysite`}><i className="iconfont icon-liebiaoicon mr5 font-15"></i><span className="text-shodow-bold">我的站点</span></Link></li>
|
||||
</ul> */}
|
||||
</ul>
|
||||
</div>
|
||||
<LongWidth>
|
||||
<Gap>
|
||||
|
|
@ -262,6 +266,13 @@ function Index(props){
|
|||
<Disposition {...props} {...p}/>
|
||||
)}
|
||||
></Route>
|
||||
{/* 创建站点 */}
|
||||
{id_card_verify && website_permission && <Route
|
||||
path="/settings/mysite/create"
|
||||
render={(p) => (
|
||||
<CreateSite {...props} {...p}/>
|
||||
)}
|
||||
></Route>}
|
||||
{/* 个人建站 */}
|
||||
<Route
|
||||
path="/settings/mysite"
|
||||
|
|
|
|||
|
|
@ -111,4 +111,9 @@
|
|||
border-color: rgba(230, 126, 34, 1);
|
||||
color: rgba(230, 126, 34, 1);
|
||||
}
|
||||
&.error{
|
||||
background-color: #fcbabe47;
|
||||
border-color: #f60011;
|
||||
color: #f60011;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
import React, { useState, useEffect, Fragment } from "react";
|
||||
import { Button, message, Form, Input, Select } from "antd";
|
||||
import "./index.scss";
|
||||
import "../../users/Material/Index.scss";
|
||||
import axios from "axios";
|
||||
import { AlignCenter, AlignTop } from "../../Component/layout";
|
||||
import { Link } from "react-router-dom";
|
||||
import { getImageUrl } from "../../../common/UrlTool";
|
||||
const { TextArea } = Input;
|
||||
|
||||
function CreateSite(props) {
|
||||
const { current_user, history, form, mygetHelmetapi:{site_page_deploy_domain} } = props;
|
||||
const { getFieldDecorator, validateFieldsAndScroll, setFieldsValue } = form;
|
||||
const [tag, setTag] = useState(undefined);
|
||||
const [language, setLanguage] = useState(undefined);
|
||||
const [themes, setThemes] = useState([]);
|
||||
const [theme, setTheme] = useState(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
document.title = "创建站点";
|
||||
}, []);
|
||||
|
||||
useEffect(()=>{
|
||||
axios.get(`/site_pages/themes.json`, {params: {language_frame: language}}).then(res=>{
|
||||
if(res && res.status === 200){
|
||||
setThemes(res.data.themes);
|
||||
setTheme(res.data.themes[0])
|
||||
}
|
||||
})
|
||||
}, [language])
|
||||
|
||||
useEffect(()=>{
|
||||
if(theme && theme.clone_url && theme && theme.clone_url.indexOf("/") > -1){
|
||||
let arr = theme && theme.clone_url.split("/");
|
||||
let first = arr[arr.length-1];
|
||||
if(first.indexOf(".") > -1){
|
||||
let second = first.split('.')[0];
|
||||
if(!second)return;
|
||||
setFieldsValue({
|
||||
repository_name:second,
|
||||
name:second
|
||||
})
|
||||
}else{
|
||||
setFieldsValue({
|
||||
repository_name:first
|
||||
})
|
||||
}
|
||||
}
|
||||
}, [theme])
|
||||
|
||||
function submit(e) {
|
||||
e.preventDefault();
|
||||
validateFieldsAndScroll((err, values) => {
|
||||
if (!err) {
|
||||
axios.post(`/projects/page_migrate.json`,{
|
||||
...values,
|
||||
clone_addr: theme && theme.clone_url,
|
||||
theme: theme && theme.name,
|
||||
user_id: current_user.user_id
|
||||
}).then(res=>{
|
||||
if(res && res.status === 200){
|
||||
message.success("新建成功");
|
||||
history.push(`/settings/mysite`);
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<div className="mySites_head mb30"><Link to={`/settings/mysite`} className="font-16">我的站点 / </Link><span>新建站点</span></div>
|
||||
<Form
|
||||
form={form}
|
||||
name="register"
|
||||
className="createSiteForm"
|
||||
scrollToFirstError
|
||||
layout="horizontal"
|
||||
labelCol={{ span: 3 }}
|
||||
wrapperCol={{ span: 16 }}
|
||||
onSubmit={submit}
|
||||
>
|
||||
<div className="formTitle font-16 mb20 ml20">站点配置</div>
|
||||
<Form.Item name="站点名称" label="站点名称">
|
||||
{getFieldDecorator("site_name", {
|
||||
rules: [{ required: true, message: "请输入站点名称" },
|
||||
{type: 'string', max: 50, min: 1, message: "长度1-50"}],
|
||||
})(<Input placeholder="请输入站点名称" />)}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="站点标识"
|
||||
label="站点标识"
|
||||
className="Create-Form-biaoshi"
|
||||
>
|
||||
{getFieldDecorator("identifier", {
|
||||
rules: [{ required: true, message: "请输入站点标识" },
|
||||
{pattern: /^[a-zA-Z0-9]{2,100}$/, message: '长度2-100,只能包含数字和字母'}],
|
||||
})(
|
||||
<Input
|
||||
onChange={(e) => {
|
||||
setTag(e.target.value);
|
||||
}}
|
||||
placeholder="请输入站点标识"
|
||||
/>
|
||||
)}
|
||||
<span style={{wordBreak: 'break-all'}}>
|
||||
http://{current_user.login}.{site_page_deploy_domain}/{tag}
|
||||
</span>
|
||||
</Form.Item>
|
||||
<Form.Item name="建站工具" label="建站工具">
|
||||
{getFieldDecorator("language_frame", {initialValue: 0})(
|
||||
<Select style={{ width: 100 }} onChange={(value)=>{setLanguage(value)}}>
|
||||
<Select.Option value={0}>hugo</Select.Option>
|
||||
<Select.Option value={1}>jekyll</Select.Option>
|
||||
<Select.Option value={2}>hexo</Select.Option>
|
||||
</Select>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item name="主题选择" label="主题选择" wrapperCol={{span: 20}}>
|
||||
<AlignTop style={{flexWrap: 'wrap'}}>
|
||||
{themes && themes.map(item=>{
|
||||
const {image, name, clone_url} = item;
|
||||
return <div className="mr20 themeBox" onClick={()=>{setTheme(item)}}>
|
||||
<i className={`iconfont icon-wancheng ${theme && theme.clone_url === clone_url ? 'active' : ''}`}></i>
|
||||
<img src={getImageUrl(image)} alt="" width="125px" height="85px"/>
|
||||
<p className="task-hide" style={{maxWidth: '120px'}}>{name}</p>
|
||||
</div>
|
||||
})}
|
||||
</AlignTop>
|
||||
</Form.Item>
|
||||
<div className="formTitle font-16 mb20 ml20">项目配置</div>
|
||||
<div className="font-15 mb20 ml40">我们会为您生成一个项目,来管理您的个人主页,请配置您的项目信息</div>
|
||||
<AlignCenter style={{marginLeft: '58px'}}>
|
||||
<Form.Item name="拥有者" label="拥有者" style={{width: '260px'}} labelCol={{span: 6}}>
|
||||
<Input
|
||||
placeholder={current_user.username}
|
||||
disabled
|
||||
/>
|
||||
</Form.Item>
|
||||
{/* <span className="mb20 font-18">/</span> 0.87 */}
|
||||
<Form.Item name="项目标识" label="项目标识" style={{flex: '0.83'}} labelCol={{span: 4}}>
|
||||
{getFieldDecorator("repository_name", {
|
||||
rules: [{ required: true, message: "请输入项目标识" },
|
||||
{pattern: /^[a-zA-Z0-9][a-zA-Z0-9_.-]{2,100}[a-zA-Z0-9]$/, message: "长度2-100,只能包含数字、字母、下划线、中划线、英文句号,必须以数字和字母开头,不能以下划线/中划线/英文句号开头和结尾"}],
|
||||
})(
|
||||
<Input placeholder="请输入项目标识"/>
|
||||
)}
|
||||
</Form.Item>
|
||||
</AlignCenter>
|
||||
<Form.Item
|
||||
name="项目名称"
|
||||
label="项目名称"
|
||||
>
|
||||
{getFieldDecorator("name", {
|
||||
rules: [{ required: true, message: "请输入项目名称" },
|
||||
{type: 'string', max: 50, min: 1, message: "长度1-50"}],
|
||||
})(
|
||||
<Input placeholder="请输入项目名称"/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item name="项目简介" label="项目简介">
|
||||
{getFieldDecorator("description", {
|
||||
rules: [{type: 'string', max: 200, message: "长度200"}]
|
||||
})(
|
||||
<TextArea rows={4} />
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item style={{marginLeft: '123px'}} className="mt40">
|
||||
<Button type="primary" htmlType="submit" className="mr20">
|
||||
创建站点
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
history.push("/settings/mysite");
|
||||
}}
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
export default Form.create()(CreateSite);
|
||||
|
|
@ -1,8 +1,33 @@
|
|||
.default_head{
|
||||
.mySites_head{
|
||||
height: 65px;
|
||||
line-height: 64px;
|
||||
border-bottom: 1px solid rgba(224, 230, 245, 1);
|
||||
color:#151d40;
|
||||
font-size:17px;
|
||||
padding-bottom:20px;
|
||||
}
|
||||
.createSiteForm{
|
||||
.themeBox{
|
||||
text-align: center;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
.icon-wancheng{
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
top: -7px;
|
||||
color: #e5e3e0;
|
||||
&.active{
|
||||
color: #ffa13a;
|
||||
}
|
||||
}
|
||||
}
|
||||
.siteBox{
|
||||
padding: 15px 0;
|
||||
border-bottom: 1px dashed #e0e6f5;
|
||||
.siteName{
|
||||
color: #05101a;
|
||||
&:active, &:hover{
|
||||
color: $primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +1,88 @@
|
|||
import React, { useState, useEffect, Fragment } from "react";
|
||||
import { Button, Icon, message, Modal } from "antd";
|
||||
import { Button, Pagination, Spin } from "antd";
|
||||
import './index.scss';
|
||||
import '../../users/Material/Index.scss';
|
||||
import '../bot/exploit/index.scss'
|
||||
import axios from "axios";
|
||||
import Nodata from "../../Nodata";
|
||||
import { FlexAJ } from "../../Component/layout";
|
||||
|
||||
function MySiteList(props){
|
||||
const {current_user} = props;
|
||||
const {current_user, history} = props;
|
||||
const {id_card_verify, website_permission} = current_user;
|
||||
const [total, setTotal] = useState(0);
|
||||
const [pages, setPages] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [page, setPage] = useState(1);
|
||||
|
||||
useEffect(()=>{
|
||||
document.title='我的站点';
|
||||
window.scrollTo(0,0);
|
||||
}, [])
|
||||
|
||||
return <Fragment>
|
||||
<div className="default_head">我的站点</div>
|
||||
</Fragment>
|
||||
useEffect(()=>{
|
||||
if(id_card_verify){
|
||||
setLoading(true);
|
||||
// 获取站点列表
|
||||
axios.get(`/site_pages.json`, {params: {limit: 15, page: page}}).then(res=>{
|
||||
if(res && res.status === 200){
|
||||
const {total_count, pages} = res.data;
|
||||
setPages(pages);
|
||||
setTotal(total_count);
|
||||
}
|
||||
setLoading(false);
|
||||
})
|
||||
}
|
||||
}, [id_card_verify, page])
|
||||
|
||||
return <Spin spinning={loading}>
|
||||
<FlexAJ className="mySites_head">
|
||||
<span>我的站点</span>
|
||||
{id_card_verify && website_permission && <Button type="primary" onClick={()=>{history.push(`/settings/mysite/create`)}}>新建站点</Button>}
|
||||
</FlexAJ>
|
||||
{/* 站点权限被管理员关闭 */}
|
||||
{id_card_verify && !website_permission && <div className="tipsBox font-15 mt20">您的站点权限被锁定,请联系平台管理员。</div>}
|
||||
{/* 未通过实名认证 */}
|
||||
{!id_card_verify && <Fragment>
|
||||
<div className="tipsBox font-15 mt30">您尚未通过实名认证,无法使用此服务,如需使用,请先进行实名认证。</div>
|
||||
<Button type="primary" className="mt20" onClick={()=>{history.push(`/settings/verification`)}}>前往验证</Button>
|
||||
</Fragment>}
|
||||
|
||||
{/* 通过实名认证 */}
|
||||
{id_card_verify && <Fragment>
|
||||
{!loading && total === 0 ? <div className="mt30">
|
||||
<div style={{marginBottom: '130px'}}>个人建站是一个免费的静态网页托管服务,您可以使用 个人建站服务托管个人主页、项目站点等静态网页。</div>
|
||||
<Nodata _html="暂无数据"/>
|
||||
</div> : <div className="mt10">
|
||||
{pages && pages.map(item=>{
|
||||
const {owner, repo, url, state, state_description, build_state} = item
|
||||
return <FlexAJ>
|
||||
<div className="siteBox">
|
||||
<i className="iconfont icon-cangkuyuyanicon font-16 mr5 color-grey-6"></i>
|
||||
{state && build_state ? <a className={`font-16 siteName`} onClick={()=>{window.open(url)}}>{item.site_name}</a> : <span className={`font-16`}>{item.site_name}</span>}
|
||||
<span className={`statusBot ml20 font-12 ${state ? build_state!== null ? build_state ? "public" : "error" : "private" : "error"}`}>{state ? build_state!== null ? build_state ? "已部署" : "部署失败" : "未部署" : '被关闭'}</span>
|
||||
<div className="color-grey-6 mt8">
|
||||
<span className="">主题:</span> {item.theme}
|
||||
<span className="ml5 mr5 color-grey-ccc">|</span>
|
||||
建站工具:{item.language_frame}
|
||||
</div>
|
||||
{!state && <div className="color-grey-6 mt5">关闭原因:{state_description}</div>}
|
||||
</div>
|
||||
<Button className="themeCorBorBut" onClick={()=>{history.push(`/${owner}/${repo}/service/pages`)}}>去仓库</Button>
|
||||
</FlexAJ>
|
||||
})}
|
||||
<div className="mt50" style={{textAlign: 'center'}}>
|
||||
<Pagination
|
||||
simple
|
||||
current = {page}
|
||||
pageSize={15}
|
||||
onChange={(page)=>{setPage(page)}}
|
||||
total = {total}
|
||||
hideOnSinglePage
|
||||
></Pagination>
|
||||
</div>
|
||||
</div>}
|
||||
</Fragment>}
|
||||
</Spin>
|
||||
}
|
||||
export default MySiteList;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
import "./index.scss";
|
||||
import Loadable from 'react-loadable';
|
||||
|
|
@ -18,7 +18,22 @@ const Reposyncer = Loadable({
|
|||
loader: () => import('./reposyncer'),
|
||||
loading: Loading,
|
||||
})
|
||||
// 个人建站服务
|
||||
const Pages = Loadable({
|
||||
loader: () => import('./pages'),
|
||||
loading: Loading,
|
||||
})
|
||||
function ServerIndex(props){
|
||||
const { projectDetail } = props;
|
||||
const { owner , projectsId } = props.match.params;
|
||||
|
||||
useEffect(()=>{
|
||||
// 非项目协作者不可进入服务tab (3397)
|
||||
if(projectDetail && projectDetail.permission === ""){
|
||||
props.history.push(`/${owner}/${projectsId}`);
|
||||
}
|
||||
}, [projectDetail])
|
||||
|
||||
return(
|
||||
<div className="panels">
|
||||
<Switch {...props}>
|
||||
|
|
@ -32,6 +47,12 @@ function ServerIndex(props){
|
|||
() => (<Data {...props}/>)
|
||||
}
|
||||
></Route>
|
||||
{/* 个人建站服务 */}
|
||||
{projectDetail && projectDetail.author.type === "User" && <Route path="/:owner/:projectsId/service/pages"
|
||||
render={
|
||||
() => (<Pages {...props}/>)
|
||||
}
|
||||
></Route>}
|
||||
<Route path="/:owner/:projectsId/service"
|
||||
render={
|
||||
() => (<List {...props}/>)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ function Main(props){
|
|||
const { owner , projectsId } = props.match.params;
|
||||
const [ has_trace_user , setHas_trace_user ] = useState(false);
|
||||
|
||||
const { current_user , resetUserInfo, projectDetail, showNotification } = props;
|
||||
const { current_user , resetUserInfo, projectDetail, showNotification, isDeveloper, isManager, isReporter } = props;
|
||||
|
||||
useEffect(()=>{
|
||||
// 更新网页标题
|
||||
|
|
@ -64,6 +64,16 @@ function Main(props){
|
|||
<Link to={`/${owner}/${projectsId}/service/reposyncer`} className="btnhover">查看详情</Link>
|
||||
</span>
|
||||
</li> */}
|
||||
{projectDetail && projectDetail.author.type === "User" && <li>
|
||||
<span className="servername">
|
||||
<img src={require('./img/logo.png')} alt=""/>
|
||||
<a onClick={openDetail}>个人建站服务</a>
|
||||
</span>
|
||||
<p className="task-hide-2 serverdesc">支持Hugo、Jekyll、Hexo静态网站服务</p>
|
||||
<span className="serverbtn">
|
||||
<Link to={`/${owner}/${projectsId}/service/pages`} className="btnhover">查看详情</Link>
|
||||
</span>
|
||||
</li>}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,209 @@
|
|||
import React, { Fragment, useEffect, useState } from 'react';
|
||||
import { Spin, message, Button, Form, Input, Select, Descriptions, Alert } from 'antd';
|
||||
import axios from 'axios';
|
||||
import SelectBranch from '../../../forge/Branch/Select';
|
||||
import './index.scss';
|
||||
|
||||
function Pages(props) {
|
||||
const {current_user, history, form, projectDetail, defaultBranch, isManager} = props;
|
||||
const { owner , projectsId } = props.match.params;
|
||||
const {id_card_verify} = current_user;
|
||||
const { getFieldDecorator, validateFieldsAndScroll } = form;
|
||||
const [reload, setReload] = useState(undefined);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [pageInfo, setPageInfo] = useState(undefined);
|
||||
const [tag, setTag] = useState(undefined);
|
||||
const [branch, setBranch ] = useState(null);
|
||||
const [building, setBuilding] = useState(false);
|
||||
const [isBuild, setIsBuild] = useState(false);
|
||||
const [result, setResult] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
// 更新网页标题
|
||||
if(projectDetail){
|
||||
const { author, name} = projectDetail;
|
||||
document.title = `个人建站-${author.name}/${name}`;
|
||||
}
|
||||
}, [projectDetail])
|
||||
|
||||
useEffect(()=>{
|
||||
if(id_card_verify){
|
||||
setLoading(true);
|
||||
// 获取站点列表
|
||||
axios.get(`/site_pages/x.json`, {params:{owner, repo: projectsId}}).then(res=>{
|
||||
if(res && res.status === 200){
|
||||
setPageInfo(res.data.data);
|
||||
if(res.data.data){
|
||||
const {last_build_info} = res.data.data;
|
||||
last_build_info && last_build_info.length && setResult(last_build_info);
|
||||
}
|
||||
}
|
||||
setLoading(false);
|
||||
})
|
||||
}
|
||||
}, [id_card_verify, reload])
|
||||
|
||||
// 基于仓库创建站点
|
||||
function submit(e) {
|
||||
e.preventDefault();
|
||||
validateFieldsAndScroll((err, values) => {
|
||||
if (!err) {
|
||||
axios.post(`/site_pages.json`,{
|
||||
...values,
|
||||
owner,
|
||||
repo: projectsId
|
||||
}).then(res=>{
|
||||
if(res && res.status === 200){
|
||||
setReload(Math.random());
|
||||
message.success("创建成功");
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 部署
|
||||
function startBuild(){
|
||||
setBuilding(true);
|
||||
axios.post(`/site_pages/${pageInfo.id}/build.json`,{
|
||||
owner,
|
||||
repo: projectsId,
|
||||
branch: branch || defaultBranch
|
||||
}).then(res=>{
|
||||
if(res && res.data && res.data.status === 0){
|
||||
setResult(res.data.data);
|
||||
}else{
|
||||
message.error((res && res.data && res.data.message) || "部署失败,请稍后重试");
|
||||
}
|
||||
setReload(Math.random());
|
||||
setIsBuild(false);
|
||||
setBuilding(false);
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='mb100'>
|
||||
<div className="servertitle">
|
||||
<span className="systitle">个人建站服务{!pageInfo && !loading && id_card_verify && isManager && ' - 新建站点'}</span>
|
||||
</div>
|
||||
{/* 未通过实名认证 */}
|
||||
{!id_card_verify && <Fragment>
|
||||
<div className="tipsBox font-15 mt30">您尚未通过实名认证,无法使用此服务,如需使用,请先进行实名认证。</div>
|
||||
<Button type="primary" className="mt20" onClick={()=>{history.push(`/settings/verification`)}}>前往验证</Button>
|
||||
</Fragment>}
|
||||
{id_card_verify && isManager && <Spin spinning={loading}>
|
||||
{/* 未开通 */}
|
||||
{!pageInfo ? <Form
|
||||
form={form}
|
||||
name="register"
|
||||
className="mt30"
|
||||
scrollToFirstError
|
||||
layout="horizontal"
|
||||
labelCol={{ span: 2 }}
|
||||
wrapperCol={{ span: 14 }}
|
||||
onSubmit={submit}
|
||||
>
|
||||
<Form.Item name="站点名称" label="站点名称">
|
||||
{getFieldDecorator("site_name", {
|
||||
rules: [{ required: true, message: "请输入站点名称" },
|
||||
{type: 'string', max: 50, min: 1, message: "长度1-50"}],
|
||||
})(<Input placeholder="请输入站点名称" />)}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="站点标识"
|
||||
label="站点标识"
|
||||
className="Create-Form-biaoshi"
|
||||
>
|
||||
{getFieldDecorator("identifier", {
|
||||
rules: [{ required: true, message: "请输入站点标识" },
|
||||
{pattern: /^[a-zA-Z0-9]{2,100}$/, message: '长度2-100,只能包含数字和字母'}],
|
||||
})(
|
||||
<Input
|
||||
onChange={(e) => {
|
||||
setTag(e.target.value);
|
||||
}}
|
||||
placeholder="请输入站点标识"
|
||||
/>
|
||||
)}
|
||||
<span style={{wordBreak: 'break-all'}}>
|
||||
http://{current_user.login}.kingchan.cn/{tag}
|
||||
</span>
|
||||
</Form.Item>
|
||||
<Form.Item name="建站工具" label="建站工具">
|
||||
{getFieldDecorator("language_frame", {initialValue: 0})(
|
||||
<Select style={{ width: 100 }}>
|
||||
<Select.Option value={0}>hugo</Select.Option>
|
||||
<Select.Option value={1}>jekyll</Select.Option>
|
||||
<Select.Option value={2}>hexo</Select.Option>
|
||||
</Select>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item style={{marginLeft: '100px'}} className="mt40">
|
||||
<Button type="primary" htmlType="submit" className="mr20">
|
||||
创建站点
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
history.push(`/${owner}/${projectsId}/service`);
|
||||
}}
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form> : pageInfo.state ? <div className='mt30'>
|
||||
{/* 已开通 */}
|
||||
<Descriptions title="站点信息">
|
||||
<Descriptions.Item label="站点名称">
|
||||
<span onClick={()=>{pageInfo.last_build_at && window.open(pageInfo.url)}} className= {pageInfo.last_build_at && 'theme-btn'}>{pageInfo.site_name}</span>
|
||||
<span className={`pageStateBox ml20 font-12 ${pageInfo.build_state ? "public" : "private"}`}>{pageInfo.build_state === null ? '未部署' : pageInfo.build_state ? "已部署" : "部署失败"}</span>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="网站地址" span={2}><span onClick={()=>{pageInfo.last_build_at && window.open(pageInfo.url)}} className= {pageInfo.last_build_at && 'theme-btn'}>{pageInfo.url}</span></Descriptions.Item>
|
||||
<Descriptions.Item label="建站工具">{pageInfo.language_frame}</Descriptions.Item>
|
||||
<Descriptions.Item label="建站时间">{pageInfo.created_at}</Descriptions.Item>
|
||||
{pageInfo.last_build_at && <Descriptions.Item label="上次部署时间">{pageInfo.last_build_at}</Descriptions.Item>}
|
||||
</Descriptions>
|
||||
{!isBuild && <Button type='primary' style={{width: '135px', height: '34px'}} className='mt20' onClick={()=>{setIsBuild(true)}}>去部署</Button>}
|
||||
{isBuild && <div>
|
||||
<div className='mt20 ant-descriptions-title'>部署</div>
|
||||
<div style={{display: 'inline-flex'}}>
|
||||
<span>部署分支:</span>
|
||||
<SelectBranch
|
||||
repo_id={projectDetail && projectDetail.repo_id}
|
||||
projectsId={projectsId}
|
||||
branch={branch || defaultBranch}
|
||||
changeBranch={(params)=>{setBranch(params)}}
|
||||
owner={owner}
|
||||
history={props.history}
|
||||
tagflag={false}
|
||||
branchList={projectDetail && projectDetail.branches && projectDetail.branches.list}
|
||||
></SelectBranch>
|
||||
<span className='color-grey-6 ml30'>选择您要部署的分支</span>
|
||||
</div>
|
||||
<div className='mt20'>
|
||||
<Button type='primary' loading={building} onClick={startBuild}>确定</Button>
|
||||
<Button type='primary' ghost onClick={()=>{setIsBuild(false)}} className='ml30'>取消</Button>
|
||||
</div>
|
||||
</div>}
|
||||
</div> : <div className='mt30'>
|
||||
{/* 站点权限被关闭 */}
|
||||
{pageInfo && <Alert type="error" message={`您的个人站点 ${pageInfo.site_name} 已被关闭,关闭原因为:${pageInfo.state_description}`}></Alert>}
|
||||
</div>}
|
||||
{result && <div className='mt30'>
|
||||
<div className='mt20 ant-descriptions-title'>部署结果</div>
|
||||
<div className='buildResult'>{result.map(item=> <p>{item}</p>)}</div>
|
||||
</div>}
|
||||
</Spin>}
|
||||
{!isManager && <div className='mt30'>
|
||||
{/* 非管理员权限 */}
|
||||
{pageInfo && pageInfo.state && <Descriptions title="站点信息">
|
||||
<Descriptions.Item label="站点名称"><span onClick={()=>{pageInfo.last_build_at && window.open(pageInfo.url)}} className= {pageInfo.last_build_at && 'theme-btn'}>{pageInfo.site_name}</span></Descriptions.Item>
|
||||
<Descriptions.Item label="网站地址" span={2}><span onClick={()=>{pageInfo.last_build_at && window.open(pageInfo.url)}} className= {pageInfo.last_build_at && 'theme-btn'}>{pageInfo.url}</span></Descriptions.Item>
|
||||
<Descriptions.Item label="建站工具">{pageInfo.language_frame}</Descriptions.Item>
|
||||
<Descriptions.Item label="建站时间">{pageInfo.created_at}</Descriptions.Item>
|
||||
{pageInfo.last_build_at && <Descriptions.Item label="上次部署时间">{pageInfo.last_build_at}</Descriptions.Item>}
|
||||
</Descriptions>}
|
||||
</div>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Form.create()(Pages);
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
.buildResult{
|
||||
padding: 10px 20px;
|
||||
background-color: rgb(20, 20, 20);
|
||||
color: white;
|
||||
}
|
||||
.theme-btn{
|
||||
color: $primary-color;
|
||||
cursor: pointer;
|
||||
}
|
||||
.pageStateBox{
|
||||
border: 1px solid $primary-color;
|
||||
border-radius: 5px;
|
||||
color: $primary-color;
|
||||
padding: 4px 6px 4px;
|
||||
background-color: rgba(70, 106, 255, 0.09);
|
||||
line-height: 12px;
|
||||
&.public{
|
||||
background-color: rgba(58, 213, 115, 0.08);
|
||||
border-color: #25c589;
|
||||
color: #25c589;
|
||||
}
|
||||
&.private{
|
||||
background-color: rgba(230, 126, 34, 0.08);
|
||||
border-color: rgba(230, 126, 34, 1);
|
||||
color: rgba(230, 126, 34, 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ const TokenKey = 'autologin_trustie';
|
|||
|
||||
function UploadImage({ getImage , url, getImageId, maxSize = 2, action = getUploadActionUrl(), getImageUrl }){
|
||||
const [ imageUrl , setImageUrl ] = useState(undefined);
|
||||
const [loading, setLoading] = useState(false);
|
||||
useEffect(()=>{
|
||||
setImageUrl(url);
|
||||
},[url])
|
||||
|
|
@ -29,11 +30,13 @@ function UploadImage({ getImage , url, getImageId, maxSize = 2, action = getUplo
|
|||
if (!isLt2M) {
|
||||
message.error(`上传的图片不能超过${maxSize}MB!`);
|
||||
}
|
||||
isJpgOrPng && isLt2M && setLoading(true);
|
||||
return isJpgOrPng && isLt2M;
|
||||
}
|
||||
// 上传完成后
|
||||
function handleChange(info){
|
||||
if(info && info.file && info.file.status === "done"){
|
||||
setLoading(false);
|
||||
getImageId && getImageId(info.file.response.id);
|
||||
getImageUrl && getImageUrl(info.file.response.url)
|
||||
getBase64(info.file.originFileObj, imageUrl =>
|
||||
|
|
@ -58,7 +61,7 @@ function UploadImage({ getImage , url, getImageId, maxSize = 2, action = getUplo
|
|||
<img src={imageUrl} alt="avatar" style={{ width: '100%' }} />
|
||||
:
|
||||
<div>
|
||||
<Icon type={'plus'} />
|
||||
<Icon type={loading ? 'loading' : 'plus'} />
|
||||
<div className="ant-upload-text">点击上传</div>
|
||||
</div>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import Item from './ListItem';
|
|||
import Right from './RightBox';
|
||||
import NoData from '../Nodata';
|
||||
import CheckProfile from '../Component/ProfileModal/Profile';
|
||||
import { Menu , Pagination , Dropdown , Spin , Tabs , Radio } from 'antd';
|
||||
import { Menu , Pagination , Dropdown , Spin , Tooltip , Radio } from 'antd';
|
||||
import ConcentrateProject from '../users/GeneralView/ConcentrateProject';
|
||||
import { getImageUrl } from 'educoder';
|
||||
import RenderHtml from "../../components/render-html";
|
||||
|
|
@ -117,7 +117,11 @@ function List(props){
|
|||
<Link to={`/${OIdentifier}/setting`} className="color-blue ml10 font-14 df"><i className="iconfont icon-shezhi2"></i>设置</Link>
|
||||
:""}
|
||||
</p>
|
||||
<div className="orz-desc task-hide-2">{organizeDetail && organizeDetail.description}</div>
|
||||
<div className="orz-desc task-hide-2">
|
||||
<Tooltip title={organizeDetail && organizeDetail.description} placement={"bottomLeft"}>
|
||||
<span style={{display:"block"}}>{organizeDetail && organizeDetail.description}</span>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -331,19 +331,20 @@ form{
|
|||
border: 1px solid #DDDDDD !important;
|
||||
background-color: #fff !important;
|
||||
}
|
||||
.ant-btn.ant-btn-background-ghost{
|
||||
border-color: #D0D0D0;
|
||||
}
|
||||
.ant-btn.ant-btn-background-ghost.ant-btn-primary:hover{
|
||||
background-color:$primary-color!important;
|
||||
border-color: $primary-color;
|
||||
color: #fff;
|
||||
}
|
||||
.ant-btn.ant-btn-background-ghost.ant-btn-danger:hover{
|
||||
background-color: #DF0002!important;
|
||||
border-color: #DF0002;
|
||||
color: #fff;
|
||||
}
|
||||
// ghost模式按钮:如有影响,单独修改
|
||||
// .ant-btn.ant-btn-background-ghost{
|
||||
// border-color: #D0D0D0;
|
||||
// }
|
||||
// .ant-btn.ant-btn-background-ghost.ant-btn-primary:hover{
|
||||
// background-color:$primary-color!important;
|
||||
// border-color: $primary-color;
|
||||
// color: #fff;
|
||||
// }
|
||||
// .ant-btn.ant-btn-background-ghost.ant-btn-danger:hover{
|
||||
// background-color: #DF0002!important;
|
||||
// border-color: #DF0002;
|
||||
// color: #fff;
|
||||
// }
|
||||
.newPopUl{
|
||||
li{
|
||||
height: 30px;
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ function Index(props){
|
|||
break;
|
||||
case '/settings/verification':
|
||||
setType('4');
|
||||
documentTitle = "身份认证";
|
||||
documentTitle = "实名认证";
|
||||
break;
|
||||
default:
|
||||
setType('2');
|
||||
|
|
@ -55,7 +55,7 @@ function Index(props){
|
|||
<Menu.Item key="3" className="font-16" onClick={()=>{props.history.push('/settings/phone')}}>手机号管理</Menu.Item>
|
||||
<Menu.Item key="0" className="font-16" onClick={()=>{props.history.push('/settings/emails')}}>邮箱管理</Menu.Item>
|
||||
<Menu.Item key="1" className="font-16" onClick={()=>{props.history.push('/settings/password')}}>密码管理</Menu.Item>
|
||||
{/* <Menu.Item key="4" className="font-16" onClick={()=>{props.history.push('/settings/verification')}}>身份认证</Menu.Item> */}
|
||||
<Menu.Item key="4" className="font-16" onClick={()=>{props.history.push('/settings/verification')}}>实名认证</Menu.Item>
|
||||
<Menu.Item key="2" className="font-16" onClick={()=>{props.history.push('/settings/cancel')}}>账号注销</Menu.Item>
|
||||
</Menu>}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { getImageUrl } from '../../../common/UrlTool';
|
|||
export default Form.create()(
|
||||
forwardRef((props)=>{
|
||||
const { getFieldDecorator, validateFields , setFieldsValue } = props && props.form;
|
||||
const { resetUserInfo , current_user } = props;
|
||||
const { current_user } = props;
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [data, setData] = useState(undefined);
|
||||
const [reload, setReload] = useState(undefined);
|
||||
|
|
@ -18,7 +18,7 @@ export default Form.create()(
|
|||
if(current_user && current_user.login){
|
||||
setLoading(true);
|
||||
// 获取用户身份认证信息
|
||||
axios.get(`http://ng.learnerhub.net/api/identity_verifications.json`).then(res=>{
|
||||
axios.get(`/identity_verifications.json`).then(res=>{
|
||||
setLoading(false);
|
||||
if(res && res.status === 200){
|
||||
if(res.data && res.data.status === 0){
|
||||
|
|
@ -38,7 +38,7 @@ export default Form.create()(
|
|||
validateFields((error, values) => {
|
||||
if (!error) {
|
||||
if(data){
|
||||
axios.put(`http://ng.learnerhub.net/api/identity_verifications/${data.id}.json`, values).then(res => {
|
||||
axios.put(`/identity_verifications/${data.id}.json`, values).then(res => {
|
||||
if(res && res.status === 200){
|
||||
message.success("提交成功");
|
||||
setReload(Math.random());
|
||||
|
|
@ -46,7 +46,7 @@ export default Form.create()(
|
|||
}).catch((error) => {});
|
||||
}else{
|
||||
// 新增
|
||||
axios.post(`http://ng.learnerhub.net/api/identity_verifications.json`, values).then(res=>{
|
||||
axios.post(`/identity_verifications.json`, values).then(res=>{
|
||||
if(res && res.status === 200){
|
||||
message.success("提交成功");
|
||||
setReload(Math.random());
|
||||
|
|
@ -67,15 +67,16 @@ export default Form.create()(
|
|||
</div>
|
||||
)}
|
||||
{data.state === "已拒绝" && <div className="tipsBox mb20 errorTip">
|
||||
您提交的身份信息未提供,原因:{data.description || "无,如有需要请联系平台管理员"}
|
||||
您提交的身份信息未审核通过,原因:{data.description || "无,如有需要请联系平台管理员"}
|
||||
</div>}
|
||||
{data.state === "已通过" && <Alert className="mb20" type='success' message="您提交的身份信息已通过" />}
|
||||
{data.state === "已通过" && <Alert className="mb20" type='success' message="您提交的身份信息已审核通过" />}
|
||||
</Fragment>
|
||||
)}
|
||||
<Form layout={"inline"} className="verificationForm">
|
||||
<Form.Item label="真实姓名" labelCol={{ span: 4 }}>
|
||||
{getFieldDecorator("name", {
|
||||
rules: [{ required: true, message: "请输入真实姓名" }],
|
||||
rules: [{ required: true, message: "请输入真实姓名" },
|
||||
{type: 'string', max: 50, min: 1, message: "长度1-50"}],
|
||||
})(
|
||||
<Input
|
||||
placeholder="请输入与身份证一致的真实姓名"
|
||||
|
|
@ -83,7 +84,7 @@ export default Form.create()(
|
|||
/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="身份证号" labelCol={{ span: 4 }}>
|
||||
<Form.Item label="身份证号码" labelCol={{ span: 4 }}>
|
||||
{getFieldDecorator("number", {
|
||||
rules: [
|
||||
{ required: true, message: "请输入大陆身份证号码" },
|
||||
|
|
|
|||
|
|
@ -285,4 +285,13 @@ export function formatParsedResult(setting, type= "range") {
|
|||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// 是否通过中期考核
|
||||
export function hasFinalExam(data) {
|
||||
return fetch({
|
||||
url: `/api/applyInformation/hasFinalExam`,
|
||||
method: 'get',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
|
@ -22,8 +22,7 @@ import './index.scss';
|
|||
// period: 项目报名阶段("repoApply")
|
||||
// match:{params:{id=2023}} 注意默认值记得每届修改
|
||||
export default (props) => {
|
||||
const { current_user, history, round, match:{params:{id=2023}}, period, showLoginDialog, glccSettings, repoPublic, showMatchingBut, isResultPublic, hasRole, isMediumExamineByToTutor, checkedTaskId} = props;
|
||||
|
||||
const { current_user, history, round, match:{params:{id=2023}}, period, showLoginDialog, glccSettings, repoPublic, showMatchingBut, isResultPublic, hasRole, isMediumExamineByToTutor, isFinalExamineByToTutor, checkedTaskId} = props;
|
||||
useEffect(() => {
|
||||
if (!current_user.user_id) {
|
||||
history.push('/403');
|
||||
|
|
@ -131,6 +130,16 @@ export default (props) => {
|
|||
</div>
|
||||
<div className="pt6">学生提交考核材料</div>
|
||||
</Link>}
|
||||
|
||||
{/* 学生结项考核 */}
|
||||
{period === "finalExamine1" && checkedTaskId && <Link className="apply" to={`/glcc/submit`}>
|
||||
<div>
|
||||
<img src={img1} alt="" className="applyIcon" />
|
||||
<span className="til">结项考核</span>
|
||||
</div>
|
||||
<div className="pt6">学生提交考核材料</div>
|
||||
</Link>}
|
||||
|
||||
{/* 导师中期考核 */}
|
||||
{hasRole && isMediumExamineByToTutor && <Link className="apply" to={`/glcc/middle/examination`}>
|
||||
<div>
|
||||
|
|
@ -139,7 +148,17 @@ export default (props) => {
|
|||
</div>
|
||||
<div className="pt6">导师拟定中期考核结果</div>
|
||||
</Link>}
|
||||
{/* 结项考核结果公示页 */}
|
||||
|
||||
{/* 导师结项考核 */}
|
||||
{hasRole && isFinalExamineByToTutor && <Link className="apply" to={`/glcc/final/examination`}>
|
||||
<div>
|
||||
<img src={img1} alt="" className="applyIcon" />
|
||||
<span className="til">结项考核</span>
|
||||
</div>
|
||||
<div className="pt6">导师拟定结项考核结果</div>
|
||||
</Link>}
|
||||
|
||||
{/* 中期考核结果公示页 */}
|
||||
{period === "mediumExamine3" && <Link className="apply" to={`/glcc/2023/result`}>
|
||||
<div>
|
||||
<img src={img1} alt="" className="applyIcon" />
|
||||
|
|
@ -147,6 +166,15 @@ export default (props) => {
|
|||
</div>
|
||||
<div className="pt6">中期课题考核结果公示</div>
|
||||
</Link>}
|
||||
|
||||
{/* 结项考核结果公示页 */}
|
||||
{period === "finalExamine3" && <Link className="apply" to={`/glcc/2023/result`}>
|
||||
<div>
|
||||
<img src={img1} alt="" className="applyIcon" />
|
||||
<span className="til">考核结果</span>
|
||||
</div>
|
||||
<div className="pt6">结项课题考核结果公示</div>
|
||||
</Link>}
|
||||
</div>}
|
||||
{/* </div> */}
|
||||
<div className="introduce">
|
||||
|
|
|
|||
|
|
@ -102,6 +102,8 @@ const Glcc = (propsF) => {
|
|||
const [isResultPublic, setIsResultPublic] = useState(false);
|
||||
// 导师中期考核评审阶段
|
||||
const [isMediumExamineByToTutor, setIsMediumExamineByToTutor] = useState(false);
|
||||
// 导师结项考核评审阶段
|
||||
const [isFinalExamineByToTutor, setIsFinalExamineByToTutor] = useState(false);
|
||||
|
||||
useEffect(()=>{
|
||||
if(id && id === "2022"){
|
||||
|
|
@ -129,8 +131,11 @@ const Glcc = (propsF) => {
|
|||
// 是否处于导师中期考核评审阶段
|
||||
const mediumExamine2 = res.data.filter(item=>item.name === "mediumExamine2");
|
||||
mediumExamine2[0] && setIsMediumExamineByToTutor(judge(nowTime, mediumExamine2[0].value, "range"))
|
||||
// 是否处于导师结项考核评审阶段
|
||||
const finalExamine2 = res.data.filter(item=>item.name === "finalExamine2");
|
||||
finalExamine2[0] && setIsFinalExamineByToTutor(judge(nowTime, finalExamine2[0].value, "range"))
|
||||
// 过滤掉 可并行进行的阶段
|
||||
const filterRepoPublic = res.data.filter(item=>["repoPublic", "repoApply1", "matching", "stuPublic", "mediumExamine2"].indexOf(item.name) === -1);
|
||||
const filterRepoPublic = res.data.filter(item=>["repoPublic", "repoApply1", "matching", "stuPublic", "mediumExamine2", "finalExamine2"].indexOf(item.name) === -1);
|
||||
const periodIndex = filterRepoPublic.findIndex(item=>judge(nowTime, item.value, "range"));
|
||||
periodIndex !== -1 && setPeriod(filterRepoPublic[periodIndex].name);
|
||||
}
|
||||
|
|
@ -168,17 +173,17 @@ const Glcc = (propsF) => {
|
|||
const [hasRole, setHasRole] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// 登录状态+导师-学生配对阶段/中期审核
|
||||
current_user && current_user.user_id && (isMatching || isMediumExamineByToTutor) && hasAuditRole({ userId: current_user.user_id, round: currentRound }).then(res => {
|
||||
// 登录状态+导师-学生配对阶段/中期审核/结项审核
|
||||
current_user && current_user.user_id && (isMatching || isMediumExamineByToTutor || isFinalExamineByToTutor) && hasAuditRole({ userId: current_user.user_id, round: currentRound }).then(res => {
|
||||
if (res && res.message === 'success' && res.data.hasRole) {
|
||||
setHasRole(true);
|
||||
}
|
||||
})
|
||||
}, [current_user, isMatching, isMediumExamineByToTutor])
|
||||
}, [current_user, isMatching, isMediumExamineByToTutor, isFinalExamineByToTutor])
|
||||
|
||||
useEffect(()=>{
|
||||
// 获取用户课题报名信息
|
||||
(period === "stuApply" || period === "stuApply1" || period === "mediumExamine1") && current_user && current_user.login && getStudentApplyInfo({userId: current_user.user_id, round: round}).then(response=>{
|
||||
(period === "stuApply" || period === "stuApply1" || period === "mediumExamine1" || period === "finalExamine1") && current_user && current_user.login && getStudentApplyInfo({userId: current_user.user_id, round: round}).then(response=>{
|
||||
if(response && response.message === "success"){
|
||||
// setData(response.data.rows);
|
||||
const data = {};
|
||||
|
|
@ -312,27 +317,36 @@ const Glcc = (propsF) => {
|
|||
)}
|
||||
></Route>
|
||||
|
||||
{/* 中期审核-结果公示 */}
|
||||
{(round === 1 || period === "mediumExamine3" || period === "finalExamine3") && <Route
|
||||
{/* 中期/结项审核-结果公示 */}
|
||||
{(round === 1 || period === "mediumExamine3" || period === "finalExamine3") &&
|
||||
<Route
|
||||
path="/glcc/:id/result"
|
||||
render={(props) => (
|
||||
<MiddleResult id={id} round={round} current_user={current_user} history={props.history}/>
|
||||
<MiddleResult id={id} round={round} period={period} current_user={current_user} history={props.history}/>
|
||||
)}
|
||||
></Route>}
|
||||
|
||||
{/* 中期审核-学生 */}
|
||||
{period === "mediumExamine1" && <Route
|
||||
{/* 中期/结项审核-学生 */}
|
||||
{(period === "mediumExamine1" || period === "finalExamine1") && <Route
|
||||
path="/glcc/submit"
|
||||
render={(props) => (
|
||||
<StudentSubmit current_user={current_user} history={props.history} checkedTaskId={checkedTaskId} studentRegId={studentRegId} period={period} currentRound={currentRound} glccSettings={glccSettings} isMediumExamineByToTutor={isMediumExamineByToTutor}/>
|
||||
<StudentSubmit current_user={current_user} history={props.history} checkedTaskId={checkedTaskId} studentRegId={studentRegId} period={period} currentRound={currentRound} glccSettings={glccSettings} isMediumExamineByToTutor={isMediumExamineByToTutor} isFinalExamineByToTutor={isFinalExamineByToTutor}/>
|
||||
)}
|
||||
></Route>}
|
||||
|
||||
{/* 中期审核-导师 */}
|
||||
{isMediumExamineByToTutor && <Route
|
||||
{(isMediumExamineByToTutor) && <Route
|
||||
path="/glcc/middle/examination"
|
||||
render={(props) => (
|
||||
<TutorReview current_user={current_user} history={props.history} hasRole={hasRole} isMediumExamineByToTutor={isMediumExamineByToTutor} currentRound={currentRound} glccSettings={glccSettings}/>
|
||||
<TutorReview current_user={current_user} period={'mediumExamine'} history={props.history} hasRole={hasRole} isMediumExamineByToTutor={isMediumExamineByToTutor} currentRound={currentRound} glccSettings={glccSettings}/>
|
||||
)}
|
||||
></Route>}
|
||||
|
||||
{/* 结项审核-导师 */}
|
||||
{(isFinalExamineByToTutor) && <Route
|
||||
path="/glcc/final/examination"
|
||||
render={(props) => (
|
||||
<TutorReview current_user={current_user} period={'finalExamine'} history={props.history} hasRole={hasRole} isMediumExamineByToTutor={isFinalExamineByToTutor} currentRound={currentRound} glccSettings={glccSettings}/>
|
||||
)}
|
||||
></Route>}
|
||||
|
||||
|
|
@ -340,7 +354,7 @@ const Glcc = (propsF) => {
|
|||
<Route
|
||||
path="/glcc/:id"
|
||||
render={(props) => (
|
||||
<Home repoPublic={repoPublic} period={period} round={round} {...propsF} {...props} checkedTaskId={checkedTaskId} glccSettings={glccSettings} showMatchingBut={isMatching && hasRole} isResultPublic={isResultPublic} hasRole={hasRole} isMediumExamineByToTutor={isMediumExamineByToTutor}/>
|
||||
<Home repoPublic={repoPublic} period={period} round={round} {...propsF} {...props} checkedTaskId={checkedTaskId} glccSettings={glccSettings} showMatchingBut={isMatching && hasRole} isResultPublic={isResultPublic} hasRole={hasRole} isMediumExamineByToTutor={isMediumExamineByToTutor} isFinalExamineByToTutor={isFinalExamineByToTutor}/>
|
||||
)}
|
||||
></Route>
|
||||
|
||||
|
|
@ -348,7 +362,7 @@ const Glcc = (propsF) => {
|
|||
<Route
|
||||
path="/glcc"
|
||||
render={(props) => (
|
||||
<Home repoPublic={repoPublic} period={period} round={round} {...propsF} {...props} checkedTaskId={checkedTaskId} glccSettings={glccSettings} showMatchingBut={isMatching && hasRole} isResultPublic={isResultPublic} hasRole={hasRole} isMediumExamineByToTutor={isMediumExamineByToTutor}/>
|
||||
<Home repoPublic={repoPublic} period={period} round={round} {...propsF} {...props} checkedTaskId={checkedTaskId} glccSettings={glccSettings} showMatchingBut={isMatching && hasRole} isResultPublic={isResultPublic} hasRole={hasRole} isMediumExamineByToTutor={isMediumExamineByToTutor} isFinalExamineByToTutor={isFinalExamineByToTutor}/>
|
||||
)}
|
||||
></Route>
|
||||
|
||||
|
|
|
|||
|
|
@ -275,4 +275,15 @@
|
|||
.resultListTable .ant-table{
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
.inputErrColor{
|
||||
.ant-form-item-control-wrapper{
|
||||
.has-error{
|
||||
.ant-form-item-children{
|
||||
input{
|
||||
border-color: #f5222d;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@ import '../project/index.scss';
|
|||
const { Search } = Input;
|
||||
|
||||
// 课题列表
|
||||
function CheckResult({round, id}) {
|
||||
function CheckResult({round, id, period}) {
|
||||
// 输入搜索框
|
||||
const [keyword, setKeyword] = useState(undefined);
|
||||
const [data, setData] = useState([]);
|
||||
|
|
@ -30,7 +30,7 @@ function CheckResult({round, id}) {
|
|||
curPage: current,
|
||||
keyword,
|
||||
pageSize,
|
||||
term: id === "2023" ? 1 : 2,
|
||||
term: period === 'mediumExamine3' ? 1 : 2,
|
||||
round
|
||||
}
|
||||
getMediumTermExamineInfoList(params).then(response => {
|
||||
|
|
@ -98,10 +98,10 @@ function CheckResult({round, id}) {
|
|||
|
||||
return (
|
||||
<div className="interimBox taskList resultListBox">
|
||||
<img className="bannerInterim" src={id === "2023" ? resultBanner1 : resultBanner} alt=""></img>
|
||||
<img className="bannerInterim" src={period === 'mediumExamine3' ? resultBanner1 : resultBanner} alt=""></img>
|
||||
<div className='bgBox'>
|
||||
<div className="resultList">
|
||||
<div className='goBackBox'><a href={`/glcc/${id}`}>开源夏令营 / </a>{id === "2023" ? '中期' : '结项'}课题考核结果公示</div>
|
||||
<div className='goBackBox'><a href={`/glcc/${id}`}>开源夏令营 / </a>{period === 'mediumExamine3' ? '中期' : '结项'}课题考核结果公示</div>
|
||||
<div className='searchBox'>
|
||||
<Search className='search' placeholder='请输入学生姓名或课题名称进行搜索' allowClear enterButton onSearch={(value) => { setCurrent(1); setKeyword(value) }} />
|
||||
<div style={{width: 100}}></div>
|
||||
|
|
|
|||
|
|
@ -1,24 +1,35 @@
|
|||
import React, { useEffect, useState, Fragment } from "react";
|
||||
import { Form, Upload, Input, Icon, Button, message, Modal, Checkbox } from "antd";
|
||||
import { Form, Upload, Input, Icon, Button, message, Modal, Checkbox, notification} from "antd";
|
||||
import { Link } from "react-router-dom";
|
||||
import axios from "axios";
|
||||
import banner from '../img/banner-interim.png';
|
||||
import banner1 from '../img/banner-interim1.png';
|
||||
import img1 from '../img/img1.png';
|
||||
import bg from '../img/bgPng.png';
|
||||
import { httpUrl, main_site_url } from '../fetch';
|
||||
import {getMediumTermExamineInfo, submitMedium, formatParsedResult} from '../api';
|
||||
import {getMediumTermExamineInfo, submitMedium, formatParsedResult, hasFinalExam} from '../api';
|
||||
import './index.scss';
|
||||
|
||||
function StudentSubmit(props){
|
||||
const {form, checkedTaskId, studentRegId, period, currentRound, glccSettings, isMediumExamineByToTutor} = props;
|
||||
const {form, checkedTaskId, studentRegId, period, currentRound, glccSettings, isMediumExamineByToTutor, isFinalExamineByToTutor,history, current_user} = props;
|
||||
const mediumExamine = glccSettings && glccSettings.filter(item=>item.name === "mediumExamine1");
|
||||
const mediumExamine2 = glccSettings && glccSettings.filter(item=>item.name === "mediumExamine2");
|
||||
const finalExamine = glccSettings && glccSettings.filter(item=>item.name === "finalExamine1");
|
||||
const finalExamine2 = glccSettings && glccSettings.filter(item=>item.name === "finalExamine2");
|
||||
let overtime = false;
|
||||
if(mediumExamine2 && mediumExamine2[0]){
|
||||
if (mediumExamine2 && mediumExamine2[0] && period === "mediumExamine1") {
|
||||
const nowTime = new Date().getTime();
|
||||
const timeArr = mediumExamine2[0].value.split(",").map(item=>{
|
||||
const timeArr = mediumExamine2[0].value.split(",").map((item) => {
|
||||
// replaceAll不兼容低版本浏览器(如搜狗)
|
||||
const data = item && item.replace(/-/g,'/');
|
||||
const data = item && item.replace(/-/g, "/");
|
||||
return new Date(data).getTime();
|
||||
});
|
||||
overtime = nowTime > timeArr[1];
|
||||
} else if (finalExamine2 && finalExamine2[0] && period != "mediumExamine1") {
|
||||
const nowTime = new Date().getTime();
|
||||
const timeArr = finalExamine2[0].value.split(",").map((item) => {
|
||||
// replaceAll不兼容低版本浏览器(如搜狗)
|
||||
const data = item && item.replace(/-/g, '/');
|
||||
return new Date(data).getTime()
|
||||
});
|
||||
overtime = nowTime > timeArr[1];
|
||||
|
|
@ -32,6 +43,17 @@ function StudentSubmit(props){
|
|||
// 是否授权主办方
|
||||
const [isAuthed, setIsAuthed] = useState(false);
|
||||
|
||||
useEffect(()=>{
|
||||
if(period != "mediumExamine1"){
|
||||
hasFinalExam({round:currentRound,userId:current_user.user_id}).then((res)=>{
|
||||
if(res && res.data && res.data.hasRole) return;
|
||||
history.push("/glcc");
|
||||
}).catch((err)=>{
|
||||
history.push("/glcc");
|
||||
})
|
||||
}
|
||||
},[])
|
||||
|
||||
useEffect(()=>{
|
||||
// 查询是否已经提交考核材料
|
||||
checkedTaskId && getMediumTermExamineInfo(checkedTaskId,{round: currentRound, term: period === "mediumExamine1" ? 1 : 2}).then(res=>{
|
||||
|
|
@ -82,6 +104,10 @@ function StudentSubmit(props){
|
|||
})
|
||||
}else{
|
||||
setDisabled(false);
|
||||
const { codeOrPrUrl } = values;
|
||||
if (codeOrPrUrl) {
|
||||
checkPrLink("", codeOrPrUrl, () => {});
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -108,6 +134,46 @@ function StudentSubmit(props){
|
|||
}
|
||||
return isLt100M;
|
||||
}
|
||||
function checkPrLink(rule, value, callback) {
|
||||
const getUrl = `${
|
||||
window.location.href.indexOf("gitlink.org.cn") > -1
|
||||
? "https://www.gitlink.org.cn"
|
||||
: "https://testforgeplus.trustie.net"
|
||||
}`;
|
||||
if (value) {
|
||||
axios
|
||||
.get(`${getUrl}/check_pr_url`, {
|
||||
params: {
|
||||
url: value,
|
||||
task: checkedTaskId,
|
||||
term: period === "mediumExamine1" ? 1 : 2,
|
||||
},
|
||||
})
|
||||
.then((response) => {
|
||||
if (response && response.data && response.data.state && response.data.state.length > 0) {
|
||||
callback("");
|
||||
const description = (
|
||||
<div
|
||||
dangerouslySetInnerHTML={{ __html: response.data.state_html }}
|
||||
></div>
|
||||
);
|
||||
notification.open({
|
||||
message: "提示",
|
||||
description,
|
||||
key:'notificationKey',
|
||||
style: {
|
||||
zIndex: 99999999,
|
||||
},
|
||||
});
|
||||
}
|
||||
callback();
|
||||
}).catch(() => {
|
||||
callback("请正确输入pr地址");
|
||||
});
|
||||
} else {
|
||||
callback("请输入pr地址");
|
||||
}
|
||||
}
|
||||
return <div className="interimBox">
|
||||
<img src={period === "mediumExamine1" ? banner1 : banner} alt="" className="bannerInterim"/>
|
||||
<img src={bg} alt="" className="bg1"/>
|
||||
|
|
@ -116,18 +182,18 @@ function StudentSubmit(props){
|
|||
<div className="navBox font-16"><Link to={`/glcc`} className="linkBox">开源夏令营 / </Link>提交{period === "mediumExamine1" ? '中期' : '结项'}考核材料</div>
|
||||
<div className="tipBox mt30">
|
||||
<div className="font-15 spanBox">材料提交说明:</div>
|
||||
<div>1、请各位学生<a href={main_site_url+`/api/attachments/${period === "mediumExamine1" ? '428107' : '427720'}`} className="blueSpan">下载PPT模板</a> ,根据课题开发进展,按照PPT模板要求填写课题学习调研方案、开发进度及开发成果等考核材料;</div>
|
||||
<div>1、请各位学生<a href={`https://www.gitlink.org.cn/api/attachments/${period === "mediumExamine1" ? '428107' : '427720'}`} className="blueSpan">下载PPT模板</a> ,根据课题开发进展,按照PPT模板要求填写课题学习调研方案、开发进度及开发成果等考核材料;</div>
|
||||
<div>2、欢迎各位学生录制本课题答辩视频,将视频链接填写至下方视频介绍填写栏;</div>
|
||||
<div>3、学生提交考核材料的时间为<span className="spanBox">{formatParsedResult(mediumExamine)}</span>,请在截止时间前提交;</div>
|
||||
<div>3、学生提交考核材料的时间为<span className="spanBox">{formatParsedResult( period === "mediumExamine1" ? mediumExamine : finalExamine)}</span>,请在截止时间前提交;</div>
|
||||
<div>4、若导师已给出评分,各位学生可在此页面查看自己的结项考核成绩。对考核成绩有异议的学生请及时联系您的导师。</div>
|
||||
</div>
|
||||
<div className="titleBox mt25 font-18">
|
||||
<img src={img1} alt="" width={24} className="mr5"/>
|
||||
{period === "mediumExamine1" ? '中期' : '结项'}考核
|
||||
</div>
|
||||
{period === "mediumExamine1" && !detail ? <Form className="referBox" onSubmit={submit} colon={false}>
|
||||
{(period === "mediumExamine1" || period === "finalExamine1") && !detail ? <Form className="referBox" onSubmit={submit} colon={false}>
|
||||
{
|
||||
period === "mediumExamine1"?
|
||||
period === "mediumExamine1" || period === "finalExamine1" ?
|
||||
<Fragment>
|
||||
<Form.Item label="答辩视频" className="referItem oneCont oneLine">
|
||||
{getFieldDecorator('defenceVideoUrl', {
|
||||
|
|
@ -139,9 +205,17 @@ function StudentSubmit(props){
|
|||
授权主办方进行下载和剪辑,用作比赛宣传
|
||||
</Checkbox>
|
||||
</Form.Item>
|
||||
<Form.Item label="pr地址" className="referItem oneCont oneLine">
|
||||
<Form.Item label="pr地址" className="referItem oneCont oneLine inputErrColor">
|
||||
{getFieldDecorator('codeOrPrUrl', {
|
||||
rules: [{ required: true, message: '请输入视频链接!'},{pattern: /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/,message: "请正确输入链接"}],
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
validator: (rule, value, callback) => {
|
||||
checkPrLink(rule, value, callback, checkedTaskId);
|
||||
},
|
||||
},
|
||||
],
|
||||
validateTrigger: "onBlur",
|
||||
})(<Input placeholder="请输入pr地址" maxLength={900}/>)}
|
||||
</Form.Item>
|
||||
<Form.Item className="referItem oneCont referTips">
|
||||
|
|
@ -170,6 +244,9 @@ function StudentSubmit(props){
|
|||
{period === "mediumExamine1"?
|
||||
<Button className="uploadBox" style={{color:'#466aff'}}>上传GLCC中期汇报ppt</Button>
|
||||
:
|
||||
period === 'finalExamine1' ?
|
||||
<Button className="uploadBox" style={{ color: "#466aff" }}>上传GLCC结项汇报ppt</Button>
|
||||
:
|
||||
<Button className="uploadBox"><Icon type="upload" /> 上传</Button>
|
||||
}
|
||||
</Upload>)}
|
||||
|
|
@ -180,11 +257,12 @@ function StudentSubmit(props){
|
|||
</Form> : <div className={`reviewBox referBox resultBox ${!detail ? 'nullData' : ''}`}>
|
||||
<div className="flexBox">
|
||||
<div className="mustSpan mb20"> 答辩视频<span className="blueBg ml10"><a href={detail && detail.defenceVideoUrl} target="_blank">{detail && detail.defenceVideoUrl}</a></span></div>
|
||||
<div style={{ margin: "-10px 0 20px 90px" }}><Checkbox checked={detail && detail.isAuthed ? true: false} disabled></Checkbox><span style={{ marginLeft: "5px" }}>授权主办方进行下载和剪辑,用作比赛宣传</span></div>
|
||||
<div>代码/pr地址<span className="blueBg ml10"><a href={detail && detail.codeOrPrUrl} target="_blank">{detail && detail.codeOrPrUrl}</a></span></div>
|
||||
<div className="mustSpan ppt mb20"> PPT附件{detail && <i className="iconfont icon-lianjie3 font-13 mr5 ml10"></i>}{detail ? <a className="pptAttachment mr10" href={`${httpUrl}/busiAttachments/download/${detail.pptAttachment.id}`}>{`${detail.pptAttachment.fileName}`}</a> : <Button className="uploadBox ml10" disabled><Icon type="upload" /> 上传</Button>}{detail && detail.pptAttachment && detail.pptAttachment.fileSizeString}</div>
|
||||
</div>
|
||||
{!detail && <div className="nullDateTip font-15">很遗憾,您在指定时间内未提交考核材料</div>}
|
||||
{detail && isMediumExamineByToTutor && !detail.glccTutorEvaluation && <div className="font-15 nullDateTip">您的课题导师暂未评分,请提醒导师尽快提交结项考核评分</div>}
|
||||
{detail && (isMediumExamineByToTutor || isFinalExamineByToTutor) && !detail.glccTutorEvaluation && <div className="font-15 nullDateTip">您的课题导师暂未评分,请提醒导师尽快提交结项考核评分</div>}
|
||||
{detail && overtime && !detail.glccTutorEvaluation && <div className="font-15 nullDateTip">很遗憾,您的导师尚未评分,您未通过结项考核</div>}
|
||||
{detail && detail.glccTutorEvaluation && <div className="tutorRes">
|
||||
<div>您的课题导师对您的结项考核评价如下,如有异议,请及时联系导师进行更改。</div>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ const { TabPane } = Tabs;
|
|||
const { TextArea } = Input;
|
||||
|
||||
function TutorReview(props){
|
||||
const {form, current_user, showNotification, history, hasRole, isMediumExamineByToTutor, currentRound, glccSettings} = props;
|
||||
const {form, current_user, showNotification, history, hasRole, isMediumExamineByToTutor, period, currentRound, glccSettings} = props;
|
||||
const {getFieldDecorator, setFieldsValue, validateFieldsAndScroll, resetFields } = form;
|
||||
const [taskId, setTaskId] = useState();
|
||||
const [taskList, setTaskList] = useState([]);
|
||||
|
|
@ -24,15 +24,26 @@ function TutorReview(props){
|
|||
const [disabled, setDisabled] = useState(false);
|
||||
const mediumExamine2 = glccSettings && glccSettings.filter(item=>item.name === "mediumExamine2");
|
||||
const mediumExamine3 = glccSettings && glccSettings.filter(item=>item.name === "mediumExamine3");
|
||||
const finalExamine2 = glccSettings && glccSettings.filter(item=>item.name === "finalExamine2");
|
||||
const finalExamine3 = glccSettings && glccSettings.filter(item=>item.name === "finalExamine3");
|
||||
useEffect(() => {
|
||||
if(!isMediumExamineByToTutor || !hasRole){
|
||||
history.push("/glcc");
|
||||
}else if (!current_user.login) {
|
||||
history.push('/login?go_page=/glcc/middle/examination');
|
||||
if(period === 'mediumExamine'){
|
||||
history.push('/login?go_page=/glcc/middle/examination');
|
||||
}else{
|
||||
history.push('/login?go_page=/glcc/final/examination');
|
||||
}
|
||||
}
|
||||
getLockedAuditList({ userId: current_user.user_id, pass: 1, round: currentRound }).then(res => {
|
||||
if (res.message === 'success') {
|
||||
const filterStuNull = res.data.rows.filter(item=>{return item.studentName !== null})
|
||||
let filterStuNull = [];
|
||||
if(period === 'mediumExamine'){
|
||||
filterStuNull = res.data.rows.filter(item=>{return item.studentName !== null})
|
||||
}else{
|
||||
filterStuNull = res.data.rows.filter(item=>{return item.studentName !== null && item.canSubmitFinalExaminationMaterial})
|
||||
}
|
||||
setTaskList(filterStuNull);
|
||||
filterStuNull.length && setTaskId(filterStuNull[0].id);
|
||||
} else {
|
||||
|
|
@ -44,7 +55,7 @@ function TutorReview(props){
|
|||
useEffect(()=>{
|
||||
// 查询是否已经提交考核材料
|
||||
// 结项考核
|
||||
taskId && getMediumTermExamineInfo(taskId,{round: currentRound, term: isMediumExamineByToTutor ? 1 : 2}).then(res=>{
|
||||
taskId && getMediumTermExamineInfo(taskId,{round: currentRound, term: period === 'mediumExamine' ? 1 : 2}).then(res=>{
|
||||
if(res && res.message === "success"){
|
||||
setDetail(res.data);
|
||||
if(res && res.data && res.data.glccTutorEvaluation){
|
||||
|
|
@ -66,7 +77,7 @@ function TutorReview(props){
|
|||
mediumTermExamineMaterialId: detail.id,
|
||||
tutorUserId: detail.studentRegId,
|
||||
round: currentRound,
|
||||
term: isMediumExamineByToTutor ? 1 : 2
|
||||
term: period === 'mediumExamine' ? 1 : 2
|
||||
}
|
||||
if(detail.glccTutorEvaluation){
|
||||
params['id'] = detail.glccTutorEvaluation.id;
|
||||
|
|
@ -115,9 +126,16 @@ function TutorReview(props){
|
|||
<div className="tipBox mt30">
|
||||
<div className="font-15 spanBox">导师考核说明:</div>
|
||||
<div>1、请各位导师从“工作态度”“开发进度”“项目完成质量”“总体评分”四个角度,根据学生提交的考核材料与实际开发情况客观地进行打分。打分标准分为:S:特别优秀、A:优秀、B:良好、C:合格、D:不合格五个等级。</div>
|
||||
<div>2、“总体评分”这一项将决定学生是否通过本次考核。若总体评分为“S、A、B、C”,则视为通过中期考核。若该结果为“D”,则该课题中期考核不通过,课题将自动终止。请各位导师谨慎做出评价。</div>
|
||||
<div>3、导师提交打分结果后,可对考核结果进行更改,更改考核结果截止日期为<span className="spanBox">{mediumExamine2 && formatParsedResult(mediumExamine2, 'end')}</span>。</div>
|
||||
<div>4、北京时间<span className="spanBox">{mediumExamine3 && formatParsedResult(mediumExamine3, "start")}</span>前GLCC官网将公布中期考核结果,敬请留意。</div>
|
||||
<div>2、“总体评分”这一项将决定学生是否通过本次考核。若总体评分为“S、A、B、C”,则视为通过{period === 'mediumExamine'?'中期':'结项'}考核。若该结果为“D”,则该课题{period === 'mediumExamine'?'中期':'结项'}考核不通过,课题将自动终止。请各位导师谨慎做出评价。</div>
|
||||
<div>3、导师提交打分结果后,可对考核结果进行更改,更改考核结果截止日期为{period === 'mediumExamine' ?
|
||||
<span className="spanBox">{mediumExamine2 && formatParsedResult(mediumExamine2, 'end')}</span>
|
||||
:
|
||||
<span className="spanBox">{finalExamine2 && formatParsedResult(finalExamine2, 'end')}</span>}。</div>
|
||||
<div>4、北京时间{period === 'mediumExamine' ?
|
||||
<span className="spanBox">{mediumExamine3 && formatParsedResult(mediumExamine3, "start")}</span>
|
||||
:
|
||||
<span className="spanBox">{finalExamine3 && formatParsedResult(finalExamine3, "start")}</span>
|
||||
}前GLCC官网将公布{period === 'mediumExamine'?'中期':'结项'}考核结果,敬请留意。</div>
|
||||
{/* 结项考核文案 */}
|
||||
{/* <div>2、“总体评分”这一项将决定学生是否通过本次考核。若总体评分为“S、A、B、C”,则视为通过结项考核。若该结果为“D”,则该课题结项考核不通过,不予发放结项奖金。请各位导师谨慎做出评价。</div>
|
||||
<div>3、导师提交打分结果后,可对考核结果进行更改,期间考核结果也将实时反馈给学生;更改考核结果截止时间为<span className="spanBox">2022年10月20日24点</span>。</div>
|
||||
|
|
|
|||
|
|
@ -29,18 +29,18 @@ function ThirdEdition() {
|
|||
return flag;
|
||||
},[flag])
|
||||
|
||||
useEffect(()=>{
|
||||
let box = document.getElementById('thirdUl');
|
||||
var myTimer = setTimeout(intervalActive, 2500);
|
||||
box.onmouseover = () => {
|
||||
clearTimeout(myTimer);
|
||||
setFlag(false);
|
||||
}
|
||||
box.onmouseleave = () => {
|
||||
myTimer = setTimeout(intervalActive, 2500);
|
||||
setFlag(true);
|
||||
}
|
||||
},[active,flag])
|
||||
// useEffect(()=>{
|
||||
// let box = document.getElementById('thirdUl');
|
||||
// var myTimer = setTimeout(intervalActive, 2500);
|
||||
// box.onmouseover = () => {
|
||||
// clearTimeout(myTimer);
|
||||
// setFlag(false);
|
||||
// }
|
||||
// box.onmouseleave = () => {
|
||||
// myTimer = setTimeout(intervalActive, 2500);
|
||||
// setFlag(true);
|
||||
// }
|
||||
// },[active,flag])
|
||||
|
||||
function intervalActive() {
|
||||
if(doubleFlag){
|
||||
|
|
@ -179,7 +179,7 @@ function ThirdEdition() {
|
|||
return(
|
||||
<li>
|
||||
<a href={i.url} target="_blank">{i.title}</a>
|
||||
<span className="listboxcount"><i className="iconfont icon-a-liulanicon2x mr5"></i>{i.visits}</span>
|
||||
{i.from !== "other" && <span className="listboxcount"><i className="iconfont icon-a-liulanicon2x mr5"></i>{i.visits}</span> }
|
||||
<span>{i.created_time && i.created_time.split(" ")[0]}</span>
|
||||
</li>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ import 'codemirror/lib/codemirror.css';
|
|||
import './css/newquestion.css';
|
||||
const $ = window.$
|
||||
|
||||
const mdIcons = ["bold", "italic", "del", "|", "list-ul", "list-ol", "|", "code", "code-block", "link", "|", "inline-latex", "latex", '|', "image", "table", '|', "line-break", "watch", "clear","fullscreen"];
|
||||
const mdIcons = ["bold", "italic", "del", "|", "list-ul", "list-ol", "|", "code", "code-block", "link", "|", "image", "table", '|', "line-break", "watch", "clear","fullscreen"];
|
||||
const mdIconsHasLatex = ["bold", "italic", "del", "|", "list-ul", "list-ol", "|", "code", "code-block", "link", "|", "inline-latex", "latex", '|', "image", "table", '|', "line-break", "watch", "clear","fullscreen"];
|
||||
|
||||
const NULL_CH = '▁';
|
||||
|
||||
|
|
@ -74,7 +75,7 @@ function md_elocalStorage(editor, mdu, id) {
|
|||
return tid
|
||||
}
|
||||
|
||||
export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, className = '', noStorage = false, imageExpand = true, placeholder = '', width = '100%', height = 400, initValue = '', emoji, watch=true, showNullButton = false, showResizeBar = false, startInit = true , forMember = true , isCanAtme = false , isQuoteIssue = false , changeAtWhoLoginList, owner, projectsId , isFocus = true}) => {
|
||||
export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, className = '', noStorage = false, imageExpand = true, placeholder = '', width = '100%', height = 400, initValue = '', emoji, watch=true, showNullButton = false, showResizeBar = false, startInit = true , forMember = true , isCanAtme = false , isQuoteIssue = false , changeAtWhoLoginList, owner, projectsId , isFocus = true, showLatexButton = true}) => {
|
||||
|
||||
const editorEl = useRef();
|
||||
const resizeBarEl = useRef();
|
||||
|
|
@ -353,7 +354,7 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
|
|||
imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp", "JPG", "JPEG", "GIF", "PNG", "BMP", "WEBP"],
|
||||
imageUploadURL: getUploadActionUrl(),
|
||||
toolbarIcons: function () {
|
||||
return showNullButton ? [...mdIcons, 'null-button'] : mdIcons
|
||||
return showLatexButton ? mdIconsHasLatex : mdIcons
|
||||
},
|
||||
toolbarIconsClass: {
|
||||
"line-break": "fa-minus",
|
||||
|
|
|
|||
Loading…
Reference in New Issue