feat: 完善移动端会员业务流程

This commit is contained in:
richjohnrambo
2026-10-13 14:19:55 +08:00
parent 32c6238751
commit b2ebea4c80
59 changed files with 1516 additions and 470 deletions
+40 -14
View File
@@ -5,7 +5,7 @@
<img :src="mobileAssets.claimMachine" alt="每日参与任务,赚取更多佣金" />
<button
type="button"
:disabled="claiming || !halls.length || eligibility?.eligible === false"
:disabled="claiming || !eligibleHalls.length"
aria-label="自动领取任务"
@click="claim"
>
@@ -22,7 +22,7 @@
</div>
</section>
<div v-if="eligibility?.eligible === false" class="m-claim2-warning">
<div v-if="halls.length && !eligibleHalls.length && eligibility?.eligible === false" class="m-claim2-warning">
<n-icon :component="AlertCircleOutline" />
<span>{{ eligibility.message || '暂不满足领取条件' }}</span>
<button v-if="eligibility.pending_order_id" type="button" @click="openPendingOrder">查看任务</button>
@@ -100,7 +100,7 @@
import { useRouter } from 'vue-router';
import { AlertCircleOutline, ImageOutline } from '@vicons/ionicons5';
import AsyncState from '../components/AsyncState.vue';
import { dateTime, errorText, money, request, safeImage } from '../api';
import { MobileApiError, dateTime, errorText, money, request, safeImage } from '../api';
import { mobileAssets } from '../assets';
type ResultState = 'matching' | 'success' | 'failed' | 'cancelled';
@@ -112,6 +112,7 @@
const halls = ref<any[]>([]);
const stats = ref<any>();
const eligibility = ref<any>();
const eligibilityByHall = ref<Record<number, any>>({});
const order = ref<any>();
const showResult = ref(false);
const resultState = ref<ResultState>('matching');
@@ -128,6 +129,9 @@
{ label: '今日佣金', value: money(stats.value?.today_commission_amount), icon: mobileAssets.claimToday },
{ label: '今日领取数', value: stats.value?.today_claim_count ?? 0, icon: mobileAssets.claimCount },
]);
const eligibleHalls = computed(() =>
halls.value.filter((hall) => eligibilityByHall.value[hall.id]?.eligible)
);
const businessHours = computed(() => {
const start = String(eligibility.value?.business_start_at || '09:00').slice(0, 5);
const end = String(eligibility.value?.business_end_at || '21:00').slice(0, 5);
@@ -154,7 +158,7 @@
]);
halls.value = hallData || [];
stats.value = statData;
if (halls.value.length) await loadEligibility(halls.value[0].id);
if (halls.value.length) await loadEligibilities();
} catch (err) {
error.value = errorText(err);
} finally {
@@ -162,8 +166,15 @@
}
}
async function loadEligibility(hallID: number) {
eligibility.value = await request({ url: '/v1/tasks/eligibility', params: { hall_id: hallID } });
async function loadEligibilities() {
const entries = await Promise.all(
halls.value.map(async (hall) => [
hall.id,
await request<any>({ url: '/v1/tasks/eligibility', params: { hall_id: hall.id } }),
] as const)
);
eligibilityByHall.value = Object.fromEntries(entries);
eligibility.value = entries.find(([, value]) => value?.eligible)?.[1] || entries[0]?.[1];
}
async function claim() {
@@ -171,18 +182,33 @@
claiming.value = true;
pollFinished.value = false;
try {
await loadEligibility(halls.value[0].id);
if (!eligibility.value?.eligible) {
await loadEligibilities();
const candidates = eligibleHalls.value;
if (!candidates.length) {
window.$message.warning(eligibility.value?.message || '暂不满足领取条件');
return;
}
openMatching();
order.value = await request<any>({
url: '/v1/task-orders/claim',
method: 'POST',
data: { hall_id: halls.value[0].id },
});
handleOrder(order.value);
let stockError: unknown;
for (const hall of candidates) {
eligibility.value = eligibilityByHall.value[hall.id];
try {
order.value = await request<any>({
url: '/v1/task-orders/claim',
method: 'POST',
data: { hall_id: hall.id },
});
handleOrder(order.value);
return;
} catch (err) {
if (err instanceof MobileApiError && err.reason === 'NO_TASK_AVAILABLE') {
stockError = err;
continue;
}
throw err;
}
}
throw stockError || new Error('当前暂无可领取任务');
} catch (err) {
failureMessage.value = errorText(err);
resultState.value = 'failed';