# SuperForm 超级表单组件

一个基于 Vue 3 + Element Plus 的强大表单组件，支持高度可配置化和完整的属性透传。

## ✨ 特性

- 🎯 **支持所有 Element Plus 表单组件** - 完整支持 input、select、date、upload 等 20+ 种组件
- 🔄 **原生属性透传** - 所有原生属性和组件库属性均可直接透传
- 📝 **灵活的表单验证** - 支持规则配置，默认不验证，可选择性开启
- 🌐 **接口集成** - 内置保存、更新、获取详情接口配置
- ✏️ **编辑模式** - 自动识别新增/编辑模式
- 🎨 **高度可定制** - 支持自定义插槽、自定义组件
- 📱 **响应式布局** - 支持栅格系统和响应式配置
- 🚀 **开发效率** - 通过配置快速生成复杂表单，提升开发效率 10 倍+

## 📦 安装

将 `SuperForm.vue` 复制到你的项目中：

```
src/components/common/SuperForm.vue
```

## 🚀 快速开始

### 基础用法

```vue
<template>
  <SuperForm
    v-model="formData"
    :config="formConfig"
    :items="formItems"
    @submit="handleSubmit"
  />
</template>

<script setup>
import SuperForm from '@/components/common/SuperForm.vue'
import { ref } from 'vue'

const formData = ref({})

const formConfig = {
  labelWidth: '120px',
  showButtons: true
}

const formItems = [
  {
    field: 'username',
    label: '用户名',
    type: 'input',
    required: true,
    placeholder: '请输入用户名'
  },
  {
    field: 'email',
    label: '邮箱',
    type: 'input',
    rules: [
      { type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
    ]
  }
]

const handleSubmit = (data) => {
  console.log('表单数据:', data)
}
</script>
```

## 📋 Props 配置

### 表单配置 (config)

| 参数 | 说明 | 类型 | 默认值 |
|------|------|------|--------|
| labelWidth | 表单标签宽度 | string | '120px' |
| labelPosition | 表单标签位置 | string | 'right' |
| inline | 是否行内表单 | boolean | false |
| disabled | 是否禁用 | boolean | false |
| size | 表单尺寸 | string | 'default' |
| gutter | 栅格间隔 | number | 20 |
| showButtons | 是否显示操作按钮 | boolean | true |
| showSubmit | 是否显示提交按钮 | boolean | true |
| showReset | 是否显示重置按钮 | boolean | true |
| submitText | 提交按钮文本 | string | '提交' |
| resetText | 重置按钮文本 | string | '重置' |
| submitType | 提交按钮类型 | string | 'primary' |
| submitIcon | 提交按钮图标 | string | - |
| customButtons | 自定义按钮数组 | array | [] |
| buttonSpan | 按钮容器占位 | number | 24 |
| statusIcon | 是否显示验证图标 | boolean | true |
| scrollToError | 滚动到错误字段 | boolean | true |

### 表单项配置 (items)

#### 基础配置

| 参数 | 说明 | 类型 | 必填 |
|------|------|------|------|
| field | 字段名 | string | 是 |
| label | 标签文本 | string | 是 |
| type | 组件类型 | string | 是 |
| defaultValue | 默认值 | any | 否 |
| required | 是否必填 | boolean | 否 |
| rules | 验证规则 | array | 否 |
| span | 栅格占位 | number | 否 |
| offset | 栅格偏移 | number | 否 |

#### 支持的组件类型

