为什么都没了

This commit is contained in:
路 范
2021-09-24 12:59:34 +08:00
parent a975b3bdee
commit a66921f0f4
962 changed files with 252792 additions and 0 deletions

View File

@@ -0,0 +1,148 @@
import React, { Component } from 'react'
import { Button, Card, Col, message as Message, Modal, Row, Spin } from 'antd'
import { AntIcon, ComponentDynamic, Container } from 'components'
import { isEqual } from 'lodash'
import { api } from 'common/api'
const parts = [
{
component: () => import('./part'),
},
]
export default class index extends Component {
state = {
loading: true,
record: null,
saving: false,
}
children = []
formData = {}
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
componentDidMount() {
// 获取详细数据
const { id } = this.props.param
if (id) {
api.houseCodeDetail({ id }).then(({ data }) => {
this.setState({
record: data,
loading: false,
})
})
} else {
this.setState({ loading: false })
}
}
async onSubmit() {
for (const child of this.children) {
try {
const data = await child.getData()
this.formData = {
...this.formData,
...data,
}
} catch (e) {
return e
}
}
//#region 提交数据
this.setState({ saving: true })
if (!this.state.record) {
// 新增
try {
const { success } = await api.houseCodeAdd(this.formData)
if (success) {
Message.success('保存成功')
Modal.confirm({
content: '已添加成功,是否继续添加?',
onOk: () => {},
onCancel: () => {
window.closeContentWindow()
},
})
}
} finally {
this.setState({ saving: false })
}
} else {
// 编辑
try {
const { success } = await api.houseCodeEdit({
id: this.state.record.id,
...this.formData,
})
if (success) {
Message.success('保存成功')
}
} finally {
this.setState({ saving: false })
}
}
//#endregion
}
render() {
const { id } = this.props
const { loading, record, saving } = this.state
return (
<div className="yo-form-page">
<Container mode="fluid" ref={this.setContainer}>
<Row gutter={16} type="flex">
<Col flex="1">
<br />
<div className="yo-adorn--house-top" />
<Card className="yo-form-page--body">
{parts.map((item, i) => (
<section key={i} id={`form-${i}-${id}`}>
{item.title && <h5>{item.title}</h5>}
<Spin
spinning={loading}
indicator={<AntIcon type="loading" />}
wrapperClassName={loading && 'h-400-min'}
>
{!loading && (
<ComponentDynamic
is={item.component}
record={record}
onRef={r => this.children.push(r)}
/>
)}
</Spin>
</section>
))}
</Card>
</Col>
</Row>
</Container>
<div className="yo-form-page--bar">
<Container mode="fluid">
<div className="yo-form-page--bar-inner">
<span></span>
<span>
<Button onClick={() => window.closeContentWindow()}>取消</Button>
<Button
loading={saving}
type="primary"
onClick={() => this.onSubmit()}
>
保存
</Button>
</span>
</div>
</Container>
</div>
</div>
)
}
}

View File

@@ -0,0 +1,744 @@
import React, { Component } from 'react'
import {
Button,
Cascader,
Form,
Input,
InputNumber,
Radio,
Spin,
Select,
Row,
Col,
Tag,
Alert,
Tooltip,
} from 'antd'
import { AntIcon, Auth } from 'components'
import { cloneDeep, isEqual } from 'lodash'
import getDictData from 'util/dic'
import { api } from 'common/api'
import { CITY } from 'util/global'
import auth from 'components/authorized/handler'
import store from 'store'
const { getState } = store
const initialValues = {
type: 1,
industry: 1,
}
const labelCol = { flex: '150px' }
const wrapperCol = { flex: '1' }
export default class form extends Component {
state = {
// 加载状态
loading: true,
codes: {
houseType: [],
houseIndustry: [],
},
options: {
areaTree: [],
projects: [],
zones: [],
},
houseCode: '',
showIndustry: false,
}
theme = getState('layout').theme
nav = getState('nav').nav
// 表单实例
form = React.createRef()
iconSelector = React.createRef()
// 初始化数据
record = {}
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
/**
* mount后回调
*/
componentDidMount() {
if (this.props.onRef) {
this.props.onRef(this)
}
this.fillData({
record: this.props.record,
})
console.log(this.nav)
}
componentWillUnmount() {
if (this.map) this.map.destroy()
}
/**
* 填充数据
* 可以在设置this.record之后对其作出数据结构调整
* [异步,必要]
* @param {*} params
*/
async fillData(params) {
this.record = cloneDeep(params.record)
//#region 从后端转换成前段所需格式
await this.initMap()
const options = { ...this.state.options }
const { data: areaTree } = await api.getAreaTree()
options.areaTree = areaTree
// 有数据
if (this.record) {
const { type, areaCode } = this.record
// areaCode需要从字符串转为数组
this.record.areaCode = [
areaCode.substr(0, 4),
areaCode.substr(0, 6),
areaCode.substr(0, 9),
areaCode,
]
switch (areaTree[0].adCode.length) {
case 6:
this.record.areaCode = this.record.areaCode.slice(1)
break
case 9:
this.record.areaCode = this.record.areaCode.slice(2)
break
default:
break
}
// 获取项目和片区列表
const data = await this.getProjectsAndZones({
areaCode: this.record.areaCode,
type,
})
Object.assign(options, data)
// 定位
const position = [this.record.lng, this.record.lat]
this.setMarker(position)
this.map.setCenter(position)
}
const codes = await getDictData('house_type', 'house_industry')
this.setState({
codes,
options,
})
this.showHouseCode()
//#endregion
this.form.current.setFieldsValue(this.record)
this.setState({
loading: false,
})
}
/**
* 获取数据
* 可以对postData进行数据结构调整
* [异步,必要]
* @returns
*/
async getData() {
const form = this.form.current
const valid = await form.validateFields()
if (valid) {
const postData = form.getFieldsValue()
//#region 从前段转换后端所需格式
if (postData.areaCode) {
postData.areaCode = postData.areaCode[postData.areaCode.length - 1]
}
//#endregion
return postData
}
}
//#region 自定义方法
initMap() {
// eslint-disable-next-line no-undef
const amap = AMap
return new Promise(resolve => {
const city = CITY
const district = new amap.DistrictSearch({
subdistrict: 0,
extensions: 'all',
level: 'city',
})
district.search(city, (status, result) => {
const bounds = result.districtList[0].boundaries,
mask = []
for (let i = 0; i < bounds.length; i += 1) {
mask.push([bounds[i]])
}
const geocoder = new amap.Geocoder({ city })
geocoder.getLocation(city, (status, result) => {
if (status !== 'complete' || !(result.geocodes && result.geocodes.length))
return
this.citycode = result.geocodes[0].addressComponent.citycode
this.map = new amap.Map(this.refs.map, {
mask,
zoom: 12,
center: result.geocodes[0].location,
mapStyle: `amap://styles/${this.theme}`,
})
this.map.on('click', e => {
this.setMarker(e.lnglat, geocoder)
})
this.map.on('complete', () => {
this.map.setFitView()
this.map.setZoom(12)
for (const path of bounds) {
new amap.Polyline({
path,
strokeColor: '#ccc',
strokeWeight: 4,
map: this.map,
})
}
resolve()
})
const auto = new amap.AutoComplete({
input: this.refs['map-search'].input,
city,
citylimit: true,
})
const placeSearch = new amap.PlaceSearch({
city,
citylimit: true,
pageSize: 1,
})
auto.on('select', ({ poi: { name: keywords, adcode } }) => {
placeSearch.search(keywords, async (status, result) => {
const {
poiList: { pois },
} = result
for (const poi of pois) {
await this.setMarker(poi.location, geocoder)
this.map.setCenter(poi.location)
}
})
})
})
})
})
}
setMarker(position, geocoder) {
const set = position => {
if (this.marker) {
this.marker.setPosition(position)
} else {
this.marker = new amap.Marker({
map: this.map,
icon: '//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png',
position,
offset: new amap.Pixel(-13, -30),
})
}
}
// eslint-disable-next-line no-undef
const amap = AMap
return new Promise((resolve, reject) => {
if (geocoder) {
geocoder.getAddress(position, (status, result) => {
if (status === 'complete' && result.regeocode) {
if (result.regeocode.addressComponent.citycode !== this.citycode) {
// 如果选到了别的城市,则中断
return
}
this.setPosition(result.regeocode.formattedAddress, position)
set(position)
resolve(position)
} else {
console.error('根据经纬度查询地址失败')
reject()
}
})
} else {
set(position)
resolve(position)
}
})
}
setPosition(address, { lng, lat }) {
this.form.current.setFieldsValue({
address,
lng,
lat,
})
}
async getProjectsAndZones({ areaCode, type } = {}, mode = ['projects', 'zones']) {
if (!areaCode || !type) {
const value = this.form.current.getFieldsValue()
areaCode = value.areaCode
type = value.type
}
if (!(areaCode && areaCode.length > 0 && areaCode[areaCode.length - 1].length === 12))
return null
try {
const result = {}
if (mode.includes('projects')) {
const { data: projects } = await api.houseProjectList({
areaCode: areaCode[areaCode.length - 1],
type,
})
result.projects = projects
}
if (mode.includes('zones')) {
const { data: zones } = await api.houseZoneList({
areaCode: areaCode[areaCode.length - 1],
})
result.zones = zones
}
return result
} catch {
return null
}
}
async getCodeNo(projectId) {
this.setState({ loading: true })
const { data: no } = await api.houseCodeNo({ projectId })
this.form.current.setFieldsValue({
no,
})
this.setState({ loading: false })
}
showHouseCode(values) {
if (this.record) {
this.setState({
houseCode: this.record.houseCode,
})
} else if (values) {
const { type, industry, areaCode, projectId, no } = values
if (
areaCode &&
areaCode.length > 0 &&
areaCode[areaCode.length - 1].length === 12 &&
projectId &&
no
) {
let houseCode = areaCode[areaCode.length - 1]
const projectSort = this.state.options.projects.find(p => p.id === projectId).sort
houseCode += projectSort.toString().padStart(3, '0')
houseCode += no.toString().padStart(3, '0')
if (type == 1) {
this.setState({ houseCode })
} else {
if (!industry) {
this.setState({ houseCode: '' })
} else {
const tag = this.state.codes.houseIndustry.find(p => p.code == industry)
.extCode.tag
houseCode += `-${tag}`
this.setState({ houseCode })
}
}
} else {
this.setState({ houseCode: '' })
}
}
}
async onValuesChange(changedValues, allValues) {
const form = this.form.current
// 房屋性质
if (changedValues.hasOwnProperty('type')) {
this.setState({
showIndustry: changedValues.type == 2,
loading: true,
})
const data = await this.getProjectsAndZones()
form.setFieldsValue({ projectId: undefined })
this.setState({
options: {
...this.state.options,
...data,
},
loading: false,
})
this.showHouseCode(form.getFieldsValue())
}
// 所属区域
else if (changedValues.hasOwnProperty('areaCode')) {
const flag = auth({ houseCode: 'getNextNoByCode' })
if (flag) {
this.setState({ loading: true })
const data = await this.getProjectsAndZones()
form.setFieldsValue({ projectId: undefined, zoneId: undefined })
this.setState({
options: {
...this.state.options,
...data,
},
loading: false,
})
this.showHouseCode(form.getFieldsValue())
}
}
// 所属项目
else if (changedValues.hasOwnProperty('projectId')) {
await this.getCodeNo(changedValues.projectId)
this.showHouseCode(form.getFieldsValue())
}
// 所属行业
else if (changedValues.hasOwnProperty('industry')) {
this.showHouseCode(allValues)
}
// 编号
else if (changedValues.hasOwnProperty('no')) {
this.showHouseCode(allValues)
}
}
async onReloadProjectsOrZones(mode) {
this.setState({ loading: true })
const data = await this.getProjectsAndZones({}, [mode])
this.setState({
options: {
...this.state.options,
...data,
},
loading: false,
})
}
//#endregion
render() {
const { loading, codes, options, showIndustry, houseCode } = this.state
return (
<Form
initialValues={initialValues}
ref={this.form}
// labelCol={labelCol}
// wrapperCol={wrapperCol}
onValuesChange={(changedValues, allValues) =>
this.onValuesChange(changedValues, allValues)
}
layout="vertical"
>
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
<Row gutter={16}>
<Col span={10}>
<div className="yo-map-container">
<div className="yo-map--search">
<Input.Search
allowClear
placeholder="请输入关键字"
ref="map-search"
/>
</div>
<div className="h-700" ref="map"></div>
</div>
</Col>
<Col span={14}>
<Form.Item
label="房屋性质"
name="type"
rules={[{ required: true, message: '请选择房屋性质' }]}
>
<Radio.Group disabled={!!this.record} buttonStyle="solid">
{codes.houseType.map(item => (
<Radio.Button key={item.code} value={+item.code}>
{item.value}
</Radio.Button>
))}
</Radio.Group>
</Form.Item>
{showIndustry && (
<Form.Item
label="所属行业、系统"
name="industry"
rules={[{ required: true, message: '请选择所属行业、系统' }]}
>
<Radio.Group disabled={!!this.record} buttonStyle="solid">
{codes.houseIndustry.map(item => (
<Radio.Button key={item.code} value={+item.code}>
{item.value}
</Radio.Button>
))}
</Radio.Group>
</Form.Item>
)}
<Form.Item label="房屋编码" required className="mb-none">
<Row gutter={16}>
<Col flex="100%">
<Form.Item
name="areaCode"
rules={[
{
required: true,
message: '请选择房屋所在区域',
},
]}
>
<Cascader
allowClear={false}
displayRender={labels => labels.join(' - ')}
fieldNames={{
label: 'name',
value: 'code',
children: 'children',
}}
options={options.areaTree}
expandTrigger="hover"
/>
</Form.Item>
</Col>
<Col flex="1">
<Row gutter={8}>
<Col flex="1">
<Form.Item
name="projectId"
rules={[
{ required: true, message: '请选择项目' },
]}
>
<Select placeholder="请选择项目">
{options.projects.map(item => (
<Select.Option
key={item.id}
value={item.id}
>
{item.name}({item.note})
</Select.Option>
))}
</Select>
</Form.Item>
</Col>
<Auth auth="houseProjectInfo:page">
<Col>
<Button.Group>
<Button
onClick={() =>
window.openContentWindowByMenuName(
'bs_house_project_manage'
)
}
>
项目管理
</Button>
<Tooltip
placement="top"
title="重新加载项目列表"
>
<Button
icon={<AntIcon type="reload" />}
onClick={() =>
this.onReloadProjectsOrZones(
'projects'
)
}
></Button>
</Tooltip>
</Button.Group>
</Col>
</Auth>
</Row>
</Col>
<Col flex="1">
<Form.Item
name="no"
rules={[{ required: true, message: '请输入房屋序号' }]}
>
<InputNumber
formatter={value => value.padStart(3, '0')}
max={999}
min={1}
precision={0}
step={1}
className="w-100-p"
placeholder="请输入房屋序号"
/>
</Form.Item>
</Col>
{showIndustry && <Col>-</Col>}
</Row>
</Form.Item>
{houseCode && (
<Form.Item>
<Tag
color="purple"
style={{
fontSize: '24px',
fontWeight: 'bold',
letterSpacing: '10px',
padding: '12px',
}}
>
{houseCode}
</Tag>
</Form.Item>
)}
<Alert
message={<b>房屋编码说明</b>}
description={
<>
房屋所在市
<AntIcon type="border" />
<AntIcon type="border" />
<AntIcon type="border" />
<AntIcon type="border" />
<AntIcon type="border" />
<AntIcon type="border" />
街道
<AntIcon type="border" />
<AntIcon type="border" />
<AntIcon type="border" />
社区委会
<AntIcon type="border" />
<AntIcon type="border" />
<AntIcon type="border" />
项目
<AntIcon type="border" />
<AntIcon type="border" />
<AntIcon type="border" />
实物幢序号
<AntIcon type="border" />
<AntIcon type="border" />
<AntIcon type="border" />
根据省厅既有建筑物编号规则房屋所在区域编号按照市街道社区委会项目分类其中市区部分按照中华人民共和国行政区划代码GB2260标准编码街道县以下行政区划代码编码规则GB10114-88标准编码社区委会部分按照统计局提供编码设定各地上报各街道社区名称后上述编号由系统自动生成
<br />
各社区下辖项目由各地负责统一编码住宅项目序号一般一个小区一号采用3位数001号起编范围为001~999实物幢序号由各地负责统一编码以幢为单位采用3位数001号起编范围为001~999
</>
}
/>
<br />
<Form.Item label="所属片区" required>
<Row gutter={8}>
<Col flex="1">
<Form.Item
name="zoneId"
rules={[{ required: true, message: '请选择所属片区' }]}
noStyle
>
<Select
placeholder="请选择所属片区"
className="w-100-p"
>
{options.zones.map(item => (
<Select.Option key={item.id} value={item.id}>
{item.name}
</Select.Option>
))}
</Select>
</Form.Item>
</Col>
<Auth auth="houseZone:page">
<Col>
<Button.Group>
<Button
onClick={() =>
window.openContentWindowByMenuName(
'bs_house_zone_manage'
)
}
>
片区管理
</Button>
<Tooltip placement="top" title="重新加载片区列表">
<Button
icon={<AntIcon type="reload" />}
onClick={() =>
this.onReloadProjectsOrZones('zones')
}
></Button>
</Tooltip>
</Button.Group>
</Col>
</Auth>
</Row>
</Form.Item>
<Form.Item
label="房屋地址"
name="address"
rules={[{ required: true, message: '请输入房屋地址' }]}
>
<Input
autoComplete="off"
placeholder="请输入房屋地址或在地图上选择地点"
/>
</Form.Item>
<Form.Item label="地理坐标" required>
<Row gutter={16}>
<Col span={12}>
<Form.Item
name="lng"
className="mb-none"
rules={[
{ required: true, message: '请在地图中选择坐标' },
]}
>
<Input
disabled
placeholder="请在地图中选择坐标"
prefix="经度"
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="lat"
className="mb-none"
rules={[
{ required: true, message: '请在地图中选择坐标' },
]}
>
<Input
disabled
placeholder="请在地图中选择坐标"
prefix="纬度"
/>
</Form.Item>
</Col>
</Row>
</Form.Item>
</Col>
</Row>
</Spin>
</Form>
)
}
}

View File

@@ -0,0 +1,369 @@
import React, { Component } from 'react'
import {
Button,
Card,
Cascader,
Form,
Input,
Tag,
Popconfirm,
message as Message,
Radio,
Select,
Drawer,
} from 'antd'
import { isEqual } from 'lodash'
import { AntIcon, Auth, Container, HouseLog, QueryTable, QueryTableActions } from 'components'
import { api } from 'common/api'
import getDictData from 'util/dic'
import auth from 'components/authorized/handler'
import { toCamelCase } from 'util/format'
import { getSearchInfo } from 'util/query'
// 配置页面所需接口函数
const apiAction = {
page: api.houseCodePage,
}
// 用于弹窗标题
const name = '房屋编码'
export default class index extends Component {
state = {
codes: {
houseType: [],
houseIndustry: [],
},
options: {
areaTree: [],
},
type: '',
visibleLog: false,
}
// 表格实例
table = React.createRef()
// 表格字段
columns = [
{
title: '房屋编码',
dataIndex: 'houseCode',
sorter: true,
width: 400,
render: (text, record) => (
<>
{`${record.areaName}-${record.roadName}-${record.commName}-${
record.fullProjName
}-${record.no.toString().padStart(3, '0')}`}
<br />
<Tag color="purple">{text}</Tag>
</>
),
},
{
title: '房屋性质及行业',
dataIndex: 'type',
sorter: true,
width: 150,
render: (text, record) =>
this.bindCodeValue(text, 'house_type') +
(text === 2 ? `(${this.bindCodeValue(record.industry, 'house_industry')})` : ''),
},
{
title: '地址',
dataIndex: 'address',
sorter: true,
},
{
title: '登记时间',
width: 180,
dataIndex: 'createdTime',
sorter: true,
},
]
/**
* 构造函数,在渲染前动态添加操作字段等
* @param {*} props
*/
constructor(props) {
super(props)
const flag = auth({ houseCode: [['edit'], ['delete']] })
if (flag) {
this.columns.push({
title: '操作',
width: 180,
dataIndex: 'actions',
render: (text, record) => (
<QueryTableActions>
<Auth auth="houseCode:edit">
<a onClick={() => this.onOpen(record)}>编辑</a>
</Auth>
<Auth auth="houseCode:delete">
<Popconfirm
placement="topRight"
title="是否确认删除"
onConfirm={() => this.onDelete(record)}
>
<a>删除</a>
</Popconfirm>
</Auth>
<a onClick={() => this.onShowLog(record.id)}>日志</a>
</QueryTableActions>
),
})
}
}
/**
* 阻止外部组件引发的渲染,提升性能
* 可自行添加渲染条件
* [必要]
* @param {*} props
* @param {*} state
* @returns
*/
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
/**
* 加载字典数据,之后开始加载表格数据
* 如果必须要加载字典数据,可直接对表格设置autoLoad=true
*/
componentDidMount() {
const { onLoading, onLoadData } = this.table.current
onLoading()
getDictData('house_type', 'house_industry').then(async res => {
const { data } = await api.getAreaTree()
this.setState(
{
codes: res,
options: {
areaTree: data,
},
},
() => {
onLoadData()
}
)
})
}
/**
* 调用加载数据接口,可在调用前对query进行处理
* [异步,必要]
* @param {*} params
* @param {*} query
* @returns
*/
loadData = async (params, query) => {
if (query.areaCode) {
query.areaCode = query.areaCode.pop()
}
const searchInfo = getSearchInfo({
query,
queryType: {
areaCode: '=',
houseCode: 'like',
type: '=',
address: 'like',
},
})
const { data } = await apiAction.page({
...params,
searchInfo,
})
return data
}
/**
* 绑定字典数据
* @param {*} code
* @param {*} name
* @returns
*/
bindCodeValue(code, name) {
name = toCamelCase(name)
const codes = this.state.codes[name]
if (codes) {
const c = codes.find(p => p.code == code)
if (c) {
return c.value
}
}
return null
}
/**
* 打开新增/编辑弹窗
* @param {*} modal
* @param {*} record
*/
onOpen(record) {
const path = 'business/house/code/form'
window.openContentWindow({
key: record ? record.id : path,
title: record ? '修改房屋编码' : '新增房屋编码',
subTitle:
record &&
`${record.areaName}-${record.roadName}-${record.commName}-${record.note}-${record.no
.toString()
.padStart(3, '0')}`,
path,
param: {
id: record && record.id,
},
})
// modal.current.open({
// record
// })
}
/**
* 对表格上的操作进行统一处理
* [异步]
* @param {*} action
* @param {*} successMessage
*/
async onAction(action, successMessage) {
const { onLoading, onLoaded, onReloadData } = this.table.current
onLoading()
try {
if (action) {
await action
}
if (successMessage) {
Message.success(successMessage)
}
onReloadData()
} catch {
onLoaded()
}
}
/**
* 删除
* @param {*} record
*/
onDelete(record) {
this.onAction(apiAction.delete(record), '删除成功')
}
//#region 自定义方法
onShowLog(id) {
this.setState({ visibleLog: id })
}
//#endregion
render() {
const { options, codes, type, visibleLog } = this.state
return (
<Container mode="fluid">
<br />
<Card bordered={false}>
<QueryTable
ref={this.table}
autoLoad={false}
loadData={this.loadData}
columns={this.columns}
queryInitialValues={{
type: '',
}}
onQueryChange={values => {
if (values.hasOwnProperty('type')) {
this.setState({ type: values.type })
}
}}
query={
<Auth auth="houseCode:page">
<Form.Item name="areaCode">
<Cascader
displayRender={labels => labels.join(' - ')}
fieldNames={{
label: 'name',
value: 'code',
children: 'children',
}}
options={options.areaTree}
className="w-400"
expandTrigger="hover"
placeholder="请选择所在区域"
/>
</Form.Item>
<Form.Item label="房屋唯一编码" name="houseCode">
<Input autoComplete="off" placeholder="请输入房屋唯一编码" />
</Form.Item>
{/* <Form.Item label="编号" name="no">
<InputNumber
formatter={value => value && value.padStart(3, '0')}
max={999}
min={1}
precision={0}
step={1}
placeholder="请输入房屋序号"
/>
</Form.Item> */}
<Form.Item label="房屋性质" name="type">
<Radio.Group buttonStyle="solid">
<Radio.Button value="">全部</Radio.Button>
{codes.houseType.map(item => (
<Radio.Button key={item.code} value={item.code}>
{item.value}
</Radio.Button>
))}
</Radio.Group>
</Form.Item>
{type == 2 && (
<Form.Item label="行业" name="industry">
<Select
allowClear
className="w-150"
placeholder="请选择行业"
>
{codes.houseIndustry.map(item => (
<Select.Option key={item.code} value={item.code}>
{item.value}
</Select.Option>
))}
</Select>
</Form.Item>
)}
<Form.Item label="地址" name="address">
<Input autoComplete="off" placeholder="请输入地址" />
</Form.Item>
</Auth>
}
operator={
<Auth auth="houseCode:add">
<Button
icon={<AntIcon type="plus" />}
onClick={() => this.onOpen()}
>
新增{name}
</Button>
</Auth>
}
/>
</Card>
<Drawer
width={600}
visible={visibleLog}
onClose={() => this.setState({ visibleLog: false })}
destroyOnClose
>
<HouseLog id={visibleLog} />
</Drawer>
</Container>
)
}
}

View File

