feat: add PinnedFlowchartPanel draggable component

This commit is contained in:
2026-05-25 00:03:36 -06:00
parent ad18800ca6
commit d05f4a8918

View File

@@ -0,0 +1,106 @@
<template>
<div
v-if="store.isPinned"
ref="panel"
class="pinned-panel"
:style="{ left: `${x}px`, top: `${y}px` }"
>
<n-card size="small" :content-style="collapsed ? 'display:none' : 'padding:8px'">
<template #header>
<div ref="handle" class="pinned-handle">
<n-flex align="center" :size="8">
<Icon icon="mdi:pin" style="font-size: 14px" />
<n-text strong style="font-size: 13px">流程图预览</n-text>
</n-flex>
</div>
</template>
<template #header-extra>
<n-flex :size="4">
<n-button quaternary size="small" @click="collapsed = !collapsed">
<template #icon>
<Icon :icon="collapsed ? 'mdi:chevron-up' : 'mdi:chevron-down'" />
</template>
</n-button>
<n-button quaternary size="small" @click="store.unpin()">
<template #icon>
<Icon icon="mdi:close" />
</template>
</n-button>
</n-flex>
</template>
<n-alert v-if="renderError" type="error" title="渲染失败" size="small">
{{ renderError }}
</n-alert>
<div v-else ref="mermaidContainer" class="mermaid-area"></div>
</n-card>
</div>
</template>
<script setup lang="ts">
import { Icon } from "@iconify/vue"
import { usePinnedFlowchartStore } from "shared/store/pinnedFlowchart"
import { useMermaid } from "shared/composables/useMermaid"
const store = usePinnedFlowchartStore()
const { renderError, renderFlowchart } = useMermaid()
const panelRef = useTemplateRef<HTMLElement>("panel")
const handleRef = useTemplateRef<HTMLElement>("handle")
const mermaidContainer = useTemplateRef<HTMLElement>("mermaidContainer")
const collapsed = ref(false)
const { x, y } = useDraggable(panelRef, {
handle: handleRef,
initialValue: {
x: window.innerWidth - 440,
y: window.innerHeight - 400,
},
})
watch(
() => store.mermaidCode,
async (code) => {
if (!code || collapsed.value) return
await nextTick()
await renderFlowchart(mermaidContainer.value, code)
},
{ immediate: true },
)
watch(collapsed, async (val) => {
if (!val && store.mermaidCode) {
await nextTick()
await renderFlowchart(mermaidContainer.value, store.mermaidCode)
}
})
</script>
<style scoped>
.pinned-panel {
position: fixed;
z-index: 1000;
width: 420px;
}
.pinned-handle {
cursor: grab;
user-select: none;
}
.pinned-handle:active {
cursor: grabbing;
}
.mermaid-area {
height: 340px;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
}
:deep(.mermaid-area > svg) {
max-width: 100%;
max-height: 100%;
}
</style>