当前位置: 代码网 > it编程>编程语言>Javascript > vue3+elementPlus二次封装表单的实现代码

vue3+elementPlus二次封装表单的实现代码

2024年05月18日 Javascript 我要评论
功能input输入框autocomplete自动补齐输入框radio 单选框checkbox 复选框date 日期选择框select 下拉框如需添加更多功能参考elementplus或者根据业务需求自

功能

  • input输入框

  • autocomplete自动补齐输入框

  • radio 单选框

  • checkbox 复选框

  • date 日期选择框

  • select 下拉框

  • 如需添加更多功能参考elementplus或者根据业务需求自行自定义组件

效果图

目录结构

input

<template>
  <el-input
    v-bind="$attrs"
    v-model="modelvalue"
    w-full
    @blur="props.blur ? props.blur($event) : false"
    @focus="props.focus ? props.focus($event) : false"
    @change="props.change ? props.change($event) : false"
    @input="props.input ? props.input($event) : false"
    @clear="props.clear ? props.clear() : false"
  />
</template>
<script lang="ts" setup>
import { ref, watch } from "vue";
const emit = defineemits(["update:modelvalue"]);
const props = defineprops({
  modelvalue: {
    type: string,
    default: () => "",
  },
  blur: {
    type: function,
    default: () => () => {},
  },
  focus: {
    type: function,
    default: () => () => {},
  },
  change: {
    type: function,
    default: () => () => {},
  },
  input: {
    type: function,
    default: () => () => {},
  },
  clear: {
    type: function,
    default: () => () => {},
  },
});

const modelvalue = ref(props.modelvalue);
//监听父组件的值
watch(
  () => props.modelvalue,
  () => {
    modelvalue.value = props.modelvalue;
  },
);

// 通过emit将值传递给父组件
emit("update:modelvalue", modelvalue);
</script>
<style lang="scss" scoped></style>

select

<template>
  <el-select
    v-model="modelvalue"
    v-bind="$attrs"
    w-full
    @change="props.change ? props.change($event) : false"
    @visible-change="props.visiblechange ? props.visiblechange($event) : false"
    @remove-tag="props.removetag ? props.removetag($event) : false"
    @clear="props.clear ? props.clear() : false"
    @blur="props.blur ? props.blur($event) : false"
    @focus="props.focus ? props.focus($event) : false"
  >
    <el-option
      v-for="item in options"
      :key="item[valuefiled]"
      :label="item[labelfiled]"
      :value="item[valuefiled]"
    ></el-option>
  </el-select>
</template>
<script lang="ts" setup>
import { ref, watch } from "vue";
const emit = defineemits(["update:modelvalue"]);
const props = defineprops({
  modelvalue: {
    type: [string, array],
    default: () => "",
  },
  options: {
    type: array as any,
    default: () => [],
  },
  valuefiled: {
    type: string,
    default: "value",
  },
  labelfiled: {
    type: string,
    default: "label",
  },
  change: {
    type: function,
    default: () => () => {},
  },
  visiblechange: {
    type: function,
    default: () => () => {},
  },
  removetag: {
    type: function,
    default: () => () => {},
  },
  clear: {
    type: function,
    default: () => () => {},
  },
  blur: {
    type: function,
    default: () => () => {},
  },
  focus: {
    type: function,
    default: () => () => {},
  },
});

const modelvalue = ref(props.modelvalue);

//监听父组件的值
watch(
  () => props.modelvalue,
  () => {
    modelvalue.value = props.modelvalue;
  },
);

// 通过emit将值传递给父组件
emit("update:modelvalue", modelvalue);
</script>
<style lang="scss" scoped></style>

autocomplete

<template>
  <el-autocomplete
    v-bind="$attrs"
    v-model="modelvalue"
    style="width: 100%"
    @select="props.select ? props.select($event) : false"
    @change="props.change ? props.change($event) : false"
  />
</template>
<script lang="ts" setup>
import { ref, watch } from "vue";
const emit = defineemits(["update:modelvalue"]);
const props = defineprops({
  modelvalue: {
    type: string,
    default: () => "",
  },
  select: {
    type: function,
    default: () => () => {},
  },
  change: {
    type: function,
    default: () => () => {},
  },
});

const modelvalue = ref(props.modelvalue);
//监听父组件的值
watch(
  () => props.modelvalue,
  () => {
    modelvalue.value = props.modelvalue;
  },
);

