Compare commits

...

95 Commits

Author SHA1 Message Date
jasder d9e3e0d96b ADD 成果库模块功 2021-05-25 14:15:20 +08:00
何童崇 563ca38d45 上传需求代码,微调成果库代码 2021-05-21 17:15:59 +08:00
何童崇 db702ed816 增加成果模块,跨域调用,修改原资源中跨域不能传cookie的设置 2021-05-17 13:22:58 +08:00
caishi 52fa8da9a1 隐藏找回密码功能入口 2021-04-17 13:59:02 +08:00
caishi 19c35eb6da 资源库不需要按引用次数排序 2021-04-14 10:28:43 +08:00
caishi 53e33414ae 资源库数量显示错误 2021-04-13 11:13:39 +08:00
caishi 3701abc2c9 资源库接口地址是另一个服务器 2021-04-13 11:10:54 +08:00
caishi d73e127f3d 组织-团队列表的头像login为undefined 2021-04-13 11:06:45 +08:00
caishi c39635af32 默认进项目详情,去掉多余提示 2021-04-13 10:59:03 +08:00
caishi f5df464d90 资源库模块+合并请求分页 2021-04-12 17:58:46 +08:00
caishi 690def741c 去掉右侧悬浮的使用手册按钮 2021-04-12 16:23:51 +08:00
caishi c32b333bc2 Merge branch 'dev_m_copy' into dev_military
# Conflicts:
#	src/forge/DevOps/Index.jsx
#	src/forge/Index.js
#	src/forge/Main/Detail.js
#	src/forge/Merge/MessageCount.js
#	src/forge/Settings/Collaborator.js
#	src/forge/Settings/CollaboratorMember.jsx
#	src/forge/users/watch_users.js
#	src/modules/tpm/NewHeader.js
2021-04-12 15:57:30 +08:00
caishi c965da7dd5 默认 2021-03-22 09:47:34 +08:00
caishi b44399968f 协作者分页 2021-03-22 09:46:30 +08:00
caishi 2591f28ccc bug 2021-03-22 09:37:37 +08:00
caishi 5530e8c723 diff-查看文件 2021-03-10 10:48:08 +08:00
caishi fa26dc9fa5 diff-查看文件 2021-03-10 10:47:23 +08:00
caishi 4cf40f9dfc update 2021-03-09 15:33:18 +08:00
caishi 2c034f5dff 修改资料-url 2021-03-09 14:09:06 +08:00
caishi d68d8318c1 account 2021-03-09 13:59:02 +08:00
caishi 41ae6b1f8d 修改资料练级 2021-03-09 13:55:02 +08:00
caishi a6e2171fca filedetail 2021-03-09 11:49:48 +08:00
caishi 916cc293ac url 2021-03-08 11:15:05 +08:00
caishi 5565eac601 nodata 2021-03-08 09:55:12 +08:00
caishi 860b71c7c6 外围贡献者 2021-03-04 17:02:20 +08:00
caishi f1c2841fe0 logo 2021-03-03 16:46:45 +08:00
caishi c6d5078d42 Merge branch 'develop' into dev_m_copy 2021-03-02 11:43:58 +08:00
caishi ebe2d625fa Merge branch 'dev_military' of https://git.trustie.net/jasder/forgeplus-react into dev_military 2021-03-02 11:43:26 +08:00
caishi b5e1a91af5 style 2021-03-02 11:43:19 +08:00
caishi 4b8d72a6eb Merge branch 'develop' into dev_m_copy 2021-03-02 10:52:09 +08:00
caishi f0e1858cd4 update 2021-03-01 16:01:26 +08:00
caishi a92468953a style 2021-03-01 11:11:30 +08:00
caishi fdab967b6a Merge branch 'develop' into dev_m_copy
# Conflicts:
#	src/AppConfig.js
#	src/forge/DevOps/Index.jsx
#	src/forge/Index.js
#	src/forge/Main/Detail.js
#	src/forge/Main/IndexItem.js
#	src/forge/Merge/MessageCount.js
#	src/forge/Settings/Collaborator.js
#	src/forge/users/watch_users.js
#	src/modules/tpm/NewHeader.js
2021-03-01 10:21:14 +08:00
caishi 7e8929f166 appconfig 2021-03-01 10:17:38 +08:00
caishi 0a39ed80da Merge branch 'dev_m_copy' into dev_military
# Conflicts:
#	src/forge/Merge/MessageCount.js
2021-03-01 10:11:48 +08:00
caishi 5ed44f1d63 特殊项目审核 2021-02-26 16:58:31 +08:00
caishi 64e639ebea update 2021-02-26 11:53:08 +08:00
caishi 3fb9eb40f3 watch_users 2021-02-25 17:07:35 +08:00
caishi efc2443bb8 列表查询 2021-02-25 16:58:59 +08:00
caishi 2c3d917bd4 mygetHelmetapi2 2021-02-09 10:44:41 +08:00
caishi 617f139f52 fault 2021-02-04 15:13:08 +08:00
caishi de550d5f42 remove 2021-02-04 15:05:37 +08:00
caishi f12230dc91 url 2021-02-04 15:02:26 +08:00
caishi 1df2639cd5 pulls权限 2021-01-13 15:47:10 +08:00
caishi c746e9e634 pulls 2021-01-13 14:21:12 +08:00
caishi d8d464a332 pullrequest 2021-01-12 17:45:54 +08:00
caishi 83e337b2e9 pullrequest 2021-01-12 17:44:46 +08:00
caishi 5a6b7bd717 update 2021-01-12 09:30:30 +08:00
caishi c68a3dbd6f 项目成员访问特殊开源许可证项目不用上传文件 2021-01-11 18:14:59 +08:00
caishi 350f9426ea 工作流 2021-01-05 10:33:58 +08:00
caishi 5bda100e32 update 2021-01-05 09:23:58 +08:00
caishi dde7fa730a 同上-update 2021-01-04 16:43:15 +08:00
caishi 3f8f1b8083 特殊项目申请列表 2021-01-04 16:28:19 +08:00
caishi 6eef4bd09e 信息 2020-12-31 15:16:26 +08:00
caishi 0dcaea3db4 检测是否上传了文件 2020-12-31 14:13:31 +08:00
caishi 033134fa83 json 2020-12-29 16:28:08 +08:00
caishi 1bab0b01f7 debug 2020-12-29 16:18:20 +08:00
caishi 7b2f233cae 隐藏devops 2020-12-29 11:38:08 +08:00
caishi 2ecdd73c7f 外围贡献者 2020-12-28 20:42:12 +08:00
caishi 4e7a2fa3d7 Merge branch 'develop' into dev_m_copy
# Conflicts:
#	public/css/edu-purge.css
#	public/css/iconfont.css
#	src/AppConfig.js
#	src/forge/Main/CoderRootDirectory.js
#	src/forge/Main/Index.js
#	src/forge/Main/list.css
#	src/forge/Newfile/m_editor.js
#	src/forge/css/index.scss
#	src/modules/tpm/NewHeader.js
#	src/modules/tpm/TPMIndex.css
2020-12-24 17:50:26 +08:00
caishi 91662e2e3e back 2020-12-24 15:37:40 +08:00
sylor_huang@126.com c2129c994a change Header5 2020-09-16 15:14:46 +08:00
sylor_huang@126.com e968ece34c Change Header4 2020-09-16 15:08:54 +08:00
sylor_huang@126.com 70d407963e Change Header2 2020-09-16 14:58:37 +08:00
sylor_huang@126.com 00ccba74a1 Change Header1 2020-09-16 14:48:58 +08:00
sylor_huang@126.com 0790abb6f9 Change Header 2020-09-16 14:39:13 +08:00
caishi 6c4c161a1b Merge branch 'dev_military' of https://git.trustie.net/jasder/forgeplus-react into dev_military
# Conflicts:
#	src/AppConfig.js
2020-09-15 17:21:42 +08:00
caishi 65a2bd43cf route 2020-09-15 17:21:10 +08:00
sylor_huang@126.com 10f813a443 Change banner 2020-09-15 09:38:57 +08:00
sylor_huang@126.com e79ec30c81 Change Index Category8 2020-09-14 14:53:09 +08:00
sylor_huang@126.com 2a7fea3612 Change Index Category6 2020-09-14 14:44:41 +08:00
sylor_huang@126.com cc46a3ac30 Change Index Category5 2020-09-14 14:08:03 +08:00
sylor_huang@126.com cca5f98c9b Change Index Category4 2020-09-14 11:50:45 +08:00
sylor_huang@126.com d4535005c8 Change Index Category3 2020-09-14 11:28:20 +08:00
sylor_huang@126.com 73d128e0c9 Change Resume 2020-09-14 09:26:46 +08:00
sylor_huang@126.com f09457a0ac Change Index Category 2020-09-11 18:49:41 +08:00
sylor_huang@126.com 78c218b12b Change Index Category 2020-09-11 18:45:34 +08:00
sylor_huang@126.com d9f87fdd18 Change Index Page 2020-09-11 18:31:31 +08:00
caishi 33c3395221 getpath 2020-08-27 10:14:07 +08:00
sylor_huang@126.com 74d26a40d3 Change Edit Url 2020-08-24 11:37:51 +08:00
sylor_huang@126.com 3f78ed249c Fix:Issues 2020-08-21 11:29:05 +08:00
caishi 07e1525f09 router 2020-08-20 18:10:40 +08:00
sylor_huang@126.com a9161b86a2 Change 2020-08-20 15:01:01 +08:00
sylor_huang@126.com e0e6cdcc79 Fix: Issues 2020-08-20 11:57:36 +08:00
sylor_huang@126.com 3b0c708d82 Fix: Issues 2020-08-20 11:44:27 +08:00
sylor_huang@126.com 0922df3875 Hide LoginDialog 2020-08-19 18:18:03 +08:00
sylor_huang@126.com ad3fe09cfb Merge branch 'newVersion_forge' into dev_military 2020-08-18 17:43:24 +08:00
sylor_huang@126.com 91f4327eb4 Change 2020-08-18 17:43:04 +08:00
sylor_huang@126.com f2f910b5e4 Add Dun Check For Project 2020-08-18 10:36:33 +08:00
sylor_huang@126.com 85d924db70 Fix: Add New File Language Api Wrong6 2020-08-13 11:56:39 +08:00
Jasder f1614a4b62 Delete url 2020-08-10 11:27:41 +08:00
Jasder 2fa71241db FIX 更改header的背景色 2020-08-09 23:24:12 +08:00
Jasder 13c6556574 FIX 更改logo导航链接 2020-08-09 23:20:08 +08:00
caishi 5601b71937 junke 2020-08-09 23:08:11 +08:00
Jasder 1a024f8011 FIX 去掉使用手册 2020-08-09 22:20:51 +08:00
74 changed files with 5314 additions and 271 deletions

View File

@ -87,6 +87,7 @@
"react-infinite-scroller": "^1.2.4",
"react-loadable": "^5.3.1",
"react-monaco-editor": "0.37",
"react-pdf": "^4.2.0",
"react-player": "^1.15.3",
"react-redux": "5.0.7",
"react-resizable": "^1.10.1",

View File

