import { cdn, upCdn } from '@/services/api'; import { checkData } from '@/services/utils'; import axios from 'axios'; // import { MessagePlugin } from 'tdesign-vue-next'; import { useUser } from '@/services/user'; import JSZip from 'jszip'; import { Pen, getGlobalColor, isShowChild } from '@meta2d/core'; export const img_cdn = 'https://assets.le5lecdn.com'; export const img_upCdn = 'https://drive.le5lecdn.com'; const { user, signout } = useUser(); const components = [ 'inputDom', 'selectDom', 'menuDom', 'headMenuDom', 'sliderVerifyDom', 'dropdownDom', 'flvPlayerDom', 'countdown', 'swiperDom', 'threeDSence', 'rtspPlayerDom', 'timeline', 'swiperline', 'tab', 'radio', 'checkbox', 'calendar', 'indicator', 'progress', 'pagination', 'steps', 'notification', 'list', 'tree', 'rockerSwitch', 'roundSwitch', 'breadcrumb', 'transferSwitch', 'pie3D', 'hikVideo', 'thermometer', 'watermeter', 'indicatorLight', 'toggleSwitch', 'knifeSwitch', 'compass', 'thermometer1', 'airSwitch', 'waterTank', ]; export const getDownloadList = (meta2dData: any, path: string = 'v') => { const lists = new Set(); //TODO 加一个type区分是数据/还是接口 //背景图片 // const meta2dData = meta2d.data(); let img = meta2dData.bkImage; if (img) { if ( img.startsWith('/') || img.startsWith(img_cdn) || img.startsWith(img_upCdn) ) { let _img = img.replace(img_cdn, '').replace(img_upCdn, ''); if (_img.startsWith('/v/')) { _img = _img.slice(2); } lists.add({ url: img, path: `/view/projects/${path}` + _img, }); } } //图片图元(image strokeImage backgroundImage) const imageKeys = ['image', 'strokeImage', 'backgroundImage']; const images: string[] = []; for (const pen of meta2dData.pens) { for (const i of imageKeys) { const image = pen[i]; if (image) { if ( image.startsWith('/') || image.startsWith(img_cdn) || image.startsWith(img_upCdn) ) { // 只考虑相对路径下的 image ,绝对路径图片无需下载 let _img = image.replace(img_cdn, '').replace(img_upCdn, ''); if (_img.startsWith('/v/')) { _img = _img.slice(2); } if (!images.includes(image)) { // let _img = image.replace(cdn, '').replace(upCdn, ''); lists.add({ url: image, path: `/view/projects/${path}` + _img, }); } pen[i] = `/view/projects/${path}` + _img; } } } } //其他文件 const files = [ '/view/assets/index.js', '/view/assets/index.css', '/view/css/index.css', '/view/js/marked.min.js', '/view/js/mycharts.js', '/view/js/echarts.min.js', '/view/js/lcjs.iife.js', '/view/js/highcharts.js', '/view/js/highcharts-more.js', // '/view/js/2d-components.js', '/view/js/r.js', '/view/index.html', '/view/favicon.ico', '/view/view.conf', '/view/离线部署包使用说明.pdf', ]; files.forEach((file) => { lists.add({ url: (cdn ? cdn : '') + file, path: file, }); }); //数据 // const data: any = meta2d.data(); if (meta2dData._id) delete meta2dData._id; if (meta2dData.id) delete meta2dData.id; if ((meta2dData as any).image) delete (meta2dData as any).image; checkData(meta2dData); lists.add({ data: JSON.stringify(meta2dData) .replaceAll(img_cdn, '') .replaceAll(img_upCdn, ''), path: `/view/projects/${path}/data`, }); return lists; }; export const getPayList = (meta2dData: any) => { const pngs = new Set(); const jsPens = new Set(); const iotPens = new Set(); const svgPens = new Set(); for (const pen of meta2dData.pens) { if (pen.image) { if ( pen.image.startsWith(`${img_cdn}/png/`) || pen.image.startsWith('/png/') ) { pngs.add(pen.image.replace(img_cdn, '')); } } else if (pen.subClassName && pen.fullname) { jsPens.add(pen.name); } else if (components.includes(pen.name)) { iotPens.add(pen.name); } else if (pen.svgUrl) { if ( pen.svgUrl.startsWith(`${img_cdn}/svg/`) || pen.svgUrl.startsWith('/svg/') ) { svgPens.add(pen.svgUrl.replace(img_cdn, '')); } } } if (![...svgPens].length) { //判断是否为老数据 if (meta2dData.paths) { let keys = []; for (let key of Object.keys(meta2dData.paths)) { let path = meta2dData.paths[key]; if ( path.indexOf('-1.18Zm4-1') !== -1 || path.indexOf('-1.19Zm4-1') !== -1 || path.indexOf('2.85ZM') !== -1 || path.indexOf('-1-2.39.3') !== -1 ) { keys.push(keys); } } let flag = meta2dData.pens.some( (pen) => pen.name === 'svgPath' && keys.includes(pen.pathId) ); if (flag) { svgPens.add('*'); //需要购买所有 } } } return { pngs: [...pngs], jsPens: [...jsPens], iotPens: [...iotPens], svgPens: [...svgPens], }; }; //获取已购买 export const getComponentPurchased = async (list: any) => { let _list = []; list.pngs.forEach((item) => { _list.push({ type: '图片图元', name: item, }); }); list.jsPens.forEach((item) => { _list.push({ type: 'JS线性图元', name: item, }); }); list.iotPens.forEach((item) => { _list.push({ type: '控件', name: item, }); }); if ([...list.svgPens].includes('*')) { _list.push({ type: 'SVG线性图元', }); } else { list.svgPens.forEach((item) => { _list.push({ type: 'SVG线性图元', name: item, }); }); } if(!_list.length){ return []; } const res: any = await axios.post('/api/paid/2d/component', { list: _list, }); if (res.error) { return []; } // let purchasedPngs = res.list.filter((item) => list.pngs.has(item)); // let purchasedJs = res.list.filter((item) => list.jsPens.has(item)); // let purchasedIot = res.list.filter((item) => list.iotPens.has(item)); // let purchasedSvg = res.list.filter((item) => list.svgPens.has(item)); // let price = // ([...list.pngs].length - purchasedPngs.length) * 50 + // ([...list.jsPens].length - purchasedJs.length) * 10 + // ([...list.iotPens].length - purchasedIot.length) * 70+ // ([...list.svgPens].length - purchasedSvg.length) * 10; return res.list; }; export const get2dComponentJs = async (names: string[] = components) => { let list = []; names.forEach((item) => { list.push({ type: '控件', name: item, }); }); const res: any = await axios.post( '/api/2d-component.js', { // list, }, { responseType: 'blob', } ); return res; }; export const getTemPngs = async (names: string[]) => { const res: any = await axios.post('/api/file/presign', { names, }); return res; }; export const getGoods = async () => { const res: any = await axios.get('/api/goods/2d/component/types'); // console.log("res",res); return res; }; export const getDeployGoods = async () => { const ret:any = await axios.post('/api/goods/list',{ type:'私有部署' }); return ret.list; } export enum Frame { vue2, vue3, react, html, } let frameFlag = -1; export const _preFrameDownload = async (type: Frame) => { frameFlag = type; // MessagePlugin.info('正在下载打包中,可能需要几分钟,请耐心等待...'); zip3D( type === Frame.vue3 ? 'toVue3' : type === Frame.vue2 ? 'toVue2' : 'toReact' ); zip2D( type === Frame.vue3 ? 'downloadVue3' : type === Frame.vue2 ? 'downloadVue2' : 'downloadReact' ); const data: any = meta2d.data(); if (data._id) delete data._id; if (data.id) delete data.id; if (data.image) delete data.image; data.userId = user.id; checkData(data); const [{ default: JSZip }, { saveAs }] = await Promise.all([ import('jszip'), import('file-saver'), ]); const zip = new JSZip(); let _fileName = (data.name && data.name.replace(/\//g, '_').replace(/:/g, '_')) || 'le5le.meta2d'; //处理付费svg if (Object.keys(data.paths).length >= 3) { //简单判断有无svg图元 const res: any = await axios.post('/api/paid/2d/component', { type: 'SVG线性图元', }); if (res.list.length === 1 && !res.list[0].name) { //已经购买全部 for (let key of Object.keys(data.paths)) { let path = data.paths[key]; if ( path.indexOf('-1.18Zm4-1') !== -1 || path.indexOf('-1.19Zm4-1') !== -1 || path.indexOf('2.85ZM') !== -1 || path.indexOf('-1-2.39.3') !== -1 ) { data.paths[key] = ''; } } } else { //购买部分 let purchasedList = res.list.map((i) => i.name); data.pens.forEach((pen) => { if (pen.name === 'svgPath' && pen.svgUrl) { if (purchasedList.includes(pen.svgUrl.replace(img_cdn, ''))) { pen.pathId = null; } } }); } } const _zip: any = zip.folder(`${_fileName}`); _zip.file( `${ type === Frame.vue3 ? 'meta2d-vue3' : type === Frame.vue2 ? 'meta2d-vue2' : 'meta2d-react' }/public/json/data.json`, JSON.stringify(data).replaceAll(img_cdn, '').replaceAll(img_upCdn, '') ); await Promise.all([ zipJs(_zip), zipBkImg(_zip), zipImages(_zip, meta2d.store.data.pens), type === Frame.vue3 ? zipVue3Files(_zip) : type === Frame.vue2 ? zipVue2Files(_zip) : zipReactFiles(_zip), zipIotPens(_zip), ]); const blob = await zip.generateAsync({ type: 'blob' }); saveAs(blob, `${_fileName}.zip`); frameFlag = -1; }; async function zipIotPens(zip: JSZip) { //处理控件 const js = await get2dComponentJs(); zip.file( `${ frameFlag === Frame.vue3 ? 'meta2d-vue3' : frameFlag === Frame.vue2 ? 'meta2d-vue2' : 'meta2d-react' }/public/js/2d-components.js`, js, { createFolders: true } ); const res: Blob = await axios.get( cdn+'/view/js/r.js', { responseType: 'blob', }); zip.file( `${ frameFlag === Frame.vue3 ? 'meta2d-vue3' : frameFlag === Frame.vue2 ? 'meta2d-vue2' : 'meta2d-react' }/public/js/r.js`, res, { createFolders: true } ); } async function zipJs(zip: JSZip) { const files = ['/view/js/marked.min.js', '/view/js/lcjs.iife.js']; await Promise.all( files.map(async (filePath) => { const res: Blob = await axios.get(cdn+filePath, { responseType: 'blob', }); zip.file( `${ frameFlag === Frame.vue3 ? 'meta2d-vue3' : frameFlag === Frame.vue2 ? 'meta2d-vue2' : 'meta2d-react' }/public` + filePath.replace('/view', ''), res, { createFolders: true } ); }) ); } export async function zipBkImg(zip: JSZip) { let img = meta2d.store.data.bkImage; if (img) { if (img.startsWith('/') || img.startsWith(img_cdn) || img.startsWith(img_upCdn)) { const pngs = await getTemPngs([img.replace(img_cdn, '').replace(img_upCdn, '')]); await zipImage(zip, img, pngs[img.replace(img_cdn, '').replace(img_upCdn, '')]); } } } async function zipImage(zip: JSZip, image: string, temImage?: string) { const res: Blob = await axios.get(temImage || image, { responseType: 'blob', // params: { // isZip: true, // }, }); zip.file( (frameFlag === -1 ? '' : `${ frameFlag === Frame.vue3 ? 'meta2d-vue3' : frameFlag === Frame.vue2 ? 'meta2d-vue2' : 'meta2d-react' }/public`) + (cdn ? image.replace(cdn, '').replace(upCdn, '') : image), res, { createFolders: true, } ); } const zip3D = (name: string) => { const pen_3d = meta2d.store.data.pens.filter( (pen) => pen.name === 'iframe' && (pen.tags.includes('meta3d') || pen.iframe.indexOf('/3d') !== -1) ); if (pen_3d && pen_3d.length) { //存在3d场景 pen_3d.forEach((pen) => { //发送消息 // let params = queryURLParams(pen.iframe.split('?')[1]); ( pen.calculative.singleton.div.children[0] as HTMLIFrameElement ).contentWindow.postMessage( JSON.stringify({ type: 1, name, // id: params.id, }), '*' ); }); } }; const zip2D = (name: string) => { const pen_2d = meta2d.store.data.pens.filter( (pen) => pen.name === 'iframe' && (pen.iframe.indexOf('2d.le5le.com') !== -1 || pen.iframe.indexOf('/2d') !== -1 || pen.iframe.indexOf('data=2d') !== -1 || pen.iframe.indexOf('v.le5le.com') !== -1 || pen.iframe.indexOf('/view/v') !== -1 ||pen.iframe.indexOf('data=v') !== -1|| pen.iframe.indexOf('/preview') !== -1) ); if (pen_2d && pen_2d.length) { //存在3d场景 pen_2d.forEach((pen) => { //发送消息 // let params = queryURLParams(pen.iframe.split('?')[1]); ( pen.calculative.singleton.div.children[0] as HTMLIFrameElement ).contentWindow.postMessage( JSON.stringify({ name, type: 1, }), '*' ); }); } }; /** * 图片放到 zip 里 * @param pens 可以是非具有 calculative 的 pen */ export async function zipImages(zip: JSZip, pens: Pen[]) { if (!pens) { return; } // 不止 image 上有图片, strokeImage ,backgroundImage 也有图片 const imageKeys = [ { string: 'image', }, { string: 'strokeImage' }, { string: 'backgroundImage' }, ] as const; const images: string[] = []; for (const pen of pens) { for (const i of imageKeys) { const image = pen[i.string]; if (image) { // HTMLImageElement 无法精确控制图片格式 if ( image.startsWith('/') || image.startsWith(cdn) || image.startsWith(upCdn) ) { // 只考虑相对路径下的 image ,绝对路径图片无需下载 if (!images.includes(image)) { images.push(image); } } } } // 无需递归遍历子节点,现在所有的节点都在外层 } //付费pngs const pngs = await getTemPngs( images.map((i) => i.replace(img_cdn, '').replace(img_upCdn, '')) ); await Promise.all( images.map((image) => zipImage(zip, image, pngs[image.replace(img_cdn, '').replace(img_upCdn, '')]) ) ); } //新 async function zipVue3Files(zip: JSZip) { const files = [ '/view/meta2d-vue3/src/components/Meta2d.vue', '/view/meta2d-vue3/src/App.vue', '/view/meta2d-vue3/src/main.js', '/view/meta2d-vue3/src/style.css', '/view/meta2d-vue3/index.html', '/view/meta2d-vue3/package.json', '/view/meta2d-vue3/README.md', '/view/meta2d-vue3/vite.config.js', ] as const; // 文件同时加载 await Promise.all(files.map((filePath) => zipFile(zip, filePath))); } async function zipVue2Files(zip: JSZip) { const files = [ '/view/meta2d-vue2/src/components/Meta2d.vue', '/view/meta2d-vue2/src/App.vue', '/view/meta2d-vue2/src/main.js', // '/view/meta2d-vue2/src/style.css', '/view/meta2d-vue2/public/index.html', '/view/meta2d-vue2/package.json', '/view/meta2d-vue2/README.md', // '/view/meta2d-vue3/vite.config.js', ] as const; // 文件同时加载 await Promise.all(files.map((filePath) => zipFile(zip, filePath))); } async function zipReactFiles(zip: JSZip) { const files = [ '/view/meta2d-react/src/index.css', '/view/meta2d-react/src/index.js', '/view/meta2d-react/src/Meta2d.css', '/view/meta2d-react/src/Meta2d.jsx', '/view/meta2d-react/package.json', '/view/meta2d-react/README.md', '/view/meta2d-react/public/index.html', ] as const; // 文件同时加载 await Promise.all(files.map((filePath) => zipFile(zip, filePath))); } async function zipFile(zip: JSZip, filePath: string) { const res: Blob = await axios.get( (cdn ? cdn + '/v' : import.meta.env.BASE_URL.slice(0, -1)) + filePath, { responseType: 'blob', } ); zip.file(filePath.replace('/view', ''), res, { createFolders: true }); } export const getFrameDownloadList = (meta2dData: any, path: string = 'v',type:Frame) => { const lists = new Set(); let img = meta2dData.bkImage; if (img) { if ( img.startsWith('/') || img.startsWith(img_cdn) || img.startsWith(img_upCdn) ) { let _img = img.replace(img_cdn, '').replace(img_upCdn, ''); if (_img.startsWith('/v/')) { _img = _img.slice(2); } lists.add({ url: img, path: (`${ type === Frame.vue3 ? 'meta2d-vue3' : type === Frame.vue2 ? 'meta2d-vue2' : 'meta2d-react' }/public`) + _img, }); } } //图片图元(image strokeImage backgroundImage) const imageKeys = ['image', 'strokeImage', 'backgroundImage']; const images: string[] = []; for (const pen of meta2dData.pens) { for (const i of imageKeys) { const image = pen[i]; if (image) { if ( image.startsWith('/') || image.startsWith(img_cdn) || image.startsWith(img_upCdn) ) { // 只考虑相对路径下的 image ,绝对路径图片无需下载 let _img = image.replace(img_cdn, '').replace(img_upCdn, ''); if (_img.startsWith('/v/')) { _img = _img.slice(2); } let path = (`${ type === Frame.vue3 ? 'meta2d-vue3' : type === Frame.vue2 ? 'meta2d-vue2' : 'meta2d-react' }/public`) + _img if (!images.includes(image)) { // let _img = image.replace(cdn, '').replace(upCdn, ''); lists.add({ url: image, path, }); } pen[i] = path; } } } } let folderName =type===Frame.vue3?'meta2d-vue3':type===Frame.vue2?'meta2d-vue2': 'meta2d-react'; if(path === 'v'){ //iframe嵌入的页面无需再次下载 //其他文件 let files = []; switch (type) { case Frame.vue3: files =[ '/view/meta2d-vue3/src/router/index.ts', '/view/meta2d-vue3/src/views/2d/Meta2d.vue', // '/view/meta2d-vue3/src/views/3d/Meta2d.vue', '/view/meta2d-vue3/src/App.vue', '/view/meta2d-vue3/src/main.ts', '/view/meta2d-vue3/src/style.css', '/view/meta2d-vue3/index.html', '/view/meta2d-vue3/package.json', '/view/meta2d-vue3/README.md', '/view/meta2d-vue3/tsconfig.json', '/view/meta2d-vue3/tsconfig.node.json', '/view/meta2d-vue3/vite.config.js', ]; break; case Frame.vue2: files =[ '/view/meta2d-vue2/public/index.html', '/view/meta2d-vue2/src/router/index.js', '/view/meta2d-vue2/src/views/2d/Meta2d.vue', // '/view/meta2d-vue2/src/views/3d/Meta2d.vue', '/view/meta2d-vue2/src/App.vue', '/view/meta2d-vue2/src/main.js', '/view/meta2d-vue2/package.json', '/view/meta2d-vue2/README.md' ]; break; case Frame.react: files =[] break; default: break; } files.forEach((file) => { lists.add({ url: (cdn ? cdn : '') + file, path: file.replace('/view', ''), }); }); let jsFiles = [ '/view/js/marked.min.js', '/view/js/mycharts.js', '/view/js/echarts.min.js', '/view/js/lcjs.iife.js', '/view/js/highcharts.js', '/view/js/highcharts-more.js', // '/view/js/2d-components.js', '/view/js/r.js', ] jsFiles.forEach((file) => { lists.add({ url: (cdn ? cdn : '') + file, path:`/${folderName}/public${file.replace('/view', '')}`, }); }); } //图纸数据 if (meta2dData._id) delete meta2dData._id; if (meta2dData.id) delete meta2dData.id; if ((meta2dData as any).image) delete (meta2dData as any).image; lists.add({ data: JSON.stringify(meta2dData) .replaceAll(img_cdn, '') .replaceAll(img_upCdn, ''), path: `/${folderName}/public/json/${path}.json`, }); return lists; };