【招生用户端】 新增# 工作台:对接成交用户统计接口

main
kaeery 2025-03-05 23:11:42 +08:00
parent c57cc282fb
commit 5110a2c267
5 changed files with 143 additions and 45 deletions

View File

@ -12,15 +12,15 @@ export function convertProcessApi(params?: any) {
export function clueStatusApi(params?: any) { export function clueStatusApi(params?: any) {
return request.get({ url: '/control/clueStatistics', params }) return request.get({ url: '/control/clueStatistics', params })
} }
// Top5
export function rankListApi(params?: any) {
return request.get({ url: '/control/top', params })
}
// 成交客户统计
export function completedCustomerListApi(params?: any) {
return request.get({ url: '/control/transactionCustomerStatistics', params })
}
// 获取所有组织以及人员信息 // 获取所有组织以及人员信息
export function allUserListApi(params?: any) { export function allUserListApi(params?: any) {
return request.get({ url: '/organization/getAllChildOrgInfo', params }) return request.get({ url: '/organization/getAllChildOrgInfo', params })
} }
// 获取所有团队
export function allTeamListApi(params?: any) {
return request.get({ url: '/organization/getAllGroup', params })
}
// 获取所有组织
export function allOrgListApi(params?: any) {
return request.get({ url: '/organization/getAllOrg', params })
}

View File

@ -19,6 +19,7 @@ import { apiFeedbackAgreement, apiMasterWorkerApplyAgreement, apiMasterWorkerPhy
import { applyForEdit } from '@/api/finance/withdraw' import { applyForEdit } from '@/api/finance/withdraw'
import { useCreateModal } from './useCreateModal' import { useCreateModal } from './useCreateModal'
import { toast, formatFileSize } from '@/utils/util' import { toast, formatFileSize } from '@/utils/util'
import { postLists } from '@/api/account_center/postion'
export interface CategoryProp { export interface CategoryProp {
id: number id: number
@ -311,3 +312,21 @@ export function useUploadMoreAction() {
previewPdf previewPdf
} }
} }
export function usePositionData() {
const positionOptions = ref<any[]>([])
const postId = ref()
const fetchPostionData = async (callback?: (params: []) => void) => {
try {
const result = await postLists()
positionOptions.value = result.lists ?? []
if (result.lists.length > 0) postId.value = result.lists[0].id
callback && callback(result.lists)
} catch (error) {}
}
return {
positionOptions,
postId,
fetchPostionData
}
}

View File

@ -1,18 +1,16 @@
<template> <template>
<div class="flex-1 w-full chart-card"> <div class="flex-1 w-full chart-card">
<card title="成交客户统计"> <card title="成交客户统计">
<v-charts style="height: 350px" :autoresize="true" :option="option" /> <v-charts style="height: 350px" ref="chartRef" v-loading="loading" :autoresize="true" :option="option" />
</card> </card>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { completedCustomerListApi } from '@/api/workbench'
import card from './card.vue' import card from './card.vue'
import vCharts from 'vue-echarts' import vCharts from 'vue-echarts'
import type { IForm } from '../index.vue'
const data = [
{ name: '湛江团队', clueNumber: 52, client: 52, rate: 100 },
{ name: '广州团队', clueNumber: 8, client: 6, rate: 15 }
]
function createBarSeries(data, name, field) { function createBarSeries(data, name, field) {
return { return {
name, name,
@ -20,9 +18,11 @@ function createBarSeries(data, name, field) {
data: data.map(item => item[field]) data: data.map(item => item[field])
} }
} }
const chartRef = ref()
const data = ref<any[]>([])
const xAxisData = ref([])
const xAxisData = () => data.map(item => item.name) const series = [createBarSeries(data.value, '客户数', 'clientCount'), createBarSeries(data.value, '成交客户数', 'transactionClient')]
const series = [createBarSeries(data, '客户数', 'clueNumber'), createBarSeries(data, '成交客户数', 'client')]
const option = ref({ const option = ref({
color: ['#0E66FB', '#96B2D9'], color: ['#0E66FB', '#96B2D9'],
@ -40,7 +40,7 @@ const option = ref({
xAxis: [ xAxis: [
{ {
type: 'category', type: 'category',
data: xAxisData() data: xAxisData.value
} }
], ],
yAxis: [ yAxis: [
@ -53,7 +53,54 @@ const option = ref({
], ],
series series
}) })
const loading = ref(false)
const fetchData = async (payload: IForm) => {
loading.value = true
try {
const result = await completedCustomerListApi(payload)
data.value = result.map((item: any) => {
const { clientCount, transactionClient } = item.leadToCustomerStatisticsVo
return {
name: item.organizationName,
clientCount,
transactionClient
}
})
xAxisData.value = result.map((item: any) => item.organizationName)
const allZero = data.value.every(item => item.clientCount == 0 && item.transactionClient == 0)
if (allZero) {
chartRef.value.setOption({
title: {
text: '暂无数据',
x: 'center',
y: 'center'
},
xAxis: [
{
type: 'category',
data: [],
splitLine: {
show: false
},
axisLine: {
show: false
}
}
],
legend: {
show: false
}
})
}
} catch (error) {}
loading.value = false
}
defineExpose({
fetchData
})
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@media (max-width: 1000px) { @media (max-width: 1000px) {
.chart-card { .chart-card {

View File

@ -3,45 +3,70 @@
<card title="TOP5"> <card title="TOP5">
<template #right> <template #right>
<div class="positions"> <div class="positions">
<span class="default" :class="{ active: item.id == postId }" v-for="item in positions" :key="item.id" @click="postId = item.id"> <span
class="default"
:class="{ active: item.id == postId }"
v-for="item in positionOptions"
:key="item.id"
@click="postId = item.id"
>
{{ item.name }} {{ item.name }}
</span> </span>
</div> </div>
</template> </template>
<el-table :data="tableData"> <template v-if="tableData.length > 0">
<el-table-column prop="index" label="排名" width="70" /> <el-table :data="tableData" v-loading="loading">
<el-table-column prop="name" label="负责人" width="180" /> <el-table-column prop="rank" label="排名" width="70" />
<el-table-column prop="totalNumber" label="总数" /> <el-table-column prop="username" label="负责人" width="180" />
<el-table-column prop="clueCount" label="总数" />
</el-table> </el-table>
</template>
<template v-else>
<el-empty description="暂无数据" />
</template>
</card> </card>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { rankListApi } from '@/api/workbench'
import card from './card.vue' import card from './card.vue'
const positions = ref([ import type { IForm } from '../index.vue'
{ name: '电销', id: 5 }, import { usePositionData } from '@/hooks/useCommon'
{ name: '招生', id: 6 }
])
const postId = ref(5)
const tableData = ref([ const { positionOptions, postId, fetchPostionData } = usePositionData()
{
index: 1, const emit = defineEmits(['refresh'])
name: 'John Brown',
totalNumber: 32 const tableData = ref([])
}, const loading = ref(false)
{ const fetchData = async (payload: IForm) => {
index: 2, if (!positionOptions.value.length) await fetchPostionData()
name: 'John Brown', if (tableData.value.length > 0) tableData.value = []
totalNumber: 32 loading.value = true
}, try {
{ const newPayload = {
index: 3, ...payload,
name: 'John Brown', post: postId.value
totalNumber: 32
} }
]) const result = await rankListApi(newPayload)
console.log(result)
tableData.value = result ?? []
} catch (error) {}
loading.value = false
}
watch(
() => postId.value,
(newVal, oldVal) => {
if (newVal !== oldVal) {
emit('refresh')
}
}
)
defineExpose({
fetchData
})
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.rank { .rank {

View File

@ -4,10 +4,10 @@
<data-overview ref="dataOverviewRef" /> <data-overview ref="dataOverviewRef" />
<div class="flex gap-[16px] flex-wrap"> <div class="flex gap-[16px] flex-wrap">
<conversion-process-chart ref="coversionProcessChartRef" /> <conversion-process-chart ref="coversionProcessChartRef" />
<converted-chart /> <converted-chart ref="convertedChartRef" />
</div> </div>
<div class="flex gap-[16px] flex-wrap"> <div class="flex gap-[16px] flex-wrap">
<rank /> <rank ref="rankRef" @refresh="refreshData" />
<clue-status-pie ref="clueStatusPieRef" /> <clue-status-pie ref="clueStatusPieRef" />
<!-- <converted-line-chart /> --> <!-- <converted-line-chart /> -->
</div> </div>
@ -37,6 +37,8 @@ const form = ref<IForm>({
createTimeEnd: getCurDate('end'), createTimeEnd: getCurDate('end'),
userId: '' userId: ''
}) })
const convertedChartRef = ref<InstanceType<typeof convertedChart>>()
const rankRef = ref<InstanceType<typeof rank>>()
const clueStatusPieRef = ref<InstanceType<typeof clueStatusPie>>() const clueStatusPieRef = ref<InstanceType<typeof clueStatusPie>>()
const coversionProcessChartRef = ref<InstanceType<typeof conversionProcessChart>>() const coversionProcessChartRef = ref<InstanceType<typeof conversionProcessChart>>()
const dataOverviewRef = ref<InstanceType<typeof dataOverview>>() const dataOverviewRef = ref<InstanceType<typeof dataOverview>>()
@ -57,6 +59,11 @@ const fetchAllData = (defaultValue?: number) => {
dataOverviewRef.value?.fetchData(form.value) dataOverviewRef.value?.fetchData(form.value)
coversionProcessChartRef.value?.fetchData(form.value) coversionProcessChartRef.value?.fetchData(form.value)
clueStatusPieRef.value?.fetchData(form.value) clueStatusPieRef.value?.fetchData(form.value)
rankRef.value?.fetchData(form.value)
convertedChartRef.value?.fetchData(form.value)
}
const refreshData = () => {
rankRef.value?.fetchData(form.value)
} }
</script> </script>