Vite+Vue3+TS爆改ColorUI:GridGroup和菜单组件的封装

596 阅读11分钟

今天我们将要使用vue3+ts改造@ColorUI宫格列表(GridGroup)和菜单列表(MenuList)。这两种常见且重要的布局方式,它们通过不同的排列方式和交互方式,帮助用户快速找到所需的功能或选项。本文将详细介绍这两种布局方式的改造实现方法,并给出具体的代码示例和设计思路。

image.png

宫格列表 (GridGroup)

宫格列表是一种界面布局方式,将多个功能或选项以网格形式排列在一起,通常每个格子(宫格)内包含一个图标和一个简短的文字说明。这种布局方式直观、整齐,用户可以快速找到所需功能。

宫格列表一般用在以下场景:

  1. 手机应用主界面:许多手机应用的主界面使用宫格列表来展示主要功能,例如社交媒体应用、购物应用等。
  2. 设置菜单:系统设置或应用设置界面常用宫格列表来分类展示各项设置选项。
  3. 仪表盘(Dashboard):企业管理系统或数据分析平台的仪表盘界面,使用宫格列表来展示不同的数据模块或功能入口。
  4. 网站导航:一些网站的导航页面或首页也会使用宫格列表来展示主要的栏目或功能。
  5. 游戏界面:游戏主界面或功能菜单,使用宫格列表来展示不同的游戏模式、道具、商店等。

宫格列表的优点在于结构清晰、易于操作,用户可以通过图标和简短的文字快速理解每个选项的功能。

原代码

<view
  class="cu-list grid"
  :class="['col-' + gridCol,gridBorder?'':'no-border']"
>
  <view
    class="cu-item"
    v-for="(item,index) in cuIconList"
    :key="index"
    v-if="index<gridCol*2"
  >
    <view :class="['cuIcon-' + item.cuIcon,'text-' + item.color]">
      <view class="cu-tag badge" v-if="item.badge!=0">
        <block v-if="item.badge!=1">{{item.badge>99?'99+':item.badge}}</block>
      </view>
    </view>
    <text>{{item.name}}</text>
  </view>
</view>

解释

  1. 外层容器<view class="cu-list grid" :class="['col-' + gridCol, gridBorder ? '' : 'no-border']">
    • cu-list grid 是基础的 CSS 类,定义了宫格列表的基本样式。
    • :class 动态绑定类名,col- + gridCol 定义了每行的列数,gridBorder ? '' : 'no-border' 根据 gridBorder 是否为真来决定是否显示边框。
  2. 宫格项<view class="cu-item" v-for="(item, index) in cuIconList" :key="index" v-if="index < gridCol * 2">
    • v-for 指令用来遍历 cuIconList 数组,生成每个宫格项。
    • :key="index" 为每个项设置唯一的 key,优化渲染性能。
    • v-if="index < gridCol * 2" 控制显示的项数,确保不超过预定的列数。
  3. 图标及颜色<view :class="['cuIcon-' + item.cuIcon, 'text-' + item.color]">
    • 动态绑定类名,cuIcon- + item.cuIcon 用于设置图标,text- + item.color 用于设置文字颜色。
  4. 徽标<view class="cu-tag badge" v-if="item.badge != 0">
    • 如果 item.badge 不为 0,则显示徽标。
    • 内部根据 item.badge 的值判断是否显示具体数字或 99+
  5. 文本<text>{{ item.name }}</text>
    • 显示每个宫格项的名称。

需求提取

  1. 宫格列表展示

    • 根据传入的 cuIconList 数据,动态生成宫格列表。
    • 每个宫格包含图标、颜色、徽标和名称。
  2. 宫格列数

    • 通过 gridCol 属性控制每行显示的列数。
  3. 边框显示

    • 通过 gridBorder 属性控制是否显示宫格边框。
  4. 点击跳转

    • 每个宫格项点击后,根据配置跳转到指定路径或链接。
    • 支持自定义路由函数 routeFn,否则使用默认的 Vue Router 跳转。

改造组件

