index.jsx 18KB

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