Graphics.vue 64 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465
  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. <template v-if="group.type === 'iconfont'">
  21. <i class="l-icon" :class="group.icon"> </i>
  22. {{ group.name }}
  23. </template>
  24. <template v-else>
  25. <!-- <t-icon :name="group.icon" /> -->
  26. <DesktopIcon v-if="group.icon==='desktop'" />
  27. <RootListIcon v-else-if="group.icon==='root-list'"/>
  28. <ImageIcon v-else-if="group.icon==='image'"/>
  29. <ControlPlatformIcon v-else-if="group.icon==='control-platform'"/>
  30. <ChartIcon v-else-if="group.icon==='chart'"/>
  31. <RelativityIcon v-else-if="group.icon==='relativity'"/>
  32. <ChartBubbleIcon v-else-if="group.icon==='chart-bubble'"/>
  33. {{ group.name }}
  34. </template>
  35. </div>
  36. </div>
  37. <div class="list" :class="groupType ? 'two-columns' : ''">
  38. <div class="input-search">
  39. <div class="btn">
  40. <!-- <t-icon name="search" /> -->
  41. <img src="/img/icon_search_gray.svg" />
  42. </div>
  43. <t-input
  44. v-model="search"
  45. @change="onSearch"
  46. @enter="onSearch"
  47. placeholder="搜索"
  48. />
  49. <div class="ml-16">
  50. <t-tooltip content="展开/折叠">
  51. <menu-fold-icon class="hover"
  52. style="font-size: 16px"
  53. @click="onFold"/>
  54. <!-- <t-icon
  55. name="menu-fold"
  56. class="hover"
  57. style="font-size: 16px"
  58. @click="onFold"
  59. /> -->
  60. </t-tooltip>
  61. </div>
  62. </div>
  63. <div v-if="loading" class="center mt-16">
  64. <t-loading text="加载中..." size="small" :indicator="false" />
  65. </div>
  66. <template v-else>
  67. <div
  68. v-if="
  69. activedGroup === '图片' ||
  70. (activeAssets === 'user' &&
  71. (activedGroup === '方案' || activedGroup === '模板' || activedGroup === '组件'))
  72. "
  73. class="px-16 mt-12 mb-8 ml-4"
  74. >
  75. <a @click="onCreateFolder">+ 新建文件夹</a>
  76. </div>
  77. <t-collapse v-model="activedPanels[activedGroup]">
  78. <t-collapse-panel
  79. :value="item.name"
  80. v-for="item in subGroups"
  81. :key="item.name"
  82. >
  83. <template #header>
  84. <div class="flex middle">
  85. <div v-if="item.edited" @click.stop>
  86. <t-input
  87. v-model="item.label"
  88. style="width: 140px"
  89. @blur="createFoder"
  90. @enter="createFoder"
  91. @keyup="onKeyHeader"
  92. :autofocus="true"
  93. />
  94. </div>
  95. <div v-else>
  96. {{ item.name }}
  97. </div>
  98. </div>
  99. </template>
  100. <template #headerRightContent>
  101. <t-space size="small" @click.stop tabindex="0">
  102. <t-upload
  103. v-if="item.canEdited && activedGroup === '图片'"
  104. action="/api/image/upload"
  105. accept="image/*"
  106. :headers="headers"
  107. :data="{
  108. directory: `/大屏/${activedGroup}/${item.name}`,
  109. conflict:'new'
  110. }"
  111. :auto-upload="true"
  112. :upload-all-files-in-one-request="false"
  113. :before-upload="beforeUpload"
  114. @selectChange="onSelectFiles(item)"
  115. allowUploadDuplicateFile
  116. @success="fileSuccessed"
  117. theme="custom"
  118. >
  119. <image-icon class="hover" />
  120. <!-- <t-icon name="image" class="hover" /> -->
  121. </t-upload>
  122. <template v-if="item.canEdited">
  123. <add-icon v-if="['组件', '方案', '模板'].includes(activedGroup)" class="hover"
  124. @click="onAdd(item)"/>
  125. <!-- <t-icon
  126. v-if="['组件', '方案', '模板'].includes(activedGroup)"
  127. name="add"
  128. class="hover"
  129. @click="onAdd(item)"
  130. /> -->
  131. <edit-icon class="hover"
  132. @click="onEditHeader(item)"/>
  133. <!-- <t-icon
  134. name="edit"
  135. class="hover"
  136. @click="onEditHeader(item)"
  137. /> -->
  138. <t-popconfirm
  139. content="确认删除该文件夹吗"
  140. placement="left"
  141. @confirm="delFolder(item)"
  142. >
  143. <delete-icon class="hover"/>
  144. <!-- <t-icon name="delete" class="hover" /> -->
  145. </t-popconfirm>
  146. </template>
  147. </t-space>
  148. </template>
  149. <template v-for="elem in item.list">
  150. <div
  151. v-show="elem.visible !== false"
  152. class="graphic"
  153. :draggable="true"
  154. @dragstart="dragStart($event, elem)"
  155. @click.stop="dragStart($event, elem)"
  156. @dblclick.stop="open(elem)"
  157. @contextmenu="onContextMenu($event, item, elem)"
  158. @touchstart.passive="dragStart($event, elem)"
  159. >
  160. <!-- img 路径这样拼凑避免更新后路径一致图片使用缓存不更新 -->
  161. <t-image
  162. v-if="!elem.svg && elem.image"
  163. :src="elem.image + '?' + Math.random()"
  164. :lazy="true"
  165. fit="contain"
  166. @load="loadImage(elem)"
  167. />
  168. <div class="svg-box" v-else-if="elem.svg" v-html="elem.svg" />
  169. <svg v-else class="l-icon" aria-hidden="true">
  170. <use :xlink:href="'#' + elem.icon"></use>
  171. </svg>
  172. <p :title="elem.name">{{ elem.name }}</p>
  173. <div class="price" v-if="elem.price > 0">
  174. ¥{{ elem.price }}
  175. </div>
  176. </div>
  177. </template>
  178. <div
  179. v-if="!item.list || !item.list.length"
  180. class="gray"
  181. style="white-space: nowrap; margin-left: 34px"
  182. >
  183. 暂无数据
  184. </div>
  185. </t-collapse-panel>
  186. </t-collapse>
  187. </template>
  188. <div class="div-manage" v-if="['图元', '素材'].includes(activedGroup)">
  189. <t-button
  190. class="w-full"
  191. @click="onManageGraphic"
  192. style="height: 30px"
  193. >
  194. {{ activedGroup }}管理
  195. </t-button>
  196. </div>
  197. </div>
  198. </div>
  199. <div
  200. class="context-menu-box"
  201. ref="contextmenuDom"
  202. v-show="contextmenu.visible"
  203. tabindex="0"
  204. :style="contextmenu.style"
  205. @blur="hideContextmenu"
  206. >
  207. <t-menu
  208. class="context-menu"
  209. @change="onMenu"
  210. expandType="popup"
  211. v-model="contextmenu.activeValue"
  212. >
  213. <t-submenu
  214. value="move"
  215. title="移动到"
  216. v-if="contextmenu.subMenus.length"
  217. :disabled="contextmenu.component['3d']"
  218. >
  219. <t-menu-item
  220. v-for="subMenu in contextmenu.subMenus"
  221. :value="'move:' + subMenu.name"
  222. >
  223. {{ subMenu.name }}
  224. </t-menu-item>
  225. </t-submenu>
  226. <t-menu-item value="edit" :disabled="activedGroup === '图片'">
  227. 编辑
  228. </t-menu-item>
  229. <t-menu-item value="del" :disabled="contextmenu.component['3d']">
  230. 删除
  231. </t-menu-item>
  232. </t-menu>
  233. </div>
  234. <t-dialog
  235. v-if="delDialog.show"
  236. theme="danger"
  237. header="删除"
  238. :visible="true"
  239. @close="delDialog.show = false"
  240. @confirm="delComponent"
  241. >
  242. 确定删除该数据吗?删除后不可恢复!
  243. </t-dialog>
  244. <!-- <t-dialog
  245. v-if="chargeDialog.show"
  246. :header="chargeDialog.data.name"
  247. :visible="true"
  248. @close="chargeDialog.show = false"
  249. width="70%"
  250. :top="8"
  251. >
  252. <t-image :src="chargeDialog.data.image" />
  253. <template #footer>
  254. <div class="flex between" style="margin-top: -7px">
  255. <p>付费项目,购买后查看并使用</p>
  256. <div>
  257. <label>金额:</label>
  258. <label class="bland">¥{{ chargeDialog.data.price }}</label>
  259. <t-button class="primary ml-12" @click="onSubmitOrder"
  260. >购买</t-button
  261. >
  262. </div>
  263. </div>
  264. </template>
  265. </t-dialog> -->
  266. <t-dialog
  267. class="dialog-charge"
  268. v-if="chargeDialog.show"
  269. :header="chargeDialog.name"
  270. :visible="true"
  271. @close="chargeDialog.show = false"
  272. width="80%"
  273. :top="8"
  274. :footer="false"
  275. :closeOnOverlayClick="false"
  276. >
  277. <iframe
  278. :src="`/enterprise/preview?product=v&id=${chargeDialog.data.id}`"
  279. frameborder="no"
  280. scrolling="no"
  281. allowtransparency="true"
  282. />
  283. </t-dialog>
  284. <t-dialog
  285. v-if="wechatPayDialog.show"
  286. v-model:visible="wechatPayDialog.show"
  287. class="pay-dialog"
  288. header="乐吾乐收银台"
  289. :close-on-overlay-click="false"
  290. :top="95"
  291. :width="700"
  292. confirm-btn="支付完成"
  293. :cancel-btn="null"
  294. @close="getPayResult"
  295. :footer="false"
  296. >
  297. <Pay
  298. :order="wechatPayDialog.order"
  299. :alipay-url="wechatPayDialog.order.alipayUrl"
  300. :code-url="wechatPayDialog.order.codeUrl"
  301. @success="onChargeSuccess"
  302. />
  303. </t-dialog>
  304. <t-dialog
  305. class="dialog-manage"
  306. :header="activedGroup + '管理'"
  307. :visible="manageDialog.show"
  308. @close="manageDialog.show = false"
  309. @confirm="manageConfirm"
  310. width="90%"
  311. :top="8"
  312. >
  313. <div class="manage-body">
  314. <t-collapse v-model="activedPanels[activedGroup]">
  315. <t-collapse-panel
  316. :value="item.name"
  317. v-for="item in manageDialog.data"
  318. :key="item.name"
  319. >
  320. <template #header>
  321. <div class="flex middle">
  322. {{ item.name }}
  323. </div>
  324. </template>
  325. <template v-for="elem in item.list">
  326. <div
  327. :style="{
  328. background: elem.visible ? '#42516c' : '',
  329. }"
  330. class="graphic manage-list"
  331. >
  332. <t-checkbox v-model:checked="elem.visible"></t-checkbox>
  333. <t-image
  334. :src="elem.image"
  335. :lazy="true"
  336. fit="contain"
  337. @load="loadImage(elem)"
  338. />
  339. </div>
  340. </template>
  341. <div
  342. v-if="!item.list || !item.list.length"
  343. class="gray"
  344. style="white-space: nowrap; margin-left: 34px"
  345. >
  346. 暂无数据
  347. </div>
  348. </t-collapse-panel>
  349. </t-collapse>
  350. </div>
  351. </t-dialog>
  352. </div>
  353. </template>
  354. <script lang="ts" setup>
  355. import { onMounted, onUnmounted, reactive, ref } from 'vue';
  356. import { useRouter } from 'vue-router';
  357. import { MessagePlugin } from 'tdesign-vue-next';
  358. import axios from 'axios';
  359. import { deepClone } from '@meta2d/core';
  360. import { cases, shapes, formComponents, templates } from '@/services/defaults';
  361. import { charts } from '@/services/echarts';
  362. import { getFolders, makeSvg } from '@/services/png';
  363. import {
  364. addCollection,
  365. delImage,
  366. getComponentsList,
  367. getLe5leV,
  368. updateCollection,
  369. getCollectionList,
  370. imageDrive,
  371. } from '@/services/api';
  372. import { convertPen } from '@/services/upgrade';
  373. import { isGif } from '@/services/utils';
  374. import { autoSave, delAttrs, blank, useFolder } from '@/services/common';
  375. import { debounce, throttle } from '@/services/debouce';
  376. import { searchObjectPinyin } from '@/services/pinyin';
  377. import { getCookie } from '@/services/cookie';
  378. import { parseSvg } from '@meta2d/svg';
  379. import Pay from './Pay.vue';
  380. import { filename } from '@/services/file';
  381. import { useUser } from '@/services/user';
  382. import { iframeCustom } from '@/services/defaults';
  383. import { getLe5le3d } from '@/services/api';
  384. import { useSelection } from '@/services/selections';
  385. import localforage from 'localforage';
  386. import { MenuFoldIcon, ImageIcon, AddIcon, EditIcon, DeleteIcon, DesktopIcon, RootListIcon, ControlPlatformIcon, ChartIcon, RelativityIcon, ChartBubbleIcon } from 'tdesign-icons-vue-next';
  387. const { user } = useUser();
  388. const { setFolder, getFolder } = useFolder();
  389. const router = useRouter();
  390. const { select } = useSelection();
  391. const activedGroup = ref('');
  392. const activeAssets = ref('system');
  393. let groups = reactive([]);
  394. const systemGroups = [
  395. {
  396. icon: 'desktop',
  397. name: '方案',
  398. key: '',
  399. class: 'tow',
  400. },
  401. {
  402. icon: 'root-list',
  403. name: '模板',
  404. key: '',
  405. },
  406. {
  407. icon: 'l-zujian',
  408. name: '组件',
  409. key: 'chart',
  410. type: 'iconfont',
  411. },
  412. {
  413. icon: 'chart',
  414. name: '图表',
  415. key: 'chart',
  416. },
  417. {
  418. icon: 'image',
  419. name: '素材',
  420. key: '',
  421. },
  422. {
  423. icon: 'control-platform',
  424. name: '图元',
  425. key: '',
  426. },
  427. {
  428. icon: 'relativity',
  429. name: '控件',
  430. key: '',
  431. },
  432. {
  433. icon: 'chart-bubble',
  434. name: '图形',
  435. key: 'shape',
  436. },
  437. // {
  438. // icon: 'app',
  439. // name: '我的',
  440. // key: '',
  441. // },
  442. ];
  443. const userGroups = [
  444. {
  445. icon: 'desktop',
  446. name: '方案',
  447. key: '',
  448. class: 'tow',
  449. },
  450. {
  451. icon: 'root-list',
  452. name: '模板',
  453. key: '',
  454. },
  455. {
  456. icon: 'l-zujian',
  457. name: '组件',
  458. key: 'chart',
  459. type: 'iconfont',
  460. },
  461. {
  462. icon: 'image',
  463. name: '图片',
  464. key: '',
  465. },
  466. {
  467. icon: 'control-platform',
  468. name: '3D',
  469. key: '',
  470. },
  471. ];
  472. groups = systemGroups;
  473. const subGroups = ref<any[]>([]);
  474. const groupType = ref(0);
  475. const activedPanels = reactive<any>({});
  476. const caseCaches = [];
  477. const templateCaches = [];
  478. const componentCaches = [];
  479. const materials = [];
  480. const pngs = [];
  481. const components = [
  482. {
  483. name:"默认",
  484. list:[]
  485. }
  486. ];
  487. let dropped = false;
  488. const chargeDialog = reactive<any>({});
  489. const wechatPayDialog = reactive<any>({
  490. show: false,
  491. });
  492. const manageDialog = reactive<any>({
  493. name: '',
  494. data: [],
  495. show: false,
  496. });
  497. const search = ref('');
  498. const loading = ref(false);
  499. const headers = {
  500. Authorization: 'Bearer ' + (getCookie('token') || ''),
  501. };
  502. const updataData = { directory: '/大屏/默认' };
  503. let lastName = '方案';
  504. let userLastName = '方案';
  505. const assetsChange = (value) => {
  506. if (value === 'system') {
  507. groups = systemGroups;
  508. activedGroup.value = lastName;
  509. } else if (value === 'user') {
  510. groups = userGroups;
  511. activedGroup.value = userLastName;
  512. }
  513. groupChange(activedGroup.value);
  514. };
  515. const groupChange = async (name: string) => {
  516. activedGroup.value = name;
  517. groupType.value = 0;
  518. switch (name) {
  519. case '方案':
  520. if (activeAssets.value === 'system') {
  521. if (!caseCaches.length) {
  522. loading.value = true;
  523. caseCaches.push(...(await getCaseProjects('系统方案',1)));
  524. for (const group of cases) {
  525. group.list = [];
  526. for (const item of caseCaches) {
  527. if (item.case === group.name) {
  528. group.list.push(item);
  529. }
  530. }
  531. }
  532. loading.value = false;
  533. }
  534. groupType.value = 1;
  535. subGroups.value = cases;
  536. lastName = name;
  537. } else {
  538. // subGroups.value = await getUserProjects('le5leV');
  539. // groupType.value = 1;
  540. // await getPrivateProjects('le5leV');
  541. // userLastName = name;
  542. //用户方案
  543. subGroups.value = await getCollectionImageList('方案', 'v',1);
  544. groupType.value = 1;
  545. userLastName = name;
  546. }
  547. break;
  548. case '模板':
  549. if (activeAssets.value === 'system') {
  550. if (!templateCaches.length) {
  551. loading.value = true;
  552. templateCaches.push(...(await getCaseProjects('系统模板',2)));
  553. for (const group of templates) {
  554. group.list = [];
  555. for (const item of templateCaches) {
  556. if (item.case === group.name) {
  557. group.list.push(item);
  558. }
  559. }
  560. }
  561. loading.value = false;
  562. }
  563. groupType.value = 1;
  564. subGroups.value = templates;
  565. lastName = name;
  566. } else {
  567. // subGroups.value = await getUserProjects('le5leV-template');
  568. // groupType.value = 1;
  569. // await getPrivateProjects('le5leV-template');
  570. // userLastName = name;
  571. subGroups.value = await getCollectionImageList('模板', 'v',2);
  572. groupType.value = 1;
  573. userLastName = name;
  574. }
  575. break;
  576. case '图表':
  577. subGroups.value = charts;
  578. lastName = name;
  579. break;
  580. case '控件':
  581. subGroups.value = formComponents;
  582. lastName = name;
  583. break;
  584. case '素材':
  585. groupType.value = 1;
  586. if (!materials.length) {
  587. loading.value = true;
  588. materials.push(...(await getFolders('v/material')));
  589. loading.value = false;
  590. const _materials: string = await localforage.getItem(
  591. 'le5leV-materials'
  592. );
  593. if (_materials) {
  594. Object.assign(materials, JSON.parse(_materials));
  595. }
  596. }
  597. subGroups.value = materials;
  598. lastName = name;
  599. break;
  600. case '图元':
  601. if (!pngs.length) {
  602. loading.value = true;
  603. pngs.push(...(await getFolders('png')));
  604. pngs.push(...(await getFolders('svg', true)));
  605. loading.value = false;
  606. const _pngs: string = await localforage.getItem('le5leV-pngs');
  607. if (_pngs) {
  608. Object.assign(pngs, JSON.parse(_pngs));
  609. }
  610. }
  611. subGroups.value = pngs;
  612. lastName = name;
  613. break;
  614. case '图形':
  615. subGroups.value = shapes;
  616. lastName = name;
  617. break;
  618. case '组件':
  619. if (activeAssets.value === 'system') {
  620. if (!componentCaches.length) {
  621. loading.value = true;
  622. componentCaches.push(...(await getCaseProjects('系统组件',1)));
  623. loading.value = false;
  624. for (const component of componentCaches) {
  625. if(component.case){
  626. let group = components.filter((item)=>{item.name===component.case});
  627. if(group&&group.length){
  628. group[0].list.push(component);
  629. }else{
  630. components.push({
  631. name:component.case,
  632. list:[component]
  633. })
  634. }
  635. }else{
  636. components[0].list.push(component);
  637. }
  638. }
  639. }
  640. groupType.value = 1;
  641. subGroups.value = components;
  642. lastName = name;
  643. } else {
  644. // subGroups.value = await getUserComponents();
  645. // groupType.value = 1;
  646. // await getPrivateGraphics();
  647. // userLastName = name;
  648. subGroups.value = await getCollectionImageList(
  649. '组件',
  650. 'v.component',
  651. 1
  652. );
  653. groupType.value = 1;
  654. userLastName = name;
  655. }
  656. break;
  657. case '图片':
  658. loading.value = true;
  659. subGroups.value = await getImageList();
  660. loading.value = false;
  661. userLastName = name;
  662. break;
  663. case '3D':
  664. subGroups.value = [
  665. {
  666. name: '3D',
  667. list: [],
  668. },
  669. ];
  670. groupType.value = 1;
  671. await getPrivateGraphics();
  672. userLastName = name;
  673. break;
  674. }
  675. // if (!activedPanels[name]) {
  676. activedPanels[name] = [];
  677. for (const item of subGroups.value) {
  678. activedPanels[name].push(item.name);
  679. }
  680. // }
  681. // searchGraphics();
  682. };
  683. // TODO 获取方案文件
  684. //获取方案文件夹
  685. const getCollectionImageList = async (name?: string, collection?: string, userFlag?:number) => {
  686. //1. 获取网盘文件夹
  687. const fullpath = `/大屏/${name}`;
  688. let ret: { list: any[] } = await axios.post('/api/directory/list', {
  689. fullpath,
  690. });
  691. if (!ret) {
  692. return [];
  693. }
  694. let list = [];
  695. for (let i of ret.list) {
  696. if (
  697. i.fullpath !== `${fullpath}/默认` &&
  698. i.fullpath.split('/').length === 4
  699. ) {
  700. //不取当前文件夹
  701. list.push(i);
  702. }
  703. }
  704. const data = {
  705. // directory: `/大屏/${name}`,
  706. // query: {
  707. // // folder: '',
  708. // tags: name !== '组件' ? name : undefined,
  709. // },
  710. userFlag,
  711. // projection: {
  712. // image: 1,
  713. // id: 1,
  714. // name: 1,
  715. // tags: 1,
  716. // folder: 1,
  717. // component: 1,
  718. // filename: 1,
  719. // },
  720. };
  721. const config = {
  722. params: {
  723. current: 1,
  724. pageSize: 1000,
  725. },
  726. };
  727. //2.请求所有图纸/组件数据
  728. const res: any = await getCollectionList(collection, data, config);
  729. //3.将数据对应到云盘文件夹
  730. const results = [];
  731. const resultsMap = {
  732. 默认: [],
  733. };
  734. for (const item of list) {
  735. let folder = item.fullpath.split('/')[3];
  736. if (!resultsMap[folder]) {
  737. resultsMap[folder] = [];
  738. }
  739. }
  740. for (const item of res.list) {
  741. if(collection === 'v.component') {
  742. item.component = true;
  743. }
  744. if (item.folder && resultsMap[item.folder]) {
  745. resultsMap[item.folder].push(item);
  746. } else {
  747. resultsMap['默认'].push(item);
  748. }
  749. }
  750. for (const item of list) {
  751. let folder = item.fullpath.split('/')[3];
  752. results.push({
  753. name: folder,
  754. canEdited: true,
  755. id: item.id,
  756. list: resultsMap[folder],
  757. });
  758. }
  759. results.push({
  760. name: '默认',
  761. list: resultsMap['默认'],
  762. });
  763. return results;
  764. };
  765. const getImageList = async () => {
  766. let ret: { list: any[] } = await axios.post('/api/directory/list', {
  767. fullpath: '/大屏/图片',
  768. });
  769. if (!ret) {
  770. return [];
  771. }
  772. let list = [];
  773. for (let i of ret.list) {
  774. if (i.fullpath.split('/').length === 4) {
  775. //不取当前文件夹
  776. list.push(i);
  777. }
  778. }
  779. return await Promise.all(
  780. list.map(async (item) => {
  781. let secondDir = item.fullpath.split('/');
  782. const _ret: { list: any[]; total: number } = await axios.post(
  783. '/api/file/list',
  784. {
  785. type: '图片',
  786. directory: item.fullpath,
  787. },
  788. {
  789. params: {
  790. current: 1,
  791. pageSize: 100,
  792. },
  793. }
  794. );
  795. let list = _ret.list.map((_item) => {
  796. return {
  797. ..._item,
  798. image: _item.url || `/file${_item.filename}`,
  799. visible: true,
  800. folder: item.fullpath,
  801. id: _item.id || _item._id,
  802. };
  803. });
  804. return {
  805. name: secondDir[3],
  806. id: item.id || item._id,
  807. canEdited: secondDir[3] !== '默认',
  808. list: list,
  809. };
  810. })
  811. );
  812. };
  813. const getCaseProjects = async (name: string,systemFlag = 1, current = 1, pageSize = 1000) => {
  814. const query: any = { tags: name };
  815. let collection = name == '系统组件' ? 'v.component' : 'v';
  816. const ret: any = await axios.post(
  817. `/api/data/${collection}/list`,
  818. {
  819. // query: {
  820. // tags: "系统方案"
  821. // },
  822. //shared: true,
  823. // projection: "id,_id,name,image,price,case",
  824. // sort: { createdAt: 1 },
  825. systemFlag
  826. },
  827. {
  828. params: {
  829. current,
  830. pageSize,
  831. },
  832. }
  833. );
  834. if (!ret) {
  835. return [];
  836. }
  837. for (const item of ret.list) {
  838. if (!item.id) {
  839. item.id = item._id;
  840. }
  841. if(name !== '系统组件'){
  842. item.draggable = false;
  843. }else{
  844. item.component = true;
  845. }
  846. }
  847. return ret.list;
  848. };
  849. // const getUserProjects = async (name: string, current = 1, pageSize = 1000) => {
  850. // const query: any = { tags: name };
  851. // const ret: any = await axios.post(
  852. // '/api/data/le5leV/list',
  853. // {
  854. // query,
  855. // projection: {
  856. // id: 1,
  857. // _id: 1,
  858. // name: 1,
  859. // image: 1,
  860. // price: 1,
  861. // case: 1,
  862. // folder: 1,
  863. // },
  864. // sort: { createdAt: 1 },
  865. // },
  866. // {
  867. // params: {
  868. // current,
  869. // pageSize,
  870. // },
  871. // }
  872. // );
  873. // if (!ret) {
  874. // return [];
  875. // }
  876. // for (const item of ret.list) {
  877. // if (!item.id) {
  878. // item.id = item._id;
  879. // }
  880. // item.draggable = false;
  881. // }
  882. // return ret.list;
  883. // };
  884. const getPrivateGroups = async () => {
  885. const list = [
  886. {
  887. name: '默认',
  888. list: [],
  889. },
  890. ];
  891. const config = {
  892. params: {
  893. current: 1,
  894. pageSize: 1000,
  895. },
  896. };
  897. let ret: any = await axios.post(
  898. '/api/data/folders/list',
  899. {
  900. // projection: {
  901. // image: 1,
  902. // _id: 1,
  903. // name: 1,
  904. // list: 1,
  905. // },
  906. projection:'image,id,name,list',
  907. // query: {
  908. // type: `v-components`,
  909. // },
  910. sort: { createdAt: 1 },
  911. },
  912. config
  913. );
  914. if (!ret) {
  915. ret = { list: [] };
  916. }
  917. for (const item of ret.list) {
  918. item.canEdited = true;
  919. }
  920. list.push(...ret.list);
  921. list.push({
  922. name: '3D',
  923. list: [],
  924. });
  925. return list;
  926. };
  927. const getUserComponents = async () => {
  928. const list = [
  929. {
  930. name: '默认',
  931. list: [],
  932. },
  933. ];
  934. const config = {
  935. params: {
  936. current: 1,
  937. pageSize: 1000,
  938. },
  939. };
  940. let ret: any = await axios.post(
  941. '/api/data/folders/list',
  942. {
  943. // projection: {
  944. // image: 1,
  945. // _id: 1,
  946. // name: 1,
  947. // list: 1,
  948. // },
  949. projection:'image,id,name,list',
  950. // query: {
  951. // type: `v.component`,
  952. // },
  953. sort: { createdAt: 1 },
  954. },
  955. config
  956. );
  957. if (!ret) {
  958. ret = { list: [] };
  959. }
  960. for (const item of ret.list) {
  961. item.canEdited = true;
  962. }
  963. list.push(...ret.list);
  964. return list;
  965. };
  966. const getUserProjects = async (type: string) => {
  967. const list = [
  968. {
  969. name: '默认',
  970. list: [],
  971. },
  972. ];
  973. const config = {
  974. params: {
  975. current: 1,
  976. pageSize: 1000,
  977. },
  978. };
  979. let ret: any = await axios.post(
  980. '/api/data/folders/list',
  981. {
  982. // projection: {
  983. // image: 1,
  984. // _id: 1,
  985. // name: 1,
  986. // list: 1,
  987. // },
  988. projection:'image,id,name,list',
  989. // query: {
  990. // type,
  991. // },
  992. sort: { createdAt: 1 },
  993. },
  994. config
  995. );
  996. if (!ret) {
  997. ret = { list: [] };
  998. }
  999. for (const item of ret.list) {
  1000. item.canEdited = true;
  1001. }
  1002. list.push(...ret.list);
  1003. return list;
  1004. };
  1005. const getPrivateProjects = async (type: string) => {
  1006. for (const item of subGroups.value) {
  1007. if (!item.list.length) {
  1008. item.loading = true;
  1009. //TODO 方案/模板分类
  1010. if (item.name === '默认') {
  1011. const data = {
  1012. // query: {
  1013. // folder: '',
  1014. // tags: type === 'v-template' ? '模板' : '方案',
  1015. // },
  1016. // projection: {
  1017. // image: 1,
  1018. // _id: 1,
  1019. // name: 1,
  1020. // tags: 1,
  1021. // },
  1022. projection:'image,id,name,tags'
  1023. };
  1024. const config = {
  1025. params: {
  1026. current: 1,
  1027. pageSize: 1000,
  1028. },
  1029. };
  1030. const res: any = await getCollectionList('v', data, config);
  1031. if (res?.list) {
  1032. // res.list.forEach((item) => {
  1033. // item.draggable = false;
  1034. // })
  1035. item.list = res.list;
  1036. // if (type === 'le5leV') {
  1037. // //过滤模板
  1038. // // item.list = res.list.filter((item) => !item.isTemplate);
  1039. // }
  1040. }
  1041. }
  1042. item.loading = false;
  1043. }
  1044. }
  1045. };
  1046. const dragStart = async (event: DragEvent | MouseEvent|TouchEvent, item: any) => {
  1047. // event.stopPropagation();
  1048. if (!item) {
  1049. return;
  1050. }
  1051. meta2d.canvas.addCaches = [];
  1052. dropped = false;
  1053. let data = null;
  1054. const id = item.id || item._id;
  1055. let isAsync: boolean;
  1056. if (
  1057. activeAssets.value === 'user' &&
  1058. ['方案', '模板'].includes(activedGroup.value)
  1059. ) {
  1060. if (!item._id) {
  1061. item._id = item.id;
  1062. }
  1063. item.draggable = false;
  1064. data = item.data || item;
  1065. dropped =true;
  1066. } else if (item.draggable === false) {
  1067. //方案
  1068. data = item.data || item;
  1069. dropped =true;
  1070. } else if (item['3d']) {
  1071. const res: any = await getLe5le3d(item.id || item._id, {
  1072. image: 1,
  1073. _id: 1,
  1074. name: 1,
  1075. });
  1076. data = {
  1077. name: 'iframe',
  1078. x: 0,
  1079. y: 0,
  1080. tags: ['meta3d'],
  1081. zIndex: 1,
  1082. operationalRect: {
  1083. x: 0.2,
  1084. y: 0.2,
  1085. height: 0.8,
  1086. width: 0.6,
  1087. },
  1088. props: {
  1089. custom: iframeCustom,
  1090. },
  1091. width: meta2d.store.data.width || meta2d.store.options.width,
  1092. height: meta2d.store.data.height || meta2d.store.options.height,
  1093. externElement: true,
  1094. thumbImg: res.image,
  1095. iframe: location.origin+'/view/3d/?id=' + (item.id || item._id),
  1096. };
  1097. } else if (item.component) {
  1098. // 默认
  1099. if (!item.componentDatas && !item.componentData) {
  1100. isAsync = true;
  1101. const ret: any = await axios.post(`/api/data/v.component/get`, {
  1102. id,
  1103. });
  1104. item.componentDatas = ret.data.componentDatas;
  1105. item.componentData = ret.data.componentData;
  1106. }
  1107. if (item.componentData) {
  1108. const pens = convertPen([item.componentData]);
  1109. data = deepClone(pens);
  1110. } else if (item.componentDatas) {
  1111. data = deepClone(item.componentDatas);
  1112. }
  1113. } else if (item.data) {
  1114. // 普通图元
  1115. data = item.data;
  1116. } else if (item.image) {
  1117. // 拖拽图片
  1118. let target: any = event.target;
  1119. target.children[0] && (target = target.children[0].children[0]);
  1120. // firefox naturalWidth svg 图片 可能是 0
  1121. const width = target.naturalWidth || target.width;
  1122. const height = target.naturalHeight || target.height;
  1123. const [name, lockedOnCombine] = await isGif(item.image)
  1124. ? ['gif', 0]
  1125. : ['image', undefined];
  1126. data = {
  1127. name,
  1128. width,
  1129. height,
  1130. isBottom:true,
  1131. image: item.image,
  1132. imageRatio: true,
  1133. ratio: true,
  1134. lockedOnCombine,
  1135. };
  1136. } else {
  1137. return;
  1138. }
  1139. if (!Array.isArray(data)) {
  1140. data = deepClone([data]);
  1141. }
  1142. !dropped && (meta2d.canvas.addCaches = data);
  1143. if (event instanceof DragEvent) {
  1144. event.dataTransfer.setData(
  1145. 'Meta2d',
  1146. isAsync ? undefined : JSON.stringify(data)
  1147. );
  1148. }
  1149. };
  1150. const dragstart = (event: any) => {
  1151. event.target.style.opacity = 0.5;
  1152. };
  1153. const dragend = (event: any) => {
  1154. event.target.style.opacity = 1;
  1155. };
  1156. const open = async (item: any) => {
  1157. if (!item || item.draggable !== false) {
  1158. return;
  1159. }
  1160. if (activeAssets.value === 'user') {
  1161. router.push({
  1162. path: '/',
  1163. query: {
  1164. r: Date.now() + '',
  1165. id:item.id || item._id,
  1166. },
  1167. });
  1168. } else {
  1169. const ret: any = await getLe5leV(item._id || item.id);
  1170. // if (!ret) {
  1171. // if (item.price > 0) {
  1172. // chargeDialog.data = item;
  1173. // chargeDialog.show = true;
  1174. // }
  1175. // return;
  1176. // }
  1177. if(!ret.data&&ret.price>0){
  1178. chargeDialog.data = item;
  1179. chargeDialog.show = true;
  1180. return;
  1181. }
  1182. sessionStorage.setItem('opening', '1');
  1183. router.push({
  1184. path: '/',
  1185. query: {
  1186. r: Date.now() + '',
  1187. },
  1188. });
  1189. for (const k of delAttrs) {
  1190. delete ret[k];
  1191. }
  1192. autoSave();
  1193. meta2d.open(ret.data);
  1194. meta2d.fitView();
  1195. }
  1196. select();
  1197. };
  1198. const getPrivateGraphics = async () => {
  1199. for (const item of subGroups.value) {
  1200. if (!item.list.length) {
  1201. item.loading = true;
  1202. if (item.name === '默认') {
  1203. const data = {
  1204. // query: { folder: '' },
  1205. // projection: {
  1206. // image: 1,
  1207. // _id: 1,
  1208. // name: 1,
  1209. // component: 1,
  1210. // },
  1211. projection:'image,id,name,component'
  1212. };
  1213. const config = {
  1214. params: {
  1215. current: 1,
  1216. pageSize: 1000,
  1217. },
  1218. };
  1219. const res: any = await getComponentsList(data, config);
  1220. if (res?.list) {
  1221. item.list = res.list;
  1222. }
  1223. } else if (item.name === '3D') {
  1224. const data = {
  1225. // projection: {
  1226. // image: 1,
  1227. // _id: 1,
  1228. // name: 1,
  1229. // },
  1230. projection:"id, name, image"
  1231. };
  1232. const config = {
  1233. params: {
  1234. current: 1,
  1235. pageSize: 1000,
  1236. },
  1237. };
  1238. const res: any = await axios.post(
  1239. '/api/data/3d/list',
  1240. data,
  1241. config
  1242. );
  1243. if (res?.list) {
  1244. for (const item of res.list) {
  1245. item['3d'] = true;
  1246. item['draggable'] = true;
  1247. }
  1248. item.list = res.list;
  1249. }
  1250. }
  1251. item.loading = false;
  1252. }
  1253. }
  1254. };
  1255. const editedFolder = ref<any>(undefined);
  1256. const onCreateFolder = () => {
  1257. activedPanels[activedGroup.value].splice(
  1258. 0,
  1259. activedPanels[activedGroup.value].length
  1260. );
  1261. editedFolder.value = {
  1262. _id: '',
  1263. name: '',
  1264. label: '新建文件夹',
  1265. list: [],
  1266. edited: true,
  1267. canEdited: true,
  1268. };
  1269. subGroups.value.splice(subGroups.value.length - 1, 0, editedFolder.value);
  1270. };
  1271. const createFoder = async () => {
  1272. if (!editedFolder.value.label) {
  1273. return;
  1274. }
  1275. if (editedFolder.value.label === editedFolder.value.name) {
  1276. editedFolder.value.edited = false;
  1277. return;
  1278. }
  1279. const found = subGroups.value.findIndex(
  1280. (group: any) => group.name === editedFolder.value.label
  1281. );
  1282. if (found >= 0) {
  1283. MessagePlugin.error('已经存在相同名称文件夹');
  1284. return;
  1285. }
  1286. if (activeAssets.value !== 'user') {
  1287. return;
  1288. }
  1289. if (editedFolder.value.id || editedFolder.value._id) {
  1290. const ret: any = await axios.post('/api/directory/update', {
  1291. oldFullpath: `/大屏/${activedGroup.value}/` + editedFolder.value.oldLabel,
  1292. newFullpath: `/大屏/${activedGroup.value}/` + editedFolder.value.label,
  1293. });
  1294. if (ret) {
  1295. editedFolder.value.name = editedFolder.value.label;
  1296. editedFolder.value.edited = false;
  1297. }
  1298. //更新图纸的folder
  1299. if (['组件', '方案', '模板'].includes(activedGroup.value)) {
  1300. const collection =
  1301. activedGroup.value === '组件' ? 'v.component' : 'v';
  1302. editedFolder.value.list?.forEach(async (item) => {
  1303. item.folder = editedFolder.value.label;
  1304. await updateCollection(collection, {
  1305. id: item.id || item._id,
  1306. folder: editedFolder.value.label,
  1307. });
  1308. });
  1309. }
  1310. } else {
  1311. const ret: any = await axios.post('/api/directory/add', {
  1312. fullpath: `/大屏/${activedGroup.value}/` + editedFolder.value.label,
  1313. });
  1314. if (ret) {
  1315. editedFolder.value.name = editedFolder.value.label;
  1316. editedFolder.value.id = ret.id;
  1317. editedFolder.value.edited = false;
  1318. }
  1319. }
  1320. };
  1321. const _createFoder = async () => {
  1322. if (!editedFolder.value.label) {
  1323. return;
  1324. }
  1325. if (editedFolder.value.label === editedFolder.value.name) {
  1326. editedFolder.value.edited = false;
  1327. return;
  1328. }
  1329. const found = subGroups.value.findIndex(
  1330. (group: any) => group.name === editedFolder.value.label
  1331. );
  1332. if (found >= 0) {
  1333. MessagePlugin.error('已经存在相同名称文件夹');
  1334. return;
  1335. }
  1336. if (activeAssets.value !== 'user') {
  1337. return;
  1338. }
  1339. if (['组件', '方案', '模板'].includes(activedGroup.value)) {
  1340. if (editedFolder.value._id) {
  1341. const ret: any = await axios.post('/api/data/folders/update', {
  1342. _id: editedFolder.value._id,
  1343. name: editedFolder.value.label,
  1344. });
  1345. if (ret) {
  1346. editedFolder.value.name = editedFolder.value.label;
  1347. editedFolder.value.edited = false;
  1348. }
  1349. } else {
  1350. let type =
  1351. activedGroup.value === '组件'
  1352. ? 'v.component'
  1353. : activedGroup.value === '模板'
  1354. ? 'v-template'
  1355. : 'v';
  1356. const ret: any = await axios.post('/api/data/folders/add', {
  1357. name: editedFolder.value.label,
  1358. type,
  1359. list: [],
  1360. });
  1361. if (ret) {
  1362. editedFolder.value.name = editedFolder.value.label;
  1363. editedFolder.value._id = ret._id;
  1364. editedFolder.value.edited = false;
  1365. }
  1366. }
  1367. } else if (activedGroup.value === '图片') {
  1368. if (editedFolder.value._id) {
  1369. const ret: any = await axios.post('/api/directory/update', {
  1370. oldFullpath: '/大屏/' + editedFolder.value.oldLabel,
  1371. newFullpath: '/大屏/' + editedFolder.value.label,
  1372. });
  1373. if (ret) {
  1374. editedFolder.value.name = editedFolder.value.label;
  1375. editedFolder.value.edited = false;
  1376. }
  1377. } else {
  1378. const ret: any = await axios.post('/api/directory/add', {
  1379. fullpath: '/大屏/' + editedFolder.value.label,
  1380. });
  1381. if (ret) {
  1382. editedFolder.value.name = editedFolder.value.label;
  1383. editedFolder.value._id = ret.id || ret._id;
  1384. editedFolder.value.edited = false;
  1385. }
  1386. }
  1387. }
  1388. };
  1389. const onEditHeader = (item: any) => {
  1390. item.label = item.name;
  1391. item.edited = true;
  1392. item.oldLabel = item.name;
  1393. editedFolder.value = item;
  1394. };
  1395. const onAdd = (item: any) => {
  1396. blank();
  1397. if (activedGroup.value === '方案') {
  1398. item.vType = 'v';
  1399. } else if (activedGroup.value === '模板') {
  1400. item.vType = 'v-template';
  1401. } else if (activedGroup.value === '组件') {
  1402. item.vType = 'v.component';
  1403. }
  1404. setFolder(item);
  1405. const query: any = {
  1406. r: Date.now() + '',
  1407. folder: item.name,
  1408. tags: activedGroup.value,
  1409. };
  1410. if (activedGroup.value === '组件') {
  1411. query.c = 1;
  1412. }
  1413. router.push({
  1414. path: '/',
  1415. query,
  1416. });
  1417. activeAssets.value = 'system';
  1418. assetsChange('system');
  1419. // meta2d.open({
  1420. // name: '新建项目',
  1421. // pens: [],
  1422. // enableMock: true,
  1423. // tags: activedGroup.value,
  1424. // folder: item.name,
  1425. // } as any);
  1426. };
  1427. const onKeyHeader = (text: string, event: any) => {
  1428. if (event.e.key === 'Escape') {
  1429. editedFolder.value.edited = false;
  1430. }
  1431. };
  1432. // 默认右键菜单
  1433. const contextmenu = reactive<any>({
  1434. visible: false,
  1435. style: {},
  1436. // 子分类
  1437. group: undefined,
  1438. // 组件图纸
  1439. component: {},
  1440. // 右键二级子菜单
  1441. subMenus: [],
  1442. });
  1443. const contextmenuDom = ref<any>(null);
  1444. const onContextMenu = async (e: MouseEvent, group: any, item: any) => {
  1445. e.preventDefault();
  1446. e.stopPropagation();
  1447. if (activeAssets.value === 'system') {
  1448. return;
  1449. }
  1450. contextmenu.group = group;
  1451. contextmenu.component = item;
  1452. if (document.body.clientHeight - e.clientY < 128) {
  1453. contextmenu.style = {
  1454. left: e.clientX + 'px',
  1455. bottom: '4px',
  1456. };
  1457. } else {
  1458. contextmenu.style = {
  1459. left: e.clientX + 'px',
  1460. top: e.clientY + 'px',
  1461. };
  1462. }
  1463. contextmenu.subMenus = [];
  1464. for (const elem of subGroups.value) {
  1465. if (elem === group || elem.name === '默认' || elem.name === '3D') {
  1466. continue;
  1467. }
  1468. contextmenu.subMenus.push(elem);
  1469. }
  1470. contextmenu.visible = true;
  1471. setTimeout(() => {
  1472. if (contextmenuDom.value) {
  1473. contextmenuDom.value.focus();
  1474. }
  1475. }, 500);
  1476. };
  1477. const delDialog = reactive<any>({
  1478. contextmenuObj: {},
  1479. });
  1480. // TODO 右键菜单移动图纸
  1481. const onMenu = async (val: string) => {
  1482. const id = contextmenu.component.id || contextmenu.component._id;
  1483. setTimeout(() => {
  1484. contextmenu.group = '';
  1485. contextmenu.component = {};
  1486. contextmenu.subMenus = [];
  1487. }, 500);
  1488. switch (val) {
  1489. case 'edit':
  1490. if (
  1491. contextmenu.component.tags &&
  1492. contextmenu.component.tags.includes('svg')
  1493. ) {
  1494. MessagePlugin.warning('解析的svg图元不允许编辑!');
  1495. hideContextmenu();
  1496. contextmenu.activeValue = 0;
  1497. return;
  1498. }
  1499. // if (contextmenu.component.component) {
  1500. if (activedGroup.value == '组件') {
  1501. autoSave();
  1502. router.push({
  1503. path: '/',
  1504. query: {
  1505. id,
  1506. c: 1,
  1507. r: Date.now() + '',
  1508. },
  1509. });
  1510. } else if (contextmenu.component['3d']) {
  1511. let url = 'https://3d.le5le.com/?id=';
  1512. if (window.url3D) {
  1513. url = window.url3D;
  1514. }
  1515. window.open(url + id, '_blank');
  1516. } else {
  1517. if ((contextmenu.group.id||contextmenu.group._id) && contextmenu.group.name !== '默认') {
  1518. setFolder(contextmenu.group);
  1519. }
  1520. autoSave();
  1521. //文件夹
  1522. router.push({
  1523. path: '/',
  1524. query: {
  1525. id,
  1526. r: Date.now() + '',
  1527. },
  1528. });
  1529. }
  1530. hideContextmenu();
  1531. break;
  1532. case 'del':
  1533. delDialog.show = true;
  1534. Object.assign(delDialog.contextmenuObj, contextmenu);
  1535. break;
  1536. default:
  1537. if (val.indexOf('move:')) {
  1538. return;
  1539. }
  1540. val = val.replace('move:', '');
  1541. const group = contextmenu.subMenus.find(
  1542. (element: any) => element.name === val
  1543. );
  1544. if (!group) {
  1545. return;
  1546. }
  1547. // 前端: 添加组件到目标文件夹
  1548. let data: any;
  1549. if (contextmenu.component.component) {
  1550. data = {
  1551. component: true,
  1552. image: contextmenu.component.image,
  1553. name: contextmenu.component.name,
  1554. visible: true,
  1555. _id: contextmenu.component._id || contextmenu.component.id,
  1556. filename: contextmenu.component.filename,
  1557. };
  1558. } else {
  1559. data = {
  1560. image: contextmenu.component.image,
  1561. name: contextmenu.component.name,
  1562. tags: contextmenu.component.tags,
  1563. visible: true,
  1564. _id: contextmenu.component._id || contextmenu.component.id,
  1565. filename: contextmenu.component.filename,
  1566. };
  1567. }
  1568. group.list.push(data);
  1569. // 前端:从源文件夹移出组件
  1570. contextmenu.group.list.forEach((item: any, index: number, arr: any[]) => {
  1571. if (id === item._id || id === item.id) {
  1572. arr.splice(index, 1);
  1573. }
  1574. });
  1575. if (activedGroup.value !== '图片') {
  1576. let collection = activedGroup.value === '组件'
  1577. ? 'v.component'
  1578. : 'v';
  1579. // 更新后端组件信息
  1580. let ret = await updateCollection(collection, {
  1581. id,
  1582. folder: val === '默认' ? '' : val,
  1583. });
  1584. if (!ret) {
  1585. return;
  1586. }
  1587. }
  1588. // // 更新后端源文件夹列表
  1589. // if (contextmenu.group.name !== '默认') {
  1590. // await axios.post('/api/data/folders/update', {
  1591. // _id: contextmenu.group._id || contextmenu.group.id,
  1592. // list: contextmenu.group.list,
  1593. // });
  1594. // }
  1595. // 更新后端目标文件夹列表
  1596. // if (group.name !== '默认') {
  1597. // await axios.post('/api/data/folders/update', {
  1598. // _id: group._id || group.id,
  1599. // list: group.list,
  1600. // });
  1601. // }
  1602. // let filename: string =
  1603. // contextmenu.component.filename || contextmenu.component.image;
  1604. // if (imageDrive && filename.startsWith(imageDrive)) {
  1605. // filename = filename.slice(imageDrive.length);
  1606. // }
  1607. let path: string =
  1608. contextmenu.component.filename || contextmenu.component.image;
  1609. //更新缩略图位置
  1610. // await axios.post('/api/files/update', {
  1611. // filenames: [filename],
  1612. // 'metadata.directory': `/大屏/${activedGroup.value}/${val || '默认'}`,
  1613. // });
  1614. // 原接口路径是 /api/file/{fullname}
  1615. // 因为img的路径中包含file,所以做如下拼接
  1616. if (imageDrive && path.startsWith(imageDrive)) {
  1617. path = path.slice(imageDrive.length);
  1618. }
  1619. if(!path.startsWith('/api/file')) {
  1620. path = '/api/file' + path;
  1621. }
  1622. await axios.patch(`${path}`, {
  1623. // filenames: [filename],
  1624. directory: `/大屏/${activedGroup.value}/${val || '默认'}`,
  1625. });
  1626. break;
  1627. }
  1628. contextmenu.activeValue = 0;
  1629. };
  1630. const hideContextmenu = () => {
  1631. contextmenu.visible = false;
  1632. };
  1633. const _delComponent = async () => {
  1634. // const id = contextmenu.component._id || contextmenu.component.id;
  1635. if (['组件', '方案', '模板'].includes(activedGroup.value)) {
  1636. let collection = delDialog.contextmenuObj.component.component
  1637. ? 'v.component'
  1638. : 'v';
  1639. const id =
  1640. delDialog.contextmenuObj.component._id ||
  1641. delDialog.contextmenuObj.component.id;
  1642. try {
  1643. await axios.post(`/api/data/${collection}/delete`, {
  1644. id,
  1645. });
  1646. } catch (e) {
  1647. console.error(e);
  1648. return;
  1649. }
  1650. //删除缩略图
  1651. if (delDialog.contextmenuObj.component.image) {
  1652. await delImage(delDialog.contextmenuObj.component.image);
  1653. }
  1654. // 前端:从源文件夹移出组件
  1655. delDialog.contextmenuObj.group.list.forEach(
  1656. (item: any, index: number, arr: any[]) => {
  1657. if (id === item._id || id === item.id) {
  1658. arr.splice(index, 1);
  1659. }
  1660. }
  1661. );
  1662. // 更新后端源文件夹列表
  1663. if (delDialog.contextmenuObj.group.name !== '默认') {
  1664. await axios.post('/api/data/folders/update', {
  1665. _id:
  1666. delDialog.contextmenuObj.group._id ||
  1667. delDialog.contextmenuObj.group.id,
  1668. list: delDialog.contextmenuObj.group.list,
  1669. });
  1670. }
  1671. } else {
  1672. const id =
  1673. delDialog.contextmenuObj.component._id ||
  1674. delDialog.contextmenuObj.component.id;
  1675. delDialog.contextmenuObj.group.list.forEach(
  1676. (item: any, index: number, arr: any[]) => {
  1677. if (id && (id === item._id || id === item.id)) {
  1678. arr.splice(index, 1);
  1679. }
  1680. if (!id) {
  1681. if (item.url === delDialog.contextmenuObj.component.url) {
  1682. arr.splice(index, 1);
  1683. }
  1684. }
  1685. }
  1686. );
  1687. await axios.post(`/api/files/delete`, {
  1688. fullnames: [delDialog.contextmenuObj.component.filename],
  1689. physically: true,
  1690. });
  1691. }
  1692. delDialog.show = false;
  1693. delDialog.contextmenuObj = {};
  1694. MessagePlugin.success('删除成功');
  1695. };
  1696. const delComponent = async () => {
  1697. // const id = contextmenu.component._id || contextmenu.component.id;
  1698. if (['组件', '方案', '模板'].includes(activedGroup.value)) {
  1699. // let collection = delDialog.contextmenuObj.component.component
  1700. // ? 'v.component'
  1701. // : 'v';
  1702. let collection = '';
  1703. if(activedGroup.value == '组件') {
  1704. collection = 'v.component'
  1705. } else {
  1706. collection = 'v'
  1707. }
  1708. const id =
  1709. delDialog.contextmenuObj.component._id ||
  1710. delDialog.contextmenuObj.component.id;
  1711. try {
  1712. await axios.post(`/api/data/${collection}/delete`, {
  1713. id,
  1714. });
  1715. } catch (e) {
  1716. console.error(e);
  1717. return;
  1718. }
  1719. // 前端:从源文件夹移出组件
  1720. delDialog.contextmenuObj.group.list.forEach(
  1721. (item: any, index: number, arr: any[]) => {
  1722. if (id === item._id || id === item.id) {
  1723. arr.splice(index, 1);
  1724. }
  1725. }
  1726. );
  1727. } else {
  1728. const id =
  1729. delDialog.contextmenuObj.component._id ||
  1730. delDialog.contextmenuObj.component.id;
  1731. delDialog.contextmenuObj.group.list.forEach(
  1732. (item: any, index: number, arr: any[]) => {
  1733. if (id && (id === item.id || id === item._id)) {
  1734. arr.splice(index, 1);
  1735. }
  1736. if (!id) {
  1737. if (item.url === delDialog.contextmenuObj.component.url) {
  1738. arr.splice(index, 1);
  1739. }
  1740. }
  1741. }
  1742. );
  1743. }
  1744. let fullname: string =
  1745. delDialog.contextmenuObj.component.fullname ||
  1746. delDialog.contextmenuObj.component.image;
  1747. if (imageDrive && fullname.startsWith(imageDrive)) {
  1748. fullname = fullname.slice(imageDrive.length);
  1749. }
  1750. if(fullname.startsWith('/file')) {
  1751. fullname = fullname.slice('/file'.length);
  1752. }
  1753. if(fullname.startsWith('/api/file/')) {
  1754. fullname = fullname.slice('/api/file/'.length);
  1755. }
  1756. await axios.post(`/api/files/delete`, {
  1757. fullnames: [fullname],
  1758. physically: true,
  1759. });
  1760. delDialog.show = false;
  1761. delDialog.contextmenuObj = {};
  1762. MessagePlugin.success('删除成功');
  1763. };
  1764. const drop = (obj: any) => {
  1765. dropped = true;
  1766. if (obj) {
  1767. Array.isArray(obj) && open(obj[0]);
  1768. } else {
  1769. MessagePlugin.warning('正在请求网络数据中,请稍后重试');
  1770. }
  1771. };
  1772. const onSubmitOrder = async () => {
  1773. const result: any = await axios.post('/api/order/datas/submit', {
  1774. collection: 'v',
  1775. id: chargeDialog.data.id||chargeDialog.data._id,
  1776. });
  1777. if (result) {
  1778. wechatPayDialog.order = result;
  1779. wechatPayDialog.show = true;
  1780. }
  1781. };
  1782. const getPayResult = async () => {
  1783. const result: { state: number } = await axios.post('/api/order/pay/state', {
  1784. id: wechatPayDialog.order.id || wechatPayDialog.order._id,
  1785. });
  1786. if (result && result.state) {
  1787. return true;
  1788. }
  1789. };
  1790. const onChargeSuccess = () => {
  1791. MessagePlugin.success('支付成功!');
  1792. wechatPayDialog.show = false;
  1793. chargeDialog.show = false;
  1794. };
  1795. const onSearch = () => {
  1796. debounce(searchGraphics, 300);
  1797. };
  1798. const searchGraphics = async () => {
  1799. if (search.value) {
  1800. activedPanels[activedGroup.value].splice(
  1801. 0,
  1802. activedPanels[activedGroup.value].length
  1803. );
  1804. }
  1805. for (const group of subGroups.value) {
  1806. for (const item of group.list) {
  1807. if (search.value) {
  1808. item.visible = searchObjectPinyin(item, 'name', search.value);
  1809. } else {
  1810. item.visible = true;
  1811. }
  1812. }
  1813. if (search.value) {
  1814. activedPanels[activedGroup.value].push(group.name);
  1815. }
  1816. }
  1817. };
  1818. const onFold = () => {
  1819. if (!activedPanels[activedGroup.value]) {
  1820. return;
  1821. }
  1822. if (activedPanels[activedGroup.value].length) {
  1823. activedPanels[activedGroup.value] = [];
  1824. } else {
  1825. activedPanels[activedGroup.value] = [];
  1826. for (const item of subGroups.value) {
  1827. activedPanels[activedGroup.value].push(item.name);
  1828. }
  1829. }
  1830. };
  1831. const loadImage = (elem: any) => {
  1832. if (elem.isSvg) {
  1833. makeSvg(elem);
  1834. if (activedGroup.value === '图元') {
  1835. throttle(renderPngGroup, 100);
  1836. }
  1837. }
  1838. };
  1839. const renderPngGroup = () => {
  1840. subGroups.value = pngs;
  1841. };
  1842. let uploadGroup: any;
  1843. const onSelectFiles = (item: any) => {
  1844. uploadGroup = item;
  1845. };
  1846. const beforeUpload = (file: any) => {
  1847. if (!(user && user.id)) {
  1848. MessagePlugin.warning('请先登录!');
  1849. return false;
  1850. }
  1851. return true;
  1852. };
  1853. /*
  1854. * @description 根据上传的文件处理为meta2d能够识别的内容
  1855. * */
  1856. function processFileObj(fileObj, c) {
  1857. return new Promise((resolve) => {
  1858. if (fileObj.name.endsWith('.svg')) {
  1859. let fileReader = new FileReader();
  1860. fileReader.readAsText(fileObj.raw);
  1861. fileReader.onload = async () => {
  1862. let svgText = fileReader.result;
  1863. c.componentDatas = parseSvg(svgText as string);
  1864. c.component = true;
  1865. c.tags = ['svg'];
  1866. resolve(c);
  1867. };
  1868. } else {
  1869. // 除了svg 其他一律按照图片处理,这样是否会有问题?
  1870. resolve(c);
  1871. }
  1872. });
  1873. }
  1874. const fileSuccessed = async (content: any) => {
  1875. // 组件中去掉了上传图片入口
  1876. // if (activedGroup.value === '组件') {
  1877. // const c: any = {
  1878. // name: filename(content.file.name),
  1879. // image:
  1880. // content.response.url ||
  1881. // content.response.metadata.url ||
  1882. // `/file${content.response.filename}`,
  1883. // folder: uploadGroup.name === '默认' ? '' : uploadGroup.name,
  1884. // };
  1885. // let rst = await processFileObj(
  1886. // { raw: content.file.raw, name: content.file.name },
  1887. // c
  1888. // );
  1889. // const ret: any = await addCollection('v.component', rst);
  1890. // c._id = ret._id || ret.id;
  1891. // if (ret && uploadGroup.name !== '默认') {
  1892. // if (!uploadGroup.list) {
  1893. // uploadGroup.list = [];
  1894. // }
  1895. // uploadGroup.list.push(c);
  1896. // await axios.post('/api/data/folders/update', {
  1897. // _id: uploadGroup._id || uploadGroup.id,
  1898. // list: uploadGroup.list,
  1899. // });
  1900. // } else {
  1901. // if (!uploadGroup.list) {
  1902. // uploadGroup.list = [];
  1903. // }
  1904. // uploadGroup.list.push(c);
  1905. // }
  1906. // uploadGroup.list.push(c);
  1907. // } else if (activedGroup.value === '图片') {
  1908. uploadGroup.list.push({
  1909. ...content.response,
  1910. image:
  1911. content.response.url ||
  1912. content.response.metadata.url ||
  1913. `/file${content.response.filename}`,
  1914. visible: true,
  1915. folder: content.response.directory || content.response.metadata.directory,
  1916. });
  1917. // }
  1918. MessagePlugin.success('上传成功');
  1919. };
  1920. const delFolder = async (item: any) => {
  1921. if (item.list?.length) {
  1922. MessagePlugin.error('文件夹不为空!');
  1923. return;
  1924. }
  1925. const id = item.id || item._id;
  1926. let ret: any;
  1927. if (activeAssets.value !== 'user') {
  1928. return;
  1929. }
  1930. // if (['组件', '方案', '模板'].includes(activedGroup.value)) {
  1931. // ret = await axios.post('/api/data/folders/delete', {
  1932. // id,
  1933. // });
  1934. // } else if (activedGroup.value === '图片') {
  1935. // ret = await axios.post('/api/directory/delete', {
  1936. // fullpaths: [`/大屏/${item.name}`],
  1937. // });
  1938. // }
  1939. ret = await axios.post('/api/directory/delete', {
  1940. fullpaths: [`/大屏/${activedGroup.value}/${item.name}`],
  1941. physically: true
  1942. });
  1943. if (ret) {
  1944. const i = subGroups.value.findIndex(
  1945. (elem: any) => id === elem._id || id === elem.id
  1946. );
  1947. i >= 0 && subGroups.value.splice(i, 1);
  1948. }
  1949. };
  1950. const reloadCurrent = () => {
  1951. groupChange(activedGroup.value);
  1952. };
  1953. const updateAfterSave = (e) => {
  1954. if (activeAssets.value === 'user') {
  1955. if (e === 'v.component' && activedGroup.value === '组件') {
  1956. groupChange('组件');
  1957. } else if (e === 'v-template' && activedGroup.value === '模板') {
  1958. groupChange('模板');
  1959. } else if (e === '' && activedGroup.value === '方案') {
  1960. groupChange('方案');
  1961. }
  1962. }
  1963. };
  1964. const onManageGraphic = () => {
  1965. if (activedGroup.value === '素材') {
  1966. manageDialog.data = deepClone(materials);
  1967. } else if (activedGroup.value === '图元') {
  1968. manageDialog.data = deepClone(pngs);
  1969. }
  1970. manageDialog.show = true;
  1971. };
  1972. const manageConfirm = () => {
  1973. //TODO 后续存放到后端 目前本地存储
  1974. if (activedGroup.value === '素材') {
  1975. localforage.setItem('le5leV-materials', JSON.stringify(manageDialog.data));
  1976. materials.length = 0;
  1977. materials.push(...manageDialog.data);
  1978. } else if (activedGroup.value === '图元') {
  1979. localforage.setItem('le5leV-pngs', JSON.stringify(manageDialog.data));
  1980. pngs.length = 0;
  1981. pngs.push(...manageDialog.data);
  1982. }
  1983. subGroups.value = manageDialog.data;
  1984. manageDialog.show = false;
  1985. };
  1986. onMounted(() => {
  1987. groupChange('方案');
  1988. document.addEventListener('dragstart', dragstart, false);
  1989. document.addEventListener('dragend', dragend, false);
  1990. setTimeout(() => {
  1991. meta2d.on('drop', drop);
  1992. meta2d.on('logout', reloadCurrent);
  1993. meta2d.on('business-save', updateAfterSave);
  1994. meta2d.on('business-assets', changeAssets);
  1995. }, 2000);
  1996. });
  1997. const changeAssets = (e)=>{
  1998. if(e){
  1999. activeAssets.value = 'system';
  2000. assetsChange( activeAssets.value);
  2001. }
  2002. }
  2003. onUnmounted(() => {
  2004. document.removeEventListener('dragstart', dragstart);
  2005. document.removeEventListener('dragend', dragend);
  2006. meta2d.off('drop', drop);
  2007. meta2d.off('logout', reloadCurrent);
  2008. meta2d.off('business-save', updateAfterSave);
  2009. meta2d.off('business-assets', changeAssets);
  2010. });
  2011. </script>
  2012. <style lang="postcss" scoped>
  2013. .graphics {
  2014. display: flex;
  2015. flex-direction: column;
  2016. background-color: var(--color-background);
  2017. z-index: 3;
  2018. .group-asset {
  2019. height: 40px;
  2020. justify-content: center;
  2021. align-items: center;
  2022. display: flex;
  2023. .t-radio-group.t-radio-group--filled {
  2024. padding: 0px;
  2025. height: 30px;
  2026. }
  2027. :deep(.t-radio-button__label) {
  2028. margin: auto;
  2029. }
  2030. .t-radio-button {
  2031. width: 120px;
  2032. height: 100%;
  2033. color: #fff;
  2034. &:hover {
  2035. color: var(--color-primary);
  2036. }
  2037. &.t-is-checked {
  2038. background-color: var(--color-primary) !important;
  2039. color: #fff !important;
  2040. }
  2041. }
  2042. }
  2043. .groups-panel {
  2044. display: grid;
  2045. grid-template-columns: 50px 1fr;
  2046. border-top: 1px solid var(--color-background-input);
  2047. flex-grow: 1;
  2048. overflow-y: auto;
  2049. font-size: 12px;
  2050. z-index: 7;
  2051. .groups {
  2052. & > div {
  2053. display: flex;
  2054. flex-direction: column;
  2055. align-items: center;
  2056. padding: 16px 4px;
  2057. line-height: 1;
  2058. cursor: pointer;
  2059. .t-icon {
  2060. font-size: 20px;
  2061. margin-bottom: 8px;
  2062. }
  2063. .l-icon {
  2064. font-size: 24px;
  2065. margin-bottom: 8px;
  2066. }
  2067. &:hover {
  2068. color: var(--color-primary);
  2069. }
  2070. }
  2071. .active {
  2072. background-color: var(--color-background-active);
  2073. color: var(--color-primary);
  2074. border-left: 2px solid var(--color-primary);
  2075. }
  2076. }
  2077. .list {
  2078. overflow-y: auto;
  2079. max-height: calc(100vh - 80px);
  2080. background-color: var(--color-background-active);
  2081. /* padding-top: 8px; */
  2082. .input-search {
  2083. flex-shrink: 0;
  2084. /* height: 40px; */
  2085. position: sticky;
  2086. top: 0px;
  2087. z-index: 10;
  2088. padding: 16px;
  2089. color: #878f9c;
  2090. margin-top: -10px;
  2091. .btn {
  2092. background: #fff0;
  2093. img {
  2094. background-color: #fff0;
  2095. margin-top: 9px;
  2096. }
  2097. /* .t-icon {
  2098. background: #fff0;
  2099. } */
  2100. }
  2101. }
  2102. .div-manage {
  2103. position: sticky;
  2104. bottom: 2px;
  2105. z-index: 10;
  2106. margin-top: 800px;
  2107. }
  2108. * {
  2109. background-color: var(--color-background-active);
  2110. }
  2111. :deep(.t-collapse) {
  2112. /* min-height: 100vh; */
  2113. border: none;
  2114. }
  2115. :deep(.t-collapse-panel__header) {
  2116. border: none;
  2117. font-size: 12px;
  2118. font-weight: 400;
  2119. padding: 8px 8px 8px 16px;
  2120. & > .t-space {
  2121. display: none;
  2122. }
  2123. & > .t-space:focus {
  2124. display: inline-flex;
  2125. }
  2126. &:hover .t-collapse-panel__icon,
  2127. &:hover > .flex {
  2128. color: var(--color-primary);
  2129. }
  2130. .t-collapse-panel__icon,
  2131. .t-collapse-panel__icon * {
  2132. transition: none;
  2133. }
  2134. .t-icon {
  2135. font-size: 13px;
  2136. }
  2137. }
  2138. :deep(.t-collapse-panel__wrapper:hover) {
  2139. .t-collapse-panel__header > .t-space {
  2140. display: inline-flex;
  2141. }
  2142. }
  2143. :deep(.t-collapse-panel__body) {
  2144. border: none;
  2145. }
  2146. :deep(.t-collapse-panel__content) {
  2147. background-color: var(--color-background-active);
  2148. padding: 4px 4px 20px 4px;
  2149. display: grid;
  2150. grid-template-columns: 1fr 1fr 1fr;
  2151. grid-row-gap: 12px;
  2152. }
  2153. :deep(.t-loading--center) {
  2154. width: 100px;
  2155. .t-loading__text {
  2156. margin-left: 8px;
  2157. height: 24px;
  2158. }
  2159. }
  2160. :deep(.t-image__error) {
  2161. .t-space-item:last-child {
  2162. display: none;
  2163. }
  2164. }
  2165. :deep(.t-image__loading) {
  2166. .t-space-item:last-child {
  2167. display: none;
  2168. }
  2169. }
  2170. .graphic {
  2171. position: relative;
  2172. padding: 10px 0;
  2173. border-radius: 2px;
  2174. border: 1px solid transparent;
  2175. &:hover {
  2176. cursor: pointer;
  2177. border-color: var(--color-primary);
  2178. }
  2179. p {
  2180. margin-top: 10px;
  2181. padding: 0 8px;
  2182. text-align: center;
  2183. font-size: 12px;
  2184. height: 12px;
  2185. line-height: 1;
  2186. overflow: hidden;
  2187. text-overflow: ellipsis;
  2188. display: -webkit-box;
  2189. -webkit-line-clamp: 1;
  2190. word-break: break-all;
  2191. -webkit-box-orient: vertical;
  2192. }
  2193. .t-image__wrapper {
  2194. height: 32px;
  2195. width: 32px;
  2196. margin: auto;
  2197. :deep(.t-image) {
  2198. border-radius: 2px;
  2199. }
  2200. }
  2201. svg {
  2202. color: var(--color);
  2203. height: 32px;
  2204. width: 100%;
  2205. margin: auto;
  2206. }
  2207. .svg-box {
  2208. height: 32px;
  2209. width: 32px;
  2210. margin-left: calc(50% - 16px);
  2211. margin-top: 10px;
  2212. margin-bottom: 10px;
  2213. &:deep(svg) {
  2214. height: 100%;
  2215. width: 100%;
  2216. .cls-1 {
  2217. stroke: var(--color) !important;
  2218. stroke-width: 4px;
  2219. fill: none;
  2220. stroke-dasharray: none;
  2221. opacity: 1;
  2222. }
  2223. }
  2224. }
  2225. .price {
  2226. position: absolute;
  2227. top: 8px;
  2228. right: 8px;
  2229. display: inline-block;
  2230. z-index: 1;
  2231. border-radius: 2px;
  2232. background-color: #ff400060;
  2233. color: var(--color-bland);
  2234. font-size: 10px;
  2235. line-height: 1;
  2236. padding: 3px;
  2237. }
  2238. }
  2239. }
  2240. .two-columns {
  2241. :deep(.t-collapse-panel__content) {
  2242. grid-template-columns: 1fr 1fr;
  2243. }
  2244. .graphic {
  2245. .t-image__wrapper {
  2246. width: 100px;
  2247. height: 56.25px;
  2248. background-color: '#162030';
  2249. }
  2250. }
  2251. }
  2252. }
  2253. .context-menu-box {
  2254. position: fixed;
  2255. z-index: 200;
  2256. & > div {
  2257. width: 140px !important;
  2258. position: static;
  2259. }
  2260. :deep(.t-menu) {
  2261. .t-menu__item {
  2262. &.t-is-opened {
  2263. background-color: var(--color-background-popup-hover);
  2264. transition: none !important;
  2265. }
  2266. }
  2267. .t-fake-arrow {
  2268. transform: rotate(-90deg) !important;
  2269. }
  2270. .t-fake-arrow--active {
  2271. transform: rotate(90deg) !important;
  2272. }
  2273. }
  2274. }
  2275. :deep(.dialog-manage) {
  2276. .t-dialog__body {
  2277. height: 500px;
  2278. overflow: scroll;
  2279. }
  2280. .t-collapse {
  2281. border: 0px;
  2282. }
  2283. .t-collapse-panel__header {
  2284. border: 0px;
  2285. }
  2286. .t-collapse-panel__body {
  2287. border: 0px;
  2288. }
  2289. .t-collapse-panel__content {
  2290. display: grid;
  2291. grid-template-columns: 1fr 1fr 1fr 1fr;
  2292. /* grid-template-rows: repeat(100, 100px); */
  2293. grid-auto-rows: minmax(100px, auto);
  2294. grid-row-gap: 20px;
  2295. grid-column-gap: 20px;
  2296. }
  2297. .t-collapse-panel__body {
  2298. background: #fff0;
  2299. }
  2300. .manage-list {
  2301. max-height: 310px;
  2302. border: 1px solid #fff;
  2303. position: relative;
  2304. border: 1px solid #535f79;
  2305. position: relative;
  2306. border-radius: 2px;
  2307. .t-checkbox {
  2308. position: absolute;
  2309. right: 0px;
  2310. top: 10px;
  2311. z-index: 10;
  2312. }
  2313. }
  2314. }
  2315. :deep(.dialog-charge){
  2316. .t-dialog__body {
  2317. iframe{
  2318. width: 100%;
  2319. height: 80vh;
  2320. }
  2321. }
  2322. }
  2323. }
  2324. </style>