Browse Source

后台排队项目页面

master
liujiangyong 2 years ago
parent
commit
575024fd50
  1. 4
      web/client/src/app.js
  2. 2
      web/client/src/layout/containers/layout/index.js
  3. 6
      web/client/src/sections/homePage/actions/profile.js
  4. 4
      web/client/src/sections/index.js
  5. 6
      web/client/src/sections/party/actions/index.js
  6. 18
      web/client/src/sections/party/actions/party.js
  7. 82
      web/client/src/sections/party/components/edit-party.js
  8. 92
      web/client/src/sections/party/containers/index.js
  9. 64
      web/client/src/sections/wait/actions/article.js
  10. 9
      web/client/src/sections/wait/actions/index.js
  11. 163
      web/client/src/sections/wait/components/import_model.js
  12. 96
      web/client/src/sections/wait/components/modeal.js
  13. 192
      web/client/src/sections/wait/containers/index.js
  14. 4
      web/client/src/sections/wait/index.js
  15. 4
      web/client/src/sections/wait/nav-item.js
  16. 0
      web/client/src/sections/wait/reducers/index.js
  17. 4
      web/client/src/sections/wait/routes.js
  18. 6
      web/client/src/utils/webapi.js

4
web/client/src/app.js

@ -1,6 +1,6 @@
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { import {
Auth, AlarmConfure, Article, Party, Personnel Auth, AlarmConfure, Article, Wait, Personnel
} from './sections'; } from './sections';
import Layout from './layout'; import Layout from './layout';
@ -19,7 +19,7 @@ function App(props) {
return ( return (
<Layout <Layout
title={projectName} title={projectName}
sections={[Auth, AlarmConfure, Article, Personnel, Party,]} sections={[Auth, AlarmConfure, Article, Personnel, Wait,]}
/> />
); );
} }

2
web/client/src/layout/containers/layout/index.js