<template>
  <div
    class="cu-list grid"
    :class="['col-' + gridCol, gridBorder ? '' : 'no-border']"
  >
    <div
      class="cu-item"
      v-for="(item, index) in cuIconList"
      :key="index"
      :style="`width: calc(100% / ${gridCol})`"
      @click="toPage(item)"
    >
      <div :class="['cuIcon-' + item.cuIcon, 'text-' + item.color]">
        <div class="cu-tag badge" v-if="item.badge != 0">
          <i v-if="item.badge != 1">{{
            (item.badge || 0) > 99 ? "99+" : item.badge
          }}</i>
        </div>
      </div>
      <text>{{ item.name }}</text>
    </div>
  </div>
</template>

<script setup lang="ts">
import { useRouter } from "vue-router";

const props = withDefaults(
  defineProps<{
    cuIconList?: GridGroupItem[];
    gridCol?: number;
    gridBorder?: boolean;
    routeFn?: Function;
  }>(),
  {
    cuIconList: () => [
      {
        cuIcon: "cardboardfill",
        color: "red",
        badge: 120,
        name: "VR",
        link: "http://www.baidu.com/",
      },
      {
        cuIcon: "recordfill",
        color: "orange",
        badge: 1,
        name: "录像",
      },
      {
        cuIcon: "picfill",
        color: "yellow",
        badge: 0,
        name: "图像",
      },
      {
        cuIcon: "noticefill",
        color: "olive",
        badge: 22,
        name: "通知",
      },
      {
        cuIcon: "upstagefill",
        color: "cyan",
        badge: 0,
        name: "排行榜",
      },
      {
        cuIcon: "clothesfill",
        color: "blue",
        badge: 0,
        name: "皮肤",
      },
      {
        cuIcon: "discoverfill",
        color: "purple",
        badge: 0,
        name: "发现",
      },
      {
        cuIcon: "questionfill",
        color: "mauve",
        badge: 0,
        name: "帮助",
      },
      {
        cuIcon: "commandfill",
        color: "purple",
        badge: 0,
        name: "问答",
      },
      {
        cuIcon: "brandfill",
        color: "mauve",
        badge: 0,
        name: "版权",
      },
    ],
    gridCol: 4,
    gridBorder: false,
    routeFn: undefined,
  }
);

const router = useRouter();

const toPage = (item: GridGroupItem) => {
  if (item.path) {
    if (props.routeFn) {
      props.routeFn().push({
        path: item.path,
      });
    } else {
      router.push({
        path: item.path,
      });
    }
    return;
  }
  if (item.link) {
    window.location.href = item.link;
    return;
  }
};
</script>

<script lang="ts">
export default {
  name: "TGridGroup",
};
</script>

代码解释

Template 部分

  1. 外层容器

    • class="cu-list grid":基础样式类。
    • :class="['col-' + gridCol, gridBorder ? '' : 'no-border']":动态类名,控制列数和边框。
  2. 宫格项

    • v-for="(item, index) in cuIconList":遍历 cuIconList 数组生成宫格项。
    • :key="index":确保每个项有唯一的 key。
    • :style="width: calc(100% / ${gridCol})":动态设置每个宫格项的宽度。
    • @click="toPage(item)":绑定点击事件,跳转到对应页面或链接。
  3. 图标及颜色

    • :class="['cuIcon-' + item.cuIcon, 'text-' + item.color]":动态类名,设置图标和颜色。
  4. 徽标

    • v-if="item.badge != 0":徽标不为 0 时显示。
    • v-if="item.badge != 1":徽标不为 1 时显示具体数字或 99+
  5. 文本

    • <text>{{ item.name }}</text>:显示宫格项的名称。

Script 部分

  1. 导入 Vue Router

    • import { useRouter } from "vue-router";:导入 Vue Router 的 useRouter 钩子。
  2. 定义组件属性

    • defineProps 定义组件的属性,包括 cuIconListgridColgridBorderrouteFn
    • withDefaults 设置属性的默认值。
  3. 路由跳转函数

    • const router = useRouter();:获取 Vue Router 实例。
    • const toPage = (item: GridGroupItem) => { ... }:定义点击事件处理函数,根据 itempathlink 进行跳转。

