import { reactive, ref } from 'vue'; import { MessagePlugin } from 'tdesign-vue-next'; import localforage from 'localforage'; import dayjs from 'dayjs'; import axios from 'axios'; import router from '@/router/index'; import { useUser } from '@/services/user'; import { showNotification, Meta2dBackData, checkData } from '@/services/utils'; import { noLoginTip, localStorageName } from '@/services/utils'; import { upload, dataURLtoBlob } from '@/services/file'; import { delImage, addCollection, updateCollection } from '@/services/api'; import { baseVer } from '@/services/upgrade'; import { debounce } from './debouce'; const assets = reactive({ home: 'https://le5le.com', account: 'https://account.le5le.com', '3d': 'https://3d.le5le.com', '2d': 'https://2d.le5le.com', helps: [ { name: '产品介绍', url: 'https://doc.le5le.com/document/118756411', }, { name: '快速上手', url: 'https://doc.le5le.com/document/119363000', }, { name: '使用手册', url: 'https://doc.le5le.com/document/118764244', }, { name: '快捷键', url: 'https://doc.le5le.com/document/119620214', divider: true, }, { name: '企业服务与支持', url: 'https://doc.le5le.com/document/119296274', divider: true, }, { name: '关于我们', url: 'https://le5le.com/about.html', }, ], }); export const useAssets = () => { const getAssets = async () => { // 官网或安装包版本 if ( import.meta.env.VITE_TRIAL == undefined || import.meta.env.VITE_TRIAL == 1 ) { return; } // 企业版 const ret = await axios.get('/api/assets'); if (ret) { Object.assign(assets, ret); } }; return { assets, getAssets, }; }; const dot = ref(false); export const useDot = () => { const getDot = async () => { return dot; }; const setDot = async (value = true) => { dot.value = value; if (value) { tree.patch = true; debounce(autoSave, 3000); } }; return { dot, getDot, setDot, }; }; const { user } = useUser(); export enum SaveType { Save, SaveAs, } export const save = async ( type: SaveType = SaveType.Save, component?: boolean, notice?: boolean ) => { meta2d.stopAnimate(); const data: Meta2dBackData = meta2d.data(); if (!(user && user.id)) { MessagePlugin.warning(noLoginTip); localforage.setItem(localStorageName, JSON.stringify(data)); return; } checkData(data); if (!data._id && router.currentRoute.value.query.id) { data._id = router.currentRoute.value.query.id as string; } if ( (globalThis as any).beforeSaveMeta2d && !(await (globalThis as any).beforeSaveMeta2d(data)) ) { return; } if (type === SaveType.SaveAs) { //另存为去掉teams信息 delete data.teams; } //如果不是自己创建的团队图纸,就不去修改缩略图(没有权限去删除缩略图) if (!(data.teams && data.owner?.id !== user.id)) { let blob: Blob; try { blob = dataURLtoBlob(meta2d.toPng(0) + ''); } catch (e) { MessagePlugin.error( '无法下载,宽度不合法,画布可能没有画笔/画布大小超出浏览器最大限制' ); return; } if (data._id && type === SaveType.Save) { if (data.image && !(await delImage(data.image))) { return; } } const file = await upload(blob, true); if (!file) { return; } // 缩略图 data.image = file.url; (meta2d.store.data as Meta2dBackData).image = data.image; } if (data.component || component) { data.component = true; // pens 存储原数据用于二次编辑 ; componentDatas 组合后的数据,用于复用 data.componentDatas = meta2d.toComponent( undefined, (meta2d.store.data as Meta2dBackData).showChild, false //自定义组合节点生成默认锚点 ); } else { data.component = false; // 必要值 } let collection = data.component ? 'le5leV-components' : 'le5leV'; let ret: any; if (!data.name) { // 文件名称 data.name = `meta2d.${new Date().toLocaleString()}`; (meta2d.store.data as Meta2dBackData).name = data.name; } !data.version && (data.version = baseVer); if (!data.folder) { data.folder = ''; } if (type === SaveType.SaveAs) { // 另存为一定走 新增 ,由于后端 未控制 userId 等属性,清空一下 for (const k of delAttrs) { delete (data as any)[k]; } ret = await addCollection(collection, data); } else { if (data._id && data.teams && data.owner?.id !== user.id) { // 团队图纸 不允许修改文件夹信息 delete data.folder; ret = await updateCollection(collection, data); } else if (data._id) { ret = await updateCollection(collection, data); } else { ret = await addCollection(collection, data); // 新增 } } if (ret.error) { return; } // 保存图纸之后的钩子函数 globalThis.afterSaveMeta2d && (await globalThis.afterSaveMeta2d(ret)); if ( !data._id || data.owner?.id !== user.id || router.currentRoute.value.query.version || type === SaveType.SaveAs // 另存为肯定走新增,也会产生新的 id ) { data._id = ret._id; (meta2d.store.data as Meta2dBackData)._id = data._id; router.replace({ path: '/', query: { id: data._id, r: Date.now() + '', component: data.component + '', }, }); } notice && MessagePlugin.success('保存成功!'); dot.value = false; localforage.removeItem(localStorageName); return true; }; const pen = ref(false); export const drawPen = () => { meta2d.inactive(); try { if (!meta2d.canvas.drawingLineName) { // 开了钢笔,需要关掉铅笔 meta2d.canvas.pencil && drawingPencil(); meta2d.drawLine(meta2d.store.options.drawingLineName); } else { meta2d.finishDrawLine(); meta2d.drawLine(); } //钢笔 pen.value = !!meta2d.canvas.drawingLineName; } catch (e: any) { MessagePlugin.warning(e.message); } }; const pencil = ref(false); const drawingPencil = () => { try { if (!meta2d.canvas.pencil) { // 开了铅笔需要关掉钢笔 meta2d.canvas.drawingLineName && drawPen(); meta2d.drawingPencil(); } else { meta2d.stopPencil(); } pencil.value = meta2d.canvas.pencil || false; } catch (e: any) { MessagePlugin.warning(e.message); } }; export const magnifier = ref(false); export const showMagnifier = () => { if (!meta2d.canvas.magnifierCanvas.magnifier) { meta2d.showMagnifier(); } else { meta2d.hideMagnifier(); } magnifier.value = meta2d.canvas.magnifierCanvas.magnifier; }; export const map = ref(false); export const showMap = () => { if (!meta2d.map?.isShow) { meta2d.showMap(); } else { meta2d.hideMap(); } map.value = meta2d.map?.isShow; }; export const title = '系统可能不会保存您所做的更改,是否继续?'; export const unLogin = '未登录,系统可能不会保存您的文件,是否继续?'; export const unsave = '当前文件未保存,是否继续?(开通vip可享受自动保存服务)'; export function autoSave(force = false) { if (!dot.value && (!force || router.currentRoute.value.query.id)) { return; } const data: any = meta2d.data(); if ( user && user.id && user.isVip && data._id && !data.component && data.owner && data.owner.id === user.id ) { save(SaveType.Save); } else { data.updateAt = dayjs().format(); localforage.setItem(localStorageName, JSON.stringify(data)); } } export const notificFn = async (fn: Function, params: any) => { if (!(user && user.id)) { if (await showNotification(unLogin)) { fn(params); } } else { if (dot.value) { if (user.isVip) { if (await save(SaveType.Save)) { fn(params); } } else { if (await showNotification(unsave)) { fn(params); } } } else { fn(params); } } }; export const onScaleWindow = () => { // meta2d.fitView(); meta2d.fitSizeView(true, 32); }; export const onScaleView = () => { meta2d.scale(1); // meta2d.centerView(); const { x, y, origin, center } = meta2d.store.data; meta2d.translate(-x - origin.x, -y - origin.y); meta2d.translate(meta2d.store.options.x || 0, meta2d.store.options.y || 0); }; export const blank = async (save = true) => { meta2d.canvas.drawingLineName && drawPen(); meta2d.canvas.pencil && drawingPencil(); meta2d.canvas.magnifierCanvas.magnifier && showMagnifier(); meta2d.map?.isShow && showMap(); save && autoSave(true); dot.value = false; meta2d.open({ pens: [] } as any); }; export const newFile = () => { blank(); router.push({ path: '/', query: { r: Date.now() + '', }, }); setTimeout(() => { autoSave(true); }, 300); }; export const inTreePanel = reactive({ value: false, timer: undefined, }); const tree = reactive({ list: [], patch: true, }); export const getPenTree = () => { if (tree.patch) { tree.patch = false; const list = []; for (const item of meta2d.store.data.pens) { if (item.parentId) { continue; } const elem = calcElem(item); elem && list.push(elem); } tree.list = list; } return tree.list; }; export const getPenAnimations = (idOrTag?: string) => { const animations = []; let pens: any[] = meta2d.store.active || []; if (idOrTag) { pens = meta2d.find(idOrTag) || []; } for (const pen of pens) { if (pen.animations) { for (const a of pen.animations) { animations.push(a.name); } } } return Array.from(new Set(animations)); }; const calcElem = (node: any) => { if (!node) { return; } const elem: any = { label: (node as any).description || node.name, value: node.id, locked: node.locked, visible: node.visible, }; if (!node.children) { return elem; } elem.children = []; for (const id of node.children) { const child = calcElem(meta2d.store.pens[id]); child && elem.children.push(child); } return elem; }; export const setChildrenVisible = (node: any, v: boolean) => { const children = node.getChildren(); if (children && children.length > 0) { for (const child of children) { child.data.visible = v; setChildrenVisible(child, v); } } }; export const fonts = [ '新宋体', '微软雅黑', '黑体', '楷体', '-apple-system', 'BlinkMacSystemFont', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', 'fangsong', 'Source Han Sans CN', 'sans-serif', 'serif', 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', ]; export const delAttrs = [ 'userId', 'shared', 'team', 'owner', 'username', 'editor', 'editorId', 'editorName', 'createdAt', 'tags', 'image', 'id', '_id', 'view', 'updatedAt', 'star', 'recommend', ]; export const typeOptions = [ { label: '字符串', value: 'string', }, { label: '数字', value: 'number', }, { label: '布尔', value: 'bool', }, { label: '对象', value: 'object', }, { label: '数组', value: 'array', }, ];