AGILE TEAM
Skip to content

c_formSections - 表单区块组件

基于配置的表单折叠面板组件,集成楼层导航、工具栏、布局切换等功能

📦 功能特性

核心功能

  • 配置驱动 - 基于 JSON 配置自动渲染表单区块
  • 类型丰富 - 支持 input、select、textarea、date、datetime、number 等多种类型
  • 动态显示 - 支持根据条件动态显示/隐藏区块
  • 插槽扩展 - 支持特殊区块和自定义字段插槽
  • TypeScript - 完整的类型定义支持

🆕 增强功能

  • 楼层导航 - 左侧/右侧导航,支持锚点跳转
  • 工具栏 - 内置必填字段过滤开关,可扩展自定义工具
  • 布局切换 - 支持 2/3/4/5 列布局快速切换
  • 顶部操作栏 - 可配置标题和操作按钮
  • 自动过滤 - 组件内部自动处理必填字段过滤逻辑

📖 基础用法

vue
<template>
  <c_formSections
    :sections="sectionsConfig"
    :form="form"
    v-model:activeNames="activeNames"
  />
</template>

<script setup lang="ts">
import { ref } from "vue";
import c_formSections from "@/components/local/c_formSections/index.vue";

const form = ref({
  name: "",
  type: "",
  description: ""
});

const activeNames = ref(["1", "2"]);

const sectionsConfig = [
  {
    name: "1",
    id: "section-1",
    title: "基本信息",
    fieldsConfig: [
      { prop: "name", label: "名称", type: "input", required: true },
      {
        prop: "type",
        label: "类型",
        type: "select",
        required: true,
        options: [
          { label: "类型A", value: "A" },
          { label: "类型B", value: "B" }
        ]
      }
    ]
  },
  {
    name: "2",
    id: "section-2",
    title: "详细信息",
    fieldsConfig: [
      { prop: "description", label: "描述", type: "textarea", rows: 4 }
    ]
  }
];
</script>

🆕 完整功能用法

vue
<template>
  <c_formSections
    :sections="sectionsConfig"
    :form="form"
    v-model:activeNames="activeNames"
    show-header
    header-title="主档维护"
    :header-actions="headerActions"
    show-toolbar
    show-required-filter
    show-layout-switch
    :default-layout="5"
    :layout-options="[2, 3, 4, 5]"
    show-nav-tabs
    nav-tabs-position="left"
    :nav-tabs="navTabsConfig"
    label-width="100px"
    label-position="right"
    :gutter="20"
  >
    <!-- 特殊区块插槽 -->
    <template #special-3>
      <el-button>自定义内容</el-button>
    </template>
  </c_formSections>
</template>

<script setup lang="ts">
import { Check, Document } from "@element-plus/icons-vue";

const headerActions = [
  {
    label: "保存",
    type: "primary",
    icon: Check,
    onClick: () => console.log("保存")
  },
  {
    label: "取消",
    onClick: () => console.log("取消")
  }
];

const navTabsConfig = [
  { label: "基本信息", name: "basic", sectionName: "1" },
  { label: "详细信息", name: "detail", sectionName: "2" }
];
</script>

🔧 Props

基础属性

参数说明类型默认值
sections表单区块配置数组SectionConfig[][]
form表单数据对象FormDataType{}
activeNames激活的折叠面板 name 数组(支持 v-model)string[][]
rules表单验证规则any-
labelWidth表单标签宽度string'100px'
labelPosition表单标签位置'left' | 'right' | 'top''right'
gutter栅格间距number20
fieldSpan每个字段占据的栅格数(手动控制时使用)number-

🆕 增强属性

参数说明类型默认值
showHeader是否显示顶部标题栏booleanfalse
headerTitle标题栏标题文本string'主档维护'
headerActions顶部操作按钮HeaderAction[][]
showToolbar是否显示工具栏booleanfalse
showRequiredFilter是否显示必填字段过滤开关booleanfalse
showLayoutSwitch是否显示布局切换器booleanfalse
defaultLayout默认布局列数2 | 3 | 4 | 55
layoutOptions可选布局列数Array<2 | 3 | 4 | 5>[2,3,4,5]
showNavTabs是否显示楼层导航booleanfalse
navTabsPosition楼层导航位置'left' | 'right''left'
navTabs楼层导航配置(不传则自动从 sections 生成)NavTabConfig[][]