@@ -0,0 +1,180 @@
import React, { Component } from 'react'
import { Button, Col, Form, Input, Row, Select, Spin } from 'antd'
import { AntIcon } from 'components'
import { api } from 'common/api'
import getDictData from 'util/dic'
const initialValues = {}
export default class form extends Component {
state = {
// 加载状态
loading: true,
codes: {
houseCompanyType: [],
},
}
// 表单实例
form = React.createRef()
// 初始化数据
record = {}
/**
* mount后回调
*/
componentDidMount() {
this.props.created && this.props.created(this)
}
/**
* 填充数据
* 可以在设置this.record之后对其作出数据结构调整
* [异步,必要]
* @param {*} params
*/
async fillData(params) {
const state = { loading: false }
//#region 从后端转换成前段所需格式,也可以在此处调用获取详细数据接口
state.codes = await getDictData('house_company_type')
if (params.id) {
this.record = (await api.houseCompanyDetail({ id: params.id })).data
const { type, info } = this.record
if (type) {
this.record.type = type.split(',')
}
}
//#endregion
this.form.current.setFieldsValue(this.record)
this.setState(state)
}
/**
* 获取数据
* 可以对postData进行数据结构调整
* [异步,必要]
* @returns
*/
async getData() {
const form = this.form.current
const valid = await form.validateFields()
if (valid) {
const postData = form.getFieldsValue()
if (this.record) {
postData.id = this.record.id
}
//#region 从前段转换后端所需格式
const { type, info } = postData
if (Array.isArray(type)) {
postData.type = type.join(',')
}
//#endregion
return postData
}
}
//#region 自定义方法
//#endregion
render() {
const { codes } = this.state
return (
<Form initialValues={initialValues} ref={this.form} className="yo-form">
<Spin spinning={this.state.loading} indicator={<AntIcon type="loading" />}>
<div className="yo-form-group">
<Form.Item
label="名称"
name="name"
rules={[{ required: true, message: '请输入名称' }]}
>
<Input autoComplete="off" placeholder="请输入名称" />
</Form.Item>
<Form.Item
label="类型"
name="type"
rules={[{ required: true, message: '请选择类型' }]}
>
<Select mode="multiple" placeholder="请选择类型">
{codes.houseCompanyType.map(item => (
<Select.Option key={item.code} value={item.code}>
{item.value}
</Select.Option>
))}
</Select>
</Form.Item>
<div className="yo-form--fluid">
<Form.List name="info">
{(fields, { add, remove }) => (
<>
{fields.map(({ key, name, fieldKey, ...restField }) => (
<Row key={key} align="middle">
<Col flex="30px" className="text-right">
<AntIcon
type="minus-circle"
onClick={() => remove(name)}
/>
</Col>
<Col flex="187px">
<Form.Item
{...restField}
name={[name, 'name']}
fieldKey={[fieldKey, 'name']}
rules={[
{
required: true,
message: '请输入信息名称',
},
]}
>
<Input
autoComplete="off"
placeholder="请输入信息名称"
style={{ minWidth: 0 }}
/>
</Form.Item>
</Col>
<Col flex="1">
<Form.Item
{...restField}
name={[name, 'value']}
fieldKey={[fieldKey, 'value']}
rules={[
{
required: true,
message: '请输入信息内容',
},
]}
>
<Input
autoComplete="off"
placeholder="请输入信息内容"
/>
</Form.Item>
</Col>
</Row>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => add()}
block
icon={<AntIcon type="plus" />}
>
添加单位信息
</Button>
</Form.Item>
</>
)}
</Form.List>
</div>
</div>
</Spin>
</Form>
)
}
}

View File

@@ -0,0 +1,295 @@
import React, { Component } from 'react'
import {
Button,
Card,
Descriptions,
Form,
Input,
message as Message,
Popconfirm,
Select,
} from 'antd'
import { AntIcon, Auth, Container, ModalForm, QueryTable, QueryTableActions } from 'components'
import { api } from 'common/api'
import auth from 'components/authorized/handler'
import { isEqual } from 'lodash'
import getDictData from 'util/dic'
import { toCamelCase } from 'util/format'
import FormBody from './form'
/**
* 注释段[\/**\/]为必须要改
*/
/**
* 配置页面所需接口函数
*/
const apiAction = {
page: api.houseCompanyPage,
add: api.houseCompanyAdd,
edit: api.houseCompanyEdit,
delete: api.houseCompanyDelete,
}
/**
* 用于弹窗标题
* [必要]
*/
const name = '单位'
/**
* 统一配置权限标识
* [必要]
*/
const authName = 'houseCompany'
export default class index extends Component {
state = {
codes: {
houseCompanyType: [],
},
}
// 表格实例
table = React.createRef()
// 新增窗口实例
addForm = React.createRef()
// 编辑窗口实例
editForm = React.createRef()
columns = [
{
title: '名称',
width: 300,
dataIndex: 'name',
},
{
title: '类型',
width: 300,
dataIndex: 'type',
render: text =>
text
.split(',')
.map(p => this.bindCodeValue(p, 'house_company_type'))
.join(' / '),
},
]
/**
* 构造函数,在渲染前动态添加操作字段等
* @param {*} props
*/
constructor(props) {
super(props)
const flag = auth({ [authName]: [['edit'], ['delete']] })
if (flag) {
this.columns.push({
title: '操作',
width: 150,
dataIndex: 'actions',
render: (text, { id }) => (
<QueryTableActions>
<Auth auth={{ [authName]: 'edit' }}>
<a onClick={() => this.onOpen(this.editForm, id)}>编辑</a>
</Auth>
<Auth auth={{ [authName]: 'delete' }}>
<Popconfirm
placement="topRight"
title="是否确认删除"
onConfirm={() => this.onDelete(id)}
>
<a>删除</a>
</Popconfirm>
</Auth>
</QueryTableActions>
),
})
}
}
/**
* 阻止外部组件引发的渲染,提升性能
* 可自行添加渲染条件
* [必要]
* @param {*} props
* @param {*} state
* @returns
*/
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
/**
* 加载字典数据,之后开始加载表格数据
* 如果必须要加载字典数据,可直接对表格设置autoLoad=true
*/
componentDidMount() {
const { onLoading, onLoadData } = this.table.current
onLoading()
api.houseCompanyList({ type: '1' })
getDictData('house_company_type').then(codes => {
this.setState({ codes }, () => {
onLoadData()
})
})
}
/**
* 调用加载数据接口,可在调用前对query进行处理
* [异步,必要]
* @param {*} params
* @param {*} query
* @returns
*/
loadData = async (params, query) => {
const { data } = await apiAction.page({
...params,
...query,
})
return data
}
/**
* 绑定字典数据
* @param {*} code
* @param {*} name
* @returns
*/
bindCodeValue(code, name) {
name = toCamelCase(name)
const codes = this.state.codes[name]
if (codes) {
const c = codes.find(p => p.code == code)
if (c) {
return c.value
}
}
return null
}
/**
* 打开新增/编辑弹窗
* @param {*} modal
* @param {*} id
*/
onOpen(modal, id) {
modal.current.open({ id })
}
/**
* 对表格上的操作进行统一处理
* [异步]
* @param {*} action
* @param {*} successMessage
*/
async onAction(action, successMessage) {
const { onLoading, onLoaded, onReloadData } = this.table.current
onLoading()
try {
if (action) {
await action
}
if (successMessage) {
Message.success(successMessage)
}
onReloadData()
} catch {
onLoaded()
}
}
/**
* 删除
* @param {*} id
*/
onDelete(id) {
this.onAction(apiAction.delete({ id }), '删除成功')
}
//#region 自定义方法
//#endregion
render() {
const { codes } = this.state
return (
<Container mode="fluid">
<br />
<Card bordered={false}>
<QueryTable
ref={this.table}
autoLoad={false}
loadData={this.loadData}
columns={this.columns}
query={
<Auth auth={{ [authName]: 'page' }}>
<Form.Item label="名称" name="name">
<Input
autoComplete="off"
placeholder="请输入名称"
className="w-200"
/>
</Form.Item>
<Form.Item label="类型" name="type">
<Select placeholder="请选择类型" className="w-200" allowClear>
{codes.houseCompanyType.map(item => (
<Select.Option key={item.code} value={item.code}>
{item.value}
</Select.Option>
))}
</Select>
</Form.Item>
</Auth>
}
operator={
<Auth auth={{ [authName]: 'add' }}>
<Button
icon={<AntIcon type="plus" />}
onClick={() => this.onOpen(this.addForm)}
>
新增{name}
</Button>
</Auth>
}
expandedRowRender={record => (
<Descriptions bordered labelStyle={{ width: 200 }}>
{record.info.map((item, i) => (
<Descriptions.Item key={i} label={item.name}>
{item.value}
</Descriptions.Item>
))}
</Descriptions>
)}
/>
</Card>
<Auth auth={{ [authName]: 'add' }}>
<ModalForm
width={600}
title={`新增${name}`}
action={apiAction.add}
ref={this.addForm}
onSuccess={() => this.table.current.onReloadData()}
>
<FormBody />
</ModalForm>
</Auth>
<Auth auth={{ [authName]: 'edit' }}>
<ModalForm
width={600}
title={`编辑${name}`}
action={apiAction.edit}
ref={this.editForm}
onSuccess={() => this.table.current.onReloadData()}
>
<FormBody />
</ModalForm>
</Auth>
</Container>
)
}
}

View File

@@ -0,0 +1,230 @@
import React, { Component } from 'react'
import { Form, Spin, Upload } from 'antd'
import { AntIcon, PhotoPreview } from 'components'
import { cloneDeep, isEqual } from 'lodash'
import { BlobToBase64, GetFileName, PreviewFile } from 'util/file'
import { api } from 'common/api'
const initialValues = {}
const layout = {
labelCol: { flex: '150px' },
wrapperCol: { flex: '1' },
}
export default class aspect extends Component {
state = {
loading: true,
codes: {},
options: {},
}
// 表单实例
form = React.createRef()
photoPreview = React.createRef()
// 初始化数据
record = {}
/**
* 阻止外部组件引发的渲染,提升性能
* 可自行添加渲染条件
* [必要]
* @param {*} props
* @param {*} state
* @returns
*/
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
/**
* 绑定数据
*/
componentDidMount() {
this.fillData({
record: this.props.record,
})
}
/**
* DOM加载完成钩子,在此将自身传递给父级
*/
call() {
if (this.props.onRef) {
this.props.onRef(this)
}
}
/**
* 填充数据
* 可以在设置this.record之后对其作出数据结构调整
* [异步,必要]
* @param {*} params
*/
async fillData(params) {
this.record = cloneDeep(params.record)
//#region 从后端转换成前段所需格式
if (this.record) {
const { houseInfo } = this.record
const fileValue = []
const fileList =
!houseInfo.facadePhoto || !houseInfo.facadePhoto.length
? []
: houseInfo.facadePhoto.split(',')
for (const fileId of fileList) {
try {
const file = await PreviewFile(fileId)
const base64 = await BlobToBase64(file)
fileValue.push({
uid: fileId,
response: fileId,
name: file.name,
url: base64,
status: 'done',
})
} catch {
const { data: file } = await api.sysFileInfoDetail({ id: fileId })
fileValue.push({
uid: fileId,
response: '文件已丢失',
name: file.fileOriginName,
status: 'error',
})
}
}
houseInfo.facadePhoto = fileValue
}
//#endregion
this.form.current && this.form.current.setFieldsValue(this.record)
this.setState({ loading: false })
this.call()
}
/**
* 获取数据
* 可以对postData进行数据结构调整
* [异步,必要]
* @returns
*/
async getData() {
const form = this.form.current
const valid = await form.validateFields()
if (valid) {
const postData = form.getFieldsValue()
//#region 从前段转换后端所需格式
postData.houseInfo.facadePhoto = postData.houseInfo.facadePhoto
.map(item => (item.uid.startsWith('rc-upload') ? item.response : item.uid))
.join(',')
//#endregion
return postData
}
}
//#region 自定义方法
/**
* 表单change事件处理,包括了所有字段的change
* [异步,非必要]
* @param {*} changedValues
* @param {*} allValues
*/
async onValuesChange(changedValues, allValues) {}
async onFileUpload({ file, onProgress, onSuccess, onError }) {
onProgress({
percent: 0,
})
const fd = new FormData()
fd.append('file', file)
try {
const { data: fileId } = await api.sysFileInfoUpload(fd)
onSuccess(fileId)
} catch {
onError()
}
}
async onFilePreview(file, key) {
const fileList = this.form.current
.getFieldValue(['houseInfo', key])
.filter(p => p.status === 'done')
const items = []
for (const _file of fileList) {
const img = new Image()
const src = _file.url || _file.thumbUrl
img.src = src
items.push({
src,
w: img.naturalWidth,
h: img.naturalHeight,
})
}
this.photoPreview.current.initPhotoSwipe(items, {
index: fileList.indexOf(file),
})
}
async onFileDownload(file) {
const { data, headers } = await api.sysFileInfoDownload({ id: file.response })
const url = window.URL.createObjectURL(data)
const fileName = GetFileName(headers['content-disposition'])
const a = document.createElement('a')
a.href = url
a.download = fileName
a.click()
window.URL.revokeObjectURL(url)
a.remove()
}
//#endregion
render() {
const { loading } = this.state
return (
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
<Form
initialValues={initialValues}
ref={this.form}
{...layout}
onValuesChange={(changedValues, allValues) =>
this.onValuesChange(changedValues, allValues)
}
>
<Form.Item
label="外立面照片"
name={['houseInfo', 'facadePhoto']}
valuePropName="fileList"
getValueFromEvent={e => {
if (Array.isArray(e)) {
return e
}
return e && e.fileList
}}
>
<Upload
listType="picture-card"
customRequest={e => this.onFileUpload(e)}
showUploadList={{
showRemoveIcon: true,
showDownloadIcon: true,
}}
onPreview={file => this.onFilePreview(file, 'facadePhoto')}
onDownload={file => this.onFileDownload(file)}
>
<div>
<AntIcon type="plus" />
<div className="ant-upload-text">外立面照片</div>
</div>
</Upload>
</Form.Item>
</Form>
<PhotoPreview ref={this.photoPreview} />
</Spin>
)
}
}

View File

@@ -0,0 +1,238 @@
import React, { Component } from 'react'
import { Button, Form, Spin, Upload } from 'antd'
import { AntIcon } from 'components'
import { cloneDeep, isEqual } from 'lodash'
import { BlobToBase64, GetFileName, PreviewFile } from 'util/file'
import { api } from 'common/api'
const initialValues = {}
const layout = {
labelCol: { flex: '150px' },
wrapperCol: { flex: '1' },
}
const uploads = [
{
key: 'anEntryDocument',
label: '立项文件',
},
{
key: 'planningPermission',
label: '规划许可',
},
{
key: 'completionRecord',
label: '竣工验收备案',
},
{
key: 'monitorDocument',
label: '监理文件',
},
{
key: 'identificationReport',
label: '鉴定报告',
},
{
key: 'otherDocument',
label: '其他附件',
},
]
export default class attachments extends Component {
state = {
loading: true,
codes: {},
options: {},
}
// 表单实例
form = React.createRef()
// 初始化数据
record = {}
/**
* 阻止外部组件引发的渲染,提升性能
* 可自行添加渲染条件
* [必要]
* @param {*} props
* @param {*} state
* @returns
*/
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
/**
* 绑定数据
*/
componentDidMount() {
this.fillData({
record: this.props.record,
})
}
/**
* DOM加载完成钩子,在此将自身传递给父级
*/
call() {
if (this.props.onRef) {
this.props.onRef(this)
}
}
/**
* 填充数据
* 可以在设置this.record之后对其作出数据结构调整
* [异步,必要]
* @param {*} params
*/
async fillData(params) {
this.record = cloneDeep(params.record)
//#region 从后端转换成前段所需格式
if (this.record) {
const { houseInfo } = this.record
const keys = uploads.map(p => p.key)
for (const key of keys) {
const fileValue = []
const fileList =
!houseInfo[key] || !houseInfo[key].length ? [] : houseInfo[key].split(',')
for (const fileId of fileList) {
try {
const file = await PreviewFile(fileId)
const base64 = await BlobToBase64(file)
fileValue.push({
uid: fileId,
response: fileId,
name: file.name,
url: base64,
status: 'done',
})
} catch {
const { data: file } = await api.sysFileInfoDetail({ id: fileId })
fileValue.push({
uid: fileId,
response: '文件已丢失',
name: file.fileOriginName,
status: 'error',
})
}
}
houseInfo[key] = fileValue
}
}
//#endregion
this.form.current && this.form.current.setFieldsValue(this.record)
this.setState({ loading: false })
this.call()
}
/**
* 获取数据
* 可以对postData进行数据结构调整
* [异步,必要]
* @returns
*/
async getData() {
const form = this.form.current
const valid = await form.validateFields()
if (valid) {
const postData = form.getFieldsValue()
//#region 从前段转换后端所需格式
const { houseInfo } = postData
for (const key in houseInfo) {
houseInfo[key] = houseInfo[key]
.map(item => (item.uid.startsWith('rc-upload') ? item.response : item.uid))
.join(',')
}
//#endregion
return postData
}
}
//#region 自定义方法
/**
* 表单change事件处理,包括了所有字段的change
* [异步,非必要]
* @param {*} changedValues
* @param {*} allValues
*/
async onValuesChange(changedValues, allValues) {}
async onFileUpload({ file, onProgress, onSuccess, onError }) {
onProgress({
percent: 0,
})
const fd = new FormData()
fd.append('file', file)
try {
const { data: fileId } = await api.sysFileInfoUpload(fd)
onSuccess(fileId)
} catch {
onError()
}
}
async onFileDownload(file) {
const { data, headers } = await api.sysFileInfoDownload({ id: file.response })
const url = window.URL.createObjectURL(data)
const fileName = GetFileName(headers['content-disposition'])
const a = document.createElement('a')
a.href = url
a.download = fileName
a.click()
window.URL.revokeObjectURL(url)
a.remove()
}
//#endregion
render() {
const { loading } = this.state
return (
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
<Form
initialValues={initialValues}
ref={this.form}
{...layout}
onValuesChange={(changedValues, allValues) =>
this.onValuesChange(changedValues, allValues)
}
>
{uploads.map((item, i) => (
<Form.Item
key={i}
label={item.label}
name={['houseInfo', item.key]}
valuePropName="fileList"
getValueFromEvent={e => {
if (Array.isArray(e)) {
return e
}
return e && e.fileList
}}
>
<Upload
customRequest={e => this.onFileUpload(e)}
showUploadList={{
showRemoveIcon: true,
showDownloadIcon: true,
}}
onPreview={() => false}
onDownload={file => this.onFileDownload(file)}
>
<Button type="dashed" icon={<AntIcon type="upload" />}>
上传{item.label}
</Button>
</Upload>
</Form.Item>
))}
</Form>
</Spin>
)
}
}

View File

@@ -0,0 +1,955 @@
import React, { Component } from 'react'
import {
Button,
Row,
Col,
Form,
Input,
InputNumber,
Radio,
Checkbox,
Switch,
DatePicker,
Spin,
} from 'antd'
import { cloneDeep, first, isEqual, last, sortBy } from 'lodash'
import { AntIcon } from 'components'
import getDictData from 'util/dic'
import moment from 'moment'
import { CITY } from 'util/global'
import store from 'store'
const initialValues = {
houseInfo: {
houseUsedStatus: 1,
landAttribute: 1,
curtainWall: 0,
faceBrick: 0,
coating: 0,
painting: 0,
elevator: '0',
},
}
const { getState, dispatch } = store
const layout = {
labelCol: { flex: '150px' },
wrapperCol: { flex: '1' },
}
const checkboxKeys = ['insulationMaterial', 'wallMaterial']
export default class building extends Component {
state = {
loading: true,
codes: {
houseUsedStatus: [],
landAttribute: [],
houseStructureType: [],
houseAseismicGrade: [],
houseBaseInfo: [],
houseInsulationMaterial: [],
houseWallMaterial: [],
houseFireproofGrade: [],
houseBuildingCurtainWall: [],
houseElevator: [],
},
showMap: false,
showKeepWarmMaterialText: false,
theme: getState('layout').theme,
}
form = React.createRef()
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
componentDidMount() {
this.fillData({
record: this.props.record,
})
}
componentWillUnmount() {
dispatch({
type: 'PATROL_REMOVE_INIT_GRADE_BY_COMPLETED_DATE',
id: this.props.id,
})
}
call() {
if (this.props.onRef) {
this.props.onRef(this)
}
}
/**
* 填充数据
* 可以在设置this.record之后对其作出数据结构调整
* [异步,必要]
* @param {*} params
*/
async fillData(params) {
this.record = cloneDeep(params.record)
const _state = { loading: false }
//#region 从后端转换成前段所需格式
if (this.record) {
const { houseInfo } = this.record
if (houseInfo.completedDate) {
houseInfo.completedDate = moment(houseInfo.completedDate)
dispatch({
type: 'PATROL_INIT_GRADE_BY_COMPLETED_DATE',
date: {
id: this.props.id,
value: +houseInfo.completedDate.format('YYYY'),
},
})
}
// checkbox
checkboxKeys.forEach(key => {
if (houseInfo[key]) {
houseInfo[key] = houseInfo[key].split(',')
}
})
if (houseInfo.insulationMaterial) {
_state.showKeepWarmMaterialText = houseInfo.insulationMaterial.includes('100')
}
}
_state.codes = await getDictData(
'house_used_status',
'land_attribute',
'house_structure_type',
'house_aseismic_grade',
'house_base_info',
'house_insulation_material',
'house_wall_material',
'house_fireproof_grade',
'house_building_curtain_wall',
'house_elevator'
)
//#endregion
this.form.current && this.form.current.setFieldsValue(this.record)
this.setState(_state)
this.call()
}
/**
* 获取数据
* 可以对postData进行数据结构调整
* [异步,必要]
* @returns
*/
async getData() {
const form = this.form.current
const valid = await form.validateFields()
if (valid) {
const postData = form.getFieldsValue()
//#region 从前段转换后端所需格式
const { houseInfo } = postData
if (houseInfo.completedDate) {
houseInfo.completedDate = houseInfo.completedDate.format('YYYY-MM-DD')
}
// checkbox
checkboxKeys.forEach(key => {
if (houseInfo[key]) {
houseInfo[key] = sortBy(houseInfo[key], p => +p).join(',')
}
})
//#endregion
return postData
}
}
onValuesChange(changedValues, allValues) {
const form = this.form.current
const { houseInfo } = changedValues
if (houseInfo) {
if (
houseInfo.hasOwnProperty('landFloorCount') ||
houseInfo.hasOwnProperty('underFloorCount')
) {
const {
houseInfo: { landFloorCount, underFloorCount },
} = allValues
form.setFieldsValue({
houseInfo: {
totalFloor: +landFloorCount + +underFloorCount,
},
})
}
if (houseInfo.hasOwnProperty('insulationMaterial')) {
const value = this.checkedNone(houseInfo.insulationMaterial, 'insulationMaterial')
this.setState({
showKeepWarmMaterialText: value.includes('100'),
})
}
if (houseInfo.hasOwnProperty('completedDate')) {
dispatch({
type: 'PATROL_INIT_GRADE_BY_COMPLETED_DATE',
date: {
id: this.props.id,
value: +houseInfo.completedDate.format('YYYY'),
},
})
}
}
}
//#region 自定义方法
onShowMap() {
this.setState({ showMap: true }, async () => {
await this.initMap()
const { lng, lat } = this.record.houseCode
const position = [lng, lat]
this.setMarker(position)
this.map.setCenter(position)
})
}
initMap() {
// eslint-disable-next-line no-undef
const amap = AMap
return new Promise(resolve => {
const city = CITY
const district = new amap.DistrictSearch({
subdistrict: 0,
extensions: 'all',
level: 'city',
})
district.search(city, (status, result) => {
const bounds = result.districtList[0].boundaries,
mask = []
for (let i = 0; i < bounds.length; i += 1) {
mask.push([bounds[i]])
}
const geocoder = new amap.Geocoder({ city })
geocoder.getLocation(city, (status, result) => {
if (status !== 'complete' || !(result.geocodes && result.geocodes.length))
return
this.citycode = result.geocodes[0].addressComponent.citycode
this.map = new amap.Map(this.refs.map, {
mask,
zoom: 12,
center: result.geocodes[0].location,
mapStyle: `amap://styles/${this.state.theme}`,
})
this.map.on('click', e => {
this.setMarker(e.lnglat, geocoder)
})
this.map.on('complete', () => {
this.map.setFitView()
this.map.setZoom(12)
for (const path of bounds) {
new amap.Polyline({
path,
strokeColor: '#ccc',
strokeWeight: 4,
map: this.map,
})
}
resolve()
})
const auto = new amap.AutoComplete({
input: this.refs['map-search'].input,
city,
citylimit: true,
})
const placeSearch = new amap.PlaceSearch({
city,
citylimit: true,
pageSize: 1,
})
auto.on('select', ({ poi: { name: keywords, adcode } }) => {
placeSearch.search(keywords, async (status, result) => {
const {
poiList: { pois },
} = result
for (const poi of pois) {
await this.setMarker(poi.location, geocoder)
this.map.setCenter(poi.location)
}
})
})
})
})
})
}
setMarker(position, geocoder) {
const set = position => {
if (this.marker) {
this.marker.setPosition(position)
} else {
this.marker = new amap.Marker({
map: this.map,
icon: '//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png',
position,
offset: new amap.Pixel(-13, -30),
})
}
}
// eslint-disable-next-line no-undef
const amap = AMap
return new Promise((resolve, reject) => {
if (geocoder) {
geocoder.getAddress(position, (status, result) => {
if (status === 'complete' && result.regeocode) {
if (result.regeocode.addressComponent.citycode !== this.citycode) {
// 如果选到了别的城市,则中断
return
}
this.setPosition(result.regeocode.formattedAddress, position)
set(position)
resolve(position)
} else {
console.error('根据经纬度查询地址失败')
reject()
}
})
} else {
set(position)
resolve(position)
}
})
}
setPosition(address, { lng, lat }) {
this.form.current.setFieldsValue({ houseCode: { address, lng, lat } })
}
checkedNone(value, key) {
const form = this.form.current
if (first(value) == 0 && value.length > 1) {
// 在'无'之后选中其他值
value.shift()
form.setFieldsValue({
houseInfo: { [key]: value },
})
} else if (last(value) == 0 && value.length > 1) {
// 在其他值之后选中'无'
value = ['0']
form.setFieldsValue({
houseInfo: { [key]: value },
})
}
return value
}
//#endregion
render() {
const { loading, codes, showMap, showKeepWarmMaterialText } = this.state
return (
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
<Form
initialValues={initialValues}
{...layout}
ref={this.form}
onValuesChange={(changedValues, allValues) =>
this.onValuesChange(changedValues, allValues)
}
>
<Row gutter={16}>
<Col span={12}>
<Form.Item
name={['houseInfo', 'buildingName']}
label="幢名称"
rules={[{ required: true, message: '请输入幢名称' }]}
>
<Input autoComplete="off" placeholder="请输入幢名称" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="坐落地址"
name={['houseCode', 'address']}
rules={[{ required: true, message: '请输入坐落地址' }]}
>
<Input autoComplete="off" placeholder="请输入坐落地址" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="房屋使用状态"
name={['houseInfo', 'houseUsedStatus']}
rules={[{ required: true, message: '请选择房屋使用状态' }]}
>
<Radio.Group buttonStyle="solid">
{codes.houseUsedStatus.map(item => (
<Radio.Button key={item.code} value={+item.code}>
{item.value}
</Radio.Button>
))}
</Radio.Group>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="土地性质"
name={['houseInfo', 'landAttribute']}
rules={[{ required: true, message: '请选择土地性质' }]}
>
<Radio.Group buttonStyle="solid">
{codes.landAttribute.map(item => {
return (
<Radio.Button key={item.code} value={+item.code}>
{item.value}
</Radio.Button>
)
})}
</Radio.Group>
</Form.Item>
</Col>
</Row>
<Form.Item className="mb-none" label="地理坐标" required>
<Row gutter={16}>
<Col span={12}>
<Form.Item
name={['houseCode', 'lng']}
rules={[{ required: true, message: '请在地图上选择坐标' }]}
>
<Input
autoComplete="off"
className="yo-input-prefix-2"
disabled
placeholder="请在地图上选择坐标"
prefix="经度"
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name={['houseCode', 'lat']}
rules={[{ required: true, message: '请在地图上选择坐标' }]}
>
<Input
autoComplete="off"
className="yo-input-prefix-2"
disabled
placeholder="请在地图上选择坐标"
prefix="纬度"
/>
</Form.Item>
</Col>
</Row>
</Form.Item>
<Form.Item colon={false} label={true}>
{showMap ? (
<div className="yo-map-container">
<div className="yo-map--search">
<Input.Search
autoComplete="off"
allowClear
placeholder="请输入关键字"
ref="map-search"
/>
</div>
<div className="h-500" ref="map"></div>
</div>
) : (
<Button
shape="round"
size="large"
icon={<AntIcon type="picture" />}
onClick={() => this.onShowMap()}
>
查看地图
</Button>
)}
</Form.Item>
<Form.Item
label="结构类型"
name={['houseInfo', 'structureType']}
rules={[{ required: true, message: '请选择结构类型' }]}
>
<Radio.Group buttonStyle="solid" placeholder="请选择结构类型">
{codes.houseStructureType.map(item => {
return (
<Radio.Button key={item.code} value={+item.code}>
{item.value}
</Radio.Button>
)
})}
</Radio.Group>
</Form.Item>
<Row gutter={16}>
<Col span={12}>
<Form.Item
label="抗震等级"
name={['houseInfo', 'seismicGrade']}
rules={[{ required: true, message: '请选择抗震等级' }]}
>
<Radio.Group buttonStyle="solid">
{codes.houseAseismicGrade.map(item => {
return (
<Radio.Button key={item.code} value={+item.code}>
{item.value}
</Radio.Button>
)
})}
</Radio.Group>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="基础情况"
name={['houseInfo', 'baseInfo']}
rules={[{ required: true, message: '请选择基础情况' }]}
>
<Radio.Group buttonStyle="solid">
{codes.houseBaseInfo.map(item => {
return (
<Radio.Button key={item.code} value={+item.code}>
{item.value}
</Radio.Button>
)
})}
</Radio.Group>
</Form.Item>
</Col>
</Row>
<Form.Item label="外墙保温材料" name={['houseInfo', 'insulationMaterial']}>
<Checkbox.Group>
{codes.houseInsulationMaterial.map(item => {
return (
<Checkbox key={item.code} value={item.code}>
{item.value}
</Checkbox>
)
})}
</Checkbox.Group>
</Form.Item>
{showKeepWarmMaterialText && (
<Form.Item
colon={false}
label=" "
name={['houseInfo', 'keepWarmMaterialText']}
>
<Input.TextArea autoSize placeholder="请输入其他外墙保温材料" />
</Form.Item>
)}
<Form.Item label="外墙墙体材料" name={['houseInfo', 'wallMaterial']}>
<Checkbox.Group>
{codes.houseWallMaterial.map(item => {
return (
<Checkbox key={item.code} value={item.code}>
{item.value}
</Checkbox>
)
})}
</Checkbox.Group>
</Form.Item>
<Form.Item
label={
<span>
外墙外保温材料
<br />
防火等级
</span>
}
name={['houseInfo', 'fireproofGrade']}
>
<Radio.Group buttonStyle="solid">
{codes.houseFireproofGrade.map(item => {
return (
<Radio.Button key={item.code} value={item.code}>
{item.value}
</Radio.Button>
)
})}
</Radio.Group>
</Form.Item>
<Form.Item
label="建筑幕墙"
name={['houseInfo', 'curtainWall']}
rules={[{ required: true, message: '请选择建筑幕墙' }]}
>
<Radio.Group buttonStyle="solid">
{codes.houseBuildingCurtainWall.map(item => {
return (
<Radio.Button key={item.code} value={+item.code}>
{item.value}
</Radio.Button>
)
})}
</Radio.Group>
</Form.Item>
<Row gutter={16}>
<Col span={8}>
<Form.Item
label="有无外墙面砖"
name={['houseInfo', 'faceBrick']}
valuePropName="checked"
>
<Switch />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
label="有无外墙粉刷"
name={['houseInfo', 'whiteWash']}
valuePropName="checked"
>
<Switch />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
label="有无外墙涂料"
name={['houseInfo', 'coating']}
valuePropName="checked"
>
<Switch />
</Form.Item>
</Col>
<Col span={24}>
<Form.Item
label="电梯"
name={['houseInfo', 'elevator']}
rules={[{ required: true, message: '请选择电梯' }]}
>
<Radio.Group buttonStyle="solid">
{codes.houseElevator.map(item => {
return (
<Radio.Button key={item.code} value={item.code}>
{item.value}
</Radio.Button>
)
})}
</Radio.Group>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
label="竣工日期"
name={['houseInfo', 'completedDate']}
rules={[{ required: true, message: '请选择竣工日期' }]}
>
<DatePicker
onChange={date => {
/*$root.transfer.completedYear = date.format('YYYY') */
}}
className="w-100-p"
placeholder="请选择竣工日期"
/>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item label="设计使用年限" required>
<Row align="middle">
<Col flex="1">
<Form.Item
name={['houseInfo', 'usefulYear']}
rules={[
{ required: true, message: '请输入设计使用年限' },
]}
noStyle
>
<InputNumber
min={0}
className="w-100-p"
placeholder="请输入设计使用年限"
/>
</Form.Item>
</Col>
<Col>
<span className="yo-addon"></span>
</Col>
</Row>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item label="总建筑面积" required>
<Row align="middle">
<Col flex="1">
<Form.Item
name={['houseInfo', 'totalArea']}
rules={[
{ required: true, message: '请输入总建筑面积' },
]}
noStyle
>
<InputNumber
min={0}
className="w-100-p"
placeholder="请输入总建筑面积"
/>
</Form.Item>
</Col>
<Col>
<span className="yo-addon"></span>
</Col>
</Row>
</Form.Item>
</Col>
<Col span={24}>
<Form.Item label="建筑层数" className="mb-none">
<Row>
<Col>
<Form.Item colon={false} label="地上" required>
<Form.Item
name={['houseInfo', 'landFloorCount']}
rules={[
{ required: true, message: '请输入地上层' },
]}
noStyle
>
<InputNumber
min={0}
precision={0}
step={1}
className="w-150"
placeholder="请输入地上层"
/>
</Form.Item>
<div className="ant-form-text"></div>
</Form.Item>
</Col>
<Col>
<Form.Item colon={false} label="地下" required>
<Form.Item
name={['houseInfo', 'underFloorCount']}
rules={[
{ required: true, message: '请输入地下层' },
]}
noStyle
>
<InputNumber
min={0}
precision={0}
step={1}
className="w-150"
placeholder="请输入地下层"
/>
</Form.Item>
<div className="ant-form-text"></div>
</Form.Item>
</Col>
<Col>
<Form.Item colon={false} label="总共">
<Form.Item name={['houseInfo', 'totalFloor']} noStyle>
<InputNumber
disabled
min={0}
precision={0}
step={1}
className="w-150"
placeholder="请输入总层数"
/>
</Form.Item>
<div className="ant-form-text"></div>
</Form.Item>
</Col>
</Row>
</Form.Item>
<Form.Item label="其中">
<Row align="middle">
<Col>
<span className="yo-addon">地上第</span>
</Col>
<Col>
<Form.Item name={['houseInfo', 'landBsFloorStart']} noStyle>
<InputNumber min={0} step={1} placeholder="几" />
</Form.Item>
</Col>
<Col>
<span className="yo-addon"></span>
</Col>
<Col>
<Form.Item name={['houseInfo', 'landBsFloorEnd']} noStyle>
<InputNumber min={0} step={1} placeholder="几" />
</Form.Item>
</Col>
<Col>
<span className="yo-addon">层为商业用房</span>
</Col>
<Col>
<span className="yo-addon">地上</span>
</Col>
<Col>
<Form.Item
name={['houseInfo', 'landBikeFloorStart']}
noStyle
>
<InputNumber min={0} step={1} placeholder="几" />
</Form.Item>
</Col>
<Col>
<span className="yo-addon">层为车棚层</span>
</Col>
<Col>
<span className="yo-addon">地上第</span>
</Col>
<Col>
<Form.Item
name={['houseInfo', 'landResidenceFloorStart']}
noStyle
>
<InputNumber min={0} step={1} placeholder="几" />
</Form.Item>
</Col>
<Col>
<span className="yo-addon"></span>
</Col>
<Col>
<Form.Item
name={['houseInfo', 'landResidenceFloorEnd']}
noStyle
>
<InputNumber min={0} step={1} placeholder="几" />
</Form.Item>
</Col>
<Col>
<span className="yo-addon">层为住宅</span>
</Col>
</Row>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item label="总户数" required>
<Row align="middle">
<Col flex="1">
<Form.Item
name={['houseInfo', 'houseHolds']}
rules={[{ required: true, message: '请输入总户数' }]}
noStyle
>
<InputNumber
min={0}
precision={0}
step={1}
className="w-100-p"
placeholder="请输入总户数"
/>
</Form.Item>
</Col>
<Col>
<span className="yo-addon"></span>
</Col>
</Row>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item label="房屋单元数" required>
<Row align="middle">
<Col flex="1">
<Form.Item
name={['houseInfo', 'units']}
rules={[
{ required: true, message: '请输入房屋单元数' },
]}
noStyle
>
<InputNumber
min={0}
precision={0}
step={1}
className="w-100-p"
placeholder="请输入房屋单元数"
/>
</Form.Item>
</Col>
<Col>
<span className="yo-addon">单元</span>
</Col>
</Row>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item label="每层每单元户数" required>
<Row align="middle">
<Col flex="1">
<Form.Item
name={['houseInfo', 'unitFloorHolds']}
rules={[
{ required: true, message: '请输入每层每单元户数' },
]}
noStyle
>
<InputNumber
min={0}
precision={0}
step={1}
className="w-100-p"
placeholder="请输入每层每单元户数"
/>
</Form.Item>
</Col>
<Col>
<span className="yo-addon"></span>
</Col>
</Row>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item label="建设单位" name={['houseInfo', 'buildingUnit']}>
<Input autoComplete="off" placeholder="请输入建设单位" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
label="建设单位联系人"
name={['houseInfo', 'buildingUnitUser']}
>
<Input autoComplete="off" placeholder="请输入建设单位联系人" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
label="建设单位联系电话"
name={['houseInfo', 'buildingUnitTel']}
>
<Input autoComplete="off" placeholder="请输入建设单位联系电话" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item label="设计单位" name={['houseInfo', 'desingerUnit']}>
<Input autoComplete="off" placeholder="请输入设计单位" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item label="施工单位" name={['houseInfo', 'constructionUnit']}>
<Input autoComplete="off" placeholder="请输入施工单位" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item label="监理单位" name={['houseInfo', 'monitorUnit']}>
<Input autoComplete="off" placeholder="请输入监理单位" />
</Form.Item>
</Col>
</Row>
</Form>
</Spin>
)
}
}

View File

@@ -0,0 +1,167 @@
import React, { Component } from 'react'
import { Checkbox, Col, Form, Input, Row, Spin } from 'antd'
import { AntIcon } from 'components'
import { cloneDeep, isEqual, sortBy } from 'lodash'
import getDictData from 'util/dic'
const initialValues = {}
const layout = {
labelCol: { flex: '150px' },
wrapperCol: { flex: '1' },
}
export default class drawing extends Component {
state = {
loading: true,
codes: {
houseStorageOfDrawings: [],
},
options: {},
showDrawingMaterialText: false,
}
// 表单实例
form = React.createRef()
// 初始化数据
record = {}
/**
* 阻止外部组件引发的渲染,提升性能
* 可自行添加渲染条件
* [必要]
* @param {*} props
* @param {*} state
* @returns
*/
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
/**
* DOM加载完成钩子,绑定数据
*/
componentDidMount() {
this.fillData({
record: this.props.record,
})
}
/**
* 加载完成,通知父级组件并传递自身
*/
call() {
const { onRef } = this.props
if (onRef) onRef(this)
}
/**
* 填充数据
* 可以在设置this.record之后对其作出数据结构调整
* [异步,必要]
* @param {*} params
*/
async fillData(params) {
this.record = cloneDeep(params.record)
//#region 从后端转换成前段所需格式
if (this.record) {
const { houseInfo } = this.record
// checkbox
if (houseInfo.drawingMaterial) {
houseInfo.drawingMaterial = houseInfo.drawingMaterial.split(',')
}
this.setState({
showDrawingMaterialText:
!!houseInfo.drawingMaterial && houseInfo.drawingMaterial.includes('100'),
})
}
const codes = await getDictData('house_storage_of_drawings')
console.log(codes)
this.setState({ codes })
//#endregion
this.form.current && this.form.current.setFieldsValue(this.record)
this.setState({ loading: false })
this.call()
}
/**
* 获取数据
* 可以对postData进行数据结构调整
* [异步,必要]
* @returns
*/
async getData() {
const form = this.form.current
const valid = await form.validateFields()
if (valid) {
const postData = form.getFieldsValue()
//#region 从前段转换后端所需格式
const { houseInfo } = postData
// checkbox
if (houseInfo.drawingMaterial) {
houseInfo.drawingMaterial = sortBy(houseInfo.drawingMaterial, p => +p).join(',')
}
//#endregion
return postData
}
}
//#region 自定义方法
/**
* 表单change事件处理,包括了所有字段的change
* [异步,非必要]
* @param {*} changedValues
* @param {*} allValues
*/
async onValuesChange(changedValues, allValues) {
const { houseInfo } = changedValues
if (houseInfo.hasOwnProperty('drawingMaterial')) {
this.setState({ showDrawingMaterialText: houseInfo.drawingMaterial.includes('100') })
}
}
//#endregion
render() {
const { loading, codes, showDrawingMaterialText } = this.state
return (
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
<Form
initialValues={initialValues}
ref={this.form}
{...layout}
onValuesChange={(changedValues, allValues) =>
this.onValuesChange(changedValues, allValues)
}
>
<Form.Item
label="图纸资料存档处"
name={['houseInfo', 'drawingMaterial']}
rules={[{ required: true, message: '请选择图纸资料存档处' }]}
>
<Checkbox.Group>
{codes.houseStorageOfDrawings.map(item => (
<Checkbox key={item.code} value={item.code}>
{item.value}
</Checkbox>
))}
</Checkbox.Group>
</Form.Item>
{showDrawingMaterialText && (
<Form.Item
colon={false}
label=" "
name={['houseInfo', 'drawingMaterialText']}
>
<Input.TextArea autoSize placeholder="请输入其他图纸资料存档处" />
</Form.Item>
)}
</Form>
</Spin>
)
}
}

View File

@@ -0,0 +1,155 @@
import React, { Component } from 'react'
import { Form, Radio, Spin } from 'antd'
import { AntIcon } from 'components'
import { cloneDeep, isEqual } from 'lodash'
import getDictData from 'util/dic'
const initialValues = {}
const layout = {
labelCol: { flex: '150px' },
wrapperCol: { flex: '1' },
}
export default class identification extends Component {
state = {
loading: true,
codes: {
houseIdentification: [],
houseGovernment: [],
houseUsedStatus: [],
houseGrade: [],
},
options: {},
}
// 表单实例
form = React.createRef()
// 初始化数据
record = {}
/**
* 阻止外部组件引发的渲染,提升性能
* 可自行添加渲染条件
* [必要]
* @param {*} props
* @param {*} state
* @returns
*/
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
/**
* DOM加载完成钩子,绑定数据
*/
componentDidMount() {
this.fillData({
record: this.props.record,
})
}
/**
* 加载完成,通知父级组件并传递自身
*/
call() {
const { onRef } = this.props
if (onRef) onRef(this)
}
/**
* 填充数据
* 可以在设置this.record之后对其作出数据结构调整
* [异步,必要]
* @param {*} params
*/
async fillData(params) {
this.record = cloneDeep(params.record)
//#region 从后端转换成前段所需格式
const codes = await getDictData(
'house_identification',
'house_government',
'house_used_status',
'house_grade'
)
this.setState({ codes })
//#endregion
this.form.current && this.form.current.setFieldsValue(this.record)
this.setState({ loading: false })
this.call()
}
/**
* 获取数据
* 可以对postData进行数据结构调整
* [异步,必要]
* @returns
*/
async getData() {
const form = this.form.current
const valid = await form.validateFields()
if (valid) {
const postData = form.getFieldsValue()
//#region 从前段转换后端所需格式
//#endregion
return postData
}
}
//#region 自定义方法
/**
* 表单change事件处理,包括了所有字段的change
* [异步,非必要]
* @param {*} changedValues
* @param {*} allValues
*/
async onValuesChange(changedValues, allValues) {}
//#endregion
render() {
const { loading, codes } = this.state
return (
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
<Form
initialValues={initialValues}
ref={this.form}
{...layout}
onValuesChange={(changedValues, allValues) =>
this.onValuesChange(changedValues, allValues)
}
>
<Form.Item
label="房屋使用状态"
name={['houseInfo', 'houseUsedStatus']}
rules={[{ required: true, message: '请选择房屋使用状态' }]}
>
<Radio.Group buttonStyle="solid">
{codes.houseUsedStatus.map(item => (
<Radio.Button key={item.code} value={+item.code}>
{item.value}
</Radio.Button>
))}
</Radio.Group>
</Form.Item>
{/* <Form.Item
label="综合等级"
name={['houseInfo', 'houseGrade']}
rules={[{ required: true, message: '请选择综合等级' }]}
>
<Radio.Group buttonStyle="solid">
{codes.houseGrade.map(item => (
<Radio.Button key={item.code} value={+item.code}>
{item.value}
</Radio.Button>
))}
</Radio.Group>
</Form.Item> */}
</Form>
</Spin>
)
}
}

View File

@@ -0,0 +1,126 @@
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import { Row, Col, Card, Anchor, Spin, Divider } from 'antd'
import { merge } from 'lodash'
import { AntIcon, ComponentDynamic, Container } from 'components'
const parts = [
{
title: '建筑物基本信息',
component: () => import('./building'),
},
{
title: '权属情况',
component: () => import('./ownership'),
},
// {
// title: '调查情况',
// component: () => import('./investigation'),
// },
// {
// title: '鉴定治理',
// component: () => import('./identification'),
// },
{
title: '图纸资料存档处',
component: () => import('./drawing'),
},
{
title: '相关附件资料',
component: () => import('./attachments'),
},
{
title: '建筑概貌',
component: () => import('./aspect'),
},
{
title: '调查单位',
component: () => import('./unit'),
},
]
export default class index extends Component {
container = window
children = []
formData = {}
shouldComponentUpdate(props) {
return this.props.loading !== props.loading
}
// 通知上层组件已加载完毕
call(child, index) {
this.children[index] = child
if (this.children.filter(p => p).length === parts.length) {
if (this.props.onRef) {
this.props.onRef(this)
}
}
}
setContainer = container => {
this.container = (ReactDOM.findDOMNode(container) || {}).parentNode
}
async getData() {
for (const child of this.children) {
const data = await child.getData()
merge(this.formData, data)
}
return this.formData
}
render() {
const { id, loading } = this.props
return (
<Container mode="fluid" ref={this.setContainer}>
<Row gutter={16} type="flex">
<Col flex="1">
<br />
<div className="yo-adorn--house-top" />
<Card className="yo-form-page--body">
{parts.map((part, i) => (
<React.Fragment key={i}>
<section id={`form-${i}-${id}`}>
{part.title && <h5>{part.title}</h5>}
<Spin
spinning={loading}
indicator={<AntIcon type="loading" />}
wrapperClassName={loading && 'h-400-min'}
>
{!loading && (
<ComponentDynamic
is={part.component}
{...this.props}
onRef={child => this.call(child, i)}
/>
)}
</Spin>
</section>
{i < parts.length - 1 && <Divider />}
</React.Fragment>
))}
</Card>
</Col>
<Col flex="240px">
<Anchor
getContainer={() => this.container}
offsetTop={24}
targetOffset={100}
wrapperStyle={{ backgroundColor: 'transparent' }}
onClick={e => e.preventDefault()}
>
{parts.map((part, i) => (
<Anchor.Link key={i} href={`#form-${i}-${id}`} title={part.title} />
))}
</Anchor>
</Col>
</Row>
</Container>
)
}
}

View File

@@ -0,0 +1,312 @@
import React, { Component } from 'react'
import { Checkbox, Form, Input, Radio, Spin } from 'antd'
import { AntIcon } from 'components'
import { cloneDeep, first, isEqual, last, sortBy } from 'lodash'
import getDictData from 'util/dic'
const initialValues = {
houseInfo: {
houseSite: ['1'],
adjacentConstruction: ['0'],
chemicalErosion: ['0'],
structuralDismantling: 0,
addingLayer: 0,
repairAndReinforce: ['0'],
historicalCalamity: ['0'],
functionalChange: ['0'],
},
}
const layout = {
labelCol: { flex: '150px' },
wrapperCol: { flex: '1' },
}
const checkboxKeys = [
'houseSite',
'adjacentConstruction',
'chemicalErosion',
'repairAndReinforce',
'historicalCalamity',
'functionalChange',
]
export default class investigation extends Component {
state = {
loading: true,
codes: {
houseHouseSite: [],
houseAdjacentConstruction: [],
houseChemicalErosion: [],
houseStructuralDismantling: [],
houseAddingLayer: [],
houseRepairAndReinforce: [],
houseHistoricalCalamity: [],
houseFunctionalChange: [],
},
options: {},
}
// 表单实例
form = React.createRef()
// 初始化数据
record = {}
/**
* 阻止外部组件引发的渲染,提升性能
* 可自行添加渲染条件
* [必要]
* @param {*} props
* @param {*} state
* @returns
*/
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
/**
* DOM加载完成钩子,绑定数据
*/
componentDidMount() {
this.fillData({
record: this.props.record,
})
}
/**
* 加载完成,通知父级组件并传递自身
*/
call() {
const { onRef } = this.props
if (onRef) onRef(this)
}
/**
* 填充数据
* 可以在设置this.record之后对其作出数据结构调整
* [异步,必要]
* @param {*} params
*/
async fillData(params) {
this.record = cloneDeep(params.record)
//#region 从后端转换成前段所需格式
if (this.record) {
const { houseInfo } = this.record
// checkbox
checkboxKeys.forEach(key => {
if (houseInfo[key]) {
houseInfo[key] = houseInfo[key].split(',')
}
})
}
const codes = await getDictData(
'house_house_site',
'house_adjacent_construction',
'house_chemical_erosion',
'house_structural_dismantling',
'house_adding_layer',
'house_repair_and_reinforce',
'house_historical_calamity',
'house_functional_change'
)
this.setState({ codes })
//#endregion
this.form.current.setFieldsValue(this.record)
this.setState({ loading: false })
this.call()
}
/**
* 获取数据
* 可以对postData进行数据结构调整
* [异步,必要]
* @returns
*/
async getData() {
const form = this.form.current
const valid = await form.validateFields()
if (valid) {
const postData = form.getFieldsValue()
//#region 从前段转换后端所需格式
const { houseInfo } = postData
// checkbox
checkboxKeys.forEach(key => {
if (houseInfo[key]) {
houseInfo[key] = sortBy(houseInfo[key], p => +p).join(',')
}
})
//#endregion
return postData
}
}
//#region 自定义方法
/**
* 表单change事件处理,包括了所有字段的change
* [异步,非必要]
* @param {*} changedValues
* @param {*} allValues
*/
async onValuesChange(changedValues, allValues) {
const { houseInfo } = changedValues
const key = Object.keys(houseInfo).shift()
if (
[
'adjacentConstruction',
'chemicalErosion',
'repairAndReinforce',
'historicalCalamity',
'functionalChange',
].includes(key)
) {
this.checkedNone(houseInfo[key], key)
}
}
checkedNone(value, key) {
const form = this.form.current
if (first(value) == 0 && value.length > 1) {
// 在'无'之后选中其他值
value.shift()
form.setFieldsValue({
houseInfo: { [key]: value },
})
} else if (last(value) == 0 && value.length > 1) {
// 在其他值之后选中'无'
value = ['0']
form.setFieldsValue({
houseInfo: { [key]: value },
})
}
return value
}
//#endregion
render() {
const { loading, codes } = this.state
return (
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
<Form
initialValues={initialValues}
ref={this.form}
{...layout}
onValuesChange={(changedValues, allValues) =>
this.onValuesChange(changedValues, allValues)
}
>
<Form.Item
label="房屋场地"
name={['houseInfo', 'houseSite']}
rules={[{ required: true, message: '请选择房屋场地' }]}
>
<Checkbox.Group>
{codes.houseHouseSite.map(item => (
<Checkbox key={item.code} value={item.code}>
{item.value}
</Checkbox>
))}
</Checkbox.Group>
</Form.Item>
<Form.Item
label="相邻施工"
name={['houseInfo', 'adjacentConstruction']}
rules={[{ required: true, message: '请选择相邻施工' }]}
>
<Checkbox.Group>
{codes.houseAdjacentConstruction.map(item => (
<Checkbox key={item.code} value={item.code}>
{item.value}
</Checkbox>
))}
</Checkbox.Group>
</Form.Item>
<Form.Item
label="化学侵蚀"
name={['houseInfo', 'chemicalErosion']}
rules={[{ required: true, message: '请选择化学侵蚀' }]}
>
<Checkbox.Group>
{codes.houseChemicalErosion.map(item => (
<Checkbox key={item.code} value={item.code}>
{item.value}
</Checkbox>
))}
</Checkbox.Group>
</Form.Item>
<Form.Item
label="结构拆改"
name={['houseInfo', 'structuralDismantling']}
rules={[{ required: true, message: '请选择结构拆改' }]}
>
<Radio.Group buttonStyle="solid">
{codes.houseStructuralDismantling.map(item => (
<Radio.Button key={item.code} value={+item.code}>
{item.value}
</Radio.Button>
))}
</Radio.Group>
</Form.Item>
<Form.Item
label="加层改造"
name={['houseInfo', 'addingLayer']}
rules={[{ required: true, message: '请选择加层改造' }]}
>
<Radio.Group buttonStyle="solid">
{codes.houseAddingLayer.map(item => (
<Radio.Button key={item.code} value={+item.code}>
{item.value}
</Radio.Button>
))}
</Radio.Group>
</Form.Item>
<Form.Item
label="修缮加固"
name={['houseInfo', 'repairAndReinforce']}
rules={[{ required: true, message: '请选择修缮加固' }]}
>
<Checkbox.Group>
{codes.houseRepairAndReinforce.map(item => (
<Checkbox key={item.code} value={item.code}>
{item.value}
</Checkbox>
))}
</Checkbox.Group>
</Form.Item>
<Form.Item
label="历史灾害"
name={['houseInfo', 'historicalCalamity']}
rules={[{ required: true, message: '请选择历史灾害' }]}
>
<Checkbox.Group>
{codes.houseHistoricalCalamity.map(item => (
<Checkbox key={item.code} value={item.code}>
{item.value}
</Checkbox>
))}
</Checkbox.Group>
</Form.Item>
<Form.Item
label="使用功能变更"
name={['houseInfo', 'functionalChange']}
rules={[{ required: true, message: '请选择使用功能变更' }]}
>
<Checkbox.Group>
{codes.houseFunctionalChange.map(item => (
<Checkbox key={item.code} value={item.code}>
{item.value}
</Checkbox>
))}
</Checkbox.Group>
</Form.Item>
<Form.Item label="其他调查内容" name={['houseInfo', 'otherContents']}>
<Input.TextArea autoSize placeholder="请输入其他调查内容" />
</Form.Item>
</Form>
</Spin>
)
}
}

View File

@@ -0,0 +1,305 @@
import React, { Component } from 'react'
import { Col, Form, Input, InputNumber, Radio, Row, Spin } from 'antd'
import { AntIcon } from 'components'
import { cloneDeep, isEqual } from 'lodash'
import getDictData from 'util/dic'
const initialValues = {
houseInfo: {
straightHouseCount: 0,
selfHouseCount: 0,
privateHouseCount: 0,
businessCount: 0,
changeHouseCount: 0,
resettlementHouseCount: 0,
otherCount: 0,
houseCount: 0,
},
}
const layout = {
labelCol: { flex: '150px' },
wrapperCol: { flex: '1' },
}
export default class ownership extends Component {
state = {
loading: true,
codes: {
housePropertyRights: [],
},
options: {},
}
// 表单实例
form = React.createRef()
// 初始化数据
record = {}
/**
* 阻止外部组件引发的渲染,提升性能
* 可自行添加渲染条件
* [必要]
* @param {*} props
* @param {*} state
* @returns
*/
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
/**
* DOM加载完成钩子,绑定数据
*/
componentDidMount() {
this.fillData({
record: this.props.record,
})
}
/**
* 加载完成,通知父级组件并传递自身
*/
call() {
const { onRef } = this.props
if (onRef) onRef(this)
}
/**
* 填充数据
* 可以在设置this.record之后对其作出数据结构调整
* [异步,必要]
* @param {*} params
*/
async fillData(params) {
this.record = cloneDeep(params.record)
//#region 从后端转换成前段所需格式
const codes = await getDictData('house_property_rights')
this.setState({ codes })
//#endregion
this.form.current && this.form.current.setFieldsValue(this.record)
this.setState({ loading: false })
this.call()
}
/**
* 获取数据
* 可以对postData进行数据结构调整
* [异步,必要]
* @returns
*/
async getData() {
const form = this.form.current
const valid = await form.validateFields()
if (valid) {
const postData = form.getFieldsValue()
//#region 从前段转换后端所需格式
//#endregion
return postData
}
}
//#region 自定义方法
/**
* 表单change事件处理,包括了所有字段的change
* [异步,非必要]
* @param {*} changedValues
* @param {*} allValues
*/
async onValuesChange(changedValues, allValues) {
const form = this.form.current
const { houseInfo } = changedValues
if (
houseInfo.hasOwnProperty('straightHouseCount') ||
houseInfo.hasOwnProperty('selfHouseCount') ||
houseInfo.hasOwnProperty('otherCount') ||
houseInfo.hasOwnProperty('businessCount') ||
houseInfo.hasOwnProperty('changeHouseCount') ||
houseInfo.hasOwnProperty('resettlementHouseCount') ||
houseInfo.hasOwnProperty('privateHouseCount')
) {
const {
houseInfo: {
straightHouseCount,
selfHouseCount,
otherCount,
businessCount,
changeHouseCount,
resettlementHouseCount,
privateHouseCount,
},
} = allValues
form.setFieldsValue({
houseInfo: {
houseCount:
+straightHouseCount +
+selfHouseCount +
+otherCount +
+businessCount +
+changeHouseCount +
+resettlementHouseCount +
+privateHouseCount,
},
})
}
}
//#endregion
render() {
const { loading, codes } = this.state
return (
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
<Form
initialValues={initialValues}
ref={this.form}
{...layout}
onValuesChange={(changedValues, allValues) =>
this.onValuesChange(changedValues, allValues)
}
>
<Form.Item
label="产权性质"
name={['houseInfo', 'propertyRights']}
rules={[{ required: true, message: '请选择产权性质' }]}
>
<Radio.Group buttonStyle="solid">
{codes.housePropertyRights.map(item => (
<Radio.Button key={item.code} value={item.code}>
{item.value}
</Radio.Button>
))}
</Radio.Group>
</Form.Item>
<Form.Item label="房屋包含的住宅总套数" className="mb-none">
<Row>
<Col>
<Form.Item colon={false} label="直管公房" required>
<Form.Item
name={['houseInfo', 'straightHouseCount']}
rules={[{ required: true, message: '请输入直管公房' }]}
noStyle
>
<InputNumber min={0} step={1} placeholder="几" />
</Form.Item>
<div className="ant-form-text"></div>
</Form.Item>
</Col>
<Col>
<Form.Item colon={false} label="单位自管公房" required>
<Form.Item
name={['houseInfo', 'selfHouseCount']}
rules={[{ required: true, message: '请输入单位自管公房' }]}
noStyle
>
<InputNumber min={0} step={1} placeholder="几" />
</Form.Item>
<div className="ant-form-text"></div>
</Form.Item>
</Col>
<Col>
<Form.Item colon={false} label="其他">
<Form.Item name={['houseInfo', 'otherCount']} noStyle>
<InputNumber min={0} step={1} placeholder="几" />
</Form.Item>
<div className="ant-form-text"></div>
</Form.Item>
</Col>
</Row>
<Row>
<Col>
<Form.Item colon={false} label="商品房" required>
<Form.Item
name={['houseInfo', 'businessCount']}
rules={[{ required: true, message: '请输入商品房' }]}
noStyle
>
<InputNumber min={0} step={1} placeholder="几" />
</Form.Item>
<div className="ant-form-text"></div>
</Form.Item>
</Col>
<Col>
<Form.Item colon={false} label="房改房" required>
<Form.Item
name={['houseInfo', 'changeHouseCount']}
rules={[{ required: true, message: '请输入房改房' }]}
noStyle
>
<InputNumber min={0} step={1} placeholder="几" />
</Form.Item>
<div className="ant-form-text"></div>
</Form.Item>
</Col>
<Col>
<Form.Item colon={false} label="拆迁安置房" required>
<Form.Item
name={['houseInfo', 'resettlementHouseCount']}
rules={[{ required: true, message: '请输入拆迁安置房' }]}
noStyle
>
<InputNumber min={0} step={1} placeholder="几" />
</Form.Item>
<div className="ant-form-text"></div>
</Form.Item>
</Col>
</Row>
<Row>
<Col>
<Form.Item colon={false} label="私房">
<Form.Item name={['houseInfo', 'privateHouseCount']} noStyle>
<InputNumber min={0} step={1} placeholder="几" />
</Form.Item>
<div className="ant-form-text"></div>
</Form.Item>
</Col>
<Col>
<Form.Item colon={false} label="总共">
<Form.Item name={['houseInfo', 'houseCount']} noStyle>
<InputNumber disabled min={0} step={1} placeholder="几" />
</Form.Item>
<div className="ant-form-text"></div>
</Form.Item>
</Col>
</Row>
</Form.Item>
<Form.Item label="产权单位" name={['houseInfo', 'propertyUnit']}>
<Input autoComplete="off" placeholder="请输入产权单位" />
</Form.Item>
<Row gutter={16}>
<Col span={12}>
<Form.Item label="负责人" name={['houseInfo', 'propertyUnitUser']}>
<Input autoComplete="off" placeholder="请输入负责人" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="负责人电话"
name={['houseInfo', 'propertyUnitUserTel']}
>
<Input autoComplete="off" placeholder="请输入负责人电话" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="联系人" name={['houseInfo', 'propertyUnitConent']}>
<Input autoComplete="off" placeholder="请输入联系人" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="联系人电话"
name={['houseInfo', 'propertyUnitConentTel']}
>
<Input autoComplete="off" placeholder="请输入联系人电话" />
</Form.Item>
</Col>
</Row>
</Form>
</Spin>
)
}
}

View File

@@ -0,0 +1,154 @@
import React, { Component } from 'react'
import { Col, Form, Input, Row, Spin } from 'antd'
import { AntIcon } from 'components'
import { cloneDeep, isEqual } from 'lodash'
const initialValues = {}
const layout = {
labelCol: { flex: '150px' },
wrapperCol: { flex: '1' },
}
export default class unit extends Component {
state = {
loading: true,
codes: {},
options: {},
}
// 表单实例
form = React.createRef()
// 初始化数据
record = {}
/**
* 阻止外部组件引发的渲染,提升性能
* 可自行添加渲染条件
* [必要]
* @param {*} props
* @param {*} state
* @returns
*/
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
/**
* DOM加载完成钩子,绑定数据
*/
componentDidMount() {
this.fillData({
record: this.props.record,
})
}
/**
* 加载完成,通知父级组件并传递自身
*/
call() {
const { onRef } = this.props
if (onRef) onRef(this)
}
/**
* 填充数据
* 可以在设置this.record之后对其作出数据结构调整
* [异步,必要]
* @param {*} params
*/
async fillData(params) {
this.record = cloneDeep(params.record)
//#region 从后端转换成前段所需格式
//#endregion
this.form.current && this.form.current.setFieldsValue(this.record)
this.setState({ loading: false })
this.call()
}
/**
* 获取数据
* 可以对postData进行数据结构调整
* [异步,必要]
* @returns
*/
async getData() {
const form = this.form.current
const valid = await form.validateFields()
if (valid) {
const postData = form.getFieldsValue()
//#region 从前段转换后端所需格式
//#endregion
return postData
}
}
//#region 自定义方法
/**
* 表单change事件处理,包括了所有字段的change
* [异步,非必要]
* @param {*} changedValues
* @param {*} allValues
*/
async onValuesChange(changedValues, allValues) {}
//#endregion
render() {
const { loading } = this.state
return (
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
<Form
initialValues={initialValues}
ref={this.form}
{...layout}
onValuesChange={(changedValues, allValues) =>
this.onValuesChange(changedValues, allValues)
}
>
<Row gutter={16}>
<Col span={12}>
<Form.Item
label="调查登记机构"
name={['houseInfo', 'investigateAgency']}
rules={[{ required: true, message: '请输入调查登记机构' }]}
>
<Input autoComplete="off" placeholder="请输入调查登记机构" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="调查人员"
name={['houseInfo', 'investigateUser']}
rules={[{ required: true, message: '请输入调查人员' }]}
>
<Input autoComplete="off" placeholder="请输入调查人员" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="审核人员"
name={['houseInfo', 'offlineAuditor']}
rules={[{ required: true, message: '请输入审核人员' }]}
>
<Input autoComplete="off" placeholder="请输入审核人员" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="主管部门"
name={['houseInfo', 'competentDepartment']}
rules={[{ required: true, message: '请输入主管部门' }]}
>
<Input autoComplete="off" placeholder="请输入主管部门" />
</Form.Item>
</Col>
</Row>
</Form>
</Spin>
)
}
}

View File

@@ -0,0 +1,369 @@
import React, { Component } from 'react'
import { Form, Button, Input, Descriptions, message as Message, Modal, Spin, Tabs } from 'antd'
import { merge, isEqual, pickBy } from 'lodash'
import { AntIcon, ComponentDynamic, Container, Auth } from 'components'
import { api } from 'common/api'
const tabs = [
{
title: '房屋基本情况',
component: () => import('./base'),
active: true,
show: true,
},
// {
// title: '幕墙信息',
// name: 'curtainWall',
// path: 'curtainWall',
// active: false,
// show: false,
// },
// {
// title: '面砖信息',
// name: 'faceBrick',
// path: 'faceBrick',
// active: false,
// show: false,
// },
// {
// title: '墙面粉刷信息',
// name: 'whiteWash',
// path: 'whiteWash',
// active: false,
// show: false,
// },
// {
// title: '墙面涂料信息',
// name: 'coating',
// path: 'coating',
// active: false,
// show: false,
// },
{
title: '巡查登记',
component: () => import('./patrol'),
active: false,
show: true,
},
]
const actions = {
save: {
action: 'houseInfoSave',
remark: '保存',
after: 'reload',
},
submit: {
action: 'houseInfoSubmitToCheck',
remark: '提交',
after: 'close',
},
check: {
action: 'houseInfoCheck',
remark: '审核',
after: 'close',
},
}
export default class index extends Component {
state = {
actived: '0',
loading: true,
record: null,
saveDisabled: true,
saving: false,
taskStatus: 0,
}
children = []
formData = {}
checkForm = React.createRef()
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
componentDidMount() {
// 获取详细数据
this.onLoadInitData()
}
call(child, index) {
this.children[index] = child
if (this.children.filter(p => p).length === tabs.filter(p => p.show).length) {
this.setState({ saveDisabled: false })
}
}
onLoadInitData() {
this.setState({
loading: true,
})
const { taskId } = this.props.param
if (taskId) {
api.houseInfoGetByTaskId({ taskId }).then(({ data }) => {
// 删除空节点
for (const key in data) {
data[key] = pickBy(data[key], p => p !== null && p !== undefined)
}
this.setState({
taskStatus: data.patrolInfo.status,
record: data,
loading: false,
})
})
}
}
async onSave() {
await this.onPostData(actions.save)
}
async onSubmit() {
Modal.confirm({
content: '确认提交审核吗?',
onOk: () => {
this.onPostData(actions.submit)
},
onCancel: () => {},
})
}
async onCheck(pass_or_back) {
const form = this.checkForm.current
const valid = await form.validateFields()
Modal.confirm({
content: '审核结果即将提交,请确认',
onOk: () => {
if (valid) {
var checkRecord = {
taskCheckRecord: {
taskId: this.props.param.taskId,
passOrBack: +pass_or_back,
content: form.getFieldValue(['taskCheckRecord', 'content']),
},
}
this.onPostData(actions.check, checkRecord)
}
},
onCancel: () => {},
})
}
async onPostData(action, append) {
for (const child of this.children) {
try {
const data = await child.getData()
merge(this.formData, data)
} catch (e) {
return e
}
}
//#region 提交数据
if (append) {
this.formData = {
...this.formData,
...append,
}
}
if (this.formData.houseCode) {
this.formData.houseCode.id = this.state.record.houseCode.id
}
if (this.formData.patrolInfo && this.props.param.taskId) {
this.formData.patrolInfo.id = this.props.param.taskId
}
this.setState({ saving: true })
if (action) {
try {
const { success } = await api[action.action](this.formData)
if (success) {
Message.success(action.remark + '成功')
this.setState({ saving: false })
if (this.props.param.table.current) {
this.props.param.table.current.onReloadData()
}
switch (action.after) {
case 'close':
window.closeContentWindow()
break
default:
this.onLoadInitData()
break
}
}
} finally {
this.setState({ saving: false })
}
}
// setTimeout(() => {
// Message.success('提交成功')
// this.setState({ saving: false })
// }, 3000)
//#endregion
}
render() {
const { loading, record, saveDisabled, saving } = this.state
return (
<div className="yo-form-page">
<div className="yo-form-page-layout">
{/* 底部工具栏(需放在前面) */}
<div className="yo-form-page--bar yo-form-page--bar--with-tab">
<Container mode="fluid">
<div className="yo-form-page--bar-inner">
<span>
<Auth auth={{ houseInfo: 'check' }}>
{this.state.taskStatus == 3 && (
<Form ref={this.checkForm} layout="inline">
<Form.Item
label="审核意见"
name={['taskCheckRecord', 'content']}
rules={[
{
required: true,
message: '请输入审核意见',
},
]}
>
<Input.TextArea
autoSize
autoComplete="off"
placeholder="请输入审核意见"
className="w-500"
/>
</Form.Item>
<Form.Item>
<Button
type="primary"
onClick={() => this.onCheck(6)}
>
通过
</Button>
<Button
type="danger"
onClick={() => this.onCheck(-1)}
>
退回
</Button>
</Form.Item>
</Form>
)}
</Auth>
</span>
<span>
{this.state.taskStatus >= -1 && this.state.taskStatus < 3 && (
<Button
disabled={saveDisabled}
loading={saving}
type="primary"
onClick={() => this.onSave()}
>
保存
</Button>
)}
{this.state.taskStatus == 2 && (
<Button type="primary" onClick={() => this.onSubmit()}>
提交审核
</Button>
)}
<Button onClick={() => window.closeContentWindow()}>
取消
</Button>
</span>
</div>
</Container>
</div>
<div className="yo-form-page--header" style={{ paddingBottom: 0 }}>
<Container mode="fluid">
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
<Descriptions column={4}>
<Descriptions.Item label="区县(市)">
{record && record.houseCode.areaName}
</Descriptions.Item>
<Descriptions.Item label="街道(乡镇)">
{record && record.houseCode.roadName}
</Descriptions.Item>
<Descriptions.Item label="社区">
{record && record.houseCode.commName}
</Descriptions.Item>
<Descriptions.Item label="片区">
{record && record.houseCode.zoneName}
</Descriptions.Item>
<Descriptions.Item span="2" label="编号">
{record &&
`${record.houseCode.areaName}-${
record.houseCode.roadName
}-${record.houseCode.commName}-${
record.houseCode.fullProjName
}-${record.houseCode.no.toString().padStart(3, '0')}`}
</Descriptions.Item>
<Descriptions.Item span="2" label="编码">
{record && record.houseCode.houseCode}
</Descriptions.Item>
</Descriptions>
</Spin>
</Container>
</div>
<div className="yo-tab-external-mount">
<Tabs
activeKey={this.state.actived}
animated={false}
onChange={activeKey => {
this.setState({ actived: activeKey })
}}
>
{tabs.map((tab, i) => {
if (tab.show) {
return (
<Tabs.TabPane
key={i}
forceRender={false}
tab={tab.title}
></Tabs.TabPane>
)
}
return <></>
})}
</Tabs>
<div className="yo-tab-external-mount-content">
{tabs.map((tab, i) => {
if (tab.show) {
return (
<div
key={i}
className={[
this.state.actived === i.toString()
? 'yo-tab-external-tabpane-active'
: 'yo-tab-external-tabpane-inactive',
'yo-tab-external-tabpane',
].join(' ')}
>
<ComponentDynamic
is={tab.component}
id={this.props.id}
record={record}
loading={loading}
onRef={child => this.call(child, i)}
/>
</div>
)
}
return <></>
})}
</div>
</div>
</div>
</div>
)
}
}

View File

@@ -0,0 +1,112 @@
import React, { Component } from 'react'
import { Row, Col, Form, Input, DatePicker, Spin } from 'antd'
import { cloneDeep, isEqual } from 'lodash'
import { AntIcon } from 'components'
import moment from 'moment'
import { CITY } from 'util/global'
const layout = {
labelCol: { flex: '150px' },
wrapperCol: { flex: '1' },
}
export default class base extends Component {
state = {
loading: true,
}
form = React.createRef()
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
componentDidMount() {
this.fillData({
record: this.props.record,
})
}
call() {
if (this.props.onRef) {
this.props.onRef(this)
}
}
/**
* 填充数据
* 可以在设置this.record之后对其作出数据结构调整
* [异步,必要]
* @param {*} params
*/
async fillData(params) {
this.record = cloneDeep(params.record)
//#region 从后端转换成前段所需格式
if (this.record) {
const { patrolDate } = this.record.patrolInfo
this.record.patrolInfo.patrolDate = patrolDate ? moment(patrolDate) : patrolDate
}
//#endregion
this.form.current && this.form.current.setFieldsValue(this.record)
this.setState({ loading: false })
this.call()
}
/**
* 获取数据
* 可以对postData进行数据结构调整
* [异步,必要]
* @returns
*/
async getData() {
const form = this.form.current
const valid = await form.validateFields()
if (valid) {
const postData = form.getFieldsValue()
//#region 从前段转换后端所需格式
if (postData.patrolInfo.patrolDate) {
postData.patrolInfo.patrolDate = postData.patrolInfo.patrolDate.format('YYYY-MM-DD')
}
//#endregion
return postData
}
}
render() {
const { loading } = this.state
return (
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
<Form {...layout} ref={this.form}>
<Row gutter={16}>
<Col span={12}>
<Form.Item
name={['patrolInfo', 'patrolDate']}
label="巡查日期"
rules={[{ required: true, message: '请选择巡查日期' }]}
>
<DatePicker
onChange={date => {
/*$root.transfer.completedYear = date.format('YYYY') */
}}
className="w-100-p"
placeholder="请选择巡查日期"
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="巡查人/单位"
name={['patrolInfo', 'patrolUser']}
rules={[{ required: true, message: '请输入巡查人/单位' }]}
>
<Input autoComplete="off" placeholder="请输入巡查人/单位" />
</Form.Item>
</Col>
</Row>
</Form>
</Spin>
)
}
}

View File

@@ -0,0 +1,182 @@
import React, { Component } from 'react'
import { Form, Tooltip, Radio, Spin } from 'antd'
import { AntIcon } from 'components'
import { cloneDeep, first, isEqual, last, sortBy } from 'lodash'
import getDictData from 'util/dic'
import store from 'store'
const { getState, subscribe } = store
const layout = {
labelCol: { flex: '150px' },
wrapperCol: { flex: '1' },
}
export default class handling extends Component {
state = {
loading: true,
codes: {
housePatrolInitGrade: [],
housePatrolDamageGrade: [],
houseGrade: [],
},
}
form = React.createRef()
constructor(props) {
super(props)
this.unsubscribe = subscribe('business', business => {
const initGrade = this.getInitGrade(business.completedDate)
this.form.current.setFieldsValue({
patrolInfo: {
initGrade,
},
})
})
}
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
componentDidMount() {
this.fillData({
record: this.props.record,
})
}
componentWillUnmount() {
this.unsubscribe()
}
call() {
if (this.props.onRef) {
this.props.onRef(this)
}
}
/**
* 填充数据
* 可以在设置this.record之后对其作出数据结构调整
* [异步,必要]
* @param {*} params
*/
async fillData(params) {
this.record = cloneDeep(params.record)
const _state = { loading: false }
//#region 从后端转换成前段所需格式
if (this.record) {
const { patrolInfo } = this.record
if (this.record.houseInfo.completedDate)
patrolInfo.initGrade = this.getInitGrade(getState('business').completedDate)
}
_state.codes = await getDictData(
'house_patrol_init_grade',
'house_patrol_damage_grade',
'house_grade'
)
//#endregion
this.form.current && this.form.current.setFieldsValue(this.record)
this.setState(_state)
this.call()
}
/**
* 获取数据
* 可以对postData进行数据结构调整
* [异步,必要]
* @returns
*/
async getData() {
const form = this.form.current
const valid = await form.validateFields()
if (valid) {
const postData = form.getFieldsValue()
//#region 从前段转换后端所需格式
//#endregion
return postData
}
}
getInitGrade(completedDate) {
const date = completedDate.find(p => p.id === this.props.id)
if (date) {
const { value: year } = date
if (year > 1999) {
return 1
}
if (year > 1994 && year < 2000) {
return 2
}
if (year > 1979 && year < 1995) {
return 3
}
if (year < 1980) {
return 4
}
}
}
render() {
const { loading, codes, initGradeValue } = this.state
return (
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
<Form {...layout} ref={this.form}>
<Form.Item
label="初始等级"
name={['patrolInfo', 'initGrade']}
tooltip="初始等级无法手动更改由房屋详情的竣工日期决定2000年之后竣工的为一级1995年~1999年竣工的为二级1980年~1994年竣工的为三级早于1980年竣工的为四级。选择房屋竣工日期后初始等级会自动填充。"
rules={[{ required: true, message: '请选择初始等级' }]}
>
<Radio.Group disabled buttonStyle="solid">
{codes.housePatrolInitGrade.map(item => {
return (
<Radio.Button key={item.code} value={+item.code}>
{item.value}
</Radio.Button>
)
})}
</Radio.Group>
</Form.Item>
<Form.Item
label="损坏等级"
name={['patrolInfo', 'damageGrade']}
rules={[{ required: true, message: '请选择损坏等级' }]}
>
<Radio.Group buttonStyle="solid">
{codes.housePatrolDamageGrade.map(item => {
return (
<Radio.Button key={item.code} value={+item.code}>
{item.value}
</Radio.Button>
)
})}
</Radio.Group>
</Form.Item>
<Form.Item
label="综合等级"
name={['patrolInfo', 'comprehensiveGrade']}
rules={[{ required: true, message: '请选择综合等级' }]}
>
<Radio.Group buttonStyle="solid">
{codes.houseGrade.map(item => {
return (
<Radio.Button key={item.code} value={+item.code}>
{item.value}
</Radio.Button>
)
})}
</Radio.Group>
</Form.Item>
</Form>
</Spin>
)
}
}

View File

@@ -0,0 +1,131 @@
import React, { Component } from 'react'
import { Form, Input, Radio, Spin } from 'antd'
import { AntIcon } from 'components'
import { cloneDeep, first, isEqual, last, sortBy } from 'lodash'
import getDictData from 'util/dic'
const layout = {
labelCol: { flex: '150px' },
wrapperCol: { flex: '1' },
}
export default class handling extends Component {
state = {
loading: true,
codes: {
housePatrolHandlingOpinion: [],
housePatrolRectifyReform: [],
},
}
form = React.createRef()
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
componentDidMount() {
this.fillData({
record: this.props.record,
})
}
call() {
if (this.props.onRef) {
this.props.onRef(this)
}
}
/**
* 填充数据
* 可以在设置this.record之后对其作出数据结构调整
* [异步,必要]
* @param {*} params
*/
async fillData(params) {
this.record = cloneDeep(params.record)
const _state = { loading: false }
//#region 从后端转换成前段所需格式
_state.codes = await getDictData(
'house_patrol_handling_opinion',
'house_patrol_rectify_Reform'
)
//#endregion
this.form.current && this.form.current.setFieldsValue(this.record)
this.setState(_state)
this.call()
}
/**
* 获取数据
* 可以对postData进行数据结构调整
* [异步,必要]
* @returns
*/
async getData() {
const form = this.form.current
const valid = await form.validateFields()
if (valid) {
const postData = form.getFieldsValue()
//#region 从前段转换后端所需格式
//#endregion
return postData
}
}
render() {
const { loading, codes } = this.state
return (
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
<Form {...layout} ref={this.form}>
<Form.Item
label="处理建议"
name={['patrolInfo', 'handlingOpinion']}
rules={[{ required: true, message: '请选择处理建议' }]}
>
<Radio.Group buttonStyle="solid">
{codes.housePatrolHandlingOpinion.map(item => {
return (
<Radio.Button key={item.code} value={+item.code}>
{item.value}
</Radio.Button>
)
})}
</Radio.Group>
</Form.Item>
<Form.Item label="处理建议备注" name={['patrolInfo', 'handlingOpinionRemark']}>
<Input.TextArea
autoSize={{ minRows: 3 }}
placeholder="请输入处理建议备注"
/>
</Form.Item>
<Form.Item
label="整改情况"
name={['patrolInfo', 'rectifyAndReform']}
rules={[{ required: true, message: '请选择整改情况' }]}
>
<Radio.Group buttonStyle="solid">
{codes.housePatrolRectifyReform.map(item => {
return (
<Radio.Button key={item.code} value={+item.code}>
{item.value}
</Radio.Button>
)
})}
</Radio.Group>
</Form.Item>
<Form.Item label="整改情况备注" name={['patrolInfo', 'rectifyAndReformRemark']}>
<Input.TextArea
autoSize={{ minRows: 3 }}
placeholder="请输入整改情况备注"
/>
</Form.Item>
</Form>
</Spin>
)
}
}

View File

@@ -0,0 +1,150 @@
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import { Anchor, Card, Col, Divider, Row, Spin } from 'antd'
import { AntIcon, ComponentDynamic, Container } from 'components'
import { isEqual, merge } from 'lodash'
const parts = [
{
title: '巡查基本情况',
component: () => import('./base'),
},
{
title: '房屋检查',
component: () => import('./inspection'),
},
{
title: '等级划分',
component: () => import('./grade'),
},
{
title: '调查情况',
component: () => import('./investigation'),
},
{
title: '处理情况',
component: () => import('./handling'),
},
{
title: '本期巡查结果',
component: () => import('./result'),
},
]
export default class index extends Component {
// 子表单实例集合
children = []
// 整合提交数据
formData = {}
// 锚点挂载DOM
container = window
/**
* 阻止外部组件引发的渲染,提升性能
* 可自行添加渲染条件
* [必要]
* @param {*} props
* @param {*} state
* @returns
*/
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state) || this.props.loading !== props.loading
}
/**
* 加载完成,通知父级组件并传递自身
*/
call(child, index) {
this.children[index] = child
if (this.children.filter(p => p).length === parts.length) {
const { onRef } = this.props
if (onRef) onRef(this)
}
}
/**
* 从下级组件获取表单数据,并传递给更上级组件
* [异步,必要]
* @returns
*/
async getData() {
for (const child of this.children) {
const data = await child.getData()
merge(this.formData, data)
}
return this.formData
}
/**
* 设置锚点容器
* [非必要]
* @param {*} container
*/
setContainer = container => {
this.container = (ReactDOM.findDOMNode(container) || {}).parentNode
}
/**
* 渲染
* 当前渲染结构已完善,非必要可以不用修改
* [必要]
* @returns
*/
render() {
const { id, loading } = this.props
return (
<Container mode="fluid" ref={this.setContainer}>
<Row gutter={16}>
<Col flex="1">
<br />
<div className="yo-adorn--house-top" />
<Card className="yo-form-page--body">
{parts.map((item, i) => (
<React.Fragment key={i}>
<section id={`form-patrol-${i}-${id}`}>
{item.title && <h5>{item.title}</h5>}
<Spin
spinning={loading}
indicator={<AntIcon type="loading" />}
wrapperClassName={loading && 'h-400-min'}
>
{!loading && (
<ComponentDynamic
is={item.component}
{...this.props}
onRef={child => this.call(child, i)}
/>
)}
</Spin>
</section>
{i < parts.length - 1 && <Divider />}
</React.Fragment>
))}
</Card>
</Col>
{/* 锚点,如果不需要可以删除以下节点 */}
<Col flex="240px">
<Anchor
getContainer={() => this.container}
offsetTop={24}
targetOffset={100}
wrapperStyle={{ backgroundColor: 'transparent' }}
onClick={e => e.preventDefault()}
>
{parts.map((part, i) => (
<Anchor.Link
key={i}
href={`#form-patrol-${i}-${id}`}
title={part.title}
/>
))}
</Anchor>
</Col>
</Row>
</Container>
)
}
}

