|
@@ -0,0 +1,236 @@
|
|
|
+<template>
|
|
|
+ <div id="c-form">
|
|
|
+ <el-row type="flex" justify="space-around">
|
|
|
+ <el-col>
|
|
|
+ <el-form ref="formRef" :model="form" :rules="rules" :label-width="labelWidth" class="form" @submit.prevent :disabled="disabled">
|
|
|
+ <!-- <template > -->
|
|
|
+ <el-col :span="span" v-for="(item, index) in fields" :key="index">
|
|
|
+ <el-form-item v-if="display(item)" :key="'form-field-' + index" :label="getField('label', item)" :prop="item.model" :required="item.required">
|
|
|
+ <template v-if="!item.custom">
|
|
|
+ <template v-if="item.type === 'textarea'">
|
|
|
+ <el-input
|
|
|
+ clearable
|
|
|
+ v-model="form[item.model]"
|
|
|
+ :type="item.type"
|
|
|
+ :placeholder="getField('placeholder', item)"
|
|
|
+ v-bind="item.options"
|
|
|
+ @change="dataChange(item.model)"
|
|
|
+ show-word-limit
|
|
|
+ ></el-input>
|
|
|
+ </template>
|
|
|
+ <template v-else-if="item.type === 'numbers'">
|
|
|
+ <el-input-number
|
|
|
+ v-model="form[item.model]"
|
|
|
+ :placeholder="getField('placeholder', item)"
|
|
|
+ @change="dataChange(item.model)"
|
|
|
+ style="width: 100%"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ <template v-else-if="item.type === 'radio'">
|
|
|
+ <el-radio-group v-model="form[item.model]" :type="item.type" v-bind="item.options" @change="dataChange(item.model)">
|
|
|
+ <slot :name="item.model" v-bind="{ item }"></slot>
|
|
|
+ </el-radio-group>
|
|
|
+ </template>
|
|
|
+ <template v-else-if="item.type === 'checkbox'">
|
|
|
+ <el-checkbox-group v-model="form[item.model]" :type="item.type" v-bind="item.options">
|
|
|
+ <slot :name="item.model" v-bind="{ item }"></slot>
|
|
|
+ </el-checkbox-group>
|
|
|
+ </template>
|
|
|
+ <template v-else-if="item.type === 'select'">
|
|
|
+ <el-select
|
|
|
+ clearable
|
|
|
+ filterable
|
|
|
+ allow-create
|
|
|
+ default-first-option
|
|
|
+ v-model="form[item.model]"
|
|
|
+ :type="item.type"
|
|
|
+ :placeholder="getField('selectplaceholder', item)"
|
|
|
+ v-bind="item.options"
|
|
|
+ @change="dataChange(item.model)"
|
|
|
+ style="width: 100%"
|
|
|
+ >
|
|
|
+ <slot :name="item.model" v-bind="{ item }"></slot>
|
|
|
+ </el-select>
|
|
|
+ </template>
|
|
|
+ <template v-else-if="item.type === 'selectMany'">
|
|
|
+ <el-select
|
|
|
+ filterable
|
|
|
+ clearable
|
|
|
+ multiple
|
|
|
+ collapse-tags
|
|
|
+ v-model="form[item.model]"
|
|
|
+ :type="item.type"
|
|
|
+ :placeholder="getField('selectplaceholder', item)"
|
|
|
+ v-bind="item.options"
|
|
|
+ @change="dataChange(item.model)"
|
|
|
+ style="width: 100%"
|
|
|
+ >
|
|
|
+ <slot :name="item.model" v-bind="{ item }"></slot>
|
|
|
+ </el-select>
|
|
|
+ </template>
|
|
|
+ <template
|
|
|
+ v-else-if="
|
|
|
+ item.type === `year` ||
|
|
|
+ item.type == 'month' ||
|
|
|
+ item.type == 'date' ||
|
|
|
+ item.type == 'daterange' ||
|
|
|
+ item.type == 'datetime' ||
|
|
|
+ item.type == 'datetimerange'
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <el-date-picker
|
|
|
+ v-model="form[item.model]"
|
|
|
+ :type="item.type"
|
|
|
+ :placeholder="getField('selectplaceholder', item)"
|
|
|
+ :format="getDateFormat(item.type)"
|
|
|
+ :value-format="getDateFormat(item.type)"
|
|
|
+ v-bind="item.options"
|
|
|
+ @change="dataChange(item.model)"
|
|
|
+ range-separator="至"
|
|
|
+ style="width: 100%"
|
|
|
+ >
|
|
|
+ </el-date-picker>
|
|
|
+ </template>
|
|
|
+ <template v-else-if="item.type === `time`">
|
|
|
+ <el-time-picker
|
|
|
+ v-model="form[item.model]"
|
|
|
+ :placeholder="getField('selectplaceholder', item)"
|
|
|
+ :format="getDateFormat(item.type)"
|
|
|
+ :value-format="getDateFormat(item.type)"
|
|
|
+ v-bind="item.options"
|
|
|
+ @change="dataChange(item.model)"
|
|
|
+ style="width: 100%"
|
|
|
+ >
|
|
|
+ </el-time-picker>
|
|
|
+ </template>
|
|
|
+ <template v-else-if="item.type === `inputnumber`">
|
|
|
+ <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>
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <el-input
|
|
|
+ clearable
|
|
|
+ v-model="form[item.model]"
|
|
|
+ :type="getField('type', item)"
|
|
|
+ :placeholder="getField('placeholder', item)"
|
|
|
+ :show-password="getField('type', item) === 'password'"
|
|
|
+ v-bind="item.options"
|
|
|
+ @change="dataChange(item.model)"
|
|
|
+ ></el-input>
|
|
|
+ </template>
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <slot :name="item.model" v-bind="{ item }"></slot>
|
|
|
+ </template>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <!-- </template> -->
|
|
|
+ <el-col :span="24" label="" class="btn" v-if="isSave">
|
|
|
+ <slot name="submit">
|
|
|
+ <el-button type="primary" @click="save(formRef)">{{ submitText }}</el-button>
|
|
|
+ </slot>
|
|
|
+ </el-col>
|
|
|
+ </el-form>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { ref, toRefs } from 'vue';
|
|
|
+import type { FormInstance } from 'element-plus';
|
|
|
+import _ from 'lodash';
|
|
|
+// #region 传递
|
|
|
+interface fieldsItem {
|
|
|
+ model: string;
|
|
|
+ type: string;
|
|
|
+ options: object;
|
|
|
+ custom: string;
|
|
|
+ required: string;
|
|
|
+ limit: number | undefined;
|
|
|
+ url: string;
|
|
|
+}
|
|
|
+const props = defineProps({
|
|
|
+ fields: { type: Array<fieldsItem>, default: () => [] },
|
|
|
+ rules: { type: Object, default: () => {} },
|
|
|
+ labelWidth: { type: String, default: '120px' },
|
|
|
+ submitText: { type: String, default: '提交保存' },
|
|
|
+ form: { type: Object, default: () => {} },
|
|
|
+ reset: { type: Boolean, default: false },
|
|
|
+ isSave: { type: Boolean, default: true },
|
|
|
+ span: { type: Number, default: 24 }, // 限制两侧的距离,24就是整行全用
|
|
|
+ disabled: { type: Boolean, default: false }
|
|
|
+});
|
|
|
+const { fields } = toRefs(props);
|
|
|
+const { rules } = toRefs(props);
|
|
|
+const { labelWidth } = toRefs(props);
|
|
|
+const { submitText } = toRefs(props);
|
|
|
+const { form } = toRefs(props);
|
|
|
+const { reset } = toRefs(props);
|
|
|
+const { isSave } = toRefs(props);
|
|
|
+const { span } = toRefs(props);
|
|
|
+const { disabled } = toRefs(props);
|
|
|
+const formRef = ref<FormInstance>();
|
|
|
+const getField = (item: string, data: any) => {
|
|
|
+ let res: string | null | boolean = _.get(data, item, null);
|
|
|
+ if (item === 'type') res = res === null ? `text` : res;
|
|
|
+ if (item === 'placeholder') res = res === null ? `请输入${data.label}` : res;
|
|
|
+ if (item === `selectplaceholder`) res = res === null ? `请选择${data.label}` : res;
|
|
|
+ if (item === 'required') res = res === null ? false : res;
|
|
|
+ if (item === `error`) res = res === null ? `${data.label}错误` : res;
|
|
|
+ return res;
|
|
|
+};
|
|
|
+
|
|
|
+const getDateFormat = (e: string) => {
|
|
|
+ if (e === 'year') return 'YYYY';
|
|
|
+ if (e === 'month') return 'MM';
|
|
|
+ if (e === 'date') return 'YYYY-MM-DD';
|
|
|
+ if (e === 'daterange') return 'YYYY-MM-DD';
|
|
|
+ if (e === 'datetime') return 'YYYY-MM-DD HH:mm:ss';
|
|
|
+ if (e === 'datetimerange') return 'YYYY-MM-DD HH:mm:ss';
|
|
|
+ if (e === 'time') return 'HH:mm:ss';
|
|
|
+};
|
|
|
+const emit = defineEmits(['save', 'dataChange']);
|
|
|
+const clear = ref<any>();
|
|
|
+// 提交
|
|
|
+const save = async (formEl: FormInstance | undefined) => {
|
|
|
+ if (!formEl) return;
|
|
|
+ await formEl.validate((valid, fields) => {
|
|
|
+ if (valid) {
|
|
|
+ emit('save', form.value);
|
|
|
+ if (reset.value) clear.value.resetFields();
|
|
|
+ } else {
|
|
|
+ console.log('error submit!', fields);
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|
|
|
+const display = (field: any) => {
|
|
|
+ let dis = _.get(field, `display`);
|
|
|
+ if (!_.isFunction(dis)) return true;
|
|
|
+ else return dis(field, form);
|
|
|
+};
|
|
|
+const dataChange = (model: string) => {
|
|
|
+ const value = form.value[model];
|
|
|
+ emit('dataChange', { model, value });
|
|
|
+};
|
|
|
+const sss = () => {
|
|
|
+ console.log(1);
|
|
|
+};
|
|
|
+defineExpose({ sss });
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.btn {
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+.form {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: row;
|
|
|
+ flex-wrap: wrap;
|
|
|
+}
|
|
|
+</style>
|