最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于Vue實(shí)現(xiàn)一個(gè)"蛇形"步驟條

 更新時(shí)間:2024年11月29日 10:11:33   作者:coderzsp  
在現(xiàn)代Web應(yīng)用中,步驟條作為一種常見的UI組件,廣泛應(yīng)用于表單提交、任務(wù)進(jìn)度以及多步驟操作等場景,下面我們來看看如何利用Vue實(shí)現(xiàn)一個(gè)蛇形步驟條吧

前言

在現(xiàn)代Web應(yīng)用中,步驟條(Step Progression)作為一種常見的UI組件,廣泛應(yīng)用于表單提交、任務(wù)進(jìn)度以及多步驟操作等場景。為了提升用戶體驗(yàn),設(shè)計(jì)師往往會采用更加直觀和有趣的方式展示步驟進(jìn)度。今天,我們將通過 Vue 實(shí)現(xiàn)一個(gè)“蛇形”步驟圖組件,這種獨(dú)特的設(shè)計(jì)方式不僅能清晰地表達(dá)步驟的完成狀態(tài),還能給用戶帶來更加流暢和動(dòng)態(tài)的交互體驗(yàn)。

前幾天在做一個(gè)需求時(shí)就需要寫一個(gè)步驟圖組件,大致原型如下:

看到這個(gè),內(nèi)心無一點(diǎn)波瀾,直接去項(xiàng)目配套 Ant Desin Vue 組件庫中找對應(yīng)組件拿來改改就 ojbk 了。

But,后端同事告訴我業(yè)務(wù)場景后發(fā)現(xiàn)還有操作空間,因?yàn)?UI 組件的 Step 步驟條不太符合當(dāng)前業(yè)務(wù),里面所有步驟都是連續(xù)的,但是當(dāng)前業(yè)務(wù)場景是可以跳過其中一些步驟項(xiàng),于是打算自己寫一個(gè),最后根據(jù)后端返回的數(shù)據(jù)來決定步驟項(xiàng)是否執(zhí)行了。

customStep(V1)

customStep.vue 完整代碼:

<template>
    <div class="custom-step">
        <div v-for="(item, index) in stepList" :key="index" class="step-item">
            <div class="item-content">
                <div class="step-title" @click="handleStepClick(index)">
                    <div class="step-num" :class="{ 'step-num-finished': item.status === 'finished' }">{{ index + 1 }}
                    </div>
                    <div class="setp-txt" :class="{ 'step-txt-finished': item.status === 'finished' }">{{ item.title }}
                    </div>
                </div>
                <div class="split-line" v-if="!item.isLast" :class="{ 'split-line-finished': isFinished(index) }"></div>
            </div>
        </div>
    </div>
</template>

<script setup>
import { ref, computed } from 'vue'

const props = defineProps({
    stepList: {
        type: Array,
        default: () => []
    }
})

// 步驟列表
// const stepList = ref([
//     { title: '確定意向', status: 'finished', isLast: false },
//     { title: '對接洽談', status: 'finished', isLast: false },
//     { title: '項(xiàng)目報(bào)價(jià)', status: 'unfinished', isLast: false },
//     { title: '投標(biāo)對比', status: 'unfinished', isLast: false },
//     { title: '合同擬定', status: 'unfinished', isLast: false },
//     { title: '轉(zhuǎn)化完成', status: 'finished', isLast: false },
//     { title: '轉(zhuǎn)化完成1', status: 'finished', isLast: false },
//     { title: '轉(zhuǎn)化完成2', status: 'finished', isLast: true },
// ])

const isFinished = computed(() => index => {
    const prevStep = props.stepList[index];
    const nextStep = props.stepList[index + 1];
    return prevStep.status === 'finished' && nextStep.status === 'finished';
})

const emit = defineEmits(['stepClick'])
const handleStepClick = index => {
    emit('stepClick', index)
}
</script>

