index.vue 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. <template>
  2. <el-input v-model="currentSelect" class="le-icon-picker" :disabled="disabled" clearable placeholder="请输入图标名称">
  3. <template #append>
  4. <el-popover v-model:visible="visible" popper-class="le-icon-picker_popover" placement="bottom-end" trigger="click">
  5. <template #reference>
  6. <PickerIcon class="icon-selected" :icon-class="currentSelect || 'Grid'" />
  7. </template>
  8. <el-input v-model="searchValue" placeholder="搜索图标" @clear="searchIconsHandler" @input="searchIconsHandler">
  9. <template #prepend>
  10. <el-select v-model="curIconType" :teleported="false" placeholder="Select" style="width: 100px">
  11. <el-option v-for="item in iconTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
  12. </el-select>
  13. </template>
  14. </el-input>
  15. <el-scrollbar class="local_scrollbar">
  16. <div class="icon-list">
  17. <template v-if="currentList.length">
  18. <div
  19. v-for="(item, index) in currentList"
  20. :key="index"
  21. :title="item"
  22. :class="`item ${item === currentSelect_name ? 'active' : ''}`"
  23. @click="updateIcon(item)"
  24. >
  25. <!--<le-icon class="local_icon" :icon-class="`${curIconType}${item}`" />-->
  26. <PickerIcon class="local_icon" :icon-class="`${curIconType}${item}`" />
  27. <span class="text text-overflow_ellipsis_line_2">{{ item }}</span>
  28. </div>
  29. </template>
  30. <LeNoData v-else size="small"></LeNoData>
  31. </div>
  32. </el-scrollbar>
  33. </el-popover>
  34. </template>
  35. </el-input>
  36. </template>
  37. <script setup lang="ts">
  38. import PickerIcon from '@/components/IconPicker/PickerIcon.vue'
  39. // import svgIcons from 'virtual:svg-icons-names'
  40. import { iconTypeOptions } from './iconsData.ts'
  41. import { useDebounceFn } from '@vueuse/core'
  42. import { ref, watch, computed } from 'vue'
  43. const props = defineProps({
  44. modelValue: {
  45. type: String,
  46. default: ''
  47. },
  48. disabled: {
  49. type: Boolean,
  50. default: false
  51. }
  52. })
  53. const curIconType = ref(iconTypeOptions[0].value)
  54. const curIconType_icons = ref(iconTypeOptions[0].icons)
  55. const currentList = ref(iconTypeOptions[0].icons)
  56. watch(curIconType, value => {
  57. const option = iconTypeOptions.find(v => v.value === value)
  58. if (option) {
  59. curIconType_icons.value = option.icons
  60. currentList.value = option.icons
  61. // 更新当前filter
  62. update_currentList()
  63. }
  64. })
  65. console.error(curIconType_icons, 'curIconType_icons')
  66. const currentSelect = ref('')
  67. const currentSelect_type = ref<string>()
  68. // 去除前缀的 name
  69. const currentSelect_name = computed(() => {
  70. let name = currentSelect.value
  71. if (name) {
  72. // icon, le-iconfont
  73. const bool = ['icon-', 'le-'].some(prefix => {
  74. if (name.startsWith(prefix)) {
  75. name = name.replace(prefix, '')
  76. currentSelect_type.value = prefix
  77. return true
  78. }
  79. })
  80. if (!bool) {
  81. // eslint-disable-next-line vue/no-side-effects-in-computed-properties
  82. currentSelect_type.value = ''
  83. }
  84. } else {
  85. // eslint-disable-next-line vue/no-side-effects-in-computed-properties
  86. currentSelect_type.value = undefined
  87. }
  88. return name
  89. })
  90. const visible = ref(false)
  91. watch(visible, (bool: boolean) => {
  92. // 弹窗开启时 进行定位筛查
  93. if (bool) {
  94. searchValue.value = currentSelect_name.value
  95. // 更新当前 icon类型
  96. if (typeof currentSelect_type.value === 'string') {
  97. curIconType.value = currentSelect_type.value
  98. }
  99. // 更新当前filter
  100. update_currentList()
  101. }
  102. })
  103. watch(
  104. () => props.modelValue,
  105. (val: string) => {
  106. currentSelect.value = val
  107. }
  108. )
  109. const searchValue = ref('')
  110. const update_currentList = () => {
  111. if (searchValue.value) {
  112. currentList.value = curIconType_icons.value.filter(item => item.indexOf(searchValue.value) !== -1)
  113. } else {
  114. currentList.value = curIconType_icons.value
  115. }
  116. }
  117. // 搜索
  118. const searchIconsHandler = useDebounceFn(update_currentList, 80)
  119. const emit = defineEmits(['update:modelValue', 'change'])
  120. function updateIcon(name: string) {
  121. const realName = curIconType.value + name
  122. // const realName = name
  123. emit('update:modelValue', realName)
  124. currentSelect.value = realName
  125. document.body.click()
  126. }
  127. function reset() {
  128. currentSelect.value = ''
  129. currentList.value = curIconType_icons
  130. }
  131. defineExpose({
  132. reset
  133. })
  134. </script>
  135. <style lang="scss">
  136. .#{$prefix}icon-picker {
  137. .el-input-group__append {
  138. padding: 0;
  139. }
  140. // 选中的icon
  141. .icon-selected {
  142. font-size: 16px;
  143. margin: 0 0.5rem;
  144. cursor: pointer;
  145. }
  146. // 弹窗
  147. &_popover {
  148. &.el-popper {
  149. //width: 360px;
  150. min-width: 300px;
  151. line-height: 1;
  152. //padding: 12px 0 0;
  153. }
  154. //.el-scrollbar__view {
  155. .local_scrollbar {
  156. margin-top: 10px;
  157. box-shadow: 0 0 0 1px var(--el-input-border-color, var(--el-border-color)) inset;
  158. border-radius: var(--el-input-border-radius, var(--el-border-radius-base));
  159. }
  160. .el-scrollbar__wrap {
  161. overflow-y: scroll;
  162. overflow-x: hidden;
  163. max-height: 22vh;
  164. }
  165. .icon-list {
  166. margin-top: 8px;
  167. //height: 200px;
  168. //overflow-y: scroll;
  169. .item {
  170. //height: 30px;
  171. //line-height: 30px;
  172. padding-bottom: 5px;
  173. cursor: pointer;
  174. width: 20%;
  175. float: left;
  176. //color: #999;
  177. //color: var(--el-text-color-secondary);
  178. color: var(--el-text-color-placeholder);
  179. display: flex;
  180. flex-direction: column;
  181. align-items: center;
  182. justify-content: center;
  183. &.active {
  184. color: var(--el-color-primary);
  185. }
  186. }
  187. .local_icon {
  188. height: 24px;
  189. width: 24px;
  190. font-size: 24px;
  191. //width: 16px;
  192. //margin-right: 5px;
  193. }
  194. .text {
  195. font-size: 12px;
  196. padding: 2px 2px 0;
  197. -webkit-line-clamp: 1;
  198. // flex: 1;
  199. }
  200. }
  201. }
  202. }
  203. </style>