Vue3步骤条(Steps)

news2025/1/23 13:35:40

Vue2步骤条(Steps)

可自定义设置以下属性:

  • 步骤数组(steps),类型:Array<{title?: string, description?: string}>,默认 []

  • 当前选中的步骤,设置 v-model 后,Steps 变为可点击状态(v-model:current),类型:number,默认 1,从1开始计数

  • 步骤条总宽度(width),类型:string|number,默认 '100%'

  • 描述文本最大宽度(descMaxWidth),类型:number,默认 140px

效果如下图:

①创建步骤条组件Steps.vue:

<script setup lang="ts">
import { computed } from 'vue'
interface Step {
  title?: string, // 标题
  description?: string // 描述
}
const props = defineProps({
    steps: { // 步骤数组
      type: Array<Step>,
      default: () => []
    },
    current: { // 当前选中的步骤(v-model),设置 v-model 后,Steps 变为可点击状态。从1开始计数
      type: Number,
      default: 1
    },
    width: { // 步骤条总宽度
      type: [String, Number],
      default: '100%'
    },
    descMaxWidth: { // 描述文本最大宽度
      type: Number,
      default: 140
    }
  })
const totalWidth = computed(() => {
  if (typeof props.width === 'number') {
    return props.width + 'px'
  } else {
    return props.width
  }
})
const totalSteps = computed(() => { // 步骤总数
  return props.steps.length
})
const currentStep = computed(() => {
  if (props.current < 1) {
    return 1
  } else if (props.current > totalSteps.value + 1) {
    return totalSteps.value + 1
  } else {
    return props.current
  }
})
// 若当前选中步骤超过总步骤数,则默认选择步骤1
const emits = defineEmits(['update:current', 'change'])
function onChange (index: number) { // 点击切换选择步骤
  if (currentStep.value !== index) {
    emits('update:current', index)
    emits('change', index)
  }
}
</script>
<template>
  <div class="m-steps-area" :style="`width: ${totalWidth};`">
    <div class="m-steps">
      <div :class="['m-steps-item',
          {
            'finish': currentStep > index + 1,
            'process': currentStep === index + 1,
            'wait': currentStep < index + 1
          }
        ]"
        v-for="(step, index) in steps" :key="index">
        <div class="m-info-wrap" @click="onChange(index + 1)">
          <div class="m-steps-icon">
            <span class="u-num" v-if="currentStep<=index + 1">{{ index + 1 }}</span>
            <svg class="u-icon" v-else viewBox="64 64 896 896" data-icon="check" aria-hidden="true" focusable="false"><path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z"></path></svg>
          </div>
          <div class="m-steps-content">
            <div class="u-steps-title">{{ step.title }}</div>
            <div class="u-steps-description" v-show="step.description" :style="`max-width: ${descMaxWidth}px;`">{{ step.description }}</div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<style lang="less" scoped>
