上传创客新增任务及列表代码

This commit is contained in:
何童崇 2021-06-17 15:28:13 +08:00
parent 6156d98af2
commit 97bc41e771
17 changed files with 1065 additions and 343 deletions

View File

@ -16,10 +16,10 @@ export default (props) => {
<div className="shop-box">
<div className="choose-title">{title}</div>
<div className="choose-list">
<div className={classNames({ "choose-item-checked": option.code === "", "choose-item": true })} key={"all"} onClick={() => { setOption({ code: "", dicItemName: "" }) }}>全部</div>
<div className={classNames({ "choose-item-checked": option.dicItemCode === "", "choose-item": true })} key={"all"} onClick={() => { setOption({ dicItemCode: "", dicItemName: "" }) }}>全部</div>
{
options.map((item) => {
return <div className={classNames({ "choose-item-checked": option.code === item.code, "choose-item": true })} key={item.dicItemName} onClick={() => { setOption(item) }} >{item.dicItemName}</div>
return <div className={classNames({ "choose-item-checked": option.dicItemCode === item.dicItemCode, "choose-item": true })} key={item.dicItemCode} onClick={() => { setOption(item) }} >{item.dicItemName}</div>
})
}
</div>

View File

@ -1,21 +1,56 @@
import React from 'react';
import React, { useEffect, useState } from 'react';
import { Icon, Pagination } from 'antd';
import Nodata from '../../../forge/Nodata';
import './index.scss';
const statusArr = ["草稿", "待审核", "已拒绝", "成果征集中", "成果评选中", "公示中", "协议签订中", "支付中", "已完成"];
const classArr = ['', 'list-done', 'list-error', 'list-red', 'list-yellow', '', '', 'list-pay', 'list-gray',];
export default (props) => {
const { list, itemClick, } = props;
const { list, itemClick, curPage, total, changePage } = props;
const [page, setPage] = useState(1);
useEffect(() => {
changePage(page);
}, [page]);
return (
list.map(item => {
return (
<div className="list-box" key={item.id}>
<div className="list-title" onClick={() => { itemClick(item.id) }}>
{item.achievementName || item.title}
</div>
<div className="list-other">
{item.publisher && <p>发布单位{item.publisher}</p>}
<p>发布时间{item.publishDate || item.createTime}</p>
</div>
</div>
)
})
<React.Fragment>
{
list.map(item => {
return (
<div className="list-box" key={item.id}>
<div className="list-content">
<div className="list-title mb10" onClick={() => { itemClick(item.id) }}>
{item.name || item.title}
{item.status && <span className={classArr[item.status]}>{statusArr[item.status]}</span>}
</div>
<div className="list-other">
<span className=" mr30"><i className="iconfont icon-dianjiliang mr3 font-12" />{item.visits}</span>
<span className=" mr30"><Icon type="user" /><span className="color-orange">{item.papersCount}</span>人参与</span>
</div>
</div>
<span className="price color-deep-blue ">
<span className="font-16"></span>
<span className="font-24">{item.bounty}</span>
</span>
</div>
)
})
}
{list.length > 0 ?
<div className="edu-txt-center mt20 mb20">
<Pagination
showQuickJumper
onChange={(page) => { setPage(page) }}
current={curPage}
total={total}
showTotal={total => `${total}`}
/>
</div> :
<Nodata _html="暂无数据" />}
</React.Fragment>
)
}

View File

@ -1,70 +1,60 @@
.list-box {
position: relative;
display: flex;
justify-content: space-between;
padding: 20px;
margin: 0 1.5rem;
background: #fff;
border-bottom: 1px solid #dedede;
}
.price {
font-weight: 500;
padding-top: 10px;
}
.list-title {
font-size: 17px;
color: #000;
font-size: 1rem;
color: #333;
cursor: pointer;
font-weight: 500;
}
.list-title:hover {
color: #409eff;
}
.list-title span {
padding: 3px 5px;
margin-left:.5em;
padding: 2px 10px;
margin-left: 0.5em;
background: #f8c753;
font-size: 13px;
color: #fff;
border-radius: 3px;
border-radius: 14px;
}
span.list-yellow {
background: #fa6400;
}
span.list-red {
background: #fe0e36;
}
span.list-orange {
background: #ffb121;
}
span.list-done {
background: #35d77e;
}
span.list-pay {
background: #1ad757;
}
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;
span.list-gray {
background: #bababa;
}
.list-other > p {
display: inline-block;
margin: 0 10px 0 0;
.list-other {
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;
color: #888;
i {
font-size: 14px;
margin-right: 5px;
}
}

View File

@ -7,7 +7,7 @@ export default (props) => {
const { options, changeOptionId, type, defaultValue } = props;
const [myOptions, setMyOptions] = useState(options);
const [option, setOption] = useState(defaultValue || { name: "", type: "" });
const [option, setOption] = useState(defaultValue);
useEffect(() => {
changeOptionId(option, type);
@ -16,11 +16,11 @@ export default (props) => {
function itemClick(activeItem) {
const newOption = {
...activeItem,
value: !activeItem.value
desc: !activeItem.desc
};
for(const item of myOptions){
if(item.type===activeItem.type){
item.value=newOption.value;
item.desc=newOption.desc;
}
}
setOption(newOption);
@ -36,8 +36,8 @@ export default (props) => {
{item.name}
{
item.icon && <span className="caret-up-down">
<Icon type="caret-up" className={classNames({ "caret-checked": !item.value })} />
<Icon type="caret-down" className={classNames({ "caret-checked": item.value })} />
<Icon type="caret-up" className={classNames({ "caret-checked": !item.desc })} />
<Icon type="caret-down" className={classNames({ "caret-checked": item.desc })} />
</span>}
</div>
})

View File

@ -0,0 +1,26 @@
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({ code: "", dicItemName: "" ,dicItemCode:""});
useEffect(() => {
changeOptionId(option, type);
}, [option])
return (
<div className="status-list">
<div className={classNames({ "status-item-checked": option.dicItemCode === "", "status-item": true })} key={"all"} onClick={() => { setOption({ dicItemCode: "", dicItemName: "" }) }}>全部</div>
{
options.map((item) => {
return <div className={classNames({ "status-item-checked": option.dicItemCode === item.dicItemCode, "status-item": true })} key={item.dicItemCode} onClick={() => { setOption(item) }} >{item.dicItemName}</div>
})
}
</div>
)
}

View File

@ -0,0 +1,20 @@
.status-list {
padding: 1rem 1.8rem;
display: flex;
justify-content: start;
}
.status-item {
font-size: 14px;
color: #666;
text-align: center;
padding: 3px 15px;
cursor: pointer;
&:hover {
color: #4cacff;
}
}
.status-item-checked {
background: #f7f7f7;
color: #4cacff ;
}

View File

@ -26,7 +26,6 @@ const service = axios.create({
timeout: 5000 // 请求超时时间
});
// request拦截器
service.interceptors.request.use(config => {
if (cookie.load(TokenKey)) {
@ -45,7 +44,7 @@ service.interceptors.response.use(
if (res.status === 400) {
notification.open({
message: "提示",
description: '请求错误',
description: '验证失败',
});
return Promise.reject('error');
}

View File

@ -1,5 +1,16 @@
// 本模块公共样式
.color-grey3{
color: #333;
}
.color-grey9{
color: #999;
}
.color-orange {
color: #ff6800;
}
.color-deep-blue{
color:#1B8FFF;
}
.centerbox {
width: 1200px;
margin: 40px auto;
@ -18,6 +29,7 @@
width: 80vw;
max-width: 1280px;
margin: 40px auto;
position: relative;
}
.center-content {
background: #fff;
@ -61,6 +73,24 @@
color: #fff;
}
.head-navigation {
position: absolute;
top: -2.3em;
a:hover {
color: #409eff;
}
}
// 内容标题左侧样式
.center-left-but {
display: flex;
justify-content: start;
align-items: center;
margin-left: 20px;
font-size: 16px;
font-weight: 600;
}
// 内容标题右侧样式
.center-right-but {
display: flex;
@ -73,18 +103,6 @@
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;
@ -112,6 +130,7 @@
max-width: 500px;
}
<<<<<<< HEAD
.link{
color: #0089ff;
cursor: pointer;
@ -130,6 +149,9 @@
}
.none_panels{
=======
.none_panels {
>>>>>>> 2ed7e0d... 上传创客新增任务及列表代码
display: flex;
justify-content: center;
align-items: center;
@ -137,8 +159,7 @@
height: 40vh;
}
.newFooter .footerInfos>ul {
.newFooter .footerInfos > ul {
padding: 0 40px;
box-sizing: border-box;
max-width: 25%;

View File

@ -25,6 +25,10 @@ const TaskAdd = Loadable({
loading: Loading,
});
const MyTask = Loadable({
loader: () => import("./task/myTask"),
loading: Loading,
});
class Index extends Component {
render() {
@ -46,6 +50,13 @@ class Index extends Component {
)}
></Route>
<Route
path="/task/myTask"
render={(props) => (
<MyTask {...this.props} {...props} />
)}
></Route>
<Route
path="/task"
render={(props) => (

View File

@ -9,14 +9,47 @@ export function getDictionary(id) {
});
}
// 获取任务领域
export async function getTaskCategory() {
let res = await fetch({
url: '/api/taskCategory/getTaskCategory',
method: 'get',
});
if (Array.isArray(res.data.rows)) {
return res.data.rows;
} else {
notification.open({
message: "提示",
description: res.message || '请求错误',
});
}
}
// 列表查询
export async function getTaskList(params) {
let res = await fetch({
url: '/api/announcements/',
url: '/api/tasks/',
method: 'get',
params,
});
if (res.code === '1') {
if (res.data) {
return res.data;
} else {
notification.open({
message: "提示",
description: res.message || '请求错误',
});
}
}
// 我的列表查询
export async function getMyTaskList(params) {
let res = await fetch({
url: '/api/myTasks/',
method: 'get',
params,
});
if (res.data) {
return res.data;
} else {
notification.open({
@ -30,7 +63,7 @@ export async function getTaskList(params) {
// 详情查询
export async function getTaskDetail(id) {
let res = await fetch({
url: '/api/announcements/' + id,
url: '/api/tasks/' + id,
method: 'get',
});
if (res.code === '1') {
@ -46,7 +79,7 @@ export async function getTaskDetail(id) {
//新增公告
export function addTask(data) {
return fetch({
url: '/api/announcements/add',
url: '/api/tasks/add',
method: 'post',
data: data
});
@ -55,7 +88,7 @@ export function addTask(data) {
//删除
export function deleteTask(id) {
return fetch({
url: '/api/announcements/' + id,
url: '/api/tasks/' + id,
method: 'DELETE',
});
}
@ -63,7 +96,7 @@ export function deleteTask(id) {
//更新
export function editTask(data) {
return fetch({
url: '/api/announcements/update',
url: '/api/tasks/update',
method: 'put',
data: data
});

View File

@ -0,0 +1,103 @@
import React, { useEffect, useState } from 'react';
import { Tabs, Pagination, Input, Button } from 'antd';
import moment from 'moment';
import StatusNav from '../../components/statusNav';
import SortBox from '../../components/sortBox';
import ItemListTask from '../../components/itemListTask';
import Nodata from '../../../forge/Nodata';
import { taskTimeArr, taskStatusArr, taskStatusAllArr, sortArr, taskModeIdArr } from '../static';
import { getTaskList, getTaskCategory } from '../api';
import './index.scss';
const Search = Input.Search;
const { TabPane } = Tabs;
const publishStatusArr = taskStatusAllArr.slice(3);
const unpublishStatusArr = taskStatusAllArr.slice(0, 3);
export default ({ history, current_user }) => {
const [identity, setIdentity] = useState('1');
const [type, setType] = useState('1');
const [status, setStatus] = useState('');
const [searchInput, setSearchInput] = useState('');
const [orderBy, setOrderBy] = useState('');
const [curPage, setCurPage] = useState(1);
const [total, setTotal] = useState(0);
const [taskList, setTaskList] = useState([]);
useEffect(() => {
const params = {
status,
searchInput,
orderBy,
curPage,
pageSize: 10,
};
getTaskList(params).then(data => {
if (data) {
setTaskList(data.rows);
setTotal(data.total);
}
})
}, [status, searchInput, orderBy, curPage]);
function taskClick(id) {
history.push(`/task/taskDetail/${id}`);
}
function goAdd() {
history.push("/task/taskAdd");
}
function changeOptionId(option, type) {
if(type==='publishStatus'){
setStatus(option.dicItemCode);
}
}
return (
<div className="centerbox my-task">
<Tabs defaultActiveKey={identity} onChange={(key) => { setIdentity(key) }}>
<TabPane tab="我是雇主" key="1">
<Tabs className="childTab" defaultActiveKey={type} onChange={(key) => { setType(key) }}>
<TabPane tab="我发布的需求" key="1">
<StatusNav
key={'publishStatus'}
type={'publishStatus'}
options={publishStatusArr}
changeOptionId={changeOptionId}
/>
<ItemListTask
list={taskList}
itemClick={taskClick}
curPage={curPage}
total={total}
changePage={(page) => { setCurPage(page) }}
/>
</TabPane>
<TabPane tab="需求草稿" key="2">
<StatusNav
key={'unpublishStatus'}
type={'unpublishStatus'}
options={unpublishStatusArr}
changeOptionId={changeOptionId}
/>
</TabPane>
</Tabs>
</TabPane>
<TabPane tab="我是创客" key="2">
</TabPane>
</Tabs>
</div>
)
}

View File

@ -0,0 +1,30 @@
.my-task {
margin-top: 1.25rem;
.ant-tabs-top-bar {
background: #fff;
text-align: center;
}
.ant-tabs-tab{
height: 4.5rem;
line-height: 3rem;
font-size: 1.15rem;
font-weight: 500;
}
.childTab{
background: #fff;
.ant-tabs-top-bar {
background: #fff;
text-align: left;
}
.ant-tabs-tab{
height: 4.5rem;
line-height: 3rem;
font-size: 1rem;
font-weight: normal;
}
.ant-tabs-ink-bar{
visibility: hidden;
}
}
}

View File

@ -1,19 +1,91 @@
// 公告开始
export const taskStatus = [
{ code: 0, name: "关闭", dicItemName: '关闭' },
{ code: 1, name: "正常", dicItemName: '正常' },
{ code: 2, name: "草稿", dicItemName: '草稿' },
// 开始
export const taskStatusArr = [
{ dicItemCode: 999, name: "正在进行中", dicItemName: '正在进行中' },
{ dicItemCode: 0, name: "已完成", dicItemName: '已完成' },
];
export const taskType = [
{ code: 1, name: "更正", dicItemName: "更正" },
{ code: 2, name: "中标", dicItemName: "中标" },
{ code: 3, name: "废标", dicItemName: "废标" },
export const taskStatusAllArr = [
{ dicItemCode: 0, name: "未发布", dicItemName: '未发布' },
{ dicItemCode: 1, name: "待审核", dicItemName: '待审核' },
{ dicItemCode: 2, name: "已拒绝", dicItemName: '已拒绝' },
{ dicItemCode: 3, name: "成果征集中", dicItemName: '成果征集中' },
{ dicItemCode: 4, name: "成果评选中", dicItemName: '成果评选中' },
{ dicItemCode: 5, name: "公示中", dicItemName: '公示中' },
{ dicItemCode: 6, name: "协议签订中", dicItemName: '协议签订中' },
{ dicItemCode: 7, name: "支付中", dicItemName: '支付中' },
{ dicItemCode: 8, name: "已完成", dicItemName: '已完成' },
]
export const taskTimeArr = [
{ dicItemCode: 1, name: "24小时到期", dicItemName: "24小时到期" },
{ dicItemCode: 3, name: "3天内到期", dicItemName: "3天内到期" },
{ dicItemCode: 7, name: "7天内到期", dicItemName: "7天内到期" },
];
export const taskChecked = [
{ code: 0, name: "未通过", dicItemName: "未通过" },
{ code: 1, name: "通过", dicItemName: "通过" },
{ code: 2, name: "未处理", dicItemName: "未处理" },
];
//公告结束
export const taskModeIdArr = [
{ dicItemCode: 1, name: "单人悬赏", dicItemName: "单人悬赏" },
{ dicItemCode: 2, name: "多人悬赏", dicItemName: "多人悬赏" },
{ dicItemCode: 3, name: "计件悬赏", dicItemName: "计件悬赏" },
]
export const sortArr = [{
name: '综合',
type: 'default',
}, {
name: '最新',
type: 'createdAt',
icon: true,
desc: false,
},
{
name: '最热',
type: 'visits',
icon: true,
desc: false,
},
{
name: '价格',
type: 'bounty',
icon: true,
desc: false,
},];
export const formItemLayout = {
labelCol: {
xs: { span: 10 },
sm: { span: 6 },
lg: { span: 3 },
},
wrapperCol: {
xs: { span: 14 },
sm: { span: 18 },
lg: { span: 21 },
},
};
export const formModalLayout = {
labelCol: {
xs: { span: 10 },
sm: { span: 6 },
},
wrapperCol: {
xs: { span: 14 },
sm: { span: 18 },
},
};
export const tailFormItemLayout = {
wrapperCol: {
xs: {
span: 20,
offset: 4,
},
sm: {
span: 20,
offset: 4,
},
},
};

View File

@ -1,61 +1,122 @@
import React, { forwardRef, useCallback, useEffect, useState } from 'react';
import { Form, Input, Select, Button, DatePicker } from 'antd';
import moment from 'moment';
import { Form, Radio, Input, InputNumber, Icon, Button, Modal } from 'antd';
import classNames from 'classnames';
import ReactWEditor from 'wangeditor-for-react';
import { Link } from "react-router-dom";
import Upload from '../../components/Upload';
import { httpUrl } from '../../fetch';
import { getDictionary, getTaskDetail, addTask, updateTask } from '../api';
import { getTaskDetail, addTask, updateTask, getTaskCategory } from '../api';
import {formItemLayout,tailFormItemLayout,formModalLayout}from '../static';
import './index.scss';
const Option = Select.Option;
const { TextArea } = Input;
const format = "YYYY-MM-DD HH:mm:ss";
let actionUrl = '';
if (window.location.href.indexOf('localhost') > -1) {
actionUrl = httpUrl;
}
export default Form.create()(forwardRef(({current_user, form, showNotification, match, history }, ref) => {
export default Form.create()(forwardRef(({ current_user, form, showNotification, match, history }, ref) => {
const [requireType, setRequireType] = useState([]);
const [tagType, setTagType] = useState([]);
const [taskCategoryArr, setTaskCategoryArr] = useState([]);
const [fileList, setFileList] = useState(null);
const [typeCode, setTypeCode] = useState('');
const [typeName, setTypeName] = useState('');
const [publishMode, setPublishMode] = useState(0);
const [categoryId, setCategoryId] = useState('7');
const [description, setDescription] = useState('');
const [visible, setVisible] = useState(false);
const [num, setNum] = useState(0) //
const [isSend, setIsSend] = useState(false) //
const id = match.params.requireId;
const { getFieldDecorator, validateFields, setFieldsValue, } = form;
// id
useEffect(() => {
id && getTaskDetail(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
let formValue = {
taskModeId: 1,
collectionMode: 1,
publishMode: 0,
collectingDays: 30,
choosingDays: 15,
makePublicDays: 7,
signingDays: 15,
payingDays: 15,
};
let categoryId = '7';
if (id) {
getTaskDetail(id).then(data => {
formValue = {
name: data.name,
contactName: data.contactName,
contactPhone: data.contactPhone,
uploadFileNumbers: data.uploadFileNumbers,
bounty: data.bounty,
taskModeId: data.taskModeId,
collectionMode: data.collectionMode,
publishMode: data.publishMode,
description: data.description,
collectingDays: data.collectingDays,
choosingDays: data.choosingDays,
makePublicDays: data.makePublicDays,
signingDays: data.signingDays,
payingDays: data.payingDays,
};
categoryId = data.categoryId;
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);
}
setFileList(data.attachmentList);
}
})
});
}
setFieldsValue(formValue);
setCategoryId(categoryId);
}, [id]);
//
useEffect(() => {
getDictionary('requirement_type').then((res) => {
setRequireType(res.data);
});
getDictionary("tag_type").then((res) => {
setTagType(res.data);
getTaskCategory().then(data => {
if (data) {
for (const item of data) {
item.dicItemCode = item.id;
item.dicItemName = item.name;
}
setTaskCategoryArr(data);
}
});
}, []);
/** 倒计时显示*/
useEffect(() => {
let timer = 0;
if (isSend && num !== 0) {
timer = setInterval(() => {
setNum(n => {
if (n === 1) {
setIsSend(false)
clearInterval(timer)
}
return n - 1;
});
}, 1000);
}
return () => {
//
clearInterval(timer)
};
}, [isSend]);
function getCode(){
setIsSend(true);
setNum(60);
}
//
function UploadFunc(fileList) {
setFileList(fileList);
@ -64,55 +125,50 @@ export default Form.create()(forwardRef(({current_user, form, showNotification,
files.push(item.id || item.response.data.id);
}
setFieldsValue({
attachments: files.join()
uploadFileNumbers: files.join()
});
}
const helper = useCallback(
(label, name, rules, widget, initialValue) => (
(label, name, rules, widget, initialValue, rightComponent) => (
<Form.Item label={label}>
{getFieldDecorator(name, { rules, initialValue, validateFirst: true, })(widget)}
{rightComponent}
</Form.Item>
), []);
const formItemLayout = {
labelCol: {
xs: { span: 12 },
sm: { span: 4 },
},
wrapperCol: {
xs: { span: 12 },
sm: { span: 16 },
},
};
const helperNoLabel = useCallback(
(title, name, rules, widget, initialValue) => (
<div className="timing_task" key={name}>
<div className="mbt10 color-grey-9 lineh-35"><span className="inline-span">{title}</span></div>
<Form.Item className="no-label">
{getFieldDecorator(name, { rules, initialValue, validateFirst: true, })(widget)}
</Form.Item>
<span className="days-word color-grey-9 "></span>
</div>
), []);
const tailFormItemLayout = {
wrapperCol: {
xs: {
span: 20,
offset: 4,
},
sm: {
span: 20,
offset: 4,
},
},
};
function changeHtml(html) {
setFieldsValue({
description: html
});
setDescription(html);
}
//
//
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, //
enterpriseName: '神马科技股份有限公司',
categoryId,
};
params.tag = params.tag.join();
params.endTime = params.endTime.format(format);
let dateSum = params.collectingDays + params.choosingDays + params.makePublicDays + params.signingDays + params.payingDays;
if (dateSum > 180) {
showNotification("任务天数总和不得超过180天");
return;
}
if (id) {
//
params.id = id;
@ -139,108 +195,280 @@ export default Form.create()(forwardRef(({current_user, form, showNotification,
})
}
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 className="head-navigation">
<Link className="color-grey-9" to="/task">创客空间 &gt;</Link>
<Link className="color-grey-9" to="/task">任务大厅 &gt;</Link>
&nbsp;<span >发布需求 </span>
</div>
<p className="font-18 font-bd mb15">任务需求提交</p>
<div className="edu-back-white mb110">
<div className="padding30 bor-bottom-greyE">
<p className="partTitle">联系方式<span className="color-red font-14">*必填</span></p>
<Form {...formItemLayout}>
<Form.Item label='主体信息:'>
{"神马科技股份有限公司"}
</Form.Item>
{helper(
"联系人:",
"contactName",
[{ required: true, message: "请输入联系人" }],
<Input
className="contact-input"
placeholder="请输入联系人"
/>
)}
{helper(
"联系电话:",
"contactPhone",
[{ required: true, message: "请输入联系电话" }],
<Input
className="contact-input"
placeholder="请输入联系电话"
// disabled
/>, '', <Icon className="editPhone" type="edit" onClick={() => { setVisible(true) }} />
)}
</Form>
</div>
<div className="padding30 bor-bottom-greyE">
<p className="partTitle">需求内容<span className="color-red font-14">*必填</span>
<span>
<a href="http://117.50.100.12:8000/attachments/download/523/%E5%88%9B%E5%AE%A2%E4%BB%BB%E5%8A%A1%E5%88%97%E8%A1%A8_2019-07-26_20-53.xlsx" className="icon icon-attachment font-13 color-blue" length="32" target="_blank">创客任务列表_2019-07-26_20-53.xlsx</a>
</span><span className="color-grey-9 ml5 font-12 ">点击下载示例模版</span>
</p>
<div className="pl15">
<p className="color-grey3 mb20">选择需求所在领域</p>
<div className="mb20 clearfix areaDiv" >
{
taskCategoryArr.map(item => {
return <button
className={classNames({ "choose-button": true, "active": item.dicItemCode == categoryId })}
key={item.dicItemCode}
onClick={() => { setCategoryId(item.dicItemCode) }}
>{item.dicItemName}</button>
})
}
</div>
{helper(
"",
"name",
[{ required: true, message: "请用一句话概括您要做什么比如开源项目网站开发最大限制60个字符" }],
<Input
placeholder="请用一句话概括您要做什么比如开源项目网站开发最大限制60个字符"
/>
)}
<Form.Item >
<ReactWEditor
value={description}
config={
{
placeholder: "把您的需求内容补充详细一些吧,越清晰具体,任务完成质量越高哟~",
uploadImgServer: actionUrl + '/busiAttachments/upload',
uploadFileName: 'file',
uploadImgHeaders: {
'X-Requested-With': 'XMLHttpRequest'
},
uploadImgHooks: {
//
customInsert: function (insertImgFn, result) {
// insertImgFn src
if (result && result.data && result.data.id) {
insertImgFn(`${actionUrl}/busiAttachments/view/${result.data.id}`);
}
}
},
}
}
onChange={(html) => {
changeHtml(html);
}}
/>
{/* 用一个隐藏的input实现上必填校验 */}
{getFieldDecorator('description', {
rules: [{ required: true, message: "把您的需求内容补充详细一些吧,越清晰具体,任务完成质量越高哟~" }],
validateFirst: true
})(<Input style={{ display: 'none' }} />)}
</Form.Item>
<p className="color-grey3 mb10 mt20">
<span className="color-orange mr2 ">*</span>
需求发布之后将会公开展示在交易中心不要把与项目客户相关等隐私信息以及QQ号微信号电话号码等联系方式填写在需求中
</p>
<Form.Item >
<Upload
className="commentStyle"
load={UploadFunc}
size={50}
showNotification={showNotification}
// actionUrl={httpUrl}
actionUrl={'http://117.50.100.12:8001'}
fileList={fileList}
/>
{getFieldDecorator('uploadFileNumbers', {
validateFirst: true
})(<Input style={{ display: 'none' }} />)}
</Form.Item>
</div>
</div>
<div className="padding30 ">
<p className="partTitle">选择任务周期价格和发布模式<span className="color-red font-14">*必填</span></p>
<Form className="gray-form" {...formItemLayout}>
{helper(
"赏金:",
"bounty",
[{ required: true, message: "您打算支付多少赏金呢" }],
<InputNumber
className="number-input"
placeholder="您打算支付多少赏金呢"
formatter={value => `${value}¥`}
/>
)}
{helper(
"赏金分配:",
"taskModeId",
[{ required: true, message: "请选择赏金分配" }],
<Radio.Group>
<Radio value={1}>单人悬赏只设置一个中标者</Radio>
<Radio value={2}>多人悬赏设置多分中标分享赏金</Radio>
<Radio value={3}>计件悬赏合格一稿支付一稿稿件数量2</Radio>
</Radio.Group>
)}
{helper(
"征集方式:",
"collectionMode",
[{ required: true, message: "请选择赏金分配" }],
<Radio.Group>
<Radio value={1}>创意征集应征者以开放讨论的形式参与</Radio>
<Radio value={0}>物化成果征集应征者以各自提交成果物的形式参与</Radio>
</Radio.Group>
)}
{helper(
"发布方式:",
"publishMode",
[{ required: true, message: "请选择赏金分配" }],
<Radio.Group onChange={(e) => { setPublishMode(e.target.value) }}>
<Radio value={0}>自主提交方式由发布方自行支付赏金一键自助发布</Radio>
<Radio value={1}>统筹任务由平台支付赏金需经过平台遴选方能发布</Radio>
</Radio.Group>
)}
<div className="task-setting-days">
<div className="timing_task">
<div className="mbt10 color-grey-9 lineh-35"><span className="inline-span active">发布任务</span></div>
<div className="color-grey-9 ">自主提交立即发布</div>
<div className="color-grey-9 ">统筹任务遴选后发布</div>
</div>
{helperNoLabel(
"成果提交",
"collectingDays",
[{ required: true, message: "请输入天数" }],
<InputNumber
className="date-input"
/>
)}
{/* <div className="color-grey-9 format-time-days-1 format-time-day-show">2021-06-16 07:10</div> */}
{/* <div className="days-error"><span className="color-red none">成果提交时间不能为空</span></div> */}
{helperNoLabel(
"成果评选",
"choosingDays",
[{ required: true, message: "请输入天数" }],
<InputNumber
className="date-input"
/>
)}
{helperNoLabel(
"结果公示",
"makePublicDays",
[{ required: true, message: "请输入天数" }],
<InputNumber
className="date-input"
/>
)}
{helperNoLabel(
"任务协议签订",
"signingDays",
[{ required: true, message: "请输入天数" }],
<InputNumber
className="date-input"
/>
)}
{helperNoLabel(
"支付",
"payingDays",
[{ required: true, message: "请输入天数" }],
<InputNumber
className="date-input"
/>
)}
<div className="timing_task">
<div className="mbt10 color-grey-9 lineh-35"><span className="inline-span">任务完成</span></div>
<div className="color-grey-9 ">支付确认后任务完成</div>
</div>
</div>
<p className="color-grey3 mb10 ml40 mt20">
<span className="color-orange mr2 ">*</span>
任务天数总和不得超过180天
</p>
<Form.Item {...tailFormItemLayout}>
<Button className="mr20" type={"primary"} onClick={saveItem}>保存</Button>
<Button onClick={() => { history.go(-1) }}>取消</Button>
</Form.Item>
</Form>
</div>
</div>
<Modal
title="修改联系电话"
visible={visible}
// onOk={checkItem}
onCancel={() => { setVisible(false) }}
className="form-edit-modal"
>
<Form {...formModalLayout}>
<Form.Item label={"新手机号码:"} >
<Input
className="tel-input"
placeholder="请输入11位手机号"
/>
</Form.Item>
<Form.Item label={"验证码:"} >
<Input
className="code-input"
placeholder="请输入验证码"
/>
<Button className="ml10" type="primary" disabled={num!==0} onClick={getCode}>{num||'获取验证码'}</Button>
</Form.Item>
</Form>
</Modal>
</div>
)
})

View File

@ -1,4 +1,143 @@
.achieve-form{
padding:24px 40px 0px 40px;
.achieve-form {
padding: 24px 40px 0px 40px;
}
.partTitle {
color: #05101a;
font-size: 16px;
position: relative;
height: 20px;
line-height: 20px;
margin-bottom: 20px !important;
}
.partTitle:before {
position: absolute;
left: -10px;
top: 3px;
width: 2px;
height: 16px;
background: #459be6;
content: "";
}
.color-red {
color: red;
}
.areaDiv {
overflow-y: hidden;
}
.choose-button {
float: left;
border: 1px solid #eee;
border-radius: 0px;
height: 28px;
line-height: 28px;
padding: 0px 12px;
color: #656565;
margin: 0px 10px 10px;
cursor: pointer;
}
.choose-button.active {
color: #4cacff !important;
border: 1px solid #4cacff;
}
.gray-form {
.ant-form-item-required {
color: #999;
}
}
.contact-input {
max-width: 300px;
}
.editPhone {
margin-left: 1rem;
padding: 0.25em;
font-size: 1rem;
color: #fff;
background: #1484ef;
border-radius: 50%;
cursor: pointer;
}
.number-input {
width: 200px;
.ant-input-number-input {
padding-right: 1em;
text-align: right;
}
}
.task-setting-days {
display: flex;
flex-flow: row wrap;
text-align: center;
}
.timing_task {
flex: 1px;
position: relative;
line-height: 2;
&:before {
position: absolute;
content: "";
width: 100%;
height: 2px;
background: #efefef;
top: 0px;
left: 0px;
}
}
.inline-span:before {
position: absolute;
content: "";
width: 10px;
height: 10px;
border-radius: 50%;
top: -4px;
left: 50%;
margin-left: -6px;
background: #bfbfbf;
z-index: 1;
}
.inline-span.active {
font-size: 16px;
color: #333;
&:before {
background: #4cacff;
}
}
.date-input {
margin-right: 0.4rem;
}
.tel-input{
width: 290px;
}
.code-input{
width: 180px;
}
.no-label {
display: inline-block;
.ant-form-explain {
white-space: nowrap;
}
}
.days-word {
line-height: 40px;
}
.ant-modal-footer{
text-align: center;
}

View File

@ -1,52 +1,95 @@
import React, { useEffect, useState } from 'react';
import { Pagination, Icon, Input, Button } from 'antd';
import { Pagination, Input, Button } from 'antd';
import moment from 'moment';
import ChooseNav from '../../components/chooseNav';
import SortBox from '../../components/sortBox';
import ItemList from '../../components/itemListTask';
import ItemListTask from '../../components/itemListTask';
import Nodata from '../../../forge/Nodata';
import { taskType, taskStatus } from '../static';
import { getTaskList } from '../api';
import './index.scss';
import { taskTimeArr, taskStatusArr, sortArr, taskModeIdArr } from '../static';
import { getTaskList, getTaskCategory } from '../api';
const Search = Input.Search;
export default ({ history }) => {
const [type, setType] = useState(undefined);
const [title, setTitle] = useState(undefined);
export default ({ history, current_user }) => {
console.log(current_user);
const [taskCategoryArr, setTaskCategoryArr] = useState([]);
const [categoryId, setCategoryId] = useState('');
const [taskModeId, setTaskModeId] = useState('');
const [expiredStartTime, setExpiredStartTime] = useState('');
const [expiredEndTime, setExpiredEndTime] = useState('');
const [status, setStatus] = useState('');
const [searchInput, setSearchInput] = useState('');
const [orderBy, setOrderBy] = useState('');
const [curPage, setCurPage] = useState(1);
const [total, setTotal] = useState(0);
const [taskList, setAchiveList] = useState([]);
const [taskList, setTaskList] = useState([]);
useEffect(() => {
getTaskCategory().then(data => {
if (data) {
for (const item of data) {
item.dicItemCode = item.id;
item.dicItemName = item.name;
}
setTaskCategoryArr(data);
}
});
}, []);
useEffect(() => {
const params = {
categoryId,
taskModeId,
expiredStartTime,
expiredEndTime,
status,
searchInput,
orderBy,
curPage,
isChecked: 1,
pageSize: 10,
status: 1,
type,
title,
flag: 1, //21
};
getTaskList(params).then(data => {
setAchiveList(data.rows);
setTotal(data.total);
if (data) {
setTaskList(data.rows);
setTotal(data.total);
}
})
}, [type, title, orderBy, curPage]);
}, [categoryId, taskModeId, expiredStartTime, expiredEndTime, status, searchInput, orderBy, curPage]);
function changeOptionId(option, type) {
console.log(option);
setType(option.code || '');
if (type === 'taskCategory') {
setCategoryId(option.dicItemCode);
} else if (type === 'taskModeId') {
setTaskModeId(option.dicItemCode);
} else if (type === 'taskTime') {
if (option.dicItemCode) {
let nowTime = moment(new Date()).format('YYYY-MM-DD HH:mm:ss');
let nextTime = moment(new Date().setDate(new Date().getDate() + option.dicItemCode)).format('YYYY-MM-DD HH:mm:ss');
setExpiredStartTime(nowTime);
setExpiredEndTime(nextTime);
} else {
setExpiredStartTime('');
setExpiredEndTime('');
}
} else if (type === 'taskStatus') {
setStatus(option.dicItemCode);
}
}
function changeSort(sortType) {
// setOrderBy(sortType);
setCurPage(1);
console.log(sortType);
let sortValue = '';
if (sortType.type !== 'default') {
if (sortType.desc) {
sortValue = sortType.type + 'Desc';
} else {
sortValue = sortType.type + 'Asc';
}
}
setOrderBy(sortValue);
setCurPage(1);
}
function taskClick(id) {
@ -62,18 +105,18 @@ export default ({ history }) => {
<div className="centerbox">
<div className="nav-content">
<ChooseNav
key={'taskDomain'}
type={'taskDomain'}
key={'taskCategory'}
type={'taskCategory'}
title={'任务领域'}
options={taskType}
options={taskCategoryArr}
changeOptionId={changeOptionId}
/>
<ChooseNav
key={'taskModel'}
type={'taskModel'}
key={'taskModeId'}
type={'taskModeId'}
title={'任务模式'}
options={taskType}
options={taskModeIdArr}
changeOptionId={changeOptionId}
/>
@ -81,7 +124,7 @@ export default ({ history }) => {
key={'taskTime'}
type={'taskTime'}
title={'任务时限'}
options={taskType}
options={taskTimeArr}
changeOptionId={changeOptionId}
/>
@ -89,7 +132,7 @@ export default ({ history }) => {
key={'taskStatus'}
type={'taskStatus'}
title={'任务状态'}
options={taskStatus}
options={taskStatusArr}
changeOptionId={changeOptionId}
/>
</div>
@ -98,59 +141,36 @@ export default ({ history }) => {
<div className="centerScreen" >
<SortBox
options={[{
name: '综合',
type: 1,
}, {
name: '最新',
type: 2,
icon: true,
value: false,
},
{
name: '最热',
type: 3,
icon: true,
value: false,
},
{
name: '价格',
type: 4,
icon: true,
value: false,
},
]}
options={sortArr}
changeOptionId={changeSort}
defaultValue={{
name: '综合',
type: 'default',
}}
/>
<div className="center-right-but">
<Search
maxLength={20}
style={{ width: "300px" }}
placeholder="请输入任务编号/任务名称"
onSearch={(value) => setTitle(value)} />
placeholder="请输入任务编号/任务名称关键字"
onSearch={(value) => setSearchInput(value)} />
<Button className="mr20 font-12" type="primary" onClick={goAdd}><i className="iconfont icon-zaibianji font-12 mr3"></i>发布需求</Button>
</div>
</div>
<ItemList
<ItemListTask
list={taskList}
itemClick={taskClick}
curPage={curPage}
total={total}
changePage={(page) => { setCurPage(page) }}
/>
</div>
{taskList.length > 0 ? <div className="edu-txt-center mt10">
<Pagination
showQuickJumper
onChange={(page) => { setCurPage(page) }}
current={curPage}
total={total}
showTotal={total => `${total}`}
/>
</div> : <Nodata _html="暂无数据" />}
</div>
)

View File

@ -1,5 +0,0 @@
.center-right-but{
.ant-input-group-addon{
border: 0 !important;
}
}