Vue3 封装多行步骤条

效果:

封装代码:

<template>
	<div style="width: 100%">
		<div class="my-steps">
			<div class="steps">
				<template v-for="(mItem, mIndex) in pageNum">
					<div
						class="stepMain"
						:style="{
							'margin-top': mIndex > 0 ? '50px' : '0px',
							'justify-content':
								mIndex % 2 == 1 ? 'flex-end' : 'flex-start',
						}"
					>
						<template v-for="(item, index) in mStepsData[mIndex]">
							<div
								class="step"
								:style="{
									'flex-basis': flexBasis,
								}"
							>
								<div
									class="step-pane"
									:class="[getActiveClass(item)]"
								>
									<div class="left">
										<el-icon class="step-icon">
											<el-icon-success-filled />
										</el-icon>
									</div>
									<div class="right">
										<div class="title">
											{{ item.title }}
										</div>
										<div class="description">
											{{ item.description }}
										</div>
									</div>
								</div>
								<div
									class="line"
									:class="[
										getLineClass(
											item,
											mStepsData[mIndex].length,
											mIndex
										),
									]"
								></div>
							</div>
						</template>
					</div>
				</template>
			</div>
		</div>
	</div>
</template>

<script>
export default {
	props: {
		stepsData: {
			type: Array,
			default() {
				return [];
			},
		},
		active: {
			type: Number,
			default: 0,
		},
		//每行的数量
		rowCount: {
			type: Number,
			default: 5,
		},
	},
	data() {
		return {
			mStepsData: [],
			pageNum: 0,
			flexBasis: "",
		};
	},
	computed: {
		getActiveClass() {
			return (item) => {
				return this.active > item.myStepIndex
					? "is-finish"
					: this.active == item.myStepIndex
					? "is-process"
					: "";
			};
		},
		getLineClass() {
			return (item, len, rowIndex) => {
				if (item.colNum == len) {
					if (rowIndex + 1 == this.pageNum) {
						//如果是最后一行,则不显示连接线
						return "none";
					}
					//最后一列
					return "bottom" + item.lineDirection;
				} else {
					return item.lineDirection;
				}
			};
		},
	},
	mounted() {
		this.init();
	},
	methods: {
		// 初始化数据
		init() {
			const stepsData = this.stepsData;
			if (stepsData && stepsData.length > 0) {
				this.pageNum = Math.ceil(stepsData.length / this.rowCount);

				this.flexBasis = parseFloat(100.0 / this.rowCount) + "%";

				let list = [];
				let index = 0;
				stepsData.forEach((item) => {
					item.myStepIndex = index;

					let colNum = (index % this.rowCount) + 1;
					item.colNum = colNum;

					if (this.mStepsData.length % 2 == 1) {
						//连接线的位置
						item.lineDirection = "left";
					} else {
						item.lineDirection = "right";
					}

					list.push(item);
					if (list.length >= this.rowCount) {
						if (
							this.mStepsData.length > 0 &&
							this.mStepsData.length % 2 == 1
						) {
							list = list.reverse();
						}

						this.mStepsData.push(JSON.parse(JSON.stringify(list)));
						list = [];
					}

					index++;
				});
				if (list.length > 0) {
					if (
						this.mStepsData.length > 0 &&
						this.mStepsData.length % 2 == 1
					) {
						list = list.reverse();
					}
					this.mStepsData.push(JSON.parse(JSON.stringify(list)));
				}
			}
		},
	},
};
</script>

<style scoped lang="scss">
.my-steps {
	padding: 10px;
	display: flex;
	.steps {
		width: 100%;
		.stepMain {
			display: flex;

			.step {
				color: #b9abb2;
				position: relative;
				.step-pane {
					position: relative;
					display: flex;
					border: 1px solid #b9abb2;
					width: 60%;
					padding: 10px;
					align-items: center;
					gap: 10px;
					background: white;
					.left {
						height: 30px;
						display: flex;

						.step-icon {
							font-size: 30px;
						}
					}
					.right {
					}

					z-index: 100;
				}

				.line {
					position: absolute;
					width: 100%;
					height: 3px;
					background: #b9abb2;
					top: 50%;
					z-index: 99;

					&.left {
						right: 100%;
					}

					&.bottomright {
						/* transform: rotate(90deg); */
						width: 3px;
						height: 100%;
						top: 100%;
						left: 50%;
					}

					&.bottomleft {
						/* transform: rotate(90deg); */
						width: 3px;
						height: 100%;
						top: 100%;
						left: 10%;
					}

					&.none {
						width: 0px;
						height: 0px;
						display: none;
					}
				}

				.is-finish {
					color: var(--el-color-primary);
					border-color: var(--el-color-primary);

					& + .line {
						background: var(--el-color-primary);
					}
				}

				.is-process {
					color: var(--el-text-color-primary);
					border-color: var(--el-text-color-primary);
					& + .line {
						background: var(--el-text-color-primary);
					}
				}
			}
		}
	}
}
</style>

使用:

<template>
	<div style="width: 100%; padding: 20px">
		<custom-steps
			:steps-data="stepData"
			:rowCount="5"
			:active="active"
		></custom-steps>
	</div>

	<el-button type="primary" @click="nextStep">下一步骤</el-button>
</template>

<script>
//引入步骤条组件
import customSteps from "@/components/customComp/steps";
export default {
	name: "Test",
	components: {
		customSteps,
	},
	data() {
		return {
			active: 0,
			stepData: [
				{
					title: "Step 1",
					description: "步骤00",
				},
				{ title: "Step 2", description: "步骤11" },
				{ title: "Step 3", description: "步骤22" },
				{ title: "Step 4", description: "步骤33" },
				{ title: "Step 5", description: "步骤44" },
				{ title: "Step 6", description: "步骤55" },
				{ title: "Step 7", description: "步骤66" },
				{ title: "Step 8", description: "步骤77" },
				{ title: "Step 9", description: "步骤88" },
				{ title: "Step 10", description: "步骤99" },
				{ title: "Step 11", description: "步骤110" },
				{ title: "Step 12", description: "步骤120" },
			],
		};
	},
	methods: {
		nextStep() {
			if (this.active + 1 > this.stepData.length) {
				this.active = 0;
			} else {
				this.active++;
			}
		},
	},
};
</script>

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值