@mx-sose-front/mx-sose-graph 1.1.6 → 1.1.7

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/style.css CHANGED
@@ -1 +1 @@
1
- @charset "UTF-8";.selection-box[data-v-98567c09]{pointer-events:none;background:transparent}.resize-handles[data-v-98567c09]{position:relative;width:100%;height:100%}.resize-handle[data-v-98567c09]{position:absolute;width:10px;height:10px;background-color:#007bff;border:2px solid #fff;border-radius:50%;pointer-events:all;transition:all .2s ease;box-shadow:0 2px 4px #0000004d;z-index:999}.resize-handle.is-disabled[data-v-98567c09]{cursor:default!important}.resize-handle[data-v-98567c09]:hover{background-color:#0056b3;transform:scale(1.2)}.action-buttons[data-v-98567c09]{display:flex;flex-direction:column;gap:4px;pointer-events:all;background:rgba(255,255,255,.95);padding:6px;border-radius:4px;box-shadow:0 2px 8px #00000026;border:1px solid #e0e0e0;backdrop-filter:blur(2px)}.action-btn[data-v-98567c09]{width:28px;height:28px;border:1px solid #d0d0d0;border-radius:3px;background:linear-gradient(to bottom,#f8f9fa,#e9ecef);cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;font-family:Arial,sans-serif;color:#495057;transition:all .2s ease;box-shadow:0 1px 2px #0000000d}.action-btn[data-v-98567c09]:hover{background:linear-gradient(to bottom,#e3f2fd,#bbdefb);border-color:#90caf9;transform:translateY(-1px);box-shadow:0 2px 4px #2196f34d;color:#1976d2}.edit-btn[data-v-98567c09]:hover{background:#e3f2fd}.border-btn[data-v-98567c09]{padding-bottom:4px;border-bottom:1px solid #e0e0e0}.name-text-box-container[data-v-f0d3e2ba]{position:absolute;z-index:1001;pointer-events:all}.name-text-box[data-v-f0d3e2ba]{border:1px dashed #007bff;background:rgba(255,255,255,.2);cursor:pointer;pointer-events:all;transition:all .2s ease;border-radius:4px;box-shadow:0 0 0 1px #007bff1a;height:100%;display:flex;align-items:center;justify-content:center}.name-text-box[data-v-f0d3e2ba]:hover{border-color:#0056b3;background:rgba(0,123,255,.05);box-shadow:0 0 0 2px #007bff33;transform:scale(1.02)}.name-editor-container[data-v-f0d3e2ba]{position:absolute;z-index:1002;display:flex;justify-content:center;align-items:center;pointer-events:all}.name-input[data-v-f0d3e2ba]{width:calc(100% - 20px);padding:2px 4px;border:2px solid #007bff;border-radius:6px;font-size:12px;font-weight:600;background:#fff;box-shadow:0 4px 12px #00000026;outline:none;text-align:center;font-family:inherit;resize:none;overflow:hidden}.name-input[data-v-f0d3e2ba]:focus{border-color:#0056b3;box-shadow:0 0 0 3px #007bff40}.context-menu[data-v-0f7f66ba]{background-color:#fff;border:1px solid #ebeef5;border-radius:6px;box-shadow:0 2px 12px #0000001a;padding:6px 0;overflow-y:auto;box-sizing:border-box;transition:opacity .2s ease,transform .2s ease;transform-origin:top left}.context-menu[style*="display: block"][data-v-0f7f66ba]{animation:fadeIn-0f7f66ba .2s ease forwards}@keyframes fadeIn-0f7f66ba{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}.context-menu[data-v-0f7f66ba]::-webkit-scrollbar{width:6px}.context-menu[data-v-0f7f66ba]::-webkit-scrollbar-thumb{background-color:#ddd;border-radius:3px}.context-menu[data-v-0f7f66ba]::-webkit-scrollbar-track{background-color:#f5f5f5}.menu-item[data-v-0f7f66ba]{padding:0 15px;cursor:pointer;font-size:14px;color:#606266;border-radius:4px;height:36px;line-height:36px;box-sizing:border-box}.menu-item[data-v-0f7f66ba]:hover{background-color:#f5f7fa;color:#303133}.menu-disabled[data-v-0f7f66ba]{color:#c0c4cc!important;cursor:not-allowed!important;pointer-events:none}.menu-disabled[data-v-0f7f66ba]:hover{background-color:transparent!important}.menu-disabled img[data-v-0f7f66ba]{opacity:.5}.context-menu[style*="--edge-hint: left"][data-v-0f7f66ba]{border-top-left-radius:0}.context-menu[style*="--edge-hint: right"][data-v-0f7f66ba]{border-top-right-radius:0}.context-menu[style*="--edge-hint: top"][data-v-0f7f66ba]{border-top-left-radius:0;border-top-right-radius:0}.context-menu[style*="--edge-hint: bottom"][data-v-0f7f66ba]{border-bottom-left-radius:0;border-bottom-right-radius:0}.display-tooltip[data-v-0f7f66ba]{background-color:#fff;border:1px solid #ebeef5;border-radius:6px;box-shadow:0 2px 12px #0000001a;padding:6px 0;min-width:120px;animation:tooltipFadeIn-0f7f66ba .2s ease forwards}.tooltip-content[data-v-0f7f66ba]{overflow:hidden}.tooltip-item[data-v-0f7f66ba]{padding:8px 16px;cursor:pointer;font-size:14px;color:#606266;border-radius:4px;transition:background-color .2s ease;display:flex;align-items:center}.tooltip-item[data-v-0f7f66ba]:hover{background-color:#f5f7fa;color:#303133}.tooltip-item span[data-v-0f7f66ba]{display:block}@keyframes tooltipFadeIn-0f7f66ba{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}.connection-line[data-v-c4c3e8c0]{position:absolute;pointer-events:none;z-index:10}.interaction-layer[data-v-a7475c72]{position:absolute;top:0;left:0;width:100%;height:100%;transform-origin:0 0;transform:scale(var(--c994dcd8));pointer-events:all;z-index:999}.hover-container-outline[data-v-a7475c72]{position:absolute;pointer-events:none;padding:6px;top:6px}.hover-container-outline.is-valid[data-v-a7475c72]{border:2px solid #0000ff}.hover-container-outline.is-invalid[data-v-a7475c72]{border:2px solid #f1eded;background:rgba(240,237,237,.842)}.resize-ghost[data-v-a7475c72]{position:absolute;border:2px solid #8a6d3b;background:transparent;box-shadow:0 0 0 2px #8a6d3b26 inset;pointer-events:none;z-index:9999}.connect-layer[data-v-a7475c72]{position:absolute;pointer-events:none;z-index:1000}.connect-dot-direct[data-v-a7475c72],.connect-dot-target[data-v-a7475c72]{width:8px;height:8px;background-color:#000;border-radius:50%;border:1px solid #fff;box-shadow:0 0 4px #0000004d}.connection-line[data-v-a7475c72]{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:1000}.highlight-overlay[data-v-a7475c72]{position:absolute;pointer-events:none;box-sizing:border-box;z-index:1001;border-radius:2px}.highlight-overlay.blue[data-v-a7475c72]{border:3px solid #1890ff}.highlight-overlay.red[data-v-a7475c72]{border:3px solid #f56c6c}.ghost-outline[data-v-a7475c72]{position:absolute;border:4px solid #8a6d3b;background:transparent;pointer-events:none;border-radius:4px;z-index:9998;box-sizing:border-box}.diagram-shape[data-v-1f4cb409]{background:#e5ecf0;border-radius:10px;box-shadow:0 2px 8px #0000001a;display:flex;flex-direction:column;overflow:hidden;box-sizing:border-box;margin:0;padding:0;border:var(--bw) var(--bs) var(--bc)}.hit-edge[data-v-1f4cb409]{position:absolute;background:transparent;z-index:999;pointer-events:auto}.hit-top[data-v-1f4cb409]{left:0;right:0;top:0;height:var(--bw)}.hit-right[data-v-1f4cb409]{top:0;right:0;bottom:0;width:var(--bw)}.hit-bottom[data-v-1f4cb409]{left:0;right:0;bottom:0;height:var(--bw)}.hit-left[data-v-1f4cb409]{top:0;left:0;bottom:0;width:var(--bw)}.diagram-header[data-v-1f4cb409]{background:rgba(255,255,255,.5);padding:3px 16px 3px 5px;display:inline-flex;align-items:center;position:relative;white-space:nowrap;align-self:flex-start}.diagram-header[data-v-1f4cb409]:after{content:"";position:absolute;bottom:-12px;right:-12px;width:20px;height:20px;background-color:var(--5113555a);transform:rotate(45deg);z-index:0}.diagram-title[data-v-1f4cb409]{font-family:var(--name-ff);font-size:var(--name-fs);font-weight:400;line-height:normal;letter-spacing:0em;font-variation-settings:"opsz" auto;font-feature-settings:"kern" on;color:var(--name-color);position:relative;z-index:2}.diagram-shape[data-v-05edf0d1]{user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;pointer-events:auto;touch-action:none;-webkit-touch-action:none;-ms-touch-action:none;zoom:1;transform:none!important;-webkit-transform:none!important;-moz-transform:none!important;-ms-transform:none!important}.diagram-svg[data-v-05edf0d1]{width:100%;height:100%;transform:none!important;-webkit-transform:none!important}.capability-shape[data-v-5ba17051]{display:flex;align-items:center;justify-content:center;overflow:hidden}.capability-svg[data-v-5ba17051]{display:block}.capability-shape[data-v-8e8054b6]{display:flex;align-items:center;justify-content:center;overflow:hidden}.capability-svg[data-v-8e8054b6]{display:block}.capability-shape[data-v-27d34586]{display:flex;align-items:center;justify-content:center;overflow:hidden}.capability-svg[data-v-27d34586]{display:block}.capability-shape[data-v-5f0b14f5]{display:flex;align-items:center;justify-content:center;overflow:hidden}.capability-svg[data-v-5f0b14f5]{display:block}.capability-shape[data-v-4f0bb734]{display:inline-block;position:relative;overflow:visible;box-sizing:content-box}[data-v-4f0bb734] .name-text-svg{text-align:left}[data-v-4f0bb734] .shape-name-svg{text-align:center}[data-v-4f0bb734] .content-text-svg{font-size:13px;line-height:1.4;color:#000;font-family:Arial,sans-serif;min-height:20px;overflow:visible;width:100%;box-sizing:border-box}[data-v-4f0bb734] .content-item{margin-bottom:4px;word-break:break-word;white-space:normal;overflow:visible;width:100%;box-sizing:border-box}[data-v-4f0bb734] .content-item:last-child{margin-bottom:0}.edge-component[data-v-8ed11c37]{position:absolute;pointer-events:all;cursor:pointer}.edge-svg[data-v-8ed11c37]{width:100%;height:100%;overflow:visible}.edge-svg polyline[data-v-8ed11c37]{pointer-events:all;stroke-linecap:round;stroke-linejoin:round}.edge-svg circle[data-v-8ed11c37]{pointer-events:none}.diagram-shape[data-v-daf180ad]{-webkit-user-select:none;-moz-user-select:none;user-select:none}.diagram-svg[data-v-daf180ad]{width:100%;height:100%}.capability-shape[data-v-ec8f07ea]{display:flex;align-items:center;justify-content:center;overflow:hidden}.capability-svg[data-v-ec8f07ea]{display:block}.output-pin-shape[data-v-2081b7c7]{display:flex;align-items:center;justify-content:center;overflow:visible}.output-pin-svg[data-v-2081b7c7]{display:block;overflow:visible}.port-shape[data-v-4aa98dd6]{display:flex;align-items:center;justify-content:center;overflow:visible}.port-svg[data-v-4aa98dd6]{display:block;overflow:visible}.diagram-list-tooltip[data-v-4e4791fa]{position:fixed;background:#ffffff;border:1px solid #e5e7eb;border-radius:6px;padding:10px 6px;box-shadow:0 4px 12px #00000026;z-index:9999;width:180px;max-width:300px}.tooltip-title[data-v-4e4791fa]{padding:0 12px}.diagram-item{&[data-v-4e4791fa]{display:flex;align-items:center;padding:0 12px;cursor:pointer;border-radius:4px;height:36px;line-height:36px;transition:background-color .2s}.diagram-name[data-v-4e4791fa]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}}.diagram-item[data-v-4e4791fa]:hover{background-color:#f0f3f6}.diagram-icon[data-v-4e4791fa]{width:24px;height:24px;margin-right:6px}.graph-view[data-v-f90f1704]{width:100%;height:100%;background-color:#fff;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;position:relative;overflow:hidden;-webkit-user-select:none;-moz-user-select:none;user-select:none}.diagram-content[data-v-f90f1704]{position:absolute;inset:10px 0 10px 10px;overflow:auto;touch-action:none}.shapes-container[data-v-f90f1704]{position:relative;width:100%;height:100%;min-height:600px;transform-origin:0 0;transform:scale(var(--478247fa))}.shapes-container[data-v-f90f1704]>*{position:absolute;will-change:transform}.zoom-level[data-v-f90f1704]{position:absolute;bottom:30px;right:15px;font-size:10px;font-weight:500;min-width:35px;text-align:center;color:#333;background:rgba(255,255,255,.9);padding:2px 6px;border-radius:3px;box-shadow:0 1px 3px #0000001a}.cut-overlay-svg[data-v-f90f1704]{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:998}
1
+ @charset "UTF-8";.selection-box[data-v-98567c09]{pointer-events:none;background:transparent}.resize-handles[data-v-98567c09]{position:relative;width:100%;height:100%}.resize-handle[data-v-98567c09]{position:absolute;width:10px;height:10px;background-color:#007bff;border:2px solid #fff;border-radius:50%;pointer-events:all;transition:all .2s ease;box-shadow:0 2px 4px #0000004d;z-index:999}.resize-handle.is-disabled[data-v-98567c09]{cursor:default!important}.resize-handle[data-v-98567c09]:hover{background-color:#0056b3;transform:scale(1.2)}.action-buttons[data-v-98567c09]{display:flex;flex-direction:column;gap:4px;pointer-events:all;background:rgba(255,255,255,.95);padding:6px;border-radius:4px;box-shadow:0 2px 8px #00000026;border:1px solid #e0e0e0;backdrop-filter:blur(2px)}.action-btn[data-v-98567c09]{width:28px;height:28px;border:1px solid #d0d0d0;border-radius:3px;background:linear-gradient(to bottom,#f8f9fa,#e9ecef);cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;font-family:Arial,sans-serif;color:#495057;transition:all .2s ease;box-shadow:0 1px 2px #0000000d}.action-btn[data-v-98567c09]:hover{background:linear-gradient(to bottom,#e3f2fd,#bbdefb);border-color:#90caf9;transform:translateY(-1px);box-shadow:0 2px 4px #2196f34d;color:#1976d2}.edit-btn[data-v-98567c09]:hover{background:#e3f2fd}.border-btn[data-v-98567c09]{padding-bottom:4px;border-bottom:1px solid #e0e0e0}.name-text-box-container[data-v-f0d3e2ba]{position:absolute;z-index:1001;pointer-events:all}.name-text-box[data-v-f0d3e2ba]{border:1px dashed #007bff;background:rgba(255,255,255,.2);cursor:pointer;pointer-events:all;transition:all .2s ease;border-radius:4px;box-shadow:0 0 0 1px #007bff1a;height:100%;display:flex;align-items:center;justify-content:center}.name-text-box[data-v-f0d3e2ba]:hover{border-color:#0056b3;background:rgba(0,123,255,.05);box-shadow:0 0 0 2px #007bff33;transform:scale(1.02)}.name-editor-container[data-v-f0d3e2ba]{position:absolute;z-index:1002;display:flex;justify-content:center;align-items:center;pointer-events:all}.name-input[data-v-f0d3e2ba]{width:calc(100% - 20px);padding:2px 4px;border:2px solid #007bff;border-radius:6px;font-size:12px;font-weight:600;background:#fff;box-shadow:0 4px 12px #00000026;outline:none;text-align:center;font-family:inherit;resize:none;overflow:hidden}.name-input[data-v-f0d3e2ba]:focus{border-color:#0056b3;box-shadow:0 0 0 3px #007bff40}.context-menu[data-v-0f7f66ba]{background-color:#fff;border:1px solid #ebeef5;border-radius:6px;box-shadow:0 2px 12px #0000001a;padding:6px 0;overflow-y:auto;box-sizing:border-box;transition:opacity .2s ease,transform .2s ease;transform-origin:top left}.context-menu[style*="display: block"][data-v-0f7f66ba]{animation:fadeIn-0f7f66ba .2s ease forwards}@keyframes fadeIn-0f7f66ba{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}.context-menu[data-v-0f7f66ba]::-webkit-scrollbar{width:6px}.context-menu[data-v-0f7f66ba]::-webkit-scrollbar-thumb{background-color:#ddd;border-radius:3px}.context-menu[data-v-0f7f66ba]::-webkit-scrollbar-track{background-color:#f5f5f5}.menu-item[data-v-0f7f66ba]{padding:0 15px;cursor:pointer;font-size:14px;color:#606266;border-radius:4px;height:36px;line-height:36px;box-sizing:border-box}.menu-item[data-v-0f7f66ba]:hover{background-color:#f5f7fa;color:#303133}.menu-disabled[data-v-0f7f66ba]{color:#c0c4cc!important;cursor:not-allowed!important;pointer-events:none}.menu-disabled[data-v-0f7f66ba]:hover{background-color:transparent!important}.menu-disabled img[data-v-0f7f66ba]{opacity:.5}.context-menu[style*="--edge-hint: left"][data-v-0f7f66ba]{border-top-left-radius:0}.context-menu[style*="--edge-hint: right"][data-v-0f7f66ba]{border-top-right-radius:0}.context-menu[style*="--edge-hint: top"][data-v-0f7f66ba]{border-top-left-radius:0;border-top-right-radius:0}.context-menu[style*="--edge-hint: bottom"][data-v-0f7f66ba]{border-bottom-left-radius:0;border-bottom-right-radius:0}.display-tooltip[data-v-0f7f66ba]{background-color:#fff;border:1px solid #ebeef5;border-radius:6px;box-shadow:0 2px 12px #0000001a;padding:6px 0;min-width:120px;animation:tooltipFadeIn-0f7f66ba .2s ease forwards}.tooltip-content[data-v-0f7f66ba]{overflow:hidden}.tooltip-item[data-v-0f7f66ba]{padding:8px 16px;cursor:pointer;font-size:14px;color:#606266;border-radius:4px;transition:background-color .2s ease;display:flex;align-items:center}.tooltip-item[data-v-0f7f66ba]:hover{background-color:#f5f7fa;color:#303133}.tooltip-item span[data-v-0f7f66ba]{display:block}@keyframes tooltipFadeIn-0f7f66ba{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}.connection-line[data-v-c4c3e8c0]{position:absolute;pointer-events:none;z-index:10}.interaction-layer[data-v-24a22c8c]{position:absolute;top:0;left:0;width:100%;height:100%;transform-origin:0 0;transform:scale(var(--65f24407));pointer-events:all;z-index:999}.hover-container-outline[data-v-24a22c8c]{position:absolute;pointer-events:none;padding:6px;top:6px}.hover-container-outline.is-valid[data-v-24a22c8c]{border:2px solid #0000ff}.hover-container-outline.is-invalid[data-v-24a22c8c]{border:2px solid #f1eded;background:rgba(240,237,237,.842)}.resize-ghost[data-v-24a22c8c]{position:absolute;border:2px solid #8a6d3b;background:transparent;box-shadow:0 0 0 2px #8a6d3b26 inset;pointer-events:none;z-index:9999}.connect-layer[data-v-24a22c8c]{position:absolute;pointer-events:none;z-index:1000}.connect-dot-direct[data-v-24a22c8c],.connect-dot-target[data-v-24a22c8c]{width:8px;height:8px;background-color:#000;border-radius:50%;border:1px solid #fff;box-shadow:0 0 4px #0000004d}.connection-line[data-v-24a22c8c]{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:1000}.highlight-overlay[data-v-24a22c8c]{position:absolute;pointer-events:none;box-sizing:border-box;z-index:1001;border-radius:2px}.highlight-overlay.blue[data-v-24a22c8c]{border:3px solid #1890ff}.highlight-overlay.red[data-v-24a22c8c]{border:3px solid #f56c6c}.diagram-shape[data-v-1f4cb409]{background:#e5ecf0;border-radius:10px;box-shadow:0 2px 8px #0000001a;display:flex;flex-direction:column;overflow:hidden;box-sizing:border-box;margin:0;padding:0;border:var(--bw) var(--bs) var(--bc)}.hit-edge[data-v-1f4cb409]{position:absolute;background:transparent;z-index:999;pointer-events:auto}.hit-top[data-v-1f4cb409]{left:0;right:0;top:0;height:var(--bw)}.hit-right[data-v-1f4cb409]{top:0;right:0;bottom:0;width:var(--bw)}.hit-bottom[data-v-1f4cb409]{left:0;right:0;bottom:0;height:var(--bw)}.hit-left[data-v-1f4cb409]{top:0;left:0;bottom:0;width:var(--bw)}.diagram-header[data-v-1f4cb409]{background:rgba(255,255,255,.5);padding:3px 16px 3px 5px;display:inline-flex;align-items:center;position:relative;white-space:nowrap;align-self:flex-start}.diagram-header[data-v-1f4cb409]:after{content:"";position:absolute;bottom:-12px;right:-12px;width:20px;height:20px;background-color:var(--5113555a);transform:rotate(45deg);z-index:0}.diagram-title[data-v-1f4cb409]{font-family:var(--name-ff);font-size:var(--name-fs);font-weight:400;line-height:normal;letter-spacing:0em;font-variation-settings:"opsz" auto;font-feature-settings:"kern" on;color:var(--name-color);position:relative;z-index:2}.diagram-shape[data-v-05edf0d1]{user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;pointer-events:auto;touch-action:none;-webkit-touch-action:none;-ms-touch-action:none;zoom:1;transform:none!important;-webkit-transform:none!important;-moz-transform:none!important;-ms-transform:none!important}.diagram-svg[data-v-05edf0d1]{width:100%;height:100%;transform:none!important;-webkit-transform:none!important}.capability-shape[data-v-5ba17051]{display:flex;align-items:center;justify-content:center;overflow:hidden}.capability-svg[data-v-5ba17051]{display:block}.capability-shape[data-v-8e8054b6]{display:flex;align-items:center;justify-content:center;overflow:hidden}.capability-svg[data-v-8e8054b6]{display:block}.capability-shape[data-v-27d34586]{display:flex;align-items:center;justify-content:center;overflow:hidden}.capability-svg[data-v-27d34586]{display:block}.capability-shape[data-v-5f0b14f5]{display:flex;align-items:center;justify-content:center;overflow:hidden}.capability-svg[data-v-5f0b14f5]{display:block}.capability-shape[data-v-4f0bb734]{display:inline-block;position:relative;overflow:visible;box-sizing:content-box}[data-v-4f0bb734] .name-text-svg{text-align:left}[data-v-4f0bb734] .shape-name-svg{text-align:center}[data-v-4f0bb734] .content-text-svg{font-size:13px;line-height:1.4;color:#000;font-family:Arial,sans-serif;min-height:20px;overflow:visible;width:100%;box-sizing:border-box}[data-v-4f0bb734] .content-item{margin-bottom:4px;word-break:break-word;white-space:normal;overflow:visible;width:100%;box-sizing:border-box}[data-v-4f0bb734] .content-item:last-child{margin-bottom:0}.edge-component[data-v-8ed11c37]{position:absolute;pointer-events:all;cursor:pointer}.edge-svg[data-v-8ed11c37]{width:100%;height:100%;overflow:visible}.edge-svg polyline[data-v-8ed11c37]{pointer-events:all;stroke-linecap:round;stroke-linejoin:round}.edge-svg circle[data-v-8ed11c37]{pointer-events:none}.diagram-shape[data-v-daf180ad]{-webkit-user-select:none;-moz-user-select:none;user-select:none}.diagram-svg[data-v-daf180ad]{width:100%;height:100%}.capability-shape[data-v-ec8f07ea]{display:flex;align-items:center;justify-content:center;overflow:hidden}.capability-svg[data-v-ec8f07ea]{display:block}.output-pin-shape[data-v-2081b7c7]{display:flex;align-items:center;justify-content:center;overflow:visible}.output-pin-svg[data-v-2081b7c7]{display:block;overflow:visible}.port-shape[data-v-4aa98dd6]{display:flex;align-items:center;justify-content:center;overflow:visible}.port-svg[data-v-4aa98dd6]{display:block;overflow:visible}.diagram-list-tooltip[data-v-4e4791fa]{position:fixed;background:#ffffff;border:1px solid #e5e7eb;border-radius:6px;padding:10px 6px;box-shadow:0 4px 12px #00000026;z-index:9999;width:180px;max-width:300px}.tooltip-title[data-v-4e4791fa]{padding:0 12px}.diagram-item{&[data-v-4e4791fa]{display:flex;align-items:center;padding:0 12px;cursor:pointer;border-radius:4px;height:36px;line-height:36px;transition:background-color .2s}.diagram-name[data-v-4e4791fa]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}}.diagram-item[data-v-4e4791fa]:hover{background-color:#f0f3f6}.diagram-icon[data-v-4e4791fa]{width:24px;height:24px;margin-right:6px}.graph-view[data-v-d4982c78]{width:100%;height:100%;background-color:#fff;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;position:relative;overflow:hidden;-webkit-user-select:none;-moz-user-select:none;user-select:none}.diagram-content[data-v-d4982c78]{position:absolute;inset:10px 0 10px 10px;overflow:auto;touch-action:none}.shapes-container[data-v-d4982c78]{position:relative;width:100%;height:100%;min-height:600px;transform-origin:0 0;transform:scale(var(--18ea7fc8))}.shapes-container[data-v-d4982c78]>*{position:absolute;will-change:transform}.zoom-level[data-v-d4982c78]{position:absolute;bottom:30px;right:15px;font-size:10px;font-weight:500;min-width:35px;text-align:center;color:#333;background:rgba(255,255,255,.9);padding:2px 6px;border-radius:3px;box-shadow:0 1px 3px #0000001a}.cut-overlay-svg[data-v-d4982c78]{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:998}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mx-sose-front/mx-sose-graph",
3
- "version": "1.1.6",
3
+ "version": "1.1.7",
4
4
  "description": "A Vue3 graph visualization plugin library",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",
@@ -26,24 +26,9 @@
26
26
  <!-- 框选预览矩形 -->
27
27
  <div v-show="marqueeRect" class="marquee-rect"
28
28
  :style="getMarqueeStyle(marqueeRect || { x: 0, y: 0, width: 0, height: 0 })" />
29
- <!-- 拖动和缩放的预览框 - 使用简化的 div 轮廓框,性能更好 -->
30
- <div
31
- v-for="g in dragGhostRects"
32
- :key="g.id"
33
- class="ghost-outline"
34
- :style="{
35
- left: g.x + 'px',
36
- top: g.y + 'px',
37
- width: g.width + 'px',
38
- height: g.height + 'px',
39
- }"
40
- />
41
- <!-- 缩放预览框仍使用完整组件(数量少,需要看到内容变化) -->
42
- <component v-for="g in resizeGhosts" :key="g.id" class="ghost-shape" :is="getShapeComponent(g)" :shape="g"
29
+ <!-- 拖动和缩放的预览框 -->
30
+ <component v-for="g in allGhosts" :key="g.id" class="ghost-shape" :is="getShapeComponent(g)" :shape="g"
43
31
  :style="getGhostShapeStyle(g)" />
44
- <!-- 高亮图元 -->
45
- <component v-if="highlightedGhost" :key="highlightedGhost.id" class="ghost-shape" :is="getShapeComponent(highlightedGhost)" :shape="highlightedGhost"
46
- :style="getGhostShapeStyle(highlightedGhost)" />
47
32
  <!-- 名称编辑组件 -->
48
33
  <NameEditor :selected-shape="graphStore.selectedShape" :can-edit="!graphStore.pendingNestedIds.includes(
49
34
  graphStore.selectedShape?.id || ''
@@ -172,71 +157,7 @@ const hoverRect = computed(() => {
172
157
  const b = getBounds(s);
173
158
  return { x: b.x, y: b.y, width: b.width, height: b.height };
174
159
  });
175
-
176
- // 拖动预览框 - 只返回简单的矩形数据,用于渲染轻量级 div
177
- type GhostRect = { id: string; x: number; y: number; width: number; height: number };
178
-
179
- const dragGhostRects = computed<GhostRect[]>(() => {
180
- if (!graphStore.isDragging || isExternalCreateDragging.value) return [];
181
-
182
- const rects: GhostRect[] = [];
183
- const dragBase = graphStore.dragBase;
184
- const descendantsSnapshot = graphStore.dragDescendantsSnapshot;
185
-
186
- for (const ghost of graphStore.ghostShadow) {
187
- if (!ghost.bounds) continue;
188
-
189
- // 从 dragBase 获取拖动开始时的坐标(而不是实时坐标)
190
- const baseBounds = dragBase[ghost.id];
191
- if (!baseBounds) continue;
192
-
193
- // 计算位移量
194
- const dx = (ghost.bounds.x ?? 0) - baseBounds.x;
195
- const dy = (ghost.bounds.y ?? 0) - baseBounds.y;
196
-
197
- // 添加父元素预览框
198
- rects.push({
199
- id: ghost.id,
200
- x: ghost.bounds.x ?? 0,
201
- y: ghost.bounds.y ?? 0,
202
- width: ghost.bounds.width ?? 0,
203
- height: ghost.bounds.height ?? 0,
204
- });
205
-
206
- // 从快照中获取后代,应用位移(不需要每帧递归收集)
207
- const cachedDescendants = descendantsSnapshot.get(ghost.id);
208
- if (cachedDescendants && cachedDescendants.length > 0) {
209
- for (const desc of cachedDescendants) {
210
- rects.push({
211
- id: desc.id,
212
- x: desc.x + dx,
213
- y: desc.y + dy,
214
- width: desc.width,
215
- height: desc.height,
216
- });
217
- }
218
- }
219
- }
220
- return rects;
221
- });
222
-
223
- // 缩放预览框 - 需要完整渲染组件
224
- const resizeGhosts = computed<Shape[]>(() => resizeGhostShadow.value);
225
-
226
- // 高亮图元的 ghost
227
- const highlightedGhost = computed<Shape | null>(() => {
228
- if (!highlightedShape.value) return null;
229
- return {
230
- ...highlightedShape.value,
231
- meta: {
232
- ...(highlightedShape.value as any).meta,
233
- isHighlighted: true,
234
- isGhost: true,
235
- },
236
- } as Shape;
237
- });
238
-
239
- // 保留 allGhosts 用于兼容(如果其他地方用到)
160
+ // 把拖动缩放预览框合并,
240
161
  const allGhosts = computed<Shape[]>(() => {
241
162
  const byId = new Map<string, Shape>();
242
163
  // 收集所有Ghost形状
@@ -1854,15 +1775,4 @@ defineExpose({
1854
1775
  .highlight-overlay.red {
1855
1776
  border: 3px solid #f56c6c;
1856
1777
  }
1857
-
1858
- /* 拖动预览框 - 简化的轮廓框,性能更好 */
1859
- .ghost-outline {
1860
- position: absolute;
1861
- border: 4px solid #8a6d3b;
1862
- background: transparent;
1863
- pointer-events: none;
1864
- border-radius: 4px;
1865
- z-index: 9998;
1866
- box-sizing: border-box;
1867
- }
1868
1778
  </style>
@@ -1,5 +1,5 @@
1
- import { defineStore } from 'pinia'
2
- import { ref, computed, shallowReactive, shallowRef, triggerRef, nextTick, type Ref } from 'vue'
1
+ import { defineStore } from 'pinia'
2
+ import { ref, computed, shallowReactive, nextTick, type Ref } from 'vue'
3
3
  import type { Shape, OwnerPayload, ShapeSizeUpdatePayload } from '../types'
4
4
  import { eventBus } from './eventBus'
5
5
  import { getPolicy, checkNestViaFront, } from '../utils/policy'
@@ -74,132 +74,43 @@ export const useGraphStore = defineStore('graph', () => {
74
74
  const cutShapeIds = ref<Set<string>>(new Set())
75
75
  // 剪贴板中图元的数量(用于工具栏按钮状态同步)
76
76
  const copiedShapesCount = ref<number>(0)
77
- // 拖动时的后代坐标快照(只在 startDrag 时计算一次,避免每帧重算)
78
- // key: 被拖动的父元素 ID, value: 所有后代的坐标快照
79
- type DescendantSnapshot = { id: string; x: number; y: number; width: number; height: number };
80
- const dragDescendantsSnapshot = ref<Map<string, DescendantSnapshot[]>>(new Map())
81
77
  // 计算属性
82
78
  const shapeCount = computed(() => shapes.value.length)
83
79
  const hasSelectedShape = computed(() => selectedShape.value !== null)
84
80
 
85
- // ========== 增量索引:手动维护,避免 computed 全量重建 ==========
86
-
87
81
  /**
88
- * 图元ID索引映射(增量维护版本)
89
- * 用于快速通过ID查找图元对象
90
- * key: 图元 ID, value: 图元对象
91
- * 性能优化: O(1) 增删改查,避免每次 shapes 变化都 O(n) 重建
92
- */
93
- const _shapeMapRef = shallowRef(new Map<string, Shape>())
94
- // 使用 computed 包装,确保 Pinia 正确解包
95
- const shapeMap = computed(() => _shapeMapRef.value)
96
-
97
- /**
98
- * 父子关系索引映射(增量维护版本)
82
+ * 父子关系索引映射
99
83
  * 用于快速查找某个图元的所有直接子图元
100
84
  * key: 父图元 ID, value: 子图元 ID 数组
101
- * 性能优化: O(1) 查询,增删时只更新受影响的条目
85
+ * 性能优化: O(n) 构建, O(1) 查询
102
86
  */
103
- const _parentChildMapRef = shallowRef(new Map<string, string[]>())
104
- // 使用 computed 包装,确保 Pinia 正确解包
105
- const parentChildMap = computed(() => _parentChildMapRef.value)
106
-
107
- // ========== 索引维护内部方法 ==========
108
-
109
- /** 添加图元到索引 */
110
- function _indexAdd(shape: Shape) {
111
- _shapeMapRef.value.set(shape.id, shape)
112
- if (shape.parenShapeId) {
113
- const children = _parentChildMapRef.value.get(shape.parenShapeId)
114
- if (children) {
115
- if (!children.includes(shape.id)) {
116
- children.push(shape.id)
117
- }
118
- } else {
119
- _parentChildMapRef.value.set(shape.parenShapeId, [shape.id])
120
- }
121
- }
122
- }
123
-
124
- /** 从索引中移除图元 */
125
- function _indexRemove(shapeId: string) {
126
- const shape = _shapeMapRef.value.get(shapeId)
127
- if (!shape) return
128
- _shapeMapRef.value.delete(shapeId)
129
- if (shape.parenShapeId) {
130
- const children = _parentChildMapRef.value.get(shape.parenShapeId)
131
- if (children) {
132
- const idx = children.indexOf(shapeId)
133
- if (idx !== -1) children.splice(idx, 1)
134
- if (children.length === 0) {
135
- _parentChildMapRef.value.delete(shape.parenShapeId)
136
- }
137
- }
138
- }
139
- // 如果这个图元是父节点,清理它的子节点列表
140
- _parentChildMapRef.value.delete(shapeId)
141
- }
142
-
143
- /** 更新图元索引(处理父节点变更) */
144
- function _indexUpdate(shape: Shape, oldParentId: string | null | undefined) {
145
- _shapeMapRef.value.set(shape.id, shape)
146
- const newParentId = shape.parenShapeId ?? null
147
- const normalizedOldParent = oldParentId ?? null
148
-
149
- if (normalizedOldParent !== newParentId) {
150
- // 从旧父节点移除
151
- if (normalizedOldParent) {
152
- const oldChildren = _parentChildMapRef.value.get(normalizedOldParent)
153
- if (oldChildren) {
154
- const idx = oldChildren.indexOf(shape.id)
155
- if (idx !== -1) oldChildren.splice(idx, 1)
156
- if (oldChildren.length === 0) {
157
- _parentChildMapRef.value.delete(normalizedOldParent)
158
- }
159
- }
160
- }
161
- // 添加到新父节点
162
- if (newParentId) {
163
- const newChildren = _parentChildMapRef.value.get(newParentId)
164
- if (newChildren) {
165
- if (!newChildren.includes(shape.id)) {
166
- newChildren.push(shape.id)
167
- }
168
- } else {
169
- _parentChildMapRef.value.set(newParentId, [shape.id])
170
- }
171
- }
172
- }
173
- }
174
-
175
- /** 全量重建索引(用于 replace 操作或初始化) */
176
- function _rebuildIndex() {
177
- const newShapeMap = new Map<string, Shape>()
178
- const newParentChildMap = new Map<string, string[]>()
179
-
87
+ const parentChildMap = computed(() => {
88
+ const map = new Map<string, string[]>();
180
89
  shapes.value.forEach(shape => {
181
- newShapeMap.set(shape.id, shape)
182
90
  if (shape.parenShapeId) {
183
- const children = newParentChildMap.get(shape.parenShapeId)
184
- if (children) {
185
- children.push(shape.id)
186
- } else {
187
- newParentChildMap.set(shape.parenShapeId, [shape.id])
91
+ if (!map.has(shape.parenShapeId)) {
92
+ map.set(shape.parenShapeId, []);
188
93
  }
94
+ map.get(shape.parenShapeId)!.push(shape.id);
189
95
  }
190
- })
191
-
192
- _shapeMapRef.value = newShapeMap
193
- _parentChildMapRef.value = newParentChildMap
194
- triggerRef(_shapeMapRef)
195
- triggerRef(_parentChildMapRef)
196
- }
96
+ });
97
+ return map;
98
+ })
197
99
 
198
- /** 触发索引的响应式更新 */
199
- function _triggerIndexUpdate() {
200
- triggerRef(_shapeMapRef)
201
- triggerRef(_parentChildMapRef)
202
- }
100
+ /**
101
+ * 图元ID索引映射
102
+ * 用于快速通过ID查找图元对象
103
+ * key: 图元 ID, value: 图元对象
104
+ * 性能优化: O(n) 构建, O(1) 查询
105
+ * 解决嵌套图元场景下大量 find() 调用导致的性能问题
106
+ */
107
+ const shapeMap = computed(() => {
108
+ const map = new Map<string, Shape>();
109
+ shapes.value.forEach(shape => {
110
+ map.set(shape.id, shape);
111
+ });
112
+ return map;
113
+ })
203
114
 
204
115
  //图元shapeKey
205
116
  const taggedValueLabels = ref<string[]>([]) // 隔间组件的图元类型
@@ -236,9 +147,6 @@ export const useGraphStore = defineStore('graph', () => {
236
147
  }
237
148
 
238
149
  shapes.value.push(shape)
239
- // 增量更新索引
240
- _indexAdd(shape)
241
- _triggerIndexUpdate()
242
150
  // 通过事件总线发送事件
243
151
  // eventBus.emit('shape-added', shape)
244
152
 
@@ -255,10 +163,6 @@ export const useGraphStore = defineStore('graph', () => {
255
163
  const removeShape = (shapeId: string) => {
256
164
  const index = shapes.value.findIndex(s => s.id == shapeId)
257
165
  if (index > -1) {
258
- // 增量更新索引(在删除数组元素之前)
259
- _indexRemove(shapeId)
260
- shapes.value.splice(index, 1)
261
- _triggerIndexUpdate()
262
166
  // 如果删除的是当前选中的形状,清除选中状态
263
167
  if (selectedShape.value?.id === shapeId) {
264
168
  selectedShape.value = null
@@ -293,8 +197,6 @@ export const useGraphStore = defineStore('graph', () => {
293
197
  const updateShapeRaw = (shapeId: string, updates: Partial<Shape>, id: 'id' | 'modelId' = 'id') => {
294
198
  const shape = shapes.value.find(s => s[id] === shapeId)
295
199
  if (shape) {
296
- // 记录旧的父节点ID,用于索引更新
297
- const oldParentId = shape.parenShapeId
298
200
  // 对 Pin 做强保护:永不允许把 parenShapeId 置空/undefined
299
201
  if (shape.shapeType === 'pin' && 'parenShapeId' in updates) {
300
202
  const v: any = (updates as any).parenShapeId
@@ -311,9 +213,6 @@ export const useGraphStore = defineStore('graph', () => {
311
213
  const nextBounds = updates.bounds ? { ...shape.bounds, ...updates.bounds } : shape.bounds
312
214
  const nextStyle = updates.style ? { ...(shape.style || {}), ...(updates.style as any) } : shape.style
313
215
  Object.assign(shape, updates, { bounds: nextBounds, style: nextStyle })
314
- // 增量更新索引(处理父节点变更)
315
- _indexUpdate(shape, oldParentId)
316
- _triggerIndexUpdate()
317
216
  eventBus.emit('shape-updated', shape, updates)
318
217
  }
319
218
  }
@@ -372,46 +271,32 @@ export const useGraphStore = defineStore('graph', () => {
372
271
  }
373
272
  // 被框选中的多个 shape/映射 selectedIds.value中的数据
374
273
  const marqueeShapes = computed(() => {
274
+ const byId = (id: string) => shapes.value.find(s => s.id === id)
275
+ const isShape = (x: Shape | undefined): x is Shape => !!x
375
276
  const pending = new Set(pendingNestedIds.value)
376
- const result: Shape[] = []
377
- // 使用 shapeMap 进行 O(1) 查找,避免 O(n²) 的 find
378
- for (const id of selectedIds.value) {
379
- if (pending.has(id)) continue
380
- const shape = shapeMap.value.get(id)
381
- if (shape) result.push(shape)
382
- }
383
- return result
277
+ return selectedIds.value
278
+ .map(byId)
279
+ .filter(isShape)
280
+ .filter(s => !pending.has(s.id))
384
281
  })
385
282
  //获取元素拖动预览框
386
- // 性能优化:只返回"直接被拖动"的图元的 ghost,不包含后代
387
- // 后代图元会跟随父元素移动,不需要单独显示 ghost
388
283
  const ghostShadow = computed<Shape[]>(() => {
389
- // 如果没有在拖动,直接返回空数组
390
- if (!isDragging.value || draggingIds.value.length === 0) return []
391
-
392
284
  const out: Shape[] = []
393
285
  // 兜底:防止被持久化还原成非数组
394
286
  const pendingList = Array.isArray(pendingNestedIds.value)
395
287
  ? pendingNestedIds.value
396
288
  : []
397
289
  const pendingSet = new Set(pendingList)
398
- // 只为"直接被选中拖动"的图元创建 ghost,不包含后代
399
- // 这样可以大幅减少 ghost 数量(从 3000 减少到用户实际选中的数量)
400
- const directDragSet = new Set(dragSelectionSnapshot.value.length > 0
401
- ? dragSelectionSnapshot.value
402
- : draggingIds.value.slice(0, 1)) // 至少保留主拖动项
403
-
290
+ // shapeMap O(1) 查找原始图元,避免 find O(N)
404
291
  const map = shapeMap.value
405
- for (const id of directDragSet) {
292
+ for (const id in dragGhost) {
406
293
  if (pendingSet.has(id)) continue
407
- const ghostRect = dragGhost[id]
408
- if (!ghostRect) continue
409
294
  const orig = map.get(id)
410
295
  if (!orig) continue
411
296
 
412
297
  out.push({
413
298
  ...orig,
414
- bounds: { ...ghostRect },
299
+ bounds: { ...dragGhost[id] },
415
300
  meta: { ...(orig as any).meta, isGhost: true } as any,
416
301
  } as Shape)
417
302
  }
@@ -420,22 +305,18 @@ export const useGraphStore = defineStore('graph', () => {
420
305
  // 选择一组(用于框选/Shift 叠加)
421
306
  const selectMany = (ids: string[]) => {
422
307
  selectedIds.value = Array.from(new Set(ids))
423
- // 使用 shapeMap 进行 O(1) 查找,避免 O(n) 的 find
424
308
  selectedShape.value = ids.length
425
- ? (shapeMap.value.get(ids[0]) ?? null)
309
+ ? (shapes.value.find(s => s.id === ids[0]) ?? null)
426
310
  : null
427
311
  }
428
312
  // 清空选择
429
313
  const clearSelection = () => selectShape(null)
430
314
  // 全选图元
431
315
  const selectAll = () => {
432
- // 优化:直接遍历一次收集 id,避免 filter + map 两次遍历
433
- const allShapeIds: string[] = []
434
- for (const shape of shapes.value) {
435
- if (shape.shapeType?.toLowerCase() !== 'diagram') {
436
- allShapeIds.push(shape.id)
437
- }
438
- }
316
+ // 获取所有非diagram类型的图元
317
+ const allShapeIds = shapes.value
318
+ .filter(shape => shape.shapeType?.toLowerCase() !== 'diagram')
319
+ .map(shape => shape.id)
439
320
  selectMany(allShapeIds)
440
321
  }
441
322
  // 设置图表标题
@@ -449,7 +330,7 @@ export const useGraphStore = defineStore('graph', () => {
449
330
  /**
450
331
  * 批量操作图元(新增 / 修改 / 删除)
451
332
  * @param payload - 新增/修改时传 Shape[];删除时可传 Shape[](只需要 id)或 id 数组
452
- * @param op - 操作类型:add | update | delete | upsert(默认)| replace
333
+ * @param op - 操作类型:add | update | delete | upsert(默认)
453
334
  * @param options - 额外选项(保持你原来的 autoExpandParents)
454
335
  */
455
336
  const updateShapes = (
@@ -459,44 +340,11 @@ export const useGraphStore = defineStore('graph', () => {
459
340
  ) => {
460
341
  // 先把 comparents[*].comparentShapes 铺平同步到运行时
461
342
  // const expanded = syncComparentShapesIntoRuntime(newShapes)
462
- const { changedShapes, changeDetails } = applyShapeOp({
343
+ const { changedShapes } = applyShapeOp({
463
344
  list: shapes.value,
464
345
  payload: payload as any, // delete 时是 id[],其余是 Shape[]
465
346
  op,
466
347
  })
467
-
468
- // 根据操作类型选择索引更新策略
469
- if (op === 'replace' || changeDetails.length === 0) {
470
- // replace 操作或无变更详情时,全量重建索引
471
- _rebuildIndex()
472
- } else {
473
- // 增量更新索引
474
- for (const detail of changeDetails) {
475
- if (detail.type === 'add') {
476
- _indexAdd(detail.shape)
477
- } else if (detail.type === 'delete') {
478
- // 删除时 shape 已经从数组移除,需要手动清理索引
479
- _shapeMapRef.value.delete(detail.shape.id)
480
- const parentId = (detail.shape as any).parenShapeId
481
- if (parentId) {
482
- const children = _parentChildMapRef.value.get(parentId)
483
- if (children) {
484
- const idx = children.indexOf(detail.shape.id)
485
- if (idx !== -1) children.splice(idx, 1)
486
- if (children.length === 0) {
487
- _parentChildMapRef.value.delete(parentId)
488
- }
489
- }
490
- }
491
- _parentChildMapRef.value.delete(detail.shape.id)
492
- } else if (detail.type === 'update') {
493
- const oldParentId = (detail.oldShape as any)?.parenShapeId
494
- _indexUpdate(detail.shape, oldParentId)
495
- }
496
- }
497
- _triggerIndexUpdate()
498
- }
499
-
500
348
  pendingNestedIds.value = []
501
349
  // eventBus.emit('shapes-updated', newShapes)
502
350
  // hydrateAllComparents()
@@ -516,11 +364,6 @@ export const useGraphStore = defineStore('graph', () => {
516
364
  //清空图元数据
517
365
  const clearAll = () => {
518
366
  shapes.value = []
519
- // 清空索引
520
- _shapeMapRef.value = new Map()
521
- _parentChildMapRef.value = new Map()
522
- triggerRef(_shapeMapRef)
523
- triggerRef(_parentChildMapRef)
524
367
  diagrams.value = []
525
368
  selectedShape.value = null
526
369
  pendingNestedIds.value = []
@@ -533,13 +376,17 @@ export const useGraphStore = defineStore('graph', () => {
533
376
  startDrag([shapeId], pointer)
534
377
  }
535
378
  const startDrag = (ids: string[], pointer: { x: number; y: number }) => {
536
- // 记录"当前真正被用户选中"的快照 结束时恢复
379
+ // 记录“当前真正被用户选中”的快照 —— 结束时恢复
537
380
  dragSelectionSnapshot.value = selectedIds.value.length ? [...selectedIds.value] : [ids[0]]
538
381
  primaryDragId.value = ids[0] || null
539
382
 
540
- // 性能优化:只对"直接选中"的图元建立 dragBase/dragGhost
541
- // 后代图元会跟随父元素移动,不需要单独创建 ghost,大幅减少渲染开销
542
- const validIds = ids.filter(id => {
383
+ // 展开“选中项 + 后代”的集合(你现有的逻辑保留)
384
+ const expanded: string[] = []
385
+ ids.forEach(id => {
386
+ expanded.push(id)
387
+ collectDescendantIds(shapes.value, id).forEach(cid => expanded.push(cid))
388
+ })
389
+ const validIds = Array.from(new Set(expanded)).filter(id => {
543
390
  const s = byId(id); return s && getPolicy(s!).draggable
544
391
  })
545
392
  if (!validIds.length) return
@@ -553,32 +400,6 @@ export const useGraphStore = defineStore('graph', () => {
553
400
  draggingIds.value = validIds
554
401
  dragAnchor.value = { x: pointer.x, y: pointer.y }
555
402
 
556
- // 构建后代坐标快照(只计算一次,供预览框渲染使用)
557
- const newSnapshot = new Map<string, { id: string; x: number; y: number; width: number; height: number }[]>()
558
- for (const parentId of validIds) {
559
- const descendants: { id: string; x: number; y: number; width: number; height: number }[] = []
560
- // 递归收集所有后代
561
- const collectDescendants = (pid: string) => {
562
- const childIds = _parentChildMapRef.value.get(pid) || []
563
- for (const childId of childIds) {
564
- const childShape = _shapeMapRef.value.get(childId)
565
- if (childShape?.bounds) {
566
- descendants.push({
567
- id: childId,
568
- x: childShape.bounds.x ?? 0,
569
- y: childShape.bounds.y ?? 0,
570
- width: childShape.bounds.width ?? 0,
571
- height: childShape.bounds.height ?? 0,
572
- })
573
- }
574
- collectDescendants(childId) // 递归
575
- }
576
- }
577
- collectDescendants(parentId)
578
- newSnapshot.set(parentId, descendants)
579
- }
580
- dragDescendantsSnapshot.value = newSnapshot
581
-
582
403
  const currentDiagram = shapes.value.find(s => s.shapeType === 'diagram')
583
404
  if (currentDiagram) {
584
405
  dragBaseCanvasSize.value = {
@@ -834,7 +655,7 @@ export const useGraphStore = defineStore('graph', () => {
834
655
  // 在生成 payloads 之前,把父扩容先做掉
835
656
  const shapeId = ownerPayload?.shapeId
836
657
  if (shapeId != null) {
837
- const child = shapes.value.find(s => s.id == shapeId)
658
+ const child = shapes.value.find(s => s.id === shapeId)
838
659
  if (child) {
839
660
  expandParentByChild({
840
661
  shapes: shapes.value,
@@ -931,7 +752,6 @@ export const useGraphStore = defineStore('graph', () => {
931
752
  dragSelectionSnapshot.value = []
932
753
  primaryDragId.value = null
933
754
  dragBaseCanvasSize.value = null
934
- dragDescendantsSnapshot.value = new Map() // 清理后代快照
935
755
  for (const k in dragGhost) delete dragGhost[k]
936
756
  }
937
757
  //设置当前打开的画布id
@@ -1087,8 +907,6 @@ export const useGraphStore = defineStore('graph', () => {
1087
907
  shapeMap, // 图元ID索引映射 (性能优化)
1088
908
  marqueeShapes,
1089
909
  ghostShadow,
1090
- dragDescendantsSnapshot, // 拖动时的后代坐标快照
1091
- dragBase, // 拖动开始时的坐标快照
1092
910
  scales,
1093
911
  activeDiagramId,
1094
912
  // 当前活动画布的缩放比例
@@ -709,11 +709,9 @@ const pointInRect = (pt: { x: number; y: number }, r: Rect, margin = 0) =>
709
709
  pt.y <= r.y + r.height - margin
710
710
 
711
711
  const depthOf = (shapes: Shape[], id: string): number => {
712
- const graphStore = useGraphStore();
713
- const byId = graphStore.shapeMap;
714
712
  let d = 0
715
- let p = byId.get(id)?.parenShapeId ?? null
716
- while (p) { d++; p = byId.get(p)?.parenShapeId ?? null }
713
+ let p = shapes.find(s => s.id === id)?.parenShapeId ?? null
714
+ while (p) { d++; p = shapes.find(s => s.id === p)?.parenShapeId ?? null }
717
715
  return d
718
716
  }
719
717