project.ts 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. import type { ProjectInfo, Page, ReferLine } from "#/project";
  2. import { defineStore } from "pinia";
  3. import { asyncComponentAll } from "shalu-dashboard-ui";
  4. import { ScreenFillEnum } from "@/enum/screenFillEnum";
  5. import { update, defaultsDeep } from "lodash";
  6. import { getNormalizedContainer } from "@/utils/common";
  7. import { editPageDesignApi } from "@/api/index";
  8. import { message } from "ant-design-vue";
  9. type ProjectState = {
  10. projectInfo: ProjectInfo;
  11. activePageIndex: number;
  12. addCompData: {
  13. key: number;
  14. name: string;
  15. componentType: string;
  16. container: {
  17. style: Record<string, any>;
  18. props: Record<string, any>;
  19. };
  20. } | null;
  21. mode: "edit" | "player";
  22. selectedElementKeys: number[];
  23. };
  24. const defaultPage: Page = {
  25. key: "1",
  26. name: "页面1",
  27. background: {
  28. type: "color",
  29. color: "#0B074BFF",
  30. image: "",
  31. fillType: "",
  32. },
  33. elements: [],
  34. referLines: [],
  35. };
  36. const CURRENT_PROJECT = "currentProject";
  37. export const useProjectStore = defineStore({
  38. id: "project",
  39. state: (): ProjectState => ({
  40. // 项目信息
  41. projectInfo: {
  42. pageId: "",
  43. name: "",
  44. description: "",
  45. sizeType: "",
  46. width: 0,
  47. height: 0,
  48. fillType: ScreenFillEnum.AUTO,
  49. pages: [{ ...defaultPage }],
  50. },
  51. // 当前编辑页面索引
  52. activePageIndex: 0,
  53. // 添加组件临时数据
  54. addCompData: null,
  55. // 视图模式
  56. mode: "edit",
  57. // 选中的元素
  58. selectedElementKeys: [],
  59. }),
  60. getters: {
  61. referLines(state) {
  62. return state.projectInfo.pages[state.activePageIndex].referLines;
  63. },
  64. elements(state) {
  65. return state.projectInfo.pages[state.activePageIndex].elements;
  66. },
  67. currentPage(state) {
  68. return state.projectInfo.pages[state.activePageIndex];
  69. },
  70. currentSelectedElements(state) {
  71. return state.projectInfo.pages[state.activePageIndex].elements.filter(
  72. (item) => state.selectedElementKeys.includes(item.key)
  73. );
  74. },
  75. },
  76. actions: {
  77. setProjectInfo(info: any) {
  78. Object.assign(this.projectInfo, info);
  79. localStorage.setItem(CURRENT_PROJECT, JSON.stringify(info));
  80. },
  81. getCurrentProjectInfo(): ProjectInfo | undefined {
  82. let info = JSON.parse(localStorage.getItem(CURRENT_PROJECT) || "null");
  83. if (!info) {
  84. info = {
  85. name: "默认项目",
  86. description: "这是一个默认项目",
  87. sizeType: "custom",
  88. width: 1280,
  89. height: 720,
  90. fillType: ScreenFillEnum.AUTO,
  91. pages: [{ ...defaultPage }],
  92. };
  93. }
  94. this.setProjectInfo(info as unknown as ProjectInfo);
  95. return info;
  96. },
  97. updateProjectInfo(info: any) {
  98. Object.assign(this.projectInfo, info);
  99. },
  100. updateProjectInfoByPath(path: string, payload: any) {
  101. update(this.projectInfo, path, () => payload);
  102. },
  103. addReferLine(line: ReferLine) {
  104. this.projectInfo.pages[this.activePageIndex].referLines.push(line);
  105. },
  106. removeReferLine(key: number) {
  107. const index = this.referLines.findIndex((line) => line.key === key);
  108. index !== -1 &&
  109. this.projectInfo.pages[this.activePageIndex].referLines.splice(
  110. index,
  111. 1
  112. );
  113. },
  114. updateReferLine(line: ReferLine) {
  115. const index = this.referLines.findIndex((l) => l.key === line.key);
  116. if (index !== -1) {
  117. this.projectInfo.pages[this.activePageIndex].referLines[index] = line;
  118. }
  119. },
  120. // 添加组件
  121. async addElement(element: any) {
  122. this.addCompData = null;
  123. if (!element) return;
  124. const elements = this.projectInfo.pages[this.activePageIndex].elements;
  125. // 获取每个自定义组件暴露出来的默认属性
  126. const { defaultPropsValue } =
  127. (await asyncComponentAll[element.componentType]?.()) || {};
  128. const { width = 400, height = 260 } =
  129. defaultPropsValue?.container?.props || {};
  130. const { props: containerProps = {}, style = {} } =
  131. defaultPropsValue?.container || {};
  132. const index =
  133. elements.filter((item) => item.componentType === element.componentType)
  134. .length + 1;
  135. const { x, y } = element.container.props;
  136. const container = getNormalizedContainer({
  137. style,
  138. props: {
  139. ...containerProps,
  140. width,
  141. height,
  142. x: x - width / 2,
  143. y: y - height / 2,
  144. },
  145. });
  146. // 添加组件
  147. this.projectInfo.pages[this.activePageIndex].elements.push({
  148. ...defaultsDeep(element, defaultPropsValue),
  149. name: element.name + index,
  150. zIndex: elements.length + 1,
  151. visible: true,
  152. locked: false,
  153. container,
  154. });
  155. this.selectedElementKeys = [element.key];
  156. },
  157. // 更新组件
  158. updateElement(key: number, path: string, payload: any) {
  159. const pageIndex = this.activePageIndex;
  160. const element = this.projectInfo.pages[pageIndex].elements.find((item) => item.key === key);
  161. const elementIndex = this.projectInfo.pages[pageIndex].elements.findIndex((item) => item.key === key);
  162. // 如果是锁定状态不能修改宽高 位置
  163. if (
  164. element &&
  165. element.locked &&
  166. [
  167. "container.props.width",
  168. "container.props.height",
  169. "container.props.x",
  170. "container.props.y",
  171. ].includes(path)
  172. )
  173. return;
  174. if (element) {
  175. update(this.projectInfo.pages[pageIndex].elements[elementIndex], path, () => payload);
  176. }
  177. },
  178. // 删除组件
  179. removeElement(key: number) {
  180. const index = this.projectInfo.pages[
  181. this.activePageIndex
  182. ].elements.findIndex((item) => item.key === key);
  183. if (index !== -1) {
  184. this.projectInfo.pages[this.activePageIndex].elements.splice(index, 1);
  185. }
  186. },
  187. // 设置临时添加组件数据
  188. setAddCompData(data: any) {
  189. this.addCompData = data;
  190. },
  191. // 清除临时添加组件数据
  192. clearAddCompData() {
  193. this.addCompData = null;
  194. },
  195. setMode(mode: "edit" | "player") {
  196. this.mode = mode;
  197. },
  198. // 设置选中的元素
  199. setSelectedElementKeys(keys: number[]) {
  200. this.selectedElementKeys = keys;
  201. },
  202. // 删除所有选中的元素
  203. clearAllSelectedElement() {
  204. this.selectedElementKeys = [];
  205. },
  206. // 设置当前页面背景
  207. setCurrentPageBackground(background: any) {
  208. this.projectInfo.pages[this.activePageIndex].background = background;
  209. },
  210. setFillType(fillType: ScreenFillEnum) {
  211. this.projectInfo.fillType = fillType;
  212. },
  213. // 保存当前项目到服务器
  214. async handleSaveProject() {
  215. const params = {
  216. appPageId: this.projectInfo.pageId,
  217. json: JSON.stringify(this.projectInfo),
  218. html: "",
  219. js: "",
  220. type: 0,
  221. };
  222. editPageDesignApi(params);
  223. message.success("保存成功");
  224. },
  225. },
  226. });