diff --git a/web-react/public/index.html b/web-react/public/index.html index aa069f2..b92c542 100644 --- a/web-react/public/index.html +++ b/web-react/public/index.html @@ -25,6 +25,7 @@ Learn how to configure a non-root public URL by running `npm run build`. --> React App + diff --git a/web-react/src/assets/image/adorn/house-top-01.png b/web-react/src/assets/image/adorn/house-top-01.png new file mode 100644 index 0000000..fbaee95 Binary files /dev/null and b/web-react/src/assets/image/adorn/house-top-01.png differ diff --git a/web-react/src/assets/style/lib/input.less b/web-react/src/assets/style/lib/input.less index 1417fec..d464a3c 100644 --- a/web-react/src/assets/style/lib/input.less +++ b/web-react/src/assets/style/lib/input.less @@ -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; } diff --git a/web-react/src/assets/style/public.less b/web-react/src/assets/style/public.less index c726287..d935b12 100644 --- a/web-react/src/assets/style/public.less +++ b/web-react/src/assets/style/public.less @@ -22,11 +22,14 @@ width: 25%; min-width: 300px; - padding: @padding-xxs @padding-sm; - background: fade(@black, 20%); box-shadow: @box-shadow-base; - - backdrop-filter: blur(5px); + } +} +.yo-adorn { + &--house-top { + height: 65px; + + background: url('~assets/image/adorn/house-top-01.png') no-repeat bottom right; } } diff --git a/web-react/src/components/component-dynamic/index.jsx b/web-react/src/components/component-dynamic/index.jsx index 2ef2371..45d447c 100644 --- a/web-react/src/components/component-dynamic/index.jsx +++ b/web-react/src/components/component-dynamic/index.jsx @@ -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() } diff --git a/web-react/src/pages/business/house/code/form/index.jsx b/web-react/src/pages/business/house/code/form/index.jsx index 6814566..865b1b1 100644 --- a/web-react/src/pages/business/house/code/form/index.jsx +++ b/web-react/src/pages/business/house/code/form/index.jsx @@ -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 (
- +
+
{ parts.map((item, i) => (
{item.title &&
{parts.title}
} - + this.children.push(r)} />
)) }
- +
- +
diff --git a/web-react/src/pages/business/house/code/form/part.jsx b/web-react/src/pages/business/house/code/form/part.jsx index dd76dec..ed86511 100644 --- a/web-react/src/pages/business/house/code/form/part.jsx +++ b/web-react/src/pages/business/house/code/form/part.jsx @@ -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 从后端转换成前段所需格式 + 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 + codes, + options }) + this.showHouseCode() //#endregion this.form.current.setFieldsValue(this.record) @@ -73,50 +120,467 @@ 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 (
this.onValuesChange(changedValues, allValues)} + layout="vertical" + > }> - - - {codes.dicHouseType.map(item => ( - {item.value} - ))} - - - - - {codes.dicHouseIndustry.map(item => ( - {item.value} - ))} - - + + +
+
+ +
+
+
+ + + + + {codes.dicHouseType.map(item => ( + {item.value} + ))} + + + { + showIndustry && + + + {codes.dicHouseIndustry.map(item => ( + {item.value} + ))} + + + } + + + + + labels.join(' - ')} + fieldNames={{ + label: 'name', + value: 'code', + children: 'children' + }} + options={options.areaTree} + expandTrigger="hover" + /> + + + + + + + + + + + + + + + + + + + + + + + + value.padStart(3, '0')} + max={999} + min={1} + precision={0} + step={1} + className="w-100-p" + placeholder="请输入房屋序号" + /> + + + { + showIndustry && + - + } + + + { + houseCode && + + {houseCode} + + } + 房屋编码说明} + description={<> + 房屋所在市—县(市、区)—街道(乡、镇)—社区、居(村)委会)—项目—实物幢序号。 根据省厅既有建筑物编号规则,房屋所在区域编号按照市、县(市、区)、街道(乡、镇)、社区、居(村)委会)、项目分类,其中市、县(市)区部分按照《中华人民共和国行政区划代码》(GB2260)标准编码,街道(乡、镇)按《县以下行政区划代码编码规则》(GB10114-88)标准编码,社区、居(村)委会部分按照统计局提供编码设定。各地上报各街道社区名称后,上述编号由系统自动生成。
各社区下辖项目由各地负责统一编码,住宅项目序号一般一个小区一号,采用3位数,001号起编,范围为001~999。实物幢序号由各地负责统一编码,以幢为单位,采用3位数,001号起编,范围为001~999。 + } /> +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
- this.form.current.setFieldsValue({ - icon - })} /> ) }