View File

@@ -0,0 +1,291 @@
import React, { Component } from 'react'
import { Col, Form, Input, Row, Spin, Upload } from 'antd'
import { AntIcon, PhotoPreview } from 'components'
import { cloneDeep, isEqual } from 'lodash'
import { BlobToBase64, GetFileName, PreviewFile } from 'util/file'
import { api } from 'common/api'
const initialValues = {}
const layout = {
labelCol: { flex: '150px' },
wrapperCol: { flex: '1' },
}
const imageUploads = [{ key: 'settlementTiltFiles' }, { key: 'otherInfoFiles' }]
export default class inspection extends Component {
state = {
loading: true,
codes: {},
options: {},
}
// 表单实例
form = React.createRef()
photoPreview = React.createRef()
// 初始化数据
record = {}
/**
* 阻止外部组件引发的渲染,提升性能
* 可自行添加渲染条件
* [必要]
* @param {*} props
* @param {*} state
* @returns
*/
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
/**
* DOM加载完成钩子,绑定数据
*/
componentDidMount() {
this.fillData({
record: this.props.record,
})
}
/**
* 加载完成,通知父级组件并传递自身
*/
call() {
const { onRef } = this.props
if (onRef) onRef(this)
}
/**
* 填充数据
* 可以在设置this.record之后对其作出数据结构调整
* [异步,必要]
* @param {*} params
*/
async fillData(params) {
this.record = cloneDeep(params.record)
//#region 从后端转换成前段所需格式
if (this.record) {
const { patrolInfo } = this.record
const keys = imageUploads.map(p => p.key)
for (const key of keys) {
const fileValue = []
const fileList =
!patrolInfo[key] || !patrolInfo[key].length ? [] : patrolInfo[key].split(',')
for (const fileId of fileList) {
try {
const file = await PreviewFile(fileId)
const base64 = await BlobToBase64(file)
fileValue.push({
uid: fileId,
response: fileId,
name: file.name,
url: base64,
status: 'done',
})
} catch {
const { data: file } = await api.sysFileInfoDetail({ id: fileId })
fileValue.push({
uid: fileId,
response: '文件已丢失',
name: file.fileOriginName,
status: 'error',
})
}
}
patrolInfo[key] = fileValue
}
}
//#endregion
this.form.current && this.form.current.setFieldsValue(this.record)
this.setState({ loading: false })
this.call()
}
/**
* 获取数据
* 可以对postData进行数据结构调整
* [异步,必要]
* @returns
*/
async getData() {
const form = this.form.current
const valid = await form.validateFields()
if (valid) {
const postData = form.getFieldsValue()
//#region 从前段转换后端所需格式
const { patrolInfo } = postData
const keys = imageUploads.map(p => p.key)
for (const key of keys) {
patrolInfo[key] = patrolInfo[key]
.map(item => (item.uid.startsWith('rc-upload') ? item.response : item.uid))
.join(',')
}
//#endregion
return postData
}
}
//#region 自定义方法
/**
* 表单change事件处理,包括了所有字段的change
* [异步,非必要]
* @param {*} changedValues
* @param {*} allValues
*/
async onValuesChange(changedValues, allValues) {}
async onFileUpload({ file, onProgress, onSuccess, onError }) {
onProgress({
percent: 0,
})
const fd = new FormData()
fd.append('file', file)
try {
const { data: fileId } = await api.sysFileInfoUpload(fd)
onSuccess(fileId)
} catch {
onError()
}
}
async onFilePreview(file, key) {
const fileList = this.form.current
.getFieldValue(['patrolInfo', key])
.filter(p => p.status === 'done')
const items = []
for (const _file of fileList) {
const img = new Image()
const src = _file.url || _file.thumbUrl
img.src = src
items.push({
src,
w: img.naturalWidth,
h: img.naturalHeight,
})
}
this.photoPreview.current.initPhotoSwipe(items, {
index: fileList.indexOf(file),
})
}
async onFileDownload(file) {
const { data, headers } = await api.sysFileInfoDownload({ id: file.response })
const url = window.URL.createObjectURL(data)
const fileName = GetFileName(headers['content-disposition'])
const a = document.createElement('a')
a.href = url
a.download = fileName
a.click()
window.URL.revokeObjectURL(url)
a.remove()
}
//#endregion
render() {
const { loading } = this.state
return (
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
<Form
initialValues={initialValues}
ref={this.form}
{...layout}
onValuesChange={(changedValues, allValues) =>
this.onValuesChange(changedValues, allValues)
}
>
<Row gutter={16}>
<Col span={12}>
<Form.Item label="沉降倾斜情况" name={['patrolInfo', 'settlementTilt']}>
<Input.TextArea
autoSize={{ minRows: 4.6 }}
placeholder="请输入沉降倾斜情况"
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name={['patrolInfo', 'settlementTiltFiles']}
valuePropName="fileList"
getValueFromEvent={e => {
if (Array.isArray(e)) {
return e
}
return e && e.fileList
}}
>
<Upload
listType="picture-card"
customRequest={e => this.onFileUpload(e)}
showUploadList={{
showRemoveIcon: true,
showDownloadIcon: true,
}}
onPreview={file =>
this.onFilePreview(file, 'settlementTiltFiles')
}
onDownload={file => this.onFileDownload(file)}
>
<div>
<AntIcon type="plus" />
<div className="ant-upload-text">沉降倾斜照片</div>
</div>
</Upload>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="其他情况" name={['patrolInfo', 'otherInfo']}>
<Input.TextArea
autoSize={{ minRows: 4.6 }}
placeholder="请输入其他情况"
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name={['patrolInfo', 'otherInfoFiles']}
valuePropName="fileList"
getValueFromEvent={e => {
if (Array.isArray(e)) {
return e
}
return e && e.fileList
}}
>
<Upload
listType="picture-card"
customRequest={e => this.onFileUpload(e)}
showUploadList={{
showRemoveIcon: true,
showDownloadIcon: true,
}}
onPreview={file => this.onFilePreview(file, 'otherInfoFiles')}
onDownload={file => this.onFileDownload(file)}
>
<div>
<AntIcon type="plus" />
<div className="ant-upload-text">其他情况照片</div>
</div>
</Upload>
</Form.Item>
</Col>
</Row>
<Form.Item label="主要安全隐患综述" name={['patrolInfo', 'mainSafety']}>
<Input.TextArea
autoSize={{ minRows: 4.6 }}
placeholder="请输入主要安全隐患综述"
/>
</Form.Item>
</Form>
<PhotoPreview ref={this.photoPreview} />
</Spin>
)
}
}

