mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-04-06 03:57:56 +08:00
refactor(editor): 代码编辑、数据源重构
This commit is contained in:
parent
258d2bc2ea
commit
741140fa71
@ -25,6 +25,10 @@
|
||||
<slot name="layer-node-content" :data="data"></slot>
|
||||
</template>
|
||||
|
||||
<template #layer-node-label="{ data }">
|
||||
<slot name="layer-node-label" :data="data"></slot>
|
||||
</template>
|
||||
|
||||
<template #layer-node-tool="{ data }">
|
||||
<slot name="layer-node-tool" :data="data"></slot>
|
||||
</template>
|
||||
|
@ -6,6 +6,10 @@
|
||||
<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>
|
||||
@ -28,6 +32,7 @@ import TreeNode from './TreeNode.vue';
|
||||
|
||||
defineSlots<{
|
||||
'tree-node-content'(props: { data: TreeNodeData }): any;
|
||||
'tree-node-label'(props: { data: TreeNodeData }): any;
|
||||
'tree-node-tool'(props: { data: TreeNodeData }): any;
|
||||
}>();
|
||||
|
||||
|
@ -25,7 +25,9 @@
|
||||
|
||||
<div class="tree-node-content" @click="nodeClickHandler">
|
||||
<slot name="tree-node-content" :data="data">
|
||||
<div class="tree-node-label">{{ `${data.name} (${data.id})` }}</div>
|
||||
<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>
|
||||
@ -44,6 +46,9 @@
|
||||
<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>
|
||||
@ -63,6 +68,7 @@ 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;
|
||||
}>();
|
||||
|
@ -1,18 +1,16 @@
|
||||
import { computed, type ComputedRef, ref } from 'vue';
|
||||
import { type Ref, ref } from 'vue';
|
||||
|
||||
import { Id, MNode } from '@tmagic/schema';
|
||||
import type { Id, MNode } from '@tmagic/schema';
|
||||
|
||||
import { LayerNodeStatus, Services } from '@editor/type';
|
||||
import type { LayerNodeStatus, TreeNodeData } from '@editor/type';
|
||||
import { traverseNode } from '@editor/utils';
|
||||
import { updateStatus } from '@editor/utils/tree';
|
||||
|
||||
export const useFilter = (
|
||||
services: Services | undefined,
|
||||
nodeStatusMap: ComputedRef<Map<Id, LayerNodeStatus> | undefined>,
|
||||
nodeData: Ref<TreeNodeData[]>,
|
||||
nodeStatusMap: Ref<Map<Id, LayerNodeStatus> | undefined>,
|
||||
filterNodeMethod: (value: string, data: MNode) => boolean,
|
||||
) => {
|
||||
const page = computed(() => services?.editorService.get('page'));
|
||||
|
||||
// tree方法:对树节点进行筛选时执行的方法
|
||||
const filterIsMatch = (value: string | string[], data: MNode): boolean => {
|
||||
const string = !Array.isArray(value) ? [value] : value;
|
||||
@ -25,18 +23,14 @@ export const useFilter = (
|
||||
};
|
||||
|
||||
const filter = (text: string | string[]) => {
|
||||
if (!page.value?.items?.length) return;
|
||||
if (!nodeData.value.length) return;
|
||||
|
||||
page.value.items.forEach((node) => {
|
||||
nodeData.value.forEach((node) => {
|
||||
traverseNode(node, (node: MNode, parents: MNode[]) => {
|
||||
if (!nodeStatusMap.value) return;
|
||||
|
||||
const visible = filterIsMatch(text, node);
|
||||
if (visible && parents.length) {
|
||||
console.log(
|
||||
node.id,
|
||||
parents.map((a) => a.id),
|
||||
);
|
||||
parents.forEach((parent) => {
|
||||
updateStatus(nodeStatusMap.value!, parent.id, {
|
||||
visible,
|
48
packages/editor/src/hooks/use-node-status.ts
Normal file
48
packages/editor/src/hooks/use-node-status.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import { ComputedRef, ref, watch } from 'vue';
|
||||
|
||||
import type { Id, MNode } from '@tmagic/schema';
|
||||
|
||||
import { LayerNodeStatus, TreeNodeData } from '@editor/type';
|
||||
import { traverseNode } from '@editor/utils';
|
||||
|
||||
const createPageNodeStatus = (nodeData: TreeNodeData[], initalLayerNodeStatus?: Map<Id, LayerNodeStatus>) => {
|
||||
const map = new Map<Id, LayerNodeStatus>();
|
||||
|
||||
nodeData.forEach((node: MNode) =>
|
||||
traverseNode(node, (node) => {
|
||||
map.set(
|
||||
node.id,
|
||||
initalLayerNodeStatus?.get(node.id) || {
|
||||
visible: true,
|
||||
expand: false,
|
||||
selected: false,
|
||||
draggable: false,
|
||||
},
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
return map;
|
||||
};
|
||||
|
||||
export const useNodeStatus = (nodeData: ComputedRef<TreeNodeData[]>) => {
|
||||
/** 所有页面的节点状态 */
|
||||
const nodeStatusMap = ref(new Map<Id, LayerNodeStatus>());
|
||||
|
||||
// 切换页面或者新增页面,重新生成节点状态
|
||||
watch(
|
||||
nodeData,
|
||||
(nodeData) => {
|
||||
// 生成节点状态
|
||||
nodeStatusMap.value = createPageNodeStatus(nodeData, nodeStatusMap.value);
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true,
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
nodeStatusMap,
|
||||
};
|
||||
};
|
@ -74,6 +74,11 @@
|
||||
<component v-else-if="config.slots?.layerNodeContent" :is="config.slots.layerNodeContent" :data="nodeData" />
|
||||
</template>
|
||||
|
||||
<template #layer-node-label="{ data: nodeData }" v-if="config.$key === 'layer' || config.slots?.layerNodeLabel">
|
||||
<slot v-if="config.$key === 'layer'" name="layer-node-label" :data="nodeData"></slot>
|
||||
<component v-else-if="config.slots?.layerNodeLabel" :is="config.slots.layerNodeTool" :data="nodeData" />
|
||||
</template>
|
||||
|
||||
<template #layer-node-tool="{ data: nodeData }" v-if="config.$key === 'layer' || config.slots?.layerNodeTool">
|
||||
<slot v-if="config.$key === 'layer'" name="layer-node-tool" :data="nodeData"></slot>
|
||||
<component v-else-if="config.slots?.layerNodeTool" :is="config.slots.layerNodeTool" :data="nodeData" />
|
||||
|
@ -1,57 +1,42 @@
|
||||
<template>
|
||||
<TMagicTree
|
||||
ref="tree"
|
||||
class="magic-editor-layer-tree"
|
||||
node-key="id"
|
||||
empty-text="暂无代码块"
|
||||
:default-expanded-keys="expandedKeys"
|
||||
:expand-on-click-node="false"
|
||||
:data="codeList"
|
||||
:props="{
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
}"
|
||||
:highlight-current="true"
|
||||
:filter-node-method="filterNode"
|
||||
@node-click="clickHandler"
|
||||
>
|
||||
<template #default="{ data }">
|
||||
<div :id="data.id" class="list-container">
|
||||
<div class="list-item">
|
||||
<CodeIcon v-if="data.type === 'code'" class="codeIcon"></CodeIcon>
|
||||
<AppManageIcon v-if="data.type === 'node'" class="compIcon"></AppManageIcon>
|
||||
<span class="name" :class="{ code: data.type === 'code', hook: data.type === 'key' }"
|
||||
>{{ data.name }} ({{ data.id }})</span
|
||||
>
|
||||
<!-- 右侧工具栏 -->
|
||||
<div class="right-tool" v-if="data.type === 'code'">
|
||||
<TMagicTooltip effect="dark" :content="editable ? '编辑' : '查看'" placement="bottom">
|
||||
<Icon :icon="editable ? Edit : View" class="edit-icon" @click.stop="editCode(data.id)"></Icon>
|
||||
</TMagicTooltip>
|
||||
<TMagicTooltip v-if="editable" effect="dark" content="删除" placement="bottom">
|
||||
<Icon :icon="Close" class="edit-icon" @click.stop="deleteCode(`${data.id}`)"></Icon>
|
||||
</TMagicTooltip>
|
||||
<slot name="code-block-panel-tool" :id="data.id" :data="data.codeBlockContent"></slot>
|
||||
</div>
|
||||
</div>
|
||||
<Tree :data="codeList" :node-status-map="nodeStatusMap" @node-click="clickHandler">
|
||||
<template #tree-node-label="{ data }">
|
||||
<div
|
||||
:class="{
|
||||
code: data.type === 'code',
|
||||
hook: data.type === 'key',
|
||||
disabled: data.type === 'key' || data.type === 'code',
|
||||
}"
|
||||
>
|
||||
{{ data.name }} {{ data.key ? `(${data.key})` : '' }}
|
||||
</div>
|
||||
</template>
|
||||
</TMagicTree>
|
||||
|
||||
<template #tree-node-tool="{ data }">
|
||||
<TMagicTooltip v-if="data.type === 'code'" effect="dark" :content="editable ? '编辑' : '查看'" placement="bottom">
|
||||
<Icon :icon="editable ? Edit : View" class="edit-icon" @click.stop="editCode(`${data.key}`)"></Icon>
|
||||
</TMagicTooltip>
|
||||
<TMagicTooltip v-if="data.type === 'code' && editable" effect="dark" content="删除" placement="bottom">
|
||||
<Icon :icon="Close" class="edit-icon" @click.stop="deleteCode(`${data.key}`)"></Icon>
|
||||
</TMagicTooltip>
|
||||
<slot name="code-block-panel-tool" :id="data.key" :data="data.codeBlockContent"></slot>
|
||||
</template>
|
||||
</Tree>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject, ref } from 'vue';
|
||||
import { computed, inject } from 'vue';
|
||||
import { Close, Edit, View } from '@element-plus/icons-vue';
|
||||
|
||||
import { DepTargetType } from '@tmagic/dep';
|
||||
import { tMagicMessage, tMagicMessageBox, TMagicTooltip, TMagicTree } from '@tmagic/design';
|
||||
import type { Id } from '@tmagic/schema';
|
||||
import { tMagicMessage, tMagicMessageBox, TMagicTooltip } from '@tmagic/design';
|
||||
import type { Id, MNode } from '@tmagic/schema';
|
||||
|
||||
import Icon from '@editor/components/Icon.vue';
|
||||
import AppManageIcon from '@editor/icons/AppManageIcon.vue';
|
||||
import CodeIcon from '@editor/icons/CodeIcon.vue';
|
||||
import { type CodeBlockListSlots, CodeDeleteErrorType, type CodeDslItem, type Services } from '@editor/type';
|
||||
import Tree from '@editor/components/Tree.vue';
|
||||
import { useFilter } from '@editor/hooks/use-filter';
|
||||
import { useNodeStatus } from '@editor/hooks/use-node-status';
|
||||
import { type CodeBlockListSlots, CodeDeleteErrorType, type Services, type TreeNodeData } from '@editor/type';
|
||||
|
||||
defineSlots<CodeBlockListSlots>();
|
||||
|
||||
@ -68,38 +53,49 @@ const emit = defineEmits<{
|
||||
remove: [id: string];
|
||||
}>();
|
||||
|
||||
const { codeBlockService, depService, editorService } = inject<Services>('services') || {};
|
||||
const services = inject<Services>('services');
|
||||
const { codeBlockService, depService, editorService } = services || {};
|
||||
|
||||
// 代码块列表
|
||||
const codeList = computed(() =>
|
||||
const codeList = computed<TreeNodeData[]>(() =>
|
||||
Object.values(depService?.getTargets(DepTargetType.CODE_BLOCK) || {}).map((target) => {
|
||||
// 组件节点
|
||||
const compNodes = Object.entries(target.deps).map(([id, dep]) => ({
|
||||
const compNodes: TreeNodeData[] = Object.entries(target.deps).map(([id, dep]) => ({
|
||||
name: dep.name,
|
||||
type: 'node',
|
||||
id,
|
||||
children: dep.keys.map((key) => ({ name: key, id: key, type: 'key' })),
|
||||
id: `${target.id}_${id}`,
|
||||
key: id,
|
||||
items: dep.keys.map((key) => {
|
||||
const data: TreeNodeData = { name: `${key}`, id: `${target.id}_${id}_${key}`, type: 'key' };
|
||||
return data;
|
||||
}),
|
||||
}));
|
||||
return {
|
||||
|
||||
const data: TreeNodeData = {
|
||||
id: target.id,
|
||||
key: target.id,
|
||||
name: target.name,
|
||||
type: 'code',
|
||||
codeBlockContent: codeBlockService?.getCodeContentById(target.id),
|
||||
children: compNodes,
|
||||
items: compNodes,
|
||||
};
|
||||
|
||||
return data;
|
||||
}),
|
||||
);
|
||||
// 默认展开组件层级的节点
|
||||
const expandedKeys = computed(() => codeList.value.map((item) => item.id));
|
||||
const editable = computed(() => codeBlockService?.getEditStatus());
|
||||
|
||||
const filterNode = (value: string, data: CodeDslItem): boolean => {
|
||||
const filterNode = (value: string, data: MNode): boolean => {
|
||||
if (!value) {
|
||||
return true;
|
||||
}
|
||||
return `${data.name}${data.id}`.toLocaleLowerCase().indexOf(value.toLocaleLowerCase()) !== -1;
|
||||
return `${data.name}${data.id}`.toLocaleLowerCase().includes(value.toLocaleLowerCase());
|
||||
};
|
||||
|
||||
const { nodeStatusMap } = useNodeStatus(codeList);
|
||||
const { filterTextChangeHandler } = useFilter(codeList, nodeStatusMap, filterNode);
|
||||
|
||||
const editable = computed(() => codeBlockService?.getEditStatus());
|
||||
|
||||
// 选中组件
|
||||
const selectComp = (compId: Id) => {
|
||||
const stage = editorService?.get('stage');
|
||||
@ -107,11 +103,9 @@ const selectComp = (compId: Id) => {
|
||||
stage?.select(compId);
|
||||
};
|
||||
|
||||
const clickHandler = (data: any, node: any) => {
|
||||
const clickHandler = (event: MouseEvent, data: any) => {
|
||||
if (data.type === 'node') {
|
||||
selectComp(data.id);
|
||||
} else if (data.type === 'key') {
|
||||
selectComp(node.parent.data.id);
|
||||
selectComp(data.key);
|
||||
}
|
||||
};
|
||||
|
||||
@ -122,7 +116,7 @@ const editCode = (id: string) => {
|
||||
|
||||
const deleteCode = async (id: string) => {
|
||||
const currentCode = codeList.value.find((codeItem) => codeItem.id === id);
|
||||
const existBinds = Boolean(currentCode?.children.length);
|
||||
const existBinds = Boolean(currentCode?.items?.length);
|
||||
const undeleteableList = codeBlockService?.getUndeletableList() || [];
|
||||
if (!existBinds && !undeleteableList.includes(id)) {
|
||||
await tMagicMessageBox.confirm('确定删除该代码块吗?', '提示', {
|
||||
@ -142,11 +136,7 @@ const deleteCode = async (id: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
const tree = ref<InstanceType<typeof TMagicTree>>();
|
||||
|
||||
defineExpose({
|
||||
filter(val: string) {
|
||||
tree.value?.filter(val);
|
||||
},
|
||||
filter: filterTextChangeHandler,
|
||||
});
|
||||
</script>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<TMagicScrollbar class="m-editor-code-block-list m-editor-dep-list-panel">
|
||||
<TMagicScrollbar class="m-editor-code-block-list m-editor-layer-panel">
|
||||
<slot name="code-block-panel-header">
|
||||
<div class="search-wrapper">
|
||||
<SearchInput @search="filterTextChangeHandler"></SearchInput>
|
||||
@ -17,6 +17,7 @@
|
||||
</template>
|
||||
</CodeBlockList>
|
||||
</TMagicScrollbar>
|
||||
|
||||
<!-- 代码块编辑区 -->
|
||||
<CodeBlockEditor
|
||||
v-if="codeConfig"
|
||||
|
@ -1,48 +1,40 @@
|
||||
<template>
|
||||
<TMagicTree
|
||||
ref="tree"
|
||||
class="magic-editor-layer-tree"
|
||||
node-key="id"
|
||||
empty-text="暂无代码块"
|
||||
default-expand-all
|
||||
:expand-on-click-node="false"
|
||||
:data="list"
|
||||
:highlight-current="true"
|
||||
@node-click="clickHandler"
|
||||
>
|
||||
<template #default="{ data }">
|
||||
<div :id="data.id" class="list-container">
|
||||
<div class="list-item">
|
||||
<Icon v-if="data.type === 'code'" class="codeIcon" :icon="Coin"></Icon>
|
||||
<Icon v-if="data.type === 'node'" class="compIcon" :icon="Aim"></Icon>
|
||||
<span class="name" :class="{ code: data.type === 'ds', hook: data.type === 'key' }">{{
|
||||
data.type === 'key' ? data.name : `${data.name}(${data.id})`
|
||||
}}</span>
|
||||
<!-- 右侧工具栏 -->
|
||||
<div class="right-tool" v-if="data.type === 'ds'">
|
||||
<TMagicTooltip effect="dark" :content="editable ? '编辑' : '查看'" placement="bottom">
|
||||
<Icon :icon="editable ? Edit : View" class="edit-icon" @click.stop="editHandler(`${data.id}`)"></Icon>
|
||||
</TMagicTooltip>
|
||||
<TMagicTooltip v-if="editable" effect="dark" content="删除" placement="bottom">
|
||||
<Icon :icon="Close" class="edit-icon" @click.stop="removeHandler(`${data.id}`)"></Icon>
|
||||
</TMagicTooltip>
|
||||
<slot name="data-source-panel-tool" :data="data"></slot>
|
||||
</div>
|
||||
</div>
|
||||
<Tree :data="list" :node-status-map="nodeStatusMap" @node-click="clickHandler">
|
||||
<template #tree-node-label="{ data }">
|
||||
<div
|
||||
:class="{
|
||||
ds: data.type === 'ds',
|
||||
hook: data.type === 'key',
|
||||
disabled: data.type === 'key' || data.type === 'ds',
|
||||
}"
|
||||
>
|
||||
{{ data.name }} {{ data.key ? `(${data.key})` : '' }}
|
||||
</div>
|
||||
</template>
|
||||
</TMagicTree>
|
||||
<template #tree-node-tool="{ data }">
|
||||
<TMagicTooltip v-if="data.type === 'ds'" effect="dark" :content="editable ? '编辑' : '查看'" placement="bottom">
|
||||
<Icon :icon="editable ? Edit : View" class="edit-icon" @click.stop="editHandler(`${data.key}`)"></Icon>
|
||||
</TMagicTooltip>
|
||||
<TMagicTooltip v-if="data.type === 'ds' && editable" effect="dark" content="删除" placement="bottom">
|
||||
<Icon :icon="Close" class="edit-icon" @click.stop="removeHandler(`${data.key}`)"></Icon>
|
||||
</TMagicTooltip>
|
||||
<slot name="data-source-panel-tool" :data="data"></slot>
|
||||
</template>
|
||||
</Tree>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject, ref } from 'vue';
|
||||
import { Aim, Close, Coin, Edit, View } from '@element-plus/icons-vue';
|
||||
import { computed, inject } from 'vue';
|
||||
import { Close, Edit, View } from '@element-plus/icons-vue';
|
||||
|
||||
import { DepTargetType } from '@tmagic/dep';
|
||||
import { tMagicMessageBox, TMagicTooltip, TMagicTree } from '@tmagic/design';
|
||||
import { DepData, Id } from '@tmagic/schema';
|
||||
import { tMagicMessageBox, TMagicTooltip } from '@tmagic/design';
|
||||
import { DepData, Id, MNode } from '@tmagic/schema';
|
||||
|
||||
import Icon from '@editor/components/Icon.vue';
|
||||
import Tree from '@editor/components/Tree.vue';
|
||||
import { useFilter } from '@editor/hooks/use-filter';
|
||||
import { useNodeStatus } from '@editor/hooks/use-node-status';
|
||||
import type { DataSourceListSlots, Services } from '@editor/type';
|
||||
|
||||
defineSlots<DataSourceListSlots>();
|
||||
@ -66,32 +58,39 @@ const dsDep = computed(() => depService?.getTargets(DepTargetType.DATA_SOURCE) |
|
||||
const dsMethodDep = computed(() => depService?.getTargets(DepTargetType.DATA_SOURCE_METHOD) || {});
|
||||
const dsCondDep = computed(() => depService?.getTargets(DepTargetType.DATA_SOURCE_COND) || {});
|
||||
|
||||
const getKeyTreeConfig = (dep: DepData[string], type?: string) =>
|
||||
dep.keys.map((key) => ({ name: key, id: key, type: 'key', isMethod: type === 'method', isCond: type === 'cond' }));
|
||||
const getKeyTreeConfig = (dep: DepData[string], type?: string, parentKey?: Id) =>
|
||||
dep.keys.map((key) => ({
|
||||
name: key,
|
||||
id: `${parentKey}_${key}`,
|
||||
type: 'key',
|
||||
isMethod: type === 'method',
|
||||
isCond: type === 'cond',
|
||||
}));
|
||||
|
||||
const getNodeTreeConfig = (id: string, dep: DepData[string], type?: string) => ({
|
||||
const getNodeTreeConfig = (id: string, dep: DepData[string], type?: string, parentKey?: Id) => ({
|
||||
name: dep.name,
|
||||
type: 'node',
|
||||
id,
|
||||
children: getKeyTreeConfig(dep, type),
|
||||
id: `${parentKey}_${id}`,
|
||||
key: id,
|
||||
items: getKeyTreeConfig(dep, type, `${parentKey}_${id}`),
|
||||
});
|
||||
|
||||
/**
|
||||
* 生成tree中依赖节点的数据
|
||||
* @param children 节点
|
||||
* @param items 节点
|
||||
* @param deps 依赖
|
||||
* @param type 依赖类型
|
||||
*/
|
||||
const mergeChildren = (children: any[], deps: DepData, type?: string) => {
|
||||
const mergeChildren = (dsId: Id, items: any[], deps: DepData, type?: string) => {
|
||||
Object.entries(deps).forEach(([id, dep]) => {
|
||||
// 已经生成过的节点
|
||||
const nodeItem = children.find((item) => item.id === id);
|
||||
const nodeItem = items.find((item) => item.key === id);
|
||||
// 节点存在,则追加依赖的key
|
||||
if (nodeItem) {
|
||||
nodeItem.children = nodeItem.children.concat(getKeyTreeConfig(dep, type));
|
||||
nodeItem.items = nodeItem.items.concat(getKeyTreeConfig(dep, type, nodeItem.key));
|
||||
} else {
|
||||
// 节点不存在,则生成
|
||||
children.push(getNodeTreeConfig(id, dep, type));
|
||||
items.push(getNodeTreeConfig(id, dep, type, dsId));
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -102,21 +101,32 @@ const list = computed(() =>
|
||||
const dsMethodDeps = dsMethodDep.value[ds.id]?.deps || {};
|
||||
const dsCondDeps = dsCondDep.value[ds.id]?.deps || {};
|
||||
|
||||
const children: any[] = [];
|
||||
const items: any[] = [];
|
||||
// 数据源依赖分为三种类型:key/node、method、cond,是分开存储,这里将其合并展示
|
||||
mergeChildren(children, dsDeps);
|
||||
mergeChildren(children, dsMethodDeps, 'method');
|
||||
mergeChildren(children, dsCondDeps, 'cond');
|
||||
mergeChildren(ds.id, items, dsDeps);
|
||||
mergeChildren(ds.id, items, dsMethodDeps, 'method');
|
||||
mergeChildren(ds.id, items, dsCondDeps, 'cond');
|
||||
|
||||
return {
|
||||
id: ds.id,
|
||||
key: ds.id,
|
||||
name: ds.title,
|
||||
type: 'ds',
|
||||
children,
|
||||
items,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
const filterNode = (value: string, data: MNode): boolean => {
|
||||
if (!value) {
|
||||
return true;
|
||||
}
|
||||
return `${data.name}${data.id}`.toLocaleLowerCase().includes(value.toLocaleLowerCase());
|
||||
};
|
||||
|
||||
const { nodeStatusMap } = useNodeStatus(list);
|
||||
const { filterTextChangeHandler } = useFilter(list, nodeStatusMap, filterNode);
|
||||
|
||||
const editHandler = (id: string) => {
|
||||
emit('edit', id);
|
||||
};
|
||||
@ -138,19 +148,13 @@ const selectComp = (compId: Id) => {
|
||||
stage?.select(compId);
|
||||
};
|
||||
|
||||
const clickHandler = (data: any, node: any) => {
|
||||
const clickHandler = (event: MouseEvent, data: any) => {
|
||||
if (data.type === 'node') {
|
||||
selectComp(data.id);
|
||||
} else if (data.type === 'key') {
|
||||
selectComp(node.parent.data.id);
|
||||
selectComp(data.key);
|
||||
}
|
||||
};
|
||||
|
||||
const tree = ref<InstanceType<typeof TMagicTree>>();
|
||||
|
||||
defineExpose({
|
||||
filter(val: string) {
|
||||
tree.value?.filter(val);
|
||||
},
|
||||
filter: filterTextChangeHandler,
|
||||
});
|
||||
</script>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<TMagicScrollbar class="data-source-list-panel m-editor-dep-list-panel">
|
||||
<TMagicScrollbar class="data-source-list-panel m-editor-layer-panel">
|
||||
<div class="search-wrapper">
|
||||
<SearchInput @search="filterTextChangeHandler"></SearchInput>
|
||||
<TMagicPopover v-if="editable" placement="right">
|
||||
|
@ -8,7 +8,7 @@
|
||||
v-if="page && nodeStatusMap"
|
||||
tabindex="-1"
|
||||
ref="tree"
|
||||
:data="[page]"
|
||||
:data="nodeData"
|
||||
:node-status-map="nodeStatusMap"
|
||||
@node-dragover="handleDragOver"
|
||||
@node-dragstart="handleDragStart"
|
||||
@ -18,14 +18,18 @@
|
||||
@node-mouseenter="mouseenterHandler"
|
||||
@node-click="nodeClickHandler"
|
||||
>
|
||||
<template #tree-node-content="{ data: nodeData }">
|
||||
<slot name="layer-node-content" :data="nodeData"> </slot>
|
||||
</template>
|
||||
|
||||
<template #tree-node-tool="{ data: nodeData }">
|
||||
<slot name="layer-node-tool" :data="nodeData">
|
||||
<LayerNodeTool :data="nodeData"></LayerNodeTool>
|
||||
</slot>
|
||||
</template>
|
||||
|
||||
<template #tree-node-content="{ data: nodeData }">
|
||||
<slot name="layer-node-content" :data="nodeData"> </slot>
|
||||
<template #tree-node-label="{ data: nodeData }">
|
||||
<slot name="layer-node-label" :data="nodeData"></slot>
|
||||
</template>
|
||||
</Tree>
|
||||
|
||||
@ -43,13 +47,13 @@ import type { MNode } from '@tmagic/schema';
|
||||
|
||||
import SearchInput from '@editor/components/SearchInput.vue';
|
||||
import Tree from '@editor/components/Tree.vue';
|
||||
import type { LayerPanelSlots, MenuButton, MenuComponent, Services } from '@editor/type';
|
||||
import { useFilter } from '@editor/hooks/use-filter';
|
||||
import { LayerPanelSlots, MenuButton, MenuComponent, Services, TreeNodeData } from '@editor/type';
|
||||
|
||||
import LayerMenu from './LayerMenu.vue';
|
||||
import LayerNodeTool from './LayerNodeTool.vue';
|
||||
import { useClick } from './use-click';
|
||||
import { useDrag } from './use-drag';
|
||||
import { useFilter } from './use-filter';
|
||||
import { useKeybinding } from './use-keybinding';
|
||||
import { useNodeStatus } from './use-node-status';
|
||||
|
||||
@ -69,6 +73,7 @@ const editorService = services?.editorService;
|
||||
const tree = ref<InstanceType<typeof Tree>>();
|
||||
|
||||
const page = computed(() => editorService?.get('page'));
|
||||
const nodeData = computed<TreeNodeData[]>(() => (!page.value ? [] : [page.value]));
|
||||
|
||||
const { nodeStatusMap } = useNodeStatus(services);
|
||||
const { isCtrlKeyDown } = useKeybinding(services, tree);
|
||||
@ -84,7 +89,7 @@ const filterNodeMethod = (v: string, data: MNode): boolean => {
|
||||
return `${data.id}${name}${data.type}`.includes(v);
|
||||
};
|
||||
|
||||
const { filterTextChangeHandler } = useFilter(services, nodeStatusMap, filterNodeMethod);
|
||||
const { filterTextChangeHandler } = useFilter(nodeData, nodeStatusMap, filterNodeMethod);
|
||||
|
||||
const collapseAllHandler = () => {
|
||||
if (!page.value || !nodeStatusMap.value) return;
|
||||
|
@ -12,7 +12,7 @@
|
||||
<template #body>
|
||||
<Tree
|
||||
class="m-editor-node-list-menu magic-editor-layer-tree"
|
||||
:data="[page]"
|
||||
:data="nodeData"
|
||||
:node-status-map="nodeStatusMap"
|
||||
@node-click="clickHandler"
|
||||
></Tree>
|
||||
@ -28,7 +28,7 @@ import type { MNode } from '@tmagic/schema';
|
||||
|
||||
import FloatingBox from '@editor/components/FloatingBox.vue';
|
||||
import Tree from '@editor/components/Tree.vue';
|
||||
import { useFilter } from '@editor/layouts/sidebar/layer/use-filter';
|
||||
import { useFilter } from '@editor/hooks/use-filter';
|
||||
import { useNodeStatus } from '@editor/layouts/sidebar/layer/use-node-status';
|
||||
import type { Services, TreeNodeData } from '@editor/type';
|
||||
|
||||
@ -42,12 +42,13 @@ const box = ref<InstanceType<typeof FloatingBox>>();
|
||||
const stage = computed(() => editorService?.get('stage'));
|
||||
const page = computed(() => editorService?.get('page'));
|
||||
const nodes = computed(() => editorService?.get('nodes') || []);
|
||||
const nodeData = computed<TreeNodeData[]>(() => (!page.value ? [] : [page.value]));
|
||||
|
||||
const { nodeStatusMap } = useNodeStatus(services);
|
||||
|
||||
const filterNodeMethod = (value: string, data: MNode): boolean => data.id === value;
|
||||
|
||||
const { filterTextChangeHandler } = useFilter(services, nodeStatusMap, filterNodeMethod);
|
||||
const { filterTextChangeHandler } = useFilter(nodeData, nodeStatusMap, filterNodeMethod);
|
||||
|
||||
const unWatch = watch(
|
||||
stage,
|
||||
|
@ -1,59 +0,0 @@
|
||||
.m-editor-dep-list-panel {
|
||||
.magic-editor-layer-tree {
|
||||
padding-top: 48px;
|
||||
}
|
||||
|
||||
.search-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tmagic-design-button {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.list-container {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
.list-item {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
|
||||
.right-tool {
|
||||
display: flex;
|
||||
width: fit-content !important;
|
||||
align-items: center;
|
||||
.edit-icon {
|
||||
margin: 0 5px;
|
||||
}
|
||||
}
|
||||
.name {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: 0 !important;
|
||||
flex: 1;
|
||||
line-height: 22px;
|
||||
|
||||
&.code {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
&.hook {
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,4 +4,23 @@
|
||||
.m-editor-tree {
|
||||
padding-top: 48px;
|
||||
}
|
||||
|
||||
.search-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tmagic-design-button {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
@import "./event.scss";
|
||||
@import "./layout.scss";
|
||||
@import "./breadcrumb.scss";
|
||||
@import "./dep-list.scss";
|
||||
@import "./data-source.scss";
|
||||
@import "./data-source-fields.scss";
|
||||
@import "./data-source-methods.scss";
|
||||
|
@ -59,13 +59,24 @@
|
||||
width: 100px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
.disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.hook {
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tree-node-tool {
|
||||
margin-right: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.tmagic-design-icon {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -81,12 +81,12 @@ export interface DataSourceListSlots {
|
||||
|
||||
export interface LayerNodeSlots {
|
||||
'layer-node-content'(props: { data: MNode }): any;
|
||||
'layer-node-tool'(props: { data: MNode }): any;
|
||||
'layer-node-label'(props: { data: MNode }): any;
|
||||
}
|
||||
|
||||
export interface LayerPanelSlots extends LayerNodeSlots {
|
||||
'layer-panel-header'(props: {}): any;
|
||||
'layer-node-tool'(props: { data: MNode }): any;
|
||||
'layer-node-content'(props: { data: MNode }): any;
|
||||
}
|
||||
|
||||
export interface PropsPanelSlots {
|
||||
|
Loading…
x
Reference in New Issue
Block a user