【可视化】封装滚动菜单列表组件

在这里插入图片描述
在这里插入图片描述

源码及源码分析


<template>
  <!-- 滚动菜单的主要容器 -->
  <div class="scrolling-menu">
    <!-- 如果headers数组有内容,就渲染表头 -->
    <div
      class="table-header"
      v-if="headers.length"
      :style="{ backgroundColor: headerBackground }"
    >
      <!-- 遍历表头的每一个项,并渲染 -->
      <div
        class="header-item"
        v-for="(header, index) in headers"
        :key="index"
        :style="getHeaderStyle()"
      >
        {{ header }}
      </div>
    </div>
    <!-- 表体部分,包含所有的表项 -->
    <div class="table-body" ref="menuContainer">
      <!-- 遍历当前展示的items,并渲染 -->
      <div
        class="menu-item"
        v-for="(item, index) in displayedItems"
        :key="index"
        :style="getItemStyle(index)"
      >
        <!-- 根据配置的列,渲染每一列的数据 -->
        <div
          v-for="(field, colIndex) in columns"
          :key="colIndex"
          class="column-item"
          style="color: aliceblue;"
          :style="getColumnStyle()"
        >
          {{ formatItem(item, field) }}
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted, watch, defineProps } from 'vue';

const props = defineProps({
  data: {
    type: Array, // 传入的数据数组
    required: true, // 必须传入该数据
  },
  headers: {
    type: Array, // 表头内容
    default: () => [], // 如果没有传入,则为空数组
  },
  columns: {
    type: Array, // 显示数据的列名
    required: true, // 必须传入
  },
  rows: {
    type: Number, // 显示的行数
    default: 5, // 默认显示5行
  },
  scrollSpeed: {
    type: Number, // 滚动速度,单位为毫秒
    default: 3000, // 默认每隔3000ms滚动一次
  },
  itemFormatter: {
    type: Function, // 自定义数据格式化函数
    default: (item, field) => item[field]?.toString() || '', // 默认返回对应字段的值,如果不存在则为空字符串
  },
  rowHeight: {
    type: String,
    default: '20%', // 每行高度占父容器的20%,即5行平均分配
  },
  fontSize: {
    type: String,
    default: '16px', // 默认字体大小
  },
  textAlign: {
    type: String,
    default: 'center', // 默认文字居中对齐
  },
  headerBackground: {
    type: String,
    default: 'rgba(0, 0, 50, 0.5)', // 表头背景色
  },
  rowBackground: {
    type: String,
    default: 'rgba(0, 0, 50, 0.2)', // 行背景色
  },
  alternateRowBackground: {
    type: String,
    default: 'rgba(62, 62, 173, 0.2)', // 每隔一行的背景色
  },
});

const menuContainer = ref(null); // 获取表体的引用
const displayedItems = ref([]); // 当前显示的行项

let interval = null; // 存储setInterval的引用,用于清除
let currentIndex = 0; // 当前滚动的起始索引

const startScrolling = () => {
  // 开始自动滚动
  interval = setInterval(() => {
    currentIndex += props.rows; // 每次滚动显示rows数量的数据
    if (currentIndex >= props.data.length) {
      // 如果到达数据末尾,回到起始位置
      currentIndex = 0;
    }
    updateDisplayedItems(); // 更新当前显示的项
  }, props.scrollSpeed);
};

const updateDisplayedItems = () => {
  // 更新显示的数据项,切片当前的rows个数据
  displayedItems.value = props.data.slice(currentIndex, currentIndex + props.rows);
};

const formatItem = (item, field) => {
  // 使用自定义的格式化函数,格式化每个数据项
  return props.itemFormatter(item, field);
};

const getHeaderStyle = () => {
  // 获取表头样式
  return {
    width: `${100 / props.columns.length}%`, // 宽度根据列的数量平分
    fontSize: props.fontSize,
    textAlign: props.textAlign,
  };
};

const getItemStyle = (index) => {
  // 获取每行的样式
  return {
    display: 'flex',
    width: '100%',
    height: props.rowHeight,
    backgroundColor: index % 2 === 0 ? props.rowBackground : props.alternateRowBackground, // 根据行的索引决定背景颜色
  };
};

const getColumnStyle = () => {
  // 获取每列的样式
  return {
    width: `${100 / props.columns.length}%`, // 宽度根据列的数量平分
    fontSize: props.fontSize,
    textAlign: props.textAlign,
  };
};

onMounted(() => {
  // 组件挂载后,初始化显示的数据并开始滚动
  updateDisplayedItems();
  startScrolling();
});

onUnmounted(() => {
  // 组件卸载前,清除滚动的定时器
  clearInterval(interval);
});

watch(
  () => props.data, // 监听数据变化
  () => {
    // 当数据变化时,重置索引并更新显示项
    currentIndex = 0;
    updateDisplayedItems();
  }
);
</script>

<style scoped>
.scrolling-menu {
  display: flex;
  flex-direction: column; /* 使子元素垂直排列 */
  overflow: hidden; /* 隐藏溢出的内容 */
  width: 100%;
  height: 100%;
}

.table-header {
  display: flex;
  padding: 10px;
  font-weight: bold; /* 表头字体加粗 */
  color: white;
}

.header-item {
  text-align: center; /* 表头项居中对齐 */
}

.table-body {
  display: flex;
  flex-direction: column;
  flex-grow: 1; /* 表体部分填充剩余空间 */
  overflow: hidden; /* 隐藏溢出的内容 */
}