View File

@@ -0,0 +1,312 @@
import React, { Component } from 'react'
import { Checkbox, Form, Input, Radio, Spin } from 'antd'
import { AntIcon } from 'components'
import { cloneDeep, first, isEqual, last, sortBy } from 'lodash'
import getDictData from 'util/dic'
const initialValues = {
patrolInfo: {
houseSite: ['1'],
adjacentConstruction: ['0'],
chemicalErosion: ['0'],
structuralDismantling: 0,
addingLayer: 0,
repairAndReinforce: ['0'],
historicalCalamity: ['0'],
functionalChange: ['0'],
},
}
const layout = {
labelCol: { flex: '150px' },
wrapperCol: { flex: '1' },
}
const checkboxKeys = [
'houseSite',
'adjacentConstruction',
'chemicalErosion',
'repairAndReinforce',
'historicalCalamity',
'functionalChange',
]
export default class investigation extends Component {
state = {
loading: true,
codes: {
houseHouseSite: [],
houseAdjacentConstruction: [],
houseChemicalErosion: [],
houseStructuralDismantling: [],
houseAddingLayer: [],
houseRepairAndReinforce: [],
houseHistoricalCalamity: [],
houseFunctionalChange: [],
},
options: {},
}
// 表单实例
form = React.createRef()
// 初始化数据
record = {}
/**
* 阻止外部组件引发的渲染,提升性能
* 可自行添加渲染条件
* [必要]
* @param {*} props
* @param {*} state
* @returns
*/
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
/**
* DOM加载完成钩子,绑定数据
*/
componentDidMount() {
this.fillData({
record: this.props.record,
})
}
/**
* 加载完成,通知父级组件并传递自身
*/
call() {
const { onRef } = this.props
if (onRef) onRef(this)
}
/**
* 填充数据
* 可以在设置this.record之后对其作出数据结构调整
* [异步,必要]
* @param {*} params
*/
async fillData(params) {
this.record = cloneDeep(params.record)
//#region 从后端转换成前段所需格式
if (this.record) {
const { patrolInfo } = this.record
// checkbox
checkboxKeys.forEach(key => {
if (patrolInfo[key]) {
patrolInfo[key] = patrolInfo[key].split(',')
}
})
}
const codes = await getDictData(
'house_house_site',
'house_adjacent_construction',
'house_chemical_erosion',
'house_structural_dismantling',
'house_adding_layer',
'house_repair_and_reinforce',
'house_historical_calamity',
'house_functional_change'
)
this.setState({ codes })
//#endregion
this.form.current.setFieldsValue(this.record)
this.setState({ loading: false })
this.call()
}
/**
* 获取数据
* 可以对postData进行数据结构调整
* [异步,必要]
* @returns
*/
async getData() {
const form = this.form.current
const valid = await form.validateFields()
if (valid) {
const postData = form.getFieldsValue()
//#region 从前段转换后端所需格式
const { patrolInfo } = postData
// checkbox
checkboxKeys.forEach(key => {
if (patrolInfo[key]) {
patrolInfo[key] = sortBy(patrolInfo[key], p => +p).join(',')
}
})
//#endregion
return postData
}
}
//#region 自定义方法
/**
* 表单change事件处理,包括了所有字段的change
* [异步,非必要]
* @param {*} changedValues
* @param {*} allValues
*/
async onValuesChange(changedValues, allValues) {
const { patrolInfo } = changedValues
const key = Object.keys(patrolInfo).shift()
if (
[
'adjacentConstruction',
'chemicalErosion',
'repairAndReinforce',
'historicalCalamity',
'functionalChange',
].includes(key)
) {
this.checkedNone(patrolInfo[key], key)
}
}
checkedNone(value, key) {
const form = this.form.current
if (first(value) == 0 && value.length > 1) {
// 在'无'之后选中其他值
value.shift()
form.setFieldsValue({
patrolInfo: { [key]: value },
})
} else if (last(value) == 0 && value.length > 1) {
// 在其他值之后选中'无'
value = ['0']
form.setFieldsValue({
patrolInfo: { [key]: value },
})
}
return value
}
//#endregion
render() {
const { loading, codes } = this.state
return (
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
<Form
initialValues={initialValues}
ref={this.form}
{...layout}
onValuesChange={(changedValues, allValues) =>
this.onValuesChange(changedValues, allValues)
}
>
<Form.Item
label="房屋场地"
name={['patrolInfo', 'houseSite']}
rules={[{ required: true, message: '请选择房屋场地' }]}
>
<Checkbox.Group>
{codes.houseHouseSite.map(item => (
<Checkbox key={item.code} value={item.code}>
{item.value}
</Checkbox>
))}
</Checkbox.Group>
</Form.Item>
<Form.Item
label="相邻施工"
name={['patrolInfo', 'adjacentConstruction']}
rules={[{ required: true, message: '请选择相邻施工' }]}
>
<Checkbox.Group>
{codes.houseAdjacentConstruction.map(item => (
<Checkbox key={item.code} value={item.code}>
{item.value}
</Checkbox>
))}
</Checkbox.Group>
</Form.Item>
<Form.Item
label="化学侵蚀"
name={['patrolInfo', 'chemicalErosion']}
rules={[{ required: true, message: '请选择化学侵蚀' }]}
>
<Checkbox.Group>
{codes.houseChemicalErosion.map(item => (
<Checkbox key={item.code} value={item.code}>
{item.value}
</Checkbox>
))}
</Checkbox.Group>
</Form.Item>
<Form.Item
label="结构拆改"
name={['patrolInfo', 'structuralDismantling']}
rules={[{ required: true, message: '请选择结构拆改' }]}
>
<Radio.Group buttonStyle="solid">
{codes.houseStructuralDismantling.map(item => (
<Radio.Button key={item.code} value={+item.code}>
{item.value}
</Radio.Button>
))}
</Radio.Group>
</Form.Item>
<Form.Item
label="加层改造"
name={['patrolInfo', 'addingLayer']}
rules={[{ required: true, message: '请选择加层改造' }]}
>
<Radio.Group buttonStyle="solid">
{codes.houseAddingLayer.map(item => (
<Radio.Button key={item.code} value={+item.code}>
{item.value}
</Radio.Button>
))}
</Radio.Group>
</Form.Item>
<Form.Item
label="修缮加固"
name={['patrolInfo', 'repairAndReinforce']}
rules={[{ required: true, message: '请选择修缮加固' }]}
>
<Checkbox.Group>
{codes.houseRepairAndReinforce.map(item => (
<Checkbox key={item.code} value={item.code}>
{item.value}
</Checkbox>
))}
</Checkbox.Group>
</Form.Item>
<Form.Item
label="历史灾害"
name={['patrolInfo', 'historicalCalamity']}
rules={[{ required: true, message: '请选择历史灾害' }]}
>
<Checkbox.Group>
{codes.houseHistoricalCalamity.map(item => (
<Checkbox key={item.code} value={item.code}>
{item.value}
</Checkbox>
))}
</Checkbox.Group>
</Form.Item>
<Form.Item
label="使用功能变更"
name={['patrolInfo', 'functionalChange']}
rules={[{ required: true, message: '请选择使用功能变更' }]}
>
<Checkbox.Group>
{codes.houseFunctionalChange.map(item => (
<Checkbox key={item.code} value={item.code}>
{item.value}
</Checkbox>
))}
</Checkbox.Group>
</Form.Item>
<Form.Item label="其他调查内容" name={['patrolInfo', 'otherContents']}>
<Input.TextArea autoSize placeholder="请输入其他调查内容" />
</Form.Item>
</Form>
</Spin>
)
}
}

