Projet React projet d'application de location de combat réel (7) liste de dépistage des conditions pour le module de logement

avant-propos

1. Module de recherche de maisons de liste - filtrage conditionnel (ci-dessous)

1.1 Implémentation du composant de contenu FilterPicker correspondant aux trois premiers menus

1.1.1 Analyse des idées

1、点击前三个标题展示该组件,点击取消的时候隐藏
2、使用PickerView组件来实现页面效果
3、获取到PickerView组件中,选中的筛选条件值
4、点击确定按钮,隐藏该组件,将获取到的筛选条件值传递给父组件
5、由父组件提供展示或隐藏对话框的状态,通过props传递给子组件
6、由父组件提供通过筛选条件获取到的数据(因为所有筛选条件是通过后端接口来获取的),通过props传递给子组件

1.1.2 Étapes de mise en œuvre

Les étapes qui doivent être complétées dans le composant Filtre :

1、提供组件展示或隐藏的状态:openType
2、在render方法中判断当openType的值为 area/mode/price 时,就显示 FilterPicker组件,以及遮罩层
3、在onTitleClick方法中,修改状态 openType为当前 type,展示对话框
4、提供onCancel方法(作为取消按钮和遮罩层的事件)
5、在onCancel方法中,修改状态 openType为空,隐藏对话框
6、将onCancel通过props传递给FilterPicker组件
7、提供onSave方法,作为确定按钮的事件处理
8、发送请求,获取所有筛选条件数据,将数据保存为状态:filtersData
9、封装方法 renderFilterPicker 来渲染FilterPicker组件
10、在方法中,根据openType的类型,从filtersData中获取需要的数据
11、将数据通过props传递给FilterPicker组件

Étapes à suivre dans le composant FilterPicker :

1、接收到数据后,将其作为PickerView组件的data
2、在取消按钮的单击事件中调用父组件传过来的方法

1.1.3 Exemple de code

Ajoutez le code suivant dans src/pages/HouseList/components/Filter/index.js :
Tout d'abord, fournissez les données de statut et de condition de filtre affichées ou masquées par le composant :

  state = {
    
    
    // 控制 FilterPicker 或 FilterMore 组件的展示或隐藏
    openType: '',
    // 所有筛选条件数据
    filtersData: {
    
    }
  }

Ensuite, lorsque la valeur de openType est déterminée comme étant area/mode/price dans la méthode de rendu, le composant FilterPicker et le calque de masque sont affichés :

{
    
    /* 前三个菜单的遮罩层 */}
{
    
     openType === "area" || openType === "mode" || openType === "price" ? (<div className={
    
    styles.mask}></div>) : ("")}
...
{
    
    /* 前三个菜单对应的内容: */}
{
    
    openType === "area" || openType === "mode" || openType === "price" ? (<FilterPicker />) : ("")}

Ensuite, dans l'événement de clic sur le titre, modifiez l'état openType au type actuel et affichez la boîte de dialogue :

  onTitleClick = type => {
    
    
    this.setState(prevState => {
    
    
      return {
    
    
        titleSelectedStatus: {
    
    
          // 获取当前对象中所有属性的值
          ...prevState.titleSelectedStatus,
          [type]: true
        },

        // 展示对话框
        openType: type
      }
    })
  }

Ensuite, créez une nouvelle méthode onCancel en tant qu'événement du bouton d'annulation et du calque de masque :

  // 取消(隐藏对话框)
  onCancel = () => {
    
    
    this.setState({
    
    
      openType: ''
    })
  }

Ensuite, fournissez la méthode onSave en tant que gestionnaire d'événements pour le bouton OK

// 保存,隐藏对话框
onSave = () => {
    
    
  this.setState({
    
    
    openType: ""
  });
};

// 传递给FilterPicker
<FilterPicker
    onCancel={
    
    this.onCancel}
    onSave={
    
    this.onSave}
/>

//在FilterPicker里面进行一次中转,最后这个按钮是在FilterFooter里面
render() {
    
    
  let {
    
     onCancel ,onSave} = this.props;
  return (
    <>
      {
    
    /* 选择器组件: */}
      <PickerView data={
    
    province} value={
    
    null} cols={
    
    3} />


      {
    
    /* 底部按钮 */}
      <FilterFooter onCancel={
    
    onCancel} onOk={
    
    onSave}/>
    </>
  );
}


