v-nuxt-ui 0.1.36 → 0.2.1

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.
Files changed (72) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +1 -0
  3. package/dist/runtime/components/Watermark.d.vue.ts +3 -3
  4. package/dist/runtime/components/Watermark.vue.d.ts +3 -3
  5. package/dist/runtime/components/button/CircleColor.d.vue.ts +17 -0
  6. package/dist/runtime/components/button/CircleColor.vue +37 -0
  7. package/dist/runtime/components/button/CircleColor.vue.d.ts +17 -0
  8. package/dist/runtime/components/flow/FlowEdge.client.vue +170 -41
  9. package/dist/runtime/components/flow/FlowEditor.client.d.vue.ts +51 -0
  10. package/dist/runtime/components/flow/FlowEditor.client.vue +294 -0
  11. package/dist/runtime/components/flow/FlowEditor.client.vue.d.ts +51 -0
  12. package/dist/runtime/components/flow/FlowNode.client.d.vue.ts +13 -2
  13. package/dist/runtime/components/flow/FlowNode.client.vue +44 -48
  14. package/dist/runtime/components/flow/FlowNode.client.vue.d.ts +13 -2
  15. package/dist/runtime/components/flow/FlowToolbar.d.vue.ts +41 -5
  16. package/dist/runtime/components/flow/FlowToolbar.vue +554 -88
  17. package/dist/runtime/components/flow/FlowToolbar.vue.d.ts +41 -5
  18. package/dist/runtime/components/flow/FlowToolbarItemWrapper.d.vue.ts +17 -0
  19. package/dist/runtime/components/flow/FlowToolbarItemWrapper.vue +16 -0
  20. package/dist/runtime/components/flow/FlowToolbarItemWrapper.vue.d.ts +17 -0
  21. package/dist/runtime/components/sys/flow/CreateModal.d.vue.ts +13 -0
  22. package/dist/runtime/components/sys/flow/CreateModal.vue +32 -0
  23. package/dist/runtime/components/sys/flow/CreateModal.vue.d.ts +13 -0
  24. package/dist/runtime/components/sys/flow/EditNodeModal.d.vue.ts +13 -0
  25. package/dist/runtime/components/sys/flow/EditNodeModal.vue +30 -0
  26. package/dist/runtime/components/sys/flow/EditNodeModal.vue.d.ts +13 -0
  27. package/dist/runtime/components/sys/flow/Table.d.vue.ts +3 -0
  28. package/dist/runtime/components/sys/flow/Table.vue +98 -0
  29. package/dist/runtime/components/sys/flow/Table.vue.d.ts +3 -0
  30. package/dist/runtime/components/sys/table/CreateModal.vue +9 -191
  31. package/dist/runtime/components/sys/table/Table.vue +0 -11
  32. package/dist/runtime/components/sys/table/TableColumnList.d.vue.ts +54 -0
  33. package/dist/runtime/components/sys/table/TableColumnList.vue +196 -0
  34. package/dist/runtime/components/sys/table/TableColumnList.vue.d.ts +54 -0
  35. package/dist/runtime/components/sys/table/TableColumnModal.d.vue.ts +3 -13
  36. package/dist/runtime/components/sys/table/TableColumnModal.vue +32 -100
  37. package/dist/runtime/components/sys/table/TableColumnModal.vue.d.ts +3 -13
  38. package/dist/runtime/components/table/query/order/Item.d.vue.ts +2 -2
  39. package/dist/runtime/components/table/query/order/Item.vue.d.ts +2 -2
  40. package/dist/runtime/composables/api/sys/index.d.ts +3 -0
  41. package/dist/runtime/composables/api/sys/index.js +3 -0
  42. package/dist/runtime/composables/api/sys/useFlowApi.d.ts +2 -0
  43. package/dist/runtime/composables/api/sys/useFlowApi.js +5 -0
  44. package/dist/runtime/composables/api/sys/useFlowEdgeApi.d.ts +2 -0
  45. package/dist/runtime/composables/api/sys/useFlowEdgeApi.js +3 -0
  46. package/dist/runtime/composables/api/sys/useFlowNodeApi.d.ts +2 -0
  47. package/dist/runtime/composables/api/sys/useFlowNodeApi.js +3 -0
  48. package/dist/runtime/composables/flow/index.d.ts +3 -0
  49. package/dist/runtime/composables/flow/index.js +3 -0
  50. package/dist/runtime/composables/flow/useFlow.d.ts +33 -0
  51. package/dist/runtime/composables/flow/useFlow.js +401 -0
  52. package/dist/runtime/composables/flow/useFlowNode.d.ts +17 -0
  53. package/dist/runtime/composables/flow/useFlowNode.js +106 -0
  54. package/dist/runtime/composables/flow/useFlowResize.d.ts +21 -0
  55. package/dist/runtime/composables/flow/useFlowResize.js +84 -0
  56. package/dist/runtime/composables/flow/useFlowStyles.d.ts +62 -9
  57. package/dist/runtime/composables/flow/useFlowStyles.js +127 -23
  58. package/dist/runtime/composables/table/useTableColumnPermission.d.ts +36 -0
  59. package/dist/runtime/composables/useSidebarMenu.js +0 -2
  60. package/dist/runtime/composables/useTheme.d.ts +1 -1
  61. package/dist/runtime/composables/useTheme.js +0 -1
  62. package/dist/runtime/constants/flow.d.ts +166 -0
  63. package/dist/runtime/constants/flow.js +171 -0
  64. package/dist/runtime/index.css +1 -1
  65. package/dist/runtime/types/models/flow.d.ts +61 -0
  66. package/dist/runtime/types/models/flow.js +0 -0
  67. package/dist/runtime/types/models/index.d.ts +1 -0
  68. package/dist/runtime/types/models/index.js +1 -0
  69. package/dist/runtime/types/models/table.d.ts +1 -0
  70. package/dist/runtime/types/storage.d.ts +3 -4
  71. package/dist/runtime/types/storage.js +3 -4
  72. package/package.json +3 -2
