IconSelect.vue 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. <script lang="ts" setup>
  2. import { CSSProperties } from 'vue'
  3. import { cloneDeep } from 'lodash-es'
  4. import { IconJson } from '@/components/Icon/src/data'
  5. defineOptions({ name: 'IconSelect' })
  6. type ParameterCSSProperties = (item?: string) => CSSProperties | undefined
  7. const props = defineProps({
  8. modelValue: {
  9. require: false,
  10. type: String
  11. }
  12. })
  13. const emit = defineEmits<{ (e: 'update:modelValue', v: string) }>()
  14. const visible = ref(false)
  15. const inputValue = toRef(props, 'modelValue')
  16. const iconList = ref(IconJson)
  17. const icon = ref('add-location')
  18. const currentActiveType = ref('ep:')
  19. // 深拷贝图标数据,前端做搜索
  20. const copyIconList = cloneDeep(iconList.value)
  21. const pageSize = ref(96)
  22. const currentPage = ref(1)
  23. // 搜索条件
  24. const filterValue = ref('')
  25. const tabsList = [
  26. {
  27. label: 'Element Plus',
  28. name: 'ep:'
  29. },
  30. {
  31. label: 'Font Awesome 4',
  32. name: 'fa:'
  33. },
  34. {
  35. label: 'Font Awesome 5 Solid',
  36. name: 'fa-solid:'
  37. }
  38. ]
  39. const pageList = computed(() => {
  40. if (currentPage.value === 1) {
  41. return copyIconList[currentActiveType.value]
  42. ?.filter((v) => v.includes(filterValue.value))
  43. .slice(currentPage.value - 1, pageSize.value)
  44. } else {
  45. return copyIconList[currentActiveType.value]
  46. ?.filter((v) => v.includes(filterValue.value))
  47. .slice(
  48. pageSize.value * (currentPage.value - 1),
  49. pageSize.value * (currentPage.value - 1) + pageSize.value
  50. )
  51. }
  52. })
  53. const iconCount = computed(() => {
  54. return copyIconList[currentActiveType.value] == undefined
  55. ? 0
  56. : copyIconList[currentActiveType.value].length
  57. })
  58. const iconItemStyle = computed((): ParameterCSSProperties => {
  59. return (item) => {
  60. if (inputValue.value === currentActiveType.value + item) {
  61. return {
  62. borderColor: 'var(--el-color-primary)',
  63. color: 'var(--el-color-primary)'
  64. }
  65. }
  66. }
  67. })
  68. function handleClick({ props }) {
  69. currentPage.value = 1
  70. currentActiveType.value = props.name
  71. emit('update:modelValue', currentActiveType.value + iconList.value[currentActiveType.value][0])
  72. icon.value = iconList.value[currentActiveType.value][0]
  73. }
  74. function onChangeIcon(item) {
  75. icon.value = item
  76. emit('update:modelValue', currentActiveType.value + item)
  77. visible.value = false
  78. }
  79. function onCurrentChange(page) {
  80. currentPage.value = page
  81. }
  82. watch(
  83. () => {
  84. return props.modelValue
  85. },
  86. () => {
  87. if (props.modelValue && props.modelValue.indexOf(':') >= 0) {
  88. currentActiveType.value = props.modelValue.substring(0, props.modelValue.indexOf(':') + 1)
  89. icon.value = props.modelValue.substring(props.modelValue.indexOf(':') + 1)
  90. }
  91. }
  92. )
  93. watch(
  94. () => {
  95. return filterValue.value
  96. },
  97. () => {
  98. currentPage.value = 1
  99. }
  100. )
  101. </script>
  102. <template>
  103. <div class="selector">
  104. <ElInput v-model="inputValue" @click="visible = !visible">
  105. <template #append>
  106. <ElPopover
  107. :popper-options="{
  108. placement: 'auto'
  109. }"
  110. :visible="visible"
  111. :width="350"
  112. popper-class="pure-popper"
  113. trigger="click"
  114. >
  115. <template #reference>
  116. <div
  117. class="h-32px w-40px flex cursor-pointer items-center justify-center"
  118. @click="visible = !visible"
  119. >
  120. <Icon :icon="currentActiveType + icon" />
  121. </div>
  122. </template>
  123. <ElInput v-model="filterValue" class="p-2" clearable placeholder="搜索图标" />
  124. <ElDivider border-style="dashed" />
  125. <ElTabs v-model="currentActiveType" @tab-click="handleClick">
  126. <ElTabPane
  127. v-for="(pane, index) in tabsList"
  128. :key="index"
  129. :label="pane.label"
  130. :name="pane.name"
  131. >
  132. <ElDivider border-style="dashed" class="tab-divider" />
  133. <ElScrollbar height="220px">
  134. <ul class="ml-2 flex flex-wrap px-2">
  135. <li
  136. v-for="(item, key) in pageList"
  137. :key="key"
  138. :style="iconItemStyle(item)"
  139. :title="item"
  140. class="icon-item mr-2 mt-1 w-1/10 flex cursor-pointer items-center justify-center border border-solid p-2"
  141. @click="onChangeIcon(item)"
  142. >
  143. <Icon :icon="currentActiveType + item" />
  144. </li>
  145. </ul>
  146. </ElScrollbar>
  147. </ElTabPane>
  148. </ElTabs>
  149. <ElDivider border-style="dashed" />
  150. <ElPagination
  151. :current-page="currentPage"
  152. :page-size="pageSize"
  153. :total="iconCount"
  154. background
  155. class="h-10 flex items-center justify-center"
  156. layout="prev, pager, next"
  157. small
  158. @current-change="onCurrentChange"
  159. />
  160. </ElPopover>
  161. </template>
  162. </ElInput>
  163. </div>
  164. </template>
  165. <style lang="scss" scoped>
  166. .el-divider--horizontal {
  167. margin: 1px auto !important;
  168. }
  169. .tab-divider.el-divider--horizontal {
  170. margin: 0 !important;
  171. }
  172. .icon-item {
  173. &:hover {
  174. color: var(--el-color-primary);
  175. border-color: var(--el-color-primary);
  176. transform: scaleX(1.05);
  177. transition: all 0.4s;
  178. }
  179. }
  180. :deep(.el-tabs__nav-next) {
  181. font-size: 15px;
  182. line-height: 32px;
  183. box-shadow: -5px 0 5px -6px #ccc;
  184. }
  185. :deep(.el-tabs__nav-prev) {
  186. font-size: 15px;
  187. line-height: 32px;
  188. box-shadow: 5px 0 5px -6px #ccc;
  189. }
  190. :deep(.el-input-group__append) {
  191. padding: 0;
  192. }
  193. :deep(.el-tabs__item) {
  194. height: 30px;
  195. font-size: 12px;
  196. font-weight: normal;
  197. line-height: 30px;
  198. }
  199. :deep(.el-tabs__header),
  200. :deep(.el-tabs__nav-wrap) {
  201. position: static;
  202. margin: 0;
  203. }
  204. </style>