✨ 支持应收账单异步作废
This commit is contained in:
@@ -84,6 +84,10 @@ export function voidArBill(arBillId, data) {
|
||||
return request({ url: '/ar/bill/' + arBillId + '/void', method: 'put', data })
|
||||
}
|
||||
|
||||
export function getArBillVoidTask(taskId) {
|
||||
return request({ url: '/ar/bill/void/task/' + taskId, method: 'get' })
|
||||
}
|
||||
|
||||
export function exportArBill(arBillId) {
|
||||
return request({ url: '/ar/bill/' + arBillId + '/export', method: 'post' })
|
||||
}
|
||||
|
||||
@@ -44,11 +44,17 @@
|
||||
<template #col-unwrittenAmount="{ row }"><span class="unwritten">{{ money(row.unwrittenAmount) }}</span></template>
|
||||
<template #col-action="{ row }">
|
||||
<el-button link type="primary" icon="View" @click="viewBill(row)">详情</el-button>
|
||||
<el-button v-if="row.status === AR_BILL_STATUS.DRAFT" link type="success" v-hasPermi="['ar:bill:confirm']" @click="confirmBill(row)">确认</el-button>
|
||||
<el-button v-if="row.status === AR_BILL_STATUS.DRAFT || row.status === AR_BILL_STATUS.CONFIRMED" link type="warning" v-hasPermi="['ar:bill:void']" @click="voidBill(row)">作废</el-button>
|
||||
<el-button v-if="row.status !== AR_BILL_STATUS.DRAFT && row.status !== AR_BILL_STATUS.VOID" link type="primary" v-hasPermi="['ar:bill:export']" @click="exportBill(row)">导出明细</el-button>
|
||||
<el-button v-if="canGoWriteoff(row)" link type="primary" v-hasPermi="['customer:bill:writeoff']" @click="goWriteoff(row)">核销</el-button>
|
||||
<el-button v-if="canGoReverseWriteoff(row)" link type="warning" v-hasPermi="['customer:bill:writeoff:cancel']" @click="goReverseWriteoff(row)">反核销</el-button>
|
||||
<span v-if="isVoiding(row)" class="voiding-status"><el-icon class="is-loading"><Loading /></el-icon> 作废中</span>
|
||||
<template v-else>
|
||||
<el-tooltip v-if="row.voidTaskError" :content="row.voidTaskError" placement="top">
|
||||
<span class="void-failed-status">作废失败</span>
|
||||
</el-tooltip>
|
||||
<el-button v-if="row.status === AR_BILL_STATUS.DRAFT" link type="success" v-hasPermi="['ar:bill:confirm']" @click="confirmBill(row)">确认</el-button>
|
||||
<el-button v-if="row.status === AR_BILL_STATUS.DRAFT || row.status === AR_BILL_STATUS.CONFIRMED" link type="warning" v-hasPermi="['ar:bill:void']" @click="voidBill(row)">作废</el-button>
|
||||
<el-button v-if="row.status !== AR_BILL_STATUS.DRAFT && row.status !== AR_BILL_STATUS.VOID" link type="primary" v-hasPermi="['ar:bill:export']" @click="exportBill(row)">导出明细</el-button>
|
||||
<el-button v-if="canGoWriteoff(row)" link type="primary" v-hasPermi="['customer:bill:writeoff']" @click="goWriteoff(row)">核销</el-button>
|
||||
<el-button v-if="canGoReverseWriteoff(row)" link type="warning" v-hasPermi="['customer:bill:writeoff:cancel']" @click="goReverseWriteoff(row)">反核销</el-button>
|
||||
</template>
|
||||
</template>
|
||||
</basic-table>
|
||||
|
||||
@@ -69,7 +75,7 @@
|
||||
|
||||
<script setup name="ArBill">
|
||||
import { saveAs } from 'file-saver'
|
||||
import { listArBillCustomerOptions, listArBill, getArBill, confirmArBill, voidArBill, exportArBill, getArBillExport, downloadArBillExport, getArBillExportDownloadInfo, getArBillGenerateTask, listActiveArBillGenerateTasks, cancelArBillGenerateTask, retryArBillGenerateTask } from '@/api/ar/bill'
|
||||
import { listArBillCustomerOptions, listArBill, getArBill, confirmArBill, voidArBill, getArBillVoidTask, exportArBill, getArBillExport, downloadArBillExport, getArBillExportDownloadInfo, getArBillGenerateTask, listActiveArBillGenerateTasks, cancelArBillGenerateTask, retryArBillGenerateTask } from '@/api/ar/bill'
|
||||
import BillDetail from './components/BillDetail.vue'
|
||||
import GenerateBillDialog from './components/GenerateBillDialog.vue'
|
||||
import { AR_BILL_STATUS, AR_BILL_STATUS_OPTIONS, AR_BILL_STATUS_TYPES } from '@/constants/ar/bill'
|
||||
@@ -91,6 +97,9 @@ const defaultSearchParams = computed(() => ({
|
||||
const detailOpen = ref(false)
|
||||
const detailLoading = ref(false)
|
||||
const detail = ref(null)
|
||||
const voidTasks = reactive(new Map())
|
||||
const voidPollTimers = new Map()
|
||||
let voidPageClosed = false
|
||||
const openedBillId = ref()
|
||||
const generateOpen = ref(false)
|
||||
// 任务可能在首次轮询前就完成,至少展示一段时间让用户感知到“正在生成”。
|
||||
@@ -177,7 +186,17 @@ function normalizeBillQuery(params) {
|
||||
delete query.billDateRange
|
||||
return query
|
||||
}
|
||||
function queryList(params) { return listArBill(normalizeBillQuery(params)) }
|
||||
function queryList(params) {
|
||||
return listArBill(normalizeBillQuery(params)).then(res => {
|
||||
;(res.rows || []).forEach(row => {
|
||||
if (row.voidTaskId && !voidTasks.has(row.arBillId)) {
|
||||
voidTasks.set(row.arBillId, row.voidTaskId)
|
||||
pollVoidTask(row.arBillId, row.voidTaskId)
|
||||
}
|
||||
})
|
||||
return res
|
||||
})
|
||||
}
|
||||
function handleExport() {
|
||||
const query = normalizeBillQuery(tableRef.value?.getQueryParams() || defaultSearchParams.value)
|
||||
delete query.pageNum
|
||||
@@ -322,7 +341,40 @@ function viewBill(row) {
|
||||
})
|
||||
}
|
||||
function confirmBill(row) { proxy.$modal.confirm(`确认账单「${row.billNo}」?确认后将进入正式账单状态,请确认订单及费用金额无误后继续。`).then(() => confirmArBill(row.arBillId)).then(() => { proxy.$modal.msgSuccess('账单已确认'); tableRef.value.refresh(); viewBill(row) }).catch(() => {}) }
|
||||
function voidBill(row) { proxy.$prompt(`请输入账单「${row.billNo}」的作废原因`, '作废账单', { inputPattern: /\S+/, inputErrorMessage: '作废原因不能为空' }).then(({ value }) => voidArBill(row.arBillId, { remark: value })).then(() => { proxy.$modal.msgSuccess('账单已作废'); tableRef.value.refresh(); viewBill(row) }).catch(() => {}) }
|
||||
function isVoiding(row) { return voidTasks.has(row.arBillId) || !!row.voidTaskId }
|
||||
function voidBill(row) {
|
||||
proxy.$prompt(`请输入账单「${row.billNo}」的作废原因`, '作废账单', { inputPattern: /\S+/, inputErrorMessage: '作废原因不能为空' })
|
||||
.then(({ value }) => voidArBill(row.arBillId, { remark: value }))
|
||||
.then(res => {
|
||||
const task = res.data
|
||||
if (!task?.taskId) return
|
||||
voidTasks.set(row.arBillId, task.taskId)
|
||||
pollVoidTask(row.arBillId, task.taskId)
|
||||
proxy.$modal.msgSuccess('作废任务已提交')
|
||||
}).catch(() => {})
|
||||
}
|
||||
function pollVoidTask(billId, taskId) {
|
||||
if (voidPollTimers.has(taskId) || voidPageClosed) return
|
||||
const poll = async () => {
|
||||
voidPollTimers.delete(taskId)
|
||||
if (voidPageClosed) return
|
||||
try {
|
||||
const res = await getArBillVoidTask(taskId)
|
||||
if (voidPageClosed) return
|
||||
const task = res.data
|
||||
if (task && ['SUCCESS', 'FAILED'].includes(task.status)) {
|
||||
voidTasks.delete(billId)
|
||||
tableRef.value?.refresh()
|
||||
if (detailOpen.value && openedBillId.value === billId) viewBill({ arBillId: billId })
|
||||
if (task.status === 'SUCCESS') proxy.$modal.msgSuccess('账单已作废')
|
||||
else proxy.$modal.msgError(`账单作废失败:${task.errorMessage || '请稍后重试'}`)
|
||||
return
|
||||
}
|
||||
} catch (_) { /* 临时网络错误时继续轮询 */ }
|
||||
if (!voidPageClosed) voidPollTimers.set(taskId, window.setTimeout(poll, 2000))
|
||||
}
|
||||
voidPollTimers.set(taskId, window.setTimeout(poll, 1000))
|
||||
}
|
||||
function exportBill(row) {
|
||||
if (!row?.arBillId) return
|
||||
exportSubmittingCount.value += 1
|
||||
@@ -415,6 +467,9 @@ watch(() => route.query.customerId, (newId, oldId) => {
|
||||
|
||||
// 页面销毁时清理所有轮询定时器,避免切换菜单后仍访问旧页面状态。
|
||||
onBeforeUnmount(() => {
|
||||
voidPageClosed = true
|
||||
voidPollTimers.forEach(timer => window.clearTimeout(timer))
|
||||
voidPollTimers.clear()
|
||||
generatePollTimers.forEach(timer => window.clearTimeout(timer))
|
||||
generatePollTimers.clear()
|
||||
generateFinishTimers.forEach(timer => window.clearTimeout(timer))
|
||||
@@ -462,6 +517,8 @@ function statusType(value) { return AR_BILL_STATUS_TYPES[value] || 'info' }
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.voiding-status { color: var(--el-color-warning); margin-left: 8px; white-space: nowrap; }
|
||||
.void-failed-status { color: var(--el-color-danger); margin-left: 8px; white-space: nowrap; }
|
||||
.ar-bill-page :deep(.basic-table > .basic-form) {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
|
||||
Reference in New Issue
Block a user