form.vue 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. <!-- 通用表单组件 -->
  2. <template>
  3. <el-form
  4. class="no-scrollbar"
  5. :disabled="disabled"
  6. label-position="right"
  7. :label-width="labelWidth.replace(/[^0-9]/g, '') + 'px'"
  8. :rules="rules"
  9. :model="data"
  10. ref="form"
  11. status-icon
  12. >
  13. <div class="row">
  14. <div class="col" v-for="(item, index) in cols" :key="index" :style="'width:' + width">
  15. <template v-for="(row, index) in item">
  16. <el-form-item
  17. v-if="row.offShow ? false : true"
  18. :key="index"
  19. :label="row.label"
  20. :prop="row.prop || row.slot"
  21. :class="{ 'show-required-icon': row.showRequired }"
  22. :error="errors[row.prop || row.slot]"
  23. >
  24. <!-- 过滤器 -->
  25. <p v-if="row.filter">{{ ft[row.filter](data[row.prop], row.filterParams) }}</p>
  26. <!-- 过滤方法 -->
  27. <p v-else-if="row.format">{{ row.format(data[row.prop], data) }}</p>
  28. <!-- 绑定事件 -->
  29. <a v-else-if="row.click" href="javascript:;" @click="row.click"
  30. ><span>{{ data[row.prop] }}</span></a
  31. >
  32. <!-- 日期控件 -->
  33. <el-date-picker
  34. v-else-if="row.date"
  35. v-model="data[row.prop]"
  36. :value-format="row.dateFormat || 'yyyy-MM-dd'"
  37. type="date"
  38. :editable="row.editable || false"
  39. placeholder="选择日期"
  40. :class="row.widt ? 'width100':''"
  41. @change="(obj) => (row.change ? row.change(obj) : {})"
  42. ></el-date-picker>
  43. <!-- input输入框 -->
  44. <el-input
  45. v-else-if="row.input"
  46. clearable
  47. v-trim
  48. :disabled="row.disabled"
  49. v-model.trim="data[row.prop]"
  50. :placeholder="row.placeholder || '请输入' + row.label.replace(':', '').replace(':', '')"
  51. :maxlength="row.maxlength"
  52. ></el-input>
  53. <!-- textarea输入框 -->
  54. <el-input
  55. v-else-if="row.textarea"
  56. v-trim
  57. v-model.trim="data[row.prop]"
  58. type="textarea"
  59. resize="none"
  60. :rows="row.rows || 3"
  61. :placeholder="'请输入' + row.label.replace(':', '').replace(':', '')"
  62. :maxlength="row.maxlength"
  63. :show-word-limit="row.maxlength"
  64. ></el-input>
  65. <!-- 插槽 -->
  66. <slot v-else-if="row.slot" :name="row.slot"></slot>
  67. <!-- zz-select -->
  68. <zz-select
  69. v-else-if="row.options"
  70. :disabled="row.disabled"
  71. v-model="data[row.prop]"
  72. :options="row.options"
  73. :placeholder="'请选择' + row.label.replace(':', '').replace(':', '')"
  74. @change="(val) => row.change && row.change(val)"
  75. ></zz-select>
  76. <!-- zz-cache -->
  77. <zz-cache
  78. v-else-if="row.cache"
  79. :getter="row.cache"
  80. v-model="data[row.prop]"
  81. @change="(val) => row.change && row.change(val)"
  82. :labelValue="row.labelValue"
  83. ></zz-cache>
  84. <!-- 默认输出绑定的值 -->
  85. <p v-else class="ellipsis" v-tip :data-txt="data[row.prop]">{{ data[row.prop] || '-' }}</p>
  86. </el-form-item>
  87. </template>
  88. </div>
  89. </div>
  90. <!-- 表单按钮插槽 -->
  91. <div v-if="$slots.btns">
  92. <el-form-item>
  93. <slot name="btns"></slot>
  94. </el-form-item>
  95. </div>
  96. <slot name="row" class="slot-row"></slot>
  97. </el-form>
  98. </template>
  99. <script>
  100. export default {
  101. name: 'zz-form',
  102. props: {
  103. /*
  104. 字段设置:二维数组,包括label名字设置,字段绑定设置,第二维有多少个集合就有多少列
  105. [
  106. [
  107. {
  108. lable: 'label名,必须',
  109. prop: '绑定字段,必须',
  110. filter: 'vue过滤器名,',
  111. filterParams: '过滤的参数',
  112. format: '过滤方法,',
  113. click: '绑定的click事件',
  114. date: '日期输入控件',
  115. dateFormat: '日期控件的返回值格式化字符串,默认为"yyyy-MM-dd"'
  116. change: 'date改变时触发的事件'
  117. input: 'input输入框',
  118. textarea: '内容输入框',
  119. rows: '内容输入框的行数,默认4行',
  120. slot: '插槽名',
  121. }
  122. ]
  123. .
  124. .
  125. .
  126. ]
  127. */
  128. cols: {
  129. type: Array,
  130. default() {
  131. return [];
  132. }
  133. },
  134. // 绑定数据
  135. data: {
  136. type: Object,
  137. default() {
  138. return {};
  139. }
  140. },
  141. // 表单校验
  142. rules: {
  143. type: Object,
  144. default() {
  145. return {};
  146. }
  147. },
  148. // 表单标签宽度
  149. labelWidth: {
  150. type: String,
  151. default() {
  152. return '80px';
  153. }
  154. },
  155. // 错误的项目字段消息对象{prop: '错误消息'}
  156. errors: {
  157. type: Object,
  158. default() {
  159. return {};
  160. }
  161. },
  162. disabled: {
  163. type: Boolean,
  164. default() {
  165. return false;
  166. }
  167. }
  168. },
  169. data() {
  170. let self = this;
  171. return {
  172. ft: self.$root.$options.filters
  173. };
  174. },
  175. methods: {
  176. validate(resolve, reject) {
  177. this.$parent.errors = {};
  178. this.$refs.form.validate((valid) => {
  179. if (valid) {
  180. resolve && resolve();
  181. } else {
  182. this.$nextTick(() => {
  183. const isError = this.$el.querySelectorAll('.is-error');
  184. if (isError && isError[0]) {
  185. isError[0].querySelector('input').focus();
  186. }
  187. });
  188. reject && reject();
  189. }
  190. });
  191. },
  192. resetFields() {
  193. this.$refs.form.resetFields();
  194. },
  195. clearValidate() {
  196. this.$refs.form.clearValidate();
  197. }
  198. },
  199. computed: {
  200. width() {
  201. return 100 / this.cols.length + '%';
  202. }
  203. },
  204. watch: {
  205. errors(n) {
  206. const len = Object.keys(n).length;
  207. if (len) {
  208. this.$nextTick(() => {
  209. const isError = this.$el.querySelectorAll('.is-error');
  210. if (isError && isError[0]) {
  211. isError[0].querySelector('input').focus();
  212. }
  213. });
  214. }
  215. }
  216. }
  217. };
  218. </script>
  219. <style lang='scss' scoped>
  220. .row {
  221. &:after {
  222. content: '';
  223. width: 0;
  224. height: 0;
  225. visibility: hidden;
  226. clear: both;
  227. display: block;
  228. .col {
  229. float: left;
  230. }
  231. }
  232. }
  233. .slot-row {
  234. margin-top: -22px;
  235. }
  236. .show-required-icon /deep/ .el-form-item__label {
  237. &:before {
  238. content: '*';
  239. color: #f56c6c;
  240. margin-right: 4px;
  241. }
  242. }
  243. </style>