v-nuxt-ui 0.1.35 → 0.2.0
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/dist/module.json +1 -1
- package/dist/module.mjs +1 -0
- package/dist/runtime/components/Watermark.d.vue.ts +3 -3
- package/dist/runtime/components/Watermark.vue.d.ts +3 -3
- package/dist/runtime/components/button/CircleColor.d.vue.ts +17 -0
- package/dist/runtime/components/button/CircleColor.vue +37 -0
- package/dist/runtime/components/button/CircleColor.vue.d.ts +17 -0
- package/dist/runtime/components/button/Dropdown.vue +5 -1
- package/dist/runtime/components/date-picker/Input.d.vue.ts +9 -1
- package/dist/runtime/components/date-picker/Input.vue +55 -11
- package/dist/runtime/components/date-picker/Input.vue.d.ts +9 -1
- package/dist/runtime/components/date-picker/index.vue +3 -3
- package/dist/runtime/components/flow/FlowEdge.client.vue +170 -41
- package/dist/runtime/components/flow/FlowEditor.client.d.vue.ts +51 -0
- package/dist/runtime/components/flow/FlowEditor.client.vue +287 -0
- package/dist/runtime/components/flow/FlowEditor.client.vue.d.ts +51 -0
- package/dist/runtime/components/flow/FlowNode.client.d.vue.ts +13 -2
- package/dist/runtime/components/flow/FlowNode.client.vue +44 -48
- package/dist/runtime/components/flow/FlowNode.client.vue.d.ts +13 -2
- package/dist/runtime/components/flow/FlowToolbar.d.vue.ts +41 -5
- package/dist/runtime/components/flow/FlowToolbar.vue +554 -88
- package/dist/runtime/components/flow/FlowToolbar.vue.d.ts +41 -5
- package/dist/runtime/components/flow/FlowToolbarItemWrapper.d.vue.ts +17 -0
- package/dist/runtime/components/flow/FlowToolbarItemWrapper.vue +16 -0
- package/dist/runtime/components/flow/FlowToolbarItemWrapper.vue.d.ts +17 -0
- package/dist/runtime/components/form/field/DatePicker.vue +1 -1
- package/dist/runtime/components/sys/flow/CreateModal.d.vue.ts +13 -0
- package/dist/runtime/components/sys/flow/CreateModal.vue +32 -0
- package/dist/runtime/components/sys/flow/CreateModal.vue.d.ts +13 -0
- package/dist/runtime/components/sys/flow/EditNodeModal.d.vue.ts +13 -0
- package/dist/runtime/components/sys/flow/EditNodeModal.vue +30 -0
- package/dist/runtime/components/sys/flow/EditNodeModal.vue.d.ts +13 -0
- package/dist/runtime/components/sys/flow/Table.d.vue.ts +3 -0
- package/dist/runtime/components/sys/flow/Table.vue +103 -0
- package/dist/runtime/components/sys/flow/Table.vue.d.ts +3 -0
- package/dist/runtime/components/sys/table/CreateModal.vue +9 -193
- package/dist/runtime/components/sys/table/Table.vue +6 -15
- package/dist/runtime/components/sys/table/TableColumnList.d.vue.ts +54 -0
- package/dist/runtime/components/sys/table/TableColumnList.vue +196 -0
- package/dist/runtime/components/sys/table/TableColumnList.vue.d.ts +54 -0
- package/dist/runtime/components/sys/table/TableColumnModal.d.vue.ts +3 -13
- package/dist/runtime/components/sys/table/TableColumnModal.vue +32 -100
- package/dist/runtime/components/sys/table/TableColumnModal.vue.d.ts +3 -13
- package/dist/runtime/components/sys/user/Table.vue +44 -27
- package/dist/runtime/components/table/permission/TablePermissionConfig.d.vue.ts +2 -2
- package/dist/runtime/components/table/permission/TablePermissionConfig.vue +3 -3
- package/dist/runtime/components/table/permission/TablePermissionConfig.vue.d.ts +2 -2
- package/dist/runtime/components/table/permission/TablePermissionTab.vue +7 -4
- package/dist/runtime/components/table/query/order/Item.d.vue.ts +2 -2
- package/dist/runtime/components/table/query/order/Item.vue.d.ts +2 -2
- package/dist/runtime/components/table/query/where/Newer.vue +2 -0
- package/dist/runtime/components/table/query/where/index.vue +46 -15
- package/dist/runtime/components/table/query/where/simple/item/ColumnPicker.vue +9 -4
- package/dist/runtime/components/table/query/where/simple/item/OprPicker.vue +3 -3
- package/dist/runtime/components/table/query/where/simple/item/opr/AsyncSelect.vue +45 -48
- package/dist/runtime/components/table/query/where/simple/item/opr/DatePicker.vue +137 -131
- package/dist/runtime/components/table/query/where/simple/item/opr/Select.d.vue.ts +4 -2
- package/dist/runtime/components/table/query/where/simple/item/opr/Select.vue +40 -40
- package/dist/runtime/components/table/query/where/simple/item/opr/Select.vue.d.ts +4 -2
- package/dist/runtime/components/table/query/where/simple/item/opr/index.vue +2 -0
- package/dist/runtime/components/table/settings/TableSettings.d.vue.ts +1 -1
- package/dist/runtime/components/table/settings/TableSettings.vue +0 -3
- package/dist/runtime/components/table/settings/TableSettings.vue.d.ts +1 -1
- package/dist/runtime/composables/api/sys/index.d.ts +3 -0
- package/dist/runtime/composables/api/sys/index.js +3 -0
- package/dist/runtime/composables/api/sys/useFlowApi.d.ts +2 -0
- package/dist/runtime/composables/api/sys/useFlowApi.js +5 -0
- package/dist/runtime/composables/api/sys/useFlowEdgeApi.d.ts +2 -0
- package/dist/runtime/composables/api/sys/useFlowEdgeApi.js +3 -0
- package/dist/runtime/composables/api/sys/useFlowNodeApi.d.ts +2 -0
- package/dist/runtime/composables/api/sys/useFlowNodeApi.js +3 -0
- package/dist/runtime/composables/api/sys/useRoleApi.js +3 -1
- package/dist/runtime/composables/api/sys/useUserApi.js +3 -1
- package/dist/runtime/composables/flow/index.d.ts +3 -0
- package/dist/runtime/composables/flow/index.js +3 -0
- package/dist/runtime/composables/flow/useFlow.d.ts +33 -0
- package/dist/runtime/composables/flow/useFlow.js +401 -0
- package/dist/runtime/composables/flow/useFlowNode.d.ts +17 -0
- package/dist/runtime/composables/flow/useFlowNode.js +106 -0
- package/dist/runtime/composables/flow/useFlowResize.d.ts +21 -0
- package/dist/runtime/composables/flow/useFlowResize.js +84 -0
- package/dist/runtime/composables/flow/useFlowStyles.d.ts +62 -9
- package/dist/runtime/composables/flow/useFlowStyles.js +127 -23
- package/dist/runtime/composables/table/useTableColumnPermission.d.ts +36 -0
- package/dist/runtime/composables/useDate.js +8 -8
- package/dist/runtime/composables/useSidebarMenu.js +0 -2
- package/dist/runtime/composables/useTheme.d.ts +1 -1
- package/dist/runtime/composables/useTheme.js +0 -1
- package/dist/runtime/constants/flow.d.ts +166 -0
- package/dist/runtime/constants/flow.js +171 -0
- package/dist/runtime/constants/index.d.ts +1 -0
- package/dist/runtime/constants/index.js +1 -0
- package/dist/runtime/constants/options.js +2 -3
- package/dist/runtime/constants/table.d.ts +2 -0
- package/dist/runtime/constants/table.js +8 -0
- package/dist/runtime/index.css +1 -1
- package/dist/runtime/types/components/table/column.d.ts +4 -3
- package/dist/runtime/types/components/table/query/where.d.ts +1 -5
- package/dist/runtime/types/models/flow.d.ts +61 -0
- package/dist/runtime/types/models/flow.js +0 -0
- package/dist/runtime/types/models/index.d.ts +1 -0
- package/dist/runtime/types/models/index.js +1 -0
- package/dist/runtime/types/models/table.d.ts +1 -0
- package/dist/runtime/types/storage.d.ts +3 -4
- package/dist/runtime/types/storage.js +3 -4
- package/package.json +3 -2
- package/dist/runtime/components/table/query/where/simple/index.d.vue.ts +0 -21
- package/dist/runtime/components/table/query/where/simple/index.vue +0 -52
- package/dist/runtime/components/table/query/where/simple/index.vue.d.ts +0 -21
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
import { shallowRef, ref, watch } from "vue";
|
|
2
|
+
import { GRID_SIZE } from "#v/constants";
|
|
3
|
+
export function useFlow(options) {
|
|
4
|
+
const { flow, onUpdateModel, api } = options;
|
|
5
|
+
const nodes = shallowRef([]);
|
|
6
|
+
const edges = shallowRef([]);
|
|
7
|
+
const _pendingCount = ref(0);
|
|
8
|
+
const loading = ref(false);
|
|
9
|
+
const pendingEdgeDeletes = /* @__PURE__ */ new Map();
|
|
10
|
+
const withLoading = async (fn) => {
|
|
11
|
+
_pendingCount.value++;
|
|
12
|
+
loading.value = true;
|
|
13
|
+
try {
|
|
14
|
+
return await fn();
|
|
15
|
+
} finally {
|
|
16
|
+
_pendingCount.value--;
|
|
17
|
+
if (_pendingCount.value <= 0) {
|
|
18
|
+
_pendingCount.value = 0;
|
|
19
|
+
loading.value = false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const getErrorMessage = (error) => {
|
|
24
|
+
return error instanceof Error ? error.message : String(error);
|
|
25
|
+
};
|
|
26
|
+
const deleteEdgeById = async (edgeId) => {
|
|
27
|
+
if (!edges.value.some((e) => e.id === edgeId)) return;
|
|
28
|
+
const existingDelete = pendingEdgeDeletes.get(edgeId);
|
|
29
|
+
if (existingDelete) {
|
|
30
|
+
await existingDelete;
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const deletePromise = (async () => {
|
|
34
|
+
if (api?.value?.deleteEdge) {
|
|
35
|
+
if (!Number.isNaN(Number(edgeId))) {
|
|
36
|
+
try {
|
|
37
|
+
await api.value.deleteEdge(Number(edgeId));
|
|
38
|
+
} catch (error) {
|
|
39
|
+
throw new Error(`\u5220\u9664\u8FDE\u7EBF ${edgeId} \u5931\u8D25: ${getErrorMessage(error)}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
edges.value = edges.value.filter((e) => e.id !== edgeId);
|
|
44
|
+
})();
|
|
45
|
+
pendingEdgeDeletes.set(edgeId, deletePromise);
|
|
46
|
+
try {
|
|
47
|
+
await deletePromise;
|
|
48
|
+
} finally {
|
|
49
|
+
pendingEdgeDeletes.delete(edgeId);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const toVueFlowNode = (node) => ({
|
|
53
|
+
id: String(node.id),
|
|
54
|
+
type: "custom",
|
|
55
|
+
position: { x: node.positionX ?? 0, y: node.positionY ?? 0 },
|
|
56
|
+
data: {
|
|
57
|
+
...node,
|
|
58
|
+
id: node.id,
|
|
59
|
+
name: node.name,
|
|
60
|
+
width: node.width,
|
|
61
|
+
height: node.height
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
const toVueFlowEdge = (edge) => ({
|
|
65
|
+
id: String(edge.id),
|
|
66
|
+
type: "custom",
|
|
67
|
+
source: String(edge.parentId),
|
|
68
|
+
target: String(edge.childId),
|
|
69
|
+
sourceHandle: edge.parentHandlePos ?? void 0,
|
|
70
|
+
targetHandle: edge.childHandlePos ?? void 0,
|
|
71
|
+
label: edge.label
|
|
72
|
+
});
|
|
73
|
+
watch(
|
|
74
|
+
() => flow.value,
|
|
75
|
+
(newFlow) => {
|
|
76
|
+
if (!newFlow) return;
|
|
77
|
+
nodes.value = (newFlow.nodes ?? []).map(toVueFlowNode);
|
|
78
|
+
edges.value = (newFlow.edges ?? []).map(toVueFlowEdge);
|
|
79
|
+
},
|
|
80
|
+
{ immediate: true }
|
|
81
|
+
);
|
|
82
|
+
const emitUpdate = () => {
|
|
83
|
+
if (!onUpdateModel?.value) return;
|
|
84
|
+
const updatedFlow = {
|
|
85
|
+
id: flow.value?.id ?? 0,
|
|
86
|
+
...flow.value,
|
|
87
|
+
nodes: nodes.value.map((n) => ({
|
|
88
|
+
id: n.data.id ?? Number(n.id),
|
|
89
|
+
name: n.data.name ?? "",
|
|
90
|
+
positionX: n.position.x,
|
|
91
|
+
positionY: n.position.y,
|
|
92
|
+
width: n.data.width,
|
|
93
|
+
height: n.data.height
|
|
94
|
+
})),
|
|
95
|
+
edges: edges.value.map((e) => ({
|
|
96
|
+
id: Number(e.id) || void 0,
|
|
97
|
+
parentId: Number(e.source),
|
|
98
|
+
childId: Number(e.target),
|
|
99
|
+
parentHandlePos: e.sourceHandle ?? void 0,
|
|
100
|
+
childHandlePos: e.targetHandle ?? void 0,
|
|
101
|
+
label: typeof e.label === "string" ? e.label : void 0
|
|
102
|
+
}))
|
|
103
|
+
};
|
|
104
|
+
onUpdateModel.value(updatedFlow);
|
|
105
|
+
};
|
|
106
|
+
const deleteNode = async (nodeId) => {
|
|
107
|
+
const doDelete = async () => {
|
|
108
|
+
const relatedEdgeIds = [...new Set(edges.value.filter((e) => e.source === nodeId || e.target === nodeId).map((e) => e.id))];
|
|
109
|
+
try {
|
|
110
|
+
await Promise.all(relatedEdgeIds.map((edgeId) => deleteEdgeById(edgeId)));
|
|
111
|
+
} catch (error) {
|
|
112
|
+
emitUpdate();
|
|
113
|
+
throw new Error(`\u5220\u9664\u8282\u70B9 ${nodeId} \u7684\u5173\u8054\u8FDE\u7EBF\u5931\u8D25: ${getErrorMessage(error)}`);
|
|
114
|
+
}
|
|
115
|
+
if (api?.value?.deleteNode) {
|
|
116
|
+
try {
|
|
117
|
+
await api.value.deleteNode(Number(nodeId));
|
|
118
|
+
} catch (error) {
|
|
119
|
+
emitUpdate();
|
|
120
|
+
throw new Error(`\u5220\u9664\u8282\u70B9 ${nodeId} \u5931\u8D25: ${getErrorMessage(error)}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
nodes.value = nodes.value.filter((n) => n.id !== nodeId);
|
|
124
|
+
emitUpdate();
|
|
125
|
+
};
|
|
126
|
+
if (api?.value) await withLoading(doDelete);
|
|
127
|
+
else await doDelete();
|
|
128
|
+
};
|
|
129
|
+
const deleteEdge = async (edgeId) => {
|
|
130
|
+
const doDelete = async () => {
|
|
131
|
+
await deleteEdgeById(edgeId);
|
|
132
|
+
emitUpdate();
|
|
133
|
+
};
|
|
134
|
+
if (api?.value) await withLoading(doDelete);
|
|
135
|
+
else await doDelete();
|
|
136
|
+
};
|
|
137
|
+
const createEdge = async (params) => {
|
|
138
|
+
const doCreate = async () => {
|
|
139
|
+
let edgeId = `e-${params.source}-${params.sourceHandle}-${params.target}-${params.targetHandle}-${Date.now()}`;
|
|
140
|
+
let label;
|
|
141
|
+
if (api?.value?.createEdge) {
|
|
142
|
+
const { data } = await api.value.createEdge({
|
|
143
|
+
id: 0,
|
|
144
|
+
flowId: flow.value?.id,
|
|
145
|
+
parentId: Number(params.source),
|
|
146
|
+
childId: Number(params.target),
|
|
147
|
+
parentHandlePos: params.sourceHandle ?? void 0,
|
|
148
|
+
childHandlePos: params.targetHandle ?? void 0
|
|
149
|
+
});
|
|
150
|
+
if (data.value.data) {
|
|
151
|
+
edgeId = String(data.value.data.id);
|
|
152
|
+
label = data.value.data.label;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const newEdge = {
|
|
156
|
+
id: edgeId,
|
|
157
|
+
type: "custom",
|
|
158
|
+
source: params.source,
|
|
159
|
+
target: params.target,
|
|
160
|
+
sourceHandle: params.sourceHandle ?? void 0,
|
|
161
|
+
targetHandle: params.targetHandle ?? void 0,
|
|
162
|
+
label
|
|
163
|
+
};
|
|
164
|
+
edges.value = [...edges.value, newEdge];
|
|
165
|
+
emitUpdate();
|
|
166
|
+
};
|
|
167
|
+
if (api?.value) await withLoading(doCreate);
|
|
168
|
+
else await doCreate();
|
|
169
|
+
};
|
|
170
|
+
const reconnectEdge = async (oldEdgeId, connection) => {
|
|
171
|
+
if (!api?.value) {
|
|
172
|
+
edges.value = edges.value.filter((e) => e.id !== oldEdgeId);
|
|
173
|
+
const newEdge = {
|
|
174
|
+
id: `e-${connection.source}-${connection.sourceHandle}-${connection.target}-${connection.targetHandle}-${Date.now()}`,
|
|
175
|
+
type: "custom",
|
|
176
|
+
source: connection.source,
|
|
177
|
+
target: connection.target,
|
|
178
|
+
sourceHandle: connection.sourceHandle ?? void 0,
|
|
179
|
+
targetHandle: connection.targetHandle ?? void 0
|
|
180
|
+
};
|
|
181
|
+
edges.value = [...edges.value, newEdge];
|
|
182
|
+
emitUpdate();
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const oldEdge = edges.value.find((e) => e.id === oldEdgeId);
|
|
186
|
+
const oldEdgeBackup = oldEdge ? { ...oldEdge } : void 0;
|
|
187
|
+
const tempId = `e-pending-${Date.now()}`;
|
|
188
|
+
const pendingEdge = {
|
|
189
|
+
id: tempId,
|
|
190
|
+
type: "custom",
|
|
191
|
+
source: connection.source,
|
|
192
|
+
target: connection.target,
|
|
193
|
+
sourceHandle: connection.sourceHandle ?? void 0,
|
|
194
|
+
targetHandle: connection.targetHandle ?? void 0,
|
|
195
|
+
label: oldEdge?.label,
|
|
196
|
+
style: { ...oldEdge?.style ?? {}, opacity: 0.5 }
|
|
197
|
+
};
|
|
198
|
+
edges.value = [...edges.value.filter((e) => e.id !== oldEdgeId), pendingEdge];
|
|
199
|
+
emitUpdate();
|
|
200
|
+
await withLoading(async () => {
|
|
201
|
+
try {
|
|
202
|
+
if (api.value?.deleteEdge) {
|
|
203
|
+
if (!Number.isNaN(Number(oldEdgeId))) {
|
|
204
|
+
await api.value.deleteEdge(Number(oldEdgeId));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
let finalId = tempId;
|
|
208
|
+
let label = typeof pendingEdge.label === "string" ? pendingEdge.label : void 0;
|
|
209
|
+
if (api.value?.createEdge) {
|
|
210
|
+
const { data } = await api.value.createEdge({
|
|
211
|
+
id: 0,
|
|
212
|
+
flowId: flow.value?.id,
|
|
213
|
+
parentId: Number(connection.source),
|
|
214
|
+
childId: Number(connection.target),
|
|
215
|
+
parentHandlePos: connection.sourceHandle ?? void 0,
|
|
216
|
+
childHandlePos: connection.targetHandle ?? void 0
|
|
217
|
+
});
|
|
218
|
+
if (data.value.data) {
|
|
219
|
+
finalId = String(data.value.data.id);
|
|
220
|
+
label = data.value.data.label;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const idx = edges.value.findIndex((e) => e.id === tempId);
|
|
224
|
+
if (idx !== -1) {
|
|
225
|
+
const { opacity: _, ...restStyle } = edges.value[idx].style ?? {};
|
|
226
|
+
edges.value[idx] = {
|
|
227
|
+
...edges.value[idx],
|
|
228
|
+
id: finalId,
|
|
229
|
+
label,
|
|
230
|
+
style: Object.keys(restStyle).length > 0 ? restStyle : void 0
|
|
231
|
+
};
|
|
232
|
+
edges.value = [...edges.value];
|
|
233
|
+
emitUpdate();
|
|
234
|
+
}
|
|
235
|
+
} catch {
|
|
236
|
+
edges.value = edges.value.filter((e) => e.id !== tempId);
|
|
237
|
+
if (oldEdgeBackup) {
|
|
238
|
+
edges.value = [...edges.value, oldEdgeBackup];
|
|
239
|
+
}
|
|
240
|
+
emitUpdate();
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
};
|
|
244
|
+
const updateNodePosition = async (nodeId, x, y) => {
|
|
245
|
+
const doUpdate = async () => {
|
|
246
|
+
const node = nodes.value.find((n) => n.id === nodeId);
|
|
247
|
+
if (node) {
|
|
248
|
+
if (api?.value?.updateNode) {
|
|
249
|
+
await api.value.updateNode({
|
|
250
|
+
...node.data,
|
|
251
|
+
id: node.data.id ?? Number(nodeId),
|
|
252
|
+
positionX: x,
|
|
253
|
+
positionY: y
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
node.position = { x, y };
|
|
257
|
+
emitUpdate();
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
if (api?.value) await withLoading(doUpdate);
|
|
261
|
+
else await doUpdate();
|
|
262
|
+
};
|
|
263
|
+
const updateNodeDimensions = async (nodeId, dimensions) => {
|
|
264
|
+
const doUpdate = async () => {
|
|
265
|
+
const node = nodes.value.find((n) => n.id === nodeId);
|
|
266
|
+
if (node) {
|
|
267
|
+
if (api?.value?.updateNode) {
|
|
268
|
+
await api.value.updateNode({
|
|
269
|
+
...node.data,
|
|
270
|
+
id: node.data.id ?? Number(nodeId),
|
|
271
|
+
width: dimensions.width,
|
|
272
|
+
height: dimensions.height
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
node.data = {
|
|
276
|
+
...node.data,
|
|
277
|
+
width: dimensions.width,
|
|
278
|
+
height: dimensions.height
|
|
279
|
+
};
|
|
280
|
+
emitUpdate();
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
if (api?.value) await withLoading(doUpdate);
|
|
284
|
+
else await doUpdate();
|
|
285
|
+
};
|
|
286
|
+
const updateEdgeLabel = async (edgeId, label) => {
|
|
287
|
+
const doUpdate = async () => {
|
|
288
|
+
const edge = edges.value.find((e) => e.id === edgeId);
|
|
289
|
+
if (edge) {
|
|
290
|
+
if (api?.value?.updateEdge) {
|
|
291
|
+
if (!Number.isNaN(Number(edgeId))) {
|
|
292
|
+
await api.value.updateEdge({
|
|
293
|
+
id: Number(edgeId),
|
|
294
|
+
parentId: Number(edge.source),
|
|
295
|
+
childId: Number(edge.target),
|
|
296
|
+
parentHandlePos: edge.sourceHandle ?? void 0,
|
|
297
|
+
childHandlePos: edge.targetHandle ?? void 0,
|
|
298
|
+
label: label || void 0
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
edge.label = label || void 0;
|
|
303
|
+
emitUpdate();
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
if (api?.value) await withLoading(doUpdate);
|
|
307
|
+
else await doUpdate();
|
|
308
|
+
};
|
|
309
|
+
const updateNode = async (updatedNode) => {
|
|
310
|
+
const doUpdate = async () => {
|
|
311
|
+
const index = nodes.value.findIndex((n) => n.id === String(updatedNode.id));
|
|
312
|
+
if (index !== -1) {
|
|
313
|
+
if (api?.value?.updateNode) {
|
|
314
|
+
await api.value.updateNode(updatedNode);
|
|
315
|
+
}
|
|
316
|
+
nodes.value[index].data = {
|
|
317
|
+
...nodes.value[index].data,
|
|
318
|
+
...updatedNode,
|
|
319
|
+
id: updatedNode.id,
|
|
320
|
+
name: updatedNode.name
|
|
321
|
+
};
|
|
322
|
+
emitUpdate();
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
if (api?.value) await withLoading(doUpdate);
|
|
326
|
+
else await doUpdate();
|
|
327
|
+
};
|
|
328
|
+
const createNode = async () => {
|
|
329
|
+
const doCreate = async () => {
|
|
330
|
+
const positionX = Math.round((200 + Math.random() * 200) / GRID_SIZE) * GRID_SIZE;
|
|
331
|
+
const positionY = Math.round((200 + Math.random() * 200) / GRID_SIZE) * GRID_SIZE;
|
|
332
|
+
const defaultName = `\u8282\u70B9 ${nodes.value.length + 1}`;
|
|
333
|
+
let nodeId = Date.now();
|
|
334
|
+
let nodeName = defaultName;
|
|
335
|
+
let nodeWidth = 120;
|
|
336
|
+
let nodeHeight = 40;
|
|
337
|
+
if (api?.value?.createNode) {
|
|
338
|
+
const { data } = await api.value.createNode({
|
|
339
|
+
id: 0,
|
|
340
|
+
flowId: flow.value?.id,
|
|
341
|
+
name: defaultName,
|
|
342
|
+
positionX,
|
|
343
|
+
positionY,
|
|
344
|
+
width: 120,
|
|
345
|
+
height: 40
|
|
346
|
+
});
|
|
347
|
+
if (data.value.data) {
|
|
348
|
+
nodeId = data.value.data.id;
|
|
349
|
+
nodeName = data.value.data.name ?? defaultName;
|
|
350
|
+
nodeWidth = data.value.data.width ?? 120;
|
|
351
|
+
nodeHeight = data.value.data.height ?? 40;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
const newNode = {
|
|
355
|
+
id: String(nodeId),
|
|
356
|
+
type: "custom",
|
|
357
|
+
position: { x: positionX, y: positionY },
|
|
358
|
+
data: {
|
|
359
|
+
id: nodeId,
|
|
360
|
+
name: nodeName,
|
|
361
|
+
width: nodeWidth,
|
|
362
|
+
height: nodeHeight
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
nodes.value = [...nodes.value, newNode];
|
|
366
|
+
emitUpdate();
|
|
367
|
+
};
|
|
368
|
+
if (api?.value) await withLoading(doCreate);
|
|
369
|
+
else await doCreate();
|
|
370
|
+
};
|
|
371
|
+
const syncNodes = (createHandlers) => {
|
|
372
|
+
nodes.value.forEach((node) => {
|
|
373
|
+
const handlers = createHandlers(node.id);
|
|
374
|
+
Object.assign(node.data, handlers);
|
|
375
|
+
});
|
|
376
|
+
};
|
|
377
|
+
const syncEdges = (createHandlers) => {
|
|
378
|
+
edges.value.forEach((edge) => {
|
|
379
|
+
const handlers = createHandlers(edge.id);
|
|
380
|
+
if (!edge.data) edge.data = {};
|
|
381
|
+
Object.assign(edge.data, handlers);
|
|
382
|
+
});
|
|
383
|
+
};
|
|
384
|
+
return {
|
|
385
|
+
nodes,
|
|
386
|
+
edges,
|
|
387
|
+
GRID_SIZE,
|
|
388
|
+
loading,
|
|
389
|
+
deleteNode,
|
|
390
|
+
deleteEdge,
|
|
391
|
+
createEdge,
|
|
392
|
+
reconnectEdge,
|
|
393
|
+
updateNodePosition,
|
|
394
|
+
updateNodeDimensions,
|
|
395
|
+
updateNode,
|
|
396
|
+
createNode,
|
|
397
|
+
syncNodes,
|
|
398
|
+
updateEdgeLabel,
|
|
399
|
+
syncEdges
|
|
400
|
+
};
|
|
401
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Ref } from 'vue';
|
|
2
|
+
import type { FlowHandle } from '#v/constants';
|
|
3
|
+
interface UseFlowNodeOptions {
|
|
4
|
+
/** 节点 data (reactive) */
|
|
5
|
+
data: Ref<any>;
|
|
6
|
+
/** 是否选中 (reactive) */
|
|
7
|
+
selected: Ref<boolean>;
|
|
8
|
+
}
|
|
9
|
+
export declare function useFlowNode(options: UseFlowNodeOptions): {
|
|
10
|
+
nodeRef: Ref<HTMLElement | null, HTMLElement | null>;
|
|
11
|
+
isHoveredLocal: Ref<boolean, boolean>;
|
|
12
|
+
borderColor: import("vue").ComputedRef<any>;
|
|
13
|
+
showHandles: import("vue").ComputedRef<boolean>;
|
|
14
|
+
activeHandles: import("vue").ComputedRef<FlowHandle[]>;
|
|
15
|
+
handleStyle: (handle: FlowHandle) => Record<string, string | number | undefined>;
|
|
16
|
+
};
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { ref, computed, inject, onMounted, onBeforeUnmount } from "vue";
|
|
2
|
+
import {
|
|
3
|
+
FLOW_HANDLES,
|
|
4
|
+
FLOW_HANDLE_TIER_THRESHOLDS,
|
|
5
|
+
FLOW_MOUSE_POSITION_KEY,
|
|
6
|
+
FLOW_NODE_PROXIMITY_THRESHOLD
|
|
7
|
+
} from "#v/constants";
|
|
8
|
+
import { handleSizePreview } from "./useFlowStyles.js";
|
|
9
|
+
export function useFlowNode(options) {
|
|
10
|
+
const { data, selected } = options;
|
|
11
|
+
const nodeRef = ref(null);
|
|
12
|
+
const isHoveredLocal = ref(false);
|
|
13
|
+
const injectedMousePosition = inject(FLOW_MOUSE_POSITION_KEY, null);
|
|
14
|
+
const localMousePosition = ref({ x: 0, y: 0 });
|
|
15
|
+
const mousePosition = computed(() => injectedMousePosition?.value ?? localMousePosition.value);
|
|
16
|
+
const borderColor = computed(() => {
|
|
17
|
+
if (selected.value) return "var(--ui-primary)";
|
|
18
|
+
if (data.value.borderColor) return data.value.borderColor;
|
|
19
|
+
return "var(--ui-border-default)";
|
|
20
|
+
});
|
|
21
|
+
const isNearby = computed(() => {
|
|
22
|
+
if (!nodeRef.value) return false;
|
|
23
|
+
const rect = nodeRef.value.getBoundingClientRect();
|
|
24
|
+
const mouseX = mousePosition.value.x;
|
|
25
|
+
const mouseY = mousePosition.value.y;
|
|
26
|
+
const dx = Math.max(rect.left - mouseX, 0, mouseX - rect.right);
|
|
27
|
+
const dy = Math.max(rect.top - mouseY, 0, mouseY - rect.bottom);
|
|
28
|
+
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
29
|
+
return distance <= FLOW_NODE_PROXIMITY_THRESHOLD;
|
|
30
|
+
});
|
|
31
|
+
const showHandles = computed(() => isHoveredLocal.value || isNearby.value || handleSizePreview.value);
|
|
32
|
+
const activeHandles = computed(() => {
|
|
33
|
+
const w = data.value.width ?? 120;
|
|
34
|
+
const h = data.value.height ?? 40;
|
|
35
|
+
const t = FLOW_HANDLE_TIER_THRESHOLDS;
|
|
36
|
+
const hTier = w < t.small.maxWidth ? 0 : w < t.medium.maxWidth ? 1 : 2;
|
|
37
|
+
const vTier = h < t.small.maxHeight ? 0 : h < t.medium.maxHeight ? 1 : 2;
|
|
38
|
+
return FLOW_HANDLES.filter((handle) => {
|
|
39
|
+
const id = handle.id;
|
|
40
|
+
if (id === "t2" || id === "b2") return true;
|
|
41
|
+
if (id === "r2" || id === "l2") return true;
|
|
42
|
+
if (id === "tl" || id === "tr" || id === "bl" || id === "br") return true;
|
|
43
|
+
if (id === "t1" || id === "t3" || id === "b1" || id === "b3") return hTier >= 2;
|
|
44
|
+
if (id === "r1" || id === "r3" || id === "l1" || id === "l3") return vTier >= 2;
|
|
45
|
+
return false;
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
const handleStyle = (handle) => {
|
|
49
|
+
const bw = data.value.borderWidth ?? 2;
|
|
50
|
+
const handleSize = data.value.handleSize ?? 6;
|
|
51
|
+
const handleColor = data.value.handleColor ?? "";
|
|
52
|
+
const offset = -(bw / 2);
|
|
53
|
+
const base = {
|
|
54
|
+
pointerEvents: "all",
|
|
55
|
+
opacity: showHandles.value ? 1 : 0,
|
|
56
|
+
transition: "opacity 0.2s",
|
|
57
|
+
width: `${handleSize}px`,
|
|
58
|
+
height: `${handleSize}px`,
|
|
59
|
+
zIndex: 20,
|
|
60
|
+
borderRadius: "50%",
|
|
61
|
+
...handleColor ? { backgroundColor: handleColor } : {}
|
|
62
|
+
};
|
|
63
|
+
const pos = handle.position;
|
|
64
|
+
if (pos === "top") {
|
|
65
|
+
base.top = `${offset}px`;
|
|
66
|
+
} else if (pos === "bottom") {
|
|
67
|
+
base.bottom = `${offset}px`;
|
|
68
|
+
} else if (pos === "left") {
|
|
69
|
+
base.left = `${offset}px`;
|
|
70
|
+
} else if (pos === "right") {
|
|
71
|
+
base.right = `${offset}px`;
|
|
72
|
+
}
|
|
73
|
+
if (handle.offsetPercent?.x !== void 0) {
|
|
74
|
+
const pct = handle.offsetPercent.x;
|
|
75
|
+
const shift = (pct / 50 - 1) * (bw / 2);
|
|
76
|
+
base.left = `calc(${pct}% + ${shift}px)`;
|
|
77
|
+
}
|
|
78
|
+
if (handle.offsetPercent?.y !== void 0) {
|
|
79
|
+
const pct = handle.offsetPercent.y;
|
|
80
|
+
const shift = (pct / 50 - 1) * (bw / 2);
|
|
81
|
+
base.top = `calc(${pct}% + ${shift}px)`;
|
|
82
|
+
}
|
|
83
|
+
return base;
|
|
84
|
+
};
|
|
85
|
+
const handleGlobalMouseMove = (e) => {
|
|
86
|
+
localMousePosition.value = { x: e.clientX, y: e.clientY };
|
|
87
|
+
};
|
|
88
|
+
onMounted(() => {
|
|
89
|
+
if (!injectedMousePosition) {
|
|
90
|
+
window.addEventListener("mousemove", handleGlobalMouseMove);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
onBeforeUnmount(() => {
|
|
94
|
+
if (!injectedMousePosition) {
|
|
95
|
+
window.removeEventListener("mousemove", handleGlobalMouseMove);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
return {
|
|
99
|
+
nodeRef,
|
|
100
|
+
isHoveredLocal,
|
|
101
|
+
borderColor,
|
|
102
|
+
showHandles,
|
|
103
|
+
activeHandles,
|
|
104
|
+
handleStyle
|
|
105
|
+
};
|
|
106
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ShallowRef } from 'vue';
|
|
2
|
+
import type { Node } from '@vue-flow/core';
|
|
3
|
+
import type { FlowNode, UseFlowResizeDimensions } from '#v/types';
|
|
4
|
+
import type { ResizeEdge } from '#v/constants';
|
|
5
|
+
interface UseFlowResizeOptions {
|
|
6
|
+
gridSize: number;
|
|
7
|
+
nodes: ShallowRef<Node[]>;
|
|
8
|
+
getViewport: () => {
|
|
9
|
+
x: number;
|
|
10
|
+
y: number;
|
|
11
|
+
zoom: number;
|
|
12
|
+
};
|
|
13
|
+
onResizeEnd: (nodeId: string, dimensions: UseFlowResizeDimensions) => void;
|
|
14
|
+
}
|
|
15
|
+
export declare function useFlowResize(options: UseFlowResizeOptions): {
|
|
16
|
+
isResizing: import("vue").Ref<boolean, boolean>;
|
|
17
|
+
startResize: (event: MouseEvent, nodeId: string, node: FlowNode, edge: ResizeEdge) => void;
|
|
18
|
+
handleMouseMove: (event: MouseEvent) => void;
|
|
19
|
+
handleMouseUp: () => void;
|
|
20
|
+
};
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { ref } from "vue";
|
|
2
|
+
export function useFlowResize(options) {
|
|
3
|
+
const { gridSize, nodes, getViewport, onResizeEnd } = options;
|
|
4
|
+
const isResizing = ref(false);
|
|
5
|
+
const resizingNodeId = ref(null);
|
|
6
|
+
const resizeEdge = ref(null);
|
|
7
|
+
const resizeStartX = ref(0);
|
|
8
|
+
const resizeStartY = ref(0);
|
|
9
|
+
const resizeStartWidth = ref(0);
|
|
10
|
+
const resizeStartHeight = ref(0);
|
|
11
|
+
const resizeStartNodeX = ref(0);
|
|
12
|
+
const resizeStartNodeY = ref(0);
|
|
13
|
+
const startResize = (event, nodeId, node, edge) => {
|
|
14
|
+
event.preventDefault();
|
|
15
|
+
event.stopPropagation();
|
|
16
|
+
isResizing.value = true;
|
|
17
|
+
resizingNodeId.value = nodeId;
|
|
18
|
+
resizeEdge.value = edge;
|
|
19
|
+
resizeStartX.value = event.clientX;
|
|
20
|
+
resizeStartY.value = event.clientY;
|
|
21
|
+
resizeStartWidth.value = node.width ?? 120;
|
|
22
|
+
resizeStartHeight.value = node.height ?? 40;
|
|
23
|
+
resizeStartNodeX.value = node.positionX ?? 0;
|
|
24
|
+
resizeStartNodeY.value = node.positionY ?? 0;
|
|
25
|
+
document.body.style.userSelect = "none";
|
|
26
|
+
};
|
|
27
|
+
const handleMouseMove = (event) => {
|
|
28
|
+
if (!isResizing.value || !resizingNodeId.value || !resizeEdge.value) return;
|
|
29
|
+
const viewport = getViewport();
|
|
30
|
+
const zoom = viewport.zoom;
|
|
31
|
+
const dx = (event.clientX - resizeStartX.value) / zoom;
|
|
32
|
+
const dy = (event.clientY - resizeStartY.value) / zoom;
|
|
33
|
+
const node = nodes.value.find((n) => n.id === resizingNodeId.value);
|
|
34
|
+
if (!node) return;
|
|
35
|
+
let newWidth = resizeStartWidth.value;
|
|
36
|
+
let newHeight = resizeStartHeight.value;
|
|
37
|
+
let newX = resizeStartNodeX.value;
|
|
38
|
+
let newY = resizeStartNodeY.value;
|
|
39
|
+
const edge = resizeEdge.value;
|
|
40
|
+
if (edge.includes("right")) {
|
|
41
|
+
newWidth = Math.max(60, resizeStartWidth.value + dx);
|
|
42
|
+
}
|
|
43
|
+
if (edge.includes("left")) {
|
|
44
|
+
const deltaWidth = Math.min(dx, resizeStartWidth.value - 60);
|
|
45
|
+
newWidth = resizeStartWidth.value - deltaWidth;
|
|
46
|
+
newX = resizeStartNodeX.value + deltaWidth;
|
|
47
|
+
}
|
|
48
|
+
if (edge.includes("bottom")) {
|
|
49
|
+
newHeight = Math.max(40, resizeStartHeight.value + dy);
|
|
50
|
+
}
|
|
51
|
+
if (edge === "top" || edge === "top-left" || edge === "top-right") {
|
|
52
|
+
const deltaHeight = Math.min(dy, resizeStartHeight.value - 40);
|
|
53
|
+
newHeight = resizeStartHeight.value - deltaHeight;
|
|
54
|
+
newY = resizeStartNodeY.value + deltaHeight;
|
|
55
|
+
}
|
|
56
|
+
newWidth = Math.round(newWidth / gridSize) * gridSize;
|
|
57
|
+
newHeight = Math.round(newHeight / gridSize) * gridSize;
|
|
58
|
+
newX = Math.round(newX / gridSize) * gridSize;
|
|
59
|
+
newY = Math.round(newY / gridSize) * gridSize;
|
|
60
|
+
node.data = { ...node.data, width: newWidth, height: newHeight };
|
|
61
|
+
node.position = { x: newX, y: newY };
|
|
62
|
+
};
|
|
63
|
+
const handleMouseUp = () => {
|
|
64
|
+
if (isResizing.value && resizingNodeId.value) {
|
|
65
|
+
const node = nodes.value.find((n) => n.id === resizingNodeId.value);
|
|
66
|
+
if (node) {
|
|
67
|
+
onResizeEnd(resizingNodeId.value, {
|
|
68
|
+
width: node.data.width ?? 120,
|
|
69
|
+
height: node.data.height ?? 40
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
isResizing.value = false;
|
|
74
|
+
resizingNodeId.value = null;
|
|
75
|
+
resizeEdge.value = null;
|
|
76
|
+
document.body.style.userSelect = "";
|
|
77
|
+
};
|
|
78
|
+
return {
|
|
79
|
+
isResizing,
|
|
80
|
+
startResize,
|
|
81
|
+
handleMouseMove,
|
|
82
|
+
handleMouseUp
|
|
83
|
+
};
|
|
84
|
+
}
|