为了适应 Nuxt3 或者其他环境的逻辑,可能需要自定义页面跳转函数。可以通过提供一个组件参数,让使用者传入自定义的页面跳转函数,或者在不同环境注册时,使用依赖注入的方式将跳转函数传入组件。

可惜,我懒滴很。下次一定~

默认导出

  • export default { name: "GridGroup" };:导出组件,设置组件名称为 TGridGroup

注意,这里需要声明组件名称不然无法注册

小节

这个 Vue 3 + TypeScript 的GridGroup组件实现了一个动态的宫格列表,通过传入的属性控制宫格的内容、列数、边框显示和点击跳转行为。组件灵活且易于扩展,适用于多种场景。

image.png

菜单列表 (MenuList)

菜单列表是一种UI组件,用于展示一系列可交互的选项或功能项。每个选项通常包括图标、标题、描述等,可以点击或触发其他操作。菜单列表通常以垂直或水平的方式排列,便于用户快速浏览和选择。

菜单列表广泛应用于各种应用程序和网站中,包括但不限于以下场景:

  1. 导航菜单
    • 用于在应用程序或网站中提供导航,帮助用户快速访问不同的页面或功能模块。
  2. 设置页面
    • 用于展示各种设置选项,如账户设置、安全设置、通知设置等。
  3. 功能菜单
    • 提供一系列功能选项,如文件操作(上传、下载)、编辑操作(复制、粘贴)等。
  4. 个人中心
    • 在用户个人中心页面,展示用户信息、操作选项等。
  5. 工具栏
    • 在工具栏中展示一系列工具或操作按钮,便于用户快速执行操作。

原代码

<view
  class="cu-list menu"
  :class="[menuBorder?'sm-border':'',menuCard?'card-menu margin-top':'']"
