Vue3 封装Step

这篇文章详细描述了一个Vue组件,用于展示动态步骤条,每个步骤包含状态指示(成功、进行中、等待、危险),并允许用户选择。组件使用了AntV图标库,还涉及自定义样式和事件处理函数。
摘要由CSDN通过智能技术生成

index.vue

<template>
  <div class="step">
    <div
      v-for="(item, index) in stepList"
      :key="index"
      class="step-item"
      :class="{ 'active': index === currentStep, 'clickable': item.status !== 'wait', 'unclickable': item.status === 'wait' }"
      @click="selectStep(index)"
    >
      <div class="step-item-flex">
        <div class="step-icon">
          <Icon
            v-if="item.status === 'success'"
            :size="24"
            color="success"
          >
            <Presets.Success />
          </Icon>
          <div
            v-if="item.status === 'process'"
            class="process-style"
          >
            <Text class="process-style-index">{{ index + 1 }}</Text>
          </div>
          <Icon
            v-if="item.status === 'danger'"
            :size="24"
            color="danger"
          >
            <Presets.Danger />
          </Icon>
          <div
            v-if="item.status === 'wait'"
            class="nostart-style"
          >
            <Text class="nostart-style-index">{{ index + 1 }}</Text>
          </div>
        </div>
        <div class="step-title-msg">
          <Text
            class="step-text-title"
            color="text-title"
            bold
          >{{ item.name }}</Text>
          <div><Text
            class="msg-text"
            color="text-description"
            ellipsis
          >{{ item.content }}</Text><Link
            v-if="currentStep === ZERO && index === ZERO"
            @click.stop="setClickFn"
          >设置</Link></div>
        </div>
        <!-- 每一步之间 -->
        <span
          v-if="index < stepList.length - 1"
          class="step-divider"
        >
          <Icon
            :icon="DoubleRight"
            color="text-description"
          />
        </span>
      </div>
      <div
        v-if="item.status"
        class="step-underline"
        :style="{ 'background-color': underlineColor(index, item.status) }"
      />
    </div>
  </div>
</template>

<script lang="ts" setup>
  import { defineProps, defineEmits } from 'vue'
  import { Icon, Text, Link } from '@antv'
  import { Presets, DoubleRight } from '@icons'

  import { StepsProps, StepsStatus, ZERO } from './data-source'

  const props = withDefaults(
    defineProps<{
      stepList: StepsProps['steps']
      currentStep: number
      loading?: boolean
    }>(),
    {
      stepList: () => [],
      currentStep: 0,
      loading: false,
    },
  )

  const emits = defineEmits(['handleStepNodeAction', 'setCommitId'])
  const selectStep = (index: number) => {
    if (props.stepList[index].status !== 'wait') {
      emits('handleStepNodeAction', index)
    }
  }

  const underlineColor = (index: number, state: StepsStatus) => {
    if (index === props.currentStep) {
      if (state === 'success') {
        return '#00AB46'
      } if (state === 'process') {
        return '#386BFF'
      } if (state === 'danger') {
        return '#FB3367'
      }
      return '#F3F3F6'
      // 当前选中的项下划线加深
    }
    if (state === 'success') {
      return '#E1FAEB'
    } if (state === 'process') {
      return '#ECF0FF'
    } if (state === 'danger') {
      return '#FFECF2'
    }
    return '#F3F3F6'
  }

  const setClickFn = () => {
    emits('setCommitId')
  }
</script>

<style lang="stylus" scoped>
.step
  display grid
  grid-template-columns repeat(4, 1fr)
  gap 32px
  width 100%

.step-item
  min-width 214px
  position relative
  display flex
  flex-direction column
  align-items center
  cursor pointer
  &.unclickable
    cursor not-allowed

