1 场景

一般表单我们直接默认布局,也就是单列布局,突然有个人员信息表单,需要双列布局的需求,简单实现并拓展下

2 思路

直接无脑div+flex布局实现

3 代码

<template>
  <el-form ref="formRef" :model="formData" label-width="80px">
    <div class="form-top">
      <div class="form-top--left">
        <el-form-item label="姓名" prop="name">
          <el-input v-model="formData.name" placeholder="请输入姓名" />
        </el-form-item>
        <el-form-item label="年龄" prop="age">
          <el-input v-model="formData.age" placeholder="请输入年龄" />
        </el-form-item>
        <el-form-item label="性别" prop="sex">
          <el-radio-group v-model="formData.sex">
            <el-radio :label="1"></el-radio>
            <el-radio :label="2"></el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item label="邮箱" prop="email">
          <el-input v-model="formData.email" placeholder="请输入邮箱" />
        </el-form-item>

        <el-form-item label="排序" prop="sort" placeholder="请输入排序号">
          <el-input-number v-model="formData.sort" :min="1" />
        </el-form-item>
      </div>
      <div class="form-top--right">
        <el-form-item label="备注" prop="remark">
          <el-input
            v-model="formData.remark"
            type="textarea"
            placeholder="请输入备注"
            maxlength="100"
            show-word-limit
            resize="none"
          />
        </el-form-item>
        <el-form-item label="说明" prop="config">
          <el-input
            v-model="formData.config"
            placeholder="请输入说明"
            type="textarea"
            show-word-limit
            maxlength="200"
            :autosize="{ minRows: 6, maxRows: 6 }"
          />
        </el-form-item>
      </div>
    </div>
    <div class="form-bottom">
      <el-form-item>
        <el-button type="primary" @click="handleSubmit">确 定</el-button>
        <el-button type="warning" @click="handleClose">取 消</el-button>
      </el-form-item>
    </div>
  </el-form>
</template>

<script setup lang="ts">
const formRef = ref();

const formData = reactive({
  remark: "",
  age: 10,
  config: "",
  email: "",
  sex: "",
  id: null,
  name: "",
  sort: 0,
});

const handleClose = () => {};
const handleSubmit = () => {};
</script>
<style lang="scss" scoped>
.container {
  width: 600px;
  margin-top: 100px;
  .form-top {
    display: flex;
    justify-content: space-between;
    .form-top--right {
      flex: 1;
    }
  }
  .form-bottom {
    display: flex;
    justify-content: flex-end;
  }
}
</style>

在这里插入图片描述

这样的无脑实现实在是对不起付出的时间,不嫩复用是最大问题

4 拓展

封装el-form,增加slot

// Form.vue

<template>
  <el-form>
    <slot></slot>
    <div class="form-top" v-if="!slot.default">
      <div class="form-top--left" v-if="slot.left">
        <slot name="left"></slot>
      </div>
      <div class="form-top--right" v-if="slot.right">
        <slot name="right"></slot>
      </div>
    </div>
    <div class="form-bottom" v-if="slot.bottom">
      <slot name="bottom"></slot>
    </div>
  </el-form>
</template>

<script setup lang="ts">
const slot = useSlots();
</script>
<style lang="scss" scoped>
.form-top {
  display: flex;
  justify-content: space-between;
  .form-top--left {
    flex: 1;
  }
  .form-top--right {
    flex: 1;
  }
}
.form-bottom {
  display: flex;
  justify-content: flex-end;
}
</style>

// index.vue

<template>
  <Form ref="formRef" :model="formData" label-width="80px">
    <template #left>
      <el-form-item label="姓名" prop="name">
        <el-input v-model="formData.name" placeholder="请输入姓名" />
      </el-form-item>
      <el-form-item label="年龄" prop="age">
        <el-input v-model="formData.age" placeholder="请输入年龄" />
      </el-form-item>
      <el-form-item label="性别" prop="sex">
        <el-radio-group v-model="formData.sex">
          <el-radio :label="1"></el-radio>
          <el-radio :label="2"></el-radio>
        </el-radio-group>
      </el-form-item>
      <el-form-item label="邮箱" prop="email">
        <el-input v-model="formData.email" placeholder="请输入邮箱" />
      </el-form-item>

      <el-form-item label="排序" prop="sort" placeholder="请输入排序号">
        <el-input-number v-model="formData.sort" :min="1" />
      </el-form-item>
    </template>
    <template #right>
      <el-form-item label="备注" prop="remark">
        <el-input
          v-model="formData.remark"
          type="textarea"
          placeholder="请输入备注"
          maxlength="100"
          show-word-limit
          resize="none"
        />
      </el-form-item>
      <el-form-item label="说明" prop="config">
        <el-input
          v-model="formData.config"
          placeholder="请输入说明"
          type="textarea"
          show-word-limit
          maxlength="200"
          :autosize="{ minRows: 6, maxRows: 6 }"
        />
      </el-form-item>
    </template>
    <template #bottom>
      <el-form-item>
        <el-button type="primary" @click="handleSubmit">确 定</el-button>
        <el-button type="warning" @click="handleClose">取 消</el-button>
      </el-form-item>
    </template>
  </Form>
