96b96bcaaa
Split current project diagnostics into a dedicated analysis document and trim the main architecture strategy to focus on core guidance. This makes the documentation easier to navigate and separates observed issues from recommended architectural principles.docs: reorganize architecture strategy documentation Split current project diagnostics into a dedicated analysis document and trim the main architecture strategy to focus on core guidance. This makes the documentation easier to navigate and separates observed issues from recommended architectural principles.
126 lines
3.7 KiB
TypeScript
126 lines
3.7 KiB
TypeScript
import { nextTick, type Ref } from 'vue'
|
|
|
|
interface UseCrudCommandsOptions<TRecord extends { id: number }, TPayload> {
|
|
clearAllErrors: () => void
|
|
dialogMode: Ref<'create' | 'edit' | 'view'>
|
|
dialogVisible: Ref<boolean>
|
|
editingId: Ref<number | null>
|
|
fieldErrors: Ref<Record<string, string[]>>
|
|
form: Ref<TPayload>
|
|
highlightedId: Ref<number | null>
|
|
isDirty: Readonly<Ref<boolean>>
|
|
isLoading: Ref<boolean>
|
|
isSaving: Ref<boolean>
|
|
isViewMode: Readonly<Ref<boolean>>
|
|
loadDelay?: number
|
|
loadSequence: Ref<number>
|
|
resetForm: () => void
|
|
saveDelay?: number
|
|
scrollToField: (field: string) => void
|
|
setForm: (payload: TPayload) => void
|
|
syncInitialForm: () => void
|
|
toFormPayload: (record: TRecord) => TPayload
|
|
toSavePayload: (form: TPayload) => TPayload
|
|
updateRecord: (id: number, payload: TPayload) => unknown
|
|
createRecord: (payload: TPayload) => number
|
|
validateForm: () => Array<{ field: string; message: string }>
|
|
}
|
|
|
|
export function useCrudCommands<TRecord extends { id: number }, TPayload>(
|
|
options: UseCrudCommandsOptions<TRecord, TPayload>
|
|
) {
|
|
function openAddDialog() {
|
|
options.loadSequence.value += 1
|
|
options.dialogMode.value = 'create'
|
|
options.editingId.value = null
|
|
options.resetForm()
|
|
options.isLoading.value = false
|
|
options.dialogVisible.value = true
|
|
}
|
|
|
|
function loadRecord(record: TRecord, mode: 'edit' | 'view') {
|
|
options.loadSequence.value += 1
|
|
const sequence = options.loadSequence.value
|
|
options.dialogMode.value = mode
|
|
options.editingId.value = record.id
|
|
options.dialogVisible.value = true
|
|
options.isLoading.value = true
|
|
options.clearAllErrors()
|
|
window.setTimeout(() => {
|
|
if (sequence !== options.loadSequence.value || !options.dialogVisible.value) return
|
|
options.setForm(options.toFormPayload(record))
|
|
options.syncInitialForm()
|
|
options.isLoading.value = false
|
|
}, options.loadDelay ?? 350)
|
|
}
|
|
|
|
function openEditDialog(record: TRecord) {
|
|
loadRecord(record, 'edit')
|
|
}
|
|
|
|
function openViewDialog(record: TRecord) {
|
|
loadRecord(record, 'view')
|
|
}
|
|
|
|
async function requestSaveConfirmation(confirmSaveVisible: Ref<boolean>) {
|
|
if (
|
|
options.isSaving.value ||
|
|
options.isLoading.value ||
|
|
!options.isDirty.value ||
|
|
options.isViewMode.value
|
|
) {
|
|
return
|
|
}
|
|
options.clearAllErrors()
|
|
|
|
const errors = options.validateForm()
|
|
if (errors.length > 0) {
|
|
for (const error of errors) {
|
|
options.fieldErrors.value[error.field] = [error.message]
|
|
}
|
|
await nextTick()
|
|
const firstError = errors[0]
|
|
if (firstError) options.scrollToField(firstError.field)
|
|
return
|
|
}
|
|
|
|
confirmSaveVisible.value = true
|
|
}
|
|
|
|
function confirmSave(confirmSaveVisible: Ref<boolean>) {
|
|
confirmSaveVisible.value = false
|
|
return saveRecord()
|
|
}
|
|
|
|
async function saveRecord() {
|
|
if (options.isSaving.value || options.isLoading.value) return
|
|
options.isSaving.value = true
|
|
await new Promise((resolve) => window.setTimeout(resolve, options.saveDelay ?? 450))
|
|
const payload = options.toSavePayload(options.form.value)
|
|
|
|
if (options.editingId.value) {
|
|
const updated = options.updateRecord(options.editingId.value, payload)
|
|
if (updated) options.highlightedId.value = options.editingId.value
|
|
} else {
|
|
const createdId = options.createRecord(payload)
|
|
options.highlightedId.value = createdId
|
|
}
|
|
|
|
options.syncInitialForm()
|
|
options.dialogVisible.value = false
|
|
options.isSaving.value = false
|
|
window.setTimeout(() => {
|
|
options.highlightedId.value = null
|
|
}, 1600)
|
|
}
|
|
|
|
return {
|
|
confirmSave,
|
|
openAddDialog,
|
|
openEditDialog,
|
|
openViewDialog,
|
|
requestSaveConfirmation,
|
|
saveRecord,
|
|
}
|
|
}
|