表单快照
在日常业务中,表单是我们经常打交道的一种场景;尤其在一些复杂的收集数据的业务系统中,尤为繁多;此时,开发的调试、和测试人员的工作变得繁重起来,由此想到是否可以将这种工作变得不那么繁琐;
需要实现的功能
1、在第一次填充数据的时候将其保存;(fill按钮)
2、在需要调用的时候将其取出赋值到对应表单上;(snapshot按钮)
3、确保其不影响功能。(拖拽)
代码
// 提示可以引用自己项目库中的api
import { message } from 'antd';
import { deepReadMoment, dealMomentFormData } from './utils';
const { NODE_ENV } = process.env;
export default class FormSnapshot {
constructor({ component, form }) {
// 此处可以根据自己设定的环境变量做选择
if (NODE_ENV !== 'development') {
return;
}
this.component = component;
this.form = form;
this.mask = document.createElement('div');
this.createButton();
document.body.appendChild(this.mask);
this.setMask();
this.ondragstartX = 0;
this.ondragstartY = 0;
// this.dom.style.left = `${localStorage.getItem(`${prefix}left`)}px`;
// this.dom.style.top = `${localStorage.getItem(`${prefix}top`)}px`;
this.mask.addEventListener('dragstart', this.dragstart.bind(this));
this.mask.addEventListener('dragend', this.dragend.bind(this));
}
// 创建button
createButton() {
const fillButton = document.createElement('div');
fillButton.style.display = 'flex';
fillButton.style.width = '60px';
fillButton.style.height = '30px';
fillButton.style.background = 'green';
fillButton.style.color = 'white';
fillButton.style.justifyContent = 'center';
fillButton.style.lineHeight = '30px';
fillButton.innerHTML = 'fill';
fillButton.addEventListener('click', this.saveFormData.bind(this));
const snapshotButton = document.createElement('div');
snapshotButton.style.display = 'flex';
snapshotButton.style.width = '70px';
snapshotButton.style.height = '30px';
snapshotButton.style.background = 'green';
snapshotButton.style.color = 'white';
snapshotButton.style.justifyContent = 'center';
snapshotButton.style.lineHeight = '30px';
snapshotButton.innerHTML = 'snapshot';
snapshotButton.addEventListener('click',this.snapshotFormData.bind(this));
this.mask.appendChild(fillButton);
this.mask.appendChild(snapshotButton);
}
setMask() {
this.mask.setAttribute('draggable', true);
this.mask.style.position = 'fixed';
this.mask.style.zIndex = 9999;
this.mask.style.width = '200px';
this.mask.style.height = '50px';
this.mask.style.display = 'flex';
this.mask.style.alignItems = 'center';
this.mask.style.justifyContent = 'space-around';
this.mask.style.border = '1px solid green';
this.mask.style.borderRadius = '10px';
this.mask.style.top = '80px';
this.mask.style.right = '20px';
this.mask.ondragover = (event) => event.preventDefault();
}
// 保存表单数据
saveFormData() {
if (!this.component) {
message.error('请传入组件实例');
return;
}
if (!this.form) {
message.error('当前组件中没有form表单');
return;
}
let formData = this.form.getFieldsValue();
if (!formData || JSON.stringify(formData) === '{}') {
message.error('请先填写表单数据');
return;
}
formData = dealMomentFormData(formData);
localStorage.setItem(
`${this.component.constructor.name}formData`,
JSON.stringify(formData),
);
}
// 生成快照 获取数据
snapshotFormData() {
if (!this.component) {
message.error('请传入组件实例');
return;
}
if (!this.form) {
message.error('当前组件中没有form表单');
return;
}
let formData = localStorage.getItem(
`${this.component.constructor.name}formData`,
);
if (!formData) {
message.error('当前组件还没有存储过数据');
return;
}
formData = deepReadMoment(JSON.parse(formData));
this.form.setFieldsValue({
...formData,
});
}
dragstart(ev) {
const event = ev;
document.body.appendChild(this.mask);
this.ondragstartX = event.x;
this.ondragstartY = event.y;
event.target.style.opacity = 0.328;
}
dragend(ev) {
const event = ev;
const { target } = event;
const offsetX = event.x - this.ondragstartX;
const offsetY = event.y - this.ondragstartY;
let left = target.offsetLeft + offsetX;
let top = target.offsetTop + offsetY;
const width = target.offsetWidth;
const bodyWidth = document.body.offsetWidth;
if (left + width > bodyWidth) {
left = bodyWidth - width;
}
left = left < 0 ? 0 : left;
const height = target.offsetHeight;
const bodyHeight = document.body.offsetHeight;
if (top + height > bodyHeight) {
top = bodyHeight - height;
}
top = top < 0 ? 0 : top;
// 此处可以存储上次拖拽位置
// localStorage.setItem(`${prefix}left`, left);
// localStorage.setItem(`${prefix}top`, top);
target.style.left = `${left}px`;
target.style.top = `${top}px`;
event.target.style.opacity = 1;
// document.body.removeChild(this.mask);
}
leavePage() {
if (this.mask) {
// 卸载的时候删除引用 避免内存泄漏
document.body.removeChild(this.mask);
this.mask = null;
}
}
}
// window.FormSnapshot = FormSnapshot;
我们是以localStorage来实现的、所以在其中需要单独处理时间不能直接存储的问题 且无法直接json.stringify,以moment为例
import moment from 'moment';
// 在存储的时候对moment数据转化
export const dealMomentFormData = (formData) => {
//
if (typeof formData !== 'object') {
return formData;
}
//
if (formData instanceof moment) {
return {
value: formData.unix() * 1000,
type: 'moment',
};
}
// 数组或对象
const result = formData instanceof Array ? [] : {};
for (const key in formData) {
result[key] = dealMomentFormData(formData[key]);
}
return result;
};
// 在读取的时候如果type为moment需要单独处理
export const deepReadMoment = (formData) => {
if (JSON.stringify(formData) === '{}') {
return null;
}
if (typeof formData !== 'object' || formData instanceof moment) {
return formData;
}
if (formData.type === 'moment') {
return moment(formData.value);
}
const result = formData instanceof Array ? [] : {};
for (const key in formData) {
result[key] = deepReadMoment(formData[key]);
}
return result;
};
此处是以class的name名字存储的,所以此方法不支持传入函数组件;
使用
import FormSnapshot from '@/formSnapshot/index.js';
constructor(props) {
super(props);
this.formSnapshot = null; // 初始化
}
componentDidMount() {
// 此处的form 如果是ant4 就用对应formRef.current 此工具方法可以根据自己的项目业务再次封装,这里只是提供一个思路
this.formSnapshot = new FormSnapshot({ component: this, form: this.props.form });
}
componentWillUnmount() {
this.formSnapshot?.leavePage();
}