custom-form.vue 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. <template>
  2. <div id="custom-form">
  3. <el-form ref="formRef" :model="form" :rules="rules" :label-width="labelWidth" class="form" @submit.prevent :disabled="disabled">
  4. <el-col :span="span" v-for="(item, index) in fields" :key="index">
  5. <el-form-item v-if="display(item)" :key="`form-field-${item.model}`" :label="getField('label', item)" :prop="item.model" :required="item.required">
  6. <template v-if="item.custom">
  7. <slot :name="item.model" v-bind="{ item }"></slot>
  8. </template>
  9. <template v-else>
  10. <template v-if="item.type === 'textarea'">
  11. <el-input
  12. clearable
  13. v-model="form[item.model]"
  14. :type="item.type"
  15. :placeholder="getField('placeholder', item)"
  16. v-bind="item.options"
  17. @change="dataChange(item.model)"
  18. show-word-limit
  19. ></el-input>
  20. </template>
  21. <template v-else-if="item.type === 'numbers'">
  22. <el-input-number v-model="form[item.model]" :placeholder="getField('placeholder', item)" @change="dataChange(item.model)" style="width: 100%" />
  23. </template>
  24. <template v-else-if="item.type === 'radio'">
  25. <el-radio-group v-model="form[item.model]" :type="item.type" v-bind="item.options" @change="dataChange(item.model)">
  26. <slot :name="item.model" v-bind="{ item }"></slot>
  27. </el-radio-group>
  28. </template>
  29. <template v-else-if="item.type === 'checkbox'">
  30. <el-checkbox-group v-model="form[item.model]" :type="item.type" v-bind="item.options">
  31. <slot :name="item.model" v-bind="{ item }"></slot>
  32. </el-checkbox-group>
  33. </template>
  34. <template v-else-if="item.type === 'select'">
  35. <el-select
  36. clearable
  37. filterable
  38. allow-create
  39. default-first-option
  40. v-model="form[item.model]"
  41. :type="item.type"
  42. :placeholder="getField('selectplaceholder', item)"
  43. v-bind="item.options"
  44. @change="dataChange(item.model)"
  45. style="width: 100%"
  46. >
  47. <slot :name="item.model" v-bind="{ item }"></slot>
  48. </el-select>
  49. </template>
  50. <template v-else-if="item.type === 'selectMany'">
  51. <el-select
  52. filterable
  53. clearable
  54. multiple
  55. collapse-tags
  56. v-model="form[item.model]"
  57. :type="item.type"
  58. :placeholder="getField('selectplaceholder', item)"
  59. v-bind="item.options"
  60. @change="dataChange(item.model)"
  61. style="width: 100%"
  62. >
  63. <slot :name="item.model" v-bind="{ item }"></slot>
  64. </el-select>
  65. </template>
  66. <template v-else-if="item.type === `year` || item.type == 'month' || item.type == 'date' || item.type == 'daterange' || item.type == 'datetime' || item.type == 'datetimerange'">
  67. <el-date-picker
  68. v-model="form[item.model]"
  69. :type="item.type"
  70. :placeholder="getField('selectplaceholder', item)"
  71. :format="getDateFormat(item.type)"
  72. :value-format="getDateFormat(item.type)"
  73. v-bind="item.options"
  74. @change="dataChange(item.model)"
  75. range-separator="至"
  76. style="width: 100%"
  77. >
  78. </el-date-picker>
  79. </template>
  80. <template v-else-if="item.type === `time`">
  81. <el-time-picker
  82. v-model="form[item.model]"
  83. :placeholder="getField('selectplaceholder', item)"
  84. :format="getDateFormat(item.type)"
  85. :value-format="getDateFormat(item.type)"
  86. v-bind="item.options"
  87. @change="dataChange(item.model)"
  88. style="width: 100%"
  89. >
  90. </el-time-picker>
  91. </template>
  92. <template v-else-if="item.type === `inputnumber`">
  93. <el-input-number v-model="form[item.model]" :placeholder="getField('placeholder', item)" v-bind="item.options" @change="dataChange(item.model)" style="width: 100%"></el-input-number>
  94. </template>
  95. <template v-else>
  96. <el-input
  97. clearable
  98. v-model="form[item.model]"
  99. :type="getField('type', item)"
  100. :placeholder="getField('placeholder', item)"
  101. :show-password="getField('type', item) === 'password'"
  102. v-bind="item.options"
  103. @change="dataChange(item.model)"
  104. ></el-input>
  105. </template>
  106. </template>
  107. </el-form-item>
  108. </el-col>
  109. <el-col :span="24" label="" class="btn" v-if="useSave">
  110. <slot name="submit">
  111. <el-button type="primary" @click="save(formRef)">{{ submitText || submitTextDefault }}</el-button>
  112. </slot>
  113. </el-col>
  114. </el-form>
  115. </div>
  116. </template>
  117. <script setup>
  118. import { get, isFunction } from 'lodash-es'
  119. const { t } = useI18n()
  120. const submitTextDefault = t('common.save')
  121. const props = defineProps({
  122. modelValue: { type: Object },
  123. rules: { type: Array, default: () => {} },
  124. labelWidth: { type: String, default: 'auto' },
  125. disabled: { type: Boolean, default: false },
  126. fields: { type: Array, default: () => [] },
  127. submitText: { type: String },
  128. useSave: { type: Boolean, default: true },
  129. span: { type: Number, default: 24 } // 限制两侧的距离,24就是整行全用
  130. })
  131. const emits = defineEmits(['update:modelValue', 'dataChange', 'save'])
  132. const formRef = ref()
  133. const form = computed({
  134. get() {
  135. return props.modelValue
  136. },
  137. set(value) {
  138. console.log(value)
  139. emits('update:modelValue', value)
  140. }
  141. })
  142. const save = async (formEl) => {
  143. if (!formEl) return
  144. await formEl.validate((valid, fields) => {
  145. if (valid) {
  146. emits('save', form.value)
  147. } else {
  148. console.log('error submit!', fields)
  149. }
  150. })
  151. }
  152. const getField = (item, data) => {
  153. let res = get(data, item, null)
  154. if (item === 'type') res = res === null ? `text` : res
  155. if (item === 'placeholder') res = res === null ? `请输入${data.label}` : res
  156. if (item === `selectplaceholder`) res = res === null ? `请选择${data.label}` : res
  157. if (item === 'required') res = res === null ? false : res
  158. if (item === `error`) res = res === null ? `${data.label}错误` : res
  159. return res
  160. }
  161. const dataChange = (model) => {
  162. const value = form.value[model]
  163. emits('dataChange', { model, value })
  164. }
  165. const display = (field) => {
  166. let dis = get(field, `display`)
  167. if (!isFunction(dis)) return true
  168. else {
  169. return dis(field, form)
  170. }
  171. }
  172. const getDateFormat = (e) => {
  173. if (e === 'year') return 'YYYY'
  174. if (e === 'month') return 'MM'
  175. if (e === 'date') return 'YYYY-MM-DD'
  176. if (e === 'daterange') return 'YYYY-MM-DD'
  177. if (e === 'datetime') return 'YYYY-MM-DD HH:mm:ss'
  178. if (e === 'datetimerange') return 'YYYY-MM-DD HH:mm:ss'
  179. if (e === 'time') return 'HH:mm:ss'
  180. }
  181. </script>
  182. <style scoped>
  183. .btn {
  184. text-align: center;
  185. }
  186. .form {
  187. display: flex;
  188. flex-direction: row;
  189. flex-wrap: wrap;
  190. }
  191. </style>