| type 值 | 说明 | 组件 |
|---------|------|------|
| input | 输入框 | el-input |
| textarea | 文本域 | el-input[type=textarea] |
| password | 密码框 | el-input[type=password] |
| inputNumber / number | 数字输入 | el-input-number |
| select | 选择器 | el-select |
| radio / radioGroup | 单选框 | el-radio-group |
| checkbox / checkboxGroup | 多选框 | el-checkbox-group |
| date | 日期选择 | el-date-picker |
| datetime | 日期时间选择 | el-date-picker |
| daterange | 日期范围 | el-date-picker |
| timePicker / time | 时间选择 | el-time-picker |
| switch | 开关 | el-switch |
| slider | 滑块 | el-slider |
| rate | 评分 | el-rate |
| colorPicker | 颜色选择 | el-color-picker |
| cascader | 级联选择 | el-cascader |
| treeSelect | 树形选择 | el-tree-select |
| transfer | 穿梭框 | el-transfer |
| upload | 上传 | el-upload |
| autocomplete | 自动完成 | el-autocomplete |
| text / display | 文本展示 | span |
| html | HTML 内容 | div |
| divider | 分割线 | el-divider |
| alert | 警告 | el-alert |
| slot / custom | 自定义插槽 | slot |
| component | 自定义组件 | component |

## 🎯 详细用法示例

### 1. 输入框相关

```javascript
{
  field: 'username',
  label: '用户名',
  type: 'input',
  required: true,
  clearable: true,
  placeholder: '请输入用户名',
  maxlength: 20,
  'show-word-limit': true,
  prefixIcon: 'User',
  suffixIcon: 'ArrowRight'
}

// 文本域
{
  field: 'description',
  label: '描述',
  type: 'textarea',
  rows: 4,
  autosize: { minRows: 2, maxRows: 6 }
}

// 密码框
{
  field: 'password',
  label: '密码',
  type: 'password',
  'show-password': true
}
```

### 2. 选择器相关

```javascript
// 下拉选择
{
  field: 'category',
  label: '分类',
  type: 'select',
  clearable: true,
  filterable: true,
  multiple: true,
  placeholder: '请选择分类',
  options: [
    { label: '技术', value: 'tech' },
    { label: '生活', value: 'life' }
  ]
}

// 单选框
{
  field: 'gender',
  label: '性别',
  type: 'radio',
  options: [
    { label: '男', value: '1' },
    { label: '女', value: '2' }
  ]
}

// 多选框
{
  field: 'hobby',
  label: '爱好',
  type: 'checkbox',
  options: [
    { label: '阅读', value: 'reading' },
    { label: '运动', value: 'sports' }
  ]
}

// 级联选择器
{
  field: 'region',
  label: '地区',
  type: 'cascader',
  clearable: true,
  options: [
    {
      value: 'zhejiang',
      label: '浙江省',
      children: [
        { value: 'hangzhou', label: '杭州市' }
      ]
    }
  ]
}

// 树形选择器
{
  field: 'department',
  label: '部门',
  type: 'treeSelect',
  clearable: true,
  data: [
    {
      id: '1',
      label: '技术部',
      children: [
        { id: '1-1', label: '前端组' }
      ]
    }
  ]
}
```

### 3. 日期时间相关

```javascript
// 日期选择
{
  field: 'birthDate',
  label: '出生日期',
  type: 'date',
  'value-format': 'YYYY-MM-DD',
  placeholder: '请选择日期'
}

// 日期时间选择
{
  field: 'publishTime',
  label: '发布时间',
  type: 'datetime',
  'value-format': 'YYYY-MM-DD HH:mm:ss'
}

// 日期范围
{
  field: 'validDate',
  label: '有效期',
  type: 'daterange',
  'value-format': 'YYYY-MM-DD',
  'start-placeholder': '开始日期',
  'end-placeholder': '结束日期'
}

// 时间选择
{
  field: 'workTime',
  label: '工作时间',
  type: 'timePicker',
  'value-format': 'HH:mm:ss'
}
```

### 4. 数值与评分

```javascript
// 数字输入
{
  field: 'age',
  label: '年龄',
  type: 'number',
  min: 0,
  max: 150,
  step: 1,
  precision: 0
}

// 开关
{
  field: 'isPublished',
  label: '是否发布',
  type: 'switch',
  'active-text': '已发布',
  'inactive-text': '未发布',
  'active-value': 1,
  'inactive-value': 0
}

// 滑块
{
  field: 'priority',
  label: '优先级',
  type: 'slider',
  min: 0,
  max: 100,
  step: 5
}

// 评分
{
  field: 'rating',
  label: '评分',
  type: 'rate',
  max: 5,
  'allow-half': true
}
```

