Vue3动态表单

大家好我是程序员阿晶啊,我又来了。

Vue3动态表单

前言

在查询表格数据的时候,常常见到表格上方有一个表单区域,用于数据查询条件动态查询表格数据。如下图:

Vue3动态表单

在此,本人查询资料结合Vue3开发了一个可以复用的组件。

Vue3动态表单

可根据需要进行配置表单项(支持input,select,date类型的表单)和表格(支持表头配置)及动态表格数据等。

表单配置组件

表单配置组件:

QueryCondition.vue

<template>
  <div style="width: 100%; height: 100%" class="form-condition">
    <div class="form-content">
      <el-form label-width="100px">
        <el-row v-for="(formType, indexs) in formTypeArray" :key="indexs">
          <el-col
            :span="span"
            v-for="(data, index) in formTypeArray[indexs]"
            :key="index"
          >

            <el-form-item :label="data.label">
              <!-- 输入框类型 -->
              <el-input
                v-if="data.type == 'input'"
                v-model="formTypeArray[indexs][index].value"
                :placeholder="data.placeholder"
              >
</el-input>
              <!-- 时间类型 -->
              <el-date-picker
                popper-class="date-style"
                v-if="data.type == 'date'"
                type="date"
                :placeholder="data.placeholder"
                v-model="formTypeArray[indexs][index].value"
              >
</el-date-picker>
              <!-- 下拉框类型 -->
              <el-select
                :popper-append-to-body="false"
                v-if="data.type == 'select'"
                v-model="formTypeArray[indexs][index].value"
                :placeholder="data.placeholder"
              >

                <el-option
                  v-for="(item, i) in data.option"
                  :key="i"
                  :label="item.label"
                  :value="item.value"
                >
</el-option>
              </el-select>
            </el-form-item>
            <div v-if="data.type == 'searchAndClear'" id = "searchAndClear">
              <div class="content-btn">
                <div
                  class="btn-search"
                  style="float: left"
                  @click="getCondition"
                >

                  <i class="el-icon-search"></i>搜索
                </div>
                <div
                  class="btn-clean"
                  style="float: left"
                  @click="cleanFormValue"
                >

                  清空
                </div>
              </div>
            </div>
          </el-col>
        </el-row>
      </el-form>
    </div>
  </div>
</template>
 
<script lang="ts">
import { inject, ref } from "@vue/runtime-core";
import { watchEffect, defineComponent } from "vue";

