增加进件功能

This commit is contained in:
2026-01-09 18:52:26 +08:00
parent 6c08b3b878
commit 684014e183
28 changed files with 4765 additions and 67 deletions

View 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>