新增佐证上传与审核

This commit is contained in:
何童崇 2021-07-06 21:28:38 +08:00
parent 21a3c54754
commit a4d38f8c86
15 changed files with 604 additions and 25 deletions

View File

@ -3,7 +3,6 @@ import axios from 'axios';
import cookie from 'react-cookies';
let actionUrl = '';
if (window.location.href.indexOf('localhost') > -1) {
actionUrl='http://117.50.100.12:8008';

View File

@ -46,6 +46,11 @@ const PaperComplain = Loadable({
loading: Loading,
});
const ProofManage = Loadable({
loader: () => import("./task/proofManage"),
loading: Loading,
});
const Index = (propsTransmit) => {
// 开发时,从代理的位置获取用户信息
const [currentUser, setCurrentUser] = useState(null);
@ -115,6 +120,13 @@ const Index = (propsTransmit) => {
)}
></Route>
<Route
path="/task/proofManage"
render={(props) => (
<ProofManage {...propsF} {...props} />
)}
></Route>
<Route
path="/task"
render={(props) => (

View File

@ -298,3 +298,37 @@ export function checkComplain(data){
}
// 佐证上传
export function proofAdd(data){
return fetch({
url: `/api/taskResultProof/addTaskResultProof`,
method: 'post',
data,
});
}
// 审核佐证材料列表查询
export async function proofList(params) {
let res = await fetch({
url: '/api/tasks/backend/proofList',
method: 'get',
params,
});
if (res.data) {
return res.data;
} else {
notification.open({
message: "提示",
description: res.message || '请求错误',
});
}
}
// 审核佐证
export function checkProof(data){
return fetch({
url: `/api/taskResultProof/adminCheckResultAndProof`,
method: 'post',
data,
});
}

View File

@ -13,11 +13,11 @@ for (const item of taskStatusAllArr) {
statusArr[item.dicItemCode] = item.dicItemName;
}
export default (props) => {
const { list, curPage, total, changePage, taskCategoryValueArr, loading, publish, } = props;
const { list, curPage, total, changePage, taskCategoryValueArr, loading, publish, showNotification ,reloadList} = props;
const [page, setPage] = useState(1);
const [visibleProofs, setVisibleProofs] = useState(false);
const [taskId, setTaskId] = useState({});
const [taskId, setTaskId] = useState();
const pageSize = props.pageSize || 10;
useEffect(() => {
@ -36,6 +36,7 @@ export default (props) => {
})
}
return (
<React.Fragment>
<ul className="df mt10 needs_condition_content_nav">
@ -116,6 +117,8 @@ export default (props) => {
taskId={taskId}
visible={visibleProofs}
changeVisible={setVisibleProofs}
showNotification={showNotification}
reloadList={reloadList}
/>
</React.Fragment>

View File

@ -106,7 +106,7 @@ export default Form.create()((props) => {
if (!error) {
let files = [];
for (const item of fileList) {
files.push(item.id || item.response.data.id);
files.push(item.id || (item.response.data && item.response.data.id));
}
complainPaper({
paperId: checkedItem.id,
@ -171,12 +171,12 @@ export default Form.create()((props) => {
</li>
</ul>
<div className="paper-detail-content markdown-body editormd-html-preview editor-w-text" dangerouslySetInnerHTML={{ __html: item.paperDetail.content }}>
<div className="paper-detail-content markdown-body editormd-html-preview editor-w-text" dangerouslySetInnerHTML={{ __html: item.paperDetail ? item.paperDetail.content : '' }}>
</div>
<div className="attachments" >
{
item.paperDetail.busiAttachments && item.paperDetail.busiAttachments.map(fileItem => {
item.paperDetail && item.paperDetail.busiAttachments && item.paperDetail.busiAttachments.map(fileItem => {
return <div className="file-list-box" key={fileItem.id}>
<a onClick={() => { downFile(fileItem) }}><i className="iconfont icon-fujian color-green font-14 mr3"></i>
{fileItem.fileName} </a>

View File

@ -0,0 +1,181 @@
import React, { useEffect, useState } from 'react';
import { Pagination, Modal, Input } from 'antd';
import { Link } from "react-router-dom";
import Nodata from 'forge/Nodata';
import Loading from "src/Loading";
import { timeAgo } from 'educoder';
import { checkProof } from '../../api';
import { httpUrl } from 'military/fetch';
import './index.scss';
import winpng from '../../image/win.png';
const { TextArea } = Input;
export default (props) => {
const { list, curPage, total, changePage, loading, showNotification, reloadList } = props;
const [checkedItem, setCheckedItem] = useState('');
const [page, setPage] = useState(1);
const [visible, setVisible] = useState(false);
const [advice, setRepairAdvice] = useState('');
const pageSize = props.pageSize || 10;
useEffect(() => {
changePage(page);
}, [page]);
function agreeClick(item) {
Modal.confirm({
title: '是否确认审批通过?',
onOk() {
checkProof({
id: item.taskResultProof.id,
isPassed: 1,
taskId: item.id,
advice: "",
}).then(res => {
if (res && res.message === 'success') {
showNotification('操作成功');
reloadList();
}
})
}
});
}
function refuseClick(item) {
setCheckedItem(item);
setVisible(true);
}
function refuse() {
if (!advice) {
showNotification('请输入拒绝的理由');
return;
}
checkProof({
id: checkedItem.taskResultProof.id,
taskId: checkedItem.id,
isPassed: 0,
advice: advice
}).then(res => {
if (res && res.message === 'success') {
showNotification('操作成功');
reloadList();
setRepairAdvice('');
setVisible(false);
}
});
}
function goUser(login) {
window.location.href = `/users/${login}`;
}
function goUserMes(login) {
window.location.href = `/users/${login}/message_detail`;
}
function downFile(item) {
let url = httpUrl + '/busiAttachments/download/' + item.id;
window.open(url);
}
return (
loading ? <Loading /> :
<React.Fragment>
{
list.map(item => {
return (
<div className="list-box" key={item.id}>
<img alt="头像加载失败" className="radius mr15" height="50px" src={winpng} width="50px" />
<div className="flex1">
<li className="clearfix mb20">
<a className="user-box fl mr15 color-grey-3 font-16" onClick={() => { goUser(item.user.login) }}>{item.user.nickname || item.user.login}</a>
<span className="fl color-grey-9 mt3 mr15">{timeAgo(item.createdAt)}</span>
<span className="with40 fl lineh-35">
<span className="color-grey-9 fl">主体名称:</span>
<span className="infos_item">{item.enterpriseName}</span>
</span>
<span className="fr">
{item.taskResultProof.status === 1 && <span className="spanTitle color-grey-6 fl ml20">已同意</span>}
{item.taskResultProof.status === 0 && <span className="spanTitle color-red fl ml20">已拒绝</span>}
{
item.taskResultProof.status === 2 && <React.Fragment>
<a className="edu-default-btn edu-orangeline-btn ml20 fl" onClick={() => { goUserMes(item.user.login) }}>私信</a>
<a className="edu-default-btn edu-blueline-btn ml20 fl" onClick={() => { agreeClick(item) }}>同意</a>
<a className="edu-default-btn edu-greyline-btn ml20 fl" onClick={() => { refuseClick(item) }}>拒绝</a>
</React.Fragment>
}
</span>
</li>
<ul className="clearfix">
<div className="width100 lineh-35" style={{ display: "inline-flex" }}>
<span className="color-grey-9 fl">任务编号:</span>
<span className="infos_item mr15">{item.number}</span>
<span className="fl lineh-35 ml5">
<Link className="primary-link" to={`/task/taskDetail/${item.id}`}>{item.name}</Link>
</span>
</div>
<div className="clearfix"></div>
<div className="width100 lineh-35">
<span className="color-grey-9 fl">评选胜出{item.taskResultProof.winnerName.split(',').length}</span>
<span className="infos_item">{item.taskResultProof.winnerName}</span>
</div>
<div className="clearfix"></div>
<div className="width100 lineh-35 clearfix">
<span className="color-grey-9 fl">佐证材料</span>
{
item.taskProofAttachments && item.taskProofAttachments.map(fileItem => {
return <span className="file-list-prof " key={fileItem.id}>
<a onClick={() => { downFile(fileItem) }}><i className="iconfont icon-fujian color-green font-14 mr3"></i>
{fileItem.fileName} </a>
<span className="ml10 color-grey-9">({fileItem.fileSizeString})</span>
</span>
})
}
</div>
{item.taskResultProof.status === 0 && <p className="color-orange lineh-35 "><span className="fl color-orange mr5">拒绝原因:</span>test1111</p>}
</ul>
</div>
</div>
)
})
}
{list.length > 0 ?
<div className="edu-txt-center mt20 mb20">
{total > pageSize && <Pagination
showQuickJumper
onChange={(page) => { setPage(page) }}
current={curPage}
total={total}
showTotal={total => `${total}`}
/>}
</div> :
<Nodata _html="暂无数据" />}
<Modal
title="拒绝原因"
visible={visible}
onOk={refuse}
onCancel={() => { setVisible(false) }}
className="form-edit-modal"
>
<TextArea
value={advice}
placeholder="(必填)我想说点什么呢,200字以内"
autoSize={{ minRows: 6 }}
className="applyText"
onChange={(e) => { setRepairAdvice(e.target.value) }}
maxLength={200}
/>
</Modal>
</React.Fragment>
)
}

View File

@ -0,0 +1,33 @@
.list-box {
position: relative;
display: flex;
justify-content: space-between;
padding: 20px;
margin: 0 1.5rem;
background: #fff;
border-bottom: 1px solid #dedede;
a.edu-orangeline-btn {
padding: 0px 10px;
}
}
a.primary-link {
color: #1890ff;
}
.infos_item {
float: left;
max-width: 273px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
color: #343434;
margin-right: 6px;
line-height: 35px;
margin-left: 5px;
}
.file-list-prof{
background: #fafafa;
padding:.25rem .5rem;
}

View File

@ -1,15 +1,17 @@
import React, { useEffect, useState, useCallback } from 'react';
import React, { useEffect, useState, useCallback, useMemo } from 'react';
import { Modal, Table, Radio, Form, Input, Button, Pagination } from 'antd';
import { Link } from "react-router-dom";
import Nodata from 'forge/Nodata';
import Loading from "src/Loading";
import { getTaskPaper } from "../../api";
import Upload from 'military/components/Upload';
import { getTaskPaper, proofAdd } from "../../api";
import { httpUrl } from 'military/fetch';
import '../../index.scss';
import './index.scss';
export default Form.create()((props) => {
const { changeVisible, taskId, visible, form } = props;
const { changeVisible, taskId, visible, form, showNotification,reloadList } = props;
const { getFieldDecorator, validateFields, setFieldsValue, } = form;
const pageSize = props.pageSize || 10;
@ -19,13 +21,17 @@ export default Form.create()((props) => {
const [total, setTotal] = useState(0);
const [dataList, setDataList] = useState([]);
const [fileList, setFileList] = useState([]);
const [selectedRows, setSelectedRows] = useState([]);
//
useEffect(() => {
setLoading(true);
let params = {
...searchObj,
taskId,
orderBy: '',
pageSize: 10,
pageSize,
curPage,
status: '',
}
@ -39,7 +45,7 @@ export default Form.create()((props) => {
setLoading(false);
setTotal(data.total);
});
}, [taskId, curPage,]);
}, [taskId, curPage,]);
const helper = useCallback(
@ -66,14 +72,100 @@ export default Form.create()((props) => {
setSearchObj({});
}
//
function UploadFunc(fileList) {
setFileList(fileList);
}
const columns = useMemo(() => {
return [{
title: '作者',
dataIndex: 'name',
render: (text, record) => {
return record.user ? record.user.nickname || record.user.login : ''
}
},
{
title: '成果物编号',
dataIndex: 'number',
},
{
title: '更新时间',
dataIndex: 'updatedAt',
},
{
title: '操作',
dataIndex: 'action',
render: (text, record) => {
return <Link className="line_1 color-grey3" to={`/task/taskDetail/${taskId}`}>查看详情</Link>
}
}];
}, [taskId]);
const rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
setSelectedRows(selectedRows);
// console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
},
};
const handleRow = record => {
return {
onClick: event => {
event.currentTarget.getElementsByClassName("ant-checkbox-wrapper")[0].click();
},
};
};
function proofItem() {
if (!selectedRows.length) {
showNotification("请至少选择一条数据!");
}
if (!fileList && !fileList.length) {
showNotification("请上传文件!");
}
let files = [];
for (const item of fileList) {
files.push(item.id || (item.response.data && item.response.data.id));
}
let paperNumber = [];
let winnerIds = [];
let winnerName = [];
for (const item of selectedRows) {
paperNumber.push(item.number);
winnerIds.push(item.user.id);
winnerName.push(item.user.nickname || item.user.login);
}
let params = {
paperNumber: paperNumber.join(),
winnerIds: winnerIds.join(),
winnerName: winnerName.join(),
proofFileNumbers: files.join(),
status: 2,
taskId,
};
proofAdd(params).then(res => {
if (res && res.message === 'success') {
changeVisible(false);
reloadList();
}
});
}
return (
<React.Fragment>
<Modal
title="上传评选佐证材料"
visible={visible}
// onOk={checkItem}
onOk={proofItem}
onCancel={() => { changeVisible(false) }}
className="task-manage proof-modal"
className="proof-modal"
draggable={true}
>
<h3 className="margin10">选出胜出者<span className="color-red">*</span></h3>
<div className="center-right-but">
@ -95,7 +187,38 @@ export default Form.create()((props) => {
<Button className="mr10" type="primary" onClick={onSearch}>搜索</Button>
<Button className="mr10" type="" onClick={clearSearch}>清除</Button>
</div>
<Table
loading={loading}
rowKey={(row) => row.id}
dataSource={dataList}
columns={columns}
pagination={false}
rowSelection={{
type: 'checkbox',
...rowSelection,
}}
onRow={handleRow}
/>
{total > 10 &&
<Pagination
onChange={(page) => { setCurPage(page) }}
current={curPage}
total={total}
/>}
<Form.Item >
<Upload
className="commentStyle"
load={UploadFunc}
size={50}
showNotification={showNotification}
actionUrl={httpUrl}
fileList={fileList}
/>
{/* {getFieldDecorator('files', {
validateFirst: true
})(<Input style={{ display: 'none' }} />)} */}
</Form.Item>
</Modal>
</React.Fragment>

View File

@ -0,0 +1,31 @@
.proof-modal {
min-width: 800px;
width: 80vw;
.ant-form-item-control {
width: 200px;
}
.center-right-but {
.ant-form-item {
margin: 0 1rem 0 0;
}
}
.ant-table-thead > tr > th,
tr > td {
text-align: center;
}
.commentStyle {
.ant-upload {
max-width: 500px;
width: 60vw;
margin-top: 1rem ;
}
.ant-upload-list{
max-width: 500px;
width: 60vw;
}
}
.ant-form-item{
margin-bottom: 0;
}
}

View File

@ -71,10 +71,4 @@
text-align: center;
}
.proof-modal{
min-width: 800px;
width: 80vw;
.ant-form-item-control{
width: 200px;
}
}

View File

@ -18,7 +18,7 @@ for (const item of taskStatusAllArr) {
statusArr[item.dicItemCode] = item.dicItemName;
}
export default ({ location, history, current_user }) => {
export default ({ location, history, current_user,showNotification }) => {
let defaultValue = decodeURI(location.search.split("=")[1] || "");
const [identity, setIdentity] = useState('1');
@ -37,6 +37,7 @@ export default ({ location, history, current_user }) => {
const [total, setTotal] = useState(0);
const [myTaskList, setMyTaskList] = useState([]);
const [loading, setLoading] = useState(false);
const [reload,setReload]=useState(0);
//
useEffect(() => {
@ -67,7 +68,7 @@ export default ({ location, history, current_user }) => {
}
setLoading(false);
})
}, [statusString, searchInput, orderBy, curPage]);
}, [statusString, searchInput, orderBy, curPage ,reload]);
function taskClick(id) {
history.push(`/task/taskDetail/${id}`);
@ -102,6 +103,9 @@ export default ({ location, history, current_user }) => {
setStatusType(key);
}
const reloadList=useCallback(()=>{
setReload(reload+1);
},[]);
return (
<div className="centerbox my-task">
@ -137,6 +141,8 @@ export default ({ location, history, current_user }) => {
changePage={(page) => { setCurPage(page) }}
loading={loading}
publish={true}
showNotification={showNotification}
reloadList={reloadList}
/>
</TabPane>
@ -156,6 +162,8 @@ export default ({ location, history, current_user }) => {
taskCategoryValueArr={taskCategoryValueArr}
changePage={(page) => { setCurPage(page) }}
loading={loading}
showNotification={showNotification}
reloadList={reloadList}
/>
</TabPane>

View File

@ -0,0 +1,152 @@
import React, { useCallback, forwardRef, useEffect, useState } from 'react';
import { Input, Button, Form } from 'antd';
import ItemProofManage from '../components/itemProofManage';
import StatusNav from '../../components/statusNav';
import { proofArr } from '../static';
import { proofList, } from '../api';
import '../index.scss';
const proofArrCheck=proofArr.slice(0,2);
export default Form.create()(({ current_user, form, showNotification, match, history }) => {
const { getFieldDecorator, validateFields, setFieldsValue } = form;
const [approve, setApprove] = useState(1);
const [loading, setLoading] = useState(false);
const [searchObj, setSearchObj] = useState({});
const [proofStatusString, setProofStatusString] = useState('2');
const [curPage, setCurPage] = useState(1);
const [total, setTotal] = useState(0);
const [taskList, setTaskList] = useState([]);
const [reload, setReload] = useState(0);
useEffect(() => {
const params = {
...searchObj,
proofStatusString,
orderBy: 'createdAtDesc',
curPage,
pageSize: 10,
};
setLoading(true);
proofList(params).then(data => {
if (data) {
setTaskList(data.rows);
setTotal(data.total);
}
setLoading(false);
})
}, [reload, proofStatusString, curPage, searchObj]);
const helper = useCallback(
(name, rules, widget) => (
<Form.Item>
{getFieldDecorator(name, { rules, validateFirst: true, })(widget)}
</Form.Item>
), []);
function onSearch() {
validateFields((err, values) => {
if (!err) {
setSearchObj(values);
}
});
}
const changeOptionId = useCallback((option) => {
setProofStatusString(option.dicItemCode.toString() || '0,1');
setCurPage(1);
}, []);
function changeApprove(approve) {
setApprove(approve);
setCurPage(1);
if (approve === 1) {
setProofStatusString('2');
} else {
setProofStatusString('0,1');
}
}
function clearSearch() {
setFieldsValue({
numberInput: '',
nameInput: '',
enterpriseNameInput: ''
});
setSearchObj({});
}
const reloadList = useCallback(() => {
setReload(reload + 1);
}, [])
return (
<div className="centerbox task-manage">
<div className="center-screen" >
<div className="center-left-but">
<Button className="circle-button" type={approve === 1 ? 'primary' : ''} onClick={() => { changeApprove(1) }}>待审批</Button>
<Button className="circle-button" type={approve === 2 ? 'primary' : ''} onClick={() => { changeApprove(2) }}>已审批</Button>
</div>
<div className="center-right-but">
{helper(
"numberInput",
[{ max: 20, message: '长度不能超过20个字符' }],
<Input
placeholder="输入任务编号进行检索"
/>
)}
{helper(
"nameInput",
[{ max: 20, message: '长度不能超过20个字符' }],
<Input
placeholder="输入任务名称进行检索"
/>
)}
{helper(
"enterpriseNameInput",
[{ max: 20, message: '长度不能超过20个字符' }],
<Input
placeholder="输入发布主体名称进行检索"
/>
)}
<Button className="mr10" type="primary" onClick={onSearch}>搜索</Button>
<Button className="mr10" type="" onClick={clearSearch}>清除</Button>
</div>
</div>
<div className="center-content">
{
approve === 2 && <StatusNav
key={'approveStatus'}
type={'approveStatus'}
options={proofArrCheck}
changeOptionId={changeOptionId}
/>
}
<ItemProofManage
list={taskList}
curPage={curPage}
total={total}
changePage={(page) => { setCurPage(page) }}
loading={loading}
showNotification={showNotification}
reloadList={reloadList}
/>
</div>
</div>
)
}
)

View File

@ -35,6 +35,12 @@ export const approveArr = [
{ dicItemCode: '9', name: "待修缮", dicItemName: "待修缮" },
]
export const proofArr = [
{ dicItemCode: 1, name: "已同意", dicItemName: "已同意" },
{ dicItemCode: 0, name: "已拒绝", dicItemName: "已拒绝" },
{ dicItemCode: 2, name: "待审核", dicItemName: "待审核" },
]
export const publishModeArr=["自主提交","统筹任务"];
export const sortArr = [{

View File

@ -143,7 +143,7 @@ export default Form.create()(
id && getTaskPaper(params).then(data => {
if (data && Array.isArray(data.rows)) {
for (const item of data.rows) {
item.detail = item.paperDetail.content;
item.detail = item.paperDetail?item.paperDetail.content:"";
}
}
setDataList(data.rows || []);
@ -204,7 +204,7 @@ export default Form.create()(
setFileList(fileList);
let files = [];
for (const item of fileList) {
files.push(item.id || item.response.data.id);
files.push(item.id || (item.response.data && item.response.data.id));
}
setFieldsValue({
files: files.join()

View File

@ -147,7 +147,10 @@ export default Form.create()(forwardRef(({ current_user, form, showNotification,
setFileList(fileList);
let files = [];
for (const item of fileList) {
item && files.push(item.id || item.response.data.id);
if (item) {
let itemId = (item.response && item.response.data && item.response.data.id) || item.id;
itemId && files.push(itemId);
}
}
setFieldsValue({
uploadFileNumbers: files.join()