176 lines
7.7 KiB
Vue
176 lines
7.7 KiB
Vue
<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>
|