// 通过emit将值传递给父组件
emit("update:modelvalue", modelvalue);
</script>

date

<template>
  <el-date-picker
    v-model="val"
    v-bind="$attrs"
    style="width: 100%"
    @change="props.change ? props.change($event) : false"
    @blur="props.blur ? props.blur($event) : false"
    @focus="props.focus ? props.focus($event) : false"
    @calendar-change="props.calendarchange ? props.calendarchange($event) : false"
    @panel-change="(a, b, c) => (props.panelchange ? props.panelchange(a, b, c) : false)"
    @visible-change="props.visiblechange ? props.visiblechange($event) : false"
  ></el-date-picker>
</template>

<script lang="ts" setup>
import { ref, watch } from "vue";
const emit = defineemits(["update:modelvalue"]);
const props = defineprops({
  modelvalue: {
    type: [string, array, date],
    default: () => "",
  },
  change: {
    type: function,
    default: () => () => {},
  },
  blur: {
    type: function,
    default: () => () => {},
  },
  focus: {
    type: function,
    default: () => () => {},
  },
  calendarchange: {
    type: function,
    default: () => () => {},
  },
  panelchange: {
    type: function,
    default: () => () => {},
  },
  visiblechange: {
    type: function,
    default: () => () => {},
  },
});

const val = ref(props.modelvalue);
//监听父组件的值
watch(
  () => props.modelvalue,
  () => {
    val.value = props.modelvalue;
  },
);

// 通过emit将值传递给父组件
emit("update:modelvalue", val);
</script>

checkbox

<template>
  <el-checkbox-group
    v-model="val"
    v-bind="$attrs"
    @change="props.change ? props.change($event) : false"
  >
    <template v-if="props.ctype === 'button'">
      <el-checkbox-button
        v-for="item in options"
        :key="item[valuefiled]"
        :value="item[valuefiled]"
        >{{ item[labelfiled] }}</el-checkbox-button
      >
    </template>
    <template v-else>
      <el-checkbox
        v-for="item in options"
        :key="item[valuefiled]"
        :value="item[valuefiled]"
        :border="props.ctype === 'border'"
        >{{ item[labelfiled] }}</el-checkbox
      >
    </template>
  </el-checkbox-group>
</template>
<script lang="ts" setup>
import { ref, watch } from "vue";
const emit = defineemits(["update:modelvalue"]);
const props = defineprops({
  modelvalue: {
    type: array,
    default: () => "",
  },
  options: {
    type: array as any,
    default: () => [],
  },
  valuefiled: {
    type: string,
    default: "value",
  },
  labelfiled: {
    type: string,
    default: "label",
  },
  ctype: {
    type: string,
    default: "",
  },
  change: {
    type: function,
    default: () => () => {},
  },
});

const val = ref(props.modelvalue);
//监听父组件的值
watch(
  () => props.modelvalue,
  () => {
    val.value = props.modelvalue;
  },
);

// 通过emit将值传递给父组件
emit("update:modelvalue", val);
</script>


radio

<template>
  <el-radio-group
    v-model="modelvalue"
    v-bind="$attrs"
    @change="props.change ? props.change($event) : false"
  >
    <template v-if="props.ctype === 'button'">
      <el-radio-button v-for="item in options" :key="item[valuefiled]" :value="item[valuefiled]">{{
        item[labelfiled]
      }}</el-radio-button>
    </template>
    <template v-else>
      <el-radio
        v-for="item in options"
        :border="props.ctype === 'border'"
        :key="item[valuefiled]"
        :value="item[valuefiled]"
        >{{ item[labelfiled] }}</el-radio
      >
    </template>
  </el-radio-group>
</template>
<script lang="ts" setup>
import { ref, watch } from "vue";
const emit = defineemits(["update:modelvalue"]);
const props = defineprops({
  modelvalue: {
    type: [number, string, boolean],
    default: () => "",
  },
  options: {
    type: array as any,
    default: () => [],
  },
  valuefiled: {
    type: string,
    default: "value",
  },
  labelfiled: {
    type: string,
    default: "label",
  },
  ctype: {
    //radio类型:button/border
    type: string,
    default: "",
  },
  change: {
    type: function,
    default: () => () => {},
  },
});

const modelvalue = ref(props.modelvalue);
//监听父组件的值
watch(
  () => props.modelvalue,
  () => {
    modelvalue.value = props.modelvalue;
  },
);

// 通过emit将值传递给父组件
emit("update:modelvalue", modelvalue);
</script>

cascader

<template>
  <el-cascader
    v-bind="$attrs"
    v-model="modelvalue"
    style="width: 100%"
    @change="props.change ? props.change($event) : false"
    @expand-change="props.expandchange ? props.expandchange($event) : false"
  />
</template>
<script lang="ts" setup>
import { ref, watch } from "vue";
const emit = defineemits(["update:modelvalue"]);
const props = defineprops({
  modelvalue: {
    type: array,
    default: () => [],
  },
  change: {
    type: function,
    default: () => () => {},
  },
  expandchange: {
    type: function,
    default: () => () => {},
  },
});

const modelvalue = ref(props.modelvalue);
//监听父组件的值
watch(
  () => props.modelvalue,
  () => {
    modelvalue.value = props.modelvalue;
  },
);

// 通过emit将值传递给父组件
emit("update:modelvalue", modelvalue);
</script>

types

/*
 * @author: vhen
 * @date: 2024-03-24 00:36:03
 * @lastedittime: 2024-03-24 15:21:30
 * @description: 现在的努力是为了小时候吹过的牛逼!
 * @filepath: \vhen-vue3-admin-pro\src\components\searchform\types.ts
 * 
 */
export type formtype = "input" | "select" | "radio" | "cascader" | "autocomplete" | "date" | "daterange" | "checkbox";

export interface itemoption {
  label: string
  value: string | number
  disabled?: boolean
}

export interface formitemvo {
  type: formtype //输入框类型
  label: string //输入框标题
  disabled?: boolean//表单是否可修改 默认false
  placeholder?: any //输入框默认显示内容
  prop: string //表单校验
  options?: itemoption[] | (() => itemoption[]) //选择器的可选子选项 select
  span?: number // 表单栅格数
  offset?: number // 表单栅格偏移
  clearable?: boolean // 是否可清空
  size?: string // 输入框大小
  multiple?: boolean // 是否多选
  collapsetags?: boolean // 是否折叠
  collapsetagsthreshold?: number // 多选时标签的阈值,大于该阈值时折叠
  filterable?: boolean // 是否可搜索
  allowcreate?: boolean // 是否支持创建
  radiotype?: string // 单选框类型
}

export interface propsvo {
  formdata: object  // 表单数据
  formitem: formitemvo[] // 表单配置项
  span?: number // 表单栅格数
  isseniorsearch?: boolean // 是否高级搜索
  gutter?: number // 表单栅格间隔
  showbutton?: boolean // 是否显示查询按钮
}

index.vue

<template>
  <section class="search-form">
    <el-form :model="props.formdata" v-bind="$attrs">
      <el-row :gutter="props.gutter">
        <el-col
          v-for="column in useformitem"
          :key="column.prop"
          :span="column.span"
          :offset="column.offset"
        >
          <el-form-item :label="`${column.label}`" :prop="column.prop">
            <component
              :is="componenttype[column.type]"
              v-bind="column"
              v-model="props.formdata[column.prop]"
            />
          </el-form-item>
        </el-col>
        <template v-if="$slots.default">
          <slot />
        </template>
        <el-col :span="props.span" style="flex: 1; max-width: 100%" v-if="showbutton">
          <div flex justify="end" items-center w-full h-full>
            <div
              v-if="isseniorsearch"
              flex
              items-center
              mr-2
              class="senior-search"
              @click="isshow = !isshow"
              cursor="pointer"
            >
              <div class="text">{{ isshow ? "收起" : "展开" }}</div>
              <div class="flex m-left-4">
                <el-icon>
                  <arrowup v-if="isshow" />
                  <arrowdown v-else />
                </el-icon>
              </div>
            </div>
            <el-button @click="$emit('reset')" :icon="refreshleft">重置</el-button>
            <el-button type="primary" class="m-bottom-12" @click="$emit('search')" :icon="search"
              >查询</el-button
            >
          </div>
        </el-col>
      </el-row>
    </el-form>
  </section>