<style lang="less" scoped>
.custom-step {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    row-gap: 20px;
    width: 100%;
    padding: 0 30px;

    .step-item {
        width: calc(100% / 6);

        .item-content {
            box-sizing: border-box;
            display: flex;
            align-items: center;
        }

        .step-title {
            width: 80px;
            text-align: center;
            font-weight: 600;
            color: rgb(153, 153, 166);
            cursor: pointer;

            .step-num {
                box-sizing: content-box;
                width: 35px;
                margin: 0 auto;
                line-height: 35px;
                font-size: 16px;
                border: 3px solid #e3e8ec;
                border-radius: 100%;
            }

            .setp-txt {
                margin-top: 10px;
            }

            .step-num-finished {
                color: rgb(26, 188, 156);
                border: 3px solid rgb(26, 188, 156);
            }

            .step-txt-finished {
                color: rgb(26, 188, 156);
            }
        }

        .split-line {
            width: calc(100% - 80px);
            height: 3px;
            margin-top: -25px;
            background-color: #e3e8ec;
            border-radius: 5px;
        }

        .split-line-finished {
            background-color: rgb(26, 188, 156);
        }
    }
}
</style>

最后效果如下:

頁面初始渲染步驟,然后可以點(diǎn)擊某個(gè)步驟項(xiàng),進(jìn)行業(yè)務(wù)操作后刷新頁面重新渲染步驟條,這樣可以根據(jù)業(yè)務(wù)需求跳過某些步驟項(xiàng)。

But,還有事,我這固定了一行顯示6個(gè)步驟項(xiàng),超過6個(gè)會換行,但是顯示就有一點(diǎn)瑕疵,因?yàn)檎T夭季侄际菑淖蟮接?,所以超過6個(gè)步驟項(xiàng)就會這樣顯示:

1 -> 2 -> 3 -> 4 -> 5 -> 6 ->

7 -> 8 -> ...

產(chǎn)品給我說這樣看著不太連貫,想實(shí)現(xiàn)一個(gè)類似 "S" 形的,看著會連貫一些,后面找了個(gè)例子改了改,算是符合當(dāng)前需求了。

customStep(V2)

customStep_.vue 完整代碼:

<template>
    <div class="container">
        <div v-for="(item, index) in stepList" class="grid-item" :key="index">
            <div class="step" :class="{ 'step-finished': item.status === 'finished' }" @click="handleStepClick(index)">
                {{ item.title }}</div>
        </div>
    </div>
</template>

<script setup>
import { ref } from 'vue';

const props = defineProps({
    stepList: {
        type: Array,
        default: () => []
    }
})

// 步驟列表
// const stepList = ref([
//     { title: '確定意向', status: 'finished' },
//     { title: '對接洽談', status: 'finished' },
//     { title: '項(xiàng)目報(bào)價(jià)', status: 'unfinished' },
//     { title: '投標(biāo)對比', status: 'unfinished' },
//     { title: '合同擬定', status: 'unfinished' },
//     { title: '轉(zhuǎn)化完成', status: 'finished' },
//     { title: '制定方案', status: 'unfinished' },
//     { title: '合同簽訂', status: 'finished' },
//     { title: '合同跟蹤', status: 'finished' },
//     { title: '合同回款', status: 'unfinished' },
//     { title: '項(xiàng)目交付', status: 'unfinished' },
//     { title: '項(xiàng)目驗(yàn)收', status: 'unfinished' },
//     { title: '項(xiàng)目結(jié)束', status: 'unfinished' },
// ])

const emit = defineEmits(['stepClick'])
const handleStepClick = index => {
    emit('stepClick', index)
}

</script>

<style lang="less" scoped>
@colNum: 6; // 單行排列的步驟項(xiàng)個(gè)數(shù)(2、3、4、5、6、...) 
@colEven: @colNum * 2; // 兩行元素?cái)?shù)
@lineWidth: 35px; // 步驟間連線長度
@rowDistance: 50px; // 行間距
@colDistance: @lineWidth; // 列間距
@arrowSize: 6px; // 箭頭大小
@stepColor: #9e9e9e; // 步驟顏色

