vue组件开发:构建响应式快捷导航

前言

快捷导航不仅能够显著提升系统的灵活性和用户交互性,还极大地增强了用户的操作体验。本文将展示如何在 vue 中实现一个既可自定义又具备响应式特性的快捷导航菜单。


一、实现思路

列表页

  • 结构设计

    定义页面结构,包含一个导航卡片和一个对话框组件;
    导航卡片中包含一个标题、添加按钮和导航项列表;
    使用 v-for 指令遍历 routeData 数组,动态生成每个导航项。

  • 数据管理

    data 中定义 routeData 数组,存储导航项的信息(如图标和标题);
    定义 dialogChild 变量,用于控制对话框的显隐状态。

  • 事件处理

    handleCellClick(item):处理点击导航项的事件,输出所点击的项的信息;
    deleteItem(index):处理删除导航项的事件,通过 splice 方法从 routeData 中移除指定项;
    navigationAdd():打开添加导航项的对话框,将 dialogChild 设置为 true

1.1 列表组件

<template>
  <div class="navigation">
    <div class="cardContent">
      <el-card class="box-card">
        <div slot="header">
          <span />
          <span class="cardName">快捷导航</span>
          <el-link style="float: right" type="primary" @click="navigationAdd">添加</el-link>
        </div>
        <div v-if="routeData && routeData.length" class="gridContainer">
          <!-- 遍历路由数据,生成每个导航项 -->
          <div v-for="(item, index) in routeData" :key="index" class="gridContent">
            <div class="content" :title="item.title" @click.stop="handleCellClick(item)">
              <p><img :src="item.imgUrl" alt="" /></p>
              <p>{{ item.title }}</p>
            </div>
            <div class="overlay">
              <i title="删除" class="el-icon-circle-close" @click.stop="deleteItem(index)"></i>
            </div>
          </div>
        </div>
        <div v-else><el-empty description="暂无数据"></el-empty></div>
      </el-card>
    </div>
    <navigation-dialog :dialogChild.sync="dialogChild" />
  </div>
</template>

<script>
import NavigationDialog from "./navigationDialog.vue";
export default {
  components: { NavigationDialog },
  data() {
    return {
      dialogChild: false, // 控制对话框显隐状态
      routeData: [
        //模拟数据
        {
          imgUrl: require("../assets/demo.png"),
          title: "数据统计",
        },
        {
          imgUrl: require("../assets/demo.png"),
          title: "订单管理",
        },
        {
          imgUrl: require("../assets/demo.png"),
          title: "汇总信息",
        },
        {
          imgUrl: require("../assets/demo.png"),
          title: "客服管理",
        },
        {
          imgUrl: require("../assets/demo.png"),
          title: "财物中心",
        },
        {
          imgUrl: require("../assets/demo.png"),
          title: "系统管理",
        },
        {
          imgUrl: require("../assets/demo.png"),
          title: "系统监控",
        },
        {
          imgUrl: require("../assets/demo.png"),
          title: "系统工具",
        },
      ],
    };
  },
  methods: {
    // 点击导航项的处理函数
    handleCellClick(item) {
      console.log("item:", item);
    },
    // 删除导航项的处理函数
    deleteItem(index) {
      this.routeData.splice(index, 1);
    },
    // 打开添加导航项对话框的处理函数
    navigationAdd() {
      this.dialogChild = true;
    },
  },
};
</script>

<style lang="less" scoped>
.navigation {
  width: 36%;
  padding: 16px;
  .cardContent {
    span:first-child {
      display: inline-block;
      width: 10px;
      height: 10px;
      background: #409eff;
      margin-right: 10px;
    }
    .cardName,
    .el-link {
      font-weight: bold;
    }
    .gridContainer {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
      gap: 16px;
      padding: 16px;
      .gridContent {
        width: 100px;
        height: 100px;
        border-radius: 4px;
        position: relative;
        box-sizing: border-box;
        overflow: hidden;
        background: linear-gradient(to bottom, rgba(106, 183, 255, 0.7), rgba(106, 183, 255, 0.2));
        display: flex;
        align-items: center;
        justify-content: center;
        text-align: center;
        .content {
          cursor: pointer;
          color: rgb(60, 60, 60);
          img {
            width: 50px;
            height: 50px;
          }
          p:nth-child(2) {
            margin-top: 2px;
            width: 92px;
            padding: 0px 4px;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
          }
        }
      }
      .overlay {
        position: absolute;
        top: 3px;
        right: 3px;
      }
      .el-icon-circle-close {
        cursor: pointer;
        font-size: 18px;
        color: rgb(255, 255, 255);
      }
      .el-icon-circle-close:hover {
        color: rgba(255, 255, 255, 0.5);
      }
    }
  }
}
::v-deep {
  .el-checkbox-button__inner {
    margin: 0 8px 10px 8px;
    border: 1px solid #d3d3d3;
    border-radius: 4px !important;
  }
}
</style>

