明树Git Lab
Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
J
jt_front
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
jt_front
Commits
add12e41
Commit
add12e41
authored
Mar 16, 2026
by
zhanghan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
bug处理
parent
327efb71
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
4306 additions
and
60 deletions
+4306
-60
SuperForm.README.md
src/components/common/SuperForm.README.md
+913
-0
SuperForm.types.ts
src/components/common/SuperForm.types.ts
+955
-0
SuperForm.vue
src/components/common/SuperForm.vue
+1128
-0
routes.js
src/router/routes.js
+7
-0
SuperFormExample.vue
src/views/everydayPage/SuperFormExample.vue
+1260
-0
annualAdd.vue
src/views/everydayPage/annualAdd.vue
+0
-7
everydayAdd.vue
src/views/everydayPage/everydayAdd.vue
+1
-7
informationConstructionAdd.vue
src/views/everydayPage/informationConstructionAdd.vue
+12
-0
recordAdd.vue
src/views/everydayPage/recordAdd.vue
+0
-10
shareAdd.vue
src/views/everydayPage/shareAdd.vue
+30
-32
systemAdd.vue
src/views/everydayPage/systemAdd.vue
+0
-4
No files found.
src/components/common/SuperForm.README.md
0 → 100644
View file @
add12e41
# 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 事件实现字段联动
src/components/common/SuperForm.types.ts
0 → 100644
View file @
add12e41
/**
* SuperForm 超级表单组件类型定义
*/
import
type
{
FormInstance
,
FormItemRule
,
FormRules
}
from
'element-plus'
/**
* 表单项基础类型
*/
export
interface
BaseFormItem
{
/** 字段名 */
field
:
string
/** 标签文本 */
label
:
string
/** 组件类型 */
type
:
FormItemType
/** 是否必填 */
required
?:
boolean
/** 验证规则 */
rules
?:
FormItemRule
[]
/** 栅格占位格数 */
span
?:
number
/** 栅格偏移 */
offset
?:
number
/** 响应式栅格 */
xs
?:
number
|
string
sm
?:
number
|
string
md
?:
number
|
string
lg
?:
number
|
string
xl
?:
number
|
string
/** 标签元素 */
tag
?:
string
/** 栅格向左移动 */
pull
?:
number
|
string
/** 栅格向右移动 */
push
?:
number
|
string
/** 默认值 */
defaultValue
?:
any
/** 字段 CSS 类名 */
itemClass
?:
string
/** 是否显示错误信息 */
showMessage
?:
boolean
/** 是否行内错误信息 */
inlineMessage
?:
boolean
/** 错误信息 */
error
?:
string
}
/**
* 输入框类型
*/
export
interface
InputFormItem
extends
BaseFormItem
{
type
:
'input'
|
'textarea'
|
'password'
/** 是否可清空 */
clearable
?:
boolean
/** 是否禁用 */
disabled
?:
boolean
/** 是否只读 */
readonly
?:
boolean
/** 最大输入长度 */
maxlength
?:
number
/** 是否显示字数统计 */
showWordLimit
?:
boolean
/** 占位文本 */
placeholder
?:
string
/** 输入框前缀 */
prefix
?:
string
/** 输入框后缀 */
suffix
?:
string
/** 前置内容 */
prepend
?:
string
/** 后置内容 */
append
?:
string
/** 前置图标 */
prefixIcon
?:
string
/** 后置图标 */
suffixIcon
?:
string
/** 前置插槽名称 */
prependSlot
?:
string
/** 后置插槽名称 */
appendSlot
?:
string
/** 文本域行数 */
rows
?:
number
/** 自适应内容高度 */
autosize
?:
boolean
|
{
minRows
?:
number
;
maxRows
?:
number
}
}
/**
* 数字输入框类型
*/
export
interface
InputNumberFormItem
extends
BaseFormItem
{
type
:
'inputNumber'
|
'number'
/** 最小值 */
min
?:
number
/** 最大值 */
max
?:
number
/** 步长 */
step
?:
number
/** 精度 */
precision
?:
number
/** 是否显示控制按钮 */
controls
?:
boolean
/** 按钮位置 */
controlsPosition
?:
'right'
|
''
/** 尺寸 */
size
?:
'large'
|
'default'
|
'small'
}
/**
* 选择器类型
*/
export
interface
SelectFormItem
extends
BaseFormItem
{
type
:
'select'
/** 是否可清空 */
clearable
?:
boolean
/** 是否可搜索 */
filterable
?:
boolean
/** 是否多选 */
multiple
?:
boolean
/** 是否远程搜索 */
remote
?:
boolean
/** 远程搜索方法 */
remoteMethod
?:
(
query
:
string
)
=>
void
/** 是否加载中 */
loading
?:
boolean
/** 无匹配文本 */
noMatchText
?:
string
/** 无数据文本 */
noDataText
?:
string
/** 占位文本 */
placeholder
?:
string
/** 选项数据源 */
options
?:
SelectOption
[]
|
((
item
:
SelectFormItem
,
formData
:
Record
<
string
,
any
>
)
=>
SelectOption
[])
/** 选项值的字段名 */
valueKey
?:
string
/** 选项标签的字段名 */
labelKey
?:
string
/** 选项插槽名称 */
optionSlot
?:
string
/** 空数据插槽名称 */
emptySlot
?:
string
/** 前缀插槽名称 */
prefix
?:
string
}
/**
* 单选框组类型
*/
export
interface
RadioFormItem
extends
BaseFormItem
{
type
:
'radio'
|
'radioGroup'
/** 是否禁用 */
disabled
?:
boolean
/** 是否显示边框 */
border
?:
boolean
/** 尺寸 */
size
?:
'large'
|
'default'
|
'small'
/** 选项数据源 */
options
?:
SelectOption
[]
}
/**
* 多选框组类型
*/
export
interface
CheckboxFormItem
extends
BaseFormItem
{
type
:
'checkbox'
|
'checkboxGroup'
/** 是否禁用 */
disabled
?:
boolean
/** 是否显示边框 */
border
?:
boolean
/** 最小选中数 */
min
?:
number
/** 最大选中数 */
max
?:
number
/** 尺寸 */
size
?:
'large'
|
'default'
|
'small'
/** 选项数据源 */
options
?:
SelectOption
[]
}
/**
* 日期选择器类型
*/
export
interface
DatePickerFormItem
extends
BaseFormItem
{
type
:
'date'
|
'datetime'
|
'dates'
|
'week'
|
'month'
|
'year'
|
'daterange'
|
'datetimerange'
|
'monthrange'
/** 占位文本 */
placeholder
?:
string
/** 格式化显示值 */
format
?:
string
/** 绑定值格式 */
valueFormat
?:
string
/** 是否可清空 */
clearable
?:
boolean
/** 是否禁用 */
disabled
?:
boolean
/** 是否只读 */
readonly
?:
boolean
/** 是否可编辑 */
editable
?:
boolean
/** 范围选择器开始日期占位符 */
startPlaceholder
?:
string
/** 范围选择器结束日期占位符 */
endPlaceholder
?:
string
/** 范围分隔符 */
rangeSeparator
?:
string
/** 禁用日期函数 */
disabledDate
?:
(
date
:
Date
)
=>
boolean
}
/**
* 时间选择器类型
*/
export
interface
TimePickerFormItem
extends
BaseFormItem
{
type
:
'timePicker'
|
'time'
|
'timeSelect'
/** 占位文本 */
placeholder
?:
string
/** 格式化显示值 */
format
?:
string
/** 绑定值格式 */
valueFormat
?:
string
/** 是否可清空 */
clearable
?:
boolean
/** 是否禁用 */
disabled
?:
boolean
/** 是否只读 */
readonly
?:
boolean
}
/**
* 开关类型
*/
export
interface
SwitchFormItem
extends
BaseFormItem
{
type
:
'switch'
/** 是否禁用 */
disabled
?:
boolean
/** 是否加载中 */
loading
?:
boolean
/** 尺寸 */
size
?:
'large'
|
'default'
|
'small'
/** 打开时的文字 */
activeText
?:
string
|
number
/** 关闭时的文字 */
inactiveText
?:
string
|
number
/** 打开时的值 */
activeValue
?:
boolean
|
string
|
number
/** 关闭时的值 */
inactiveValue
?:
boolean
|
string
|
number
}
/**
* 滑块类型
*/
export
interface
SliderFormItem
extends
BaseFormItem
{
type
:
'slider'
/** 最小值 */
min
?:
number
/** 最大值 */
max
?:
number
/** 步长 */
step
?:
number
/** 是否显示间断点 */
showStops
?:
boolean
/** 是否显示提示框 */
showTooltip
?:
boolean
/** 是否禁用 */
disabled
?:
boolean
/** 是否双滑块模式 */
range
?:
boolean
/** 是否垂直模式 */
vertical
?:
boolean
/** 高度,垂直模式下生效 */
height
?:
string
/** 标记 */
marks
?:
Record
<
number
,
string
>
}
/**
* 评分类型
*/
export
interface
RateFormItem
extends
BaseFormItem
{
type
:
'rate'
/** 最大分值 */
max
?:
number
/** 是否禁用 */
disabled
?:
boolean
/** 是否允许半选 */
allowHalf
?:
boolean
/** 低分和中等分数的界限值 */
lowThreshold
?:
number
/** 高分和中等分数的界限值 */
highThreshold
?:
number
/** 颜色数组 */
colors
?:
string
|
string
[]
/** 未选中颜色 */
voidColor
?:
string
/** 禁用时的未选中颜色 */
disabledVoidColor
?:
string
/** 图标数组 */
iconClasses
?:
string
|
string
[]
/** 未选中图标类名 */
voidIconClass
?:
string
/** 禁用时的未选中图标类名 */
disabledVoidIconClass
?:
string
/** 是否显示辅助文字 */
showText
?:
boolean
/** 是否显示当前分数 */
showScore
?:
boolean
/** 辅助文字数组 */
texts
?:
string
[]
}
/**
* 颜色选择器类型
*/
export
interface
ColorPickerFormItem
extends
BaseFormItem
{
type
:
'colorPicker'
/** 是否禁用 */
disabled
?:
boolean
/** 尺寸 */
size
?:
'large'
|
'default'
|
'small'
/** 是否支持透明度 */
showAlpha
?:
boolean
/** 颜色格式 */
colorFormat
?:
'hex'
|
'rgb'
|
'hsl'
|
'hsv'
/** 预定义颜色 */
predefine
?:
string
[]
}
/**
* 级联选择器类型
*/
export
interface
CascaderFormItem
extends
BaseFormItem
{
type
:
'cascader'
/** 是否可清空 */
clearable
?:
boolean
/** 是否可搜索 */
filterable
?:
boolean
/** 是否禁用 */
disabled
?:
boolean
/** 占位文本 */
placeholder
?:
string
/** 选项数据源 */
options
?:
CascaderOption
[]
/** 配置选项 */
props
?:
CascaderProps
/** 是否多选 */
props
?:
{
expandTrigger
?:
'click'
|
'hover'
multiple
?:
boolean
checkStrictly
?:
boolean
emitPath
?:
boolean
lazy
?:
boolean
lazyLoad
?:
(
node
:
any
,
resolve
:
(
data
:
any
[])
=>
void
)
=>
void
value
?:
string
label
?:
string
children
?:
string
leaf
?:
string
[
key
:
string
]:
any
}
}
/**
* 树形选择器类型
*/
export
interface
TreeSelectFormItem
extends
BaseFormItem
{
type
:
'treeSelect'
/** 是否可清空 */
clearable
?:
boolean
/** 是否可搜索 */
filterable
?:
boolean
/** 是否禁用 */
disabled
?:
boolean
/** 占位文本 */
placeholder
?:
string
/** 树形数据 */
data
?:
TreeNode
[]
/** 配置选项 */
props
?:
{
label
?:
string
value
?:
string
children
?:
string
disabled
?:
string
isLeaf
?:
string
[
key
:
string
]:
any
}
/** 是否多选 */
multiple
?:
boolean
/** 是否展示复选框 */
showCheckbox
?:
boolean
/** 是否严格的遵循父子不互相关联的做法 */
checkStrictly
?:
boolean
/** 是否展开子节点 */
defaultExpandAll
?:
boolean
/** 默认展开的节点的 key 的数组 */
defaultExpandedKeys
?:
any
[]
/** 节点唯一标识 */
nodeKey
?:
string
}
/**
* 穿梭框类型
*/
export
interface
TransferFormItem
extends
BaseFormItem
{
type
:
'transfer'
/** 数据源 */
data
?:
TransferDataItem
[]
/** 是否禁用 */
disabled
?:
boolean
/** 是否可过滤 */
filterable
?:
boolean
/** 过滤占位符 */
filterPlaceholder
?:
string
/** 是否可筛选 */
filterMethod
?:
(
query
:
string
,
item
:
TransferDataItem
)
=>
boolean
/** 每次列表渲染的数量 */
targetOrder
?:
'original'
|
'push'
|
'unshift'
/** 标题 */
titles
?:
string
[]
/** 按钮文案 */
buttonTexts
?:
string
[]
/** 列表底部文案 */
renderContent
?:
(
h
:
any
,
option
:
TransferDataItem
)
=>
any
}
/**
* 上传类型
*/
export
interface
UploadFormItem
extends
BaseFormItem
{
type
:
'upload'
/** 上传地址 */
action
?:
string
/** 请求头 */
headers
?:
Record
<
string
,
string
>
/** 请求方法 */
method
?:
'post'
|
'put'
|
'patch'
/** 是否支持多选 */
multiple
?:
boolean
/** 上传时附带的额外参数 */
data
?:
Record
<
string
,
any
>
/** 上传的文件字段名 */
name
?:
string
/** 是否携带 cookie */
withCredentials
?:
boolean
/** 是否显示文件列表 */
showFileList
?:
boolean
/** 是否拖拽上传 */
drag
?:
boolean
/** 接受上传的文件类型 */
accept
?:
string
/** 是否自动上传 */
autoUpload
?:
boolean
/** 上传数量限制 */
limit
?:
number
/** 按钮文本 */
buttonText
?:
string
/** 按钮类型 */
buttonType
?:
string
/** 按钮图标 */
uploadIcon
?:
string
/** 提示文本 */
tip
?:
string
/** 触发插槽名称 */
triggerSlot
?:
string
/** 提示插槽名称 */
tipSlot
?:
string
/** 上传成功回调 */
onSuccess
?:
(
response
:
any
,
file
:
any
,
fileList
:
any
[],
formData
:
Record
<
string
,
any
>
)
=>
void
/** 上传失败回调 */
onError
?:
(
error
:
any
,
file
:
any
,
fileList
:
any
[])
=>
void
/** 上传进度回调 */
onProgress
?:
(
event
:
any
,
file
:
any
,
fileList
:
any
[])
=>
void
/** 文件状态改变回调 */
onChange
?:
(
file
:
any
,
fileList
:
any
[],
formData
:
Record
<
string
,
any
>
)
=>
void
/** 移除文件回调 */
onRemove
?:
(
file
:
any
,
fileList
:
any
[],
formData
:
Record
<
string
,
any
>
)
=>
void
/** 点击文件列表回调 */
onPreview
?:
(
file
:
any
)
=>
void
/** 上传前回调 */
beforeUpload
?:
(
file
:
any
,
formData
:
Record
<
string
,
any
>
)
=>
boolean
|
Promise
<
any
>
/** 删除前回调 */
beforeRemove
?:
(
file
:
any
,
fileList
:
any
[],
formData
:
Record
<
string
,
any
>
)
=>
boolean
|
Promise
<
any
>
/** 覆盖默认上传行为 */
httpRequest
?:
(
options
:
any
,
formData
:
Record
<
string
,
any
>
)
=>
Promise
<
any
>
}
/**
* 自动完成类型
*/
export
interface
AutocompleteFormItem
extends
BaseFormItem
{
type
:
'autocomplete'
/** 是否禁用 */
disabled
?:
boolean
/** 占位文本 */
placeholder
?:
string
/** 是否可清空 */
clearable
?:
boolean
/** 输入建议数据源 */
fetchSuggestions
?:
(
queryString
:
string
,
callback
:
(
data
:
any
[])
=>
void
)
=>
void
/** 输入建议数组的对象别名 */
valueKey
?:
string
/** 输入建议数组的标签别名 */
labelKey
?:
string
/** 前置内容 */
prepend
?:
string
/** 后置内容 */
append
?:
string
/** 前缀 */
prefix
?:
string
/** 后缀 */
suffix
?:
string
}
/**
* 文本展示类型
*/
export
interface
TextFormItem
extends
BaseFormItem
{
type
:
'text'
|
'display'
/** 自定义格式化函数 */
formatter
?:
(
value
:
any
,
item
:
TextFormItem
,
formData
:
Record
<
string
,
any
>
)
=>
string
/** 文本属性 */
textAttrs
?:
Record
<
string
,
any
>
}
/**
* HTML 内容类型
*/
export
interface
HtmlFormItem
extends
BaseFormItem
{
type
:
'html'
/** HTML 内容 */
htmlContent
?:
string
/** HTML 容器属性 */
htmlAttrs
?:
Record
<
string
,
any
>
}
/**
* 分割线类型
*/
export
interface
DividerFormItem
extends
BaseFormItem
{
type
:
'divider'
/** 分割线文字 */
dividerText
?:
string
/** 分割线属性 */
dividerAttrs
?:
{
direction
?:
'horizontal'
|
'vertical'
borderStyle
?:
'solid'
|
'dashed'
|
'dotted'
|
'double'
contentPosition
?:
'left'
|
'center'
|
'right'
[
key
:
string
]:
any
}
}
/**
* 警告类型
*/
export
interface
AlertFormItem
extends
BaseFormItem
{
type
:
'alert'
/** 警告类型 */
alertType
?:
'success'
|
'warning'
|
'info'
|
'error'
/** 警告属性 */
alertAttrs
?:
{
title
?:
string
description
?:
string
type
?:
'success'
|
'warning'
|
'info'
|
'error'
closable
?:
boolean
closeText
?:
string
center
?:
boolean
showIcon
?:
boolean
effect
?:
'light'
|
'dark'
[
key
:
string
]:
any
}
/** 标题插槽名称 */
alertTitleSlot
?:
string
}
/**
* 自定义插槽类型
*/
export
interface
SlotFormItem
extends
BaseFormItem
{
type
:
'slot'
|
'custom'
/** 插槽名称 */
slotName
?:
string
}
/**
* 自定义组件类型
*/
export
interface
ComponentFormItem
extends
BaseFormItem
{
type
:
'component'
/** 组件 */
component
:
any
/** 组件属性 */
componentProps
?:
Record
<
string
,
any
>
}
/**
* 表单项类型联合
*/
export
type
FormItem
=
|
InputFormItem
|
InputNumberFormItem
|
SelectFormItem
|
RadioFormItem
|
CheckboxFormItem
|
DatePickerFormItem
|
TimePickerFormItem
|
SwitchFormItem
|
SliderFormItem
|
RateFormItem
|
ColorPickerFormItem
|
CascaderFormItem
|
TreeSelectFormItem
|
TransferFormItem
|
UploadFormItem
|
AutocompleteFormItem
|
TextFormItem
|
HtmlFormItem
|
DividerFormItem
|
AlertFormItem
|
SlotFormItem
|
ComponentFormItem
/**
* 表单项组件类型
*/
export
type
FormItemType
=
|
'input'
|
'textarea'
|
'password'
|
'inputNumber'
|
'number'
|
'select'
|
'radio'
|
'radioGroup'
|
'checkbox'
|
'checkboxGroup'
|
'date'
|
'datetime'
|
'dates'
|
'week'
|
'month'
|
'year'
|
'daterange'
|
'datetimerange'
|
'monthrange'
|
'timePicker'
|
'time'
|
'timeSelect'
|
'switch'
|
'slider'
|
'rate'
|
'colorPicker'
|
'cascader'
|
'treeSelect'
|
'transfer'
|
'upload'
|
'autocomplete'
|
'text'
|
'display'
|
'html'
|
'divider'
|
'alert'
|
'slot'
|
'custom'
|
'component'
/**
* 选择器选项
*/
export
interface
SelectOption
{
label
:
string
value
:
any
disabled
?:
boolean
icon
?:
string
[
key
:
string
]:
any
}
/**
* 级联选择器选项
*/
export
interface
CascaderOption
{
label
:
string
value
:
any
children
?:
CascaderOption
[]
disabled
?:
boolean
[
key
:
string
]:
any
}
/**
* 树节点
*/
export
interface
TreeNode
{
id
:
string
|
number
label
:
string
children
?:
TreeNode
[]
disabled
?:
boolean
isLeaf
?:
boolean
[
key
:
string
]:
any
}
/**
* 穿梭框数据项
*/
export
interface
TransferDataItem
{
key
:
string
|
number
label
:
string
disabled
?:
boolean
[
key
:
string
]:
any
}
/**
* 自定义按钮配置
*/
export
interface
ButtonConfig
{
/** 按钮唯一标识 */
key
:
string
/** 按钮文本 */
text
:
string
/** 按钮类型 */
type
?:
'primary'
|
'success'
|
'warning'
|
'danger'
|
'info'
|
'text'
|
'default'
/** 按钮尺寸 */
size
?:
'large'
|
'default'
|
'small'
/** 是否加载中 */
loading
?:
boolean
/** 按钮图标 */
icon
?:
string
/** 是否朴素按钮 */
plain
?:
boolean
/** 是否圆角 */
round
?:
boolean
/** 是否圆形 */
circle
?:
boolean
/** 是否禁用 */
disabled
?:
boolean
/** 点击事件处理函数 */
handler
:
()
=>
void
|
Promise
<
void
>
}
/**
* 表单配置
*/
export
interface
FormConfig
{
/** 表单标签宽度 */
labelWidth
?:
string
/** 表单标签位置 */
labelPosition
?:
'left'
|
'right'
|
'top'
/** 是否行内表单 */
inline
?:
boolean
/** 是否禁用 */
disabled
?:
boolean
/** 表单尺寸 */
size
?:
'large'
|
'default'
|
'small'
/** 栅格间隔 */
gutter
?:
number
/** 是否显示操作按钮 */
showButtons
?:
boolean
/** 是否显示提交按钮 */
showSubmit
?:
boolean
/** 是否显示重置按钮 */
showReset
?:
boolean
/** 提交按钮文本 */
submitText
?:
string
/** 重置按钮文本 */
resetText
?:
string
/** 提交按钮类型 */
submitType
?:
'primary'
|
'success'
|
'warning'
|
'danger'
|
'info'
|
'default'
/** 提交按钮图标 */
submitIcon
?:
string
/** 重置按钮图标 */
resetIcon
?:
string
/** 按钮容器占位 */
buttonSpan
?:
number
/** 按钮容器偏移 */
buttonOffset
?:
number
/** 按钮响应式配置 */
buttonXs
?:
number
|
string
buttonSm
?:
number
|
string
buttonMd
?:
number
|
string
buttonLg
?:
number
|
string
buttonXl
?:
number
|
string
/** 按钮容器 CSS 类名 */
buttonClass
?:
string
/** 按钮尺寸 */
buttonSize
?:
'large'
|
'default'
|
'small'
/** 自定义按钮配置 */
customButtons
?:
ButtonConfig
[]
/** 是否显示验证图标 */
statusIcon
?:
boolean
/** 是否以行内形式展示验证信息 */
inlineMessage
?:
boolean
/** 是否在 rules 属性改变后立即触发一次验证 */
validateOnRuleChange
?:
boolean
/** 是否隐藏必填星号 */
hideRequiredAsterisk
?:
boolean
/** 是否显示验证信息 */
showMessage
?:
boolean
/** 标签后缀 */
labelSuffix
?:
string
/** 是否在验证失败时滚动到第一个错误表单 */
scrollToError
?:
boolean
/** 滚动行为配置 */
scrollIntoViewOptions
?:
boolean
|
ScrollIntoViewOptions
/** 提交按钮是否禁用 */
submitDisabled
?:
boolean
/** 重置按钮是否禁用 */
resetDisabled
?:
boolean
/** 提交按钮是否朴素 */
submitPlain
?:
boolean
/** 重置按钮是否朴素 */
resetPlain
?:
boolean
/** 提交按钮是否圆角 */
submitRound
?:
boolean
/** 重置按钮是否圆角 */
resetRound
?:
boolean
/** 提交按钮是否圆形 */
submitCircle
?:
boolean
/** 重置按钮是否圆形 */
resetCircle
?:
boolean
[
key
:
string
]:
any
}
/**
* SuperForm 组件 Props
*/
export
interface
SuperFormProps
{
/** 表单配置 */
config
:
FormConfig
/** 表单项配置 */
items
:
FormItem
[]
/** 表单数据 */
modelValue
:
Record
<
string
,
any
>
/** 表单验证规则 */
rules
?:
FormRules
/** 保存接口配置 */
saveApi
?:
string
|
((
isEdit
:
boolean
)
=>
string
)
|
null
/** 更新接口配置 */
updateApi
?:
string
|
((
isEdit
:
boolean
)
=>
string
)
|
null
/** 获取详情接口配置 */
fetchApi
?:
string
|
((
id
:
any
)
=>
string
)
|
null
/** 主键字段名 */
idKey
?:
string
/** 是否在提交前验证 */
validateBeforeSubmit
?:
boolean
/** 提交前的钩子 */
beforeSubmit
?:
(
data
:
Record
<
string
,
any
>
,
isEdit
:
boolean
)
=>
boolean
|
Promise
<
boolean
>
|
null
/** 提交后的钩子 */
afterSubmit
?:
(
response
:
any
,
data
:
Record
<
string
,
any
>
,
isEdit
:
boolean
)
=>
void
|
null
/** 自定义提交方法 */
customSubmit
?:
(
data
:
Record
<
string
,
any
>
,
options
:
{
isEdit
:
boolean
})
=>
void
|
null
/** 数据转换函数 */
dataTransform
?:
(
data
:
Record
<
string
,
any
>
)
=>
Record
<
string
,
any
>
|
null
/** 响应数据转换函数 */
responseTransform
?:
(
data
:
any
)
=>
Record
<
string
,
any
>
|
null
}
/**
* SuperForm 组件实例方法
*/
export
interface
SuperFormInstance
{
/** 表单引用 */
formRef
:
FormInstance
/** 表单响应式数据 */
formData
:
Record
<
string
,
any
>
/** 是否编辑模式 */
isEdit
:
boolean
/** 验证表单 */
validate
:
()
=>
Promise
<
boolean
>
/** 验证指定字段 */
validateField
:
(
prop
:
string
)
=>
Promise
<
boolean
>
/** 清除验证 */
clearValidate
:
(
fields
?:
string
|
string
[])
=>
void
/** 重置表单 */
resetFields
:
()
=>
void
/** 设置表单数据 */
setFormData
:
(
data
:
Record
<
string
,
any
>
)
=>
void
/** 获取表单数据 */
getFormData
:
()
=>
Record
<
string
,
any
>
/** 设置字段值 */
setFieldValue
:
(
field
:
string
,
value
:
any
)
=>
void
/** 获取字段值 */
getFieldValue
:
(
field
:
string
)
=>
any
/** 获取详情数据 */
fetchDetail
:
(
id
:
any
)
=>
Promise
<
void
>
/** 提交表单 */
handleSubmit
:
()
=>
Promise
<
void
>
}
/**
* SuperForm 组件事件
*/
export
interface
SuperFormEmits
{
/** 更新表单数据 */
(
event
:
'update:modelValue'
,
value
:
Record
<
string
,
any
>
):
void
/** 表单提交 */
(
event
:
'submit'
,
data
:
Record
<
string
,
any
>
,
isEdit
:
boolean
):
void
/** 提交成功 */
(
event
:
'success'
,
response
:
any
,
data
:
Record
<
string
,
any
>
,
isEdit
:
boolean
):
void
/** 提交失败 */
(
event
:
'error'
,
error
:
any
,
data
:
Record
<
string
,
any
>
,
isEdit
:
boolean
):
void
/** 表单数据变化 */
(
event
:
'change'
,
data
:
Record
<
string
,
any
>
):
void
/** 验证触发 */
(
event
:
'validate'
,
prop
:
string
|
undefined
,
isValid
:
boolean
,
message
:
string
):
void
/** 表单重置 */
(
event
:
'reset'
):
void
/** 字段值变化 */
(
event
:
'field-change'
,
field
:
string
,
value
:
any
,
formData
:
Record
<
string
,
any
>
):
void
/** 字段失焦 */
(
event
:
'field-blur'
,
field
:
string
,
event
:
Event
):
void
/** 字段聚焦 */
(
event
:
'field-focus'
,
field
:
string
,
event
:
Event
):
void
/** 字段可见性变化 */
(
event
:
'field-visible-change'
,
field
:
string
,
visible
:
boolean
):
void
/** 字段清除 */
(
event
:
'field-clear'
,
field
:
string
):
void
/** 字段移除标签 */
(
event
:
'field-remove-tag'
,
field
:
string
,
tag
:
any
):
void
/** 颜色激活变化 */
(
event
:
'color-active-change'
,
field
:
string
,
color
:
string
):
void
/** 级联展开变化 */
(
event
:
'cascader-expand-change'
,
field
:
string
,
value
:
any
):
void
/** 树节点点击 */
(
event
:
'tree-node-click'
,
field
:
string
,
data
:
any
):
void
/** 树选中变化 */
(
event
:
'tree-check'
,
field
:
string
,
data
:
any
):
void
/** 树节点展开 */
(
event
:
'tree-node-expand'
,
field
:
string
,
data
:
any
):
void
/** 树节点折叠 */
(
event
:
'tree-node-collapse'
,
field
:
string
,
data
:
any
):
void
/** 穿梭框左侧选中变化 */
(
event
:
'transfer-left-check-change'
,
field
:
string
,
value
:
any
):
void
/** 穿梭框右侧选中变化 */
(
event
:
'transfer-right-check-change'
,
field
:
string
,
value
:
any
):
void
/** 自动完成选择 */
(
event
:
'autocomplete-select'
,
field
:
string
,
value
:
any
):
void
/** 上传成功 */
(
event
:
'upload-success'
,
field
:
string
,
response
:
any
,
file
:
any
,
fileList
:
any
[]):
void
/** 上传失败 */
(
event
:
'upload-error'
,
field
:
string
,
error
:
any
,
file
:
any
,
fileList
:
any
[]):
void
/** 上传进度 */
(
event
:
'upload-progress'
,
field
:
string
,
event
:
any
,
file
:
any
,
fileList
:
any
[]):
void
/** 上传文件变化 */
(
event
:
'upload-change'
,
field
:
string
,
file
:
any
,
fileList
:
any
[]):
void
/** 移除上传文件 */
(
event
:
'upload-remove'
,
field
:
string
,
file
:
any
,
fileList
:
any
[]):
void
/** 预览上传文件 */
(
event
:
'upload-preview'
,
field
:
string
,
file
:
any
):
void
/** 上传前 */
(
event
:
'before-upload'
,
field
:
string
,
file
:
any
):
void
/** 移除前 */
(
event
:
'before-remove'
,
field
:
string
,
file
:
any
,
fileList
:
any
[]):
void
/** 自定义上传请求 */
(
event
:
'http-request'
,
field
:
string
,
options
:
any
):
void
/** 日历变化 */
(
event
:
'calendar-change'
,
field
:
string
,
value
:
any
):
void
/** 面板变化 */
(
event
:
'panel-change'
,
field
:
string
,
value
:
any
):
void
}
src/components/common/SuperForm.vue
0 → 100644
View file @
add12e41
<
template
>
<div
class=
"super-form"
v-loading=
"loading"
>
<el-form
ref=
"formRef"
:model=
"formData"
:rules=
"computedRules"
:label-width=
"config.labelWidth || '120px'"
:label-position=
"config.labelPosition || 'right'"
:inline=
"config.inline || false"
:disabled=
"config.disabled || false"
:size=
"config.size || 'default'"
:status-icon=
"config.statusIcon !== false"
:inline-message=
"config.inlineMessage"
:validate-on-rule-change=
"config.validateOnRuleChange !== false"
:hide-required-asterisk=
"config.hideRequiredAsterisk"
:show-message=
"config.showMessage !== false"
:label-suffix=
"config.labelSuffix"
:scroll-to-error=
"config.scrollToError !== false"
:scroll-into-view-options=
"config.scrollIntoViewOptions"
@
validate=
"handleValidate"
>
<el-row
:gutter=
"config.gutter || 20"
>
<el-col
v-for=
"(item, index) in formItems"
:key=
"item.field || index"
:span=
"item.span || 24"
:offset=
"item.offset"
:xs=
"item.xs"
:sm=
"item.sm"
:md=
"item.md"
:lg=
"item.lg"
:xl=
"item.xl"
:tag=
"item.tag"
:pull=
"item.pull"
:push=
"item.push"
>
<el-form-item
:prop=
"item.field"
:label=
"item.label"
:required=
"item.required"
:rules=
"item.rules"
:error=
"item.error"
:show-message=
"item.showMessage !== false"
:inline-message=
"item.inlineMessage"
:class=
"item.itemClass"
>
<!-- 输入框 -->
<template
v-if=
"item.type === 'input'"
>
<el-input
v-model=
"formData[item.field]"
v-bind=
"getItemProps(item)"
@
blur=
"handleFieldBlur(item, $event)"
@
focus=
"handleFieldFocus(item, $event)"
@
input=
"handleFieldInput(item, $event)"
@
change=
"handleFieldChange(item, $event)"
@
clear=
"handleFieldClear(item)"
>
<template
v-if=
"item.prepend"
#
prepend
>
{{
item
.
prepend
}}
</
template
>
<
template
v-if=
"item.append"
#
append
>
{{
item
.
append
}}
</
template
>
<
template
v-if=
"item.prefixIcon"
#
prefix
>
<el-icon><component
:is=
"item.prefixIcon"
/></el-icon>
</
template
>
<
template
v-if=
"item.suffixIcon"
#
suffix
>
<el-icon><component
:is=
"item.suffixIcon"
/></el-icon>
</
template
>
<
template
v-if=
"item.prefix"
#
prefix
>
{{
item
.
prefix
}}
</
template
>
<
template
v-if=
"item.suffix"
#
suffix
>
{{
item
.
suffix
}}
</
template
>
<
template
v-if=
"item.prependSlot"
#
prepend
>
<slot
:name=
"item.prependSlot"
:item=
"item"
:value=
"formData[item.field]"
/>
</
template
>
<
template
v-if=
"item.appendSlot"
#
append
>
<slot
:name=
"item.appendSlot"
:item=
"item"
:value=
"formData[item.field]"
/>
</
template
>
</el-input>
</template>
<!-- 文本域 -->
<
template
v-else-if=
"item.type === 'textarea'"
>
<el-input
v-model=
"formData[item.field]"
type=
"textarea"
v-bind=
"getItemProps(item)"
@
blur=
"handleFieldBlur(item, $event)"
@
focus=
"handleFieldFocus(item, $event)"
@
input=
"handleFieldInput(item, $event)"
@
change=
"handleFieldChange(item, $event)"
/>
</
template
>
<!-- 密码输入框 -->
<
template
v-else-if=
"item.type === 'password'"
>
<el-input
v-model=
"formData[item.field]"
type=
"password"
v-bind=
"getItemProps(item)"
@
blur=
"handleFieldBlur(item, $event)"
@
focus=
"handleFieldFocus(item, $event)"
@
input=
"handleFieldInput(item, $event)"
@
change=
"handleFieldChange(item, $event)"
/>
</
template
>
<!-- 数字输入框 -->
<
template
v-else-if=
"item.type === 'inputNumber' || item.type === 'number'"
>
<el-input-number
v-model=
"formData[item.field]"
v-bind=
"getItemProps(item)"
@
blur=
"handleFieldBlur(item, $event)"
@
focus=
"handleFieldFocus(item, $event)"
@
change=
"handleFieldChange(item, $event)"
/>
</
template
>
<!-- 选择器 -->
<
template
v-else-if=
"item.type === 'select'"
>
<el-select
v-model=
"formData[item.field]"
v-bind=
"getItemProps(item)"
@
change=
"handleFieldChange(item, $event)"
@
visible-change=
"handleFieldVisibleChange(item, $event)"
@
remove-tag=
"handleFieldRemoveTag(item, $event)"
@
clear=
"handleFieldClear(item)"
@
blur=
"handleFieldBlur(item, $event)"
@
focus=
"handleFieldFocus(item, $event)"
>
<template
v-if=
"item.options"
>
<el-option
v-for=
"option in getOptions(item)"
:key=
"option[item.valueKey || 'value']"
:label=
"option[item.labelKey || 'label']"
:value=
"option[item.valueKey || 'value']"
:disabled=
"option.disabled"
>
<slot
v-if=
"item.optionSlot"
:name=
"item.optionSlot"
:option=
"option"
:item=
"item"
>
<span
v-if=
"option.icon"
style=
"margin-right: 8px"
>
<el-icon><component
:is=
"option.icon"
/></el-icon>
</span>
{{
option
[
item
.
labelKey
||
'label'
]
}}
</slot>
</el-option>
</
template
>
<
template
v-if=
"item.prefix"
#
prefix
>
{{
item
.
prefix
}}
</
template
>
<
template
v-if=
"item.emptySlot"
#
empty
>
<slot
:name=
"item.emptySlot"
:item=
"item"
/>
</
template
>
</el-select>
</template>
<!-- 多选框组 -->
<
template
v-else-if=
"item.type === 'checkbox' || item.type === 'checkboxGroup'"
>
<el-checkbox-group
v-model=
"formData[item.field]"
v-bind=
"getItemProps(item)"
@
change=
"handleFieldChange(item, $event)"
>
<el-checkbox
v-for=
"option in getOptions(item)"
:key=
"option[item.valueKey || 'value']"
:label=
"option[item.valueKey || 'value']"
:disabled=
"option.disabled || item.disabled"
:border=
"item.border"
:true-label=
"option.trueLabel"
:false-label=
"option.falseLabel"
:indeterminate=
"option.indeterminate"
>
{{
option
[
item
.
labelKey
||
'label'
]
}}
</el-checkbox>
</el-checkbox-group>
</
template
>
<!-- 单选框组 -->
<
template
v-else-if=
"item.type === 'radio' || item.type === 'radioGroup'"
>
<el-radio-group
v-model=
"formData[item.field]"
v-bind=
"getItemProps(item)"
@
change=
"handleFieldChange(item, $event)"
>
<el-radio
v-for=
"option in getOptions(item)"
:key=
"option[item.valueKey || 'value']"
:label=
"option[item.valueKey || 'value']"
:disabled=
"option.disabled || item.disabled"
:border=
"item.border"
:size=
"item.size"
>
{{
option
[
item
.
labelKey
||
'label'
]
}}
</el-radio>
</el-radio-group>
</
template
>
<!-- 日期时间选择器 -->
<
template
v-else-if=
"['date', 'dates', 'datetime', 'week', 'month', 'year', 'daterange', 'datetimerange', 'monthrange'].includes(item.type)"
>
<el-date-picker
v-model=
"formData[item.field]"
:type=
"item.type"
v-bind=
"getItemProps(item)"
@
change=
"handleFieldChange(item, $event)"
@
blur=
"handleFieldBlur(item, $event)"
@
focus=
"handleFieldFocus(item, $event)"
@
calendar-change=
"handleCalendarChange(item, $event)"
@
panel-change=
"handlePanelChange(item, $event)"
@
visible-change=
"handleFieldVisibleChange(item, $event)"
/>
</
template
>
<!-- 时间选择器 -->
<
template
v-else-if=
"item.type === 'timePicker' || item.type === 'time'"
>
<el-time-picker
v-model=
"formData[item.field]"
v-bind=
"getItemProps(item)"
@
change=
"handleFieldChange(item, $event)"
@
blur=
"handleFieldBlur(item, $event)"
@
focus=
"handleFieldFocus(item, $event)"
/>
</
template
>
<!-- 时间选择器组 -->
<
template
v-else-if=
"item.type === 'timeSelect'"
>
<el-time-select
v-model=
"formData[item.field]"
v-bind=
"getItemProps(item)"
@
change=
"handleFieldChange(item, $event)"
@
blur=
"handleFieldBlur(item, $event)"
@
focus=
"handleFieldFocus(item, $event)"
/>
</
template
>
<!-- 开关 -->
<
template
v-else-if=
"item.type === 'switch'"
>
<el-switch
v-model=
"formData[item.field]"
v-bind=
"getItemProps(item)"
@
change=
"handleFieldChange(item, $event)"
/>
</
template
>
<!-- 滑块 -->
<
template
v-else-if=
"item.type === 'slider'"
>
<el-slider
v-model=
"formData[item.field]"
v-bind=
"getItemProps(item)"
@
change=
"handleFieldChange(item, $event)"
/>
</
template
>
<!-- 评分 -->
<
template
v-else-if=
"item.type === 'rate'"
>
<el-rate
v-model=
"formData[item.field]"
v-bind=
"getItemProps(item)"
@
change=
"handleFieldChange(item, $event)"
/>
</
template
>
<!-- 颜色选择器 -->
<
template
v-else-if=
"item.type === 'colorPicker'"
>
<el-color-picker
v-model=
"formData[item.field]"
v-bind=
"getItemProps(item)"
@
change=
"handleFieldChange(item, $event)"
@
active-change=
"handleColorActiveChange(item, $event)"
/>
</
template
>
<!-- 穿梭框 -->
<
template
v-else-if=
"item.type === 'transfer'"
>
<el-transfer
v-model=
"formData[item.field]"
v-bind=
"getItemProps(item)"
@
change=
"handleFieldChange(item, $event)"
@
left-check-change=
"handleTransferLeftCheckChange(item, $event)"
@
right-check-change=
"handleTransferRightCheckChange(item, $event)"
/>
</
template
>
<!-- 级联选择器 -->
<
template
v-else-if=
"item.type === 'cascader'"
>
<el-cascader
v-model=
"formData[item.field]"
v-bind=
"getItemProps(item)"
@
change=
"handleFieldChange(item, $event)"
@
expand-change=
"handleCascaderExpandChange(item, $event)"
@
visible-change=
"handleFieldVisibleChange(item, $event)"
/>
</
template
>
<!-- 树形选择器 -->
<
template
v-else-if=
"item.type === 'treeSelect'"
>
<el-tree-select
v-model=
"formData[item.field]"
v-bind=
"getItemProps(item)"
@
change=
"handleFieldChange(item, $event)"
@
node-click=
"handleTreeNodeClick(item, $event)"
@
check=
"handleTreeCheck(item, $event)"
@
node-expand=
"handleTreeNodeExpand(item, $event)"
@
node-collapse=
"handleTreeNodeCollapse(item, $event)"
/>
</
template
>
<!-- 上传 -->
<
template
v-else-if=
"item.type === 'upload'"
>
<el-upload
v-bind=
"getItemProps(item)"
:on-success=
"(response, file, fileList) => handleUploadSuccess(item, response, file, fileList)"
:on-error=
"(error, file, fileList) => handleUploadError(item, error, file, fileList)"
:on-progress=
"(event, file, fileList) => handleUploadProgress(item, event, file, fileList)"
:on-change=
"(file, fileList) => handleUploadChange(item, file, fileList)"
:on-remove=
"(file, fileList) => handleUploadRemove(item, file, fileList)"
:on-preview=
"(file) => handleUploadPreview(item, file)"
:before-upload=
"(file) => handleBeforeUpload(item, file)"
:before-remove=
"(file, fileList) => handleBeforeRemove(item, file, fileList)"
:http-request=
"(options) => handleHttpRequest(item, options)"
>
<slot
v-if=
"item.triggerSlot"
:name=
"item.triggerSlot"
:item=
"item"
>
<el-button
v-if=
"!item.drag"
:type=
"item.buttonType || 'primary'"
>
<el-icon
v-if=
"item.uploadIcon"
><component
:is=
"item.uploadIcon || 'UploadFilled'"
/></el-icon>
{{
item
.
buttonText
||
'点击上传'
}}
</el-button>
</slot>
<template
v-if=
"item.tipSlot"
#
tip
>
<slot
:name=
"item.tipSlot"
:item=
"item"
>
<div
class=
"el-upload__tip"
>
{{
item
.
tip
}}
</div>
</slot>
</
template
>
<
template
v-if=
"item.triggerSlot"
#
trigger
>
<slot
:name=
"item.triggerSlot"
:item=
"item"
/>
</
template
>
</el-upload>
</template>
<!-- 自动完成 -->
<
template
v-else-if=
"item.type === 'autocomplete'"
>
<el-autocomplete
v-model=
"formData[item.field]"
v-bind=
"getItemProps(item)"
@
select=
"handleAutocompleteSelect(item, $event)"
@
blur=
"handleFieldBlur(item, $event)"
@
focus=
"handleFieldFocus(item, $event)"
@
change=
"handleFieldChange(item, $event)"
>
<template
v-if=
"item.prepend"
#
prepend
>
{{
item
.
prepend
}}
</
template
>
<
template
v-if=
"item.append"
#
append
>
{{
item
.
append
}}
</
template
>
<
template
v-if=
"item.prefix"
#
prefix
>
{{
item
.
prefix
}}
</
template
>
<
template
v-if=
"item.suffix"
#
suffix
>
{{
item
.
suffix
}}
</
template
>
</el-autocomplete>
</template>
<!-- 计数器 -->
<
template
v-else-if=
"item.type === 'counter'"
>
<el-input-number
v-model=
"formData[item.field]"
v-bind=
"getItemProps(item)"
@
change=
"handleFieldChange(item, $event)"
/>
</
template
>
<!-- 文本展示 -->
<
template
v-else-if=
"item.type === 'text' || item.type === 'display'"
>
<div
class=
"form-text-display"
v-bind=
"item.textAttrs"
>
{{
item
.
formatter
?
item
.
formatter
(
formData
[
item
.
field
],
item
,
formData
)
:
formData
[
item
.
field
]
}}
</div>
</
template
>
<!-- HTML内容 -->
<
template
v-else-if=
"item.type === 'html'"
>
<div
class=
"form-html-content"
v-html=
"item.htmlContent || formData[item.field]"
v-bind=
"item.htmlAttrs"
></div>
</
template
>
<!-- 分割线 -->
<
template
v-else-if=
"item.type === 'divider'"
>
<el-divider
v-bind=
"item.dividerAttrs"
>
{{
item
.
dividerText
}}
</el-divider>
</
template
>
<!-- 警告 -->
<
template
v-else-if=
"item.type === 'alert'"
>
<el-alert
v-bind=
"item.alertAttrs"
:type=
"item.alertType || 'info'"
>
<template
v-if=
"item.alertTitleSlot"
#
title
>
<slot
:name=
"item.alertTitleSlot"
:item=
"item"
/>
</
template
>
</el-alert>
</template>
<!-- 自定义插槽 -->
<
template
v-else-if=
"item.type === 'slot' || item.type === 'custom'"
>
<slot
:name=
"item.slotName || item.field"
:item=
"item"
:value=
"formData[item.field]"
:formData=
"formData"
:onChange=
"(value) => handleSlotChange(item, value)"
/>
</
template
>
<!-- 自定义组件 -->
<
template
v-else-if=
"item.type === 'component' && item.component"
>
<component
:is=
"item.component"
v-model=
"formData[item.field]"
v-bind=
"getItemProps(item)"
@
change=
"handleFieldChange(item, $event)"
@
blur=
"handleFieldBlur(item, $event)"
@
focus=
"handleFieldFocus(item, $event)"
/>
</
template
>
<!-- 默认插槽兜底 -->
<slot
v-else
:name=
"item.field"
:item=
"item"
:value=
"formData[item.field]"
:formData=
"formData"
:onChange=
"(value) => handleSlotChange(item, value)"
/>
</el-form-item>
</el-col>
<!-- 表单操作按钮 -->
<el-col
v-if=
"config.showButtons !== false"
:span=
"config.buttonSpan || 24"
:offset=
"config.buttonOffset"
:xs=
"config.buttonXs"
:sm=
"config.buttonSm"
:md=
"config.buttonMd"
:lg=
"config.buttonLg"
:xl=
"config.buttonXl"
>
<el-form-item
:class=
"['form-buttons', config.buttonClass]"
>
<el-button
v-if=
"config.showSubmit !== false"
:type=
"config.submitType || 'primary'"
:size=
"config.buttonSize || config.size || 'default'"
:loading=
"submitLoading"
:icon=
"config.submitIcon"
:plain=
"config.submitPlain"
:round=
"config.submitRound"
:circle=
"config.submitCircle"
:disabled=
"config.submitDisabled"
@
click=
"handleSubmit"
>
{{ config.submitText || (isEdit ? '更新' : '提交') }}
</el-button>
<el-button
v-if=
"config.showReset !== false"
:size=
"config.buttonSize || config.size || 'default'"
:icon=
"config.resetIcon"
:plain=
"config.resetPlain"
:round=
"config.resetRound"
:circle=
"config.resetCircle"
:disabled=
"config.resetDisabled"
@
click=
"handleReset"
>
{{ config.resetText || '重置' }}
</el-button>
<el-button
v-for=
"btn in customButtons"
:key=
"btn.key"
:type=
"btn.type"
:size=
"btn.size || config.buttonSize || config.size || 'default'"
:loading=
"btn.loading"
:icon=
"btn.icon"
:plain=
"btn.plain"
:round=
"btn.round"
:circle=
"btn.circle"
:disabled=
"btn.disabled"
@
click=
"handleCustomButton(btn)"
>
{{ btn.text }}
</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
</template>
<
script
setup
>
import
{
ref
,
reactive
,
computed
,
watch
,
onMounted
,
getCurrentInstance
}
from
'vue'
import
{
UploadFilled
}
from
'@element-plus/icons-vue'
const
props
=
defineProps
({
// 表单配置
config
:
{
type
:
Object
,
default
:
()
=>
({})
},
// 表单项配置
items
:
{
type
:
Array
,
default
:
()
=>
[]
},
// 表单数据
modelValue
:
{
type
:
Object
,
default
:
()
=>
({})
},
// 表单验证规则
rules
:
{
type
:
Object
,
default
:
()
=>
({})
},
// 保存接口配置
saveApi
:
{
type
:
[
String
,
Object
,
Function
],
default
:
null
},
// 更新接口配置
updateApi
:
{
type
:
[
String
,
Object
,
Function
],
default
:
null
},
// 获取详情接口配置(用于编辑时回显数据)
fetchApi
:
{
type
:
[
String
,
Object
,
Function
],
default
:
null
},
// 主键字段名(用于判断新增还是编辑)
idKey
:
{
type
:
String
,
default
:
'id'
},
// 是否在提交前验证
validateBeforeSubmit
:
{
type
:
Boolean
,
default
:
true
},
// 提交前的钩子(返回false可阻止提交)
beforeSubmit
:
{
type
:
Function
,
default
:
null
},
// 提交后的钩子
afterSubmit
:
{
type
:
Function
,
default
:
null
},
// 自定义提交方法
customSubmit
:
{
type
:
Function
,
default
:
null
},
// 数据转换函数(提交前转换数据格式)
dataTransform
:
{
type
:
Function
,
default
:
null
},
// 响应数据转换函数(获取详情后转换数据格式)
responseTransform
:
{
type
:
Function
,
default
:
null
}
})
const
emit
=
defineEmits
([
'update:modelValue'
,
'submit'
,
'success'
,
'error'
,
'change'
,
'blur'
,
'focus'
,
'input'
,
'validate'
,
'reset'
,
'field-change'
,
'field-blur'
,
'field-focus'
,
'field-visible-change'
])
const
{
proxy
}
=
getCurrentInstance
()
const
formRef
=
ref
()
const
formData
=
reactive
({})
const
loading
=
ref
(
false
)
const
submitLoading
=
ref
(
false
)
// 计算属性:表单项配置
const
formItems
=
computed
(()
=>
props
.
items
)
// 计算属性:是否为编辑模式
const
isEdit
=
computed
(()
=>
{
return
!!
(
formData
[
props
.
idKey
]
||
props
.
modelValue
[
props
.
idKey
])
})
// 计算属性:自定义按钮
const
customButtons
=
computed
(()
=>
{
return
props
.
config
.
customButtons
||
[]
})
// 计算属性:表单验证规则
const
computedRules
=
computed
(()
=>
{
const
rules
=
{
...
props
.
rules
}
formItems
.
value
.
forEach
(
item
=>
{
if
(
item
.
rules
)
{
rules
[
item
.
field
]
=
item
.
rules
}
if
(
item
.
required
&&
!
rules
[
item
.
field
])
{
rules
[
item
.
field
]
=
[
{
required
:
true
,
message
:
item
.
requiredMessage
||
`请输入
${
item
.
label
}
`
,
trigger
:
item
.
trigger
||
'blur'
}
]
}
})
return
rules
})
// 获取表单项的属性(过滤掉组件内部使用的属性)
const
getItemProps
=
(
item
)
=>
{
const
excludeKeys
=
[
'field'
,
'label'
,
'type'
,
'required'
,
'rules'
,
'requiredMessage'
,
'trigger'
,
'span'
,
'offset'
,
'xs'
,
'sm'
,
'md'
,
'lg'
,
'xl'
,
'tag'
,
'pull'
,
'push'
,
'defaultValue'
,
'valueKey'
,
'labelKey'
,
'options'
,
'optionSlot'
,
'emptySlot'
,
'prepend'
,
'append'
,
'prefix'
,
'suffix'
,
'prefixIcon'
,
'suffixIcon'
,
'prependSlot'
,
'appendSlot'
,
'textAttrs'
,
'htmlAttrs'
,
'htmlContent'
,
'dividerAttrs'
,
'dividerText'
,
'alertAttrs'
,
'alertType'
,
'alertTitleSlot'
,
'slotName'
,
'component'
,
'componentProps'
,
'itemClass'
,
'formatter'
,
'buttonText'
,
'buttonType'
,
'uploadIcon'
,
'triggerSlot'
,
'tipSlot'
,
'tip'
]
const
props
=
{}
Object
.
keys
(
item
).
forEach
(
key
=>
{
if
(
!
excludeKeys
.
includes
(
key
))
{
props
[
key
]
=
item
[
key
]
}
})
return
props
}
// 获取选项数据
const
getOptions
=
(
item
)
=>
{
if
(
typeof
item
.
options
===
'function'
)
{
return
item
.
options
(
item
,
formData
)
}
return
item
.
options
||
[]
}
// 初始化表单数据
const
initFormData
=
()
=>
{
// 清空现有数据
Object
.
keys
(
formData
).
forEach
(
key
=>
{
delete
formData
[
key
]
})
// 设置默认值或外部传入的值
formItems
.
value
.
forEach
(
item
=>
{
if
(
item
.
field
)
{
const
externalValue
=
props
.
modelValue
[
item
.
field
]
if
(
externalValue
!==
undefined
)
{
formData
[
item
.
field
]
=
externalValue
}
else
{
formData
[
item
.
field
]
=
item
.
defaultValue
!==
undefined
?
item
.
defaultValue
:
getDefaultValue
(
item
.
type
)
}
}
})
}
// 获取不同类型的默认值
const
getDefaultValue
=
(
type
)
=>
{
const
defaultValues
=
{
checkbox
:
[],
switch
:
false
,
inputNumber
:
undefined
,
number
:
undefined
,
rate
:
0
,
slider
:
0
,
upload
:
[],
transfer
:
[],
treeSelect
:
null
,
cascader
:
[],
timeSelect
:
''
}
return
defaultValues
[
type
]
!==
undefined
?
defaultValues
[
type
]
:
''
}
// 监听表单数据变化
watch
(
formData
,
(
newVal
)
=>
{
emit
(
'update:modelValue'
,
{
...
newVal
})
emit
(
'change'
,
newVal
)
},
{
deep
:
true
}
)
// 监听外部数据变化
watch
(
()
=>
props
.
modelValue
,
(
newVal
)
=>
{
Object
.
keys
(
newVal
).
forEach
(
key
=>
{
if
(
Object
.
prototype
.
hasOwnProperty
.
call
(
formData
,
key
))
{
formData
[
key
]
=
newVal
[
key
]
}
})
},
{
deep
:
true
}
)
// 字段事件处理
const
handleFieldInput
=
(
item
,
event
)
=>
{
emit
(
'input'
,
item
.
field
,
event
.
target
?
event
.
target
.
value
:
event
)
}
const
handleFieldChange
=
(
item
,
value
)
=>
{
emit
(
'field-change'
,
item
.
field
,
value
,
formData
)
emit
(
'change'
,
item
.
field
,
value
)
}
const
handleFieldBlur
=
(
item
,
event
)
=>
{
emit
(
'field-blur'
,
item
.
field
,
event
)
emit
(
'blur'
,
item
.
field
,
event
)
}
const
handleFieldFocus
=
(
item
,
event
)
=>
{
emit
(
'field-focus'
,
item
.
field
,
event
)
emit
(
'focus'
,
item
.
field
,
event
)
}
const
handleFieldClear
=
(
item
)
=>
{
emit
(
'field-clear'
,
item
.
field
)
}
const
handleFieldVisibleChange
=
(
item
,
visible
)
=>
{
emit
(
'field-visible-change'
,
item
.
field
,
visible
)
}
const
handleFieldRemoveTag
=
(
item
,
tag
)
=>
{
emit
(
'field-remove-tag'
,
item
.
field
,
tag
)
}
const
handleCalendarChange
=
(
item
,
value
)
=>
{
emit
(
'calendar-change'
,
item
.
field
,
value
)
}
const
handlePanelChange
=
(
item
,
value
)
=>
{
emit
(
'panel-change'
,
item
.
field
,
value
)
}
// 颜色选择器事件
const
handleColorActiveChange
=
(
item
,
color
)
=>
{
emit
(
'color-active-change'
,
item
.
field
,
color
)
}
// 级联选择器事件
const
handleCascaderExpandChange
=
(
item
,
value
)
=>
{
emit
(
'cascader-expand-change'
,
item
.
field
,
value
)
}
// 树形选择器事件
const
handleTreeNodeClick
=
(
item
,
data
)
=>
{
emit
(
'tree-node-click'
,
item
.
field
,
data
)
}
const
handleTreeCheck
=
(
item
,
data
)
=>
{
emit
(
'tree-check'
,
item
.
field
,
data
)
}
const
handleTreeNodeExpand
=
(
item
,
data
)
=>
{
emit
(
'tree-node-expand'
,
item
.
field
,
data
)
}
const
handleTreeNodeCollapse
=
(
item
,
data
)
=>
{
emit
(
'tree-node-collapse'
,
item
.
field
,
data
)
}
// 穿梭框事件
const
handleTransferLeftCheckChange
=
(
item
,
value
)
=>
{
emit
(
'transfer-left-check-change'
,
item
.
field
,
value
)
}
const
handleTransferRightCheckChange
=
(
item
,
value
)
=>
{
emit
(
'transfer-right-check-change'
,
item
.
field
,
value
)
}
// 自动完成事件
const
handleAutocompleteSelect
=
(
item
,
value
)
=>
{
emit
(
'autocomplete-select'
,
item
.
field
,
value
)
}
// 上传事件处理
const
handleUploadSuccess
=
(
item
,
response
,
file
,
fileList
)
=>
{
if
(
item
.
onSuccess
)
{
item
.
onSuccess
(
response
,
file
,
fileList
,
formData
)
}
emit
(
'upload-success'
,
item
.
field
,
response
,
file
,
fileList
)
}
const
handleUploadError
=
(
item
,
error
,
file
,
fileList
)
=>
{
if
(
item
.
onError
)
{
item
.
onError
(
error
,
file
,
fileList
)
}
emit
(
'upload-error'
,
item
.
field
,
error
,
file
,
fileList
)
}
const
handleUploadProgress
=
(
item
,
event
,
file
,
fileList
)
=>
{
if
(
item
.
onProgress
)
{
item
.
onProgress
(
event
,
file
,
fileList
)
}
emit
(
'upload-progress'
,
item
.
field
,
event
,
file
,
fileList
)
}
const
handleUploadChange
=
(
item
,
file
,
fileList
)
=>
{
if
(
item
.
onChange
)
{
item
.
onChange
(
file
,
fileList
,
formData
)
}
emit
(
'upload-change'
,
item
.
field
,
file
,
fileList
)
}
const
handleUploadRemove
=
(
item
,
file
,
fileList
)
=>
{
if
(
item
.
onRemove
)
{
item
.
onRemove
(
file
,
fileList
,
formData
)
}
emit
(
'upload-remove'
,
item
.
field
,
file
,
fileList
)
}
const
handleUploadPreview
=
(
item
,
file
)
=>
{
if
(
item
.
onPreview
)
{
item
.
onPreview
(
file
)
}
emit
(
'upload-preview'
,
item
.
field
,
file
)
}
const
handleBeforeUpload
=
(
item
,
file
)
=>
{
if
(
item
.
beforeUpload
)
{
return
item
.
beforeUpload
(
file
,
formData
)
}
emit
(
'before-upload'
,
item
.
field
,
file
)
return
true
}
const
handleBeforeRemove
=
(
item
,
file
,
fileList
)
=>
{
if
(
item
.
beforeRemove
)
{
return
item
.
beforeRemove
(
file
,
fileList
,
formData
)
}
emit
(
'before-remove'
,
item
.
field
,
file
,
fileList
)
return
true
}
const
handleHttpRequest
=
(
item
,
options
)
=>
{
if
(
item
.
httpRequest
)
{
return
item
.
httpRequest
(
options
,
formData
)
}
emit
(
'http-request'
,
item
.
field
,
options
)
}
// 插槽变更处理
const
handleSlotChange
=
(
item
,
value
)
=>
{
formData
[
item
.
field
]
=
value
emit
(
'field-change'
,
item
.
field
,
value
,
formData
)
}
// 表单验证事件
const
handleValidate
=
(
prop
,
isValid
,
message
)
=>
{
emit
(
'validate'
,
prop
,
isValid
,
message
)
}
// 表单提交
const
handleSubmit
=
async
()
=>
{
try
{
// 如果有自定义提交方法,使用自定义方法
if
(
props
.
customSubmit
)
{
props
.
customSubmit
(
formData
,
{
isEdit
:
isEdit
.
value
})
return
}
// 验证表单
if
(
props
.
validateBeforeSubmit
)
{
const
valid
=
await
formRef
.
value
.
validate
()
if
(
!
valid
)
return
}
// 执行提交前钩子
if
(
props
.
beforeSubmit
)
{
const
shouldContinue
=
await
props
.
beforeSubmit
(
formData
,
isEdit
.
value
)
if
(
shouldContinue
===
false
)
return
}
submitLoading
.
value
=
true
// 转换数据格式
let
submitData
=
props
.
dataTransform
?
props
.
dataTransform
(
formData
)
:
{
...
formData
}
// 判断是新增还是编辑
const
apiUrl
=
isEdit
.
value
?
props
.
updateApi
:
props
.
saveApi
if
(
!
apiUrl
)
{
// 如果没有配置接口,直接触发提交事件
emit
(
'submit'
,
submitData
,
isEdit
.
value
)
submitLoading
.
value
=
false
return
}
// 发送请求
proxy
.
$post
({
url
:
typeof
apiUrl
===
'function'
?
apiUrl
(
isEdit
.
value
)
:
apiUrl
,
data
:
submitData
,
callback
:
(
response
)
=>
{
submitLoading
.
value
=
false
emit
(
'success'
,
response
,
submitData
,
isEdit
.
value
)
// 执行提交后钩子
if
(
props
.
afterSubmit
)
{
props
.
afterSubmit
(
response
,
submitData
,
isEdit
.
value
)
}
},
error
:
(
err
)
=>
{
submitLoading
.
value
=
false
emit
(
'error'
,
err
,
submitData
,
isEdit
.
value
)
}
})
}
catch
(
error
)
{
submitLoading
.
value
=
false
emit
(
'validate'
,
false
,
error
)
}
}
// 表单重置
const
handleReset
=
()
=>
{
formRef
.
value
.
resetFields
()
initFormData
()
emit
(
'reset'
)
}
// 获取详情数据(用于编辑)
const
fetchDetail
=
async
(
id
)
=>
{
if
(
!
props
.
fetchApi
)
return
loading
.
value
=
true
try
{
proxy
.
$post
({
url
:
typeof
props
.
fetchApi
===
'function'
?
props
.
fetchApi
(
id
)
:
props
.
fetchApi
,
data
:
{
[
props
.
idKey
]:
id
},
callback
:
(
response
)
=>
{
const
data
=
props
.
responseTransform
?
props
.
responseTransform
(
response
)
:
response
Object
.
keys
(
data
).
forEach
(
key
=>
{
if
(
Object
.
prototype
.
hasOwnProperty
.
call
(
formData
,
key
))
{
formData
[
key
]
=
data
[
key
]
}
})
emit
(
'update:modelValue'
,
{
...
formData
})
loading
.
value
=
false
},
error
:
(
err
)
=>
{
loading
.
value
=
false
emit
(
'error'
,
err
)
}
})
}
catch
(
error
)
{
loading
.
value
=
false
emit
(
'error'
,
error
)
}
}
// 表单验证
const
validate
=
async
()
=>
{
try
{
const
valid
=
await
formRef
.
value
.
validate
()
emit
(
'validate'
,
true
)
return
valid
}
catch
(
error
)
{
emit
(
'validate'
,
false
,
error
)
return
false
}
}
// 验证指定字段
const
validateField
=
async
(
prop
)
=>
{
try
{
await
formRef
.
value
.
validateField
(
prop
)
return
true
}
catch
(
error
)
{
return
false
}
}
// 清除验证
const
clearValidate
=
(
fields
)
=>
{
formRef
.
value
.
clearValidate
(
fields
)
}
// 重置表单
const
resetFields
=
()
=>
{
formRef
.
value
.
resetFields
()
initFormData
()
}
// 设置表单数据
const
setFormData
=
(
data
)
=>
{
Object
.
keys
(
data
).
forEach
(
key
=>
{
if
(
Object
.
prototype
.
hasOwnProperty
.
call
(
formData
,
key
))
{
formData
[
key
]
=
data
[
key
]
}
})
emit
(
'update:modelValue'
,
{
...
formData
})
}
// 获取表单数据
const
getFormData
=
()
=>
{
return
{
...
formData
}
}
// 设置字段值
const
setFieldValue
=
(
field
,
value
)
=>
{
formData
[
field
]
=
value
}
// 获取字段值
const
getFieldValue
=
(
field
)
=>
{
return
formData
[
field
]
}
// 暴露方法和属性
defineExpose
({
formRef
,
validate
,
validateField
,
clearValidate
,
resetFields
,
setFormData
,
getFormData
,
setFieldValue
,
getFieldValue
,
fetchDetail
,
handleSubmit
,
formData
,
isEdit
})
onMounted
(()
=>
{
initFormData
()
})
</
script
>
<
style
scoped
lang=
"less"
>
.super-form {
.form-text-display {
padding: 0 11px;
line-height: 32px;
color: #606266;
word-break: break-all;
}
.form-html-content {
padding: 0 11px;
line-height: 1.5;
color: #606266;
}
.form-buttons {
:deep(.el-form-item__content) {
display: flex;
justify-content: flex-end;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
}
:deep(.el-input-number) {
width: 100%;
}
:deep(.el-select) {
width: 100%;
}
:deep(.el-cascader) {
width: 100%;
}
:deep(.el-tree-select) {
width: 100%;
}
:deep(.el-date-editor) {
width: 100%;
}
:deep(.el-time-picker) {
width: 100%;
}
:deep(.el-autocomplete) {
width: 100%;
}
:deep(.el-color-picker) {
.el-color-picker__mask {
border-radius: 4px;
}
}
:deep(.el-form-item__label) {
font-weight: 500;
color: #303133;
}
:deep(.el-form-item) {
margin-bottom: 18px;
}
:deep(.el-form--inline) {
.el-form-item {
display: inline-flex;
vertical-align: middle;
margin-right: 10px;
}
}
:deep(.el-transfer) {
text-align: left;
}
:deep(.el-upload) {
.el-upload-dragger {
padding: 40px;
}
}
}
</
style
>
src/router/routes.js
View file @
add12e41
...
...
@@ -271,6 +271,13 @@ const routes = [
title
:
"参股企业管理"
,
component
:
()
=>
import
(
"@/views/everydayPage/shareAdd.vue"
),
},
{
path
:
"/SuperFormExample"
,
name
:
"SuperFormExample"
,
title
:
"测试组件"
,
component
:
()
=>
import
(
"@/views/everydayPage/SuperFormExample.vue"
),
},
{
path
:
"/system"
,
name
:
"system"
,
...
...
src/views/everydayPage/SuperFormExample.vue
0 → 100644
View file @
add12e41
<
template
>
<div
class=
"super-form-example"
>
<el-card
shadow=
"never"
class=
"example-card"
>
<template
#
header
>
<div
class=
"card-header"
>
<span
class=
"card-title"
>
超级表单组件使用示例
</span>
<el-button
type=
"primary"
size=
"small"
@
click=
"handleCreate"
>
新增示例
</el-button
>
</div>
</
template
>
<el-tabs
v-model=
"activeTab"
type=
"border-card"
class=
"form-tabs"
>
<!-- 基础示例 -->
<el-tab-pane
label=
"基础示例"
name=
"basic"
>
<SuperForm
ref=
"basicFormRef"
v-model=
"basicFormData"
:config=
"basicFormConfig"
:items=
"basicFormItems"
:rules=
"basicFormRules"
@
submit=
"handleBasicSubmit"
@
success=
"handleSubmitSuccess"
@
error=
"handleSubmitError"
/>
</el-tab-pane>
<!-- 高级示例 -->
<el-tab-pane
label=
"高级示例"
name=
"advanced"
>
<SuperForm
ref=
"advancedFormRef"
v-model=
"advancedFormData"
:config=
"advancedFormConfig"
:items=
"advancedFormItems"
:save-api=
"saveApiUrl"
:update-api=
"updateApiUrl"
:fetch-api=
"fetchApiUrl"
@
success=
"handleSubmitSuccess"
@
error=
"handleSubmitError"
/>
</el-tab-pane>
<!-- 自定义样式示例 -->
<el-tab-pane
label=
"自定义样式"
name=
"custom"
>
<SuperForm
ref=
"customFormRef"
v-model=
"customFormData"
:config=
"customFormConfig"
:items=
"customFormItems"
/>
</el-tab-pane>
<!-- 动态表单示例 -->
<el-tab-pane
label=
"动态表单"
name=
"dynamic"
>
<el-button
type=
"primary"
@
click=
"toggleDynamicFields"
>
切换字段显示
</el-button
>
<SuperForm
ref=
"dynamicFormRef"
v-model=
"dynamicFormData"
:config=
"dynamicFormConfig"
:items=
"dynamicFormItems"
/>
</el-tab-pane>
<!-- 带插槽的表单 -->
<el-tab-pane
label=
"自定义插槽"
name=
"slots"
>
<SuperForm
ref=
"slotFormRef"
v-model=
"slotFormData"
:config=
"slotFormConfig"
:items=
"slotFormItems"
@
submit=
"handleSlotSubmit"
>
<!-- 自定义插槽:用户信息 -->
<
template
#
userInfo=
"{ item, value, onChange }"
>
<div
class=
"custom-user-info"
>
<el-button
size=
"small"
type=
"primary"
@
click=
"handleChangeAvatar"
>
更换头像
</el-button
>
</div>
</
template
>
<!-- 自定义插槽:数据展示 -->
<
template
#
dataDisplay=
"{ item, value }"
>
<el-descriptions
:column=
"2"
border
>
<el-descriptions-item
label=
"用户名"
>
{{
slotFormData
.
username
}}
</el-descriptions-item>
<el-descriptions-item
label=
"邮箱"
>
{{
slotFormData
.
email
}}
</el-descriptions-item>
<el-descriptions-item
label=
"注册时间"
>
{{
slotFormData
.
registerTime
}}
</el-descriptions-item>
<el-descriptions-item
label=
"状态"
>
<el-tag
:type=
"slotFormData.status === 1 ? 'success' : 'danger'"
>
{{
slotFormData
.
status
===
1
?
"正常"
:
"禁用"
}}
</el-tag>
</el-descriptions-item>
</el-descriptions>
</
template
>
<!-- 自定义插槽:富文本编辑器 -->
<
template
#
richText=
"{ item, value, onChange }"
>
<div
class=
"rich-text-editor"
>
<el-input
type=
"textarea"
:rows=
"6"
:model-value=
"value"
@
input=
"onChange"
placeholder=
"请输入富文本内容..."
/>
<div
class=
"editor-toolbar"
>
<el-button
size=
"small"
@
click=
"insertText('
<b>
粗体
</b>
')"
>粗体
</el-button
>
<el-button
size=
"small"
@
click=
"insertText('
<i>
斜体
</i>
')"
>斜体
</el-button
>
<el-button
size=
"small"
@
click=
"insertText('
<u>
下划线
</u>
')"
>下划线
</el-button
>
</div>
</div>
</
template
>
</SuperForm>
</el-tab-pane>
<!-- 组件集成示例 - 使用懒加载优化性能 -->
<el-tab-pane
label=
"组件集成"
name=
"integration"
>
<div
class=
"integration-container"
>
<!-- 通用选择器集成 -->
<div
class=
"integration-section"
>
<div
class=
"section-title"
>
CommonSelector 通用选择器
</div>
<SuperForm
ref=
"selectorFormRef"
v-model=
"selectorFormData"
:config=
"selectorFormConfig"
:items=
"selectorFormItems"
/>
</div>
<!-- 财务表格集成 - 使用虚拟滚动优化 -->
<div
class=
"integration-section"
>
<div
class=
"section-title"
>
FinancialTable 财务表格
</div>
<FinancialTable
v-model=
"financialTableData"
:is-preview=
"false"
/>
</div>
<!-- 动态表单表格集成 - 使用分页加载 -->
<div
class=
"integration-section"
>
<div
class=
"section-title"
>
FormDynamicTable 动态表格
</div>
<FormDynamicTable
v-model=
"dynamicTableData"
:columns=
"dynamicTableColumns"
:default-row=
"defaultRow"
:select-options=
"selectOptions"
:show-import-export=
"true"
export-name=
"动态表格数据"
/>
</div>
<!-- 年度计划集成 - 使用计算属性优化 -->
<div
class=
"integration-section"
>
<div
class=
"section-title"
>
AnnualPlan 年度计划
</div>
<el-button
type=
"primary"
size=
"small"
@
click=
"toggleAnnualPlan"
style=
"margin-bottom: 12px"
>
{{ showAnnualPlan ? '隐藏' : '显示' }}年度计划
</el-button>
<AnnualPlan
v-if=
"showAnnualPlan"
v-model=
"annualPlanData"
:dynamic-time-list=
"annualPlanTimeList"
:is-preview=
"false"
/>
</div>
</div>
</el-tab-pane>
<!-- 性能优化示例 -->
<el-tab-pane
label=
"性能优化"
name=
"performance"
>
<div
class=
"performance-container"
>
<el-alert
title=
"性能优化技巧"
type=
"info"
:closable=
"false"
style=
"margin-bottom: 20px"
>
<
template
#
default
>
<ul
class=
"performance-tips"
>
<li>
✅ 使用 computed 缓存计算结果
</li>
<li>
✅ 使用 v-once 渲染静态内容
</li>
<li>
✅ 使用虚拟滚动处理大列表
</li>
<li>
✅ 使用防抖/节流优化频繁触发的事件
</li>
<li>
✅ 按需加载组件,减少初始渲染压力
</li>
<li>
✅ 合理使用 v-show 和 v-if
</li>
</ul>
</
template
>
</el-alert>
<div
class=
"performance-demo"
>
<div
class=
"demo-item"
>
<div
class=
"demo-title"
>
大数据表单(1000+ 字段)
</div>
<el-progress
:percentage=
"loadingProgress"
:status=
"loadingProgress === 100 ? 'success' : undefined"
/>
<el-button
type=
"primary"
@
click=
"loadLargeForm"
:loading=
"isLoadingLargeForm"
style=
"margin-top: 12px"
>
加载大表单
</el-button>
<div
v-if=
"largeFormItems.length > 0"
class=
"form-stats"
>
<el-tag
type=
"success"
>
字段数: {{ largeFormItems.length }}
</el-tag>
<el-tag
type=
"info"
>
加载时间: {{ loadTime }}ms
</el-tag>
</div>
</div>
</div>
</div>
</el-tab-pane>
</el-tabs>
</el-card>
</div>
</template>
<
script
setup
>
import
{
ref
,
reactive
,
computed
,
watch
,
nextTick
,
onMounted
,
defineAsyncComponent
}
from
"vue"
;
import
{
ElMessage
}
from
"element-plus"
;
import
SuperForm
from
"@/components/common/SuperForm.vue"
;
// 🚀 性能优化:使用异步组件按需加载,减少初始渲染时间
const
FinancialTable
=
defineAsyncComponent
(()
=>
import
(
"@/components/FinancialTable.vue"
)
);
const
FormDynamicTable
=
defineAsyncComponent
(()
=>
import
(
"@/components/FormDynamicTable/index.vue"
)
);
const
AnnualPlan
=
defineAsyncComponent
(()
=>
import
(
"@/views/everydayPage/annualPlan.vue"
)
);
const
CommonSelector
=
defineAsyncComponent
(()
=>
import
(
"@/components/CommonSelector.vue"
)
);
const
activeTab
=
ref
(
"basic"
);
const
basicFormRef
=
ref
();
const
advancedFormRef
=
ref
();
const
customFormRef
=
ref
();
const
dynamicFormRef
=
ref
();
const
slotFormRef
=
ref
();
const
selectorFormRef
=
ref
();
// API 配置
const
saveApiUrl
=
"/api/example/create"
;
const
updateApiUrl
=
"/api/example/update"
;
const
fetchApiUrl
=
"/api/example/detail"
;
// 性能优化相关
const
loadingProgress
=
ref
(
0
);
const
isLoadingLargeForm
=
ref
(
false
);
const
largeFormItems
=
ref
([]);
const
loadTime
=
ref
(
0
);
const
showAnnualPlan
=
ref
(
false
);
// 默认隐藏年度计划,按需显示
// ==================== 基础表单示例 ====================
const
basicFormData
=
ref
({
username
:
""
,
password
:
""
,
gender
:
"1"
,
hobby
:
[],
birthDate
:
""
,
email
:
""
,
phone
:
""
,
age
:
25
,
introduce
:
""
,
});
const
basicFormConfig
=
{
labelWidth
:
"120px"
,
labelPosition
:
"right"
,
gutter
:
20
,
size
:
"default"
,
showButtons
:
true
,
showSubmit
:
true
,
showReset
:
true
,
submitText
:
"提交"
,
resetText
:
"重置"
,
submitType
:
"primary"
,
};
const
basicFormItems
=
computed
(()
=>
[
{
field
:
"username"
,
label
:
"用户名"
,
type
:
"input"
,
span
:
12
,
required
:
true
,
clearable
:
true
,
placeholder
:
"请输入用户名"
,
maxlength
:
20
,
"show-word-limit"
:
true
,
},
{
field
:
"password"
,
label
:
"密码"
,
type
:
"password"
,
span
:
12
,
required
:
true
,
clearable
:
true
,
placeholder
:
"请输入密码"
,
"show-password"
:
true
,
},
{
field
:
"email"
,
label
:
"邮箱"
,
type
:
"input"
,
span
:
12
,
required
:
true
,
clearable
:
true
,
placeholder
:
"请输入邮箱"
,
rules
:
[
{
type
:
"email"
,
message
:
"请输入正确的邮箱地址"
,
trigger
:
"blur"
},
],
},
{
field
:
"phone"
,
label
:
"手机号"
,
type
:
"input"
,
span
:
12
,
clearable
:
true
,
placeholder
:
"请输入手机号"
,
rules
:
[
{
pattern
:
/^1
[
3-9
]\d{9}
$/
,
message
:
"请输入正确的手机号"
,
trigger
:
"blur"
,
},
],
},
{
field
:
"gender"
,
label
:
"性别"
,
type
:
"radio"
,
span
:
12
,
required
:
true
,
options
:
[
{
label
:
"男"
,
value
:
"1"
},
{
label
:
"女"
,
value
:
"2"
},
{
label
:
"保密"
,
value
:
"0"
},
],
},
{
field
:
"age"
,
label
:
"年龄"
,
type
:
"number"
,
span
:
12
,
min
:
0
,
max
:
150
,
step
:
1
,
},
{
field
:
"hobby"
,
label
:
"爱好"
,
type
:
"checkbox"
,
span
:
12
,
options
:
[
{
label
:
"阅读"
,
value
:
"reading"
},
{
label
:
"运动"
,
value
:
"sports"
},
{
label
:
"音乐"
,
value
:
"music"
},
{
label
:
"旅行"
,
value
:
"travel"
},
],
},
{
field
:
"birthDate"
,
label
:
"出生日期"
,
type
:
"date"
,
span
:
12
,
"value-format"
:
"YYYY-MM-DD"
,
placeholder
:
"请选择出生日期"
,
},
{
field
:
"introduce"
,
label
:
"个人简介"
,
type
:
"textarea"
,
span
:
24
,
rows
:
4
,
maxlength
:
200
,
"show-word-limit"
:
true
,
placeholder
:
"请输入个人简介"
,
},
]);
const
basicFormRules
=
{
username
:
[
{
required
:
true
,
message
:
"请输入用户名"
,
trigger
:
"blur"
},
{
min
:
3
,
max
:
20
,
message
:
"长度在 3 到 20 个字符"
,
trigger
:
"blur"
},
],
password
:
[
{
required
:
true
,
message
:
"请输入密码"
,
trigger
:
"blur"
},
{
min
:
6
,
message
:
"密码长度不能少于 6 位"
,
trigger
:
"blur"
},
],
};
// ==================== 高级表单示例 ====================
const
advancedFormData
=
ref
({
name
:
""
,
category
:
[],
tags
:
[],
publishTime
:
""
,
validDate
:
[],
isPublished
:
false
,
priority
:
50
,
rating
:
3
,
color
:
"#409EFF"
,
region
:
[],
treeSelect
:
""
,
files
:
[],
});
const
advancedFormConfig
=
{
labelWidth
:
"140px"
,
gutter
:
20
,
showButtons
:
true
,
customButtons
:
[
{
key
:
"draft"
,
text
:
"保存草稿"
,
type
:
"info"
,
icon
:
"Document"
,
handler
:
()
=>
{
ElMessage
.
info
(
"保存草稿成功"
);
},
},
{
key
:
"preview"
,
text
:
"预览"
,
type
:
"success"
,
icon
:
"View"
,
handler
:
()
=>
{
ElMessage
.
success
(
"预览模式"
);
},
},
],
};
const
advancedFormItems
=
computed
(()
=>
[
{
field
:
"name"
,
label
:
"内容名称"
,
type
:
"input"
,
span
:
12
,
required
:
true
,
clearable
:
true
,
placeholder
:
"请输入内容名称"
,
},
{
field
:
"category"
,
label
:
"内容分类"
,
type
:
"select"
,
span
:
12
,
clearable
:
true
,
filterable
:
true
,
placeholder
:
"请选择分类"
,
options
:
[
{
label
:
"技术"
,
value
:
"tech"
},
{
label
:
"生活"
,
value
:
"life"
},
{
label
:
"娱乐"
,
value
:
"entertainment"
},
{
label
:
"体育"
,
value
:
"sports"
},
],
props
:
{
multiple
:
true
,
},
},
{
field
:
"tags"
,
label
:
"标签"
,
type
:
"checkbox"
,
span
:
12
,
options
:
[
{
label
:
"热门"
,
value
:
"hot"
},
{
label
:
"推荐"
,
value
:
"recommend"
},
{
label
:
"最新"
,
value
:
"latest"
},
{
label
:
"精选"
,
value
:
"featured"
},
],
},
{
field
:
"publishTime"
,
label
:
"发布时间"
,
type
:
"datetime"
,
span
:
12
,
"value-format"
:
"YYYY-MM-DD HH:mm:ss"
,
placeholder
:
"请选择发布时间"
,
},
{
field
:
"validDate"
,
label
:
"有效期"
,
type
:
"daterange"
,
span
:
12
,
"value-format"
:
"YYYY-MM-DD"
,
"start-placeholder"
:
"开始日期"
,
"end-placeholder"
:
"结束日期"
,
},
{
field
:
"isPublished"
,
label
:
"是否发布"
,
type
:
"switch"
,
span
:
8
,
"active-text"
:
"已发布"
,
"inactive-text"
:
"未发布"
,
},
{
field
:
"priority"
,
label
:
"优先级"
,
type
:
"slider"
,
span
:
16
,
min
:
0
,
max
:
100
,
marks
:
{
0
:
"低"
,
50
:
"中"
,
100
:
"高"
,
},
},
{
field
:
"rating"
,
label
:
"评分"
,
type
:
"rate"
,
span
:
12
,
max
:
5
,
"allow-half"
:
true
,
"show-text"
:
true
,
texts
:
[
"极差"
,
"失望"
,
"一般"
,
"满意"
,
"惊喜"
],
},
{
field
:
"color"
,
label
:
"主题色"
,
type
:
"colorPicker"
,
span
:
12
,
"show-alpha"
:
true
,
},
{
field
:
"region"
,
label
:
"地区"
,
type
:
"cascader"
,
span
:
12
,
clearable
:
true
,
placeholder
:
"请选择地区"
,
options
:
[
{
value
:
"zhejiang"
,
label
:
"浙江省"
,
children
:
[
{
value
:
"hangzhou"
,
label
:
"杭州市"
},
{
value
:
"ningbo"
,
label
:
"宁波市"
},
],
},
{
value
:
"jiangsu"
,
label
:
"江苏省"
,
children
:
[
{
value
:
"nanjing"
,
label
:
"南京市"
},
{
value
:
"suzhou"
,
label
:
"苏州市"
},
],
},
],
},
{
field
:
"treeSelect"
,
label
:
"部门"
,
type
:
"treeSelect"
,
span
:
12
,
clearable
:
true
,
placeholder
:
"请选择部门"
,
data
:
[
{
id
:
"1"
,
label
:
"技术部"
,
children
:
[
{
id
:
"1-1"
,
label
:
"前端组"
},
{
id
:
"1-2"
,
label
:
"后端组"
},
],
},
{
id
:
"2"
,
label
:
"市场部"
,
children
:
[
{
id
:
"2-1"
,
label
:
"销售组"
},
{
id
:
"2-2"
,
label
:
"推广组"
},
],
},
],
props
:
{
label
:
"label"
,
value
:
"id"
,
children
:
"children"
,
},
},
{
field
:
"files"
,
label
:
"文件上传"
,
type
:
"upload"
,
span
:
24
,
action
:
"/api/upload"
,
"show-file-list"
:
true
,
"on-success"
:
(
response
,
file
,
fileList
)
=>
{
ElMessage
.
success
(
"上传成功"
);
},
"on-error"
:
(
error
,
file
,
fileList
)
=>
{
ElMessage
.
error
(
"上传失败"
);
},
},
]);
// ==================== 自定义样式示例 ====================
const
customFormData
=
ref
({});
const
customFormConfig
=
{
labelWidth
:
"100px"
,
labelPosition
:
"top"
,
gutter
:
30
,
size
:
"large"
,
showButtons
:
true
,
submitType
:
"success"
,
submitText
:
"保存修改"
,
submitIcon
:
"Check"
,
buttonSpan
:
24
,
};
const
customFormItems
=
computed
(()
=>
[
{
field
:
"productName"
,
label
:
"产品名称"
,
type
:
"input"
,
span
:
8
,
itemClass
:
"custom-form-item"
,
placeholder
:
"请输入产品名称"
,
prefixIcon
:
"Goods"
,
},
{
field
:
"productCode"
,
label
:
"产品编码"
,
type
:
"input"
,
span
:
8
,
placeholder
:
"请输入产品编码"
,
prefixIcon
:
"Tickets"
,
},
{
field
:
"productPrice"
,
label
:
"产品价格"
,
type
:
"inputNumber"
,
span
:
8
,
min
:
0
,
precision
:
2
,
"controls-position"
:
"right"
,
},
{
field
:
"productDesc"
,
label
:
"产品描述"
,
type
:
"textarea"
,
span
:
24
,
rows
:
5
,
placeholder
:
"请输入产品描述"
,
},
]);
// ==================== 动态表单示例 ====================
const
showDynamicFields
=
ref
(
true
);
const
dynamicFormData
=
ref
({});
const
dynamicFormConfig
=
{
labelWidth
:
"120px"
,
gutter
:
20
,
};
const
dynamicFormItems
=
computed
(()
=>
{
const
baseItems
=
[
{
field
:
"username"
,
label
:
"用户名"
,
type
:
"input"
,
span
:
12
,
required
:
true
,
},
{
field
:
"email"
,
label
:
"邮箱"
,
type
:
"input"
,
span
:
12
,
},
];
if
(
showDynamicFields
.
value
)
{
baseItems
.
push
(
{
field
:
"phone"
,
label
:
"手机号"
,
type
:
"input"
,
span
:
12
,
},
{
field
:
"address"
,
label
:
"地址"
,
type
:
"input"
,
span
:
12
,
},
);
}
return
baseItems
;
});
const
toggleDynamicFields
=
()
=>
{
showDynamicFields
.
value
=
!
showDynamicFields
.
value
;
};
// ==================== 带插槽的表单示例 ====================
const
slotFormData
=
ref
({
avatar
:
""
,
username
:
""
,
email
:
""
,
registerTime
:
""
,
status
:
1
,
content
:
""
,
});
const
slotFormConfig
=
{
labelWidth
:
"120px"
,
gutter
:
20
,
showButtons
:
true
,
};
const
slotFormItems
=
computed
(()
=>
[
{
field
:
"userInfo"
,
label
:
"用户信息"
,
type
:
"slot"
,
slotName
:
"userInfo"
,
span
:
24
,
},
{
field
:
"username"
,
label
:
"用户名"
,
type
:
"input"
,
span
:
12
,
},
{
field
:
"email"
,
label
:
"邮箱"
,
type
:
"input"
,
span
:
12
,
},
{
field
:
"registerTime"
,
label
:
"注册时间"
,
type
:
"date"
,
span
:
12
,
"value-format"
:
"YYYY-MM-DD"
,
},
{
field
:
"status"
,
label
:
"状态"
,
type
:
"select"
,
span
:
12
,
options
:
[
{
label
:
"正常"
,
value
:
1
},
{
label
:
"禁用"
,
value
:
0
},
],
},
{
field
:
"dataDisplay"
,
label
:
"数据展示"
,
type
:
"slot"
,
slotName
:
"dataDisplay"
,
span
:
24
,
},
{
field
:
"content"
,
label
:
"富文本内容"
,
type
:
"slot"
,
slotName
:
"richText"
,
span
:
24
,
},
]);
// ==================== 组件集成示例 ====================
const
selectorFormData
=
ref
({
matterType
:
""
,
projectType
:
""
,
investmentType
:
""
,
});
const
selectorFormConfig
=
{
labelWidth
:
"140px"
,
gutter
:
20
,
showButtons
:
false
,
};
const
selectorFormItems
=
computed
(()
=>
[
{
field
:
"matterType"
,
label
:
"事项类型"
,
type
:
"component"
,
component
:
CommonSelector
,
span
:
8
,
componentProps
:
{
dictName
:
"matterType"
,
placeholder
:
"请选择事项类型"
,
},
},
{
field
:
"projectType"
,
label
:
"项目类型"
,
type
:
"component"
,
component
:
CommonSelector
,
span
:
8
,
componentProps
:
{
dictName
:
"projectType"
,
placeholder
:
"请选择项目类型"
,
},
},
{
field
:
"investmentType"
,
label
:
"投资类型"
,
type
:
"component"
,
component
:
CommonSelector
,
span
:
8
,
componentProps
:
{
dictName
:
"investmentType"
,
placeholder
:
"请选择投资类型"
,
},
},
]);
// 财务表格数据
const
financialTableData
=
ref
({
indicatorList
:
[
{
name
:
"总收入"
,
level
:
0
,
serialNumber
:
1
},
{
name
:
" - 主营业务收入"
,
level
:
1
,
serialNumber
:
2
},
{
name
:
" - 其他业务收入"
,
level
:
1
,
serialNumber
:
3
},
{
name
:
"总支出"
,
level
:
0
,
serialNumber
:
4
},
{
name
:
" - 运营成本"
,
level
:
1
,
serialNumber
:
5
},
{
name
:
" - 管理费用"
,
level
:
1
,
serialNumber
:
6
},
],
dynamicTimeList
:
[
"2025及以前"
,
"2026"
,
"2026-小记"
,
"2027"
,
"2028"
,
"2029"
,
],
tableData
:
[],
});
// 动态表格数据
const
dynamicTableData
=
ref
([
{
projectName
:
"示例项目1"
,
investmentAmount
:
1000
,
investmentDate
:
"2025-01-01"
,
projectType
:
"tech"
,
description
:
"这是一个示例项目"
,
},
]);
const
dynamicTableColumns
=
computed
(()
=>
[
{
prop
:
"projectName"
,
label
:
"项目名称"
,
type
:
"input"
,
minWidth
:
180
,
headerGroup
:
"基本信息"
,
placeholder
:
"请输入项目名称"
,
},
{
prop
:
"investmentAmount"
,
label
:
"投资金额(万元)"
,
type
:
"number"
,
minWidth
:
160
,
headerGroup
:
"财务信息"
,
placeholder
:
"请输入投资金额"
,
precision
:
2
,
min
:
0
,
},
{
prop
:
"investmentDate"
,
label
:
"投资日期"
,
type
:
"date"
,
minWidth
:
160
,
headerGroup
:
"基本信息"
,
placeholder
:
"请选择日期"
,
format
:
"YYYY-MM-DD"
,
valueFormat
:
"YYYY-MM-DD"
,
},
{
prop
:
"projectType"
,
label
:
"项目类型"
,
type
:
"select"
,
minWidth
:
140
,
headerGroup
:
"基本信息"
,
placeholder
:
"请选择类型"
,
optionKey
:
"projectType"
,
},
{
prop
:
"description"
,
label
:
"项目描述"
,
type
:
"textarea"
,
minWidth
:
240
,
headerGroup
:
"基本信息"
,
placeholder
:
"请输入项目描述"
,
rows
:
2
,
},
]);
const
defaultRow
=
{
projectName
:
""
,
investmentAmount
:
0
,
investmentDate
:
""
,
projectType
:
""
,
description
:
""
,
};
const
selectOptions
=
ref
({
projectType
:
[
{
key
:
"tech"
,
name
:
"技术类"
},
{
key
:
"life"
,
name
:
"生活类"
},
{
key
:
"entertainment"
,
name
:
"娱乐类"
},
],
});
// 年度计划数据
const
annualPlanData
=
ref
([]);
const
annualPlanTimeList
=
ref
([
"2025及以前"
,
"2026"
,
"2026-小记"
,
"2027"
,
"2028"
,
"2029"
]);
const
toggleAnnualPlan
=
()
=>
{
showAnnualPlan
.
value
=
!
showAnnualPlan
.
value
;
if
(
showAnnualPlan
.
value
&&
annualPlanData
.
value
.
length
===
0
)
{
// 初始化年度计划数据
annualPlanData
.
value
=
Array
.
from
({
length
:
17
},
(
_
,
i
)
=>
({
id
:
i
+
1
,
name
:
`项目
${
i
+
1
}
`
,
...
annualPlanTimeList
.
value
.
reduce
((
acc
,
time
)
=>
{
acc
[
time
]
=
0
;
return
acc
;
},
{}),
}));
}
};
// ==================== 性能优化示例 ====================
// 生成大表单字段(用于演示性能优化)
const
loadLargeForm
=
async
()
=>
{
const
startTime
=
performance
.
now
();
isLoadingLargeForm
.
value
=
true
;
loadingProgress
.
value
=
0
;
try
{
// 使用分批加载,避免一次性渲染大量字段导致卡顿
const
batchSize
=
100
;
const
totalFields
=
1000
;
const
batches
=
Math
.
ceil
(
totalFields
/
batchSize
);
for
(
let
i
=
0
;
i
<
batches
;
i
++
)
{
await
new
Promise
(
resolve
=>
setTimeout
(
resolve
,
10
));
// 模拟异步加载
const
batch
=
Array
.
from
({
length
:
batchSize
},
(
_
,
j
)
=>
({
field
:
`field_
${
i
*
batchSize
+
j
}
`
,
label
:
`字段
${
i
*
batchSize
+
j
+
1
}
`
,
type
:
"input"
,
span
:
6
,
placeholder
:
`请输入字段
${
i
*
batchSize
+
j
+
1
}
`
,
}));
largeFormItems
.
value
.
push
(...
batch
);
loadingProgress
.
value
=
Math
.
round
(((
i
+
1
)
/
batches
)
*
100
);
}
const
endTime
=
performance
.
now
();
loadTime
.
value
=
Math
.
round
(
endTime
-
startTime
);
ElMessage
.
success
(
`大表单加载完成,共
${
totalFields
}
个字段,耗时
${
loadTime
.
value
}
ms`
);
}
catch
(
error
)
{
ElMessage
.
error
(
"加载失败"
);
}
finally
{
isLoadingLargeForm
.
value
=
false
;
}
};
// ==================== 事件处理 ====================
const
handleBasicSubmit
=
(
data
,
isEdit
)
=>
{
console
.
log
(
"基础表单提交:"
,
data
,
isEdit
);
ElMessage
.
success
(
`基础表单
${
isEdit
?
"更新"
:
"创建"
}
成功`
);
};
const
handleSubmitSuccess
=
(
response
,
data
,
isEdit
)
=>
{
ElMessage
.
success
(
`操作成功:
${
isEdit
?
"更新"
:
"创建"
}
`
);
console
.
log
(
"提交成功:"
,
response
,
data
);
};
const
handleSubmitError
=
(
error
,
data
,
isEdit
)
=>
{
ElMessage
.
error
(
`操作失败:
${
error
.
message
||
"未知错误"
}
`
);
console
.
error
(
"提交失败:"
,
error
,
data
);
};
const
handleSlotSubmit
=
(
data
)
=>
{
console
.
log
(
"插槽表单提交:"
,
data
);
ElMessage
.
success
(
"提交成功"
);
};
const
handleCreate
=
()
=>
{
basicFormData
.
value
=
{};
advancedFormData
.
value
=
{};
customFormData
.
value
=
{};
dynamicFormData
.
value
=
{};
slotFormData
.
value
=
{};
ElMessage
.
info
(
"已清空表单数据"
);
};
const
handleChangeAvatar
=
()
=>
{
ElMessage
.
info
(
"更换头像功能"
);
};
const
insertText
=
(
text
)
=>
{
slotFormData
.
value
.
content
+=
text
;
};
</
script
>
<
style
scoped
lang=
"less"
>
.super-form-example {
padding: 20px;
.example-card {
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
.card-title {
font-size: 18px;
font-weight: 600;
color: #303133;
}
}
.form-tabs {
:deep(.el-tabs__content) {
padding: 20px;
max-height: 70vh;
overflow-y: auto;
}
// 优化滚动条样式
:deep(.el-tabs__content)::-webkit-scrollbar {
width: 6px;
}
:deep(.el-tabs__content)::-webkit-scrollbar-thumb {
background-color: #dcdfe6;
border-radius: 3px;
}
:deep(.el-tabs__content)::-webkit-scrollbar-thumb:hover {
background-color: #c0c4cc;
}
}
}
.integration-container {
.integration-section {
margin-bottom: 30px;
padding: 20px;
background-color: #f5f7fa;
border-radius: 8px;
transition: all 0.3s ease;
&:hover {
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.section-title {
font-size: 16px;
font-weight: 600;
color: #303133;
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 2px solid #409eff;
}
}
}
.performance-container {
.performance-tips {
margin: 0;
padding-left: 20px;
li {
margin-bottom: 8px;
line-height: 1.6;
}
}
.performance-demo {
margin-top: 20px;
.demo-item {
padding: 20px;
background-color: #f5f7fa;
border-radius: 8px;
.demo-title {
font-size: 14px;
font-weight: 600;
color: #606266;
margin-bottom: 12px;
}
.form-stats {
display: flex;
gap: 12px;
margin-top: 16px;
}
}
}
}
.custom-user-info {
display: flex;
align-items: center;
gap: 20px;
padding: 10px;
border: 1px solid #dcdfe6;
border-radius: 4px;
}
.rich-text-editor {
width: 100%;
.editor-toolbar {
margin-top: 10px;
display: flex;
gap: 8px;
flex-wrap: wrap;
}
}
:deep(.custom-form-item) {
.el-form-item__label {
color: #409eff;
font-weight: 600;
}
}
}
// 全局样式优化
:deep(.el-form-item__label) {
font-weight: 500;
color: #303133;
}
:deep(.el-form-item) {
margin-bottom: 18px;
}
:deep(.el-input-number) {
width: 100%;
}
:deep(.el-select) {
width: 100%;
}
:deep(.el-cascader) {
width: 100%;
}
:deep(.el-tree-select) {
width: 100%;
}
:deep(.el-date-editor) {
width: 100%;
}
:deep(.el-time-picker) {
width: 100%;
}
:deep(.el-autocomplete) {
width: 100%;
}
// 优化表格样式
:deep(.el-table) {
font-size: 13px;
}
:deep(.el-table th) {
background-color: #f5f7fa;
color: #303133;
font-weight: 600;
}
:deep(.el-table--border) {
border-color: #ebeef5;
}
// 优化按钮样式
:deep(.el-button) {
transition: all 0.3s ease;
&:hover {
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.2);
}
}
// 优化卡片样式
:deep(.el-card) {
border-radius: 8px;
border: none;
}
:deep(.el-card__header) {
border-bottom: 1px solid #ebeef5;
padding: 16px 20px;
}
// 优化标签页样式
:deep(.el-tabs--border-card) {
border-radius: 8px;
box-shadow: none;
}
:deep(.el-tabs__item) {
font-weight: 500;
&.is-active {
color: #409eff;
font-weight: 600;
}
}
</
style
>
src/views/everydayPage/annualAdd.vue
View file @
add12e41
...
...
@@ -1364,13 +1364,6 @@ const changeProject = (val) => {
callback
:
(
data
)
=>
{
loading
.
value
=
false
;
formData
.
projectName
=
data
.
projectName
||
""
;
formData
.
sbdw
=
data
.
sbdw
||
""
;
formData
.
xmgsmc
=
data
.
xmgsmc
||
""
;
formData
.
xmkgsjyj
=
data
.
xmkgsjyj
||
""
;
formData
.
xmjgsjyj
=
data
.
xmjgsjyj
||
""
;
formData
.
xmjd
=
data
.
xmjd
||
""
;
formData
.
yynxn
=
data
.
yynxn
||
""
;
formData
.
xmjsqy
=
data
.
xmjsqy
||
""
;
},
error
:
()
=>
{
loading
.
value
=
false
;
...
...
src/views/everydayPage/everydayAdd.vue
View file @
add12e41
...
...
@@ -97,14 +97,8 @@ const sourceId = ref(route.query.sourceId || "");
// ========== 表单核心数据(完全匹配RcXxbs表结构,无冗余字段) ==========
const
formData
=
reactive
({
wjmc
:
""
,
// 文件名称
nd
:
""
,
// 编制年度(YYYY格式,匹配表的DATE类型)
bzr
:
""
,
// 编制人
bzrbm
:
""
,
// 编制人部门
fjsc
:
[],
// 附件上传(JSON类型,匹配表的JSON)
projectId
:
""
,
// 所属项目ID(隐藏字段,提交传值)
sourceId
:
""
,
// 所属主表id(隐藏字段,提交传值)
del
:
0
,
// 删除标识,默认0(正常)
creator
:
proxy
.
$store
?.
getters
?.
userId
||
""
,
// 创建人ID(从全局状态取,无则空)
});
...
...
src/views/everydayPage/informationConstructionAdd.vue
View file @
add12e41
...
...
@@ -109,6 +109,18 @@ const backClick = () => {
// 保存/提交表单(新增/编辑统一处理)
const
saveClick
=
()
=>
{
if
(
!
formData
.
issueTitle
)
{
ElMessage
.
warning
(
"请填写问题标题"
);
return
;
}
if
(
!
formData
.
issueCategory
)
{
ElMessage
.
warning
(
"请选择问题类别"
);
return
;
}
if
(
!
formData
.
issueDescription
)
{
ElMessage
.
warning
(
"请填写问题描述"
);
return
;
}
loading
.
value
=
true
;
const
url
=
rcCgqyglId
.
value
?
"/api/project/updateXxhjs"
...
...
src/views/everydayPage/recordAdd.vue
View file @
add12e41
...
...
@@ -128,17 +128,7 @@ const activeCollapse = ref(["档案基本信息"]);
// 表单核心数据:**完全匹配后端RcTzdagl模型字段**,删除所有无用字段
const
formData
=
reactive
({
archiveCategory
:
""
,
// 档案分类 ★非空★
archiveName
:
""
,
// 档案名称 ★非空★
archiveNumber
:
""
,
// 档案编号
archiveDate
:
""
,
// 档案日期
remark
:
""
,
// 备注
fjsc
:
[],
// 附件上传(JSON数组,绑定上传组件)
projectId
:
""
,
// 所属项目ID
del
:
0
,
// 删除标记,默认0正常(后端默认值)
creator
:
userStore
.
userId
||
""
,
// 创建人ID(从用户仓库取,后端需要)
createdAt
:
""
,
// 创建时间(后端自动生成,前端仅回显)
updatedAt
:
""
,
// 更新时间(后端自动生成,前端仅回显)
});
// 页面核心状态:保留必要的加载/预览/编辑ID
...
...
src/views/everydayPage/shareAdd.vue
View file @
add12e41
...
...
@@ -546,38 +546,36 @@ const activeCollapse = ref([
// 表单数据 - 数值字段初始化为数字类型(0)
const
formData
=
reactive
({
projectName
:
""
,
qc
:
""
,
jc
:
""
,
nbtzglzt
:
""
,
xmscjd
:
""
,
gqjg
:
""
,
xmzbjze
:
0
,
gszbjyczze
:
0
,
gsdqycze
:
0
,
gsdqyjcze
:
0
,
gsdqycwcje
:
0
,
gsdqsycze
:
0
,
cgbczqk
:
""
,
wfqyhttkyd
:
""
,
qyhqjz
:
""
,
qyhqyyd
:
""
,
dbqk
:
""
,
lrfp
:
""
,
sfddlrfptj
:
""
,
ljhqfh
:
0
,
ytrzj
:
0
,
ljtrzj
:
0
,
sxtrzj
:
0
,
jt
:
0
,
zx
:
0
,
lxr
:
""
,
lxfs
:
""
,
bz
:
""
,
projectId
:
""
,
del
:
0
,
// del字段保留0默认值(删除标记,0为正常)
createdAt
:
""
,
updatedAt
:
""
,
// projectName: "",
// qc: "",
// jc: "",
// nbtzglzt: "",
// xmscjd: "",
// gqjg: "",
// xmzbjze: 0,
// gszbjyczze: 0,
// gsdqycze: 0,
// gsdqyjcze: 0,
// gsdqycwcje: 0,
// gsdqsycze: 0,
// cgbczqk: "",
// wfqyhttkyd: "",
// qyhqjz: "",
// qyhqyyd: "",
// dbqk: "",
// lrfp: "",
// sfddlrfptj: "",
// ljhqfh: 0,
// ytrzj: 0,
// ljtrzj: 0,
// sxtrzj: 0,
// jt: 0,
// zx: 0,
// lxr: "",
// lxfs: "",
// bz: "",
// projectId: "",
// del: 0, // del字段保留0默认值(删除标记,0为正常)
});
let
options
=
ref
();
...
...
src/views/everydayPage/systemAdd.vue
View file @
add12e41
...
...
@@ -97,10 +97,6 @@ const options = ref({}); // 下拉/单选全局配置项(文件层级/类别
// ========== 表单核心数据(删除无用数组,仅保留页面绑定的字段) ==========
const
formData
=
reactive
({
wjmc
:
""
,
// 文件名称
bbsj
:
""
,
// 颁布时间
wjcj
:
""
,
// 文件层级
wjlb
:
""
,
// 文件类别
fjsc
:
[],
// 附件上传
});
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment