在使用的react版本:
希望实现以下效果:
1、点击“区域”后显示:
2、点击“方式”后显示:
3、点击“租金”后显示:
4、点击“筛选”后显示:
组件结构:
已写的代码——
Filter组件代码:
import React, { Component } from 'react'
// 导入 Spring 组件
import { Spring } from 'react-spring'
import FilterTitle from '../FilterTitle'
import FilterPicker from '../FilterPicker'
import FilterMore from '../FilterMore'
// 导入自定义的axios
import { API } from '../../../../utils/api'
import styles from './index.module.css'
// 标题高亮状态
// true 表示高亮; false 表示不高亮
const titleSelectedStatus = {
area: false,
mode: false,
price: false,
more: false
}
// FilterPicker 和 FilterMore 组件的选中值
const selectedValues = {
area: ['area', 'null'],
mode: ['null'],
price: ['null'],
more: []
}
export default class Filter extends Component {
state = {
titleSelectedStatus,
// 控制 FilterPicker 或 FilterMore 组件的展示或隐藏
openType: '',
// 所有筛选条件数据
filtersData: {
// FilterMore
roomType: [],
oriented: [],
floor: [],
characteristic: [],
// FilterPicker
area: {},
subway: {},
rentType: [],
price: []
},
// 筛选条件的选中值
selectedValues
}
componentDidMount() {
// 获取到body
this.htmlBody = document.body
this.getFiltersData()
}
// 封装获取所有筛选条件的方法
async getFiltersData() {
// 获取当前定位城市id
const { value } = JSON.parse(localStorage.getItem('hkzf_city'))
const res = await API.get(`/houses/condition?id=${value}`)
this.setState({
filtersData: res.data.body
})
}
// 点击标题菜单实现高亮
onTitleClick = type => {
// 给 body 添加样式
this.htmlBody.className = 'body-fixed'
const { titleSelectedStatus, selectedValues } = this.state
// 创建新的标题选中状态对象
const newTitleSelectedStatus = { ...titleSelectedStatus }
// 遍历标题选中状态对象
// Object.keys() => ['area', 'mode', 'price', 'more']
Object.keys(titleSelectedStatus).forEach(key => {
// key 表示数组中的每一项,此处,就是每个标题的 type 值。
if (key === type) {
// 当前标题
newTitleSelectedStatus[type] = true
return
}
// 其他标题:
const selectedVal = selectedValues[key]
if (
key === 'area' &&
(selectedVal.length !== 2 || selectedVal[0] !== 'area')
) {
// 高亮
newTitleSelectedStatus[key] = true
} else if (key === 'mode' && selectedVal[0] !== 'null') {
// 高亮
newTitleSelectedStatus[key] = true
} else if (key === 'price' && selectedVal[0] !== 'null') {
// 高亮
newTitleSelectedStatus[key] = true
} else if (key === 'more' && selectedVal.length !== 0) {
// 更多选择项 FilterMore 组件
newTitleSelectedStatus[key] = true
} else {
newTitleSelectedStatus[key] = false
}
})
this.setState({
// 展示对话框
openType: type,
// 使用新的标题选中状态对象来更新
titleSelectedStatus: newTitleSelectedStatus
})
}
// 取消(隐藏对话框)
onCancel = type => {
this.htmlBody.className = ''
const { titleSelectedStatus, selectedValues } = this.state
// 创建新的标题选中状态对象
const newTitleSelectedStatus = { ...titleSelectedStatus }
// 菜单高亮逻辑处理
const selectedVal = selectedValues[type]
if (
type === 'area' &&
(selectedVal.length !== 2 || selectedVal[0] !== 'area')
) {
// 高亮
newTitleSelectedStatus[type] = true
} else if (type === 'mode' && selectedVal[0] !== 'null') {
// 高亮
newTitleSelectedStatus[type] = true
} else if (type === 'price' && selectedVal[0] !== 'null') {
// 高亮
newTitleSelectedStatus[type] = true
} else if (type === 'more' && selectedVal.length !== 0) {
// 更多选择项 FilterMore 组件
newTitleSelectedStatus[type] = true
} else {
newTitleSelectedStatus[type] = false
}
// 隐藏对话框
this.setState({
openType: '',
// 更新菜单高亮状态数据
titleSelectedStatus: newTitleSelectedStatus
})
}
// 确定(隐藏对话框)
onSave = (type, value) => {
this.htmlBody.className = ''
const { titleSelectedStatus } = this.state
// 创建新的标题选中状态对象
const newTitleSelectedStatus = { ...titleSelectedStatus }
// 菜单高亮逻辑处理
const selectedVal = value
if (
type === 'area' &&
(selectedVal.length !== 2 || selectedVal[0] !== 'area')
) {
// 高亮
newTitleSelectedStatus[type] = true
} else if (type === 'mode' && selectedVal[0] !== 'null') {
// 高亮
newTitleSelectedStatus[type] = true
} else if (type === 'price' && selectedVal[0] !== 'null') {
// 高亮
newTitleSelectedStatus[type] = true
} else if (type === 'more' && selectedVal.length !== 0) {
// 更多选择项 FilterMore 组件
newTitleSelectedStatus[type] = true
} else {
newTitleSelectedStatus[type] = false
}
const newSelectedValues = {
...this.state.selectedValues,
// 只更新当前 type 对应的选中值
[type]: value
}
const { area, mode, price, more } = newSelectedValues
// 筛选条件数据
const filters = {}
// 区域
const areaKey = area[0]
let areaValue = 'null'
if (area.length === 3) {
areaValue = area[2] !== 'null' ? area[2] : area[1]
}
filters[areaKey] = areaValue
// 方式和租金
filters.mode = mode[0]
filters.price = price[0]
// 更多筛选条件 more
filters.more = more.join(',')
// console.log(filters)
// 调用父组件中的方法,来将筛选数据传递给父组件
this.props.onFilter(filters)
// 隐藏对话框
this.setState({
openType: '',
// 更新菜单高亮状态数据
titleSelectedStatus: newTitleSelectedStatus,
selectedValues: newSelectedValues
})
}
// 渲染 FilterPicker 组件的方法
renderFilterPicker() {
const {
openType,
filtersData: { area, subway, rentType, price },
selectedValues
} = this.state
if (openType !== 'area' && openType !== 'mode' && openType !== 'price') {
return null
}
// 根据 openType 来拿到当前筛选条件数据
let data = []
let cols = 3
let defaultValue = selectedValues[openType]
switch (openType) {
case 'area':
// 获取到区域数据
data = [area, subway]
cols = 3
break
case 'mode':
data = rentType
cols = 1
break
case 'price':
data = price
cols = 1
break
default:
break
}
return (
<FilterPicker
key={openType}
onCancel={this.onCancel}
onSave={this.onSave}
data={data}
cols={cols}
type={openType}
defaultValue={defaultValue}
/>
)
}
// 渲染 FilterMore 组件
renderFilterMore() {
const {
openType,
selectedValues,
filtersData: { roomType, oriented, floor, characteristic }
} = this.state
// 移除 return null
const data = {
roomType,
oriented,
floor,
characteristic
}
const defaultValue = selectedValues.more
return (
<FilterMore
data={data}
type={openType}
onSave={this.onSave}
onCancel={this.onCancel}
defaultValue={defaultValue}
/>
)
}
// 渲染遮罩层div
renderMask() {
const { openType } = this.state
// 遮罩层是否隐藏
const isHide = openType === 'more' || openType === ''
return (
<Spring from={{ opacity: 0 }} to={{ opacity: isHide ? 0 : 1 }}>
{props => {
// 说明遮罩层已经完成动画效果,隐藏了
if (props.opacity === 0) {
return null
}
return (
<div
style={props}
className={styles.mask}
onClick={() => this.onCancel(openType)}
/>
)
}}
</Spring>
)
}
render() {
const { titleSelectedStatus } = this.state
return (
<div className={styles.root}>
{/* 前三个菜单的遮罩层 */}
{this.renderMask()}
<div className={styles.content}>
{/* 标题栏 */}
<FilterTitle
titleSelectedStatus={titleSelectedStatus}
onClick={this.onTitleClick}
/>
{/* 前三个菜单对应的内容: */}
{this.renderFilterPicker()}
{/* 最后一个菜单对应的内容: */}
{this.renderFilterMore()}
</div>
</div>
)
}
}
FilterTitle组件代码:
import React from 'react'
// import { Flex } from 'antd-mobile'
import styles from './index.module.css'
// 条件筛选栏标题数组:
const titleList = [
{ title: '区域', type: 'area' },
{ title: '方式', type: 'mode' },
{ title: '租金', type: 'price' },
{ title: '筛选', type: 'more' }
]
export default function FilterTitle({ titleSelectedStatus, onClick }) {
return (
// <Flex align="center" className={styles.root}>
<div align="center" className={styles.root}>
{titleList.map(item => {
// item.type => 'area'
const isSelected = titleSelectedStatus[item.type]
return (
// <Flex.Item key={item.type} onClick={() => onClick(item.type)}>
<div key={item.type} onClick={() => onClick(item.type)}>
{/* 选中类名: selected */}
<span
className={[
styles.dropdown,
isSelected ? styles.selected : ''
].join(' ')}
>
<span>{item.title}</span>
<i className="iconfont icon-arrow" />
</span>
</div>
// </Flex.Item>
)
})}
</div>
// </Flex>
)
}
FilterPicker组件代码:
import React, { Component } from 'react'
import { PickerView } from 'antd-mobile'
import FilterFooter from '../../../../components/FilterFooter'
export default class FilterPicker extends Component {
state = {
value: this.props.defaultValue
}
// constructor(props) {
// super(props)
// console.log('FilterPicker 创建了')
// this.state = {
// value: this.props.defaultValue
// }
// }
render() {
const { onCancel, onSave, data, cols, type } = this.props
const { value } = this.state
return (
<>
<PickerView
data={data}
value={value}
cols={cols}
onChange={val => {
this.setState({
value: val
})
}}
/>
{/* 底部按钮 */}
<FilterFooter
onCancel={() => onCancel(type)}
onOk={() => onSave(type, value)}
/>
</>
)
}
}
FilterMore组件代码:
import React, { Component } from 'react'
import { Spring } from 'react-spring'
import FilterFooter from '../../../../components/FilterFooter'
import styles from './index.module.css'
export default class FilterMore extends Component {
state = {
selectedValues: this.props.defaultValue
}
// 标签点击事件
onTagClick(value) {
const { selectedValues } = this.state
// 创建新数组
const newSelectedValues = [...selectedValues]
if (newSelectedValues.indexOf(value) <= -1) {
// 没有当前项的值
newSelectedValues.push(value)
} else {
// 有
const index = newSelectedValues.findIndex(item => item === value)
newSelectedValues.splice(index, 1)
}
this.setState({
selectedValues: newSelectedValues
})
}
// 渲染标签
renderFilters(data) {
const { selectedValues } = this.state
// 高亮类名: styles.tagActive
return data.map(item => {
const isSelected = selectedValues.indexOf(item.value) > -1
return (
<span
key={item.value}
className={[styles.tag, isSelected ? styles.tagActive : ''].join(' ')}
onClick={() => this.onTagClick(item.value)}
>
{item.label}
</span>
)
})
}
// 取消按钮的事件处理程序
onCancel = () => {
this.setState({
selectedValues: []
})
}
// 确定按钮的事件处理程序
onOk = () => {
const { type, onSave } = this.props
// onSave 是父组件中的方法
onSave(type, this.state.selectedValues)
}
render() {
const {
data: { roomType, oriented, floor, characteristic },
onCancel,
type
} = this.props
// 该组件是否展示
const isOpen = type === 'more'
return (
<div className={styles.root}>
{/* 遮罩层 */}
<Spring to={{ opacity: isOpen ? 1 : 0 }}>
{props => {
if (props.opacity === 0) {
return null
}
return (
<div
style={props}
className={styles.mask}
onClick={() => onCancel(type)}
/>
)
}}
</Spring>
<Spring
to={{ transform: `translate(${isOpen ? '0px' : '100%'}, 0px)` }}
>
{props => {
return (
<>
{/* 条件内容 */}
<div style={props} className={styles.tags}>
<dl className={styles.dl}>
<dt className={styles.dt}>户型</dt>
<dd className={styles.dd}>
{this.renderFilters(roomType)}
</dd>
<dt className={styles.dt}>朝向</dt>
<dd className={styles.dd}>
{this.renderFilters(oriented)}
</dd>
<dt className={styles.dt}>楼层</dt>
<dd className={styles.dd}>{this.renderFilters(floor)}</dd>
<dt className={styles.dt}>房屋亮点</dt>
<dd className={styles.dd}>
{this.renderFilters(characteristic)}
</dd>
</dl>
</div>
{/* 底部按钮 */}
<FilterFooter
style={props}
className={styles.footer}
cancelText="清除"
onCancel={this.onCancel}
onOk={this.onOk}
/>
</>
)
}}
</Spring>
</div>
)
}
}
chrome浏览器报错:
尝试过很多方法也没能解决问题,请问代码哪里出了问题?如何修改?