import { Pen, Meta2dData } from '@meta2d/core'; import { MessagePlugin, NotifyPlugin, Button } from 'tdesign-vue-next'; import { h, ref } from 'vue'; import { upCdn } from './api'; import axios from 'axios'; import i18n from '../i18n'; const $t = i18n.global.t; export const noLoginTip = $t('请先登录,否则无法保存!'); export const localStorageName = 'le5leV'; export interface Meta2dBackData extends Meta2dData { id?: string; name?: string; userId?: string; image?: string; component?: boolean; componentDatas?: Pen[]; version?: string; folder?: string; shared?: boolean; // 是否分享 class?: string; // 分类,架构拓扑图那些 // 组合为状态,组件保存使用,无需存储到后端 showChild?: number; _id?: string; owner?: { id?: string; }; folderId?: string; editor?: { id?: string; username?: string; }; username?: string; editorId?: string; editorName?: string; teams?: { id?: string; name?: string }[]; tags?: string[]; //标签数组 filename?: string; //后端存储的文件名 ownerId?:string; ownerName?:string; case?:string; isSystem?:boolean; isTemplate?:boolean; } const notification = ref(null); export function showNotification(title: string): Promise { return new Promise((resolve) => { const btnClick = () => { NotifyPlugin.close(notification.value); notification.value = null; resolve(true); }; if (!notification.value) { notification.value = NotifyPlugin.info({ title: $t('提示'), content: title, closeBtn: true, onCloseBtnClick: () => { //关闭按钮 notification.value = null; resolve(false); }, // duration: 1000000, // @ts-ignore footer: h( Button, { theme: 'primary', size: 'small', style: { 'margin-top': '16px', 'margin-left': '256px', }, onClick: btnClick, }, $t('确定') ), }); } }); } export function startViewTransition(callback) { if (!document.startViewTransition) { callback(); return; } document.startViewTransition(callback); } export async function dealwithFormatbeforeOpen(data: Meta2dBackData) { if (!data) { return; } if ((!data.https || data.https?.length == 0) && data.http) { data.https = [ { http: data.http, httpTimeInterval: data.httpTimeInterval, httpHeaders: data.httpHeaders, }, ]; delete data.http; delete data.httpHeaders; delete data.httpTimeInterval; } //新版渐进色 data.pens && data.pens.forEach((pen) => { if (pen.lineGradientFromColor && pen.lineGradientToColor) { pen.lineGradientColors = `linear-gradient(${ pen.lineGradientAngle ? Number(pen.lineGradientAngle) + 90 : 0 }deg,${pen.lineGradientFromColor} 0%,${pen.lineGradientToColor} 100%)`; } if (pen.gradientFromColor && pen.gradientToColor) { pen.gradientColors = `linear-gradient(${ pen.gradientAngle ? Number(pen.gradientAngle) + 90 : 0 }deg,${pen.gradientFromColor} 0%,${pen.gradientToColor} 100%)`; } if ( pen.image && pen.image.startsWith('/') && !pen.image.startsWith('/v/') && !pen.image.startsWith('/png/') ) { pen.image = upCdn + pen.image; } }); } export function gotoAccount() { MessagePlugin.info({ content: $t('请开通vip,即将跳转到开通页面...'), duration: 3, }); setTimeout(() => { if (import.meta.env.BASE_URL[0] === '/') { window.open('/account?unVip=true'); } else { let arr = location.host.split('.'); arr[0] = 'http://account'; let accountUrl = arr.join('.'); window.open(`${accountUrl}?unVip=true`); } }, 3000); } export async function isGif(url: string): Promise { if (url.endsWith('.svg')) { //请求svg图片 // if (url.startsWith('https://drive.le5lecdn.com/')) { let res: any = await axios.get(url); if ((res as string).indexOf('@keyframes') !== -1) { //有动画的svg return true; } else { return false; } // } else { // return false; // } } else { let arr = url.split('?'); return arr[0].endsWith('.gif'); } } /** * 正常的 assign 操作,是存在弊端的, * 若源对象存在该属性,但目标对象不存在该属性(不存在并非 undefined),则会导致无法覆盖 * 该方法会把源对象的属性全部清空,然后再把目标对象的属性覆盖到源对象上 * source 可能是个监听的对象,所有最后一步再更改它的属性值 * @param source 原对象 * @param target 目标对象 */ export function strictAssign( source: Record, target: Record ) { // source 的全部属性都是 undefined 的对象,而非没有这个属性 const undefinedSource: Record = {}; Object.keys(source).forEach((key) => { undefinedSource[key] = undefined; }); Object.assign(undefinedSource, target); Object.assign(source, undefinedSource); } export function checkData(data: Meta2dData) { const pens: Pen[] = data.pens || []; for (let i = 0; i < pens.length; i++) { const pen: any = pens[i]; pen.props?.custom?.forEach((prop) => { if (prop.key?.includes('.')) { delete pen[prop.key]; } }); pen.realTimes?.forEach((realTime) => { if (realTime.key?.includes('.')) { delete pen[realTime.key]; } }); pen.events?.forEach((event: any) => { delete event.setProps; }); //处理画笔是脏数据的情况 if ( !( pen.x > -Infinity && pen.x < Infinity && pen.y > -Infinity && pen.y < Infinity && pen.width > -Infinity && pen.width < Infinity && pen.height > -Infinity && pen.height < Infinity ) ) { pens.splice(i, 1); --i; } else if ( pen.x == null || pen.y == null || pen.width == null || pen.height == null ) { pens.splice(i, 1); --i; } } if (Array.isArray(data.mqttOptions)) { // mqttOptions 是数组则认为是脏数据,删掉 data.mqttOptions = {}; } } export const transformData = (obj,operation) => { let newObj:any = null; if(operation == 'toMetaNetwork') { newObj = { name:obj.name, type:obj.type, ...obj.data, id:obj.id, } } else { newObj = { name:obj.name, type:obj.type, data:{} } if(obj.id) { newObj.id = obj.id; } if(obj.type == 'dataset') { newObj.data = { devices:obj.devices, mode: obj.mode, url: obj.url } } else { if( obj.protocol == 'http') { newObj.data = { body: obj.body, headers: obj.headers, httpTimeInterval: obj.httpTimeInterval, method: obj.method, url: obj.url, protocol: obj.protocol } } else { newObj.data = { options:obj.options, protocol: obj.protocol, topics: obj.topics, url: obj.url } } } // return { // data:{ // body: obj.body, // headers: obj.headers, // httpTimeInterval: obj.httpTimeInterval, // method: obj.method, // options:obj.options, // protocol: obj.protocol, // topics: obj.topics, // url: obj.url // } // } } return newObj; } export const getImgUrl = (url: string) => { if (url.startsWith('http')) { return url } return import.meta.env.VITE_IMG_API + '/' + url } // monaco代码提示类型管理 export class MonacoTypeManager { private monaco: any; constructor(monacoInstance) { this.monaco = monacoInstance; } loadTypeFile(path: string,callback) { fetch(path) .then(response => response.text()) .then(data => { this.registerTypeFile(data) callback() }) .catch(error => { console.error('Error fetching type file:', error); }); } /** * 加载并注册 .d.ts 文件内容 * @param {string} typeContent - 类型声明字符串内容 * @param {string} filePath - 唯一虚拟文件路径 */ registerTypeFile(typeContent) { this.monaco.languages.typescript.javascriptDefaults.addExtraLib( typeContent, ); } clearTypeFiles() { this.monaco.languages.typescript.javascriptDefaults.setExtraLibs([]); } /** * 为指定变量生成类型声明并注入 * @param {string} varName - 变量名,例如 ctx * @param {string} typeName - 类型名,例如 CanvasRenderingContext2D */ injectVariableType(varName, typeName) { // const filePath = `file:///injected-types/${varName}.d.ts`; const typeContent = ` /** @type {${typeName}} */ var ${varName}; `; this.registerTypeFile(typeContent); } /** * 批量注册变量类型 * @param {Array<{ varName: string, typeName: string }>} typeList */ injectMultipleVariables(typeList) { typeList.forEach(({ varName, typeName }) => { this.injectVariableType(varName, typeName); }); } }