📊 配置接口

SectionConfig

typescript
interface SectionConfig {
  /** 折叠面板的 name 值(唯一标识) */
  name: string;
  /** 折叠面板 ID(用于锚点跳转) */
  id: string;
  /** 显示的标题 */
  title: string;
  /** 字段配置数组 */
  fieldsConfig: FieldConfig[];
  /** 显示条件函数(可选) */
  visible?: () => boolean;
  /** 是否为特殊处理的区块 */
  isSpecial?: boolean;
}

FieldConfig

typescript
interface FieldConfig {
  /** 字段属性名 */
  prop: string;
  /** 字段标签 */
  label: string;
  /** 字段类型 */
  type?:
    | "input"
    | "select"
    | "textarea"
    | "date"
    | "datetime"
    | "number"
    | "custom";
  /** 栅格占据列数 */
  span?: number;
  /** 是否必填 */
  required?: boolean;
  /** 占位符文本 */
  placeholder?: string;
  /** 是否可清空(select) */
  clearable?: boolean;
  /** 下拉选项(select) */
  options?: Array<{ label: string; value: string | number }>;
  /** 文本域行数(textarea) */
  rows?: number;
  /** 最小值(number) */
  min?: number;
  /** 最大值(number) */
  max?: number;
  /** 数字精度(number) */
  precision?: number;
  /** 步长(number) */
  step?: number;
}

🆕 HeaderAction

typescript
interface HeaderAction {
  /** 按钮文本 */
  label: string;
  /** 按钮类型 */
  type?: "primary" | "success" | "warning" | "danger" | "info" | "text" | "";
  /** 图标(Element Plus Icon 组件) */
  icon?: Component;
  /** 点击事件回调 */
  onClick: () => void;
  /** 是否禁用 */
  disabled?: boolean;
}

🆕 NavTabConfig

typescript
interface NavTabConfig {
  /** 标签名称(唯一标识) */
  name: string;
  /** 显示文本 */
  label: string;
  /** 关联的 section name(用于锚点跳转) */
  sectionName?: string | null;
}

🎨 插槽

🆕 工具栏插槽

toolbar-left - 工具栏左侧自定义内容:

vue
<c_formSections>
  <template #toolbar-left>
    <el-button size="small">自定义按钮</el-button>
  </template>
</c_formSections>

toolbar-right - 工具栏右侧自定义内容:

vue
<c_formSections>
  <template #toolbar-right>
    <span>自定义文本</span>
  </template>
</c_formSections>

特殊区块插槽

用于渲染 isSpecial: true 的区块自定义内容:

vue
<c_formSections :sections="sections" :form="form">
  <template #special-3="{ section }">
    <el-row :gutter="20">
      <el-col :span="24">
        <el-button type="primary" @click="addItem">+ 添加项</el-button>
      </el-col>
    </el-row>
  </template>
</c_formSections>

自定义字段插槽

用于渲染 type: 'custom' 的字段自定义内容:

vue
<c_formSections :sections="sections" :form="form">
  <template #field-customField="{ field, form }">
    <custom-input v-model="form[field.prop]" />
  </template>
</c_formSections>

📝 完整示例

vue
<template>
  <div class="form-container">
    <!-- 必填字段过滤开关 -->
    <el-switch v-model="showRequiredOnly" />

    <!-- 表单区块组件 -->
    <c-form-sections
      :sections="visibleSections"
      :form="form"
      v-model:activeNames="activeNames"
      :rules="formRules"
      :fieldSpan="layoutColumns === 2 ? 12 : 6"
    >
      <!-- 特殊需求区块 -->
      <template #special-3>
        <el-row :gutter="20">
          <el-col :span="24">
            <el-button type="primary" plain @click="addRequirement">
              + 添加需求
            </el-button>
          </el-col>
        </el-row>
      </template>
    </c-form-sections>
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from "vue";
import CFormSections from "@/components/local/c_formSections/index.vue";
import { filterFieldsByRequired } from "@/components/local/c_formSections/data";

