util.ts 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. import { reactive, watch } from "vue";
  2. import {
  3. Attrib,
  4. Entity,
  5. EntityClass,
  6. GetSetPick,
  7. TGroup,
  8. TLayer,
  9. TShape,
  10. } from "../type";
  11. import { inRevise, toRawType } from "./public";
  12. export const setShapeConfig = <T extends TShape | TGroup>(
  13. shape: T,
  14. config: GetSetPick<T>
  15. ) => {
  16. for (const key in config) {
  17. shape[key as any](config[key]);
  18. }
  19. };
  20. export const getChangePart = <T>(newIds: T[], oldIds: T[] = []) => {
  21. const addPort = newIds.filter((newId) => !oldIds.includes(newId));
  22. const delPort = oldIds.filter((oldId) => !newIds.includes(oldId));
  23. const holdPort = oldIds.filter((oldId) => newIds.includes(oldId));
  24. return { addPort, delPort, holdPort };
  25. };
  26. export const getChangeAllPoart = <T extends Attrib>(
  27. newAttribs: T[],
  28. oldAttribs: T[]
  29. ) => {
  30. const newIds = newAttribs.map(({ id }) => id);
  31. const oldIds = oldAttribs.map(({ id }) => id);
  32. const ports = getChangePart(newIds, oldIds);
  33. // 数组子项引用变化
  34. const changePort = newAttribs
  35. .filter(
  36. (newAttrib) =>
  37. !oldAttribs.includes(newAttrib) && oldIds.includes(newAttrib.id)
  38. )
  39. .map((attrib) => attrib.id);
  40. oldAttribs = newAttribs;
  41. return { ...ports, changePort };
  42. };
  43. type AttribsChange = ReturnType<typeof getChangePart> & {
  44. changePort: string[];
  45. };
  46. export const watchAttribs = (
  47. attribs: Attrib[],
  48. callback: (data: AttribsChange) => void,
  49. immediate = true
  50. ) => {
  51. return watch(
  52. () => [...attribs],
  53. (newAttribs, oldAttribs = []) => {
  54. callback(getChangeAllPoart(newAttribs, oldAttribs));
  55. },
  56. { immediate, flush: "pre" }
  57. );
  58. };
  59. export const attribsJoinEntity = <
  60. T extends Attrib,
  61. C extends EntityClass<T>,
  62. P extends Attrib
  63. >(
  64. attribs: T[],
  65. Type: C,
  66. parent?: Entity<P>,
  67. joinDestory = false,
  68. extra?: (self: InstanceType<C>) => void
  69. ) => {
  70. const findAttrib = (id: Attrib["id"]) =>
  71. attribs.find((attrib) => attrib.id === id);
  72. const cache: { [key in Attrib["id"]]: InstanceType<C> } = reactive({});
  73. const destory = (id: Attrib["id"]) => {
  74. if (id in cache) {
  75. cache[id].destory();
  76. }
  77. const shape = cache[id].getShape();
  78. shape && shape.destroy();
  79. delete cache[id];
  80. };
  81. const add = (id: Attrib["id"]) => {
  82. const attrib = findAttrib(id);
  83. if (attrib) {
  84. cache[id] = new Type({
  85. attrib,
  86. }) as InstanceType<C>;
  87. extra && extra(cache[id]);
  88. cache[id].init && cache[id].init();
  89. cache[id].attachBefore && cache[id].attachBefore();
  90. parent && cache[id].setParent(parent);
  91. cache[id].attachAfter && cache[id].attachAfter();
  92. }
  93. };
  94. const stopWatchAttribs = watchAttribs(
  95. attribs,
  96. ({ addPort, delPort, changePort }) => {
  97. delPort.forEach(destory);
  98. addPort.forEach(add);
  99. changePort.forEach((id) => {
  100. cache[id].setAttrib(findAttrib(id));
  101. });
  102. }
  103. );
  104. return {
  105. entitys: cache,
  106. destory: () => {
  107. stopWatchAttribs();
  108. if (joinDestory) {
  109. attribs.forEach((attrib) => destory(attrib.id));
  110. }
  111. },
  112. };
  113. };
  114. export const deptComputed = <T extends Record<string, any>>(
  115. getter: () => T
  116. ) => {
  117. const data = reactive(getter());
  118. const stop = watch(getter, (newData) => {
  119. if (inRevise(data, newData)) {
  120. if (Array.isArray(newData)) {
  121. newData.forEach((item, ndx) => {
  122. data[ndx] = item;
  123. });
  124. } else {
  125. Object.keys(data).forEach((key) => delete data[key]);
  126. Object.assign(data, newData);
  127. }
  128. }
  129. });
  130. return {
  131. data: data as T,
  132. stop,
  133. };
  134. };
  135. export const partialComputed = <T extends Attrib>(getter: () => T[]) => {
  136. const data = reactive(getter()) as T[];
  137. const stop = watch(getter, (newData, oldData) => {
  138. const { addPort, delPort, changePort } = getChangeAllPoart(
  139. newData,
  140. oldData
  141. );
  142. for (const delId of delPort) {
  143. const ndx = data.findIndex((i) => i.id === delId);
  144. ~ndx && data.splice(ndx, 1);
  145. }
  146. for (const addId of addPort) {
  147. const addItem = newData.find((i) => i.id === addId);
  148. addItem && data.push(addItem);
  149. }
  150. for (const changeId of changePort) {
  151. const dataNdx = data.findIndex((i) => i.id === changeId);
  152. const newDataNdx = newData.findIndex((i) => i.id === changeId);
  153. if (inRevise(data[dataNdx], newData[newDataNdx])) {
  154. data[dataNdx] = newData[newDataNdx];
  155. }
  156. }
  157. });
  158. return {
  159. data,
  160. stop,
  161. };
  162. };
  163. export const depPartialUpdate = <T>(newData: T, oldData: T): T => {
  164. if (!inRevise(newData, oldData)) {
  165. return oldData;
  166. }
  167. const nData = newData as any,
  168. oData = oldData as any;
  169. const type = toRawType(nData);
  170. switch (type) {
  171. case "Array":
  172. for (let i = 0; i < nData.length; i++) {
  173. oData[i] = depPartialUpdate(nData[i], oData[i]);
  174. }
  175. while (oData.length !== nData.length) {
  176. oData.pop();
  177. }
  178. break;
  179. case "Object":
  180. const oKeys = Object.keys(oData).sort();
  181. const nKeys = Object.keys(nData).sort();
  182. const { addPort, delPort, holdPort } = getChangePart(nKeys, oKeys);
  183. for (let i = 0; i < holdPort.length; i++) {
  184. oData[oKeys[i]] = depPartialUpdate(
  185. nData[holdPort[i]],
  186. oData[holdPort[i]]
  187. );
  188. }
  189. addPort.forEach((key) => (oData[key] = nData[key]));
  190. delPort.forEach((key) => delete oData[key]);
  191. break;
  192. default:
  193. return newData;
  194. }
  195. return oldData;
  196. };