form-item.vue 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. <template>
  2. <div class="cube-form-item border-bottom-1px" ref="formItem" :class="itemClass">
  3. <template v-if="!isBtnField">
  4. <slot name="label">
  5. <div class="cube-form-label" v-show="fieldValue.label"><span>{{fieldValue.label}}</span></div>
  6. </slot>
  7. <cube-validator
  8. class="cube-form-field"
  9. v-if="hasRules"
  10. ref="validator"
  11. v-model="originValid"
  12. :disabled="validatorDisabled"
  13. :model="validatorModel"
  14. :model-key="validatorModelKey"
  15. :rules="fieldValue.rules"
  16. :messages="fieldValue.messages"
  17. @input="validatorChangeHandler"
  18. @validating="validatingHandler"
  19. @validated="validatedHandler"
  20. @msg-click="msgClick"
  21. >
  22. <slot>
  23. <component :is="componentName" v-model="modelValue" v-bind="fieldValue.props" v-on="fieldValue.events"></component>
  24. </slot>
  25. </cube-validator>
  26. <div class="cube-form-field" v-else>
  27. <slot>
  28. <component :is="componentName" v-model="modelValue" v-bind="fieldValue.props" v-on="fieldValue.events"></component>
  29. </slot>
  30. </div>
  31. </template>
  32. <cube-button v-bind="fieldValue.props" v-else>{{fieldValue.label}}</cube-button>
  33. </div>
  34. </template>
  35. <script>
  36. import { processField } from './fields/index'
  37. import { resetTypeValue, cb2PromiseWithResolve, debounce } from '../../common/helpers/util'
  38. import CubeValidator from '../validator/validator.vue'
  39. import LAYOUTS from './layouts'
  40. import { getResetValueByType } from './fields/reset'
  41. import mixin from './mixin'
  42. import components from './components'
  43. components.CubeValidator = CubeValidator
  44. const COMPONENT_NAME = 'cube-form-item'
  45. const EVENT_FOCUSIN = 'focusin'
  46. const EVENT_FOCUSOUT = 'focusout'
  47. export default {
  48. name: COMPONENT_NAME,
  49. mixins: [mixin],
  50. props: {
  51. field: {
  52. type: Object,
  53. default() {
  54. /* istanbul ignore next */
  55. return {}
  56. }
  57. }
  58. },
  59. data() {
  60. const validatorModelKey = 'value'
  61. const modelKey = this.field.modelKey
  62. const modelValue = modelKey ? this.form.model[modelKey] : null
  63. return {
  64. validatorDisabled: false,
  65. validatorModelKey,
  66. modelValue: modelValue,
  67. validatorModel: {
  68. [validatorModelKey]: modelValue
  69. }
  70. }
  71. },
  72. computed: {
  73. fieldValue() {
  74. return processField(this.field)
  75. },
  76. hasRules() {
  77. return Object.keys(this.fieldValue.rules || {}).length > 0
  78. },
  79. isBtnField() {
  80. return this.fieldValue.type === 'button'
  81. },
  82. itemClass() {
  83. const rules = this.fieldValue.rules
  84. return {
  85. // only handle required rule for now
  86. 'cube-form-item_required': rules && rules.required,
  87. 'cube-form-item_btn': this.isBtnField,
  88. 'cube-form-item_validating': this.validating,
  89. 'cube-form-item_pending': this.pending,
  90. 'cube-form-item_valid': this.valid,
  91. 'cube-form-item_invalid': this.invalid
  92. }
  93. },
  94. modelVal() {
  95. return this.form.model[this.fieldValue.modelKey]
  96. },
  97. componentName() {
  98. const fieldValue = this.fieldValue
  99. const component = fieldValue.component
  100. if (component) {
  101. return component
  102. }
  103. const type = fieldValue.type
  104. const cubeType = `cube-${type}`
  105. if (components[cubeType]) {
  106. return cubeType
  107. }
  108. return type
  109. }
  110. },
  111. watch: {
  112. modelVal(newModel) {
  113. if (this.modelValue !== newModel) {
  114. this.modelValue = newModel
  115. }
  116. },
  117. modelValue: {
  118. handler(newModel) {
  119. // update form model
  120. this.form.model[this.fieldValue.modelKey] = newModel
  121. this.updateValidatorModel()
  122. },
  123. sync: true
  124. },
  125. originValid(newVal) {
  126. this.lastOriginValid = newVal
  127. }
  128. },
  129. beforeCreate() {
  130. this.form = this.$parent.form
  131. },
  132. created() {
  133. this.form.addField(this)
  134. this.getValidatorModel = (modelValue) => {
  135. this.pending = false
  136. return modelValue
  137. }
  138. },
  139. mounted() {
  140. this.initDebounce()
  141. this.initFocusEvents()
  142. },
  143. methods: {
  144. initDebounce() {
  145. let debounceTime = this.fieldValue.debounce
  146. if (debounceTime === true) {
  147. debounceTime = 200
  148. }
  149. if ((!debounceTime && debounceTime !== 0) || debounceTime < 0 || this.fieldValue.trigger === 'blur') return
  150. this.getValidatorModel = debounce((modelValue) => {
  151. this.pending = false
  152. this.validatorModel[this.validatorModelKey] = modelValue
  153. this.form.updatePending()
  154. this.validate()
  155. return modelValue
  156. }, debounceTime, false, this.validatorModel[this.validatorModelKey])
  157. },
  158. focusInHandler() {
  159. this.focused = true
  160. },
  161. focusOutHandler() {
  162. this.focused = false
  163. this.updateValidatorModel()
  164. this.validate()
  165. },
  166. initFocusEvents() {
  167. if (this.fieldValue.trigger === 'blur') {
  168. const formItem = this.$refs.formItem
  169. formItem.addEventListener(EVENT_FOCUSIN, this.focusInHandler, false)
  170. formItem.addEventListener(EVENT_FOCUSOUT, this.focusOutHandler, false)
  171. this.getValidatorModel = (modelValue) => {
  172. if (this.focused) {
  173. return this.validatorModel[this.validatorModelKey]
  174. } else {
  175. this.pending = false
  176. this.form.updatePending()
  177. return modelValue
  178. }
  179. }
  180. }
  181. },
  182. removeFocusEvents() {
  183. const formItem = this.$refs.formItem
  184. formItem.removeEventListener(EVENT_FOCUSIN, this.focusInHandler, false)
  185. formItem.removeEventListener(EVENT_FOCUSOUT, this.focusOutHandler, false)
  186. },
  187. updateValidatorModel() {
  188. this.pending = true
  189. this.validatorModel[this.validatorModelKey] = this.getValidatorModel(this.modelValue)
  190. if (this.pending) {
  191. this.form.setPending(this.pending)
  192. this.originValid = undefined
  193. }
  194. },
  195. validatorChangeHandler() {
  196. // disabled or true to true no update validity
  197. if (this.validatorDisabled || (this.originValid && this.lastOriginValid)) {
  198. return
  199. }
  200. this.updateValidity()
  201. },
  202. validatingHandler() {
  203. this.validating = true
  204. this.form.setValidating(true)
  205. },
  206. validatedHandler() {
  207. this.validating = false
  208. this.form.updateValidating()
  209. },
  210. updateValidity() {
  211. const validator = this.$refs.validator
  212. if (validator) {
  213. // sync update validaty
  214. this.form.updateValidity(this.fieldValue.modelKey, validator.valid, validator.result, validator.dirty)
  215. }
  216. },
  217. validate(cb) {
  218. const promise = cb2PromiseWithResolve(cb)
  219. if (promise) {
  220. cb = promise.resolve
  221. }
  222. const validator = this.$refs.validator
  223. if (validator) {
  224. validator.validate(() => {
  225. this.validatorDisabled = true
  226. this.updateValidity()
  227. cb && cb()
  228. this.$nextTick(() => {
  229. this.validatorDisabled = false
  230. })
  231. })
  232. } else {
  233. cb && cb()
  234. }
  235. return promise
  236. },
  237. reset() {
  238. const fieldValue = this.fieldValue
  239. if (fieldValue.modelKey) {
  240. const defValue = getResetValueByType(fieldValue.type)
  241. this.validatorDisabled = true
  242. resetTypeValue(this, 'modelValue', defValue)
  243. this.$refs.validator && this.$refs.validator.reset()
  244. this.$nextTick(() => {
  245. this.validatorDisabled = false
  246. })
  247. }
  248. this.validating = false
  249. this.pending = false
  250. },
  251. msgClick() {
  252. /* istanbul ignore if */
  253. if (this.form.layout !== LAYOUTS.STANDARD) {
  254. return
  255. }
  256. /* istanbul ignore next */
  257. this.$createToast && this.$createToast({
  258. type: 'warn',
  259. txt: this.$refs.validator.msg,
  260. time: 1000
  261. }).show()
  262. }
  263. },
  264. beforeDestroy() {
  265. this.removeFocusEvents()
  266. this.form.destroyField(this)
  267. this.form = null
  268. },
  269. components
  270. }
  271. </script>
  272. <style lang="stylus" rel="stylesheet/stylus">
  273. @require "../../common/stylus/variable.styl"
  274. @require "../../common/stylus/mixin.styl"
  275. .cube-form-item
  276. position: relative
  277. display: flex
  278. align-items: center
  279. padding: 0 15px
  280. &:last-child
  281. &::after
  282. display: none
  283. .cube-checkbox-group, .cube-radio-group
  284. background-color: transparent
  285. .cube-checkbox, .cube-radio
  286. padding-left: 0
  287. padding-right: 0
  288. .cube-form-item_btn
  289. margin: 15px 0
  290. &::after
  291. display: none
  292. .cube-form-label
  293. display: flex
  294. align-items: center
  295. word-wrap: break-word
  296. word-break: break-word
  297. flex-shrink: 0
  298. .cube-form-item_required
  299. .cube-form-label
  300. &::before
  301. content: "*"
  302. display: block
  303. margin-top: 1px
  304. margin-right: .3em
  305. color: $form-label-required-color
  306. </style>