index.jsx 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. import React, { useState } from 'react'
  2. import { connect } from 'dva';
  3. import { Button, Spin, DatePicker, Form, Input, InputNumber, Radio, notification, Select,Switch} from 'antd'
  4. import FileUpload from '@/components/XForm/FileUpload'
  5. import ImageUpload from '@/components/XForm/ImageUpload'
  6. import ImageListUpload from '@/components/XForm/ImageListUpload'
  7. import SelectCity from '@/components/SelectButton/CitySelect'
  8. import AreaSelect from '@/components/SelectButton/AreaSelect'
  9. import InstitutionSelect from '@/components/SelectButton/InstitutionSelect'
  10. import AuthButton from '@/components/AuthButton'
  11. import { POI_TYPES_KETY, POI_TYPES, getPoiData } from '@/utils/map'
  12. import Amap from '../components/Amap'
  13. import BuildingType from '../components/BuildingTypeSelect'
  14. import OpenTimePicker from '../components/OpenTimePicker'
  15. import EditableArround from '../components/EditableArround'
  16. import FormGroupItem, { gourpItemLayout } from '../components/FormGroupItem'
  17. import { formItemLayout, validMinNum } from '../utils'
  18. import useArrounds from './useArrounds'
  19. import usePois from './usePois'
  20. import useQuery from './useQuery'
  21. import useBrands from './useBrands'
  22. import { initForm, saveData } from './form'
  23. import { router } from 'umi'
  24. const fullWidth= { width: '100%' }
  25. const Item = Form.Item
  26. const BuildingBasic = React.forwardRef((props, ref) => {
  27. const { form, history, setMarketingCode, user } = props;
  28. const { getFieldDecorator, getFieldValue, setFieldsValue } = form;
  29. const { query } = history.location;
  30. const { id } = query;
  31. const [cityId,setCityId] = useState()
  32. const [ loading, setLoading ] = useState({ form: false, arround: false })
  33. const [brands] = useBrands()
  34. const [
  35. arrounds,
  36. initArrounds,
  37. updateArrounds,
  38. deleteArround,
  39. ] = useArrounds()
  40. const [
  41. pois,
  42. initPois,
  43. deletePoi,
  44. ] = usePois()
  45. const updateLoading = (key) => (state) => setLoading({ ...loading, [key]: state })
  46. const updateFormLoading = updateLoading('form')
  47. useQuery(id, updateFormLoading, (res) => {
  48. initPois(res.mapJson)
  49. initArrounds(res)
  50. initForm(form, res)
  51. setMarketingCode(res.marketingCode)
  52. setCityId(res.cityId)
  53. })
  54. // 视频文件上传前 回调
  55. const fileUploadBeforeUpload = (file, fileList) => {
  56. return new Promise((resolve, reject) => {
  57. if (file.type === 'video/mp4' || file.type === '.mp4') {
  58. // setVideoImage(true)
  59. resolve(file)
  60. } else {
  61. notification.error({ message: '项目视频,仅支持MP4格式' })
  62. reject()
  63. }
  64. })
  65. }
  66. // 周边设施 回调
  67. function handleMapScopeChange(e) {
  68. const coordinateValue = getFieldValue('coordinate')
  69. if (!coordinateValue) {
  70. notification.error({ message: '请先选择项目坐标位置' })
  71. return
  72. }
  73. const lngLat = getFieldValue('coordinate').split(',')
  74. // 把支持的周边分类都去检索一遍
  75. setLoading({ ...loading, arround: true })
  76. Promise.all(POI_TYPES.map(({ key }) => getPoiData({ type: key, location: lngLat, radius: e }))).then((res) => {
  77. const pois = POI_TYPES.reduce((acc, { key }, inx) => {
  78. const poiArr = (res[inx] || {}).pois || []
  79. // 只用到部分字段
  80. const arrVal = poiArr.map((poi) => ({
  81. name: poi.name,
  82. id: poi.id,
  83. location: poi.location,
  84. type: poi.type,
  85. typecode: poi.typecode,
  86. pname: poi.pname,
  87. cityname: poi.cityname,
  88. adname: poi.adname,
  89. address: poi.address,
  90. pcode: poi.pcode,
  91. adcode: poi.adcode,
  92. citycode: poi.citycode,
  93. distance: poi.distance,
  94. }))
  95. return {
  96. ...acc,
  97. [key]: arrVal
  98. }
  99. }, {})
  100. initPois(pois, true)
  101. setLoading({ ...loading, arround: false })
  102. }).catch((err) => {
  103. setLoading({ ...loading, arround: false })
  104. console.error(err)
  105. notification.error({ message: '周边数据获取异常' })
  106. })
  107. }
  108. function handleSubmit(e) {
  109. e.preventDefault();
  110. props.form.validateFieldsAndScroll((err, values) => {
  111. if (!err) {
  112. updateFormLoading(true)
  113. saveData({
  114. ...values,
  115. pois,
  116. arrounds,
  117. }).then((res) => {
  118. updateFormLoading(false)
  119. notification.success({ message: '保存项目基础信息成功' })
  120. if (!id) {
  121. router.replace(`/building/add?id=${res.buildingId}`)
  122. }
  123. }).catch((err) => {
  124. console.error(err)
  125. updateFormLoading(false)
  126. notification.error({ message: err.message || err })
  127. })
  128. }
  129. });
  130. }
  131. return (
  132. <Spin spinning={loading.form}>
  133. <Form {...formItemLayout} onSubmit={handleSubmit}>
  134. <Item label="项目Id" style={{ display: 'none' }}>
  135. {getFieldDecorator('buildingId')(<Input disabled />)}
  136. </Item>
  137. <Item label="城市公司">
  138. {getFieldDecorator('institutionId',{
  139. rules: [{ required: true, message: '请选择城市公司号' }],
  140. })(
  141. <InstitutionSelect />
  142. )}
  143. </Item>
  144. <Item label="楼盘编号" >
  145. {getFieldDecorator('code', {
  146. rules: [{ required: true, message: '请输入楼盘编号' }],
  147. })(<Input />)}
  148. </Item>
  149. <Item label="楼盘名称" >
  150. {getFieldDecorator('buildingName', {
  151. rules: [{ required: true, message: '请输入楼盘名' }],
  152. })(<Input />)}
  153. </Item>
  154. <Form.Item label="项目类型">
  155. {getFieldDecorator('buildingProjectType', {
  156. rules: [{ required: true, message: '请选择项目类型' }],
  157. })(<BuildingType />)}
  158. </Form.Item>
  159. <FormGroupItem>
  160. <Form.Item label="文旅商办" help="是否将本项目归纳在【首页-文旅商办】中" {...gourpItemLayout.ab.a}>
  161. {getFieldDecorator('isCommerce', { valuePropName: 'checked' })(<Switch />)}
  162. </Form.Item>
  163. <Form.Item label="近期开盘" help="本项目是否近期开盘" {...gourpItemLayout.ab.b}>
  164. {getFieldDecorator('isRecentOpening', { valuePropName: 'checked' })(<Switch />)}
  165. </Form.Item>
  166. </FormGroupItem>
  167. <Form.Item label="列表均价" help="项目列表展示价格,示例:约10000元/㎡、约1000万元/套起">
  168. {getFieldDecorator('price', {
  169. rules: [{ required: true, message: '请输入列表均价' }],
  170. })(<Input />)}
  171. </Form.Item>
  172. <FormGroupItem>
  173. <Form.Item label="开盘日期" {...gourpItemLayout.ab.a} >
  174. {getFieldDecorator('openingDate')(
  175. <OpenTimePicker placeholder="请选择开盘日期" style={fullWidth} />
  176. )}
  177. </Form.Item>
  178. <Form.Item label="电话" {...gourpItemLayout.ab.b } >
  179. {getFieldDecorator('tel')(<Input placeholder="手机或者座机号码" />)}
  180. </Form.Item>
  181. </FormGroupItem>
  182. {/* <Form.Item label="项目说明" >
  183. {getFieldDecorator('dynamic')(<Input placeholder="项目标签补充,不超过30个字" maxLength={30}/>)}
  184. </Form.Item> */}
  185. {/* <Form.Item label="物业类型" >
  186. {getFieldDecorator('propertyType')(<Input />)}
  187. </Form.Item> */}
  188. <Form.Item label="销售状态" >
  189. {getFieldDecorator('marketStatus', {
  190. rules: [{ required: true, message: '请选择销售状态' }],
  191. })(
  192. <Select placeholder="销售状态" style={fullWidth}>
  193. <Select.Option value="未开盘">未开盘</Select.Option>
  194. <Select.Option value="在售">在售</Select.Option>
  195. <Select.Option value="售罄">售罄</Select.Option>
  196. {/* <Select.Option value="在租">在租</Select.Option> */}
  197. </Select>,
  198. )}
  199. </Form.Item>
  200. <Form.Item label="项目标签" >
  201. {getFieldDecorator('tag')(
  202. <Select mode='multiple' placeholder="项目标签" style={fullWidth}>
  203. <Select.Option value="临地铁">临地铁</Select.Option>
  204. <Select.Option value="改善盘">改善盘</Select.Option>
  205. <Select.Option value="刚需盘">刚需盘</Select.Option>
  206. <Select.Option value="配套成熟">配套成熟</Select.Option>
  207. <Select.Option value="人车分流">人车分流</Select.Option>
  208. <Select.Option value="康养社区">康养社区</Select.Option>
  209. <Select.Option value="旅游地产">旅游地产</Select.Option>
  210. <Select.Option value="品牌房企">品牌房企</Select.Option>
  211. <Select.Option value="海外地产">海外地产</Select.Option>
  212. </Select>,
  213. )}
  214. </Form.Item>
  215. <FormGroupItem>
  216. <Form.Item label="项目视频" help="视频仅支持mp4格式,建议尺寸:750*600,比例5:4,用于楼盘详情" {...gourpItemLayout.ab.a} >
  217. {getFieldDecorator('videoUrl')(
  218. <FileUpload accept=".mp4" beforeUpload={fileUploadBeforeUpload} label="上传视频" size={1} />,
  219. )}
  220. </Form.Item>
  221. <Form.Item label="视频封面图" help="建议图片尺寸:750*600px,比例5:4,格式:jpg,用于视频封面" labelCol={{span: 8}} wrapperCol={{span:16}}>
  222. {getFieldDecorator('videoImage')(
  223. <ImageUpload />,
  224. )}
  225. </Form.Item>
  226. </FormGroupItem>
  227. <Form.Item label="楼盘主图" help="建议图片尺寸:750*600px,比例5:4,格式:jpg,用于楼盘详情">
  228. {getFieldDecorator('avatarImage', {
  229. rules: [{ required: true, message: '请选择项目主图' }],
  230. })(
  231. <ImageListUpload unlimited />,
  232. )}
  233. </Form.Item>
  234. <Form.Item label="楼盘封面图" help="建议图片尺寸:750*420px,比例16:9,格式:jpg,用于楼盘列表">
  235. {getFieldDecorator('listImage', {
  236. rules: [{ required: true, message: '请选择列表图' }],
  237. })(
  238. <ImageUpload />,
  239. )}
  240. </Form.Item>
  241. <FormGroupItem>
  242. <Form.Item label="首页推荐" {...gourpItemLayout.ab.a} >
  243. {getFieldDecorator('isMain', { initialValue: 1 })(
  244. <Radio.Group>
  245. <Radio value={1}>是</Radio>
  246. <Radio value={2}>否</Radio>
  247. </Radio.Group>,
  248. )}
  249. </Form.Item>
  250. <Form.Item label="排序" help="数值越大,楼盘在小程序列表页中展示越靠前" {...gourpItemLayout.ab.b}>
  251. {getFieldDecorator('orderNo')(<InputNumber min={0} style={fullWidth} />)}
  252. </Form.Item>
  253. </FormGroupItem>
  254. <FormGroupItem>
  255. <Form.Item label="所在城市" {...gourpItemLayout.ab.a } >
  256. {getFieldDecorator('cityId', {
  257. rules: [{ required: true, message: '请选择城市' }],
  258. })(
  259. <SelectCity style={fullWidth} onChange={e=>{setCityId(e)}} />,
  260. )}
  261. </Form.Item>
  262. <Form.Item label="楼盘区域" {...gourpItemLayout.ab.b } >
  263. {getFieldDecorator('buildingArea', {
  264. rules: [{ required: true, message: '请输入楼盘区域' }],
  265. })(<AreaSelect cityId={cityId} style={fullWidth} />)}
  266. </Form.Item>
  267. </FormGroupItem>
  268. <Form.Item label="项目地址" >
  269. {getFieldDecorator('address', {
  270. rules: [{ required: true, message: '请输入项目地址' }],
  271. })(<Input />)}
  272. </Form.Item>
  273. <Form.Item label="项目坐标" >
  274. {getFieldDecorator('coordinate', {
  275. rules: [{ required: true, message: '请输入项目坐标' }],
  276. })(<Amap onChange={e => setFieldsValue({ coordinate: e })} />)}
  277. </Form.Item>
  278. <Form.Item label="周边范围" >
  279. {getFieldDecorator('mapScope', {
  280. rules: [{ required: true, message: '请选择周边设施搜索范围' }],
  281. })(
  282. <Select placeholder="周边设施搜索范围" style={fullWidth} onChange={handleMapScopeChange}>
  283. <Select.Option value={1000}>1公里</Select.Option>
  284. <Select.Option value={3000}>3公里</Select.Option>
  285. <Select.Option value={5000}>5公里</Select.Option>
  286. <Select.Option value={10000}>10公里</Select.Option>
  287. </Select>,
  288. )}
  289. </Form.Item>
  290. <Spin spinning={loading.arround}>
  291. <Form.Item label="周边交通" >
  292. <EditableArround
  293. type="Transport"
  294. pois={pois.Transport}
  295. tags={arrounds.buildingTransport}
  296. onChange={updateArrounds}
  297. onDelete={deleteArround}
  298. onPoiDelete={deletePoi}
  299. />
  300. </Form.Item>
  301. <Form.Item label="周边商业" >
  302. <EditableArround
  303. type="Mall"
  304. pois={pois.Mall}
  305. tags={arrounds.buildingMall}
  306. onChange={updateArrounds}
  307. onDelete={deleteArround}
  308. onPoiDelete={deletePoi}
  309. />
  310. </Form.Item>
  311. <Form.Item label="周边学校" >
  312. <EditableArround
  313. type="Edu"
  314. pois={pois.Edu}
  315. tags={arrounds.buildingEdu}
  316. onChange={updateArrounds}
  317. onDelete={deleteArround}
  318. onPoiDelete={deletePoi}
  319. />
  320. </Form.Item>
  321. <Form.Item label="周边医院" >
  322. <EditableArround
  323. type="Hospital"
  324. pois={pois.Hospital}
  325. tags={arrounds.buildingHospital}
  326. onChange={updateArrounds}
  327. onDelete={deleteArround}
  328. onPoiDelete={deletePoi}
  329. />
  330. </Form.Item>
  331. <Form.Item label="周边银行" >
  332. <EditableArround
  333. type="Bank"
  334. pois={pois.Bank}
  335. tags={arrounds.buildingBank}
  336. onChange={updateArrounds}
  337. onDelete={deleteArround}
  338. onPoiDelete={deletePoi}
  339. />
  340. </Form.Item>
  341. <Form.Item label="周边餐饮" >
  342. <EditableArround
  343. type="Restaurant"
  344. pois={pois.Restaurant}
  345. tags={arrounds.buildingRestaurant}
  346. onChange={updateArrounds}
  347. onDelete={deleteArround}
  348. onPoiDelete={deletePoi}
  349. />
  350. </Form.Item>
  351. </Spin>
  352. <FormGroupItem>
  353. <Form.Item label="绿化率" {...gourpItemLayout.ab.a } >
  354. {getFieldDecorator('greeningRate')(<Input />)}
  355. </Form.Item>
  356. <Form.Item label="容积率" {...gourpItemLayout.ab.b }>
  357. {getFieldDecorator('volumeRate')(<Input />)}
  358. </Form.Item>
  359. </FormGroupItem>
  360. <FormGroupItem>
  361. <Form.Item label="车位数量" {...gourpItemLayout.ab.a }>
  362. {getFieldDecorator('parkingRate', {
  363. rules: [{ validator: validMinNum }]
  364. })(<InputNumber min={0} style={fullWidth}/>)}
  365. </Form.Item>
  366. <Form.Item label="规划户数" {...gourpItemLayout.ab.b }>
  367. {getFieldDecorator('familyNum', {
  368. rules: [{ validator: validMinNum }]
  369. })(<InputNumber min={0} style={fullWidth}/>)}
  370. </Form.Item>
  371. </FormGroupItem>
  372. <FormGroupItem>
  373. <Form.Item label="供水" {...gourpItemLayout.abc.a }>
  374. {getFieldDecorator('waterSupply')(
  375. <Select style={fullWidth}>
  376. <Select.Option value="民水">民水</Select.Option>
  377. <Select.Option value="商用">商用</Select.Option>
  378. <Select.Option value="暂无">暂无</Select.Option>
  379. </Select>
  380. )}
  381. </Form.Item>
  382. <Form.Item label="供电" {...gourpItemLayout.abc.b }>
  383. {getFieldDecorator('powerSupply')(
  384. <Select style={fullWidth}>
  385. <Select.Option value="民电">民电</Select.Option>
  386. <Select.Option value="商用">商用</Select.Option>
  387. <Select.Option value="暂无">暂无</Select.Option>
  388. </Select>
  389. )}
  390. </Form.Item>
  391. <Form.Item label="供暖" {...gourpItemLayout.abc.c }>
  392. {getFieldDecorator('heatingSupply')(
  393. <Select style={fullWidth}>
  394. <Select.Option value="集中供暖">集中供暖</Select.Option>
  395. <Select.Option value="自采暖">自采暖</Select.Option>
  396. <Select.Option value="暂无">暂无</Select.Option>
  397. </Select>
  398. )}
  399. </Form.Item>
  400. </FormGroupItem>
  401. <Form.Item label="物业公司" >
  402. {getFieldDecorator('serviceCompany', {
  403. rules: [{ max: 30, message: '不超过30个字' }]
  404. })(<Input placeholder="不超过30个字"/>)}
  405. </Form.Item>
  406. <Form.Item label="物业费" >
  407. {getFieldDecorator('serviceFee', {
  408. rules: [{ max: 30, message: '不超过30个字' }]
  409. })(<Input placeholder="不超过30个字"/>)}
  410. </Form.Item>
  411. <FormGroupItem>
  412. <Form.Item label="装修标准" {...gourpItemLayout.ab.a }>
  413. {getFieldDecorator('decoration')(
  414. <Select style={fullWidth}>
  415. <Select.Option value="精装">精装</Select.Option>
  416. <Select.Option value="毛坯">毛坯</Select.Option>
  417. <Select.Option value="暂无">暂无</Select.Option>
  418. </Select>
  419. )}
  420. </Form.Item>
  421. <Form.Item label="楼栋总数" {...gourpItemLayout.ab.b }>
  422. {getFieldDecorator('buildingNum', {
  423. rules: [{ validator: validMinNum }]
  424. })(<InputNumber style={fullWidth}/>)}
  425. </Form.Item>
  426. </FormGroupItem>
  427. <FormGroupItem>
  428. <Form.Item label="交房时间" {...gourpItemLayout.ab.a }>
  429. {getFieldDecorator('receivedDate')(<DatePicker style={fullWidth}/>)}
  430. </Form.Item>
  431. <Form.Item label="产权年限" {...gourpItemLayout.ab.b }>
  432. {getFieldDecorator('rightsYear', {
  433. rules: [{ validator: validMinNum }]
  434. })(<InputNumber style={fullWidth}/>)}
  435. </Form.Item>
  436. </FormGroupItem>
  437. <Form.Item label="品牌开发商">
  438. {getFieldDecorator('brandId')(
  439. <Select style={fullWidth}>
  440. {
  441. brands.map((x) => (<Select.Option key={x.brandId} value={x.brandId}>{x.brandName}</Select.Option>))
  442. }
  443. </Select>
  444. )}
  445. </Form.Item>
  446. <Form.Item label="开发商" >
  447. {getFieldDecorator('propertyDeveloper', {
  448. rules: [{ max: 30, message: '不超过30个字' }]
  449. })(<Input placeholder="不超过30个字"/>)}
  450. </Form.Item>
  451. <Form.Item label="备案名" >
  452. {getFieldDecorator('recordName', {
  453. rules: [{ max: 30, message: '不超过30个字' }]
  454. })(<Input placeholder="不超过30个字" />)}
  455. </Form.Item>
  456. <Form.Item label="预售许可证" >
  457. {getFieldDecorator('preSalePermit',{
  458. rules: [{ max: 30, message: '不超过30个字' }]
  459. })(<Input placeholder="不超过30个字" />)}
  460. </Form.Item>
  461. <Form.Item label=" " colon={false}>
  462. <AuthButton name="building.detail.save">
  463. <Button style={{marginLeft: '6em'}} type="primary" htmlType="submit">
  464. 确定
  465. </Button>
  466. </AuthButton>
  467. <Button style={{marginLeft: '2em'}} onClick={() => router.go(-1)}>
  468. 取消
  469. </Button>
  470. </Form.Item>
  471. </Form>
  472. </Spin>
  473. )
  474. })
  475. export default connect(({ user }) => ({
  476. user: user.currentUser,
  477. }))(Form.create()(BuildingBasic))