vue3通过el-cascader实现动态菜单切换页面

如果只有一级菜单只会显示一个按钮
在这里插入图片描述

 <div style="width: 100%; margin-top: 10px; display: flex; align-items: center; border-bottom: 1px solid #ccc;">
    <template v-for="(menu, index) in cascaderData" :key="index">
      <el-cascader
          v-if="menu.children && menu.children.length > 0"
          style="width: 120px;"
          v-model="selectedOptions[index]"
          :options="menu.children"
          @change="handleCascaderChange(index, $event)"
          :placeholder="menu.label"
      >
      </el-cascader>

      <el-button :style="{color: buttonColor}" plain @click="handleSelect(String(index))" v-else>
        {{ menu.label }}
      </el-button>
    </template>
  </div>

菜单结构说明:component对应的就是你要跳转的页面组件,也就是上面import进来的组件。然后通过emitComponentChange方法来跳转对应页面。

<script setup>
import {useRouter} from 'vue-router';
import signal from '@/views/drawing/station/index';
import permutation from '@/views/drawing/permutationtable/permutation';
import gdgtable from '@/views/drawing/permutationtable/gdgtable';
import request from "../../../utils/request";
import {watch} from "vue";

import {useStationsStore} from "@/views/drawing/store/index";

const stationsStore = useStationsStore();

const router = useRouter();

const buttonColor = ref('black')

const drawCodeList = ref([]);

const xhjMenu = ref([])

const cascaderData =ref( [
  {
    value: '0',
    label: '基础数据',
    children: [],
    component: signal
  },
  {
    value: '1',
    label: '封面及目录',
    children: [
      {value: '1-1', label: '封面'},
      {value: '1-2', label: '目录'},
    ]
  },
  {
    value: '2',
    label: '测试1',
    children: [
      {value: '2-1', label: '测试1-1'},
      {value: '2-2', label: '测试1-2'},
    ]
  },
  {
    value: '4',
    label: '测试3',
    children: [
      {value: '4-1', label: '测试3-1', component: lsbtable},
      {value: '4-2', label: '测试3-2', component: dclsbtable},
    ]
  },
  {
    value: '6',
    label: '测试4',
    children: [
      {value: '6-1', label: '测试4-1', children: xhjMenu.value},	//这里用到了动态请求后端获取的三级菜单
      {value: '6-2', label: '测试4-2', component: dctable},
      {value: '6-3', label: '测试4-3'},
      {
        value: '6-4', label: '测试4-4', children: [
          {value: '6-4-1', label: '测试4-4-1', component: qdcjtable},
          {value: '6-4-2', label: '测试4-4-2'},
        ]
      },
      {value: '6-5', label: '测试4-5'},
    ]
  },
]);

// 用于存储选择的菜单项
const selectedOptions = ref([]);

// 处理 el-cascader 改变事件
const handleCascaderChange = (index, value) => {
  recoverButton(index);
  let menu = String(value)
  if (value.length === 2) {			//如果是二级菜单,截取第二位数据
    menu = String(value[1])
  }
  handleSelect(menu)
}

//处理菜单跳转事件
const handleSelect = async (menu) => {
  buttonColor.value = '#a7aab1'
  let component = findComponentByValue(cascaderData.value, menu);
  if (menu === '0') {
    buttonColor.value = 'black';
    recoverButton(menu);
  }
  if (menu === '7-3') {
    const type = await changeStation();
    if (type === 4) {
      emitComponentChange(jkgksktable);
    } else {
      emitComponentChange(jkgTktable);
    }
  }
  if (menu.includes('xhjtable')){
    let tuzhiCode = findTuZhiCodeByValue(cascaderData.value, menu);
    stationsStore.setTuzhiCode(tuzhiCode);
    emitComponentChange(component);
  }
  if (component != null) {
    emitComponentChange(component);
  }
};

//这个方法是用来跳转对应页面的
const emitComponentChange = (component) => {
  // 触发自定义事件,传递组件或其他数据
  const event = new CustomEvent('componentChange', {detail: component});
  window.dispatchEvent(event);
};