// 在FilterFooter里面调用
function FilterFooter({
    
    
  cancelText = '取消',
  okText = '确定',
  onCancel,
  onOk,
  className
}) {
    
    
  return (
    <Flex className={
    
    [styles.root, className || ''].join(' ')}>
      {
    
    /* 取消按钮 */}
      <span
        className={
    
    [styles.btn, styles.cancel].join(' ')}
        onClick={
    
    onCancel}
      >
        {
    
    cancelText}
      </span>

      {
    
    /* 确定按钮 */}
      <span className={
    
    [styles.btn, styles.ok].join(' ')} onClick={
    
    onOk}>
        {
    
    okText}
      </span>
    </Flex>
  )
}

Ensuite, envoyez une requête pour obtenir toutes les données du filtre :

  // 封装获取所有筛选条件的方法
  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
    })
  }

Ensuite, la méthode d'encapsulation rend le composant FilterPicker :

  // 渲染 FilterPicker 组件的方法
  renderFilterPicker() {
    
    
    const {
    
    
      openType,
      filtersData: {
    
     area, subway, rentType, price }
    } = this.state

    if (openType !== 'area' && openType !== 'mode' && openType !== 'price') {
    
    
      return null
    }

    // 根据 openType 来拿到当前筛选条件数据
    let data = []
    let cols = 3
    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
        onCancel={
    
    this.onCancel}
        onSave={
    
    this.onSave}
        data={
    
    data}
        cols={
    
    cols}
      />
    )
  }

Ajoutez le code suivant dans src/pages/HouseList/components/FilterPicker/index.js :
Tout d'abord, après avoir reçu les données, utilisez-les comme données du composant PickerView :

export default class FilterPicker extends Component {
    
    
  render() {
    
    
    const {
    
     onCancel, onSave, data, cols } = this.props
    return (
      <>
        {
    
    /* 选择器组件: */}
        <PickerView data={
    
    data} value={
    
    null} cols={
    
    cols} />

        {
    
    /* 底部按钮 */}
        <FilterFooter onCancel={
    
    () => onCancel()} onOk={
    
    () => onSave()} />
      </>
    )
  }
}

Ensuite, ajoutez la valeur d'état pour obtenir la valeur sélectionnée du composant PickerView :

  state = {
    
    
    value: null
  }

Ensuite, ajoutez l'élément de configuration onChange au composant PickerView, obtenez la valeur sélectionnée via le paramètre et mettez à jour la valeur d'état :

<PickerView
  data={
    
    data}
  // 我们一旦监听了 onChange事件,同步了value值,那么这个组件成了受控组件,所以我们需要同步value的值
  value={
    
    this.state.value}
  cols={
    
    cols}
  onChange={
    
    val => {
    
    
    this.setState({
    
     value: val });
  }}
/>

Ensuite, dans le gestionnaire d'événements du bouton OK, transmettez le type et la valeur en tant que paramètres au composant parent :

{
    
    /* 底部按钮 */}
<FilterFooter onCancel={
    
    onCancel} onOk={
    
    () => onSave(type,this.state.value)} />

1.2 Définir la valeur sélectionnée par défaut

1.2.1 Exigences

Si un élément a déjà été sélectionné, lorsque nous affichons à nouveau le FilterPicker, l'élément sélectionné par défaut doit être affiché

1.2.2 Étapes de mise en œuvre

1、在Filter组件中,提供选中值状态: selectedValues
2、通过openType获取到当前类型的选中值(defaultValue),通过props传递给FilterPicker组件
3、在FilterPicker组件中,将当前defaultValue设置为状态value的默认值
4、在点击确定按钮后,在Filter组件中更新当前type对应的selectedValues状态值

1.2.3 Exemple de code

Ajoutez le code suivant dans src/pages/HouseList/components/Filter/index.js :
 Premièrement, indiquez le statut de la valeur sélectionnée :

// 默认选中的状态
const selectedValues = {
    
    
  area: ["area", null],
  mode: ["null"],
  price: ["null"],
  more: []
};
...
  state = {
    
    
    ...
    // 筛选默认选中的状态值
    selectedValues
  };

Ensuite, récupérez la valeur sélectionnée du type actuel et transmettez-la au composant FilterPicker via les accessoires :

const {
    
    
  ...,
  selectedValues
} = this.state;
// 默认选中值
let defaultValue = selectedValues[openType];
...
<FilterPicker
  ...
  defaultValue={
    
    defaultValue}
/>

Puis, après avoir cliqué sur le bouton OK, mettez à jour la valeur d'état de selectedValues ​​correspondant au type actuel :