.step-item-flex
  position relative
  width 100%
  display flex
  align-items center
  margin-bottom 6px
  padding 4px 8px
  .step-icon
    width 32px
    display flex
    justify-content space-around
    align-items center
    margin 0 8px
    .process-style
      width 26px
      height 26px
      border-radius 50%
      background-color #ECF0FF
      display flex
      align-items center
      justify-content center
    .process-style-index
      color #386BFF
      text-align center
      font-family Inter
      font-size 16px
      font-style normal
      font-weight 600
      line-height 24px
    .nostart-style
      width 26px
      height 26px
      border-radius 50%
      background-color #F3F3F6
      display flex
      align-items center
      justify-content center
      .nostart-style-index
        color var(--Text-placeholder, rgba(12, 1, 12, 0.32))
        text-align center
        font-family Inter
        font-size 16px
        font-style normal
        font-weight 600
        line-height 24px
  .step-title-msg
    flex 1
    padding 4px 8px 4px 0
    white-space nowrap
    .step-text-title
      font-size 16px
      margin-bottom 4px
    .msg-text
      width 80%
      margin-right 4px
.active
  font-weight bold

.clickable:hover
  background-color rgba(211, 211, 211, 0.5)
  border-radius 4px
.step-divider
  position absolute
  text-align center
  padding 0 8px
  min-width 32px
  top 50%
  transform translateY(-50%)
  right -32px
.step-underline
  position absolute
  bottom -11px
  width 100%
  height 2px
  background-color lightgray
</style>

data-source

import { toast } from ''

export const ZERO = 0

export enum StepsStatus {
  PENDING = 'wait',
  PROCESS = 'process',
  SUCCESS = 'success',
  DANGER = 'danger',
}
enum DeploymentFlow {
  Success = 'success',
  Doing = 'doing',
  NoStart = 'nostart',
  Failed = 'failed',
}

const stepTitleMapping = ['step1', 'step2', 'step3', 'step4']

type SolutionFlow = {
  num: number
  state: DeploymentFlow
  msg: string
  newButton?: {
    btnAction?: string
    btnTitle?: string
    btnType?: string
  }[]
}
const stepStatusMapping = {
  [DeploymentFlow.NoStart]: StepsStatus.PENDING,
  [DeploymentFlow.Doing]: StepsStatus.PROCESS,
  [DeploymentFlow.Success]: StepsStatus.SUCCESS,
  [DeploymentFlow.Failed]: StepsStatus.DANGER,
}
export interface StepListItem {
  name: string
  // 每一个步骤的描述
  content?: string
  // 每一个步骤的状态
  status?: StepsStatus
  // 按钮列表
  actionButtons?: {
    // 按钮文字
    text: string
    // 按钮ovga
    type: 'default' | 'primary' | 'secondary' | 'danger' | 'light'
    // 点击按钮callback
    onClick: (callBack: (val: string) => void) => void
  }[]
}
export type ISolutionFlowRes = SolutionFlow[]
export interface StepsProps {
  currentStep: number
  steps: StepListItem[]
}

export const newButtonType: Record<
string,
'default' | 'danger' | 'primary' | 'secondary' | 'light'
> = {
  def: 'default',
  danger: 'danger',
  primary: 'primary',
  secondary: 'secondary',
  light: 'light',
  link: 'light',
}
export const btnAction: Record<string, 'selectCommit'> = {
  SelectCommit: 'selectCommit',
}

export const getStepList = (deploymentFlow: ISolutionFlowRes): StepListItem[] => {
  if (!Array.isArray(deploymentFlow) || deploymentFlow.length === 0) {
    return []
  }
  const filteredFlow = deploymentFlow.filter(item => item !== undefined && item !== null)

  filteredFlow.sort((a, b) => a.num - b.num)

  return filteredFlow?.map((flow: SolutionFlow) => ({
    name: stepTitleMapping?.[flow.num - 1] || '',
    status: stepStatusMapping?.[flow.state] ?? StepsStatus.SUCCESS,
    content: flow.msg,
    actionButtons:
      flow.newButton?.map(btn => ({
        text: btn.btnTitle || '',
        type: (btn.btnType && newButtonType[btn.btnType]) || 'default',
        onClick: async (callBack: (val: string) => void) => {
          try {
            if (btn.btnAction === btnAction.SelectCommit) {
              callBack(btnAction.SelectCommit)
              return
            }
          } catch (e) {
            toast({
              type: 'danger',
              description: `${e}`,
              placement: 'top',
            })
          }
        },
      })) || [],
  }))
}

export const getCurrentStep = (deploymentFlow: ISolutionFlowRes): number => {
  let currentStep = 0

  if (!Array.isArray(deploymentFlow)) {
    return currentStep
  }

  currentStep = deploymentFlow.findIndex(item => item.state === 'doing')
  return currentStep
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值