//将其他按钮恢复原状
function recoverButton(index) {
  console.log(selectedOptions.value)
  for (const item of cascaderData.value) {
    selectedOptions.value[item.value] = []; //全部恢复原状
    // if (parseInt(item.value) !== index) {
    //   selectedOptions.value[item.value] = [];//将除了当前选中的菜单框都恢复原状
    // }
  }
  console.log(selectedOptions.value)
}

// 匹配菜单页面
function findComponentByValue(data, targetValue) {
  for (let i = 0; i < data.length; i++) {
    const item = data[i];
    if (item.value === targetValue) {
      return item.component;
    }
    if (item.children && item.children.length > 0) {
      const result = findComponentByValue(item.children, targetValue);
      if (result) {
        return result;
      }
    }
  }
  return null;
}

// 获取图纸code
function findTuZhiCodeByValue(data, targetValue) {
  for (let i = 0; i < data.length; i++) {
    const item = data[i];
    if (item.value === targetValue) {
      return item.tuzhiCode;
    }
    if (item.children && item.children.length > 0) {
      const result = findTuZhiCodeByValue(item.children, targetValue);
      if (result) {
        return result;
      }
    }
  }
  return null;
}

listDingXingTuList().then((data) => { 	//这里通过请求后端接口动态获取三级菜单并赋值
  drawCodeList.value = data.data;
  cascaderData.value[6].children[0].children = drawCodeList.value.map(item => ({
    value: 'xhjtable'+item.id,		
    label: item.tuzhiName,
    component: xhjtable,
    tuzhiCode: item.tuzhiCode		//格式和一二级菜单保持一致,可以增加字段
  }));
})

</script>

下面是切换页面后要显示的目标页面,这里接收上面emitComponentChange 方法传递过来的组件

<template>
  <el-row>
    <el-col :span="20">
    
      <!-- 顶部菜单 -->
      <div v-if="showTop">
        <topmenu />
      </div>
      
      <!-- 右侧下方区域动态切换的内容 -->
      <div style="flex: 1;">
        <component :is="currentComponent" />
      </div>
      
    </el-col>
  </el-row>
</template>

<script setup>
import {onMounted, shallowRef} from 'vue';
import Topmenu from "./station/topmenu";

const currentComponent = shallowRef(signal);

//主动监听事件
onMounted(() => {
  window.addEventListener('componentChange', (event) => {
    const component = event.detail;
    // 在这里处理接收到的组件或其他数据
    currentComponent.value = component;
  });
});

getStationList()
</script>

还有一种方式是通过el-menu实现的,效果同上,使用的js方法都是一样的

<el-menu
      :default-active="activeIndex"
      class="el-menu-demo"
      mode="horizontal"
      @select="handleSelect"
      background-color="#f8f8f9"
      style="margin-top: 20px;margin-left: 1px"
  >
    <template v-for="(item, index) in cascaderData">
      <template v-if="!item.children">
        <el-menu-item :key="item.index" :index="item.value">
          {{ item.label }}
        </el-menu-item>
      </template>
      <el-sub-menu v-else :key="item.value" :index="item.value">
        <template #title>{{ item.label }}</template>
        <template v-for="(child, childIndex) in item.children">
          <template v-if="!child.children">
            <el-menu-item :key="child.value" :index="child.value">
              {{ child.label }}
            </el-menu-item>
          </template>
          <el-sub-menu v-else :key="child.value + '-sub'" :index="child.value">
            <template #title>{{ child.label }}</template>
            <el-menu-item
                v-for="(subItem, subIndex) in child.children"
                :key="subItem.value"
                :index="subItem.value"
            >
              {{ subItem.label }}
            </el-menu-item>
          </el-sub-menu>
        </template>
      </el-sub-menu>
    </template>
  </el-menu>
