Files
threeonecheck_web/utils/useDraftCache.js

159 lines
3.8 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { ref, nextTick, watch } from 'vue';
import {
loadDraftWithFallback,
saveDraftToStorage,
removeDraft,
defaultHasContent
} from './draftCache.js';
/**
* 页面草稿 Composable统一 save / restore / clear 流程
*
* @param {Object} options
* @param {() => string} options.getKey 返回 Storage key
* @param {() => Object} options.getPayload 返回要保存的数据
* @param {(payload: Object) => boolean} [options.hasContent] 判断是否有有效草稿
* @param {(payload: Object) => void} options.applyPayload 恢复草稿到表单
* @param {() => void} options.clearForm 清空表单
* @param {() => boolean} [options.canSave] 是否允许保存(如 hazardId 未就绪时为 false
* @param {boolean} [options.requireInitialized=false] 为 true 时restore 完成前不自动 save
* @param {string} [options.restoreToast]
* @param {string} [options.clearToast]
* @param {boolean} [options.showRestoreToast=true]
* @param {(payload: Object) => void} [options.onAfterRestore] 恢复并 apply 后的额外处理
* @param {() => string[]} [options.getFallbackKeys] 读取草稿时的兼容 key 列表
*/
export function useDraftCache(options) {
const {
getKey,
getPayload,
hasContent = defaultHasContent,
applyPayload,
clearForm,
canSave = () => true,
requireInitialized = false,
restoreToast = '已自动恢复您上次未提交的内容',
clearToast = '草稿已清空',
showRestoreToast = true,
onAfterRestore,
getFallbackKeys
} = options;
const hasDraft = ref(false);
const showRestoreBanner = ref(false);
const isRestoring = ref(false);
const isInitialized = ref(false);
const finishRestoreState = (callback) => {
nextTick(() => {
isRestoring.value = false;
isInitialized.value = true;
callback?.();
});
};
const save = () => {
if (isRestoring.value) return;
if (requireInitialized && !isInitialized.value) return;
if (!canSave()) return;
const key = getKey();
const payload = getPayload();
if (!hasContent(payload)) {
removeDraft(key);
hasDraft.value = false;
return;
}
saveDraftToStorage(key, payload);
hasDraft.value = true;
};
const clear = (showToast = true) => {
removeDraft(getKey());
hasDraft.value = false;
showRestoreBanner.value = false;
isRestoring.value = true;
clearForm();
finishRestoreState();
if (showToast) {
uni.showToast({ title: clearToast, icon: 'none' });
}
};
/**
* 恢复草稿
* @returns {boolean} 是否成功恢复
*/
const restore = () => {
const key = getKey();
const { data, fromFallback, fallbackKey } = loadDraftWithFallback(
key,
getFallbackKeys?.() || []
);
if (!data || !hasContent(data)) {
isInitialized.value = true;
return false;
}
isRestoring.value = true;
try {
applyPayload(data);
hasDraft.value = true;
showRestoreBanner.value = true;
onAfterRestore?.(data);
// 从兼容 key 读到后,立即写回标准 key 并清理旧 key
if (fromFallback && fallbackKey) {
saveDraftToStorage(key, data);
removeDraft(fallbackKey);
}
finishRestoreState(() => {
if (showRestoreToast) {
uni.showToast({
title: restoreToast,
icon: 'none',
duration: 2500
});
}
});
return true;
} catch (error) {
console.error('[useDraftCache] 恢复草稿失败:', error);
isRestoring.value = false;
isInitialized.value = true;
return false;
}
};
/**
* 绑定自动保存(等价于页面里的 watch + saveDraft
* @param {import('vue').WatchSource|import('vue').WatchSource[]} source
* @param {import('vue').WatchOptions} [watchOptions]
*/
const bindAutoSave = (source, watchOptions = {}) => {
watch(
source,
() => save(),
{ deep: true, ...watchOptions }
);
};
return {
hasDraft,
showRestoreBanner,
isRestoring,
isInitialized,
save,
clear,
restore,
bindAutoSave
};
}