修改同步计费

This commit is contained in:
wuweihua
2026-10-13 10:21:28 +08:00
parent 26bdd3ba87
commit 4d27887d4a
14 changed files with 1231 additions and 12 deletions
@@ -0,0 +1,313 @@
<template>
<el-dialog
v-model="visible"
:title="dialogTitle"
width="520px"
append-to-body
destroy-on-close
:close-on-click-modal="!loading"
:close-on-press-escape="!loading"
:show-close="!loading"
@closed="onClosed"
>
<el-form v-show="!loading && !finished" label-width="96px">
<el-form-item label="客户">
<span>{{ filterText.customers }}</span>
</el-form-item>
<el-form-item label="提货时间">
<span>{{ filterText.time }}</span>
</el-form-item>
<el-form-item v-if="filterText.nos" label="单号">
<span>{{ filterText.nos }}</span>
</el-form-item>
<el-alert
type="info"
:closable="false"
show-icon
title="按当前筛选分页查询上游订单并逐单同步计费,避免一次性加载全量。"
/>
</el-form>
<div v-if="loading || finished" class="filter-retry-progress">
<el-progress
:percentage="progressPercent"
:status="progressStatus"
:stroke-width="14"
striped
:striped-flow="loading"
/>
<div class="filter-retry-progress__msg">{{ progressMessage }}</div>
<div class="filter-retry-progress__text">
总数 {{ progress.total || 0 }}
/ 已处理 {{ progress.processed || 0 }}
/ 成功 {{ progress.ok || 0 }}
/ 失败 {{ progress.fail || 0 }}
</div>
<div v-if="progress.error" class="filter-retry-progress__error">{{ progress.error }}</div>
<p v-if="loading" class="filter-retry-progress__hint">请勿关闭页面,大量订单同步计费可能需要一些时间</p>
</div>
<template #footer>
<template v-if="!loading && !finished">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="submit">开始同步计费</el-button>
</template>
<template v-else-if="finished">
<el-button type="primary" @click="visible = false">关闭</el-button>
</template>
<template v-else>
<el-button disabled>执行中...</el-button>
</template>
</template>
</el-dialog>
</template>
<script setup>
import { syncOrderSourceByFilter, getOrderSourceSyncProgress } from '@/api/monitor/orderSource'
const emit = defineEmits(['success', 'progress'])
const { proxy } = getCurrentInstance()
const visible = ref(false)
const loading = ref(false)
const finished = ref(false)
const query = ref({})
const progress = ref({
percent: 0,
message: '',
total: 0,
processed: 0,
ok: 0,
fail: 0,
status: '',
error: ''
})
const filterText = computed(() => {
const q = query.value || {}
const codes = Array.isArray(q.customerCodes) ? q.customerCodes.filter(Boolean) : []
const nos = []
if (q.orderId) nos.push('订单号 ' + q.orderId)
if (q.orgOrderId) nos.push('运单号 ' + q.orgOrderId)
if (q.transferOrderId) nos.push('转单号 ' + q.transferOrderId)
return {
customers: codes.join(', ') || '-',
time: (q.beginTime || '-') + ' ~ ' + (q.endTime || '-'),
nos: nos.join(' / ')
}
})
const dialogTitle = computed(() => {
if (loading.value) return '异步同步计费进行中'
if (finished.value) return '异步同步计费完成'
return '条件批量同步计费'
})
const progressPercent = computed(() => Math.min(100, Math.max(0, Number(progress.value?.percent) || 0)))
const progressStatus = computed(() => {
const st = (progress.value?.status || '').toUpperCase()
if (st === 'SUCCESS') return 'success'
if (st === 'FAIL') return 'exception'
return undefined
})
const progressMessage = computed(() => mapProgressMessage(progress.value?.message, progress.value))
function mapProgressMessage(message, data) {
const m = message || ''
if (!m || m === 'queued') return '排队中...'
if (m === 'starting') return '正在统计订单...'
if (m === 'no tasks') return '无匹配订单'
if (m === 'failed') return '执行失败'
if (m.startsWith('processing ')) {
return '正在同步计费 ' + m.slice('processing '.length)
}
if (m.startsWith('done ')) {
return '完成:成功 ' + (data?.ok || 0) + ' / 失败 ' + (data?.fail || 0)
}
return m
}
function emitProgress() {
emit('progress', {
loading: loading.value,
finished: finished.value,
percent: progressPercent.value,
status: progress.value?.status || '',
message: progressMessage.value,
total: progress.value?.total || 0,
processed: progress.value?.processed || 0,
ok: progress.value?.ok || 0,
fail: progress.value?.fail || 0,
error: progress.value?.error || ''
})
}
function open(filterQuery, autoStart) {
if (loading.value || finished.value) {
visible.value = true
return
}
query.value = { ...(filterQuery || {}) }
progress.value = {
percent: 0,
message: '',
total: 0,
processed: 0,
ok: 0,
fail: 0,
status: '',
error: ''
}
loading.value = false
finished.value = false
visible.value = true
emitProgress()
if (autoStart) {
submit(true)
}
}
function onClosed() {
if (!loading.value) {
finished.value = false
}
}
function submit(skipConfirm) {
const q = query.value || {}
const codes = Array.isArray(q.customerCodes) ? q.customerCodes.filter(v => v != null && String(v).trim() !== '') : []
if (!codes.length || !q.beginTime || !q.endTime) {
proxy.$modal.msgError('请先选择客户和提货时间')
return
}
const start = () => {
loading.value = true
finished.value = false
progress.value = {
percent: 0,
message: 'queued',
total: 0,
processed: 0,
ok: 0,
fail: 0,
status: 'PENDING',
error: ''
}
emitProgress()
return syncOrderSourceByFilter({
customerCodes: codes,
beginTime: q.beginTime,
endTime: q.endTime,
orderId: q.orderId || undefined,
orgOrderId: q.orgOrderId || undefined,
transferOrderId: q.transferOrderId || undefined,
batchSize: 50
}).then(res => {
const asyncTaskId = res.data?.asyncTaskId || res.data
if (!asyncTaskId) {
throw new Error('asyncTaskId missing')
}
return poll(asyncTaskId)
}).then((data) => {
finished.value = true
emit('success', data)
proxy.$modal.msgSuccess(
'完成:成功 ' + (data.ok || 0) + ' / 失败 ' + (data.fail || 0)
)
}).catch(() => {
if (loading.value) {
finished.value = true
if (!progress.value?.status || progress.value.status === 'PENDING' || progress.value.status === 'RUNNING') {
progress.value = {
...progress.value,
status: 'FAIL',
percent: 100,
message: 'failed',
error: progress.value?.error || '批量同步计费失败'
}
}
}
}).finally(() => {
loading.value = false
emitProgress()
})
}
if (skipConfirm) {
start()
return
}
proxy.$modal.confirm(
'确认按当前筛选分页查询并异步同步计费?',
'二次确认'
).then(() => start()).catch(() => {})
}
function poll(asyncTaskId) {
return new Promise((resolve, reject) => {
const tick = () => {
getOrderSourceSyncProgress(asyncTaskId).then(res => {
const data = res.data || {}
progress.value = {
percent: Number(data.percent) || 0,
message: data.message || '',
total: data.total || 0,
processed: data.processed || 0,
ok: data.ok || 0,
fail: data.fail || 0,
status: data.status || '',
error: data.error || ''
}
emitProgress()
const st = (data.status || '').toUpperCase()
if (st === 'SUCCESS') {
progress.value.percent = 100
resolve(data)
return
}
if (st === 'FAIL') {
proxy.$modal.msgError(data.error || data.message || '批量同步计费失败')
reject(new Error(data.error || 'fail'))
return
}
setTimeout(tick, 1500)
}).catch(err => reject(err))
}
tick()
})
}
defineExpose({ open })
</script>
<style scoped>
.filter-retry-progress {
padding: 8px 4px 0;
}
.filter-retry-progress__msg {
margin-top: 14px;
font-size: 14px;
font-weight: 600;
color: #303133;
line-height: 1.5;
}
.filter-retry-progress__text {
margin-top: 8px;
font-size: 12px;
color: #606266;
line-height: 1.6;
}
.filter-retry-progress__error {
margin-top: 8px;
font-size: 12px;
color: #f56c6c;
word-break: break-all;
}
.filter-retry-progress__hint {
margin: 12px 0 0;
font-size: 12px;
color: #909399;
line-height: 1.5;
}
</style>