common.ts 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227
  1. import { reactive, ref } from 'vue';
  2. import { MessagePlugin } from 'tdesign-vue-next';
  3. import localforage from 'localforage';
  4. import dayjs from 'dayjs';
  5. import axios from 'axios';
  6. import router from '@/router/index';
  7. import { useUser } from '@/services/user';
  8. import { showNotification, Meta2dBackData, checkData } from '@/services/utils';
  9. import { noLoginTip, localStorageName } from '@/services/utils';
  10. import { upload, dataURLtoBlob } from '@/services/file';
  11. import { delImage, addCollection, updateCollection, getLe5leV } from '@/services/api';
  12. import { baseVer } from '@/services/upgrade';
  13. import { debounce } from './debouce';
  14. import { deepClone, isDomShapes } from '@meta2d/core';
  15. import { useSelection } from '@/services/selections';
  16. import { rootDomain } from './defaults';
  17. import { updateObject } from './object';
  18. import i18n from '../i18n';
  19. const $t = i18n.global.t;
  20. const { select } = useSelection();
  21. const assets = reactive({
  22. home: `https://${rootDomain.slice(1)}`,
  23. account: `https://account${rootDomain}',`,
  24. '3d': `https://3d${rootDomain}`,
  25. '2d': `https://2d${rootDomain}`,
  26. helps_v: [
  27. {
  28. name:$t( '产品介绍'),
  29. url: `https://doc${rootDomain}/document/118756411`,
  30. },
  31. {
  32. name: $t('快速上手'),
  33. url: `https://doc${rootDomain}/document/119363000`,
  34. },
  35. {
  36. name: $t('使用手册'),
  37. url: `https://doc${rootDomain}/document/118764244`,
  38. },
  39. {
  40. name: $t('快捷键'),
  41. url: `https://doc${rootDomain}/document/119620214`,
  42. divider: true,
  43. },
  44. {
  45. name: $t('企业服务与支持'),
  46. url: `https://doc${rootDomain}/document/119296274`,
  47. divider: true,
  48. },
  49. {
  50. name: $t('关于我们'),
  51. url: `https://${rootDomain.slice(1)}/about.html`,
  52. },
  53. ],
  54. });
  55. export const useAssets = () => {
  56. const getAssets = async () => {
  57. // 官网或安装包版本
  58. // if (import.meta.env.VITE_TRIAL != 1) {
  59. // return;
  60. // }
  61. // 企业版
  62. const ret = await axios.get('/api/assets');
  63. if (ret) {
  64. Object.assign(assets, ret);
  65. }
  66. };
  67. return {
  68. assets,
  69. getAssets,
  70. };
  71. };
  72. const meta2dData = reactive({
  73. name:$t('新建项目')
  74. });
  75. export const useMeta2dData = () => {
  76. const getMeta2dData = async () => {
  77. return meta2dData;
  78. };
  79. const setMeta2dData = async (data:any) => {
  80. for(const key in data){
  81. meta2d.store.data[key] = data[key];
  82. }
  83. updateObject(meta2dData, data);
  84. };
  85. return {
  86. meta2dData,
  87. getMeta2dData,
  88. setMeta2dData,
  89. };
  90. };
  91. const dot = ref(false);
  92. export const useDot = () => {
  93. const getDot = async () => {
  94. return dot;
  95. };
  96. const setDot = async (value = true) => {
  97. dot.value = value;
  98. if (value) {
  99. tree.patch = true;
  100. debounce(autoSave, 3000);
  101. }
  102. };
  103. return {
  104. dot,
  105. getDot,
  106. setDot,
  107. };
  108. };
  109. const anchorShow = ref(false);
  110. export const useDAnchor = () => {
  111. const getDAnchor = async () => {
  112. return anchorShow.value;
  113. };
  114. const setDAnchor = async (value = true) => {
  115. anchorShow.value = value;
  116. };
  117. return {
  118. anchorShow,
  119. getDAnchor,
  120. setDAnchor,
  121. };
  122. };
  123. const { user } = useUser();
  124. export enum SaveType {
  125. Save,
  126. SaveAs,
  127. }
  128. let saveTimer: any = 0;
  129. let saveFlag: boolean = true;
  130. export function queryURLParams(value?: string) {
  131. let url = value || window.location.href.split('?')[1];
  132. const urlSearchParams = new URLSearchParams(url);
  133. const params = Object.fromEntries(urlSearchParams.entries());
  134. return params;
  135. }
  136. // TODO 保存图纸
  137. export const save = async (
  138. type: SaveType = SaveType.Save,
  139. vType?: string,
  140. notice?: boolean,
  141. userFlag?:number,
  142. onlyJson?:boolean
  143. ) => {
  144. if (!saveFlag) {
  145. return;
  146. }
  147. if (saveTimer) {
  148. clearTimeout(saveTimer);
  149. }
  150. saveFlag = false;
  151. saveTimer = setTimeout(() => {
  152. saveFlag = true;
  153. }, 2000);
  154. // 区分是模板还是方案
  155. meta2d.stopAnimate();
  156. if( vType === 'v.component'){
  157. // @ts-ignore
  158. meta2d.store.data.component = true
  159. }
  160. const data: Meta2dBackData = meta2d.data();
  161. if(!data.pens.length){
  162. MessagePlugin.warning($t('画布为空,无法保存!'));
  163. return;
  164. }
  165. if (!(user && user.id)) {
  166. MessagePlugin.warning(noLoginTip);
  167. localforage.setItem(localStorageName, JSON.stringify(data));
  168. return;
  169. }
  170. // 保存为组件
  171. if (
  172. vType === 'v.component' &&
  173. meta2d.store.active &&
  174. meta2d.store.active.length === 1 &&
  175. meta2d.store.active[0].name === 'combine'
  176. ) {
  177. let comData: any = {
  178. center: data.center,
  179. folder: '',
  180. paths:data.paths,
  181. origin: data.origin,
  182. scale: data.scale,
  183. x: data.x,
  184. y: data.y,
  185. };
  186. // comData.name =
  187. // (meta2d.store.active[0] as any).description ||
  188. // `meta2d.${new Date().toLocaleString()}`;
  189. comData.name = (meta2d.store.active[0] as any).description ||'新建项目';
  190. let blob: Blob;
  191. try {
  192. blob = dataURLtoBlob(meta2d.activeToPng(10,512) + '');
  193. } catch (e) {
  194. MessagePlugin.error(
  195. $t('无法下载,宽度不合法,可能没有选中画笔/选中画笔大小超出浏览器最大限制')
  196. );
  197. return;
  198. }
  199. //todo 传参数
  200. const file:any = await upload(blob, true, comData.name , `/大屏/组件/默认`,'new');
  201. if (!file) {
  202. return;
  203. }
  204. // // 缩略图
  205. comData.image = file.url;
  206. comData.fullname = file.fullname;
  207. comData.component = true;
  208. const allPens = meta2d.canvas.getAllByPens(meta2d.store.active);
  209. comData.componentDatas = meta2d.toComponent(
  210. allPens,
  211. (meta2d.store.data as Meta2dBackData).showChild,
  212. false
  213. );
  214. comData.version = data.version;
  215. let ids = allPens.map(item=>item.id);
  216. comData.pens = data.pens.filter((pen)=>ids.includes(pen.id));
  217. const body = {
  218. data:comData,
  219. image:comData.image,
  220. name:comData.name,
  221. folder: '',
  222. template:userFlag===2?true:(data.isTemplate||false),
  223. case:comData.case
  224. }
  225. let ret: any = await addCollection('v.component', body); // 新增
  226. if (ret.error) {
  227. return;
  228. }
  229. MessagePlugin.success($t('成功保存为组件!'));
  230. meta2d.emit('business-save', vType);
  231. return;
  232. }
  233. //删除老图片 新图片上传
  234. checkData(data);
  235. if (
  236. (globalThis as any).beforeSaveMeta2d &&
  237. !(await (globalThis as any).beforeSaveMeta2d(data))
  238. ) {
  239. return;
  240. }
  241. if(!data.isSystem && data.id){
  242. if(vType === 'v-template' && (data.component||data.isTemplate===false)){
  243. //模版 -> 组件/方案
  244. delete data.id;
  245. delete data._id;
  246. delete data.folder;
  247. }else if(vType === 'v.component' && !data.component){
  248. //组件 -> 模版/方案
  249. delete data.id;
  250. delete data._id;
  251. delete data.folder;
  252. }else if(!vType && (data.isTemplate&&data.component)){
  253. //方案 -> 模版/组件
  254. delete data.id;
  255. delete data._id;
  256. delete data.folder;
  257. }
  258. }
  259. const thumbFolder =
  260. vType === 'v-template'
  261. ? '模板'
  262. : vType === 'v.component'
  263. ? '组件'
  264. : '方案';
  265. const params = queryURLParams();
  266. if (type === SaveType.SaveAs) {
  267. //另存为去掉teams信息
  268. delete data.teams;
  269. delete data.folder;
  270. delete data.id;
  271. }
  272. //如果不是自己创建的团队图纸,就不去修改缩略图(没有权限去删除缩略图)
  273. if (!data.name) {
  274. // 文件名称
  275. // data.name = `meta2d.${new Date().toLocaleString()}`;
  276. data.name = `新建项目`;
  277. (meta2d.store.data as Meta2dBackData).name = data.name;
  278. }
  279. if (data.component || vType === 'v.component') {
  280. data.component = true;
  281. // pens 存储原数据用于二次编辑 ; componentDatas 组合后的数据,用于复用
  282. data.componentDatas = meta2d.toComponent(
  283. undefined,
  284. (meta2d.store.data as Meta2dBackData).showChild,
  285. false //自定义组合节点生成默认锚点
  286. );
  287. } else {
  288. data.component = false; // 必要值
  289. }
  290. let collection = data.component ? 'v.component' : 'v';
  291. if(data.id && data.ownerId !== user.id){
  292. //不是拥有者,判断有无编辑权限
  293. let temRet:any = await updateCollection(collection, {id:data.id || data._id, name: data.name});
  294. if(temRet.error){
  295. return;
  296. }
  297. }
  298. if(!onlyJson){
  299. // if (!data.id || (data.ownerId === user.id)) {
  300. // 图纸不是创建用户的图纸 先不改缩略图
  301. for (const pen of meta2d.store.data.pens) {
  302. if (['iframe'].includes(pen.name)) {
  303. //重新生成绘制图片
  304. pen.onRenderPenRaw?.(pen);
  305. }
  306. }
  307. let blob: Blob;
  308. try {
  309. blob = dataURLtoBlob(meta2d.toPng(0,undefined,true,512) + '');
  310. } catch (e) {
  311. MessagePlugin.error(
  312. $t('无法下载,宽度不合法,画布可能没有画笔/画布大小超出浏览器最大限制')
  313. );
  314. return;
  315. }
  316. let conflict = 'new';
  317. // if (data.id && type === SaveType.Save) {
  318. // // conflict = undefined
  319. // if (data.image && !(await delImage(data.image))) {
  320. // return;
  321. // }
  322. // }
  323. const file:any = await upload(
  324. blob,
  325. true,
  326. // 更新是缩略图路径一般为/file/2023/1025/1/1/新建项目_dfa573a5
  327. // data.image?.split('/').pop()更新操作项目名不变
  328. data.name,// + '.' + dayjs().format('YYYYMMDDHHMMss'),
  329. `/大屏/${thumbFolder}/${data.folder || '默认'}`,
  330. conflict
  331. );
  332. if (!file) {
  333. return;
  334. }
  335. // if (data.id && type === SaveType.Save) {
  336. // // conflict = undefined
  337. // if (data.image && !(await delImage(data.image))) {
  338. // return;
  339. // }
  340. // }
  341. // 缩略图
  342. data.image = file.url;
  343. data.filename = file.filename;
  344. (meta2d.store.data as Meta2dBackData).image = data.image;
  345. (meta2d.store.data as Meta2dBackData).filename = data.filename;
  346. // }
  347. }else{
  348. data.image = undefined;
  349. }
  350. let ret: any;
  351. !data.version && (data.version = baseVer);
  352. // TODO
  353. if (!data.folder) {
  354. data.folder = ''; // folder.name;
  355. }
  356. let childIds = [];
  357. data.pens.forEach((pen)=>{
  358. if(pen.name === 'iframe'){
  359. if(pen.iframe.indexOf(`view${rootDomain}`) !== -1){
  360. let params = queryURLParams(pen.iframe.split('?')[1])
  361. childIds.push(params.id);
  362. }
  363. }
  364. });
  365. // data.childIds = childIds;
  366. const body:any = {
  367. data,
  368. image:data.image,
  369. name:data.name,
  370. folder:data.folder,
  371. // shared:true,
  372. template:userFlag===2?true:(data.isTemplate||false),
  373. case:data.case,
  374. childIds
  375. }
  376. delete data.name;
  377. delete data.image;
  378. delete data.folder;
  379. delete data.case;
  380. delete data.isSystem;
  381. delete data.isTemplate;
  382. if(params.n && data.ownerId !== user.id){
  383. for (const k of delAttrs) {
  384. delete (data as any)[k];
  385. }
  386. }
  387. if (type === SaveType.SaveAs) {
  388. // 另存为一定走 新增 ,由于后端 未控制 userId 等属性,清空一下
  389. for (const k of delAttrs) {
  390. delete (data as any)[k];
  391. }
  392. body.template = false;
  393. body.component = false;
  394. ret = await addCollection(collection, body);
  395. } else {
  396. // if (data._id && data.teams && data.owner?.id !== user.id) {
  397. // // 团队图纸 不允许修改文件夹信息
  398. // delete data.folder;
  399. // ret = await updateCollection(collection, data);
  400. // } else
  401. if (data.id || data._id) {
  402. body.ownerId = data.ownerId;
  403. body.ownerName = data.ownerName;
  404. ret = await updateCollection(collection, {id:data.id || data._id,...body});
  405. } else {
  406. ret = await addCollection(collection, body); // 新增
  407. }
  408. }
  409. if (ret.error) {
  410. return;
  411. }
  412. // 保存图纸之后的钩子函数
  413. globalThis.afterSaveMeta2d && (await globalThis.afterSaveMeta2d(ret));
  414. if (
  415. !data.id ||
  416. router.currentRoute.value.query.version ||
  417. type === SaveType.SaveAs // 另存为肯定走新增,也会产生新的 id
  418. ) {
  419. data.id = ret.id;
  420. (meta2d.store.data as Meta2dBackData).id = data.id;
  421. router.replace({
  422. path: '/',
  423. query: {
  424. id: data.id,
  425. r: Date.now() + '',
  426. c: vType === 'v.component' ? 1 : undefined,
  427. },
  428. });
  429. }
  430. notice && MessagePlugin.success($t('保存成功!'));
  431. meta2d.emit('business-save', vType);
  432. dot.value = false;
  433. localforage.removeItem(localStorageName);
  434. return true;
  435. };
  436. export const chargeDialogShow = ref(false);
  437. export const autoSaveAS = async(id:string)=>{
  438. //先请求打开图纸
  439. const ret: any = await getLe5leV(id);
  440. if(!ret.data && ret.price>0){
  441. return;
  442. }
  443. sessionStorage.setItem('opening', '1');
  444. router.push({
  445. path: '/',
  446. query: {
  447. r: Date.now() + '',
  448. },
  449. });
  450. //去除图纸一些信息
  451. for (const k of delAttrs) {
  452. delete ret[k];
  453. delete ret.data[k];
  454. }
  455. chargeDialogShow.value = false;
  456. meta2d.open(ret.data);
  457. meta2d.fitView();
  458. //另存为付费用户图纸
  459. setTimeout(()=>{
  460. save(SaveType.SaveAs, '', undefined, 1);
  461. },500)
  462. }
  463. const pen = ref(false);
  464. export const drawPen = () => {
  465. meta2d.inactive();
  466. try {
  467. if (!meta2d.canvas.drawingLineName) {
  468. // 开了钢笔,需要关掉铅笔
  469. meta2d.canvas.pencil && drawingPencil();
  470. meta2d.drawLine(meta2d.store.options.drawingLineName);
  471. } else {
  472. meta2d.finishDrawLine();
  473. meta2d.drawLine();
  474. }
  475. //钢笔
  476. pen.value = !!meta2d.canvas.drawingLineName;
  477. } catch (e: any) {
  478. MessagePlugin.warning(e.message);
  479. }
  480. };
  481. const pencil = ref(false);
  482. const drawingPencil = () => {
  483. try {
  484. if (!meta2d.canvas.pencil) {
  485. // 开了铅笔需要关掉钢笔
  486. meta2d.canvas.drawingLineName && drawPen();
  487. meta2d.drawingPencil();
  488. } else {
  489. meta2d.stopPencil();
  490. }
  491. pencil.value = meta2d.canvas.pencil || false;
  492. } catch (e: any) {
  493. MessagePlugin.warning(e.message);
  494. }
  495. };
  496. export const magnifier = ref(false);
  497. export const showMagnifier = () => {
  498. if (!meta2d.canvas.magnifierCanvas.magnifier) {
  499. meta2d.showMagnifier();
  500. } else {
  501. meta2d.hideMagnifier();
  502. }
  503. magnifier.value = meta2d.canvas.magnifierCanvas.magnifier;
  504. };
  505. export const map = ref(false);
  506. export const showMap = () => {
  507. if (!meta2d.map?.isShow) {
  508. meta2d.showMap();
  509. } else {
  510. meta2d.hideMap();
  511. }
  512. map.value = meta2d.map?.isShow;
  513. };
  514. export const title = $t( '系统可能不会保存您所做的更改,是否继续?');
  515. export const unLogin = $t('未登录,系统可能不会保存您的文件,是否继续?');
  516. export const unsave = $t('当前文件未保存,是否继续?(开通vip可享受自动保存服务)');
  517. export function autoSave(force = false) {
  518. if (!dot.value && (!force || router.currentRoute.value.query.id)) {
  519. return;
  520. }
  521. const data: any = meta2d.data();
  522. if (
  523. user &&
  524. user.id &&
  525. user.vip &&
  526. (data.id||data._id) &&
  527. !data.component &&
  528. // data.owner &&
  529. data.ownerId === user.id
  530. ) {
  531. let _vType = '';
  532. if (data.isTemplate) {
  533. _vType = 'v-template';
  534. }
  535. save(SaveType.Save, _vType, false, undefined, true);
  536. } else {
  537. // data.updateAt = dayjs().format();
  538. localforage.setItem(localStorageName, JSON.stringify(data));
  539. }
  540. }
  541. // export const notificFn = async (fn: Function, params: any) => {
  542. // if (!(user && user.id)) {
  543. // if (await showNotification(unLogin)) {
  544. // fn(params);
  545. // }
  546. // } else {
  547. // if (dot.value) {
  548. // if (user.isVip) {
  549. // if (await save(SaveType.Save)) {
  550. // fn(params);
  551. // }
  552. // } else {
  553. // if (await showNotification(unsave)) {
  554. // fn(params);
  555. // }
  556. // }
  557. // } else {
  558. // fn(params);
  559. // }
  560. // }
  561. // };
  562. export const onScaleWindow = () => {
  563. // meta2d.fitView();
  564. meta2d.fitSizeView(true, 32);
  565. };
  566. export const onScaleFull = () => {
  567. meta2d.scale(1);
  568. // meta2d.centerView();
  569. const { x, y, origin, center } = meta2d.store.data;
  570. meta2d.translate(-x - origin.x, -y - origin.y);
  571. meta2d.translate(meta2d.store.options.x || 0, meta2d.store.options.y || 0);
  572. };
  573. export const blank = async (save = true) => {
  574. meta2d.canvas.drawingLineName && drawPen();
  575. meta2d.canvas.pencil && drawingPencil();
  576. meta2d.canvas.magnifierCanvas.magnifier && showMagnifier();
  577. meta2d.map?.isShow && showMap();
  578. save && autoSave(true);
  579. dot.value = false;
  580. sessionStorage.removeItem('openingId');
  581. meta2d.open({ pens: [], background: '#1e2430', color: '#bdc7db', width: 1920, height: 1080 } as any);
  582. select();
  583. };
  584. export const newFileDialog = ref({
  585. show:false
  586. });
  587. // let lastTime = false;
  588. export const newFile = () => {
  589. const hasPen = meta2d.store.data.pens.length;
  590. if(hasPen&&dot.value){
  591. // MessagePlugin.warning('当前画布未保存,请先保存!');
  592. // lastTime = true;
  593. // return;
  594. newFileDialog.value.show = true;
  595. return;
  596. }
  597. // lastTime = false;
  598. blank();
  599. router.push({
  600. path: '/',
  601. query: {
  602. r: Date.now() + '',
  603. },
  604. });
  605. // setTimeout(() => {
  606. // autoSave(true);
  607. // }, 300);
  608. meta2d.emit('business-assets',true);
  609. };
  610. export const inTreePanel = reactive({
  611. value: false,
  612. timer: undefined,
  613. });
  614. const tree = reactive({
  615. list: [],
  616. patch: true,
  617. });
  618. export const getPenTree = () => {
  619. tree.patch = false;
  620. const list = [];
  621. for (const item of meta2d.store.data.pens) {
  622. if (item.parentId) {
  623. continue;
  624. }
  625. const elem = calcElem(item);
  626. elem && list.push(elem);
  627. }
  628. tree.list = list.reverse();
  629. return tree.list;
  630. };
  631. const iframeTree = reactive({
  632. list: [],
  633. patch: true,
  634. });
  635. export const getIframeTree = () => {
  636. iframeTree.patch = false;
  637. const list = [];
  638. for (const item of meta2d.store.data.pens) {
  639. if (item.name !== 'iframe') {
  640. continue;
  641. }
  642. const elem = calcElem(item);
  643. elem && list.push(elem);
  644. }
  645. iframeTree.list = list.reverse();
  646. return iframeTree.list;
  647. };
  648. export const getPenAnimations = (idOrTag?: string) => {
  649. const animations = [];
  650. let pens: any[] = meta2d.store.active || [];
  651. if (idOrTag) {
  652. pens = meta2d.find(idOrTag) || [];
  653. }
  654. for (const pen of pens) {
  655. if (pen.animations) {
  656. for (const a of pen.animations) {
  657. animations.push(a.name);
  658. }
  659. }
  660. }
  661. return Array.from(new Set(animations));
  662. };
  663. const calcElem = (node: any) => {
  664. if (!node) {
  665. return;
  666. }
  667. let tag = '中';
  668. if (
  669. isDomShapes.includes(node.name) ||
  670. node.name.endsWith('Dom') ||
  671. meta2d.store.options.domShapes.includes(node.name)
  672. ) {
  673. tag = 'dom';
  674. } else if (node.canvasLayer === 1) {
  675. tag = '模板';
  676. } else if (node.name === 'image') {
  677. if(node.canvasLayer === 2){
  678. tag = '下层';
  679. }else if(node.canvasLayer===3){
  680. tag = '中层';
  681. }else{
  682. tag = '上层';
  683. }
  684. } else {
  685. tag = '中层';
  686. }
  687. const elem: any = {
  688. label: (node as any).description || node.name,
  689. value: node.id,
  690. locked: node.locked,
  691. visible: node.visible,
  692. tag,
  693. };
  694. if (!node.children) {
  695. return elem;
  696. }
  697. elem.children = [];
  698. for (const id of node.children) {
  699. const child = calcElem(meta2d.store.pens[id]);
  700. child && elem.children.push(child);
  701. }
  702. return elem;
  703. };
  704. export const setChildrenVisible = (node: any, v: boolean) => {
  705. const children = node.getChildren();
  706. if (children && children.length > 0) {
  707. for (const child of children) {
  708. child.data.visible = v;
  709. setChildrenVisible(child, v);
  710. }
  711. }
  712. };
  713. export const fonts = [
  714. '新宋体',
  715. '微软雅黑',
  716. '黑体',
  717. '楷体',
  718. '斗鱼追光体',
  719. '庞门正道标题体',
  720. 'DS-DIGI-1',
  721. 'DS-DIGIB-2',
  722. 'DS-DIGII-3',
  723. 'DS-DIGIT-4',
  724. 'ALIBABA Regular',
  725. 'ALIBABA Bold',
  726. '-apple-system',
  727. 'BlinkMacSystemFont',
  728. 'PingFang SC',
  729. 'Hiragino Sans GB',
  730. 'Microsoft YaHei UI',
  731. 'Microsoft YaHei',
  732. 'fangsong',
  733. 'Source Han Sans CN',
  734. 'sans-serif',
  735. 'serif',
  736. 'Apple Color Emoji',
  737. 'Segoe UI Emoji',
  738. 'Segoe UI Symbol',
  739. ];
  740. export const delAttrs = [
  741. 'userId',
  742. 'shared',
  743. 'team',
  744. 'owner',
  745. 'username',
  746. 'editor',
  747. 'editorId',
  748. 'editorName',
  749. 'createdAt',
  750. 'tags',
  751. 'image',
  752. 'id',
  753. '_id',
  754. 'view',
  755. 'updatedAt',
  756. 'star',
  757. 'recommend',
  758. ];
  759. export const typeOptions = [
  760. {
  761. label: $t('字符串'),
  762. value: 'string',
  763. },
  764. {
  765. label: $t('整数'),
  766. value: 'integer',
  767. },
  768. {
  769. label: $t('浮点数'),
  770. value: 'float',
  771. },
  772. {
  773. label: $t('布尔'),
  774. value: 'bool',
  775. },
  776. {
  777. label: $t('对象'),
  778. value: 'object',
  779. },
  780. {
  781. label: $t('数组'),
  782. value: 'array',
  783. },
  784. ];
  785. let folder = reactive<any>({
  786. _id: '',
  787. });
  788. export const useFolder = () => {
  789. const getFolder = async () => {
  790. return folder;
  791. };
  792. const setFolder = async (value: any) => {
  793. folder = deepClone(value);
  794. };
  795. return {
  796. folder,
  797. getFolder,
  798. setFolder,
  799. };
  800. };
  801. export function changeType(value: string) {
  802. if (value === 'false') {
  803. return false;
  804. } else if (value === 'true') {
  805. return true;
  806. } else if (
  807. value === '' ||
  808. isNaN(Number(value)) ||
  809. (typeof value === 'string' && value.endsWith('.'))
  810. ) {
  811. // 小数也进入这个
  812. return value;
  813. }
  814. if(value.length>15){
  815. return value;
  816. }
  817. return Number(value);
  818. }
  819. // 附加缓存数据
  820. const extendData = reactive<any>({
  821. lineIntersect: false,
  822. });
  823. export const useExtendData = () => {
  824. const setExtendData = (key: any,value:any) => {
  825. extendData[key] = value;
  826. }
  827. const getExtendData = (key: any) => {
  828. return extendData[key];
  829. }
  830. return {
  831. extendData,
  832. getExtendData,
  833. setExtendData,
  834. };
  835. };
  836. const typeMap = {
  837. 'text': 'string',
  838. // 'integer',
  839. 'textarea':'string',
  840. 'number':'float',
  841. 'switch':'bool',
  842. // 'array',
  843. 'code':'object'
  844. }
  845. export function dealDataBeforeOpen(data){
  846. data.oldV = false;
  847. data.pens.forEach((pen)=>{
  848. if(pen.image){
  849. pen.image = pen.image.split('?')[0];
  850. }
  851. if (!(pen.animations && pen.animations.length)) {
  852. if (!pen.type && pen.frames) {
  853. pen.animations = [
  854. {
  855. name: $t('动画1'),
  856. temType: 'id',
  857. animate: 'custom',
  858. frames: deepClone(pen.frames),
  859. animateCycle: pen.animateCycle,
  860. keepAnimateState: pen.keepAnimateState,
  861. linear: pen.linear,
  862. autoPlay: pen.autoPlay,
  863. nextAnimate: pen.nextAnimate,
  864. },
  865. ];
  866. data.oldV = true;
  867. } else if (pen.type && pen.lineAnimateType !== undefined) {
  868. pen.animations = [
  869. {
  870. name: $t('动画1'),
  871. temType: 'id',
  872. animateSpan: pen.animateSpan || 1,
  873. lineAnimateType: pen.lineAnimateType,
  874. animateColor: pen.animateColor,
  875. animateReverse: pen.animateReverse,
  876. animateCycle: pen.animateCycle,
  877. animateLineWidth: pen.animateLineWidth,
  878. autoPlay: pen.autoPlay,
  879. nextAnimate: pen.nextAnimate,
  880. },
  881. ];
  882. data.oldV = true;
  883. }
  884. }
  885. if (pen.events?.length) {
  886. for (let i = 0; i < pen.events.length; i++) {
  887. const event = pen.events[i];
  888. // pen.events.forEach((event)=>{
  889. if (event.action !== undefined) {
  890. data.oldV = true;
  891. // 老格式
  892. if (event.name === 'valueUpdate') {
  893. if (event.where && event.where.key) {
  894. /*
  895. if (!pen.realTimes) {
  896. pen.realTimes = [];
  897. }
  898. let index = pen.realTimes.findIndex(
  899. (el) => el.key === event.where.key
  900. );
  901. let trigger = {
  902. name: '状态1',
  903. conditionType: 'and',
  904. conditions: [
  905. {
  906. type: event.where?.type === 'custom' ? 'fn' : '',
  907. key: event.where?.key,
  908. operator: event.where?.comparison,
  909. value: event.where?.value,
  910. fnJs: event.where?.fnJs,
  911. },
  912. ],
  913. actions: [
  914. {
  915. action: event.action,
  916. value: event.value,
  917. params: event.params,
  918. },
  919. ],
  920. };
  921. if (index !== -1) {
  922. pen.realTimes[index].triggers.push(trigger);
  923. } else {
  924. pen.realTimes.push({
  925. label: event.where.key,
  926. key: event.where.key,
  927. triggers: [trigger],
  928. });
  929. }
  930. pen.events.splice(i, 1);
  931. i--;*/
  932. //new
  933. if (!pen.triggers) {
  934. pen.triggers = [];
  935. }
  936. let trigger = {
  937. name: $t('状态1'),
  938. conditionType: 'and',
  939. conditions: [
  940. {
  941. type: event.where?.type === 'custom' ? 'fn' : '',
  942. key: event.where?.key,
  943. operator: event.where?.comparison,
  944. value: event.where?.value,
  945. fnJs: event.where?.fnJs,
  946. },
  947. ],
  948. actions: [
  949. {
  950. action: event.action,
  951. value: event.value,
  952. params: event.params,
  953. },
  954. ],
  955. };
  956. let index = pen.triggers.findIndex(
  957. (el) => el.temKey === event.where.key
  958. );
  959. if(index !== -1){
  960. const state = pen.triggers[index].status;
  961. const idx = state.findIndex((el)=>el.conditions?.length&&el.conditions[0].key === trigger.conditions[0].key&&el.conditions[0].operator === trigger.conditions[0].operator&&el.conditions[0].value === trigger.conditions[0].value);
  962. if(idx!==-1){
  963. state[idx].actions.push(...deepClone(trigger.actions));
  964. }else{
  965. pen.triggers[index].status.push(trigger);
  966. }
  967. }else{
  968. pen.triggers.push({
  969. temKey:event.where?.key,
  970. name:$t('场景状态') + (i+1),
  971. status:[trigger]
  972. });
  973. }
  974. pen.events.splice(i, 1);
  975. i--;
  976. } else {
  977. //TODO 没有选择触发条件的情况
  978. if (event.value.indexOf('pen.value') !== -1) {
  979. if (!pen.realTimes) {
  980. pen.realTimes = [];
  981. }
  982. pen.realTimes.push({
  983. label: 'value',
  984. key: 'value',
  985. triggers: [
  986. {
  987. name: $t('状态1'),
  988. conditionType: 'and',
  989. conditions: [],
  990. actions: [
  991. {
  992. action: event.action,
  993. value: event.value,
  994. params: event.params,
  995. },
  996. ],
  997. },
  998. ],
  999. });
  1000. pen.events.splice(i, 1);
  1001. i--;
  1002. }
  1003. }
  1004. } else {
  1005. event.conditionType = 'and';
  1006. event.actions = [
  1007. {
  1008. action: event.action,
  1009. value: event.value,
  1010. params: event.params,
  1011. },
  1012. ];
  1013. if (event.where && event.where.type) {
  1014. event.conditions = [
  1015. {
  1016. type: event.where.type === 'custom' ? 'fn' : '',
  1017. key: event.where.key,
  1018. operator: event.where.comparison,
  1019. //valueType target
  1020. value: event.where.value,
  1021. fnJs: event.where.fnJs,
  1022. },
  1023. ];
  1024. }
  1025. delete event.action;
  1026. delete event.value;
  1027. delete event.params;
  1028. delete event.where;
  1029. }
  1030. }
  1031. // });
  1032. }
  1033. }
  1034. //状态格式转换
  1035. if(!(pen.triggers?.length) && pen.realTimes?.length){
  1036. pen.triggers = [];
  1037. pen.realTimes.forEach((realTime,index)=>{
  1038. const delIdx = [];
  1039. realTime.triggers?.forEach((trigger,_idx)=>{
  1040. if(trigger.conditions?.length){
  1041. trigger.conditions.forEach((condition,_idx)=>{
  1042. condition.key = realTime.key;
  1043. condition.keyLabel = realTime.label || realTime.key;
  1044. });
  1045. if(trigger.conditions?.length === 1){
  1046. const _idx2 = realTime.triggers.findIndex((el)=>el.conditions?.length&&el.conditions[0].key === trigger.conditions[0].key&&el.conditions[0].operator === trigger.conditions[0].operator&&el.conditions[0].value === trigger.conditions[0].value);
  1047. if(_idx !== _idx2){
  1048. realTime.triggers[_idx2].actions.push(...deepClone(realTime.triggers[_idx].actions));
  1049. delIdx.push(_idx);
  1050. }
  1051. }
  1052. }
  1053. });
  1054. delIdx.forEach((idx,index)=>{
  1055. realTime.triggers.splice((idx-index),1);
  1056. });
  1057. if(realTime.triggers?.length){
  1058. pen.triggers.push({
  1059. name: $t('状态情况') + (index+1),
  1060. status:deepClone(realTime.triggers)
  1061. });
  1062. }
  1063. delete realTime.triggers;
  1064. });
  1065. }
  1066. if(pen.triggers?.length){
  1067. pen.triggers = pen.triggers.filter((item)=>item.status?.length);
  1068. }
  1069. //绑定变量
  1070. if(pen.form&&pen.name !== 'echarts'){
  1071. if(!pen.realTimes){
  1072. pen.realTimes = [];
  1073. }
  1074. pen.form.forEach((form)=>{
  1075. pen.realTimes.push({
  1076. "label": form.name,
  1077. "key": form.key,
  1078. "type": typeMap[form.type]||'string',
  1079. "bind": {
  1080. "label": form.dataIds?.name,
  1081. "id": form.dataIds?.dataId
  1082. },
  1083. "enableMock": false
  1084. });
  1085. });
  1086. delete pen.form;
  1087. }
  1088. if(pen.name === 'table2'){
  1089. pen.name = 'tablePlus'
  1090. }
  1091. });
  1092. if(!data.networks){
  1093. data.networks = [];
  1094. }
  1095. if(data.http){
  1096. if(!data.https?.length){
  1097. data.networks.push({
  1098. name: `https`,
  1099. type:'subscribe',
  1100. headers:data.httpHeaders,
  1101. interval:data.httpTimeInterval,
  1102. protocol:'http',
  1103. url:data.http,
  1104. })
  1105. }
  1106. delete data.http;
  1107. delete data.httpHeaders;
  1108. delete data.httpTimeInterval;
  1109. }
  1110. if(data.https){
  1111. data.https.forEach((item,i)=>{
  1112. data.networks.push({
  1113. name: `https${i+1}`,
  1114. type:'subscribe',
  1115. body:item.body,
  1116. headers:item.httpHeaders,
  1117. interval:item.httpTimeInterval,
  1118. method:item.method,
  1119. protocol:'http',
  1120. url:item.http,
  1121. // id:item.id
  1122. })
  1123. });
  1124. delete data.https;
  1125. }
  1126. if(data.mqtt){
  1127. data.networks.push({
  1128. "name": "mqtt",
  1129. "type": "subscribe",
  1130. "protocol": "mqtt",
  1131. "url": data.mqtt,
  1132. "options": data.mqttOptions,
  1133. "topics": data.mqttTopics,
  1134. });
  1135. delete data.mqtt;
  1136. delete data.mqttOptions;
  1137. delete data.mqttTopics;
  1138. }
  1139. if(data.websocket){
  1140. data.networks.push({
  1141. "name": "websocket",
  1142. "type": "subscribe",
  1143. "protocol": "websocket",
  1144. "protocols": data.websocketProtocols,
  1145. "url": data.websocket,
  1146. });
  1147. delete data.websocket;
  1148. delete data.websocketProtocols;
  1149. }
  1150. }