refactor(mobile): 流转树固定100%比例,移除双指缩放功能
- 移除 movable-view 的 :scale/:scale-min/:scale-max/:scale-value 属性 - 移除 onScale/zoomIn/zoomOut 方法及工具栏缩放按钮 - 移除顶部双指缩放·单指拖拽提示文字 - fitAll 简化为纯居中定位 (mvX/mvY),不再调整缩放比 - 工具栏仅保留: 返回 | 流转蓝图 | 卡片切换 | 全览
This commit is contained in:
@ -3,16 +3,10 @@
|
|||||||
<view class="tc-toolbar">
|
<view class="tc-toolbar">
|
||||||
<view class="tc-toolbar-left">
|
<view class="tc-toolbar-left">
|
||||||
<view class="tc-back-btn" @tap="$emit('back')">← 返回</view>
|
<view class="tc-back-btn" @tap="$emit('back')">← 返回</view>
|
||||||
<view style="display:flex;flex-direction:column;gap:2px;">
|
<text class="tc-title">🌳 流转蓝图</text>
|
||||||
<text class="tc-title">🌳 流转蓝图</text>
|
|
||||||
<text class="tc-hint">双指缩放 · 单指拖拽</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
<view class="tc-toolbar-right">
|
<view class="tc-toolbar-right">
|
||||||
<view class="tc-zoom-btn tc-mode-btn" @tap="$emit('swipe')">📇 卡片</view>
|
<view class="tc-zoom-btn tc-mode-btn" @tap="$emit('swipe')">📇 卡片</view>
|
||||||
<view class="tc-zoom-btn" @tap="zoomOut">-</view>
|
|
||||||
<text class="tc-zoom-label">{{ Math.round(mvScale * 100) }}%</text>
|
|
||||||
<view class="tc-zoom-btn" @tap="zoomIn">+</view>
|
|
||||||
<view class="tc-zoom-btn tc-fit-btn" @tap="fitAll">⊡ 全览</view>
|
<view class="tc-zoom-btn tc-fit-btn" @tap="fitAll">⊡ 全览</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@ -24,10 +18,9 @@
|
|||||||
<template v-else>
|
<template v-else>
|
||||||
<movable-area class="tree-movable-area" :style="{ width: viewportW + 'px', height: areaHeight + 'px' }">
|
<movable-area class="tree-movable-area" :style="{ width: viewportW + 'px', height: areaHeight + 'px' }">
|
||||||
<movable-view class="tree-movable-view" direction="all"
|
<movable-view class="tree-movable-view" direction="all"
|
||||||
:x="mvX" :y="mvY" :scale="true" :scale-min="0.1" :scale-max="5"
|
:x="mvX" :y="mvY" :style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }"
|
||||||
:scale-value="mvScale" :style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }"
|
|
||||||
:inertia="true" :friction="2" :damping="40"
|
:inertia="true" :friction="2" :damping="40"
|
||||||
@scale="onScale" @change="onChange">
|
@change="onChange">
|
||||||
|
|
||||||
<view class="canvas-inner">
|
<view class="canvas-inner">
|
||||||
<!-- 🔧 连线层 -->
|
<!-- 🔧 连线层 -->
|
||||||
@ -67,16 +60,13 @@ export default {
|
|||||||
emits: ["viewRecords", "back", "swipe"],
|
emits: ["viewRecords", "back", "swipe"],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
mvX: 0, mvY: 0, mvScale: 1,
|
mvX: 0, mvY: 0,
|
||||||
viewportW: 375, viewportH: 600,
|
viewportW: 375, viewportH: 600,
|
||||||
areaHeight: 500,
|
areaHeight: 500,
|
||||||
_scalePending: false,
|
|
||||||
_pendingScale: 1,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
try { const info = uni.getSystemInfoSync(); this.viewportW = info.windowWidth || 375; this.viewportH = info.windowHeight || 600; } catch {}
|
try { const info = uni.getSystemInfoSync(); this.viewportW = info.windowWidth || 375; this.viewportH = info.windowHeight || 600; } catch {}
|
||||||
// 🚀 给 movable-area 明确像素高度,避免 flex:1 导致触摸区域计算不准
|
|
||||||
this.areaHeight = Math.max(this.viewportH - 100, 400);
|
this.areaHeight = Math.max(this.viewportH - 100, 400);
|
||||||
this.$nextTick(() => this.fitAll());
|
this.$nextTick(() => this.fitAll());
|
||||||
},
|
},
|
||||||
@ -208,33 +198,13 @@ export default {
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
canvasWidth() { const ns = this.treeNodes; if (!ns.length) return 400; return Math.max(...ns.map(n => n.x)) + 200; },
|
canvasWidth() { const ns = this.treeNodes; if (!ns.length) return 400; return Math.max(...ns.map(n => n.x)) + 200; },
|
||||||
canvasHeight() { const ns = this.treeNodes; if (!ns.length) return 400; return Math.max(...ns.map(n => n.y)) + 200; },
|
canvasHeight() { const ns = this.treeNodes; if (!ns.length) return 400; return Math.max(...ns.map(n => n.y)) + 200; },
|
||||||
fitScale() {
|
|
||||||
if (!this.treeNodes.length) return 1;
|
|
||||||
return Math.max(0.1, Math.min((this.viewportW - 32) / this.canvasWidth, (this.areaHeight - 16) / this.canvasHeight, 1));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// 🚀 缩放事件节流:用 rAF 限频,避免每帧触发 Vue 响应式更新导致卡顿
|
// 🚀 手势结束后同步位置
|
||||||
onScale(e) {
|
|
||||||
const s = e?.detail?.scale;
|
|
||||||
if (!s) return;
|
|
||||||
this._pendingScale = s;
|
|
||||||
if (!this._scalePending) {
|
|
||||||
this._scalePending = true;
|
|
||||||
// uni-app 环境优先用定时器(兼容小程序),H5 用 rAF
|
|
||||||
const schedule = typeof requestAnimationFrame === 'function' ? requestAnimationFrame : (fn) => setTimeout(fn, 16);
|
|
||||||
schedule(() => {
|
|
||||||
this.mvScale = this._pendingScale;
|
|
||||||
this._scalePending = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 🚀 手势结束后同步位置(仅记录,不回绑 :x/:y 避免干扰原生拖拽)
|
|
||||||
onChange(e) {
|
onChange(e) {
|
||||||
if (e?.detail?.x !== undefined) this.mvX = e.detail.x;
|
if (e?.detail?.x !== undefined) this.mvX = e.detail.x;
|
||||||
if (e?.detail?.y !== undefined) this.mvY = e.detail.y;
|
if (e?.detail?.y !== undefined) this.mvY = e.detail.y;
|
||||||
},
|
},
|
||||||
zoomIn() { this.mvScale = Math.min(5, +(this.mvScale + 0.2).toFixed(1)); },
|
|
||||||
handleNodeTap(node) {
|
handleNodeTap(node) {
|
||||||
if (node.records && node.records.length > 0) {
|
if (node.records && node.records.length > 0) {
|
||||||
this.$emit('viewRecords', node);
|
this.$emit('viewRecords', node);
|
||||||
@ -242,18 +212,14 @@ export default {
|
|||||||
uni.showToast({ title: '当前节点暂无流转记录', icon: 'none' });
|
uni.showToast({ title: '当前节点暂无流转记录', icon: 'none' });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
zoomOut() { this.mvScale = Math.max(0.1, +(this.mvScale - 0.2).toFixed(1)); },
|
// 🚀 全览居中:固定 100% 比例,内容整体在视口中居中
|
||||||
// 🚀 全览居中:缩放至适配 + 主分支水平居中 + 垂直起始位置
|
|
||||||
fitAll() {
|
fitAll() {
|
||||||
const scale = this.fitScale;
|
|
||||||
const canvasW = this.canvasWidth;
|
const canvasW = this.canvasWidth;
|
||||||
const canvasH = this.canvasHeight;
|
const canvasH = this.canvasHeight;
|
||||||
const viewW = this.viewportW;
|
const viewW = this.viewportW;
|
||||||
const viewH = this.areaHeight;
|
const viewH = this.areaHeight;
|
||||||
// 内容整体在视口中居中
|
this.mvX = (viewW - canvasW) / 2;
|
||||||
this.mvScale = scale;
|
this.mvY = (viewH - canvasH) / 2;
|
||||||
this.mvX = (viewW - canvasW * scale) / 2;
|
|
||||||
this.mvY = (viewH - canvasH * scale) / 2;
|
|
||||||
},
|
},
|
||||||
fmtDate(d) { if (!d) return ""; const dt = new Date(d); return (dt.getMonth() + 1) + '/' + dt.getDate(); },
|
fmtDate(d) { if (!d) return ""; const dt = new Date(d); return (dt.getMonth() + 1) + '/' + dt.getDate(); },
|
||||||
statusLabel(s) { const m = { PENDING: "待接收", WIP: "进行中", COMPLETED: "已完成", REJECTED: "已驳回", ARCHIVED: "已入库", CANCELED: "已撤回" }; return m[s] || s; },
|
statusLabel(s) { const m = { PENDING: "待接收", WIP: "进行中", COMPLETED: "已完成", REJECTED: "已驳回", ARCHIVED: "已入库", CANCELED: "已撤回" }; return m[s] || s; },
|
||||||
|
|||||||
Reference in New Issue
Block a user