Graphics.vue 69 KB

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