CompTree.tsx 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  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. import { CompObject } from "@/modules/editor/controllers/SelectCtrl/compObj";
  12. type TreeItem = {
  13. key: string;
  14. title: string;
  15. value: string;
  16. children: TreeItem[];
  17. };
  18. export const CompTree = defineComponent({
  19. setup() {
  20. const { store, actions, helper, controls } = useEditor();
  21. const { compUICtrl } = controls;
  22. const state = useReactive(() => ({
  23. expandedKeys: [] as string[],
  24. treeData() {
  25. const rootComp = helper.findRootComp();
  26. let deep = 0;
  27. function getCompChildren(ids: string[]): TreeItem[] {
  28. deep += 1;
  29. if (deep > 2) ids = ids.reverse();
  30. return ids.map((id) => {
  31. const comp = helper.findComp(id) as DesignComp;
  32. return {
  33. key: comp.id,
  34. title:
  35. comp.title ||
  36. compUICtrl.state.components.get(comp.compKey)?.name ||
  37. "未命名",
  38. value: comp.id,
  39. children: getCompChildren(comp.getChildIds()),
  40. };
  41. });
  42. }
  43. return getCompChildren(rootComp?.id ? [rootComp.id] : []);
  44. },
  45. }));
  46. watch(
  47. () => store.currCompId,
  48. () => {
  49. // expandedKeys需要等Tree的内部状态更新后再赋值
  50. nextTick(() => {
  51. state.expandedKeys = store.currCompId
  52. ? helper.getCompTrees(store.currCompId).map((comp) => comp.id)
  53. : [];
  54. });
  55. }
  56. );
  57. const onDrop = (info: any) => {
  58. const dragNode = info.dragNode; //被拖拽
  59. const dropNode = info.node; //拖拽落下
  60. const dragKey = dragNode.key;
  61. const dropKey = dropNode.key;
  62. const dragPos = dragNode.pos.split("-");
  63. const dropPos = dropNode.pos.split("-");
  64. const dragLevel = dragPos.length;
  65. const dropLevel = dropPos.length;
  66. if (
  67. dragLevel >= 5 ||
  68. dragKey == "root" ||
  69. dragLevel < dropLevel ||
  70. dragLevel - dropLevel > 1
  71. ) {
  72. return;
  73. }
  74. let parentComp: any = null;
  75. if (dragLevel > dropLevel) {
  76. parentComp = helper.findComp(dropKey);
  77. } else {
  78. parentComp = helper.findParentComp(dropKey);
  79. }
  80. const currComp = helper.findComp(dragKey);
  81. const currParent = helper.findParentComp(dragKey);
  82. if (!currComp) return;
  83. const currParentComp = helper.findParentComp(dragKey);
  84. const index = currParentComp?.children.default?.indexOf(currComp.id);
  85. if (index != -1) {
  86. currParentComp?.children.default?.splice(index as number, 1);
  87. helper.extendStreamCard(currParentComp?.id || "");
  88. }
  89. if (!info.dropToGap) {
  90. parentComp?.children.default == parentComp?.children.default || [];
  91. if (dragLevel <= 3) {
  92. parentComp?.children.default?.unshift(currComp?.id);
  93. } else {
  94. parentComp?.children.default?.push(currComp?.id);
  95. }
  96. if (currParent?.id != parentComp.id) {
  97. const len = parentComp?.children.default?.length;
  98. const index = dragLevel > 3 ? len - 1 : 0;
  99. const revert = dragLevel > 3 ? true : false;
  100. sortCompInCard(parentComp, index, revert);
  101. }
  102. } else {
  103. if (dragLevel <= 3) {
  104. parentComp?.children.default?.splice(
  105. info.dropPosition,
  106. 0,
  107. currComp.id
  108. );
  109. } else {
  110. const len = parentComp?.children.default.length;
  111. parentComp?.children.default?.splice(
  112. len - info.dropPosition,
  113. 0,
  114. currComp.id
  115. );
  116. }
  117. if (currParent?.id != parentComp.id) {
  118. const revert = dragLevel > 3 ? true : false;
  119. const len = parentComp?.children.default.length - 1;
  120. const index = revert ? len - info.dropPosition : info.dropPosition;
  121. sortCompInCard(parentComp, index, revert);
  122. }
  123. }
  124. nextTick(() => {
  125. actions.pickComp(currComp.id, false);
  126. helper.extendStreamCard(parentComp.id);
  127. });
  128. };
  129. const sortCompInCard = (
  130. parentComp: DesignComp,
  131. index: number,
  132. revert = false
  133. ) => {
  134. if (parentComp?.compKey != "Container") {
  135. return;
  136. }
  137. const children = parentComp.children.default || [];
  138. if (children?.length > 0) {
  139. const prveCompId = revert ? children[index + 1] : children[index - 1];
  140. const nowCompId = children[index];
  141. const nowComp = helper.findComp(nowCompId);
  142. if (!prveCompId) {
  143. const nowCompBound = helper.getCardCompBound(nowCompId);
  144. if (!nowComp) {
  145. return;
  146. }
  147. const nowObj = new CompObject(nowComp);
  148. nowObj.worldTransform.translate(-nowCompBound.x, -nowCompBound.y);
  149. nowComp.layout.transformMatrix = nowObj.worldTransform.getMatrixStr();
  150. return;
  151. }
  152. const prevCompBound = helper.getCardCompBound(prveCompId);
  153. const prevComp = helper.findComp(prveCompId);
  154. if (!prevComp || !nowComp) {
  155. return;
  156. }
  157. const obj = new CompObject(prevComp);
  158. const yOffset = prevCompBound.h;
  159. obj.worldTransform.translate(0, yOffset);
  160. nowComp.layout.transformMatrix = obj.worldTransform.getMatrixStr();
  161. }
  162. };
  163. return () => (
  164. <div class="!overflow-x-auto">
  165. <Tree
  166. class={treeStyle}
  167. treeData={state.treeData}
  168. v-model={[state.expandedKeys, "expandedKeys"]}
  169. selectedKeys={[store.currCompId]}
  170. blockNode={true}
  171. draggable={true}
  172. onDrop={onDrop}
  173. onSelect={(ids) => {
  174. const id =
  175. (ids[0] as string) || state.expandedKeys.at(-2) || "root";
  176. actions.pickComp(id);
  177. }}
  178. >
  179. {{
  180. title: (data: any) => {
  181. return <CompNode title={data.title} id={data.key} />;
  182. },
  183. }}
  184. </Tree>
  185. </div>
  186. );
  187. },
  188. });
  189. const CompNode = defineComponent({
  190. props: {
  191. id: string().isRequired,
  192. title: string().isRequired,
  193. },
  194. setup(props) {
  195. const editor = useEditor();
  196. const comp = editor.helper.findComp(props.id);
  197. const textTitle = (value: string) => {
  198. const reg = /<[^>]+>/gi;
  199. const text = value.replace(reg, "");
  200. return text.substring(0, 10);
  201. };
  202. return () => {
  203. if (!comp) return;
  204. const compOpts = editor.controls.compUICtrl.state.components.get(
  205. comp.compKey
  206. );
  207. const actions = TreeToolbars[comp.compKey] || TreeToolbars.default;
  208. const thumbnail =
  209. comp.compKey === "Image"
  210. ? comp.value.url
  211. : comp.thumbnail || compOpts?.thumbnail;
  212. const title =
  213. comp.compKey === "Text" ? textTitle(comp.value) : props.title;
  214. return (
  215. <div class={[nodeStyle, "flex"]}>
  216. <Image src={thumbnail} size={240} />
  217. <span class="flex-1 w-0 text-12px whitespace-nowrap overflow-hidden overflow-ellipsis pr-6px">
  218. {title}
  219. </span>
  220. <span class="actions space-x-2px whitespace-nowrap transition">
  221. {actions.map((action, i) => {
  222. return action.getVisible.call(editor, comp) ? (
  223. <action.component
  224. key={i}
  225. class={["p-4px", action.getValue?.(comp) ? "active" : ""]}
  226. value={action.getValue?.(comp)}
  227. onClick={(e: MouseEvent) => {
  228. e.stopPropagation();
  229. action.onClick.call(editor, comp);
  230. }}
  231. />
  232. ) : null;
  233. })}
  234. </span>
  235. </div>
  236. );
  237. };
  238. },
  239. });
  240. const treeStyle = css`
  241. .ant-tree-switcher {
  242. display: flex;
  243. width: 14px;
  244. padding-right: 4px;
  245. justify-content: center;
  246. align-items: center;
  247. }
  248. .ant-tree-indent-unit {
  249. width: 4px;
  250. }
  251. `;
  252. const nodeStyle = css`
  253. display: flex;
  254. align-items: center;
  255. padding: 6px 2px;
  256. @apply rounded;
  257. > img {
  258. width: 20px;
  259. height: 20px;
  260. margin-right: 4px;
  261. object-fit: contain;
  262. }
  263. .actions {
  264. .inficon {
  265. opacity: 0;
  266. font-size: 14px;
  267. &.active {
  268. opacity: 1;
  269. }
  270. }
  271. }
  272. &:hover {
  273. .actions {
  274. .inficon {
  275. opacity: 1;
  276. }
  277. }
  278. }
  279. `;