要实现像 element-plus 的多级 Cascader 级联选择器,可以结合使用 @hotwired/stimulus 和 turbo。

首先,需要定义一个 CascaderController,用于管理 Cascader 组件的行为。在 CascaderController 中,需要定义一些属性和方法,如:

import { Controller } from "@hotwired/stimulus"

export default class CascaderController extends Controller {
  static targets = ["input", "menu", "item"]

  connect() {
    this.levels = []
    this.levelData = []
    this.selectedValues = []
    this.updateMenu()
  }

  updateMenu() {
    // 更新菜单
  }

  fetchData(level, value) {
    // 获取级别数据
  }

  selectValue(value) {
    // 选中某个值
  }

  deselectValue(value) {
    // 取消选中某个值
  }

  toggleMenu() {
    // 切换菜单显示状态
  }
}

接下来,需要在 HTML 中定义一个 Cascader 组件,如:

<div data-controller="cascader">
  <input type="text" data-cascader-target="input" readonly>
  <ul data-cascader-target="menu" hidden>
    <!-- 级别菜单 -->
  </ul>
</div>

在 CascaderController 中,需要实现 updateMenu 方法,用于更新菜单。updateMenu 方法的实现方式如下:

updateMenu() {
  // 清空菜单
  this.menuTarget.innerHTML = ""

  // 根据 selectedValues 和 levelData 构建菜单
  let data = this.levelData
  this.selectedValues.forEach((value, index) => {
    let item = data.find(item => item.value === value)
    if (item) {
      let li = document.createElement("li")
      li.textContent = item.label
      li.dataset.value = item.value
      li.dataset.index = index
      li.dataset.action = "cascader#selectValue"
      li.dataset.turbo = "false"
      this.menuTarget.appendChild(li)

      data = item.children
    }
  })

  // 如果还有剩余级别,则添加一个子菜单项
  if (data.length > 0) {
    let li = document.createElement("li")
    li.textContent = "请选择"
    li.dataset.index = this.selectedValues.length
    li.dataset.action = "cascader#toggleMenu"
    li.dataset.turbo = "false"
    this.menuTarget.appendChild(li)
  }
}

updateMenu 方法首先会清空菜单,然后根据 selectedValues 和 levelData 构建新的菜单。在构建菜单时,会依次遍历 selectedValues 中的每个值,并查找对应的数据项。如果找到了,就在菜单中添加一个 li 元素,同时更新当前的 data 变量为该数据项的 children。如果还有剩余级别,则添加一个子菜单项。

接下来,需要实现 fetchData 方法,用于获取级别数据。fetchData 方法的实现方式如下:

fetchData(level, value) {
  // 如果当前级别已经有数据,则不需要再次请求数据
  if (this.levelData[level]) {
    return Promise.resolve()
  }

  // 发送请求获取数据
  return fetch(`/api/data/${value}`)
    .then(response => response.json())
    .then(data => {
      this.levelData[level] = data
      this.updateMenu()
    })
}

fetchData 方法会判断当前级别是否已经有数据,如果已经有数据,则不需要再次请求数据。否则,会发送一个请求获取数据,并更新 levelData 变量。请求完成后,会调用 updateMenu 方法更新菜单。

接下来,需要实现 selectValue 和 deselectValue 方法,用于选中和取消选中某个值。selectValue 和 deselectValue 方法的实现方式如下:

selectValue(value) {
  // 如果当前级别是最后一个级别,则直接选中该值
  if (this.selectedValues.length === this.levels.length - 1) {
    this.selectedValues.push(value)
    this.updateMenu()
    return
  }

  // 如果当前级别不是最后一个级别,则需要继续加载下一级别的数据
  let level = this.selectedValues.length
  this.selectedValues.push(value)
  this.fetchData(level + 1, value)
}

deselectValue(value) {
  // 如果当前级别是最后一个级别,则直接取消选中该值
  if (this.selectedValues.length === this.levels.length - 1) {
    this.selectedValues = this.selectedValues.filter(v => v !== value)
    this.updateMenu()
    return
  }

  // 如果当前级别不是最后一个级别,则需要取消选中该值,并清空下一级别的数据
  let level = this.selectedValues.length
  this.selectedValues = this.selectedValues.filter(v => v !== value)
  this.levelData = this.levelData.slice(0, level)
  this.updateMenu()
}

selectValue 方法会根据当前级别是否为最后一个级别来决定如何选中该值。如果当前级别是最后一个级别,则直接选中该值,并更新菜单。否则,需要继续加载下一级别的数据,并更新 selectedValues 和 levelData 变量。

deselectValue 方法同样会根据当前级别是否为最后一个级别来决定如何取消选中该值。如果当前级别是最后一个级别,则直接取消选中该值,并更新菜单。否则,需要取消选中该值,并清空下一级别的数据,并更新 selectedValues 和 levelData 变量。

最后,需要实现 toggleMenu 方法,用于切换菜单的显示状态。toggleMenu 方法的实现方式如下:

toggleMenu() {
  this.menuTarget.hidden = !this.menuTarget.hidden
}

toggleMenu 方法会切换菜单的 hidden 属性来实现菜单的显示和隐藏。

到此为止,一个基本的多级 Cascader 级联选择器就已经实现了。当然,还可以根据具体需求进行更多的定制和扩展。

hotwiredstimulus 和 turbo 实现像element-plus 一样的多级Cascader 级联选择器

原文地址: https://www.cveoy.top/t/topic/Fww 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录