export default defineComponent({
  name"QueryCondition",
  emits: ["conditionParams"],
  setup(props: any, context: any) {
    let span = ref(6);
    let formTypeArray = ref(new Array());
    const formTypeConfig = inject("formTypeConfig");
    /**
     * 给父组件返回表单输入的内容
     */

    const getCondition = () => {
      context.emit("conditionParams", formTypeArray.value);
    };
    /**
     * 清空表单内容
     */

    const cleanFormValue = () => {
      formTypeArray.value.forEach((e, index) => {
        for (let i = 0; i < e.length; i++) {
          formTypeArray.value[index][i].value = "";
        }
      });
    };
    // 监听表单配置数据 数据变化时, 更新配置
    watchEffect(() => {
      console.log("-------监听formTypeConfig--------");
      console.log(formTypeConfig);
      setFormType(formTypeConfig, 4);
    });
    /**
     * 设置表单类型
     * @param array 表单配置
     * @param col 每行表单项个数
     */

    function setFormType(array: any, col: any{
      if (col == null || col == "" || array == null || array == "") {
        col = 4// 默认分4列
      }
      span.value = 24 / col;
      // 只有一行
      if (col >= array.length) {
        formTypeArray.value.push(array);
      } else {
        // 超过一行
        let deb = col;
        let i = 0;
        while (i < array.length) {
          let item = []; // null
          while (i < deb) {
            item.push(array[i]);
            console.log(deb);
            i++;
          }
          formTypeArray.value.push(item);
          if (array.length - i < col) {
            deb = array.length;
          } else {
            deb = i + col;
          }
        }
      }
      console.log("---------表单配置数据 SUCCESS----------");
    }
    return {
      formTypeArray,
      span,
      getCondition,
      cleanFormValue,
    };
  },
});
</script>
<style>
.form-condition .el-input__inner {
  border2px solid #285267;
  height35px;
  background-color#111d30;
}
.form-condition .el-select-dropdown {
  background#1f3758e0;
}
.form-condition .el-select-dropdown__item.hover,
.el-select-dropdown__item:hover {
  background-color#314665a1;
}
.date-style .el-picker__popper .date-style .el-popper .is-light .is-pure {
  background#1f3758e0;
}
.date-style .el-picker-panel {
  background#1f3758e0;
}
.search-third .el-input__inner {
  height36px;
}
.search-third .el-input {
  margin-top: -3px;
}
.search-header .el-input__inner {
  border2px solid #285267;
  height35px;
  background-color#111d30;
}
</style>
<style scoped>
.form-condition {
  border-radius3px 3px 3px 3px;
  backgroundrgba(811210.48);
  padding-top16px;
  margin-bottom8px;
}
.form-condition .el-dialog__header {
  padding-top0px;
}

/*事件*/
.btn-search:hover {
  color#aa8383;
  background#2fce7c;
}

.btn-clean:hover {
  color#aa8383;
}

.content-btn > div {
  margin-right16px;
}

.content-btn > div:first-child {
  border2px solid #328597;
  height28px;
  width69px;
  color#d2cbcb;
  text-align: center;
  line-height28px;
  border-radius3px 3px 3px 3px;
  background-imagelinear-gradient(#347489, #0e1d32);
  font-size10px;
  cursor: pointer;
}

.content-btn > div:last-child {
  border2px solid #415770;
  height28px;
  width69px;
  color#d2cbcb;
  text-align: center;
  line-height28px;
  border-radius3px 3px 3px 3px;
  background-color#1a2942;
  font-size10px;
  cursor: pointer;
}
.search-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom16px;
  padding0 16px;
}
.search-first {
  float: left;
  border1px solid rgba(1022262510.24);
  border-radius5px 5px 5px 5px;
}
/* 搜索框 */
.search-first > ul {
  list-style: none;
  display: flow-root;
}
.search-first > ul li {
  float: left;
  margin3px 9px;
  line-height22px;
}
.search-first > ul li:not(:first-child)::after {
  content"";
  width1px;
  height14px;
  backgroundlinear-gradient(180deg, #b1d8ff 0%, rgba(1772162550100%);
  display: inline-block;
  position: absolute;
  top10px;
  margin0 -9px;
}
.search-first > ul li div {
  /*border: 2px solid #328597;*/
  height22px;
  width54px;
  color#99bbdf;
  text-align: center;
  line-height27px;
  /*border-radius: 3px 3px 3px 3px;*/
  /*background-image: linear-gradient(#347489, #0E1D32);*/
  font-size10px;
  cursor: pointer;
}
/* 选中*/
.search-first > ul li:first-child div {
  border1px solid #328597;
  height22px;
  width54px;
  color#d2cbcb;
  text-align: center;
  line-height22px;
  border-radius2px;
  background-imagelinear-gradient(
    180deg,
    rgba(1022262510.40%,
    rgba(1022262510100%
  );
  font-size10px;
}
.header-right {
  display: flex;
  align-items: center;
}
.header-right-button {
  padding5px 10px;
  margin-right16px;
  backgroundlinear-gradient(
    180deg,
    rgba(1022262510.40%,
    rgba(1022262510100%
  );
  border-radius2px;
  border1px solid;
  font-size12px;
  color#99bbdf;
  cursor: pointer;
}
.search-second {
  display: flow-root;
  float: left;
  color#d4cccc;
  margin-top1px;
  height26px;
  border1px solid #21dde6;
  box-shadow0 0 0 1px #5d4b4b;
  border-radius4px 4px 4px 4px;
  padding5px 4px 1px 7px;
  margin-left12px;
  margin-right12px;
  cursor: pointer;
}
.search-second > div {
  float: left;
}
.search-second div:first-child img {
  width18px;
  height19px;
}
.search-second div:last-child img {
  width20px;
  height21px;
}
.search-third {
  float: left;
  margin-top1px;
}
.form-content {
  padding16px 16px 0;
}

#searchAndClear {
  display: flex;
  align-items: center;
  height0px;
  padding0 30px;
  margin-left20px;
}
</style>

1、上面的formTypeConfig变量会接收从父组件传来的数据配置表单项,所以先从它入手

Vue3动态表单

父组件:

App.vue

provide("formTypeConfig", [
      {
        label"用户名称",
        type"input",
        placeholder"请输入. . .",
        value"",
      },
      { label"省份"type"input"placeholder"请输入. . ."value"" },
      { label"模式"type"input"placeholder"请输入. . ."value"" },
      { label"行业"type"input"placeholder"请输入. . ."value"" },
      {
        label"标签",
        type"select",
        placeholder"请选择",
        option: [
          { label"第一项"value"1" },
          { label"第二项"value"2" },
        ],
        value"",
      },
      {
        label"分公司",
        type"select",
        placeholder"请选择",
        option: [
          { label"第一项"value"1" },
          { label"第二项"value"2" },
        ],
        value"",
      },
      {
        label"受理员工号",
        type"input",
        placeholder"请输入. . .",
        value"",
      },
      { label"流水号"type"input"placeholder"请输入. . ."value"" },
      { label"时间"type"date"placeholder"请输入. . ."value"" },
      { label""type"searchAndClear"placeholder""value"" },
    ]);

在父组件中配置好数据后,表单就能根据配置自己显示出来啦。

Vue3动态表单

2、搜索和清空

搜索绑定getCondition函数,它会把你输入到表单的数据发送给父组件,具体的搜索操作(数据接口)请在父组件中写,搜索出来的数据再次传给表单组件的formTypeConfig即可实现动态改变数据。

/**
 * 给父组件返回表单输入的内容
 */

const getCondition = () => {
    context.emit("conditionParams", formTypeArray.value);
};
/**
 * 清空表单内容
 */

const cleanFormValue = () => {
    formTypeArray.value.forEach((e, index) => {
        for (let i = 0; i < e.length; i++) {
            formTypeArray.value[index][i].value = "";
        }
    });
};

在组件中还有一个重要的方法setFormType。他是组件的一些逻辑,可不了解,直接copy,需要注意col参数,它是配置每行显示的表单项个数,可根据需要进行修改。

// 监听表单配置数据 数据变化时, 更新配置
    watchEffect(() => {
      console.log("-------监听formTypeConfig--------");
      console.log(formTypeConfig);
      setFormType(formTypeConfig, 4);
    });
    /**
     * 设置表单类型
     * @param array 表单配置
     * @param col 每行表单项个数
     */

    function setFormType(array: any, col: any{
      if (col == null || col == "" || array == null || array == "") {
        col = 4// 默认分4列
      }
      span.value = 24 / col;
      // 只有一行
      if (col >= array.length) {
        formTypeArray.value.push(array);
      } else {
        // 超过一行
        let deb = col;
        let i = 0;
        while (i < array.length) {
          let item = []; // null
          while (i < deb) {
            item.push(array[i]);
            console.log(deb);
            i++;
          }
          formTypeArray.value.push(item);
          if (array.length - i < col) {
            deb = array.length;
          } else {
            deb = i + col;
          }
        }
      }
      console.log("---------表单配置数据 SUCCESS----------");
    }

表格配置组件

TableDialog.vue

<template>
  <div class="content-table" style="width: 100%">
    <!-- 表头 -->
    <el-table
      :data="tableData"
      row-class-name="row-style"
      header-row-class-name="row-style"
      :show-header="isShowHeader"
    >

      <!--<el-table-column type="selection" width="55"></el-table-column>-->
      <el-table-column
        v-for="(head, index) in tableHeader"
        :key="index"
        :label="head.label"
        :sortable="head.sortable"
        :prop="head.fieldName"
      >

      </el-table-column>
      <el-table-column align="center" width="60px" label="操作">
      </el-table-column>
    </el-table>

     <div class="pagination">
      <el-pagination
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :current-page="page.currentPage"
        :page-sizes="[10, 20, 30, 40]"
        :page-size="page.size"
        layout="total, sizes, prev, pager, next"
        :total="page.total"
        popper-class="select_bottom"
      >

      </el-pagination>
    </div>
  </div>
</template>
<script>
import {
  watchEffect,
  defineComponent,
  inject,
  ref,
  reactive,
  toRefs,
  toRef,
from "vue";

export default defineComponent({
  name"TableDialog",
  emits: ["tableMessage"],
  setup(props, context) {
    // 数据及配置
    let tableData = reactive([]);
    let page = reactive({});
    let isShowHeader = ref(true);
    // 表头配置
    let tableHeader = reactive([]);
    const tableHeaderConfig = inject("tableHeaderConfig");
    const tableDataConfig = inject("tableDataConfig");
    // 监听表单配置数据
    watchEffect(() => {
      console.log("-------监听tableHeaderConfig,tableDataConfig--------");
      tableHeader = tableHeaderConfig ? tableHeaderConfig : [];
      tableData = tableDataConfig ? tableDataConfig.tableData : [];
      page = tableDataConfig ? tableDataConfig.page : {};
    });

    function submitData(scope{
      context.emit("tableMessage", { page: page, scope: scope });
    }

    function handleSizeChange(val{
      console.log(`每页 ${val} 条`);
      context.emit("tableMessage", { page: page, scopenull });
    }

    function handleCurrentChange(val{
      console.log(`当前页: ${val}`);
      context.emit("tableMessage", { page: page, scopenull });
    }

    return {
      tableHeader,
      tableData,
      submitData,
      handleSizeChange,
      handleCurrentChange,
      page,
      isShowHeader,
    };
  },
});
</script>
<style scoped>
.content-table :deep(.el-table) {
  backgroundrgba(811210.48);
}
:deep(.row-style) {
  backgroundrgba(811210.48);
}
:deep(.el-table::before) {
  height0;
}
.content-table :deep(.el-table th.el-table__cell) {
  backgroundrgba(811210.48);
  border-bottomrgba(811210.48);
}

.content-table .el-table tr {
  background-color#202c5d00;
  font-size10px;
}

.content-table :deep(.el-table td.el-table__cell) {
  border-bottomrgba(811210.48);
}
.content-table
  :deep(.el-table--enable-row-hover
    .el-table__body
    tr:hover
    > td.el-table__cell) {
  background-color#131a2b;
  border-bottomrgba(811210.48);
}

.content-table :deep(.el-checkbox__inner) {
  background-color#12203c2b;
}
.pagination {
  margin-top16px;
  text-align: right;
}
.pagination :deep(.el-input__inner) {
  border2px solid #285267;
  height32px;
  background-color#111d30;
  color#b1d8ff;
}

.pagination :deep(.el-pager li) {
  border1px solid #285267;
  height30px;
  color#b1d8ff;
  background-color#111d30;
}
.pagination :deep(.el-pager li.active) {
  backgroundlinear-gradient(
    180deg,
    rgba(1022262510.40%,
    rgba(1022262510100%
  );
}

.pagination :deep(.el-pagination .btn-prev) {
  background-color#111d30;
  height30px;
  color#b1d8ff;
  border1px solid rgba(1772162550.4);
}
.pagination :deep(.el-pagination .btn-next) {
  background-color#111d30;
  height30px;
  color#b1d8ff;
  border1px solid rgba(1772162550.4);
}
:deep(.el-pagination__total) {
  color#b1d8ff;
}
.select_bottom :deep(.el-select-dropdown__item.selected) {
  color#606266;
  background#1f3758e0;
  border1px solid #517ca5;
}

.select_bottom :deep(.el-select-dropdown) {
  background-color#1f3758e0;
}
.btn-option {
  font-size22px;
  color#78b7b1;
  text-shadow0px 0px 1px #26a7ff;
  cursor: pointer;
}
</style>

和表单类似,表格的配置也需要接收父组件传来的配置,负责接收的参数如下:

// 表头
const tableHeaderConfig = inject("tableHeaderConfig");
// 表格数据
const tableDataConfig = inject("tableDataConfig");

数据格式如下:

App.vue

<template>
  <div>
    <QueryCondition @conditionParams="getCondition"> </QueryCondition>
    <TableDialog @tableMessage="getTableMessage"></TableDialog>
  </div>
</template>
 
<script lang = "ts">
import { defineComponent, provide, ref, reactive } from "vue";
import QueryCondition from "@/components/QueryCondition.vue";
import TableDialog from "@/components/TableDialog.vue";
export default defineComponent({
  components: {
    QueryCondition,
    TableDialog,
  },
  setup() {
    provide("formTypeConfig", [
      {
        label"用户名称",
        type"input",
        placeholder"请输入. . .",
        value"",
      },
      { label"省份"type"input"placeholder"请输入. . ."value"" },
      { label"模式"type"input"placeholder"请输入. . ."value"" },
      { label"行业"type"input"placeholder"请输入. . ."value"" },
      {
        label"标签",
        type"select",
        placeholder"请选择",
        option: [
          { label"第一项"value"1" },
          { label"第二项"value"2" },
        ],
        value"",
      },
      {
        label"分公司",
        type"select",
        placeholder"请选择",
        option: [
          { label"第一项"value"1" },
          { label"第二项"value"2" },
        ],
        value"",
      },
      {
        label"受理员工号",
        type"input",
        placeholder"请输入. . .",
        value"",
      },
      { label"流水号"type"input"placeholder"请输入. . ."value"" },
      { label"时间"type"date"placeholder"请输入. . ."value"" },
      { label""type"searchAndClear"placeholder""value"" },
    ]);

    const getCondition = (params: any) => {
      // 获取表单数据
      let result: Array<any> = [];
      for (let i = 0; i < params.length; i++) {
        for (let j = 0; j < params[i].length; j++) {
          result.push(params[i][j]);
        }
      }
      result = result.filter((e: any) => {
        if (e.label) {
          return e;
        }
      });
      console.log(result);
    };

    let tableHeader: any = ref([]);
    let tableDatas: any = ref([]);

    // 这里的数据,在真实开发中,其实可以从后台获取,这样就实现了动态表头了。
    tableHeader.value = [
      {
        fieldName"id",
        label"序号",
        width"250px",
        sortabletrue,
      },
      { fieldName"name"label"姓名"width"200px"sortabletrue },
      {
        fieldName"award",
        label"奖项",
        width"200px",
        sortabletrue,
      },
      {
        fieldName"address",
        label"来自",
        width"200px",
        sortabletrue,
      },
    ];

    // 这里的数据,在真实开发中,是从后台获取的,结合之前的动态表头,组成了动态表格。
    tableDatas.value = [
      {
        id1,
        name"李晶",
        award"金牌",
        address"江苏",
      },
      {
        id2,
        name"苏炳添",
        award"银牌",
        address"广东",
      },
    ];

    // 分发给子组件
    // 配置表头数据
    provide("tableHeaderConfig", tableHeader);
    // 配置单元格数据及属性/分页配置默认值
    provide("tableDataConfig", {
      page: { currentPage1size10total0 },
      tableData: tableDatas,
    });

    return {
      getCondition,
      //   getTableMessage,
    };
  },
});
</script>

<style scoped>
</style>

注:父组件中导入

表单:

<QueryCondition @conditionParams="getCondition"> </QueryCondition>

表格:

<TableDialog @tableMessage="getTableMessage"></TableDialog>

完整代码,其实上面已经给出,结果图在前言部分。

Vue3动态表单


原文始发于微信公众号(程序员阿晶):Vue3动态表单

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

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/19751.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!