utils.ts 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. import { Pen, Meta2dData } from '@meta2d/core';
  2. import { MessagePlugin, NotifyPlugin, Button } from 'tdesign-vue-next';
  3. import { h, ref } from 'vue';
  4. import { upCdn } from './api';
  5. import axios from 'axios';
  6. import i18n from '../i18n';
  7. const $t = i18n.global.t;
  8. export const noLoginTip = $t('请先登录,否则无法保存!');
  9. export const localStorageName = 'le5leV';
  10. export interface Meta2dBackData extends Meta2dData {
  11. id?: string;
  12. name?: string;
  13. userId?: string;
  14. image?: string;
  15. component?: boolean;
  16. componentDatas?: Pen[];
  17. version?: string;
  18. folder?: string;
  19. shared?: boolean; // 是否分享
  20. class?: string; // 分类,架构拓扑图那些
  21. // 组合为状态,组件保存使用,无需存储到后端
  22. showChild?: number;
  23. _id?: string;
  24. owner?: {
  25. id?: string;
  26. };
  27. folderId?: string;
  28. editor?: {
  29. id?: string;
  30. username?: string;
  31. };
  32. username?: string;
  33. editorId?: string;
  34. editorName?: string;
  35. teams?: { id?: string; name?: string }[];
  36. tags?: string[]; //标签数组
  37. filename?: string; //后端存储的文件名
  38. ownerId?:string;
  39. ownerName?:string;
  40. case?:string;
  41. isSystem?:boolean;
  42. isTemplate?:boolean;
  43. }
  44. const notification = ref<any>(null);
  45. export function showNotification(title: string): Promise<boolean> {
  46. return new Promise<boolean>((resolve) => {
  47. const btnClick = () => {
  48. NotifyPlugin.close(notification.value);
  49. notification.value = null;
  50. resolve(true);
  51. };
  52. if (!notification.value) {
  53. notification.value = NotifyPlugin.info({
  54. title: $t('提示'),
  55. content: title,
  56. closeBtn: true,
  57. onCloseBtnClick: () => {
  58. //关闭按钮
  59. notification.value = null;
  60. resolve(false);
  61. },
  62. // duration: 1000000,
  63. // @ts-ignore
  64. footer: h(
  65. Button,
  66. {
  67. theme: 'primary',
  68. size: 'small',
  69. style: {
  70. 'margin-top': '16px',
  71. 'margin-left': '256px',
  72. },
  73. onClick: btnClick,
  74. },
  75. $t('确定')
  76. ),
  77. });
  78. }
  79. });
  80. }
  81. export function startViewTransition(callback) {
  82. if (!document.startViewTransition) {
  83. callback();
  84. return;
  85. }
  86. document.startViewTransition(callback);
  87. }
  88. export async function dealwithFormatbeforeOpen(data: Meta2dBackData) {
  89. if (!data) {
  90. return;
  91. }
  92. if ((!data.https || data.https?.length == 0) && data.http) {
  93. data.https = [
  94. {
  95. http: data.http,
  96. httpTimeInterval: data.httpTimeInterval,
  97. httpHeaders: data.httpHeaders,
  98. },
  99. ];
  100. delete data.http;
  101. delete data.httpHeaders;
  102. delete data.httpTimeInterval;
  103. }
  104. //新版渐进色
  105. data.pens &&
  106. data.pens.forEach((pen) => {
  107. if (pen.lineGradientFromColor && pen.lineGradientToColor) {
  108. pen.lineGradientColors = `linear-gradient(${
  109. pen.lineGradientAngle ? Number(pen.lineGradientAngle) + 90 : 0
  110. }deg,${pen.lineGradientFromColor} 0%,${pen.lineGradientToColor} 100%)`;
  111. }
  112. if (pen.gradientFromColor && pen.gradientToColor) {
  113. pen.gradientColors = `linear-gradient(${
  114. pen.gradientAngle ? Number(pen.gradientAngle) + 90 : 0
  115. }deg,${pen.gradientFromColor} 0%,${pen.gradientToColor} 100%)`;
  116. }
  117. if (
  118. pen.image &&
  119. pen.image.startsWith('/') &&
  120. !pen.image.startsWith('/v/') &&
  121. !pen.image.startsWith('/png/')
  122. ) {
  123. pen.image = upCdn + pen.image;
  124. }
  125. });
  126. }
  127. export function gotoAccount() {
  128. MessagePlugin.info({
  129. content: $t('请开通vip,即将跳转到开通页面...'),
  130. duration: 3,
  131. });
  132. setTimeout(() => {
  133. if (import.meta.env.BASE_URL[0] === '/') {
  134. window.open('/account?unVip=true');
  135. } else {
  136. let arr = location.host.split('.');
  137. arr[0] = 'http://account';
  138. let accountUrl = arr.join('.');
  139. window.open(`${accountUrl}?unVip=true`);
  140. }
  141. }, 3000);
  142. }
  143. export async function isGif(url: string): Promise<boolean> {
  144. if (url.endsWith('.svg')) {
  145. //请求svg图片
  146. // if (url.startsWith('https://drive.le5lecdn.com/')) {
  147. let res: any = await axios.get(url);
  148. if ((res as string).indexOf('@keyframes') !== -1) {
  149. //有动画的svg
  150. return true;
  151. } else {
  152. return false;
  153. }
  154. // } else {
  155. // return false;
  156. // }
  157. } else {
  158. let arr = url.split('?');
  159. return arr[0].endsWith('.gif');
  160. }
  161. }
  162. /**
  163. * 正常的 assign 操作,是存在弊端的,
  164. * 若源对象存在该属性,但目标对象不存在该属性(不存在并非 undefined),则会导致无法覆盖
  165. * 该方法会把源对象的属性全部清空,然后再把目标对象的属性覆盖到源对象上
  166. * source 可能是个监听的对象,所有最后一步再更改它的属性值
  167. * @param source 原对象
  168. * @param target 目标对象
  169. */
  170. export function strictAssign(
  171. source: Record<string, any>,
  172. target: Record<string, any>
  173. ) {
  174. // source 的全部属性都是 undefined 的对象,而非没有这个属性
  175. const undefinedSource: Record<string, any> = {};
  176. Object.keys(source).forEach((key) => {
  177. undefinedSource[key] = undefined;
  178. });
  179. Object.assign(undefinedSource, target);
  180. Object.assign(source, undefinedSource);
  181. }
  182. export function checkData(data: Meta2dData) {
  183. const pens: Pen[] = data.pens || [];
  184. for (let i = 0; i < pens.length; i++) {
  185. const pen: any = pens[i];
  186. pen.props?.custom?.forEach((prop) => {
  187. if (prop.key?.includes('.')) {
  188. delete pen[prop.key];
  189. }
  190. });
  191. pen.realTimes?.forEach((realTime) => {
  192. if (realTime.key?.includes('.')) {
  193. delete pen[realTime.key];
  194. }
  195. });
  196. pen.events?.forEach((event: any) => {
  197. delete event.setProps;
  198. });
  199. //处理画笔是脏数据的情况
  200. if (
  201. !(
  202. pen.x > -Infinity &&
  203. pen.x < Infinity &&
  204. pen.y > -Infinity &&
  205. pen.y < Infinity &&
  206. pen.width > -Infinity &&
  207. pen.width < Infinity &&
  208. pen.height > -Infinity &&
  209. pen.height < Infinity
  210. )
  211. ) {
  212. pens.splice(i, 1);
  213. --i;
  214. } else if (
  215. pen.x == null ||
  216. pen.y == null ||
  217. pen.width == null ||
  218. pen.height == null
  219. ) {
  220. pens.splice(i, 1);
  221. --i;
  222. }
  223. }
  224. if (Array.isArray(data.mqttOptions)) {
  225. // mqttOptions 是数组则认为是脏数据,删掉
  226. data.mqttOptions = {};
  227. }
  228. }
  229. export const transformData = (obj,operation) => {
  230. let newObj:any = null;
  231. if(operation == 'toMetaNetwork') {
  232. newObj = {
  233. name:obj.name,
  234. type:obj.type,
  235. ...obj.data,
  236. id:obj.id,
  237. }
  238. } else {
  239. newObj = {
  240. name:obj.name,
  241. type:obj.type,
  242. data:{}
  243. }
  244. if(obj.id) {
  245. newObj.id = obj.id;
  246. }
  247. if(obj.type == 'dataset') {
  248. newObj.data = {
  249. devices:obj.devices,
  250. mode: obj.mode,
  251. url: obj.url
  252. }
  253. } else {
  254. if( obj.protocol == 'http') {
  255. newObj.data = {
  256. body: obj.body,
  257. headers: obj.headers,
  258. httpTimeInterval: obj.httpTimeInterval,
  259. method: obj.method,
  260. url: obj.url,
  261. protocol: obj.protocol
  262. }
  263. } else {
  264. newObj.data = {
  265. options:obj.options,
  266. protocol: obj.protocol,
  267. topics: obj.topics,
  268. url: obj.url
  269. }
  270. }
  271. }
  272. // return {
  273. // data:{
  274. // body: obj.body,
  275. // headers: obj.headers,
  276. // httpTimeInterval: obj.httpTimeInterval,
  277. // method: obj.method,
  278. // options:obj.options,
  279. // protocol: obj.protocol,
  280. // topics: obj.topics,
  281. // url: obj.url
  282. // }
  283. // }
  284. }
  285. return newObj;
  286. }
  287. export const getImgUrl = (url: string) => {
  288. if (url.startsWith('http')) {
  289. return url
  290. }
  291. return import.meta.env.VITE_IMG_API + '/' + url
  292. }
  293. // monaco代码提示类型管理
  294. export class MonacoTypeManager {
  295. private monaco: any;
  296. constructor(monacoInstance) {
  297. this.monaco = monacoInstance;
  298. }
  299. loadTypeFile(path: string,callback) {
  300. fetch(path)
  301. .then(response => response.text())
  302. .then(data => {
  303. this.registerTypeFile(data)
  304. callback()
  305. })
  306. .catch(error => {
  307. console.error('Error fetching type file:', error);
  308. });
  309. }
  310. /**
  311. * 加载并注册 .d.ts 文件内容
  312. * @param {string} typeContent - 类型声明字符串内容
  313. * @param {string} filePath - 唯一虚拟文件路径
  314. */
  315. registerTypeFile(typeContent) {
  316. this.monaco.languages.typescript.javascriptDefaults.addExtraLib(
  317. typeContent,
  318. );
  319. }
  320. clearTypeFiles() {
  321. this.monaco.languages.typescript.javascriptDefaults.setExtraLibs([]);
  322. }
  323. /**
  324. * 为指定变量生成类型声明并注入
  325. * @param {string} varName - 变量名,例如 ctx
  326. * @param {string} typeName - 类型名,例如 CanvasRenderingContext2D
  327. */
  328. injectVariableType(varName, typeName) {
  329. // const filePath = `file:///injected-types/${varName}.d.ts`;
  330. const typeContent = `
  331. /** @type {${typeName}} */
  332. var ${varName};
  333. `;
  334. this.registerTypeFile(typeContent);
  335. }
  336. /**
  337. * 批量注册变量类型
  338. * @param {Array<{ varName: string, typeName: string }>} typeList
  339. */
  340. injectMultipleVariables(typeList) {
  341. typeList.forEach(({ varName, typeName }) => {
  342. this.injectVariableType(varName, typeName);
  343. });
  344. }
  345. }