Graphics.vue 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859
  1. <template>
  2. <div class="graphics">
  3. <div class="group-asset">
  4. <t-radio-group
  5. v-model="activeAssets"
  6. @change="assetsChange"
  7. variant="primary-filled"
  8. >
  9. <t-radio-button value="system">系统资源</t-radio-button>
  10. <t-radio-button value="user">我的资源</t-radio-button>
  11. </t-radio-group>
  12. </div>
  13. <div class="groups-panel">
  14. <div class="groups">
  15. <div
  16. v-for="group in groups"
  17. :class="group.name === activedGroup ? 'active' : ''"
  18. @click="groupChange(group.name)"
  19. >
  20. <t-icon :name="group.icon" />
  21. {{ group.name }}
  22. </div>
  23. </div>
  24. <div class="list" :class="groupType ? 'two-columns' : ''">
  25. <div class="input-search">
  26. <div class="btn">
  27. <!-- <t-icon name="search" /> -->
  28. <img src="/img/icon_search_gray.svg" />
  29. </div>
  30. <t-input
  31. v-model="search"
  32. @change="onSearch"
  33. @enter="onSearch"
  34. placeholder="搜索"
  35. />
  36. <div class="ml-16">
  37. <t-tooltip content="展开/折叠">
  38. <t-icon
  39. name="menu-fold"
  40. class="hover"
  41. style="font-size: 16px"
  42. @click="onFold"
  43. />
  44. </t-tooltip>
  45. </div>
  46. </div>
  47. <div v-if="loading" class="center mt-16">
  48. <t-loading text="加载中..." size="small" :indicator="false" />
  49. </div>
  50. <template v-else>
  51. <div
  52. v-if="
  53. activedGroup === '组件' ||
  54. activedGroup === '图片' ||
  55. (activeAssets === 'user' &&
  56. (activedGroup === '方案' || activedGroup === '模板'))
  57. "
  58. class="px-16 mt-12 mb-8 ml-4"
  59. >
  60. <a @click="onCreateFolder">+ 新建文件夹</a>
  61. </div>
  62. <t-collapse v-model="activedPanels[activedGroup]">
  63. <t-collapse-panel
  64. :value="item.name"
  65. v-for="item in subGroups"
  66. :key="item.name"
  67. >
  68. <template #header>
  69. <div class="flex middle">
  70. <div v-if="item.edited" @click.stop>
  71. <t-input
  72. v-model="item.label"
  73. style="width: 140px"
  74. @blur="createFoder"
  75. @enter="createFoder"
  76. @keyup="onKeyHeader"
  77. :autofocus="true"
  78. />
  79. </div>
  80. <div v-else>
  81. {{ item.name }}
  82. </div>
  83. </div>
  84. </template>
  85. <template #headerRightContent>
  86. <t-space size="small" @click.stop tabindex="0">
  87. <t-upload
  88. v-if="
  89. item.canEdited &&
  90. (activedGroup === '组件' || activedGroup === '图片')
  91. "
  92. action="/api/image/upload"
  93. :accept="activedGroup === '组件' ? '.svg' : 'image/*'"
  94. :headers="headers"
  95. :data="{
  96. directory:
  97. activedGroup === '图片'
  98. ? `/大屏/${item.name}`
  99. : '/大屏/默认',
  100. }"
  101. :auto-upload="true"
  102. :upload-all-files-in-one-request="false"
  103. :before-upload="beforeUpload"
  104. @selectChange="onSelectFiles(item)"
  105. allowUploadDuplicateFile
  106. @success="fileSuccessed"
  107. theme="custom"
  108. >
  109. <t-icon name="image" class="hover" />
  110. </t-upload>
  111. <template v-if="item.canEdited">
  112. <t-icon
  113. v-if="activedGroup === '方案' || activedGroup === '模板'"
  114. name="add"
  115. class="hover"
  116. @click="onAdd(item)"
  117. />
  118. <t-icon
  119. name="edit"
  120. class="hover"
  121. @click="onEditHeader(item)"
  122. />
  123. <t-popconfirm
  124. content="确认删除该文件夹吗"
  125. placement="left"
  126. @confirm="delFolder(item)"
  127. >
  128. <t-icon name="delete" class="hover" />
  129. </t-popconfirm>
  130. </template>
  131. </t-space>
  132. </template>
  133. <template v-for="elem in item.list">
  134. <div
  135. v-show="elem.visible !== false"
  136. class="graphic"
  137. :draggable="true"
  138. @dragstart="dragStart($event, elem)"
  139. @click.prevent="dragStart($event, elem)"
  140. @dblclick.stop="open(elem)"
  141. @contextmenu="onContextMenu($event, item, elem)"
  142. >
  143. <t-image
  144. v-if="!elem.svg && elem.image"
  145. :src="elem.image"
  146. :lazy="true"
  147. fit="contain"
  148. @load="loadImage(elem)"
  149. />
  150. <div class="svg-box" v-else-if="elem.svg" v-html="elem.svg" />
  151. <svg v-else class="l-icon" aria-hidden="true">
  152. <use :xlink:href="'#' + elem.icon"></use>
  153. </svg>
  154. <p :title="elem.name">{{ elem.name }}</p>
  155. <div class="price" v-if="elem.price > 0">
  156. ¥{{ elem.price }}
  157. </div>
  158. </div>
  159. </template>
  160. <div
  161. v-if="!item.list || !item.list.length"
  162. class="gray"
  163. style="white-space: nowrap; margin-left: 34px"
  164. >
  165. 暂无数据
  166. </div>
  167. </t-collapse-panel>
  168. </t-collapse>
  169. </template>
  170. </div>
  171. </div>
  172. <div
  173. class="context-menu-box"
  174. ref="contextmenuDom"
  175. v-show="contextmenu.visible"
  176. tabindex="0"
  177. :style="contextmenu.style"
  178. @blur="hideContextmenu"
  179. >
  180. <t-menu
  181. class="context-menu"
  182. @change="onMenu"
  183. expandType="popup"
  184. v-model="contextmenu.activeValue"
  185. >
  186. <t-submenu
  187. value="move"
  188. title="移动到"
  189. v-if="contextmenu.subMenus.length"
  190. :disabled="contextmenu.component['3d'] || activedGroup === '图片'"
  191. >
  192. <t-menu-item
  193. v-for="subMenu in contextmenu.subMenus"
  194. :value="'move:' + subMenu.name"
  195. >
  196. {{ subMenu.name }}
  197. </t-menu-item>
  198. </t-submenu>
  199. <t-menu-item value="edit" :disabled="activedGroup === '图片'">
  200. 编辑
  201. </t-menu-item>
  202. <t-menu-item value="del" :disabled="contextmenu.component['3d']">
  203. 删除
  204. </t-menu-item>
  205. </t-menu>
  206. </div>
  207. <t-dialog
  208. v-if="delDialog.show"
  209. theme="danger"
  210. header="删除"
  211. :visible="true"
  212. @close="delDialog.show = false"
  213. @confirm="delComponent"
  214. >
  215. 确定删除该数据吗?删除后不可恢复!
  216. </t-dialog>
  217. <t-dialog
  218. v-if="chargeDialog.show"
  219. :header="chargeDialog.data.name"
  220. :visible="true"
  221. @close="chargeDialog.show = false"
  222. width="70%"
  223. :top="8"
  224. >
  225. <t-image :src="chargeDialog.data.image" />
  226. <template #footer>
  227. <div class="flex between" style="margin-top: -7px">
  228. <p>付费项目,购买后查看并使用</p>
  229. <div>
  230. <label>金额:</label>
  231. <label class="bland">¥{{ chargeDialog.data.price }}</label>
  232. <t-button class="primary ml-12" @click="onSubmitOrder"
  233. >购买</t-button
  234. >
  235. </div>
  236. </div>
  237. </template>
  238. </t-dialog>
  239. <t-dialog
  240. v-if="wechatPayDialog.show"
  241. v-model:visible="wechatPayDialog.show"
  242. class="pay-dialog"
  243. header="乐吾乐收银台"
  244. :close-on-overlay-click="false"
  245. :top="95"
  246. :width="700"
  247. confirm-btn="支付完成"
  248. :cancel-btn="null"
  249. @close="getPayResult"
  250. >
  251. <WechatPay
  252. :order="wechatPayDialog.order"
  253. :code-url="wechatPayDialog.order.codeUrl"
  254. @success="onChargeSuccess"
  255. />
  256. </t-dialog>
  257. </div>
  258. </template>
  259. <script lang="ts" setup>
  260. import { onMounted, onUnmounted, reactive, ref } from 'vue';
  261. import { useRouter } from 'vue-router';
  262. import { MessagePlugin } from 'tdesign-vue-next';
  263. import axios from 'axios';
  264. import { deepClone } from '@meta2d/core';
  265. import { cases, shapes, formComponents, templates } from '@/services/defaults';
  266. import { charts } from '@/services/echarts';
  267. import { getFolders, makeSvg } from '@/services/png';
  268. import {
  269. addCollection,
  270. delImage,
  271. getComponentsList,
  272. getLe5leV,
  273. updateCollection,
  274. getCollectionList,
  275. } from '@/services/api';
  276. import { convertPen } from '@/services/upgrade';
  277. import { isGif } from '@/services/utils';
  278. import { autoSave, delAttrs, blank, useFolder } from '@/services/common';
  279. import { debounce, throttle } from '@/services/debouce';
  280. import { searchObjectPinyin } from '@/services/pinyin';
  281. import { getCookie } from '@/services/cookie';
  282. import { parseSvg } from '@meta2d/svg';
  283. import WechatPay from './WechatPay.vue';
  284. import { filename } from '@/services/file';
  285. import { useUser } from '@/services/user';
  286. import { iframeCustom } from '@/services/defaults';
  287. const { user } = useUser();
  288. const { setFolder, getFolder } = useFolder();
  289. const router = useRouter();
  290. const activedGroup = ref('');
  291. const activeAssets = ref('system');
  292. let groups = reactive([]);
  293. const systemGroups = [
  294. {
  295. icon: 'desktop',
  296. name: '方案',
  297. key: '',
  298. class: 'tow',
  299. },
  300. {
  301. icon: 'root-list',
  302. name: '模板',
  303. key: '',
  304. },
  305. {
  306. icon: 'chart',
  307. name: '图表',
  308. key: 'chart',
  309. },
  310. {
  311. icon: 'image',
  312. name: '素材',
  313. key: '',
  314. },
  315. {
  316. icon: 'control-platform',
  317. name: '图元',
  318. key: '',
  319. },
  320. {
  321. icon: 'relativity',
  322. name: '控件',
  323. key: '',
  324. },
  325. {
  326. icon: 'chart-bubble',
  327. name: '图形',
  328. key: 'shape',
  329. },
  330. // {
  331. // icon: 'app',
  332. // name: '我的',
  333. // key: '',
  334. // },
  335. ];
  336. const userGroups = [
  337. {
  338. icon: 'desktop',
  339. name: '方案',
  340. key: '',
  341. class: 'tow',
  342. },
  343. {
  344. icon: 'root-list',
  345. name: '模板',
  346. key: '',
  347. },
  348. {
  349. icon: 'app',
  350. name: '组件',
  351. key: 'chart',
  352. },
  353. {
  354. icon: 'image',
  355. name: '图片',
  356. key: '',
  357. },
  358. {
  359. icon: 'control-platform',
  360. name: '3D',
  361. key: '',
  362. },
  363. ];
  364. groups = systemGroups;
  365. const subGroups = ref<any[]>([]);
  366. const groupType = ref(0);
  367. const activedPanels = reactive<any>({});
  368. const caseCaches = [];
  369. const templateCaches = [];
  370. const materials = [];
  371. const pngs = [];
  372. let dropped = false;
  373. const chargeDialog = reactive<any>({});
  374. const wechatPayDialog = reactive<any>({
  375. show: false,
  376. });
  377. const search = ref('');
  378. const loading = ref(false);
  379. const headers = {
  380. Authorization: 'Bearer ' + (localStorage.token || getCookie('token') || ''),
  381. };
  382. const updataData = { directory: '/大屏/默认' };
  383. let lastName = '方案';
  384. let userLastName = '方案';
  385. const assetsChange = (value) => {
  386. if (value === 'system') {
  387. groups = systemGroups;
  388. activedGroup.value = lastName;
  389. } else if (value === 'user') {
  390. groups = userGroups;
  391. activedGroup.value = userLastName;
  392. }
  393. groupChange(activedGroup.value);
  394. };
  395. const groupChange = async (name: string) => {
  396. activedGroup.value = name;
  397. groupType.value = 0;
  398. switch (name) {
  399. case '方案':
  400. if (activeAssets.value === 'system') {
  401. if (!caseCaches.length) {
  402. loading.value = true;
  403. caseCaches.push(...(await getCaseProjects(name)));
  404. for (const group of cases) {
  405. group.list = [];
  406. for (const item of caseCaches) {
  407. if (item.case === group.name) {
  408. group.list.push(item);
  409. }
  410. }
  411. }
  412. loading.value = false;
  413. }
  414. groupType.value = 1;
  415. subGroups.value = cases;
  416. lastName = name;
  417. } else {
  418. subGroups.value = await getUserProjects('le5leV');
  419. groupType.value = 1;
  420. await getPrivateProjects('le5leV');
  421. userLastName = name;
  422. }
  423. break;
  424. case '模板':
  425. if (activeAssets.value === 'system') {
  426. if (!templateCaches.length) {
  427. loading.value = true;
  428. templateCaches.push(...(await getCaseProjects(name)));
  429. for (const group of templates) {
  430. group.list = [];
  431. for (const item of templateCaches) {
  432. if (item.case === group.name) {
  433. group.list.push(item);
  434. }
  435. }
  436. }
  437. loading.value = false;
  438. }
  439. groupType.value = 1;
  440. subGroups.value = templates;
  441. lastName = name;
  442. } else {
  443. subGroups.value = await getUserProjects('le5leV-template');
  444. groupType.value = 1;
  445. await getPrivateProjects('le5leV-template');
  446. userLastName = name;
  447. }
  448. break;
  449. case '图表':
  450. subGroups.value = charts;
  451. lastName = name;
  452. break;
  453. case '控件':
  454. subGroups.value = formComponents;
  455. lastName = name;
  456. break;
  457. case '素材':
  458. groupType.value = 1;
  459. if (!materials.length) {
  460. loading.value = true;
  461. materials.push(...(await getFolders('v/material')));
  462. loading.value = false;
  463. }
  464. subGroups.value = materials;
  465. lastName = name;
  466. break;
  467. case '图元':
  468. if (!pngs.length) {
  469. loading.value = true;
  470. pngs.push(...(await getFolders('png')));
  471. pngs.push(...(await getFolders('svg', true)));
  472. loading.value = false;
  473. }
  474. subGroups.value = pngs;
  475. lastName = name;
  476. break;
  477. case '图形':
  478. subGroups.value = shapes;
  479. userLastName = name;
  480. break;
  481. case '组件':
  482. subGroups.value = await getUserComponents();
  483. groupType.value = 1;
  484. await getPrivateGraphics();
  485. userLastName = name;
  486. break;
  487. case '图片':
  488. loading.value = true;
  489. subGroups.value = await getImageList();
  490. loading.value = false;
  491. userLastName = name;
  492. break;
  493. case '3D':
  494. subGroups.value = [
  495. {
  496. name: '3D',
  497. list: [],
  498. },
  499. ];
  500. groupType.value = 1;
  501. await getPrivateGraphics();
  502. userLastName = name;
  503. break;
  504. }
  505. // if (!activedPanels[name]) {
  506. activedPanels[name] = [];
  507. for (const item of subGroups.value) {
  508. activedPanels[name].push(item.name);
  509. }
  510. // }
  511. searchGraphics();
  512. };
  513. const getImageList = async () => {
  514. let ret: { list: any[] } = await axios.post('/api/directory/list', {
  515. fullpath: '/大屏',
  516. });
  517. if (!ret) {
  518. return [];
  519. }
  520. let list = [];
  521. for (let i of ret.list) {
  522. if (i.fullpath.indexOf('缩略图') === -1) {
  523. list.push(i);
  524. }
  525. }
  526. return await Promise.all(
  527. list.map(async (item) => {
  528. let secondDir = item.fullpath.split('/');
  529. const _ret: { list: any[]; total: number } = await axios.post(
  530. '/api/file/list',
  531. {
  532. type: '图片',
  533. directory: item.fullpath,
  534. },
  535. {
  536. params: {
  537. current: 1,
  538. pageSize: 100,
  539. },
  540. }
  541. );
  542. let list = _ret.list.map((_item) => {
  543. return {
  544. image: _item.metadata.url,
  545. visible: true,
  546. folder: item.fullpath,
  547. _id: item._id,
  548. };
  549. });
  550. return {
  551. name: secondDir[2],
  552. _id: item._id,
  553. canEdited: secondDir[2] !== '默认',
  554. list: list,
  555. };
  556. })
  557. );
  558. };
  559. const getCaseProjects = async (name: string, current = 1, pageSize = 1000) => {
  560. const query: any = { tags: name };
  561. const ret: any = await axios.post(
  562. '/api/data/le5leV/list',
  563. {
  564. query,
  565. shared: 'true',
  566. projection: {
  567. id: 1,
  568. _id: 1,
  569. name: 1,
  570. image: 1,
  571. price: 1,
  572. case: 1,
  573. },
  574. sort: { createdAt: 1 },
  575. },
  576. {
  577. params: {
  578. current,
  579. pageSize,
  580. },
  581. }
  582. );
  583. if (!ret) {
  584. return [];
  585. }
  586. for (const item of ret.list) {
  587. if (!item.id) {
  588. item.id = item._id;
  589. }
  590. item.draggable = false;
  591. }
  592. return ret.list;
  593. };
  594. // const getUserProjects = async (name: string, current = 1, pageSize = 1000) => {
  595. // const query: any = { tags: name };
  596. // const ret: any = await axios.post(
  597. // '/api/data/le5leV/list',
  598. // {
  599. // query,
  600. // projection: {
  601. // id: 1,
  602. // _id: 1,
  603. // name: 1,
  604. // image: 1,
  605. // price: 1,
  606. // case: 1,
  607. // folder: 1,
  608. // },
  609. // sort: { createdAt: 1 },
  610. // },
  611. // {
  612. // params: {
  613. // current,
  614. // pageSize,
  615. // },
  616. // }
  617. // );
  618. // if (!ret) {
  619. // return [];
  620. // }
  621. // for (const item of ret.list) {
  622. // if (!item.id) {
  623. // item.id = item._id;
  624. // }
  625. // item.draggable = false;
  626. // }
  627. // return ret.list;
  628. // };
  629. const getPrivateGroups = async () => {
  630. const list = [
  631. {
  632. name: '默认',
  633. list: [],
  634. },
  635. ];
  636. const config = {
  637. params: {
  638. current: 1,
  639. pageSize: 1000,
  640. },
  641. };
  642. let ret: any = await axios.post(
  643. '/api/data/folders/list',
  644. {
  645. projection: {
  646. image: 1,
  647. _id: 1,
  648. name: 1,
  649. list: 1,
  650. },
  651. query: {
  652. type: `le5leV-components`,
  653. },
  654. sort: { createdAt: 1 },
  655. },
  656. config
  657. );
  658. if (!ret) {
  659. ret = { list: [] };
  660. }
  661. for (const item of ret.list) {
  662. item.canEdited = true;
  663. }
  664. list.push(...ret.list);
  665. list.push({
  666. name: '3D',
  667. list: [],
  668. });
  669. return list;
  670. };
  671. const getUserComponents = async () => {
  672. const list = [
  673. {
  674. name: '默认',
  675. list: [],
  676. },
  677. ];
  678. const config = {
  679. params: {
  680. current: 1,
  681. pageSize: 1000,
  682. },
  683. };
  684. let ret: any = await axios.post(
  685. '/api/data/folders/list',
  686. {
  687. projection: {
  688. image: 1,
  689. _id: 1,
  690. name: 1,
  691. list: 1,
  692. },
  693. query: {
  694. type: `le5leV-components`,
  695. },
  696. sort: { createdAt: 1 },
  697. },
  698. config
  699. );
  700. if (!ret) {
  701. ret = { list: [] };
  702. }
  703. for (const item of ret.list) {
  704. item.canEdited = true;
  705. }
  706. list.push(...ret.list);
  707. return list;
  708. };
  709. const getUserProjects = async (type: string) => {
  710. const list = [
  711. {
  712. name: '默认',
  713. list: [],
  714. },
  715. ];
  716. const config = {
  717. params: {
  718. current: 1,
  719. pageSize: 1000,
  720. },
  721. };
  722. let ret: any = await axios.post(
  723. '/api/data/folders/list',
  724. {
  725. projection: {
  726. image: 1,
  727. _id: 1,
  728. name: 1,
  729. list: 1,
  730. },
  731. query: {
  732. type,
  733. },
  734. sort: { createdAt: 1 },
  735. },
  736. config
  737. );
  738. if (!ret) {
  739. ret = { list: [] };
  740. }
  741. for (const item of ret.list) {
  742. item.canEdited = true;
  743. }
  744. list.push(...ret.list);
  745. return list;
  746. };
  747. const getPrivateProjects = async (type: string) => {
  748. for (const item of subGroups.value) {
  749. if (!item.list.length) {
  750. item.loading = true;
  751. //TODO 方案/模板分类
  752. if (item.name === '默认') {
  753. const data = {
  754. query: {
  755. folder: '',
  756. tags: type === 'le5leV-template' ? '模板' : '方案',
  757. },
  758. projection: {
  759. image: 1,
  760. _id: 1,
  761. name: 1,
  762. tags: 1,
  763. },
  764. };
  765. const config = {
  766. params: {
  767. current: 1,
  768. pageSize: 1000,
  769. },
  770. };
  771. const res: any = await getCollectionList('le5leV', data, config);
  772. if (res?.list) {
  773. // res.list.forEach((item) => {
  774. // item.draggable = false;
  775. // })
  776. item.list = res.list;
  777. // if (type === 'le5leV') {
  778. // //过滤模板
  779. // // item.list = res.list.filter((item) => !item.isTemplate);
  780. // }
  781. }
  782. }
  783. item.loading = false;
  784. }
  785. }
  786. };
  787. const dragStart = async (event: DragEvent | MouseEvent, item: any) => {
  788. event.stopPropagation();
  789. if (!item) {
  790. return;
  791. }
  792. meta2d.canvas.addCaches = [];
  793. dropped = false;
  794. let data = null;
  795. const id = item._id || item.id;
  796. let isAsync: boolean;
  797. if (
  798. activeAssets.value === 'user' &&
  799. ['方案', '模板'].includes(activedGroup.value)
  800. ) {
  801. item.draggable = false;
  802. data = item.data || item;
  803. } else if (item.draggable === false) {
  804. //方案
  805. data = item.data || item;
  806. } else if (item['3d']) {
  807. data = {
  808. name: 'iframe',
  809. x: 0,
  810. y: 0,
  811. tags: ['meta3d'],
  812. zIndex: 1,
  813. operationalRect: {
  814. x: 0.2,
  815. y: 0.2,
  816. height: 0.8,
  817. width: 0.6,
  818. },
  819. props: {
  820. custom: iframeCustom,
  821. },
  822. width: meta2d.store.data.width || meta2d.store.options.width,
  823. height: meta2d.store.data.height || meta2d.store.options.height,
  824. externElement: true,
  825. iframe: 'https://view3d.le5le.com/?id=' + (item._id || item.id),
  826. };
  827. } else if (item.component) {
  828. // 默认
  829. if (!item.componentDatas && !item.componentData) {
  830. isAsync = true;
  831. const ret: any = await axios.post(`/api/data/le5leV-components/get`, {
  832. id,
  833. });
  834. item.componentDatas = ret.componentDatas;
  835. item.componentData = ret.componentData;
  836. }
  837. if (item.componentData) {
  838. const pens = convertPen([item.componentData]);
  839. data = deepClone(pens);
  840. } else if (item.componentDatas) {
  841. data = deepClone(item.componentDatas);
  842. }
  843. } else if (item.data) {
  844. // 普通图元
  845. data = item.data;
  846. } else if (item.image) {
  847. // 拖拽图片
  848. let target: any = event.target;
  849. target.children[0] && (target = target.children[0].children[0]);
  850. // firefox naturalWidth svg 图片 可能是 0
  851. const width = target.naturalWidth || target.width;
  852. const height = target.naturalHeight || target.height;
  853. const [name, lockedOnCombine] = isGif(item.image)
  854. ? ['gif', 0]
  855. : ['image', undefined];
  856. data = {
  857. name,
  858. width,
  859. height,
  860. image: item.image,
  861. imageRatio: true,
  862. ratio: true,
  863. lockedOnCombine,
  864. };
  865. } else {
  866. return;
  867. }
  868. if (!Array.isArray(data)) {
  869. data = deepClone([data]);
  870. }
  871. !dropped && (meta2d.canvas.addCaches = data);
  872. if (event instanceof DragEvent) {
  873. event.dataTransfer.setData(
  874. 'Meta2d',
  875. isAsync ? undefined : JSON.stringify(data)
  876. );
  877. }
  878. };
  879. const dragstart = (event: any) => {
  880. event.target.style.opacity = 0.5;
  881. };
  882. const dragend = (event: any) => {
  883. event.target.style.opacity = 1;
  884. };
  885. const open = async (item: any) => {
  886. if (!item || item.draggable !== false) {
  887. return;
  888. }
  889. const ret: any = await getLe5leV(item._id || item.id);
  890. if (!ret) {
  891. if (item.price > 0) {
  892. chargeDialog.data = item;
  893. chargeDialog.show = true;
  894. }
  895. return;
  896. }
  897. if (activeAssets.value === 'user') {
  898. router.push({
  899. path: '/',
  900. query: {
  901. r: Date.now() + '',
  902. id: item._id,
  903. },
  904. });
  905. } else {
  906. sessionStorage.setItem('opening', '1');
  907. router.push({
  908. path: '/',
  909. query: {
  910. r: Date.now() + '',
  911. },
  912. });
  913. for (const k of delAttrs) {
  914. delete ret[k];
  915. }
  916. autoSave();
  917. meta2d.open(ret);
  918. meta2d.fitView();
  919. }
  920. };
  921. const getPrivateGraphics = async () => {
  922. for (const item of subGroups.value) {
  923. if (!item.list.length) {
  924. item.loading = true;
  925. if (item.name === '默认') {
  926. const data = {
  927. query: { folder: '' },
  928. projection: {
  929. image: 1,
  930. _id: 1,
  931. name: 1,
  932. component: 1,
  933. },
  934. };
  935. const config = {
  936. params: {
  937. current: 1,
  938. pageSize: 1000,
  939. },
  940. };
  941. const res: any = await getComponentsList(data, config);
  942. if (res?.list) {
  943. item.list = res.list;
  944. }
  945. } else if (item.name === '3D') {
  946. const data = {
  947. projection: {
  948. image: 1,
  949. _id: 1,
  950. name: 1,
  951. },
  952. };
  953. const config = {
  954. params: {
  955. current: 1,
  956. pageSize: 1000,
  957. },
  958. };
  959. const res: any = await axios.post(
  960. '/api/data/le5le3d/list',
  961. data,
  962. config
  963. );
  964. if (res?.list) {
  965. for (const item of res.list) {
  966. item['3d'] = true;
  967. item['draggable'] = true;
  968. }
  969. item.list = res.list;
  970. }
  971. }
  972. item.loading = false;
  973. }
  974. }
  975. };
  976. const editedFolder = ref<any>(undefined);
  977. const onCreateFolder = () => {
  978. activedPanels[activedGroup.value].splice(
  979. 0,
  980. activedPanels[activedGroup.value].length
  981. );
  982. editedFolder.value = {
  983. _id: '',
  984. name: '',
  985. label: '新建文件夹',
  986. list: [],
  987. edited: true,
  988. canEdited: true,
  989. };
  990. subGroups.value.splice(subGroups.value.length - 1, 0, editedFolder.value);
  991. };
  992. const createFoder = async () => {
  993. if (!editedFolder.value.label) {
  994. return;
  995. }
  996. if (editedFolder.value.label === editedFolder.value.name) {
  997. editedFolder.value.edited = false;
  998. return;
  999. }
  1000. const found = subGroups.value.findIndex(
  1001. (group: any) => group.name === editedFolder.value.label
  1002. );
  1003. if (found >= 0) {
  1004. MessagePlugin.error('已经存在相同名称文件夹');
  1005. return;
  1006. }
  1007. if (activeAssets.value !== 'user') {
  1008. return;
  1009. }
  1010. if (['组件', '方案', '模板'].includes(activedGroup.value)) {
  1011. if (editedFolder.value._id) {
  1012. const ret: any = await axios.post('/api/data/folders/update', {
  1013. _id: editedFolder.value._id,
  1014. name: editedFolder.value.label,
  1015. });
  1016. if (ret) {
  1017. editedFolder.value.name = editedFolder.value.label;
  1018. editedFolder.value.edited = false;
  1019. }
  1020. } else {
  1021. let type =
  1022. activedGroup.value === '组件'
  1023. ? 'le5leV-components'
  1024. : activedGroup.value === '模板'
  1025. ? 'le5leV-template'
  1026. : 'le5leV';
  1027. const ret: any = await axios.post('/api/data/folders/add', {
  1028. name: editedFolder.value.label,
  1029. type,
  1030. list: [],
  1031. });
  1032. if (ret) {
  1033. editedFolder.value.name = editedFolder.value.label;
  1034. editedFolder.value._id = ret._id;
  1035. editedFolder.value.edited = false;
  1036. }
  1037. }
  1038. } else if (activedGroup.value === '图片') {
  1039. if (editedFolder.value._id) {
  1040. const ret: any = await axios.post('/api/directory/update', {
  1041. oldFullpath: '/大屏/' + editedFolder.value.oldLabel,
  1042. newFullpath: '/大屏/' + editedFolder.value.label,
  1043. });
  1044. if (ret) {
  1045. editedFolder.value.name = editedFolder.value.label;
  1046. editedFolder.value.edited = false;
  1047. }
  1048. } else {
  1049. const ret: any = await axios.post('/api/directory/add', {
  1050. fullpath: '/大屏/' + editedFolder.value.label,
  1051. });
  1052. if (ret) {
  1053. editedFolder.value.name = editedFolder.value.label;
  1054. editedFolder.value._id = ret._id || ret.id;
  1055. editedFolder.value.edited = false;
  1056. }
  1057. }
  1058. }
  1059. };
  1060. const onEditHeader = (item: any) => {
  1061. item.label = item.name;
  1062. item.edited = true;
  1063. item.oldLabel = item.name;
  1064. editedFolder.value = item;
  1065. };
  1066. const onAdd = (item: any) => {
  1067. blank();
  1068. if (activedGroup.value === '方案') {
  1069. item.vType = 'le5leV';
  1070. } else if (activedGroup.value === '模板') {
  1071. item.vType = 'le5leV-template';
  1072. }
  1073. setFolder(item);
  1074. router.push({
  1075. path: '/',
  1076. query: {
  1077. r: Date.now() + '',
  1078. folder: item.name,
  1079. tags: activedGroup.value,
  1080. },
  1081. });
  1082. // meta2d.open({
  1083. // name: '新建项目',
  1084. // pens: [],
  1085. // enableMock: true,
  1086. // tags: activedGroup.value,
  1087. // folder: item.name,
  1088. // } as any);
  1089. };
  1090. const onKeyHeader = (text: string, event: any) => {
  1091. if (event.e.key === 'Escape') {
  1092. editedFolder.value.edited = false;
  1093. }
  1094. };
  1095. // 默认右键菜单
  1096. const contextmenu = reactive<any>({
  1097. visible: false,
  1098. style: {},
  1099. // 子分类
  1100. group: undefined,
  1101. // 组件图纸
  1102. component: {},
  1103. // 右键二级子菜单
  1104. subMenus: [],
  1105. });
  1106. const contextmenuDom = ref<any>(null);
  1107. const onContextMenu = async (e: MouseEvent, group: any, item: any) => {
  1108. e.preventDefault();
  1109. e.stopPropagation();
  1110. if (activeAssets.value === 'system') {
  1111. return;
  1112. }
  1113. contextmenu.group = group;
  1114. contextmenu.component = item;
  1115. if (document.body.clientHeight - e.clientY < 128) {
  1116. contextmenu.style = {
  1117. left: e.clientX + 'px',
  1118. bottom: '4px',
  1119. };
  1120. } else {
  1121. contextmenu.style = {
  1122. left: e.clientX + 'px',
  1123. top: e.clientY + 'px',
  1124. };
  1125. }
  1126. contextmenu.subMenus = [];
  1127. for (const elem of subGroups.value) {
  1128. if (elem === group || elem.name === '3D') {
  1129. continue;
  1130. }
  1131. contextmenu.subMenus.push(elem);
  1132. }
  1133. contextmenu.visible = true;
  1134. setTimeout(() => {
  1135. if (contextmenuDom.value) {
  1136. contextmenuDom.value.focus();
  1137. }
  1138. }, 500);
  1139. };
  1140. const delDialog = reactive<any>({
  1141. contextmenuObj: {},
  1142. });
  1143. const onMenu = async (val: string) => {
  1144. const id = contextmenu.component._id || contextmenu.component.id;
  1145. setTimeout(() => {
  1146. contextmenu.group = '';
  1147. contextmenu.component = {};
  1148. contextmenu.subMenus = [];
  1149. }, 500);
  1150. switch (val) {
  1151. case 'edit':
  1152. if (contextmenu.component.component) {
  1153. autoSave();
  1154. router.push({
  1155. path: '/',
  1156. query: {
  1157. id,
  1158. c: 1,
  1159. r: Date.now() + '',
  1160. },
  1161. });
  1162. } else if (contextmenu.component['3d']) {
  1163. let url = 'https://3d.le5le.com/?id=';
  1164. if (window.url3D) {
  1165. url = window.url3D;
  1166. }
  1167. window.open(url + id, '_blank');
  1168. } else {
  1169. if (contextmenu.group._id && contextmenu.group.name !== '默认') {
  1170. setFolder(contextmenu.group);
  1171. }
  1172. autoSave();
  1173. //文件夹
  1174. router.push({
  1175. path: '/',
  1176. query: {
  1177. id,
  1178. r: Date.now() + '',
  1179. },
  1180. });
  1181. }
  1182. break;
  1183. case 'del':
  1184. delDialog.show = true;
  1185. Object.assign(delDialog.contextmenuObj, contextmenu);
  1186. break;
  1187. default:
  1188. if (val.indexOf('move:')) {
  1189. return;
  1190. }
  1191. val = val.replace('move:', '');
  1192. const group = contextmenu.subMenus.find(
  1193. (element: any) => element.name === val
  1194. );
  1195. if (!group) {
  1196. return;
  1197. }
  1198. // 前端: 添加组件到目标文件夹
  1199. group.list.push(contextmenu.component);
  1200. // 前端:从源文件夹移出组件
  1201. contextmenu.group.list.forEach((item: any, index: number, arr: any[]) => {
  1202. if (id === item._id || id === item.id) {
  1203. arr.splice(index, 1);
  1204. }
  1205. });
  1206. let collection = contextmenu.component.component
  1207. ? 'le5leV-components'
  1208. : 'le5leV';
  1209. // 更新后端组件信息
  1210. let ret = await updateCollection(collection, {
  1211. _id: id,
  1212. folder: val === '默认' ? '' : val,
  1213. });
  1214. if (!ret) {
  1215. return;
  1216. }
  1217. // 更新后端源文件夹列表
  1218. if (contextmenu.group.name !== '默认') {
  1219. await axios.post('/api/data/folders/update', {
  1220. _id: contextmenu.group._id || contextmenu.group.id,
  1221. list: contextmenu.group.list,
  1222. });
  1223. }
  1224. // 更新后端目标文件夹列表
  1225. if (group.name !== '默认') {
  1226. await axios.post('/api/data/folders/update', {
  1227. _id: group._id || group.id,
  1228. list: group.list,
  1229. });
  1230. }
  1231. break;
  1232. }
  1233. contextmenu.activeValue = 0;
  1234. };
  1235. const hideContextmenu = () => {
  1236. contextmenu.visible = false;
  1237. };
  1238. const delComponent = async () => {
  1239. // const id = contextmenu.component._id || contextmenu.component.id;
  1240. if (['组件', '方案', '模板'].includes(activedGroup.value)) {
  1241. let collection = delDialog.contextmenuObj.component.component
  1242. ? 'le5leV-components'
  1243. : 'le5leV';
  1244. const id =
  1245. delDialog.contextmenuObj.component._id ||
  1246. delDialog.contextmenuObj.component.id;
  1247. try {
  1248. await axios.post(`/api/data/${collection}/delete`, {
  1249. id,
  1250. });
  1251. } catch (e) {
  1252. console.error(e);
  1253. return;
  1254. }
  1255. //删除缩略图
  1256. if (delDialog.contextmenuObj.component.image) {
  1257. await delImage(delDialog.contextmenuObj.component.image);
  1258. }
  1259. // 前端:从源文件夹移出组件
  1260. delDialog.contextmenuObj.group.list.forEach(
  1261. (item: any, index: number, arr: any[]) => {
  1262. if (id === item._id || id === item.id) {
  1263. arr.splice(index, 1);
  1264. }
  1265. }
  1266. );
  1267. // 更新后端源文件夹列表
  1268. if (delDialog.contextmenuObj.group.name !== '默认') {
  1269. await axios.post('/api/data/folders/update', {
  1270. _id:
  1271. delDialog.contextmenuObj.group._id ||
  1272. delDialog.contextmenuObj.group.id,
  1273. list: delDialog.contextmenuObj.group.list,
  1274. });
  1275. }
  1276. } else {
  1277. const id =
  1278. delDialog.contextmenuObj.component._id ||
  1279. delDialog.contextmenuObj.component.id;
  1280. delDialog.contextmenuObj.group.list.forEach(
  1281. (item: any, index: number, arr: any[]) => {
  1282. if (id === item._id || id === item.id) {
  1283. arr.splice(index, 1);
  1284. }
  1285. }
  1286. );
  1287. await axios.post(`/api/files/delete`, {
  1288. filenames: [delDialog.contextmenuObj.component.image],
  1289. physically: false,
  1290. });
  1291. }
  1292. delDialog.show = false;
  1293. delDialog.contextmenuObj = {};
  1294. MessagePlugin.success('删除成功');
  1295. };
  1296. const drop = (obj: any) => {
  1297. dropped = true;
  1298. if (obj) {
  1299. Array.isArray(obj) && open(obj[0]);
  1300. } else {
  1301. MessagePlugin.warning('正在请求网络数据中,请稍后重试');
  1302. }
  1303. };
  1304. const onSubmitOrder = async () => {
  1305. const result: any = await axios.post('/api/order/datas/submit', {
  1306. collection: 'le5leV',
  1307. id: chargeDialog.data._id,
  1308. });
  1309. if (result) {
  1310. wechatPayDialog.order = result;
  1311. wechatPayDialog.show = true;
  1312. }
  1313. };
  1314. const getPayResult = async () => {
  1315. const result: { state: number } = await axios.post('/api/order/pay/state', {
  1316. id: wechatPayDialog.order.id || wechatPayDialog.order._id,
  1317. });
  1318. if (result && result.state) {
  1319. return true;
  1320. }
  1321. };
  1322. const onChargeSuccess = () => {
  1323. MessagePlugin.success('支付成功!');
  1324. wechatPayDialog.show = false;
  1325. chargeDialog.show = false;
  1326. };
  1327. const onSearch = () => {
  1328. debounce(searchGraphics, 300);
  1329. };
  1330. const searchGraphics = async () => {
  1331. if (search.value) {
  1332. activedPanels[activedGroup.value].splice(
  1333. 0,
  1334. activedPanels[activedGroup.value].length
  1335. );
  1336. }
  1337. for (const group of subGroups.value) {
  1338. for (const item of group.list) {
  1339. if (search.value) {
  1340. item.visible = searchObjectPinyin(item, 'name', search.value);
  1341. } else {
  1342. item.visible = true;
  1343. }
  1344. }
  1345. if (search.value) {
  1346. activedPanels[activedGroup.value].push(group.name);
  1347. }
  1348. }
  1349. };
  1350. const onFold = () => {
  1351. if (!activedPanels[activedGroup.value]) {
  1352. return;
  1353. }
  1354. if (activedPanels[activedGroup.value].length) {
  1355. activedPanels[activedGroup.value] = [];
  1356. } else {
  1357. activedPanels[activedGroup.value] = [];
  1358. for (const item of subGroups.value) {
  1359. activedPanels[activedGroup.value].push(item.name);
  1360. }
  1361. }
  1362. };
  1363. const loadImage = (elem: any) => {
  1364. if (elem.isSvg) {
  1365. makeSvg(elem);
  1366. if (activedGroup.value === '图元') {
  1367. throttle(renderPngGroup, 100);
  1368. }
  1369. }
  1370. };
  1371. const renderPngGroup = () => {
  1372. subGroups.value = pngs;
  1373. };
  1374. let uploadGroup: any;
  1375. const onSelectFiles = (item: any) => {
  1376. uploadGroup = item;
  1377. };
  1378. const beforeUpload = (file: any) => {
  1379. if (!(user && user.id)) {
  1380. MessagePlugin.warning('请先登录!');
  1381. return false;
  1382. }
  1383. return true;
  1384. };
  1385. /*
  1386. * @description 根据上传的文件处理为meta2d能够识别的内容
  1387. * */
  1388. function processFileObj(fileObj, c) {
  1389. return new Promise((resolve) => {
  1390. if (fileObj.name.endsWith('.svg')) {
  1391. let fileReader = new FileReader();
  1392. fileReader.readAsText(fileObj.raw);
  1393. fileReader.onload = async () => {
  1394. let svgText = fileReader.result;
  1395. c.componentDatas = parseSvg(svgText as string);
  1396. c.component = true;
  1397. resolve(c);
  1398. };
  1399. } else {
  1400. // 除了svg 其他一律按照图片处理,这样是否会有问题?
  1401. resolve(c);
  1402. }
  1403. });
  1404. }
  1405. const fileSuccessed = async (content: any) => {
  1406. if (activedGroup.value === '组件') {
  1407. const c: any = {
  1408. name: filename(content.file.name),
  1409. image: content.response.url,
  1410. folder: uploadGroup.name === '默认' ? '' : uploadGroup.name,
  1411. };
  1412. let rst = await processFileObj(
  1413. { raw: content.file.raw, name: content.file.name },
  1414. c
  1415. );
  1416. const ret: any = await addCollection('le5leV-components', rst);
  1417. c._id = ret._id || ret.id;
  1418. if (ret && uploadGroup.name !== '默认') {
  1419. if (!uploadGroup.list) {
  1420. uploadGroup.list = [];
  1421. }
  1422. uploadGroup.list.push(c);
  1423. await axios.post('/api/data/folders/update', {
  1424. _id: uploadGroup._id || uploadGroup.id,
  1425. list: uploadGroup.list,
  1426. });
  1427. } else {
  1428. if (!uploadGroup.list) {
  1429. uploadGroup.list = [];
  1430. }
  1431. uploadGroup.list.push(c);
  1432. }
  1433. } else if (activedGroup.value === '图片') {
  1434. uploadGroup.list.push({
  1435. image: content.response.filename,
  1436. visible: true,
  1437. folder: content.response.metadata.directory,
  1438. });
  1439. }
  1440. MessagePlugin.success('上传成功');
  1441. };
  1442. const delFolder = async (item: any) => {
  1443. if (item.list?.length) {
  1444. MessagePlugin.error('文件夹不为空!');
  1445. return;
  1446. }
  1447. const id = item._id || item.id;
  1448. let ret: any;
  1449. if (activeAssets.value !== 'user') {
  1450. return;
  1451. }
  1452. if (['组件', '方案', '模板'].includes(activedGroup.value)) {
  1453. ret = await axios.post('/api/data/folders/delete', {
  1454. id,
  1455. });
  1456. } else if (activedGroup.value === '图片') {
  1457. ret = await axios.post('/api/directory/delete', {
  1458. fullpaths: [`/大屏/${item.name}`],
  1459. });
  1460. }
  1461. if (ret) {
  1462. const i = subGroups.value.findIndex(
  1463. (elem: any) => id === elem._id || id === elem.id
  1464. );
  1465. i >= 0 && subGroups.value.splice(i, 1);
  1466. }
  1467. };
  1468. const reloadCurrent = () => {
  1469. groupChange(activedGroup.value);
  1470. };
  1471. onMounted(() => {
  1472. groupChange('方案');
  1473. document.addEventListener('dragstart', dragstart, false);
  1474. document.addEventListener('dragend', dragend, false);
  1475. setTimeout(() => {
  1476. meta2d.on('drop', drop);
  1477. meta2d.on('logout', reloadCurrent);
  1478. }, 2000);
  1479. });
  1480. onUnmounted(() => {
  1481. document.removeEventListener('dragstart', dragstart);
  1482. document.removeEventListener('dragend', dragend);
  1483. meta2d.off('drop', drop);
  1484. meta2d.off('logout', reloadCurrent);
  1485. });
  1486. </script>
  1487. <style lang="postcss" scoped>
  1488. .graphics {
  1489. display: flex;
  1490. flex-direction: column;
  1491. background-color: var(--color-background);
  1492. z-index: 3;
  1493. .group-asset {
  1494. height: 40px;
  1495. justify-content: center;
  1496. align-items: center;
  1497. display: flex;
  1498. .t-radio-group.t-radio-group--filled {
  1499. padding: 0px;
  1500. height: 30px;
  1501. }
  1502. :deep(.t-radio-button__label) {
  1503. margin: auto;
  1504. }
  1505. .t-radio-button {
  1506. width: 120px;
  1507. height: 100%;
  1508. color: #fff;
  1509. &:hover {
  1510. color: var(--color-primary);
  1511. }
  1512. &.t-is-checked {
  1513. background-color: var(--color-primary) !important;
  1514. color: #fff !important;
  1515. }
  1516. }
  1517. }
  1518. .groups-panel {
  1519. display: grid;
  1520. grid-template-columns: 50px 1fr;
  1521. border-top: 1px solid var(--color-background-input);
  1522. flex-grow: 1;
  1523. overflow-y: auto;
  1524. font-size: 12px;
  1525. z-index: 7;
  1526. .groups {
  1527. & > div {
  1528. display: flex;
  1529. flex-direction: column;
  1530. align-items: center;
  1531. padding: 16px 4px;
  1532. line-height: 1;
  1533. cursor: pointer;
  1534. .t-icon {
  1535. font-size: 20px;
  1536. margin-bottom: 8px;
  1537. }
  1538. &:hover {
  1539. color: var(--color-primary);
  1540. }
  1541. }
  1542. .active {
  1543. background-color: var(--color-background-active);
  1544. color: var(--color-primary);
  1545. border-left: 2px solid var(--color-primary);
  1546. }
  1547. }
  1548. .list {
  1549. overflow-y: auto;
  1550. max-height: calc(100vh - 100px);
  1551. background-color: var(--color-background-active);
  1552. /* padding-top: 8px; */
  1553. .input-search {
  1554. flex-shrink: 0;
  1555. /* height: 40px; */
  1556. position: sticky;
  1557. top: 0px;
  1558. z-index: 10;
  1559. padding: 16px;
  1560. color: #878f9c;
  1561. .btn {
  1562. background: #fff0;
  1563. img {
  1564. background-color: #fff0;
  1565. margin-top: 9px;
  1566. }
  1567. /* .t-icon {
  1568. background: #fff0;
  1569. } */
  1570. }
  1571. }
  1572. * {
  1573. background-color: var(--color-background-active);
  1574. }
  1575. :deep(.t-collapse) {
  1576. min-height: 100vh;
  1577. border: none;
  1578. }
  1579. :deep(.t-collapse-panel__header) {
  1580. border: none;
  1581. font-size: 12px;
  1582. font-weight: 400;
  1583. padding: 8px 8px 8px 16px;
  1584. & > .t-space {
  1585. display: none;
  1586. }
  1587. & > .t-space:focus {
  1588. display: inline-flex;
  1589. }
  1590. &:hover .t-collapse-panel__icon,
  1591. &:hover > .flex {
  1592. color: var(--color-primary);
  1593. }
  1594. .t-collapse-panel__icon,
  1595. .t-collapse-panel__icon * {
  1596. transition: none;
  1597. }
  1598. .t-icon {
  1599. font-size: 13px;
  1600. }
  1601. }
  1602. :deep(.t-collapse-panel__wrapper:hover) {
  1603. .t-collapse-panel__header > .t-space {
  1604. display: inline-flex;
  1605. }
  1606. }
  1607. :deep(.t-collapse-panel__body) {
  1608. border: none;
  1609. }
  1610. :deep(.t-collapse-panel__content) {
  1611. background-color: var(--color-background-active);
  1612. padding: 4px 4px 20px 4px;
  1613. display: grid;
  1614. grid-template-columns: 1fr 1fr 1fr;
  1615. grid-row-gap: 12px;
  1616. }
  1617. :deep(.t-loading--center) {
  1618. width: 100px;
  1619. .t-loading__text {
  1620. margin-left: 8px;
  1621. height: 24px;
  1622. }
  1623. }
  1624. :deep(.t-image__error) {
  1625. .t-space-item:last-child {
  1626. display: none;
  1627. }
  1628. }
  1629. :deep(.t-image__loading) {
  1630. .t-space-item:last-child {
  1631. display: none;
  1632. }
  1633. }
  1634. .graphic {
  1635. position: relative;
  1636. padding: 10px 0;
  1637. border-radius: 2px;
  1638. border: 1px solid transparent;
  1639. &:hover {
  1640. cursor: pointer;
  1641. border-color: var(--color-primary);
  1642. }
  1643. p {
  1644. margin-top: 10px;
  1645. padding: 0 8px;
  1646. text-align: center;
  1647. font-size: 12px;
  1648. height: 12px;
  1649. line-height: 1;
  1650. overflow: hidden;
  1651. text-overflow: ellipsis;
  1652. display: -webkit-box;
  1653. -webkit-line-clamp: 1;
  1654. word-break: break-all;
  1655. -webkit-box-orient: vertical;
  1656. }
  1657. .t-image__wrapper {
  1658. height: 32px;
  1659. width: 32px;
  1660. margin: auto;
  1661. :deep(.t-image) {
  1662. border-radius: 2px;
  1663. }
  1664. }
  1665. svg {
  1666. color: var(--color);
  1667. height: 32px;
  1668. width: 100%;
  1669. margin: auto;
  1670. }
  1671. .svg-box {
  1672. height: 32px;
  1673. width: 32px;
  1674. margin-left: calc(50% - 16px);
  1675. margin-top: 10px;
  1676. margin-bottom: 10px;
  1677. &:deep(svg) {
  1678. height: 100%;
  1679. width: 100%;
  1680. .cls-1 {
  1681. stroke: var(--color) !important;
  1682. stroke-width: 4px;
  1683. fill: none;
  1684. stroke-dasharray: none;
  1685. opacity: 1;
  1686. }
  1687. }
  1688. }
  1689. .price {
  1690. position: absolute;
  1691. top: 8px;
  1692. right: 8px;
  1693. display: inline-block;
  1694. z-index: 1;
  1695. border-radius: 2px;
  1696. background-color: #ff400060;
  1697. color: var(--color-bland);
  1698. font-size: 10px;
  1699. line-height: 1;
  1700. padding: 3px;
  1701. }
  1702. }
  1703. }
  1704. .two-columns {
  1705. :deep(.t-collapse-panel__content) {
  1706. grid-template-columns: 1fr 1fr;
  1707. }
  1708. .graphic {
  1709. .t-image__wrapper {
  1710. width: 100px;
  1711. height: 56.25px;
  1712. background-color: var(--color-background);
  1713. }
  1714. }
  1715. }
  1716. }
  1717. .context-menu-box {
  1718. position: fixed;
  1719. z-index: 200;
  1720. & > div {
  1721. width: 140px !important;
  1722. position: static;
  1723. }
  1724. :deep(.t-menu) {
  1725. .t-menu__item {
  1726. &.t-is-opened {
  1727. background-color: var(--color-background-popup-hover);
  1728. transition: none !important;
  1729. }
  1730. }
  1731. .t-fake-arrow {
  1732. transform: rotate(-90deg) !important;
  1733. }
  1734. .t-fake-arrow--active {
  1735. transform: rotate(90deg) !important;
  1736. }
  1737. }
  1738. }
  1739. }
  1740. </style>