vue3二次封装form表单

组成:form的index.vue + 逻辑处理use-form.js+各个组件(input、select等).vue

使用:提供formItem.js

结果:

1、form的index.vue

<template>
  <div class="hFormContainer">
    <el-form ref="hForm" class="h-form" :model="_formData">
      <el-row :gutter="20">
        <el-col
          v-for="(
            { prop, label, span, type, preSlot, sufSlot, rules }, index
          ) in formItems"
          :key="prop"
          :span="span || 5"
        >
          <el-form-item :label="label" :prop="prop" :rules="rules">
            <!-- preSlot -->
            <slot :name="preSlot"></slot>
            <!-- input -->
            <hInput
              v-if="type === 'input'"
              v-model="_formData[prop]"
              :config="formItems[index]"
            >
              <template #inputPrefix>
                <slot
                  :name="formItems[index].iptSufSlot"
                  :form-data="{ ..._formData }"
                ></slot>
              </template>
              <template #inputSuffix>
                <slot
                  :name="formItems[index].iptSufSlot"
                  :form-data="{ ..._formData }"
                ></slot>
              </template>
            </hInput>
            <!-- select -->
            <hSelect
              v-if="type === 'select'"
              v-model="_formData[prop]"
              :config="formItems[index]"
            />
            <!-- datePick -->
            <hDatePicker
              v-if="type === 'datepick'"
              v-model="_formData[prop]"
              :config="formItems[index]"
            />
            <!-- hCheckBox -->
            <hCheckbox
              v-if="type === 'checkbox'"
              v-model="_formData[prop]"
              :config="formItems[index]"
            />
            <!-- radio -->
            <hRadio
              v-if="type === 'radio'"
              v-model="_formData[prop]"
              :config="formItems[index]"
            />
            <slot :name="sufSlot"></slot>
          </el-form-item>
        </el-col>
      </el-row>
    </el-form>
  </div>
</template>

<script setup>
import { useForm } from "./use-form";
import hInput from "./components/hInput/index.vue";
import hSelect from "./components/hSelect/index.vue";
import hRadio from "./components/hRadio/index.vue";
import hCheckbox from "./components/hChexkbox/index.vue";
import hDatePicker from "./components/hDatePicker/index.vue";

const props = defineProps({
  formItems: Array,
});
const { _formData, getFormData, setFormData, resetFormData } = useForm(
  props.formItems
);

defineExpose({
  getFormData,
  setFormData,
  resetFormData,
});
</script>

<style lang="scss" scoped></style>

 2、use-form.js

// 初始化所有的formItem
import { ref } from "vue";
const initFormDataHandle = (formItems) => {
  const formData = {};
  formItems.forEach(({ prop, initValue, type }) => {
    if (type == "checkbox") {
      formData[prop] = Array.isArray(initValue)
        ? initValue
        : initValue
        ? [initValue]
        : [];
    } else {
      formData[prop] = initValue || "";
    }
  });
};

export const useForm = (formItems) => {
  const initFormData = initFormDataHandle(formItems);
  // 接收
  const _formData = ref({ ...initFormData });
  const getFormData = () => ({ ..._formData.value });
  // 外部传过来的formData
  const setFormData = (formData, isReset) => {
    _formData.value = isReset
      ? { ...formData }
      : { ..._formData.value, ...formData };
  };
  const resetFormData = () => setFormData(initFormData, true);
  return {
    _formData,
    getFormData,
    setFormData,
    resetFormData,
  };
};

3、input

<script>
export default { name: "hInput" };
</script>
<script setup>
const props = defineProps({
  modelValue: String,
  config: Object,
});
const emit = defineEmits(["update:modelValue"]);
const input = (value, config) => {
  emit("update:modelValue", validateField(value, config));
};