const showRequiredOnly = ref(false);
const layoutColumns = ref(5);
const activeNames = ref(["1", "2", "3"]);

const form = ref({
  name: "",
  code: "",
  type: "",
  count: 0,
  date: null
});

const sectionsConfigRaw = [
  {
    name: "1",
    id: "section-1",
    title: "基本信息",
    fieldsConfig: [
      { prop: "name", label: "名称", required: true },
      { prop: "code", label: "编码", required: true },
      {
        prop: "type",
        label: "类型",
        type: "select",
        options: [
          { label: "类型A", value: "A" },
          { label: "类型B", value: "B" }
        ]
      }
    ]
  },
  {
    name: "2",
    id: "section-2",
    title: "详细信息",
    fieldsConfig: [
      { prop: "count", label: "数量", type: "number", min: 0 },
      { prop: "date", label: "日期", type: "date" }
    ]
  },
  {
    name: "3",
    id: "section-3",
    title: "特殊需求",
    fieldsConfig: [],
    isSpecial: true,
    visible: () => !showRequiredOnly.value
  }
];

// 动态过滤字段
const visibleSections = computed(() => {
  return sectionsConfigRaw.map((section) => ({
    ...section,
    fieldsConfig: filterFieldsByRequired(
      section.fieldsConfig,
      showRequiredOnly.value
    )
  }));
});

const formRules = {
  name: [{ required: true, message: "请输入名称", trigger: "blur" }],
  code: [{ required: true, message: "请输入编码", trigger: "blur" }]
};

const addRequirement = () => {
  console.log("添加特殊需求");
};
</script>

🛠️ 工具函数

filterFieldsByRequired

根据必填条件过滤字段配置:

typescript
import { filterFieldsByRequired } from "@/components/local/c_formSections/data";

const filteredFields = filterFieldsByRequired(fieldsConfig, true);

hasSectionFields

判断区块是否有可显示的字段:

typescript
import { hasSectionFields } from "@/components/local/c_formSections/data";

const hasFields = hasSectionFields(section.fieldsConfig);

generateDefaultFormData

生成默认表单数据:

typescript
import { generateDefaultFormData } from "@/components/local/c_formSections/data";

const defaultForm = generateDefaultFormData(sectionsConfig);

💡 最佳实践

  1. 配置管理 - 将 sections 配置放在独立的 data.ts 文件中
  2. 类型安全 - 使用 TypeScript 定义表单数据接口
  3. 动态过滤 - 使用 computed 实现响应式字段过滤
  4. 插槽使用 - 对于复杂的自定义字段,使用插槽扩展
  5. 验证规则 - 配合 Element Plus 的 rules 实现表单验证

🎯 应用场景

  • ✅ 订单新增/编辑页面
  • ✅ 配置管理表单
  • ✅ 多区块复杂表单
  • ✅ 需要动态显示/隐藏字段的表单
  • ✅ 企业级业务系统表单页面

📚 真实项目示例

项目中已有完整可用的示例页面,可作为开发模板:

新增编辑页示例

路径: src/views/sale/demo/add-demo/

特性:

  • ✅ 使用 c_formSections 组件展示所有功能
  • ✅ 集成楼层导航、布局切换、必填过滤
  • ✅ 配置驱动的表单字段(50+ 字段)
  • ✅ 真实的保存/草稿/取消逻辑(使用 request + Mock)
  • ✅ 项次信息管理(带分页,使用 jh-pagination)
  • ✅ 全屏模式支持
  • ✅ jh-drag-col 拖动调整布局

使用方式:

  1. 复制 add-demo 目录作为模板
  2. 修改 data.ts 中的 API_CONFIG 为实际接口地址
  3. 修改 formFieldsData 配置为业务字段
  4. 修改 modalConfig 为业务需求
  5. 创建对应的 Mock 文件(开发阶段)

关键代码结构:

add-demo/
├── index.vue      # 视图层(176行)- 组件使用和布局
├── data.ts        # 逻辑层(400+行)- 配置和业务逻辑
├── index.scss     # 样式层(207行)- 页面样式
└── mock/
    └── add-demo.ts  # Mock 数据(50条项次数据)

推荐作为团队内部的标准模板使用!

You may not distribute, modify, or sell this software without permission.