.menu-item {
  display: flex;
  align-items: center;
  justify-content: space-between; /* 在行内的列之间均匀分布 */
  box-sizing: border-box; /* 包括边框和内边距在内的大小计算方式 */
}
</style>




代码逻辑解析
Props定义: 组件接收多种参数用于自定义显示效果,比如 data (数据数组), headers (表头数组), columns (要显示的列), rows (显示的行数), scrollSpeed (滚动速度), 等等。这些参数让组件在不同的使用场景中保持灵活性。

Data定义: displayedItems 是一个 ref,用来存放当前显示的表项。menuContainer 是一个 ref,指向表体部分的 DOM 元素。

startScrolling函数: 通过 setInterval 实现自动滚动的效果,每隔 scrollSpeed 毫秒更新显示的项。当 currentIndex 达到数据长度的末尾时,会重置为 0,实现循环滚动。

updateDisplayedItems函数: 这个函数根据 currentIndex 来更新 displayedItems,只显示 rows 数量的数据。

getHeaderStyle函数和getItemStyle函数: 用来动态生成样式,确保组件的每一列和每一行都能根据传入的参数灵活调整。

生命周期钩子:

在 onMounted 中,组件加载后初始化显示的数据并开始滚动。
在 onUnmounted 中,组件卸载前清除定时器,防止内存泄漏。
数据监听: 使用 watch 监听 props.data,当数据发生变化时,重置索引并重新渲染显示项。

滚动菜单列表组件使用文档


目录
  1. 概述
  2. 安装
  3. 快速开始
  4. API 参考
  5. 示例
  6. 常见问题

概述

滚动菜单列表组件是一个适用于大屏可视化项目的自定义组件。它支持自定义表格头部、多种数据格式、自动滚动显示数据,并具有高度的灵活性和可配置性。无论是单列数据还是多列数据,都可以通过简单配置快速展示,并且支持自定义样式以匹配不同的视觉风格。


安装

  1. 通过 npm 安装:

    npm install vue-scroll-menu-list --save
    
  2. 通过 yarn 安装:

    yarn add vue-scroll-menu-list
    

快速开始

在 Vue 项目中使用滚动菜单列表组件:

<template>
  <ScrollMenuList 
    :data="menuData"
    :headers="['项目名称', '漏洞数量', '严重等级']"
    :columns="['name', 'count', 'severity']"
    :rows="5"
    :scrollSpeed="3000"
    :itemFormatter="formatMenuItem"
    rowHeight="19%"
    fontSize="18px"
    textAlign="center"
    headerBackground="rgba(10, 10, 50, 0.7)"
    rowBackground="rgba(0, 0, 50, 0.3)"
    alternateRowBackground="rgba(62, 62, 173, 0.3)"
  />
</template>

<script>
import ScrollMenuList from 'vue-scroll-menu-list';

export default {
  components: { ScrollMenuList },
  data() {
    return {
      menuData: [
        { name: '项目A', count: 100, severity: '高' },
        { name: '项目B', count: 200, severity: '中' },
        // 更多数据...
      ],
    };
  },
  methods: {
    formatMenuItem(item, field) {
      return item[field] || '-';
    }
  }
};
</script>

API 参考

Props
Prop 名称类型默认值说明
dataArray[]必需。要显示的数据列表。每个对象表示一行数据。
headersArray[]可选。表头名称列表。与 columns 对应,若为空则不显示表头。
columnsArray[]必需。要显示的列名列表,定义每列对应的数据字段。
rowsNumber5显示的行数。默认为5。
scrollSpeedNumber3000可选。自动滚动的速度,单位为毫秒。
itemFormatterFunction`(item, field) => item[field]?.toString()
rowHeightString'20%'可选。每行的高度,支持百分比或像素值,默认为父容器高度的20%。
fontSizeString'16px'可选。表格文字的大小。
textAlignString'center'可选。文字对齐方式。可以是 'left', 'center', 'right'
headerBackgroundString'rgba(0, 0, 50, 0.5)'可选。表头背景颜色。
rowBackgroundString'rgba(0, 0, 50, 0.2)'可选。表格行的背景颜色。
alternateRowBackgroundString'rgba(62, 62, 173, 0.2)'可选。表格隔行背景颜色。

示例

<template>
  <ScrollMenuList 
    :data="menuData"
    :headers="['项目名称', '漏洞数量', '严重等级']"
    :columns="['name', 'count', 'severity']"
    :rows="5"
    :scrollSpeed="3000"
    :itemFormatter="formatMenuItem"
    rowHeight="19%"
    fontSize="18px"
    textAlign="center"
    headerBackground="rgba(10, 10, 50, 0.7)"
    rowBackground="rgba(0, 0, 50, 0.3)"
    alternateRowBackground="rgba(62, 62, 173, 0.3)"
  />
</template>

<script>
export default {
  data() {
    return {
      menuData: [
        { name: '项目A', count: 100, severity: '高' },
        { name: '项目B', count: 200, severity: '中' },
        { name: '项目C', count: 150, severity: '低' },
        // 更多数据...
      ],
    };
  },
  methods: {
    formatMenuItem(item, field) {
      return item[field] || '-';
    }
  }
};
</script>

常见问题

  1. 组件不滚动: 检查 scrollSpeed 是否设置过长或者数据量是否不足以触发滚动。
  2. 自定义样式未生效: 请确保传入的 props 符合组件要求,并且样式值合法。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

接着奏乐接着舞。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值