selector.vue 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. <template>
  2. <div class="el-selector" @click="handleFocus">
  3. <div
  4. v-if="selected && selected.length > 0"
  5. ref="tags"
  6. :style="{
  7. 'max-width': inputWidth + 'px',
  8. width: '100%',
  9. overflow: 'hidden',
  10. 'white-space': 'ellipsis',
  11. 'word-break': 'break-all',
  12. 'text-overflow': 'ellipsis',
  13. }"
  14. class="el-selector__tags"
  15. >
  16. <transition-group @after-leave="resetInputHeight">
  17. <template v-for="(item, index) in selected">
  18. <el-tag
  19. v-if="index === 0 || !tempSearch"
  20. :key="item + index"
  21. type="primary"
  22. :closable="!selectDisabled"
  23. :size="collapseTagSize"
  24. disable-transitions
  25. @close="deleteTag(index)"
  26. >
  27. <span class="el-selector__tags-text">{{ item }}</span>
  28. </el-tag>
  29. <el-tag
  30. v-else-if="tempSearch && index === 1"
  31. :key="item + index"
  32. :size="collapseTagSize"
  33. type="primary"
  34. >
  35. <span class="el-selector__tags-text"
  36. >+ {{ selected.length - 1 }}</span
  37. >
  38. </el-tag>
  39. </template>
  40. </transition-group>
  41. </div>
  42. <el-input
  43. ref="reference"
  44. v-model="selectedLabel"
  45. :disabled="selectDisabled"
  46. :validate-event="false"
  47. :size="selectSize"
  48. :placeholder="currentPlaceholder"
  49. type="text"
  50. :class="inputClass"
  51. @focus="handleFocus"
  52. @mouseenter.native="inputHovering = true"
  53. @mouseleave.native="inputHovering = false"
  54. >
  55. <i v-if="prefixIconClass" slot="prefix" :class="prefixIconClass" />
  56. </el-input>
  57. </div>
  58. </template>
  59. <script>
  60. import {
  61. addResizeListener,
  62. removeResizeListener,
  63. } from "@/plugins/element-ui/src/utils/resize-event";
  64. import emitter from "@/plugins/element-ui/src/mixins/emitter";
  65. const sizeMap = {
  66. medium: 36,
  67. small: 32,
  68. mini: 28,
  69. };
  70. export default {
  71. name: "ibps-selector",
  72. mixins: [emitter],
  73. props: {
  74. items: {
  75. type: Array,
  76. },
  77. placeholder: {
  78. type: String,
  79. default: "请选择",
  80. },
  81. multiple: {
  82. // 是否多选
  83. type: Boolean,
  84. default: false,
  85. },
  86. icon: {
  87. type: String,
  88. default: "el-icon-plus",
  89. },
  90. disabledIcon: {
  91. type: Boolean,
  92. default: false,
  93. },
  94. disabled: {
  95. type: Boolean,
  96. default: false,
  97. },
  98. readonly: {
  99. type: Boolean,
  100. default: false,
  101. },
  102. showPlaceholder: {
  103. // 是否显示占位符
  104. type: Boolean,
  105. default: false,
  106. },
  107. /**
  108. * 只读样式 【text original】
  109. */
  110. readonlyText: {
  111. type: String,
  112. default: "original",
  113. },
  114. inputBorderStyle: {
  115. type: String,
  116. },
  117. size: {
  118. type: String,
  119. default: "mini",
  120. },
  121. tempSearch: { // 是否是数据模板使用的筛选条件
  122. type: Boolean,
  123. default: false
  124. }
  125. },
  126. data() {
  127. return {
  128. query: "",
  129. inputLength: 20,
  130. inputWidth: 0,
  131. inputHovering: false,
  132. selected: [],
  133. };
  134. },
  135. computed: {
  136. hasValue() {
  137. return this.items && this.items.length > 0;
  138. },
  139. selectedLabel() {
  140. return this.hasValue ? " " : "";
  141. },
  142. prefixIconClass() {
  143. let classes = ["el-selector__caret", "el-input__icon"];
  144. if ((this.disabled || this.readonly) && !this.disabledIcon) {
  145. return;
  146. }
  147. if (this.hasValue) {
  148. classes = [...classes, this.icon, "is-show-close"];
  149. } else {
  150. classes.push(this.icon);
  151. }
  152. return classes;
  153. },
  154. inputClass() {
  155. let classes = [];
  156. if (this.readonlyText === "text") {
  157. classes = ["el-selector__readonly-text"];
  158. }
  159. if (this.inputBorderStyle) {
  160. classes = [...classes, "el-selector__" + this.inputBorderStyle];
  161. }
  162. return classes;
  163. },
  164. selectDisabled() {
  165. return this.disabled || (this.elForm || {}).disabled;
  166. },
  167. selectSize() {
  168. return (
  169. this.size ||
  170. (this.elFormItem || {}).elFormItemSize ||
  171. (this.$ELEMENT || {}).size
  172. );
  173. },
  174. collapseTagSize() {
  175. return ["small", "mini"].indexOf(this.selectSize) > -1
  176. ? "mini"
  177. : "small";
  178. },
  179. currentPlaceholder() {
  180. if (
  181. !this.showPlaceholder &&
  182. (this.readonly || this.selectDisabled)
  183. ) {
  184. return "";
  185. }
  186. if (
  187. !this.items ||
  188. (Array.isArray(this.items) && this.items.length === 0)
  189. ) {
  190. return this.placeholder;
  191. } else {
  192. return "";
  193. }
  194. },
  195. },
  196. watch: {
  197. items(val) {
  198. if (val) {
  199. this.setSelected();
  200. }
  201. },
  202. },
  203. mounted() {
  204. addResizeListener(this.$el, this.handleResize);
  205. const reference = this.$refs.reference;
  206. this.$nextTick(() => {
  207. if (reference && reference.$el) {
  208. this.inputWidth = reference.$el.getBoundingClientRect().width;
  209. }
  210. });
  211. this.setSelected();
  212. },
  213. beforeDestroy() {
  214. removeResizeListener(this.$el, this.handleResize);
  215. },
  216. methods: {
  217. setSelected() {
  218. const result = [];
  219. if (Array.isArray(this.items)) {
  220. this.items.forEach((item) => {
  221. result.push(item);
  222. });
  223. }
  224. this.selected = result;
  225. if (this.multiple) {
  226. this.$nextTick(this.resetInputHeight);
  227. }
  228. },
  229. resetInputWidth() {
  230. if (!this.$refs.reference) return;
  231. this.inputWidth =
  232. this.$refs.reference.$el.getBoundingClientRect().width;
  233. },
  234. resetInputHeight() {
  235. this.$nextTick(() => {
  236. if (!this.$refs.reference) return;
  237. const inputEl = this.$refs.reference.$refs.input;
  238. const tags = this.$refs.tags;
  239. let height = sizeMap[this.selectSize] || 40;
  240. if (this.selected.length !== 0) {
  241. height = Math.max(
  242. tags.clientHeight +
  243. (tags.clientHeight > height ? 6 : 0),
  244. height
  245. );
  246. }
  247. inputEl.style.height = `${height}px`;
  248. });
  249. },
  250. handleResize() {
  251. this.resetInputWidth();
  252. if (this.multiple) this.resetInputHeight();
  253. },
  254. handleFocus() {
  255. if (this.disabled) return;
  256. this.$refs.reference.blur();
  257. this.$emit("click");
  258. },
  259. deleteTag(index) {
  260. this.$emit("remove", index);
  261. },
  262. },
  263. };
  264. </script>