CompTree.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import { TreeToolbars } from "@/modules/editor/objects/Toolbars";
  2. import { css } from "@linaria/core";
  3. import { Image } from "@queenjs/ui";
  4. import { useReactive } from "@queenjs/use";
  5. import { Tree, TreeProps } from "ant-design-vue";
  6. import { defineComponent, nextTick, watch } from "vue";
  7. import { string, bool } from "vue-types";
  8. import { useEditor } from "../../../..";
  9. import { DesignComp } from "../../../../objects/DesignTemp/DesignComp";
  10. import { TreeDataItem } from "ant-design-vue/lib/tree";
  11. type TreeItem = {
  12. key: string;
  13. title: string;
  14. value: string;
  15. children: TreeItem[];
  16. };
  17. export const CompTree = defineComponent({
  18. setup() {
  19. const { store, actions, helper, controls } = useEditor();
  20. const { compUICtrl } = controls;
  21. const state = useReactive(() => ({
  22. expandedKeys: [] as string[],
  23. treeData() {
  24. const rootComp = helper.findRootComp();
  25. function getCompChildren(ids: string[]): TreeItem[] {
  26. return ids.map((id) => {
  27. const comp = helper.findComp(id) as DesignComp;
  28. return {
  29. key: comp.id,
  30. title:
  31. comp.title ||
  32. compUICtrl.state.components.get(comp.compKey)?.name ||
  33. "未命名",
  34. value: comp.id,
  35. children: getCompChildren(comp.getChildIds()),
  36. };
  37. });
  38. }
  39. return getCompChildren(rootComp?.id ? [rootComp.id] : []);
  40. },
  41. }));
  42. watch(
  43. () => store.currCompId,
  44. () => {
  45. // expandedKeys需要等Tree的内部状态更新后再赋值
  46. nextTick(() => {
  47. state.expandedKeys = store.currCompId
  48. ? helper.getCompTrees(store.currCompId).map((comp) => comp.id)
  49. : [];
  50. });
  51. }
  52. );
  53. const onDrop = (info: any) => {
  54. const dragNode = info.dragNode; //被拖拽
  55. const dropNode = info.node; //拖拽落下
  56. const dragKey = dragNode.key;
  57. const dropKey = dropNode.key;
  58. const dragPos = dragNode.pos.split("-");
  59. const dropPos = dropNode.pos.split("-");
  60. const dragLevel = dragPos.length;
  61. const dropLevel = dropPos.length;
  62. if (
  63. dragLevel >= 5 ||
  64. dragKey == "root" ||
  65. dragLevel < dropLevel ||
  66. dragLevel - dropLevel > 1
  67. ) {
  68. return;
  69. }
  70. let parentComp = null;
  71. if (dragLevel > dropLevel) {
  72. parentComp = helper.findComp(dropKey);
  73. } else {
  74. parentComp = helper.findParentComp(dropKey);
  75. }
  76. const currComp = helper.findComp(dragKey);
  77. if (!currComp) return;
  78. const currParentComp = helper.findParentComp(dragKey);
  79. const index = currParentComp?.children.default?.indexOf(currComp.id);
  80. if (index != -1) {
  81. currParentComp?.children.default?.splice(index as number, 1);
  82. }
  83. if (!info.dropToGap) {
  84. parentComp?.children.default == parentComp?.children.default || [];
  85. parentComp?.children.default?.unshift(currComp?.id);
  86. } else {
  87. parentComp?.children.default?.splice(info.dropPosition, 0, currComp.id);
  88. }
  89. };
  90. return () => (
  91. <Tree
  92. class={treeStyle}
  93. treeData={state.treeData}
  94. v-model={[state.expandedKeys, "expandedKeys"]}
  95. selectedKeys={[store.currCompId]}
  96. blockNode={true}
  97. draggable={true}
  98. onDrop={onDrop}
  99. onSelect={(ids) => {
  100. const id = (ids[0] as string) || state.expandedKeys.at(-2) || "root";
  101. actions.pickComp(id);
  102. }}
  103. >
  104. {{
  105. title: (data: any) => {
  106. return <CompNode title={data.title} id={data.key} />;
  107. },
  108. }}
  109. </Tree>
  110. );
  111. },
  112. });
  113. const CompNode = defineComponent({
  114. props: {
  115. id: string().isRequired,
  116. title: string().isRequired,
  117. },
  118. setup(props) {
  119. const editor = useEditor();
  120. const comp = editor.helper.findComp(props.id);
  121. return () => {
  122. if (!comp) return;
  123. const compOpts = editor.controls.compUICtrl.state.components.get(
  124. comp.compKey
  125. );
  126. const actions = TreeToolbars[comp.compKey] || TreeToolbars.default;
  127. const thumbnail =
  128. comp.compKey === "Image"
  129. ? comp.value.url
  130. : comp.thumbnail || compOpts?.thumbnail;
  131. return (
  132. <div class={nodeStyle}>
  133. <Image src={thumbnail} size={240} />
  134. <span class="w-0 flex-1 truncate">{props.title}</span>
  135. <span class="space-x-4px">
  136. {actions.map((action, i) =>
  137. action.getVisible.call(editor, comp) ? (
  138. <action.component
  139. key={i}
  140. class="p-4px"
  141. value={action.getValue?.(comp)}
  142. onClick={(e: MouseEvent) => {
  143. e.stopPropagation();
  144. action.onClick.call(editor, comp);
  145. }}
  146. />
  147. ) : null
  148. )}
  149. </span>
  150. </div>
  151. );
  152. };
  153. },
  154. });
  155. const treeStyle = css`
  156. .ant-tree-switcher {
  157. display: flex;
  158. justify-content: center;
  159. align-items: center;
  160. }
  161. `;
  162. const nodeStyle = css`
  163. display: flex;
  164. align-items: center;
  165. padding: 6px;
  166. @apply rounded;
  167. > img {
  168. width: 24px;
  169. height: 24px;
  170. margin-right: 6px;
  171. object-fit: contain;
  172. }
  173. `;