IconSelect.vue 5.6 KB

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