弹框页

  • 结构设计

    使用 <el-dialog> 组件创建一个对话框,包含标题和内容区域;
    在内容区域中,使用 v-for 遍历 cities 数组,生成每个菜单的选项和复选框。

  • 数据管理

    data 中定义 cities 数组,模拟系统管理和监控等菜单项;
    定义 checkStates 对象,用于存储每个菜单的选中状态(全选、部分选中和选中的城市)。

  • 事件处理

    initializeCheckStates():初始化每个菜单的选中状态,设置全选状态和选中的菜单;
    handleCheckAllChange(item):处理全选复选框的变化,更新选中状态;
    handleCheckedCitiesChange(item):处理菜单复选框的变化,更新全选和部分选中状态;
    submit():提交选中的信息,过滤出选中的路由项,并输出到控制台;
    cancel():取消操作,重置选中状态并关闭对话框。

2.1 弹框组件

<template>
  <el-dialog :close-on-click-modal="false" title="添加快捷导航" width="60%" :visible.sync="dialogVisible" @close="cancel">
    <el-card class="box-card">
      <!-- 遍历菜单数组,生成每个菜单的选项 -->
      <div v-for="(item, index) in cities" :key="index" class="checkboxContent">
        <span class="navTit">{{ item.label }}</span>
        <!-- 全选复选框 -->
        <el-checkbox v-model="checkStates[item.label].checkAll" :indeterminate="checkStates[item.label].isIndeterminate" @change="handleCheckAllChange(item)">全选</el-checkbox>
        <!-- 菜单的复选框组 -->
        <el-checkbox-group v-model="checkStates[item.label].checkedCities" size="medium" @change="handleCheckedCitiesChange(item)">
          <el-checkbox-button v-for="(e, isx) in item.router" :key="isx" :label="e.title" border>{{ e.title }}</el-checkbox-button>
        </el-checkbox-group>
      </div>
      <div class="btns">
        <el-button size="medium" type="primary" @click="submit">保 存</el-button>
        <el-button size="medium" @click="cancel">取 消</el-button>
      </div>
    </el-card>
  </el-dialog>
</template>
<script>
export default {
  props: {
    dialogChild: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      dialogVisible: false, // 控制对话框显隐状态
      cities: [
        //模拟数据
        {
          label: "系统管理",
          router: [
            { title: "用户管理", id: 0 },
            { title: "角色管理", id: 1 },
            { title: "菜单管理", id: 2 },
            { title: "部门管理", id: 3 },
            { title: "日志管理", id: 4 },
            { title: "字典管理", id: 5 },
          ],
        },
        {
          label: "系统监控",
          router: [
            { title: "在线用户", id: 6 },
            { title: "定时任务", id: 7 },
          ],
        },
        {
          label: "系统工具",
          router: [{ title: "表单构建", id: 8 }],
        },
      ],
      checkStates: {}, // 存储每个菜单的选中状态
    };
  },
  watch: {
    dialogChild: {
      handler(newName) {
        this.dialogVisible = newName;
      },
      deep: true,
    },
  },
  mounted() {
    this.initializeCheckStates();
  },
  methods: {
    // 初始化每个菜单的选中状态
    initializeCheckStates() {
      this.cities.forEach((item) => {
        this.$set(this.checkStates, item.label, {
          checkAll: false, // 全选状态
          checkedCities: [], // 选中的菜单
          isIndeterminate: false, // 是否为部分选中状态
        });
      });
    },
    // 处理全选复选框的变化
    handleCheckAllChange(item) {
      this.checkStates[item.label].checkedCities = this.checkStates[item.label].checkAll ? item.router.map((e) => e.title) : [];
      this.checkStates[item.label].isIndeterminate = false; // 重置部分选中状态
    },
    // 处理菜单复选框的变化
    handleCheckedCitiesChange(item) {
      const checkedCount = this.checkStates[item.label].checkedCities.length; // 选中的菜单数量
      this.checkStates[item.label].checkAll = checkedCount === item.router.length; // 更新全选状态
      this.checkStates[item.label].isIndeterminate = checkedCount > 0 && checkedCount < item.router.length; // 更新部分选中状态
    },
    // 提交选中的信息
    submit() {
      const selectedInfo = this.cities.map((item) => {
        const { checkedCities } = this.checkStates[item.label]; // 获取当前导航项的选中状态
        const selectedRoutes = item.router.filter((route) => checkedCities.includes(route.title)); // 过滤出选中的路由项
        return {
          label: item.label, // 导航项标签
          selectedRoutes: selectedRoutes.map((route) => ({
            title: route.title,
            id: route.id,
          })), // 选中的路由项
        };
      });
      console.log("选中的信息:", selectedInfo); // 输出选中的信息
    },
    // 取消操作
    cancel() {
      this.initializeCheckStates(); // 重置选中状态
      this.$emit("update:dialogChild", false);
    },
  },
};
</script>