.m-steps-area {
  margin: 0px auto;
  .m-steps {
    display: flex;
    .m-steps-item {
      display: inline-block;
      overflow: hidden;
      font-size: 16px;
      line-height: 32px;
      &:not(:last-child) {
        margin-right: 16px;
        flex: 1; // 弹性盒模型对象的子元素都有相同的长度,且忽略它们内部的内容
        .u-steps-title {
          &:after {
            background: #e8e8e8;
            position: absolute;
            top: 16px;
            left: 100%;
            display: block;
            width: 3000px;
            height: 1px;
            content: "";
            cursor: auto;
            transition: all .3s;
          }
        }
      }
      .m-info-wrap {
        display: inline-block;
        .m-steps-icon {
          display: inline-block;
          margin-right: 8px;
          width: 30px;
          height: 30px;
          border-radius: 50%;
          text-align: center;
          background: #fff;
          border: 1px solid rgba(0,0,0,.25);
          transition: all .3s;
          .u-num {
            display: inline-block;
            vertical-align: top;
            font-size: 16px;
            line-height: 1;
            margin-top: 7px;
            color: rgba(0, 0, 0, .25);
            transition: all .3s;
          }
          .u-icon {
            fill: @themeColor;
            width: 16px;
            height: 16px;
            vertical-align: top;
            margin-top: 7px;
          }
        }
        .m-steps-content {
          display: inline-block;
          vertical-align: top;
          .u-steps-title {
            position: relative;
            display: inline-block;
            padding-right: 16px;
            color: rgba(0,0,0,.45);
            transition: all .3s;
          }
          .u-steps-description {
            font-size: 14px;
            color: rgba(0,0,0,.45);
            word-wrap: break-word;
            transition: all .3s;
          }
        }
      }
    }
    .finish {
      .m-info-wrap {
        cursor: pointer;
        .m-steps-icon {
          background: #fff;
          border: 1px solid rgba(0,0,0,.25);
          border-color: @themeColor;
        }
        .m-steps-content {
          .u-steps-title {
            color: rgba(0,0,0,.85);
            &:after {
              background: @themeColor;
            }
          }
          .u-steps-description {
            color: rgba(0,0,0,.45);
          }
        }
        &:hover {
          .m-steps-content {
            .u-steps-title {
              color: @themeColor;
            }
            .u-steps-description {
              color: @themeColor;
            }
          }
        }
      }
    }
    .process {
      .m-info-wrap {
        .m-steps-icon {
          background: @themeColor;
          border: 1px solid rgba(0,0,0,.25);
          border-color: @themeColor;
          .u-num {
            color: #fff;
          }
        }
        .m-steps-content {
          .u-steps-title {
            font-weight: 500;
            color: rgba(0,0,0,.85);
          }
          .u-steps-description {
            color: rgba(0,0,0,.85);
          }
        }
      }
    }
    .wait {
      .m-info-wrap {
        cursor: pointer;
        &:hover {
          .m-steps-icon {
            border-color: @themeColor;
            .u-num {
              color: @themeColor;
            }
          }
          .m-steps-content {
            .u-steps-title {
              color: @themeColor;
            }
            .u-steps-description {
              color: @themeColor;
            }
          }
        }
      }
    }
  }
}
</style>

②在要使用的页面引入:

<script setup lang="ts">
import { Steps } from './Steps.vue'
import { ref, watch } from 'vue'
const steps = ref([
  {
    title: 'Step 1',
    description: 'description 1'
  },
  {
    title: 'Step 2',
    description: 'description 2'
  },
  {
    title: 'Step 3',
    description: 'description 3'
  },
  {
    title: 'Step 4',
    description: 'description 4'
  },
  {
    title: 'Step 5',
    description: 'description 5'
  }
])
const current = ref(3)
watch(current, (to) => {
  console.log('p to:', to)
})

function onChange (index: number) { // 父组件获取切换后的选中步骤
  console.log('current:', index)
}
function onPrevious () {
  if (current.value > 1) {
    current.value--
  }
}
function onNext () {
  if (steps.value.length >= current.value) {
    current.value++
  }
}
</script>
<template>
  <div>
    <h2 class="mb10">Steps 步骤条基本使用</h2>
    <Steps
      :steps="steps"
      :width="'100%'"
      :descMaxWidth="160"
      :current="current"
      @change="onChange" />
    <h2 class="mb10">Steps 步骤条,设置(v-model: current后可点击)</h2>
    <Steps
      :steps="steps"
      :width="'100%'"
      :descMaxWidth="160"
      v-model:current="current"
      @change="onChange" />
    <Button @click="onPrevious()" size="large" class="mt30 mr30">Previous</Button>
    <Button @click="onNext()" size="large">Next</Button>
  </div>
</template>
<style lang="less" scoped>
</style>

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java13-多线程

