index.vue 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. <template>
  2. <div class="go-chart-search">
  3. <n-popover
  4. class="chart-search-popover"
  5. :show-arrow="false"
  6. :show="showPopover"
  7. :to="false"
  8. trigger="hover"
  9. placement="bottom-start"
  10. >
  11. <template #trigger>
  12. <n-input-group>
  13. <n-input
  14. v-model:value.trim="search"
  15. size="small"
  16. :loading="loading"
  17. placeholder="请输入组件名称"
  18. @update:value="searchHandle"
  19. >
  20. <template #suffix>
  21. <n-icon v-show="!loading" :component="SearchIcon" />
  22. </template>
  23. </n-input>
  24. </n-input-group>
  25. </template>
  26. <div class="search-list-box">
  27. <n-scrollbar style="max-height: 500px">
  28. <n-empty
  29. v-show="!searchRes.length"
  30. size="small"
  31. description="没有找到组件~"
  32. ></n-empty>
  33. <div
  34. class="list-item go-flex-items-center go-ellipsis-1"
  35. v-for="item in searchRes"
  36. :key="item.key"
  37. :title="item.title"
  38. @click="selectChartHandle(item)"
  39. >
  40. <img class="list-item-img" v-lazy="item.image" alt="展示图" />
  41. <n-text depth="2">{{ item.title }}</n-text>
  42. </div>
  43. </n-scrollbar>
  44. <div class="popover-modal"></div>
  45. </div>
  46. </n-popover>
  47. </div>
  48. </template>
  49. <script setup lang="ts">
  50. import { ref, onUnmounted } from 'vue'
  51. import { icon } from '@/plugins'
  52. import { themeColor, MenuOptionsType } from '../../hooks/useAside.hook'
  53. import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
  54. import { ConfigType, CreateComponentType } from '@/packages/index.d'
  55. import { createComponent } from '@/packages'
  56. import { isString, addEventListener, removeEventListener } from '@/utils'
  57. import { fetchConfigComponent, fetchChartComponent } from '@/packages/index'
  58. import {
  59. componentInstall,
  60. loadingStart,
  61. loadingFinish,
  62. loadingError,
  63. } from '@/utils'
  64. const props = defineProps({
  65. menuOptions: {
  66. type: Array,
  67. default: () => [],
  68. },
  69. })
  70. const { SearchIcon } = icon.ionicons5
  71. const chartEditStore = useChartEditStore()
  72. const showPopover = ref<boolean>(false)
  73. const loading = ref<boolean | undefined>(undefined)
  74. const search = ref<string | null>(null)
  75. const searchRes = ref<ConfigType[]>([])
  76. // 组件数组提取
  77. const listFormatHandle = (options: any[]) => {
  78. const arr = []
  79. for (const item of options) {
  80. arr.push(...item.list)
  81. }
  82. return arr
  83. }
  84. // 组件列表
  85. const List = listFormatHandle(props.menuOptions as unknown as ConfigType[])
  86. // 关闭处理
  87. const closeHandle = () => {
  88. loading.value = undefined
  89. showPopover.value = false
  90. search.value = null
  91. searchRes.value = []
  92. }
  93. // 搜索处理
  94. const searchHandle = (key: string | null) => {
  95. if (!isString(key) || !key.length) {
  96. closeHandle()
  97. return
  98. }
  99. loading.value = true
  100. showPopover.value = true
  101. searchRes.value = List.filter(
  102. (e: ConfigType) => !key || e.title.toLowerCase().includes(key.toLowerCase())
  103. )
  104. setTimeout(() => {
  105. loading.value = undefined
  106. }, 500)
  107. }
  108. // 关闭处理
  109. const listenerCloseHandle = (e: Event) => {
  110. if (!showPopover.value) return
  111. if (!e.target) return
  112. if (!(e.target as any).closest('.go-chart-search')) {
  113. closeHandle()
  114. }
  115. }
  116. // 选择处理
  117. const selectChartHandle = async (item: ConfigType) => {
  118. try {
  119. loadingStart()
  120. // 动态注册图表组件
  121. componentInstall(item.chartKey, fetchChartComponent(item))
  122. componentInstall(item.conKey, fetchConfigComponent(item))
  123. // 创建新图表组件
  124. let newComponent: CreateComponentType = await createComponent(item)
  125. // 添加
  126. chartEditStore.addComponentList(newComponent, false, true)
  127. // 选中
  128. chartEditStore.setTargetSelectChart(newComponent.id)
  129. // 清空搜索
  130. closeHandle()
  131. loadingFinish()
  132. } catch (error) {
  133. loadingError()
  134. window['$message'].warning(`图表正在研发中, 敬请期待...`)
  135. }
  136. }
  137. addEventListener(document, 'click', (e: Event) => {
  138. listenerCloseHandle(e)
  139. })
  140. onUnmounted(() => {
  141. removeEventListener(document, 'click', listenerCloseHandle)
  142. })
  143. </script>
  144. <style lang="scss" scoped>
  145. $width: 178px;
  146. @include go('chart-search') {
  147. width: $width;
  148. margin-right: -12px;
  149. .chart-search-popover {
  150. .search-list-box {
  151. width: calc(#{$width} - 30px);
  152. .list-item {
  153. z-index: 2;
  154. position: relative;
  155. cursor: pointer;
  156. padding: 2px;
  157. margin-bottom: 5px;
  158. &-img {
  159. height: 30px;
  160. margin-right: 5px;
  161. border-radius: 5px;
  162. }
  163. &:hover {
  164. &::after {
  165. z-index: -1;
  166. content: '';
  167. position: absolute;
  168. width: 100%;
  169. height: 100%;
  170. opacity: 0.1;
  171. left: 0;
  172. top: 0;
  173. border-radius: 5px;
  174. background-color: v-bind('themeColor');
  175. }
  176. }
  177. }
  178. }
  179. }
  180. }
  181. </style>