<style lang="less" scoped>
.checkboxContent {
  margin-bottom: 16px;
  .navTit {
    margin-right: 10px;
    color: rgb(41, 107, 255);
    font-weight: bold;
    font-size: 16px;
  }
  .el-checkbox {
    margin: 16px 0;
  }
}
.btns {
  margin-top: 16px;
  display: flex;
  justify-content: right;
}
::v-deep {
  .el-checkbox-button__inner:nth-child(n + 2) {
    margin: 0 !important;
    margin: 0 10px 10px 0 !important;
  }
}
</style>

1.3 组件通信

使用 props$emit 实现父子组件之间的通信。A 页面通过 dialogChild 属性控制 B 页面的对话框显隐状态,并通过 update:dialogChild 事件通知 B 页面关闭对话框。


二、实现效果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/936743.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

事务管理与锁机制

title: 事务管理与锁机制 date: 2024/12/14 updated: 2024/12/14 author: cmdragon excerpt: 在数据库系统中,事务管理至关重要,它确保多个数据库操作能够作为一个单一的逻辑单元来执行,从而维护数据的一致性和完整性。一个良好的事务管理系统能够解决并发操作带来的问题…

《操作系统 - 清华大学》7 -1:全局页面置换算法:局部页替换算法的问题、工作集模型

文章目录 1. 局部页替换算法的问题2. 全局置换算法的工作原理3. 工作集模式3.1 工作集3.2 工作集的变化 4 常驻集 1. 局部页替换算法的问题 局部页面置换算法 OPT&#xff0c;FIFO&#xff0c;LRU&#xff0c;Clock 等等&#xff0c;这些算法都是针对一个正在运行的程序来讲的…

力扣-图论-12【算法学习day.62】

前言 ###我做这类文章一个重要的目的还是给正在学习的大家提供方向和记录学习过程&#xff08;例如想要掌握基础用法&#xff0c;该刷哪些题&#xff1f;&#xff09;我的解析也不会做的非常详细&#xff0c;只会提供思路和一些关键点&#xff0c;力扣上的大佬们的题解质量是非…

每日十题八股-2024年12月14日

1.类加载器有哪些&#xff1f; 2.双亲委派模型的作用 3.讲一下类加载过程&#xff1f; 4.讲一下类的加载和双亲委派原则 5.什么是Java里的垃圾回收&#xff1f;如何触发垃圾回收&#xff1f; 6.判断垃圾的方法有哪些&#xff1f; 7.垃圾回收算法是什么&#xff0c;是为了解决了…

智能引导小车充电系统设计(论文+源码)

1总体方案设计 在16*16点阵LED字符显示器的设计中&#xff0c;系统总体框架如图2.4所示&#xff0c;包括单片机主控模复位电路模块、晶振电路模块、按键电路模块、LED点阵驱动电路模块&#xff0c;蓝牙模块等构成。系统功能实现主要是利用系统在软件程序编写过程中&#xff0c…

【Vue】自定义指令、插槽

目录 自定义指令 是什么 作用 使用方法 定义 使用 自定义指令配合绑定数据 语法 自定义指令的简写 语法 使用时机 插槽 什么是插槽 默认&#xff08;匿名&#xff09;插槽 ​编辑插槽的默认值 具名插槽 使用方法 简写 使用示例 作用域插槽 自定义指令 是什…

顺序队列的实现及其应用

一、概念 队列是允许在两端&#xff08;队头、队尾&#xff09;进行插入和读出操作的线性表 默认情况下&#xff0c;队尾插入&#xff0c;队头读出&#xff08;这一点和排队很像&#xff09;&#xff0c;先进先出FIFO 队中没有元素时称为空队 当队列两端都允许插入、读出时&…

Web安全深度剖析

1.Web安全简介 ​ 攻击者想要对计算机进行渗透&#xff0c;有一个条件是必须的&#xff0c;就是攻击者的计算机与服务器必须能够正常通信&#xff0c;服务器与客户端进行通信依靠的就是端口。 ​ 如今的web应该称之为web应用程序&#xff0c;功能强大&#xff0c;离不开四个要…

C# 探险之旅:第九节 - 循环(for):无限循环的魔法轮盘!