### 5. 上传相关

```javascript
{
  field: 'files',
  label: '文件上传',
  type: 'upload',
  action: '/api/upload',
  'show-file-list': true,
  multiple: true,
  drag: true,
  limit: 5,
  accept: '.jpg,.png,.pdf',
  'on-success': (response, file, fileList) => {
    console.log('上传成功', response)
  },
  'before-upload': (file) => {
    const isLt2M = file.size / 1024 / 1024 < 2
    if (!isLt2M) {
      ElMessage.error('文件大小不能超过 2MB')
    }
    return isLt2M
  }
}
```

### 6. 自定义插槽

```vue
<template>
  <SuperForm v-model="formData" :items="formItems">
    <!-- 自定义插槽 -->
    <template #customField="{ item, value, onChange }">
      <div class="custom-content">
        <span>{{ value }}</span>
        <el-button @click="handleCustomChange(onChange)">修改</el-button>
      </div>
    </template>
  </SuperForm>
</template>

<script>
const formItems = [
  {
    field: 'customField',
    label: '自定义字段',
    type: 'slot',
    slotName: 'customField'
  }
]
</script>
```

### 7. 自定义按钮

```javascript
const formConfig = {
  showButtons: true,
  customButtons: [
    {
      key: 'save',
      text: '保存草稿',
      type: 'info',
      icon: 'Document',
      loading: false,
      handler: () => {
        console.log('保存草稿')
      }
    },
    {
      key: 'preview',
      text: '预览',
      type: 'success',
      handler: () => {
        console.log('预览')
      }
    }
  ]
}
```

## 🔌 接口集成

### 自动提交接口

```vue
<SuperForm
  v-model="formData"
  :items="formItems"
  :save-api="'/api/user/create'"
  :update-api="'/api/user/update'"
  :fetch-api="'/api/user/detail'"
  @success="handleSuccess"
  @error="handleError"
/>
```

### 动态接口

```vue
<SuperForm
  :save-api="(isEdit) => isEdit ? '/api/update' : '/api/create'"
  :update-api="getUpdateApi"
/>
```

### 数据转换

```javascript
// 提交前转换数据
const dataTransform = (data) => {
  return {
    ...data,
    publishTime: dayjs(data.publishTime).unix()
  }
}

// 获取详情后转换数据
const responseTransform = (data) => {
  return {
    ...data,
    publishTime: dayjs.unix(data.publishTime).format('YYYY-MM-DD HH:mm:ss')
  }
}
```

## 🎨 高级特性

### 1. 动态表单项

```javascript
const showExtraFields = ref(true)

const formItems = computed(() => {
  const baseItems = [
    { field: 'name', label: '名称', type: 'input' }
  ]

  if (showExtraFields.value) {
    baseItems.push(
      { field: 'email', label: '邮箱', type: 'input' },
      { field: 'phone', label: '电话', type: 'input' }
    )
  }

  return baseItems
})
```

### 2. 条件验证

```javascript
const formItems = [
  {
    field: 'password',
    label: '密码',
    type: 'password',
    rules: [
      { required: true, message: '请输入密码', trigger: 'blur' },
      { min: 6, message: '密码长度不能少于6位', trigger: 'blur' },
      {
        validator: (rule, value, callback) => {
          if (value && !/(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/.test(value)) {
            callback(new Error('密码必须包含大小写字母和数字'))
          } else {
            callback()
          }
        },
        trigger: 'blur'
      }
    ]
  }
]
```

### 3. 字段联动

```javascript
const handleFieldChange = (field, value) => {
  if (field === 'country') {
    // 根据选择的国家更新城市选项
    updateCityOptions(value)
  }
}
```

### 4. 自定义组件

```javascript
const formItems = [
  {
    field: 'customData',
    label: '自定义组件',
    type: 'component',
    component: MyCustomComponent,
    componentProps: {
      // 传递给自定义组件的属性
      config: {}
    }
  }
]
```

## 📢 事件

