Graphics.vue 63 KB

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