<think>好的,用户的问题是“el-cascader-panel如何配合el-input使用”,我需要详细解答如何将这两个Element UI组件结合使用。首先,我需要回忆Element UI的文档,了解el-cascader-panelel-input的各自用途。el-cascader-panel是一个级联选择器面板,通常用于展示多级菜单,而el-input是输入框。用户可能希望实现一个常见的场景:点击输入框时弹出级联面板,选择后输入框显示选中的路径,同时隐藏面板。 首先,我需要考虑如何将这两个组件组合起来。可能需要用el-input作为触发元素,当用户点击输入框时,显示el-cascader-panel。这可以通过控制一个变量来切换面板的显示状态。然后,当用户在级联面板中选择选项时,将选中的值绑定到输入框的值上。 接下来,需要处理数据绑定。el-cascader-panel的v-model绑定一个数组,表示选中的各级值。而el-input的v-model绑定一个字符串,通常是选中的路径标签,比如“一级/二级/三级”。因此,需要将选中的节点对象转换为路径字符串。 此外,还需要处理级联面板的关闭时机。当用户完成选择后,或者点击面板外部时,应该隐藏面板。这可以通过在面板外包裹一个div,并监听点击事件,判断点击是否在面板内部来实现。另外,样式方面需要注意,将级联面板定位在输入框下方,使用绝对定位,并设置z-index以确保面板显示在顶层。同时,输入框设置为只读,防止用户手动输入,只能通过选择来填充内容。 然后,考虑代码结构。在Vue组件中,需要引入el-input和el-cascader-panel,并注册。数据部分包括输入框的值、级联选中的值、控制面板显示的布尔值,以及级联的选项数据。方法部分包括处理选中的回调函数,将选中的节点数组转换为路径字符串,并更新输入框的值,同时关闭面板。 还需要处理面板的关闭逻辑,可能需要使用一个div包裹级联面板,并监听document的点击事件,判断点击是否发生在面板或输入框之外,从而决定是否关闭面板。 最后,测试可能的边界情况,比如选项未完全选择时是否关闭面板,或者如何处理多选等情况。不过根据问题描述,这里应该是单选的情况。 总结步骤: 1.使用el-input作为触发元素,设置readonly,点击时显示级联面板。 2. 使用v-show或v-if控制级联面板的显示。 3. 级联面板的选中事件触发时,更新输入框的值并隐藏面板。 4. 处理点击外部关闭面板的逻辑。 5.样式调整,确保面板正确显示在输入框下方。</think>要实现`el-cascader-panel`与`el-input`的配合使用,可以通过以下方式实现级联选择器的输入框形式: ```html <template> <div class="cascader-container"> <el-input v-model="inputValue" placeholder="请选择" readonly @click="showPanel = !showPanel" ></el-input> <div v-show="showPanel" class="panel-wrapper"> <el-cascader-panel v-model="selectedValue" :options="options" @change="handleChange" ></el-cascader-panel> </div> </div> </template> <script> export default { data() { return { inputValue: &#39;&#39;, selectedValue: [], showPanel: false, options: [{ value: &#39;zhejiang&#39;, label: &#39;浙江&#39;, children: [{ value: &#39;hangzhou&#39;, label: &#39;杭州&#39; }] }] } }, methods: { handleChange(value) { // 获取完整路径标签 const getLabelPath = (options, valuePath) => { return valuePath.map((val, index) => { const option = options.find(item => item.value === val) options = option?.children || [] return option?.label }).join(&#39;/&#39;) } this.inputValue = getLabelPath(this.options, value) this.showPanel = false } } } </script> <style> .cascader-container { position: relative; width: 300px; } .panel-wrapper { position: absolute; z-index: 2000; margin-top: 5px; border: 1px solid #e4e7ed; border-radius: 4px; box-shadow: 0 2px 12px 0 rgba(0,0,0,.1); } </style> ``` 代码解释: 1. 使用`el-input`作为触发元素,设置`readonly`防止手动输入 2. 通过`v-show`控制级联面板的显示状态 3. `el-cascader-panel`的`@change`事件触发时,通过递归查找选项的label值拼接完整路径 4. 使用绝对定位将级联面板定位在输入框下方 5. 添加阴影和边框样式提升视觉效果
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值