c-form.vue 8.5 KB

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