web-zk-h5/pages/architecture/resettleArchite/resettle1.vue

857 lines
22 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view>
<view class="seach">
<view class="neibox"> 子点位 </view>
<view class="seach_i">
<view class="inputbox" @click="childNodeListVisible = true">
<view class="">
{{ selectedChildNodeName ? selectedChildNodeName : '请选择子点位' }}
</view>
<u-icon name="arrow-right" size="24rpx" color="#005bac"></u-icon>
</view>
</view>
<view class="neibox" @click="popShow = true"> 筛选 </view>
</view>
<view class="mainbox">
<view class="main_bottom">
<view
class="scoll_main"
ref="scrollMain"
@touchstart.prevent="handleTouchStart"
@touchmove.prevent="handleTouchMove"
@touchend="handleTouchEnd"
@dblclick="handleDoubleClick"
:style="containerStyle"
>
<TreeChart
:size="size"
:json="data"
:class="{ landscape: landscape.length }"
@click-node="clickNode"
/>
</view>
</view>
</view>
<u-popup
:show="popShow"
mode="right"
@close="popShow = false"
:closeOnClickOverlay="false"
>
<view class="rightPopup">
<view class="popup_top">
<view
@click="
() => {
getDataList(), (popShow = false)
}
"
>筛选</view
>
<view class="top_red" @click="popShow = false">{{ '返回' }}</view>
</view>
<view class="typesBox">
<view class="typeTitle" @click="stageListVisible = true"> 阶段 </view>
<view class="choiceBox">
<view class="inputbox" @click="stageListVisible = true">
<view class="">
{{ stageName ? stageName : '请选择' }}
</view>
<u-icon name="arrow-right" size="24rpx" color="#090000"></u-icon>
</view>
</view>
</view>
<view class="typesBox">
<view class="typeTitle" @click="statusListVisible = true">
状态
</view>
<view class="choiceBox">
<view class="inputbox" @click="statusListVisible = true">
<view class="">
{{ statusName ? statusName : '请选择' }}
</view>
<u-icon name="arrow-right" size="24rpx" color="#090000"></u-icon>
</view>
</view>
</view>
<view class="popup_bottom">
<view class="bottom_btn thebtn1" @click="clearAll">清空筛选条件</view>
<view
class="bottom_btn thebtn2"
@click="
() => {
handleSearch(), (popShow = false)
}
"
>{{ '确定' }}
</view>
</view>
</view>
<u-picker
@cancel="stageListVisible = false"
:show="stageListVisible"
ref="uPicker"
:columns="stageList"
@confirm="stagePickerHandleConfirm"
keyName="label"
></u-picker>
<u-picker
@cancel="statusListVisible = false"
:show="statusListVisible"
ref="uPicker"
:columns="statusList"
@confirm="statusPickerHandleConfirm"
keyName="label"
></u-picker>
</u-popup>
<!-- 子点位选择picker -->
<u-picker
@cancel="childNodeListVisible = false"
:show="childNodeListVisible"
ref="childNodePicker"
:columns="childNodeColumns"
@confirm="childNodePickerHandleConfirm"
keyName="childNode"
></u-picker>
<Eposter
width="750"
height="1334"
:list="list"
backgroundColor="rgb(255, 255, 255)"
@on-success="onSuccess"
ref="Eposter"
>
</Eposter>
</view>
</template>
<script>
import html2canvas from 'html2canvas'
import TreeChart from '@/components/architectures/resettleSO.vue'
import Eposter from '@/components/architectures/Poster.vue'
import * as arc from '@/config/architecture.js'
export default {
components: {
TreeChart,
Eposter,
},
data() {
return {
childNodeList: [],
childNodeColumns: [[]],
childNodeListVisible: false,
selectedChildNodeName: '',
treeData: [],
queryParams: {
childNode: '',
stage: 1,
stageStatus: null,
},
stageList: [
[
{ value: 1, label: '阶段一' },
{ value: 2, label: '阶段二' },
{ value: 3, label: '阶段三' },
],
], //期数
statusList: [
[
{ value: 0, label: '已完成' },
{ value: 1, label: '未完成' },
],
], //期数
popShow: false,
stageListVisible: false,
statusListVisible: false,
stageName: '',
statusName: '',
data: {},
size: 0.5,
landscape: [],
popMould: {},
isPop: false,
touchStartPosition1: {
x: 0,
y: 0,
},
touchStartPosition2: {
x: 0,
y: 0,
},
initialDistance: 0,
initialSize: 0.4, // 记录缩放开始时的初始尺寸
minSize: 0.2, // 最小缩放值
maxSize: 3.0, // 最大缩放值
isZooming: false, // 是否正在缩放
isDragging: false, // 是否正在拖动
dragStartPosition: { x: 0, y: 0 }, // 拖动开始位置
containerTransform: { x: 0, y: 0 }, // 容器变换位置
lastTouchTime: 0, // 上次触摸时间,用于区分点击和拖动
lastTouchPosition: { x: 0, y: 0 }, // 上次触摸位置,用于计算速度
velocity: { x: 0, y: 0 }, // 拖动速度,用于惯性滚动
maxTransform: { x: 1000, y: 1000 }, // 最大变换距离限制
list: [],
}
},
onLoad() {
this.init()
this.getChildList().then(() => {
if (this.queryParams.childNode) {
this.getDataList()
}
})
},
mounted() {
// 初始化容器变换
this.$nextTick(() => {
console.log('组件已挂载')
})
},
computed: {
containerStyle() {
return {
transform: `translate(${this.containerTransform.x}px, ${this.containerTransform.y}px) scale(${this.size})`,
transformOrigin: 'center center',
transition:
this.isDragging || this.isZooming
? 'none'
: 'transform 0.1s ease-out',
}
},
},
methods: {
init() {
this.stageName = this.stageList[0][0].label
// this.statusName = this.statusList[0][0].label
this.queryParams.stage = this.stageList[0][0].value
},
//复制文字
copyText() {
let self = this
let md = self.popMould
let text = `会员编号:${md.memberCode} \n会员姓名${md.name}支付时间:${md.payDate} \n业绩  左区  右区 \n真实新增  ${md.leftRealNewPv}  ${md.rightRealNewPv} \n首购新增  ${md.leftFirstPurchaseAdd}  ${md.rightFirstPurchaseAdd} \n复购新增 ${md.leftRepeatPurchaseSurplus}  ${md.rightRepeatPurchaseSurplus} \n真实累计 ${md.leftRealTotal}  ${md.rightRealTotal} \n首购累计 ${md.leftFirstTotal}  ${md.rightFirstTotal} \n复购累计 ${md.leftRepeatPurchaseTotal}  ${md.rightRepeatPurchaseTotal} \n首购结余 ${md.leftFirstSurplus}  ${md.rightFirstSurplus} \n复购结余 ${md.leftRepeatPurchaseSurplus}  ${md.rightRepeatPurchaseSurplus} \n`
uni.setClipboardData({
data: text,
success: function (res) {
uni.getClipboardData({
success: function (res) {
uni.showToast({
title: '复制成功',
})
},
})
},
})
},
//下载图片
downImage(elClass) {
this.$refs.Eposter.createForElRect(elClass, false)
},
downloadImg() {
let self = this
let element = document.querySelector('.Poster1')
uni.showLoading({
title: '图片保存中',
})
html2canvas(element)
.then(function (canvas) {
let dataURL = canvas.toDataURL('image/jpeg')
let link = document.createElement('a')
link.style.display = 'none'
link.href = dataURL
link.download = 'image.jpg'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
uni.showToast({
icon: 'none',
title: '保存成功',
duration: 2000,
})
uni.hideLoading()
})
.catch(function (error) {
uni.hideLoading()
uni.showModal({
title: '保存失败',
})
})
},
onSuccess(val) {
// console.log('🌈val',val)
this.posterImg = val
this.downloadImg(this.posterImg)
},
handleTouchStart(event) {
console.log('handleTouchStart triggered', event.touches.length)
const touch1 = event.touches[0]
const touch2 = event.touches[1]
const currentTime = Date.now()
if (touch2) {
// 双指触摸 - 缩放模式
this.isZooming = true
this.isDragging = false
this.initialSize = this.size
this.touchStartPosition1 = {
x: touch1.clientX,
y: touch1.clientY,
}
this.touchStartPosition2 = {
x: touch2.clientX,
y: touch2.clientY,
}
this.initialDistance = Math.hypot(
touch2.clientX - touch1.clientX,
touch2.clientY - touch1.clientY
)
} else {
// 单指触摸 - 拖动模式
this.isZooming = false
this.isDragging = true
this.dragStartPosition = {
x: touch1.clientX,
y: touch1.clientY,
}
this.lastTouchTime = currentTime
}
},
handleTouchMove(event) {
const touch1 = event.touches[0]
const touch2 = event.touches[1]
if (touch2 && this.isZooming && this.initialDistance > 0) {
// 双指缩放处理
const currentDistance = Math.hypot(
touch2.clientX - touch1.clientX,
touch2.clientY - touch1.clientY
)
const scale = currentDistance / this.initialDistance
let newSize = this.initialSize * scale
// 应用边界限制
newSize = Math.max(this.minSize, Math.min(this.maxSize, newSize))
// 平滑缩放
const sizeDiff = newSize - this.size
this.size = this.size + sizeDiff * 0.3
} else if (!touch2 && this.isDragging) {
// 单指拖动处理
const deltaX = touch1.clientX - this.dragStartPosition.x
const deltaY = touch1.clientY - this.dragStartPosition.y
console.log('拖动中', {
deltaX,
deltaY,
touch: { x: touch1.clientX, y: touch1.clientY },
dragStart: this.dragStartPosition,
containerTransform: this.containerTransform,
})
// 计算拖动速度
this.velocity.x = deltaX * 0.1
this.velocity.y = deltaY * 0.1
// 更新容器变换位置(添加阻尼效果)
let newX = this.containerTransform.x + deltaX * 0.8
let newY = this.containerTransform.y + deltaY * 0.8
// 应用边界限制(根据缩放调整边界)
const scaledMaxX = this.maxTransform.x * this.size
const scaledMaxY = this.maxTransform.y * this.size
newX = Math.max(-scaledMaxX, Math.min(scaledMaxX, newX))
newY = Math.max(-scaledMaxY, Math.min(scaledMaxY, newY))
this.containerTransform.x = newX
this.containerTransform.y = newY
// 更新拖动起始位置
this.dragStartPosition.x = touch1.clientX
this.dragStartPosition.y = touch1.clientY
// 应用变换到DOM元素
this.updateContainerTransform()
}
},
handleTouchEnd(event) {
const currentTime = Date.now()
const touchDuration = currentTime - this.lastTouchTime
// 如果是拖动结束,启动惯性滚动
if (
this.isDragging &&
(Math.abs(this.velocity.x) > 1 || Math.abs(this.velocity.y) > 1)
) {
this.startInertiaScroll()
}
// 重置触摸状态
this.isZooming = false
this.isDragging = false
this.touchStartPosition1 = { x: 0, y: 0 }
this.touchStartPosition2 = { x: 0, y: 0 }
this.initialDistance = 0
this.initialSize = this.size
this.dragStartPosition = { x: 0, y: 0 }
},
// 惯性滚动
startInertiaScroll() {
const friction = 0.95 // 摩擦系数
const minVelocity = 0.1 // 最小速度阈值
const animateInertia = () => {
// 应用速度到位置
let newX = this.containerTransform.x + this.velocity.x
let newY = this.containerTransform.y + this.velocity.y
// 应用边界限制
const scaledMaxX = this.maxTransform.x * this.size
const scaledMaxY = this.maxTransform.y * this.size
newX = Math.max(-scaledMaxX, Math.min(scaledMaxX, newX))
newY = Math.max(-scaledMaxY, Math.min(scaledMaxY, newY))
this.containerTransform.x = newX
this.containerTransform.y = newY
// 减少速度
this.velocity.x *= friction
this.velocity.y *= friction
// 更新DOM
this.updateContainerTransform()
// 继续动画或停止
if (
Math.abs(this.velocity.x) > minVelocity ||
Math.abs(this.velocity.y) > minVelocity
) {
requestAnimationFrame(animateInertia)
}
}
requestAnimationFrame(animateInertia)
},
// 更新容器变换 - 现在使用响应式样式,这个方法可以简化
updateContainerTransform() {
// 使用响应式计算属性无需手动更新DOM
console.log('容器变换更新', this.containerTransform, this.size)
},
// 重置缩放和位置到默认状态
resetZoom() {
this.size = 0.4
this.initialSize = 0.4
this.containerTransform = { x: 0, y: 0 }
this.updateContainerTransform()
},
// 双击重置缩放和位置
handleDoubleClick() {
this.resetZoom()
},
clickNode(e) {
return
},
stagePickerHandleConfirm(e) {
this.queryParams.memberSettlePeriodId = e.value[0].value
this.stageName = e.value[0].label
this.stageListVisible = false
},
statusPickerHandleConfirm(e) {
this.queryParams.stageStatus = e.value[0].value
this.statusName = e.value[0].label
this.statusListVisible = false
},
childNodePickerHandleConfirm(e) {
this.queryParams.childNode = e.value[0].childNode
this.selectedChildNodeName = e.value[0].childNode
this.childNodeListVisible = false
// 选择子点位后自动获取数据
this.getDataList()
},
getChildList() {
return new Promise((resolve, reject) => {
arc.getChildList(this.queryParams).then(res => {
if (res.code === 200) {
this.childNodeList = res.data
// 格式化为picker需要的数据格式
this.childNodeColumns = [res.data]
this.queryParams.childNode = res.data[0]?.childNode
this.selectedChildNodeName = res.data[0]?.childNode
resolve()
}
})
})
},
getDataList() {
arc.getAzFramework(this.queryParams).then(res => {
this.data = res.data[0]
})
},
handleSearch() {
this.getChildList().then(() => {
if (this.childNodeList.length > 0) {
this.queryParams.childNode = this.childNodeList[0].childNode
this.getDataList()
} else {
uni.showToast({
title: '当前筛选条件无数据',
icon: 'none',
})
this.data = {}
}
})
},
clearAll() {
this.popShow = false
this.init()
this.queryParams = {
stage: this.stageList[0][0].value,
stageStatus: this.statusList[0][0].value,
}
// this.handleSearch()
},
},
}
</script>
<style lang="scss" scoped>
.ispop_box {
padding: 27rpx;
padding-top: 60rpx;
width: 670rpx;
font-size: 24rpx;
color: #333333;
.pop_top {
display: flex;
justify-content: space-between;
align-items: center;
.poster1 {
height: 88rpx;
width: 88rpx;
}
.poster2 {
width: 88rpx;
height: 54rpx;
border-radius: 10rpx;
margin-left: 20rpx;
}
.top_right {
display: flex;
flex-direction: column;
align-items: center;
}
}
.pop_center {
.center_flex {
display: flex;
align-items: center;
margin-top: 20rpx;
.c1 {
width: 20%;
}
.c2 {
width: 80%;
display: flex;
justify-content: center;
align-items: center;
background: #f3f3f3;
border: 2rpx solid #eeeeee;
border-radius: 8rpx;
padding: 10rpx 0;
}
}
}
.pop_bottom {
margin: 20rpx 0;
border-top: 2rpx solid #eeeeee;
border-top: 2rpx solid #eeeeee;
.b_flex {
display: flex;
.bt1 {
display: flex;
justify-content: center;
align-items: center;
margin-top: 22rpx;
flex: 1;
}
.bt2 {
display: flex;
justify-content: center;
align-items: center;
margin-top: 22rpx;
flex: 1;
background: #f3f3f3;
border: 2rpx solid #eeeeee;
border-radius: 8rpx;
padding: 12rpx 0;
margin: 11rpx 18rpx;
}
}
}
.btn_box {
display: flex;
align-items: center;
justify-content: space-between;
.small-btn {
width: 312rpx;
height: 72rpx;
background: #005bac;
border-radius: 34rpx;
font-size: 28rpx;
font-weight: 400;
color: #ffffff;
display: flex;
justify-content: center;
align-items: center;
}
.small-text-btn {
width: 312rpx;
height: 72rpx;
border: 1px solid #005bac;
border-radius: 34rpx;
display: flex;
justify-content: center;
align-items: center;
font-size: 28rpx;
color: #005bac;
}
}
}
.operate-btnboxs {
padding: 29rpx 26rpx;
background-color: #ffffff;
.d-c-c {
display: flex;
align-items: center;
justify-content: center;
.operate-btn {
width: 64rpx;
height: 64rpx;
margin: 5rpx;
// background: #F2F2F2;
border-radius: 5rpx;
padding: 0;
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
}
}
}
.rightPopup {
width: 645rpx;
height: 100%;
overflow: auto;
.popup_top {
padding: 25rpx;
background-color: rgba(176, 196, 222, 0.45);
display: flex;
justify-content: space-between;
align-items: center;
font-size: 28rpx;
font-family: Source Han Sans CN;
font-weight: 400;
color: #333333;
.top_red {
color: #005bac;
}
}
.popup_bottom {
display: flex;
align-items: center;
position: absolute;
left: 0;
bottom: 0;
width: 100%;
.bottom_btn {
flex: 1;
padding: 20rpx 0;
text-align: center;
font-size: 28rpx;
}
.thebtn1 {
background-color: rgba(176, 196, 222, 0.45);
color: #333333;
}
.thebtn2 {
background-color: #005bac;
color: #ffffff;
}
}
.typesBox {
margin-top: 40rpx;
.typeTitle {
padding: 0 24rpx;
font-size: 30rpx;
font-family: Source Han Sans CN;
font-weight: bold;
color: #333333;
}
.choiceBox {
padding: 0 12rpx;
display: flex;
margin-top: 17rpx;
align-items: center;
flex-wrap: wrap;
.inputbox {
font-size: 26rpx;
width: 100%;
padding: 20rpx 32rpx;
border-radius: 20rpx;
background-color: rgba(176, 196, 222, 0.45);
display: flex;
justify-content: space-between;
align-items: center;
}
.flex_btn {
background-color: rgba(176, 196, 222, 0.45);
display: flex;
align-items: center;
justify-content: center;
padding: 14rpx 48rpx;
border-radius: 26rpx;
font-size: 24rpx;
font-family: Source Han Sans CN;
font-weight: 400;
color: #333333;
margin: 17rpx 5rpx;
}
.timesbtn {
flex: 1;
}
.selectbtn {
background-color: #005bac;
color: #ffffff;
}
}
}
}
.seach {
background: #fff;
overflow: hidden;
padding: 20rpx 23rpx;
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
border-bottom: 2rpx solid #eee;
.these {
border: none;
padding: 10rpx 0 !important;
}
.seach_i {
padding: 0 20rpx;
border-radius: 34rpx;
background: #fff;
flex: 1;
background: #f5f6f8;
margin: 0 20rpx;
.inputbox {
font-size: 26rpx;
width: 100%;
padding: 15rpx 20rpx;
border-radius: 34rpx;
background-color: #f5f6f8;
display: flex;
justify-content: space-between;
align-items: center;
color: #333333;
&:active {
background-color: #e8e9eb;
}
}
}
.neibox {
display: flex;
align-items: center;
font-size: 26rpx;
font-family: PingFang SC;
font-weight: 400;
color: #999999;
}
.seatch_r {
background: #005bac;
border-radius: 50%;
padding: 8rpx;
}
}
.mainbox {
padding: 26rpx 22rpx;
height: 100%;
.main_bottom {
width: 100%;
height: 100%;
margin-top: 25rpx;
background-color: #ffffff;
padding: 38rpx 0;
border-radius: 20rpx;
}
.scoll_main {
width: 700rpx;
transform-origin: center center; // 设置变换原点为中心
cursor: grab; // 显示可拖动光标
&:active {
cursor: grabbing; // 拖动时的光标
}
}
}
</style>