feat(resettle2): 树结构改为列表展示
This commit is contained in:
parent
703a76a0df
commit
e035e591da
|
|
@ -192,48 +192,54 @@
|
||||||
</view>
|
</view>
|
||||||
</u-popup>
|
</u-popup>
|
||||||
|
|
||||||
<!-- 树形图弹窗 -->
|
<!-- 会员列表弹窗 -->
|
||||||
<u-popup
|
<u-popup
|
||||||
:show="showTreePopup"
|
:show="showMemberListPopup"
|
||||||
mode="center"
|
mode="center"
|
||||||
@close="showTreePopup = false"
|
@close="showMemberListPopup = false"
|
||||||
:closeOnClickOverlay="true"
|
:closeOnClickOverlay="true"
|
||||||
borderRadius="20"
|
borderRadius="20"
|
||||||
width="96"
|
width="90%"
|
||||||
height="80%"
|
height="70%"
|
||||||
>
|
>
|
||||||
<view class="tree-popup">
|
<view class="member-list-popup">
|
||||||
<view class="tree-popup-header">
|
<view class="popup-header">
|
||||||
<text class="popup-title">点位详情</text>
|
<text class="popup-title">点位信息</text>
|
||||||
<u-icon
|
<u-icon
|
||||||
name="close"
|
name="close"
|
||||||
@click="showTreePopup = false"
|
@click="showMemberListPopup = false"
|
||||||
size="32rpx"
|
size="32rpx"
|
||||||
></u-icon>
|
></u-icon>
|
||||||
</view>
|
</view>
|
||||||
<view class="tree-popup-content">
|
<view class="popup-content">
|
||||||
<view v-if="treeLoading" class="loading-tree">
|
<view v-if="memberListLoading" class="loading-list">
|
||||||
<u-loading-icon mode="spinner"></u-loading-icon>
|
<u-loading-icon mode="spinner"></u-loading-icon>
|
||||||
<text>加载中...</text>
|
<text>加载中...</text>
|
||||||
</view>
|
</view>
|
||||||
<view v-else class="tree-container">
|
<scroll-view v-else class="member-scroll-list" scroll-y="true">
|
||||||
<view
|
<view v-if="memberListData.length > 0" class="member-list">
|
||||||
class="tree-scroll-main"
|
<view
|
||||||
ref="treeScrollMain"
|
v-for="(item, index) in memberListData"
|
||||||
@touchstart.prevent="handleTreeTouchStart"
|
:key="index"
|
||||||
@touchmove.prevent="handleTreeTouchMove"
|
class="member-item"
|
||||||
@touchend="handleTreeTouchEnd"
|
>
|
||||||
@dblclick="handleTreeDoubleClick"
|
<view v-if="item.memberCode" class="member-info">
|
||||||
:style="treeContainerStyle"
|
<text class="member-code">{{ item.point }}</text>
|
||||||
>
|
<text class="member-code">{{
|
||||||
<TreeChart
|
`${item.memberCode}-${item.stage}-${item.stageSort}(${item.childNode})`
|
||||||
:size="treeSize"
|
}}</text>
|
||||||
:json="treeData"
|
<text class="creation-time">{{ item.creationTime }}</text>
|
||||||
@click-node="clickTreeNode"
|
</view>
|
||||||
:stageName="stageName"
|
<view v-else class="member-info">
|
||||||
/>
|
<text class="member-code">{{ item.point }}</text>
|
||||||
|
<text class="member-code">空点位</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
<view v-else class="empty-list">
|
||||||
|
<u-empty mode="data" text="暂无会员数据"></u-empty>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</u-popup>
|
</u-popup>
|
||||||
|
|
@ -323,21 +329,10 @@ export default {
|
||||||
treeLoading: false,
|
treeLoading: false,
|
||||||
selectedTreeItem: {},
|
selectedTreeItem: {},
|
||||||
|
|
||||||
// 树形图缩放和拖拽相关
|
// 会员列表弹窗相关
|
||||||
treeSize: 0.8,
|
showMemberListPopup: false,
|
||||||
treeIsZooming: false,
|
memberListData: [],
|
||||||
treeIsDragging: false,
|
memberListLoading: false,
|
||||||
treeTouchStartPosition1: { x: 0, y: 0 },
|
|
||||||
treeTouchStartPosition2: { x: 0, y: 0 },
|
|
||||||
treeInitialDistance: 0,
|
|
||||||
treeInitialSize: 0.5,
|
|
||||||
treeMinSize: 0.2,
|
|
||||||
treeMaxSize: 3.0,
|
|
||||||
treeDragStartPosition: { x: 0, y: 0 },
|
|
||||||
treeContainerTransform: { x: 0, y: 0 },
|
|
||||||
treeLastTouchTime: 0,
|
|
||||||
treeVelocity: { x: 0, y: 0 },
|
|
||||||
treeMaxTransform: { x: 1000, y: 1000 },
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -357,19 +352,6 @@ export default {
|
||||||
this.loadMore()
|
this.loadMore()
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
|
||||||
treeContainerStyle() {
|
|
||||||
return {
|
|
||||||
transform: `translate(${this.treeContainerTransform.x}rpx, ${this.treeContainerTransform.y}px) scale(${this.treeSize})`,
|
|
||||||
transformOrigin: 'center center',
|
|
||||||
transition:
|
|
||||||
this.treeIsDragging || this.treeIsZooming
|
|
||||||
? 'none'
|
|
||||||
: 'transform 0.1s ease-out',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
init() {
|
init() {
|
||||||
this.stageName = this.stageOptions[0][0].label
|
this.stageName = this.stageOptions[0][0].label
|
||||||
|
|
@ -584,15 +566,15 @@ export default {
|
||||||
// 处理会员编号点击事件
|
// 处理会员编号点击事件
|
||||||
handleMemberCodeClick(item) {
|
handleMemberCodeClick(item) {
|
||||||
this.selectedTreeItem = item
|
this.selectedTreeItem = item
|
||||||
this.showTreePopup = true
|
this.showMemberListPopup = true
|
||||||
this.loadTreeData(item)
|
this.loadMemberListData(item)
|
||||||
},
|
},
|
||||||
|
|
||||||
// 加载树形图数据
|
// 加载会员列表数据
|
||||||
async loadTreeData(item) {
|
async loadMemberListData(item) {
|
||||||
try {
|
try {
|
||||||
this.treeLoading = true
|
this.memberListLoading = true
|
||||||
this.treeData = {}
|
this.memberListData = []
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
stage: this.queryParams.stage,
|
stage: this.queryParams.stage,
|
||||||
|
|
@ -601,190 +583,24 @@ export default {
|
||||||
|
|
||||||
const res = await arc.getAzFramework(params)
|
const res = await arc.getAzFramework(params)
|
||||||
|
|
||||||
if (res.code === 200 && res.data && res.data.length > 0) {
|
if (res.code === 200 && res.data) {
|
||||||
this.treeData = res.data[0]
|
this.memberListData = res.data
|
||||||
} else {
|
} else {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: res.msg || '获取树形图数据失败',
|
title: res.msg || '获取会员列表数据失败',
|
||||||
icon: 'none',
|
icon: 'none',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载树形图数据失败:', error)
|
console.error('加载会员列表数据失败:', error)
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '网络异常,请稍后重试',
|
title: '网络异常,请稍后重试',
|
||||||
icon: 'none',
|
icon: 'none',
|
||||||
})
|
})
|
||||||
} finally {
|
} finally {
|
||||||
this.treeLoading = false
|
this.memberListLoading = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 树形图节点点击事件
|
|
||||||
clickTreeNode(e) {
|
|
||||||
// 可以在这里处理树节点的点击事件
|
|
||||||
console.log('树节点点击:', e)
|
|
||||||
},
|
|
||||||
|
|
||||||
// 树形图触摸开始
|
|
||||||
handleTreeTouchStart(event) {
|
|
||||||
const touch1 = event.touches[0]
|
|
||||||
const touch2 = event.touches[1]
|
|
||||||
const currentTime = Date.now()
|
|
||||||
|
|
||||||
if (touch2) {
|
|
||||||
// 双指触摸 - 缩放模式
|
|
||||||
this.treeIsZooming = true
|
|
||||||
this.treeIsDragging = false
|
|
||||||
this.treeInitialSize = this.treeSize
|
|
||||||
|
|
||||||
this.treeTouchStartPosition1 = {
|
|
||||||
x: touch1.clientX,
|
|
||||||
y: touch1.clientY,
|
|
||||||
}
|
|
||||||
this.treeTouchStartPosition2 = {
|
|
||||||
x: touch2.clientX,
|
|
||||||
y: touch2.clientY,
|
|
||||||
}
|
|
||||||
this.treeInitialDistance = Math.hypot(
|
|
||||||
touch2.clientX - touch1.clientX,
|
|
||||||
touch2.clientY - touch1.clientY
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// 单指触摸 - 拖动模式
|
|
||||||
this.treeIsZooming = false
|
|
||||||
this.treeIsDragging = true
|
|
||||||
this.treeDragStartPosition = {
|
|
||||||
x: touch1.clientX,
|
|
||||||
y: touch1.clientY,
|
|
||||||
}
|
|
||||||
this.treeLastTouchTime = currentTime
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 树形图触摸移动
|
|
||||||
handleTreeTouchMove(event) {
|
|
||||||
const touch1 = event.touches[0]
|
|
||||||
const touch2 = event.touches[1]
|
|
||||||
|
|
||||||
if (touch2 && this.treeIsZooming && this.treeInitialDistance > 0) {
|
|
||||||
// 双指缩放处理
|
|
||||||
const currentDistance = Math.hypot(
|
|
||||||
touch2.clientX - touch1.clientX,
|
|
||||||
touch2.clientY - touch1.clientY
|
|
||||||
)
|
|
||||||
|
|
||||||
const scale = currentDistance / this.treeInitialDistance
|
|
||||||
let newSize = this.treeInitialSize * scale
|
|
||||||
|
|
||||||
// 应用边界限制
|
|
||||||
newSize = Math.max(
|
|
||||||
this.treeMinSize,
|
|
||||||
Math.min(this.treeMaxSize, newSize)
|
|
||||||
)
|
|
||||||
|
|
||||||
// 平滑缩放
|
|
||||||
const sizeDiff = newSize - this.treeSize
|
|
||||||
this.treeSize = this.treeSize + sizeDiff * 0.3
|
|
||||||
} else if (!touch2 && this.treeIsDragging) {
|
|
||||||
// 单指拖动处理
|
|
||||||
const deltaX = touch1.clientX - this.treeDragStartPosition.x
|
|
||||||
const deltaY = touch1.clientY - this.treeDragStartPosition.y
|
|
||||||
|
|
||||||
// 计算拖动速度
|
|
||||||
this.treeVelocity.x = deltaX * 0.1
|
|
||||||
this.treeVelocity.y = deltaY * 0.1
|
|
||||||
|
|
||||||
// 更新容器变换位置(添加阻尼效果)
|
|
||||||
let newX = this.treeContainerTransform.x + deltaX * 0.8
|
|
||||||
let newY = this.treeContainerTransform.y + deltaY * 0.8
|
|
||||||
|
|
||||||
// 应用边界限制(根据缩放调整边界)
|
|
||||||
const scaledMaxX = this.treeMaxTransform.x * this.treeSize
|
|
||||||
const scaledMaxY = this.treeMaxTransform.y * this.treeSize
|
|
||||||
|
|
||||||
newX = Math.max(-scaledMaxX, Math.min(scaledMaxX, newX))
|
|
||||||
newY = Math.max(-scaledMaxY, Math.min(scaledMaxY, newY))
|
|
||||||
|
|
||||||
this.treeContainerTransform.x = newX
|
|
||||||
this.treeContainerTransform.y = newY
|
|
||||||
|
|
||||||
// 更新拖动起始位置
|
|
||||||
this.treeDragStartPosition.x = touch1.clientX
|
|
||||||
this.treeDragStartPosition.y = touch1.clientY
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 树形图触摸结束
|
|
||||||
handleTreeTouchEnd(event) {
|
|
||||||
const currentTime = Date.now()
|
|
||||||
const touchDuration = currentTime - this.treeLastTouchTime
|
|
||||||
|
|
||||||
// 如果是拖动结束,启动惯性滚动
|
|
||||||
if (
|
|
||||||
this.treeIsDragging &&
|
|
||||||
(Math.abs(this.treeVelocity.x) > 1 || Math.abs(this.treeVelocity.y) > 1)
|
|
||||||
) {
|
|
||||||
this.startTreeInertiaScroll()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重置触摸状态
|
|
||||||
this.treeIsZooming = false
|
|
||||||
this.treeIsDragging = false
|
|
||||||
this.treeTouchStartPosition1 = { x: 0, y: 0 }
|
|
||||||
this.treeTouchStartPosition2 = { x: 0, y: 0 }
|
|
||||||
this.treeInitialDistance = 0
|
|
||||||
this.treeInitialSize = this.treeSize
|
|
||||||
this.treeDragStartPosition = { x: 0, y: 0 }
|
|
||||||
},
|
|
||||||
|
|
||||||
// 树形图惯性滚动
|
|
||||||
startTreeInertiaScroll() {
|
|
||||||
const friction = 0.95 // 摩擦系数
|
|
||||||
const minVelocity = 0.1 // 最小速度阈值
|
|
||||||
|
|
||||||
const animateInertia = () => {
|
|
||||||
// 应用速度到位置
|
|
||||||
let newX = this.treeContainerTransform.x + this.treeVelocity.x
|
|
||||||
let newY = this.treeContainerTransform.y + this.treeVelocity.y
|
|
||||||
|
|
||||||
// 应用边界限制
|
|
||||||
const scaledMaxX = this.treeMaxTransform.x * this.treeSize
|
|
||||||
const scaledMaxY = this.treeMaxTransform.y * this.treeSize
|
|
||||||
|
|
||||||
newX = Math.max(-scaledMaxX, Math.min(scaledMaxX, newX))
|
|
||||||
newY = Math.max(-scaledMaxY, Math.min(scaledMaxY, newY))
|
|
||||||
|
|
||||||
this.treeContainerTransform.x = newX
|
|
||||||
this.treeContainerTransform.y = newY
|
|
||||||
|
|
||||||
// 减少速度
|
|
||||||
this.treeVelocity.x *= friction
|
|
||||||
this.treeVelocity.y *= friction
|
|
||||||
|
|
||||||
// 继续动画或停止
|
|
||||||
if (
|
|
||||||
Math.abs(this.treeVelocity.x) > minVelocity ||
|
|
||||||
Math.abs(this.treeVelocity.y) > minVelocity
|
|
||||||
) {
|
|
||||||
requestAnimationFrame(animateInertia)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
requestAnimationFrame(animateInertia)
|
|
||||||
},
|
|
||||||
|
|
||||||
// 重置树形图缩放和位置
|
|
||||||
resetTreeZoom() {
|
|
||||||
this.treeSize = 0.5
|
|
||||||
this.treeInitialSize = 0.5
|
|
||||||
this.treeContainerTransform = { x: 0, y: 0 }
|
|
||||||
},
|
|
||||||
|
|
||||||
// 双击重置树形图缩放和位置
|
|
||||||
handleTreeDoubleClick() {
|
|
||||||
this.resetTreeZoom()
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -1255,4 +1071,106 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 会员列表弹窗样式 */
|
||||||
|
.member-list-popup {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.popup-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 30rpx;
|
||||||
|
background: #f5f6f8;
|
||||||
|
border-bottom: 2rpx solid #eee;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
.popup-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-content {
|
||||||
|
flex: 1;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.loading-list {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #999;
|
||||||
|
|
||||||
|
text {
|
||||||
|
margin-left: 12rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.member-scroll-list {
|
||||||
|
width: 100%;
|
||||||
|
height: 80vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.member-list {
|
||||||
|
padding: 20rpx;
|
||||||
|
width: 70vw;
|
||||||
|
.member-item {
|
||||||
|
background: #fff;
|
||||||
|
border: 2rpx solid #f0f0f0;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 24rpx 20rpx;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: #005bac;
|
||||||
|
box-shadow: 0 4rpx 12rpx rgba(0, 91, 172, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.member-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12rpx;
|
||||||
|
|
||||||
|
.member-code {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #005bac;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.creation-time {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #666;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-list {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
padding: 60rpx 20rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue