feat(customer): implement customer analysis page and drawer flow
This commit is contained in:
158
apps/web-antd/src/views/customer/analysis/index.vue
Normal file
158
apps/web-antd/src/views/customer/analysis/index.vue
Normal file
@@ -0,0 +1,158 @@
|
||||
<script setup lang="ts">
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { Empty, Spin } from 'ant-design-vue';
|
||||
|
||||
import CustomerDetailDrawer from '#/views/customer/list/components/CustomerDetailDrawer.vue';
|
||||
import CustomerProfileDrawer from '#/views/customer/list/components/CustomerProfileDrawer.vue';
|
||||
|
||||
import AmountDistributionCard from './components/AmountDistributionCard.vue';
|
||||
import AnalysisStatsGrid from './components/AnalysisStatsGrid.vue';
|
||||
import AnalysisToolbar from './components/AnalysisToolbar.vue';
|
||||
import CompositionCard from './components/CompositionCard.vue';
|
||||
import GrowthTrendCard from './components/GrowthTrendCard.vue';
|
||||
import MemberDetailDrawer from './components/MemberDetailDrawer.vue';
|
||||
import RfmMatrixCard from './components/RfmMatrixCard.vue';
|
||||
import SegmentDrawer from './components/SegmentDrawer.vue';
|
||||
import TopCustomerTableCard from './components/TopCustomerTableCard.vue';
|
||||
import { useCustomerAnalysisPage } from './composables/useCustomerAnalysisPage';
|
||||
|
||||
const {
|
||||
detail,
|
||||
handleExport,
|
||||
handleSegmentPageChange,
|
||||
handleSegmentSearch,
|
||||
isDetailDrawerOpen,
|
||||
isDetailLoading,
|
||||
isExporting,
|
||||
isMemberDrawerOpen,
|
||||
isMemberLoading,
|
||||
isOverviewLoading,
|
||||
isProfileDrawerOpen,
|
||||
isProfileLoading,
|
||||
isSegmentDrawerOpen,
|
||||
isSegmentLoading,
|
||||
isStoreLoading,
|
||||
memberDetail,
|
||||
openDetail,
|
||||
openMember,
|
||||
openMemberFromDetail,
|
||||
openProfile,
|
||||
openProfilePage,
|
||||
openSegmentByCode,
|
||||
openTopCustomerDetail,
|
||||
overview,
|
||||
period,
|
||||
profile,
|
||||
segmentKeyword,
|
||||
segmentPagination,
|
||||
segmentResult,
|
||||
selectedStoreId,
|
||||
setDetailDrawerOpen,
|
||||
setMemberDrawerOpen,
|
||||
setPeriod,
|
||||
setProfileDrawerOpen,
|
||||
setSegmentDrawerOpen,
|
||||
setSegmentKeyword,
|
||||
setSelectedStoreId,
|
||||
storeOptions,
|
||||
} = useCustomerAnalysisPage();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page title="客户分析" content-class="page-customer-analysis">
|
||||
<div class="ca-page">
|
||||
<AnalysisToolbar
|
||||
:selected-store-id="selectedStoreId"
|
||||
:store-options="storeOptions"
|
||||
:is-store-loading="isStoreLoading"
|
||||
:period="period"
|
||||
:is-exporting="isExporting"
|
||||
@update:selected-store-id="setSelectedStoreId"
|
||||
@update:period="setPeriod"
|
||||
@export="handleExport"
|
||||
/>
|
||||
|
||||
<div v-if="!selectedStoreId" class="ca-empty">
|
||||
<Empty description="暂无门店,请先创建门店" />
|
||||
</div>
|
||||
|
||||
<Spin v-else :spinning="isOverviewLoading">
|
||||
<AnalysisStatsGrid :overview="overview" @segment="openSegmentByCode" />
|
||||
|
||||
<div class="ca-grid">
|
||||
<GrowthTrendCard
|
||||
:points="overview.growthTrend"
|
||||
@segment="openSegmentByCode"
|
||||
/>
|
||||
|
||||
<CompositionCard
|
||||
:items="overview.composition"
|
||||
:total-customers="overview.totalCustomers"
|
||||
@segment="openSegmentByCode"
|
||||
/>
|
||||
|
||||
<AmountDistributionCard
|
||||
:items="overview.amountDistribution"
|
||||
@segment="openSegmentByCode"
|
||||
/>
|
||||
|
||||
<RfmMatrixCard
|
||||
:rows="overview.rfmRows"
|
||||
@segment="openSegmentByCode"
|
||||
/>
|
||||
|
||||
<TopCustomerTableCard
|
||||
:rows="overview.topCustomers"
|
||||
@detail="openTopCustomerDetail"
|
||||
@segment="openSegmentByCode"
|
||||
/>
|
||||
</div>
|
||||
</Spin>
|
||||
</div>
|
||||
|
||||
<SegmentDrawer
|
||||
:open="isSegmentDrawerOpen"
|
||||
:loading="isSegmentLoading"
|
||||
:result="segmentResult"
|
||||
:pagination="segmentPagination"
|
||||
:keyword="segmentKeyword"
|
||||
@close="setSegmentDrawerOpen(false)"
|
||||
@update:keyword="setSegmentKeyword"
|
||||
@search="handleSegmentSearch"
|
||||
@page-change="handleSegmentPageChange"
|
||||
@detail="openDetail"
|
||||
@profile="openProfile"
|
||||
@member="openMember"
|
||||
/>
|
||||
|
||||
<CustomerDetailDrawer
|
||||
:open="isDetailDrawerOpen"
|
||||
:loading="isDetailLoading"
|
||||
:detail="detail"
|
||||
:show-member-action="true"
|
||||
@close="setDetailDrawerOpen(false)"
|
||||
@profile="openProfile"
|
||||
@profile-page="openProfilePage"
|
||||
@member="openMemberFromDetail"
|
||||
/>
|
||||
|
||||
<CustomerProfileDrawer
|
||||
:open="isProfileDrawerOpen"
|
||||
:loading="isProfileLoading"
|
||||
:profile="profile"
|
||||
@close="setProfileDrawerOpen(false)"
|
||||
/>
|
||||
|
||||
<MemberDetailDrawer
|
||||
:open="isMemberDrawerOpen"
|
||||
:loading="isMemberLoading"
|
||||
:detail="memberDetail"
|
||||
@close="setMemberDrawerOpen(false)"
|
||||
/>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
@import './styles/index.less';
|
||||
</style>
|
||||
Reference in New Issue
Block a user