Pre Merge pull request !787 from puhui999/master-fix1

pull/787/MERGE
puhui999 2025-07-06 08:45:27 +00:00 committed by Gitee
commit e55e2a011c
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
2 changed files with 767 additions and 0 deletions

View File

@ -0,0 +1,177 @@
# FormCreate 组件联动示例
这个示例页面演示了 FormCreate 中各种组件联动的使用方法,参考了官方文档 https://form-create.com/v3/guide/control
## 功能特性
### 1. 基础联动 (Basic Control)
- **显示/隐藏控制**:根据用户类型选择显示不同的表单字段
- **条件逻辑**:个人用户显示身份证号,企业用户显示公司信息
- **实时响应**:选择变化时立即更新表单结构
### 2. 禁用联动 (Disabled Control)
- **启用/禁用控制**:通过开关控制表单字段的可编辑状态
- **批量操作**:一次性控制多个字段的禁用状态
- **状态保持**:禁用时保持原有数据不变
### 3. 选项联动 (Options Control)
- **三级联动**:省市区三级下拉选择联动
- **动态选项**:根据上级选择动态更新下级选项
- **选项替换**:使用 `replaceOptions` 方法更新选项列表
### 4. 复杂联动 (Complex Control)
- **多条件控制**:根据订单类型显示不同的业务字段
- **业务场景**:模拟真实的订单管理场景
- **混合控制**:同时使用显示/隐藏和选项联动
### 5. 动态规则 (Dynamic Rules)
- **动态添加**:运行时动态添加表单字段
- **动态删除**:运行时删除已添加的字段
- **API操作**:使用 FormCreate API 进行动态操作
## 技术要点
### Control 配置结构
```typescript
control: [
{
value: '触发值', // 触发联动的值
condition: '==', // 条件操作符 (==, !=, >, <, >=, <=, in, !in)
method: '联动方法', // 联动方法 (hidden, display, disabled, enabled, replaceOptions)
rule: ['字段名'], // 受影响的字段
options: [] // 新选项 (仅 replaceOptions 方法使用)
}
]
```
### 联动方法说明
- **hidden**: 隐藏字段
- **display**: 显示字段
- **disabled**: 禁用字段
- **enabled**: 启用字段
- **replaceOptions**: 替换选项
### 条件操作符
- **==**: 等于
- **!=**: 不等于
- **>**: 大于
- **<**: 小于
- **>=**: 大于等于
- **<=**: 小于等于
- **in**: 包含在数组中
- **!in**: 不包含在数组中
## 使用示例
### 基础显示/隐藏联动
```typescript
{
type: 'radio',
field: 'userType',
control: [
{
value: 'personal',
condition: '==',
method: 'hidden',
rule: ['companyName', 'businessLicense']
}
]
}
```
### 选项联动
```typescript
{
type: 'select',
field: 'province',
control: [
{
value: 'guangdong',
condition: '==',
method: 'replaceOptions',
rule: ['city'],
options: [
{ label: '广州市', value: 'guangzhou' },
{ label: '深圳市', value: 'shenzhen' }
]
}
]
}
```
### 动态操作
```typescript
// 添加字段
formApi.append({
type: 'input',
field: 'newField',
title: '新字段'
})
// 删除字段
formApi.removeField('fieldName')
// 更新字段属性
formApi.updateRule('fieldName', {
props: { disabled: true }
})
```
## 最佳实践
1. **合理使用联动**:避免过于复杂的联动逻辑,保持用户体验流畅
2. **性能考虑**:大量字段联动时注意性能影响
3. **用户体验**:联动变化要有明确的视觉反馈
4. **数据一致性**:隐藏字段时考虑是否需要清空数据
5. **错误处理**:联动失败时要有合适的错误处理机制
## 扩展功能
- 支持自定义联动方法
- 支持异步联动(如远程获取选项)
- 支持复杂条件表达式
- 支持联动动画效果
- 支持联动历史记录
## 访问方式
### 开发环境访问
直接在浏览器中访问:
```url
http://localhost:3000/#/infra/demo/formCreateControl
```
### 路由配置
如果需要在菜单中显示,可以在后台管理系统中添加菜单项:
- **菜单名称**: FormCreate联动示例
- **路由地址**: `demo/formCreateControl`
- **组件路径**: `infra/demo/formCreateControl/index`
### 文件结构
```text
src/views/infra/demo/formCreateControl/
├── index.vue # 主页面组件
└── README.md # 说明文档
```
## 相关文档
- [FormCreate 官方文档](https://form-create.com/v3/)
- [Control 联动控制](https://form-create.com/v3/guide/control)
- [API 参考](https://form-create.com/v3/guide/api)

View File

@ -0,0 +1,590 @@
<template>
<ContentWrap>
<div class="mb-20px">
<el-alert
title="FormCreate 组件联动示例"
type="info"
description="演示 FormCreate 中各种组件联动的使用方法,包括显示/隐藏、禁用/启用、选项联动等"
show-icon
:closable="false"
/>
</div>
<el-tabs v-model="activeTab" type="border-card">
<!-- 基础联动示例 -->
<el-tab-pane label="基础联动" name="basic">
<div class="demo-container">
<h3>基础显示/隐藏联动</h3>
<p class="demo-desc">根据选择的值控制其他组件的显示和隐藏</p>
<form-create
v-model="basicForm.value"
v-model:api="basicForm.api"
:rule="basicForm.rule"
:option="basicForm.option"
/>
<div class="form-data">
<h4>表单数据</h4>
<pre>{{ JSON.stringify(basicForm.value, null, 2) }}</pre>
</div>
</div>
</el-tab-pane>
<!-- 禁用联动示例 -->
<el-tab-pane label="禁用联动" name="disabled">
<div class="demo-container">
<h3>组件禁用/启用联动</h3>
<p class="demo-desc">根据条件动态禁用或启用表单组件</p>
<form-create
v-model="disabledForm.value"
v-model:api="disabledForm.api"
:rule="disabledForm.rule"
:option="disabledForm.option"
/>
<div class="form-data">
<h4>表单数据</h4>
<pre>{{ JSON.stringify(disabledForm.value, null, 2) }}</pre>
</div>
</div>
</el-tab-pane>
<!-- 选项联动示例 -->
<el-tab-pane label="选项联动" name="options">
<div class="demo-container">
<h3>下拉选项联动</h3>
<p class="demo-desc">省市区三级联动根据上级选择动态更新下级选项</p>
<form-create
v-model="optionsForm.value"
v-model:api="optionsForm.api"
:rule="optionsForm.rule"
:option="optionsForm.option"
/>
<div class="form-data">
<h4>表单数据</h4>
<pre>{{ JSON.stringify(optionsForm.value, null, 2) }}</pre>
</div>
</div>
</el-tab-pane>
<!-- 复杂联动示例 -->
<el-tab-pane label="复杂联动" name="complex">
<div class="demo-container">
<h3>复杂业务联动</h3>
<p class="demo-desc">模拟真实业务场景的复杂联动逻辑</p>
<form-create
v-model="complexForm.value"
v-model:api="complexForm.api"
:rule="complexForm.rule"
:option="complexForm.option"
/>
<div class="form-data">
<h4>表单数据</h4>
<pre>{{ JSON.stringify(complexForm.value, null, 2) }}</pre>
</div>
</div>
</el-tab-pane>
<!-- 动态规则示例 -->
<el-tab-pane label="动态规则" name="dynamic">
<div class="demo-container">
<h3>动态添加/删除组件</h3>
<p class="demo-desc">根据用户操作动态添加或删除表单组件</p>
<form-create
v-model="dynamicForm.value"
v-model:api="dynamicForm.api"
:rule="dynamicForm.rule"
:option="dynamicForm.option"
/>
<div class="mt-20px">
<el-button type="primary" @click="addField"></el-button>
<el-button type="danger" @click="removeField"></el-button>
</div>
<div class="form-data">
<h4>表单数据</h4>
<pre>{{ JSON.stringify(dynamicForm.value, null, 2) }}</pre>
</div>
</div>
</el-tab-pane>
</el-tabs>
</ContentWrap>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
defineOptions({ name: 'FormCreateControlDemo' })
const activeTab = ref('basic')
//
const basicForm = reactive({
value: {},
api: null,
option: {
form: {
labelWidth: '120px'
}
},
rule: [
{
type: 'radio',
field: 'userType',
title: '用户类型',
value: 'personal',
options: [
{ label: '个人用户', value: 'personal' },
{ label: '企业用户', value: 'enterprise' }
],
control: [
{
value: 'personal',
condition: '==',
method: 'hidden',
rule: ['companyName', 'businessLicense']
},
{
value: 'enterprise',
condition: '==',
method: 'hidden',
rule: ['idCard']
}
]
},
{
type: 'input',
field: 'userName',
title: '用户名',
props: {
placeholder: '请输入用户名'
}
},
{
type: 'input',
field: 'idCard',
title: '身份证号',
props: {
placeholder: '请输入身份证号'
}
},
{
type: 'input',
field: 'companyName',
title: '公司名称',
props: {
placeholder: '请输入公司名称'
}
},
{
type: 'input',
field: 'businessLicense',
title: '营业执照号',
props: {
placeholder: '请输入营业执照号'
}
}
]
})
//
const disabledForm = reactive({
value: {},
api: null,
option: {
form: {
labelWidth: '120px'
}
},
rule: [
{
type: 'switch',
field: 'enableEdit',
title: '启用编辑',
value: false,
control: [
{
value: false,
condition: '==',
method: 'disabled',
rule: ['name', 'email', 'phone']
}
]
},
{
type: 'input',
field: 'name',
title: '姓名',
value: '张三',
props: {
placeholder: '请输入姓名'
}
},
{
type: 'input',
field: 'email',
title: '邮箱',
value: 'zhangsan@example.com',
props: {
placeholder: '请输入邮箱'
}
},
{
type: 'input',
field: 'phone',
title: '手机号',
value: '13800138000',
props: {
placeholder: '请输入手机号'
}
}
]
})
//
const optionsForm = reactive({
value: {},
api: null,
option: {
form: {
labelWidth: '120px'
}
},
rule: [
{
type: 'select',
field: 'province',
title: '省份',
options: [
{ label: '广东省', value: 'guangdong' },
{ label: '江苏省', value: 'jiangsu' },
{ label: '浙江省', value: 'zhejiang' }
],
control: [
{
value: 'guangdong',
condition: '==',
method: 'replaceOptions',
rule: ['city'],
options: [
{ label: '广州市', value: 'guangzhou' },
{ label: '深圳市', value: 'shenzhen' },
{ label: '珠海市', value: 'zhuhai' }
]
},
{
value: 'jiangsu',
condition: '==',
method: 'replaceOptions',
rule: ['city'],
options: [
{ label: '南京市', value: 'nanjing' },
{ label: '苏州市', value: 'suzhou' },
{ label: '无锡市', value: 'wuxi' }
]
},
{
value: 'zhejiang',
condition: '==',
method: 'replaceOptions',
rule: ['city'],
options: [
{ label: '杭州市', value: 'hangzhou' },
{ label: '宁波市', value: 'ningbo' },
{ label: '温州市', value: 'wenzhou' }
]
}
]
},
{
type: 'select',
field: 'city',
title: '城市',
options: [],
control: [
{
value: 'guangzhou',
condition: '==',
method: 'replaceOptions',
rule: ['district'],
options: [
{ label: '天河区', value: 'tianhe' },
{ label: '越秀区', value: 'yuexiu' },
{ label: '海珠区', value: 'haizhu' }
]
},
{
value: 'shenzhen',
condition: '==',
method: 'replaceOptions',
rule: ['district'],
options: [
{ label: '南山区', value: 'nanshan' },
{ label: '福田区', value: 'futian' },
{ label: '罗湖区', value: 'luohu' }
]
},
{
value: 'hangzhou',
condition: '==',
method: 'replaceOptions',
rule: ['district'],
options: [
{ label: '西湖区', value: 'xihu' },
{ label: '拱墅区', value: 'gongshu' },
{ label: '江干区', value: 'jianggan' }
]
}
]
},
{
type: 'select',
field: 'district',
title: '区县',
options: []
}
]
})
//
const complexForm = reactive({
value: {},
api: null,
option: {
form: {
labelWidth: '120px'
}
},
rule: [
{
type: 'select',
field: 'orderType',
title: '订单类型',
options: [
{ label: '普通订单', value: 'normal' },
{ label: '预售订单', value: 'presale' },
{ label: '定制订单', value: 'custom' }
],
control: [
{
value: 'presale',
condition: '==',
method: 'display',
rule: ['presaleDate', 'deposit']
},
{
value: 'custom',
condition: '==',
method: 'display',
rule: ['customRequirement', 'designFee']
},
{
value: 'normal',
condition: '==',
method: 'hidden',
rule: ['presaleDate', 'deposit', 'customRequirement', 'designFee']
}
]
},
{
type: 'input',
field: 'productName',
title: '产品名称',
props: {
placeholder: '请输入产品名称'
}
},
{
type: 'inputNumber',
field: 'quantity',
title: '数量',
value: 1,
props: {
min: 1,
max: 999
}
},
{
type: 'inputNumber',
field: 'price',
title: '单价',
props: {
min: 0,
precision: 2
}
},
{
type: 'datePicker',
field: 'presaleDate',
title: '预售日期',
props: {
type: 'date',
placeholder: '请选择预售日期'
}
},
{
type: 'inputNumber',
field: 'deposit',
title: '定金',
props: {
min: 0,
precision: 2
}
},
{
type: 'input',
field: 'customRequirement',
title: '定制需求',
props: {
type: 'textarea',
rows: 3,
placeholder: '请描述定制需求'
}
},
{
type: 'inputNumber',
field: 'designFee',
title: '设计费',
props: {
min: 0,
precision: 2
}
}
]
})
//
const dynamicForm = reactive({
value: {},
api: null,
option: {
form: {
labelWidth: '120px'
}
},
rule: [
{
type: 'input',
field: 'name',
title: '姓名',
props: {
placeholder: '请输入姓名'
}
},
{
type: 'input',
field: 'email',
title: '邮箱',
props: {
placeholder: '请输入邮箱'
}
}
]
})
//
let fieldCounter = 0
//
const addField = () => {
fieldCounter++
const newField = {
type: 'input',
field: `dynamicField${fieldCounter}`,
title: `动态字段${fieldCounter}`,
props: {
placeholder: `请输入动态字段${fieldCounter}`
}
}
if (dynamicForm.api) {
dynamicForm.api.append(newField)
} else {
dynamicForm.rule.push(newField)
}
}
//
const removeField = () => {
if (fieldCounter > 0) {
const fieldToRemove = `dynamicField${fieldCounter}`
if (dynamicForm.api) {
dynamicForm.api.removeField(fieldToRemove)
} else {
const index = dynamicForm.rule.findIndex((item) => item.field === fieldToRemove)
if (index > -1) {
dynamicForm.rule.splice(index, 1)
}
}
fieldCounter--
}
}
</script>
<style scoped>
.demo-container {
padding: 20px;
background: #f8f9fa;
border-radius: 8px;
margin-bottom: 20px;
}
.demo-container h3 {
margin: 0 0 10px 0;
color: #303133;
font-size: 18px;
font-weight: 600;
}
.demo-desc {
margin: 0 0 20px 0;
color: #606266;
font-size: 14px;
line-height: 1.5;
}
.form-data {
margin-top: 30px;
padding: 15px;
background: #fff;
border: 1px solid #e4e7ed;
border-radius: 4px;
}
.form-data h4 {
margin: 0 0 10px 0;
color: #303133;
font-size: 14px;
font-weight: 600;
}
.form-data pre {
margin: 0;
padding: 10px;
background: #f5f7fa;
border-radius: 4px;
font-size: 12px;
color: #606266;
overflow-x: auto;
white-space: pre-wrap;
word-wrap: break-word;
}
:deep(.el-tabs__content) {
padding: 0;
}
:deep(.el-tab-pane) {
padding: 20px;
}
</style>