front/src/views/h5/customer-center/components/H5CreateOrderFlow.vue
zzh 8cf883c90c feat: simplify WeChat register UX and gate orders on contact info
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-27 13:07:29 +08:00

117 lines
3.1 KiB
Vue

<template>
<H5OrderTypeSheet v-model:visible="chooserVisible" @select="onSelectType" />
<H5PlaceOrderSheet v-model:visible="logisticsVisible" @success="onChildSuccess" />
<H5PickupOrderSheet v-model:visible="pickupVisible" @success="onChildSuccess" />
<H5CompleteContactSheet
v-model:visible="contactVisible"
:profile="contactProfile"
@success="onContactSuccess"
@cancel="onContactCancel"
/>
</template>
<script lang="ts" setup>
import { computed, ref, watch } from 'vue';
import type { CustomerProfileCompleteness } from '@/api/customer';
import { checkCustomerContactRequired } from '@/utils/customerContactGate';
import H5OrderTypeSheet from './H5OrderTypeSheet.vue';
import H5PlaceOrderSheet from './H5PlaceOrderSheet.vue';
import H5PickupOrderSheet from './H5PickupOrderSheet.vue';
import H5CompleteContactSheet from './H5CompleteContactSheet.vue';
defineOptions({ name: 'H5CreateOrderFlow' });
const props = defineProps<{ visible: boolean }>();
const emit = defineEmits<{
'update:visible': [boolean];
success: [];
}>();
type Phase = 'idle' | 'choose' | 'logistics' | 'pickup';
const phase = ref<Phase>('idle');
/** 从选择页切到表单时,避免 chooser 关闭误关整个 flow */
let switching = false;
const contactVisible = ref(false);
const contactProfile = ref<CustomerProfileCompleteness | null>(null);
const contactChecking = ref(false);
/** 补全成功后进入的阶段;取消则关闭整个 flow */
let pendingPhase: Phase | null = null;
watch(
() => props.visible,
async (v) => {
if (!v) {
phase.value = 'idle';
contactVisible.value = false;
pendingPhase = null;
return;
}
// 一进入下单流程即校验联系方式
contactChecking.value = true;
try {
const { needContact, profile } = await checkCustomerContactRequired();
if (needContact) {
contactProfile.value = profile;
pendingPhase = 'choose';
phase.value = 'idle';
contactVisible.value = true;
return;
}
phase.value = 'choose';
} finally {
contactChecking.value = false;
}
},
);
const chooserVisible = computed({
get: () => props.visible && phase.value === 'choose',
set: (v: boolean) => {
if (!v && phase.value === 'choose' && !switching) {
emit('update:visible', false);
}
},
});
const logisticsVisible = computed({
get: () => props.visible && phase.value === 'logistics',
set: (v: boolean) => {
if (!v && phase.value === 'logistics') {
emit('update:visible', false);
}
},
});
const pickupVisible = computed({
get: () => props.visible && phase.value === 'pickup',
set: (v: boolean) => {
if (!v && phase.value === 'pickup') {
emit('update:visible', false);
}
},
});
function onSelectType(type: 'logistics' | 'pickup') {
switching = true;
phase.value = type;
requestAnimationFrame(() => {
switching = false;
});
}
function onContactSuccess() {
const next = pendingPhase || 'choose';
pendingPhase = null;
phase.value = next;
}
function onContactCancel() {
pendingPhase = null;
emit('update:visible', false);
}
function onChildSuccess() {
emit('success');
}
</script>