@shijiu/jsview-vue 2.1.25 → 2.1.200
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/utils/JsViewEngineWidget/JsvFocusBlock.vue +1 -1
- package/utils/JsViewEngineWidget/JsvFocusHub.ts +123 -0
- package/utils/JsViewEngineWidget/JsvFocusManager.js +4 -3
- package/utils/JsViewEngineWidget/MetroWidget/AnimationManager.ts +145 -51
- package/utils/JsViewEngineWidget/MetroWidget/ListWidget.vue +51 -64
- package/utils/JsViewEngineWidget/MetroWidget/MetroWidget.vue +62 -71
- package/utils/JsViewEngineWidget/MetroWidget/MetroWidgetSetup.js +308 -613
- package/utils/JsViewEngineWidget/TemplateParser/CommonMetroTemplate.ts +127 -247
- package/utils/JsViewEngineWidget/TemplateParser/ListMetroTemplate.ts +1 -0
- package/utils/JsViewEngineWidget/TemplateParser/MetroTemplate.ts +36 -2
- package/utils/JsViewEngineWidget/WidgetCommon.ts +25 -6
- package/utils/JsViewPlugin/JsvAudio/{JsvAudioBrowser.vue → BrowserAudio/BrowserAudio.vue} +1 -1
- package/utils/JsViewPlugin/JsvAudio/index.js +1 -1
- package/utils/JsViewPlugin/JsvLatex/BrowserDomBuilder.js +37 -0
- package/utils/JsViewPlugin/JsvLatex/Color.ts +43 -0
- package/utils/JsViewPlugin/JsvLatex/JsvLatex.vue +159 -0
- package/utils/JsViewPlugin/JsvLatex/JsvLatexBridgeProxy.js +16 -0
- package/utils/JsViewPlugin/JsvLatex/JsvLatexBrowser.vue +59 -0
- package/utils/JsViewPlugin/JsvLatex/PluginLoader.js +171 -0
- package/utils/JsViewPlugin/JsvLatex/index.js +28 -0
- package/utils/JsViewPlugin/JsvLatex/mathjax-tex-svg.txt +1 -0
- package/utils/JsViewPlugin/JsvLatex/version.js +24 -0
- package/utils/JsViewPlugin/JsvLatex/version.mjs +24 -0
- package/utils/JsViewPlugin/index.js +2 -1
- package/utils/JsViewVueTools/JsvDynamicCssStyle.js +2 -52
- package/utils/JsViewVueTools/JsvPerformance.ts +11 -0
- package/utils/JsViewVueTools/JsvPreDownloader.ts +55 -11
- package/utils/JsViewVueTools/JsvStyleSheetsDeclarer.js +40 -0
- package/utils/JsViewVueTools/JsvTextureStore/CanvasTexture/CanvasTexture.ts +143 -0
- package/utils/JsViewVueTools/JsvTextureStore/CanvasTexture/CommandList.ts +24 -0
- package/utils/JsViewVueTools/JsvTextureStore/CanvasTexture/Path.ts +198 -0
- package/utils/JsViewVueTools/JsvTextureStore/JsvTextureStore.ts +31 -0
- package/utils/JsViewVueTools/JsvTextureStore/Store.ts +32 -0
- package/utils/JsViewVueTools/JsvTextureStore/Texture.ts +38 -0
- package/utils/JsViewVueTools/index.js +3 -0
- package/utils/JsViewVueWidget/JsvEnableRenderBreak.vue +17 -0
- package/utils/JsViewVueWidget/JsvFreeMoveActor/ActionRefObject.ts +6 -0
- package/utils/JsViewVueWidget/JsvFreeMoveActor/ActorControl.ts +144 -0
- package/utils/JsViewVueWidget/JsvFreeMoveActor/ActorState.ts +6 -0
- package/utils/JsViewVueWidget/JsvFreeMoveActor/{CallbackManager.js → CallbackManager.ts} +19 -10
- package/utils/JsViewVueWidget/JsvFreeMoveActor/ForgeTypeDefine.ts +45 -0
- package/utils/JsViewVueWidget/JsvFreeMoveActor/FreeMoveActor.vue +1 -1
- package/utils/JsViewVueWidget/JsvFreeMoveActor/JsvEnvBlocker.vue +124 -0
- package/utils/JsViewVueWidget/JsvFreeMoveActor/KeepFlags.ts +6 -0
- package/utils/JsViewVueWidget/JsvFreeMoveActor/SetAction.ts +553 -0
- package/utils/JsViewVueWidget/JsvFreeMoveActor/SetCondition.ts +138 -0
- package/utils/JsViewVueWidget/JsvFreeMoveActor/SetState.ts +53 -0
- package/utils/JsViewVueWidget/JsvFreeMoveActor/index.js +11 -1
- package/utils/JsViewVueWidget/JsvHole.js +1 -1
- package/utils/JsViewVueWidget/JsvLine/JsvLine.vue +101 -0
- package/utils/JsViewVueWidget/JsvLine/LineManager.js +62 -0
- package/utils/JsViewVueWidget/JsvLine/index.js +3 -0
- package/utils/JsViewVueWidget/JsvMarquee.vue +316 -139
- package/utils/JsViewVueWidget/JsvMindMap/CommonType.ts +1 -0
- package/utils/JsViewVueWidget/JsvMindMap/Constant.ts +20 -0
- package/utils/JsViewVueWidget/JsvMindMap/DataTree.ts +394 -0
- package/utils/JsViewVueWidget/JsvMindMap/Geometry.ts +277 -0
- package/utils/JsViewVueWidget/JsvMindMap/JsvMindMap.vue +653 -0
- package/utils/JsViewVueWidget/JsvMindMap/index.js +1 -0
- package/utils/JsViewVueWidget/JsvMindMap/rtree.js +628 -0
- package/utils/JsViewVueWidget/JsvNinePatch.vue +2 -2
- package/utils/JsViewVueWidget/JsvPieChart.vue +124 -0
- package/utils/JsViewVueWidget/JsvPosterImage.vue +32 -9
- package/utils/JsViewVueWidget/JsvPreload/BrowserPreload.vue +135 -133
- package/utils/JsViewVueWidget/JsvPreload/JsvPreload.vue +273 -270
- package/utils/JsViewVueWidget/JsvSector.vue +107 -0
- package/utils/JsViewVueWidget/JsvTextBox.vue +14 -1
- package/utils/JsViewVueWidget/JsvTextureAnim/JsvTextureAnim.vue +28 -2
- package/utils/JsViewVueWidget/JsvVisibleSensor/JsvVisibleSensor.vue +122 -93
- package/utils/JsViewVueWidget/index.js +15 -7
- package/utils/JsViewVueWidget/JsvFreeMoveActor/ActorControl.js +0 -112
- package/utils/JsViewVueWidget/JsvFreeMoveActor/CommonTools.js +0 -18
- package/utils/JsViewVueWidget/JsvFreeMoveActor/SetAction.js +0 -216
- package/utils/JsViewVueWidget/JsvFreeMoveActor/SetCondition.js +0 -66
- package/utils/JsViewVueWidget/JsvFreeMoveActor/SetState.js +0 -38
- package/utils/JsViewVueWidget/JsvFreeMoveActor/TypeDefine.js +0 -12
- package/utils/JsViewVueWidget/JsvTouchContainer.vue +0 -183
- package/utils/JsViewVueWidget/JsvTransparentDiv.vue +0 -87
- /package/utils/{JsViewVueWidget → JsViewPlugin/JsvAudio/BrowserAudio}/JsvSystemAudio.vue +0 -0
|
@@ -0,0 +1,653 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
* @Author: ChenChanghua
|
|
3
|
+
* @Date: 2023-11-06 16:08:58
|
|
4
|
+
* @Description: file content
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
<!--
|
|
8
|
+
[界面概述]
|
|
9
|
+
思维导图控件
|
|
10
|
+
|
|
11
|
+
[控件介绍]
|
|
12
|
+
JsvMindMap:
|
|
13
|
+
props:
|
|
14
|
+
name: {string} 焦点name
|
|
15
|
+
provideData: {function} 提供数据的函数
|
|
16
|
+
width: {number} 组件宽
|
|
17
|
+
height: {number} 组件高
|
|
18
|
+
formatNode: {function} 接受数据, 返回标准格式节点信息的函数
|
|
19
|
+
{
|
|
20
|
+
left: number, left(必需)
|
|
21
|
+
top: number, top(必需)
|
|
22
|
+
width: number, width(必需)
|
|
23
|
+
height: number, height(必需)
|
|
24
|
+
id: string, 节点的唯一id(必需)
|
|
25
|
+
childrenList: Array<any>, 子数据列表
|
|
26
|
+
moveType: "star" | "layer" | "inherit", 节点布局类型(可选)
|
|
27
|
+
lineStyle: object 此节点线的style
|
|
28
|
+
{
|
|
29
|
+
lineWidth: number, 线宽
|
|
30
|
+
backgroundColor: string, 线的颜色(与backgroundImage互斥)
|
|
31
|
+
backgroundImage: string, 线的图片(与backgroundColor互斥)
|
|
32
|
+
borderRadius: string | number, 线的圆角设置
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
left: {number} 组件left
|
|
36
|
+
top: {number} 组件top
|
|
37
|
+
slideAnim: {object} 滚动动画设置, { duration: number, easing?: string }, duration为毫秒
|
|
38
|
+
onEdge: {function} 到达边缘回调
|
|
39
|
+
focusRectPadding: {number} 焦点区域内边距
|
|
40
|
+
renderRectMargin: {number} 描画区域外边距
|
|
41
|
+
methods:
|
|
42
|
+
getCurFocusNode
|
|
43
|
+
@description 获取当前焦点的数据
|
|
44
|
+
@return {object} 当前焦点节点的数据
|
|
45
|
+
getCurFocusId
|
|
46
|
+
@description 获取当前焦点的id
|
|
47
|
+
@return {string} 当前节点的id
|
|
48
|
+
getCurVisibleRect
|
|
49
|
+
@description 获取当前的可视区域
|
|
50
|
+
@return {object} 可视区域{x: number, y: number, width: number, height: number}
|
|
51
|
+
slideTo
|
|
52
|
+
@description 滚动到指定位置
|
|
53
|
+
@params {int} x x坐标
|
|
54
|
+
@params {int} y y坐标
|
|
55
|
+
@params {object} animInfo 动画设置, 为null时不做动画. {duration: number, easing: string, onEnd: function}
|
|
56
|
+
setFocusTo
|
|
57
|
+
@description 设置焦点
|
|
58
|
+
@params {string} id 目标节点的id
|
|
59
|
+
@params {boolean} needSlide 是否需要滚动
|
|
60
|
+
@params {object} animInfo 滚动动画设置
|
|
61
|
+
moveInItem
|
|
62
|
+
@description 把指定节点移入焦点区域
|
|
63
|
+
@params {string} id 目标节点的id
|
|
64
|
+
@params {object} animInfo 动画信息
|
|
65
|
+
moveFocus
|
|
66
|
+
@description 往指定方向移动焦点
|
|
67
|
+
@params {string} direction 移动方向 "left" | "top" | "right" | "bottom"
|
|
68
|
+
getEntireTreeBoundingSize
|
|
69
|
+
@description 获取整个树的包围框宽高
|
|
70
|
+
@return {object} 宽高 {width: number, height: number}
|
|
71
|
+
refreshData
|
|
72
|
+
@description 刷新数据
|
|
73
|
+
|
|
74
|
+
[额外说明]
|
|
75
|
+
1. 焦点移动逻辑
|
|
76
|
+
每个节点首先有个父方向, 若父节点是star模式, 则父方向由父节点相对位置决定, 若父节点是layer模式, 则父方向为父节点子方向的反向
|
|
77
|
+
layer模式的节点, 有一个子方向, 其子方向是所有子所在的一侧方向(只有上下左右四侧), 若无子, 则父方向的反向为子方向. 确定了父方向和子方向后, 剩余两个方向则寻找该方向最近的兄弟节点, 若无则寻找最近分支上最近的同级或者叶节点
|
|
78
|
+
star模式的节点, 除了父方向, 剩余三个方向均寻找相应方向的最近子节点, 若未找到则按layer寻找兄弟节点相同的逻辑寻找同级节点
|
|
79
|
+
-->
|
|
80
|
+
<script setup>
|
|
81
|
+
import { ref, shallowRef, reactive } from "vue";
|
|
82
|
+
import { DataNode } from "./DataTree";
|
|
83
|
+
import { Rect, opposite } from "./Geometry";
|
|
84
|
+
import RBush from "./rtree";
|
|
85
|
+
import { EdgeDirection } from "../../JsViewEngineWidget/WidgetCommon";
|
|
86
|
+
import JsvLine from "../JsvLine";
|
|
87
|
+
|
|
88
|
+
const TAG = "JsvMindMap";
|
|
89
|
+
const dirMap = {
|
|
90
|
+
37: "left",
|
|
91
|
+
38: "top",
|
|
92
|
+
39: "right",
|
|
93
|
+
40: "bottom",
|
|
94
|
+
};
|
|
95
|
+
const edgeDirectionMap = {
|
|
96
|
+
37: EdgeDirection.left,
|
|
97
|
+
38: EdgeDirection.top,
|
|
98
|
+
39: EdgeDirection.right,
|
|
99
|
+
40: EdgeDirection.bottom,
|
|
100
|
+
};
|
|
101
|
+
const dirToKeyCode = {
|
|
102
|
+
left: 37,
|
|
103
|
+
top: 38,
|
|
104
|
+
right: 39,
|
|
105
|
+
bottom: 40,
|
|
106
|
+
};
|
|
107
|
+
const defaultLineStyle = {
|
|
108
|
+
lineWidth: 5,
|
|
109
|
+
backgroundColor: "#000000",
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const props = defineProps({
|
|
113
|
+
name: {
|
|
114
|
+
//组件的焦点名
|
|
115
|
+
type: String,
|
|
116
|
+
required: true,
|
|
117
|
+
},
|
|
118
|
+
provideData: {
|
|
119
|
+
//提供数据的回调
|
|
120
|
+
type: Function,
|
|
121
|
+
required: true,
|
|
122
|
+
},
|
|
123
|
+
width: {
|
|
124
|
+
//组件宽
|
|
125
|
+
type: Number,
|
|
126
|
+
required: true,
|
|
127
|
+
},
|
|
128
|
+
height: {
|
|
129
|
+
//组件高
|
|
130
|
+
type: Number,
|
|
131
|
+
required: true,
|
|
132
|
+
},
|
|
133
|
+
formatNode: {
|
|
134
|
+
/**
|
|
135
|
+
* 标准化节点数据的函数, (data, depth) => Object
|
|
136
|
+
* 节点数据格式
|
|
137
|
+
* {
|
|
138
|
+
* left: number, left(必需)
|
|
139
|
+
* top: number, top(必需)
|
|
140
|
+
* width: number, width(必需)
|
|
141
|
+
* height: number, height(必需)
|
|
142
|
+
* childrenList: Array<any>, 子数据列表
|
|
143
|
+
* id: string, 节点的唯一id(必需)
|
|
144
|
+
* moveType: "star" | "layer" | "inherit", 节点布局类型(可选)
|
|
145
|
+
* lineStyle: { 此节点线的style
|
|
146
|
+
* lineWidth: number, 线宽
|
|
147
|
+
* backgroundColor: string, 线的颜色(与backgroundImage互斥)
|
|
148
|
+
* backgroundImage: string, 线的图片(与backgroundColor互斥)
|
|
149
|
+
* borderRadius: string | number, 线的圆角设置
|
|
150
|
+
* }
|
|
151
|
+
* }
|
|
152
|
+
*/
|
|
153
|
+
type: Function,
|
|
154
|
+
required: true,
|
|
155
|
+
},
|
|
156
|
+
left: {
|
|
157
|
+
//组件left
|
|
158
|
+
type: Number,
|
|
159
|
+
default: 0,
|
|
160
|
+
},
|
|
161
|
+
top: {
|
|
162
|
+
//组件top
|
|
163
|
+
type: Number,
|
|
164
|
+
default: 0,
|
|
165
|
+
},
|
|
166
|
+
slideAnim: {
|
|
167
|
+
type: Object,
|
|
168
|
+
},
|
|
169
|
+
onEdge: {
|
|
170
|
+
//组件到达边缘的回调
|
|
171
|
+
type: Function,
|
|
172
|
+
},
|
|
173
|
+
focusRectPadding: {
|
|
174
|
+
//组件区域向内收缩获得的焦点区域, 获焦的节点会移动到该区域内
|
|
175
|
+
type: Number,
|
|
176
|
+
default: 50,
|
|
177
|
+
},
|
|
178
|
+
renderRectMargin: {
|
|
179
|
+
//组件区域向外延展获得的描画区域, 区域内的节点会描画
|
|
180
|
+
type: Number,
|
|
181
|
+
default: 100,
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
let rootNode = null;
|
|
186
|
+
const currentFocusId = ref(0);
|
|
187
|
+
let preFocusNode = null;
|
|
188
|
+
let curFocusNode = null;
|
|
189
|
+
const locateDiv = shallowRef(null);
|
|
190
|
+
const renderNodeList = shallowRef([]);
|
|
191
|
+
const renderLineList = shallowRef([]);
|
|
192
|
+
const widgetWidth = 500;
|
|
193
|
+
const widgetHeight = 500;
|
|
194
|
+
let widgetFocused = false;
|
|
195
|
+
let rTree = null;
|
|
196
|
+
const slideAnimInfo = {
|
|
197
|
+
duration: props.slideAnim?.duration ?? 200,
|
|
198
|
+
easing: props.slideAnim?.easing ?? "",
|
|
199
|
+
onEnd: props.slideAnim?.easing ?? (() => {}),
|
|
200
|
+
};
|
|
201
|
+
const viewPortRect = new Rect(0, 0, widgetWidth, widgetHeight); // 组件的可视范围
|
|
202
|
+
|
|
203
|
+
// 描画范围
|
|
204
|
+
const getRenderRect = () => {
|
|
205
|
+
return new Rect(
|
|
206
|
+
viewPortRect.x - props.renderRectMargin,
|
|
207
|
+
viewPortRect.y - props.renderRectMargin,
|
|
208
|
+
viewPortRect.width + 2 * props.renderRectMargin,
|
|
209
|
+
viewPortRect.height + 2 * props.renderRectMargin
|
|
210
|
+
);
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
//item获焦时移入此范围
|
|
214
|
+
const getFocusRect = () => {
|
|
215
|
+
return new Rect(
|
|
216
|
+
viewPortRect.x + props.focusRectPadding,
|
|
217
|
+
viewPortRect.y + props.focusRectPadding,
|
|
218
|
+
viewPortRect.width - 2 * props.focusRectPadding,
|
|
219
|
+
viewPortRect.height - 2 * props.focusRectPadding
|
|
220
|
+
);
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const buildNode = (root, data) => {
|
|
224
|
+
let depth = 0;
|
|
225
|
+
if (root != null) {
|
|
226
|
+
depth = root.depth + 1;
|
|
227
|
+
}
|
|
228
|
+
const formatedData = props.formatNode(data, root ? root.depth + 1 : 0);
|
|
229
|
+
const dataNode = new DataNode(
|
|
230
|
+
new Rect(
|
|
231
|
+
formatedData.left,
|
|
232
|
+
formatedData.top,
|
|
233
|
+
formatedData.width,
|
|
234
|
+
formatedData.height
|
|
235
|
+
),
|
|
236
|
+
depth,
|
|
237
|
+
formatedData.id,
|
|
238
|
+
data,
|
|
239
|
+
formatedData.moveType ?? "layer"
|
|
240
|
+
);
|
|
241
|
+
dataNode.setLineStyle(formatedData.lineStyle ?? defaultLineStyle);
|
|
242
|
+
if (root == null) {
|
|
243
|
+
rootNode = dataNode;
|
|
244
|
+
} else {
|
|
245
|
+
root.insert(dataNode);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (formatedData.childrenList?.length > 0) {
|
|
249
|
+
for (let child of formatedData.childrenList) {
|
|
250
|
+
buildNode(dataNode, child);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
//构建树
|
|
256
|
+
const buildTree = () => {
|
|
257
|
+
//clean old tree
|
|
258
|
+
rootNode = null;
|
|
259
|
+
rTree = null;
|
|
260
|
+
|
|
261
|
+
//build new tree
|
|
262
|
+
buildNode(null, props.provideData());
|
|
263
|
+
const allNodeList = rootNode.all();
|
|
264
|
+
allNodeList.forEach((i) => i.calculateMoveInfo());
|
|
265
|
+
rTree = new RBush();
|
|
266
|
+
allNodeList.forEach((node) => rTree.insert(node));
|
|
267
|
+
|
|
268
|
+
//线的处理必须在moveInfo计算完成后处理
|
|
269
|
+
for (let i = 1; i < allNodeList.length; ++i) {
|
|
270
|
+
const node = allNodeList[i];
|
|
271
|
+
const parentDir = node.getParentDir();
|
|
272
|
+
const lineStyle = node.parent.getLineStyle();
|
|
273
|
+
const lineObj = {
|
|
274
|
+
startPos: node.parent.rect.getEdge(opposite(parentDir)).center,
|
|
275
|
+
endPos: node.rect.getEdge(parentDir).center,
|
|
276
|
+
idToken: node.parent.idToken + "_" + node.idToken,
|
|
277
|
+
...lineStyle,
|
|
278
|
+
};
|
|
279
|
+
//node显示时, 其向父的连线和向子的连线均要显示, 因此同时往父子addLine
|
|
280
|
+
node.addLine(lineObj);
|
|
281
|
+
node.parent.addLine(lineObj);
|
|
282
|
+
}
|
|
283
|
+
curFocusNode = rootNode;
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
const visibleListStore = {
|
|
287
|
+
_tmpList: [],
|
|
288
|
+
_curList: [],
|
|
289
|
+
_merge(l1, l2) {
|
|
290
|
+
const pre = l1.concat();
|
|
291
|
+
const set = new Set(pre);
|
|
292
|
+
l2.forEach((i) => set.add(i));
|
|
293
|
+
return Array.from(set);
|
|
294
|
+
},
|
|
295
|
+
setCurList(list) {
|
|
296
|
+
const newTmp = this._merge(this._curList, list);
|
|
297
|
+
this._tmpList = this._merge(this._tmpList, newTmp);
|
|
298
|
+
this._curList = list;
|
|
299
|
+
},
|
|
300
|
+
getTmp() {
|
|
301
|
+
return this._tmpList;
|
|
302
|
+
},
|
|
303
|
+
getCur() {
|
|
304
|
+
this._tmpList = this._curList;
|
|
305
|
+
return this._curList;
|
|
306
|
+
},
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
const setFocusNode = (node) => {
|
|
310
|
+
if (!node) return;
|
|
311
|
+
preFocusNode = curFocusNode;
|
|
312
|
+
curFocusNode = node;
|
|
313
|
+
currentFocusId.value = curFocusNode.customerId;
|
|
314
|
+
|
|
315
|
+
//item的onFocus, onBlur
|
|
316
|
+
if (widgetFocused) {
|
|
317
|
+
preFocusNode?.onBlur();
|
|
318
|
+
curFocusNode?.onFocus();
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
//将node移入可视范围
|
|
323
|
+
const moveInNode = (node, animInfo) => {
|
|
324
|
+
if (node) {
|
|
325
|
+
const moveTarget = getMoveInfo(node);
|
|
326
|
+
if (moveTarget) {
|
|
327
|
+
moveTo(moveTarget, animInfo);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
const focusToNext = (keyCode) => {
|
|
333
|
+
const dirStr = dirMap[keyCode];
|
|
334
|
+
const nextNode = curFocusNode.findNode(dirStr);
|
|
335
|
+
if (nextNode) {
|
|
336
|
+
//设置当前焦点node
|
|
337
|
+
setFocusNode(nextNode);
|
|
338
|
+
|
|
339
|
+
//移动动画
|
|
340
|
+
moveInNode(curFocusNode, {
|
|
341
|
+
duration: slideAnimInfo.duration,
|
|
342
|
+
});
|
|
343
|
+
} else {
|
|
344
|
+
//到达边缘
|
|
345
|
+
if (props.onEdge) {
|
|
346
|
+
props.onEdge({
|
|
347
|
+
direction: edgeDirectionMap[keyCode],
|
|
348
|
+
rect: {
|
|
349
|
+
x: curFocusNode?.rect.x ?? 0,
|
|
350
|
+
y: curFocusNode?.rect.y ?? 0,
|
|
351
|
+
width: curFocusNode?.rect.width ?? 0,
|
|
352
|
+
height: curFocusNode?.rect.height ?? 0,
|
|
353
|
+
},
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
const locateDivPos = reactive({
|
|
360
|
+
left: viewPortRect.x,
|
|
361
|
+
top: viewPortRect.y,
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
const moveTo = (info, animInfo) => {
|
|
365
|
+
if (!info) return;
|
|
366
|
+
//info: {left: number, top: number} 新的focusRect的x, y
|
|
367
|
+
if (info.x != viewPortRect.x || info.y != viewPortRect.y) {
|
|
368
|
+
viewPortRect.x = info.x;
|
|
369
|
+
viewPortRect.y = info.y;
|
|
370
|
+
const preLeft = locateDivPos.left,
|
|
371
|
+
preTop = locateDivPos.top;
|
|
372
|
+
locateDivPos.left = -viewPortRect.x;
|
|
373
|
+
locateDivPos.top = -viewPortRect.y;
|
|
374
|
+
|
|
375
|
+
visibleListStore.setCurList(rTree.search(getRenderRect()));
|
|
376
|
+
|
|
377
|
+
if (animInfo && !isNaN(animInfo.duration)) {
|
|
378
|
+
const forgeAnim = new Forge.TranslateAnimation(
|
|
379
|
+
preLeft - locateDivPos.left,
|
|
380
|
+
0,
|
|
381
|
+
preTop - locateDivPos.top,
|
|
382
|
+
0,
|
|
383
|
+
animInfo.duration,
|
|
384
|
+
animInfo.easing
|
|
385
|
+
);
|
|
386
|
+
forgeAnim.SetAnimationListener(
|
|
387
|
+
new Forge.AnimationListener(
|
|
388
|
+
null,
|
|
389
|
+
() => {
|
|
390
|
+
updateVisible(visibleListStore.getCur());
|
|
391
|
+
animInfo.onEnd?.();
|
|
392
|
+
},
|
|
393
|
+
null
|
|
394
|
+
)
|
|
395
|
+
);
|
|
396
|
+
locateDiv.value?.jsvGetProxyView(true).StartAnimation(forgeAnim);
|
|
397
|
+
updateVisible(visibleListStore.getTmp());
|
|
398
|
+
} else {
|
|
399
|
+
updateVisible(visibleListStore.getCur());
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
const getMoveInfo = (node) => {
|
|
405
|
+
const rect = node.rect;
|
|
406
|
+
const rectCenter = rect.center;
|
|
407
|
+
const focusRect = getFocusRect();
|
|
408
|
+
const centerTargetRect = new Rect(
|
|
409
|
+
focusRect.x + Math.round(rect.width / 2),
|
|
410
|
+
focusRect.y + Math.round(rect.height / 2),
|
|
411
|
+
focusRect.width - rect.width,
|
|
412
|
+
focusRect.height - rect.height
|
|
413
|
+
);
|
|
414
|
+
if (!centerTargetRect.containPoint(rectCenter)) {
|
|
415
|
+
//不在可视范围内
|
|
416
|
+
const point = centerTargetRect.getClosestPoint(rectCenter);
|
|
417
|
+
return {
|
|
418
|
+
x: viewPortRect.x + rectCenter.x - point.x,
|
|
419
|
+
y: viewPortRect.y + rectCenter.y - point.y,
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
return null;
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
const updateVisible = (list) => {
|
|
426
|
+
//根据显示区域获取可视item list
|
|
427
|
+
renderNodeList.value = list;
|
|
428
|
+
//更新线的列表
|
|
429
|
+
const lineSet = new Set();
|
|
430
|
+
for (let node of list) {
|
|
431
|
+
let list = node.getLineList();
|
|
432
|
+
for (let line of list) {
|
|
433
|
+
lineSet.add(line);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
renderLineList.value = Array.from(lineSet);
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
const focusBlockOnFocus = () => {
|
|
440
|
+
widgetFocused = true;
|
|
441
|
+
curFocusNode?.onFocus();
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
const focusBlockOnBlur = () => {
|
|
445
|
+
widgetFocused = false;
|
|
446
|
+
curFocusNode?.onBlur();
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
const onKeyDown = (ev) => {
|
|
450
|
+
switch (ev.keyCode) {
|
|
451
|
+
case 37:
|
|
452
|
+
case 38:
|
|
453
|
+
case 39:
|
|
454
|
+
case 40:
|
|
455
|
+
{
|
|
456
|
+
focusToNext(ev.keyCode);
|
|
457
|
+
}
|
|
458
|
+
return true;
|
|
459
|
+
case 13:
|
|
460
|
+
curFocusNode?.onClick();
|
|
461
|
+
return true;
|
|
462
|
+
default:
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
//init
|
|
468
|
+
{
|
|
469
|
+
buildTree();
|
|
470
|
+
//初始焦点放置在屏幕中间
|
|
471
|
+
const center = curFocusNode.rect.center;
|
|
472
|
+
const viewPortPos = {
|
|
473
|
+
x: Math.round(center.x - viewPortRect.width / 2),
|
|
474
|
+
y: Math.round(center.y - viewPortRect.height / 2),
|
|
475
|
+
};
|
|
476
|
+
moveTo(viewPortPos);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// expose methos
|
|
480
|
+
/**
|
|
481
|
+
* 获取当前获焦的节点
|
|
482
|
+
*/
|
|
483
|
+
const getCurFocusId = () => {
|
|
484
|
+
return currentFocusId.value;
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
const getCurFocusNode = () => {
|
|
488
|
+
return curFocusNode.customerData;
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* 获取当前的可视区域
|
|
493
|
+
*/
|
|
494
|
+
const getCurVisibleRect = () => {
|
|
495
|
+
return viewPortRect;
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* 移动到指定位置
|
|
500
|
+
* @param {int} x
|
|
501
|
+
* @param {int} y
|
|
502
|
+
* @param {object} animInfo
|
|
503
|
+
*/
|
|
504
|
+
const slideTo = (x, y, animInfo) => {
|
|
505
|
+
if (isNaN(x) || isNaN(y)) {
|
|
506
|
+
console.warn(TAG, "slideTo failed: x and y must be number.");
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
moveTo({ x, y }, animInfo);
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* 设置焦点
|
|
514
|
+
* @param {string} id 节点id
|
|
515
|
+
* @param {boolean} needSlide 是否要将节点移入可视范围
|
|
516
|
+
* @param {object} animInfo 移动动画
|
|
517
|
+
*/
|
|
518
|
+
const setFocusTo = (id, needSlide = true, animInfo) => {
|
|
519
|
+
const node = rootNode.find((n) => n.customerId == id);
|
|
520
|
+
if (!node) {
|
|
521
|
+
console.warn(TAG, `setFocusTo failed: node ${id} not found.`);
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
setFocusNode(node);
|
|
525
|
+
if (needSlide) {
|
|
526
|
+
moveInNode(node, animInfo);
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* 将制定item移入可视范围, 若已在可视范围, 则不动
|
|
532
|
+
* @param {string} id 节点的id
|
|
533
|
+
* @param {object} animInfo 动画信息
|
|
534
|
+
*/
|
|
535
|
+
const moveInItem = (id, animInfo) => {
|
|
536
|
+
const node = rootNode.find((n) => n.customerId == id);
|
|
537
|
+
if (!node) {
|
|
538
|
+
console.warn(TAG, `moveInItem failed: node ${id} not found.`);
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
moveInNode(node, animInfo);
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* 获取全图的包围框大小
|
|
546
|
+
*/
|
|
547
|
+
const getEntireTreeBoundingSize = () => {
|
|
548
|
+
const data = rTree.data;
|
|
549
|
+
return {
|
|
550
|
+
width: data.maxX - data.minX + 1,
|
|
551
|
+
height: data.maxY - data.minY + 1,
|
|
552
|
+
};
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* 移动焦点
|
|
557
|
+
* @param {string} direction "left" | "top" | "right" | "bottom"
|
|
558
|
+
*/
|
|
559
|
+
const moveFocus = (direction) => {
|
|
560
|
+
if (!direction in dirToKeyCode) {
|
|
561
|
+
console.warn(TAG, "moveFocus failed: undefined direction " + direction);
|
|
562
|
+
}
|
|
563
|
+
focusToNext(dirToKeyCode[direction]);
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* 刷新数据
|
|
568
|
+
*/
|
|
569
|
+
const refreshData = () => {
|
|
570
|
+
buildTree();
|
|
571
|
+
//TODO 刷新后的位置保持
|
|
572
|
+
const center = curFocusNode.rect.center;
|
|
573
|
+
const viewPortPos = {
|
|
574
|
+
x: Math.round(center.x - viewPortRect.width / 2),
|
|
575
|
+
y: Math.round(center.y - viewPortRect.height / 2),
|
|
576
|
+
};
|
|
577
|
+
moveTo(viewPortPos);
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
const exposeObj = {
|
|
581
|
+
getCurFocusNode,
|
|
582
|
+
getCurFocusId,
|
|
583
|
+
getCurVisibleRect,
|
|
584
|
+
slideTo,
|
|
585
|
+
setFocusTo,
|
|
586
|
+
moveInItem,
|
|
587
|
+
moveFocus,
|
|
588
|
+
getEntireTreeBoundingSize,
|
|
589
|
+
refreshData,
|
|
590
|
+
};
|
|
591
|
+
defineExpose(exposeObj);
|
|
592
|
+
|
|
593
|
+
//debug code
|
|
594
|
+
// window.rTree = rTree;
|
|
595
|
+
// window.dataTree = rootNode;
|
|
596
|
+
// window.mindMap = exposeObj;
|
|
597
|
+
</script>
|
|
598
|
+
|
|
599
|
+
<template>
|
|
600
|
+
<div
|
|
601
|
+
:style="{
|
|
602
|
+
left: props.left,
|
|
603
|
+
top: props.top,
|
|
604
|
+
width: viewPortRect.width,
|
|
605
|
+
height: viewPortRect.height,
|
|
606
|
+
overflow: 'hidden',
|
|
607
|
+
}"
|
|
608
|
+
>
|
|
609
|
+
<div
|
|
610
|
+
ref="locateDiv"
|
|
611
|
+
:style="{
|
|
612
|
+
left: locateDivPos.left,
|
|
613
|
+
top: locateDivPos.top,
|
|
614
|
+
width: viewPortRect.width,
|
|
615
|
+
height: viewPortRect.height,
|
|
616
|
+
}"
|
|
617
|
+
>
|
|
618
|
+
<slot name="background"></slot>
|
|
619
|
+
<jsv-focus-block
|
|
620
|
+
autoFocus
|
|
621
|
+
:onAction="{
|
|
622
|
+
onKeyDown,
|
|
623
|
+
onFocus: focusBlockOnFocus,
|
|
624
|
+
onBlur: focusBlockOnBlur,
|
|
625
|
+
}"
|
|
626
|
+
>
|
|
627
|
+
<jsv-line
|
|
628
|
+
v-for="item in renderLineList"
|
|
629
|
+
v-bind="item"
|
|
630
|
+
:key="item.idToken"
|
|
631
|
+
>
|
|
632
|
+
</jsv-line>
|
|
633
|
+
<div
|
|
634
|
+
v-for="item in renderNodeList"
|
|
635
|
+
:key="item.idToken"
|
|
636
|
+
:ref="item.onRef"
|
|
637
|
+
:style="{
|
|
638
|
+
left: item.rect.left,
|
|
639
|
+
top: item.rect.top,
|
|
640
|
+
width: item.rect.width,
|
|
641
|
+
height: item.rect.height,
|
|
642
|
+
}"
|
|
643
|
+
>
|
|
644
|
+
<slot
|
|
645
|
+
:data="item.customerData"
|
|
646
|
+
:handler="exposeObj"
|
|
647
|
+
:onAction="item.getRegister()"
|
|
648
|
+
></slot>
|
|
649
|
+
</div>
|
|
650
|
+
</jsv-focus-block>
|
|
651
|
+
</div>
|
|
652
|
+
</div>
|
|
653
|
+
</template>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as JsvMindMap } from "./JsvMindMap.vue"
|