一&#xff1a;基本概念&#xff1a;程序&#xff0c;进程&#xff0c;线程 程序&#xff1a; 是完成特定任务&#xff0c;用某种语言编写的一组指令集合&#xff0c;即指一段静态的代码。 进程&#xff1a;是程序的一次执行过程&#xff0c;或是正在运行的一个程序。 线程&…

Linux系统之MobaXterm远程连接centos的GNOME桌面环境

Linux系统之MobaXterm远程连接centos的GNOME桌面环境一、MobaXterm介绍1.MobaXterm简介2.MobaXterm功能特点二、centos安装GNOME桌面1.本地环境介绍2.安装GNOME桌面环境3.本地进入Linux桌面三、MobaXterm远程连接centos1.打开MobaXterm软件2.远程连接本地Linux系统四、远程连接…

如何利用ChatGPT辅助优化刷题性能

根据土著刷题共建群里的一个小伙伴反馈&#xff0c;刷题会出现切题卡顿的情况&#xff0c;有时会出现滑不动的情况。 定位问题 为了定位切题卡顿问题的具体原因&#xff0c;测试了高低端手机&#x1f4f1;、切换2G、3G、4G低网络状态等各种影响切题的现实情况&#xff0c;经过借…

门店零售系统有哪些功能模块?能带来哪些帮助?

门店零售系统是一种用于管理门店销售、库存、采购等业务的软件系统&#xff0c;可以帮助门店提高管理效率、降低操作风险、优化运营决策&#xff0c;从而增强市场竞争力和顾客满意度。 一、门店零售系统的4大功能 1、商品管理 该模块主要用于管理门店的商品信息&#xff0c;包…

arcgis中地理配准之栅格平移

背景 前面写过一篇文章,是针对有两个对应的栅格数据进行配准的 Arcgis地理配准栅格数据 有时候需要没有对应的栅格数据,只有单幅栅格数据,而且知道平移参数,这时候可以通过平移参数来平移栅格,而且在unity中不能直接识别坐标值很大的数据,只能通过平移将坐标值减少,才…

【分布式事务AT模式 SpringCloud集成Seata框架】分布式事务框架Seata详细讲解

前言 上篇文章我们讲述了如何启动seata的本地服务&#xff0c;并且注册到nacos使用&#xff0c;这篇文章将在SpringCloud中整合Seata框架 上篇文章传送门&#xff1a;https://blog.csdn.net/Syals/article/details/130102851?spm1001.2014.3001.5501 本篇主要内容&#xff…

Docker几个概念

Docker几个概念&#xff0c;有不正确地方欢迎指正 一、首先来看一句话&#xff1a;没有Cgroups就没有LXC&#xff0c;没有LXC就没有Docker。 1、什么是Cgroup呢&#xff1f;Cgroup又名Control group&#xff0c;是Linux内核提供的一种可以限制、记录、隔离进程组所使用的物理…

VLAN 基础与划分及配置

我们都知道 VLAN 的中文名为"虚拟局域网"&#xff0c;VLAN 是一种将局域网设备从逻辑上划分成一个个不同的网段&#xff0c;从而实现虚拟工作组的新兴数据交换技术。这一新兴技术主要应用于交换机和路由器中&#xff0c;但主流应用还是在交换机之中。那今天咱们就和海…

Linux网络连接出现问题

报错截图 1.先查看NetworkManager是否启动 systemctl status NetworkManager如果输出结果中包含 "active (running)" 表示 NetworkManager 已经启动并正在运行 2.查看DNS是否配置 cat vim /etc/resolv.conf 1.查看是否有配置信息&#xff0c;如果没有请配置DNS …

华为ACL配置

模拟场景 服务器&#xff1a;192.168.3.100 销售部&#xff1a;192.168.1.1 开发部&#xff1a;192.168.2.1 模拟互联网&#xff1a;1.1.1.1 要求1&#xff1a;销售部不允许访问服务器 要求2&#xff1a;开发部可以访问服务器 要求3&#xff1a;互联网不可以访问服务器 拓扑图…

栈和队列经典题题解