View File

@@ -0,0 +1,102 @@
import React, { Component } from 'react'
import { Form, Input, Radio, Spin } from 'antd'
import { AntIcon } from 'components'
import { cloneDeep, first, isEqual, last, sortBy } from 'lodash'
const layout = {
labelCol: { flex: '150px' },
wrapperCol: { flex: '1' },
}
export default class result extends Component {
state = {
loading: true,
codes: {
patrolResult: [
{ code: '0', value: '正常' },
{ code: '-1', value: '异常' },
],
},
}
form = React.createRef()
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
componentDidMount() {
this.fillData({
record: this.props.record,
})
}
call() {
if (this.props.onRef) {
this.props.onRef(this)
}
}
/**
* 填充数据
* 可以在设置this.record之后对其作出数据结构调整
* [异步,必要]
* @param {*} params
*/
async fillData(params) {
this.record = cloneDeep(params.record)
//#region 从后端转换成前段所需格式
//#endregion
this.form.current && this.form.current.setFieldsValue(this.record)
this.setState({ loading: false })
this.call()
}
/**
* 获取数据
* 可以对postData进行数据结构调整
* [异步,必要]
* @returns
*/
async getData() {
const form = this.form.current
const valid = await form.validateFields()
if (valid) {
const postData = form.getFieldsValue()
//#region 从前段转换后端所需格式
//#endregion
return postData
}
}
render() {
const { loading, codes } = this.state
return (
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
<Form {...layout} ref={this.form}>
<Form.Item
label="正常与否"
name={['patrolInfo', 'patrolResult']}
rules={[{ required: true, message: '请选择本期巡查结果' }]}
>
<Radio.Group buttonStyle="solid">
{codes.patrolResult.map(item => {
return (
<Radio.Button key={item.code} value={+item.code}>
{item.value}
</Radio.Button>
)
})}
</Radio.Group>
</Form.Item>
<Form.Item label="异常情况描述" name={['patrolInfo', 'patrolResultRemark']}>
<Input.TextArea autoSize placeholder="请输入异常情况描述" />
</Form.Item>
</Form>
</Spin>
)
}
}

View File

@@ -0,0 +1,15 @@
import React, { Component } from 'react'
import { Button } from 'antd'
export default class index extends Component {
render() {
return (
<div>
<Button onClick={() => window.openContentWindow({
title: '房屋表单',
path: 'business/house/info/form',
})}>打开表单</Button>
</div>
)
}
}

View File

@@ -0,0 +1,115 @@
import React, { Component } from 'react'
import { Form, Spin, TreeSelect } from 'antd'
import { AntIcon } from 'components'
import { cloneDeep } from 'lodash'
import { api } from 'common/api'
export default class dataForm extends Component {
state = {
// 加载状态
loading: true,
options: {
orgData: [],
areaData: [],
orgCheckedKeys: [],
},
}
// 表单实例
form = React.createRef()
// 初始化数据
id = ''
/**
* mount后回调
*/
componentDidMount() {
this.props.created && this.props.created(this)
}
async fillData(params) {
this.id = params.id
//#region 从后端转换成前段所需格式
const orgData = await this.loadOrgData()
const areaData = await this.loadAreaData()
const orgCheckedKeys = await this.loadMemberOwn(this.id)
this.setState({
options: {
orgData,
areaData,
orgCheckedKeys,
},
})
this.form.current.setFieldsValue({
id: this.id,
grantOrgIdList: orgCheckedKeys,
grantAreaCodeList: [],
})
this.setState({
loading: false,
})
}
/**
* 获取数据
* 可以对postData进行数据结构调整
* [异步,必要]
* @returns
*/
async getData() {
const form = this.form.current
const valid = await form.validateFields()
if (valid) {
const postData = form.getFieldsValue()
if (this.id) {
postData.id = this.id
}
//#region 从前段转换后端所需格式
//#endregion
return postData
}
}
//#region 自定义方法
async loadOrgData() {
const { data } = await api.getOrgTree()
return data
}
async loadAreaData() {
const { data } = await api.getAreaTree()
return data
}
async loadMemberOwn(id) {
const { data } = await api.houseMemberOwnData({ id })
return data
}
render() {
return (
<Form ref={this.form} className="yo-form">
<Spin spinning={this.state.loading} indicator={<AntIcon type="loading" />}>
<div className="yo-form-group">
<Form.Item label="选择机构" name="grantOrgIdList">
<TreeSelect
showCheckedStrategy="SHOW_PARENT"
treeData={this.state.options.orgData}
placeholder="请选择机构"
treeCheckable
/>
</Form.Item>
<Form.Item label="选择区域" name="grantAreaCodeList">
<TreeSelect
showCheckedStrategy="SHOW_PARENT"
treeData={this.state.options.areaData}
placeholder="请选择所属区域"
treeCheckable
/>
</Form.Item>
</div>
</Spin>
</Form>
)
}
}

View File

@@ -0,0 +1,294 @@
import React, { Component } from 'react'
import { Form, Input, DatePicker, Select, Radio, Spin, TreeSelect } from 'antd'
import { AntIcon } from 'components'
import { cloneDeep } from 'lodash'
import { api } from 'common/api'
import moment from 'moment'
const initialValues = {
sex: 0,
sysEmpParam: {},
}
export default class form extends Component {
state = {
// 加载状态
loading: true,
lockRole: false,
options: {
orgData: [],
roleData: [],
},
}
// 表单实例
form = React.createRef()
// 初始化数据
record = {}
/**
* mount后回调
*/
componentDidMount() {
this.props.created && this.props.created(this)
}
/**
* 填充数据
* 可以在设置this.record之后对其作出数据结构调整
* [异步,必要]
* @param {*} params
*/
async fillData(params) {
//#region 从后端转换成前段所需格式
if (params.id) {
this.record = (await api.houseMemberDetail({ id: params.id })).data
}
const orgData = await this.loadOrgData()
const roleData = await this.LoadRoleData()
// 日期特殊处理
if (this.record.birthday) {
this.record.birthday = moment(this.record.birthday)
}
// 提交的时候是"param",而获取下来却是"info",在这里转换一下
if (this.record.sysEmpInfo) {
this.record.sysEmpParam = this.record.sysEmpInfo
delete this.record.sysEmpInfo
} else if (!this.record.sysEmpParam) {
this.record.sysEmpParam = {
extIds: [],
}
}
if (params.orgId) {
this.record.sysEmpParam.orgId = params.orgId
}
const defaultRole = params.id
? await this.loadOwnRole(params.id)
: await this.loadDefaultRole(params.orgId)
if (defaultRole.constructor === Array) {
this.record.roleId = defaultRole[0]
} else {
this.record.roleId = defaultRole.id
}
const lockRole = this.doLockRole(defaultRole)
this.setState({
options: {
orgData,
roleData,
},
lockRole,
})
this.record = {
...this.record,
}
//#endregion
this.form.current.setFieldsValue(this.record)
this.setState({
loading: false,
})
}
/**
* 获取数据
* 可以对postData进行数据结构调整
* [异步,必要]
* @returns
*/
async getData() {
const form = this.form.current
const valid = await form.validateFields()
if (valid) {
const postData = form.getFieldsValue()
if (this.record) {
postData.id = this.record.id
}
//#region 从前段转换后端所需格式
//console.log(postData)
//#endregion
return postData
}
}
//#region 自定义方法
async loadOrgData() {
const { data } = await api.getOrgTree()
return data
}
async LoadRoleData() {
const { data } = await api.houseMemberDefaultRoleList()
return data
}
async loadOwnRole(id) {
const { data } = await api.houseMemberOwnRole({ id })
return data
}
async loadDefaultRole(orgId) {
const { data } = await api.houseMemberDefaultRole({ orgId })
return data
}
async onOrgChange(orgId) {
this.setState({ loading: true })
const defaultRole = await this.loadDefaultRole(orgId)
const lockRole = this.doLockRole(defaultRole)
this.setState({ loading: false, lockRole })
}
doLockRole(defaultRole) {
if (defaultRole.constructor === Array) {
this.form.current.setFieldsValue({
roleId: defaultRole[0].id,
})
return true
} else {
this.form.current.setFieldsValue({
roleId: defaultRole.id,
})
return defaultRole.code === 'zone_manager'
}
}
render() {
return (
<Form initialValues={initialValues} ref={this.form} className="yo-form">
<Spin spinning={this.state.loading} indicator={<AntIcon type="loading" />}>
<div className="yo-form-group">
<Form.Item
label="所属组织机构"
name={['sysEmpParam', 'orgId']}
rules={[{ required: true, message: '所属组织机构' }]}
>
<TreeSelect
treeData={this.state.options.orgData}
dropdownStyle={{ maxHeight: '300px', overflow: 'auto' }}
treeDefaultExpandAll
placeholder="请选择所属组织机构"
onChange={value => this.onOrgChange(value)}
/>
</Form.Item>
<Form.Item
label="角色"
name="roleId"
rules={[{ required: true, message: '请选择角色' }]}
tooltip="片区内第一个用户必定为片区监管员,创建后不可更改角色"
>
<Select placeholder="请选择角色" disabled={this.state.lockRole}>
{this.state.options.roleData.map(item => {
return (
<Select.Option key={item.id} value={item.id}>
{item.name}
</Select.Option>
)
})}
</Select>
</Form.Item>
<Form.Item
label="账号"
name="account"
rules={[{ required: true, message: '请输入账号', trigger: 'blur' }]}
>
<Input autoComplete="off" placeholder="请输入账号" />
</Form.Item>
<Form.Item
label="姓名"
name="name"
rules={[{ required: true, message: '请输入姓名', trigger: 'blur' }]}
>
<Input autoComplete="off" placeholder="请输入姓名" />
</Form.Item>
{/* {this.props.mode == 'add' && (
<>
<Form.Item
label="密码"
name="password"
rules={[
{ required: true, message: '请输入密码', trigger: 'blur' },
]}
>
<Input.Password autoComplete="off" placeholder="请输入密码" />
</Form.Item>
<Form.Item
label="确认密码"
name="confirm"
rules={[
{ required: true, message: '请确认密码', trigger: 'blur' },
]}
>
<Input.Password autoComplete="off" placeholder="请确认密码" />
</Form.Item>
</>
)} */}
<Form.Item label="昵称" name="nickName">
<Input autoComplete="off" placeholder="请输入昵称" />
</Form.Item>
<Form.Item label="生日" name="birthday">
<DatePicker className="w-100-p" />
</Form.Item>
<Form.Item label="性别" name="sex">
<Radio.Group>
<Radio.Button value={0}>
<AntIcon className="mr-xxs" type="stop" />
<span>保密</span>
</Radio.Button>
<Radio.Button value={1}>
<AntIcon
style={{ color: '#1890ff' }}
className="mr-xxs"
type="man"
/>
<span></span>
</Radio.Button>
<Radio.Button value={2}>
<AntIcon
style={{ color: '#eb2f96' }}
className="mr-xxs"
type="woman"
/>
<span></span>
</Radio.Button>
</Radio.Group>
</Form.Item>
<Form.Item
label="邮箱"
name="email"
rules={[
{
pattern: /^\w{3,}(\.\w+)*@[A-z0-9]+(\.[A-z]{2,5}){1,2}$/,
message: '邮箱格式不正确',
trigger: 'blur',
},
]}
>
<Input autoComplete="off" placeholder="请输入邮箱" />
</Form.Item>
<Form.Item
label="手机号"
name="phone"
rules={[
{
pattern:
/^((13[0-9])|(14[5,7])|(15[^4,\\D])|(17[0,1,3,6-8])|(18[0-9])|(19[8,9])|(166))[0-9]{8}$/,
message: '手机号格式不正确',
trigger: 'blur',
},
]}
>
<Input autoComplete="off" placeholder="请输入手机号" maxLength={11} />
</Form.Item>
<Form.Item label="电话" name="tel">
<Input autoComplete="off" placeholder="请输入电话" />
</Form.Item>
</div>
</Spin>
</Form>
)
}
}

