fix: improve task matching progress feedback
Deploy olons-server production / deploy (push) Successful in 1m52s
Deploy olons-server production / deploy (push) Successful in 1m52s
This commit is contained in:
@@ -52,8 +52,8 @@
|
|||||||
<img class="orbit" :src="mobileAssets.claimSpinner" alt="" />
|
<img class="orbit" :src="mobileAssets.claimSpinner" alt="" />
|
||||||
<img class="gift" :src="mobileAssets.claimMatchingGift" alt="" />
|
<img class="gift" :src="mobileAssets.claimMatchingGift" alt="" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="isAutoMatching" class="m-match-progress"><i :style="{ width: `${matchProgress}%` }"></i></div>
|
<div class="m-match-progress"><i :style="{ width: `${matchProgress}%` }"></i></div>
|
||||||
<strong v-if="isAutoMatching" class="m-match-percent">{{ matchProgress }}%</strong>
|
<strong class="m-match-percent">{{ matchProgress }}%</strong>
|
||||||
<ol class="m-match-steps m-match-steps--single">
|
<ol class="m-match-steps m-match-steps--single">
|
||||||
<li class="active"><span>✓</span>正在匹配订单</li>
|
<li class="active"><span>✓</span>正在匹配订单</li>
|
||||||
</ol>
|
</ol>
|
||||||
@@ -117,12 +117,11 @@
|
|||||||
const resultState = ref<ResultState>('matching');
|
const resultState = ref<ResultState>('matching');
|
||||||
const failureMessage = ref('当前任务库存未分配成功,请稍后重试');
|
const failureMessage = ref('当前任务库存未分配成功,请稍后重试');
|
||||||
const matchProgress = ref(18);
|
const matchProgress = ref(18);
|
||||||
const matchRemainingSeconds = ref(0);
|
|
||||||
const matchTotalSeconds = ref(0);
|
|
||||||
const pollFinished = ref(false);
|
const pollFinished = ref(false);
|
||||||
|
const matchProgressDurationSeconds = 15 * 60;
|
||||||
let pollTimer: number | undefined;
|
let pollTimer: number | undefined;
|
||||||
let countdownTimer: number | undefined;
|
let countdownTimer: number | undefined;
|
||||||
let pollCount = 0;
|
let pollFailureCount = 0;
|
||||||
|
|
||||||
const statCards = computed(() => [
|
const statCards = computed(() => [
|
||||||
{ label: '总资产', value: money(stats.value?.total_asset_amount), icon: mobileAssets.claimTotal },
|
{ label: '总资产', value: money(stats.value?.total_asset_amount), icon: mobileAssets.claimTotal },
|
||||||
@@ -139,11 +138,10 @@
|
|||||||
Number(item?.available_amount || 0) < Number(item.minimum_amount)
|
Number(item?.available_amount || 0) < Number(item.minimum_amount)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
const isAutoMatching = computed(() => order.value?.dispatch_mode !== 'manual');
|
|
||||||
const matchingHint = computed(() => {
|
const matchingHint = computed(() => {
|
||||||
if (order.value?.dispatch_mode === 'manual') return '订单已领取,正在等待后台匹配...';
|
return pollFinished.value
|
||||||
if (matchRemainingSeconds.value > 0) return `系统正在派单... ${formatCountdown(matchRemainingSeconds.value)}`;
|
? '订单仍在匹配,可稍后前往记录查看'
|
||||||
return pollFinished.value ? '订单仍在匹配,可稍后前往记录查看' : '系统正在为您匹配订单,请稍等...';
|
: '订单已领取,正在等待后台匹配...';
|
||||||
});
|
});
|
||||||
const resultIcon = computed(() => resultState.value === 'success' ? mobileAssets.claimSuccess : resultState.value === 'failed' ? mobileAssets.claimFailed : mobileAssets.claimCancelled);
|
const resultIcon = computed(() => resultState.value === 'success' ? mobileAssets.claimSuccess : resultState.value === 'failed' ? mobileAssets.claimFailed : mobileAssets.claimCancelled);
|
||||||
const resultTitle = computed(() => resultState.value === 'success' ? '任务获取成功' : resultState.value === 'failed' ? '领取失败' : '已取消任务');
|
const resultTitle = computed(() => resultState.value === 'success' ? '任务获取成功' : resultState.value === 'failed' ? '领取失败' : '已取消任务');
|
||||||
@@ -247,23 +245,13 @@
|
|||||||
resultState.value = 'matching';
|
resultState.value = 'matching';
|
||||||
showResult.value = true;
|
showResult.value = true;
|
||||||
matchProgress.value = 18;
|
matchProgress.value = 18;
|
||||||
matchRemainingSeconds.value = 0;
|
pollFailureCount = 0;
|
||||||
matchTotalSeconds.value = 0;
|
|
||||||
pollCount = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleOrder(value: any) {
|
function handleOrder(value: any) {
|
||||||
order.value = value;
|
order.value = value;
|
||||||
if (value?.status === 'matching') {
|
if (value?.status === 'matching') {
|
||||||
if (value?.dispatch_mode === 'manual') {
|
startMatchingProgress(value);
|
||||||
if (countdownTimer) window.clearInterval(countdownTimer);
|
|
||||||
countdownTimer = undefined;
|
|
||||||
matchRemainingSeconds.value = 0;
|
|
||||||
matchTotalSeconds.value = 0;
|
|
||||||
matchProgress.value = 18;
|
|
||||||
} else {
|
|
||||||
startCountdown(value);
|
|
||||||
}
|
|
||||||
schedulePoll();
|
schedulePoll();
|
||||||
} else if (['pending', 'processing', 'completed'].includes(value?.status)) {
|
} else if (['pending', 'processing', 'completed'].includes(value?.status)) {
|
||||||
matchProgress.value = 100;
|
matchProgress.value = 100;
|
||||||
@@ -282,55 +270,38 @@
|
|||||||
function schedulePoll() {
|
function schedulePoll() {
|
||||||
if (!order.value?.id) return;
|
if (!order.value?.id) return;
|
||||||
if (pollTimer) window.clearTimeout(pollTimer);
|
if (pollTimer) window.clearTimeout(pollTimer);
|
||||||
const scheduledAt = Date.parse(String(order.value?.scheduled_match_at || ''));
|
const wait = order.value?.dispatch_mode === 'manual' ? 5000 : 3000;
|
||||||
const wait = order.value?.dispatch_mode === 'manual'
|
|
||||||
? 10000
|
|
||||||
: Number.isFinite(scheduledAt) && scheduledAt > Date.now()
|
|
||||||
? scheduledAt - Date.now() + 300
|
|
||||||
: 1200;
|
|
||||||
pollTimer = window.setTimeout(async () => {
|
pollTimer = window.setTimeout(async () => {
|
||||||
pollCount += 1;
|
|
||||||
try {
|
try {
|
||||||
|
pollFailureCount = 0;
|
||||||
handleOrder(await request({ url: `/v1/task-orders/${order.value.id}` }));
|
handleOrder(await request({ url: `/v1/task-orders/${order.value.id}` }));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (pollCount >= 10) {
|
pollFailureCount += 1;
|
||||||
|
if (pollFailureCount >= 10) {
|
||||||
pollFinished.value = true;
|
pollFinished.value = true;
|
||||||
stopPolling();
|
stopPolling();
|
||||||
} else schedulePoll();
|
} else schedulePoll();
|
||||||
}
|
}
|
||||||
if (resultState.value === 'matching' && pollCount >= 10) {
|
|
||||||
pollFinished.value = true;
|
|
||||||
stopPolling();
|
|
||||||
}
|
|
||||||
}, wait);
|
}, wait);
|
||||||
}
|
}
|
||||||
|
|
||||||
function startCountdown(value: any) {
|
function startMatchingProgress(value: any) {
|
||||||
if (countdownTimer) window.clearInterval(countdownTimer);
|
if (countdownTimer) window.clearInterval(countdownTimer);
|
||||||
if (value?.dispatch_mode === 'manual') return;
|
const createdAt = Date.parse(String(value?.created_at || ''));
|
||||||
const scheduledAt = Date.parse(String(value?.scheduled_match_at || ''));
|
const startedAt = Number.isFinite(createdAt) ? createdAt : Date.now();
|
||||||
if (!Number.isFinite(scheduledAt)) return;
|
|
||||||
matchTotalSeconds.value = Math.max(1, Number(value?.dispatch_delay_seconds || 0));
|
|
||||||
const update = () => {
|
const update = () => {
|
||||||
matchRemainingSeconds.value = Math.max(0, Math.ceil((scheduledAt - Date.now()) / 1000));
|
const elapsedSeconds = Math.max(0, Math.floor((Date.now() - startedAt) / 1000));
|
||||||
const elapsed = Math.max(0, matchTotalSeconds.value - matchRemainingSeconds.value);
|
matchProgress.value = Math.min(
|
||||||
matchProgress.value = Math.min(88, Math.round(18 + elapsed / matchTotalSeconds.value * 70));
|
88,
|
||||||
if (matchRemainingSeconds.value === 0 && countdownTimer) {
|
Math.round(18 + elapsedSeconds / matchProgressDurationSeconds * 70)
|
||||||
|
);
|
||||||
|
if (matchProgress.value >= 88 && countdownTimer) {
|
||||||
window.clearInterval(countdownTimer);
|
window.clearInterval(countdownTimer);
|
||||||
countdownTimer = undefined;
|
countdownTimer = undefined;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
update();
|
update();
|
||||||
if (matchRemainingSeconds.value > 0) countdownTimer = window.setInterval(update, 1000);
|
if (matchProgress.value < 88) countdownTimer = window.setInterval(update, 1000);
|
||||||
}
|
|
||||||
|
|
||||||
function formatCountdown(totalSeconds: number) {
|
|
||||||
const hours = Math.floor(totalSeconds / 3600);
|
|
||||||
const minutes = Math.floor(totalSeconds % 3600 / 60);
|
|
||||||
const seconds = totalSeconds % 60;
|
|
||||||
return hours > 0
|
|
||||||
? [hours, minutes, seconds].map((item) => String(item).padStart(2, '0')).join(':')
|
|
||||||
: [minutes, seconds].map((item) => String(item).padStart(2, '0')).join(':');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function stopPolling(clearProgress = true) {
|
function stopPolling(clearProgress = true) {
|
||||||
|
|||||||
Reference in New Issue
Block a user