uni-forms-item.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. <template>
  2. <view class="uni-forms-item" :class="{'uni-forms-item--border':border,'is-first-border':border&&isFirstBorder,'uni-forms-item-error':msg}">
  3. <view class="uni-forms-item__inner" :class="['is-direction-'+labelPos,]">
  4. <view v-if="label" class="uni-forms-item__label" :style="{width:labelWid+'px',justifyContent: justifyContent}">
  5. <slot name="left">
  6. <uni-icons v-if="leftIcon" class="label-icon" size="16" :type="leftIcon" :color="iconColor" />
  7. <text>{{label}}</text>
  8. <text v-if="required" class="is-required">*</text>
  9. </slot>
  10. </view>
  11. <view class="uni-forms-item__content" :class="{'is-input-error-border': msg}">
  12. <slot></slot>
  13. </view>
  14. </view>
  15. <view class="uni-error-message" :class="{'uni-error-msg--boeder':border}" :style="{
  16. paddingLeft: (labelPos === 'left'? Number(labelWid)+5:5) + 'px'
  17. }">{{ showMsg === 'undertext' ? msg:'' }}</view>
  18. </view>
  19. </template>
  20. <script>
  21. /**
  22. * Field 输入框
  23. * @description 此组件可以实现表单的输入与校验,包括 "text" 和 "textarea" 类型。
  24. * @tutorial https://ext.dcloud.net.cn/plugin?id=21001
  25. * @property {Boolean} required 是否必填,左边显示红色"*"号(默认false)
  26. * @property {String} validateTrigger = [bind|submit] 校验触发器方式 默认 submit 可选
  27. * @value bind 发生变化时触发
  28. * @value submit 提交时触发
  29. * @property {String } leftIcon label左边的图标,限 uni-ui 的图标名称
  30. * @property {String } iconColor 左边通过icon配置的图标的颜色(默认#606266)
  31. * @property {String } label 输入框左边的文字提示
  32. * @property {Number } labelWidth label的宽度,单位px(默认65)
  33. * @property {String } labelAlign = [left|center|right] label的文字对齐方式(默认left)
  34. * @value left label 左侧显示
  35. * @value center label 居中
  36. * @value right label 右侧对齐
  37. * @property {String } labelPosition = [top|left] label的文字的位置(默认left)
  38. * @value top 顶部显示 label
  39. * @value left 左侧显示 label
  40. * @property {String } errorMessage 显示的错误提示内容,如果为空字符串或者false,则不显示错误信息
  41. * @property {String } name 表单域的属性名,在使用校验规则时必填
  42. */
  43. export default {
  44. name: "uniFormsItem",
  45. props: {
  46. // 自定义内容
  47. custom: {
  48. type: Boolean,
  49. default: false
  50. },
  51. // 是否显示报错信息
  52. showMessage: {
  53. type: Boolean,
  54. default: true
  55. },
  56. name: String,
  57. required: Boolean,
  58. validateTrigger: {
  59. type: String,
  60. default: ''
  61. },
  62. leftIcon: String,
  63. iconColor: {
  64. type: String,
  65. default: '#606266'
  66. },
  67. label: String,
  68. // 左边标题的宽度单位px
  69. labelWidth: {
  70. type: [Number, String],
  71. default: ''
  72. },
  73. // 对齐方式,left|center|right
  74. labelAlign: {
  75. type: String,
  76. default: ''
  77. },
  78. // lable的位置,可选为 left-左边,top-上边
  79. labelPosition: {
  80. type: String,
  81. default: ''
  82. },
  83. errorMessage: {
  84. type: [String, Boolean],
  85. default: ''
  86. }
  87. },
  88. data() {
  89. return {
  90. errorTop: false,
  91. errorBottom: false,
  92. labelMarginBottom: '',
  93. errorWidth: '',
  94. errMsg: '',
  95. val: '',
  96. labelPos: '',
  97. labelWid: '',
  98. labelAli: '',
  99. showMsg: 'undertext',
  100. border: false,
  101. isFirstBorder: false
  102. };
  103. },
  104. computed: {
  105. msg() {
  106. return this.errorMessage || this.errMsg;
  107. },
  108. fieldStyle() {
  109. let style = {}
  110. if (this.labelPos == 'top') {
  111. style.padding = '0 0'
  112. this.labelMarginBottom = '6px'
  113. }
  114. if (this.labelPos == 'left' && this.msg !== false && this.msg != '') {
  115. style.paddingBottom = '0px'
  116. this.errorBottom = true
  117. this.errorTop = false
  118. } else if (this.labelPos == 'top' && this.msg !== false && this.msg != '') {
  119. this.errorBottom = false
  120. this.errorTop = true
  121. } else {
  122. // style.paddingBottom = ''
  123. this.errorTop = false
  124. this.errorBottom = false
  125. }
  126. return style
  127. },
  128. // uni不支持在computed中写style.justifyContent = 'center'的形式,故用此方法
  129. justifyContent() {
  130. if (this.labelAli === 'left') return 'flex-start';
  131. if (this.labelAli === 'center') return 'center';
  132. if (this.labelAli === 'right') return 'flex-end';
  133. }
  134. },
  135. watch: {
  136. validateTrigger(trigger) {
  137. this.formTrigger = trigger
  138. }
  139. },
  140. created() {
  141. this.form = this.getForm()
  142. this.group = this.getForm('uniGroup')
  143. this.formRules = []
  144. this.formTrigger = this.validateTrigger
  145. if (this.form) {
  146. this.form.childrens.push(this)
  147. }
  148. this.init()
  149. },
  150. destroyed() {
  151. if (this.form) {
  152. this.form.childrens.forEach((item, index) => {
  153. if (item === this) {
  154. this.form.childrens.splice(index, 1)
  155. delete this.form.formData[item.name]
  156. }
  157. })
  158. }
  159. },
  160. methods: {
  161. init() {
  162. if (this.form) {
  163. let {
  164. formRules,
  165. validator,
  166. formData,
  167. value,
  168. labelPosition,
  169. labelWidth,
  170. labelAlign,
  171. errShowType
  172. } = this.form
  173. this.labelPos = this.labelPosition ? this.labelPosition : labelPosition
  174. this.labelWid = this.label ? (this.labelWidth ? this.labelWidth : labelWidth) : 0
  175. this.labelAli = this.labelAlign ? this.labelAlign : labelAlign
  176. // 判断第一个 item
  177. if (!this.form.isFirstBorder) {
  178. this.form.isFirstBorder = true
  179. this.isFirstBorder = true
  180. }
  181. // 判断 group 里的第一个 item
  182. if (this.group) {
  183. if (!this.group.isFirstBorder) {
  184. this.group.isFirstBorder = true
  185. this.isFirstBorder = true
  186. }
  187. }
  188. this.border = this.form.border
  189. this.showMsg = errShowType
  190. if (formRules) {
  191. this.formRules = formRules[this.name] || {}
  192. }
  193. this.validator = validator
  194. } else {
  195. this.labelPos = this.labelPosition || 'left'
  196. this.labelWid = this.labelWidth || 65
  197. this.labelAli = this.labelAlign || 'left'
  198. }
  199. },
  200. /**
  201. * 获取父元素实例
  202. */
  203. getForm(name = 'uniForms') {
  204. let parent = this.$parent;
  205. let parentName = parent.$options.name;
  206. while (parentName !== name) {
  207. parent = parent.$parent;
  208. if (!parent) return false
  209. parentName = parent.$options.name;
  210. }
  211. return parent;
  212. },
  213. /**
  214. * 移除该表单项的校验结果
  215. */
  216. clearValidate() {
  217. this.errMsg = ''
  218. },
  219. setValue(value) {
  220. if (this.name) {
  221. if (this.errMsg) this.errMsg = ''
  222. this.form.formData[this.name] = this.form._getValue(this.name, value)
  223. if (!this.formRules || (typeof(this.formRules) && JSON.stringify(this.formRules) === '{}')) return
  224. this.triggerCheck(this.form._getValue(this.name, value))
  225. }
  226. },
  227. /**
  228. * 校验规则
  229. * @param {Object} value
  230. */
  231. async triggerCheck(value, callback) {
  232. let promise = null;
  233. this.errMsg = ''
  234. // if no callback, return promise
  235. // if (callback && typeof callback !== 'function' && Promise) {
  236. // promise = new Promise((resolve, reject) => {
  237. // callback = function(valid) {
  238. // !valid ? resolve(valid) : reject(valid)
  239. // };
  240. // });
  241. // }
  242. // if (!this.validator) {
  243. // typeof callback === 'function' && callback(null);
  244. // if (promise) return promise
  245. // }
  246. if (!this.validator) return
  247. const isNoField = this.isRequired(this.formRules.rules || [])
  248. let isTrigger = this.isTrigger(this.formRules.validateTrigger, this.validateTrigger, this.form.validateTrigger)
  249. let result = null
  250. if (!(!isTrigger)) {
  251. result = await this.validator.validateUpdate({
  252. [this.name]: value
  253. }, this.form.formData)
  254. }
  255. // 判断是否必填
  256. if (!isNoField && !value) {
  257. result = null
  258. }
  259. if (isTrigger && result && result.errorMessage) {
  260. const inputComp = this.form.inputChildrens.find(child => child.rename === this.name)
  261. if (inputComp) {
  262. inputComp.errMsg = result.errorMessage
  263. }
  264. if (this.form.errShowType === 'toast') {
  265. uni.showToast({
  266. title: result.errorMessage || '校验错误',
  267. icon: 'none'
  268. })
  269. }
  270. if (this.form.errShowType === 'modal') {
  271. uni.showModal({
  272. title: '提示',
  273. content: result.errorMessage || '校验错误'
  274. })
  275. }
  276. }
  277. this.errMsg = !result ? '' : result.errorMessage
  278. // 触发validate事件
  279. this.form.validateCheck(result ? result : null)
  280. // typeof callback === 'function' && callback(result ? result : null);
  281. // if (promise) return promise
  282. },
  283. /**
  284. * 触发时机
  285. * @param {Object} event
  286. */
  287. isTrigger(rule, itemRlue, parentRule) {
  288. let rl = true;
  289. // bind submit
  290. if (rule === 'submit' || !rule) {
  291. if (rule === undefined) {
  292. if (itemRlue !== 'bind') {
  293. if (!itemRlue) {
  294. return parentRule === 'bind' ? true : false
  295. }
  296. return false
  297. }
  298. return true
  299. }
  300. return false
  301. }
  302. return true;
  303. },
  304. // 是否有必填字段
  305. isRequired(rules) {
  306. let isNoField = false
  307. for (let i = 0; i < rules.length; i++) {
  308. const ruleData = rules[i]
  309. if (ruleData.required) {
  310. isNoField = true
  311. break
  312. }
  313. }
  314. return isNoField
  315. }
  316. }
  317. };
  318. </script>
  319. <style scoped>
  320. .uni-forms-item {
  321. position: relative;
  322. text-align: left;
  323. color: #333;
  324. font-size: 14px;
  325. margin-bottom: 22px;
  326. background-color: #fff;
  327. }
  328. .uni-forms-item__inner {
  329. /* #ifndef APP-NVUE */
  330. display: flex;
  331. /* #endif */
  332. }
  333. .is-direction-left {
  334. flex-direction: row;
  335. }
  336. .is-direction-top {
  337. flex-direction: column;
  338. }
  339. .uni-forms-item__label {
  340. /* #ifndef APP-NVUE */
  341. display: flex;
  342. flex-shrink: 0;
  343. /* #endif */
  344. flex-direction: row;
  345. align-items: center;
  346. font-size: 14px;
  347. color: #333;
  348. width: 65px;
  349. padding: 5px 0;
  350. box-sizing: border-box;
  351. height: 36px;
  352. margin-right: 5px;
  353. }
  354. .uni-forms-item__content {
  355. /* #ifndef APP-NVUE */
  356. width: 100%;
  357. /* #endif */
  358. box-sizing: border-box;
  359. min-height: 36px;
  360. }
  361. .label-icon {
  362. margin-right: 5px;
  363. margin-top: -1px;
  364. }
  365. .is-required {
  366. color: #dd524d;
  367. }
  368. .uni-error-message {
  369. position: absolute;
  370. bottom: -17px;
  371. left: 0;
  372. line-height: 12px;
  373. color: #dd524d;
  374. font-size: 12px;
  375. text-align: left;
  376. }
  377. .uni-error-msg--boeder {
  378. position: relative;
  379. bottom: 0;
  380. line-height: 22px;
  381. }
  382. .is-input-error-border {
  383. border-color: #dd524d;
  384. }
  385. .uni-forms-item--border {
  386. margin-bottom: 0;
  387. padding: 10px 15px;
  388. border-top: 1px #eee solid;
  389. }
  390. .uni-forms-item-error {
  391. padding-bottom: 0;
  392. }
  393. .is-first-border {
  394. border: none;
  395. }
  396. </style>