当我们项目中遇到比较复杂的表单提交时,业务模块超过一屏显示的时候,又需要一次性提交保存,我们该如何提高用户的体验呢?
让用户实时可以看到未填写项、需要填写的模块、已经填写完成的模块,这样可以减少用户焦虑,以提高功能的实用性
1、vue 2
2、element-ui
1、可拖拽
2、模块导航
3、模块必填校验
4、滚动锚点定位
5、提交提醒未填项
JS提供了拖拽事件,我们可以用此功能来实现拖拽。其中什么元素或者是哪里发生了拖拽事件是最关键的。有些事件是在被拖动的元素上触发,有些事件是在放置目标上触发的。拖动某元素时候,触发的事件有:dragstart事件、drag事件和dragend事件。
需要拖拽的元素增加dragstart 、dragend 事件,用来获取拖拽开始 和 结束的坐标点
JS 部分
// 拖拽
dragstart(e) {
this.distX = e.clientX
this.distY = e.clientY
},
dragend(e) {
const left = this.distX - e.clientX
const top = e.clientY - this.distY
this.offsetRight += left
this.offsetTop += top
},
保存开始拖拽的X\Y坐标点 和 结束的坐标点来完成容器的拖拽偏移
模块导航
模块导航是根据菜单点击滚动到指定位置和滚动到指定位置时,导航选中到指定菜单项
JS部分
props: {
formRules: {
type: Object
},
value: {
require: true
}
}
组件传递各模块id、name 、 必填项教研
Object.keys(this.formRules).forEach((item, index) => {
this.$set(this.formRules[item], 'requiredAll', false)
const itemEvent = document.querySelector('#' + item)
console.log(itemEvent)
if (itemEvent) {
this.itemEvents.push({
height: itemEvent.offsetHeight,
offsetTop: itemEvent.offsetTop
})
}
})
保存各模块的高度和顶部的距离
模块必填校验
模块必填校验,通过组件传递的字段,分别进行判断,如果有required 必填标识,即进行校验
JS部分
// 校验
requiredAllHandle() {
Object.keys(this.formRules).forEach((item) => {
let list = this.formRules[item]
let isRequired = true
Object.keys(list.rules || {}).forEach((itemc) => {
if (this.computedIsRequired(list.rules[itemc])) {
if (isNull(this.value[itemc])) {
isRequired = false
}
}
})
this.$set(list, 'requiredAll', isRequired)
})
},
computedIsRequired(rules) {
let isRequired = false
if (!Array.isArray(rules)) {
rules = [rules]
}
if (rules && rules.length) {
rules.every(function (rule) {
if (rule.required) {
isRequired = true
return false
}
return true
})
}
return isRequired
},
computedIsRequired 方法用于监听每个字段的值变化
滚动锚点定位
滚动锚点定位通过监听 scroll 事件,获取滚动距离计算当前滚动到指定模块,标记指定模块
JS 部分
window.addEventListener('scroll', this.handleScroll)
handleScroll() {
const top = document.body.scrollTop || document.documentElement.scrollTop
const { itemEvents } = this
const lastIndex = itemEvents.length - 1
const lastItem = itemEvents[lastIndex]
const lastCount = lastItem.offsetTop + lastItem.height
if (this.activeIndex >= 0) {
if (lastCount < top) {
return (this.activeIndex = lastIndex)
}
const firstScroll = itemEvents[0].offsetTop
const itemScroll = itemEvents[this.activeIndex]
const next = itemScroll.offsetTop + itemScroll.height
const prev = this.activeIndex > 0 ? itemEvents[this.activeIndex - 1].offsetTop : 0
if (next < top) {
this.activeIndex += 1
} else if (prev >= top) {
this.activeIndex -= 1
return
}
if (top < firstScroll - 200) {
this.activeIndex = -1
}
} else {
let firstScroll = itemEvents[0].offsetTop
if (top >= firstScroll) {
this.activeIndex = 0
}
}
},
提交提醒未填项
// 校验
submit() {
this.isSubmit = true
},
this.$refs.FloatBar.submit()
通过ref 调用子组件submit 方法
完成大体功能之后,完善并优化各功能之间联动。
有时候模块比较多的情况下会遮挡页面
随即我们加了一个收缩功能
到这里该组件就封装完成了。
下面附带案例查看
案例
HTML部分
-
立即创建
重置
-
立即创建
重置
-
立即创建
重置
-
立即创建
重置
提交
JS部分
formRules: {
formRules1: {
name: '测试1',
rules: {
name: [
{ required: true, message: '请输入活动名称', trigger: 'blur' },
{ min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }
],
}
},
formRules2: {
name: '测试2',
rules: {
name: [
{ required: true, message: '请输入活动名称', trigger: 'blur' },
{ min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }
],
region: [
{ required: true, message: '请选择活动区域', trigger: 'change' }
],
date1: [
{ type: 'date', required: true, message: '请选择日期', trigger: 'change' }
],
date2: [
{ type: 'date', required: true, message: '请选择时间', trigger: 'change' }
],
type: [
{ type: 'array', required: true, message: '请至少选择一个活动性质', trigger: 'change' }
],
resource: [
{ required: true, message: '请选择活动资源', trigger: 'change' }
],
desc: [
{ required: true, message: '请填写活动形式', trigger: 'blur' }
]
}
},
formRules3: {
name: '测试3',
rules: {
name: [
{ required: true, message: '请输入活动名称', trigger: 'blur' },
{ min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }
],
region: [
{ required: true, message: '请选择活动区域', trigger: 'change' }
],
date1: [
{ type: 'date', required: true, message: '请选择日期', trigger: 'change' }
],
date2: [
{ type: 'date', required: true, message: '请选择时间', trigger: 'change' }
],
type: [
{ type: 'array', required: true, message: '请至少选择一个活动性质', trigger: 'change' }
],
resource: [
{ required: true, message: '请选择活动资源', trigger: 'change' }
],
desc: [
{ required: true, message: '请填写活动形式', trigger: 'blur' }
]
}
},
formRules4: {
name: '测试4',
rules: {
name: [
{ required: true, message: '请输入活动名称', trigger: 'blur' },
{ min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }
],
region: [
{ required: true, message: '请选择活动区域', trigger: 'change' }
],
date1: [
{ type: 'date', required: true, message: '请选择日期', trigger: 'change' }
],
date2: [
{ type: 'date', required: true, message: '请选择时间', trigger: 'change' }
],
type: [
{ type: 'array', required: true, message: '请至少选择一个活动性质', trigger: 'change' }
],
resource: [
{ required: true, message: '请选择活动资源', trigger: 'change' }
],
desc: [
{ required: true, message: '请填写活动形式', trigger: 'blur' }
]
}
}
},
ruleForm: {
name: '',
region: '',
date1: '',
date2: '',
delivery: false,
type: [],
resource: '',
desc: ''
},
submitForm10(){
this.$refs.FloatBar.submit()
},
最后附上 源码地址 欢迎交流
码云地址