const validateField = (value, config) => {
  let _value = value;
  // 过滤空格
  _value = _value.replace(/\s|[\r\n]/gi, "");
  const reg =
    // eslint-disable-next-line no-misleading-character-class
    /([0-9|*|#]\uFE0F\u20E3)|([0-9|#]\u20E3)|([\u203C-\u3299]\uFE0F\u200D)|([\u203C-\u3299]\uFE0F)|([\u2122-\u2B55])|(\u303D)|([(A9)|(AE)]\u3030)|(\uA9)|(\uAE)|(\u3030)|([\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF])|([\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF][\u200D|\uFE0F])/g;
  // 过滤表情
  _value = _value.replace(reg, "");
  // 只能输入数字和英文
  // if (numberLetterFieldList.includes(id)) _value = _value.replace(/[\W]/g, '');
  // 只能输入数字的输入框
  if (config.number) _value = _value.replace(/[^\d]/g, "");
  return _value;
};
</script>

<template>
  <el-input
    :model-value="modelValue"
    v-bind="config"
    :type="config.textAreaConfig ? 'textarea' : 'input'"
    @update:model-value="(value) => input(value, config)"
  >
    <template v-if="config.preSlot" #prefix>
      <slot name="inputPrefix"></slot>
      <el-icon v-if="config.prefixIcon">
        <component :is="config.prefixIcon" />
      </el-icon>
    </template>
    <template #suffix>
      <slot name="inputSuffix"></slot>
      <el-icon v-if="config.prefixIcon">
        <component :is="config.suffixIcon" />
      </el-icon>
    </template>
  </el-input>
</template>

<style scoped lang="scss"></style>

4、select

<script setup>
const props = defineProps({
  config: Object,
  modelValue: String,
});
const emit = defineEmits(["update:modelValue"]);

const change = (value, config) => {
  emit("update:modelValue", value, config);
};
</script>

<template>
  <el-select
    v-bind="config"
    :model-value="modelValue"
    filterable
    clearable
    @change="(value) => change(value, config)"
  >
    <el-option
      v-for="item in config.options"
      :key="item.value"
      :label="item.label"
      :value="item.value"
    />
  </el-select>
</template>

<style scoped lang="scss">
.el-select {
  width: 100%;
}
</style>

5、radio

<script setup>
const props = defineProps({
  modelValue: String,
  config: Object,
});
const emit = defineEmits(["update:modelValue"]);
const change = (value, config) => {
  emit("update:modelValue", value);
};
</script>

<template>
  <el-radio-group
    :model-value="modelValue"
    @change="(value) => change(value, config)"
  >
    <el-radio
      v-for="{ label, value } in config.options"
      :key="value"
      :label="label"
    >
      {
   
   { label }}
    </el-radio>
  </el-radio-group>
</template>

<style scoped lang="scss"></style>

6、datePick

<script>
export default { name: "hDatePick" };
</script>
<script setup>
import { inject } from "vue";

const props = defineProps({
  modelValue: String,
  config: Object,
});
const emit = defineEmits(["update:modelValue"]);

const formInjectKey = Symbol();
const { setFormData } = inject(formInjectKey);
const change = (value) => {
  if (Array.isArray(value)) {
    const { startProp, endProps } = props.config;
    setFormData({
      [startProp || "startTime"]: value[0],
      [endProps || "endTime"]: value[1],
    });
  }
  emit("update:modelValue", value);
};
</script>

<template>
  <div class="yn-date-pick-wrapper">
    <el-date-picker
      class="yn-date-pick"
      v-bind="config"
      :model-value="modelValue"
      :type="config.dateType || 'daterange'"
      :value-format="config.valueFormat || 'YYYY-MM-DD'"
      @update:model-value="change(value)"
    />
  </div>
</template>

<style scoped lang="scss">
.yn-date-pick-wrapper {
  width: 100%;
  :deep(.yn-date-pick) {
    width: 100%;
    .el-input__wrapper {
      width: 100%;
      box-sizing: border-box;
    }
  }
}
</style>

7、checkbox

<script>
export default { name: "hCheckbox" };
</script>
<script setup>
const props = defineProps({
  modelValue: String,
  config: Object,
});
const emit = defineEmits(["update:modelValue"]);
const checkAll = ref(false);
const isIndeterminate = ref(false);
const handleCheckAllChange = (val) => {
  const value = val ? props.config.options.map((i) => i.value) : [];
  isIndeterminate.value = false;
  emit("update:modelValue", value);
};
const handleCheckedOptionChange = (value) => {
  const checkedCount = value.length;
  const optionCount = props.config.options.length;
  checkAll.value = checkedCount === optionCount;
  isIndeterminate.value = checkedCount > 0 && checkedCount < optionCount;
  emit("update:modelValue", value);
};
</script>

<template>
  <div class="checkbox-wrapper">
    <el-checkbox
      v-if="config.showCheckAll"
      v-model="checkAll"
      class="check-all block"
      :border="config.border"
      :indeterminate="isIndeterminate"
      @change="handleCheckAllChange"
    >
      {
   
   { config.checkAllText || "全选" }}
    </el-checkbox>
    <el-checkbox-group
      :model-value="modelValue"
      @change="handleCheckedOptionChange"
    >
      <el-checkbox
        v-for="{ label, value } in config.options"
        :key="value"
        :border="config.border"
        :label="value"
      >
        {
   
   { label }}
      </el-checkbox>
    </el-checkbox-group>
  </div>
</template>

<style scoped lang="scss">
.checkbox-wrapper {
  .check-all.block {
    width: 100%;
  }
}
</style>

使用form组件

1.提供formConfig

const hFormItem = [
  {
    type: "input",
    label: "时间",
    prop: "date",
  },
  {
    type: "input",
    label: "姓名",
    prop: "name",
  },
  {
    type: "input",
    label: "地址",
    prop: "address",
  },
  {
    type: "select",
    label: "状态",
    prop: "status",
    options: [
      { label: "成功", value: "1" },
      { label: "失败", value: "0" },
    ],
    placeholder: "请选择状态",
  },
  {
    type: "radio",
    label: "性别",
    prop: "sex",
    options: [
      { label: "男", value: 0 },
      { label: "女", value: 1 },
    ],
  },
];

export { hFormItem };

2、在对应的页面中使用

<template>
  <hCard :title="tableConfig.title">
    <template #top-right>
      <el-button type="primary" @click="toSearch">搜索</el-button>
      <el-button type="info" @click="resetGood">重置</el-button>
    </template>
    <template #bottom>
      <hForm ref="goodForm" :formItems="hFormItem"> </hForm>
    </template>
  </hCard>

  <hTable :tableData="tableData" v-bind="tableConfig">
    <template #hTable-before-slot>
      <el-button type="primary" size="small" @click="addGood()">新增</el-button>
    </template>
    <template #handle="{ row, column, index }">
      <el-button link type="primary" size="small" @click="handleClick(row)"
        >编辑</el-button
      >
      <el-button link type="primary" size="small" @click="deleteInfo(row)"
        >删除</el-button
      >
    </template>
  </hTable>

  <hDialog v-model="dialogFormVisible" v-bind="dialogConfig">
    <template #dialog-content>
      <hForm ref="dialogForm" :formItems="formItems"></hForm>
    </template>
  </hDialog>
</template>

<script setup>
import hTable from "@/components/hTable/index.vue";
import hForm from "@/components/hForm/index.vue";
import hCard from "@/components/hCard/index.vue";
import hDialog from "@/components/hDialog/index.vue";

import tableConfig from "./goodConfig/tableConfig";
import { dialogConfig, formItems } from "./goodConfig/dialogConfig";
import { hFormItem } from "./goodConfig/formConfig";
import { nextTick, ref } from "vue";

const dialogFormVisible = ref(false);
const goodForm = ref(null);
const dialogForm = ref(null);
const tableData = [
  {
    date: "2016-05-03",
    name: "Tom",
    address: "No. 189, Grove St, Los Angeles",
    status: 1, //1:待处理  0:已完成
    sex: 1, //0 女 1男
  },
  {
    date: "2016-05-02",
    name: "Tom",
    address: "No. 189, Grove St, Los Angeles",
    status: 0, //1:待处理  0:已完成
    sex: 0, //0 女 1男
  },
  {
    date: "2016-05-04",
    name: "Tom",
    address: "No. 189, Grove St, Los Angeles",
    status: 0, //1:待处理  0:已完成
    sex: 1, //0 女 1男
  },
  {
    date: "2016-05-01",
    name: "Tom",
    address: "No. 189, Grove St, Los Angeles",
    status: 1, //1:待处理  0:已完成
    sex: 0, //0 女 1男
  },
];

// form
const toSearch = () => {
  console.log(1212);
};
const resetGood = () => {
  goodForm.value.resetFormData();
};

// table
const handleClick = (row) => {
  const data = {
    name: "张三",
  };
  dialogFormVisible.value = true;
  nextTick(() => {
    dialogForm.value.setFormData(data);
  });
};
const deleteInfo = (row) => {
  console.log(row);
};
const addGood = () => {
  console.log(1212);
};
</script>

<style lang="scss" scoped></style>

猜你喜欢

转载自blog.csdn.net/qq_48109675/article/details/129258543