.container {
    width: 100%;
    display: grid;
    padding: 30px 0;
    grid-template-columns: repeat(@colNum, 1fr);
    gap: @rowDistance @colDistance;
}

.grid-item {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;

    &::before {
        position: absolute;
        content: '';
        right: -@lineWidth;
        width: @lineWidth;
        height: 0;
        border-top: 1px dashed @stepColor;
    }

    &::after {
        content: '';
        position: absolute;
        right: (-@colDistance / 2);
        transform: translateX(50%);
        border-top: (@arrowSize / 1.4) solid transparent;
        border-left: @arrowSize solid @stepColor;
        border-bottom: (@arrowSize / 1.4) solid transparent;
    }

    // 給每行最后一個(gè)步驟(除最后一行)添加向下的連接箭頭
    &:nth-child(@{colNum}n) {

        &:not(:last-child) {
            .step {
                &::before {
                    content: '';
                    position: absolute;
                    left: 50%;
                    bottom: -(@rowDistance / 2);
                    height: @lineWidth;
                    border-left: 1px dashed @stepColor;
                    transform: translate(-50%, 50%);
                }

                &::after {
                    content: '';
                    position: absolute;
                    left: 50%;
                    bottom: -(@rowDistance / 2);
                    border-top: @arrowSize solid @stepColor;
                    border-left: (@arrowSize / 1.4) solid transparent;
                    border-right: (@arrowSize / 1.4) solid transparent;
                    transform: translate(-50%, 50%);
                }
            }
        }
    }

    each(range(@colEven), {
        &:nth-child(@{colEven}n+@{value}) {
            @isEvenLine: boolean(@value > @colNum);
            @modNum: mod(@value, @colEven); // 余數(shù) 1、2、3、4、5、0

            /** 偶數(shù)行旋轉(zhuǎn)箭頭,步驟倒序排列(使用transform交換位置) */
            & when (@isEvenLine) {
                @transN: (@colNum + 1 + @colEven - @value - @value);
                transform: translateX(calc(@transN * 100% + @transN * @colDistance));

                &::after {
                    transform: translateX(50%) rotate(180deg) !important; // 旋轉(zhuǎn)箭頭
                }
            }

            // 最右排(n & n + 1 位)隱藏多余的箭頭(如果container設(shè)置了overflow:hidden 則不用處理)
            & when (@modNum=@colNum), (@modNum=@colNum+1) {
                &::before, &::after {
                    display: none;
                }
            }

            // 最后一個(gè)步驟在奇數(shù)行 需要隱藏連線箭頭
            & when not (@isEvenLine) {
                &:last-child {
                    &::before, &::after {
                        display: none;
                    }
                }
            }
        }
    })
}

.step {
    position: relative;
    width: 100px;
    line-height: 40px;
    font-size: 16px;
    text-align: center;
    border-radius: 5px;
    color: #9e9e9e;
    border: 2px solid #9e9e9e;
}

.step-finished {
    background-color: #4caf50;
    color: #fff;
    border: 2px solid #4caf50;
}
</style>

兩種對比效果:

到此這篇關(guān)于基于Vue實(shí)現(xiàn)一個(gè)"蛇形"步驟條的文章就介紹到這了,更多相關(guān)Vue步驟條內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vue項(xiàng)目配置國際化$t('')的介紹和用法示例

    vue項(xiàng)目配置國際化$t('')的介紹和用法示例

    這篇文章主要給大家介紹了關(guān)于vue項(xiàng)目配置國際化?$t('')的介紹和用法的相關(guān)資料,多語言和國際化現(xiàn)在已經(jīng)成為一個(gè)網(wǎng)站或應(yīng)用的必要功能之一,Vue作為一款流行的前端框架,在這方面也有著靈活的解決方案,需要的朋友可以參考下
    2023-09-09
  • 安裝vue-cli的簡易過程

    安裝vue-cli的簡易過程

    安裝vue-cli的前提是你已經(jīng)安裝了npm,安裝npm你可以直接下載node的安裝包進(jìn)行安裝。接下來通過本文給大家介紹安裝vue-cli的簡易過程,感興趣的朋友跟隨腳本之家小編一起學(xué)習(xí)吧
    2018-05-05
  • Vue3中如何使用異步請求示例詳解

    Vue3中如何使用異步請求示例詳解

    Vue3增加了很多讓人眼前一亮的特征,suspense 組件就是其中之一,對處理異步請求數(shù)據(jù)非常實(shí)用,下面這篇文章主要給大家介紹了關(guān)于Vue3中如何使用異步請求的相關(guān)資料,需要的朋友可以參考下
    2022-06-06
  • Vue原理剖析 實(shí)現(xiàn)雙向綁定MVVM

    Vue原理剖析 實(shí)現(xiàn)雙向綁定MVVM

    這篇文章主要為大家剖析了Vue原理,實(shí)現(xiàn)雙向綁定MVVM,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • vue中provide?inject的響應(yīng)式監(jiān)聽解決方案

    vue中provide?inject的響應(yīng)式監(jiān)聽解決方案

    這篇文章主要介紹了vue中provide?inject的響應(yīng)式監(jiān)聽解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • vue.config.js配置報(bào)錯(cuò)解決辦法(可能是與webpack混淆)

    vue.config.js配置報(bào)錯(cuò)解決辦法(可能是與webpack混淆)

    在Vue.js開發(fā)過程中,vue.config.js文件是用于配置項(xiàng)目的,特別是對于開發(fā)環(huán)境的設(shè)置,這篇文章主要給大家介紹了關(guān)于vue.config.js配置報(bào)錯(cuò)解決的相關(guān)資料,可能是與webpack混淆,需要的朋友可以參考下
    2024-06-06
  • Vue實(shí)現(xiàn)視頻播放vue-video-player、dplayer方式

    Vue實(shí)現(xiàn)視頻播放vue-video-player、dplayer方式

    這篇文章主要介紹了Vue實(shí)現(xiàn)視頻播放vue-video-player、dplayer方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-04-04
  • 關(guān)于vue面試題匯總

    關(guān)于vue面試題匯總

    本文給大家收藏整理了關(guān)于vue面試題匯總的一些知識,需要的朋友可以參考下
    2018-03-03
  • vue項(xiàng)目中vue.config.js文件詳解

    vue項(xiàng)目中vue.config.js文件詳解

    vue.config.js?是一個(gè)可選的配置文件,如果項(xiàng)目的?(和?package.json?同級的)?根目錄中存在這個(gè)文件,那么它會被?@vue/cli-service?自動(dòng)加載,這篇文章主要介紹了vue項(xiàng)目中vue.config.js文件的介紹,需要的朋友可以參考下
    2024-02-02
  • Vue.delete()刪除對象的屬性說明

    Vue.delete()刪除對象的屬性說明

    這篇文章主要介紹了Vue.delete()刪除對象的屬性說明,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-04-04

最新評論

富锦市| 丹巴县| 崇礼县| 呼图壁县| 镇巴县| 巴塘县| 墨竹工卡县| 明水县| 县级市| 靖边县| 三亚市| 远安县| 九寨沟县| 资阳市| 潜江市| 花垣县| 襄城县| 汪清县| 青铜峡市| 柳江县| 芦溪县| 额敏县| 珠海市| 历史| 仲巴县| 日照市| 开江县| 阳高县| 景泰县| 炉霍县| 集安市| 嘉荫县| 年辖:市辖区| 汉源县| 寿阳县| 成都市| 手游| 博湖县| 浙江省| 个旧市| 读书|