// 保存,隐藏对话框
onSave = (type, value) => {
    
    
  this.setState({
    
    
    openType: '',
    selectedValues: {
    
    
      ...this.state.selectedValues,
      [type]: value
    }
  });
};

1.2.4 La valeur par défaut ne prend pas effet

问题描述:在前面三个标签之间来回切换时候,默认选中值不会生效,只有当点击确定,重新打开FilterPicker组件时,才会生效
分析:两种操作方式的区别在于有没有重新创建FilterPicker组件,重新创建的时候,会生效,不重新创建,不会生效
原因:不重新创建FilterPicker组件时,不会再次执行state初始化,也就拿不到最新的props
解决方式:给FilterPicker组件添加key值为openType,这样,在不同标题之间切换时候,key值都不相同,React内部会在key不同时候,重新创建该组件

1.3 Améliorer la fonction de surbrillance FilterTitle

1.3.1 Idées de mise en œuvre

1、点击标题时,遍历标题高亮数据
2、如果是当前标题,直接设置为高亮
3、分别判断每个标题对应的筛选条件有没有选中值(判断每个筛选条件的选中值与默认值是否相同,相同表示没有选中值,不同,表示选中了值)
    * selectedVal 表示当前type的选中值
    * 如果type为area,此时,selectedVal.length !== 2 || selectedVal[0] !== 'area',就表示已经有选中值
    * 如果 type 为 mode,此时,selectedVal[0] !== 'null',就表示已经有选中值
    * 如果 type 为 price,此时,selectedVal[0] !== 'null',就表示有选中值
4、如果有,就让该标题保持高亮
5、如果没有,就让该标题取消高亮
在之前的初始化组件时,我们已经完成了前两步,现在来继续实现后面三步

1.3.2 Étapes de mise en œuvre

1、在标题点击事件 onTitleClick事件里面,获取到两个状态:标题选中状态对象和筛选条件的选中值对象
2、根据当前标题选中状态对象,获取到一个新的标题选中状态对象(newTitleSelectedStatus)
3、使用Object.keys(),遍历标题选中状态对象
4、先判断是否为当前标题,如果是,直接让该标题选中状态为true(高亮)
5、否则,分别判断每个标题的选中值是否与默认值相同
6、如果不同,则设置该标题的选中状态为true
7、如果相同,则设置该标题的选中状态为false
8、更新状态 titleSelectedStatus的值为: newTitleSelectedStatus

1.3.3 Exemple de code

Ajoutez le code suivant dans src/pages/Houselist/components/Filter/index.js :

  onTitleClick = type => {
    
    
    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') {
    
    
        // 更多选择项 FilterMore 组件
      } else {
    
    
        newTitleSelectedStatus[key] = false
      }
    })

    this.setState({
    
    
      // 展示对话框
      openType: type,
      // 使用新的标题选中状态对象来更新
      titleSelectedStatus: newTitleSelectedStatus
    })
  }

1.4 Module de recherche de liste - Implémentation du composant FilterMore

1.4.1 Rendre les données des composants

Étapes de mise en œuvre :

1、封装renderFilterMore方法,渲染FilterMore组件
2、从filtersData中,获取数据(roomType,oriented,floor,characteristic),通过props传递给FilterMore组件
3、FilterMore组件中,通过props获取到数据,分别将数据传递给renderFilters方法
4、在renderFilters方法中,通过参数接收数据,遍历数据,渲染标签

Exemple de code :
Ajoutez le code suivant dans src/pages/Houselist/components/Filter/index.js :

renderFilterMore() {
    
    
  // 获取对应数据 roomType,oriented,floor,characteristic
  const {
    
    
    openType,
    filtersData: {
    
     roomType, oriented, floor, characteristic }
  } = this.state;
  // 把数据封装到一个对象中,方便传递
  const data = {
    
    
    roomType,
    oriented,
    floor,
    characteristic
  };
  if (openType !== "more") {
    
    
    return null;
  }
  // 传递给子组件
  return <FilterMore data={
    
    data}/>;
}
在src/pages/Houselist/components/FilterMore/index.js中添加如下代码:

// 渲染标签
renderFilters(data) {
    
    
  // 高亮类名: styles.tagActive
  return data.map(item => {
    
    
    return (
      <span key={
    
    item.value} className={
    
    [styles.tag].join(" ")}>{
    
    item.label}</span>
    );
  });
}

render() {
    
    
  const {
    
    
    data: {
    
     roomType, oriented, floor, characteristic }
  } = this.props;
  return (
    <div className={
    
    styles.root}>
      ...
      <div 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}>
          ...
      </div>
      ...
    </div>
  );
}