View File

@@ -0,0 +1,417 @@
import React, { Component } from 'react'
import {
Button,
Card,
Descriptions,
Divider,
Form,
Input,
List,
message as Message,
Popconfirm,
Select,
Switch,
Tag,
} from 'antd'
import {
AntIcon,
Auth,
Container,
Image,
ModalForm,
QueryList,
QueryTableActions,
QueryTreeLayout,
} from 'components'
import { api } from 'common/api'
import { toCamelCase } from 'util/format'
import { isEqual } from 'lodash'
import getDictData from 'util/dic'
import FormBody from './form'
import Selector from './selector'
import DataForm from './data'
// 配置页面所需接口函数
const apiAction = {
tree: api.getOrgTree,
page: api.houseMemberPage,
add: api.houseMemberAdd,
edit: api.houseMemberEdit,
delete: api.houseMemberDelete,
changeStatus: api.houseMemberChangeStatus,
resetPwd: api.sysUserResetPwd,
grantData: api.houseMemberGrantData,
}
// 用于弹窗标题
const name = '人员'
export default class index extends Component {
state = {
codes: {
sex: [],
commonStatus: [],
},
}
// 表格实例
list = React.createRef()
// 新增窗口实例
addForm = React.createRef()
// 编辑窗口实例
editForm = React.createRef()
dataForm = React.createRef()
// 树选中节点
selectId = undefined
selectorModal = React.createRef()
/**
* 阻止外部组件引发的渲染,提升性能
* 可自行添加渲染条件
* [必要]
* @param {*} props
* @param {*} state
* @returns
*/
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
/**
* 加载字典数据,之后开始加载表格数据
* 如果必须要加载字典数据,可直接对表格设置autoLoad=true
*/
componentDidMount() {
this.list.current.onLoading()
getDictData('sex', 'common_status').then(codes => {
this.setState({ codes }, () => {
this.list.current.onLoadData()
})
})
}
/**
* 调用加载数据接口,可在调用前对query进行处理
* [异步,必要]
* @param {*} params
* @param {*} query
* @returns
*/
loadData = async (params, query) => {
query = {
...query,
sysEmpParam: {
orgId: this.selectId,
},
}
const { data } = await apiAction.page({
...params,
...query,
})
return data
}
/**
* 调用树结构数据接口
* [异步,必要]
* @returns
*/
loadTreeData = async () => {
const { data } = await apiAction.tree()
return data
}
/**
* 树节点选中事件
* [必要]
* @param {*} id
*/
onSelectTree(id) {
this.selectId = id
this.list.current.onReloadData()
}
/**
* 绑定字典数据
* @param {*} code
* @param {*} name
* @returns
*/
bindCodeValue(code, name) {
name = toCamelCase(name)
const codes = this.state.codes[name]
if (codes) {
const c = codes.find(p => p.code == code)
if (c) {
return c.value
}
}
return null
}
/**
* 打开新增/编辑弹窗
* @param {*} modal
* @param {*} id
*/
onOpen(modal, id) {
modal.current.open({
orgId: this.selectId,
id,
})
}
/**
* 对表格上的操作进行统一处理
* [异步]
* @param {*} action
* @param {*} successMessage
*/
async onAction(action, successMessage) {
this.list.current.onLoading()
try {
await action
Message.success(successMessage)
this.list.current.onReloadData()
} catch {
this.list.current.onLoaded()
}
}
/**
* 删除
* @param {*} id
*/
onDelete(id) {
this.onAction(apiAction.delete({ id }), '删除成功')
}
//#region 自定义方法
renderItem(record) {
const {
id,
account,
name,
nickName,
avatar,
sex,
phone,
email,
status,
roleCode,
roleName,
orgName,
} = record
return (
<List.Item
key={id}
actions={[
<QueryTableActions>
<Auth auth="houseMember:edit">
<a onClick={() => this.onOpen(this.editForm, id)}>编辑</a>
</Auth>
<Auth auth="houseMember:delete">
<Popconfirm
placement="topRight"
title="是否确认删除"
onConfirm={() => this.onDelete(id)}
>
<a>删除</a>
</Popconfirm>
</Auth>
<Auth auth="houseMember:resetPwd">
<a onClick={() => this.onResetPassword(id)}>重置密码</a>
</Auth>
<Auth auth="houseMember:grantData">
<a onClick={() => this.onOpen(this.dataForm, id)}>授权额外数据</a>
</Auth>
</QueryTableActions>,
]}
>
<List.Item.Meta
avatar={
<>
<Image
type="avatar"
shape="square"
size={48}
id={avatar}
icon={<AntIcon type="user" />}
/>
{roleCode && roleCode.includes('house_security_manager') && (
<Button
size="small"
type="primary"
className="block w-100-p mt-xxs"
onClick={() => this.onOpen(this.selectorModal, id)}
>
选房
</Button>
)}
</>
}
title={
<>
{nickName || name}
{roleName &&
roleName.split(',').map((item, i) => (
<span key={i}>
<Divider type="vertical" />
<Tag color="pink">{item}</Tag>
</span>
))}
</>
}
description={account}
/>
<Descriptions className="flex-1" column={2}>
<Descriptions.Item label="部门">{orgName}</Descriptions.Item>
<Descriptions.Item label="性别">
{this.bindCodeValue(sex, 'sex')}
</Descriptions.Item>
<Descriptions.Item label="手机">{phone || '未设置'}</Descriptions.Item>
<Descriptions.Item label="邮箱">{email || '未设置'}</Descriptions.Item>
</Descriptions>
<div className="yo-list-content--h">
<Auth auth="houseMember:changeStatus">
<div className="yo-list-content--h--item text-center">
<Switch
checked={!status}
checkedChildren={this.bindCodeValue(0, 'common_status')}
unCheckedChildren={this.bindCodeValue(1, 'common_status')}
onChange={checked => this.onSetUserStatus(id, checked)}
/>
</div>
</Auth>
</div>
</List.Item>
)
}
onSetUserStatus(id, checked) {
this.onAction(
apiAction.changeStatus({
id,
status: +!checked,
}),
'设置成功'
)
}
onResetPassword(id) {
this.onAction(apiAction.resetPwd({ id }), '重置成功')
}
//#endregion
render() {
return (
<QueryTreeLayout
loadData={this.loadTreeData}
defaultExpanded={true}
onSelect={key => this.onSelectTree(key)}
>
<Container mode="fluid">
<Card bordered={false}>
<QueryList
ref={this.list}
autoLoad={false}
loadData={this.loadData}
query={
<Auth auth="houseMember:page">
<Form.Item label="关键词" name="searchValue">
<Input
autoComplete="off"
placeholder="请输入姓名、账号、手机号"
className="w-200"
/>
</Form.Item>
<Form.Item label="状态" name="searchStatus">
<Select
allowClear
placeholder="请选择状态"
className="w-200"
>
{this.state.codes.commonStatus.map(item => {
return (
<Select.Option
key={item.code}
value={item.code}
>
{item.value}
</Select.Option>
)
})}
</Select>
</Form.Item>
<Form.Item label="区域范围" name="treeNodeDataScope">
<Select
allowClear
placeholder="请选择状态"
className="w-200"
defaultValue={1}
>
<Select.Option value={1}>只看本级</Select.Option>
<Select.Option value={2}>查看本级及以下</Select.Option>
</Select>
</Form.Item>
</Auth>
}
operator={
<Button
icon={<AntIcon type="plus" />}
onClick={() => this.onOpen(this.addForm)}
>
新增
{name}
</Button>
}
renderItem={record => this.renderItem(record)}
/>
</Card>
</Container>
<ModalForm
title={`新增${name}`}
action={apiAction.add}
ref={this.addForm}
onSuccess={() => this.list.current.onReloadData()}
>
<FormBody mode="add" />
</ModalForm>
<ModalForm
title={`编辑${name}`}
action={apiAction.edit}
ref={this.editForm}
onSuccess={() => this.list.current.onReloadData()}
>
<FormBody mode="edit" />
</ModalForm>
<ModalForm
title="数据授权"
action={apiAction.grantData}
ref={this.dataForm}
onSuccess={() => this.list.current.onReloadData()}
>
<DataForm />
</ModalForm>
<ModalForm
bodyStyle={{ padding: 0 }}
footer={false}
width="80%"
ref={this.selectorModal}
>
<Selector />
</ModalForm>
</QueryTreeLayout>
)
}
}

View File

@@ -0,0 +1,66 @@
import React, { Component } from 'react'
import { Tabs } from 'antd'
import SelectorList from './selector-list'
import SelectedList from './selected-list'
export default class index extends Component {
state = {
userId: '',
}
selectorList = React.createRef()
selectedList = React.createRef()
/**
* mount后回调
*/
componentDidMount() {
this.props.created && this.props.created(this)
}
/**
* 填充数据
* 可以在设置this.record之后对其作出数据结构调整
* [异步,必要]
* @param {*} params
*/
async fillData(params) {
this.setState({
userId: params.id,
})
}
//#region 自定义方法
onReloadAll() {
if (this.selectorList.current) {
this.selectorList.current.table.current.onReloadData()
}
if (this.selectedList.current) {
this.selectedList.current.table.current.onReloadData()
}
}
//#endregion
render() {
const { userId } = this.state
return (
<Tabs tabBarStyle={{ padding: '0 24px' }}>
<Tabs.TabPane key="1" tab="选房">
<SelectorList
userId={userId}
ref={this.selectorList}
onReloadAll={() => this.onReloadAll()}
/>
</Tabs.TabPane>
<Tabs.TabPane key="2" tab="已选">
<SelectedList
userId={userId}
ref={this.selectedList}
onReloadAll={() => this.onReloadAll()}
/>
</Tabs.TabPane>
</Tabs>
)
}
}

View File

@@ -0,0 +1,263 @@
import React, { Component } from 'react'
import { Button, Card, Form, Input, InputNumber, message as Message, Radio, Select } from 'antd'
import { AntIcon, Auth, Container, QueryTable } from 'components'
import { api } from 'common/api'
import { isEqual } from 'lodash'
import getDictData from 'util/dic'
import { toCamelCase } from 'util/format'
import { getSearchInfo } from 'util/query'
/**
* 注释段[\/**\/]为必须要改
*/
/**
* 配置页面所需接口函数
*/
const apiAction = {
page: api.houseSelectedPage,
}
/**
* 统一配置权限标识
* [必要]
*/
const authName = 'houseSelector'
export default class index extends Component {
state = {
codes: {
houseType: [],
houseIndustry: [],
},
saving: false,
type: '',
selectedRowKeys: [],
}
// 表格实例
table = React.createRef()
columns = [
{
title: '房屋编码',
dataIndex: 'houseCode',
sorter: true,
width: 300,
render: (text, record) =>
`${record.areaName}-${record.roadName}-${record.commName}-${
record.fullProjName
}-${record.no.toString().padStart(3, '0')}`,
},
{
title: '房屋性质及行业',
dataIndex: 'type',
sorter: true,
width: 150,
render: text => this.bindCodeValue(text, 'house_type'),
},
{
title: '地址',
dataIndex: 'address',
sorter: true,
},
{
title: '登记时间',
dataIndex: 'createdTime',
sorter: true,
width: 150,
},
]
/**
* 阻止外部组件引发的渲染,提升性能
* 可自行添加渲染条件
* [必要]
* @param {*} props
* @param {*} state
* @returns
*/
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
/**
* 加载字典数据,之后开始加载表格数据
* 如果必须要加载字典数据,可直接对表格设置autoLoad=true
*/
componentDidMount() {
const { onLoading, onLoadData } = this.table.current
onLoading()
getDictData('house_type', 'house_industry').then(codes => {
this.setState({ codes }, () => {
onLoadData()
})
})
}
/**
* 调用加载数据接口,可在调用前对query进行处理
* [异步,必要]
* @param {*} params
* @param {*} query
* @returns
*/
loadData = async (params, query) => {
const searchInfo = getSearchInfo({
query,
queryType: { type: '=' },
})
const { data } = await apiAction.page({
...params,
searchInfo,
userId: this.props.userId,
})
return data
}
/**
* 绑定字典数据
* @param {*} code
* @param {*} name
* @returns
*/
bindCodeValue(code, name) {
name = toCamelCase(name)
const codes = this.state.codes[name]
if (codes) {
const c = codes.find(p => p.code === code)
if (c) {
return c.value
}
}
return null
}
/**
* 对表格上的操作进行统一处理
* [异步]
* @param {*} action
* @param {*} successMessage
*/
async onAction(action, successMessage) {
const { onLoading, onLoaded, onReloadData } = this.table.current
onLoading()
try {
if (action) {
await action
}
if (successMessage) {
Message.success(successMessage)
}
onReloadData()
} catch {
onLoaded()
}
}
//#region 自定义方法
async onHouseSelectRevoke() {
const { selectedRowKeys } = this.state
const { userId, onReloadAll } = this.props
this.setState({ saving: true })
await this.onAction(
api.houseSelectRevoke({
ids: selectedRowKeys,
userId,
}),
'撤销成功'
)
this.setState({
saving: false,
selectedRowKeys: [],
})
if (onReloadAll) {
onReloadAll()
}
}
//#endregion
render() {
const { codes, saving, type, selectedRowKeys } = this.state
return (
<Card bordered={false} className="mb-none">
<QueryTable
ref={this.table}
autoLoad={false}
loadData={this.loadData}
columns={this.columns}
rowSelection={{
selectedRowKeys,
onChange: selectedRowKeys => this.setState({ selectedRowKeys }),
}}
queryInitialValues={{
type: '',
}}
onQueryChange={values => {
if (values.hasOwnProperty('type')) {
this.setState({ type: values.type })
}
}}
query={
<Auth auth={{ [authName]: 'selectorPage' }}>
<Form.Item label="编号" name="no">
<InputNumber
formatter={value => value && value.padStart(3, '0')}
max={999}
min={1}
precision={0}
step={1}
placeholder="请输入房屋序号"
/>
</Form.Item>
<Form.Item label="房屋性质" name="type">
<Radio.Group buttonStyle="solid">
<Radio.Button value="">全部</Radio.Button>
{codes.houseType.map(item => (
<Radio.Button key={item.code} value={item.code}>
{item.value}
</Radio.Button>
))}
</Radio.Group>
</Form.Item>
{type == 2 && (
<Form.Item label="行业" name="industry">
<Select allowClear className="w-150" placeholder="请选择行业">
{codes.houseIndustry.map(item => (
<Select.Option key={item.code} value={item.code}>
{item.value}
</Select.Option>
))}
</Select>
</Form.Item>
)}
<Form.Item label="地址" name="address">
<Input autoComplete="off" placeholder="请输入地址" />
</Form.Item>
<Form.Item label="房屋唯一编码" name="houseCode">
<Input autoComplete="off" placeholder="请输入房屋唯一编码" />
</Form.Item>
</Auth>
}
operator={
<Auth auth={{ [authName]: 'select' }}>
<Button
loading={saving}
disabled={!selectedRowKeys.length}
type="danger"
onClick={() => this.onHouseSelectRevoke()}
>
撤销
</Button>
</Auth>
}
/>
</Card>
)
}
}

View File

@@ -0,0 +1,263 @@
import React, { Component } from 'react'
import { Button, Card, Form, Input, InputNumber, message as Message, Radio, Select } from 'antd'
import { AntIcon, Auth, Container, QueryTable } from 'components'
import { api } from 'common/api'
import { isEqual } from 'lodash'
import getDictData from 'util/dic'
import { toCamelCase } from 'util/format'
import { getSearchInfo } from 'util/query'
/**
* 注释段[\/**\/]为必须要改
*/
/**
* 配置页面所需接口函数
*/
const apiAction = {
page: api.houseSelectorPage,
}
/**
* 统一配置权限标识
* [必要]
*/
const authName = 'houseSelector'
export default class index extends Component {
state = {
codes: {
houseType: [],
houseIndustry: [],
},
saving: false,
type: '',
selectedRowKeys: [],
}
// 表格实例
table = React.createRef()
columns = [
{
title: '房屋编码',
dataIndex: 'houseCode',
sorter: true,
width: 300,
render: (text, record) =>
`${record.areaName}-${record.roadName}-${record.commName}-${
record.fullProjName
}-${record.no.toString().padStart(3, '0')}`,
},
{
title: '房屋性质及行业',
dataIndex: 'type',
sorter: true,
width: 150,
render: text => this.bindCodeValue(text, 'house_type'),
},
{
title: '地址',
dataIndex: 'address',
sorter: true,
},
{
title: '登记时间',
dataIndex: 'createdTime',
sorter: true,
width: 150,
},
]
/**
* 阻止外部组件引发的渲染,提升性能
* 可自行添加渲染条件
* [必要]
* @param {*} props
* @param {*} state
* @returns
*/
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
/**
* 加载字典数据,之后开始加载表格数据
* 如果必须要加载字典数据,可直接对表格设置autoLoad=true
*/
componentDidMount() {
const { onLoading, onLoadData } = this.table.current
onLoading()
getDictData('house_type', 'house_industry').then(codes => {
this.setState({ codes }, () => {
onLoadData()
})
})
}
/**
* 调用加载数据接口,可在调用前对query进行处理
* [异步,必要]
* @param {*} params
* @param {*} query
* @returns
*/
loadData = async (params, query) => {
const searchInfo = getSearchInfo({
query,
queryType: { type: '=' },
})
const { data } = await apiAction.page({
...params,
searchInfo,
userId: this.props.userId,
})
return data
}
/**
* 绑定字典数据
* @param {*} code
* @param {*} name
* @returns
*/
bindCodeValue(code, name) {
name = toCamelCase(name)
const codes = this.state.codes[name]
if (codes) {
const c = codes.find(p => p.code === code)
if (c) {
return c.value
}
}
return null
}
/**
* 对表格上的操作进行统一处理
* [异步]
* @param {*} action
* @param {*} successMessage
*/
async onAction(action, successMessage) {
const { onLoading, onLoaded, onReloadData } = this.table.current
onLoading()
try {
if (action) {
await action
}
if (successMessage) {
Message.success(successMessage)
}
onReloadData()
} catch {
onLoaded()
}
}
//#region 自定义方法
async onHouseSelect() {
const { selectedRowKeys } = this.state
const { userId, onReloadAll } = this.props
this.setState({ saving: true })
await this.onAction(
api.houseSelect({
ids: selectedRowKeys,
userId,
}),
'选房成功'
)
this.setState({
saving: false,
selectedRowKeys: [],
})
if (onReloadAll) {
onReloadAll()
}
}
//#endregion
render() {
const { codes, saving, type, selectedRowKeys } = this.state
return (
<Card bordered={false} className="mb-none">
<QueryTable
ref={this.table}
autoLoad={false}
loadData={this.loadData}
columns={this.columns}
rowSelection={{
selectedRowKeys,
onChange: selectedRowKeys => this.setState({ selectedRowKeys }),
}}
queryInitialValues={{
type: '',
}}
onQueryChange={values => {
if (values.hasOwnProperty('type')) {
this.setState({ type: values.type })
}
}}
query={
<Auth auth={{ [authName]: 'selectorPage' }}>
<Form.Item label="编号" name="no">
<InputNumber
formatter={value => value && value.padStart(3, '0')}
max={999}
min={1}
precision={0}
step={1}
placeholder="请输入房屋序号"
/>
</Form.Item>
<Form.Item label="房屋性质" name="type">
<Radio.Group buttonStyle="solid">
<Radio.Button value="">全部</Radio.Button>
{codes.houseType.map(item => (
<Radio.Button key={item.code} value={item.code}>
{item.value}
</Radio.Button>
))}
</Radio.Group>
</Form.Item>
{type == 2 && (
<Form.Item label="行业" name="industry">
<Select allowClear className="w-150" placeholder="请选择行业">
{codes.houseIndustry.map(item => (
<Select.Option key={item.code} value={item.code}>
{item.value}
</Select.Option>
))}
</Select>
</Form.Item>
)}
<Form.Item label="地址" name="address">
<Input autoComplete="off" placeholder="请输入地址" />
</Form.Item>
<Form.Item label="房屋唯一编码" name="houseCode">
<Input autoComplete="off" placeholder="请输入房屋唯一编码" />
</Form.Item>
</Auth>
}
operator={
<Auth auth={{ [authName]: 'select' }}>
<Button
loading={saving}
disabled={!selectedRowKeys.length}
type="primary"
onClick={() => this.onHouseSelect()}
>
确认选择
</Button>
</Auth>
}
/>
</Card>
)
}
}

View File

@@ -0,0 +1,273 @@
import React, { Component } from 'react'
import { Cascader, Form, Input, InputNumber, Radio, Spin, TreeSelect } from 'antd'
import { AntIcon } from 'components'
import { cloneDeep } from 'lodash'
import { api } from 'common/api'
import { numberToChinese } from 'util/format'
const initialValues = {
sort: 100,
type: 1,
}
export default class form extends Component {
state = {
// 加载状态
loading: true,
exist: false,
options: {
areaData: [],
},
}
areaCode = ''
houseType = 1
// 表单实例
form = React.createRef()
// 初始化数据
record = {}
initRecord = {}
/**
* mount后回调
*/
componentDidMount() {
this.props.created && this.props.created(this)
}
/**
* 填充数据
* 可以在设置this.record之后对其作出数据结构调整
* [异步,必要]
* @param {*} params
*/
async fillData(params) {
let areaCodeDefault = params.pid ? params.pid : ''
this.houseType = params.record ? params.record.type : 1
if (params.id) {
this.setState({
loading: true,
})
api.houseProjectGetById({ projectId: params.id }).then(({ data }) => {
areaCodeDefault = data.areaCode
this.record = data
this.setState({
loading: false,
})
})
}
// this.record = cloneDeep(params.record)
this.initRecord = cloneDeep(params.record)
//#region 从后端转换成前段所需格式
const areaData = await this.loadAreaData()
this.setState({
exist: !!params.id,
options: { areaData },
})
const areaCode = []
const findCode = (data, level) => {
level = level || 0
for (let i = 0; i < data.length; i++) {
const item = data[i]
areaCode[level] = item.code
if (item.code === areaCodeDefault) {
areaCode.length = level + 1
return true
}
if (item.children && item.children.length) {
const found = findCode(item.children, level + 1)
if (found) {
return true
}
}
}
}
if (areaCodeDefault) {
findCode(areaData)
this.areaCode = areaCodeDefault
if (!this.state.exist) {
this.nextSort(this.areaCode, this.houseType)
}
}
this.record = {
pid: params.pid,
...this.record,
areaCode:
areaCode.length > 0 && areaCode[areaCode.length - 1].length == 12 ? areaCode : [],
}
//#endregion
this.form.current.setFieldsValue(this.record)
this.setState({
loading: false,
})
}
/**
* 获取数据
* 可以对postData进行数据结构调整
* [异步,必要]
* @returns
*/
async getData() {
const form = this.form.current
const valid = await form.validateFields()
if (valid) {
const postData = form.getFieldsValue()
if (this.record) {
postData.id = this.record.id
}
//#region 从前段转换后端所需格式
postData.areaCode = postData.areaCode[postData.areaCode.length - 1]
//#endregion
return postData
}
}
async loadAreaData() {
const { data } = await api.getAreaTree()
console.log(data)
const clearChiildren = data => {
data.forEach(item => {
if (item.children && item.children.length) {
clearChiildren(item.children)
} else {
delete item.children
}
})
}
clearChiildren(data)
return data
}
async nextSort(areaCode, houseType) {
this.loading = true
if (
!!this.initRecord &&
this.initRecord.areaCode == areaCode &&
this.initRecord.type == houseType
) {
this.form.current.setFieldsValue({
name: this.initRecord.name,
sort: this.initRecord.sort,
})
} else if (areaCode.length < 12) {
this.form.current.setFieldsValue({
name: '',
sort: 0,
areaCode: [],
})
} else {
await api
.houseProjectNextSort({ areaCode, type: houseType })
.then(({ data }) => {
this.form.current.setFieldsValue({
name: `项目${numberToChinese(data)}`,
sort: data,
})
})
.catch(() => {
this.form.current.setFieldsValue({
name: '',
sort: 0,
areaCode: [],
})
})
.finally(() => {
this.loading = false
})
}
}
onHouseTypeChange(e) {
this.houseType = e.target.value
if (this.areaCode != '') {
this.nextSort(this.areaCode, this.houseType)
}
}
onAreaCodeChange(value) {
this.areaCode = value[value.length - 1]
if (this.houseType > 0) {
this.nextSort(this.areaCode, this.houseType)
}
}
render() {
return (
<Form initialValues={initialValues} ref={this.form} className="yo-form">
<Spin spinning={this.state.loading} indicator={<AntIcon type="loading" />}>
<div className="yo-form-group">
<Form.Item label="类型" name="type">
<Radio.Group
disabled={this.state.exist}
buttonStyle="solid"
onChange={e => this.onHouseTypeChange(e)}
>
<Radio.Button value={1}>
<span>住宅</span>
</Radio.Button>
<Radio.Button value={2}>
<span>非住宅</span>
</Radio.Button>
</Radio.Group>
</Form.Item>
<Form.Item
label="所属区域"
name="areaCode"
rules={[{ required: true, message: '请选择所属区域' }]}
>
<Cascader
disabled={this.state.exist}
options={this.state.options.areaData}
fieldNames={{
label: 'name',
value: 'code',
children: 'children',
}}
expandTrigger="hover"
// changeOnSelect
placeholder="请选择所属区域"
onChange={(val, selectedOptions) => this.onAreaCodeChange(val)}
/>
</Form.Item>
<Form.Item
label="项目名称"
name="name"
rules={[{ required: true, message: '片区名称', trigger: 'blur' }]}
>
<Input
autoComplete="off"
placeholder="选择所属区域和类型之后自动生成"
disabled={true}
/>
</Form.Item>
<Form.Item label="序号" name="sort">
<InputNumber
max={1000}
min={0}
placeholder="选择所属区域和类型之后自动生成"
className="w-100-p"
disabled={true}
/>
</Form.Item>
<Form.Item label="备注" name="note">
<Input.TextArea
rows="4"
placeholder="填写房屋所属单位的名称、道路的名称或大厦的名称比如XX中学、XX大厦、XX小区等。登记项目时应在项目备注中明确项目所指对象。"
/>
</Form.Item>
</div>
</Spin>
</Form>
)
}
}