| 事件名 | 说明 | 参数 |
|--------|------|------|
| submit | 表单提交 | (formData, isEdit) |
| success | 提交成功 | (response, formData, isEdit) |
| error | 提交失败 | (error, formData, isEdit) |
| change | 表单数据变化 | (formData) |
| field-change | 字段值变化 | (field, value, formData) |
| field-blur | 字段失焦 | (field, event) |
| field-focus | 字段聚焦 | (field, event) |
| validate | 验证触发 | (prop, isValid, message) |
| reset | 表单重置 | - |

## 🔧 方法

通过 ref 可以调用以下方法：

```javascript
const formRef = ref()

// 验证表单
await formRef.value.validate()

// 验证指定字段
await formRef.value.validateField('username')

// 清除验证
formRef.value.clearValidate()

// 重置表单
formRef.value.resetFields()

// 设置表单数据
formRef.value.setFormData({ name: '张三' })

// 获取表单数据
const data = formRef.value.getFormData()

// 设置字段值
formRef.value.setFieldValue('name', '李四')

// 获取字段值
const name = formRef.value.getFieldValue('name')

// 获取详情数据（用于编辑）
await formRef.value.fetchDetail(id)
```

## 🎯 最佳实践

### 1. 配置文件分离

```javascript
// formConfig.js
export const userFormConfig = {
  labelWidth: '120px',
  size: 'default'
}

export const userFormItems = [
  { field: 'name', label: '姓名', type: 'input', required: true },
  { field: 'email', label: '邮箱', type: 'input' }
]
```

### 2. 验证规则复用

```javascript
// validators.js
export const requiredRule = (message) => ({
  required: true,
  message,
  trigger: 'blur'
})

export const emailRule = {
  type: 'email',
  message: '请输入正确的邮箱地址',
  trigger: 'blur'
}

export const phoneRule = {
  pattern: /^1[3-9]\d{9}$/,
  message: '请输入正确的手机号',
  trigger: 'blur'
}
```

### 3. 类型定义

```typescript
interface FormItem {
  field: string
  label: string
  type: string
  required?: boolean
  rules?: any[]
  span?: number
  defaultValue?: any
  [key: string]: any
}

interface FormConfig {
  labelWidth?: string
  labelPosition?: 'left' | 'right' | 'top'
  inline?: boolean
  size?: 'large' | 'default' | 'small'
  showButtons?: boolean
  customButtons?: ButtonConfig[]
}
```

## 💡 常见问题

### Q: 如何实现字段间的联动？

A: 使用 `field-change` 事件监听字段变化：

```javascript
<SuperForm @field-change="handleFieldChange" />

const handleFieldChange = (field, value, formData) => {
  if (field === 'type') {
    // 根据 type 更新其他字段
  }
}
```

### Q: 如何实现复杂的验证逻辑？

A: 使用自定义验证器：

```javascript
{
  field: 'password',
  label: '密码',
  type: 'password',
  rules: [
    {
      validator: (rule, value, callback) => {
        // 自定义验证逻辑
        callback()
      },
      trigger: 'blur'
    }
  ]
}
```

### Q: 如何实现上传文件的回显？

A: 在 `fetch-api` 获取详情后，组件会自动回显数据。

## 📄 License

MIT

---

**作者**: SuperForm Team
**更新时间**: 2025-03-13

## 📚 完整示例代码

查看完整示例代码，请参考：
- **示例文件**: `src/views/everydayPage/SuperFormExample.vue`
- **包含内容**:
  - ✅ 基础表单示例（输入框、选择器、日期等）
  - ✅ 高级表单示例（多级表头、富文本、文件上传等）
  - ✅ 自定义样式示例
  - ✅ 动态表单示例
  - ✅ 自定义插槽示例
  - ✅ 组件集成示例（FinancialTable、FormDynamicTable、CommonSelector、AnnualPlan）
  - ✅ 性能优化示例（异步加载、分批渲染、虚拟滚动）

## 🔗 组件集成说明

### 1. FinancialTable 财务表格
```vue
<SuperForm
  :items="[
    {
      field: 'financialData',
      label: '财务数据',
      type: 'component',
      component: FinancialTable,
      componentProps: {
        isPreview: false
      }
    }
  ]"
/>
```