1.4.2 Obtenir la valeur sélectionnée et la mettre en surbrillance

Étapes de mise en œuvre :

1、在state中添加状态 selectedValues
2、给标签绑定单击事件,通过参数获取到当前项的value
3、判断selectedValues中是否包含当前value值
4、如果不包含,就将当前项的value添加到selectedValues数组中
5、如果包含,就从selectedValues数组中移除(使用数组的splice方法,根据索引号删除)
6、在渲染标签时,判断selectedValues数组中,是否包含当前项的value,包含,就添加高亮类

Exemple de code :
Ajoutez le code suivant dans src/pages/Houselist/components/FilterMore/index.js :

  state = {
    
    
    selectedValues: []
  }

  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>
      )
    })
  }

1.4.3 Traitement logique des boutons Effacer et OK

Étapes de mise en œuvre :

1、设置FilterFooter组件的取消按钮文字为: 清除
2、点击取消按钮时,清空所有选中的项的值(selectedValues:[]3、点击确定按钮时,将当前选中项的值和type,传递给Filter父组件
4、在Filter组件中的onSave方法中,接收传递过来的选中值,更新状态selectedValues

Exemple de code :
Ajoutez le code suivant dans src/pages/Houselist/components/FilterMore/index.js :

 // 取消按钮的事件处理程序
  onCancel = () => {
    
    
    this.setState({
    
    
      selectedValues: []
    })
  }

  // 确定按钮的事件处理程序
  onOk = () => {
    
    
    const {
    
     type, onSave } = this.props
    // onSave 是父组件中的方法
    onSave(type, this.state.selectedValues)
  }

        <FilterFooter
          className={
    
    styles.footer}
          cancelText="清除"
          onCancel={
    
    this.onCancel}
          onOk={
    
    this.onOk}
        />
在src/pages/Houselist/components/Filter/index.js中添加如下代码:

//传递type跟onSave
<FilterMore data={
    
    data} type={
    
    openType} onSave={
    
    this.onSave} defaultValues={
    
    defaultValues}/>;

1.4.4 Définir la valeur sélectionnée par défaut

Étapes de mise en œuvre :

1、在渲染FilterMore组件时,从selectedValues中,获取到当前选中值more
2、通过props讲选中值传递给FilterMore组件
3、在FilterMore组件中,讲获取到的选中值,设置为子组件状态selectedValues的默认值
4、给遮罩层绑定事件,在事件中,调用父组件的onCancel关闭FilterMore组件

Exemple de code :
Ajoutez le code suivant dans src/pages/Houselist/components/Filter/index.js :

renderFilterMore() {
    
    
  ...
  let defaultValues = selectedValues.more
  // 传递给子组件
  return <FilterMore data={
    
    data} type={
    
    openType} onSave={
    
    this.onSave} defaultValues={
    
    defaultValues} onCancel={
    
    this.onCancel}/>;
}
在src/pages/Houselist/components/FilterMore/index.js中添加如下代码:

state = {
    
    
  selectedValues: this.props.defaultValues
};

{
    
    /* 遮罩层 */} 
<div className={
    
    styles.mask} onClick={
    
    this.props.onCancel}/>

1.4.5 Améliorer la fonction de surbrillance FilterTitle

Étapes de mise en œuvre :

1、在Filter组件的onTitleClick方法中,添加type为more的判断条件
2、当选中值数组长度不为0的时候,表示FilterMore组件中有选中项,此时,设置选中状态高亮
3、点击确定按钮时,根据参数type和value,判断当前菜单是否高亮
4、在关闭对话框时(onCancel),根据type和当前type的选中值,判断当前菜单是否高亮

Exemple de code :
Ajoutez le code suivant dans src/pages/Houselist/components/Filter/index.js :

// 保存,隐藏对话框
  onSave = (type, value) => {
    
    
    const {
    
     titleSelectedStatus } = this.state;
    let newTitleSelectedStatus = {
    
     ...titleSelectedStatus };
    let 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) {
    
    
      // 更多选择
      newTitleSelectedStatus[type] = true;
    } else {
    
    
      newTitleSelectedStatus[type] = false;
    }
    this.setState({
    
    
      openType: "",
      titleSelectedStatus: newTitleSelectedStatus,
      selectedValues: {
    
    
        ...this.state.selectedValues,
        [type]: value
      }
    });
  };

Résumer

Guess you like

Origin blog.csdn.net/qq_40652101/article/details/128518587