mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-04-06 03:57:56 +08:00
160 lines
4.6 KiB
Vue
160 lines
4.6 KiB
Vue
<template>
|
|
<div
|
|
v-show="visible"
|
|
class="m-editor-tree-node"
|
|
:draggable="draggable"
|
|
:data-node-id="data.id"
|
|
:data-parent-id="parent?.id"
|
|
:data-parents-id="parentsId"
|
|
:data-is-container="Array.isArray(data.items)"
|
|
@dragstart="handleDragStart"
|
|
@dragleave="handleDragLeave"
|
|
@dragend="handleDragEnd"
|
|
>
|
|
<div
|
|
class="tree-node"
|
|
:class="{ selected, expanded }"
|
|
:style="`padding-left: ${indent}px`"
|
|
@contextmenu="nodeContentmenuHandler"
|
|
@mouseenter="mouseenterHandler"
|
|
>
|
|
<MIcon
|
|
class="expand-icon"
|
|
:style="hasChildren ? '' : 'color: transparent; cursor: default'"
|
|
:icon="expanded ? ArrowDown : ArrowRight"
|
|
@click="expandHandler"
|
|
></MIcon>
|
|
|
|
<div class="tree-node-content" @click="nodeClickHandler">
|
|
<slot name="tree-node-content" :data="data">
|
|
<div class="tree-node-label">
|
|
<slot name="tree-node-label" :data="data">{{ `${data.name} (${data.id})` }}</slot>
|
|
</div>
|
|
<div class="tree-node-tool">
|
|
<slot name="tree-node-tool" :data="data"></slot>
|
|
</div>
|
|
</slot>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="hasChildren && expanded" class="m-editor-tree-node-children">
|
|
<TreeNode
|
|
v-for="item in data.items"
|
|
:key="item.id"
|
|
:data="item"
|
|
:parent="data"
|
|
:parentsId="[...parentsId, data.id]"
|
|
:node-status-map="nodeStatusMap"
|
|
:indent="indent + nextLevelIndentIncrement"
|
|
>
|
|
<template #tree-node-content="{ data: nodeData }">
|
|
<slot name="tree-node-content" :data="nodeData"> </slot>
|
|
</template>
|
|
<template #tree-node-label="{ data: nodeData }">
|
|
<slot name="tree-node-label" :data="nodeData"> </slot>
|
|
</template>
|
|
<template #tree-node-tool="{ data: nodeData }">
|
|
<slot name="tree-node-tool" :data="nodeData"> </slot>
|
|
</template>
|
|
</TreeNode>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, inject } from 'vue';
|
|
import { ArrowDown, ArrowRight } from '@element-plus/icons-vue';
|
|
|
|
import type { Id } from '@tmagic/core';
|
|
|
|
import MIcon from '@editor/components/Icon.vue';
|
|
import type { LayerNodeStatus, TreeNodeData } from '@editor/type';
|
|
import { updateStatus } from '@editor/utils/tree';
|
|
|
|
defineSlots<{
|
|
'tree-node-label'(props: { data: TreeNodeData }): any;
|
|
'tree-node-tool'(props: { data: TreeNodeData }): any;
|
|
'tree-node-content'(props: { data: TreeNodeData }): any;
|
|
}>();
|
|
|
|
defineOptions({
|
|
name: 'MEditorTreeNode',
|
|
});
|
|
|
|
const emit = defineEmits<{
|
|
'node-dragstart': [event: DragEvent, data: TreeNodeData];
|
|
'node-dragleave': [event: DragEvent, data: TreeNodeData];
|
|
'node-dragend': [event: DragEvent, data: TreeNodeData];
|
|
'node-contextmenu': [event: MouseEvent, data: TreeNodeData];
|
|
'node-mouseenter': [event: MouseEvent, data: TreeNodeData];
|
|
'node-click': [event: MouseEvent, data: TreeNodeData];
|
|
}>();
|
|
|
|
const treeEmit = inject<typeof emit>('treeEmit');
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
data: TreeNodeData;
|
|
parent?: TreeNodeData;
|
|
parentsId?: Id[];
|
|
nodeStatusMap: Map<Id, LayerNodeStatus>;
|
|
indent?: number;
|
|
nextLevelIndentIncrement?: number;
|
|
}>(),
|
|
{
|
|
indent: 0,
|
|
nextLevelIndentIncrement: 11,
|
|
parentsId: () => [],
|
|
},
|
|
);
|
|
|
|
const nodeStatus = computed(
|
|
() =>
|
|
props.nodeStatusMap?.get(props.data.id) || {
|
|
selected: false,
|
|
expand: false,
|
|
visible: false,
|
|
draggable: false,
|
|
},
|
|
);
|
|
|
|
const expanded = computed(() => nodeStatus.value.expand);
|
|
const selected = computed(() => nodeStatus.value.selected);
|
|
const visible = computed(() => nodeStatus.value.visible);
|
|
const draggable = computed(() => nodeStatus.value.draggable);
|
|
|
|
const hasChildren = computed(
|
|
() => Array.isArray(props.data.items) && props.data.items.some((item) => props.nodeStatusMap.get(item.id)?.visible),
|
|
);
|
|
|
|
const handleDragStart = (event: DragEvent) => {
|
|
treeEmit?.('node-dragstart', event, props.data);
|
|
};
|
|
|
|
const handleDragLeave = (event: DragEvent) => {
|
|
treeEmit?.('node-dragleave', event, props.data);
|
|
};
|
|
|
|
const handleDragEnd = (event: DragEvent) => {
|
|
treeEmit?.('node-dragend', event, props.data);
|
|
};
|
|
|
|
const nodeContentmenuHandler = (event: MouseEvent) => {
|
|
treeEmit?.('node-contextmenu', event, props.data);
|
|
};
|
|
|
|
const mouseenterHandler = (event: MouseEvent) => {
|
|
treeEmit?.('node-mouseenter', event, props.data);
|
|
};
|
|
|
|
const expandHandler = () => {
|
|
updateStatus(props.nodeStatusMap, props.data.id, {
|
|
expand: !expanded.value,
|
|
});
|
|
};
|
|
|
|
const nodeClickHandler = (event: MouseEvent) => {
|
|
treeEmit?.('node-click', event, props.data);
|
|
};
|
|
</script>
|