slide-icons.vue 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. <template>
  2. <ElCollapse class="icon-layout" v-model="activeGroups">
  3. <ElCollapseItem
  4. v-for="group in searchGroups"
  5. :name="group.name"
  6. v-if="searchGroups.length"
  7. >
  8. <template #title>
  9. <h2>{{ group.name }}</h2>
  10. </template>
  11. <div class="type-children" v-for="typeChildren in group.children">
  12. <h3 v-if="typeChildren.name">{{ typeChildren.name }}</h3>
  13. <div class="icon-items">
  14. <div
  15. v-for="item in typeChildren.children"
  16. @click="drawIcon(`./icons/${item.icon}.svg`, item.name, item)"
  17. >
  18. <Icon :name="item.icon" size="32px" :color="item.color" />
  19. <span>{{ item.name }}</span>
  20. </div>
  21. </div>
  22. </div>
  23. </ElCollapseItem>
  24. <el-empty description="暂无数据" v-else />
  25. </ElCollapse>
  26. </template>
  27. <script lang="ts" setup>
  28. import { computed, ref } from "vue";
  29. import { ElCollapse, ElCollapseItem, ElEmpty } from "element-plus";
  30. import { getSvgContent, parseSvgContent } from "@/utils/resource";
  31. import { Draw } from "../../../components/container/use-draw.ts";
  32. import { iconGroups as groups } from "../../../constant";
  33. const props = defineProps<{ draw: Draw }>();
  34. const emit = defineEmits<{ (e: "exit"): void }>();
  35. const drawIcon = async (url: string, name: string, item: any) => {
  36. const svgContent = parseSvgContent(await getSvgContent(url));
  37. const maxSize = 100;
  38. const addHeight = (maxSize / svgContent.width) * svgContent.height;
  39. const size = {
  40. width: maxSize,
  41. height: addHeight,
  42. };
  43. if (size.height > maxSize) {
  44. size.height = maxSize;
  45. size.width = (maxSize / svgContent.height) * svgContent.width;
  46. }
  47. const color = {
  48. fill: svgContent.paths[0]?.fill,
  49. stroke: svgContent.paths[0]?.stroke,
  50. };
  51. if (!color.fill && !color.stroke) {
  52. color.stroke = "#000000";
  53. }
  54. props.draw.enterDrawShape(
  55. "icon",
  56. {
  57. url,
  58. ...size,
  59. name,
  60. ...color,
  61. ...(item.parse || {}),
  62. },
  63. true
  64. );
  65. emit("exit");
  66. };
  67. const activeGroups = ref(groups.map((item) => item.name));
  68. const keyword = ref("");
  69. const searchGroups = computed(() => {
  70. return groups
  71. .map((typeChildren) => {
  72. const filterTypeChildren = typeChildren.children
  73. .map((type) => {
  74. const children = type.children.filter((item) =>
  75. item.name.includes(keyword.value)
  76. );
  77. return {
  78. ...type,
  79. children,
  80. };
  81. })
  82. .filter((type) => type.children.length > 0);
  83. return {
  84. ...typeChildren,
  85. children: filterTypeChildren,
  86. };
  87. })
  88. .filter((typeChildren) => typeChildren.children.length > 0);
  89. });
  90. </script>
  91. <style lang="scss" scoped>
  92. .icon-layout {
  93. width: 320px;
  94. height: 100%;
  95. background-color: var(--el-menu-bg-color);
  96. border-right: solid 1px var(--el-menu-border-color);
  97. overflow-y: auto;
  98. padding: 0 20px;
  99. font-size: 14px;
  100. color: #333;
  101. h2,
  102. h3 {
  103. font-size: inherit;
  104. color: inherit;
  105. }
  106. // .type-children:not(:first-child) {
  107. // margin-top: 20px;
  108. // }
  109. h3 {
  110. margin-bottom: 20px;
  111. }
  112. }
  113. .icon-items {
  114. display: flex;
  115. flex-wrap: wrap;
  116. > div {
  117. width: 25%;
  118. text-align: center;
  119. display: flex;
  120. flex-direction: column;
  121. align-items: center;
  122. justify-content: space-between;
  123. margin-bottom: 20px;
  124. span {
  125. margin-top: 10px;
  126. }
  127. }
  128. }
  129. </style>