Files
hrm/app/components/accounting/ConfirmDeleteEntry.vue
2026-04-06 15:53:14 +07:00

166 lines
5.0 KiB
Vue

<!-- components/dialog/ConfirmDeleteEntry.vue -->
<template>
<div class="has-text-centered">
<div v-if="isLoading" class="is-flex is-gap-2 is-align-items-center is-justify-content-center">
<SvgIcon v-bind="{ name: 'loading.svg', type: 'primary' }" />
<span>Đang tải thông tin bút toán...</span>
</div>
<template v-else-if="entry.allocation_amount === 0">
<div class="mb-3 p-3">
<p class="is-size-5 has-text-weight-semibold mb-4">
Bạn chắc chắn muốn xóa bút toán này?
</p>
<p class="mt-3 has-text-danger has-text-weight-semibold">
Hành động này <strong>không thể hoàn tác</strong>.<br>
Dữ liệu liên quan (nếu ) sẽ bị xóa vĩnh viễn.
</p>
</div>
<div class="field is-grouped is-justify-content-center">
<!-- Captcha addon group - shown only when captcha is not confirmed -->
<p class="control" v-if="!isConfirmed">
<div class="field has-addons">
<p class="control">
<input
class="input"
type="text"
placeholder="Nhập mã xác nhận"
v-model="userInputCaptcha"
@keyup.enter="isConfirmed && confirmDelete()"
>
</p>
<p class="control">
<a class="button is-static has-text-weight-bold has-background-grey-lighter"
style="font-family: 'Courier New', monospace; letter-spacing: 2px;">
{{ captchaCode }}
</a>
</p>
<p class="control">
<button class="button" @click="generateCaptcha" title="Tạo mã mới">
<span class="icon">
<SvgIcon name="refresh.svg" type="primary" :size="23" />
</span>
</button>
</p>
</div>
</p>
<!-- Action buttons -->
<!-- Confirm button - shown only when captcha IS confirmed -->
<p class="control" v-if="isConfirmed">
<button
class="button is-danger"
:class="{ 'is-loading': isDeleting }"
:disabled="isDeleting"
@click="confirmDelete"
>
Xác nhận xóa
</button>
</p>
<!-- Cancel button - always shown -->
<p class="control">
<button
class="button"
:disabled="isDeleting"
@click="cancel"
>
Hủy
</button>
</p>
</div>
</template>
<div v-else class="has-text-centered">
<div class="fs-18">
<span>Không được phép xoá bút toán (đã phân bổ </span>
<span class="has-text-weight-semibold">
<FormatNumber :value="entry.allocation_amount" />
<span>đ</span>
</span>
<span>).</span>
</div>
<button @click="emit('close')" class="button mt-4">Quay lại</button>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import { useNuxtApp } from '#app'
const props = defineProps({
entryId: {
type: [String, Number],
required: true
}
})
const emit = defineEmits(['close', 'deleted'])
const { $snackbar, $getdata, $insertapi } = useNuxtApp()
const entry = ref(null);
const isLoading = ref(true);
const isDeleting = ref(false)
const captchaCode = ref('')
const userInputCaptcha = ref('')
const isConfirmed = computed(() => {
return userInputCaptcha.value.toLowerCase() === captchaCode.value.toLowerCase() && userInputCaptcha.value !== ''
})
const generateCaptcha = () => {
captchaCode.value = Math.random().toString(36).substring(2, 7).toUpperCase()
userInputCaptcha.value = ''
}
// Initial generation
generateCaptcha()
const confirmDelete = async () => {
if (isDeleting.value || !isConfirmed.value) return
isDeleting.value = true
try {
// Gọi API xóa theo đúng endpoint delete-entry/{id}
const result = await $insertapi('deleteentry', {id: props.entryId})
if (result === 'error' || !result) {
throw new Error('API xóa trả về lỗi')
}
$snackbar(
`Đã xóa bút toán ID ${props.entryId} thành công`,
'Thành công',
'Success'
)
emit('deleted', props.entryId)
emit('close')
} catch (err) {
console.error('Xóa bút toán thất bại:', err)
let errorMsg = 'Không thể xóa bút toán. Vui lòng thử lại.'
// Nếu backend trả về thông báo cụ thể
if (err?.response?.data?.detail) {
errorMsg = err.response.data.detail
} else if (err?.response?.data?.non_field_errors) {
errorMsg = err.response.data.non_field_errors.join(' ')
}
$snackbar(errorMsg, 'Lỗi', 'Danger')
} finally {
isDeleting.value = false
}
}
const cancel = () => {
emit('close')
}
onMounted(async () => {
isLoading.value = true;
const entryData = await $getdata('internalentry', { id: props.entryId }, undefined, true);
entry.value = entryData;
isLoading.value = false;
})
</script>