</template>
<script lang="ts" setup>
import { refreshleft, search } from "@element-plus/icons-vue";
import { forminstance } from "element-plus";
import { computed, markraw, ref } from "vue";
import vhenautocomplete from "./src/vhenautocomplete.vue";
import vhencascader from "./src/vhencascader.vue";
import vhencheckbox from "./src/vhencheckbox.vue";
import vhendatepicker from "./src/vhendatepicker.vue";
import vheninput from "./src/vheninput.vue";
import vhenradio from "./src/vhenradio.vue";
import vhenselect from "./src/vhenselect.vue";
import { propsvo } from "./types";

const emit = defineemits<{
  (e: "reset"): void;
  (e: "search"): void;
}>();

const props = withdefaults(defineprops<propsvo>(), {
  isseniorsearch: false,
  gutter: 12,
  span: 8,
  showbutton: true,
});
const isshow = ref(false);
const useformitem = computed(() => {
  const isshowrow = props.isseniorsearch && !isshow.value && props.showbutton;
  if (isshowrow) {
    const num = math.floor(24 / props.span) - 1;
    return props.formitem.slice(0, num);
  } else {
    return props.formitem;
  }
});

const componenttype = markraw({
  input: vheninput,
  select: vhenselect,
  radio: vhenradio,
  cascader: vhencascader,
  autocomplete: vhenautocomplete,
  date: vhendatepicker,
  daterange: vhendatepicker,
  checkbox: vhencheckbox,
});

const formref = ref<forminstance>();
defineexpose({ formref });
</script>
<style lang="scss" scoped>
.senior-search {
  color: var(--el-text-color-regular);
  .text {
    font-size: 14px;
  }
}
</style>

组件案例

<template>
  <div>
    <searchform
      :formdata="formdata"
      :form-item="formitemlist"
      :rules="formrules"
      :span="6"
      label-position="top"
      label-width="100px"
      isseniorsearch
      @reset="resetdata"
      @search="handlesearch"
    >
    </searchform>
    <pre>
    {{ formdata }}
    </pre>
  </div>
</template>
<script lang="ts" setup>
import searchform from "@/components/searchform/index.vue";
import { formrules } from "element-plus";
import { reactive } from "vue";
const formdata = reactive({
  username: "",
  email: "843348394@qq.com",
  sex: "1",
  area: [],
  city: "",
});
const formitemlist = reactive([
  {
    type: "input",
    label: "姓名",
    prop: "username",
    clearable: true,
    span: 6,
    placeholder: "请输入姓名",
    // disabled: true,
    input: handleinput,
  },
  {
    type: "autocomplete",
    label: "邮箱",
    prop: "email",
    span: 6,
    "fetch-suggestions": querysearch,
  },
  {
    type: "daterange",
    label: "出生日期",
    prop: "birthday",
    span: 6,
  },
  {
    type: "radio",
    label: "-",
    prop: "sex",
    ctype: "button",
    span: 6,
    options: [
      {
        value: "0",
        label: "男",
      },
      {
        value: "1",
        label: "女",
      },
    ],
  },
  {
    type: "checkbox",
    label: "工作地点",
    prop: "area",
    span: 6,
    options: [
      {
        label: "北京",
        value: "beijing",
      },
      {
        label: "上海",
        value: "shanghai",
      },
      {
        label: "深圳",
        value: "shenzhen",
      },
    ],
  },
  {
    type: "select",
    prop: "city",
    label: "城市",
    span: 6,
    options: [
      {
        label: "北京",
        value: "beijing",
      },
      {
        label: "上海",
        value: "shanghai",
      },
      {
        label: "深圳",
        value: "shenzhen",
      },
    ],
  },
]);

const resetdata = () => {
  console.log(formdata);
};
const handlesearch = () => {
  console.log(formdata);
};

const formrules = reactive<formrules>({
  username: [{ required: true, message: "请输入姓名", trigger: "blur" }],
  email: [{ required: true, message: "请输入邮箱", trigger: "blur" }],
});
function handleinput(val: string | number) {
  console.log(val);
}
function querysearch(query: string, cb: any) {
  console.log(query);
  cb([query]);
}
</script>
<style lang="scss" scoped></style>

结束语

简单二次封装form 表单组件,如大家有更好的方案,欢迎大家评论区讨论,一起学习一起成长....

以上就是vue3+elementplus二次封装表单的实现代码的详细内容,更多关于vue3 elementplus二次封装表单的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com