23
This commit is contained in:
163
draggable-panels/src/fauto/materials/TreeViewer/index.vue
Normal file
163
draggable-panels/src/fauto/materials/TreeViewer/index.vue
Normal file
@@ -0,0 +1,163 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import draggable from 'vuedraggable'
|
||||
import { useDesignStore } from '../../stores/designStore'
|
||||
import config from './index.json'
|
||||
|
||||
const designStore = useDesignStore()
|
||||
|
||||
// 创建可响应的本地副本用于拖拽
|
||||
const localComponents = ref([...designStore.components])
|
||||
|
||||
// 监听 designStore 组件变化,同步到本地副本
|
||||
watch(
|
||||
() => designStore.components,
|
||||
(newVal) => {
|
||||
localComponents.value = [...newVal]
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
)
|
||||
|
||||
// 拖拽结束处理 - 同步顺序到 designStore
|
||||
const onDragEnd = () => {
|
||||
// 重新排序 designStore 中的组件
|
||||
designStore.reorderComponents([...localComponents.value])
|
||||
}
|
||||
|
||||
// 点击节点 - 选中组件
|
||||
const handleNodeClick = (nodeId: string) => {
|
||||
designStore.selectComponent(nodeId)
|
||||
}
|
||||
|
||||
// 获取组件类型图标
|
||||
const getComponentIcon = (componentId: string) => {
|
||||
const icons: Record<string, string> = {
|
||||
TextInput: '✏️',
|
||||
RadioSelect: '◉',
|
||||
GridTable: '☰'
|
||||
}
|
||||
return icons[componentId] || '⭕'
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="tree-viewer">
|
||||
<div class="viewer-header">
|
||||
<span class="title">{{ config.name }}</span>
|
||||
<span class="hint">设计组件列表</span>
|
||||
</div>
|
||||
<div class="viewer-body">
|
||||
<div class="tree-container">
|
||||
<draggable
|
||||
:list="localComponents"
|
||||
item-key="id"
|
||||
:animation="150"
|
||||
ghost-class="node-ghost"
|
||||
@end="onDragEnd"
|
||||
>
|
||||
<template #item="{ element: node }">
|
||||
<div
|
||||
class="tree-node"
|
||||
:class="{ selected: designStore.selectedId === node.id }"
|
||||
@click="handleNodeClick(node.id)"
|
||||
>
|
||||
<span class="node-icon">{{ getComponentIcon(node.componentId) }}</span>
|
||||
<span class="node-label">{{ node.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
|
||||
<div v-if="localComponents.length === 0" class="empty-tip">
|
||||
暂无设计组件
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.tree-viewer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
background: #1e1e1e;
|
||||
}
|
||||
|
||||
.viewer-header {
|
||||
padding: 8px 12px;
|
||||
background: #2d2d2d;
|
||||
border-bottom: 1px solid #3c3c3c;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #cccccc;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.hint {
|
||||
color: #666666;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.viewer-body {
|
||||
flex: 1;
|
||||
padding: 8px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.tree-container {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.tree-node {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 12px;
|
||||
cursor: grab;
|
||||
border-radius: 4px;
|
||||
color: #cccccc;
|
||||
user-select: none;
|
||||
margin-bottom: 2px;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.tree-node:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.tree-node:hover {
|
||||
background: #2a2d2e;
|
||||
}
|
||||
|
||||
.tree-node.selected {
|
||||
background: #094771;
|
||||
}
|
||||
|
||||
.node-icon {
|
||||
width: 20px;
|
||||
margin-right: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.node-label {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
color: #666666;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* 拖拽样式 */
|
||||
.node-ghost {
|
||||
opacity: 0.4;
|
||||
background: #094771;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user