@@ -0,0 +1,294 @@
1
+ <script setup>
2
+ import { ref, computed, provide, watchEffect, onMounted, onBeforeUnmount } from "vue";
3
+ import { VueFlow, useVueFlow, Panel } from "@vue-flow/core";
4
+ import { Background } from "@vue-flow/background";
5
+ import { FLOW_MOUSE_POSITION_KEY, FLOW_EDGE_STROKE_TYPES } from "#v/constants";
6
+ import FlowNode from "./FlowNode.client.vue";
7
+ import FlowEdge from "./FlowEdge.client.vue";
8
+ import FlowToolbar from "./FlowToolbar.vue";
9
+ import FlowStats from "./FlowStats.vue";
10
+ import { useFlow, useFlowResize, useFlowStyles } from "#v/composables";
11
+ const props = defineProps({
12
+ modelValue: { type: Object, required: false, default: () => ({ id: 0, nodes: [], edges: [] }) },
13
+ api: { type: Object, required: false, default: void 0 },
14
+ showBackground: { type: Boolean, required: false, default: true },
15
+ showToolbar: { type: Boolean, required: false, default: true },
16
+ showStats: { type: Boolean, required: false, default: true },
17
+ minZoom: { type: Number, required: false, default: 0.2 },
18
+ maxZoom: { type: Number, required: false, default: 4 },
19
+ defaultZoom: { type: Number, required: false, default: 1 }
20
+ });
21
+ const emit = defineEmits(["update:modelValue", "edit-node"]);
22
+ const handleUpdateModel = (model) => {
23
+ emit("update:modelValue", model);
24
+ };
25
+ const flowLogic = useFlow({
26
+ flow: computed(() => props.modelValue),
27
+ onUpdateModel: computed(() => handleUpdateModel),
28
+ api: computed(() => props.api)
29
+ });
30
+ const {
31
+ nodes,
32
+ edges,
33
+ GRID_SIZE,
34
+ loading,
35
+ deleteNode,
36
+ deleteEdge,
37
+ createEdge,
38
+ reconnectEdge,
39
+ updateNodePosition,
40
+ updateNodeDimensions,
41
+ createNode,
42
+ updateEdgeLabel
43
+ } = flowLogic;
44
+ const {
45
+ edgeStrokeWidth,
46
+ edgeMarkerStart,
47
+ edgeMarkerEnd,
48
+ edgeAnimated,
49
+ edgeStrokeType,
50
+ edgePathType,
51
+ edgeColor,
52
+ edgeLabelColor,
53
+ nodeBorderWidth,
54
+ nodeBorderRadius,
55
+ nodeBorderColor,
56
+ nodeBgColor,
57
+ nodeFontColor,
58
+ nodeFontSize,
59
+ nodeHandleSize,
60
+ nodeHandleColor,
61
+ colorMode,
62
+ unifiedColor,
63
+ isUnifiedMode,
64
+ effectiveNodeBorderColor,
65
+ effectiveNodeBgColor,
66
+ effectiveNodeFontColor,
67
+ effectiveNodeHandleColor,
68
+ effectiveEdgeColor
69
+ } = useFlowStyles();
70
+ const { onConnect, onNodeDragStop, onEdgeUpdate, getSelectedNodes, getSelectedEdges, getViewport } = useVueFlow();
71
+ const handleResizeEnd = async (nodeId, dimensions) => {
72
+ await updateNodeDimensions(nodeId, dimensions);
73
+ };
74
+ const resizeLogic = useFlowResize({
75
+ gridSize: GRID_SIZE,
76
+ nodes,
77
+ getViewport,
78
+ onResizeEnd: handleResizeEnd
79
+ });
80
+ const {
81
+ startResize,
82
+ handleMouseMove,
83
+ handleMouseUp
84
+ } = resizeLogic;
85
+ const mousePosition = ref({ x: 0, y: 0 });
86
+ provide(FLOW_MOUSE_POSITION_KEY, mousePosition);
87
+ const handleEditNode = (nodeId) => {
88
+ const flowNode = props.modelValue?.nodes?.find((n) => String(n.id) === nodeId);
89
+ if (flowNode) {
90
+ emit("edit-node", flowNode);
91
+ }
92
+ };
93
+ watchEffect(() => {
94
+ flowLogic.syncNodes((nodeId) => ({
95
+ onEdit: () => handleEditNode(nodeId),
96
+ onDelete: () => deleteNode(nodeId),
97
+ borderWidth: nodeBorderWidth.value,
98
+ borderRadius: nodeBorderRadius.value,
99
+ borderColor: effectiveNodeBorderColor.value,
100
+ bgColor: effectiveNodeBgColor.value,
101
+ fontColor: effectiveNodeFontColor.value,
102
+ fontSize: nodeFontSize.value,
103
+ handleSize: nodeHandleSize.value,
104
+ handleColor: effectiveNodeHandleColor.value,
105
+ onResizeStart: (event, edge) => {
106
+ const node = props.modelValue?.nodes?.find((n) => String(n.id) === nodeId);
107
+ if (node) {
108
+ startResize(event, nodeId, node, edge);
109
+ }
110
+ }
111
+ }));
112
+ });
113
+ watchEffect(() => {
114
+ const dasharray = FLOW_EDGE_STROKE_TYPES.find((t) => t.type === edgeStrokeType.value)?.dasharray || "";
115
+ edges.value.forEach((edge) => {
116
+ edge.style = {
117
+ strokeWidth: edgeStrokeWidth.value,
118
+ ...dasharray ? { strokeDasharray: dasharray } : {},
119
+ ...effectiveEdgeColor.value ? { stroke: effectiveEdgeColor.value } : {}
120
+ };
121
+ edge.markerStart = void 0;
122
+ edge.markerEnd = void 0;
123
+ edge.animated = false;
124
+ });
125
+ });
126
+ watchEffect(() => {
127
+ flowLogic.syncEdges((edgeId) => ({
128
+ onUpdateLabel: (newLabel) => updateEdgeLabel(edgeId, newLabel)
129
+ }));
130
+ });
131
+ const isDeleteKey = (event) => {
132
+ return event.key === "Delete" || event.key === "Backspace" || event.code === "Backspace";
133
+ };
134
+ const isEditableTarget = (target) => {
135
+ const element = target instanceof HTMLElement ? target : null;
136
+ if (!element) return false;
137
+ const tagName = element.tagName;
138
+ return element.isContentEditable || tagName === "INPUT" || tagName === "TEXTAREA" || tagName === "SELECT";
139
+ };
140
+ const handleKeyDown = async (event) => {
141
+ if (!isDeleteKey(event)) return;
142
+ if (isEditableTarget(event.target)) return;
143
+ event.preventDefault();
144
+ event.stopPropagation();
145
+ event.stopImmediatePropagation();
146
+ const selectedNodes = getSelectedNodes.value;
147
+ const selectedEdges = getSelectedEdges.value;
148
+ if (selectedNodes.length === 0 && selectedEdges.length === 0) return;
149
+ await Promise.all([
150
+ ...selectedNodes.map((node) => deleteNode(node.id)),
151
+ ...selectedEdges.map((edge) => deleteEdge(edge.id))
152
+ ]);
153
+ };
154
+ onMounted(() => {
155
+ const handleGlobalMouseMove = (event) => {
156
+ mousePosition.value = { x: event.clientX, y: event.clientY };
157
+ handleMouseMove(event);
158
+ };
159
+ document.addEventListener("keydown", handleKeyDown, true);
160
+ window.addEventListener("mousemove", handleGlobalMouseMove);
161
+ window.addEventListener("mouseup", handleMouseUp);
162
+ onBeforeUnmount(() => {
163
+ document.removeEventListener("keydown", handleKeyDown, true);
164
+ window.removeEventListener("mousemove", handleGlobalMouseMove);
165
+ window.removeEventListener("mouseup", handleMouseUp);
166
+ });
167
+ });
168
+ onNodeDragStop(async (event) => {
169
+ const { node } = event;
170
+ await updateNodePosition(node.id, node.position.x, node.position.y);
171
+ });
172
+ onConnect(async (params) => {
173
+ await createEdge(params);
174
+ });
175
+ onEdgeUpdate(({ edge, connection }) => {
176
+ if (connection.source && connection.target) {
177
+ reconnectEdge(edge.id, connection);
178
+ }
179
+ });
180
+ const defaultEdgeOptions = computed(() => {
181
+ const dasharray = FLOW_EDGE_STROKE_TYPES.find((t) => t.type === edgeStrokeType.value)?.dasharray || "";
182
+ return {
183
+ style: {
184
+ strokeWidth: edgeStrokeWidth.value,
185
+ ...dasharray ? { strokeDasharray: dasharray } : {},
186
+ ...effectiveEdgeColor.value ? { stroke: effectiveEdgeColor.value } : {}
187
+ },
188
+ // Markers are handled by FlowEdge custom SVG markers
189
+ markerStart: void 0,
190
+ markerEnd: void 0,
191
+ animated: false
192
+ };
193
+ });
194
+ const isValidConnection = () => true;
195
+ </script>
196
+
197
+ <template>
198
+ <VueFlow
199
+ v-model:nodes="nodes"
200
+ v-model:edges="edges"
201
+ :default-zoom="defaultZoom"
202
+ :min-zoom="minZoom"
203
+ :max-zoom="maxZoom"
204
+ :snap-to-grid="true"
205
+ :snap-grid="[GRID_SIZE, GRID_SIZE]"
206
+ :default-edge-options="defaultEdgeOptions"
207
+ :is-valid-connection="isValidConnection"
208
+ :edges-updatable="true"
209
+ >
210
+ <Background
211
+ v-if="showBackground"
212
+ pattern-color="var(--ui-bg-elevated)"
213
+ :gap="GRID_SIZE"
214
+ variant="lines"
215
+ />
216
+
217
+ <template #node-custom="nodeProps">
218
+ <FlowNode v-bind="nodeProps">
219
+ <template v-if="$slots.node" #default="{ data }">
220
+ <slot name="node" :data="data" />
221
+ </template>
222
+ </FlowNode>
223
+ </template>
224
+
225
+ <template #edge-custom="edgeProps">
226
+ <FlowEdge v-bind="edgeProps" />
227
+ </template>
228
+
229
+ <Panel v-if="showToolbar" position="bottom-center">
230
+ <FlowToolbar
231
+ :on-add-node="createNode"
232
+ :loading="loading"
233
+ :edge-stroke-width="edgeStrokeWidth"
234
+ :edge-stroke-type="edgeStrokeType"
235
+ :edge-path-type="edgePathType"
236
+ :edge-marker-start="edgeMarkerStart"
237
+ :edge-marker-end="edgeMarkerEnd"
238
+ :edge-animated="edgeAnimated"
239
+ :edge-color="edgeColor"
240
+ :edge-label-color="edgeLabelColor"
241
+ :node-border-width="nodeBorderWidth"
242
+ :node-border-radius="nodeBorderRadius"
243
+ :node-border-color="nodeBorderColor"
244
+ :node-bg-color="nodeBgColor"
245
+ :node-font-color="nodeFontColor"
246
+ :node-font-size="nodeFontSize"
247
+ :node-handle-size="nodeHandleSize"
248
+ :node-handle-color="nodeHandleColor"
249
+ :on-edge-stroke-width-change="(v) => edgeStrokeWidth = v"
250
+ :on-edge-stroke-type-change="(v) => edgeStrokeType = v"
251
+ :on-edge-path-type-change="(v) => edgePathType = v"
252
+ :on-edge-marker-start-change="(v) => edgeMarkerStart = v"
253
+ :on-edge-marker-end-change="(v) => edgeMarkerEnd = v"
254
+ :on-toggle-edge-animated="() => edgeAnimated = !edgeAnimated"
255
+ :on-edge-color-change="(v) => edgeColor = v"
256
+ :on-edge-label-color-change="(v) => edgeLabelColor = v"
257
+ :on-node-border-width-change="(v) => nodeBorderWidth = v"
258
+ :on-node-border-radius-change="(v) => nodeBorderRadius = v"
259
+ :on-node-border-color-change="(v) => nodeBorderColor = v"
260
+ :on-node-bg-color-change="(v) => nodeBgColor = v"
261
+ :on-node-font-color-change="(v) => nodeFontColor = v"
262
+ :on-node-font-size-change="(v) => nodeFontSize = v"
263
+ :on-node-handle-size-change="(v) => nodeHandleSize = v"
264
+ :on-node-handle-color-change="(v) => nodeHandleColor = v"
265
+ :color-mode="colorMode"
266
+ :unified-color="unifiedColor"
267
+ :is-unified-mode="isUnifiedMode"
268
+ :on-color-mode-change="(v) => colorMode = v"
269
+ :on-unified-color-change="(v) => unifiedColor = v"
270
+ />
271
+ </Panel>
272
+
273
+ <Panel v-if="showStats" position="top-right">
274
+ <div class="flex items-center gap-2">
275
+ <Transition name="fade">
276
+ <UIcon
277
+ v-if="loading"
278
+ name="i-lucide-loader-2"
279
+ class="text-primary animate-spin"
280
+ size="18"
281
+ />
282
+ </Transition>
283
+ <FlowStats :node-count="nodes.length" :edge-count="edges.length" />
284
+ </div>
285
+ </Panel>
286
+
287
+ <!-- 透传额外的 Panel 插槽 -->
288
+ <slot />
289
+ </VueFlow>
290
+ </template>
291
+
292
+ <style>
293
+ @import "@vue-flow/core/dist/style.css";@import "@vue-flow/core/dist/theme-default.css";.fade-enter-active,.fade-leave-active{transition:opacity .2s ease}.fade-enter-from,.fade-leave-to{opacity:0}
294
+ </style>
@@ -0,0 +1,51 @@
1
+ import type { Flow, FlowNode as FlowNodeType, FlowApi } from '#v/types';
2
+ type __VLS_Props = {
3
+ /** Flow 数据模型 (v-model) */
4
+ modelValue?: Flow;
5
+ /** CRUD API 回调,节点/边增删改时调用 */
6
+ api?: FlowApi;
7
+ /** 是否显示背景网格 */
8
+ showBackground?: boolean;
9
+ /** 是否显示工具栏 */
10
+ showToolbar?: boolean;
11
+ /** 是否显示统计面板 */
12
+ showStats?: boolean;
13
+ /** 最小缩放 */
14
+ minZoom?: number;
15
+ /** 最大缩放 */
16
+ maxZoom?: number;
17
+ /** 默认缩放 */
18
+ defaultZoom?: number;
19
+ };
20
+ declare var __VLS_21: {
21
+ data: any;
22
+ }, __VLS_62: {};
23
+ type __VLS_Slots = {} & {
24
+ node?: (props: typeof __VLS_21) => any;
25
+ } & {
26
+ default?: (props: typeof __VLS_62) => any;
27
+ };
28
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
29
+ "update:modelValue": (value: Flow) => any;
30
+ "edit-node": (node: FlowNodeType) => any;
31
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
32
+ "onUpdate:modelValue"?: ((value: Flow) => any) | undefined;
33
+ "onEdit-node"?: ((node: FlowNodeType) => any) | undefined;
34
+ }>, {
35
+ modelValue: Flow;
36
+ api: FlowApi;
37
+ showBackground: boolean;
38
+ showToolbar: boolean;
39
+ showStats: boolean;
40
+ minZoom: number;
41
+ maxZoom: number;
42
+ defaultZoom: number;
43
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
44
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
45
+ declare const _default: typeof __VLS_export;
46
+ export default _default;
47
+ type __VLS_WithSlots<T, S> = T & {
48
+ new (): {
49
+ $slots: S;
50
+ };
51
+ };
@@ -1,8 +1,19 @@
1
1
  type __VLS_Props = {
2
2
  data: any;
3
- hoveredNodeId: string | null;
4
3
  selected?: boolean;
5
4
  };
6
- declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
5
+ declare var __VLS_8: {
6
+ data: any;
7
+ };
8
+ type __VLS_Slots = {} & {
9
+ default?: (props: typeof __VLS_8) => any;
10
+ };
11
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
12
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
7
13
  declare const _default: typeof __VLS_export;
8
14
  export default _default;
15
+ type __VLS_WithSlots<T, S> = T & {
16
+ new (): {
17
+ $slots: S;
18
+ };
19
+ };
@@ -1,79 +1,75 @@
1
1
  <script setup>
2
- import { ref, computed, onMounted, onBeforeUnmount } from "vue";
2
+ import { computed } from "vue";
3
3
  import { Handle } from "@vue-flow/core";
4
- import { FLOW_HANDLES } from "#v/constants";
4
+ import { FLOW_RESIZE_HANDLES, GRID_SIZE } from "#v/constants";
5
+ import { useFlowNode } from "#v/composables";
5
6
  const props = defineProps({
6
7
  data: { type: null, required: true },
7
- hoveredNodeId: { type: [String, null], required: true },
8
8
  selected: { type: Boolean, required: false }
9
9
  });
10
- const nodeRef = ref(null);
11
- const mousePosition = ref({ x: 0, y: 0 });
12
- const PROXIMITY_THRESHOLD = 50;
13
- const borderColor = computed(
14
- () => props.selected ? "var(--ui-primary)" : "var(--ui-border-default)"
15
- );
16
- const isNearby = computed(() => {
17
- if (!nodeRef.value) return false;
18
- const rect = nodeRef.value.getBoundingClientRect();
19
- const mouseX = mousePosition.value.x;
20
- const mouseY = mousePosition.value.y;
21
- const dx = Math.max(rect.left - mouseX, 0, mouseX - rect.right);
22
- const dy = Math.max(rect.top - mouseY, 0, mouseY - rect.bottom);
23
- const distance = Math.sqrt(dx * dx + dy * dy);
24
- return distance <= PROXIMITY_THRESHOLD;
10
+ const {
11
+ nodeRef,
12
+ isHoveredLocal,
13
+ borderColor,
14
+ activeHandles,
15
+ handleStyle
16
+ } = useFlowNode({
17
+ data: computed(() => props.data),
18
+ selected: computed(() => props.selected ?? false)
25
19
  });
26
- const isHovered = computed(() => props.hoveredNodeId === String(props.data.id));
27
- const showHandles = computed(() => isHovered.value || isNearby.value);
28
- const handleGlobalMouseMove = (e) => {
29
- mousePosition.value = { x: e.clientX, y: e.clientY };
20
+ const setNodeRef = (el) => {
21
+ nodeRef.value = el instanceof HTMLElement ? el : null;
30
22
  };
31
- onMounted(() => {
32
- window.addEventListener("mousemove", handleGlobalMouseMove);
33
- });
34
- onBeforeUnmount(() => {
35
- window.removeEventListener("mousemove", handleGlobalMouseMove);
36
- });
37
23
  </script>
38
24
 
39
25
  <template>
40
26
  <div
41
- ref="nodeRef"
42
- class="bg-background border rounded-md px-3 py-2 relative flex"
27
+ :ref="setNodeRef"
28
+ class="bg-background border relative flex"
43
29
  :style="{
44
- width: data.width ? `${data.width}px` : '128px',
30
+ boxSizing: 'border-box',
31
+ width: data.width ? `${data.width}px` : GRID_SIZE * 6 + 'px',
45
32
  height: data.height ? `${data.height}px` : 'auto',
46
- minWidth: '128px',
33
+ minWidth: GRID_SIZE * 2 + 'px',
34
+ minHeight: GRID_SIZE * 2 + 'px',
47
35
  borderWidth: data.borderWidth ? `${data.borderWidth}px` : '2px',
48
36
  borderStyle: 'solid',
49
- borderColor
37
+ borderColor,
38
+ borderRadius: data.borderRadius !== void 0 ? `${data.borderRadius}px` : '6px',
39
+ ...data.bgColor ? { backgroundColor: data.bgColor } : {},
40
+ ...data.fontColor ? { color: data.fontColor } : {},
41
+ fontSize: data.fontSize ? `${data.fontSize}px` : '14px'
50
42
  }"
51
- @mouseenter="data.onMouseEnter?.()"
52
- @mouseleave="data.onMouseLeave?.()"
53
- @mousemove="(e) => data.onMouseMove?.(e, e.currentTarget)"
43
+ @mouseenter="isHoveredLocal = true"
44
+ @mouseleave="isHoveredLocal = false"
54
45
  @dblclick="data.onEdit?.()"
55
46
  >
56
- <!-- 连接点 - 可以作为 source target -->
47
+ <!-- Connection handles (z-index 高于 resize handles,优先响应连线拖拽) -->
57
48
  <Handle
58
- v-for="handle in FLOW_HANDLES"
49
+ v-for="handle in activeHandles"
59
50
  :id="`${handle.id}`"
60
51
  :key="`${handle.id}`"
61
52
  type="source"
62
53
  :position="handle.position"
63
- :style="{
64
- left: handle.offsetPercent?.x !== void 0 ? `${handle.offsetPercent.x}%` : void 0,
65
- top: handle.offsetPercent?.y !== void 0 ? `${handle.offsetPercent.y}%` : void 0,
66
- pointerEvents: 'all',
67
- opacity: showHandles ? 1 : 0,
68
- transition: 'opacity 0.2s',
69
- width: '6px',
70
- height: '6px'
71
- }"
54
+ :style="handleStyle(handle)"
72
55
  @mousedown.stop
73
56
  />
74
57
 
58
+ <!-- content -->
75
59
  <div class="flex items-center justify-center gap-2 w-full">
76
- <span class="text-sm font-medium">{{ data.name }}</span>
60
+ <slot :data="data">
61
+ <span class="font-medium">{{ data.name }}</span>
62
+ </slot>
77
63
  </div>
64
+
65
+ <!-- Resize handles (z-index 低于 connection handles,连线优先) -->
66
+ <div
67
+ v-for="rh in FLOW_RESIZE_HANDLES"
68
+ :key="rh.edge"
69
+ class="absolute nodrag"
70
+ :class="rh.class"
71
+ :style="{ zIndex: rh.zIndex, cursor: rh.cursor }"
72
+ @mousedown.stop.prevent="data.onResizeStart?.($event, rh.edge)"
73
+ />
78
74
  </div>
79
75
  </template>
@@ -1,8 +1,19 @@
1
1
  type __VLS_Props = {
2
2
  data: any;
3
- hoveredNodeId: string | null;
4
3
  selected?: boolean;
5
4
  };
6
- declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
5
+ declare var __VLS_8: {
6
+ data: any;
7
+ };
8
+ type __VLS_Slots = {} & {
9
+ default?: (props: typeof __VLS_8) => any;
10
+ };
11
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
12
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
7
13
  declare const _default: typeof __VLS_export;
8
14
  export default _default;
15
+ type __VLS_WithSlots<T, S> = T & {
16
+ new (): {
17
+ $slots: S;
18
+ };
19
+ };
@@ -1,13 +1,49 @@
1
+ import type { FlowEdgeStrokeType, FlowEdgePathType, FlowArrowType } from '#v/constants';
2
+ import type { FlowColorMode } from '#v/composables/flow/useFlowStyles';
1
3
  type __VLS_Props = {
2
4
  onAddNode?: () => void;
5
+ /** 是否有 API 操作正在进行,用于禁用交互 */
6
+ loading?: boolean;
3
7
  edgeStrokeWidth?: number;
4
- edgeMarkerStart?: boolean;
5
- edgeMarkerEnd?: boolean;
8
+ edgeStrokeType?: FlowEdgeStrokeType;
9
+ edgePathType?: FlowEdgePathType;
10
+ edgeMarkerStart?: FlowArrowType;
11
+ edgeMarkerEnd?: FlowArrowType;
12
+ edgeAnimated?: boolean;
13
+ edgeColor?: string;
14
+ edgeLabelColor?: string;
6
15
  nodeBorderWidth?: number;
7
- onEdgeStrokeWidthChange?: (width: number) => void;
8
- onToggleEdgeMarkerStart?: () => void;
9
- onToggleEdgeMarkerEnd?: () => void;
16
+ nodeBorderRadius?: number;
17
+ nodeBorderColor?: string;
18
+ nodeBgColor?: string;
19
+ nodeFontColor?: string;
20
+ nodeFontSize?: number;
21
+ nodeHandleSize?: number;
22
+ nodeHandleColor?: string;
23
+ /** 颜色模式 */
24
+ colorMode?: FlowColorMode;
25
+ /** 统一颜色名称 */
26
+ unifiedColor?: string;
27
+ /** 是否统一模式 */
28
+ isUnifiedMode?: boolean;
29
+ onEdgeStrokeWidthChange: (width: number) => void;
30
+ onEdgeStrokeTypeChange?: (type: FlowEdgeStrokeType) => void;
31
+ onEdgePathTypeChange?: (type: FlowEdgePathType) => void;
32
+ onEdgeMarkerStartChange?: (type: FlowArrowType) => void;
33
+ onEdgeMarkerEndChange?: (type: FlowArrowType) => void;
34
+ onToggleEdgeAnimated?: () => void;
35
+ onEdgeColorChange?: (color: string) => void;
36
+ onEdgeLabelColorChange?: (color: string) => void;
10
37
  onNodeBorderWidthChange?: (width: number) => void;
38
+ onNodeBorderRadiusChange?: (radius: number) => void;
39
+ onNodeBorderColorChange?: (color: string) => void;
40
+ onNodeBgColorChange?: (color: string) => void;
41
+ onNodeFontColorChange?: (color: string) => void;
42
+ onNodeFontSizeChange?: (size: number) => void;
43
+ onNodeHandleSizeChange?: (size: number) => void;
44
+ onNodeHandleColorChange?: (color: string) => void;
45
+ onColorModeChange?: (mode: FlowColorMode) => void;
46
+ onUnifiedColorChange?: (name: string) => void;
11
47
  };
12
48
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
13
49
  declare const _default: typeof __VLS_export;