### 2. FormDynamicTable 动态表格
```vue
<SuperForm
  :items="[
    {
      field: 'dynamicData',
      label: '动态数据',
      type: 'component',
      component: FormDynamicTable,
      componentProps: {
        columns: dynamicTableColumns,
        showImportExport: true
      }
    }
  ]"
/>
```

### 3. CommonSelector 通用选择器
```vue
<SuperForm
  :items="[
    {
      field: 'matterType',
      label: '事项类型',
      type: 'component',
      component: CommonSelector,
      componentProps: {
        dictName: 'matterType',
        placeholder: '请选择事项类型'
      }
    }
  ]"
/>
```

### 4. AnnualPlan 年度计划
```vue
<SuperForm
  :items="[
    {
      field: 'annualPlan',
      label: '年度计划',
      type: 'component',
      component: AnnualPlan,
      componentProps: {
        dynamicTimeList: ['2025', '2026', '2027'],
        isPreview: false
      }
    }
  ]"
/>
```

## ⚡ 性能优化最佳实践

### 1. 使用异步组件按需加载
```javascript
import { defineAsyncComponent } from 'vue';

const FinancialTable = defineAsyncComponent(() =>
  import('@/components/FinancialTable.vue')
);
```

### 2. 使用 computed 缓存计算结果
```javascript
const formItems = computed(() => [
  { field: 'name', label: '名称', type: 'input' }
]);
```

### 3. 大数据分批加载
```javascript
const loadLargeForm = async () => {
  const batchSize = 100;
  for (let i = 0; i < batches; i++) {
    await nextTick();
    // 加载批次数据
  }
};
```

### 4. 使用 v-if 而非 v-show
```javascript
// 首次渲染时不加载
<AnnualPlan v-if="showAnnualPlan" />
```

## 🎯 管理端项目常见配置

### 1. 列表页表单配置
```javascript
const searchFormConfig = {
  labelWidth: '100px',
  inline: true,
  showButtons: false
};

const searchFormItems = [
  { field: 'keyword', label: '关键词', type: 'input', clearable: true },
  { field: 'status', label: '状态', type: 'select', clearable: true, options: [...] },
  { field: 'dateRange', label: '日期范围', type: 'daterange' }
];
```

### 2. 新增/编辑页表单配置
```javascript
const formConfig = {
  labelWidth: '120px',
  gutter: 20,
  showButtons: true,
  submitText: '保存',
  resetText: '取消'
};
```

### 3. 审批页表单配置
```javascript
const approvalFormConfig = {
  labelWidth: '140px',
  disabled: false, // 根据权限动态设置
  showButtons: true
};

const approvalFormItems = [
  { field: 'approvalStatus', label: '审批状态', type: 'radio', required: true },
  { field: 'approvalOpinion', label: '审批意见', type: 'textarea', rows: 4 }
];
```

## 📖 快速开始指南

### Step 1: 引入组件
```vue
<script setup>
import SuperForm from '@/components/common/SuperForm.vue';
import { ref } from 'vue';

const formData = ref({});
</script>
```

### Step 2: 配置表单项
```javascript
const formItems = [
  { field: 'username', label: '用户名', type: 'input', required: true }
];
```

### Step 3: 使用组件
```vue
<template>
  <SuperForm v-model="formData" :items="formItems" />
</template>
```

### Step 4: 处理提交
```vue
<script setup>
const handleSubmit = (data) => {
  console.log('提交数据:', data);
  // 调用接口保存
};
</script>

<template>
  <SuperForm v-model="formData" :items="formItems" @submit="handleSubmit" />
</template>
```

## 🎨 UI/UX 优化建议

1. **合理使用栅格布局** - 重要字段使用大跨度，次要字段使用小跨度
2. **分组展示** - 使用 divider 或 alert 组件对字段分组
3. **必填标识** - 重要字段使用 required 属性
4. **提示信息** - 使用 placeholder 提供输入提示
5. **字段联动** - 使用 field-change 事件实现字段联动