>
  <view class="cu-item" :class="menuArrow?'arrow':''">
    <view class="content">
      <text class="cuIcon-circlefill text-grey"></text>
      <text class="text-grey">图标 + 标题</text>
    </view>
  </view>
  <view class="cu-item" :class="menuArrow?'arrow':''">
    <view class="content">
      <image src="/static/logo.png" class="png" mode="aspectFit"></image>
      <text class="text-grey">图片 + 标题</text>
    </view>
  </view>
  <view class="cu-item" :class="menuArrow?'arrow':''">
    <button class="cu-btn content" open-type="contact">
      <text class="cuIcon-btn text-olive"></text>
      <text class="text-grey">Open-type 按钮</text>
    </button>
  </view>
  <view class="cu-item" :class="menuArrow?'arrow':''">
    <navigator
      class="content"
      hover-class="none"
      url="../list/list"
      open-type="redirect"
    >
      <text class="cuIcon-discoverfill text-orange"></text>
      <text class="text-grey">Navigator 跳转</text>
    </navigator>
  </view>
  <view class="cu-item" :class="menuArrow?'arrow':''">
    <view class="content">
      <text class="cuIcon-emojiflashfill text-pink"></text>
      <text class="text-grey">头像组</text>
    </view>
    <view class="action">
      <view class="cu-avatar-group">
        <view
          class="cu-avatar round sm"
          style="
            background-image: url(https://ossweb-img.qq.com/images/lol/web201310/skin/big10001.jpg);
          "
        ></view>
        <view
          class="cu-avatar round sm"
          style="
            background-image: url(https://ossweb-img.qq.com/images/lol/web201310/skin/big81005.jpg);
          "
        ></view>
        <view
          class="cu-avatar round sm"
          style="
            background-image: url(https://ossweb-img.qq.com/images/lol/web201310/skin/big25002.jpg);
          "
        ></view>
        <view
          class="cu-avatar round sm"
          style="
            background-image: url(https://ossweb-img.qq.com/images/lol/web201310/skin/big91012.jpg);
          "
        ></view>
      </view>
      <text class="text-grey text-sm">4 人</text>
    </view>
  </view>
  <view class="cu-item" :class="menuArrow?'arrow':''">
    <view class="content">
      <text class="cuIcon-btn text-green"></text>
      <text class="text-grey">按钮</text>
    </view>
    <view class="action">
      <button class="cu-btn round bg-green shadow">
        <text class="cuIcon-upload"></text> 上传
      </button>
    </view>
  </view>
  <view class="cu-item" :class="menuArrow?'arrow':''">
    <view class="content">
      <text class="cuIcon-tagfill text-red margin-right-xs"></text>
      <text class="text-grey">标签</text>
    </view>
    <view class="action">
      <view class="cu-tag round bg-orange light">音乐</view>
      <view class="cu-tag round bg-olive light">电影</view>
      <view class="cu-tag round bg-blue light">旅行</view>
    </view>
  </view>
  <view class="cu-item" :class="menuArrow?'arrow':''">
    <view class="content">
      <text class="cuIcon-warn text-green"></text>
      <text class="text-grey">文本</text>
    </view>
    <view class="action">
      <text class="text-grey text-sm">小目标还没有实现!</text>
    </view>
  </view>
  <view class="cu-item">
    <view class="content padding-tb-sm">
      <view>
        <text class="cuIcon-clothesfill text-blue margin-right-xs"></text>
        多行Item</view
      >
      <view class="text-gray text-sm">
        <text class="cuIcon-infofill margin-right-xs"></text>
        小目标还没有实现!</view
      >
    </view>
    <view class="action">
      <switch
        class="switch-sex"
        @change="SwitchSex"
        :class="skin?'checked':''"
        :checked="skin?true:false"
      ></switch>
    </view>
  </view>
</view>

代码解释

原代码是uniapp+vue2的实现,使用了 viewtextimagebuttonnavigator 等组件来构建不同类型的菜单项。以下是详细的解释:

外层容器

<view class="cu-list menu" :class="[menuBorder?'sm-border':'',menuCard?'card-menu margin-top':'']">
  • class="cu-list menu":基础样式类,定义了菜单列表的基本样式。
  • :class="[menuBorder?'sm-border':'',menuCard?'card-menu margin-top':'']":动态类名,根据 menuBordermenuCard 属性的值,决定是否添加边框和卡片样式。

菜单项

每个菜单项使用 view 标签包裹,并根据 menuArrow 属性动态添加 arrow 类名。

<view class="cu-item" :class="menuArrow?'arrow':''">
  <view class="content">
    <text class="cuIcon-circlefill text-grey"></text>
    <text class="text-grey">图标 + 标题</text>
  </view>
</view>
  • class="cu-item":菜单项的基础样式类。
  • :class="menuArrow?'arrow':''":根据 menuArrow 属性动态添加 arrow 类名,可能用于显示箭头图标。

图标 + 标题

<view class="content">
  <text class="cuIcon-circlefill text-grey"></text>
  <text class="text-grey">图标 + 标题</text>
</view>
  • class="content":内容区的样式类。
  • class="cuIcon-circlefill text-grey":图标的样式类,使用了自定义图标类 cuIcon-circlefill 和文本颜色类 text-grey
  • class="text-grey":标题文本的样式类,设置文本颜色为灰色。

图片 + 标题

<view class="content">
  <image src="/static/logo.png" class="png" mode="aspectFit"></image>
  <text class="text-grey">图片 + 标题</text>
</view>
  • class="png":图片的样式类。
  • mode="aspectFit":图片的显示模式,保持长宽比缩放图片。

Open-type 按钮

<button class="cu-btn content" open-type="contact">
  <text class="cuIcon-btn text-olive"></text>
  <text class="text-grey">Open-type 按钮</text>
</button>
  • class="cu-btn content":按钮的样式类。
  • open-type="contact":按钮的 open-type 属性,设置为 contact,可能用于打开联系界面。
  • class="cuIcon-btn text-olive":按钮图标的样式类,使用了自定义图标类 cuIcon-btn 和文本颜色类 text-olive

uniapp/微信小程序限定组件,目前不打算改造

Navigator 跳转

<navigator class="content" hover-class="none" url="../list/list" open-type="redirect">
  <text class="cuIcon-discoverfill text-orange"></text>
  <text class="text-grey">Navigator 跳转</text>
</navigator>
  • class="content":内容区的样式类。
  • hover-class="none":设置点击时的样式类为 none
  • url="../list/list":跳转的目标 URL。
  • open-type="redirect":跳转类型为 redirect,即重定向。

每个菜单都会有跳转选项,但是打算在Nuxt3环境适配后再实现

头像组

<view class="content">
  <text class="cuIcon-emojiflashfill text-pink"></text>
  <text class="text-grey">头像组</text>
</view>
<view class="action">
  <view class="cu-avatar-group">
    <view class="cu-avatar round sm" style="background-image:url(https://ossweb-img.qq.com/images/lol/web201310/skin/big10001.jpg);"></view>
    <view class="cu-avatar round sm" style="background-image:url(https://ossweb-img.qq.com/images/lol/web201310/skin/big81005.jpg);"></view>
    <view class="cu-avatar round sm" style="background-image:url(https://ossweb-img.qq.com/images/lol/web201310/skin/big25002.jpg);"></view>
    <view class="cu-avatar round sm" style="background-image:url(https://ossweb-img.qq.com/images/lol/web201310/skin/big91012.jpg);"></view>
  </view>
  <text class="text-grey text-sm">4 人</text>
</view>
  • class="cu-avatar-group":头像组的样式类。
  • class="cu-avatar round sm":单个头像的样式类,使用了圆形和小尺寸样式。
  • style="background-image:url(...)":设置头像的背景图片。

按钮

<view class="content">
  <text class="cuIcon-btn text-green"></text>
  <text class="text-grey">按钮</text>
</view>
<view class="action">
  <button class="cu-btn round bg-green shadow">
    <text class="cuIcon-upload"></text> 上传
  </button>
</view>
  • class="cu-btn round bg-green shadow":按钮的样式类,使用了圆角、绿色背景和阴影样式。
  • class="cuIcon-upload":按钮图标的样式类,使用了自定义图标类 cuIcon-upload

标签

<view class="content">
  <text class="cuIcon-tagfill text-red margin-right-xs"></text>
  <text class="text-grey">标签</text>
</view>
<view class="action">
  <view class="cu-tag round bg-orange light">音乐</view>
  <view class="cu-tag round bg-olive light">电影</view>
  <view class="cu-tag round bg-blue light">旅行</view>
</view>
  • class="cu-tag round bg-orange light":标签的样式类,使用了圆角、橙色背景和浅色样式。

文本

<view class="content">
  <text class="cuIcon-warn text-green"></text>
  <text class="text-grey">文本</text>
</view>
<view class="action">
  <text class="text-grey text-sm">小目标还没有实现!</text>
</view>
  • class="text-grey text-sm":文本的样式类,设置文本颜色为灰色,字体为小号。

多行Item

<view class="content padding-tb-sm">
  <view>
    <text class="cuIcon-clothesfill text-blue margin-right-xs"></text> 多行Item
  </view>
  <view class="text-gray text-sm">
    <text class="cuIcon-infofill margin-right-xs"></text> 小目标还没有实现!
  </view>
</view>
<view class="action">
  <switch class="switch-sex" @change="SwitchSex" :class="skin?'checked':''" :checked="skin?true:false"></switch>
</view>
  • class="content padding-tb-sm":内容区的样式类,使用了上下内边距。
  • class="switch-sex":开关的样式类。
  • @change="SwitchSex":绑定 change 事件,触发 SwitchSex 方法。
  • :class="skin?'checked':''":根据 skin 属性动态添加 checked 类名。
  • :checked="skin?true:false":根据 skin 属性动态设置 checked 状态。

需求提取

  1. 菜单列表:需要一个菜单列表组件,展示不同类型的菜单项。
  2. 菜单项类型
    • 图标 + 标题:菜单项可以包含图标和标题。
    • 图片 + 标题:菜单项可以包含图片和标题。
    • 头像组:菜单项可以包含一组头像。
    • 按钮:菜单项可以包含一个按钮,并且按钮可以触发事件。
    • 标签:菜单项可以包含多个标签。
    • 文本:菜单项可以包含文本描述。
  3. 样式控制:菜单列表可以通过属性控制样式,例如是否显示边框、是否显示箭头、是否使用卡片样式。
  4. 动态内容:菜单项的内容是动态的,可以通过传递数据来生成不同的菜单项。

改造组件

<template>
  <div
    class="cu-list menu"
    :class="[menuBorder ? 'sm-border' : '', menuCard ? 'card-menu' : '']"
  >
    <div
      v-for="(item, index) in menuItems"
      :key="index"
      class="cu-item"
      :class="menuArrow ? 'arrow' : ''"
    >
      <div class="content">
        <Icon
          v-if="item.icon"
          class="icon"
          :name="item.icon.name"
          :color="item.icon.color"
        />
        <img
          v-if="item.img"
          class="icon"
          :src="item.img.url"
          :alt="item.img.alt"
        />

        <text class="text-grey">{{ item.title }}</text>
      </div>
      <div class="action">
        <TAvatarGroup v-if="item.group" :urls="item.group" />
        <text v-if="item.group" class="text-grey text-sm"
          >{{ item.group.length }}人</text
        >
        <button
          v-if="item.btn"
          class="cu-btn round shadow"
          :class="`bg-${item.btn.bg}`"
          @click="item.btn?.event"
        >
          <Icon v-if="item.btn.icon" :name="item.btn.icon" :color="'white'" />
          {{ item.btn.text }}
        </button>
        <Tag
          v-if="item.tags"
          v-for="(tag, index) in item.tags"
          :size="tag.size"
          :key="index"
          :bg="tag.bg"
          :light="tag.light"
          shape="round"
          >{{ tag.text }}</Tag
        >
        <text v-if="item.text" class="text-grey text-sm">{{ item.text }}</text>
      </div>
    </div>
  </div>
</template>
<script setup lang="ts">
import Icon from "../Base/Icon.vue";
import TAvatarGroup from "../Base/AvatarGroup.vue";
import Tag from "../Base/Tag.vue";

withDefaults(
  defineProps<{
    menuItems?: MenuItem[];
    menuBorder?: boolean;
    menuArrow?: boolean;
    menuCard?: boolean;
  }>(),
  {
    menuBorder: false,
    menuArrow: false,
    menuCard: false,
    menuItems: () => [
      {
        title: "测试菜单",
        icon: {
          name: "warn",
          color: "blue",
        },
        text: "小目标还没有实现!",
      },
      {
        title: "图片菜单",
        img: {
          url: "https://ossweb-img.qq.com/images/lol/web201310/skin/big91012.jpg",
          alt: "图片菜单",
        },
      },
      {
        title: "头像组",
        icon: {
          name: "emoji",
          color: "pink",
        },
        group: [
          "https://ossweb-img.qq.com/images/lol/web201310/skin/big10001.jpg",
          "https://ossweb-img.qq.com/images/lol/web201310/skin/big81005.jpg",
          "https://ossweb-img.qq.com/images/lol/web201310/skin/big25002.jpg",
          "https://ossweb-img.qq.com/images/lol/web201310/skin/big91012.jpg",
        ],
      },
      {
        title: "按钮",
        icon: {
          name: "btn",
          color: "green",
        },
        btn: {
          text: "上传",
          bg: "green",
          icon: "upload",
          event: () => {
            console.log("点击了按钮");
          },
        },
      },
      {
        title: "标签",
        icon: {
          name: "tagfill",
          color: "red",
        },
        tags: [
          {
            text: "音乐",
            bg: "orange",
            light: true,
            size: "sm",
          },
          {
            text: "电影",
            bg: "green",
            light: true,
            size: "sm",
          },
          {
            text: "旅行",
            light: false,
          },
        ],
      },
    ],
  }
);
</script>
<script lang="ts">
export default {
  name: "TMenuList",
};
</script>

代码解释

模板部分

  • class="cu-list menu":基础样式类,定义了菜单列表的基本样式。
  • :class="[menuBorder ? 'sm-border' : '', menuCard ? 'card-menu' : '']":动态类名,根据 menuBordermenuCard 属性的值,决定是否添加边框和卡片样式。
  • v-for="(item, index) in menuItems":遍历 menuItems 数组,生成多个菜单项。
  • :key="index":为每个菜单项设置唯一的键。
  • class="cu-item":菜单项的基础样式类。
  • :class="menuArrow ? 'arrow' : ''":根据 menuArrow 属性动态添加 arrow 类名,可能用于显示箭头图标。

动态内容

  • 图标:如果菜单项包含 icon 属性,则显示图标。

    <Icon v-if="item.icon" class="icon" :name="item.icon.name" :color="item.icon.color" />
    
  • 图片:如果菜单项包含 img 属性,则显示图片。

    <img v-if="item.img" class="icon" :src="item.img.url" :alt="item.img.alt" />
    
  • 标题:显示菜单项的标题。

    <text class="text-grey">{{ item.title }}</text>
    
  • 头像组:如果菜单项包含 group 属性,则显示头像组。

    <TAvatarGroup v-if="item.group" :urls="item.group" />
    <text v-if="item.group" class="text-grey text-sm">{{ item.group.length }}人</text>
    
  • 按钮:如果菜单项包含 btn 属性,则显示按钮,并且按钮可以触发事件。

    <button
      v-if="item.btn"
      class="cu-btn round shadow"
      :class="`bg-${item.btn.bg}`"
      @click="item.btn?.event"
    >
      <Icon v-if="item.btn.icon" :name="item.btn.icon" :color="'white'" />
      {{ item.btn.text }}
    </button>
    
  • 标签:如果菜单项包含 tags 属性,则显示多个标签。

    <Tag
      v-if="item.tags"
      v-for="(tag, index) in item.tags"
      :size="tag.size"
      :key="index"
      :bg="tag.bg"
      :light="tag.light"
      shape="round"
    >{{ tag.text }}</Tag>
    
  • 文本:如果菜单项包含 text 属性,则显示文本。

    <text v-if="item.text" class="text-grey text-sm">{{ item.text }}</text>
    

脚本部分

  • withDefaults+defineProps:定义组件的属性,并设置属性的默认值。
    • menuItems
      • 类型MenuItem[](可选)
      • 默认值:一个包含多种类型菜单项的数组。
      • 作用:定义菜单列表中的各个菜单项。每个菜单项可以包含标题、图标、图片、头像组、按钮、标签和文本等信息。通过传递不同的 menuItems 数组,可以动态生成不同的菜单列表。
    • menuBorder
      • 类型boolean(可选)
      • 默认值false
      • 作用:控制菜单项是否显示边框。如果设置为 true,菜单项将会显示边框样式。
    • menuArrow
      • 类型boolean(可选)
      • 默认值false
      • 作用:控制菜单项是否显示箭头。如果设置为 true,菜单项将会显示一个箭头图标,通常用于指示可以点击或展开的菜单项。
    • menuCard
      • 类型boolean(可选)
      • 默认值false
      • 作用:控制菜单列表是否使用卡片样式。如果设置为 true,菜单列表将会使用卡片样式,通常用于提升视觉效果和分隔内容。

小节

这个 Vue 3 + TypeScript 代码实现了一个动态的菜单列表组件,支持多种类型的菜单项(图标、图片、头像组、按钮、标签、文本)。通过传递不同的数据,可以生成不同的菜单项,并且可以通过属性控制菜单列表的样式。

总结

宫格列表和菜单列表是用户界面设计中常用的布局方式,通过合理的布局和交互设计,可以提升用户体验。本文详细介绍了这两种布局方式的实现方法、ColorUI原代码并提供了vue3+ts的代码改造示例,帮助读者在实际项目中应用这些技术。

当然,代码中一堆需要改进的地方。我们评论区见~