目录 &#x1f349;一.括号匹配问题&#x1f349; &#x1f348;二.用队列实现栈&#x1f348; &#x1f34f;三.用栈实现队列&#x1f34f; &#x1f353;四.设计循环队列&#x1f353; &#x1f349;一.括号匹配问题&#x1f349; OJ链接力扣 题目描述&#xff1a; 思路&…

77-Linux_网络编程

网络编程一.主机字节序列和网络字节序列二.套接字地址结构1.通用socket地址结构2.专用的socket地址结构3.IP地址转换函数一.主机字节序列和网络字节序列 主机字节序列分为大端字节序和小端字节序&#xff0c;不同的主机采用的字节序列可能不同。 大端字节序是指一个整数的高位…

开店必备的5款超实用零售管理软件,第1个新手也能轻松使用!

现在越来越多的零售店老板都开始用零售管理软件来管理门店&#xff0c;提升效率&#xff0c;节约人力和时间成本。 但对于刚刚接触零售管理软件的老板来说&#xff0c;应用市场上的零售管理软件那么多&#xff0c;究竟哪些好用&#xff0c;哪些容易上手……还不太了解。 别着急…

CMake项目使用ctest+gtest进行单元测试

随着CMake工具越来越强大便捷&#xff0c;越来越多的C/C项目转而使用CMake来进行编译管理&#xff0c;它还提供了用于测试的ctest命令来执行项目中编写的单元测试。 本文就以一个实例来介绍如何使用ctest来进行单元测试。 一、环境准备 本文实例环境VSCodeMinGW64CMakegtest…

Qt程序CPU过高怎么定位解决?性能优化

自己开发的一个程序采用多线程调用url从网络上下载股票数据&#xff0c;一旦开启程序就特别的卡&#xff1b;想着优化一下&#xff1b;授之于鱼&#xff0c;不如 授之以渔&#xff1b; 1.CPU过高排查方法 &#xff08;1&#xff09;打开vs的性能探测器&#xff1b; &#xff…

Android端推送消息之极光推送

推送方式 轮询 --实现方式: 周期性主动获取网络中的数据; --缺点: 费电, 费流量; SMS --实现方式: 服务器端向手机端发送短信, 手机监听短信广播, 将拦截的短信信息进行显示; --优点: 省电, 省流量, 在没有网络的偏远地点也能接收到推送消息; --缺点: 费钱, 一毛钱一条;…

国产BI厂商:数字化时代的“卖水人”,扎根本土商业生态(上)

“没有数据&#xff0c;就没有竞争力。”随着中国经济以数字经济为重要引擎转向高质量发展道路&#xff0c;数据已成为推动经济增长的关键要素。Navesink顾问公司的创始人Thomas Redman认为&#xff0c;企业需要建立起对数据的组织和处理能力&#xff0c;只有这样才能收获大数据…

前端常用vscode插件

打开vscode配置 commandshiftP 选择Open User Setting&#xff08;首选项&#xff1a;打开用户设置(JSON)&#xff09; 1 Prettier - Code formatter "[javascript]": {"editor.defaultFormatter": "esbenp.prettier-vscode"},"[typescri…

日常记录:天梯赛练习集L1-043 阅览室

题目&#xff1a; 天梯图书阅览室请你编写一个简单的图书借阅统计程序。当读者借书时&#xff0c;管理员输入书号并按下S键&#xff0c;程序开始计时&#xff1b;当读者还书时&#xff0c;管理员输入书号并按下E键&#xff0c;程序结束计时。书号为不超过1000的正整数。当管理员…

信安大佬真的用kali吗?

Kali只是现在网络安全和kali比较火的一个操作系统 下面我为大家讲讲kali系统都有那些优点 Kali介绍Kali Linux是基于Debian的Linux发行版&#xff0c; 设计用于数字取证操作系统。面向专业的渗透测试和安全审计。 集成化&#xff1a;预装超过300个渗透测试工具兼容好&#x…