2026-07-20 15:05:34 +08:00
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
import type { BlockSchema } from './types';
|
|
|
|
|
|
|
|
|
|
|
|
import MIcon from '#/components/icon/icon.vue';
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 左侧组件库:渲染所有可拖拽块类型
|
|
|
|
|
|
* 用户从这里拖动到中间画布(SortableJS 用 pull:clone 模式,左侧库不会减少)
|
|
|
|
|
|
*/
|
|
|
|
|
|
defineProps<{
|
|
|
|
|
|
/** 所有可拖拽块定义 */
|
|
|
|
|
|
blocks: BlockSchema[];
|
|
|
|
|
|
}>();
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
|
<div class="block-library">
|
|
|
|
|
|
<div class="panel-header">
|
|
|
|
|
|
<span class="title">组件库</span>
|
|
|
|
|
|
<span class="hint">拖入画布</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="block-list">
|
|
|
|
|
|
<div
|
|
|
|
|
|
v-for="block in blocks"
|
|
|
|
|
|
:key="block.type"
|
|
|
|
|
|
class="block-item"
|
|
|
|
|
|
:data-block-type="block.type"
|
|
|
|
|
|
>
|
|
|
|
|
|
<MIcon v-if="block.icon" :icon="block.icon" size="16" />
|
|
|
|
|
|
<span class="block-label">{{ block.label }}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div v-if="blocks.length === 0" class="empty-hint">暂无可拖入组件</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
|
|
.block-library {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
width: 200px;
|
|
|
|
|
|
height: 100%;
|
2026-07-23 14:15:17 +08:00
|
|
|
|
border-right: 1px solid var(--ant-color-border-secondary, #e5e6eb);
|
|
|
|
|
|
background: var(--ant-color-fill-quaternary, #fafbfc);
|
2026-07-20 15:05:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.panel-header {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
padding: 12px 16px;
|
2026-07-23 14:15:17 +08:00
|
|
|
|
border-bottom: 1px solid var(--ant-color-border-secondary, #e5e6eb);
|
2026-07-20 15:05:34 +08:00
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
|
|
|
|
|
|
.title {
|
|
|
|
|
|
font-weight: 600;
|
2026-07-23 14:15:17 +08:00
|
|
|
|
color: var(--ant-color-text, #1d2129);
|
2026-07-20 15:05:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.hint {
|
2026-07-23 14:15:17 +08:00
|
|
|
|
color: var(--ant-color-text-tertiary, #86909c);
|
2026-07-20 15:05:34 +08:00
|
|
|
|
font-size: 11px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.block-list {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
padding: 12px 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.block-item {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
padding: 10px 12px;
|
|
|
|
|
|
margin-bottom: 6px;
|
2026-07-23 14:15:17 +08:00
|
|
|
|
border: 1px dashed var(--ant-color-border, #c9cdd4);
|
2026-07-20 15:05:34 +08:00
|
|
|
|
border-radius: 4px;
|
2026-07-23 14:15:17 +08:00
|
|
|
|
background: var(--ant-color-bg-container, #fff);
|
2026-07-20 15:05:34 +08:00
|
|
|
|
font-size: 13px;
|
2026-07-23 14:15:17 +08:00
|
|
|
|
color: var(--ant-color-text-secondary, #4e5969);
|
2026-07-20 15:05:34 +08:00
|
|
|
|
cursor: grab;
|
|
|
|
|
|
user-select: none;
|
|
|
|
|
|
transition: all 0.15s;
|
|
|
|
|
|
|
|
|
|
|
|
&:hover {
|
2026-07-23 14:15:17 +08:00
|
|
|
|
border-color: var(--ant-color-primary, #165dff);
|
|
|
|
|
|
color: var(--ant-color-primary, #165dff);
|
|
|
|
|
|
background: var(--ant-color-primary-bg, #f2f7ff);
|
2026-07-20 15:05:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
&:active {
|
|
|
|
|
|
cursor: grabbing;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.block-label {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.empty-hint {
|
|
|
|
|
|
padding: 24px 12px;
|
|
|
|
|
|
text-align: center;
|
2026-07-23 14:15:17 +08:00
|
|
|
|
color: var(--ant-color-text-quaternary, #c9cdd4);
|
2026-07-20 15:05:34 +08:00
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|