InputDropdown.vue 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. <template lang="pug">
  2. .input-group
  3. .input-group-button(v-if='withPrefix')
  4. button(type='button') {{ prefix }}
  5. input.form-input(type='text' v-model='formattedValue' :style="{'width': withPrefix || withSuffix ? '88%' : '100%'}")
  6. .input-group-button(v-if='withSuffix')
  7. button(type='button' @click='onShowSuffixOptions' ref='suffixButton') {{ suffix }}
  8. span.caret(:style="{'display': withSuffixOptions() ? 'none' : 'inline-block'}")
  9. ul.dropdown(:style="{'display': isShowSuffixOptions ? 'block' : 'none'}" ref='suffixDropdown')
  10. li(v-for='option in suffixOptions' :key='option.id')
  11. a(@click='onClickSuffixOption(option)') {{ option.name }}
  12. </template>
  13. <script>
  14. export default {
  15. props: {
  16. value: {
  17. type: String,
  18. default: ''
  19. },
  20. format: {
  21. type: String,
  22. default: 'text'
  23. },
  24. withPrefix: {
  25. type: Boolean,
  26. default: false
  27. },
  28. prefix: {
  29. type: String,
  30. default: ''
  31. },
  32. prefixOptions: {
  33. type: Array,
  34. default: []
  35. },
  36. withSuffix: {
  37. type: Boolean,
  38. default: false
  39. },
  40. suffix: {
  41. type: String,
  42. default: ''
  43. },
  44. suffixOptions: {
  45. type: Array,
  46. default: []
  47. }
  48. },
  49. computed: {
  50. formattedValue: {
  51. get() {
  52. let value = ''
  53. if (this.format === 'number') {
  54. value = this.formatValue(this.value)
  55. value = !!this.value ? value : value.replace(/\d/, '')
  56. }
  57. return value
  58. },
  59. set(value) {
  60. if (this.format === 'number') {
  61. value = this.unformatValue(value)
  62. }
  63. this.value = value
  64. this.onChange(value)
  65. }
  66. }
  67. },
  68. methods: {
  69. formatValue(value, options) {
  70. value = value.toString()
  71. if (!(options instanceof Object)) {
  72. options = {}
  73. }
  74. options.thousandsSeparator = options.thousandsSeparator || '.'
  75. options.decimalPlaces = options.decimalPlaces >= 0 || options.decimalPlaces <= 2 ? options.decimalPlaces : 2
  76. options.decimalSeparator = options.decimalSeparator || ','
  77. options.decimalZeros = !!options.decimalZeros
  78. if (!!(`${options.thousandsSeparator}${options.decimalSeparator}`).replace(/\.,|,\./g, '')) {
  79. throw new Error('Same thousands and decimal separator is not allowed')
  80. }
  81. value = value.split('.')
  82. value[0] = value[0].replace(/(\d)(?=(\d\d\d)+(?!\d))/g, `$1${options.thousandsSeparator}`)
  83. if (!!value[1]) {
  84. value[1] = Number.parseFloat(`1.${value[1]}e${options.decimalPlaces}`)
  85. value[1] = Math.round(value[1]).toString().replace(/^1/, '')
  86. }
  87. value = value.join(options.decimalSeparator)
  88. if (!options.decimalZeros) {
  89. value = value.replace(/([\.|,]\d)0$/, '$1')
  90. }
  91. return value
  92. },
  93. unformatValue(value) {
  94. value = value.replace(/[\.|,](\d{0,2}$)/, '?$1').split(/\?/)
  95. value[0] = value[0].replace(/[^0-9]/g, '')
  96. value = Number.parseFloat(value.join('.')) || 0
  97. return value
  98. },
  99. onChange(data) {
  100. this.$emit('onChange', data)
  101. },
  102. hideSuffixOptions() {
  103. this.isShowSuffixOptions = false
  104. },
  105. withPrefixOptions() {
  106. return this.prefixOptions.length == 0
  107. },
  108. withSuffixOptions() {
  109. return this.suffixOptions.length == 0
  110. },
  111. onShowSuffixOptions() {
  112. if (this.withSuffixOptions()) {
  113. return
  114. }
  115. this.isShowSuffixOptions = !this.isShowSuffixOptions
  116. },
  117. onClickOutside(e) {
  118. let suffixButton = this.$refs.suffixButton
  119. if (!suffixButton) {
  120. return
  121. }
  122. let target = e.target
  123. if (target === suffixButton) {
  124. return
  125. }
  126. let el = this.$refs.suffixDropdown
  127. if (el !== target && !el.contains(target)) {
  128. this.hideSuffixOptions()
  129. }
  130. },
  131. onClickSuffixOption(suffixOption) {
  132. this.hideSuffixOptions()
  133. this.$emit('onSuffix', suffixOption)
  134. }
  135. },
  136. created() {
  137. document.addEventListener('click', this.onClickOutside)
  138. },
  139. destroyed() {
  140. document.removeEventListener('click', this.onClickOutside)
  141. },
  142. data() {
  143. return {
  144. value: '',
  145. isShowSuffixOptions: false
  146. }
  147. }
  148. }
  149. </script>
  150. <style lang="sass">
  151. .input-group
  152. position: relative
  153. display: table
  154. border-collapse: separate
  155. .form-input
  156. position: relative
  157. width: 100%
  158. height: 35px
  159. float: left
  160. z-index: 2
  161. display: table-cell
  162. margin-bottom: 0
  163. border-radius: 0
  164. font-size: 12pt
  165. line-height: 1.42857143
  166. padding: 6px 12px
  167. &:focus
  168. z-index: 3
  169. .input-group-button
  170. position: relative
  171. font-size: 0
  172. width: 1%
  173. white-space: nowrap
  174. vertical-align: middle
  175. display: table-cell
  176. &:first-child
  177. & > button
  178. margin-right: -1px
  179. &:last-child
  180. & > button
  181. margin-left: -1px
  182. button
  183. height: 35px
  184. display: inline-block
  185. margin-bottom: 0
  186. text-align: center
  187. vertical-align: middle
  188. white-space: nowrap
  189. border: 1px solid #ccc
  190. border-radius: 0
  191. box-shadow: none
  192. background: #e0e0e0
  193. z-index: 2
  194. position: relative
  195. user-select: none
  196. &:focus
  197. outline: 0 !important
  198. border: 1px solid #ccc
  199. box-shadow: none
  200. &:caret
  201. display: inline-block
  202. width: 0
  203. height: 0
  204. margin-left: 2px
  205. vertical-align: middle
  206. border-top: 4px dashed
  207. border-right: 4px solid transparent
  208. border-left: 4px solid transparent
  209. .dropdown
  210. position: absolute
  211. top: 100%
  212. float: left
  213. left: auto
  214. right: 0
  215. list-style: none
  216. text-align: left
  217. border: 1px solid #ccc
  218. background-color: #fff
  219. background-clip: padding-box
  220. min-width: 160px
  221. padding: 5px 0
  222. margin: 2px 0 0
  223. z-index: 1000
  224. & > li > a
  225. height: 35px
  226. display: block
  227. padding: 3px 20px
  228. clear: both
  229. font-weight: normal
  230. line-height: 2.1
  231. white-space: nowrap
  232. color: #333
  233. border-bottom: 1px solid #e5e5e5
  234. &:hover, &:focus
  235. text-decoration: none
  236. border-bottom: 2px solid #7c7bad
  237. </style>