Browse Source

结构物基础信息管理 10%

master
wenlele 2 years ago
parent
commit
c42951f567
  1. 2
      web/client/src/index.js
  2. 3
      web/client/src/layout/components/header/index.js
  3. 4
      web/client/src/layout/containers/layout/index.js
  4. 2
      web/client/src/sections/auth/containers/login.js
  5. 146
      web/client/src/sections/projectRegime/components/projectAddModel.js
  6. 160
      web/client/src/sections/projectRegime/components/report-modal.js
  7. 5
      web/client/src/sections/projectRegime/containers/index.js
  8. 229
      web/client/src/sections/projectRegime/containers/information.js
  9. 6
      web/client/src/sections/projectRegime/containers/qrCode.js
  10. 2
      web/client/src/sections/projectRegime/index.js
  11. 13
      web/client/src/sections/projectRegime/nav-item.js
  12. 35
      web/client/src/sections/projectRegime/routes.js

2
web/client/src/index.js

@ -4,4 +4,4 @@ import React from 'react';
import { render } from 'react-dom';
import App from './app';
render((<App projectName="中鼎国际工程项目指挥调度系统" />), document.getElementById('App'));
render((<App projectName="巡检WEB" />), document.getElementById('App'));

3
web/client/src/layout/components/header/index.js

