增加进件功能
This commit is contained in:
180
entryManager/add/components/steps.vue
Normal file
180
entryManager/add/components/steps.vue
Normal file
@@ -0,0 +1,180 @@
|
||||
<template>
|
||||
<scroll-view scroll-x="true" scroll-with-animation :scroll-left="scrollLeft" class="steps-scroll-container"
|
||||
ref="scrollViewRef">
|
||||
<view class="steps-content" ref="contentRef">
|
||||
<view v-for="(item,index) in list" class="step-item" :key="index" :data-index="index" ref="stepItemRefs">
|
||||
<view class="step-inner">
|
||||
<view class="index" :class="{active:index<=cur}">
|
||||
<text>{{index+1}}</text>
|
||||
</view>
|
||||
<view class="step-text color-999" :class="{'color-main':index<=cur}" @click="handleClick(index)">
|
||||
{{item}}
|
||||
</view>
|
||||
</view>
|
||||
<view class="step-arrow" v-if="index!=list.length-1">
|
||||
<up-icon name="arrow-rightward" size="16" :color="isActive(index)?'#318AFE':'#999'"></up-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
ref,
|
||||
nextTick,
|
||||
getCurrentInstance
|
||||
} from 'vue';
|
||||
|
||||
const cur = defineModel({
|
||||
default:0
|
||||
})
|
||||
const scrollLeft = ref(0)
|
||||
const scrollViewRef = ref(null)
|
||||
const contentRef = ref(null) // 内容容器ref
|
||||
const stepItemRefs = ref([]) // 步骤项ref数组
|
||||
const instance = getCurrentInstance()
|
||||
|
||||
function isActive(index) {
|
||||
return cur.value === index
|
||||
}
|
||||
|
||||
// 核心:精准居中计算(基于元素相对于内容容器的偏移)
|
||||
const calcScrollCenter = (index) => {
|
||||
nextTick(async () => {
|
||||
try {
|
||||
const query = uni.createSelectorQuery().in(instance)
|
||||
|
||||
// 1. 获取滚动容器宽度
|
||||
const [scrollViewRect] = await new Promise(resolve => {
|
||||
query.select('.steps-scroll-container').boundingClientRect(rect => resolve([
|
||||
rect
|
||||
])).exec()
|
||||
})
|
||||
const scrollViewWidth = scrollViewRect?.width || 0
|
||||
|
||||
// 2. 获取当前步骤项的布局信息
|
||||
const [itemRect] = await new Promise(resolve => {
|
||||
query.select(`.step-item[data-index="${index}"]`).boundingClientRect(rect =>
|
||||
resolve([rect])).exec()
|
||||
})
|
||||
|
||||
// 3. 获取内容容器的布局信息
|
||||
const [contentRect] = await new Promise(resolve => {
|
||||
query.select('.steps-content').boundingClientRect(rect => resolve([rect]))
|
||||
.exec()
|
||||
})
|
||||
|
||||
if (!itemRect || !contentRect) return
|
||||
|
||||
// 关键修正:元素相对于内容容器的左偏移(而非视口)
|
||||
const itemOffsetLeft = itemRect.left - contentRect.left
|
||||
// 居中公式:滚动距离 = 元素偏移 - (容器宽度/2) + (元素宽度/2)
|
||||
let targetScrollLeft = itemOffsetLeft - (scrollViewWidth / 2) + (itemRect.width / 2)
|
||||
|
||||
// 4. 计算最大可滚动距离(边界限制)
|
||||
const maxScrollLeft = Math.max(0, contentRect.width - scrollViewWidth)
|
||||
// 限制滚动范围,避免超出边界
|
||||
targetScrollLeft = Math.max(0, Math.min(targetScrollLeft, maxScrollLeft))
|
||||
|
||||
// 5. 设置滚动距离(强制整数,避免小数导致的偏移)
|
||||
scrollLeft.value = Math.round(targetScrollLeft)
|
||||
} catch (e) {
|
||||
console.error('计算居中失败:', e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 点击事件
|
||||
const handleClick = (index) => {
|
||||
if (cur.value === index) return
|
||||
cur.value = index
|
||||
calcScrollCenter(index)
|
||||
}
|
||||
|
||||
// 初始化居中
|
||||
nextTick(() => {
|
||||
calcScrollCenter(cur.value)
|
||||
})
|
||||
|
||||
// 步骤列表
|
||||
const list = ref(['基础信息', '法人信息', '营业执照信息', '门店信息', '结算信息'])
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// 滚动容器:固定宽度,隐藏滚动条
|
||||
.steps-scroll-container {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
box-sizing: border-box;
|
||||
overflow-x: auto;
|
||||
|
||||
// 隐藏滚动条(三端兼容)
|
||||
::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
// 内容容器:inline-flex,宽度自适应
|
||||
.steps-content {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 10rpx 0;
|
||||
}
|
||||
|
||||
// 单个步骤项:统一间距和布局
|
||||
.step-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin: 0 10rpx; // 统一间距
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
// 步骤内部布局
|
||||
.step-inner {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
// 数字索引
|
||||
.index {
|
||||
border: 1px solid #999;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 50%;
|
||||
margin-right: 10rpx;
|
||||
color: #999;
|
||||
|
||||
&.active {
|
||||
border-color: $my-main-color;
|
||||
color: $my-main-color;
|
||||
}
|
||||
}
|
||||
|
||||
// 步骤文字
|
||||
.step-text {
|
||||
white-space: nowrap;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
// 箭头容器
|
||||
.step-arrow {
|
||||
padding: 0 10rpx;
|
||||
margin-top: 2rpx;
|
||||
}
|
||||
|
||||
// 样式补充
|
||||
.color-main {
|
||||
color: $my-main-color !important;
|
||||
}
|
||||
|
||||
.color-999 {
|
||||
color: #999;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user