cl-form-item.vue 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. <template>
  2. <view :class="['cl-form-item', isLabelPosition, isRequired, isError, isSuffix]">
  3. <view :class="['cl-form-item__label']" :style="{ width: labelWidth2 }" v-if="label2">{{
  4. label2
  5. }}</view>
  6. <view class="cl-form-item__container">
  7. <view :class="['cl-form-item__content', isJustify]">
  8. <slot></slot>
  9. </view>
  10. <view class="cl-form-item__suffix">
  11. <slot name="suffix"></slot>
  12. </view>
  13. </view>
  14. <slot name="error" :message="message" :error="showMessage2">
  15. <text :class="['cl-form-item__message']" v-if="showMessage2">{{ message }}</text>
  16. </slot>
  17. </view>
  18. </template>
  19. <script>
  20. import AsyncValidator from "../../utils/async-validator";
  21. import Emitter from "../../mixins/emitter";
  22. import Parent from "../../mixins/parent";
  23. import { isArray } from "../../utils";
  24. /**
  25. * form-item 表单项
  26. * @description 表单项,基于 async-validator 的验证
  27. * @tutorial https://docs.cool-js.com/uni/components/form/rules.html
  28. * @property {Object} model 表单数据对象
  29. * @property {Object} rules 表单验证规则
  30. * @property {Boolean} border 是否带有边框
  31. * @property {Boolean} showMessage 是否显示消息提示
  32. * @property {String} labelWidth 表单域标签的宽度,默认150rpx
  33. * @property {String} labelPosition 表单域标签的位置,默认right
  34. * @property {String} justify 水平布局,默认start
  35. * @property {String} validateOnRuleChange 是否在 rules 属性改变后立即触发一次验证,默认true
  36. * @example <cl-form-item prop="name"></cl-form-item>
  37. */
  38. export default {
  39. name: "cl-form-item",
  40. componentName: "ClFormItem",
  41. props: {
  42. // 表单域 model 字段
  43. prop: String,
  44. // 标签文本
  45. label: String,
  46. // 表单域标签的的宽度
  47. labelWidth: String,
  48. // 表单域标签的位置
  49. labelPosition: String,
  50. // 是否显示消息提示
  51. showMessage: {
  52. type: Boolean,
  53. default: false,
  54. },
  55. // 水平布局
  56. justify: {
  57. type: String,
  58. default: "start",
  59. },
  60. // 是否在 rules 属性改变后立即触发一次验证
  61. validateOnRuleChange: {
  62. type: Boolean,
  63. default: false,
  64. },
  65. },
  66. mixins: [Emitter, Parent],
  67. data() {
  68. return {
  69. required: false,
  70. message: "",
  71. error: false,
  72. validator: null,
  73. Keys: [
  74. "labelWidth",
  75. "labelPosition",
  76. "showMessage",
  77. "model",
  78. "removeField",
  79. "rules2",
  80. "validateOnRuleChange",
  81. ],
  82. ComponentName: "ClForm",
  83. };
  84. },
  85. computed: {
  86. label2() {
  87. return this.label == "true" ? "" : this.label;
  88. },
  89. labelWidth2() {
  90. return this.labelWidth || this.parent.labelWidth;
  91. },
  92. labelPosition2() {
  93. return this.labelPosition || this.parent.labelPosition;
  94. },
  95. showMessage2() {
  96. return (this.showMessage || this.parent.showMessage) && this.error;
  97. },
  98. contentWidth() {
  99. return `calc(100% - ${this.labelWidth} - 20rpx)`;
  100. },
  101. isJustify() {
  102. return this.justify !== "start" ? `is-justify-${this.justify}` : "";
  103. },
  104. isRequired() {
  105. return this.required ? "cl-form-item--required" : "";
  106. },
  107. isError() {
  108. return this.required && this.error ? "cl-form-item--error" : "";
  109. },
  110. isSuffix() {
  111. return this.$slots.suffix ? "cl-form-item--suffix" : "";
  112. },
  113. isLabelPosition() {
  114. return this.labelPosition2 ? `cl-form-item--${this.labelPosition2}` : "";
  115. },
  116. },
  117. created() {
  118. this.$on("form.event", ({ props, action, model, rules }) => {
  119. let isValid = props.includes(this.prop);
  120. switch (action) {
  121. case "change-rule":
  122. this.changeRule(rules);
  123. break;
  124. case "validate":
  125. if (isValid) {
  126. this.validate(model[this.prop]);
  127. }
  128. break;
  129. case "clearValidate":
  130. if (isValid) {
  131. this.clearValidate();
  132. }
  133. break;
  134. }
  135. });
  136. },
  137. mounted() {
  138. // 初始化验证规则
  139. this.changeRule(this.parent.rules2);
  140. },
  141. destroyed() {
  142. if (this.parent.removeField) {
  143. this.parent.removeField(this.prop);
  144. }
  145. },
  146. methods: {
  147. changeRule(rules) {
  148. if (!rules) {
  149. return false;
  150. }
  151. const rule = rules[this.prop];
  152. if (rule) {
  153. this.required = false;
  154. this.clearValidate();
  155. if (isArray(rule)) {
  156. rule.forEach((e) => {
  157. if (e.required) {
  158. this.required = e.required;
  159. }
  160. if (e.message) {
  161. this.message = e.message;
  162. }
  163. });
  164. } else {
  165. this.required = rule.required;
  166. this.message = rule.message;
  167. }
  168. // 检验器
  169. this.validator = new AsyncValidator({
  170. [this.prop]: rule,
  171. });
  172. // 是否在 rules 属性改变后立即触发一次验证
  173. if (this.validateOnRuleChange || this.parent.validateOnRuleChange) {
  174. this.validate(this.parent.model[this.prop]);
  175. }
  176. }
  177. },
  178. validate(val) {
  179. if (this.required) {
  180. this.validator.validate({ [this.prop]: val }, (errors, fields) => {
  181. this.error = Boolean(errors);
  182. this.message = errors[0].message;
  183. });
  184. }
  185. },
  186. clearValidate() {
  187. this.error = false;
  188. this.message = "";
  189. },
  190. },
  191. };
  192. </script>