防止长表单数据丢失方案,缓存表单填写内容,放置卡退或误操作返回。
一、存储方案(多端兼容)
1. 使用 UniApp 的数据存储 API
javascript
复制代码
// utils/storage.js
export class FormDraftManager {
constructor(formKey = 'formDraft') {
this.formKey = formKey;
}
// 保存草稿
saveDraft(formData) {
const draft = {
data: formData,
timestamp: Date.now(),
formVersion: '1.0'
};
try {
uni.setStorageSync(this.formKey, draft);
return true;
} catch (e) {
console.error('保存草稿失败:', e);
return false;
}
}
// 获取草稿
getDraft() {
try {
return uni.getStorageSync(this.formKey);
} catch (e) {
console.error('读取草稿失败:', e);
return null;
}
}
// 清除草稿
clearDraft() {
try {
uni.removeStorageSync(this.formKey);
return true;
} catch (e) {
console.error('清除草稿失败:', e);
return false;
}
}
// 检查草稿是否有效(例如24小时内)
isDraftValid(draft, maxAge = 24 * 60 * 60 * 1000) {
if (!draft || !draft.timestamp) return false;
return (Date.now() - draft.timestamp) < maxAge;
}
}
二、恢复确认弹窗实现
1. 使用 UniApp 的模态框 API
javascript
复制代码
// pages/your-form-page.vue
<script>
import { FormDraftManager } from '@/utils/storage.js';
export default {
data() {
return {
formData: {
name: '',
email: '',
phone: '',
// ...其他表单字段
},
draftManager: new FormDraftManager(),
hasRecovered: false
}
},
onLoad() {
this.checkAndRecoverDraft();
},
onUnload() {
// 页面卸载时自动保存(可选)
this.autoSaveDraft();
},
methods: {
// 检查并恢复草稿
async checkAndRecoverDraft() {
const draft = this.draftManager.getDraft();
if (this.draftManager.isDraftValid(draft)) {
// 显示恢复确认弹窗
this.showRecoveryConfirm(draft);
}
},
// 显示恢复确认弹窗
showRecoveryConfirm(draft) {
const formattedTime = this.formatTime(draft.timestamp);
uni.showModal({
title: '恢复填写进度?',
content: `检测到您上次在 ${formattedTime} 有未提交的表单内容,是否恢复?`,
confirmText: '恢复数据',
cancelText: '重新开始',
confirmColor: '#007AFF',
success: (res) => {
if (res.confirm) {
this.restoreFormData(draft.data);
} else {
this.clearDraft();
}
}
});
},
// 恢复表单数据
restoreFormData(draftData) {
this.formData = { ...this.formData, ...draftData };
this.hasRecovered = true;
// 显示成功提示
uni.showToast({
title: '数据恢复成功',
icon: 'success',
duration: 2000
});
},
// 自动保存草稿
autoSaveDraft() {
// 检查表单是否有内容
if (this.hasFormData()) {
this.draftManager.saveDraft(this.formData);
}
},
// 检查表单是否有数据
hasFormData() {
return Object.values(this.formData).some(value =>
value !== '' && value !== null && value !== undefined
);
},
// 清除草稿
clearDraft() {
this.draftManager.clearDraft();
},
// 格式化时间
formatTime(timestamp) {
const date = new Date(timestamp);
return `${date.getMonth() + 1}月${date.getDate()}日 ${date.getHours()}:${date.getMinutes().toString().padStart(2, '0')}`;
},
// 表单提交
async submitForm() {
try {
// 你的提交逻辑
// await submitApi(this.formData);
// 提交成功后清除草稿
this.clearDraft();
uni.showToast({
title: '提交成功',
icon: 'success'
});
} catch (error) {
uni.showToast({
title: '提交失败',
icon: 'error'
});
}
},
// 手动保存草稿(可提供给用户操作)
manualSaveDraft() {
if (this.draftManager.saveDraft(this.formData)) {
uni.showToast({
title: '草稿已保存',
icon: 'success'
});
}
}
}
}
</script>
三、实时自动保存(增强体验)
1. 防抖自动保存
javascript
复制代码
// 在 data 中添加
data() {
return {
// ...其他数据
autoSaveTimer: null
}
},
// 在 methods 中添加
methods: {
// 输入时触发自动保存(防抖)
onInput(field, value) {
this.formData[field] = value;
this.debouncedAutoSave();
},
// 防抖自动保存
debouncedAutoSave() {
if (this.autoSaveTimer) {
clearTimeout(this.autoSaveTimer);
}
this.autoSaveTimer = setTimeout(() => {
this.autoSaveDraft();
}, 2000); // 2秒后自动保存
},
// 显示保存状态
showSaveStatus() {
// 可以在模板中显示保存状态
this.saveStatus = '保存中...';
setTimeout(() => {
this.saveStatus = '已保存';
setTimeout(() => {
this.saveStatus = '';
}, 2000);
}, 500);
}
}
四、模板部分
javascript
复制代码
<template>
<view class="form-container">
<!-- 保存状态提示 -->
<view v-if="saveStatus" class="save-status">
{{ saveStatus }}
</view>
<form @submit="submitForm">
<view class="form-item">
<text class="label">姓名</text>
<input
class="input"
v-model="formData.name"
@input="onInput('name', $event.detail.value)"
placeholder="请输入姓名"
/>
</view>
<view class="form-item">
<text class="label">邮箱</text>
<input
class="input"
v-model="formData.email"
@input="onInput('email', $event.detail.value)"
placeholder="请输入邮箱"
type="email"
/>
</view>
<view class="form-item">
<text class="label">电话</text>
<input
class="input"
v-model="formData.phone"
@input="onInput('phone', $event.detail.value)"
placeholder="请输入电话"
type="number"
/>
</view>
<!-- 更多表单项... -->
<view class="form-actions">
<button class="btn-save-draft" @click="manualSaveDraft" type="button">
保存草稿
</button>
<button class="btn-submit" form-type="submit">
提交表单
</button>
</view>
</form>
</view>
</template>
<style scoped>
.form-container {
padding: 30rpx;
}
.save-status {
text-align: center;
color: #07c160;
font-size: 24rpx;
margin-bottom: 20rpx;
}
.form-item {
margin-bottom: 40rpx;
}
.label {
display: block;
margin-bottom: 10rpx;
font-weight: bold;
}
.input {
border: 1px solid #ddd;
border-radius: 8rpx;
padding: 20rpx;
font-size: 28rpx;
}
.form-actions {
display: flex;
gap: 20rpx;
margin-top: 60rpx;
}
.btn-save-draft {
flex: 1;
background: #f8f9fa;
color: #333;
}
.btn-submit {
flex: 2;
background: #007AFF;
color: white;
}
</style>