View File

@@ -0,0 +1,313 @@
import React, { Component } from 'react'
import { Button, Radio, Card, Form, Input, message as Message, Popconfirm } from 'antd'
import {
AntIcon,
Auth,
Container,
ModalForm,
QueryTable,
QueryTableActions,
QueryTreeLayout,
} from 'components'
import { api } from 'common/api'
import auth from 'components/authorized/handler'
import { toCamelCase } from 'util/format'
import { isEqual } from 'lodash'
import getDictData from 'util/dic'
import FormBody from './form'
const apiAction = {
tree: api.getAreaTree,
page: api.getHouseProjectPage,
add: api.houseProejctAdd,
edit: api.houseProejctEdit,
delete: api.houseProejctDelete,
}
const name = '项目'
export default class index extends Component {
state = {
codes: {
houseType: [],
},
type: 1,
}
// 表格实例
table = React.createRef()
// 新增窗口实例
addForm = React.createRef()
// 编辑窗口实例
editForm = React.createRef()
// 树选中节点
selectCode = undefined
columns = [
{
title: '项目名称',
dataIndex: 'name',
width: 150,
sorter: true,
},
{
title: '社区',
dataIndex: 'areaName',
width: 100,
sorter: true,
},
{
title: '备注',
dataIndex: 'note',
width: 150,
sorter: true,
},
{
title: '类型',
dataIndex: 'type',
sorter: true,
width: 80,
render: text => <>{this.bindCodeValue(text, 'house_type')}</>,
},
]
/**
* 构造函数,在渲染前动态添加操作字段等
* @param {*} props
*/
constructor(props) {
super(props)
const flag = auth({ houseProjectInfo: [['edit'], ['delete']] })
if (flag) {
this.columns.push({
title: '操作',
width: 150,
dataIndex: 'actions',
render: (text, record) => (
<QueryTableActions>
<Auth auth="houseProjectInfo:edit">
<a onClick={() => this.onOpen(this.editForm, record.id)}>编辑</a>
</Auth>
<Auth auth="houseProjectInfo:delete">
<Popconfirm
placement="topRight"
title="是否确认删除"
onConfirm={() => this.onDelete(record)}
>
<a>删除</a>
</Popconfirm>
</Auth>
</QueryTableActions>
),
})
}
}
/**
* 阻止外部组件引发的渲染,提升性能
* 可自行添加渲染条件
* [必要]
* @param {*} props
* @param {*} state
* @returns
*/
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
/**
* 加载字典数据,之后开始加载表格数据
* 如果必须要加载字典数据,可直接对表格设置autoLoad=true
*/
componentDidMount() {
this.table.current.onLoading()
getDictData('house_type').then(res => {
this.setState(
{
codes: res,
},
() => {
this.table.current.onLoadData()
}
)
})
}
/**
* 调用加载数据接口,可在调用前对query进行处理
* [异步,必要]
* @param {*} params
* @param {*} query
* @returns
*/
loadData = async (params, query) => {
query = {
...query,
pid: this.selectCode,
}
//首次加载根据code列升序排序
// if (!params.sortField) {
// params.sortField = 'code';
// params.sortOrder = 'ascend';
// }
const { data } = await apiAction.page({
...params,
...query,
})
return data
}
/**
* 调用树结构数据接口
* [异步,必要]
* @returns
*/
loadTreeData = async () => {
const { data } = await apiAction.tree()
return data
}
/**
* 树节点选中事件
* [必要]
* @param {*} id
*/
onSelectTree(code) {
this.selectCode = code
this.table.current.onReloadData()
}
/**
* 绑定字典数据
* @param {*} code
* @param {*} name
* @returns
*/
bindCodeValue(code, name) {
name = toCamelCase(name)
const codes = this.state.codes[name]
if (codes) {
const c = codes.find(p => +p.code === code)
if (c) {
return c.value
}
}
return null
}
/**
* 打开新增/编辑弹窗
* @param {*} modal
* @param {*} record
*/
onOpen(modal, id) {
modal.current.open({
pid: this.selectCode,
// record,
id,
})
}
/**
* 对表格上的操作进行统一处理
* [异步]
* @param {*} action
* @param {*} successMessage
*/
async onAction(action, successMessage) {
this.table.current.onLoading()
try {
await action
Message.success(successMessage)
this.table.current.onReloadData()
} catch {
this.table.current.onLoaded()
}
}
/**
* 删除
* @param {*} record
*/
onDelete(record) {
this.onAction(apiAction.delete(record), '删除成功')
}
render() {
return (
<QueryTreeLayout
loadData={this.loadTreeData}
defaultExpanded={true}
onSelect={key => this.onSelectTree(key)}
replaceFields={{ value: 'code', title: 'name', children: 'children' }}
>
<Container mode="fluid">
<Card bordered={false}>
<QueryTable
ref={this.table}
autoLoad={false}
loadData={this.loadData}
columns={this.columns}
queryInitialValues={{
type: 1,
}}
onQueryChange={values => {
if (values.hasOwnProperty('type')) {
this.setState({ type: values.type })
}
}}
query={
<Auth auth="houseProjectInfo:page">
<Form.Item label="项目类型" name="type">
<Radio.Group buttonStyle="solid">
<Radio.Button value={0}>全部</Radio.Button>
<Radio.Button value={1}>
<span>住宅</span>
</Radio.Button>
<Radio.Button value={2}>
<span>非住宅</span>
</Radio.Button>
</Radio.Group>
</Form.Item>
<Form.Item label="项目名称" name="name">
<Input autoComplete="off" placeholder="请输入项目名称" />
</Form.Item>
<Form.Item label="项目备注" name="note">
<Input autoComplete="off" placeholder="请输入项目备注" />
</Form.Item>
</Auth>
}
operator={
<Auth auth="houseProjectInfo:add">
<Button
icon={<AntIcon type="plus" />}
onClick={() => this.onOpen(this.addForm)}
>
新增{name}
</Button>
</Auth>
}
></QueryTable>
</Card>
</Container>
<ModalForm
title={`新增${name}`}
action={apiAction.add}
ref={this.addForm}
onSuccess={() => this.table.current.onReloadData()}
>
<FormBody />
</ModalForm>
<ModalForm
title={`编辑${name}`}
action={apiAction.edit}
ref={this.editForm}
onSuccess={() => this.table.current.onReloadData()}
>
<FormBody />
</ModalForm>
</QueryTreeLayout>
)
}
}

View File

@@ -0,0 +1,35 @@
import React, { Component } from 'react'
import { Card } from 'antd'
import Container from 'components/container'
import { api } from 'common/api'
import ReactJson from 'react-json-view'
export default class detail extends Component {
state = {
loading: false,
record: null,
}
componentDidMount() {
// 获取详细数据
const { id } = this.props.param
if (id) {
api.houseQueryDetail({ id }).then(({ data }) => {
this.setState({
record: data,
loading: false,
})
})
}
}
render() {
return (
<Container>
<Card>
<ReactJson src={this.state.record} />
</Card>
</Container>
)
}
}

View File

@@ -0,0 +1,571 @@
import React, { Component } from 'react'
import {
Button,
Card,
Checkbox,
Col,
DatePicker,
Form,
Input,
InputNumber,
message as Message,
Row,
Tag,
} from 'antd'
import {
AntIcon,
Auth,
Container,
InputNumberRange,
QueryTable,
QueryTableActions,
} from 'components'
import { api } from 'common/api'
import auth from 'components/authorized/handler'
import { first, isEqual, last } from 'lodash'
import getDictData from 'util/dic'
import { toCamelCase } from 'util/format'
import { getSearchDateRange, getSearchInfo, QueryType } from 'util/query'
/**
* 注释段[\/**\/]为必须要改
*/
/**
* 配置页面所需接口函数
*/
const apiAction = {
page: api.houseQueryPage,
}
/**
* 用于弹窗标题
* [必要]
*/
const name = '/**/'
/**
* 统一配置权限标识
* [必要]
*/
const authName = 'houseQuery'
export default class index extends Component {
state = {
codes: {
houseStatus: [],
houseType: [],
houseIndustry: [],
houseUsedStatus: [],
housePropertyRights: [],
landAttribute: [],
houseBaseInfo: [],
houseStructureType: [],
houseStorageOfDrawings: [],
houseGrade: [],
},
showDrawingMaterialText: false,
}
// 表格实例
table = React.createRef()
// 新增窗口实例
addForm = React.createRef()
// 编辑窗口实例
editForm = React.createRef()
columns = [
{
title: '房屋编码',
dataIndex: 'houseCode',
sorter: true,
width: 300,
render: (text, record) => (
<>
{`${record.areaName}-${record.roadName}-${record.commName}-${
record.note
}-${record.no.toString().padStart(3, '0')}`}
<br />
<Tag color="purple">{text}</Tag>
</>
),
},
{
title: '房屋性质及行业',
dataIndex: 'type',
sorter: true,
width: 150,
render: text => this.bindCodeValue(text, 'house_type'),
},
{
title: '地址',
dataIndex: 'address',
sorter: true,
},
{
title: '建档状态',
dataIndex: 'state',
sorter: true,
width: 100,
render: text => this.bindCodeValue(text, 'house_status'),
},
]
/**
* 构造函数,在渲染前动态添加操作字段等
* @param {*} props
*/
constructor(props) {
super(props)
const flag = auth({ [authName]: 'detail' })
if (flag) {
this.columns.push({
title: '操作',
width: 150,
dataIndex: 'actions',
render: (text, record) => (
<QueryTableActions>
<Auth auth={{ [authName]: 'detail' }}>
<a onClick={() => this.onOpen(record.id)}>查看</a>
</Auth>
</QueryTableActions>
),
})
}
}
/**
* 阻止外部组件引发的渲染,提升性能
* 可自行添加渲染条件
* [必要]
* @param {*} props
* @param {*} state
* @returns
*/
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
/**
* 加载字典数据,之后开始加载表格数据
* 如果必须要加载字典数据,可直接对表格设置autoLoad=true
*/
componentDidMount() {
const { onLoading, onLoadData } = this.table.current
onLoading()
getDictData(
'house_status',
'house_type',
'house_industry',
'house_used_status',
'house_property_rights',
'land_attribute',
'house_base_info',
'house_structure_type',
'house_storage_of_drawings',
'house_grade'
).then(codes => {
this.setState({ codes }, () => {
onLoadData()
})
})
}
/**
* 调用加载数据接口,可在调用前对query进行处理
* [异步,必要]
* @param {*} params
* @param {*} query
* @returns
*/
loadData = async (params, query) => {
query.completedDate = getSearchDateRange(query.completedDate)
query.createdTime = getSearchDateRange(query.createdTime)
const searchInfo = getSearchInfo({
query,
queryType: {
areaCode: QueryType.Like,
completedDate: [QueryType.GreaterThanOrEqual, QueryType.LessThan],
createdTime: [QueryType.GreaterThanOrEqual, QueryType.LessThan],
totalArea: [QueryType.GreaterThanOrEqual, QueryType.LessThanOrEqual],
totalFloor: [QueryType.GreaterThanOrEqual, QueryType.LessThanOrEqual],
},
})
const { data } = await apiAction.page({
...params,
searchInfo,
})
return data
}
/**
* 绑定字典数据
* @param {*} code
* @param {*} name
* @returns
*/
bindCodeValue(code, name) {
name = toCamelCase(name)
const codes = this.state.codes[name]
if (codes) {
const c = codes.find(p => p.code == code)
if (c) {
return c.value
}
}
return null
}
/**
* 打开新增/编辑弹窗
* @param {*} modal
* @param {*} id
*/
onOpen(id) {
window.openContentWindow({
title: '房屋详情',
path: 'business/house/query/detail',
param: { id },
})
}
/**
* 对表格上的操作进行统一处理
* [异步]
* @param {*} action
* @param {*} successMessage
*/
async onAction(action, successMessage) {
const { onLoading, onLoaded, onReloadData } = this.table.current
onLoading()
try {
if (action) {
await action
}
if (successMessage) {
Message.success(successMessage)
}
onReloadData()
} catch {
onLoaded()
}
}
/**
* 删除
* @param {*} id
*/
onDelete(id) {
this.onAction(apiAction.delete({ id }), '删除成功')
}
//#region 自定义方法
rednerMoreQuery() {
const { codes, showDrawingMaterialText } = this.state
return (
<Row gutter={16}>
<Col span={24}>
<Form.Item label="使用状态" name="houseUsedStatus">
<Checkbox.Group>
{codes.houseUsedStatus.map(item => (
<Checkbox key={item.code} value={+item.code}>
{item.value}
</Checkbox>
))}
</Checkbox.Group>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="有无幕墙" name="curtainWall">
<Checkbox.Group>
<Checkbox value="">全部</Checkbox>
<Checkbox value={0}></Checkbox>
<Checkbox value={1}></Checkbox>
</Checkbox.Group>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="有无面砖" name="faceBrick">
<Checkbox.Group>
<Checkbox value="">全部</Checkbox>
<Checkbox value={0}></Checkbox>
<Checkbox value={1}></Checkbox>
</Checkbox.Group>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="有无涂料" name="coating">
<Checkbox.Group>
<Checkbox value="">全部</Checkbox>
<Checkbox value={0}></Checkbox>
<Checkbox value={1}></Checkbox>
</Checkbox.Group>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="有无粉刷" name="painting">
<Checkbox.Group>
<Checkbox value="">全部</Checkbox>
<Checkbox value={0}></Checkbox>
<Checkbox value={1}></Checkbox>
</Checkbox.Group>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="安全管理员">
<Input autoComplete="off" placeholder="请输入安全管理员" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="片区监管员">
<Input autoComplete="off" placeholder="请输入片区监管员" />
</Form.Item>
</Col>
<Col span={24}>
<Form.Item label="产权性质" name="propertyRights">
<Checkbox.Group>
<Checkbox value="">全部</Checkbox>
{codes.housePropertyRights.map(item => (
<Checkbox key={item.code} value={+item.code}>
{item.value}
</Checkbox>
))}
</Checkbox.Group>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="土地性质" name="landAttribute">
<Checkbox.Group>
<Checkbox value="">全部</Checkbox>
{codes.landAttribute.map(item => (
<Checkbox key={item.code} value={+item.code}>
{item.value}
</Checkbox>
))}
</Checkbox.Group>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="基础情况" name="baseInfo">
<Checkbox.Group>
<Checkbox value="">全部</Checkbox>
{codes.houseBaseInfo.map(item => (
<Checkbox key={item.code} value={+item.code}>
{item.value}
</Checkbox>
))}
</Checkbox.Group>
</Form.Item>
</Col>
<Col span={24}>
<Form.Item label="结构类型" name="structureType">
<Checkbox.Group>
<Checkbox value="">全部</Checkbox>
{codes.houseStructureType.map(item => (
<Checkbox key={item.code} value={+item.code}>
{item.value}
</Checkbox>
))}
</Checkbox.Group>
</Form.Item>
</Col>
<Col span={24}>
<Form.Item label="图纸资料存档处" name="drawingMaterial">
<Form.Item name="drawingMaterial" className="mb-none">
<Checkbox.Group>
{codes.houseStorageOfDrawings.map(item => (
<Checkbox key={item.code} value={item.code}>
{item.value}
</Checkbox>
))}
</Checkbox.Group>
</Form.Item>
{showDrawingMaterialText && (
<Form.Item name="drawingMaterialText" className="mb-none mt-xs">
<Input.TextArea autoSize placeholder="请输入其他图纸资料存档处" />
</Form.Item>
)}
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="竣工日期" name="completedDate">
<DatePicker.RangePicker
className="w-100-p"
placeholder={['请选择竣工开始日期', '请选择竣工结束日期']}
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="填表日期" name="createdTime">
<DatePicker.RangePicker
className="w-100-p"
placeholder={['请选择填表开始日期', '请选择填表结束日期']}
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="总建筑面积" name="totalArea">
<InputNumberRange
min={0}
unit="m²"
placeholder={['请输入最小总建筑面积', '请输入最大总建筑面积']}
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="总层数" name="totalFloor">
<InputNumberRange
min={0}
precision={0}
step={1}
unit="层"
placeholder={['请输入最小总层数', '请输入最大总层数']}
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="建设单位" name="buildingUnit">
<Input autoComplete="off" placeholder="请输入建设单位" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="设计单位" name="desingerUnit">
<Input autoComplete="off" placeholder="请输入设计单位" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="施工单位" name="constructionUnit">
<Input autoComplete="off" placeholder="请输入施工单位" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="监理单位" name="monitorUnit">
<Input autoComplete="off" placeholder="请输入监理单位" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="产权单位" name="propertyUnit">
<Input autoComplete="off" placeholder="请输入产权单位" />
</Form.Item>
</Col>
<Col span={24}>
<Form.Item label="综合等级" name="houseGrade">
<Checkbox.Group>
<Checkbox value="">全部</Checkbox>
{codes.houseGrade.map(item => (
<Checkbox key={item.code} value={+item.code}>
{item.value}
</Checkbox>
))}
</Checkbox.Group>
</Form.Item>
</Col>
</Row>
)
}
onQueryChange(changedValues, allValues) {
console.log(changedValues)
if (changedValues.hasOwnProperty('drawingMaterial') && changedValues.drawingMaterial) {
this.setState({
showDrawingMaterialText: changedValues.drawingMaterial.includes('100'),
})
return
}
// 全部
const { codes } = this.state
const key = first(Object.keys(changedValues))
const mapCount = {
curtainWall: 2,
faceBrick: 2,
coating: 2,
painting: 2,
propertyRights: codes.housePropertyRights.length,
landAttribute: codes.landAttribute.length,
baseInfo: codes.houseBaseInfo.length,
structureType: codes.houseStructureType.length,
houseGrade: codes.houseGrade.length,
}
if (Object.keys(mapCount).includes(key)) {
return {
[key]: this.checkedNone(changedValues[key], mapCount[key]),
}
}
}
checkedNone(value, count) {
if (first(value) == '' && value.length > 1) {
// 在'无'之后选中其他值
value.shift()
} else if ((last(value) == '' && value.length > 1) || value.length === count) {
// 在其他值之后选中'无'
value = ['']
}
return value
}
//#endregion
render() {
const { codes, type } = this.state
return (
<Container mode="fluid">
<br />
<Card bordered={false}>
<QueryTable
ref={this.table}
autoLoad={false}
loadData={this.loadData}
columns={this.columns}
queryInitialValues={{
houseUsedStatus: [1, 2],
curtainWall: [''],
faceBrick: [''],
coating: [''],
painting: [''],
propertyRights: [''],
landAttribute: [''],
baseInfo: [''],
structureType: [''],
houseGrade: [''],
}}
query={
<Auth auth={{ [authName]: 'page' }}>
<Form.Item label="地址" name="address">
<Input autoComplete="off" placeholder="请输入地址" />
</Form.Item>
<Form.Item label="房屋唯一编码" name="houseCode">
<Input autoComplete="off" placeholder="请输入房屋唯一编码" />
</Form.Item>
</Auth>
}
moreQuery={
<Auth auth={{ [authName]: 'page' }}>{this.rednerMoreQuery()}</Auth>
}
onQueryChange={(changedValues, allValues) =>
this.onQueryChange(changedValues, allValues)
}
operator={
<Auth auth={{ [authName]: 'add' }}>
<Button
icon={<AntIcon type="plus" />}
onClick={() => this.onOpen(this.addForm)}
>
新增{name}
</Button>
</Auth>
}
/>
</Card>
</Container>
)
}
}

View File

@@ -0,0 +1,338 @@
import React, { Component } from 'react'
import {
Button,
Card,
Drawer,
Form,
Input,
message as Message,
Popconfirm,
Radio,
Select,
Tag,
} from 'antd'
import {
AntIcon,
Auth,
Container,
HouseLog,
ModalForm,
QueryTable,
QueryTableActions,
} from 'components'
import { api } from 'common/api'
import auth from 'components/authorized/handler'
import { isEqual } from 'lodash'
import getDictData from 'util/dic'
import { toCamelCase } from 'util/format'
import { getSearchInfo } from 'util/query'
/**
* 注释段[\/**\/]为必须要改
*/
/**
* 配置页面所需接口函数
*/
const apiAction = {
page: api.houseTaskPage,
}
/**
* 统一配置权限标识
* [必要]
*/
const authName = 'houseTask'
export default class index extends Component {
state = {
codes: {
status: [
{ code: 3, value: '审核中' },
{ code: 6, value: '审核通过' },
],
houseType: [],
houseIndustry: [],
},
type: '',
visibleLog: false,
}
// 表格实例
table = React.createRef()
// 新增窗口实例
addForm = React.createRef()
// 编辑窗口实例
editForm = React.createRef()
columns = [
{
title: '房屋编码',
dataIndex: 'houseCode',
sorter: true,
width: 300,
render: (text, record) => (
<>
{`${record.areaName}-${record.roadName}-${record.commName}-${
record.fullProjName
}-${record.no.toString().padStart(3, '0')}`}
<br />
<Tag color="purple">{text}</Tag>
</>
),
},
{
title: '房屋性质及行业',
dataIndex: 'type',
sorter: true,
width: 150,
render: (text, record) =>
this.bindCodeValue(text, 'house_type') +
(text === 2 ? `(${this.bindCodeValue(record.industry, 'house_industry')})` : ''),
},
{
title: '地址',
dataIndex: 'address',
sorter: true,
},
{
title: '任务截止时间',
dataIndex: 'endTime',
sorter: true,
width: 150,
},
{
title: '审核状态',
dataIndex: 'status',
sorter: true,
width: 100,
render: text => this.bindCodeValue(text, 'status'),
},
]
/**
* 构造函数,在渲染前动态添加操作字段等
* @param {*} props
*/
constructor(props) {
super(props)
const flag = auth({ houseInfo: 'getByTaskId' })
if (flag) {
this.columns.push({
title: '操作',
width: 150,
dataIndex: 'actions',
render: (text, record) => (
<QueryTableActions>
<Auth auth={{ houseInfo: 'getByTaskId' }}>
<a onClick={() => this.onOpen(record.id)}>
{record.state === 3 ? `审核` : `查看`}
</a>
</Auth>
<a onClick={() => this.onShowLog(record.id)}>日志</a>
</QueryTableActions>
),
})
}
}
/**
* 阻止外部组件引发的渲染,提升性能
* 可自行添加渲染条件
* [必要]
* @param {*} props
* @param {*} state
* @returns
*/
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
/**
* 加载字典数据,之后开始加载表格数据
* 如果必须要加载字典数据,可直接对表格设置autoLoad=true
*/
componentDidMount() {
const { onLoading, onLoadData } = this.table.current
onLoading()
getDictData('house_type', 'house_industry').then(codes => {
this.setState({ codes: { ...this.state.codes, ...codes } }, () => {
onLoadData()
})
})
}
/**
* 调用加载数据接口,可在调用前对query进行处理
* [异步,必要]
* @param {*} params
* @param {*} query
* @returns
*/
loadData = async (params, query) => {
const searchInfo = getSearchInfo({
query,
queryType: {
type: '=',
industry: '=',
address: 'like',
houseCode: 'like',
status: '=',
},
})
const { data } = await apiAction.page({
...params,
searchInfo,
})
return data
}
/**
* 绑定字典数据
* @param {*} code
* @param {*} name
* @returns
*/
bindCodeValue(code, name) {
name = toCamelCase(name)
const codes = this.state.codes[name]
if (codes) {
const c = codes.find(p => p.code == code)
if (c) {
return c.value
}
}
return null
}
/**
* 打开新增/编辑弹窗
* @param {*} modal
* @param {*} record
*/
onOpen(taskId) {
window.openContentWindow({
title: '房屋登记',
path: 'business/house/info/form',
param: {
taskId,
table: this.table,
},
})
}
/**
* 对表格上的操作进行统一处理
* [异步]
* @param {*} action
* @param {*} successMessage
*/
async onAction(action, successMessage) {
const { onLoading, onLoaded, onReloadData } = this.table.current
onLoading()
try {
if (action) {
await action
}
if (successMessage) {
Message.success(successMessage)
}
onReloadData()
} catch {
onLoaded()
}
}
//#region 自定义方法
onShowLog(id) {
this.setState({ visibleLog: id })
}
//#endregion
render() {
const { codes, type, visibleLog } = this.state
return (
<Container mode="fluid">
<br />
<Card bordered={false}>
<QueryTable
ref={this.table}
autoLoad={false}
loadData={this.loadData}
columns={this.columns}
queryInitialValues={{
type: '',
status: '',
}}
onQueryChange={values => {
if (values.hasOwnProperty('type')) {
this.setState({ type: values.type })
}
}}
query={
<Auth auth={{ [authName]: 'page' }}>
<Form.Item label="房屋性质" name="type">
<Radio.Group buttonStyle="solid">
<Radio.Button value="">全部</Radio.Button>
{codes.houseType.map(item => (
<Radio.Button key={item.code} value={item.code}>
{item.value}
</Radio.Button>
))}
</Radio.Group>
</Form.Item>
{type == 2 && (
<Form.Item label="行业" name="industry">
<Select
allowClear
className="w-150"
placeholder="请选择行业"
>
{codes.houseIndustry.map(item => (
<Select.Option key={item.code} value={item.code}>
{item.value}
</Select.Option>
))}
</Select>
</Form.Item>
)}
<Form.Item label="地址" name="address">
<Input autoComplete="off" placeholder="请输入地址" />
</Form.Item>
<Form.Item label="房屋唯一编码" name="houseCode">
<Input autoComplete="off" placeholder="请输入房屋唯一编码" />
</Form.Item>
<Form.Item label="审核状态" name="status">
<Radio.Group buttonStyle="solid">
<Radio.Button value="">全部</Radio.Button>
{codes.status.map(item => (
<Radio.Button key={item.code} value={item.code}>
{item.value}
</Radio.Button>
))}
</Radio.Group>
</Form.Item>
</Auth>
}
/>
</Card>
<Drawer
width={600}
visible={visibleLog}
onClose={() => this.setState({ visibleLog: false })}
destroyOnClose
>
<HouseLog taskId={visibleLog} />
</Drawer>
</Container>
)
}
}

