petstore-frontend/src/pages/mine/Onboarding.vue

176 lines
7.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="page-shell onboarding-page">
<view class="top-nav nav-gradient" :style="navSafeStyle">
<view class="nav-back" @click="goBack"><AppIcon name="back" :size="18" color="#ffffff" /></view>
<text class="nav-title">门店开通</text>
<view class="nav-placeholder" />
</view>
<view class="page-body">
<view class="hero-card">
<text class="hero-kicker">PILOT READINESS</text>
<view class="hero-title-row">
<text class="hero-title">准备好真实接待</text>
<text class="status-tag" :class="`status-${status?.status || 'loading'}`">{{ statusLabel }}</text>
</view>
<text class="hero-desc">资料容量和服务项目是必填项单人门店可以稍后邀请员工</text>
<view class="progress-track"><view class="progress-fill" :style="`width:${progress}%`" /></view>
<text class="progress-text">{{ status?.completedStepCount || 0 }}/{{ status?.totalStepCount || 4 }} 步完成</text>
</view>
<view class="step-list">
<view
v-for="step in status?.steps || []"
:key="step.id"
class="step-card"
:class="{ done: step.completed }"
@click="openStep(step.id)"
>
<view class="step-icon">{{ step.completed ? '✓' : step.required ? '!' : '+' }}</view>
<view class="step-copy">
<view class="step-title-row">
<text class="step-title">{{ step.title }}</text>
<text class="step-kind">{{ step.required ? '必填' : '选填' }}</text>
</view>
<text class="step-hint">{{ step.hint }}</text>
</view>
<text class="step-arrow"></text>
</view>
</view>
<view v-if="status" class="finish-card">
<text v-if="status.status === 'completed'" class="finish-copy success-copy">门店已完成开通,可以进入真实试点。</text>
<text v-else-if="status.readyToComplete" class="finish-copy">必填项已齐,请确认后完成开通。</text>
<text v-else class="finish-copy">尚缺:{{ status.missingRequiredSteps.join('、') }}</text>
<button
v-if="status.status !== 'completed'"
class="van-button van-button--primary van-button--block finish-btn"
:disabled="!status.readyToComplete || completing"
@click="finish"
>{{ completing ? '确认中…' : '完成门店开通' }}</button>
<button v-else class="van-button van-button--primary van-button--block finish-btn" @click="goHome">进入工作台</button>
</view>
</view>
</view>
</template>
<script setup>
import { computed, ref } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import AppIcon from '../../components/AppIcon.vue'
import { completeOnboarding, getOnboardingStatus } from '../../api/index.js'
import { openAppPage } from '../../utils/globalState.js'
const status = ref(null)
const completing = ref(false)
const navSafeStyle = (() => {
const statusBarHeight = uni.getSystemInfoSync?.().statusBarHeight || 20
let navHeight = statusBarHeight + 44
const menuRect = uni.getMenuButtonBoundingClientRect?.()
if (menuRect?.top && menuRect?.height) {
const gap = Math.max(menuRect.top - statusBarHeight, 4)
navHeight = statusBarHeight + gap * 2 + menuRect.height
}
return `padding-top:${statusBarHeight}px;height:${navHeight}px;`
})()
const progress = computed(() => {
if (!status.value?.totalStepCount) return 0
return Math.round(status.value.completedStepCount / status.value.totalStepCount * 100)
})
const statusLabel = computed(() => {
if (status.value?.status === 'completed') return '已开通'
if (status.value?.status === 'unknown') return '待确认'
if (!status.value) return '加载中'
return '开通中'
})
async function load() {
const response = await getOnboardingStatus()
if (response.code === 200) {
status.value = response.data
} else {
uni.showToast({ title: response.message || '开通清单加载失败', icon: 'none' })
}
}
function openStep(stepId) {
const routes = {
profile: '/pages/mine/Store',
booking: '/pages/mine/Store',
services: '/pages/mine/ServiceType',
team: '/pages/mine/Staff'
}
uni.navigateTo({ url: routes[stepId] || '/pages/mine/Mine' })
}
function finish() {
uni.showModal({
title: '完成门店开通',
content: '确认门店资料、号源容量与服务项目已可用于真实接待?',
confirmText: '确认完成',
success: async (result) => {
if (!result.confirm) return
completing.value = true
try {
const response = await completeOnboarding()
if (response.code !== 200) {
uni.showToast({ title: response.message || '暂不能完成开通', icon: 'none' })
return
}
status.value = response.data
uni.showToast({ title: '门店已开通', icon: 'success' })
} finally {
completing.value = false
}
}
})
}
function goBack() {
const pages = getCurrentPages()
if (pages.length > 1) uni.navigateBack()
else openAppPage('mine')
}
function goHome() {
openAppPage('home')
}
onShow(load)
</script>
<style scoped>
.onboarding-page { min-height: 100vh; padding-bottom: 80rpx; background: #f6f8f7; }
.top-nav { position: sticky; top: 0; z-index: 10; display: flex; align-items: center; justify-content: space-between; padding: 14px 16px; }
.nav-back,.nav-placeholder { width: 32px; }
.nav-title { color: #fff; font-size: 18px; font-weight: 700; }
.page-body { padding: 14px; }
.hero-card { padding: 20px; color: #173b29; background: radial-gradient(circle at 88% 4%,rgba(46,185,109,.18),transparent 35%),#fff; border: 1px solid #d8eee1; border-radius: 18px; box-shadow: 0 8px 24px rgba(20,70,43,.06); }
.hero-kicker { display: block; color: var(--c-brand); font-size: 11px; font-weight: 800; letter-spacing: .14em; }
.hero-title-row,.step-title-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.hero-title { margin: 8px 0 5px; font-size: 23px; font-weight: 800; }
.hero-desc { display: block; color: #6d7e75; font-size: 13px; line-height: 1.55; }
.status-tag { flex-shrink: 0; padding: 5px 9px; color: #8a6518; font-size: 11px; font-weight: 700; background: #fff3cd; border-radius: 999px; }
.status-completed { color: #16733f; background: #dff6e9; }
.progress-track { height: 8px; margin-top: 18px; overflow: hidden; background: #edf1ee; border-radius: 99px; }
.progress-fill { height: 100%; background: linear-gradient(90deg,#28a961,var(--c-brand)); border-radius: 99px; transition: width .25s ease; }
.progress-text { display: block; margin-top: 7px; color: #7b887f; font-size: 11px; text-align: right; }
.step-list { display: grid; gap: 10px; margin-top: 14px; }
.step-card { display: flex; align-items: center; gap: 12px; padding: 15px; background: #fff; border: 1px solid #e8ece9; border-radius: 14px; }
.step-card.done { background: #f7fdf9; border-color: #d6eddf; }
.step-icon { display: flex; flex-shrink: 0; align-items: center; justify-content: center; width: 34px; height: 34px; color: #a17016; font-size: 14px; font-weight: 800; background: #fff2cd; border-radius: 10px; }
.step-card.done .step-icon { color: #fff; background: var(--c-brand); }
.step-copy { flex: 1; min-width: 0; }
.step-title { color: #27342d; font-size: 15px; font-weight: 700; }
.step-kind { color: #9aa49e; font-size: 10px; }
.step-hint { display: block; margin-top: 4px; color: #879189; font-size: 12px; line-height: 1.4; }
.step-arrow { color: #a9b1ac; font-size: 24px; }
.finish-card { padding: 16px; margin-top: 14px; background: #fff; border: 1px solid #e8ece9; border-radius: 14px; }
.finish-copy { display: block; margin-bottom: 12px; color: #7c6a3d; font-size: 13px; line-height: 1.5; }
.success-copy { color: #16733f; }
.finish-btn { margin: 0 !important; }
</style>