feat(customer): implement customer analysis page and drawer flow
This commit is contained in:
@@ -0,0 +1,244 @@
|
||||
import type {
|
||||
CustomerAnalysisOverviewDto,
|
||||
CustomerAnalysisPeriodFilter,
|
||||
CustomerAnalysisSegmentCode,
|
||||
CustomerAnalysisSegmentListResultDto,
|
||||
CustomerDetailDto,
|
||||
CustomerMemberDetailDto,
|
||||
CustomerProfileDto,
|
||||
} from '#/api/customer';
|
||||
import type { StoreListItemDto } from '#/api/store';
|
||||
|
||||
import { computed, onActivated, onMounted, reactive, ref, watch } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
|
||||
import { createNavigationActions } from '#/views/customer/list/composables/customer-list-page/navigation-actions';
|
||||
|
||||
import {
|
||||
CUSTOMER_ANALYSIS_VIEW_PERMISSION,
|
||||
DEFAULT_PERIOD,
|
||||
DEFAULT_SEGMENT_CODE,
|
||||
EMPTY_OVERVIEW,
|
||||
} from './customer-analysis-page/constants';
|
||||
import { createDataActions } from './customer-analysis-page/data-actions';
|
||||
import { createDrawerActions } from './customer-analysis-page/drawer-actions';
|
||||
import { createExportActions } from './customer-analysis-page/export-actions';
|
||||
import { createMemberActions } from './customer-analysis-page/member-actions';
|
||||
import { createSegmentActions } from './customer-analysis-page/segment-actions';
|
||||
|
||||
export function useCustomerAnalysisPage() {
|
||||
const accessStore = useAccessStore();
|
||||
const router = useRouter();
|
||||
|
||||
const stores = ref<StoreListItemDto[]>([]);
|
||||
const selectedStoreId = ref('');
|
||||
const isStoreLoading = ref(false);
|
||||
|
||||
const period = ref<CustomerAnalysisPeriodFilter>(DEFAULT_PERIOD);
|
||||
const overview = ref<CustomerAnalysisOverviewDto>({ ...EMPTY_OVERVIEW });
|
||||
const isOverviewLoading = ref(false);
|
||||
|
||||
const segmentResult = ref<CustomerAnalysisSegmentListResultDto | null>(null);
|
||||
const isSegmentDrawerOpen = ref(false);
|
||||
const isSegmentLoading = ref(false);
|
||||
const segmentKeyword = ref('');
|
||||
const currentSegmentCode =
|
||||
ref<CustomerAnalysisSegmentCode>(DEFAULT_SEGMENT_CODE);
|
||||
const segmentPagination = reactive({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
});
|
||||
|
||||
const detail = ref<CustomerDetailDto | null>(null);
|
||||
const isDetailDrawerOpen = ref(false);
|
||||
const isDetailLoading = ref(false);
|
||||
|
||||
const profile = ref<CustomerProfileDto | null>(null);
|
||||
const isProfileDrawerOpen = ref(false);
|
||||
const isProfileLoading = ref(false);
|
||||
|
||||
const memberDetail = ref<CustomerMemberDetailDto | null>(null);
|
||||
const isMemberDrawerOpen = ref(false);
|
||||
const isMemberLoading = ref(false);
|
||||
|
||||
const isExporting = ref(false);
|
||||
|
||||
const storeOptions = computed(() =>
|
||||
stores.value.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
})),
|
||||
);
|
||||
|
||||
const accessCodeSet = computed(
|
||||
() => new Set((accessStore.accessCodes ?? []).map(String)),
|
||||
);
|
||||
const canExport = computed(() =>
|
||||
accessCodeSet.value.has(CUSTOMER_ANALYSIS_VIEW_PERMISSION),
|
||||
);
|
||||
|
||||
const { loadStores, loadOverview, resetOverview } = createDataActions({
|
||||
stores,
|
||||
selectedStoreId,
|
||||
period,
|
||||
overview,
|
||||
isStoreLoading,
|
||||
isOverviewLoading,
|
||||
});
|
||||
|
||||
const {
|
||||
openSegment,
|
||||
loadSegmentData,
|
||||
setSegmentDrawerOpen,
|
||||
setSegmentKeyword,
|
||||
handleSegmentSearch,
|
||||
handleSegmentPageChange,
|
||||
} = createSegmentActions({
|
||||
selectedStoreId,
|
||||
period,
|
||||
currentSegmentCode,
|
||||
keyword: segmentKeyword,
|
||||
result: segmentResult,
|
||||
isSegmentDrawerOpen,
|
||||
isSegmentLoading,
|
||||
pagination: segmentPagination,
|
||||
});
|
||||
|
||||
const { openMember, setMemberDrawerOpen } = createMemberActions({
|
||||
selectedStoreId,
|
||||
detail: memberDetail,
|
||||
isMemberDrawerOpen,
|
||||
isMemberLoading,
|
||||
});
|
||||
|
||||
const { openDetail, openProfile, setDetailDrawerOpen, setProfileDrawerOpen } =
|
||||
createDrawerActions({
|
||||
selectedStoreId,
|
||||
detail,
|
||||
isDetailDrawerOpen,
|
||||
isDetailLoading,
|
||||
profile,
|
||||
isProfileDrawerOpen,
|
||||
isProfileLoading,
|
||||
});
|
||||
|
||||
const { openProfilePage } = createNavigationActions({
|
||||
selectedStoreId,
|
||||
router,
|
||||
});
|
||||
|
||||
const { handleExport } = createExportActions({
|
||||
selectedStoreId,
|
||||
period,
|
||||
isExporting,
|
||||
canExport,
|
||||
});
|
||||
|
||||
function setSelectedStoreId(value: string) {
|
||||
selectedStoreId.value = value;
|
||||
}
|
||||
|
||||
function setPeriod(value: string) {
|
||||
const normalized = (value ||
|
||||
DEFAULT_PERIOD) as CustomerAnalysisPeriodFilter;
|
||||
period.value = normalized;
|
||||
}
|
||||
|
||||
async function openSegmentByCode(segmentCode: CustomerAnalysisSegmentCode) {
|
||||
await openSegment(segmentCode);
|
||||
}
|
||||
|
||||
async function openTopCustomerDetail(customerKey: string) {
|
||||
await openDetail(customerKey);
|
||||
}
|
||||
|
||||
async function openMemberFromDetail(customerKey: string) {
|
||||
await openMember(customerKey);
|
||||
}
|
||||
|
||||
watch(selectedStoreId, async (storeId) => {
|
||||
if (!storeId) {
|
||||
resetOverview();
|
||||
segmentResult.value = null;
|
||||
segmentPagination.total = 0;
|
||||
setDetailDrawerOpen(false);
|
||||
setProfileDrawerOpen(false);
|
||||
setMemberDrawerOpen(false);
|
||||
isSegmentDrawerOpen.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
await loadOverview();
|
||||
if (isSegmentDrawerOpen.value) {
|
||||
await loadSegmentData();
|
||||
}
|
||||
});
|
||||
|
||||
watch(period, async () => {
|
||||
if (!selectedStoreId.value) {
|
||||
resetOverview();
|
||||
return;
|
||||
}
|
||||
|
||||
await loadOverview();
|
||||
if (isSegmentDrawerOpen.value) {
|
||||
await loadSegmentData();
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
void loadStores();
|
||||
});
|
||||
|
||||
onActivated(() => {
|
||||
if (stores.value.length === 0 || !selectedStoreId.value) {
|
||||
void loadStores();
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
canExport,
|
||||
currentSegmentCode,
|
||||
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,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user