forked from Gitlink/forgeplus-react
ADD 成果库模块功
This commit is contained in:
commit
d9e3e0d96b
|
|
@ -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",
|
||||
|
|
|
|||
39
src/App.js
39
src/App.js
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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))))
|
||||
);
|
||||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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>
|
||||
)
|
||||
})
|
||||
)
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
.achieve-form{
|
||||
padding:24px 40px 0px 40px;
|
||||
}
|
||||
|
||||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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>
|
||||
|
||||
)
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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>
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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))))
|
||||
);
|
||||
|
|
@ -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'
|
||||
});
|
||||
}
|
||||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
@ -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%;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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>
|
||||
)
|
||||
})
|
||||
)
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
.achieve-form{
|
||||
padding:24px 40px 0px 40px;
|
||||
}
|
||||
|
||||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
@ -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>
|
||||
)
|
||||
})
|
||||
)
|
||||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -44,6 +44,7 @@ function Index(props){
|
|||
|
||||
function getData(){
|
||||
const url = https +`/api/project/achievement/`;
|
||||
axios.defaults.withCredentials = true;
|
||||
axios.get(url,{
|
||||
params:{
|
||||
projectId:repo_id,
|
||||
|
|
|
|||
Loading…
Reference in New Issue