Refactor MasterDetailMntC.vue for improved readability and consistency
This commit is contained in:
@@ -2,14 +2,14 @@ import { computed } from 'vue'
|
||||
import { useTheme } from 'vuetify'
|
||||
import { getNextThemeName } from '@/utils/theme'
|
||||
|
||||
export function useThemeToggle () {
|
||||
export function useThemeToggle() {
|
||||
const theme = useTheme()
|
||||
|
||||
const availableThemeNames = computed(() =>
|
||||
Object.keys(theme.themes.value ?? {}).filter((name) => name.startsWith('theme'))
|
||||
)
|
||||
|
||||
function toggleTheme () {
|
||||
function toggleTheme() {
|
||||
const names = availableThemeNames.value
|
||||
if (names.length === 0) return null
|
||||
|
||||
|
||||
@@ -16,23 +16,100 @@ const statuses = ['在學', '休學', '畢業']
|
||||
|
||||
const tableHeaders = [
|
||||
{ title: '', key: 'select', sortable: false, nowrap: true },
|
||||
{ title: '學號', key: 'studentId', sortable: true, minWidth: 120, nowrap: true, cellProps: { class: 'px-1' } },
|
||||
{ title: '姓名', key: 'name', sortable: true, minWidth: 120, nowrap: true, cellProps: { class: 'px-1' } },
|
||||
{ title: '系所', key: 'department', sortable: true, minWidth: 140, nowrap: true, cellProps: { class: 'px-1' } },
|
||||
{ title: '年級', key: 'grade', sortable: true, minWidth: 140, nowrap: true, cellProps: { class: 'px-1' } },
|
||||
{ title: '入學年度', key: 'enrollYear', sortable: true, minWidth: 120, nowrap: true, cellProps: { class: 'px-1' } },
|
||||
{ title: '已修學分', key: 'credits', sortable: true, minWidth: 120, nowrap: true, cellProps: { class: 'px-1' } },
|
||||
{ title: '指導老師', key: 'advisor', sortable: true, minWidth: 120, nowrap: true, cellProps: { class: 'px-1' } },
|
||||
{ title: 'Email', key: 'email', sortable: true, minWidth: 200, nowrap: true, cellProps: { class: 'px-1' } },
|
||||
{ title: '電話', key: 'phone', sortable: true, minWidth: 150, nowrap: true, cellProps: { class: 'px-1' } },
|
||||
{ title: '狀態', key: 'status', sortable: true, minWidth: 120, nowrap: true, cellProps: { class: 'px-1' } },
|
||||
{ title: '操作', key: 'actions', sortable: false, width: 'auto', nowrap: true, cellProps: { class: 'bg-background' } },
|
||||
{
|
||||
title: '學號',
|
||||
key: 'studentId',
|
||||
sortable: true,
|
||||
minWidth: 120,
|
||||
nowrap: true,
|
||||
cellProps: { class: 'px-1' },
|
||||
},
|
||||
{
|
||||
title: '姓名',
|
||||
key: 'name',
|
||||
sortable: true,
|
||||
minWidth: 120,
|
||||
nowrap: true,
|
||||
cellProps: { class: 'px-1' },
|
||||
},
|
||||
{
|
||||
title: '系所',
|
||||
key: 'department',
|
||||
sortable: true,
|
||||
minWidth: 140,
|
||||
nowrap: true,
|
||||
cellProps: { class: 'px-1' },
|
||||
},
|
||||
{
|
||||
title: '年級',
|
||||
key: 'grade',
|
||||
sortable: true,
|
||||
minWidth: 140,
|
||||
nowrap: true,
|
||||
cellProps: { class: 'px-1' },
|
||||
},
|
||||
{
|
||||
title: '入學年度',
|
||||
key: 'enrollYear',
|
||||
sortable: true,
|
||||
minWidth: 120,
|
||||
nowrap: true,
|
||||
cellProps: { class: 'px-1' },
|
||||
},
|
||||
{
|
||||
title: '已修學分',
|
||||
key: 'credits',
|
||||
sortable: true,
|
||||
minWidth: 120,
|
||||
nowrap: true,
|
||||
cellProps: { class: 'px-1' },
|
||||
},
|
||||
{
|
||||
title: '指導老師',
|
||||
key: 'advisor',
|
||||
sortable: true,
|
||||
minWidth: 120,
|
||||
nowrap: true,
|
||||
cellProps: { class: 'px-1' },
|
||||
},
|
||||
{
|
||||
title: 'Email',
|
||||
key: 'email',
|
||||
sortable: true,
|
||||
minWidth: 200,
|
||||
nowrap: true,
|
||||
cellProps: { class: 'px-1' },
|
||||
},
|
||||
{
|
||||
title: '電話',
|
||||
key: 'phone',
|
||||
sortable: true,
|
||||
minWidth: 150,
|
||||
nowrap: true,
|
||||
cellProps: { class: 'px-1' },
|
||||
},
|
||||
{
|
||||
title: '狀態',
|
||||
key: 'status',
|
||||
sortable: true,
|
||||
minWidth: 120,
|
||||
nowrap: true,
|
||||
cellProps: { class: 'px-1' },
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
sortable: false,
|
||||
width: 'auto',
|
||||
nowrap: true,
|
||||
cellProps: { class: 'bg-background' },
|
||||
},
|
||||
] as const
|
||||
|
||||
const TABLE_BOTTOM_GAP = 64
|
||||
const TABLE_MIN_HEIGHT = 240
|
||||
|
||||
export function useEditableStudentGrid () {
|
||||
export function useEditableStudentGrid() {
|
||||
const studentStore = useStudentStore()
|
||||
const students = computed(() => studentStore.students)
|
||||
const search = ref({
|
||||
@@ -49,7 +126,7 @@ export function useEditableStudentGrid () {
|
||||
const draftRows = ref<Record<number, StudentPayload>>({})
|
||||
const selectedRowIds = ref<number[]>([])
|
||||
|
||||
function toPayload (student: StudentRecord): StudentPayload {
|
||||
function toPayload(student: StudentRecord): StudentPayload {
|
||||
return {
|
||||
studentId: student.studentId,
|
||||
name: student.name,
|
||||
@@ -64,17 +141,17 @@ export function useEditableStudentGrid () {
|
||||
}
|
||||
}
|
||||
|
||||
function rebuildDraftRows () {
|
||||
function rebuildDraftRows() {
|
||||
draftRows.value = Object.fromEntries(students.value.map((item) => [item.id, toPayload(item)]))
|
||||
}
|
||||
|
||||
function getDraftRow (id: number) {
|
||||
function getDraftRow(id: number) {
|
||||
return draftRows.value[id] ?? null
|
||||
}
|
||||
|
||||
const getSourceRow = (id: number) => students.value.find((item) => item.id === id) || null
|
||||
|
||||
function isRowDirty (id: number) {
|
||||
function isRowDirty(id: number) {
|
||||
const source = getSourceRow(id)
|
||||
const draft = getDraftRow(id)
|
||||
if (!source || !draft) return false
|
||||
@@ -112,34 +189,42 @@ export function useEditableStudentGrid () {
|
||||
})
|
||||
|
||||
const visibleStudentIds = computed(() => filteredStudents.value.map((item) => item.id))
|
||||
const isAllVisibleSelected = computed(() =>
|
||||
visibleStudentIds.value.length > 0 &&
|
||||
visibleStudentIds.value.every((id) => selectedRowIds.value.includes(id))
|
||||
const isAllVisibleSelected = computed(
|
||||
() =>
|
||||
visibleStudentIds.value.length > 0 &&
|
||||
visibleStudentIds.value.every((id) => selectedRowIds.value.includes(id))
|
||||
)
|
||||
const isPartiallyVisibleSelected = computed(() =>
|
||||
visibleStudentIds.value.some((id) => selectedRowIds.value.includes(id)) &&
|
||||
!isAllVisibleSelected.value
|
||||
const isPartiallyVisibleSelected = computed(
|
||||
() =>
|
||||
visibleStudentIds.value.some((id) => selectedRowIds.value.includes(id)) &&
|
||||
!isAllVisibleSelected.value
|
||||
)
|
||||
const hasAnyChange = computed(() =>
|
||||
students.value.some((item) => isRowDirty(item.id)) || studentStore.deletedIds.size > 0
|
||||
const hasAnyChange = computed(
|
||||
() => students.value.some((item) => isRowDirty(item.id)) || studentStore.deletedIds.size > 0
|
||||
)
|
||||
const hasSelectedRows = computed(() => selectedRowIds.value.length > 0)
|
||||
|
||||
function toggleSelectAll (checked: boolean | null) {
|
||||
function toggleSelectAll(checked: boolean | null) {
|
||||
if (isPartiallyVisibleSelected.value) {
|
||||
selectedRowIds.value = selectedRowIds.value.filter((id) => !visibleStudentIds.value.includes(id))
|
||||
selectedRowIds.value = selectedRowIds.value.filter(
|
||||
(id) => !visibleStudentIds.value.includes(id)
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if (!checked) {
|
||||
selectedRowIds.value = selectedRowIds.value.filter((id) => !visibleStudentIds.value.includes(id))
|
||||
selectedRowIds.value = selectedRowIds.value.filter(
|
||||
(id) => !visibleStudentIds.value.includes(id)
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
selectedRowIds.value = Array.from(new Set([...selectedRowIds.value, ...visibleStudentIds.value]))
|
||||
selectedRowIds.value = Array.from(
|
||||
new Set([...selectedRowIds.value, ...visibleStudentIds.value])
|
||||
)
|
||||
}
|
||||
|
||||
function toggleSingleRowSelection (id: number, checked: boolean | null) {
|
||||
function toggleSingleRowSelection(id: number, checked: boolean | null) {
|
||||
if (checked) {
|
||||
if (!selectedRowIds.value.includes(id)) {
|
||||
selectedRowIds.value = [...selectedRowIds.value, id]
|
||||
@@ -150,13 +235,13 @@ export function useEditableStudentGrid () {
|
||||
selectedRowIds.value = selectedRowIds.value.filter((selectedId) => selectedId !== id)
|
||||
}
|
||||
|
||||
function deleteSingleRow (id: number) {
|
||||
function deleteSingleRow(id: number) {
|
||||
selectedRowIds.value = selectedRowIds.value.filter((selectedId) => selectedId !== id)
|
||||
studentStore.markAsDeleted(id)
|
||||
rebuildDraftRows()
|
||||
}
|
||||
|
||||
function deleteSelectedRows () {
|
||||
function deleteSelectedRows() {
|
||||
if (selectedRowIds.value.length === 0) return
|
||||
|
||||
for (const id of selectedRowIds.value) {
|
||||
@@ -167,7 +252,7 @@ export function useEditableStudentGrid () {
|
||||
rebuildDraftRows()
|
||||
}
|
||||
|
||||
function saveAllRows () {
|
||||
function saveAllRows() {
|
||||
for (const item of students.value) {
|
||||
const draft = getDraftRow(item.id)
|
||||
if (!draft) continue
|
||||
@@ -179,17 +264,18 @@ export function useEditableStudentGrid () {
|
||||
rebuildDraftRows()
|
||||
}
|
||||
|
||||
function resetAllRows () {
|
||||
function resetAllRows() {
|
||||
studentStore.restoreDeleted()
|
||||
selectedRowIds.value = []
|
||||
rebuildDraftRows()
|
||||
}
|
||||
|
||||
function recalculateTableHeight () {
|
||||
function recalculateTableHeight() {
|
||||
const container = tableContainerRef.value
|
||||
if (!container) return
|
||||
|
||||
const scrollParent = tableScrollParentRef.value || (container.closest('.overflow-auto') as HTMLElement | null)
|
||||
const scrollParent =
|
||||
tableScrollParentRef.value || (container.closest('.overflow-auto') as HTMLElement | null)
|
||||
tableScrollParentRef.value = scrollParent
|
||||
|
||||
const parentBottom = scrollParent
|
||||
|
||||
@@ -49,8 +49,8 @@ interface UseMaintenanceCrudFlowResult<T extends { id: number }> {
|
||||
handleDialogVisibility: (nextValue: boolean) => void
|
||||
}
|
||||
|
||||
export function useMaintenanceCrudFlow<T extends { id: number }> (
|
||||
options: UseMaintenanceCrudFlowOptions<T>,
|
||||
export function useMaintenanceCrudFlow<T extends { id: number }>(
|
||||
options: UseMaintenanceCrudFlowOptions<T>
|
||||
): UseMaintenanceCrudFlowResult<T> {
|
||||
const confirmCloseVisible = ref(false)
|
||||
const confirmSaveVisible = ref(false)
|
||||
@@ -64,23 +64,22 @@ export function useMaintenanceCrudFlow<T extends { id: number }> (
|
||||
const isEditMode = computed(() => options.dialogMode.value === 'edit')
|
||||
const isViewMode = computed(() => options.dialogMode.value === 'view')
|
||||
const currentRecordIndex = computed(() =>
|
||||
options.records.value.findIndex((item) => item.id === options.editingId.value),
|
||||
options.records.value.findIndex((item) => item.id === options.editingId.value)
|
||||
)
|
||||
const currentEditingRecord = computed(
|
||||
() => options.records.value.find((item) => item.id === options.editingId.value) || null,
|
||||
() => options.records.value.find((item) => item.id === options.editingId.value) || null
|
||||
)
|
||||
const hasPrevRecord = computed(() => currentRecordIndex.value > 0)
|
||||
const hasNextRecord = computed(
|
||||
() =>
|
||||
currentRecordIndex.value >= 0
|
||||
&& currentRecordIndex.value < options.records.value.length - 1,
|
||||
currentRecordIndex.value >= 0 && currentRecordIndex.value < options.records.value.length - 1
|
||||
)
|
||||
const pendingDeleteLabel = computed(() => {
|
||||
if (!pendingDelete.value) return '這筆資料'
|
||||
return options.describeRecord(pendingDelete.value)
|
||||
})
|
||||
|
||||
function openAdjacentRecord (direction: 'prev' | 'next') {
|
||||
function openAdjacentRecord(direction: 'prev' | 'next') {
|
||||
if (!isViewMode.value && !isEditMode.value) return
|
||||
const index = currentRecordIndex.value
|
||||
if (index < 0) return
|
||||
@@ -99,7 +98,7 @@ export function useMaintenanceCrudFlow<T extends { id: number }> (
|
||||
options.openViewDialog(target)
|
||||
}
|
||||
|
||||
function openEdgeRecord (position: 'first' | 'last') {
|
||||
function openEdgeRecord(position: 'first' | 'last') {
|
||||
if (!isViewMode.value && !isEditMode.value) return
|
||||
if (options.records.value.length === 0) return
|
||||
const target = position === 'first' ? options.records.value[0] : options.records.value.at(-1)
|
||||
@@ -116,14 +115,14 @@ export function useMaintenanceCrudFlow<T extends { id: number }> (
|
||||
options.openViewDialog(target)
|
||||
}
|
||||
|
||||
function switchToEditMode () {
|
||||
function switchToEditMode() {
|
||||
if (!isViewMode.value) return
|
||||
const current = currentEditingRecord.value
|
||||
if (!current) return
|
||||
options.openEditDialog(current)
|
||||
}
|
||||
|
||||
function switchToViewMode () {
|
||||
function switchToViewMode() {
|
||||
if (!isEditMode.value) return
|
||||
const current = currentEditingRecord.value
|
||||
if (!current) return
|
||||
@@ -135,7 +134,7 @@ export function useMaintenanceCrudFlow<T extends { id: number }> (
|
||||
options.openViewDialog(current)
|
||||
}
|
||||
|
||||
function confirmSwitch () {
|
||||
function confirmSwitch() {
|
||||
const target = pendingSwitchTarget.value
|
||||
pendingSwitchTarget.value = null
|
||||
confirmSwitchVisible.value = false
|
||||
@@ -143,7 +142,7 @@ export function useMaintenanceCrudFlow<T extends { id: number }> (
|
||||
options.openViewDialog(target)
|
||||
}
|
||||
|
||||
function confirmNavigate () {
|
||||
function confirmNavigate() {
|
||||
const target = pendingNavigateTarget.value
|
||||
pendingNavigateTarget.value = null
|
||||
confirmNavigateVisible.value = false
|
||||
@@ -151,18 +150,18 @@ export function useMaintenanceCrudFlow<T extends { id: number }> (
|
||||
options.openEditDialog(target)
|
||||
}
|
||||
|
||||
function requestDeleteConfirmation (record: T) {
|
||||
function requestDeleteConfirmation(record: T) {
|
||||
pendingDelete.value = record
|
||||
confirmDeleteVisible.value = true
|
||||
}
|
||||
|
||||
function requestDeleteCurrent () {
|
||||
function requestDeleteCurrent() {
|
||||
const current = currentEditingRecord.value
|
||||
if (!current) return
|
||||
requestDeleteConfirmation(current)
|
||||
}
|
||||
|
||||
function closeDialog () {
|
||||
function closeDialog() {
|
||||
options.dialogVisible.value = false
|
||||
options.isLoading.value = false
|
||||
options.isSaving.value = false
|
||||
@@ -181,7 +180,7 @@ export function useMaintenanceCrudFlow<T extends { id: number }> (
|
||||
options.onCloseReset?.()
|
||||
}
|
||||
|
||||
function confirmDelete () {
|
||||
function confirmDelete() {
|
||||
if (!pendingDelete.value) return
|
||||
const deletedId = pendingDelete.value.id
|
||||
options.removeRecord(deletedId)
|
||||
@@ -193,7 +192,7 @@ export function useMaintenanceCrudFlow<T extends { id: number }> (
|
||||
}
|
||||
}
|
||||
|
||||
function requestCloseDialog () {
|
||||
function requestCloseDialog() {
|
||||
if (options.isDirty.value && !options.isSaving.value) {
|
||||
confirmCloseVisible.value = true
|
||||
return
|
||||
@@ -201,12 +200,12 @@ export function useMaintenanceCrudFlow<T extends { id: number }> (
|
||||
closeDialog()
|
||||
}
|
||||
|
||||
function confirmClose () {
|
||||
function confirmClose() {
|
||||
confirmCloseVisible.value = false
|
||||
closeDialog()
|
||||
}
|
||||
|
||||
function handleDialogVisibility (nextValue: boolean) {
|
||||
function handleDialogVisibility(nextValue: boolean) {
|
||||
if (nextValue) {
|
||||
options.dialogVisible.value = true
|
||||
return
|
||||
|
||||
@@ -49,11 +49,11 @@ const fieldLabels: Record<keyof StudentFormState, string> = {
|
||||
status: '狀態',
|
||||
}
|
||||
|
||||
function createDefaultForm (
|
||||
function createDefaultForm(
|
||||
departments: string[],
|
||||
gradeOptions: GradeOption[],
|
||||
enrollYears: number[],
|
||||
statuses: string[],
|
||||
statuses: string[]
|
||||
): StudentFormState {
|
||||
return {
|
||||
studentId: '',
|
||||
@@ -69,7 +69,7 @@ function createDefaultForm (
|
||||
}
|
||||
}
|
||||
|
||||
function createEmptyFieldErrors () {
|
||||
function createEmptyFieldErrors() {
|
||||
return {
|
||||
studentId: [],
|
||||
name: [],
|
||||
@@ -84,22 +84,25 @@ function createEmptyFieldErrors () {
|
||||
} as Record<keyof StudentFormState, string[]>
|
||||
}
|
||||
|
||||
export function useStudentMaintenanceForm (options: UseStudentMaintenanceFormOptions) {
|
||||
export function useStudentMaintenanceForm(options: UseStudentMaintenanceFormOptions) {
|
||||
const form = ref<StudentFormState>(
|
||||
createDefaultForm(options.departments, options.gradeOptions, options.enrollYears, options.statuses),
|
||||
createDefaultForm(
|
||||
options.departments,
|
||||
options.gradeOptions,
|
||||
options.enrollYears,
|
||||
options.statuses
|
||||
)
|
||||
)
|
||||
const initialForm = ref<StudentFormState>({ ...form.value })
|
||||
const fieldErrors = ref(createEmptyFieldErrors())
|
||||
|
||||
const isDirty = computed(
|
||||
() => JSON.stringify(form.value) !== JSON.stringify(initialForm.value),
|
||||
)
|
||||
const isDirty = computed(() => JSON.stringify(form.value) !== JSON.stringify(initialForm.value))
|
||||
|
||||
function gradeLabel (grade: number) {
|
||||
function gradeLabel(grade: number) {
|
||||
return options.gradeOptions.find((option) => option.value === grade)?.title ?? `年級 ${grade}`
|
||||
}
|
||||
|
||||
function formatSummaryValue (key: string, value: string | number | null | undefined) {
|
||||
function formatSummaryValue(key: string, value: string | number | null | undefined) {
|
||||
if (value === null || value === undefined || value === '') return '—'
|
||||
if (key === 'grade') return gradeLabel(Number(value))
|
||||
return String(value)
|
||||
@@ -124,40 +127,40 @@ export function useStudentMaintenanceForm (options: UseStudentMaintenanceFormOpt
|
||||
|
||||
const errorSummary = computed(() => {
|
||||
const entries = Object.entries(fieldErrors.value).flatMap(([field, messages]) =>
|
||||
messages.map((message) => ({ field, message })),
|
||||
messages.map((message) => ({ field, message }))
|
||||
)
|
||||
return entries.slice(0, 3)
|
||||
})
|
||||
|
||||
function setForm (nextForm: StudentFormState) {
|
||||
function setForm(nextForm: StudentFormState) {
|
||||
form.value = { ...nextForm }
|
||||
}
|
||||
|
||||
function syncInitialForm () {
|
||||
function syncInitialForm() {
|
||||
initialForm.value = { ...form.value }
|
||||
}
|
||||
|
||||
function resetForm () {
|
||||
function resetForm() {
|
||||
form.value = createDefaultForm(
|
||||
options.departments,
|
||||
options.gradeOptions,
|
||||
options.enrollYears,
|
||||
options.statuses,
|
||||
options.statuses
|
||||
)
|
||||
syncInitialForm()
|
||||
clearAllErrors()
|
||||
}
|
||||
|
||||
function clearAllErrors () {
|
||||
function clearAllErrors() {
|
||||
fieldErrors.value = createEmptyFieldErrors()
|
||||
}
|
||||
|
||||
function clearFieldError (field: keyof StudentFormState | string) {
|
||||
function clearFieldError(field: keyof StudentFormState | string) {
|
||||
if (!fieldErrors.value[field as keyof StudentFormState]?.length) return
|
||||
fieldErrors.value[field as keyof StudentFormState] = []
|
||||
}
|
||||
|
||||
function validateForm () {
|
||||
function validateForm() {
|
||||
const errors: Array<{ field: keyof StudentFormState; message: string }> = []
|
||||
const studentId = form.value.studentId.trim()
|
||||
const name = form.value.name.trim()
|
||||
@@ -180,7 +183,7 @@ export function useStudentMaintenanceForm (options: UseStudentMaintenanceFormOpt
|
||||
}
|
||||
|
||||
const duplicate = options.students.value.find(
|
||||
(item) => item.studentId === studentId && item.id !== options.editingId.value,
|
||||
(item) => item.studentId === studentId && item.id !== options.editingId.value
|
||||
)
|
||||
if (studentId && duplicate) {
|
||||
errors.push({ field: 'studentId', message: '學號已存在,請確認是否重複' })
|
||||
@@ -189,14 +192,14 @@ export function useStudentMaintenanceForm (options: UseStudentMaintenanceFormOpt
|
||||
return errors
|
||||
}
|
||||
|
||||
function statusColor (status: string) {
|
||||
function statusColor(status: string) {
|
||||
if (status === '在學') return 'success'
|
||||
if (status === '休學') return 'warning'
|
||||
if (status === '畢業') return 'secondary'
|
||||
return 'default'
|
||||
}
|
||||
|
||||
function rowProps (data: { item: StudentRecord }) {
|
||||
function rowProps(data: { item: StudentRecord }) {
|
||||
return {
|
||||
class: data.item.id === options.highlightedId.value ? 'is-highlighted' : '',
|
||||
}
|
||||
|
||||
@@ -18,19 +18,21 @@ interface UseApiCallResult<TResult, TArgs extends unknown[]> {
|
||||
reset: () => void
|
||||
}
|
||||
|
||||
function getDefaultToastLevel (error: ApiRequestError): ToastLevel {
|
||||
function getDefaultToastLevel(error: ApiRequestError): ToastLevel {
|
||||
if (typeof error.status === 'number' && error.status >= 500) return 'error'
|
||||
return 'warning'
|
||||
}
|
||||
|
||||
function levelToColor (level: ToastLevel): string {
|
||||
function levelToColor(level: ToastLevel): string {
|
||||
if (level === 'error') return 'error'
|
||||
if (level === 'warning') return 'warning'
|
||||
return 'info'
|
||||
}
|
||||
|
||||
export function useApiCall <TResult, TArgs extends unknown[]>(action: (...args: TArgs) => Promise<TResult>,
|
||||
options?: Options): UseApiCallResult<TResult, TArgs> {
|
||||
export function useApiCall<TResult, TArgs extends unknown[]>(
|
||||
action: (...args: TArgs) => Promise<TResult>,
|
||||
options?: Options
|
||||
): UseApiCallResult<TResult, TArgs> {
|
||||
const loading = ref(false)
|
||||
const data = ref<TResult | null>(null) as Ref<TResult | null>
|
||||
const error = ref<ApiRequestError | null>(null)
|
||||
|
||||
Reference in New Issue
Block a user