@ -37,7 +37,8 @@ const Header = props => {
{collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
</span>
<div className={styles['header-title']} style={{}}>
<img src='/assets/images/logo.png' style={{ margin: '0 12px 4px 12px', height: 42, borderRadius: 4 }} />中鼎国际工程项目指挥调度系统
{/* <img src='/assets/images/logo.png' style={{ margin: '0 12px 4px 12px', height: 42, borderRadius: 4 }} /> */}
巡检WEB
</div>
</div>
<div id="nav" className={styles['header-nav']}>

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

@ -44,9 +44,7 @@ const LayoutContainer = props => {
useEffect(() => {
resize_(collapsed)
scrollbar = new PerfectScrollbar('#page-content', { suppressScrollX: true });
if (!sites.length) {
dispatch(getUserSiteList());//获取工地列表
}
}, [])
useEffect(() => {

2
web/client/src/sections/auth/containers/login.js

@ -34,7 +34,7 @@ const Login = props => {
useEffect(() => {
if (user && user.authorized) {
dispatch(push('/homePage'));
dispatch(push('/projectRegime/information'));
}
}, [user])

146
web/client/src/sections/projectRegime/components/projectAddModel.js

@ -1,5 +1,6 @@
import React, { useState } from 'react';
import { Button, Form, Input, Modal, Select, DatePicker } from 'antd';
const { TextArea } = Input;
import { connect } from 'react-redux';
import Uploads from '$components/Uploads';
import { useEffect } from 'react';
@ -8,8 +9,6 @@ import moment from 'moment';
const ProjectAddModel = ({ dispatch, actions, user, modelData, close, success, firmList }) => {
const { projectRegime } = actions
const { RangePicker } = DatePicker;
const [form] = Form.useForm();
useEffect(() => {
@ -17,18 +16,6 @@ const ProjectAddModel = ({ dispatch, actions, user, modelData, close, success, f
}, [])
const validSjh = (rule, value, callback) => {
const sjh = /^1(3|4|5|6|7|8|9)\d{9}$/;//手机号
if (value) {
let valid = sjh.test(value);
if (!valid) {
return callback([new Error("手机号填写错误")]);
}
callback();
}
return callback([new Error("请输入手机号")]);
};
return (
<Modal
@ -71,8 +58,34 @@ const ProjectAddModel = ({ dispatch, actions, user, modelData, close, success, f
})
}}
>
<Form.Item label='结构物名称' name="name" style={{}}
initialValue={modelData?.name}
rules={[{ required: true, message: '请输入结构物名称' },]}>
<Input placeholder="请输入结构物名称" allowClear />
</Form.Item>
<Form.Item label='结构物类型' name="type" style={{}}
initialValue={modelData?.type || '桥梁'}
rules={[{ required: true, message: '请选择结构物类型' },]}>
<Select allowClear
options={[
{ value: '桥梁', label: '桥梁' },
{ value: '隧道', label: '隧道' },
{ value: '管廊', label: '管廊' }]} />
</Form.Item>
<div style={{}}>
<Form.Item label="所在地区:" labelCol={{ span: 11 }} labelAlign='right' name="longitude" initialValue={modelData?.longitude} style={{ display: 'inline-block', width: 'calc(60% - 30px)', }} rules={[{ required: true, message: '请输入横坐标', },]} >
<Input placeholder="经度支持数字" />
</Form.Item>
~
<Form.Item name="latitude" initialValue={modelData?.latitude} style={{ display: 'inline-block', width: 'calc(40% + 15px)', }} rules={[{ required: true, message: '请输入纵坐标', },]} >
<Input placeholder="维度支持数字" />
</Form.Item>
</div>
<Form.Item name='描述' label="Introduction">
<TextArea />
</Form.Item>
<Form.Item
label="项目图片"
label="结构物图片"
name='img'
help={<div style={{ fontSize: 12 }}>说明请上传pngjpg格式图片图片大小不超过5M建议图片宽高比16:9</div>}
rules={[{ required: true, message: '请上传图片', },]}
@ -94,105 +107,6 @@ const ProjectAddModel = ({ dispatch, actions, user, modelData, close, success, f
})}
/>
</Form.Item>
<Form.Item label='所属公司' name="companyId" style={{}}
initialValue={modelData?.companyId}
rules={[{ required: true, message: '请输入所属公司', },]}>
<Select allowClear placeholder="请选择" options={firmList} />
</Form.Item>
<Form.Item label='项目名称' name="name" style={{}}
initialValue={modelData?.name}
rules={[{ required: true, message: '请输入项目名称,不超过20个字', max: 20 },]}>
<Input placeholder="请输入项目名称,不超过20个字" allowClear />
</Form.Item>
<Form.Item label='项目类型' name="type" style={{}}
initialValue={modelData?.type}
rules={[{ required: true, message: '请选择项目类型' },]}>
<Select allowClear placeholder="全部类型"
options={[
{ value: '房建', label: '房建' },
{ value: '市政', label: '市政' },
{ value: '道路桥梁', label: '道路桥梁' },
{ value: '其他', label: '其他' }]} />
</Form.Item>
<Form.Item
label='建设规模'
name="scale"
style={{}}
initialValue={modelData?.scale?.scale}
rules={[{ required: true, message: '请输入建设规模' },]}
>
<Input placeholder="请输入建设规模" allowClear
addonAfter={<Form.Item name="scaleSuffix" initialValue={modelData?.scale?.scaleSuffix || 'm²'} noStyle>
<Select defaultValue={modelData?.scale?.scaleSuffix || 'm²'}>
<Option value='m²'>m²</Option>
<Option value='km²'>km²</Option>
</Select>
</Form.Item>
} />
</Form.Item>
<Form.Item
label='合同(投资)金额'
name="amount"
style={{}}
initialValue={modelData?.amount?.amount}
rules={[{ required: true, message: '请输入合同(投资)金额' },]}
>
<Input placeholder="请输入合同(投资)金额" allowClear
addonAfter={<Form.Item name="amountSuffix" initialValue={modelData?.amount?.amountSuffix || '元'} noStyle>
<Select >
<Option value='元'></Option>
<Option value="万"></Option>
<Option value="亿">亿</Option>
</Select>
</Form.Item>
} />
</Form.Item>
<Form.Item
label='起止时间'
name="time"
style={{}}
rules={[{ required: true, message: '请选择起止时间' },]}
initialValue={(modelData.startTime && modelData.endTime) ? [moment(modelData.startTime, "YYYY-MM-DD HH:mm:ss"), moment(modelData.endTime, "YYYY-MM-DD HH:mm:ss")] : []}
>
<RangePicker showTime format="YYYY-MM-DD HH:mm:ss" defaultValu={(modelData.startTime && modelData.endTime) ? [moment(modelData.startTime, "YYYYY-MM-DD HH:mm:ss"), moment(modelData.endTime, "YYYY-MM-DD HH:mm:ss")] : []} />
</Form.Item>
<Form.Item label='项目地址:' name="address" style={{}} initialValue={modelData?.address}>
<Input placeholder="请输入项目地址描述,不超过20个字" maxLength={20} allowClear />
</Form.Item>
<div style={{}}>
<Form.Item label="项目坐标:" labelCol={{ span: 11 }} labelAlign='right' name="longitude" initialValue={modelData?.longitude} style={{ display: 'inline-block', width: 'calc(60% - 30px)', }} rules={[{ required: true, message: '请输入横坐标', },]} >
<Input placeholder="横坐标" />
</Form.Item>
~
<Form.Item name="latitude" initialValue={modelData?.latitude} style={{ display: 'inline-block', width: 'calc(40% + 15px)', }} rules={[{ required: true, message: '请输入纵坐标', },]} >
<Input placeholder="纵坐标" />
</Form.Item>
</div>
{/* <Form.Item label="" name="coordinate" style={{}} >
</Form.Item> */}
<Form.Item label='负责人:' name="peopleInCharge" initialValue={modelData?.peopleInCharge} style={{}}
rules={[{ required: true, message: '请输入负责人姓名', },]}>
<Input placeholder="请输入负责人姓名" allowClear />
</Form.Item>
<Form.Item label='联系方式:' name="tel" initialValue={modelData?.tel} style={{}}
rules={[{ required: true, message: '', }, { validator: validSjh }]}>
<Input placeholder="请输入负责人联系方式" allowClear />
</Form.Item>
<Form.Item label='建筑单位' name="builder" initialValue={modelData?.builder} style={{}}>
<Input placeholder="请输入,不超过20个字" maxLength={20} allowClear />
</Form.Item>
<Form.Item label='监理单位' name="supervisor" initialValue={modelData?.supervisor} style={{}}>
<Input placeholder="请输入,不超过20个字" maxLength={20} allowClear />
</Form.Item>
<Form.Item label='施工单位' name="contractor" initialValue={modelData?.contractor} style={{}}>
<Input placeholder="请输入,不超过20个字" maxLength={20} allowClear />
</Form.Item>
<Form.Item label='设计单位' name="designer" initialValue={modelData?.designer} style={{}}>
<Input placeholder="请输入,不超过20个字" maxLength={20} allowClear />
</Form.Item>
</Form>
</Modal>
@ -201,12 +115,10 @@ const ProjectAddModel = ({ dispatch, actions, user, modelData, close, success, f
function mapStateToProps (state) {
const { auth, global, members } = state;
const { auth, global } = state;
return {
loading: members.isRequesting,
user: auth.user,
actions: global.actions,
members: members.data
};
}

160
web/client/src/sections/projectRegime/components/report-modal.js

@ -1,160 +0,0 @@
import React, { useState } from 'react';
import { Button, Form, Input } from 'antd';
import {
ModalForm,
ProFormText,
ProFormSelect,
ProFormTextArea
} from '@ant-design/pro-form';
import Uploads from '$components/Uploads';
import { EMERGENCY_DEGREE } from '../contants/risk-coordinate';
import { useEffect } from 'react';
import moment from 'moment';
const FormItem = Form.Item;
export default (props) => {
const { title, triggerRender, editData, onFinish, isView } = props;
const [editUrlMap, setEditUrlMap] = useState({ type1: [], type2: [] });
let formItemLayout = { labelCol: { span: 4 }, wrapperCol: { span: 14 } };
const initialValues = editData ? {
...editData,
} : null
useEffect(() => {
if (editData && editData.problemReportFiles) {
let fileList = (editData?.problemReportFiles || []).map(item => {
return {
storageUrl: item.filePath,
}
});
setEditUrlMap({ type1: fileList, type2: [] })
}
}, [editData])
// useEffect(() => {
// if (key == 'view') {
// formItemLayout.modalProps.footer = { null };
// }
// }, [key])
const vsjunct = (params, type) => {
let temp = editUrlMap
if (params.length) {
let appendix = []
for (let p of params) {
appendix.push({
fName: p.name,
size: p.size,
fileSize: p.size,
storageUrl: p.storageUrl,//必须有storageUrl
})
}
temp[type] = appendix
setEditUrlMap(temp)
} else {
temp[type] = []
setEditUrlMap(temp)
}
}
return (
<ModalForm
title={title || ''}
initialValues={initialValues}
trigger={
triggerRender ? triggerRender : <Button type="primary" >
{title || ''}
</Button>
}
layout="horizontal"
{...formItemLayout}
modalProps={{
destroyOnClose: true,
onCancel: () => { },
}}
onFinish={async (values) => {
if (isView) {
return true;
}
onFinish && await onFinish(values, editData)
return true;
}}
>
<ProFormText
width="md"
name="title"
label="上报问题标题"
disabled={isView ? true : false}
rules={[{
required: true, message: '上报问题标题不能为空',
}, {
max: 26, message: '标题长度不能大于26个字'
}, {
whitespace: true, message: '请勿输入空格'
}]}
placeholder="请输入上报问题标题,不超过26个字"
/>
<ProFormTextArea
width="md"
name="describe"
label="问题描述"
disabled={isView ? true : false}
placeholder="请输入问题描述,不超过200个字"
rules={[{
required: true, message: '上报问题标题不能为空',
}, {
max: 200, message: '描述长度不能大于200个字'
}, {
whitespace: true, message: '请勿输入空格'
}]}
/>
<ProFormSelect
width="md"
rules={[{ required: true, message: '请选择紧急程度!' }]}
options={EMERGENCY_DEGREE.map(s => s)}
name="urgencyDegree"
label="紧急程度"
disabled={isView ? true : false}
/>
<FormItem name='accessory' label='问题附件'>
<Uploads
disabled={isView ? true : false}
className='upload'
listType='text'
uploadType='project'
maxFilesNum={10}
maxFileSize={20}
isQiniu={true}
onChange={(params) => vsjunct(params, 'type1')}
fileTypes={["jpeg", "png", "jpg", "pdf", "doc", "docx"]}
value={editUrlMap.type1}
defaultValue={editUrlMap.type1}
/>
</FormItem>
{
isView ? <FormItem label='上报人员'>
<Input value={editData.user.userName} disabled={true} />
</FormItem> : ''
}
{
isView ? <FormItem label='上报时间'>
<Input value={moment(editData.reportTime).format('YYYY-MM-DD HH:mm:ss')} disabled={true} />
</FormItem> : ''
}
{/* <FormItem >
<div style={{ color: '#999' }}>说明附件格式为pngjpegjpgdocxdocpdf大小不超过20MB</div>
</FormItem> */}
</ModalForm>
);
};

5
web/client/src/sections/projectRegime/containers/index.js

@ -1,7 +1,8 @@
'use strict';
import ProjectSituation from './projectSituation'
import QrCode from './qrCode'
import Information from './information'
export { ProjectSituation };
export { QrCode, Information };

229
web/client/src/sections/projectRegime/containers/information.js

@ -0,0 +1,229 @@
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { Spin, Card, Form, Input, Select, Button, Table, Modal, Popconfirm, Tooltip } from 'antd';
import moment from "moment";
import '../style.less';
import ProjectAddModel from '../components/projectAddModel'
import { Model } from 'echarts';
const Information = (props) => {
const { dispatch, actions, user, loading } = props
const { projectRegime } = actions
const [firmList, setFirmList] = useState([])
const [tableList, settableList] = useState([])
const [addModel, setAddModel] = useState(false)
const [modelData, setModelData] = useState({})
const [query, setQuery] = useState({ limit: 10, page: 0 })
const [limits, setLimits] = useState()
const [search, setSearch] = useState({})
const [isPicture, setIsPicture] = useState(false)
const [pictureUrl, setPictureUrl] = useState()
const [companyID, setCompanyId] = useState('')
useEffect(() => {
dispatch(projectRegime.getFirmList()).then(res => {
if (res.success) {
let data = res.payload.data?.map(r => ({ value: r.id, label: r.name }))
if (user?.role?.type == 1) {
setFirmList(data)
projectList(query)
} else if (user?.role?.type == 2) {
let dataId = user?.userDepartments[0]?.department?.company?.id
setFirmList(data?.filter(v => v.value == dataId))
projectList({ ...query, companyId: dataId })
setSearch({ companyId: dataId })
setCompanyId(dataId)
}
}
})
}, [])
const projectList = (obj) => {
const { limit, page, companyId, name, type } = obj
dispatch(projectRegime.getProjectList({ limit, page, companyId, name, type })).then(res => {
// console.log(res)
if (res.success) {
settableList(res.payload.data?.rows?.map(v => ({ ...v, key: v.id })))
setLimits(res.payload.data?.count)
}
})
}
const columns = [
{
title: '序号',
dataIndex: 'index',
key: 'index',
render: (text, record, index) => index + 1
}, {
title: '结构物名称',
dataIndex: 'name',
key: 'name',
}, {
title: '所在地区',
dataIndex: 'type',
key: 'type',
}, {
title: '结构物类型',
dataIndex: 'scale',
key: 'scale',
render: (text, record, index) => record?.scale?.scale + record?.scale?.scaleSuffix
}, {
title: '描述',
dataIndex: 'amount',
key: 'amount',
render: (text, record, index) => record?.amount?.amount + record?.amount?.amountSuffix
}, {
title: '操作',
dataIndex: 'operation',
key: 'operation',
render: (text, record, index) => {
return (
<div style={{ width: 180 }}>
{(user?.isSuper || user.id == record.userId) ?
<>
<Button type="link" onClick={() => {
setAddModel(true)
setModelData(record)
}}
>编辑</Button>
<Popconfirm
title='确认删除工程信息?'
position='topLeft'
onConfirm={() => {
dispatch(projectRegime.delProject(record.id)).then(res => {
if (res.success) {
if ((limits > 11 && tableList.length > 1) || limits < 11) {
projectList({ ...query, ...search })
} else {
projectList({ limit: query?.limit, page: query?.page - 1, ...search })
setQuery({ limit: query?.limit, page: query?.page - 1 });
}
}
})
}}
>
<Button type="link" danger >删除</Button>
</Popconfirm>
</>
: <>
<Tooltip title='请联系工程创建者'>
<Button type="link" >编辑</Button>
</Tooltip>
<Tooltip title='请联系工程创建者'>
<Button type="link" danger >删除</Button>
</Tooltip>
</>
}
</div>
)
}
}
]
return (
<>
<div style={{ display: 'flex', justifyContent: 'space-between', padding: '0 10px' }}>
<Form
style={{ display: 'flex', }}
onFinish={r => {
projectList({ limit: 10, page: 0, ...r, companyId: companyID || r?.companyId })
setQuery({ limit: 10, page: 0 });
setSearch(r)
}}
>
<Form.Item
label='项目名称'
name="name"
style={{ marginRight: 16, minWidth: 180 }}
>
<Input placeholder="请输入项目名称" allowClear />
</Form.Item>
<Form.Item wrapperCol={{}}>
<Button type="primary" htmlType="submit">
搜索
</Button>
</Form.Item>
</Form>
<div style={{ display: 'flex' }}>
<Button type="primary" onClick={() => {
console.log(45513);
setAddModel(true)
}}>新建结构物</Button>
<Button type="primary" style={{ marginLeft: 20 }} onClick={() => {
console.log(45513);
setAddModel(true)
}}>一键生成二维码</Button>
</div>
</div>
<Table
columns={columns}
dataSource={tableList}
pagination={{
current: query.page + 1,
total: limits,
showSizeChanger: true,
showQuickJumper: true,
pageSizeOptions: [10, 20, 50],
showTotal: (total) => {
return <span style={{ fontSize: 15 }}>{`${Math.ceil(total / query?.limit)}页,${total}`}</span>
},
onChange: (page, pageSize) => {
setQuery({ limit: pageSize, page: page - 1 });
projectList({ limit: pageSize, page: page - 1, ...search, companyId: companyID || search?.companyId })
}
}}
/>
{
<Modal
// title={ }
width={570}
open={isPicture}
onOk={() => { }}
footer={null}
onCancel={() => {
setIsPicture(false)
setPictureUrl('')
}}
>
{pictureUrl && pictureUrl[0] ? <img src={`/_file-server/${pictureUrl}`} width={500} /> : "暂无图片"}
</Modal>
}
{
addModel ?
<ProjectAddModel
firmList={firmList}
modelData={modelData}
close={() => {
setAddModel(false)
setModelData({})
}}
success={() => {
setAddModel(false)
setModelData({})
setQuery({ limit: 10, page: 0 });
projectList({ limit: 10, page: 0, ...search, companyId: companyID || search?.companyId })
}}
/> : ""
}
</>
)
}
function mapStateToProps (state) {
const { auth, global } = state;
return {
user: auth.user,
actions: global.actions,
};
}
export default connect(mapStateToProps)(Information);

6
web/client/src/sections/projectRegime/containers/projectSituation.js → web/client/src/sections/projectRegime/containers/qrCode.js

@ -6,7 +6,7 @@ import '../style.less';
import ProjectAddModel from '../components/projectAddModel'
import { Model } from 'echarts';
const ProjectSituation = (props) => {
const QrCode = (props) => {
const { dispatch, actions, user, loading } = props
const { projectRegime } = actions
const [firmList, setFirmList] = useState([])
@ -111,7 +111,7 @@ const ProjectSituation = (props) => {
projectList({ limit: query?.limit, page: query?.page - 1, ...search })
setQuery({ limit: query?.limit, page: query?.page - 1 });
}
dispatch(getUserSiteList());//获取工地列表
}
})
}}
@ -275,4 +275,4 @@ function mapStateToProps (state) {
};
}
export default connect(mapStateToProps)(ProjectSituation);
export default connect(mapStateToProps)(QrCode);

2
web/client/src/sections/projectRegime/index.js

@ -7,7 +7,7 @@ import { getNavItem } from './nav-item';
export default {
key: 'projectRegime',
name: '项目管理',
name: '结构物管理',
reducers: reducers,
routes: routes,
actions: actions,

13
web/client/src/sections/projectRegime/nav-item.js

@ -6,12 +6,13 @@ import { SettingOutlined } from '@ant-design/icons';
const SubMenu = Menu.SubMenu;
export function getNavItem (user, dispatch) {
const { role, isSuper } = user
return isSuper || (role?.type === 1 || role?.type === 2) ? (
<SubMenu key="projectRegime" icon={<SettingOutlined />} title={'项目管理'}>
<Menu.Item key="projectSurvey">
<Link to="/projectRegime/projectSituation">工程概况</Link>
return <SubMenu key="projectRegime" icon={<SettingOutlined />} title={'结构物管理'}>
<Menu.Item key="information">
<Link to="/projectRegime/information">结构物基础信息管理</Link>
</Menu.Item>
<Menu.Item key="qrCode">
<Link to="/projectRegime/qrCode">结构物基础信息管理</Link>
</Menu.Item>
</SubMenu>
) : null
}

35
web/client/src/sections/projectRegime/routes.js

@ -1,19 +1,24 @@
'use strict';
import { RiskReport, ProjectSituation, Coordinate, ProblemReport } from './containers';
import { Information,QrCode } from './containers';
export default [{
type: 'inner',
route: {
path: '/projectRegime',
key: 'projectRegime',
breadcrumb: '项目管理',
// 不设置 component 则面包屑禁止跳转
childRoutes: [{
path: '/projectSituation',
key: 'projectSituation',
component: ProjectSituation,
breadcrumb: '工程概况',
},
]
}
type: 'inner',
route: {
path: '/projectRegime',
key: 'projectRegime',
breadcrumb: '结构物管理',
// 不设置 component 则面包屑禁止跳转
childRoutes: [{
path: '/information',
key: 'information',
component: Information,
breadcrumb: '结构物基础信息管理',
}, {
path: '/qrCode',
key: 'qrCode',
component: QrCode,
breadcrumb: '结构物二维码管理',
},
]
}
}];
Loading…
Cancel
Save