@ -1,6 +1,5 @@
/*头部导航条样式---2018-03-19--by-cs*/
.newHeader {
background: #24292D !important;
width: 100%;
height: 60px !important;
min-width: 1200px;

View File

@ -75,6 +75,17 @@ const EducoderLogin = Loadable({
loading: Loading,
})
// 成果库
const Achieve = Loadable({
loader: () => import('./achieveRequirement/achieve'),
loading: Loading,
})
// 需求管理
const Requirement = Loadable({
loader: () => import('./achieveRequirement/requirement'),
loading: Loading,
})
class App extends Component {
constructor(props) {
super(props);
@ -259,7 +270,28 @@ class App extends Component {
(props) => {
return (<InfosIndex {...this.props} {...this.state} />)
}
}></Route>
}></Route>
{/*成果库*/}
<Route
path={"/achieve"}
render={
(props) => {
return (<Achieve {...this.props} {...props} {...this.state} />)
}
}>
</Route>
{/*需求管理/创客行动*/}
<Route
path={"/requirement"}
render={
(props) => {
return (<Requirement {...this.props} {...props} {...this.state} />)
}
}>
</Route>
<Route exact path="/"
render={
(props) => (
@ -267,7 +299,12 @@ class App extends Component {
)
}
/>
<Route component={Shixunnopage} />
</Switch>
</Router>
</MuiThemeProvider>

View File

@ -4,16 +4,14 @@ import { broadcastChannelOnmessage, isDev, queryString } from 'educoder';
import { notification } from 'antd';
import cookie from 'react-cookies';
import './index.css';
let message501 = false;
broadcastChannelOnmessage('refreshPage', () => {
window.location.reload();
window.location.reload()
})
function locationurl(list) {
if (window.location.port === "3007") {
} else {
if (window.location.port !== "3007") {
window.location.href = list
}
}
@ -27,14 +25,14 @@ if (isDev) {
}
debugType = window.location.search.indexOf('debug=t') !== -1 ? 'teacher' :
window.location.search.indexOf('debug=s') !== -1 ? 'student' :
window.location.search.indexOf('debug=a') !== -1 ? 'admin' : parsed.debug || 'admin'
window.location.search.indexOf('debug=a') !== -1 ? 'admin' : parsed.debug || 's'
}
function clearAllCookie() {
cookie.remove('_educoder_session', { path: '/' });
cookie.remove('autologin_trustie', { path: '/' });
setpostcookie()
}
clearAllCookie();
// clearAllCookie();
function setpostcookie() {
const str = window.location.pathname;
if (str.indexOf("/wxcode") !== -1) {
@ -54,9 +52,11 @@ setpostcookie();
window._debugType = debugType;
export function initAxiosInterceptors(props) {
initOnlineOfflineListener();
var proxy = "http://localhost:3000";
proxy = "https://testforgeplus.trustie.net";
initOnlineOfflineListener()
// TODO 避免重复的请求 https://github.com/axios/axios#cancellation
var
proxy = "http://localhost:3000";
proxy = "http://117.50.100.12:49999";
const requestMap = {};
window.setfalseInRequestMap = function (keyName) {
@ -68,7 +68,7 @@ export function initAxiosInterceptors(props) {
setpostcookie()
clearAllCookie()
if (config.url.indexOf(proxy) !== -1) {
if (config.url.indexOf(proxy) !== -1 || config.url.indexOf("http") !== -1) {
return config
}
requestProxy(config)
@ -90,7 +90,6 @@ export function initAxiosInterceptors(props) {
}
if (config.url.indexOf('update_file') === -1) {
requestMap[config.url] = true;
window.setTimeout("setfalseInRequestMap('" + config.url + "')", 900)
}
return config;

View File

@ -0,0 +1,97 @@
import React, { Component } from "react";
import { Route, Switch } from "react-router-dom";
import { withRouter } from "react-router";
import { SnackbarHOC } from "educoder";
import { CNotificationHOC } from "../modules/courses/common/CNotificationHOC";
import { TPMIndexHOC } from "../modules/tpm/TPMIndexHOC";
import Loadable from "react-loadable";
import Loading from "../Loading";
import { ImageLayerOfCommentHOC } from "../modules/page/layers/ImageLayerOfCommentHOC";
import './index.scss';
const AchieveIndex = Loadable({
loader: () => import("./achieve/achieveList"),
loading: Loading,
});
const AchieveDetail = Loadable({
loader: () => import("./achieve/achieveDetail"),
loading: Loading,
});
const AchieveMine = Loadable({
loader: () => import("./achieve/achieveMine"),
loading: Loading,
});
const AchieveAdd = Loadable({
loader: () => import("./achieve/achieveEdit"),
loading: Loading,
});
const AchieveEdit = Loadable({
loader: () => import("./achieve/achieveEdit"),
loading: Loading,
});
class Index extends Component {
render() {
return (
<div className="newMain clearfix">
<Switch {...this.props}>
<Route
path="/achieve/achieveDetail/:achieveId"
render={(props) => (
<AchieveDetail {...this.props} {...props} />
)}
></Route>
<Route
path="/achieve/achieveMine"
render={(props) => (
<AchieveMine {...this.props} {...props} />
)}
></Route>
<Route
path="/achieve/achieveAdd"
render={(props) => (
<AchieveAdd {...this.props} {...props} />
)}
></Route>
<Route
path="/achieve/achieveEdit/:achieveId"
render={(props) => (
<AchieveEdit {...this.props} {...props} />
)}
></Route>
<Route
path="/achieve"
render={(props) => (
<AchieveIndex {...this.props} {...props} />
)}
></Route>
<Route
path="/"
render={(props) => (
<AchieveIndex {...this.props} {...props} />
)}
></Route>
{/* <Route
exact
path="/"
render={(props) => (
this.props.current_user && this.props.current_user.login ?
<Infos {...this.props} {...props} />
:
<AchieveIndex {...this.props} {...props} />
)}
></Route> */}
</Switch>
</div>
);
}
}
export default withRouter(
ImageLayerOfCommentHOC({
imgSelector: ".imageLayerParent img, .imageLayerParent .imageTarget",
parentSelector: ".newMain",
})(CNotificationHOC()(SnackbarHOC()(TPMIndexHOC(Index))))
);

View File

@ -0,0 +1,142 @@
import React, { useEffect, useState } from 'react';
import { Descriptions, Modal } from 'antd';
import { Document, Page, pdfjs } from 'react-pdf';
import DelModal from '../../components/DelModal';
import { httpUrl } from '../../../fetch';
import { deleteAchieve, getAchieveDetail } from '../api';
import './index.scss';
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
export default (props) => {
const { match, history, current_user } = props;
const id = match.params.achieveId;
const [achiveForm, setAchiveForm] = useState({});
const [createUserId, setCreateUserId] = useState("");
const [visible, setVisible] = useState(false);
const [fileType, setFileType] = useState(4);
const [fileUrl, setFileUrl] = useState(4);
const [numPages, setNumPages] = useState(null);
useEffect(() => {
getAchieveDetail(id).then(data => {
setAchiveForm(data);
setCreateUserId(data.user.id);
})
}, [id]);
function editItem() {
props.history.push(`/achieve/achieveEdit/${id}`);
}
function deletItem() {
DelModal(() => {
deleteAchieve(id).then(res => {
if (res.code === '1') {
props.showNotification("删除成功");
history.go(-1);
} else {
props.showNotification("删除失败");
}
})
});
}
function downFile(item) {
let url = httpUrl + '/busiAttachments/download/' + item.id;
window.open(url);
}
function pdfShow(e) {
setFileType(e.fileType);
if (e.fileType === 1 || e.fileType === 2 || e.fileType === 3) {
// pdf||video||img
setVisible(true);
setFileUrl(httpUrl + '/busiAttachments/view/' + e.id);
}
}
function onDocumentLoadSuccess({ numPages }) {
setNumPages(numPages);
}
return (
<div className="centerbox">
<div className="center-content">
<div className="centerScreen">
<div className="center-left-but">{achiveForm.achievementName}</div>
{
createUserId === current_user.user_id &&
<div className="center-right-but" ><span onClick={editItem}>编辑</span> <span onClick={deletItem}>删除</span></div>
}
</div>
<Descriptions className="item-content" column={1}>
<Descriptions.Item label="成果类别">{achiveForm.achievementType && achiveForm.achievementType.dicItemName}</Descriptions.Item>
<Descriptions.Item label="标签">
{
achiveForm.achievementLabel && achiveForm.achievementLabel.map(itemx => {
return <span className="list-tag" key={itemx + 'ax'}>{itemx}</span>
})
}
</Descriptions.Item>
<Descriptions.Item label="单位名称">{achiveForm.achievementBelong}</Descriptions.Item>
<Descriptions.Item label="发布时间">{achiveForm.updatedAt}</Descriptions.Item>
<Descriptions.Item label="成果描述">{achiveForm.achievementRemark}</Descriptions.Item>
<Descriptions.Item label="成果文件" className="file-list-item">
{
achiveForm.achievementAttachments && achiveForm.achievementAttachments.map(item => {
return <div className="file-list-box" key={item.id + 'down'}>
<p onClick={() => { pdfShow(item) }}>{item.fileName}</p>
<span className="downP" onClick={() => { downFile(item) }}>下载</span>
</div>
})
}
</Descriptions.Item>
</Descriptions>
</div>
<Modal
visible={visible}
maskClosable={true}
closable={true}
footer={null}
onCancel={() => { setVisible(false) }}
className="file-modal"
>
{
fileType === 3 && <div >
<img className="show-img" src={fileUrl} alt="" />
</div>
}
{
fileType === 2 && <div>
<video src={fileUrl} style={{ width: '100%' }} controls="controls">您的浏览器不支持 video 标签</video>
</div>
}
{
fileType === 1 && <div className="pdf-box">
{
<Document
file={fileUrl}
onLoadSuccess={onDocumentLoadSuccess}
>
{new Array(numPages).fill("").map((item, index) => {
return (
<Page
loading=""
noData=""
key={index}
pageNumber={index + 1}
renderTextLayer={true}
/>
);
})}
</Document>
}
</div>
}
</Modal>
</div>
)
}

View File

@ -0,0 +1,33 @@
// 内容详情
.item-content {
padding: 10px 10px 0 30px;
}
.ant-descriptions-item-label {
width: 5em;
margin-right: 5px;
color: rgba(0, 0, 0, 0.65);
text-align: right;
}
.ant-descriptions-item-content {
color: rgba(0, 0, 0, 1);
}
.file-list-item > span {
vertical-align: top;
}
.file-list-box {
width: 600px;
height: 60px;
display: flex;
justify-content: space-between;
p:hover {
cursor: pointer;
color: #0089ff;
}
.downP {
color: #0089ff;
cursor: pointer;
text-decoration: none;
}
}

View File

@ -0,0 +1,236 @@
import React, { forwardRef, useCallback, useEffect, useState } from 'react';
import { Form, Input, Select, Button, } from 'antd';
import Upload from '../../components/Upload';
import { httpUrl } from '../../../fetch';
import { getDictionary, getAchieveDetail, addAchieve, editAchieve } from '../api';
import './index.scss';
const Option = Select.Option;
const { TextArea } = Input;
export default Form.create()(
forwardRef(({ form, showNotification, match, history }, ref) => {
const [shareType, setShareType] = useState([]);
const [tagType, setTagType] = useState([]);
const [fileList, setFileList] = useState(null);
const [achievementFileTypes, setAchievementFileTypes] = useState('');
const id = match.params.achieveId;
const { getFieldDecorator, validateFields, setFieldsValue } = form;
useEffect(() => {
id && getAchieveDetail(id).then(data => {
const formValue = {
achievementName: data.achievementName,
achievementTypeId: data.achievementTypeId,
achievementBelong: data.achievementBelong,
achievementLabel: data.achievementLabel,
achievementRemark: data.achievementRemark,
achievementFiles: data.achievementFiles
};
setFieldsValue(formValue);
if (data.achievementAttachments.length) {
for (const item of data.achievementAttachments) {
item.name = item.fileName;
item.size = item.fileSize;
item.uid = "rc-upload" + item.id
}
setFileList(data.achievementAttachments);
}
})
}, [id]);
//
useEffect(() => {
getDictionary('share_type').then((res) => {
setShareType(res.data);
});
getDictionary("tag_type").then((res) => {
setTagType(res.data);
});
}, []);
//
function UploadFunc(fileList) {
setFileList(fileList);
let files = [];
let fileTypes = [];
for (const item of fileList) {
files.push(item.id || item.response.data.id);
fileTypes.push(item.fileType || item.response.data.fileType);
}
setFieldsValue({
achievementFiles: files.join()
});
setAchievementFileTypes(fileTypes.join());
}
const helper = useCallback(
(label, name, rules, widget) => (
<Form.Item label={label}>
{getFieldDecorator(name, { rules, validateFirst: true })(widget)}
</Form.Item>
),
[]
);
const formItemLayout = {
labelCol: {
xs: { span: 12 },
sm: { span: 4 },
},
wrapperCol: {
xs: { span: 12 },
sm: { span: 16 },
},
};
const tailFormItemLayout = {
wrapperCol: {
xs: {
span: 20,
offset: 4,
},
sm: {
span: 20,
offset: 4,
},
},
};
//
function saveItem() {
validateFields((error, values) => {
if (!error) {
let params = {
...values,
achievementFileTypes,
};
params.achievementLabel = params.achievementLabel.join();
if (id) {
//
params.id = id;
editAchieve(params).then(res => {
if (res.code === '1') {
showNotification("成果更新成功!");
history.go(-1);
} else {
showNotification(res.message);
}
});
} else {
//
params.uploadUserId = 1;
addAchieve(params).then(res => {
if (res.code === '1') {
showNotification("成果新增成功!");
history.go(-1);
} else {
showNotification(res.message);
}
});
}
}
})
}
return (
<div className="centerbox">
<div className="center-content">
<div className="centerScreen">
<div className="center-left-but">{id ? '修改' : '新增'}成果</div>
</div>
<Form className="achieve-form" {...formItemLayout}>
{helper(
"成果名称:",
"achievementName",
[{ required: true, message: "请输入成果名称" }],
<Input
className="edit-input"
placeholder="请输入成果名称"
/>
)}
{helper(
"成果类别:",
"achievementTypeId",
[{ required: true, message: "请选择成果类别" }],
<Select placeholder="请选择成果类别"
className="edit-input"
>
{
shareType.map(item => {
return <Option key={item.dicItemName + item.id} value={item.id}>{item.dicItemName}</Option>
})
}
</Select>
)}
{helper(
"单位名称:",
"achievementBelong",
[{ required: true, message: "请输入单位名称" }],
<Input
className="edit-input"
placeholder="请输入单位名称"
/>
)}
<Form.Item label={"成果文件:"} required={true}>
<Upload
className="commentStyle"
load={UploadFunc}
size={100}
showNotification={showNotification}
actionUrl={httpUrl}
fileList={fileList}
/>
{/* 用一个隐藏的input实现上传文件的必填校验 */}
{getFieldDecorator('achievementFiles', {
rules: [{ required: true, message: "请上传成果文件" }], validateFirst: true
})(<Input style={{ display: 'none' }} />)}
</Form.Item>
{helper(
"标签:",
"achievementLabel",
[{ required: true, message: "请选择成果标签" }],
<Select
className="edit-input"
placeholder="请选择成果标签"
mode="tags"
tokenSeparators={[',']}
>
{
tagType.map(item => {
return <Option key={item.dicItemName}>{item.dicItemName}</Option>
})
}
</Select>
)}
{helper(
"成果描述:",
"achievementRemark",
[{ required: true, message: "请输入成果描述" }],
<TextArea
placeholder="请输入成果描述"
autoSize={{ minRows: 3, maxRows: 5 }}
/>
)}
<Form.Item {...tailFormItemLayout}>
<Button className="mr20" type={"primary"} onClick={saveItem}>保存</Button>
<Button onClick={() => { history.go(-1) }}>取消</Button>
</Form.Item>
</Form>
</div>
</div>
)
})
)

View File

@ -0,0 +1,4 @@
.achieve-form{
padding:24px 40px 0px 40px;
}

View File

@ -0,0 +1,148 @@
import React, { useEffect, useState } from 'react';
import classNames from 'classnames';
import { Pagination, Button, Icon } from 'antd';
import ChooseNav from '../../components/chooseNav';
import ItemList from '../../components/itemList';
import { getDictionary, getAchieveList } from '../api';
export default (props) => {
const { current_user } = props;
// console.log(current_user);
const [achievementTypeId, setAchievementTypeId] = useState(''); //
const [achievementFileTypes, setAchievementFileTypes] = useState(''); //
const [orderBy, setOrderBy] = useState('-1');
const [curPage, setCurPage] = useState(1);
const [total, setTotal] = useState(0);
const [industryOption, setIndustryOption] = useState([]);
const [achiveList, setAchiveList] = useState([]);
useEffect(() => {
getDictionary('share_type').then(res => {
if (res && res.data) setIndustryOption(res.data);
});
}, []);
useEffect(() => {
const params = {
achievementTypeId,
achievementFileTypes,
achievementLabel: '',
isOwned: '',
orderBy,
curPage,
pageSize: 10
};
getAchieveList(params).then(data => {
setAchiveList(data.rows);
setTotal(data.total);
})
}, [achievementTypeId, achievementFileTypes, orderBy, curPage]);
function changeOptionId(option, type) {
if (type === 'share_type') {
setAchievementTypeId(option.id);
setCurPage(1);
} else if (type === 'file_type') {
setAchievementFileTypes(option.id);
setCurPage(1);
}
}
function changeSort(sortType) {
setOrderBy(sortType);
setCurPage(1);
}
function goAchieveMine() {
props.history.push('/achieve/achieveMine');
}
function goAdd() {
props.history.push("/achieve/achieveAdd");
}
function achiveClick(id) {
props.history.push(`/achieve/achieveDetail/${id}`);
}
const filetypeOption = [
{
dicItemName: '文件',
id: 1
},
{
dicItemName: '视频',
id: 2
},
{
dicItemName: '图片',
id: 3
},
{
dicItemName: '软件',
id: 4
},
{
dicItemName: '其它',
id: 5
},
];
return (
<div className="centerbox">
<div className="nav-content">
<ChooseNav
key={'share_type'}
type={'share_type'}
title={'成果类别'}
options={industryOption}
changeOptionId={changeOptionId}
/>
<ChooseNav
key={'file_type'}
type={'file_type'}
title={'文件类型'}
options={filetypeOption}
changeOptionId={changeOptionId}
/>
</div>
<div className="center-content">
<div className="centerScreen">
<div className="center-left-but">
<div className={classNames({ 'center-left-butD': true, 'center-left-butDACT': orderBy === '-1' })} onClick={() => { changeSort('-1') }}>默认</div>
<div className={classNames({ 'center-left-butD': true, 'center-left-butDACT': orderBy === 'created_at' })} onClick={() => { changeSort('created_at') }}><Icon type="arrow-down" />最新</div>
<div className={classNames({ 'center-left-butD': true, 'center-left-butDACT': orderBy === 'view_count' })} onClick={() => { changeSort('view_count') }}><Icon type="arrow-down" />最热</div>
</div>
{
// current_user.login &&
<div className="center-right-but">
<Button className="mr15 font-12" size="small" type="primary" onClick={goAchieveMine}>我的成果</Button>
<Button className="mr20 font-12" size="small" type="primary" onClick={goAdd}><i className="iconfont icon-zaibianji font-12 mr3"></i>发布成果</Button>
</div>
}
</div>
<ItemList
list={achiveList}
itemClick={achiveClick}
/>
</div>
<div className="edu-txt-center ">
<Pagination
showQuickJumper
onChange={(page) => { setCurPage(page) }}
current={curPage}
total={total}
showTotal={total => `${total}`}
/>
</div>
</div>
)
}

View File

@ -0,0 +1,82 @@
import React, { useEffect, useState } from 'react';
import { Pagination } from 'antd';
import ItemList from '../../components/itemList';
import DelModal from '../../components/DelModal';
import { deleteAchieve, getAchieveList } from '../api';
export default (props) => {
const [achiveList, setAchiveList] = useState([]);
const [curPage, setCurPage] = useState(1);
const [total, setTotal] = useState(0);
useEffect(() => {
getAcheiveListOwn();
}, [curPage]);
function getAcheiveListOwn() {
const params = {
achievementTypeId: '',
achievementFileTypes: '',
achievementLabel: '',
// isOwned: '', //便
isOwned: true,
orderBy: -1,
curPage,
pageSize: 10
};
getAchieveList(params).then(data => {
setAchiveList(data.rows);
setTotal(data.total);
})
}
function achiveClick(id) {
props.history.push(`/achieve/achieveDetail/${id}`);
}
function editItem(id) {
props.history.push(`/achieve/achieveEdit/${id}`);
}
function deletItem(id) {
DelModal(() => {
deleteAchieve(id).then(res => {
if (res.code === '1') {
props.showNotification("删除成功");
getAcheiveListOwn();
} else {
props.showNotification("删除失败");
}
})
});
}
return (
<div className="centerbox">
<div className="center-content">
<div className="centerScreen">
<div className="center-left-but">
我的成果
</div>
</div>
<ItemList
list={achiveList}
itemClick={achiveClick}
deletItem={deletItem}
editItem={editItem}
/>
</div>
<div className="edu-txt-center ">
<Pagination
showQuickJumper
onChange={(page) => { setCurPage(page) }}
current={curPage}
total={total}
showTotal={total => `${total}`}
/>
</div>
</div>
)
}

View File

@ -0,0 +1,62 @@
import fetch from '../../fetch';
// 获取字典分类列表
export function getDictionary(id) {
return fetch({
url: '/dicItem/getData?dicTypeCode=' + id,
method: 'get'
});
}
// 成果列表查询
export async function getAchieveList(params) {
let res = await fetch({
url: '/api/achievement/',
method: 'get',
params,
})
if (res && res.data) {
for (const i of res.data.rows) {
// 处理分解标签
i.tag = i.achievementLabel.split(',');
}
}
return res.data;
}
//新增成果
export function addAchieve(data) {
return fetch({
url: '/api/achievement/',
method: 'post',
data: data
});
}
//删除成果
export function deleteAchieve(id) {
return fetch({
url: '/api/achievement/' + id,
method: 'DELETE',
});
}
//更新成果
export function editAchieve(data) {
return fetch({
url: '/api/achievement/',
method: 'put',
data: data
});
}
// 成果详情查询
export async function getAchieveDetail(id) {
let res = await fetch({
url: '/api/achievement/' + id,
method: 'get',
});
res.data.achievementLabel = res.data.achievementLabel.split(',');
return res.data;
}

View File

@ -0,0 +1,20 @@
import { Modal } from 'antd';
export default (
handleOk,
title,
content,
handleCancel) => {
return Modal.confirm({
title: title || "警告",
content: content || "确认删除?",
okText: '确定',
cancelText: '取消',
onOk() {
handleOk && handleOk();
},
onCancel() {
handleCancel && handleCancel();
}
});
}

View File

@ -0,0 +1,97 @@
import React, { useEffect, useState } from "react";
import { Upload, Button } from 'antd';
import axios from 'axios';
import { appendFileSizeToUploadFileAll } from 'educoder';
axios.defaults.withCredentials = true;
function Uploads({ className, size, actionUrl, fileList, showNotification, load }) {
const [files, setFiles] = useState(undefined);
useEffect(() => {
if (fileList) {
init();
}
}, [fileList]);
function init() {
let f = appendFileSizeToUploadFileAll(fileList);
setFiles(f);
}
function onAttachmentRemove(file) {
if (!file.percent || file.percent === 100) {
deleteAttachment(file);
return false;
}
}
function deleteAttachment(file) {
let id = (file.response && file.response.data && file.response.data.id) || file.id;
//
let nf = files.filter(item => {
let itemId = (item.response && item.response.data && item.response.data.id) || item.id;
return itemId !== id;
});
setFiles(nf);
// fileIdList(nf);
load && load(nf);
//
// const url = actionUrl + `/busiAttachments/${id}`;
// axios.delete(url).then((response) => {
// if (response.data) {
// if (response.data.code === "1") {
// let nf = files.filter(item => (item.response.data.id !== id) && (item.id !== id));
// setFiles(nf);
// fileIdList(nf);
// } else {
// showNotification(response.data.message);
// }
// }
// }).catch(function (error) {
// console.log(error);
// });
}
function handleChange(info) {
if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
let fileList = info.fileList;
setFiles(appendFileSizeToUploadFileAll(fileList));
if (info.file.status === 'done' || info.file.status === 'removed') load && load(fileList);
}
}
// function fileIdList(fileList) {
// let id = [];
// let fileType = [];
// for (const item of fileList) {
// id.push(item.id || item.response.data.id);
// fileType.push(item.fileType || item.response.data.fileType);
// }
// load && load(id.join(), fileType.join());
// }
function beforeUpload(file) {
const isLt100M = file.size / 1024 / 1024 < size;
if (!isLt100M) {
showNotification(`文件大小必须小于${size}MB!`);
}
return isLt100M;
}
const upload = {
name: 'file',
fileList: files,
action: actionUrl + `/busiAttachments/upload`,
onChange: handleChange,
onRemove: onAttachmentRemove,
beforeUpload: beforeUpload,
};
return (
<Upload {...upload} className={className}>
<Button type="primary">点击上传</Button>
<span className="ml10 color-grey-9">(你可以上传小于<span className="color-red">{size}MB</span>的文件)</span>
</Upload>
)
}
export default Uploads;

View File

@ -0,0 +1,29 @@
import React, { useEffect, useState } from 'react';
import classNames from 'classnames';
import './index.scss';
export default (props) => {
const { title, options, changeOptionId, type } = props;
const [option, setOption] = useState({ id: "", dicItemName: "" ,dicItemCode:""});
useEffect(() => {
changeOptionId(option, type);
}, [option])
return (
<div className="shop-box">
<div className="choose-title">{title}</div>
<div className="choose-list">
<div className={classNames({ "choose-item-checked": option.id === "", "choose-item": true })} key={"all"} onClick={() => { setOption({ id: "", dicItemName: "" }) }}>全部</div>
{
options.map((item) => {
return <div className={classNames({ "choose-item-checked": option.id === item.id, "choose-item": true })} key={item.dicItemName} onClick={() => { setOption(item) }} >{item.dicItemName}</div>
})
}
</div>
</div>
)
}

View File

@ -0,0 +1,41 @@
.nav-content {
margin-top:20px;
background: #fff;
border-bottom: 1px solid #eaeaea;
box-shadow: #ddd 0px 0px 5px;
}
.shop-box {
width: 1280px;
border-top: 1px solid #eaeaea;
display: flex;
justify-content: start;
}
.choose-title {
width: 110px;
background: #f3f3f3;
text-align: center;
padding: 16px 0;
font-size: 14px;
color: #666;
font-weight: 600;
}
.choose-list {
width: 1170px;
padding: 16px 0;
display: flex;
justify-content: start;
}
.choose-item {
font-size: 14px;
text-align: center;
padding: 0 15px ;
cursor: pointer;
&:hover{
color: #0072ff;
}
}
.choose-item-checked {
color: #0072ff;
}

View File

@ -0,0 +1,42 @@
import React from 'react';
import './index.scss';
const statusArr = ['待审核', '进行中', '成果验收中', '完成', '审核未通过', '延期', '其他', '验收未通过'];
const classArr = ['', 'list-done', 'list-done', 'list-done', 'list-error', '', '', 'list-error'];
export default (props) => {
const { list, itemClick, editItem, deletItem, activeByStatus } = props;
return (
list.map(item => {
return (
<div className="list-box" key={item.id}>
<div className="list-title" onClick={() => { itemClick(item.id) }}>
{item.achievementName || item.title}
{item.status && <span className={classArr[item.status]}>{statusArr[item.status]}</span>}
</div>
<div >
{
item.tag.map(i => {
return <span className="list-tag" key={i + "av"}>{i}</span>;
})
}
</div>
<div className="list-other">
{item.user && <p>发布者{item.user.nickname ? item.user.nickname : item.user.login}</p>}
{item.createUser && <p>发布者{item.createUser.nickname ? item.createUser.nickname : item.createUser.login}</p>}
<p>发布时间{item.achievementPublishTime || item.createTime}</p>
</div>
{
editItem && <div className="list-box-action">
{
!activeByStatus || (activeByStatus && item.status == '0' || item.status == '4' || item.status == '5') &&
<button onClick={() => { editItem(item.id) }}><i className="iconfont icon-zaibianji font-25"></i></button>
}
<button onClick={() => { deletItem(item.id) }}><i className="iconfont icon-shanchu font-30 "></i></button>
</div>
}
</div>
)
})
)
}

View File

@ -0,0 +1,70 @@
.list-box {
position: relative;
padding: 20px;
background: #fff;
border-bottom: 1px solid #dedede;
}
.list-title {
font-size: 17px;
color: #000;
cursor: pointer;
}
.list-title:hover {
color: #409eff;
}
.list-title span {
padding: 3px 5px;
margin-left:.5em;
background: #f8c753;
font-size: 13px;
color: #fff;
border-radius: 3px;
}
span.list-done {
background: #35d77e;
}
span.list-error {
background: #f56c6c;
}
.list-tag {
display: inline-block;
margin: 5px 10px 5px 0;
background: #cfe9ff;
color: #0089ff;
font-size: 14px;
border-radius: 3px;
padding: 2px 5px;
}
.list-other > p {
display: inline-block;
margin: 0 10px 0 0;
font-size: 12px;
color: #666;
}
.list-box-action {
position: absolute;
display: flex;
align-items: center;
top: 0;
bottom: 0;
right: 100px;
visibility: hidden;
}
.list-box:hover .list-box-action {
visibility: visible;
}
.list-box-action button {
display: inline-block;
margin: 0 25px;
width: 50px;
height: 50px;
color: #0089ff;
line-height: 50px;
text-align: center;
border: 0;
border-radius: 25px;
box-shadow: 2px 2px 5px 2px #eee;
cursor: pointer;
}

View File

@ -0,0 +1,100 @@
// 本模块公共样式
.centerbox {
width: 80vw;
max-width: 1280px;
margin: 40px auto;
}
.center-content {
margin: 20px 0;
background: #fff;
border: 1px solid #dedede;
box-shadow: #eee 0px 1px 1px 3px;
}
.centerScreen {
display: flex;
justify-content: space-between;
height: 46px;
background-color: #fff;
border-bottom: 1px solid #dedede;
}
// 内容标题左侧样式
.center-left-but {
display: flex;
justify-content: start;
align-items: center;
margin-left: 20px;
font-size: 16px;
font-weight: 600;
}
.center-left-butD {
height: 24px;
font-size: 14px;
font-weight: 500;
line-height: 24px;
color: #333;
padding: 0 10px;
background-color: #fff;
border: 1px solid #dedede;
}
.center-left-butD:hover {
background-color: #fff;
color: #409eff;
cursor: pointer;
}
.center-left-butDACT {
background-color: #409eff;
color: #fff;
}
// 内容标题右侧样式
.center-right-but {
display: flex;
align-items: center;
}
.center-right-but > span {
margin: 0 10px;
font-size: 16px;
color: #0089ff;
cursor: pointer;
}
// 通用标签样式
.list-tag {
display: inline-block;
margin-right: 10px;
background: #cfe9ff;
color: #0089ff;
font-size: 14px;
border-radius: 3px;
padding: 2px 5px;
line-height: 25px;
}
// 文件预览modal样式
.file-modal {
width: 800px !important;
.ant-modal-body {
padding-top: 40px;
.pdf-box {
height: 70vh;
overflow-y: scroll;
}
}
.ant-modal-close {
top: 0 !important;
}
.show-img {
width: 100%;
}
.react-pdf__Page__canvas,
.react-pdf__Page__textContent {
margin: 0 auto;
max-width: 100%;
}
}
.edit-input {
max-width: 500px;
}

View File

@ -0,0 +1,95 @@
import React, { Component } from "react";
import { Route, Switch } from "react-router-dom";
import { withRouter } from "react-router";
import { SnackbarHOC } from "educoder";
import { CNotificationHOC } from "../modules/courses/common/CNotificationHOC";
import { TPMIndexHOC } from "../modules/tpm/TPMIndexHOC";
import Loadable from "react-loadable";
import Loading from "../Loading";
import { ImageLayerOfCommentHOC } from "../modules/page/layers/ImageLayerOfCommentHOC";
import './index.scss';
const RequireList = Loadable({
loader: () => import("./requirement/requireList"),
loading: Loading,
});
const RequireDetail = Loadable({
loader: () => import("./requirement/requireDetail"),
loading: Loading,
});
const RequireMine = Loadable({
loader: () => import("./requirement/requireMine"),
loading: Loading,
});
const RequireAdd = Loadable({
loader: () => import("./requirement/requireEdit"),
loading: Loading,
});
const RequireEdit = Loadable({
loader: () => import("./requirement/requireEdit"),
loading: Loading,
});
class Index extends Component {
render() {
return (
<div className="newMain clearfix">
<Switch {...this.props}>
<Route
path="/requirement/requireDetail/:requireId"
render={(props) => (
<RequireDetail {...this.props} {...props} />
)}
></Route>
<Route
path="/requirement/requireMine"
render={(props) => (
<RequireMine {...this.props} {...props} />
)}
></Route>
<Route
path="/requirement/requireAdd"
render={(props) => (
<RequireAdd {...this.props} {...props} />
)}
></Route>
<Route
path="/requirement/requireEdit/:requireId"
render={(props) => (
<RequireEdit {...this.props} {...props} />
)}
></Route>
<Route
path="/requirement"
render={(props) => (
<RequireList {...this.props} {...props} />
)}
></Route>
{/* <Route
exact
path="/"
render={(props) => (
this.props.current_user && this.props.current_user.login ?
<Infos {...this.props} {...props} />
:
<RequireIndex {...this.props} {...props} />
)}
></Route> */}
</Switch>
</div>
);
}
}
export default withRouter(
ImageLayerOfCommentHOC({
imgSelector: ".imageLayerParent img, .imageLayerParent .imageTarget",
parentSelector: ".newMain",
})(CNotificationHOC()(SnackbarHOC()(TPMIndexHOC(Index))))
);

View File

@ -0,0 +1,71 @@
import fetch from '../../fetch';
// 获取字典分类列表
export function getDictionary(id) {
return fetch({
url: '/dicItem/getData?dicTypeCode=' + id,
method: 'get'
});
}
// 需求列表查询
export async function getRequirementList(data) {
let res = await fetch({
url: '/requirement/page',
method: 'post',
data,
});
if (res && res.data && res.data.rows) {
for (const i of res.data.rows) {
// 处理分解标签
i.tag = i.tag.split(',');
}
}
return res.data;
}
//新增需求
export function addRequirement(data) {
return fetch({
url: '/requirement/add',
method: 'post',
data: data
});
}
//删除需求
export function deleteRequirement(id) {
return fetch({
url: '/requirement/delete?id=' + id,
method: 'DELETE',
});
}
//更新需求
export function updateRequirement(data) {
return fetch({
url: '/requirement/update',
method: 'put',
data: data
});
}
// 需求详情查询
export async function getRequirementDetail(id) {
let res = await fetch({
url: '/requirement/getInfo?id=' + id,
method: 'get',
});
if (res && res.data) {
res.data.tag = res.data.tag.split(',');
return res.data;
}
}
//获取用户列表
export function getAllUsers() {
return fetch({
url: '/user/getUsers',
method: 'get'
});
}

View File

@ -0,0 +1,217 @@
import React, { useEffect, useState } from 'react';
import { Descriptions, Modal, Button } from 'antd';
import { Document, Page, pdfjs } from 'react-pdf';
import DelModal from '../../components/DelModal';
import Upload from '../../components/Upload';
import { httpUrl } from '../../../fetch';
import { deleteRequirement, getRequirementDetail, updateRequirement } from '../api';
import './index.scss';
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
export default ({ match, history, current_user, showNotification }) => {
const id = match.params.requireId;
const [requireForm, setRequireForm] = useState({});
const [visible, setVisible] = useState(false);
const [visibleAchieve, setVisibleAchieve] = useState(false);
const [fileType, setFileType] = useState(4);
const [fileUrl, setFileUrl] = useState(4);
const [numPages, setNumPages] = useState(null);
const [fileList, setFileList] = useState(null);
const [edit, setEdit] = useState(false);
useEffect(() => {
getRequirementDetail(id).then(data => {
if (data) {
setRequireForm(data);
if (data.achievementList) {
for (const item of data.achievementList) {
item.name = item.fileName;
item.size = item.fileSize;
item.uid = "rc-upload" + item.id
}
setFileList(data.achievementList);
}
}
});
}, [id, edit]);
function editItem() {
history.push(`/requirement/requireEdit/${id}`);
}
function deletItem() {
DelModal(() => {
deleteRequirement(id).then(res => {
if (res.code === '1') {
showNotification("删除成功");
history.go(-1);
} else {
showNotification("删除失败");
}
})
});
}
function downFile(item) {
let url = httpUrl + '/busiAttachments/download/' + item.id;
window.open(url);
}
function pdfShow(e) {
setFileType(e.fileType);
if (e.fileType === 1 || e.fileType === 2 || e.fileType === 3) {
// pdf||video||img
setVisible(true);
setFileUrl(httpUrl + '/busiAttachments/view/' + e.id);
}
}
function onDocumentLoadSuccess({ numPages }) {
setNumPages(numPages);
}
//
function UploadFunc(fileList) {
setFileList(fileList);
}
function uploadAchievement() {
let files = [];
let filestype = [];
for (const item of fileList) {
files.push(item.id || item.response.data.id);
filestype.push(item.fileType || item.response.data.fileType);
}
let data = {
id,
achievements: files.toString(),
// createUserAccount: sessionStorage.getItem('SET_Account'),//
// createUserName: sessionStorage.getItem('SET_NAME'),
status: "2"
}
updateRequirement(data).then(res => {
if (res.code === '1') {
showNotification('更新成果文件成功!');
setEdit(!edit);
setVisibleAchieve(false);
}
})
}
return (
<div className="centerbox">
<div className="center-content">
<div className="centerScreen">
<div className="center-left-but">{requireForm.title}</div>
{
<div className="center-right-but">
{
// (role||operator||needsForm.assignedUserLogin==userid||needsForm.createUserAccount==userid)&&(needsForm.status==1||needsForm.status==2||needsForm.status==7||needsForm.status==5)
((current_user.admin || current_user.operator || requireForm.assignedUserLogin == current_user.login || requireForm.createUserAccount == current_user.login)
&& (requireForm.status == 1 || requireForm.status == 2 || requireForm.status == 7 || requireForm.status == 5)) &&
<Button className="mr15 font-12" size="small" type="primary" onClick={() => { setVisibleAchieve(true) }}>上传成果</Button>
}
{
requireForm.createUserAccount === current_user.login &&
<React.Fragment>
<span onClick={editItem}>编辑</span>
<span onClick={deletItem}>删除</span>
</React.Fragment>
}
</div>
}
</div>
<Descriptions className="item-content" column={1}>
<Descriptions.Item label="需求类别">{requireForm.typeName}</Descriptions.Item>
<Descriptions.Item label="标签">
{
requireForm.tag && requireForm.tag.map(itemx => {
return <span className="list-tag" key={itemx + 'ax'}>{itemx}</span>
})
}
</Descriptions.Item>
<Descriptions.Item label="指派人员">{requireForm.assignedUserName}</Descriptions.Item>
<Descriptions.Item label="结束时间">{requireForm.endTime}</Descriptions.Item>
<Descriptions.Item label="需求描述">{requireForm.remark}</Descriptions.Item>
<Descriptions.Item label="需求文件" className="file-list-item">
{
requireForm.attachmentList && requireForm.attachmentList.map(item => {
return <div className="file-list-box" key={item.id + 'down'}>
<p onClick={() => { pdfShow(item) }}>{item.fileName}</p>
<span className="downP" onClick={() => { downFile(item) }}>下载</span>
</div>
})
}
</Descriptions.Item>
</Descriptions>
</div>
<Modal
visible={visible}
maskClosable={true}
closable={true}
footer={null}
onCancel={() => { setVisible(false) }}
className="file-modal"
>
{
fileType === 3 && <div >
<img className="show-img" src={fileUrl} alt="" />
</div>
}
{
fileType === 2 && <div>
<video src={fileUrl} style={{ width: '100%' }} controls="controls">您的浏览器不支持 video 标签</video>
</div>
}
{
fileType === 1 && <div className="pdf-box">
{
<Document
file={fileUrl}
onLoadSuccess={onDocumentLoadSuccess}
>
{new Array(numPages).fill("").map((item, index) => {
return (
<Page
loading=""
noData=""
key={index}
pageNumber={index + 1}
renderTextLayer={true}
width={750}
/>
);
})}
</Document>
}
</div>
}
</Modal>
<Modal
visible={visibleAchieve}
maskClosable={true}
closable={true}
onCancel={() => { setVisibleAchieve(false) }}
onOk={uploadAchievement}
className="upload-modal"
>
<Upload
className="commentStyle"
load={UploadFunc}
size={100}
showNotification={showNotification}
actionUrl={httpUrl}
fileList={fileList}
/>
</Modal>
</div>
)
}

View File

@ -0,0 +1,40 @@
// 内容详情
.item-content {
padding: 10px 10px 0 30px;
}
.ant-descriptions-item-label {
width: 5em;
margin-right: 5px;
color: rgba(0, 0, 0, 0.65);
text-align: right;
}
.ant-descriptions-item-content {
color: rgba(0, 0, 0, 1);
}
.file-list-item > span {
vertical-align: top;
}
.file-list-box {
width: 600px;
height: 60px;
display: flex;
justify-content: space-between;
p:hover {
cursor: pointer;
color: #0089ff;
}
.downP {
color: #0089ff;
cursor: pointer;
text-decoration: none;
}
}
.upload-modal{
width: 800px !important;
.commentStyle{
display: inline-block;
width: 100%;
}
}

View File

@ -0,0 +1,247 @@
import React, { forwardRef, useCallback, useEffect, useState } from 'react';
import { Form, Input, Select, Button, DatePicker } from 'antd';
import moment from 'moment';
import Upload from '../../components/Upload';
import { httpUrl } from '../../../fetch';
import { getDictionary, getRequirementDetail, addRequirement, updateRequirement } from '../api';
import './index.scss';
const Option = Select.Option;
const { TextArea } = Input;
const format = "YYYY-MM-DD HH:mm:ss";
export default Form.create()(forwardRef(({current_user, form, showNotification, match, history }, ref) => {
const [requireType, setRequireType] = useState([]);
const [tagType, setTagType] = useState([]);
const [fileList, setFileList] = useState(null);
const [typeCode, setTypeCode] = useState('');
const [typeName, setTypeName] = useState('');
const id = match.params.requireId;
const { getFieldDecorator, validateFields, setFieldsValue, } = form;
useEffect(() => {
id && getRequirementDetail(id).then(data => {
const formValue = {
title: data.title,
attachments: data.attachments,
// typeCode: data.typeCode, //使select labelInValue
endTime: moment(data.endTime),
tag: data.tag,
remark: data.remark
};
setFieldsValue(formValue);
setTypeName(data.typeName);
setTypeCode(data.typeCode)
if (data.attachmentList.length) {
for (const item of data.attachmentList) {
item.name = item.fileName;
item.size = item.fileSize;
item.uid = "rc-upload" + item.id
}
setFileList(data.attachmentList);
}
})
}, [id]);
//
useEffect(() => {
getDictionary('requirement_type').then((res) => {
setRequireType(res.data);
});
getDictionary("tag_type").then((res) => {
setTagType(res.data);
});
}, []);
//
function UploadFunc(fileList) {
setFileList(fileList);
let files = [];
for (const item of fileList) {
files.push(item.id || item.response.data.id);
}
setFieldsValue({
attachments: files.join()
});
}
const helper = useCallback(
(label, name, rules, widget, initialValue) => (
<Form.Item label={label}>
{getFieldDecorator(name, { rules, initialValue, validateFirst: true, })(widget)}
</Form.Item>
), []);
const formItemLayout = {
labelCol: {
xs: { span: 12 },
sm: { span: 4 },
},
wrapperCol: {
xs: { span: 12 },
sm: { span: 16 },
},
};
const tailFormItemLayout = {
wrapperCol: {
xs: {
span: 20,
offset: 4,
},
sm: {
span: 20,
offset: 4,
},
},
};
//
function saveItem() {
validateFields((error, values) => {
if (!error) {
let params = {
...values,
createUserAccount:current_user.login|| sessionStorage.getItem("SET_Account"), //
createUserName:current_user.username|| sessionStorage.getItem("SET_NAME"), //使
typeName,
typeCode,
status: 0, //
};
params.tag = params.tag.join();
params.endTime = params.endTime.format(format);
if (id) {
//
params.id = id;
updateRequirement(params).then(res => {
if (res.code === '1') {
showNotification("需求更新成功!");
history.go(-1);
} else {
showNotification(res.message);
}
});
} else {
//
addRequirement(params).then(res => {
if (res.code === '1') {
showNotification("需求新增成功!");
history.go(-1);
} else {
showNotification(res.message);
}
});
}
}
})
}
function changeTypeName(value) {
setTypeCode(value.key);
setTypeName(value.label);
}
return (
<div className="centerbox">
<div className="center-content">
<div className="centerScreen">
<div className="center-left-but">{id ? '修改' : '新增'}需求</div>
</div>
<Form className="achieve-form" {...formItemLayout}>
{helper(
"需求名称:",
"title",
[{ required: true, message: "请输入需求名称" }],
<Input
className="edit-input"
placeholder="请输入需求名称"
/>
)}
{helper(
"需求类型:",
"typeCode",
[{ required: true, message: "请选择需求类别" }],
<Select placeholder="请选择需求类别"
className="edit-input"
labelInValue
onChange={changeTypeName}
>
{
requireType.map(item => {
return <Option key={item.id + ''} value={item.id + ''}>{item.dicItemName}</Option>
})
}
</Select>
, { key: typeCode, label: typeName })}
{helper(
"结束时间:",
"endTime",
[{ required: true, message: "请输入结束时间" }],
<DatePicker
showTime
format={format}
placeholder="请输入结束时间"
/>
)}
<Form.Item label={"需求文件:"} required={true}>
<Upload
className="commentStyle"
load={UploadFunc}
size={100}
showNotification={showNotification}
actionUrl={httpUrl}
fileList={fileList}
/>
{/* 用一个隐藏的input实现上传文件的必填校验 */}
{getFieldDecorator('attachments', {
rules: [{ required: true, message: "请上传需求文件" }], validateFirst: true
})(<Input style={{ display: 'none' }} />)}
</Form.Item>
{helper(
"标签:",
"tag",
[{ required: true, message: "请选择需求标签" }],
<Select
placeholder="请选择需求标签"
className="edit-input"
mode="tags"
tokenSeparators={[',']}
>
{
tagType.map(item => {
return <Option key={item.dicItemName}>{item.dicItemName}</Option>
})
}
</Select>
)}
{helper(
"需求描述:",
"remark",
[{ required: true, message: "请输入需求描述" }],
<TextArea
placeholder="请输入需求描述"
autoSize={{ minRows: 3, maxRows: 5 }}
/>
)}
<Form.Item {...tailFormItemLayout}>
<Button className="mr20" type={"primary"} onClick={saveItem}>保存</Button>
<Button onClick={() => { history.go(-1) }}>取消</Button>
</Form.Item>
</Form>
</div>
</div>
)
})
)

View File

@ -0,0 +1,4 @@
.achieve-form{
padding:24px 40px 0px 40px;
}

View File

@ -0,0 +1,117 @@
import React, { useEffect, useState } from 'react';
import { Pagination, Button } from 'antd';
import ChooseNav from '../../components/chooseNav';
import ItemList from '../../components/itemList';
import { getDictionary, getRequirementList } from '../api';
export default (props) => {
const { current_user } = props;
// console.log(current_user);
const [requirementType, setrequirementType] = useState([]); //
const [requirementStatusArr, setrequirementStatusArr] = useState([]); //
const [requirementTypeName, setRequirementTypeName] = useState(''); //
const [requirementStatus, setRequirementStatus] = useState(''); //
const [curPage, setCurPage] = useState(1);
const [total, setTotal] = useState(0);
const [requirementList, setRequirementList] = useState([]);
useEffect(() => {
getDictionary('requirement_type').then(res => {
if (res && res.data) setrequirementType(res.data);
});
getDictionary('requirement_status').then(res => {
if (res && res.data) setrequirementStatusArr(res.data);
});
}, []);
useEffect(() => {
const params = {
tag: null,
status: requirementStatus || null,
typeName: requirementTypeName || null,
queryType: 'all',
curPage,
pageSize: 10
};
getRequirementList(params).then(data => {
if (data && data.rows) {
setRequirementList(data.rows);
setTotal(data.total);
}
})
}, [requirementTypeName, requirementStatus, curPage]);
function changeOptionId(option, type) {
if (type === 'requirement_type') {
setRequirementTypeName(option.dicItemName);
setCurPage(1);
} else if (type === 'requirement_status') {
setRequirementStatus(option.dicItemCode);
setCurPage(1);
}
}
function goRequirementMine() {
props.history.push('/requirement/requireMine');
}
function goAdd() {
props.history.push("/requirement/requireAdd");
}
function achiveClick(id) {
props.history.push(`/requirement/requireDetail/${id}`);
}
return (
<div className="centerbox">
<div className="nav-content">
<ChooseNav
key={'requirement_type'}
type={'requirement_type'}
title={'需求类别'}
options={requirementType}
changeOptionId={changeOptionId}
/>
<ChooseNav
key={'requirement_status'}
type={'requirement_status'}
title={'需求状态'}
options={requirementStatusArr}
changeOptionId={changeOptionId}
/>
</div>
<div className="center-content">
<div className="centerScreen">
<div className="center-left-but">需求大厅</div>
{
current_user.login &&
<div className="center-right-but">
<Button className="mr15 font-12" size="small" type="primary" onClick={goRequirementMine}>我的需求</Button>
<Button className="mr20 font-12" size="small" type="primary" onClick={goAdd}><i className="iconfont icon-zaibianji font-12 mr3"></i>发布需求</Button>
</div>
}
</div>
<ItemList
list={requirementList}
itemClick={achiveClick}
/>
</div>
<div className="edu-txt-center ">
<Pagination
showQuickJumper
onChange={(page) => { setCurPage(page) }}
current={curPage}
total={total}
showTotal={total => `${total}`}
/>
</div>
</div>
)
}

View File

@ -0,0 +1,333 @@
import React, { useEffect, forwardRef, useCallback, useState } from 'react';
import classnames from 'classnames';
import { Form, Radio, Input, Select, Pagination, Table, Button, Modal } from 'antd';
import DelModal from '../../components/DelModal';
import { deleteRequirement, getRequirementList, getAllUsers, updateRequirement } from '../api';
const Option = Select.Option;
const { TextArea } = Input;
const statusArr = ['待审核', '进行中', '成果验收中', '完成', '审核未通过', '延期', '其他', '验收未通过'];
const chooseStatusArr = ['待审核', '进行中', '成果验收中', '完成', '审核未通过', '延期', '全部'];
const statusColor = ['#f8c753', '#67c23a', '#67c23a', '#67c23a', '#F56C6C', '#f8c753', '#f8c753', '#F56C6C'];
export default Form.create()(forwardRef(({ form, showNotification, match, history }, ref) => {
const [requirementList, setRequirementList] = useState([]);
const [curPage, setCurPage] = useState(1);
const [total, setTotal] = useState(0);
const [status, setStatus] = useState(6);
const [checkVisible, setCheckVisible] = useState(false);
const [checkStatus, setCheckStatus] = useState(1);
const [acceptVisible, setAcceptVisible] = useState(false);
const [currentId, setCurrentId] = useState('');
const [allUsers, setAllUsers] = useState([]);
const { getFieldDecorator, validateFields, } = form;
useEffect(() => {
getRequireList();
}, [curPage, status]);
useEffect(() => {
if (checkVisible) {
getAllUsers().then(res => {
if (res.code === '1') {
setAllUsers(res.data);
} else {
showNotification(res.message);
}
})
}
}, [checkVisible]);
function getRequireList() {
let searchStaus = "";
if (status !== 6) {
searchStaus = status;
}
const params = {
curPage,
pageSize: 10,
status: searchStaus,
tag: "",
title: "",
typeName: "",
currentUser: "",
queryType: "all" //all: my: tome:
};
getRequirementList(params).then(data => {
if (data) {
for (const item of data.rows) {
item.createUserName = item.createUser.nickname ? item.createUser.nickname : item.createUser.login;
}
setRequirementList(data.rows);
setTotal(data.total);
}
})
}
function detailItem(id) {
history.push(`/requirement/requireDetail/${id}`);
}
function editItem(id) {
history.push(`/requirement/requireEdit/${id}`);
}
function deletItem(id) {
DelModal(() => {
deleteRequirement(id).then(res => {
if (res.code === '1') {
showNotification("删除成功");
getRequireList();
} else {
showNotification("删除失败");
}
})
}, '此操作将删除该记录, 是否继续?');
}
//
function checkItem() {
validateFields((error, values) => {
if (!error) {
let params = {
id: currentId,
status: checkStatus, //
assignedUserLogin: values.vertifyUserAccount ? values.vertifyUserAccount.key : '',
assignedUserName: values.vertifyUserAccount ? values.vertifyUserAccount.label : '',
vertifyUserAccount: values.vertifyUserAccount ? values.vertifyUserAccount.key : '',
vertifyUserName: values.vertifyUserAccount ? values.vertifyUserAccount.label : '',
vertifyComments: values.vertifyComments,
};
console.log(params);
updateRequirement(params).then(res => {
if (res.code === '1') {
showNotification("操作成功!");
setCheckVisible(false);
getRequireList();
} else {
showNotification(res.message);
}
});
}
})
}
//
function acceptItem() {
validateFields((error, values) => {
if (!error) {
let params = {
status: values.status,
id: currentId,
checkComments: values.checkComments,
};
console.log(params);
updateRequirement(params).then(res => {
if (res.code === '1') {
showNotification("操作成功!");
setAcceptVisible(false);
getRequireList();
} else {
showNotification(res.message);
}
});
}
})
}
const columns = [
{
title: '需求标题',
dataIndex: 'title',
key: 'title',
width: '20%',
},
{
title: '类型',
dataIndex: 'typeName',
key: 'typeName',
},
{
title: '创建人',
dataIndex: 'createUserName',
key: 'createUserName',
},
{
title: '指派人',
key: 'assignedUserName',
dataIndex: 'assignedUserName',
},
{
title: '创建时间',
key: 'createTime',
dataIndex: 'createTime',
},
{
title: '结束时间',
key: 'endTime',
dataIndex: 'endTime',
},
{
title: '状态',
key: 'status',
dataIndex: 'status',
render: (text, record) => {
return <span style={{ color: statusColor[text] }}>{statusArr[text]}</span>
}
},
{
title: '操作',
key: 'action',
render: (text, record) => (
<span>
<Button className="mr5 font-12" type="primary" size="small" onClick={() => { editItem(record.id) }}><i className="iconfont icon-zaibianji font-15 mr3"></i></Button>
<Button className="mr5 font-12" type="danger" size="small" onClick={() => { deletItem(record.id) }}><i className="iconfont icon-shanchu font-15 "></i></Button>
<Button className="mr5 font-12" type="info" size="small" onClick={() => { detailItem(record.id) }}>详情</Button>
{record.status === '0' && <Button className="mr5 font-12" type="primary" size="small" onClick={() => { setCurrentId(record.id); setCheckVisible(true); }}>审核</Button>}
{record.status === '2' && <Button className="mr5 font-12" type="success" size="small" onClick={() => { setCurrentId(record.id); setAcceptVisible(true) }}>验收成果</Button>}
</span>
),
},
];
const helper = useCallback(
(label, name, rules, widget, initialValue) => (
<Form.Item label={label}>
{getFieldDecorator(name, { rules, initialValue, validateFirst: true, })(widget)}
</Form.Item>
), []);
const formItemLayout = {
labelCol: {
xs: { span: 12 },
sm: { span: 6 },
},
wrapperCol: {
xs: { span: 12 },
sm: { span: 18 },
},
};
return (
<React.Fragment>
<div className="choose-status">
{
chooseStatusArr.map((item, i) => {
return <Button className={classnames({ mr10: true, active: status === i })} type="dashed" key={i} onClick={() => { setStatus(i) }}> {item}</Button>
})
}
</div>
<Table
rowKey={(row) => row.id}
dataSource={requirementList}
columns={columns}
pagination={false}
/>
<div className="edu-txt-center mt10">
<Pagination
showQuickJumper
onChange={(page) => { setCurPage(page) }}
current={curPage}
total={total}
/>
</div>
<Modal
title="需求审核"
visible={checkVisible}
onOk={checkItem}
onCancel={() => { setCheckVisible(false) }}
className="form-edit-modal"
>
{checkVisible && <Form {...formItemLayout}>
<Form.Item label="审批结果:">
<Radio.Group value={checkStatus} onChange={(e) => { setCheckStatus(e.target.value) }}>
<Radio className="columsRadio" value={1}>
同意
</Radio>
<Radio className="columsRadio" value={4}>
拒绝
</Radio>
</Radio.Group>
</Form.Item>
{helper(
"指派用户:",
"vertifyUserAccount",
[{ required: checkStatus === 1, message: "请指派用户" }],
<Select placeholder="请指派用户"
className="edit-input"
showSearch
labelInValue
disabled={checkStatus === 4}
filterOption={(input, option) =>
option.props && option.props.children && (option.props.children.indexOf(input) > -1 || option.props.children.toLowerCase().indexOf(input.toLowerCase()) > -1)
}
>
{
allUsers.map((item, i) => {
return <Option key={item.id} value={item.login}>{item.nickName}</Option>
})
}
</Select>
)}
{helper(
"审核意见:",
"vertifyComments",
[{ required: true, message: "请输入审核意见" }],
<TextArea
className="edit-input"
placeholder="请输入审核意见"
/>
)}
</Form>}
</Modal>
<Modal
title="验收成果"
visible={acceptVisible}
onOk={acceptItem}
onCancel={() => { setAcceptVisible(false) }}
className="form-edit-modal"
>
{acceptVisible && <Form {...formItemLayout}>
{helper(
"验收结果:",
"status",
[{ required: true, message: "请选择验收结果" }],
<Radio.Group>
<Radio className="columsRadio" value={3}>
同意
</Radio>
<Radio className="columsRadio" value={7}>
拒绝
</Radio>
</Radio.Group>
)}
{helper(
"验收意见:",
"checkComments",
[],
<TextArea
className="edit-input"
placeholder="请输入验收意见"
/>
)}
</Form>}
</Modal>
</React.Fragment>
)
})
)

View File

@ -0,0 +1,25 @@
import React from 'react';
import { Tabs } from 'antd';
import AllRequire from './allRequire';
import MyRequire from './myRequire';
import './index.scss';
const { TabPane } = Tabs;
export default (props) => {
return (
<div className="centerbox">
<Tabs className="tabs-style" type="card">
<TabPane tab="全部需求" key="1">
<AllRequire {...props}/>
</TabPane>
<TabPane tab="我的需求" key="2">
<MyRequire {...props}/>
</TabPane>
</Tabs>
</div>
)
}

View File

@ -0,0 +1,54 @@
// Tabs
.tabs-style{
background: #fff;
border: 1px solid #dcdfe6;
-webkit-box-shadow: 0 2px 4px 0 rgba(0,0,0,.12), 0 0 6px 0 rgba(0,0,0,.04);
box-shadow: 0 2px 4px 0 rgba(0,0,0,.12), 0 0 6px 0 rgba(0,0,0,.04);
padding-bottom: 1vw;
.ant-tabs-tab{
margin: 0 !important;
}
.ant-tabs-tab{
border-top:0 !important;
border-left:0 !important;
border-radius:0 !important;
}
.ant-tabs-nav .ant-tabs-tab{
padding:19px 0px;
margin-right: 40px;
}
.ant-tabs-ink-bar{
width:25px!important;
bottom: 10px;
}
}
.choose-status{
padding: 1em;
background: #fff;
}
.ant-btn-info{
color: #fff;
background-color: #909399;
border-color: #909399;
}
.ant-btn-success{
color: #fff;
background-color: #67c23a;
border-color: #67c23a;
}
.mt10{
margin-top:10px;
}
.form-edit-modal{
.ant-col{
display: inline-block;
}
.edit-input{
width: 300px;
}
}

View File

@ -0,0 +1,80 @@
import React, { useEffect, useState } from 'react';
import classnames from 'classnames';
import { Pagination, Button } from 'antd';
import ItemList from '../../components/itemList';
import DelModal from '../../components/DelModal';
import { deleteRequirement, getRequirementList } from '../api';
export default (props) => {
const [requirementList, setRequirementList] = useState([]);
const [curPage, setCurPage] = useState(1);
const [total, setTotal] = useState(0);
// const [queryType, setQueryType] = useState("my");
const [queryType, setQueryType] = useState("all");
useEffect(() => {
getRequireList();
}, [curPage, queryType]);
function getRequireList() {
const params = {
curPage,
pageSize: 10,
status: "",
tag: "",
title: "",
typeName: "",
currentUser: "",
queryType, //all: my: tome:
};
getRequirementList(params).then(data => {
setRequirementList(data.rows);
setTotal(data.total);
})
}
function detailItem(id) {
props.history.push(`/requirement/requireDetail/${id}`);
}
function editItem(id) {
props.history.push(`/requirement/requireEdit/${id}`);
}
function deletItem(id) {
DelModal(() => {
deleteRequirement(id).then(res => {
if (res.code === '1') {
props.showNotification("删除成功");
getRequireList();
} else {
props.showNotification("删除失败");
}
})
}, '此操作将删除该记录, 是否继续?');
}
return (
<React.Fragment>
<div className="choose-status">
<Button className={classnames({ mr10: true, active: queryType === "my" })} type="dashed" key="my" onClick={() => { setQueryType("my") }}>我创建的</Button>
<Button className={classnames({ mr10: true, active: queryType === "tome" })} type="dashed" key="tome" onClick={() => { setQueryType("tome") }}> 指派给我的 </Button>
</div>
<ItemList
list={requirementList}
itemClick={detailItem}
deletItem={queryType === "my" && deletItem}
editItem={queryType === "my" && editItem}
activeByStatus={queryType === "my"}
/>
<div className="edu-txt-center mt10">
<Pagination
showQuickJumper
onChange={(page) => { setCurPage(page) }}
current={curPage}
total={total}
/>
</div>
</React.Fragment>
)
}

View File

@ -68,7 +68,7 @@ export function appendFileSizeToUploadFile(item) {
return `${item.title}${uploadNameSizeSeperator}${item.filesize}`
}
export function appendFileSizeToUploadFileAll(fileList) {
return fileList.map(item => {
return fileList && fileList.map(item => {
if (item.name.indexOf(uploadNameSizeSeperator) == -1) {
return Object.assign({}, item, { name: `${item.name}${uploadNameSizeSeperator}${bytesToSize(item.size)}` })
}

View File

@ -6,12 +6,12 @@ const { Search } = Input;
const $ = window.$;
const isDev = window.location.port == 3007;
const isdev2= window.location.hostname ==='www.educoder.net'
export const TEST_HOST = "https://testforgeplus.trustie.net/"
export const TEST_HOST = "http://39.105.176.215:49999"
export function getImageUrl(path) {
// https://www.educoder.net
// https://testbdweb.trustie.net
// const local = 'http://localhost:3000'
const local = 'https://testforgeplus.trustie.net';
const local = 'http://39.105.176.215:49999';
if (isDev) {
return `${local}/${path}`
}
@ -22,7 +22,7 @@ export function getImage(path) {
// https://www.educoder.net
// https://testbdweb.trustie.net
// const local = 'http://localhost:3000'
const local = 'https://testforgeplus.trustie.net/';
const local = 'http://39.105.176.215:49999';
if(path.indexOf("http://")===-1){
if (isDev) {
return `${local}/images/${path}`
@ -93,7 +93,7 @@ export function setImagesUrl(path){
}
export function getUrl(path, goTest) {
const local = 'https://testforgeplus.trustie.net'
const local = 'http://39.105.176.215:49999'
if (isDev) {
return `${local}${path?path:''}`
}
@ -162,7 +162,7 @@ export function getmyUrl(geturl) {
}
export function getUploadActionUrl(path, goTest) {
return `${getUrl()}/api/attachments.json?debug=${window._debugType || 'admin'}`;
return `${getUrl()}/api/attachments.json`;
}
export function getUploadLogoActionUrl() {

85
src/fetch.js Normal file
View File

@ -0,0 +1,85 @@
import axios from 'axios';
import cookie from 'react-cookies';
export const httpUrl='http://117.50.100.12:8001';
const TokenKey = 'autologin_forge_military';
axios.defaults.withCredentials = true;
// 创建axios实例
const service = axios.create({
// baseURL: process.env.BASE_API, // api的base_url
baseURL:httpUrl,
timeout: 5000 // 请求超时时间
// headers:{'X-Custom-Header': 'foobar'} // 请求头
});
// request拦截器
service.interceptors.request.use(config => {
if (cookie.load(TokenKey)) {
config.headers['Authorization'] = cookie.load(TokenKey); // 让每个请求携带自定义token 请根据实际情况自行修改
// config.headers['Authorization'] = 'autologin_forge_military=' + cookie.load(TokenKey); // 让每个请求携带自定义token 请根据实际情况自行修改
}
return config;
}, error => {
// Do something with request error
console.log(error); // for debug
Promise.reject(error);
});
// respone拦截器
service.interceptors.response.use(
response => {
/**
* 下面的注释为通过response自定义code来标示请求状态当code返回如下情况为权限有问题登出并返回到登录页
* 如通过xmlhttprequest 状态码标识 逻辑可写在下面error中
*/
const res = response;
if (res.status === 400) {
// Message({
// message: '请求错误',
// type: 'error',
// duration: 5 * 1000
// });
// eslint-disable-next-line prefer-promise-reject-errors
return Promise.reject('error');
}
if (res.status === 401) {
// Message({
// message: '未授权,请登录!',
// type: 'error',
// duration: 5 * 1000
// });
// eslint-disable-next-line prefer-promise-reject-errors
return Promise.reject('error');
}
if (res.status === 40001) {
// Message({
// message: '账户或密码错误!',
// type: 'warning'
// });
// eslint-disable-next-line prefer-promise-reject-errors
return Promise.reject('error');
}
if (response.status !== 200 && res.status !== 200) {
// Message({
// message: res.message,
// type: 'error',
// duration: 7 * 1000
// });
} else {
return response.data;
}
},
error => {
if (error.response&& error.response.status === 401 && !error.config.url.includes('/auth-server/oauth/token')) {
return cookie.remove(TokenKey);
// that.$router.push('/Login');
} else {
// that.$router.push('/Login');
}
return Promise.reject(error);
}
);
export default service;

View File

@ -1,3 +1,5 @@
import React , { useState , useEffect } from 'react';
import { Select } from 'antd';
import axios from 'axios';
@ -57,7 +59,6 @@ const LANGUAGE = [
export default (({ language , select_language })=>{
const [ languages , setLanguage ] = useState(undefined);
// useEffect(()=>{
// const url = '/dev_ops/languages.json';
// axios.get(url).then(result=>{

View File

@ -5,7 +5,7 @@ import './Component.scss';
export default (()=>{
return(
<div className="handleBox">
<a href="https://forum.trustie.net/forums/3075/detail" target="_blank" >
<a href="https://forum.trustie.net/forums/3075/detail" ta rget="_blank" >
<img src={Handbook} alt=""/>
</a>
</div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

View File

@ -6,7 +6,6 @@ import { withRouter } from "react-router";
import { SnackbarHOC } from "educoder";
import { CNotificationHOC } from "../modules/courses/common/CNotificationHOC";
import { TPMIndexHOC } from "../modules/tpm/TPMIndexHOC";
import Handbook from './Component/Handbook';
import "./css/index.scss";
import Loadable from "react-loadable";
@ -35,7 +34,6 @@ class Index extends Component {
render() {
return (
<div className="newMain clearfix">
<Handbook />
<Switch {...this.props}>
<Route
path="/projects/:projectsType/new/:OIdentifier"

View File

@ -145,7 +145,7 @@ class CoderRootCommit extends Component{
)
})
}
{commitDatas && commitDatas.length > 0 && <Nodata _html="暂无数据"/>}
{commitDatas && commitDatas.length === 0 && <Nodata _html="暂无数据"/>}
</div>
</div>
{

View File

@ -251,7 +251,7 @@ class CoderRootDirectory extends Component {
// readme文件内容
renderReadMeContent = (readMeContent, permission) => {
const { fileDetail, readMeFile } = this.state;
if (fileDetail) {
if (fileDetail && fileDetail.length !== 0) {
return;
}
if (readMeContent && readMeContent.length > 0) {

View File

@ -4,6 +4,7 @@ import { Link, Route, Switch } from 'react-router-dom';
import { Content } from '../Component/layout';
import '../css/index.scss'
import './list.css';
import SpecialModal from './SpecialModal';
import Loadable from 'react-loadable';
import Loading from '../../Loading';
@ -103,6 +104,10 @@ const TrendsIndex = Loadable({
loading: Loading,
})
const Source = Loadable({
loader: () => import('../Source/Index'),
loading: Loading,
})
const DevAbout = Loadable({
loader: () => import('../About/Index'),
loading: Loading,
@ -117,20 +122,23 @@ const DevIndex = Loadable({
function checkPathname(projectsId,owner,pathname){
let name = "";
if(pathname && pathname !== `/projects/${owner}/${projectsId}`){
if(pathname.indexOf("/about")>-1){
let url = pathname.split(`/projects/${owner}/${projectsId}`)[1];
if(url.indexOf("/about")>-1){
name="about"
}else if(pathname.indexOf("/issues")>-1 ||pathname.indexOf("Milepost") > 0){
}else if(url.indexOf("/issues")>-1 ||url.indexOf("Milepost") > 0){
name = "issues";
}else if(pathname.indexOf("/pulls")>-1){
}else if(url.indexOf("/pulls")>-1){
name="pulls"
}else if(pathname.indexOf("/milestones")>-1){
}else if(url.indexOf("/milestones")>-1){
name="milestones"
}else if(pathname.indexOf("/activity")>-1){
}else if(url.indexOf("/activity")>-1){
name="activity"
}else if(pathname.indexOf("/setting")>-1){
}else if(url.indexOf("/setting")>-1){
name="setting"
}else if(pathname.indexOf(`/devops`)>-1){
}else if(url.indexOf(`/devops`)>-1){
name="devops"
}else if(url.indexOf(`/source`)>-1){
name="source"
}
}
return name;
@ -161,7 +169,9 @@ class Detail extends Component {
defaultBranch:undefined,
// 非本平台项目
platform:false
platform:false,
visible:false,
user_apply_signatures:[]
}
}
@ -178,6 +188,7 @@ class Detail extends Component {
}
getProject = (num) => {
const {user} = this.props;
const { projectsId , owner } = this.props.match.params;
const url = `/${owner}/${projectsId}/simple.json`;
axios.get(url).then((result) => {
@ -187,6 +198,24 @@ class Detail extends Component {
open_devops:result.data.open_devops,
platform:result.data.platform && result.data.platform !== 'educoder'
})
let signa = result.data.user_apply_signatures && result.data.user_apply_signatures[0];
if(result.data.is_secret && !result.data.is_member && (!signa || (signa && signa.status !== "passed")) && user.login !== owner){
this.setState({
visible:true,
is_secret:result.data.is_secret,
user_apply_signatures:signa
})
}
// 工作流:两种状态进入的链接不同
const pathname = this.props.history.location.pathname;
if(pathname===`/projects/${owner}/${projectsId}/devops`){
if(result.data.open_devops && pathname === `/projects/${owner}/${projectsId}/devops`){
this.props.history.push(`/projects/${owner}/${projectsId}/devops/list`);
}else if(result.data.open_devops===false && pathname !== `/projects/${owner}/${projectsId}/devops`){
this.props.history.push(`/projects/${owner}/${projectsId}/devops`);
}
}
if (result.data.type !== 0 && result.data.mirror_status === 1) {
console.log("--------start channel --------");
@ -357,13 +386,24 @@ class Detail extends Component {
})
}
hideModal=()=>{
this.setState({
visible:false
})
}
sureModal=()=>{
this.hideModal();
this.props.history.push('/projects');
}
render() {
const { projectDetail, watchers_count, praises_count,
forked_count, firstSync , secondSync ,
isManager, watched, praised,
project , open_devops , platform , defaultBranch } = this.state;
project , open_devops , platform , defaultBranch , project_id , user_apply_signatures , visible } = this.state;
const url = this.props.history.location.pathname;
const urlArr = url.split("/");
const urlFlag = (urlArr.length === 3);
@ -390,18 +430,19 @@ class Detail extends Component {
}
return (
<div>
<SpecialModal {...this.props} visible={visible} hideModal={this.sureModal} user_apply_signatures={user_apply_signatures} project_id={project_id} sureModal={this.sureModal}></SpecialModal>
<div className="detailHeader-wrapper">
<div className="normal">
<div className="f-wrap-between pb15" style={{ position: "relative" }}>
<p className="font-22 df flex-1 lineH2 mt15" style={{ alignItems: "center" }}>
<p className="color-white font-22 df flex-1 lineH2 mt15" style={{ alignItems: "center" }}>
{project && project.author &&
<Link to={`${project.author.type ==="Organization" ? "/organize":'/users'}/${project.author.login}`} className="show-user-link">
<Link to={`/users/${project.author.login}`} className="show-user-link color-white">
{project.author.name}
</Link>
}
<span className="ml5 mr5">/</span>
<span className="hide-1 flex-1 df">
<Link to={`/projects/${owner}/${projectsId}`} className="font-22">{project && project.name}</Link>
<Link to={`/projects/${owner}/${projectsId}`} className="color-white font-22">{project && project.name}</Link>
{
projectDetail && projectDetail.forked_from_project_id && projectDetail.fork_info ?
<Tooltip placement={'right'} title={text}>
@ -438,15 +479,14 @@ class Detail extends Component {
<span>{watched ? '取消关注' : '关注'}</span>
</a>
{
watchers_count > 0 ?
platform ?
<Link className="detail_tag_btn_count" style={{color:`${watched?"#2878FF":"#666"}`}} to={platform?{ pathname: `/projects/${owner}/${projectsId}/watchers`, state }:""}>
{watchers_count}
</Link>
:
<span className="detail_tag_btn_count">{watchers_count}</span>
:""
}
</span>
<span className="detail_tag_btn">
<a className="detail_tag_btn_name" style={{cursor:platform?"pointer":"default"}} onClick={() => this.pariseFunc(praised)}>
@ -454,13 +494,11 @@ class Detail extends Component {
<span>{praised ? '取消点赞' : '点赞'}</span>
</a>
{
praises_count > 0 ?
platform ?
<Link className="detail_tag_btn_count" style={{color:`${praised?"#2878FF":"#666"}`}} to={{ pathname: `/projects/${owner}/${projectsId}/stargazers`, state }}>
{praises_count}
</Link>:
<span className="detail_tag_btn_count">{praises_count}</span>
:""
}
</span>
<span className="detail_tag_btn">
@ -468,14 +506,12 @@ class Detail extends Component {
<i className="iconfont icon-fork color-grey-9 mr3"></i> (Fork)
</a>
{
forked_count > 0 ?
platform ?
<Link className="detail_tag_btn_count" to={{ pathname: `/projects/${owner}/${projectsId}/fork_users`, state }}>
{forked_count}
</Link>
:
<span className="detail_tag_btn_count">{forked_count}</span>
:""
<span className="detail_tag_btn_count">{praises_count}</span>
}
</span>
</span>
@ -483,23 +519,23 @@ class Detail extends Component {
</div>
{
firstSync ? "" :
<div className="f-wrap-between mt15">
<div className="f-wrap-between pb20">
<ul className="headerMenu-wrapper">
<li className={pathname==="about" ? "active" : ""}>
<Link to={{ pathname: `/projects/${owner}/${projectsId}/about`, state }}>
<i className={(pathname==="" || urlFlag) ? "iconfont icon-zhuye1 color-grey-3 mr5 font-14":"iconfont icon-zhuye1 color-grey-6 font-14 mr5"}></i>
<i className={ pathname === "about" ? "iconfont icon-zhuye1 color-blue mr5 font-14":"iconfont icon-zhuye1 color-white font-14 mr5"}></i>
<span>主页</span>
</Link>
</li>
<li className={(pathname==="" || urlFlag) ? "active" : ""}>
<Link to={{ pathname: `/projects/${owner}/${projectsId}`, state }}>
<i className={(pathname==="" || urlFlag) ? "iconfont icon-daimaku color-grey-3 mr5 font-14":"iconfont icon-daimaku color-grey-6 font-14 mr5"}></i>
<i className={(pathname==="" || urlFlag) ? "iconfont icon-daimaku color-blue mr5 font-14":"iconfont icon-daimaku color-white font-14 mr5"}></i>
<span>代码库</span>
</Link>
</li>
<li className={pathname==="issues" ? "active" : ""}>
<Link to={{ pathname: `/projects/${owner}/${projectsId}/issues`, state }}>
<i className={pathname==="issues" ? "iconfont icon-renwu color-grey-3 mr5 font-14":"iconfont icon-renwu color-grey-6 font-14 mr5"}></i>
<i className={pathname==="issues" ? "iconfont icon-renwu color-blue mr5 font-14":"iconfont icon-renwu color-white font-14 mr5"}></i>
<span>易修 (Issue)</span>
{projectDetail && projectDetail.issues_count ? <span className="num">{projectDetail.issues_count}</span> : ""}
</Link>
@ -508,32 +544,39 @@ class Detail extends Component {
projectDetail && parseInt(projectDetail.type) !== 2 && platform &&
<li className={pathname==="pulls" ? "active" : ""}>
<Link to={{ pathname: `/projects/${owner}/${projectsId}/pulls`, state }}>
<i className={pathname==="pulls" ? "iconfont icon-hebingqingqiu1 color-grey-3 mr5 font-14":"iconfont icon-hebingqingqiu1 color-grey-6 font-14 mr5"}></i>
<i className={pathname==="pulls" ? "iconfont icon-hebingqingqiu1 color-blue mr5 font-14":"iconfont icon-hebingqingqiu1 color-white font-14 mr5"}></i>
<span>合并请求</span>
{projectDetail && projectDetail.pull_requests_count ? <span className="num">{projectDetail.pull_requests_count}</span> : ""}
</Link>
</li>
}
{
<li className={pathname==="source" ? "active" : ""}>
<Link to={{ pathname: `/projects/${owner}/${projectsId}/source`, state }}>
<i className={pathname==="source" ? "iconfont icon-ziyuanpaihanghetuijian color-blue mr5 font-14":"iconfont icon-ziyuanpaihanghetuijian color-white font-14 mr5"}></i>
<span>资源库</span>
{projectDetail && projectDetail.source_count ? <span className="num">{projectDetail.source_count}</span> :""}
</Link>
</li>
{/* {
platform &&
<li className={pathname==="devops" ? "active" : ""}>
<Link to={{ pathname: `/projects/${owner}/${projectsId}/devops${open_devops ? `/dispose`:""}`, state }}>
<Link to={{ pathname: `/projects/${owner}/${projectsId}/devops${open_devops ? `/list`:""}`, state }}>
<i className="iconfont icon-gongzuoliu font-13 mr8"></i>(beta)
{projectDetail && projectDetail.ops_count ? <span>{projectDetail.ops_count}</span> : ""}
</Link>
</li>
}
} */}
<li className={pathname==="milestones" ? "active" : ""}>
<Link to={{ pathname: `/projects/${owner}/${projectsId}/milestones`, state }}>
<i className={pathname==="milestones" ? "iconfont icon-lichengbei color-grey-3 mr5 font-14":"iconfont icon-lichengbei color-grey-6 font-14 mr5"}></i>
<i className={pathname==="milestones" ? "iconfont icon-lichengbei color-blue mr5 font-14":"iconfont icon-lichengbei color-white font-14 mr5"}></i>
<span>里程碑</span>
{projectDetail && projectDetail.versions_count ? <span className="num">{projectDetail.versions_count}</span> :""}
</Link>
</li>
<li className={pathname==="activity" ? "active" : ""}>
<Link to={{ pathname: `/projects/${owner}/${projectsId}/activity`, state }}>
<i className={pathname==="activity" ? "iconfont icon-tongzhi color-grey-3 mr5 font-14":"iconfont icon-tongzhi color-grey-6 font-14 mr5"}></i>
<i className={pathname==="activity" ? "iconfont icon-tongzhi color-blue mr5 font-14":"iconfont icon-tongzhi color-white font-14 mr5"}></i>
<span>动态</span>
</Link>
</li>
@ -541,7 +584,7 @@ class Detail extends Component {
isManager && platform &&
<li className={url.indexOf("/setting") > 0 ? "active" : ""}>
<Link to={`/projects/${owner}/${projectsId}/setting`}>
<i className={url.indexOf("/setting") > 0 ? "iconfont icon-cangku color-grey-3 mr5 font-14":"iconfont icon-cangku color-grey-6 font-14 mr5"}></i>
<i className={url.indexOf("/setting") > 0 ? "iconfont icon-cangku color-blue mr5 font-14":"iconfont icon-cangku color-white font-14 mr5"}></i>
<span>仓库设置</span>
</Link>
</li>
@ -560,6 +603,12 @@ class Detail extends Component {
:
<Spin spinning={secondSync} className="spinstyle" tip="正在同步镜像" size="large">
<Switch {...this.props}>
{/* 资源 */}
<Route path="/projects/:owner/:projectsId/source"
render={
() => (<Source {...this.props} {...this.state} {...common} />)
}
></Route>
{/* 主页 */}
<Route path="/projects/:owner/:projectsId/about"
render={

View File

@ -1,7 +1,6 @@
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { Menu, Input , Spin, Pagination , Popover , Select } from 'antd';
import { getUrl } from 'educoder';
import { Menu, Input , Spin, Pagination , Popover } from 'antd';
import '../css/index.scss'
import './list.css';
import './Index.scss';
@ -9,7 +8,7 @@ import ListItem from './IndexItem'
import axios from 'axios';
import img_new from '../Images/new.png';
import img_array from '../Images/array.png';
import banner from '../Images/banner_list.jpg';
import banner from '../Images/banner_list.png';
const Search = Input.Search;
class Index extends Component {
@ -23,7 +22,6 @@ class Index extends Component {
sort: undefined,
total: 0,
isSpin: true,
project_type: undefined,
category_id: undefined,
typeList: undefined,
@ -37,8 +35,8 @@ class Index extends Component {
}
componentDidMount = () => {
const { page, limit, search, sort, project_type, category_id , languageId } = this.state;
this.getListData(page, limit, search, sort, project_type, category_id , languageId);
const { page,search, sort,category_id , languageId } = this.state;
this.getListData(page,search, sort,category_id , languageId);
this.getType();
@ -73,19 +71,18 @@ class Index extends Component {
}
// 获取列表
getListData = (page, limit, search, sort, project_type, category_id,languageId) => {
getListData = (page,search, sort,category_id,language_id) => {
const { current_user } = this.props;
const url = `/projects.json`;
axios.get(url, {
params: {
user_id: current_user && current_user.user_id,
page,
limit,
limit:15,
search,
sort_by: sort,
project_type,
category_id,
language_id:languageId
language_id
}
}).then((result) => {
if (result) {
@ -112,7 +109,7 @@ class Index extends Component {
this.setState({
typeList: list.map((item, key) => {
return (
<li key={key} className={active_type && active_type === item.project_type ? 'active' : ''} onClick={() => this.changeType(`${item.project_type}`, list)}>
<li key={key} className={active_type && parseInt(active_type) === item.id ? 'active' : ''} onClick={() => this.changeType(`${item.id}`, list)}>
<p>
<span className="font-16">{item.name}</span>
<span className="color-blue">{item.projects_count}</span>
@ -124,15 +121,15 @@ class Index extends Component {
}
// 切换类型
changeType = (type, list) => {
changeType = (id, list) => {
this.setState({
isSpin: true,
project_type: type,
languageId: id,
search: undefined
})
this.setTypeList(list, type)
const { page, limit, sort, category_id , languageId } = this.state;
this.getListData(page, limit, undefined, sort, type, category_id , languageId);
this.setTypeList(list, id)
const { page,sort, category_id} = this.state;
this.getListData(page,undefined, sort,category_id , id);
}
// 获取类型
@ -167,8 +164,8 @@ class Index extends Component {
page: 1
});
this.setCategoryList(list, id)
const { limit, sort, project_type , languageId } = this.state;
this.getListData(1, limit, undefined, sort, project_type, id , languageId);
const { sort,languageId } = this.state;
this.getListData(1,undefined, sort,id , languageId);
}
// 排序
@ -179,8 +176,8 @@ class Index extends Component {
search: undefined,
isSpin: true
})
const { limit, project_type, category_id , languageId } = this.state;
this.getListData(1, limit, undefined, e.key, project_type, category_id , languageId);
const {category_id , languageId } = this.state;
this.getListData(1,undefined, e.key,category_id , languageId);
}
// 搜索
@ -192,8 +189,8 @@ class Index extends Component {
project_type: undefined,
sort: "updated_on"
})
const { limit, sort, category_id , languageId } = this.state;
this.getListData(1, limit, value, sort, undefined, category_id , languageId);
const {sort, category_id , languageId } = this.state;
this.getListData(1,value, sort,category_id , languageId);
}
changeSearchValue = (e) => {
this.setState({
@ -205,23 +202,14 @@ class Index extends Component {
this.setState({
page
})
const { limit, search, sort, project_type, category_id , languageId } = this.state;
this.getListData(page, limit, search, sort, project_type, category_id , languageId);
const {search, sort,category_id , languageId } = this.state;
this.getListData(page,search, sort, category_id , languageId);
}
getoDetail=(login,identifier)=>{
this.props.history.push(`/projects/${login}/${identifier}`);
}
// 选择语言类别
changeLanguage=(e)=>{
this.setState({
isSpin:true,
languageId:e === 0 ?undefined:e
})
const { page, limit, sort , project_type , category_id } = this.state;
this.getListData(page, limit, undefined, sort, project_type, category_id ,e === 0 ?undefined:e);
}
menu =()=> {
return(
@ -255,15 +243,14 @@ class Index extends Component {
render() {
const { current_user } = this.props;
const { projectsList , recommendList , languageList , languageId ,
isSpin, total, search, limit, page, typeList, categoryList } = this.state;
const { projectsList , isSpin , total , search , limit , page , typeList , categoryList } = this.state;
return (
<div>
<p className="t_project_banner">
<img src={banner} width="100%" alt=""/>
</p>
{
{/* {
recommendList && recommendList.length>0 &&
<div className="recommandProjects">
{
@ -285,44 +272,23 @@ class Index extends Component {
}
</div>
}
*/}
<div className="ProjectListIndex">
<div className="list-left">
<ul className="list-l-Menu">
<li className="MenuTitle"><i className="iconfont icon-xiangmuleixing color-grey-9 font-15 mr5"></i></li>
{typeList}
<li className="MenuTitle"><i className="iconfont icon-bianchengyuyan color-grey-9 font-15 mr5"></i>
语言</li>
<div className="list-affix">{typeList}</div>
</ul>
<ul className="list-l-Menu">
<li className="MenuTitle"><i className="iconfont icon-xiangmuleibie color-grey-9 font-15 mr5"></i></li>
{categoryList}
<div className="list-affix">{categoryList}</div>
</ul>
</div>
<div className="list-right boxShandow radius-2" style={{padding:0}}>
<Spin spinning={isSpin}>
<div className="list-r-operation">
<div>
<Select
showSearch
placeholder="请选择语言"
style={{width:"150px",marginRight:"20px"}}
size={"large"}
onChange={this.changeLanguage}
value={languageId}
allowClear={true}
optionFilterProp="children"
filterOption={(input,option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
>
<Select.Option key={0} value={0}>请选择语言</Select.Option>
{
languageList && languageList.length>0 && languageList.map((item,key)=>{
return(
<Select.Option key={item.id} value={item.id}>
{item.name}
</Select.Option>
)
})
}
</Select>
<Search
placeholder="输入项目名称关键字进行搜索"
enterButton="搜索"
@ -350,7 +316,7 @@ class Index extends Component {
</Popover>
</div>
</div>
<ListItem {...this.props} {...this.state} projects={projectsList}></ListItem>
<ListItem {...this.props} {...this.state} projects={projectsList} getListData={this.getListData}></ListItem>
{this.pagination(total,limit,page)}
</Spin>
</div>

View File

@ -71,4 +71,16 @@
text-align: right;
}
}
}
.singleBtn{
display: inline-block;
.ant-upload-list-item{
position: absolute;
bottom: 0px;
width: 100%;
left: 0px;
.ant-upload-list-item-name{
text-align: left;
}
}
}

View File

@ -6,72 +6,123 @@ import '../css/index.scss';
import Nodata from '../Nodata';
import './list.css';
import img_parise from '../Images/parise.png';
import SpecialModal from './SpecialModal';
class IndexItem extends Component {
constructor(props){
super(props);
this.state={
visible:false,
user_apply_signatures:[],
project_id:undefined
}
}
TurnToDetail = (login, url) => {
this.props.history.push({
pathname: url,
state: login
})
}
/**
* link:跳转到详情的地址
* user_apply_signatures:是否已经发送访问特殊开源项目的文件
* project_id项目id
* is_secret:是否是特殊开源许可证项目
* id创建者login
* is_member:是否是项目成员(如果是项目成员可以直接进入项目)
* */
projectHref=(link , user_apply_signatures,project_id,is_secret , id,is_member)=>{
const { user , showLoginDialog } = this.props;
if(is_secret && (!user || (user && !user.login))){
showLoginDialog();
return;
}
let signa = user_apply_signatures && user_apply_signatures[0];
if((is_secret && !is_member && (!signa || (signa && signa.status !== "passed"))) && user.login !== id ){
this.setState({
visible:true,
user_apply_signatures:user_apply_signatures.length>0 ? user_apply_signatures[0] : undefined,
project_id
})
}else{
this.props.history.push(link);
}
}
hideModal=()=>{
this.setState({
visible:false
})
}
sureModal=()=>{
this.hideModal();
const { getListData } = this.props;
getListData && getListData(1);
}
render() {
const { projects } = this.props;
return (
<div className="project-list minH-670">
{ projects && projects.length > 0 ? projects.map((item, key) => {
return (
<div className="p-r-Item" key={key}>
{
item.platform === "educoder" ?
<a href="javascript:void(0)" style={{cursor:"default"}} className="show-user-link">
<img className="p-r-photo" alt="" src={item.author && item.author.image_url} ></img>
</a>
:
<Link to={`/users/${item.author.login}`} className="show-user-link">
<img className="p-r-photo" alt="" src={getImageUrl(`${item.author && item.author.image_url}`)} ></img>
</Link>
}
<div className="p-r-Infos">
<div className="p-r-name">
<Link to={`/projects/${item.author.login}/${item.identifier}`} className="hide-1 color-grey-3 font-18 task-hide " style={{ whiteSpace: "wrap", display: 'flex', width: 400 }}>
{item.author.name}/{item.name}
{
item.forked_from_project_id ?
<span className="ml5">
<i className="iconfont icon-fork font-18 color-orange" />
</span>
: ""
}
{
item.type && item.type !== 0 ?
item.type === 2 ?
<Tooltip title="该项目是一个镜像" className="ml5">
<i className="iconfont icon-banbenku font-18 color-green" />
</Tooltip>:
const { visible , user_apply_signatures , project_id } = this.state;
const renderList = (
projects && projects.length > 0 ? projects.map((item, key) => {
return (
<div className="p-r-Item" key={key}>
{
item.platform === "educoder" ?
<a style={{cursor:"default"}} className="show-user-link">
<img className="p-r-photo" alt="" src={item.author && item.author.image_url} ></img>
</a>
:
<Link to={`/users/${item.author.login}`} className="show-user-link">
<img className="p-r-photo" alt="" src={getImageUrl(`${item.author && item.author.image_url}`)} ></img>
</Link>
}
<div className="p-r-Infos">
<div className="p-r-name">
<a onClick={()=>this.projectHref(`/projects/${item.author.login}/${item.identifier}/about`,item.user_apply_signatures, item.id,item.is_secret,item.author.login,item.is_member)} className="hide-1 color-grey-3 font-18 task-hide fwt-500 " style={{ whiteSpace: "wrap", display: 'flex', width: 400 }}>
{item.author.name}/{item.name}
{
item.forked_from_project_id ?
<span className="ml5">
<i className="iconfont icon-jingxiang font-18 color-green" />
</span>:""
}
</Link>
<span className="p-r-tags">
<span className="pariseTag"><img src={img_parise} alt="" className="pariseImg" /> {item.praises_count}</span>
<span><i className="iconfont icon-fork mr3 font-16" style={{ color: "#1B8FFF" }} />fork {item.forked_count}</span>
</span>
</div>
<p className="break_word task-hide-2 mt10" style={{ maxHeight: "44px",lineHeight:"22px" }}>{item.description}</p>
<i className="iconfont icon-fork font-18 color-orange" />
</span>
: ""
}
{
item.type && item.type !== 0 ?
item.type === 2 ?
<Tooltip title="该项目是一个镜像" className="ml5">
<i className="iconfont icon-banbenku font-18 color-green" />
</Tooltip>:
<span className="ml5">
<i className="iconfont icon-jingxiang font-18 color-green" />
</span>:""
}
</a>
<span className="p-r-tags">
<span className="pariseTag"><img src={img_parise} alt="" className="pariseImg" /> {item.praises_count}</span>
<span><i className="iconfont icon-fork mr3 font-16" style={{ color: "#1B8FFF" }} />fork {item.forked_count}</span>
</span>
</div>
<p className="break_word task-hide-2 mt8 color-grey-3 " style={{ maxHeight: "44px",lineHeight:"22px" }}>{item.description}</p>
<div className="p-r-about">
<span className="p-r-detail">
{/* <span><label>浏览量:</label>{item.visits}</span> */}
{/* {item.category && item.category.id && <span>{item.category.name}</span>} */}
{item.last_update_time ? <span><label>更新于</label>{item.time_ago}</span> : ""}
{item.language && item.language.id ? <span className="color-grey-3">{item.language.name}</span> : ""}
</span>
</div>
<div className="p-r-about">
<span className="p-r-detail">
{/* <span><label>浏览量:</label>{item.visits}</span> */}
{/* {item.category && item.category.id && <span>{item.category.name}</span>} */}
{item.last_update_time ? <span><label>更新于</label>{item.time_ago}</span> : ""}
{item.language && item.language.id ? <span className="color-grey-3">{item.language.name}</span> : ""}
</span>
</div>
</div>
)
}) : <Nodata _html="暂无数据~"></Nodata>}
</div>
)
}) : <Nodata _html="暂无数据~"></Nodata>
)
return (
<div className="project-list minH-670">
<SpecialModal {...this.props} visible={visible} hideModal={this.hideModal} user_apply_signatures={user_apply_signatures} project_id={project_id} sureModal={this.sureModal}></SpecialModal>
{renderList}
</div>
)
}

View File

@ -0,0 +1,49 @@
import React , { useEffect , useState } from 'react';
import { Modal } from 'antd';
import UploadSingle from '../Upload/single';
import './Index.scss';
import axios from 'axios';
import { getUrl } from 'educoder';
function SpecialModal({ visible , hideModal , sureModal , showNotification , user_apply_signatures , project_id }){
const [ id ,setId ] = useState(undefined);
function loadFunc(id){
setId(id);
}
function sure(){
if(!user_apply_signatures || (user_apply_signatures && user_apply_signatures.status !== "waiting")){
if(!id || (id && id.length === 0)){
showNotification("请先提交文件进行审核!");
return;
}
const url = `/apply_signatures.json`;
axios.post(url,{
attachment_id:id,
project_id:project_id
}).then(result=>{
if(result && result.data.id){
showNotification("已提交文件,正在等待审核!");
sureModal();
}
})
}else{
sureModal();
}
}
return(
<Modal title="提示" visible={visible} closable={false} onCancel={hideModal} onOk={sure}>
{
!user_apply_signatures || (user_apply_signatures && user_apply_signatures.status !== "waiting") ?
<div style={{width:"420px",textAlign:'center',margin:"0 auto",paddingBottom:"30px",position:"relative"}}>
<div>该项目为私有项目请先<a href={getUrl(`/api/apply_signatures/template_file`)} className="color-blue">下载</a>开源协议,阅读并填写<br/>相关信息后将协议<UploadSingle size={"5"} load={loadFunc} showNotification={showNotification} className={'singleBtn'}><span className="color-blue" style={{cursor:"pointer"}}>上传</span></UploadSingle>,平台审核通过后即可进入当前项目</div>
</div>
:
<p style={{textAlign:'center'}}>您上传的文件正在审核中,通过后才能访问当前项目</p>
}
</Modal>
)
}
export default SpecialModal;

View File

@ -208,9 +208,7 @@
}
/* -----------详情------------ */
.detailHeader-wrapper{
background-color:#FAFBFC;
/* background: url(../Images/forgeBanner.jpg) no-repeat center; */
/* background-size:cover; */
background:linear-gradient(82deg,rgba(82,91,215,1) 0%,rgba(34,24,171,1) 100%);
}
.headerMenu-wrapper{
font-size: 16px;
@ -222,36 +220,42 @@
text-align: center;
height: 40px;
line-height: 28px;
margin-right: 40px;
border:1px solid transparent;
}
.headerMenu-wrapper{
font-size: 16px;
display: flex;
flex-direction: row;
}
.headerMenu-wrapper li{
padding:0px 18px;
position: relative;
text-align: center;
height: 30px;
line-height: 30px;
}
.headerMenu-wrapper li a{
color: #666;
color: #fff;
display: flex;
align-items: center;
}
.headerMenu-wrapper li a > img{
margin-right: 8px;
}
.headerMenu-wrapper li a > span.num{
height: 28px;
line-height: 29px;
margin-left: 8px;
font-size: 12px;
color: #2878FF;
float: right;
.headerMenu-wrapper li a > span{
display: block;
margin-left: 5px;
font-size: 16px;
}
.headerMenu-wrapper li.active::after{
position: absolute;
bottom:0px;
height:2px;
background-color: #5091FF;
content:'';
left: 0px;
width:100%;
.headerMenu-wrapper li.active{
border-radius: 15px;
border:1px solid #71A6FF;
}
.detail_tag_btn{
height:26px;
line-height: 26px;
border-radius:5px;
border:1px solid #f1f1f1;
border:1px solid #71A6FF;
display: flex;
align-items: center;
margin-left: 30px
@ -259,19 +263,26 @@
.ant-tooltip {
max-width: fit-content!important;
}
.detail_tag_btn_name{
padding:0px 10px;
color: #666!important;
display: flex;
align-items: center;
color: #fff;
}
.detail_tag_btn_name img{
margin-right: 10px;
}
.detail_tag_btn_count{
padding:0px 10px;
background: #fff;
color: #fff !important;
background: rgba(255,255,255,0.2);
border-radius: 0px 4px 4px 0px;
font-size: 12px;
height:100%;
}
.detail_tag_btn_count:hover{
/* color: #1C91FF !important; */
background: rgba(255,255,255,0.5);
}
.files-md{
border:1px solid #eee;

View File

@ -47,7 +47,7 @@ function Files({data,history,owner,projectsId}){
<span>{item.name}</span>
</AlignCenter>
<span>
<Button className="mr20" onClick={()=>{history.push(`/projects/${owner}/${projectsId}${item.sha ? `/branch/${truncateCommitId(item.sha)}/`:"/"}tree/${item.name}`)}}>查看文件</Button>
<Button className="mr20" onClick={()=>{history.push(`/projects/${owner}/${projectsId}/tree/${truncateCommitId(item.sha)}/${item.name}`)}}>查看文件</Button>
<span className="color-green">+{item.addition}</span>
<span className="color-red ml20">-{item.deletion}</span>
</span>

View File

@ -256,13 +256,13 @@ class merge extends Component {
const Paginations = (
<React.Fragment>
{search_count > limit ? (
{search_count > select_params.limit ? (
<div className="mt30 mb50 edu-txt-center">
<Pagination
simple
defaultCurrent={page}
defaultCurrent={select_params.page}
total={search_count}
pageSize={limit}
pageSize={select_params.limit}
onChange={this.ChangePage}
></Pagination>
</div>

View File

@ -43,7 +43,9 @@ class Index extends Component {
project_language_name: undefined,
project_category_name: undefined,
license_name: undefined,
ignore_name: undefined
ignore_name: undefined,
licenseForDisabled:undefined
}
}
componentDidMount = () => {
@ -145,7 +147,7 @@ class Index extends Component {
_data = data.filter(item => item.name.toLowerCase().indexOf(name.toLowerCase()) > -1);
}
let list = _data && _data.map((item) => (
<Option key={item.id} value={item.name}>
<Option key={item.id} value={item.name} onClick={()=>this.selectSerect(item.is_secret)}>
{item.name}
</Option>
));
@ -155,6 +157,17 @@ class Index extends Component {
}
}
selectSerect=(flag)=>{
if(flag){
this.props.form.setFieldsValue({
private:true
})
}
this.setState({
licenseForDisabled:flag
})
}
subMitFrom = () => {
this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) {
@ -198,7 +211,8 @@ class Index extends Component {
}
ChangePlatform = (value, e, name, list) => {
this.setOptionsList(list, name, value)
this.setOptionsList(list, name, value);
this.setState({
[name + "_id"]: e.key,
[name + "_name"]: value,
@ -272,6 +286,7 @@ class Index extends Component {
project_category_list,
license_list,
ignore_list,
licenseForDisabled,
mirrorCheck
} = this.state;
@ -477,8 +492,8 @@ class Index extends Component {
style={{ margin: "0px" }}
className="privatePart"
>
{getFieldDecorator('private')(
<Checkbox value="limit">将项目设为私有<span className="ml15 font-13 color-grey-9">(只有项目所有人或拥有权限的项目成员才能看到)</span></Checkbox>
{getFieldDecorator('private',{valuePropName:"checked"})(
<Checkbox value="limit" disabled={licenseForDisabled}>将项目设为私有<span className="ml15 font-13 color-grey-9">(只有项目所有人或拥有权限的项目成员才能看到)</span></Checkbox>
)}
</Form.Item >
{

View File

@ -15,8 +15,8 @@ const MENU_LIST = [
function CollaboratorMember({projectsId,owner,project_id,author,showNotification,newId}){
const [ roleName , setRoleName ] = useState(undefined);
const [ search , setSearch ] = useState(undefined);
const [ page , setPage ] = useState(undefined);
const [ isSpin , setIsSpin ] = useState(false);
const [ page , setPage ] = useState(1);
const [ isSpin , setIsSpin ] = useState(true);
const [ role , setRole ] = useState(undefined);
const [ listData , setListData ] = useState(undefined);
const [ total , setTotal ] = useState(0);
@ -166,7 +166,11 @@ function CollaboratorMember({projectsId,owner,project_id,author,showNotification
<label className={get_color(item.role)}>
{operation && operation[0].name}
</label>
) : (
)
:
item.is_apply_signature ?
<label className="text-grey">外围贡献者</label>
:(
<Dropdown overlay={setRoles(`${item.id}`)} placement={"bottomCenter"}>
<span className={get_color(item.role)}>
{operation && operation[0].name}
@ -261,14 +265,8 @@ function CollaboratorMember({projectsId,owner,project_id,author,showNotification
</div>
</Spin>
{total > LIMIT ?
<div className="edu-txt-center mt20 mb20">
<Pagination
showQuickJumper
pageSize={LIMIT}
current={page}
total={total}
onChange={()=>setPage(page)}
></Pagination>
<div className="edu-txt-center mt20 pb20">
<Pagination simple current={page} pageSize={LIMIT} total={total} onChange={(page)=>setPage(page)}/>
</div>
:""}
</React.Fragment>

View File

@ -28,6 +28,10 @@ const Tags = Loadable({
loader: () => import("./new_tags"),
loading: Loading,
});
const Special = Loadable({
loader: () => import("./SpecialProject"),
loading: Loading,
});
const Manage = Loadable({
loader: () => import("./ManageWeb"),
loading: Loading,
@ -40,6 +44,7 @@ class Index extends Component {
render() {
const { projectsId , owner } = this.props.match.params;
const { pathname } = this.props.history.location;
const { projectDetail } = this.props;
const flag = pathname === `/projects/${owner}/${projectsId}/setting`;
return (
@ -87,6 +92,20 @@ class Index extends Component {
</Link>
</p>
</li>
{
projectDetail && projectDetail.permission && (projectDetail.permission === "Owner" || projectDetail.permission === "Admin") ?
<li
className={pathname.indexOf("setting/special") > -1 ? "active" : ""}
>
<p>
<Link to={`/projects/${owner}/${projectsId}/setting/special`} className="w-100">
<i className="iconfont icon-jingyan font-18 mr10"></i>
特殊开源许可证项目管理
</Link>
</p>
</li>
:""
}
{/* <li
className={
@ -112,7 +131,15 @@ class Index extends Component {
<Collaborator {...this.props} {...props} {...this.state} />
)}
></Route>
<Route
path="/projects/:owner/:projectsId/setting/special"
render={(props) => (
<Special {...this.props} {...props} {...this.state} />
)}
></Route>
{/* 修改仓库信息 */}
<Route
path="/projects/:owner/:projectsId/setting/tags"
render={(props) => (

View File

@ -15,6 +15,7 @@ class Setting extends Component {
CategoryList: undefined,
LanguageList: undefined,
private_check: undefined,
is_secret:false
};
}
@ -52,10 +53,11 @@ class Setting extends Component {
.then((result) => {
if (result) {
this.props.form.setFieldsValue({
...result.data,
...result.data
});
this.setState({
private_check: result.data.private,
is_secret:result.data.is_secret
});
}
})
@ -152,7 +154,7 @@ class Setting extends Component {
render() {
const { getFieldDecorator } = this.props.form;
const { CategoryList, LanguageList, private_check } = this.state;
const { CategoryList, LanguageList, private_check , is_secret } = this.state;
return (
<div>
<WhiteBack style={{paddingBottom:"20px"}}>
@ -177,6 +179,7 @@ class Setting extends Component {
<Checkbox
checked={private_check}
onChange={this.changePrivate}
disabled={is_secret}
>
将仓库设为私有
</Checkbox>

View File

@ -0,0 +1,186 @@
import React , { useEffect , useState} from 'react';
import { Input , Table , Pagination, Button , Dropdown , Menu } from 'antd';
import { Banner , WhiteBack , AlignCenterBetween } from '../Component/layout';
import axios from 'axios';
const { Search } = Input;
const LIMIT = 15;
function SpecialProject(props){
const [ page , setPage] = useState(1);
const [ searchValue , SetSearchValue ] = useState(undefined);
const [ total , setTotal ] = useState(0);
const [ list , setList ] = useState(undefined);
const [ status , setStatus ] = useState(undefined);
const [ loading ,setLoading ] = useState(true);
const { owner , projectsId} = props.match.params;
const { project_id } = props;
useEffect(()=>{
if(project_id){
setLoading(true);
Init(searchValue, status);
}
},[page,project_id]);
function Init(search,status){
const url = `/apply_signatures.json`;
axios.get(url,{
params:{
project_id,
page,limit:LIMIT,search,status
}
}).then(result=>{
setLoading(false);
if(result){
setList(result.data.apply_signatures);
setTotal(result.data.total_count);
}
}).catch(error=>{})
}
function changePage(page){
setLoading(true);
setPage(page);
}
const column = [
{
dataIndex:"column",
key:1,
width:"12%",
title:"序号",
render:(txt,item,index)=>{
return `${index+1}`
}
},
{
dataIndex:"name",
key:2,
title:"申请人",
render:(text,item,m)=>{
return item.user && <span className="task-hide" style={{maxWidth:"139px",display:"block"}}>{item.user.name}</span>
}
},
{
dataIndex:"email",
key:2,
title:"邮箱",
width:"22%",
render:(text,item,m)=>{
return item.user && <span>{item.user.email}</span>
}
},
{
dataIndex:"attachment",
key:3,
title:"附件",
width:"28%",
render:(text,item,m)=>{
return item.attachment && <a className="task-hide" style={{maxWidth:"173px",display:"block"}} href={`${item.attachment.path}`}>{item.attachment.filename}</a>
}
},
{
dataIndex:"operation",
key:4,
width:"18%",
title:"操作",
render:(text, item) =>{
return(
<React.Fragment>
{
item.status === "waiting" &&
<span>
<Button size="small" onClick={()=>operation(item.id,"unpassed")}>拒绝</Button>
<Button size="small" onClick={()=>operation(item.id,"passed")} type={"primary"} className="ml20">同意</Button>
</span>
}
{
item.status === "unpassed" &&
<span style={{color:"#ff041c"}}>已拒绝</span>
}
{
item.status === "passed" &&
<span style={{color:"#13b4f1"}}>已同意</span>
}
</React.Fragment>
)
}
}
]
// &
function operation(ids,s){
setLoading(true);
const url = `/apply_signatures/${ids}.json`;
axios.put(url,{
project_id:project_id,
status:s
}).then(result=>{
setLoading(false);
if(result){
props.showNotification(`${s==="passed"?"同意":"拒绝"}此申请已操作成功!`);
Init(searchValue,status);
}
}).catch(error=>{setLoading(false)})
}
function searchList(){
setLoading(true);
Init(searchValue,status);
}
const menu=(
<Menu onClick={chooseStatus}>
<Menu.Item key="all">全部</Menu.Item>
<Menu.Item key="waiting">审核中</Menu.Item>
<Menu.Item key="unpassed">已拒绝</Menu.Item>
<Menu.Item key="passed">已同意</Menu.Item>
</Menu>
)
function chooseStatus(e){
setStatus(e.key);
Init(searchValue, e.key);
}
return(
<WhiteBack style={{minHeight:"500px"}}>
<Banner>项目管理</Banner>
<AlignCenterBetween style={{padding:"10px 20px",textAlign:"right"}}>
<Search
placeholder="请输入用户姓名或者邮箱搜索"
allowClear
enterButton="搜索"
style={{width:400}}
size="middle"
value={searchValue}
onChange={(e)=>SetSearchValue(e.target.value)}
onSearch={searchList}
/>
<Dropdown overlay={menu} placement="bottomRight">
<span>
<span style={{color:status ? "color-blue" : "color-grey-3"}}>{status ==="waiting"?"审核中":status==="unpassed"?"已拒绝":status==="passed"?"已同意":"全部"}</span>
<i className="iconfont icon-xiajiantou color-grey-9 font-14 ml8"></i>
</span>
</Dropdown>
</AlignCenterBetween>
<Table
columns={column}
rowKey={(record) => record.id}
pagination={false}
dataSource={list}
loading={loading}
></Table>
{
total > LIMIT &&
<div className="center mt20 mb20">
<Pagination simple current={page} total={total} pageSize={LIMIT} onChange={changePage}></Pagination>
</div>
}
</WhiteBack>
)
}
export default SpecialProject;

View File

@ -139,6 +139,7 @@
.padding15-10{
padding:15px 10px;
}
.center{text-align: center;}
.w-100{width: 100%;}
.fwb{font-weight: 600;}
.text-black{color: #333;}
@ -151,6 +152,9 @@
.text-yellow{color: #FF6E21 !important;}
.text-delete{color: #BBBBBB; }
.text-delete:hover{color: #db2828; }
.text-grey{
color: #999;
}
.new-tag-div{
padding: 15px;
height: 75px;

View File

@ -0,0 +1,47 @@
import React , { forwardRef, useEffect } from 'react';
import { Modal , Form , Input } from 'antd';
function AddTag({form , visible , onCancel ,onOk}){
const { getFieldDecorator, validateFields , setFieldsValue } = form;
useEffect(()=>{
setFieldsValue({tagName:undefined})
},[visible])
function submit(){
validateFields((error,values)=>{
if(!error){
onOk(values);
}
})
}
const layout = {
labelCol: { span: 5 },
wrapperCol: { span: 18 },
};
return(
<Modal
title={"新增标签"}
closable={false}
visible={visible}
onCancel={onCancel}
onOk={submit}
cancelText="取消"
okText="确定"
width="400px"
centered
>
<Form {...layout}>
<Form.Item label="标签名">
{getFieldDecorator("tagName",{
rules:[{required:true,message:"请输入标签名"}]
})(
<Input placeholder="请输入标签名" width="200px" autoComplete="off" />
)}
</Form.Item>
</Form>
</Modal>
)
}
export default Form.create()(forwardRef(AddTag));

288
src/forge/Source/Index.jsx Normal file
View File

@ -0,0 +1,288 @@
import React, { useEffect, useState } from 'react';
import './Index.scss';
import { AlignCenter, Blueback , FlexAJ } from '../Component/layout';
import { Dropdown, Input , Menu , Pagination, Spin , Popconfirm, Button } from 'antd';
import { Link } from 'react-router-dom';
import UploadSource from './UploadSource';
import AddTag from './AddTag';
import { getImageUrl } from 'educoder';
import Nodata from '../Nodata';
import axios from 'axios';
const { Search } = Input;
const sort = [
"按上传时间排序",
"按下载次数排序"
]
const limit = 15;
const https = 'http://117.50.100.12:8001';
function Index(props){
const [ sortValue , setSortValue ] = useState(0);
const [ page , setPage ] = useState(1);
const [ total , setTotal ] = useState(0);
const [ search , setSearch ] = useState(undefined);
const [ data , setData ] = useState(undefined);
const [ isSpin , setIsSpin ] = useState(true);
const [ error , setError ] = useState(false);
const [ attachments , setAttachments ] = useState(undefined);
const [ id , setId ] = useState(undefined);
const [ visible , setVisible ] = useState(false);
const [ addVisible , setAddVisible ] = useState(false);
const repo_id = props.projectDetail && props.projectDetail.repo_id;
const owner = props.match.params.owner;
const current_user = props.current_user;
useEffect(()=>{
if(owner && repo_id){
setIsSpin(true);
getData();
}
},[repo_id,owner,search,sortValue,page])
function getData(){
const url = https +`/api/project/achievement/`;
axios.defaults.withCredentials = true;
axios.get(url,{
params:{
projectId:repo_id,
curPage:page,
pageSize:limit,
name:search,
sort:sortValue+1,
}
}).then(result=>{
if(result && result.data){
setData(result.data.data.rows);
setTotal(result.data.data.total);
setIsSpin(false);
setError(false);
}
}).catch(error=>{setIsSpin(false);setError(true);})
}
//
function onSearch(value){
setSearch(value);
}
//
function changeSort(e,index){
setSortValue(index);
}
const menu=(
<Menu>
{
sort && sort.map((item,key)=>{
return(
<Menu.Item onClick={(e)=>changeSort(e,key)} value={key} className={key=== sortValue ?"color-blue":""}>{item}</Menu.Item>
)
})
}
</Menu>
)
function listmenu(id,attachments,isPublic){
return(
<Menu>
<Menu.Item onClick={()=>{setId(id);setVisible(true);setAttachments(attachments)}}>更新版本</Menu.Item>
<Menu.Item onClick={()=>changeStatus(id,isPublic===1?0:1)}>{isPublic === 1 ? "设为私有":"设为公开"}</Menu.Item>
<Menu.Item onClick={()=>deleteSourceFunc(id)}>删除资源</Menu.Item>
</Menu>
)
}
//
function changeStatus(id,isPublic){
const url = https+`/api/project/achievement/updateStatus`;
axios.put(url,{
id,status:isPublic
}).then(result=>{
if(result && result.data){
props.showNotification(`资源${isPublic === 1 ? "设为公开":"设为私有"}成功!`);
setIsSpin(true);
getData();
}
}).catch({})
}
//
function deleteSourceFunc(id){
props.confirm({
content: "是否确认删除所选资源文件?",
onOk: () => {
const url = https + `/api/project/achievement/${id}`;
axios.delete(url).then(result=>{
if(result && result.data && result.data.code === "1"){
props.showNotification("资源删除成功");
setIsSpin(true);
getData();
}
})
}
})
}
//
function onOk(){
setVisible(false);
setIsSpin(true);
getData();
}
//
function removeTagFunc(id,tag){
const url = https + `/api/project/achievement/deleteTag`;
axios.delete(url,{
params:{id,tagName:tag}
}).then(result=>{
if(result && result.data){
props.showNotification("标签删除成功");
setIsSpin(true);
getData();
}
}).then(error=>{})
}
function addPanel(id){
setAddVisible(true);
setId(id);
}
function onCancelAdd(){
setId(undefined);
setAddVisible(false);
}
//
function sureAddTag(values){
const url = https+`/api/project/achievement/addTag?id=`+id+`&tagName=`+values.tagName;
axios.put(url).then(result=>{
if(result){
setId(undefined);
setAddVisible(false);
setIsSpin(true);
getData();
}
})
}
return(
<div className="sourcePanel">
<AddTag
visible={addVisible}
onCancel={onCancelAdd}
onOk={sureAddTag}
/>
<UploadSource
visible={visible}
onCancel={()=>setVisible(false)}
onOk={onOk}
showNotification={props.showNotification}
owner={owner}
projectsId={repo_id}
id={id}
attachments={attachments}
/>
<div className="headtitle">
<FlexAJ>
<span className="font-18">资源库{total ? <span>({total})</span>:""}</span>
{ current_user && current_user.login && (props.projectDetail && props.projectDetail.permission) ?
<Blueback onClick={()=>{setId(undefined);setVisible(true);}}>上传资源</Blueback>:""
}
</FlexAJ>
</div>
<FlexAJ className="subHeadtitle">
<Search
placeholder="在项目内搜索资源"
onSearch={onSearch}
allowClear
enterButton="搜索"
width="220px"
/>
<Dropdown overlay={menu} placement="bottomRight">
<span className="color-grey-9">{sort[sortValue]}<i className="iconfont icon-sanjiaoxing-down font-16 color-grey-9 ml3"></i></span>
</Dropdown>
</FlexAJ>
<Spin spinning={isSpin}>
<div className="bodycontent">
{
data && data.length> 0 &&
<ul className="bodycontentul">
{
data.map((item,key)=>{
return(
<li>
<Link to= {`/users/${item.login}`} className="infoImg"><img src={getImageUrl(`${item.imageUrl}`)} alt="" /></Link>
<div style={{flex:'1',width:"0"}}>
<FlexAJ>
<AlignCenter>
<a href={https+`/busiAttachments/download/${item.attachId}`} download className="infoname">{item.fileName}</a>
<a href={https + `/busiAttachments/view/${item.attachId}`}><i className="iconfont icon-shenqinggongkai font-15 ml10 color-grey-9"></i></a>
{item.isPublic === 0 && <span className="privateTip">私有</span>}
</AlignCenter>
{ current_user && current_user.login &&
<Dropdown overlay={()=>listmenu(item.id,item.attachments,item.isPublic)} placement={'bottomRight'}>
<i className="iconfont icon-gengduo1 color-grey-6"></i>
</Dropdown>
}
</FlexAJ>
<p className="infos">
<span>上传时间<span>{item.uploadTime}</span></span>
<span>文件大小<span>{item.fileSize}</span></span>
<span>下载<span>{item.download}</span></span>
</p>
<p className="infodesc task-hide-2">{item.remark}</p>
<div className="infotag">
{
item.tags && item.tags.length>0 && item.tags.map((i,k)=>{
return(
<span>{i}
{
current_user && (current_user.login === item.login) ?
<Popconfirm title="确定要删除当前标签?" onConfirm={()=>removeTagFunc(item.id,i)} okText="是" cancelText="否">
<i className="iconfont icon-guanbi font-12 ml2"></i>
</Popconfirm>:""
}
</span>
)
})
}
{
current_user && (current_user.login === item.login) &&
<a className="color-blue font-12" onClick={()=>addPanel(item.id)} style={{height:"20px",lineHeight:"20px"}}>+新增标签</a>
}
</div>
</div>
</li>
)
})
}
</ul>
}
{
((data && data.length === 0) || error) && <Nodata _html="暂无数据"/>
}
{
total > limit &&
<div className="pt20 pb20 edu-txt-center">
<Pagination
simple
current={page}
pageSize={limit}
total={total}
onChange={(p)=>{setPage(p)}}
/>
</div>
}
</div>
</Spin>
</div>
)
}
export default Index;

109
src/forge/Source/Index.scss Normal file
View File

@ -0,0 +1,109 @@
.sourcePanel{
width: 1200px;
margin: 20px auto;
background: #fff;
border-radius: 2px;
box-shadow: 0px 0px 4px rgba(0,0,0,0.1);
.headtitle{
padding:15px 20px;
border-bottom: 1px solid #eee;
}
.subHeadtitle{
padding:15px 20px;
border-bottom: 1px solid #eee;
.ant-input-group-wrapper{
width:320px;
.ant-btn.ant-input-search-button{
margin: 0px;
margin-top: -1px;
}
}
}
.bodycontent{
padding:0px 20px;
min-height: 500px;
& > ul.bodycontentul > li{
display: flex;
border-bottom: 1px solid #eee;
padding:20px 0px;
align-items: flex-start;
&:last-child{
border-bottom: none;
}
.infoImg{
img{
width: 50px;
height: 50px;
border-radius: 50%;
}
margin-right: 15px;
}
.infoname{
font-size: 16px;
}
.privateTip{
display: block;
font-size: 12px;
margin-left: 10px;
background-color: orange;
height: 18px;
line-height: 18px;
padding:0px 3px;
color: #fff;
}
.infos{
& > span{
margin-right: 20px;
color: #999;
& >span{
color: #666;
}
}
}
.infodesc{
color: #666;
line-height: 20px;
margin:5px 0px!important;
}
.infotag{
display: flex;
flex-wrap: wrap;
span{
display: block;
padding:0px 4px;
height: 20px;
line-height: 20px;
font-size: 12px;
margin-right: 10px;
border: 1px solid #f8df8c;
background: #fffce6;
color: #0d90c3;
border-radius: 2px;
cursor: pointer;
}
}
}
}
}
.versionTable{
.currentTip{
display: block;
padding:0px 3px;
border-radius: 2px;
border:1px solid #68c7ec;
font-size: 12px;
color: #68c7ec;
height: 18px;
line-height: 18px;
margin-left: 5px;
}
.ant-table-body{
margin:0px!important;
thead{
background-color: #eee;
}
thead >tr >th,tbody > tr > td{
padding:4px 5px!important;
}
}
}

View File

@ -0,0 +1,82 @@
import React, { useEffect, useState } from "react";
import { Upload, Button } from 'antd';
import { appendFileSizeToUploadFileAll } from 'educoder';
import axios from 'axios';
function Uploads({ className , size , actionUrl,fileList,showNotification , load}) {
const [ files , setFiles ] = useState(undefined);
useEffect(()=>{
if(fileList){
init();
}
},[fileList]);
function init(){
let f = appendFileSizeToUploadFileAll(fileList);
setFiles(f);
}
function onAttachmentRemove(file){
if (!file.percent || file.percent === 100) {
deleteAttachment(file);
return false;
}
}
function deleteAttachment(file){
let id = file.response && file.response.data && file.response.data.id;
const url = actionUrl + `/busiAttachments/${id}`;
axios.delete(url).then((response) => {
if (response.data) {
if (response.data.code === "1") {
let nf = files.filter(item=>item.response.data.id !== id);
setFiles(nf);
fileIdList(nf);
} else {
showNotification(response.data.message)
}
}
}).catch(function (error) {
console.log(error);
});
}
function handleChange (info) {
if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
let fileList = info.fileList;
let len = info.fileList && info.fileList.length;
setFiles(appendFileSizeToUploadFileAll([fileList[len-1]]));
fileIdList(fileList[len-1]);
}
}
function fileIdList (fileList) {
let data = fileList.response && fileList.response.data;
fileList && load && load(data && data.id,data && data.fileName);
}
function beforeUpload(file){
const isLt100M = file.size / 1024 / 1024 < size;
if (!isLt100M) {
showNotification(`文件大小必须小于${size}MB!`);
}
return isLt100M;
}
const upload = {
name: 'file',
fileList: files,
action: actionUrl+`/busiAttachments/upload`,
onChange:handleChange,
onRemove:onAttachmentRemove,
beforeUpload:beforeUpload,
maxCount:1
};
return (
<Upload {...upload} className={className}>
<Button type={"default"}>上传文件</Button>
<span className="ml10 color-grey-9">(你可以上传小于<span className="color-red">{size}MB</span>的文件)</span>
</Upload>
)
}
export default Uploads;

View File

@ -0,0 +1,150 @@
import React , { forwardRef, useEffect, useState } from 'react';
import { Modal , Form , Checkbox , Input , Table } from 'antd';
import Upload from './Upload';
import { AlignCenter } from '../Component/layout';
import axios from 'axios';
const { TextArea } = Input;
const https = 'http://117.50.100.12:8001';
function UploadSource({ form , visible , onCancel , onOk , showNotification , attachments , id ,owner,projectsId}){
const [ tableData , setTableData ] = useState(undefined);
const [ fileId , setFilesId ] = useState(undefined);
const [ fileName , setFileName ] = useState(undefined);
const { getFieldDecorator, validateFields , setFieldsValue } = form;
useEffect(()=>{
if(id && attachments){
setTableData(attachments);
}
},[id,attachments])
// id
function UploadFunc(id,name){
setFilesId(id);
setFileName(name);
}
const columns = [
{
dataIndex:"fileName",
key:"fileName",
title:"资源名称",
width:"42%",
ellipsis:true,
render:(value,item,key)=>{
return <AlignCenter>
<div className="task-hide" style={{maxWidth:key===0 ? "240px":"100%"}}>{value}</div>
{ key === 0 && <span className="currentTip">当前版本</span> }
</AlignCenter>
}
},
{
dataIndex:"downloads",
key:"downloads",
title:"下载数",
width:"14%",
className:"edu-txt-center"
},
{
dataIndex:"fileSizeString",
key:"fileSizeString",
title:"文件大小",
width:"16%",
className:"edu-txt-center"
},
{
dataIndex:"createdAt",
key:"createdAt",
title:"上传时间",
}
]
//
function submit(){
if(fileId){
validateFields((error,values)=>{
if(!error){
postInfo(values);
}
})
}else{
showNotification("请先上传文件!");
}
}
function postInfo(values){
const url = https+`/api/project/achievement/`;
if(id){
//
axios.put(url,{
id,fileName,fileId:`${fileId}`,
remark:values.remark
}).then(result=>{
if(result && result.data){
onOk();
}
}).catch(error=>{})
}else{
//
axios.post(url,{
fileId:`${fileId}`,
fileName,
login:owner,
projectId:projectsId,
...values
}).then(result=>{
if(result && result.data){
onOk();
}
}).catch(error=>{})
}
}
return(
<Modal
title={id?"更新资源版本":"上传资源"}
closable={false}
visible={visible}
onCancel={onCancel}
onOk={submit}
cancelText="取消"
okText="确定"
width="600px"
centered
>
<div>
<Form>
{id && <Table className="versionTable mb20" columns={columns} dataSource={tableData} pagination={false} size={"small"}/> }
<Form.Item style={{display:id?"none":"block"}}>
{getFieldDecorator("tagNames",{
rules:[]
})(
<Checkbox.Group>
<Checkbox value="软件版本">软件版本</Checkbox>
<Checkbox value="文档">文档</Checkbox>
<Checkbox value="代码">代码</Checkbox>
<Checkbox value="媒体">媒体</Checkbox>
<Checkbox value="论文">论文</Checkbox>
<Checkbox value="其它">其它</Checkbox>
</Checkbox.Group>
)}
</Form.Item>
<Upload
className="commentStyle"
load={UploadFunc}
size={100}
showNotification={showNotification}
actionUrl= {https}
/>
<Form.Item className="mt20">
{getFieldDecorator("remark",{
rules:[]
})(
<TextArea rows={4} placeholder="请输入资源描述" />
)}
</Form.Item>
</Form>
</div>
</Modal>
)
}
export default Form.create()(forwardRef(UploadSource));;

View File

@ -10,6 +10,7 @@ export default (({ data , current_user , successFunc }) => {
data.map((item, key) => {
return (
<Cards
user={item.user}
img={item.user.image_url}
name={item.user.name}
time={item.created_at}

View File

@ -1,5 +1,5 @@
import React, { Component } from "react";
import { Upload, Button, Icon } from 'antd';
import { Upload , Icon } from 'antd';
import { getUploadActionUrl, appendFileSizeToUploadFileAll } from 'educoder';
import axios from 'axios';
@ -45,8 +45,7 @@ class Index extends Component {
deleteAttachment = (file) => {
const url = `/attachments/${file.response ? file.response.id : file.uid}.json`
axios.delete(url, {
}).then((response) => {
axios.delete(url).then((response) => {
if (response.data) {
if (response.data.status === 0) {
this.setState((state) => {
@ -71,11 +70,26 @@ class Index extends Component {
handleChange = (info) => {
const { changeIsComplete } = this.props;
changeIsComplete && changeIsComplete(true);
if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
let fileList = info.fileList;
this.setState({ fileList: appendFileSizeToUploadFileAll(fileList) });
this.fileIdList(fileList);
if ( info.file.status === 'done') {
let filelist = info.fileList && info.fileList.length>0 && info.fileList[info.fileList.length-1];
if(filelist && filelist.response && filelist.response.status === -1){
this.props.showNotification(filelist.response.message)
this.setState((state) => {
state.fileList.pop()
return {
fileList: state.fileList,
};
});
this.fileIdList(this.state.fileList);
}
}
}
}

View File

@ -0,0 +1,78 @@
import React, { useEffect , useState } from "react";
import { Upload } from 'antd';
import { getUploadActionUrl, appendFileSizeToUploadFileAll } from 'educoder';
import axios from 'axios';
function Single({ children , showNotification , className , load , size }) {
const [ fileList , setFileList ] = useState(undefined);
//
function onAttachmentRemove(file){
if (!file.percent || file.percent === 100) {
deleteAttachment(file);
return false;
}
}
function deleteAttachment(file){
let uid = file.response ? file.response.id : file.uid;
const url = `/attachments/${uid}.json`
axios.delete(url).then((response) => {
if (response.data && response.data.status === 0) {
let list = fileList.filter(item=> item.response && item.response.id !== uid);
setFileList(list);
fileIdList(list);
}
}).catch(error=>{});
}
function handleChange(info){
if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
let fileList = [info.file];
let list = appendFileSizeToUploadFileAll(fileList);
setFileList(list);
if ( info.file.status === 'done') {
let f = info.fileList && info.fileList.length>0 && info.fileList[info.fileList.length-1];
if(f && f.response && f.response.status === -1){
showNotification(f.response.message)
setFileList(f);
}
fileIdList(fileList);
}
}
}
function fileIdList(fileList){
let l = fileList && fileList.length > 0 && fileList[0];
let array = [l && l.response && l.response.id];
load && load(array);
}
function beforeUpload(file){
if(!size) return;
const isLt100M = file.size / 1024 / 1024 < size;
if (!isLt100M) {
showNotification(`文件大小必须小于${size}MB!`);
}
return isLt100M;
}
//
const upload = {
name: 'file',
fileList: fileList,
action: `${getUploadActionUrl()}`,
onChange: handleChange,
onRemove: onAttachmentRemove,
beforeUpload: beforeUpload
};
return (
<Upload {...upload} className={className}>
{children}
</Upload>
)
}
export default Single;

View File

@ -1,21 +0,0 @@
1.请求URL https://code.ihub.org.cn/api/v1/mirrors/create.json
2.请求方式: POST
3.参数:
{
"image_url": "xxx.git", #必填,且后缀必为.git,
"language": "Ruby", #必填,如数据库不存在,则会创建新的记录
}
4. 返回值: {
"status": 1,
"message": "同步成功项目ID===1806"
}
5. 返回值说明: 仅有当有返回值且返回值的status 的值为1 才是创建成功,其余均为创建失败

View File

@ -62,6 +62,9 @@
.text-yellow{
color: #FFA802 !important
}
.text-grey{
color: #999;
}
.text-gray {
color: #888888;
}

View File

@ -166,7 +166,12 @@ form{
margin-bottom: 12px;
border-radius:2px;
background-color: #fff;
&>li{
.list-affix{
min-height: 20px;
max-height: 180px;
overflow-y: auto;
}
& li{
font-size: 1rem;
padding:0px 0px 0px 20px;
box-sizing: border-box;

View File

@ -6,7 +6,6 @@ import { withRouter } from "react-router";
import { SnackbarHOC } from "educoder";
import { CNotificationHOC } from "../../modules/courses/common/CNotificationHOC";
import { TPMIndexHOC } from "../../modules/tpm/TPMIndexHOC";
import Handbook from '../Component/Handbook';
const Infos = Loadable({
loader: () => import("./Infos"),
loading: Loading,
@ -15,7 +14,6 @@ export default withRouter(
(CNotificationHOC()(SnackbarHOC()(TPMIndexHOC((props)=>{
return(
<div>
<Handbook />
<Switch>
<Route
path="/users/:username"
@ -27,4 +25,4 @@ export default withRouter(
</div>
)
}))))
)
)

View File

@ -1,5 +1,4 @@
import React, { Component } from "react";
import { Link } from "react-router-dom";
import { Avatar, Tag, Button, Spin } from "antd";
import FocusButton from "../UsersList/focus_button";
@ -52,7 +51,7 @@ class Infos extends Component {
isSpin: false,
user: undefined,
project_type: undefined,
route_type: undefined
route_type: undefined,
};
}
@ -138,7 +137,7 @@ class Infos extends Component {
render() {
const { current_user, mygetHelmetapi } = this.props;
const { current_user, main_web_site_url } = this.props;
const { username } = this.props.match.params;
const { user, isSpin, project_type, route_type } = this.state;
@ -147,7 +146,7 @@ class Infos extends Component {
<Spin spinning={isSpin}>
<div className="new-content-flex">
<div className="list-left">
<div className="bgcF">
<div className="bgcF mb20">
<div className="list-l-Menu text-center pd20 ">
<Avatar
size={110}
@ -168,11 +167,7 @@ class Infos extends Component {
<Button
block
className="text-button-grey"
href={`${
mygetHelmetapi &&mygetHelmetapi.new_course&&
mygetHelmetapi.new_course.edit_account
}`}
target="_blank"
href={`${main_web_site_url || "https://osredm.com/"}users/${user.login}/profiles`}
>
{" "}
<i className="iconfont icon-shezhi4 font-15 mr5"></i>
@ -193,22 +188,22 @@ class Infos extends Component {
)}
</div>
<div className="width100 inline-block mt20">
<Link
to={`/users/${user && user.login}/watchers`}
<a
href={`/users/${user && user.login}/user_watchlist`}
className={`with50 text-center pull-left ${route_type === "watchers" ? "text-primary" : ""}`}
onClick={() =>this.route_link("watchers")}
>
<div>{current_user && user && user.login === current_user.login ? "我关注的" : "TA关注的"}</div>
<span>{user && user.watching_count}</span>
</Link>
<Link
to={`/users/${user && user.login}/fan_users`}
</a>
<a
href={`/users/${user && user.login}/user_fanslist`}
onClick={() =>this.route_link("fan_users")}
className={`with50 text-center pull-left ${route_type === "fan_users" ? "text-primary" : ""}`}
>
<div>{current_user && user && user.login === current_user.login ? "关注我的" : "关注TA的"}</div>
<span>{user && user.watched_count}</span>
</Link>
</a>
</div>
</div>
</div>
@ -230,7 +225,7 @@ class Infos extends Component {
<div className="bgcF">
<ul className="list-l-Menu">
<li className="MenuTitle" onClick={() => this.change_project_type(undefined)}>
<li className="MenuTitle" onClick={() => this.change_project_type()}>
<i className="iconfont icon-xiangmuleixing font-15 mr5"></i>
项目类型
<i className="iconfont icon-youjiantou font-15 mr20 color-grey-9 pull-right"></i>

View File

@ -580,9 +580,9 @@ class LoginDialog extends Component {
<label htmlFor="p_autolog" style={{ top: '0px' }}>下次自动登录</label>
</span>
<span className="fr">
<a onClick={(url) => this.getloginurl("https://www.trustie.net/account/lost_password")}
className="mr3 color-grey-9">找回密码</a><em className="vertical-line"></em>
<a onClick={(url) => this.getloginurl("https://www.trustie.net/login?login=false")} className="color-grey-9">注册</a>
{/* <a onClick={(url) => this.getloginurl("https://osredm.com/account/lost_password")}
className="mr3 color-grey-9">找回密码</a><em className="vertical-line"></em> */}
<a onClick={(url) => this.getloginurl("https://osredm.com/user_join")} className="color-grey-9">注册</a>
</span>
</p>

View File

@ -713,7 +713,7 @@ class NewHeader extends Component {
mygetHelmetapi2 && mygetHelmetapi2.nav_logo_url ?
<img alt="可控开源社区" className="logoimg" style={{ heigth: "40px" }} src={getImageUrl(mygetHelmetapi2.nav_logo_url)}></img>
:
<img alt="可控开源社区" className="logoimg" style={{ heigth: "40px" }} src={logo}></img>
""
}
</a>
<div className="head-nav pr" id={"head-navpre1"}>

File diff suppressed because it is too large Load Diff

View File

@ -33,7 +33,8 @@ body>.-task-title {
position: fixed;
}
.headerContent{
width:1200px;
width:100%;
padding:0px 70px;
margin:0px auto;
display: flex;
align-items: center;
@ -130,11 +131,6 @@ body>.-task-title {
color: #000 !important;
}
.newHeader {
background: #24292D !important;
height: 60px !important;
}
/*-------------------个人主页:右侧提示区域--------------------------*/
.-task-sidebar {
position: fixed;

View File

@ -0,0 +1,119 @@
.headerRight{
float: right;
}
.head-navnew{
width:97%;
display: flex;
}
.iconSearch{
color: #666 !important;
margin-top: -13px !important;
display: block;
}
.headerLeft{
flex:1;
}
.headerRight{
flex:0.8;
width:200px;
}
.headerRightbox{
width:100%;
display:flex;
}
.radius{border-radius: 50%;}
.posi-search{
flex:8;
margin-top:15px;
}
.headRightbox{
display:flex;
flex:4;
}
.headRightboxuse{
display:flex;
flex:2;
margin-right: 15px;
}
.headRightpan,.headRightwrite,.headRighthint{
width: 48px;
height: 70px;
line-height: 70px;
}
.headRighthint-font{
color:#666 !important;
}
.searchBox{
float:right;
}
.newslight{
left: 25px !important;
top: 14px !important;
color:#fff;
min-height:18px;
min-width: 18px;
line-height:18px;
padding:0px 3px;
border-radius:50%;
background:red;
position:absolute;
font-size:12px
}
.isLogin{
line-height:46px
}
.isLoginImg{
margin-top: 7px !important;
}
.headerRight a {
color: #1A3F5F;
font-size: 14px;
}
.headerRight a:hover{
color: #21B351 !important;
}
.headerRight i{
font-size: 18px !important;
margin-top: 17px;
vertical-align: -4px;
}
.headIcon, #header_keyword_search {
padding-top: 0px !important;
}
.search-all {
width: 300px !important;
height: 28px !important;
}
.edu-menu-list li{
text-align:left;
}
.eduSearch{
width:50px !important;
height:26px;
line-height: 26px;
}
.eduSearch:after {
position: absolute;
height: 10px;
width: 1px;
background-color: #EBEBEB;
right: 0px;
top: 9px;
content: '';
}
.searchLabel{
margin: 0px !important;
vertical-align: 0px !important;
font-size: 12px !important;
}
.search-input{
width: 73% !important;
margin-left: 2%;
}
.newHeader .search-all .search-input {
color: #000;
}
.border-Tgrey-eb{
border-top: 1px solid #ebebeb;
}

View File

@ -0,0 +1,150 @@
.newHeader {
background: #fff;
width: 100%;
height: 70px;
min-width: 1200px;
position: fixed;
top: 0px;
left: 0px;
z-index: 99;
-moz-box-shadow: 0px 0px 12px rgba(0,0,0,0.1);
box-shadow: 0px 0px 12px rgba(0,0,0,0.1);
}
.educontent {
width: 1200px;
margin: 0 auto;
}
.head-nav {
text-align: center;
height: 60px;
box-sizing: border-box;
width:100%;
padding:0px 70px
}
.newHeader .logoimg {
margin-top: 12px;
float: left;
width: 48px;
}
.headerLeft {
-ms-flex: 1 1;
flex: 1 1;
}
.head-nav ul#header-nav {
position: absolute;
top: 0px;
z-index: 3;
height: 70px;
box-sizing: border-box;
}
.head-nav ul#header-nav li {
float: left;
height: 70px;
line-height: 70px;
padding: 0px 24px;
cursor: pointer;
position: relative;
font-size: 16px;
}
.head-nav ul#header-nav li a {
display: block;
height: 100%;
width: 100%;
color: #333;
position: relative;
}
.head-nav ul#header-nav li:hover a, .head-nav ul#header-nav li.active a{
color: #1484EF;
}
.head-nav ul#header-nav li.active a::after{
content: "";
width: 100%;
height:2px;
background-color: #1484EF;
left:0px;
bottom: 12px;
position: absolute;
}
.headRightboxuse {
-ms-flex: 2 1;
flex: 2 1;
margin-right: 15px;
}
.headRightbox, .headRightboxuse {
display: -ms-flexbox;
display: flex;
}
.edu-menu-panel {
position: relative;
cursor: pointer;
}
.edu-menu-panel:hover .edu-menu-list{
display: block;
}
ul.edu-menu-list li:hover{
background: #fafafa !important;
/* color: #1484EF !important */
}
ul.edu-menu-list li:hover > a{color: #1484EF !important;}
.headRighthint, .headRightpan, .headRightwrite {
width: 48px;
height: 60px;
line-height: 60px;
}
.color-white {
color: #ffffff!important;
}
em.vertical-line {
display: inline-block;
width: 2px;
background: #999;
height: 10px;
}
.edu-menu-list {
position: absolute;
padding: 5px 0px;
box-shadow: 0 2px 8px 0 rgba(0,0,0,.2);
display: none;
width: 120px;
background: #FFFFff;
right: -5px;
border-radius: 0px 0px 4px 4px;
color: #05101a;
font-size: 14px;
z-index: 100;
}
.edu-menu-list li {
width: 100%;
padding: 0px 15px;
box-sizing: border-box;
height: 35px;
line-height: 35px;
cursor: pointer;
}
.edu-menu-list li a, .edu-menuSmall-list li a {
width: 100%;
height: 100%;
display: block;
color: #323232;
text-align: center;
}
.currentName {
display: block;
width: 100%;
padding: 0px 15px;
height: 40px;
line-height: 40px;
font-size: 16px;
box-sizing: border-box;
cursor: default;
text-align: center;
}
.border-Bgrey-eb {
border-bottom: 1px solid #ebebeb;
}
.task-hide {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}