@ -230,4 +230,4 @@ function mapStateToProps(state) {
export default connect(mapStateToProps)(LayoutContainer); export default connect(mapStateToProps)(LayoutContainer);
var pathList = ["/article", "/party", "/organization", "/personnel"]; var pathList = ["/article", "/wait", "/organization", "/personnel"];

6
web/client/src/sections/homePage/actions/profile.js

@ -99,7 +99,7 @@ export function getProject() {
type: "post", type: "post",
dispatch: dispatch, dispatch: dispatch,
actionType: "PROJECT", actionType: "PROJECT",
url: ApiTable.projectUrl, url: ApiTable.getProjectUrl,
msg: { option: "获取本周在研项目" }, msg: { option: "获取本周在研项目" },
reducer: { reducer: {
name: "project", name: "project",
@ -113,7 +113,7 @@ export function getPeople() {
type: "post", type: "post",
dispatch: dispatch, dispatch: dispatch,
actionType: "PEOPLE", actionType: "PEOPLE",
url: ApiTable.peopleUrl, url: ApiTable.getPeopleUrl,
msg: { option: "获取人员情况" }, msg: { option: "获取人员情况" },
reducer: { reducer: {
name: "people", name: "people",
@ -127,7 +127,7 @@ export function getWait() {
type: "post", type: "post",
dispatch: dispatch, dispatch: dispatch,
actionType: "WAIT", actionType: "WAIT",
url: ApiTable.waitUrl, url: ApiTable.getWaitUrl,
msg: { option: "获取待研发项目" }, msg: { option: "获取待研发项目" },
reducer: { reducer: {
name: "wait", name: "wait",

4
web/client/src/sections/index.js

@ -1,9 +1,9 @@
import Auth from './auth'; import Auth from './auth';
import AlarmConfure from './homePage'; import AlarmConfure from './homePage';
import Article from './article' import Article from './article'
import Party from './party' import Wait from './wait'
import Personnel from './personnel' import Personnel from './personnel'
export { export {
Auth, AlarmConfure, Article, Party, Personnel Auth, AlarmConfure, Article, Wait, Personnel
}; };

6
web/client/src/sections/party/actions/index.js

@ -1,6 +0,0 @@
'use strict';
import { editLaborParty } from './party'
export default {
editLaborParty
}

18
web/client/src/sections/party/actions/party.js

@ -1,18 +0,0 @@
'use strict';
import { ApiTable } from '$utils'
import { Request } from '@peace/utils'
import { basicAction } from '@peace/utils'
export function editLaborParty(obj) {
return dispatch => basicAction({
type: 'put',
dispatch: dispatch,
data: obj,
actionType: 'EDIT_PARTY',
url: `${ApiTable.getpartyMember}`,
msg: { error: '编辑党员工会人数失败' },
reducer: { name: 'editLaborParty' }
});
}

82
web/client/src/sections/party/components/edit-party.js

@ -1,82 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Modal, Form, InputNumber, Button } from 'antd'
import { useEffect } from 'react'
const EditParty = props => {
const { visible, handleOk, handleCancel, editData } = props
const [form] = Form.useForm();
useEffect(() => {
if (editData && visible) {
form.setFieldsValue({
party: editData.partyNumber,
labor: editData.laborUnion
})
}
}, [editData, visible])
return (
<Modal
visible={visible}
// onOk={handleOk}
onCancel={handleCancel}
footer={null}
title="党员、工会人数编辑"
>
<Form
name="basic"
labelCol={{ span: 8 }}
wrapperCol={{ span: 16 }}
labelAlign="left"
form={form}
onFinish={handleOk}
// onFinishFailed={onFinishFailed}
autoComplete="off"
>
<Form.Item
label="党员人数"
name="party"
rules={[{ required: true, message: '请输入党员人数' }]}
>
<InputNumber rows={1} min={0} width={180}></InputNumber>
</Form.Item>
<Form.Item
label="工会人数"
name="labor"
rules={[{ required: true, message: '请输入工会人数' }]}
>
<InputNumber rows={1} min={0} width={180}></InputNumber>
</Form.Item>
<Form.Item noStyle={true}>
<div style={{
padding: '12px 0',
width: '100%',
textAlign: 'right',
}}>
<Button
style={{ marginRight: 16, width: 88 }}
onClick={handleCancel}
>取消</Button>
<Button
htmlType="submit"
type="primary"
style={{ marginRight: 16, width: 88 }}
// onClick={() => handleOk(form)}
>确定</Button>
</div>
</Form.Item>
</Form>
</Modal>
)
}
EditParty.propTypes = {
visible: PropTypes.bool,
handleCancel: PropTypes.func,
handleOk: PropTypes.func,
editData: PropTypes.object
}
export default EditParty

92
web/client/src/sections/party/containers/index.js

@ -1,92 +0,0 @@
import PropTypes from 'prop-types'
import React, { useEffect, useState } from "react";
import { connect } from 'react-redux'
import { Table, message } from 'antd'
import { getPartyMember } from '../../homePage/actions/profile'
import { editLaborParty } from '../actions/party'
import EditParty from '../components/edit-party';
var request = false
export const Default = (props) => {
const { dispatch } = props;
const [partyLabor, setPartyLabor] = useState();
const [showEdit, setShowEdit] = useState(false);
const [editData, setEditData] = useState();
useEffect(() => {
dispatch(getPartyMember()).then(res => {
if (res.success) {
setPartyLabor(res.payload.data);
}
})
}, [request])
const onEditParty = (record) => {
setShowEdit(true);
setEditData(record)
}
const tableColumns = [
{
key: 'num',
dataIndex: 'num',
title: '序号',
render: () => {
return <span>1</span>
}
},
{
key: 'partyNumber',
dataIndex: 'partyNumber',
title: '党员人数',
},
{
key: 'laborUnion',
dataIndex: 'laborUnion',
title: '工会人数',
},
{
key: 'ation',
title: '操作',
render: (text, record) => {
return <a onClick={() => onEditParty(record)}>编辑</a>
}
}
]
const handleOk = (values) => {
if (values) {
const { labor, party } = values
const result = { partyNumber: party, laborUnion: labor }
dispatch(editLaborParty(result)).then(res => {
if (res.success) {
message.success("编辑成功");
request = !request;
handleCancel();
}
})
}
}
const handleCancel = () => {
setShowEdit(false);
}
return (
<div>
<p style={{ marginBottom: 16, fontSize: 16 }}>党员工会人数维护</p>
<Table columns={tableColumns} dataSource={partyLabor ? [partyLabor] : []} />
<EditParty editData={editData} visible={showEdit} handleOk={handleOk} handleCancel={handleCancel} />
</div>
)
}
Default.propTypes = {
second: PropTypes.third
}
const mapStateToProps = (state) => ({})
export default connect(mapStateToProps)(Default)

64
web/client/src/sections/wait/actions/article.js

@ -0,0 +1,64 @@
"use strict";
import { ApiTable } from "$utils";
import { Request } from "@peace/utils";
import { basicAction } from "@peace/utils";
export function addArticle(articleObj) {
return (dispatch) =>
basicAction({
type: "post",
dispatch: dispatch,
data: articleObj,
actionType: "ADD_ARTICLE",
url: `${ApiTable.addArticle}`,
msg: { error: "新增文章失败" },
reducer: { name: "articleInsertInfo" },
});
}
export function editArticle(articleObj) {
return (dispatch) =>
basicAction({
type: "put",
dispatch: dispatch,
data: articleObj,
actionType: "EDIT_ARTICLE",
url: `${ApiTable.addArticle}`,
msg: { error: "编辑文章失败" },
reducer: { name: "articleInsertInfo" },
});
}
export function getDataList(query) {
return (dispatch) =>
basicAction({
type: "get",
dispatch: dispatch,
actionType: "GET_DATALIST",
url: ApiTable.getDataList,
query: query,
msg: { error: "获取文章信息失败" },
reducer: {
name: "articlesfrom",
},
});
}
export function delDataList(id) {
return (dispatch) =>
basicAction({
type: "del",
dispatch: dispatch,
actionType: "DEL_DATALIST11",
url: ApiTable.delDataList + `?id=${id}`,
query: { id: id },
msg: { error: "删除文章信息失败" },
});
}
export const MODIFYARTICAL = {
REQUEST_SUCCESS: "MODIFY_ARTICAL_SUCCESS",
};
export function setModifyData(data) {
return (dispatch) => {
dispatch({ type: MODIFYARTICAL.REQUEST_SUCCESS, payload: { data } });
};
}

9
web/client/src/sections/wait/actions/index.js

@ -0,0 +1,9 @@
"use strict";
import { addArticle, editArticle, getDataList, delDataList } from "./article";
export default {
addArticle,
editArticle,
getDataList,
delDataList,
};

163
web/client/src/sections/wait/components/import_model.js

@ -0,0 +1,163 @@
/*
* @description :
* @Date : 2021-04-07 14:39:33
* @FilePath : \web\client\src\sections\user\components\userManagement\importUser.js
* @useStrict : use strict
*/
'use strict';
import React, { useState } from 'react';
import { connect } from 'react-redux';
import { Button, Input, Card, Modal, Upload, message } from 'antd';
import { Request } from '@peace/utils'
import request from 'superagent'
import XLSX from 'xlsx'
// import { userBulkAdd } from '../../actions'
//TODO 下载模板和上传文件读取
const ImportUser = props => {
const { user, dispatch, handleCancel, params,editData } = props
const [msg, setMsg] = useState('')
const [loading, setLoading] = useState('')
const [postData, setPostData] = useState([])
const confirm = () => {
if (postData.length) {
setLoading(true)
// dispatch(userBulkAdd(postData, params.departmentId ? params.departmentId : null)).then(res => {
// if (res.success) {
// }
// setLoading(false)
// })
} else {
message.warn('没有数据可以提交,请上传数据文件')
}
}
return (
<Modal
visible={props.importVisible}
onOk={confirm}
confirmLoading={loading}
onCancel={() => {
setMsg('')
setLoading(false)
setPostData([])
handleCancel()
}}
destroyOnClose
>
{/* <Button size="small" type='primary' onClick={() => {
Request.get(`attachments?src=upload/files/1.jpg&filename=${encodeURIComponent('1.jpg')}&token=${user.token}`).then(res => {
})
}}>下载模板</Button> */}
<a href={'/_api/' + `attachments?src=files/用户信息导入模板.xlsx&filename=${encodeURIComponent('用户信息导入模板.xlsx')}&token=${user.token}`} >
<Button size="small" type='primary' >下载模板</Button>
</a>
<div style={{ marginTop: 10 }}>下载模板后填写完整后上传</div>
<div style={{ marginTop: 10 }}>
<Upload {...{
name: 'user_d',
action: '/',
maxCount: 1,
showUploadList: {
showRemoveIcon: false
},
beforeUpload: (file) => {
setMsg('')
setPostData([])
const extNames = file.name.split('.');
let isDAE = false;
if (extNames.length > 0) {
let fileType = extNames[extNames.length - 1].toLowerCase();
isDAE = ['xlsx', 'xls'].some((f) => f == fileType);
}
if (!isDAE) {
setMsg(`只能上传 ${['xlsx', 'xls'].join()} 格式的文件!`);
return false;
}
},
onChange(info) {
},
customRequest: async (data) => {
const { file, onSuccess, onError } = data
const reader = new FileReader(); // 使用 FileReader 读取数据
reader.onload = function (e) { // 数据读取完成后的回调函数
const data = new Uint8Array(e.target.result);
const workbook = XLSX.read(data, { type: 'array' }); // workbook 是 xlsx 解析 excel 后返回的对象
const firstSheetName = workbook.SheetNames[0]; // 获取第一个 sheet 的名字
const worksheet = workbook.Sheets[firstSheetName]; // 获取第一个 sheet 的内容
const res = XLSX.utils.sheet_to_json(worksheet); // 使用 utils 里的方法转换内容为便于使用的数组
const error = (msg) => {
setMsg(msg)
onError({ message: msg })
}
if (res.length > 1000) {
error('一次性上传数据行数应小于1000行,请分批上传')
return
}
if (!res.length) {
error('请填写至少一行数据')
return
}
let postData = []
const pattern = /^1[3|4|5|6|7|8|9]\d{9}$/
for (let i = 0; i < res.length; i++) {
let d = res[i]
let name = String(d['姓名']).trim();
let phone = String(d['手机号码']).trim();
let account = String(d['账号']).trim();
let peopleCode = d['人员编号'] && String(d['人员编号']).trim();
if (!name || !phone || !account || !peopleCode) {
error(`${i + 1} 行有空值,请填写后重新上传`)
return
}
if (!pattern.test(phone)) {
error(`${i + 1} 行手机号码错误`)
return
}
if (name.length > 128 || account.length > 128) {
error(`${i + 1} 行数据字符长度大于 128,请更改后重新上传`)
return
}
if (postData.some(p => p.account == account)) {
error(`${i + 1} 行账号重复,请更改后重新上传`)
return
}
postData.push({
name, phone, account, peopleCode
})
}
if (postData.length) {
setPostData(postData)
}
let msg = '文件解析完成,点击确定按钮上传保存!'
setMsg(msg)
onSuccess({ message: msg })
};
reader.readAsArrayBuffer(file); // 读取数据
},
}}>
<Button>上传文件</Button>
</Upload>
<br />
<span>{msg}</span>
</div>
</Modal >
)
}
function mapStateToProps(state) {
const { auth, customizeList } = state;
return {
user: auth.user,
}
}
export default connect(mapStateToProps)(ImportUser);

96
web/client/src/sections/wait/components/modeal.js

@ -0,0 +1,96 @@
import React,{useState,useEffect} from 'react'
import { Modal, Form, Input, Button,DatePicker,Select } from 'antd'
import dayjs from 'dayjs';
function modeal(props) {
const [form] = Form.useForm();
const { visible, handleOk, handleCancel, editData } = props
console.log(editData)
useEffect(() => {
if (editData && visible) {
form.setFieldsValue({
name_project: editData.name_project,
part_people: editData.part_people,
build_time: dayjs(editData?.build_time),
publish_time: dayjs(editData.publish_time),
progress:editData.progress
})
}
}, [editData, visible])
return (
<Modal
visible={visible}
onCancel={handleCancel}
footer={null}
title="研发看板项目编辑"
>
<Form
name="basic"
labelAlign="left"
form={form}
onFinish={handleOk}
autoComplete="off"
>
<Form.Item
label="项目计划"
name="name_project"
rules={[{ required: true, message: '请输入项目计划' }]}
>
<Input/>
</Form.Item>
<Form.Item
label="投入人力"
name="part_people"
rules={[{ required: true, message: '请输入人员姓名' }]}
>
<Input/>
</Form.Item>
<Form.Item
label="构建时间"
name="build_time"
rules={[{ required: true, message: '请选择时间' }]}
>
<DatePicker />
</Form.Item>
<Form.Item
label="发布时间"
name="publish_time"
rules={[{ required: true, message: '请选择时间' }]}
>
<DatePicker />
</Form.Item>
<Form.Item
label="目前进度"
name="progress"
rules={[{ required: true, message: '请选择进度' }]}
>
<Select><Select.Option value="需求评审中">需求评审中</Select.Option>
<Select.Option value="研发中">研发中</Select.Option>
<Select.Option value="测试中">测试中</Select.Option>
<Select.Option value="已发布">已发布</Select.Option>
</Select>
</Form.Item>
<Form.Item noStyle={true}>
<div style={{
padding: '12px 0',
width: '100%',
textAlign: 'right',
}}>
<Button
style={{ marginRight: 16, width: 88 }}
onClick={handleCancel}
>取消</Button>
<Button
htmlType="submit"
type="primary"
style={{ marginRight: 16, width: 88 }}
// onClick={() => handleOk(form)}
>确定</Button>
</div>
</Form.Item>
</Form>
</Modal>
)
}
export default modeal

192
web/client/src/sections/wait/containers/index.js

@ -0,0 +1,192 @@
import PropTypes from "prop-types";
import React, { useEffect, useState, useRef } from "react";
import { connect } from "react-redux";
import { Button, Popconfirm } from "antd";
import ProTable from "@ant-design/pro-table";
import Action from "../actions/index";
import moment from "moment";
import { push } from "react-router-redux";
import { Scroller } from "$components";
import ProjectModeal from '../components/modeal'
import ImportModeal from '../components/import_model'
export const Default = (props) => {
const { dispatch } = props;
const [heading, setHeading] = useState(); //标题
const [state, setState] = useState(); //状态
const [day, setDay] = useState(); //发布时间
const [counts, setCounts] = useState(); //数据
const [visible, setVisible] = useState(false); //弹窗
const [editData, setEditData] = useState(); //数据
const [importVisible, setImportVisible] = useState(false); //弹窗
const tableActionRef = useRef();
const dataSore = [{ name_project: '企服v1.0.1', part_people: '投入研发3、测试1', build_time: new Date(), publish_time: new Date(), progress: '研发中' }]
const columns = [
{
title: "项目",
dataIndex: "name_project",
key: "name_project",
hideInSearch: true,
render: (dom, record) => {
return record.name_project;
},
},
{
title: "需求来源",
key: "from_project",
dataIndex: "from_project",
hideInSearch: true,
render: (dom, record) => {
return record.part_people
},
},
{
title: "对接人",
key: "contacts",
dataIndex: "contacts",
hideInSearch: true,
render: (dom, record) => {
return moment(record.build_time).format('YYYY-MM-DD')
},
},
{
title: '处理进度',
dataIndex: 'progress',
key: 'progress',
align: 'center'
},
{
title: "操作",
key: "since",
dataIndex: "createdAt",
valueType: "dateTime",
hideInSearch: true,
render: (dom, record) => {
return (
<div>
<a
onClick={() => {
editor(record);
}}
>
编辑
</a>
<Popconfirm
title="确认删除?"
onConfirm={() => {
delDataList(record.id);
}}
>
<Button type="link">删除</Button>
</Popconfirm>
</div>
)
},
},
{
key: "direction",
hideInTable: true,
dataIndex: "direction",
order: 6,
renderFormItem: (item, { type, defaultRender, ...rest }, form) => {
return (
<Button
type="primary"
style={{ width: "200px" }}
onClick={() => {
setImportVisible(true)
}}
>
项目信息导入
</Button>
);
},
},
];
const handleCancel = () => {
setVisible(false)
}
const handOk = () => {
}
const importHandleCancel = () => {
setImportVisible(false)
}
//删除资讯
const delDataList = (ids, type) => {
dispatch(Action.delDataList(ids)).then((res) => {
if (res.success) {
// 刷新
tableActionRef.current.reload();
}
});
};
//编辑
const editor = (record) => {
setVisible(true)
setEditData(record)
};
return (
<div style={{ height: "100%" }}>
<Scroller containerId={"article-container-query"} height={"100%"}>
<ProTable
actionRef={tableActionRef}
columns={columns}
options={false}
dataSource={dataSore}
search={{
optionRender: false,
collapsed: false,
}}
request={async (params) => {
let query = {
type: -1,
searchValue: heading,
status: state,
page: params.current,
limit: params.pageSize,
publishTime:
day && day.length && day[0].trim() != ""
? JSON.stringify([
moment(day[0]).startOf("day"),
moment(day[1]).endOf("day"),
])
: null,
};
const res = await dispatch(Action.getDataList(query));
setCounts(res.payload.data);
return {
...res,
total: res.payload.data ? res.payload.data.total : 0,
};
}}
rowKey="id"
pagination={{
showSizeChanger: true,
}}
/>
</Scroller>
<ProjectModeal visible={visible} handleOk={handOk} editData={editData} handleCancel={handleCancel}></ProjectModeal>
<ImportModeal importVisible={importVisible} handleCancel={importHandleCancel} editData={editData}></ImportModeal>
</div>
);
};
Default.propTypes = {
second: PropTypes.third,
};
const mapStateToProps = (state) => ({});
export default connect(mapStateToProps)(Default);
const data = [
{ name: 'yongchu', age: '18', 任务1: 'v1', 计划1: 'time1' },
{ name: 'yongchu', age: '18', 任务1: 'v1', 计划1: 'time1' },
{ name: 'yongchu', age: '18', 任务1: 'v1', 计划1: 'time1' },
{ name: '大王', age: '18', 任务1: 'v1', 计划1: 'time1' },
{ name: '大王', age: '18', 任务1: 'v1', 计划1: 'time1' }
]

4
web/client/src/sections/party/index.js → web/client/src/sections/wait/index.js

@ -4,8 +4,8 @@ import actions from './actions';
import nativeItem from './nav-item'; import nativeItem from './nav-item';
export default { export default {
key: 'party', key: 'wait',
name: '工会管理', name: '排队项目',
reducers, reducers,
routes, routes,
actions, actions,

4
web/client/src/sections/party/nav-item.js → web/client/src/sections/wait/nav-item.js

@ -5,8 +5,8 @@ import { BarChartOutlined } from '@ant-design/icons';
export function getNavItem() { export function getNavItem() {
return ( return (
<Menu.Item key="party" icon={<BarChartOutlined />}> <Menu.Item key="wait" icon={<BarChartOutlined />}>
<Link to="/party">工会管理</Link> <Link to="/wait">排队项目</Link>
</Menu.Item> </Menu.Item>
); );
} }

0
web/client/src/sections/party/reducers/index.js → web/client/src/sections/wait/reducers/index.js

4
web/client/src/sections/party/routes.js → web/client/src/sections/wait/routes.js

@ -3,8 +3,8 @@ export default [
{ {
type: 'inner', type: 'inner',
route: { route: {
path: '/party', path: '/wait',
key: 'party', key: 'wait',
component: Container, component: Container,
} }
} }

6
web/client/src/utils/webapi.js

@ -3,9 +3,9 @@ import noCache from "superagent-no-cache";
export const ApiTable = { export const ApiTable = {
/* 研发看板 */ /* 研发看板 */
projectUrl: "outProject", // 在研项目 getProjectUrl: "outProject", // 在研项目
peopleUrl: "outPeople", // 人员情况 getPeopleUrl: "outPeople", // 人员情况
waitUrl: "outWait", // 待研项目 getWaitUrl: "outWait", // 待研项目
getDataList: "article/management", //全部资讯 getDataList: "article/management", //全部资讯
delDataList: "article/management", //删除资讯 delDataList: "article/management", //删除资讯

Loading…
Cancel
Save