View File

@@ -0,0 +1,331 @@
import React, { Component } from 'react'
import { Card, Checkbox, Drawer, Form, Input, message as Message, Radio, Select, Tag } from 'antd'
import { Auth, Container, HouseLog, QueryTable, QueryTableActions } from 'components'
import { api } from 'common/api'
import auth from 'components/authorized/handler'
import { isEqual } from 'lodash'
import getDictData from 'util/dic'
import { toCamelCase } from 'util/format'
import { getSearchInfo } from 'util/query'
import { checkboxCheckedNone } from 'util/tool'
/**
* 注释段[\/**\/]为必须要改
*/
/**
* 配置页面所需接口函数
*/
const apiAction = {
page: api.houseTaskPage,
}
/**
* 统一配置权限标识
* [必要]
*/
const authName = 'houseTask'
export default class index extends Component {
state = {
codes: {
houseStatus: [],
houseType: [],
houseIndustry: [],
},
type: '',
visibleLog: false,
}
// 表格实例
table = React.createRef()
// 新增窗口实例
addForm = React.createRef()
// 编辑窗口实例
editForm = React.createRef()
columns = [
{
title: '房屋编码',
dataIndex: 'houseCode',
sorter: true,
width: 400,
render: (text, record) => (
<>
{`${record.areaName}-${record.roadName}-${record.commName}-${
record.fullProjName
}-${record.no.toString().padStart(3, '0')}`}
<br />
<Tag color="purple">{text}</Tag>
</>
),
},
{
title: '房屋性质及行业',
dataIndex: 'type',
sorter: true,
width: 150,
render: (text, record) =>
this.bindCodeValue(text, 'house_type') +
(text === 2 ? `(${this.bindCodeValue(record.industry, 'house_industry')})` : ''),
},
{
title: '地址',
dataIndex: 'address',
sorter: true,
},
{
title: '任务截止时间',
dataIndex: 'endTime',
sorter: true,
width: 150,
},
{
title: '建档状态',
dataIndex: 'state',
sorter: true,
width: 100,
render: text => this.bindCodeValue(text, 'house_status'),
},
]
/**
* 构造函数,在渲染前动态添加操作字段等
* @param {*} props
*/
constructor(props) {
super(props)
const flag = auth({ houseInfo: 'getByTaskId' })
if (flag) {
this.columns.push({
title: '操作',
width: 150,
dataIndex: 'actions',
render: (text, record) => (
<QueryTableActions>
<Auth auth={{ houseInfo: 'getByTaskId' }}>
<a onClick={() => this.onOpen(record.id)}>
{record.state === -1 || record.state === 1 || record.state === 2
? `修改`
: record.state === 3 || record.state === 6
? `查看`
: `登记`}
</a>
</Auth>
<a onClick={() => this.onShowLog(record.id)}>日志</a>
</QueryTableActions>
),
})
}
}
/**
* 阻止外部组件引发的渲染,提升性能
* 可自行添加渲染条件
* [必要]
* @param {*} props
* @param {*} state
* @returns
*/
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
/**
* 加载字典数据,之后开始加载表格数据
* 如果必须要加载字典数据,可直接对表格设置autoLoad=true
*/
componentDidMount() {
const { onLoading, onLoadData } = this.table.current
onLoading()
getDictData('house_status', 'house_type', 'house_industry').then(codes => {
this.setState({ codes: { ...this.state.codes, ...codes } }, () => {
onLoadData()
})
})
}
/**
* 调用加载数据接口,可在调用前对query进行处理
* [异步,必要]
* @param {*} params
* @param {*} query
* @returns
*/
loadData = async (params, query) => {
const searchInfo = getSearchInfo({
query,
queryType: {
type: '=',
industry: '=',
address: 'like',
houseCode: 'like',
state: '=',
},
})
const { data } = await apiAction.page({
...params,
searchInfo,
})
return data
}
/**
* 绑定字典数据
* @param {*} code
* @param {*} name
* @returns
*/
bindCodeValue(code, name) {
name = toCamelCase(name)
const codes = this.state.codes[name]
if (codes) {
const c = codes.find(p => p.code == code)
if (c) {
return c.value
}
}
return null
}
/**
* 打开新增/编辑弹窗
* @param {*} modal
* @param {*} record
*/
onOpen(taskId) {
window.openContentWindow({
title: '房屋登记',
path: 'business/house/info/form',
param: {
taskId,
table: this.table,
},
})
}
/**
* 对表格上的操作进行统一处理
* [异步]
* @param {*} action
* @param {*} successMessage
*/
async onAction(action, successMessage) {
const { onLoading, onLoaded, onReloadData } = this.table.current
onLoading()
try {
if (action) {
await action
}
if (successMessage) {
Message.success(successMessage)
}
onReloadData()
} catch {
onLoaded()
}
}
//#region 自定义方法
onShowLog(id) {
this.setState({ visibleLog: id })
}
//#endregion
render() {
const { codes, type, visibleLog } = this.state
return (
<Container mode="fluid">
<br />
<Card bordered={false}>
<QueryTable
ref={this.table}
autoLoad={false}
loadData={this.loadData}
columns={this.columns}
queryInitialValues={{
type: '',
state: [-1, 0, 1, 2],
}}
onQueryChange={values => {
if (values.hasOwnProperty('type')) {
this.setState({ type: values.type })
}
if (values.hasOwnProperty('state')) {
const value = checkboxCheckedNone({
value: values.state,
length: codes.houseStatus.length,
required: true,
})
return {
state: value,
}
}
}}
query={
<Auth auth={{ [authName]: 'page' }}>
<Form.Item label="房屋性质" name="type">
<Radio.Group buttonStyle="solid">
<Radio.Button value="">全部</Radio.Button>
{codes.houseType.map(item => (
<Radio.Button key={item.code} value={item.code}>
{item.value}
</Radio.Button>
))}
</Radio.Group>
</Form.Item>
{type == 2 && (
<Form.Item label="行业" name="industry">
<Select
allowClear
className="w-150"
placeholder="请选择行业"
>
{codes.houseIndustry.map(item => (
<Select.Option key={item.code} value={item.code}>
{item.value}
</Select.Option>
))}
</Select>
</Form.Item>
)}
<Form.Item label="地址" name="address">
<Input autoComplete="off" placeholder="请输入地址" />
</Form.Item>
<Form.Item label="房屋唯一编码" name="houseCode">
<Input autoComplete="off" placeholder="请输入房屋唯一编码" />
</Form.Item>
<Form.Item label="建档状态" name="state">
<Checkbox.Group>
<Checkbox value="">全部</Checkbox>
{codes.houseStatus.map(item => (
<Checkbox key={item.code} value={+item.code}>
{item.value}
</Checkbox>
))}
</Checkbox.Group>
</Form.Item>
</Auth>
}
/>
</Card>
<Drawer
width={600}
visible={visibleLog}
onClose={() => this.setState({ visibleLog: false })}
destroyOnClose
>
<HouseLog taskId={visibleLog} />
</Drawer>
</Container>
)
}
}

View File

@@ -0,0 +1,194 @@
import React, { Component } from 'react'
import { Form, Input, InputNumber, Spin, TreeSelect } from 'antd'
import { AntIcon } from 'components'
import { cloneDeep, pickBy } from 'lodash'
import { api } from 'common/api'
import { numberToChinese } from 'util/format'
import store from 'store'
const { getState, subscribe } = store
const storePath = 'user'
const initialValues = {
sort: 100,
}
export default class form extends Component {
state = {
// 加载状态
loading: true,
exist: false,
options: {
orgData: [],
},
user: getState(storePath),
}
constructor(props) {
super(props)
this.unsubscribe = subscribe(storePath, () => {
this.setState({
user: getState(storePath),
})
})
}
// 表单实例
form = React.createRef()
// 初始化数据
record = {}
/**
* mount后回调
*/
componentDidMount() {
this.props.created && this.props.created(this)
}
componentWillUnmount() {
this.unsubscribe()
}
/**
* 填充数据
* 可以在设置this.record之后对其作出数据结构调整
* [异步,必要]
* @param {*} params
*/
async fillData(params) {
const { user } = this.state
if (params.id) {
this.setState({
loading: true,
})
api.houseZoneGetById({ zoneId: params.id }).then(({ data }) => {
this.record = data
this.setState({
loading: false,
})
})
}
// this.record = cloneDeep(params.record)
//#region 从后端转换成前段所需格式
const orgData = await this.loadOrgData()
this.setState({
exist: !!params.id,
options: { orgData },
})
//街道角色新增,不管左侧树选中与否,默认值均为本街道
if (user.adminType === 2) {
user.roles.map(role => {
if (role.code == 'road_manager') {
params.orgId = user.loginEmpInfo.orgId
}
})
}
this.record = {
pid: params.orgId,
...this.record,
}
//#endregion
if (!params.id && !!params.orgId) {
this.onOrgIdChanged(params.orgId)
}
this.form.current.setFieldsValue(this.record)
this.setState({
loading: false,
})
}
/**
* 获取数据
* 可以对postData进行数据结构调整
* [异步,必要]
* @returns
*/
async getData() {
const form = this.form.current
const valid = await form.validateFields()
if (valid) {
const postData = form.getFieldsValue()
if (this.record) {
postData.id = this.record.id
}
//#region 从前段转换后端所需格
//#endregion
return postData
}
}
async loadOrgData() {
const { data } = await api.getOrgTree({ type: 4 })
return data
}
onOrgIdChanged(value) {
this.loading = true
api.houseZoneAutoIncrement({ roadId: value })
.then(({ data }) => {
this.form.current.setFieldsValue({
name: `片区${numberToChinese(data)}`,
sort: data,
})
})
.catch(() => {
this.form.current.setFieldsValue({
name: '',
sort: 0,
})
})
.finally(() => {
this.loading = false
})
}
render() {
return (
<Form initialValues={initialValues} ref={this.form} className="yo-form">
<Spin spinning={this.state.loading} indicator={<AntIcon type="loading" />}>
<div className="yo-form-group">
<Form.Item
label="所属街道"
name="pid"
rules={[{ required: true, message: '请选择所属街道' }]}
>
<TreeSelect
treeData={this.state.options.orgData}
dropdownStyle={{ maxHeight: '300px', overflow: 'auto' }}
treeDefaultExpandAll
placeholder="请选择所属街道"
onChange={(value, label, extra) => this.onOrgIdChanged(value)}
disabled={this.state.exist}
/>
</Form.Item>
<Form.Item
label="片区名称"
name="name"
rules={[{ required: true, message: '片区名称', trigger: 'blur' }]}
>
<Input autoComplete="off" placeholder="请输入机构名称" disabled />
</Form.Item>
<Form.Item label="排序" name="sort">
<InputNumber
max={1000}
min={0}
placeholder="请输入排序"
className="w-100-p"
disabled
/>
</Form.Item>
<Form.Item label="备注" name="remark">
<Input.TextArea placeholder="请输入备注" />
</Form.Item>
</div>
</Spin>
</Form>
)
}
}

View File

@@ -0,0 +1,277 @@
import React, { Component } from 'react'
import { Button, Card, Form, Input, message as Message, Popconfirm } from 'antd'
import {
AntIcon,
Auth,
Container,
ModalForm,
QueryTable,
QueryTableActions,
QueryTreeLayout,
} from 'components'
import { api } from 'common/api'
import auth from 'components/authorized/handler'
import { toCamelCase } from 'util/format'
import { isEqual } from 'lodash'
import getDictData from 'util/dic'
import FormBody from './form'
const apiAction = {
tree: api.getOrgTree,
page: api.houseZonePage,
add: api.houseZoneAdd,
edit: api.houseZoneEdit,
delete: api.sysOrgDelete,
}
const name = '片区'
export default class index extends Component {
// 树框架实例
treeLayout = React.createRef()
// 表格实例
table = React.createRef()
// 新增窗口实例
addForm = React.createRef()
// 编辑窗口实例
editForm = React.createRef()
// 树选中节点
selectId = undefined
// 表格字段
columns = [
{
title: '片区名称',
width: '400px',
dataIndex: 'name',
sorter: true,
},
{
title: '排序',
width: '80px',
dataIndex: 'sort',
sorter: true,
},
{
title: '备注',
width: '80px',
dataIndex: 'remark',
sorter: true,
},
]
/**
* 构造函数,在渲染前动态添加操作字段等
* @param {*} props
*/
constructor(props) {
super(props)
const flag = auth({ houseZone: [['edit'], ['delete']] })
if (flag) {
this.columns.push({
title: '操作',
width: 150,
dataIndex: 'actions',
render: (text, record) => (
<QueryTableActions>
<Auth auth="houseZone:edit">
<a onClick={() => this.onOpen(this.editForm, record.id)}>编辑</a>
</Auth>
<Auth auth="houseZone:delete">
<Popconfirm
placement="topRight"
title="是否确认删除"
onConfirm={() => this.onDelete(record)}
>
<a>删除</a>
</Popconfirm>
</Auth>
</QueryTableActions>
),
})
}
}
/**
* 阻止外部组件引发的渲染,提升性能
* 可自行添加渲染条件
* [必要]
* @param {*} props
* @param {*} state
* @returns
*/
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
/**
* 加载字典数据,之后开始加载表格数据
* 如果必须要加载字典数据,可直接对表格设置autoLoad=true
*/
componentDidMount() {
this.table.current.onLoading()
}
/**
* 调用加载数据接口,可在调用前对query进行处理
* [异步,必要]
* @param {*} params
* @param {*} query
* @returns
*/
loadData = async (params, query) => {
query = {
...query,
pid: this.selectId,
}
const { data } = await apiAction.page({
...params,
...query,
})
return data
}
/**
* 调用树结构数据接口
* [异步,必要]
* @returns
*/
loadTreeData = async () => {
const { data } = await apiAction.tree({ type: 4 })
return data
}
/**
* 树节点选中事件
* [必要]
* @param {*} id
*/
onSelectTree(id) {
this.selectId = id
this.table.current.onReloadData()
}
/**
* 绑定字典数据
* @param {*} code
* @param {*} name
* @returns
*/
bindCodeValue(code, name) {
name = toCamelCase(name)
const codes = this.state.codes[name]
if (codes) {
const c = codes.find(p => p.code === code)
if (c) {
return c.value
}
}
return null
}
/**
* 打开新增/编辑弹窗
* @param {*} modal
* @param {*} record
*/
onOpen(modal, id) {
modal.current.open({
orgId: this.selectId,
// record,
id,
})
}
/**
* 对表格上的操作进行统一处理
* [异步]
* @param {*} action
* @param {*} successMessage
*/
async onAction(action, successMessage) {
this.table.current.onLoading()
try {
if (action) {
await action
}
if (successMessage) {
Message.success(successMessage)
}
this.treeLayout.current.onReloadData()
this.table.current.onReloadData()
} catch {
this.table.current.onLoaded()
}
}
/**
* 删除
* @param {*} record
*/
onDelete(record) {
this.onAction(apiAction.delete(record), '删除成功')
}
//#region 自定义方法
//#endregion
render() {
return (
<QueryTreeLayout
ref={this.treeLayout}
loadData={this.loadTreeData}
defaultExpanded={true}
onSelect={key => this.onSelectTree(key)}
>
<Container mode="fluid">
<Card bordered={false}>
<QueryTable
ref={this.table}
loadData={this.loadData}
columns={this.columns}
query={
<Auth auth="houseZone:page">
<Form.Item label="片区名称" name="name">
<Input autoComplete="off" placeholder="请输入片区名称" />
</Form.Item>
</Auth>
}
operator={
<Button
icon={<AntIcon type="plus" />}
onClick={() => this.onOpen(this.addForm)}
>
新增{name}
</Button>
}
/>
</Card>
</Container>
<ModalForm
title={`新增${name}`}
action={apiAction.add}
ref={this.addForm}
onSuccess={() => this.onAction()}
>
<FormBody />
</ModalForm>
<ModalForm
title={`编辑${name}`}
action={apiAction.edit}
ref={this.editForm}
onSuccess={() => this.onAction()}
>
<FormBody />
</ModalForm>
</QueryTreeLayout>
)
}
}

View File

@@ -0,0 +1,88 @@
import React, { Component } from 'react'
import { Radio, Tabs } from 'antd'
import { ComponentDynamic } from 'components'
import AntIcon from 'components/ant-icon'
const tabs = [
{
title: '按房屋等级',
component: () => import('./tab1'),
},
{
title: '按房屋结构',
component: () => import('./tab2'),
},
]
export default class index extends Component {
state = {
activeKey: '0',
types: ['charts', 'charts']
}
render() {
const { activeKey, types } = this.state
return (
<div className="yo-form-page">
<div className="yo-form-page-layout">
<div className="yo-tab-external-mount">
<Tabs
tabBarExtraContent={
<Radio.Group
buttonStyle="solid"
value={types[activeKey]}
onChange={(e) => {
const t = [...types]
t[activeKey] = e.target.value
this.setState({
types: t
})
}}
>
<Radio.Button value="charts">
<AntIcon type="bar-chart" />
</Radio.Button>
<Radio.Button value="table">
<AntIcon type="table" />
</Radio.Button>
</Radio.Group>
}
onChange={(activeKey) => this.setState({ activeKey })}
>
{
tabs.map((item, i) => (
<Tabs.TabPane
key={i}
forceRender={true}
tab={item.title}
/>
))
}
</Tabs>
<div className="yo-tab-external-mount-content">
{
tabs.map((item, i) => (
<div
key={i}
className={[
'yo-tab-external-tabpane',
activeKey == i ?
'yo-tab-external-tabpane-active'
:
'yo-tab-external-tabpane-inactive'
].join(' ')}
>
<ComponentDynamic is={item.component} type={types[i]} />
</div>
))
}
</div>
</div>
</div>
</div>
)
}
}

View File

@@ -0,0 +1,202 @@
import React, { Component } from 'react'
import { Card, Col, Row } from 'antd'
import * as echarts from 'echarts'
const echartsColors = [
{ from: '#14dbff', to: '#007dff' },
{ from: '#45f4a6', to: '#3bb27d' },
{ from: '#fbb456', to: '#f1961b' },
{ from: '#fa7148', to: '#ef5932' },
]
function itemColor(index) {
return {
type: 'linear',
x: 0,
y: 0,
x2: 1,
y2: 1,
colorStops: [
{
offset: 0,
color: echartsColors[index % echartsColors.length].from,
},
{
offset: 1,
color: echartsColors[index % echartsColors.length].to,
},
],
}
}
function initChart1(dom) {
const chart = echarts.init(dom);
chart.setOption({
legend: {
top: 'bottom',
},
tooltip: {
formatter: '<b>{b}</b> : {c}幢 ({d}%)',
},
toolbox: {
show: true,
feature: {
mark: { show: true },
restore: { show: true },
saveAsImage: { show: true },
},
},
series: [
{
name: '面积模式',
type: 'pie',
radius: [50, 150],
startAngle: 110,
center: ['50%', '50%'],
roseType: 'area',
label: {
formatter: '{d}',
},
data: [
{
value: 70,
name: 'A级',
itemStyle: {
color: itemColor(0),
},
},
{
value: 38,
name: 'B级',
itemStyle: {
color: itemColor(1),
},
},
{
value: 32,
name: 'C级',
itemStyle: {
color: itemColor(2),
},
},
{
value: 30,
name: 'D级',
itemStyle: {
color: itemColor(3),
},
},
],
},
],
});
}
function initChart2(dom) {
const chart = echarts.init(dom);
chart.setOption({
tooltip: {
formatter: '<b>{b}</b> : {c}幢',
},
toolbox: {
show: true,
feature: {
mark: { show: true },
restore: { show: true },
saveAsImage: { show: true },
},
},
xAxis: {
type: 'category',
data: ['A级', 'B级', 'C级', 'D级'],
},
yAxis: {
type: 'value',
},
series: [
{
barWidth: 20,
itemStyle: {
color: itemColor(0),
borderRadius: 10,
},
data: [120, 200, 150, 80],
type: 'bar',
},
],
});
}
function initChart3(dom) {
const chart = echarts.init(dom);
chart.setOption({
tooltip: {
formatter: '<b>{b}</b> : {c}幢',
},
toolbox: {
show: true,
feature: {
mark: { show: true },
restore: { show: true },
saveAsImage: { show: true },
},
},
xAxis: {
type: 'category',
data: ['A级', 'B级', 'C级', 'D级'],
},
yAxis: {
type: 'value',
},
series: [
{
data: [820, 932, 800, 900],
showSymbol: false,
lineStyle: {
width: 5,
shadowBlur: 5,
shadowOffsetY: 3,
shadowColor: echartsColors[0].from,
opacity: 0.5,
cap: 'round',
},
itemStyle: {
color: itemColor(0),
},
type: 'line',
smooth: true,
},
],
});
}
export default class charts extends Component {
componentDidMount() {
initChart1(this.refs['chart-1'])
initChart2(this.refs['chart-2'])
initChart3(this.refs['chart-3'])
}
render() {
return (
<>
<Row gutter={16}>
<Col span={8}>
<Card bordered={false} type="inner">
<div style={{ height: '400px' }} ref="chart-1"></div>
</Card>
</Col>
<Col span={16}>
<Card bordered={false} type="inner">
<div style={{ height: '400px' }} ref="chart-2"></div>
</Card>
</Col>
</Row>
<Card bordered={false} type="inner">
<div style={{ height: '400px' }} ref="chart-3"></div>
</Card>
</>
)
}
}

View File

@@ -0,0 +1,68 @@
import React, { Component } from 'react'
import { Button, Card, Cascader, Divider, Form, Radio } from 'antd'
import { Container } from 'components'
import StatisticsCharts from './charts'
import StatisticsTable from './table'
export default class index extends Component {
state = {
render: 'charts'
}
static getDerivedStateFromProps(props) {
return {
render: props.type
}
}
render() {
return (
<Container mode="fluid">
<Card bordered={false} type="inner">
<Form labelCol={{ flex: '70px' }} wrapperCol={{ flex: '1' }}>
<Form.Item className="yo-filter-item" label="房屋性质">
<Radio.Group defaultValue={''} buttonStyle="solid" size="small">
<Radio.Button value={''}>全部</Radio.Button>
<Radio.Button value="1">住宅</Radio.Button>
<Radio.Button value="2">非住宅</Radio.Button>
</Radio.Group>
</Form.Item>
<Divider className="mt-sm mb-sm" dashed />
<Form.Item className="yo-filter-item" label="审核状态">
<Radio.Group defaultValue={''} buttonStyle="solid" size="small">
<Radio.Button value={''}>全部</Radio.Button>
<Radio.Button value="1">待建档</Radio.Button>
<Radio.Button value="2">暂存</Radio.Button>
<Radio.Button value="3">待提交</Radio.Button>
<Radio.Button value="4">退回</Radio.Button>
<Radio.Button value="5">待审核</Radio.Button>
<Radio.Button value="6">审核通过</Radio.Button>
</Radio.Group>
</Form.Item>
<Divider className="mt-sm mb-sm" dashed />
<Form.Item className="yo-filter-item" label="土地性质">
<Radio.Group defaultValue={''} buttonStyle="solid" size="small">
<Radio.Button value={''}>全部</Radio.Button>
<Radio.Button value="1">国有土地</Radio.Button>
<Radio.Button value="2">集体土地</Radio.Button>
</Radio.Group>
</Form.Item>
<Divider className="mt-sm mb-sm" dashed />
<Form.Item className="yo-filter-item" label="筛选范围">
<Cascader className="w-400" placeholder="请选择区域" />
</Form.Item>
<Divider className="mt-sm mb-sm" dashed />
<div className="text-center">
<Button className="mr-xs" type="primary">查询</Button>
<Button>导出Excel</Button>
</div>
</Form>
</Card>
{this.state.render == 'charts' && <StatisticsCharts />}
{this.state.render == 'table' && <StatisticsTable />}
</Container>
)
}
}

View File

@@ -0,0 +1,209 @@
import React, { Component } from 'react'
import { Card, Table } from 'antd'
import { isEqual } from 'lodash'
const columns = [
{
title: '区域',
dataIndex: 'area',
width: 150,
fixed: true
},
{
title: '总数',
children: [
{
title: '幢数(占比)',
dataIndex: 'z',
width: 120,
},
{
title: '建筑面积(占比)',
dataIndex: 'j',
width: 120,
},
{
title: '户数',
dataIndex: 'h',
width: 80,
},
],
},
{
title: '房屋等级',
children: [
{
title: '一级',
children: [
{
title: '幢数',
dataIndex: 'z1',
width: 80,
},
{
title: '建筑面积',
dataIndex: 'j1',
width: 80,
},
{
title: '户数',
dataIndex: 'h1',
width: 80,
},
],
},
{
title: '二级',
children: [
{
title: '幢数',
dataIndex: 'z2',
width: 80,
},
{
title: '建筑面积',
dataIndex: 'j2',
width: 80,
},
{
title: '户数',
dataIndex: 'h2',
width: 80,
},
],
},
{
title: '三级',
children: [
{
title: '幢数',
dataIndex: 'z3',
width: 80,
},
{
title: '建筑面积',
dataIndex: 'j3',
width: 80,
},
{
title: '户数',
dataIndex: 'h3',
width: 80,
},
],
},
{
title: '四级',
children: [
{
title: '幢数',
dataIndex: 'z4',
width: 80,
},
{
title: '建筑面积',
dataIndex: 'j4',
width: 80,
},
{
title: '户数',
dataIndex: 'h4',
width: 80,
},
],
},
{
title: 'C级',
children: [
{
title: '幢数',
dataIndex: 'zc',
width: 80,
},
{
title: '建筑面积',
dataIndex: 'jc',
width: 80,
},
{
title: '户数',
dataIndex: 'hc',
width: 80,
},
],
},
{
title: 'D级',
children: [
{
title: '幢数',
dataIndex: 'zd',
width: 80,
},
{
title: '建筑面积',
dataIndex: 'jd',
width: 80,
},
{
title: '户数',
dataIndex: 'hd',
width: 80,
},
],
},
],
},
]
const data = []
for (let i = 0; i < 30; i++) {
data.push({
key: i,
area: 'John Brown',
z: 100,
j: 1222.33,
h: 39,
z1: 20,
j1: 20,
h1: 20,
z2: 20,
j2: 20,
h2: 20,
z3: 20,
j3: 20,
h3: 20,
z4: 20,
j4: 20,
h4: 20,
zc: 20,
jc: 20,
hc: 20,
zd: 20,
jd: 20,
hd: 20,
})
}
export default class table extends Component {
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
render() {
return (
<Card bordered={false}>
<Table
columns={columns}
dataSource={data}
pagination={false}
scroll={{ x: 'max-content' }}
bordered
size="middle"
sticky
/>
</Card>
)
}
}

View File

@@ -0,0 +1,11 @@
import React, { Component } from 'react'
export default class index extends Component {
render() {
return (
<div>
1
</div>
)
}
}