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 }; }