嘿&#xff0c;勇敢的探险家们&#xff0c;欢迎回到C#的神秘世界&#xff01;在这一节里&#xff0c;我们将踏上一场关于循环的奇妙冒险&#xff0c;特别是那个能带我们无限次探险的“for循环”&#xff01;准备好了吗&#xff1f;让我们一起揭开for循环的神秘面纱&#xff0c;…

解决Logitech G hub 无法进入一直转圈的方案(2024.12)

如果你不是最新版本无法加载尝试以下方案&#xff1a;删除AppData 文件夹下的logihub文件夹 具体路径&#xff1a;用户名根据实际你的请情况修改 C:\Users\Administrator\AppData\Local 如果你有通过lua编译脚本&#xff0c;记得备份&#xff01;&#xff01; ↓如果你是最新…

如何使用 Docker Compose 创建 LAMP 环境 ?

现如今&#xff0c;通过 Docker 容器化部署环境已经逐渐成为主流&#xff0c;特别是在部署像 LAMP (Linux、Apache、MySQL、PHP) 这样的复杂环境时。本教程旨在带您完成使用 Docker-Compose 建立 LAMP 环境的整个过程&#xff0c;同时还包括定制 PHP 环境的步骤&#xff0c;安装…

12.1【JAVA EXP4】next项目

next项目构建问题 详解一下这个页面 什么是Node选项&#xff1f; Node选项是指在运行Node.js应用程序时可以传递给Node.js进程的一系列命令行参数。这些选项可以让开发者控制Node.js的行为&#xff0c;例如设置内存限制、启用或禁用某些功能、指定调试端口等 --inspect 和 --i…

PyTorch3D 可视化

PyTorch3D是非常好用的3D工具库。但是PyTorch3D对于可用于debug&#xff08;例如调整cameras参数&#xff09;的可视化工具并没有进行系统的介绍。这篇文章主要是想介绍我觉得非常使用的PyTorch3D可视化工具。 1. 新建一个Mesh 从hugging face上下载一个glb文件&#xff0c;例…

内网穿透讲解

什么是内网穿透 内网穿透是一种网络技术&#xff0c;它允许外网或者其他局域网的用户来访问这个局域网的服务器资源&#xff0c;让资源的利用率更高&#xff0c;更加灵活&#xff0c;但是也要确保网络安全。 工作原理 如果你在公司&#xff0c;但是你需要使用到你家里的那台电…

Python中PyTorch详解

文章目录 Python中PyTorch详解一、引言二、PyTorch核心概念1、张量&#xff08;Tensor&#xff09;1.1、创建张量1.2、张量操作 2、自动求导&#xff08;Autograd&#xff09;2.1、自动求导示例 三、构建神经网络1、使用nn模块2、优化器&#xff08;Optimizer&#xff09; 四、…

Linux之网络配置

一、检查虚拟机和本机通不通 测试虚拟机和本机是否通不通 winR&#xff0c;运行本机cmd&#xff0c;输入ipconfig&#xff0c;拿到本机ip地址 在虚拟机上ping一下这个地址(ctrlshitv)可以把复制的文本粘贴进虚拟机。 可以看到&#xff0c;不通&#xff0c;解决方法在最后&am…

细说Flash存储芯片W25Q128FW和W25Q16BV

目录 一、Flash存储芯片W25Q128FW 1、W25Q128硬件接口和连接 2、存储空间划分 3、数据读写的原则 4、操作指令 &#xff08;1&#xff09;“写使能”指令 &#xff08;2&#xff09;“读数据”指令 &#xff08;3&#xff09;“写数据”指令 5、状态寄存器SR1 二、Fl…

33.攻防世界upload1

进入场景 看看让上传什么类型的文件 传个木马 把txt后缀改为png 在bp里把png改为php 上传成功 用蚁剑连接 在里面找flag 得到

鸿蒙元服务上架

鸿蒙元服务上架 一、将代码打包成 .app 文件1. 基本需求2. 生成密钥和证书请求文件3. 申请发布证书4. 申请发布Profile5. 配置签名信息6. 更新公钥指纹7. 打包项目成 .app 文件 二、发布元服务1. 进入应用信息页面2. 上传软件包3. 配置隐私协议4. 配置版本信息5. 提交审核&…

ansible自动化运维(二)playbook模式详解

相关文章ansible自动化运维&#xff08;一&#xff09;简介及清单,模块-CSDN博客ansible自动化运维&#xff08;三&#xff09;jinja2模板&&roles角色管理-CSDN博客ansible自动化运维&#xff08;四&#xff09;运维实战-CSDN博客 一.Ansible中的playbook模式 Playbo…