Commit 86f88857 by zhaochengxiang

盘点 评估

parent 8f1d1581
......@@ -22,12 +22,15 @@ import AssetTree from './view/Manage/AssetManage/Component/AssetManageTree';
import DataService from './view/Manage/Pdata';
import DataServiceDetail from './view/Manage/Pdata/Component/ServiceDetail';
import GrantedDataServiceList from './view/Manage/Pdata/Component/GrantedList';
import AssetTask from './view/Manage/AssetTask';
import AssetEvaluate from './view/Manage/AssetEvaluate';
import { AssetBrowseReference, AssetDraftReference, AssetMountReference } from './util/constant';
const AssetDraft = loadable(()=> import('./view/Manage/AssetDraft'));
const AssetAction = loadable(()=> import('./view/Manage/AssetManage/Component/AssetAction'));
export const AppContext = React.createContext();
export const appId = generateUUID();
......@@ -204,6 +207,8 @@ export class App extends React.Component {
<Route path={'/center-home/menu/asset-draft'} component={AssetDraft} exact />
<Route path={'/center-home/menu/asset-manage'} component={AssetManage} exact />
<Route path={'/center-home/menu/asset-browse'} component={AssetBrowse} exact />
<Route path={'/center-home/menu/asset-task'} component={AssetTask} exact />
<Route path={'/center-home/menu/asset-evaluate'} component={AssetEvaluate} exact />
<Route path={'/center-home/menu/data-service'} component={DataService} exact />
<Route path={'/center-home/data-model-action'} component={EditModel} exact />
<Route path={'/center-home/asset-detail'} component={AssetDetailPage} exact />
......
......@@ -472,4 +472,36 @@ export function* saveWorkbookByType(payload) {
export function* deleteWorkbooks(payload) {
return yield call(service.deleteWorkbooks, payload)
}
export function* getEvaluations(payload) {
return yield call(service.getEvaluations, payload)
}
export function* saveEvaluation(payload) {
return yield call(service.saveEvaluation, payload)
}
export function* runEvaluations(payload) {
return yield call(service.runEvaluations, payload)
}
export function* deleteEvaluations(payload) {
return yield call(service.deleteEvaluations, payload)
}
export function* listTasks(payload) {
return yield call(service.listTasks, payload);
}
export function* saveTask(payload) {
return yield call(service.saveTask, payload);
}
export function* auditTask(payload) {
return yield call(service.auditTask, payload);
}
export function* uploadDataAssetExcel(payload) {
return yield call(service.uploadDataAssetExcel, payload);
}
\ No newline at end of file
......@@ -39,6 +39,14 @@ export const routes = [
name: 'asset-browse',
text: '资产浏览'
},
{
name: 'asset-task',
text: '资产盘点任务',
},
{
name: 'asset-evaluate',
text: '资产评估',
},
]
}
];
......
......@@ -479,4 +479,36 @@ export function saveWorkbookByType(payload) {
export function deleteWorkbooks(payload) {
return PostJSON("/dataassetmanagertest/reportApi/deleteWorkbooks", payload)
}
export function getEvaluations(payload) {
return GetJSON('/dataassetmanager/evaluationApi/listProjects', payload)
}
export function saveEvaluation(payload) {
return PostJSON('/dataassetmanager/evaluationApi/saveProject', payload)
}
export function runEvaluations(payload) {
return PostJSON('/dataassetmanager/evaluationApi/run', payload)
}
export function deleteEvaluations(payload) {
return PostJSON('/dataassetmanager/evaluationApi/deleteProjects', payload)
}
export function listTasks(payload) {
return PostJSON("/dataassetmanager/dataAssetCheckApi/listTasks", payload);
}
export function saveTask(payload) {
return PostJSON("/dataassetmanager/dataAssetCheckApi/saveTask", payload);
}
export function auditTask(payload) {
return PostJSON("/dataassetmanager/dataAssetCheckApi/audit", payload);
}
export function uploadDataAssetExcel(payload) {
return PostFile("/dataassetmanager/dataAssetCheckApi/uploadDataAssetExcel", payload, 'reportFile');
}
\ No newline at end of file
import React from 'react'
import { Modal, Spin, Button, Form, Input, TreeSelect, Row, Col, Checkbox } from "antd"
import produce from "immer"
import { dispatch } from '../../../model'
const FC = (props) => {
const { visible, item, action, onCancel } = props
const [waiting, setWaiting] = React.useState(false)
const [loading, setLoading] = React.useState(false)
const [elements, setElements] = React.useState()
const basicRef = React.useRef(null)
React.useEffect(() => {
getElements()
}, [])
const getElements = () => {
setLoading(true)
dispatch({
type: 'assetmanage.listComputableElements',
callback: data => {
setElements(data??[])
setLoading(false)
},
error: () => {
setLoading(false)
}
})
}
const close = (refresh = false) => {
setWaiting(false)
setLoading(false)
onCancel?.(refresh)
}
const save = async () => {
try {
const row = await basicRef.current?.validate()
setWaiting(true)
dispatch({
type: 'assetmanage.saveEvaluation',
payload: {
data: (action==='add')?row:{...item, ...row}
},
callback: data => {
close(true)
},
error: () => {
setWaiting(false)
}
})
} catch (e) {
}
}
const footer = React.useMemo(() => {
return [
<Button key={'cancel'}
onClick={() => close()}
>取消</Button>,
<Button key={'save'} type='primary'
disabled={waiting}
onClick={() => save()}
>保存</Button>
]
}, [close, save, waiting])
return (
<Modal
visible={visible}
footer={footer}
width='50%'
bodyStyle={{ padding: '15px 15px 0px 15px', overflowX: 'auto', maxHeight: '80vh' }}
title='新增评估方案'
centered destroyOnClose
onCancel={() => { close() }}
>
<Spin spinning={loading || waiting}>
<Basic ref={basicRef} item={item} elements={elements} />
</Spin>
</Modal>
)
}
export default FC
const Basic = React.forwardRef(function ({ item, elements }, ref) {
const [form] = Form.useForm()
React.useImperativeHandle(ref, () => ({
validate: async () => {
return await form?.validateFields()
},
}), [form])
React.useEffect(() => {
if (item) {
form?.setFieldsValue(item)
}
}, [item])
return (
<Form
form={form}
labelCol={{ span: 6 }}
wrapperCol={{ span: 18 }}
autoComplete="off"
>
<Form.Item
label="方案名称"
name="name"
rules={[{ required: true, message: '请输入方案名称!' }]}
>
<Input placeholder="请输入方案名称" />
</Form.Item>
<Form.Item
name='dirs'
label='资产目录'
rules={[{ required: true, message: '请选择资产目录!' }]}
>
<AssetCatalogItem />
</Form.Item>
<Form.Item
name='elementIds'
label='评估要素'
rules={[{ required: true, message: '请选择评估要素!' }]}
>
<Checkbox.Group style={{ width: '100%' }}>
<Row>
{
(elements??[]).map((item, index) => {
return (
<Col span={6} key={index}>
<Checkbox
value={item.id}
style={{
lineHeight: '32px',
}}
>
{item.name}
</Checkbox>
</Col>
)
})
}
</Row>
</Checkbox.Group>
</Form.Item>
</Form>
)
})
const AssetCatalogItem = ({ value, onChange }) => {
const [loadingTreeData, setLoadingTreeData] = React.useState(false)
const [treeData, setTreeData] = React.useState()
React.useEffect(() => {
getTreeData()
}, [])
const treeData1 = React.useMemo(() => {
if (treeData) {
const newTreeData = produce(treeData, draft => {
const setNode = (g) => {
g.key = g.nodeId
g.title = g.text
g.value = g.nodeId
g.children?.forEach((child) => {
setNode(child)
})
}
draft.forEach((child) => {
setNode(child)
})
})
return newTreeData
}
return undefined
}, [treeData])
const getTreeData = () => {
setLoadingTreeData(true)
dispatch({
type: 'assetmanage.queryAssetDirectoryAsTree',
callback: data => {
setLoadingTreeData(false)
setTreeData(data)
},
error: () => {
setLoadingTreeData(false)
}
})
}
return (
<TreeSelect
value={(value??[]).map(item => {
return {
label: item.name,
value: item.id
}
})}
loading={loadingTreeData}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={treeData1}
placeholder="请选择资产目录"
treeDefaultExpandAll
treeCheckable={true}
treeCheckStrictly={true}
showCheckedStrategy={TreeSelect.SHOW_ALL}
onChange={(value) => {
onChange?.((value??[]).map(item => {
return {
id: item.value,
name: item.label
}
}))
}}
/>
)
}
\ No newline at end of file
import React from 'react'
import { debounceTime, Subject } from 'rxjs'
import { Space, Input, Table, Button, Tooltip, Modal, Pagination, Typography } from 'antd'
import { ExclamationCircleOutlined } from '@ant-design/icons'
import { defaultPage, usePage } from '../../../util/page'
import { dispatch } from '../../../model'
import AddEvaluate from './add'
import './index.less'
import { showMessage } from '../../../util'
import { AppContext } from '../../../App'
const FC = (props) => {
const [args, setArgs] = React.useState(() => ({
params: {
page: defaultPage.pageNum,
size: defaultPage.pageSize,
keyword: undefined,
startTime: undefined,
endTime: undefined,
},
}))
const [loading, setLoading] = React.useState(false)
const [data, setData] = React.useState()
const [total, setTotal] = React.useState()
const [elements, setElements] = React.useState()
const $keyword = React.useMemo(() => new Subject(), [])
const [keyword, setKeyword] = React.useState()
const [addEvaluateParams, setAddEvaluateParams] = React.useState({
visible: false,
item: undefined,
action: undefined
})
const [selectedRows, setSelectedRows] = React.useState([])
const [page, setPage] = usePage()
const app = React.useContext(AppContext)
const [modal, contextHolder] = Modal.useModal()
React.useEffect(() => {
getElements()
}, [])
const setArgsAndPage = React.useCallback((params) => {
// 设置查询参数时将分页置为1
setPage(prevpg => {
setArgs((prev) => {
const newparams = params ? { ...prev.params, ...params } : undefined
return { params: { ...newparams, page: 1, size: prevpg.pageSize } }
})
return ({ ...prevpg, pageNum: 1 })
})
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
const setPageAndArgs = React.useCallback((page) => {
setPage(prev => ({ ...prev, ...page }))
setArgs((prev) => {
return { params: { ...prev.params, page: page.pageNum, size: page.pageSize } }
})
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
React.useEffect(() => {
const $$keyword = $keyword.pipe(debounceTime(1000)).subscribe((keyword) => {
setArgsAndPage({ keyword })
})
return () => {
$$keyword.unsubscribe()
}
}, [])
React.useEffect(() => {
getEvaluations()
}, [args])
const cols = [
{
title: '方案名称',
dataIndex: 'name',
ellipsis: true,
},
{
title: '评估范围',
dataIndex: 'dirs',
render: (dirs) => {
const dirStr = (dirs??[]).map(item => item.name).toString()
return (
<Tooltip title={dirStr}>
<Typography.Text ellipsis={true}>
{dirStr}
</Typography.Text>
</Tooltip>
)
}
},
{
title: '评估要素',
dataIndex: 'elementIds',
render: (elementIds) => {
const elementStr = (elements??[]).filter(item => (elementIds??[]).indexOf(item.id) !== -1).map(item => item.name).toString()
return (
<Tooltip title={elementStr}>
<Typography.Text ellipsis={true}>
{elementStr}
</Typography.Text>
</Tooltip>
)
}
},
{
title: '总得分',
dataIndex: 'score',
ellipsis: true,
},
{
title: '资产总数',
dataIndex: 'dataAssetTotalNum',
ellipsis: true,
},
{
title: '创建时间',
dataIndex: 'createTimestamp',
ellipsis: true,
render: (_, record) => record.createTimestamp ? new Date(record.createTimestamp).toLocaleString() : ''
},
{
title: '操作',
key: 'action',
width: 120,
render: (_,record) => (
<a onClick={() => {
setAddEvaluateParams({
visible: true,
item: record,
action: 'edit',
})
}}>编辑</a>
)
}
]
const getEvaluations = () => {
setLoading(true)
dispatch({
type: 'assetmanage.getEvaluations',
payload: {
pageNum: args.params.page,
pageSize: args.params.size,
keyword: args.params.keyword,
},
callback: data => {
setLoading(false)
setData(data?.data)
setTotal(data?.total)
},
error: () => {
setLoading(false)
}
})
}
const getElements = () => {
dispatch({
type: 'assetmanage.listElements',
callback: data => {
setElements(data??[])
}
})
}
return (
<div className='asset-evalute'>
<div className='header px-3'>
<Space>
<Button onClick={() => {
setAddEvaluateParams({
visible: true,
item: undefined,
action: 'add',
})
}}>新增</Button>
<Button onClick={() => {
getEvaluations()
}}>刷新</Button>
<Tooltip title={((selectedRows??[]).length===0)?'请先选择评估方案':''}>
<Button
disabled={(selectedRows??[]).length===0}
onClick={() => {
modal.confirm({
title: '确认运行选中的评估方案吗?',
icon: <ExclamationCircleOutlined />,
okText: '确认',
cancelText: '取消',
onOk: () => {
dispatch({
type: 'assetmanage.runEvaluations',
payload: {
params: {
evaluationProjectIds: (selectedRows??[]).map(item => item.id).toString()
}
},
callback: data => {
showMessage('success', '运行成功')
},
})
}
})
}}>运行</Button>
</Tooltip>
<Tooltip title={((selectedRows??[]).length===0)?'请先选择评估方案':''}>
<Button
disabled={(selectedRows??[]).length===0}
onClick={() => {
modal.confirm({
title: '确认删除选中的评估方案吗?',
icon: <ExclamationCircleOutlined />,
okText: '确认',
cancelText: '取消',
onOk: () => {
dispatch({
type: 'assetmanage.deleteEvaluations',
payload: {
params: {
evaluationProjectIds: (selectedRows??[]).map(item => item.id).toString()
}
},
callback: data => {
showMessage('success', '删除成功')
getEvaluations()
},
})
}
})
}}>删除</Button>
</Tooltip>
</Space>
<Space>
<Input size="middle"
placeholder="方案名称"
value={keyword}
bordered={true} allowClear
onChange={(e) => {
const keyword = e.target.value
setKeyword(keyword)
$keyword.next((keyword??'').trim())
}}
style={{
width: 250
}}
/>
</Space>
</div>
<div className='px-3 pt-3'>
<Table
scroll={{y: 'calc(100vh - 285px)'}}
rowKey='id'
loading={loading}
columns={cols}
dataSource={data||[]}
pagination={false}
rowSelection={{
selectedRowKeys: (selectedRows??[]).map(item => item?.id),
onChange: (selectedRowKeys, selectedRows) => {
setSelectedRows(selectedRows)
},
}}
/>
<Pagination
className="text-center mt-3"
showSizeChanger
showQuickJumper
onChange={(_pageNum, _pageSize) => {
setPageAndArgs({ pageNum: _pageNum||1, pageSize: _pageSize || 20 })
}}
onShowSizeChange={(_pageNum, _pageSize) => {
setPageAndArgs({ pageNum: _pageNum||1, pageSize: _pageSize || 20 })
}}
current={page.pageNum}
pageSize={page.pageSize}
defaultCurrent={1}
total={total}
pageSizeOptions={[10,20]}
showTotal={total => `共 ${total} 条`}
/>
</div>
<AddEvaluate
{...addEvaluateParams}
onCancel={(refresh) => {
setAddEvaluateParams({
visible: false,
item: undefined,
action: undefined
})
refresh && getEvaluations()
}}
/>
{contextHolder}
</div>
)
}
export default FC
\ No newline at end of file
.asset-evalute {
height: 100%;
background-color: #fff;
.header {
display: flex;
flex: none;
height: 57px;
border-bottom: 1px solid #EFEFEF;
justify-content: space-between;
align-items: center;
}
}
\ No newline at end of file
import React, { useEffect, useState, useContext } from 'react';
import { Modal, Form, Input, Select, Space, Button } from 'antd';
import { AppContext } from '../../../App';
import { dispatch } from '../../../model';
import { AuditProgress, Basic, Process, Template } from './TaskDetail';
const FC = (props) => {
const { visible, onCancel, task, action } = props;
const [form] = Form.useForm();
const [confirmLoading, setConfirmLoading] = useState(false);
const { user } = useContext(AppContext);
const handleOk = async (status = '1') => {
try {
const values = await form.validateFields();
setConfirmLoading(true);
dispatch({
type: 'assetmanage.auditTask',
payload: {
params: {
taskId: task?.id
},
data: {
dealUser: user?.userName??'test',
processDate: new Date().toLocaleString(),
status,
title: action,
value: values.content
}
},
callback: data => {
setConfirmLoading(false);
reset();
onCancel && onCancel(true);
},
error: () => {
setConfirmLoading(false);
}
})
} catch (errInfo) {
}
}
const reset = () => {
setConfirmLoading(false);
form.resetFields();
}
return (
<Modal
visible={visible}
title={`${action}${task?.title}`}
width={1000}
confirmLoading={confirmLoading}
onCancel={() => {
reset();
onCancel && onCancel();
}}
footer={
<Space>
<Button onClick={() => onCancel && onCancel() }>取消</Button>
<Button type="primary" onClick={() => {handleOk()}} loading={confirmLoading}>确定</Button>
{
(action==='审核') && <Button type="danger" onClick={() => {handleOk('-1')}} loading={confirmLoading}>驳回</Button>
}
</Space>
}
>
<Form
form={form}
>
<Form.Item
label={`${action}意见`}
name="content"
rules={[{ required: true, message: `请输入${action}意见` }]}
>
<Input.TextArea rows={4} placeholder={`请输入${action}意见`} />
</Form.Item>
</Form>
<AuditProgress task={task} />
<Basic task={task} />
<Process task={task} />
<Template task={task} />
</Modal>
);
}
export default FC;
\ No newline at end of file
import React, { useEffect, useState, useContext } from 'react';
import { Modal, Form, Input, Select, Space, Button, Upload } from 'antd';
import { UploadOutlined, DownloadOutlined } from '@ant-design/icons';
import LocalStorage from 'local-storage';
import { AppContext } from '../../../App';
import { dispatch } from '../../../model';
import { showMessage } from '../../../util';
import { AuditProgress, Basic, Process, Template } from './TaskDetail';
const FC = (props) => {
const { visible, onCancel, task } = props;
const [form] = Form.useForm();
const [confirmLoading, setConfirmLoading] = useState(false);
const [ fileList, setFileList ] = useState([]);
const { user } = useContext(AppContext);
const downloadTemplate = () => {
const env = LocalStorage.get('assetsEnv');
window.open(`/api/dataassetmanager/dataAssetCheckApi/getDataAssetTemplate?env=${env}`);
}
const uploadProps = {
onRemove: file => {
const index = fileList.indexOf(file);
const newFileList = fileList.slice();
newFileList.splice(index, 1);
setFileList(newFileList);
},
beforeUpload: file => {
const isLt2OM = file.size / 1024 / 1024 < 20;
if (!isLt2OM) {
showMessage('error', '上传文件必须小于20M');
setFileList([]);
return false;
}
setFileList([file]);
return false;
},
fileList: fileList || [],
accept:".xlsx",
};
const handleOk = async (status = '-1') => {
if ((fileList||[]).length === 0) {
showMessage('info', '请先上传盘点文件');
return;
}
setConfirmLoading(true);
dispatch({
type: 'assetmanage.uploadDataAssetExcel',
payload: { fileList: fileList },
callback: fileId => {
setConfirmLoading(false);
dispatch({
type: 'assetmanage.saveTask',
payload: {
data: {
...task,
dataAssetExcel: {
fileId,
fileName: fileList[0].name
}
}
},
callback: data => {
setConfirmLoading(false);
reset();
onCancel?.(true);
},
error: () => {
setConfirmLoading(false);
}
});
},
error: () => {
setConfirmLoading(false);
}
});
}
const reset = () => {
setConfirmLoading(false);
setFileList([]);
}
return (
<Modal
visible={visible}
title={`盘点${task?.title}`}
width={1000}
confirmLoading={confirmLoading}
onCancel={() => {
reset();
onCancel && onCancel();
}}
footer={
<Space>
<Button onClick={() => onCancel && onCancel() }>取消</Button>
<Button type="primary" onClick={() => {handleOk()}} loading={confirmLoading}>确定</Button>
</Space>
}
>
<Form layout='inline'>
<Form.Item label='盘点文件上传:'>
<Button className='mr-2' icon={<DownloadOutlined />} onClick={ downloadTemplate }>
模版下载
</Button>
<Upload style={{ display: 'inline' }} {...uploadProps }>
<Button icon={
<UploadOutlined />}>
选择文件上传
</Button>
</Upload>
</Form.Item>
</Form>
<AuditProgress task={task} />
<Basic task={task} />
<Process task={task} />
<Template task={task} />
</Modal>
);
}
export default FC;
\ No newline at end of file
import React, { useMemo } from 'react';
import { Modal, Divider, Descriptions, Button, Table, Row, Col } from 'antd';
import { DownloadOutlined } from '@ant-design/icons';
import LocalStorage from 'local-storage';
import { status } from '.';
const FC = (props) => {
const { visible, onCancel, task } = props;
return (
<Modal
visible={visible}
title={`${task?.title}详情`}
width={1000}
onCancel={() => onCancel?.()}
footer={
<Button onClick={() => onCancel?.() }>取消</Button>
}
>
<AuditProgress task={task} />
<Basic task={task} />
<Process task={task} />
<Template task={task} />
</Modal>
);
}
export default FC;
export const AuditProgress = ({ task }) => {
const auditStatus = useMemo(() => {
return ['分发', '提交', '审核'];
}, [])
const backgroundColor = '#F5F5F5';
const hightBackgroundColor = '#2E90F1';
const stateCompleted = (state) => {
const index = task?.contents?.filter(item => item.status==='1').findIndex(item => item.title.indexOf(state)!==-1);
return (index !== -1);
}
const stateContent = (state) => {
return task?.contents?.filter(item => item.status==='1').find(item => item.title.indexOf(state)!==-1);
}
const leftLineColor = (state) => {
const index = task?.contents?.filter(item => item.status==='1').findIndex(item => item.title.indexOf(state)!==-1);
return (index !== -1) ? hightBackgroundColor : backgroundColor;
}
const rightLineColor = (state) => {
const index = auditStatus.findIndex(item => item === state);
if (index === auditStatus.length -1) {
return hightBackgroundColor;
} else {
return leftLineColor(auditStatus[index+1]);
}
}
return (
<React.Fragment>
<Divider>审批进度</Divider>
<Row>
{
auditStatus.map((item, index) => {
return (
<Col span={24/auditStatus.length} key={index} style={{ height: 6 }} >
{
<Row align='middle'>
<Col span={11} style={{ backgroundColor: leftLineColor(item), height: 4, visibility: (index===0?'hidden':'visible') }}></Col>
<Col span={2} className='flex' style={{ justifyContent: 'center' }} >
<div style={{ backgroundColor: leftLineColor(item), width: 16, height: 16, borderRadius: '50%' }}></div>
</Col>
<Col span={11} style={{ backgroundColor: rightLineColor(item), height: 4, visibility: (index===auditStatus.length-1?'hidden':'visible') }}></Col>
</Row>
}
</Col>
)
})
}
</Row>
<Row className='pt-3'>
{
auditStatus.map((item, index) => {
const currentContent = stateContent(item);
return (
<Col span={24/auditStatus.length} key={index} >
<div style={{ textAlign: 'center' }}>
<div>{item}</div>
{
stateCompleted(item) && <React.Fragment>
<div>{`审批人: ${currentContent.dealUser}`}</div>
<div>{`审批状态: ${currentContent.status==='1'?'通过':'不通过'}`}</div>
<div>{`审批意见: ${currentContent.value}`}</div>
<div>{`审批日期: ${currentContent.processDate}`}</div>
</React.Fragment>
}
</div>
</Col>
)
})
}
</Row>
</React.Fragment>
)
}
export const Basic = ({ task }) => {
return (
<React.Fragment>
<Divider>基本信息</Divider>
<Descriptions title="" labelStyle={{ fontWeight: 'bold' }}>
<Descriptions.Item label="任务ID">{task?.id}</Descriptions.Item>
<Descriptions.Item label="任务标题">{task?.title}</Descriptions.Item>
<Descriptions.Item label="任务说明">{task?.comment}</Descriptions.Item>
<Descriptions.Item label="任务状态">{status[task?.status]}</Descriptions.Item>
<Descriptions.Item label="所属部门">{task?.department?.groupDisplayName??task?.department?.groupName}</Descriptions.Item>
<Descriptions.Item label="资产专员">{task?.specialist?.userDisplayName??task?.specialist?.userName}</Descriptions.Item>
<Descriptions.Item label="更新日期">{task?.updateTime}</Descriptions.Item>
<Descriptions.Item label="更新人">{task?.updateUser?.userDisplayName??task?.updateUser?.userName}</Descriptions.Item>
<Descriptions.Item label="创建日期">{task?.createTime}</Descriptions.Item>
<Descriptions.Item label="创建人">{task?.createUser?.userDisplayName??task?.createUser?.userName}</Descriptions.Item>
</Descriptions>
</React.Fragment>
)
}
export const Process = ({ task }) => {
const columns = useMemo(() => {
return [
{
title: '处理人',
dataIndex: 'dealUser',
ellipsis: true,
},
{
title: '处理时间',
dataIndex: 'processDate',
ellipsis: true,
},
{
title: '状态',
dataIndex: 'status',
ellipsis: true,
render: (state, _) => (state==='1')?'通过':'驳回'
},
{
title: '答复内容',
dataIndex: 'content',
ellipsis: true,
render: (_, record) => `${record.title}意见: ${record.value}`
}
]
}, [])
return (
<React.Fragment>
<Divider>审批过程</Divider>
{
task?.contents?.length === 0 ? <span>暂无审批过程</span> : <Table
width={980}
maxHeight='300px'
columns={columns}
dataSource={task?.contents||[]}
pagination={false}
/>
}
</React.Fragment>
)
}
export const Template = ({ task }) => {
const downloadTemplate = () => {
const env = LocalStorage.get('assetsEnv');
window.open(`/api/dataassetmanager/dataAssetCheckApi/getFileById?env=${env}&fileId=${task?.dataAssetExcel?.fileId}`);
}
return (
<React.Fragment>
<Divider>任务内容</Divider>
{
task?.dataAssetExcel ? <Button className='mr-2' icon={<DownloadOutlined />} onClick={ downloadTemplate }>
{`${task?.dataAssetExcel?.fileName}下载`}
</Button> : <span>暂无任务内容</span>
}
</React.Fragment>
)
}
\ No newline at end of file
import React, { useEffect, useState } from 'react';
import { Modal, Form, Input, Select } from 'antd';
import { dispatch } from '../../../model';
const GroupsSelect = ({value = {}, data, onChange}) => {
const handleChange = (newValue) => {
if (newValue) {
const findGroup = data?.find(group => group.id === newValue);
if (findGroup) {
onChange?.({
groupId: findGroup.id,
groupName: findGroup.name,
groupDisplayName: findGroup.desc,
});
}
} else {
onChange?.(null);
}
}
return (
<Select
value={value?.groupId}
onChange={handleChange}
allowClear
>
{
data?.map((group, index) => <Select.Option key={index} value={group.id}>{group.desc??group.name}</Select.Option>)
}
</Select>
)
}
const UsersSelect = ({value = {}, data, onChange}) => {
const handleChange = (newValue) => {
if (newValue) {
const findUser = data?.find(user => user.id === newValue);
if (findUser) {
onChange?.({
userId: findUser.id,
userName: findUser.name,
userDisplayName: findUser.dname,
});
}
} else {
onChange?.(null);
}
}
return (
<Select
value={value?.userId}
onChange={handleChange}
allowClear
>
{
data?.map((user, index) => <Select.Option key={index} value={user.id}>{user.dname??user.name}</Select.Option>)
}
</Select>
)
}
const FC = (props) => {
const { visible, onCancel, task, action = 'add' } = props;
const [form] = Form.useForm();
const [confirmLoading, setConfirmLoading] = useState(false);
const [groups, setGroups] = useState();
const [users, setUsers] = useState();
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 4 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 20 },
},
};
useEffect(() => {
if (visible) {
getGroups();
if (action !== 'add') {
form.setFieldsValue(task);
if (task.department?.groupId) {
getUsers(task.department?.groupId);
}
}
}
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [visible])
const getGroups = () => {
dispatch({
type: 'user.userGroups',
callback: (data) => {
setGroups(data);
}
})
}
const getUsers = (gid) => {
dispatch({
type: 'user.getGroupUsers',
payload: {
id: gid
},
callback: (data) => {
setUsers(data);
}
})
}
const handleOk = async () => {
try {
const values = await form.validateFields();
let newTask = {};
if (action === 'add') {
newTask = {...values};
} else {
newTask = {...task, ...values};
}
setConfirmLoading(true);
dispatch({
type: 'assetmanage.saveTask',
payload: {
data: newTask
},
callback: data => {
setConfirmLoading(false);
reset();
onCancel && onCancel(true);
},
error: () => {
setConfirmLoading(false);
}
})
} catch (errInfo) {
}
}
const onValuesChange = (changedValues, allValues) => {
if (changedValues.hasOwnProperty('department') ) {
if (changedValues.department?.groupId) {
getUsers(changedValues.department.groupId);
} else {
setUsers();
}
}
}
const reset = () => {
setConfirmLoading(false);
form.resetFields();
setGroups();
setUsers();
}
return (
<Modal
visible={visible}
title={action==='add'?'新增任务':'编辑任务'}
width={1000}
confirmLoading={confirmLoading}
onCancel={() => {
reset();
onCancel && onCancel();
}}
onOk={handleOk}
>
<Form
{...formItemLayout}
form={form}
onValuesChange={onValuesChange}
>
<Form.Item
label="盘点任务标题"
name="title"
rules={[{ required: true, message: '请输入标题名称' }]}
>
<Input />
</Form.Item>
<Form.Item
label="盘点任务说明"
name="comment"
>
<Input />
</Form.Item>
<Form.Item
label="所属部门"
name="department"
rules={[{ required: true, message: '请选择所属部门' }]}
>
<GroupsSelect data={groups} />
</Form.Item>
<Form.Item
label="资产专员"
name="specialist"
rules={[{ required: true, message: '请选择资产专员' }]}
>
<UsersSelect data={users} />
</Form.Item>
</Form>
</Modal>
);
}
export default FC;
\ No newline at end of file
import { useState, useEffect, useMemo } from 'react';
import { Button, Table, Space, Pagination } from 'antd';
import TaskDetail from './TaskDetail';
import UpdateTask from './UpdateTask';
import AuditTask from './AuditTask';
import CheckTask from './CheckTask';
import { dispatch } from '../../../model';
import { paginate } from '../../../util';
export const status = {
'tobeDistributed': '待分发',
'checking': '盘点中',
'tobeReviewed': '待审核',
'finished': '已完成',
'1': '通过',
'-1': '未通过',
}
const FC = (props) => {
const [loading, setLoading] = useState(false);
const [tasks, setTasks] = useState();
const [ pagination, setPagination ] = useState({ pageNum: 1, pageSize: 20 });
const { pageNum, pageSize } = pagination;
const [taskDetailParams, setTaskDetailParams] = useState({
visible: false,
task: undefined,
});
const [updateTaskParams, setUpdateTaskParams] = useState({
visible: false,
task: undefined,
action: 'add',
});
const [auditTaskParams, setAuditTaskParams] = useState({
visible: false,
task: undefined,
action: undefined,
});
const [checkTaskParams, setCheckTaskParams] = useState({
visible: false,
task: undefined,
});
const onActionClick = (task, action) => {
if (action === '详情') {
setTaskDetailParams({...taskDetailParams, task, visible: true});
} else if (action === '修改') {
setUpdateTaskParams({...updateTaskParams, task, visible: true, action: 'edit'});
} else if (action === '盘点') {
setCheckTaskParams({...checkTaskParams, task, visible: true});
} else {
setAuditTaskParams({...auditTaskParams, action, task, visible: true});
}
}
const columns = useMemo(() => {
return (
[
{
title: '任务ID',
dataIndex: 'id',
ellipsis: true,
},
{
title: '任务标题',
dataIndex: 'title',
ellipsis: true,
},
{
title: '任务说明',
dataIndex: 'comment',
ellipsis: true,
},
{
title: '任务状态',
dataIndex: 'status',
ellipsis: true,
render: (state, _, __) => status[state],
},
{
title: '所属部门',
dataIndex: 'department',
ellipsis: true,
render: (department, _, __) => department?.groupDisplayName??department?.groupName,
},
{
title: '资产专员',
dataIndex: 'specialist',
ellipsis: true,
render: (user, _, __) => user?.userDisplayName??user?.userName,
},
{
title: '更新日期',
dataIndex: 'updateTime',
ellipsis: true,
},
{
title: '更新人',
dataIndex: 'updateUser',
ellipsis: true,
render: (user, _, __) => user?.userDisplayName??user?.userName,
},
{
title: '创建日期',
dataIndex: 'createTime',
ellipsis: true,
},
{
title: '创建人',
dataIndex: 'createUser',
ellipsis: true,
render: (user, _, __) => user?.userDisplayName??user?.userName,
},
{
title: '操作',
key: 'action',
width: 120,
render: (_,record) => (
<Space size='small'>
{
record.actionList?.map((action, index) => <a key={index} onClick={() => {onActionClick(record, action.title)}}>{action.title}</a>)
}
</Space>
)
}
]
)
}, [onActionClick])
const _tasks = useMemo(() => {
return paginate(tasks, pagination.pageNum, paginate.pageSize
)
}, [pagination, tasks])
useEffect(() => {
getTasks();
}, [])
const getTasks = () => {
setLoading(true);
dispatch({
type: 'assetmanage.listTasks',
callback: (data) => {
setLoading(false);
setTasks(data);
},
error: () => {
setLoading(false);
}
})
}
const onAddClick = () => {
setUpdateTaskParams({...updateTaskParams, action: 'add', visible: true});
}
const onTaskDetailCancel = () => {
setTaskDetailParams({...taskDetailParams, visible: false});
}
const onUpdateTaskCancel = (refresh = false) => {
setUpdateTaskParams({...updateTaskParams, visible: false});
refresh && getTasks();
}
const onAuditTaskCancel = (refresh = false) => {
setAuditTaskParams({...auditTaskParams, visible: false});
refresh && getTasks();
}
const onCheckTaskCancel = (refresh = false) => {
setCheckTaskParams({...checkTaskParams, visible: false});
refresh && getTasks();
}
return (
<div style={{ backgroundColor: '#fff', height: '100%' }}>
<div
className='d-flex p-3'
style={{
borderBottom: '1px solid #EFEFEF',
justifyContent: 'space-between',
alignItems: 'center'
}}
>
<Button onClick={onAddClick}>新增</Button>
</div>
<div className='p-3'>
<Table
rowKey='id'
loading={loading}
columns={columns}
dataSource={_tasks||[]}
pagination={false}
scroll={{ y: (_tasks||[]).length===0?null:'calc(100vh - 121px - 57px - 24px - 38px - 44px)' }}
/>
{
(tasks||[]).length>0 && <Pagination
className="text-center mt-3"
showSizeChanger
showQuickJumper
onChange={(_pageNum, _pageSize) => {
setPagination({ pageNum: _pageNum, pageSize: _pageSize || 20 });
}}
onShowSizeChange={(_pageNum, _pageSize) => {
setPagination({ pageNum: 1, pageSize: _pageSize });
}}
current={pageNum}
pageSize={pageSize}
defaultCurrent={1}
total={(tasks||[]).length}
pageSizeOptions={[10,20,50]}
showTotal={total => `共 ${(tasks||[]).length} 条`}
/>
}
</div>
<TaskDetail
visible={taskDetailParams.visible}
task={taskDetailParams.task}
onCancel={onTaskDetailCancel}
/>
<UpdateTask
visible={updateTaskParams.visible}
task={updateTaskParams.task}
action={updateTaskParams.action}
onCancel={onUpdateTaskCancel}
/>
<AuditTask
visible={auditTaskParams.visible}
task={auditTaskParams.task}
action={auditTaskParams.action}
onCancel={onAuditTaskCancel}
/>
<CheckTask
visible={checkTaskParams.visible}
task={checkTaskParams.task}
onCancel={onCheckTaskCancel}
/>
</div>
)
}
export default FC;
\ No newline at end of file
......@@ -12,6 +12,8 @@ import DataService from './Pdata';
import AssetDraft from './AssetDraft';
import AssetManage from './AssetManage';
import AssetBrowse from './AssetBrowse';
import AssetTask from './AssetTask';
import AssetEvaluate from "./AssetEvaluate";
class Manage extends Component {
......@@ -33,6 +35,8 @@ class Manage extends Component {
<Route path={`${match.path}/asset-draft`} component={AssetDraft} />
<Route path={`${match.path}/asset-manage`} component={AssetManage} />
<Route path={`${match.path}/asset-browse`} component={AssetBrowse} />
<Route path={`${match.path}/asset-task`} component={AssetTask} />
<Route path={`${match.path}/asset-evaluate`} component={AssetEvaluate} />
</Switch>
) : (
<GetSession {...this.props} />
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment