Structure.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759
  1. <template>
  2. <div class="elements props" v-show="group === '图层'">
  3. <div
  4. class="flex mt-16 mb-16"
  5. style="justify-content: end; padding-right: 8px"
  6. >
  7. <t-tooltip content="置顶" placement="top">
  8. <div class="icon-box" @click="changeActivLayer('top')">
  9. <BacktopIcon />
  10. </div>
  11. </t-tooltip>
  12. <t-tooltip content="置底" placement="top">
  13. <div class="icon-box" @click="changeActivLayer('bottom')">
  14. <Download1Icon />
  15. </div>
  16. </t-tooltip>
  17. <t-tooltip content="上一层" placement="top">
  18. <div class="icon-box" @click="changeActivLayer('up')">
  19. <ArrowUpIcon />
  20. </div>
  21. </t-tooltip>
  22. <t-tooltip content="下一层" placement="top">
  23. <div class="icon-box" @click="changeActivLayer('down')">
  24. <ArrowDownIcon />
  25. </div>
  26. </t-tooltip>
  27. <t-tooltip :content="data.expandAll ? '折叠' : '展开'" placement="top">
  28. <div class="icon-box" @click="changeExpand">
  29. <MenuFoldIcon v-if="data.expandAll" />
  30. <MenuUnfoldIcon v-else />
  31. </div>
  32. </t-tooltip>
  33. </div>
  34. <t-tree
  35. class="flex-grow"
  36. ref="tree"
  37. activeMultiple
  38. :data="data.tree"
  39. :actived="data.actived"
  40. v-model:expanded="data.expanded"
  41. activable
  42. :expand-parent="true"
  43. style="padding: 0 4px 8px 8px"
  44. :scroll="{
  45. // rowHeight: 34,
  46. bufferSize: 20,
  47. threshold: 40,
  48. type: 'virtual',
  49. }"
  50. >
  51. <template #label="{ node }: any">
  52. <div class="flex middle" :class="{ gray: node.data.visible === false }">
  53. <template
  54. v-if="node.getChildren() || node.data.value.endsWith('Layer')"
  55. >
  56. <folder-open-icon class="mr-8" v-if="node.expanded" />
  57. <folder-icon class="mr-8" v-else />
  58. </template>
  59. <!-- <t-tooltip v-else-if="node.data.tag === 'dom'" content="DOM元素">
  60. <Code1Icon />
  61. </t-tooltip> -->
  62. <t-tooltip v-else :content="node.data.tag === 'dom' ? 'DOM元素' : ''">
  63. <control-platform-icon class="mr-8" />
  64. </t-tooltip>
  65. <t-input
  66. v-if="node.data.edited"
  67. v-model="node.data.label"
  68. :autofocus="true"
  69. style="width: 100px"
  70. @blur="onDescription(node)"
  71. @enter="onDescription(node)"
  72. />
  73. <span
  74. v-else
  75. style="width: 100px"
  76. @click="onActive($event, node.value)"
  77. @dblclick="ondblclick(node)"
  78. @contextmenu="oncontextmenu($event,node)"
  79. >
  80. {{ node.label }}
  81. </span>
  82. </div>
  83. </template>
  84. <template #operations="{ node }: any">
  85. <div
  86. class="flex middle operations"
  87. :class="{
  88. gray: node.data.visible === false,
  89. show: node.data.visible === false || node.data.locked,
  90. }"
  91. style="width: 46px; height: 16px"
  92. >
  93. <template v-if="!node.data.value.endsWith('Layer')">
  94. <t-tooltip
  95. class="mr-8"
  96. v-if="!node.data.locked"
  97. content="可编辑"
  98. placement="top"
  99. >
  100. <svg class="l-icon" aria-hidden="true" @click="lock(node, 1)">
  101. <use xlink:href="#l-unlock"></use>
  102. </svg>
  103. </t-tooltip>
  104. <t-tooltip
  105. class="mr-8"
  106. v-else-if="node.data.locked == 1"
  107. content="禁止编辑"
  108. placement="top"
  109. >
  110. <svg class="l-icon" aria-hidden="true" @click="lock(node, 2)">
  111. <use xlink:href="#l-lock"></use>
  112. </svg>
  113. </t-tooltip>
  114. <t-tooltip
  115. class="mr-8"
  116. v-else-if="node.data.locked == 2"
  117. content="禁止编辑和移动"
  118. placement="top"
  119. >
  120. <svg class="l-icon" aria-hidden="true" @click="lock(node, 10)">
  121. <use xlink:href="#l-wufayidong"></use>
  122. </svg>
  123. </t-tooltip>
  124. <t-tooltip
  125. class="mr-8"
  126. v-else-if="node.data.locked == 10"
  127. content="禁止所有事件"
  128. placement="top"
  129. >
  130. <svg class="l-icon" aria-hidden="true" @click="lock(node, 0)">
  131. <use xlink:href="#l-jinyong"></use>
  132. </svg>
  133. </t-tooltip>
  134. </template>
  135. <span v-else style="width: 24px"></span>
  136. <browse-icon
  137. v-if="node.data.visible !== false"
  138. @click="visible(node, false)"
  139. />
  140. <browse-off-icon v-else @click="visible(node, true)" />
  141. </div>
  142. </template>
  143. </t-tree>
  144. </div>
  145. <div class="elements" v-show="group === '分组'">
  146. <div class="groups-panel" style="padding: 8px 0">
  147. <div class="flex middle between" style="padding: 0 12px">
  148. <div class="title">分组</div>
  149. <t-tooltip content="新建分组" placement="top">
  150. <div class="icon-box" @click="addGroup">
  151. <AddIcon />
  152. </div>
  153. </t-tooltip>
  154. </div>
  155. <div class="groups">
  156. <div
  157. v-for="(item, i) in data.groups"
  158. class="flex middle between hover"
  159. :class="{ primary: i == data.activedGroup }"
  160. >
  161. <span
  162. class="flex-grow"
  163. v-if="i != data.editedGroup"
  164. @click="activeGroup(i)"
  165. @dblclick="
  166. data.activedGroup = data.editedGroup = i;
  167. data.group = item;
  168. "
  169. >
  170. {{ item }}
  171. </span>
  172. <t-input
  173. v-else
  174. v-model="data.group"
  175. :autofocus="true"
  176. @blur="setGroup"
  177. @enter="setGroup"
  178. />
  179. <browse-icon
  180. v-if="!data.hiddenGroups.includes(item)"
  181. @click="visibleGroup(item, false)"
  182. />
  183. <browse-off-icon v-else @click="visibleGroup(item, true)" />
  184. <t-popconfirm
  185. content="确认删除该分组吗?"
  186. @confirm="delGroup"
  187. @cancel="data.deleteGroup = undefined"
  188. >
  189. <delete-icon
  190. class="ml-8"
  191. :class="{ block: i == data.deleteGroup }"
  192. @click="data.deleteGroup = i"
  193. />
  194. </t-popconfirm>
  195. </div>
  196. </div>
  197. </div>
  198. </div>
  199. </template>
  200. <script lang="ts" setup>
  201. import { onBeforeMount, onMounted, onBeforeUnmount, reactive, ref } from 'vue';
  202. import { MessagePlugin } from 'tdesign-vue-next';
  203. import { LockState, Pen, isDomShapes } from '@meta2d/core';
  204. import { getPenTree, inTreePanel, setChildrenVisible } from '@/services/common';
  205. import {
  206. FolderOpenIcon,
  207. FolderIcon,
  208. ControlPlatformIcon,
  209. BrowseIcon,
  210. BrowseOffIcon,
  211. DeleteIcon,
  212. AddIcon,
  213. Code1Icon,
  214. BacktopIcon,
  215. Download1Icon,
  216. ArrowDownIcon,
  217. ArrowUpIcon,
  218. MenuUnfoldIcon,
  219. MenuFoldIcon,
  220. } from 'tdesign-icons-vue-next';
  221. const props = defineProps<{
  222. group: string;
  223. }>();
  224. const tree = ref<any>(null);
  225. const data = reactive<any>({
  226. tree: [],
  227. actived: [],
  228. groups: [],
  229. hiddenGroups: [],
  230. expandAll: false,
  231. expanded: [],
  232. });
  233. onMounted(() => {
  234. meta2d.on('opened', getTree);
  235. meta2d.on('add', getTree);
  236. meta2d.on('undo', getTree);
  237. meta2d.on('redo', getTree);
  238. meta2d.on('delete', getTree);
  239. meta2d.on('combine', getTree);
  240. meta2d.on('click', getActived);
  241. meta2d.on('paste', getActived);
  242. meta2d.on('layer', layerChange);
  243. meta2d.on('active', getActived);
  244. if (inTreePanel.timer) {
  245. clearTimeout(inTreePanel.timer);
  246. inTreePanel.timer = undefined;
  247. }
  248. inTreePanel.value = true;
  249. getTree();
  250. getActived();
  251. const d = meta2d.store.data as any;
  252. if (!d.groups) {
  253. d.groups = [];
  254. }
  255. data.groups = d.groups;
  256. getHiddenGroups();
  257. });
  258. const getTree = () => {
  259. data.tree = getPhysicalTree();
  260. };
  261. const getPhysicalTree = () => {
  262. const pens = meta2d.store.data.pens;
  263. const imageLayer = [];
  264. const mainLayer = [];
  265. const imageBottomLayer = [];
  266. const templateLayer = [];
  267. const doms = [];
  268. for (let i = pens.length - 1; i >= 0; i--) {
  269. if (pens[i].parentId) {
  270. continue;
  271. }
  272. if (
  273. pens[i].name.endsWith('Dom') ||
  274. isDomShapes.includes(pens[i].name) ||
  275. meta2d.store.options.domShapes.includes(pens[i].name)
  276. ) {
  277. //dom图元根据层级来决定
  278. const node = calcElem(pens[i]);
  279. node.tag = 'dom';
  280. doms.push(node);
  281. } else if (pens[i].canvasLayer === 1) {
  282. //模版层
  283. templateLayer.push(calcElem(pens[i]));
  284. } else if (pens[i].name === 'image' || pens[i].image) {
  285. if (pens[i].canvasLayer === 2) {
  286. //底层图片绘制层
  287. imageBottomLayer.push(calcElem(pens[i]));
  288. } else if (pens[i].canvasLayer === 3) {
  289. //主画布层
  290. mainLayer.push(calcElem(pens[i]));
  291. } else {
  292. //上层图片绘制层
  293. imageLayer.push(calcElem(pens[i]));
  294. }
  295. } else {
  296. //主画布层
  297. mainLayer.push(calcElem(pens[i]));
  298. }
  299. }
  300. const list = [
  301. {
  302. label: '上层图片层', //4
  303. value: 'imageLayer',
  304. children: imageLayer,
  305. zIndex: 3.5,
  306. },
  307. {
  308. label: '主画布层', //3
  309. value: 'mainLayer',
  310. children: mainLayer,
  311. zIndex: 2.5,
  312. },
  313. {
  314. label: '底层图片层', //2
  315. value: 'imageBottomLayer',
  316. children: imageBottomLayer,
  317. zIndex: 1.5,
  318. },
  319. {
  320. label: '模版层', //1
  321. value: 'templateLayer',
  322. children: templateLayer,
  323. zIndex: 0.5,
  324. },
  325. ];
  326. list.push(...doms);
  327. list.sort((a, b) => b.zIndex - a.zIndex);
  328. return list;
  329. };
  330. const getPenLayer = (pen: Pen) => {
  331. if (
  332. pen.name.endsWith('Dom') ||
  333. isDomShapes.includes(pen.name) ||
  334. meta2d.store.options.domShapes.includes(pen.name)
  335. ) {
  336. return 'dom';
  337. } else if (pen.canvasLayer === 1) {
  338. //模版层
  339. return 'templateLayer';
  340. } else if (pen.name === 'image' || pen.image) {
  341. if (pen.canvasLayer === 2) {
  342. //底层图片绘制层
  343. return 'imageBottomLayer';
  344. } else if (pen.canvasLayer === 3) {
  345. //主画布层
  346. return 'mainLayer';
  347. } else {
  348. //上层图片绘制层
  349. return 'imageLayer';
  350. }
  351. } else {
  352. //主画布层
  353. return 'mainLayer';
  354. }
  355. };
  356. const layerChange = () => {
  357. getTree();
  358. // setTimeout(() => {
  359. // if (data.actived && data.actived.length) {
  360. // const element = document.body.querySelector(
  361. // `[data-value="${data.actived[0]}"]`
  362. // );
  363. // const layer = getPenLayer(meta2d.store.active[0]);
  364. // data.expanded = [layer];
  365. // if (element) {
  366. // element.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
  367. // } else {
  368. // let index = data.tree.findIndex((item) => item.value === layer);
  369. // let innerIndex = data.tree[index]?.children.findIndex(
  370. // (item) => item.value === meta2d.store.active[0].id
  371. // );
  372. // setTimeout(() => {
  373. // tree.value?.scrollToElement({ index: index + innerIndex });
  374. // }, 500);
  375. // }
  376. // }
  377. // }, 500);
  378. };
  379. const changeActivLayer = (key: string) => {
  380. if (meta2d.store.active && meta2d.store.active.length) {
  381. meta2d[key]();
  382. } else {
  383. MessagePlugin.info('请先选中图元');
  384. }
  385. };
  386. const changeExpand = () => {
  387. data.expandAll = !data.expandAll;
  388. if (data.expandAll) {
  389. data.expanded = data.tree.map((item) => item.value);
  390. } else {
  391. data.expanded = [];
  392. }
  393. };
  394. const getHiddenGroups = () => {
  395. data.groups.forEach((item) => {
  396. if (
  397. meta2d.store.data.pens.some(
  398. (pen) =>
  399. !pen.parentId && pen.tags?.includes(item) && pen.visible === false
  400. )
  401. ) {
  402. data.hiddenGroups.push(item);
  403. }
  404. });
  405. };
  406. let getActiveFlag = false; //点击画布导致的active还是结构中选择导致的active
  407. const getActived = () => {
  408. if (getActiveFlag) {
  409. getActiveFlag = false;
  410. return;
  411. }
  412. //TODO加个异步?等展开完成后再滚动
  413. data.actived = [];
  414. if (meta2d.store.active && meta2d.store.active.length) {
  415. for (const pen of meta2d.store.active) {
  416. data.actived.push(pen.id);
  417. }
  418. const element = document.body.querySelector(
  419. `[data-value="${data.actived[0]}"]`
  420. );
  421. const layer = getPenLayer(meta2d.store.active[0]);
  422. data.expanded = [layer];
  423. if (element) {
  424. // console.log("element",element);
  425. element.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
  426. } else {
  427. // setTimeout(() => {
  428. // const element = document.body.querySelector(
  429. // `[data-value="${data.actived[0]}"]`
  430. // );
  431. // element && element.scrollIntoView({ block: 'center' });
  432. // }, 500);
  433. // let pens = meta2d.store.data.pens.filter((pen) => !pen.parentId);
  434. // let index = pens.findIndex((item) => item.id === meta2d.store.active[0].id);
  435. // let index = data.tree.findIndex(
  436. // (item) => item.value === meta2d.store.active[0].id
  437. // );
  438. let index = data.tree.findIndex((item) => item.value === layer);
  439. let innerIndex = data.tree[index]?.children.findIndex(
  440. (item) => item.value === meta2d.store.active[0].id
  441. );
  442. // tree.value?.scrollTo()
  443. // console.log("index",index,innerIndex,data.tree);
  444. setTimeout(() => {
  445. tree.value?.scrollToElement({ index: index + innerIndex });
  446. }, 500);
  447. }
  448. }
  449. };
  450. const calcElem = (node: Pen) => {
  451. if (!node) {
  452. return;
  453. }
  454. const elem: any = {
  455. label: (node as any).description || node.name,
  456. value: node.id,
  457. locked: node.locked,
  458. visible: node.visible,
  459. tag: (node as any).tag,
  460. zIndex: node.calculative.zIndex !== undefined ? node.calculative.zIndex : 5,
  461. };
  462. if (!node.children) {
  463. return elem;
  464. }
  465. elem.children = [];
  466. for (const id of node.children) {
  467. const child = calcElem(meta2d.store.pens[id]);
  468. child && elem.children.push(child);
  469. }
  470. return elem;
  471. };
  472. const onActive = (e,value: any) => {
  473. if (!value || value.endsWith('Layer')) {
  474. return;
  475. }
  476. if(e.ctrlKey){
  477. if(data.actived.includes(value)){
  478. data.actived = data.actived.filter((item) => item !== value);
  479. }else{
  480. data.actived.push(value);
  481. }
  482. }else{
  483. data.actived = [value];
  484. }
  485. getActiveFlag = true;
  486. if(data.actived.length > 1){
  487. let pens = [];
  488. data.actived.forEach((item) => {
  489. pens.push(meta2d.store.pens[item]);
  490. });
  491. meta2d.active(pens, true);
  492. }else{
  493. const pen = meta2d.store.pens[value];
  494. meta2d.active([pen], true);
  495. if (!pen.calculative?.inView) {
  496. meta2d.gotoView(pen);
  497. meta2d.resize();
  498. }
  499. }
  500. meta2d.render();
  501. };
  502. const ondblclick = (node: any) => {
  503. if (node.data.value.endsWith('Layer')) {
  504. return;
  505. } else {
  506. node.data.edited = true;
  507. }
  508. };
  509. const oncontextmenu = (ev,node: any) => {
  510. ev.preventDefault();
  511. ev.stopPropagation();
  512. let id = node.data.value;
  513. if(meta2d.store.active[0].id !== id){
  514. return;
  515. }
  516. let e = {
  517. clientX:ev.clientX ,
  518. clientY:ev.clientY
  519. }
  520. meta2d.emit('contextmenu',{e})
  521. }
  522. const lock = (node: any, v: LockState) => {
  523. node.data.locked = v;
  524. meta2d.setValue({
  525. id: node.value,
  526. locked: v,
  527. });
  528. };
  529. const visible = (node: any, v: boolean) => {
  530. node.data.visible = v;
  531. if (node.data.value.endsWith('Layer')) {
  532. if (node.data.value === 'imageLayer') {
  533. meta2d.canvas.canvasImage.canvas.style.display = v ? 'block' : 'none';
  534. } else if (node.data.value === 'mainLayer') {
  535. meta2d.canvas.canvas.style.display = v ? 'block' : 'none';
  536. } else if (node.data.value === 'imageBottomLayer') {
  537. meta2d.canvas.canvasImageBottom.canvas.style.display = v
  538. ? 'block'
  539. : 'none';
  540. } else if (node.data.value === 'templateLayer') {
  541. meta2d.canvas.canvasTemplate.canvas.style.display = v ? 'block' : 'none';
  542. }
  543. return;
  544. }
  545. setChildrenVisible(node, v);
  546. const pen = meta2d.findOne(node.value);
  547. pen && meta2d.setVisible(pen, v);
  548. meta2d.render();
  549. };
  550. const visibleGroup = (item, v: boolean) => {
  551. if (v) {
  552. let index = data.hiddenGroups.indexOf(item);
  553. if (index !== -1) {
  554. data.hiddenGroups.splice(index, 1);
  555. }
  556. } else {
  557. data.hiddenGroups.push(item);
  558. }
  559. let pens = meta2d.store.data.pens.filter(
  560. (pen) => !pen.parentId && pen.tags?.includes(item)
  561. );
  562. pens.forEach((pen) => {
  563. meta2d.setValue(
  564. { id: pen.id, visible: v },
  565. { render: false, doEvent: false }
  566. );
  567. });
  568. meta2d.render();
  569. };
  570. const onDescription = (node: any) => {
  571. node.data.edited = false;
  572. node.setData({ label: node.data.label });
  573. meta2d.setValue({
  574. id: node.value,
  575. description: node.data.label,
  576. });
  577. };
  578. const addGroup = () => {
  579. const i = data.groups.length + 1;
  580. data.group = '组' + i;
  581. data.groups.push(data.group);
  582. data.activedGroup = data.editedGroup = i;
  583. };
  584. const activeGroup = (i: number) => {
  585. data.activedGroup = i;
  586. const group = data.groups[i];
  587. const pens: Pen[] = [];
  588. for (const item of meta2d.store.data.pens) {
  589. if (item.tags?.includes(group)) {
  590. pens.push(item);
  591. }
  592. }
  593. meta2d.active(pens, false);
  594. meta2d.render();
  595. };
  596. const setGroup = () => {
  597. if (data.groups[data.editedGroup] === data.group) {
  598. data.editedGroup = undefined;
  599. return;
  600. }
  601. if (data.groups.includes(data.group)) {
  602. MessagePlugin.error('已经存在相同分组!');
  603. return;
  604. }
  605. for (const item of meta2d.store.data.pens) {
  606. // @ts-ignore
  607. if (item.group === data.groups[data.editedGroup]) {
  608. // @ts-ignore
  609. item.group === data.group;
  610. }
  611. }
  612. data.groups[data.editedGroup] = data.group;
  613. data.editedGroup = undefined;
  614. };
  615. const delGroup = () => {
  616. for (const item of meta2d.store.data.pens) {
  617. // @ts-ignore
  618. if (item.group === data.groups[data.deleteGroup]) {
  619. // @ts-ignore
  620. delete item.group;
  621. }
  622. }
  623. data.groups.splice(data.deleteGroup, 1);
  624. data.deleteGroup = undefined;
  625. };
  626. onBeforeUnmount(() => {
  627. meta2d.off('opened', getTree);
  628. meta2d.off('add', getTree);
  629. meta2d.off('undo', getTree);
  630. meta2d.off('redo', getTree);
  631. meta2d.off('delete', getTree);
  632. meta2d.off('combine', getTree);
  633. meta2d.off('click', getActived);
  634. meta2d.off('paste', getActived);
  635. meta2d.off('layer', layerChange);
  636. meta2d.off('active', getActived);
  637. inTreePanel.timer = setTimeout(() => {
  638. inTreePanel.value = false;
  639. }, 500);
  640. });
  641. </script>
  642. <style lang="postcss" scoped>
  643. .elements {
  644. display: flex;
  645. flex-direction: column;
  646. height: calc(100vh - 90px);
  647. background: var(--color-background-active);
  648. & > * {
  649. overflow-y: auto;
  650. width: 100%;
  651. }
  652. .t-tree {
  653. /* .t-tag {
  654. background-color: #4583ff33;
  655. position: absolute;
  656. right: 45px;
  657. width: 45.6px;
  658. height: 21.6px;
  659. font-size: 10px;
  660. line-height: 14px;
  661. transform: scale(0.83333);
  662. transform-origin: 0 0;
  663. } */
  664. }
  665. .groups-panel {
  666. flex-shrink: 0;
  667. height: 100%;
  668. /* height: calc(100% - 55px); */
  669. border-top: 1px solid var(--color-border-input);
  670. padding: 8px 12px;
  671. }
  672. .groups {
  673. height: calc(100% - 55px);
  674. /* height: calc(100% - 24px); */
  675. overflow-y: auto;
  676. padding: 8px;
  677. & > div {
  678. height: 32px;
  679. line-height: 32px;
  680. padding: 0 8px;
  681. svg {
  682. display: none;
  683. &.block {
  684. display: block;
  685. }
  686. }
  687. &:hover {
  688. svg {
  689. display: block;
  690. }
  691. background: var(--color-background-input);
  692. }
  693. }
  694. }
  695. }
  696. .icon-box {
  697. width: 24px;
  698. height: 24px;
  699. margin: 4px;
  700. text-align: center;
  701. line-height: 24px;
  702. border-radius: 4px;
  703. &:hover {
  704. background: var(--td-brand-color-light);
  705. }
  706. .t-icon {
  707. width: 14px;
  708. height: 14px;
  709. }
  710. }
  711. </style>