update 完成房屋编码管理

This commit is contained in:
2021-06-18 17:44:07 +08:00
parent 0dbd9c8a49
commit d6e3b3146d
7 changed files with 563 additions and 54 deletions

View File

@@ -25,6 +25,7 @@
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<script src="https://webapi.amap.com/maps?v=2.0&key=c6a4832b8afbde0361b36630a3fc5bdc&plugin=Map3D,AMap.DistrictSearch,AMap.Geocoder,AMap.AutoComplete,AMap.PlaceSearch"></script>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View File

@@ -1,10 +1,4 @@
@import (reference) '../extend.less';
/* input前缀有2个字符的 */
.yo-input-prefix-2 {
.ant-input:not(:first-child) {
padding-left: 45px;
}
}
.yo-addon {
padding: 0 @padding-xs;
}

View File

@@ -22,11 +22,14 @@
width: 25%;
min-width: 300px;
padding: @padding-xxs @padding-sm;
background: fade(@black, 20%);
box-shadow: @box-shadow-base;
}
}
.yo-adorn {
&--house-top {
height: 65px;
backdrop-filter: blur(5px);
background: url('~assets/image/adorn/house-top-01.png') no-repeat bottom right;
}
}

View File

@@ -8,14 +8,6 @@ export default class ComponentDynamic extends Component {
component: null
}
shouldComponentUpdate() {
if (this.props.onRef) {
this.props.onRef(this)
}
return true
}
componentDidMount() {
this.loadComponent()
}

View File

@@ -1,7 +1,8 @@
import React, { Component } from 'react'
import { Button, Card } from 'antd'
import { Button, Card, message as Message, Modal } from 'antd'
import { ComponentDynamic, Container } from 'components'
import { isEqual } from 'lodash'
import { api } from 'common/api'
const parts = [{
component: () => import('./part')
@@ -9,37 +10,91 @@ const parts = [{
export default class index extends Component {
state = {
saving: false
}
children = []
formData = {}
shouldComponentUpdate(props, state) {
return !isEqual(this.state, state)
}
async onSubmit() {
for (const child of this.children) {
try {
const data = await child.getData()
this.formData = {
...this.formData,
...data
}
} catch {
return
}
}
this.setState({ saving: true })
if (!this.props.param.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(this.formData)
if (success) {
Message.success('保存成功')
}
} finally {
this.setState({ saving: false })
}
}
}
render() {
const { id, param } = this.props
return (
<div className="yo-form-page">
<Container>
<Container mode="fluid">
<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>{parts.title}</h5>}
<ComponentDynamic is={item.component} param={param} />
<ComponentDynamic is={item.component} param={param} onRef={(r) => this.children.push(r)} />
</section>
))
}
</Card>
</Container>
<div className="yo-form-page--bar">
<Container>
<Container mode="fluid">
<div className="yo-form-page--bar-inner">
<span></span>
<span>
<Button>取消</Button>
<Button type="primary">保存</Button>
<Button type="primary" onClick={() => this.onSubmit()} loading={this.state.saving}>保存</Button>
</span>
</div>
</Container>

View File

@@ -1,10 +1,15 @@
import React, { Component } from 'react'
import { Form, Input, InputNumber, Radio, Spin } from 'antd'
import { AntIcon, IconSelector } from 'components'
import { Button, Cascader, Form, Input, InputNumber, Radio, Spin, Select, Row, Col, Tag, Alert, Tooltip } from 'antd'
import { AntIcon, Auth } from 'components'
import { cloneDeep } from 'lodash'
import getDictData from 'util/dic'
import { api } from 'common/api'
import { CITY } from 'util/global'
const initialValues = {}
const initialValues = {
type: 1,
industry: 1
}
const labelCol = { flex: '150px' }
const wrapperCol = { flex: '1' }
@@ -17,7 +22,15 @@ export default class form extends Component {
codes: {
dicHouseType: [],
dicHouseIndustry: []
}
},
options: {
areaTree: [],
projects: [],
zones: []
},
houseCode: '',
showIndustry: false
}
// 表单实例
@@ -32,9 +45,16 @@ export default class form extends Component {
* mount后回调
*/
componentDidMount() {
if (this.props.onRef) {
this.props.onRef(this)
}
this.fillData(this.props.param)
}
componentWillUnmount() {
if (this.map) this.map.destroy()
}
/**
* 填充数据
* 可以在设置this.record之后对其作出数据结构调整
@@ -45,10 +65,37 @@ export default class form extends Component {
this.record = cloneDeep(params.record)
//#region 从后端转换成前段所需格式
const codes = await getDictData('dic_house_type', 'dic_house_industry')
this.setState({
codes
await this.initMap()
const options = { ...this.state.options }
// 有数据
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
]
// 获取项目和片区列表
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('dic_house_type', 'dic_house_industry')
const { data: areaTree } = await api.getAreaTree()
options.areaTree = areaTree
this.setState({
codes,
options
})
this.showHouseCode()
//#endregion
this.form.current.setFieldsValue(this.record)
@@ -73,28 +120,315 @@ export default class form extends Component {
postData.id = this.record.id
}
//#region 从前段转换后端所需格式
if (postData.areaCode) {
postData.areaCode = postData.areaCode[3]
}
//#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
})
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 === 4)) return null
try {
const result = {}
if (mode.includes('projects')) {
const { data: projects } = await api.houseProjectList({
areaCode: areaCode[3],
type
})
result.projects = projects
}
if (mode.includes('zones')) {
const { data: zones } = await api.houseZoneList({
areaCode: areaCode[3]
})
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 === 4 && projectId && no) {
let houseCode = areaCode[3]
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.dicHouseIndustry.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')) {
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 } = this.state
const { loading, codes, options, showIndustry, houseCode } = this.state
return (
<Form
initialValues={initialValues}
ref={this.form}
labelCol={labelCol}
wrapperCol={wrapperCol}
// labelCol={labelCol}
// wrapperCol={wrapperCol}
onValuesChange={(changedValues, allValues) => this.onValuesChange(changedValues, allValues)}
layout="vertical"
>
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
<Form.Item label="房屋性质" name="type">
<Radio.Group buttonStyle="solid">
<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.dicHouseType.map(item => (
<Radio.Button
key={item.code}
@@ -103,8 +437,13 @@ export default class form extends Component {
))}
</Radio.Group>
</Form.Item>
<Form.Item label="所属行业、系统" name="industry">
<Radio.Group buttonStyle="solid">
{
showIndustry &&
<Form.Item label="所属行业、系统" name="industry" rules={[{ required: true, message: '请选择所属行业、系统' }]}>
<Radio.Group
disabled={!!this.record}
buttonStyle="solid"
>
{codes.dicHouseIndustry.map(item => (
<Radio.Button
key={item.code}
@@ -113,10 +452,135 @@ export default class form extends Component {
))}
</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>项目管理</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>片区管理</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>
<IconSelector ref={this.iconSelector} onSelect={(icon) => this.form.current.setFieldsValue({
icon
})} />
</Form>
)
}