子组件sandCommon:
import React, { useEffect, useState } from 'react' import Taro from '@tarojs/taro' import { View, MovableArea, MovableView, Image, Text, Label, CheckboxGroup, Checkbox } from '@tarojs/components' import classnames from 'classnames' import { includes, find } from 'lodash' import api from '@services/api' import app from '@services/request' import './common.scss' interface ISandState { value: string text: string checked: boolean } const INIT_SAND_STATE: ISandState[] = [ { value: '1', text: '在售', checked: true }, { value: '2', text: '待售', checked: true }, { value: '3', text: '售完', checked: true } ] interface IShowState { show: boolean text: string } const INIT_SAND_DATA = { sandBuilding: [] } const INIT_SHOW_STATE = { show: true, text: '收起' } interface IProps { houseId: string, outerWidth?: number, outerHeight: number, currentBuilding?: any, setCurrentBuilding: (any) => void, updateSandBuilding: (any) => void } const SandCommon = (props: IProps) => { const { houseId, outerHeight, currentBuilding = {} } = props const [movableView, setMovableView] = useState<any>({}) const [showState, setShowState] = useState<IShowState>(INIT_SHOW_STATE) const [sandState, setSandState] = useState<ISandState[]>(INIT_SAND_STATE) const [sandData, setSandData] = useState<any>(INIT_SAND_DATA) const [sandBuilding, setSandBuilding] = useState<any>(INIT_SAND_DATA.sandBuilding) const [current, setCurrent] = useState<any>({}) const safeArea = Taro.getSystemInfoSync().safeArea const outerWidth = props.outerWidth ? props.outerWidth : safeArea.width useEffect(() => { fetchSand() }, [houseId]) useEffect(() => { setCurrent(currentBuilding) }, [currentBuilding]) const handleSandImageLoad = (e: any) => { setMovableView({ e.detail.width, height: e.detail.height }) } const fetchSand = () => { app.request({ url: app.areaApiUrl(api.getHouseSand), data: { id: houseId } }).then((result: any) => { setSandData(result) showSandBuilding(INIT_SAND_STATE, result.sandBuilding) props.updateSandBuilding(result.sandBuilding) }) } const toggleShowState = () => { setShowState({ show: !showState.show, text: showState.show ? '展开' : '收起' }) } const handleCheckboxChange = (e: any) => { const values = e.detail.value for (const item of sandState) { if (includes(values, item.value)) { item.checked = true } else { item.checked = false } } setSandState(sandState) showSandBuilding(sandState, sandData.sandBuilding) } const showSandBuilding = (sandState, allSandBUilding) => { let sandBuilding: any[] = [] for (const item of allSandBUilding) { const target = find(sandState, { value: item.sale_status }) if (target.checked) { sandBuilding.push(item) } } setSandBuilding(sandBuilding) } const switchCurrent = (item: any) => { props.setCurrentBuilding(item) } return ( <View className="sand-card" style={{ '100%', height: outerHeight }}> <MovableArea className="sand-area"> <MovableView x={(outerWidth - movableView.width) / 2} y={(outerHeight - movableView.height) / 2} style={movableView} className="sand-view" direction="all" animation={false} > <Image className="sand-image" src={sandData.fang_sand_pic} onLoad={handleSandImageLoad} /> { sandBuilding.map((item: any, index: number) => ( <View key={index} style={item.style} className={classnames('sand-item', `sale-status-${item.sale_status}`, current.id === item.id && 'actived')} onClick={() => switchCurrent(item)} > <Text>{item.name}</Text> <Text className="triangle-down"></Text> </View> )) } </MovableView> </MovableArea> <View className="sand-state"> <CheckboxGroup onChange={handleCheckboxChange} className={classnames('sand-state-box', !showState.show && 'hide')} > { sandState.map((item: any, index: any) => ( <Label key={index} for={index} className={classnames('check-label', `sale-status-${item.value}`)} > <Checkbox id={index} className="check-box" value={item.value} checked={item.checked} > </Checkbox> <Text className="check-text">{item.text}</Text> </Label> )) } </CheckboxGroup> <View className="sand-state-btn" onClick={toggleShowState}>{showState.text}</View> </View> </View> ) } export default SandCommon
使用useMemo + useEffect监听props更新的字段:
useEffect(() => {
fetchSand()
}, [houseId])监听houseId发生变化重新渲染
const getSandCommonComponent = useMemo(() => { return ( <SandCommon houseId={houseData.id} outerHeight={200} currentBuilding={{}} setCurrentBuilding={toHouseSand} updateSandBuilding={() => { }} /> ) }, [houseData.id])
const { currentBuilding = {}} = props useEffect(() => { setCurrent(currentBuilding) }, [currentBuilding])监听currentBuilding发生变化重新渲染
父组件HouseSand:
import React, { useEffect, useMemo, useState } from 'react' import Taro from '@tarojs/taro' import { View, Text, ScrollView } from '@tarojs/components' import classnames from 'classnames' import api from '@services/api' import app from '@services/request' import NavBar from '@components/navbar' import SandCommon from '@house/pages/new/sand/common' import '@styles/common/house.scss' import '@house/styles/common.scss' import './index.scss' const INIT_SAND_BUILDING = [] const HouseSand = () => { const INTI_CURRENT = { id: '167' } const [sandBuilding, setSandBuilding] = useState<any>(INIT_SAND_BUILDING) const [roomData, setRoomData] = useState<any[]>([]) const [current, setCurrent] = useState<any>(INTI_CURRENT) useEffect(() => { fetchRoom() }, [current.id]) const safeArea = Taro.getSystemInfoSync().safeArea const fetchRoom = () => { app.request({ url: app.areaApiUrl(api.getHouseSandRoom), data: { id: current.id } }).then((result: any) => { setRoomData(result) }) } const handleRoomCheck = (item: any) => { console.log(item) } const getSandCommonComponent = useMemo(() => { return ( <SandCommon outerWidth={safeArea.width} outerHeight={300} currentBuilding={current} setCurrentBuilding={(currentBuilding) => setCurrent(currentBuilding)} updateSandBuilding={(sandBuilding) => setSandBuilding(sandBuilding)} ></SandCommon> ) }, [current]) return ( <View className="sand"> <NavBar title="楼盘沙盘图"></NavBar> <View className="sand-wrapper"> {getSandCommonComponent} <View className="sand-content"> <View className="sand-info"> <ScrollView className="sand-info-header" scrollX scrollIntoView={`view_${current.id}`} > <View className="sand-list"> { sandBuilding.map((item: any, index: number) => ( <View key={index} id={`view_${item.id}`} className={classnames('sand-item', current.id === item.id && 'actived')} onClick={() => setCurrent(item)} >{item.name} </View> )) } </View> </ScrollView> <View className="sand-info-detail"> <View className="sand-info-detail-content view-content"> <View className="detail-item"> <Text className="label">规划户数:</Text> <Text>{current.plan_households}</Text> </View> <View className="detail-item"> <Text className="label">楼层:</Text> <Text>{current.storey_height}</Text> </View> <View className="detail-item"> <Text className="label">梯户配比:</Text> <Text>{current.elevator_number}梯{current.elevator_households}户</Text> </View> </View> <View className="sand-info-detail-room mt20"> <View className="room-header"> <Text className="title">户型</Text> </View> <View className="room-list"> { roomData.length > 0 ? roomData.map((item: any, index: number) => { const itemData = item.fangHouseBuildingRoom return ( <View key={index} className="room-item"> <Text className="item-text">{itemData.name}</Text> <Text className="item-text">{itemData.room}室{itemData.office}厅{itemData.toilet}卫</Text> <Text className="item-text">{itemData.building_area}㎡</Text> <Text className="item-btn" onClick={() => handleRoomCheck(itemData)}>查看</Text> </View> ) }) : <View className="room-item"> <Text className="item-text">暂无数据</Text> </View> } </View> </View> </View> </View> </View> </View> </View> ) } export default HouseSand
当current发生变化后更新子组件:
const getSandCommonComponent = useMemo(() => { return ( <SandCommon outerWidth={safeArea.width} outerHeight={300} currentBuilding={current} setCurrentBuilding={(currentBuilding) => setCurrent(currentBuilding)} updateSandBuilding={(sandBuilding) => setSandBuilding(sandBuilding)} ></SandCommon> ) }, [current])
子组件样式:
.sand-card { position: relative; .sand-state { display: flex; justify-content: center; align-items: center; position: absolute; right: 30px; bottom: 30px; &-box { flex: auto; height: 60px; line-height: 60px; font-size: $font-basic; padding: 0 40px 0 20px; margin-right: -30px; border-radius: 30px; background-color: rgba($color: $white, $alpha: 0.9); transition: 0.3s; &.hide { width: 0; padding: 0; overflow: hidden; } .check-label { margin: 0 10px; padding: 2px 16px; border-radius: 20px; .check-box { vertical-align: 2px; .wx-checkbox-input { width: 30px; height: 30px; } } .check-text { color: $white; } } } &-btn { width: 90px; height: 90px; line-height: 90px; font-size: $font-basic; border-radius: 50%; text-align: center; background-color: $white; color: $title-color; } } .sand-area { width: 100%; height: 100%; overflow: hidden; background-color: $bg-color; .sand-view { .sand-image { width: 100%; height: 100%; } .sand-item { position: absolute; font-size: $font-basic; padding: 10px 16px; border-radius: $border-radius-base; color: $white; .triangle-down { position: absolute; top: 54px; left: 16px; display: block; border-style: solid; border-width: 16px 16px 0; } &.actived { background-color: $primary-color; color: $white; .triangle-down { border-color: $primary-color transparent transparent; } } } } } }