AssociationInput.vue 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. <template>
  2. <!-- 联想组件 -->
  3. <div class="association-container" ref="association">
  4. <el-input
  5. class="association-input"
  6. :value="value"
  7. :placeholder="placeholder"
  8. :suffix-icon="suffixIcon"
  9. :clearable="clearable"
  10. @focus="preSearch.focus = true"
  11. @blur="onBlur"
  12. @input="onInput"
  13. ></el-input>
  14. <div
  15. class="pre-search-list"
  16. :style="{ top: preSearch.top + 'px' }"
  17. v-show="preSearchListShow"
  18. @mouseout="preSearch.hover = false"
  19. @mouseover="preSearch.hover = true"
  20. >
  21. <div
  22. class="pre-search-item ellipsis"
  23. v-for="(item, index) in preSearch.list"
  24. :key="index"
  25. @click="selectSearchItem(item)"
  26. >
  27. {{ item.value }}
  28. </div>
  29. </div>
  30. </div>
  31. </template>
  32. <script>
  33. import { Input } from 'element-ui'
  34. import { debounce } from 'lodash'
  35. export default {
  36. name: 'AssociationInput',
  37. components: {
  38. [Input.name]: Input
  39. },
  40. props: {
  41. value: {
  42. type: String,
  43. default: ''
  44. },
  45. // 是否立即触发一次联想
  46. immediateAssociation: {
  47. type: Boolean,
  48. default: false
  49. },
  50. fetchAsync: {
  51. type: Function
  52. },
  53. clearable: {
  54. type: Boolean,
  55. default: false
  56. },
  57. suffixIcon: {
  58. type: String,
  59. default: 'el-icon-search'
  60. },
  61. placeholder: {
  62. type: String,
  63. default: ''
  64. }
  65. },
  66. data() {
  67. return {
  68. preSearch: {
  69. top: 0,
  70. hover: false,
  71. focus: false,
  72. list: []
  73. }
  74. }
  75. },
  76. computed: {
  77. preSearchListShow() {
  78. return (
  79. this.value.trim().length >= 2 &&
  80. this.preSearch.list.length &&
  81. (this.preSearch.focus || this.preSearch.hover)
  82. )
  83. }
  84. },
  85. created() {
  86. if (this.immediateAssociation) {
  87. this.preSearchList()
  88. }
  89. },
  90. mounted() {
  91. this.calcPreSearchTop()
  92. },
  93. methods: {
  94. calcPreSearchTop() {
  95. const { association } = this.$refs
  96. this.preSearch.top = association.clientHeight + 2
  97. },
  98. onInput(e) {
  99. this.$emit('input', e)
  100. this.preSearchList(e)
  101. },
  102. onBlur(e) {
  103. this.$emit('blur', e)
  104. this.$nextTick(() => {
  105. this.preSearch.focus = false
  106. })
  107. },
  108. preSearchList: debounce(async function (e) {
  109. const params = {
  110. value: this.value
  111. }
  112. if (params.value.length < 2) return
  113. let list = []
  114. if (this.fetchAsync) {
  115. try {
  116. const cbArr = await this.fetchAsync(params)
  117. if (Array.isArray(cbArr)) {
  118. list = cbArr
  119. }
  120. } catch (error) {
  121. console.log(error)
  122. }
  123. }
  124. this.preSearch.list = list
  125. }, 300),
  126. selectSearchItem(item) {
  127. this.$emit('select', item)
  128. // 选择后马上关闭备选列表
  129. this.$nextTick(() => {
  130. this.preSearch.focus = false
  131. this.preSearch.hover = false
  132. })
  133. }
  134. }
  135. }
  136. </script>
  137. <style lang="scss" scoped>
  138. ::v-deep {
  139. .el-input__inner {
  140. width: 326px;
  141. height: 30px;
  142. line-height: 30px;
  143. border-color: #e0e0e0;
  144. }
  145. .el-input__icon {
  146. line-height: 30px;
  147. }
  148. }
  149. .association-container {
  150. position: relative;
  151. display: inline-block;
  152. }
  153. .pre-search-list {
  154. padding: 10px 0;
  155. position: absolute;
  156. z-index: 6;
  157. width: 100%;
  158. background: #fff;
  159. box-shadow: 0 0 10px rgb(0, 0, 0, 0.1);
  160. border-radius: 4px;
  161. overflow: hidden;
  162. }
  163. .pre-search-item {
  164. padding: 0 20px;
  165. width: 100%;
  166. font-size: 14px;
  167. line-height: 34px;
  168. color: #606266;
  169. box-sizing: border-box;
  170. transition: all 0.3s;
  171. cursor: pointer;
  172. }
  173. .pre-search-item:hover {
  174. color: #1d1d1d;
  175. background-color: #ececec;
  176. }
  177. </style>