</template>

<script setup lang="ts">
import Form from "./Form.vue";
const formRef = ref();

//添加人员表单
const formData = reactive({
  remark: "",
  age: 10,
  config: "",
  email: "",
  sex: "",
  id: null,
  name: "",
  sort: 0,
});

const handleClose = () => {};
const handleSubmit = () => {};
</script>
<style lang="scss" scoped>
.form-top {
  display: flex;
  justify-content: space-between;
  .form-top--right {
    flex: 1;
  }
}
.form-bottom {
  display: flex;
  justify-content: flex-end;
}
</style>

依然不够通用,因为布局是固定的,如果想要其他布局,要么修改Form.vue,要么重新封装

5 继续拓展

抽出layout,形成Layout.vue组件,拿出祖传技艺slot传递
// Layout.vue

<template>
  <div>
    <slot></slot>
    <div class="form-top" v-if="!slot.default">
      <div class="form-top--left" v-if="slot.left">
        <slot name="left"></slot>
      </div>
      <div class="form-top--right" v-if="slot.right">
        <slot name="right"></slot>
      </div>
    </div>
    <div class="form-bottom" v-if="slot.bottom">
      <slot name="bottom"></slot>
    </div>
  </div>
</template>

<script setup lang="ts">
const slot = useSlots();
</script>
<style lang="scss" scoped>
.form-top {
  display: flex;
  justify-content: space-between;
  .form-top--left {
    flex: 1;
  }
  .form-top--right {
    flex: 1;
  }
}
.form-bottom {
  display: flex;
  justify-content: flex-end;
}
</style>

// Form.vue

<template>
  <el-form>
    <Layout>
      <template v-for="item in Object.keys(slot)" :key="item" #[item]>
        <slot :name="item"></slot>
      </template>
    </Layout>
  </el-form>
</template>

<script setup lang="ts">
import Layout from "./Layout.vue";
const slot = useSlots();
</script>
<style lang="scss" scoped></style>

// index.vue

<template>
  <Form ref="formRef" :model="formData" label-width="80px">
    <template #left>
      <el-form-item label="姓名" prop="name">
        <el-input v-model="formData.name" placeholder="请输入姓名" />
      </el-form-item>
      <el-form-item label="年龄" prop="age">
        <el-input v-model="formData.age" placeholder="请输入年龄" />
      </el-form-item>
      <el-form-item label="性别" prop="sex">
        <el-radio-group v-model="formData.sex">
          <el-radio :label="1"></el-radio>
          <el-radio :label="2"></el-radio>
        </el-radio-group>
      </el-form-item>
      <el-form-item label="邮箱" prop="email">
        <el-input v-model="formData.email" placeholder="请输入邮箱" />
      </el-form-item>

      <el-form-item label="排序" prop="sort" placeholder="请输入排序号">
        <el-input-number v-model="formData.sort" :min="1" />
      </el-form-item>
    </template>
    <template #right>
      <el-form-item label="备注" prop="remark">
        <el-input
          v-model="formData.remark"
          type="textarea"
          placeholder="请输入备注"
          maxlength="100"
          show-word-limit
          resize="none"
        />
      </el-form-item>
      <el-form-item label="说明" prop="config">
        <el-input
          v-model="formData.config"
          placeholder="请输入说明"
          type="textarea"
          show-word-limit
          maxlength="200"
          :autosize="{ minRows: 6, maxRows: 6 }"
        />
      </el-form-item>
    </template>
    <template #bottom>
      <el-form-item>
        <el-button type="primary" @click="handleSubmit">确 定</el-button>
        <el-button type="warning" @click="handleClose">取 消</el-button>
      </el-form-item>
    </template>
  </Form>
</template>

<script setup lang="ts">
import Form from "./Form.vue";
const formRef = ref();

//添加人员表单
const formData = reactive({
  remark: "",
  age: 10,
  config: "",
  email: "",
  sex: "",
  id: null,
  name: "",
  sort: 0,
});

const handleClose = () => {};
const handleSubmit = () => {};
</script>
<style lang="scss" scoped>
.form-top {
  display: flex;
  justify-content: space-between;
  .form-top--right {
    flex: 1;
  }
}
.form-bottom {
  display: flex;
  justify-content: flex-end;
}
</style>

6 总结

  1. 布局和数据分离

  2. 灵活扩展layout

    2.1 可以建立多个layout文件,批量引入(或动态引入)Form.vue中 ,Form.vue中利用动态组件component加载各个layout文件

    2.2 可以建立多个layout文件,批量引入(或动态引入)LayoutIndex.vue文件中,LayoutIndex.vue文件利用动态组件component加载各个layout文件,然后Form.vue中只渲染LayoutIndex,同时通过传参决定渲染那个layout

  3. 新增layout时,只需要新增文件(所有layout文件已经被批量引入<或动态引入>),符合“开闭”原则

tip:

批量引入(vite):import.meta.glob(‘./layout/**/*.vue’);

动态引入:defineAsyncComponent(() => import(./components/${layoutName}.vue));

Logo

前往低代码交流专区

更多推荐