feat(component): 新增CuteFormProDialog组件并重构搜索表单

- 新增CuteFormProDialog表单弹窗组件,支持多种输入类型
- 重命名CuteSearchFormPro为CuteSearchProForm并优化组件结构
- 更新组件演示页面,添加表单对话框Pro功能演示
- 为新组件添加完整的属性定义和事件处理机制
- 优化表单验证规则和数据绑定逻辑
- 添加日期和时间范围选择器支持
This commit is contained in:
2026-02-02 18:22:53 +08:00
parent 3ff0a08cfb
commit c3e91330b6
4 changed files with 299 additions and 25 deletions
@@ -0,0 +1,262 @@
<!--
* 表单弹窗Pro
* @author odboy
* @email tianjun@odboy.cn
* @created 2026-02-01
-->
<template>
<el-dialog
:title="title"
:visible.sync="visible"
:width="width"
:fullscreen="fullscreen"
:before-close="beforeClose"
:modal="false"
>
<div class="container-form">
<el-form ref="form" :model="model" :rule="dynamicRules" :inline="false" size="small" label-position="left" label-width="100px">
<el-form-item v-for="item in schema" :key="item.name" :prop="item.name" :label="item.title" :required="item.required === true">
<!-- 输入框 -->
<el-input
v-if="item.type === 'input'"
v-model="model[item.name]"
:placeholder="`请输入${item.title}`"
clearable
style="width: 100%"
/>
<!-- 选择框 -->
<el-select
v-if="item.type === 'select'"
v-model="model[item.name]"
clearable
:placeholder="`请选择${item.title}`"
style="width: 100%"
>
<el-option
v-for="option in (item.dataSource || [])"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
<!-- 日期选择 -->
<el-date-picker
v-if="item.type === 'date'"
v-model="model[item.name]"
type="daterange"
align="right"
unlink-panels
clearable
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
:picker-options="pickerOptions"
style="width: 100%"
/>
<!-- 日期时间选择 -->
<el-date-picker
v-if="item.type === 'datetime'"
v-model="model[item.name]"
type="datetimerange"
align="right"
clearable
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
format="yyyy-MM-dd HH:mm:ss"
value-format="yyyy-MM-dd HH:mm:ss"
:default-time="['00:00:00', '23:59:59']"
:picker-options="pickerOptions"
style="width: 100%"
/>
</el-form-item>
</el-form>
</div>
<el-divider />
<div class="dialog-footer">
<el-button type="danger" @click="hidden">取 消</el-button>
<!--<el-button @click="resetForm('form')">重 置</el-button>-->
<el-button v-prevent-re-click type="primary" @click="submitForm('form')">提 交</el-button>
</div>
</el-dialog>
</template>
<script>
export default {
name: 'CuteFormProDialog',
props: {
/**
* 弹窗标题
*/
title: {
type: String,
required: true,
default: '默认标题'
},
/**
* 弹窗宽度
*/
width: {
type: String,
required: false,
default: '40%'
},
/**
* 是否全屏
*/
fullscreen: {
type: Boolean,
required: false,
default: false
},
/**
* 表单定义
*/
schema: {
type: Array,
required: true,
default: function() {
return []
}
},
/**
* 绑定值
*/
model: {
type: Object,
required: true
}
},
data() {
return {
visible: false,
rules: {},
pickerOptions: {
shortcuts: [{
text: '最近一周',
onClick(picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
picker.$emit('pick', [start, end])
}
}, {
text: '最近一个月',
onClick(picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
picker.$emit('pick', [start, end])
}
}, {
text: '最近三个月',
onClick(picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
picker.$emit('pick', [start, end])
}
}]
}
}
},
computed: {
dynamicRules() {
const schema = this.schema
const formRules = {}
let trigger = 'blur'
for (const element of schema) {
if (element.type === 'input') {
trigger = 'blur'
} else {
trigger = 'change'
}
formRules[element.name] = [
{ required: element.required === true, message: '请输入' + element.title, trigger: trigger }
]
}
return formRules
}
},
methods: {
show() {
this.visible = true
},
hidden() {
this.resetForm('form')
this.visible = false
},
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.$emit('submit', this.model)
} else {
return false
}
})
},
resetForm(formName) {
// 调用默认的重置
this.$refs[formName].resetFields()
// 根据schema配置重置顽固选项
const schema = this.schema
const model = this.model
for (const element of schema) {
if (element.type === 'input' || element.type === 'select') {
model[element.name] = null
continue
}
if (element.type === 'date' || element.type === 'datetime') {
model[element.name] = []
}
}
this.model = { ...model }
},
beforeClose() {
this.hidden()
}
}
}
</script>
<style lang="scss" scoped>
.container-form {
overflow-y: scroll; /* 启用滚动 */
scrollbar-width: none; /* Firefox */
padding-right: 20px;
-ms-overflow-style: none; /* Internet Explorer 10+ */
max-height: calc(100vh - 200px); /* 调整高度以适应页头和底部按钮 */
}
.container-form::-webkit-scrollbar {
display: none; /* Chrome, Safari, Opera*/
}
.dialog-content-wrapper {
height: 100%;
display: flex;
flex-direction: column;
}
.dialog-footer {
text-align: right;
border-top: 1px solid #ebeef5;
padding: 16px 0 0 0;
background-color: white;
margin-top: auto;
}
::v-deep(.el-dialog__body) {
padding: 20px 20px !important;
height: 90%;
display: flex;
flex-direction: column;
}
::v-deep(.el-divider--horizontal) {
margin: 0 !important;
}
</style>
@@ -14,7 +14,6 @@
:disabled="disabled" :disabled="disabled"
size="small" size="small"
> >
<!-- <el-form-item v-for="item in schema" :key="item.name" :prop="item.name" :label="item.title" :required="item.required === true">-->
<el-form-item v-for="item in schema" :key="item.name" :prop="item.name" :label="item.title"> <el-form-item v-for="item in schema" :key="item.name" :prop="item.name" :label="item.title">
<!-- 输入框 --> <!-- 输入框 -->
<el-input <el-input
@@ -81,9 +80,12 @@
<script> <script>
export default { export default {
name: 'CuteSearchFormPro', name: 'CuteSearchProForm',
components: {}, components: {},
props: { props: {
/**
* 表单定义
*/
schema: { schema: {
type: Array, type: Array,
required: true, required: true,
@@ -91,10 +93,16 @@ export default {
return [] return []
} }
}, },
/**
* 绑定值
*/
model: { model: {
type: Object, type: Object,
required: true required: true
}, },
/**
* 是否禁用
*/
disabled: { disabled: {
type: Boolean, type: Boolean,
required: false, required: false,
@@ -103,7 +111,6 @@ export default {
}, },
data() { data() {
return { return {
// formRules: {},
pickerOptions: { pickerOptions: {
shortcuts: [{ shortcuts: [{
text: '最近一周', text: '最近一周',
@@ -133,22 +140,6 @@ export default {
} }
} }
}, },
// mounted() {
// const schema = this.schema
// const formRules = this.formRules
// let trigger = 'blur'
// for (const element of schema) {
// if (element.type === 'input') {
// trigger = 'blur'
// } else {
// trigger = 'change'
// }
// formRules[element.name] = [
// { required: element.required === true, message: '请输入' + element.title, trigger: trigger }
// ]
// }
// this.formRules = formRules
// },
methods: { methods: {
onFormSubmit(formName) { onFormSubmit(formName) {
const that = this const that = this
@@ -11,8 +11,8 @@
<el-button type="primary">查询后置按钮</el-button> <el-button type="primary">查询后置按钮</el-button>
</template> </template>
</cute-search-form> </cute-search-form>
<cute-section-title title="高级搜索Pro版 CuteSearchFormPro" /> <cute-section-title title="高级搜索Pro版 CuteSearchProForm" />
<cute-search-form-pro :model="searchFormProValues" :schema="searchFormProSchema" /> <cute-search-pro-form :model="searchFormProValues" :schema="searchFormProSchema" />
<cute-section-title title="横向滚动 CuteHorizontalScrollBar" /> <cute-section-title title="横向滚动 CuteHorizontalScrollBar" />
<cute-horizontal-scroll-bar> <cute-horizontal-scroll-bar>
<div style="width: 3000px;min-width: 3000px">模拟超长内容</div> <div style="width: 3000px;min-width: 3000px">模拟超长内容</div>
@@ -75,12 +75,12 @@ import CuteSectionTitle from '@/views/components/dev/CuteSectionTitle.vue'
import CuteButton from '@/views/components/dev/CuteButton.vue' import CuteButton from '@/views/components/dev/CuteButton.vue'
import CuteTransfer from '@/views/components/dev/CuteTransfer.vue' import CuteTransfer from '@/views/components/dev/CuteTransfer.vue'
import CuteTextAreaInput from '@/views/components/dev/CuteTextAreaInput.vue' import CuteTextAreaInput from '@/views/components/dev/CuteTextAreaInput.vue'
import CuteSearchFormPro from '@/views/components/dev/CuteSearchFormPro.vue' import CuteSearchProForm from '@/views/components/dev/CuteSearchProForm.vue'
export default { export default {
name: 'CuteOtherDemo', name: 'CuteOtherDemo',
components: { components: {
CuteSearchFormPro, CuteSearchProForm,
CuteTextAreaInput, CuteTextAreaInput,
CuteTransfer, CuteTransfer,
CuteButton, CuteButton,
@@ -20,6 +20,7 @@
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="onDialogClick">演示:表单对话框</el-button> <el-button type="primary" @click="onDialogClick">演示:表单对话框</el-button>
<el-button type="primary" @click="onDialogProClick">演示:表单对话框Pro</el-button>
<el-button type="primary" @click="onDrawerClick">演示:表单抽屉</el-button> <el-button type="primary" @click="onDrawerClick">演示:表单抽屉</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
@@ -103,6 +104,13 @@
<cute-edit-table v-model="editTableDataSource" primary-key="id" :schema="editTableSchema" /> <cute-edit-table v-model="editTableDataSource" primary-key="id" :schema="editTableSchema" />
</el-form-item> </el-form-item>
</cute-form-drawer> </cute-form-drawer>
<cute-form-pro-dialog
ref="dialogProForm"
:model="createFormProModel"
:schema="createFormProSchema"
title="演示:表单对话框Pro"
@submit="onDialogFormSubmit"
/>
</div> </div>
</template> </template>
@@ -115,10 +123,11 @@ import { FormatRowDateTimeStr } from '@/utils/KitUtil'
import CuteButton from '@/views/components/dev/CuteButton.vue' import CuteButton from '@/views/components/dev/CuteButton.vue'
import CuteTransfer from '@/views/components/dev/CuteTransfer.vue' import CuteTransfer from '@/views/components/dev/CuteTransfer.vue'
import CuteEditTable from '@/views/components/dev/CuteEditTable.vue' import CuteEditTable from '@/views/components/dev/CuteEditTable.vue'
import CuteFormProDialog from '@/views/components/dev/CuteFormProDialog.vue'
export default { export default {
name: 'CuteSimpleTableDemo', name: 'CuteSimpleTableDemo',
components: { CuteEditTable, CuteTransfer, CuteButton, CuteFormDrawer, CuteFormDialog, CuteSimpleTable }, components: { CuteFormProDialog, CuteEditTable, CuteTransfer, CuteButton, CuteFormDrawer, CuteFormDialog, CuteSimpleTable },
data() { data() {
return { return {
modeDataSource: [ modeDataSource: [
@@ -190,6 +199,7 @@ export default {
total: 0 total: 0
} }
}, },
// CuteFormDialog
createFormModel: { createFormModel: {
name: '', name: '',
region: '', region: '',
@@ -203,7 +213,15 @@ export default {
region: [ region: [
{ required: true, message: '请选择活动区域', trigger: 'change' } { required: true, message: '请选择活动区域', trigger: 'change' }
] ]
} },
// CuteFormProDialog
createFormProModel: {},
createFormProSchema: [
{ name: 'name', title: '姓名', type: 'input', required: true },
{ name: 'sex', title: '性别', type: 'select', required: true, dataSource: [{ label: '男', value: 'nn' }, { label: '女', value: 'mm' }] },
{ name: 'createDate', title: '创建日期', type: 'date', required: false },
{ name: 'createTime', title: '创建时间', type: 'datetime', required: false }
]
} }
}, },
mounted() { mounted() {
@@ -234,6 +252,9 @@ export default {
onDialogClick() { onDialogClick() {
this.$refs.dialogForm.show() this.$refs.dialogForm.show()
}, },
onDialogProClick() {
this.$refs.dialogProForm.show()
},
onDialogFormSubmit(values) { onDialogFormSubmit(values) {
console.log('onDialogFormSubmit', values) console.log('onDialogFormSubmit', values)
}, },