import { ref, watch, onMounted, onBeforeUnmount, computed } from 'vue' import { useDirtyState } from './useDirtyState' /** * useSectionForm — standard composable for schema-driven admin settings pages. * * Encapsulates: data loading, dirty tracking, save/reload logic, and loading states. * * Usage: * const { * formData, loading, saving, loadError, * dirty, load, save, reset, * } = useSectionForm({ * fetch: () => api.getBasicSettings(), * submit: (data) => api.saveBasicSettings(data), * defaults: { comment_enabled: true, lazy_load: true }, * }) * * @param {Object} options * @param {Function} options.fetch — async function returning current settings * @param {Function} options.submit — async function accepting form data to save * @param {Object} options.defaults — default values merged under fetched data * @param {Function} [options.onLoaded] — callback after data is loaded * @param {Function} [options.onSaved] — callback after successful save * @param {Function} [options.transform] — transform fetched data before applying */ export function useSectionForm(options) { const { fetch: fetchData, submit: submitData, defaults = {}, onLoaded, onSaved, transform, } = options const formData = ref({ ...defaults }) const initialSnapshot = ref('') const loading = ref(false) const saving = ref(false) const loadError = ref(null) const { isDirty, markDirty, markClean, reset: resetDirty, confirmLeave } = useDirtyState() /** Snapshot the current form state for dirty comparison */ function snapshot() { initialSnapshot.value = JSON.stringify(formData.value) } /** Check if form data differs from snapshot */ function checkDirty() { const current = JSON.stringify(formData.value) if (current !== initialSnapshot.value) { markDirty() } else { resetDirty() } } /** Deep watch form data for dirty tracking */ let watcherHandle = null function startWatching() { if (watcherHandle) watcherHandle() watcherHandle = watch(formData, checkDirty, { deep: true }) } /** Load settings from API */ async function load() { loading.value = true loadError.value = null try { const data = await fetchData() const raw = data?.data ?? data ?? {} const merged = { ...defaults, ...(transform ? transform(raw) : raw) } formData.value = merged snapshot() startWatching() onLoaded?.(merged) } catch (err) { loadError.value = err?.message || '加载失败' console.error('[useSectionForm] load error:', err) } finally { loading.value = false } } /** Save current form data */ async function save() { saving.value = true try { await submitData(formData.value) snapshot() markClean() onSaved?.(formData.value) } catch (err) { console.error('[useSectionForm] save error:', err) throw err // let caller handle the error display } finally { saving.value = false } } /** Reload form data from API (discards changes) */ async function reload() { if (watcherHandle) watcherHandle() resetDirty() await load() } /** Reset form to defaults */ function resetToDefaults() { if (watcherHandle) watcherHandle() formData.value = { ...defaults } snapshot() startWatching() resetDirty() } // Auto-load on mount onMounted(load) // Cleanup watcher onBeforeUnmount(() => { if (watcherHandle) watcherHandle() }) return { formData, loading, saving, loadError, dirty: isDirty, load, save, reload, resetToDefaults, confirmLeave, } }