【招生小程序】 优化# 团队:线索转化情况分析布局
parent
c8ace90b43
commit
4226411775
|
@ -16,6 +16,10 @@ export function apiOrganizationByIdList() {
|
||||||
export function apiOrganizationAllList() {
|
export function apiOrganizationAllList() {
|
||||||
return request.get({ url: '/organization/getAllChildOrgInfo' })
|
return request.get({ url: '/organization/getAllChildOrgInfo' })
|
||||||
}
|
}
|
||||||
|
// 岗位列表
|
||||||
|
export function postLists(params?: any) {
|
||||||
|
return request.get({ url: '/system/post/list', data: params })
|
||||||
|
}
|
||||||
|
|
||||||
// 数据简报
|
// 数据简报
|
||||||
export function apiDataOverview(params: any) {
|
export function apiDataOverview(params: any) {
|
||||||
|
@ -29,3 +33,11 @@ export function convertProcessApi(params?: any) {
|
||||||
export function clueStatusApi(params?: any) {
|
export function clueStatusApi(params?: any) {
|
||||||
return request.get({ url: '/control/clueStatistics', data: params })
|
return request.get({ url: '/control/clueStatistics', data: params })
|
||||||
}
|
}
|
||||||
|
// 成交客户转化统计
|
||||||
|
export function convertedSuccessApi(params?: any) {
|
||||||
|
return request.get({ url: '/control/clueTransactionCustomerStatistics', data: params })
|
||||||
|
}
|
||||||
|
// Top5
|
||||||
|
export function rankListApi(params?: any) {
|
||||||
|
return request.get({ url: '/control/top', data: params })
|
||||||
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
<template v-if="!loading && data.length > 0">
|
<template v-if="!loading && data.length > 0">
|
||||||
<TTable :columns="columns" :data="data">
|
<TTable :columns="columns" :data="data">
|
||||||
<template #index="scope">
|
<template #index="scope">
|
||||||
<text class="px-[16rpx] py-[6rpx] rounded-[4px]" :style="rankStyle(scope.row)">
|
<text class="px-[20rpx] py-[6rpx] rounded-[4px]" :style="rankStyle(scope.row)">
|
||||||
{{ scope.row }}
|
{{ scope.row }}
|
||||||
</text>
|
</text>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -157,7 +157,7 @@ export default defineComponent({
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
height: 66rpx;
|
height: 66rpx;
|
||||||
padding: 0 24rpx;
|
padding: 0 24rpx;
|
||||||
font-size: 26rpx;
|
// font-size: 26rpx;
|
||||||
line-height: 66rpx;
|
line-height: 66rpx;
|
||||||
color: var(--dropdown-text-color);
|
color: var(--dropdown-text-color);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@ -190,7 +190,7 @@ export default defineComponent({
|
||||||
margin-right: 20rpx;
|
margin-right: 20rpx;
|
||||||
margin-bottom: 20rpx;
|
margin-bottom: 20rpx;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
font-size: 28rpx;
|
// font-size: 28rpx;
|
||||||
color: var(--dropdown-text-color);
|
color: var(--dropdown-text-color);
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
border-radius: 999rpx;
|
border-radius: 999rpx;
|
||||||
|
|
|
@ -119,7 +119,7 @@ export default defineComponent({
|
||||||
height: var(--cell-height);
|
height: var(--cell-height);
|
||||||
padding: 0 24rpx;
|
padding: 0 24rpx;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
font-size: 28rpx;
|
// font-size: 28rpx;
|
||||||
color: var(--dropdown-text-color);
|
color: var(--dropdown-text-color);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
border-bottom: 1rpx solid #dedede;
|
border-bottom: 1rpx solid #dedede;
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
<template>
|
||||||
|
<view class="flex flex-wrap p-[32rpx] gap-[16rpx] text-[28rpx] bg-white">
|
||||||
|
<view
|
||||||
|
class="bg-[#F0F7FF] rounded-[8rpx] px-[16rpx] py-[8rpx]"
|
||||||
|
v-for="(value, key) in filteredForm"
|
||||||
|
:key="key"
|
||||||
|
>
|
||||||
|
<template v-if="key === 'createTimeRange'">
|
||||||
|
<text class="text-[#1E40AF]">{{ parseLabel(key) }}:</text>
|
||||||
|
<text class="text-primary">
|
||||||
|
{{ form.createTimeStart }} - {{ form.createTimeEnd }}
|
||||||
|
</text>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<text class="text-[#1E40AF]">{{ parseLabel(key) }}:</text>
|
||||||
|
<text class="text-primary">{{ value }}</text>
|
||||||
|
</template>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { PropType } from 'vue'
|
||||||
|
import { IForm } from './team/index.vue'
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { AdminTabEnum } from '@/enums'
|
||||||
|
|
||||||
|
const formMap: Record<AdminTabEnum, any> = {
|
||||||
|
[AdminTabEnum.TEAM]: {
|
||||||
|
organizationId: '组织',
|
||||||
|
createTimeRange: '日期'
|
||||||
|
},
|
||||||
|
[AdminTabEnum.PERSONALLY]: {
|
||||||
|
postId: '组织',
|
||||||
|
userId: '用户',
|
||||||
|
createTimeRange: '日期'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const props = defineProps({
|
||||||
|
form: {
|
||||||
|
type: Object as PropType<IForm>,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
activeTab: {
|
||||||
|
type: Number as PropType<AdminTabEnum>,
|
||||||
|
default: AdminTabEnum.TEAM
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const filteredForm = computed(() => {
|
||||||
|
const { createTimeStart, createTimeEnd, ...rest } = props.form
|
||||||
|
const activeTabMap = formMap[props.activeTab]
|
||||||
|
const filtered = Object.keys(rest).reduce((acc, key) => {
|
||||||
|
if (activeTabMap[key]) {
|
||||||
|
acc[key] = rest[key]
|
||||||
|
}
|
||||||
|
return acc
|
||||||
|
}, {} as Record<string, any>)
|
||||||
|
|
||||||
|
if (createTimeStart && createTimeEnd) {
|
||||||
|
filtered['createTimeRange'] = 'createTimeRange'
|
||||||
|
}
|
||||||
|
|
||||||
|
return filtered
|
||||||
|
})
|
||||||
|
const parseLabel = computed(() => (key: string) => {
|
||||||
|
const { activeTab } = props
|
||||||
|
return formMap[activeTab][key]
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style scoped></style>
|
|
@ -201,7 +201,7 @@ export default defineComponent({
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 24rpx;
|
padding: 24rpx;
|
||||||
font-size: 24rpx;
|
// font-size: 24rpx;
|
||||||
color: var(--dropdown-text-color);
|
color: var(--dropdown-text-color);
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
>
|
>
|
||||||
<text>{{ item.label }}</text>
|
<text>{{ item.label }}</text>
|
||||||
<template v-if="postId == item.id">
|
<template v-if="postId == item.id">
|
||||||
<TIcon name="icon-check" color="#0E66FB" />
|
<TIcon name="icon-check" color="#0E66FB" :size="24" />
|
||||||
</template>
|
</template>
|
||||||
</view>
|
</view>
|
||||||
<dropdownFooter @reset="handleReset" @confirm="handleConfirm" />
|
<dropdownFooter @reset="handleReset" @confirm="handleConfirm" />
|
||||||
|
@ -35,6 +35,7 @@ const handleCellClick = (item: any) => {
|
||||||
const { id } = item
|
const { id } = item
|
||||||
postId.value = id
|
postId.value = id
|
||||||
emit('update:modelValue', id)
|
emit('update:modelValue', id)
|
||||||
|
emit('confirm', item)
|
||||||
}
|
}
|
||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
postId.value = 0
|
postId.value = 0
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<position-tabs
|
<position-tabs
|
||||||
v-model="postId"
|
v-model="postId"
|
||||||
@reset="handleReset('position')"
|
@reset="handleReset('position')"
|
||||||
@confirm="handleConfirm('position', value)"
|
@confirm="value => handleConfirm('position', value)"
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
</u-dropdown-item>
|
</u-dropdown-item>
|
||||||
|
@ -32,7 +32,7 @@
|
||||||
</u-dropdown>
|
</u-dropdown>
|
||||||
</view>
|
</view>
|
||||||
<view class="flex-1 mt-3 px-[32rpx] overflow-auto bg-[#FAFAFE]">
|
<view class="flex-1 mt-3 px-[32rpx] overflow-auto bg-[#FAFAFE]">
|
||||||
<z-paging
|
<!-- <z-paging
|
||||||
ref="paging"
|
ref="paging"
|
||||||
v-model="dataList"
|
v-model="dataList"
|
||||||
@query="queryList"
|
@query="queryList"
|
||||||
|
@ -42,7 +42,7 @@
|
||||||
<template v-for="(item, index) in dataList" :key="`unique_${index}`">
|
<template v-for="(item, index) in dataList" :key="`unique_${index}`">
|
||||||
<telesale-card :item="item" @handleCardClick="handleCardClick" />
|
<telesale-card :item="item" @handleCardClick="handleCardClick" />
|
||||||
</template>
|
</template>
|
||||||
</z-paging>
|
</z-paging> -->
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
@ -90,6 +90,7 @@ const handleCardClick = (type: string, item: any) => {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 查询条件
|
||||||
const handleConfirm = (type: string, item) => {
|
const handleConfirm = (type: string, item) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'organization':
|
case 'organization':
|
||||||
|
@ -100,11 +101,14 @@ const handleConfirm = (type: string, item) => {
|
||||||
|
|
||||||
break
|
break
|
||||||
case 'position':
|
case 'position':
|
||||||
|
console.log(item)
|
||||||
|
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 重置条件
|
||||||
const handleReset = (type: string) => {
|
const handleReset = (type: string) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'organization':
|
case 'organization':
|
||||||
|
|
|
@ -14,12 +14,18 @@
|
||||||
:key="`unique_${index}`"
|
:key="`unique_${index}`"
|
||||||
>
|
>
|
||||||
<text class="text-gray5">{{ item.label }}</text>
|
<text class="text-gray5">{{ item.label }}</text>
|
||||||
|
<view class="flex items-center gap-[12rpx]">
|
||||||
|
<view class="flex-1">
|
||||||
<u-line-progress
|
<u-line-progress
|
||||||
:percentage="item.value"
|
:percentage="item.value"
|
||||||
activeColor="#5783FE"
|
activeColor="#5783FE"
|
||||||
shape="square"
|
shape="square"
|
||||||
|
:showText="false"
|
||||||
></u-line-progress>
|
></u-line-progress>
|
||||||
</view>
|
</view>
|
||||||
|
<text>{{ item.value + '%' }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
</card>
|
</card>
|
||||||
|
@ -56,6 +62,8 @@ const fetchData = async (payload: IForm) => {
|
||||||
value: result[item] || 0
|
value: result[item] || 0
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
console.log(data.value)
|
||||||
|
|
||||||
loading.value = false
|
loading.value = false
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import card from '../../card.vue'
|
import card from '../../card.vue'
|
||||||
import { IForm } from '../index.vue'
|
import { IForm } from '../index.vue'
|
||||||
|
import { convertedSuccessApi } from '@/api/admin'
|
||||||
|
|
||||||
interface IConvertedData {
|
interface IConvertedData {
|
||||||
name: string
|
name: string
|
||||||
|
@ -33,29 +34,24 @@ interface IConvertedData {
|
||||||
}
|
}
|
||||||
const data = ref<IConvertedData[]>([])
|
const data = ref<IConvertedData[]>([])
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const fetchData = (payload: IForm) => {
|
const fetchData = async (payload: IForm) => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
setTimeout(() => {
|
try {
|
||||||
data.value = [
|
const result = await convertedSuccessApi(payload)
|
||||||
{
|
console.log(result)
|
||||||
name: '湛江团队',
|
data.value = result.map(item => {
|
||||||
|
const { clientCount, clueCount, percentConversion } = item.leadToCustomerStatisticsVo
|
||||||
|
return {
|
||||||
|
name: item.organizationName,
|
||||||
children: [
|
children: [
|
||||||
{ label: '线索', value: '52个' },
|
{ label: '线索', value: clueCount ?? 0 + '个' },
|
||||||
{ label: '成交客户', value: '52个' },
|
{ label: '成交客户', value: clientCount ?? 0 + '个' },
|
||||||
{ label: '转化率', value: '15%' }
|
{ label: '转化率', value: percentConversion }
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '广州团队',
|
|
||||||
children: [
|
|
||||||
{ label: '线索', value: '52个' },
|
|
||||||
{ label: '成交客户', value: '52个' },
|
|
||||||
{ label: '转化率', value: '15%' }
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
})
|
||||||
|
} catch (error) {}
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}, 300)
|
|
||||||
}
|
}
|
||||||
defineExpose({
|
defineExpose({
|
||||||
fetchData
|
fetchData
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="px-[32rpx]">
|
<view class="px-[32rpx] mt-[12rpx]">
|
||||||
<card>
|
<card className="pt-2 pb-3">
|
||||||
<u-tabs
|
<u-tabs
|
||||||
:list="tabs"
|
:list="tabs"
|
||||||
:scrollable="false"
|
:scrollable="false"
|
||||||
|
@ -23,11 +23,11 @@
|
||||||
<u-loading-icon></u-loading-icon>
|
<u-loading-icon></u-loading-icon>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="!loading && data.length > 0">
|
<template v-else-if="!loading && data.length > 0">
|
||||||
<TTable :columns="columns" :data="data">
|
<TTable :columns="columns" :data="data">
|
||||||
<template #index="scope">
|
<template #index="scope">
|
||||||
<text
|
<text
|
||||||
class="px-[16rpx] py-[6rpx] rounded-[4px]"
|
class="px-[20rpx] py-[6rpx] rounded-[4px]"
|
||||||
:style="rankStyle(scope.row)"
|
:style="rankStyle(scope.row)"
|
||||||
>
|
>
|
||||||
{{ scope.row }}
|
{{ scope.row }}
|
||||||
|
@ -41,25 +41,26 @@
|
||||||
<text>查看更多</text>
|
<text>查看更多</text>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<w-empty />
|
||||||
|
</template>
|
||||||
</card>
|
</card>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { watch } from 'vue'
|
|
||||||
import card from '../../card.vue'
|
import card from '../../card.vue'
|
||||||
import { useRank } from '@/hooks/useCommon'
|
import { useRank } from '@/hooks/useCommon'
|
||||||
|
|
||||||
|
const emit = defineEmits(['refresh'])
|
||||||
|
|
||||||
const { tabs, activeTab, columns, data, loading, rankStyle, handleChangeTab, fetchData } = useRank({
|
const { tabs, activeTab, columns, data, loading, rankStyle, handleChangeTab, fetchData } = useRank({
|
||||||
width: 120
|
width: 120,
|
||||||
|
callback: () => {
|
||||||
|
emit('refresh')
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(
|
|
||||||
() => activeTab.value,
|
|
||||||
(val: number) => {
|
|
||||||
console.log(val)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
const handleMore = () => {
|
const handleMore = () => {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/bundle/pages/rank-list/index'
|
url: '/bundle/pages/rank-list/index'
|
||||||
|
|
|
@ -31,9 +31,10 @@
|
||||||
</u-dropdown-item>
|
</u-dropdown-item>
|
||||||
</u-dropdown>
|
</u-dropdown>
|
||||||
</view>
|
</view>
|
||||||
|
<filter-value :form="form" :activeTab="activeTab" />
|
||||||
<data-overview ref="dataOverviewRef" />
|
<data-overview ref="dataOverviewRef" />
|
||||||
<converted-overview ref="convertedOverviewRef" />
|
<converted-overview ref="convertedOverviewRef" />
|
||||||
<rank ref="rankRef" />
|
<rank ref="rankRef" @refresh="refreshData" />
|
||||||
<clue-status ref="clueStatusRef" />
|
<clue-status ref="clueStatusRef" />
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
@ -46,8 +47,11 @@ import rank from './components/rank.vue'
|
||||||
import clueStatus from './components/clue-status.vue'
|
import clueStatus from './components/clue-status.vue'
|
||||||
import dateDropdownPicker from '@/components/date-dropdown/daterange.vue'
|
import dateDropdownPicker from '@/components/date-dropdown/daterange.vue'
|
||||||
import selectDropdown from '@/components/select-dropdown/index.vue'
|
import selectDropdown from '@/components/select-dropdown/index.vue'
|
||||||
|
import filterValue from '../filter-value.vue'
|
||||||
import { apiOrganizationByIdList, apiOrganizationList, apiOrganizationTeamList } from '@/api/admin'
|
import { apiOrganizationByIdList, apiOrganizationList, apiOrganizationTeamList } from '@/api/admin'
|
||||||
import { formateDate, getCurDate } from '@/utils/util'
|
import { formateDate, getCurDate } from '@/utils/util'
|
||||||
|
import { PropType } from 'vue'
|
||||||
|
import { AdminTabEnum } from '@/enums'
|
||||||
|
|
||||||
export interface IForm {
|
export interface IForm {
|
||||||
organizationId: number | null
|
organizationId: number | null
|
||||||
|
@ -55,6 +59,13 @@ export interface IForm {
|
||||||
createTimeEnd: string
|
createTimeEnd: string
|
||||||
userId: string
|
userId: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
activeTab: {
|
||||||
|
type: Number as PropType<AdminTabEnum>,
|
||||||
|
default: AdminTabEnum.TEAM
|
||||||
|
}
|
||||||
|
})
|
||||||
const clueStatusRef = ref<InstanceType<typeof clueStatus>>()
|
const clueStatusRef = ref<InstanceType<typeof clueStatus>>()
|
||||||
const rankRef = ref<InstanceType<typeof rank>>()
|
const rankRef = ref<InstanceType<typeof rank>>()
|
||||||
const convertedOverviewRef = ref<InstanceType<typeof convertedOverview>>()
|
const convertedOverviewRef = ref<InstanceType<typeof convertedOverview>>()
|
||||||
|
@ -131,7 +142,9 @@ const handleReset = (type: string) => {
|
||||||
const closeDropDown = () => {
|
const closeDropDown = () => {
|
||||||
uDropdownRef.value.close()
|
uDropdownRef.value.close()
|
||||||
}
|
}
|
||||||
|
const refreshData = () => {
|
||||||
|
rankRef.value?.fetchData(form.value)
|
||||||
|
}
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await fetchOrganizationList()
|
await fetchOrganizationList()
|
||||||
fetchAllData()
|
fetchAllData()
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="empty-comp text-center text-[#969799]">
|
<view class="empty-comp text-center text-[#969799]">
|
||||||
<template v-if="type == 'data'">
|
<template v-if="type == 'data'">
|
||||||
<image :src="dataImage" mode="aspectFit" class="w-[240px]" />
|
<image :src="dataImage" mode="aspectFit" class="w-[240px] h-[200px]" />
|
||||||
<view>暂无数据</view>
|
<view>暂无数据</view>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="type == 'location'">
|
<template v-else-if="type == 'location'">
|
||||||
|
|
|
@ -21,3 +21,7 @@ export enum AdminTabEnum {
|
||||||
TEAM = 1,
|
TEAM = 1,
|
||||||
PERSONALLY = 2
|
PERSONALLY = 2
|
||||||
}
|
}
|
||||||
|
export enum PositionEnum {
|
||||||
|
TELESALE = 5, //电销
|
||||||
|
RECRUITSALE = 6 //招生
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
|
import { postLists, rankListApi } from '@/api/admin'
|
||||||
import { apiCluseDetail } from '@/api/clue'
|
import { apiCluseDetail } from '@/api/clue'
|
||||||
import { IForm } from '@/components/widgets/admin/team/index.vue'
|
import { IForm } from '@/components/widgets/admin/team/index.vue'
|
||||||
|
import { PositionEnum } from '@/enums'
|
||||||
import { computed, ref, shallowRef } from 'vue'
|
import { computed, ref, shallowRef } from 'vue'
|
||||||
|
|
||||||
// 获取线索详情
|
// 获取线索详情
|
||||||
|
@ -32,12 +34,12 @@ interface IRankTab {
|
||||||
id: number
|
id: number
|
||||||
}
|
}
|
||||||
// 主账号排行榜
|
// 主账号排行榜
|
||||||
export function useRank({ width }: { width: number }) {
|
export function useRank({ width, callback }: { width: number; callback?: () => void }) {
|
||||||
const tabs = ref<IRankTab[]>([])
|
const tabs = ref<IRankTab[]>([])
|
||||||
const activeTab = ref()
|
const activeTab = ref()
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const data = ref<IRank[]>([])
|
const data = ref<IRank[]>([])
|
||||||
const totalLabel = computed(() => (activeTab.value == 5 ? '意向' : '成交'))
|
const totalLabel = computed(() => (activeTab.value == PositionEnum.TELESALE ? '意向' : '成交'))
|
||||||
const rankStyle = computed(() => row => {
|
const rankStyle = computed(() => row => {
|
||||||
const styleMap: Record<number, string> = {
|
const styleMap: Record<number, string> = {
|
||||||
'1': '#FCAE3C',
|
'1': '#FCAE3C',
|
||||||
|
@ -52,56 +54,46 @@ export function useRank({ width }: { width: number }) {
|
||||||
|
|
||||||
const handleChangeTab = item => {
|
const handleChangeTab = item => {
|
||||||
activeTab.value = item.id
|
activeTab.value = item.id
|
||||||
generateColumns()
|
columns.value = generateColumns()
|
||||||
|
callback && callback()
|
||||||
}
|
}
|
||||||
const generateColumns = () => {
|
const generateColumns = () => {
|
||||||
return [
|
return [
|
||||||
{ name: 'index', label: '排名', slot: true },
|
{ name: 'rank', label: '排名', slot: true },
|
||||||
{ name: 'name', label: '姓名' },
|
{ name: 'username', label: '姓名' },
|
||||||
{ name: 'total', label: totalLabel.value + '客户数', width, align: 'right' }
|
{ name: 'clueCount', label: totalLabel.value + '客户数', width, align: 'right' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
const columns = ref(generateColumns())
|
const columns = ref()
|
||||||
const fetchTabs = () => {
|
// 获取岗位列表
|
||||||
return new Promise(resolve => {
|
const fetchTabs = async () => {
|
||||||
tabs.value = [
|
try {
|
||||||
{ name: '电销业绩排行榜', value: 1, id: 5 },
|
const result = await postLists()
|
||||||
{ name: '招生业绩排行榜', value: 2, id: 6 }
|
tabs.value = result.lists.map(item => {
|
||||||
]
|
const { name } = item
|
||||||
if (tabs.value.length > 0) activeTab.value = tabs.value[0]
|
return {
|
||||||
resolve(tabs.value)
|
id: item.id,
|
||||||
|
name: name + '业绩排行榜'
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
if (tabs.value.length > 0) activeTab.value = tabs.value[0].id
|
||||||
|
columns.value = generateColumns()
|
||||||
|
} catch (error) {}
|
||||||
}
|
}
|
||||||
|
// 获取每一个岗位前5名
|
||||||
const fetchData = async (payload: IForm) => {
|
const fetchData = async (payload: IForm) => {
|
||||||
await fetchTabs()
|
if (!tabs.value.length) await fetchTabs()
|
||||||
console.log(activeTab.value)
|
try {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
setTimeout(() => {
|
const newPayload = {
|
||||||
data.value = [
|
...payload,
|
||||||
{
|
post: activeTab.value
|
||||||
index: 1,
|
|
||||||
name: '王小虎1789789789789789',
|
|
||||||
total: 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
index: 2,
|
|
||||||
name: '王小虎2',
|
|
||||||
total: 20
|
|
||||||
},
|
|
||||||
{
|
|
||||||
index: 3,
|
|
||||||
name: '王小虎2',
|
|
||||||
total: 20
|
|
||||||
},
|
|
||||||
{
|
|
||||||
index: 4,
|
|
||||||
name: '王小虎2',
|
|
||||||
total: 20
|
|
||||||
}
|
}
|
||||||
]
|
const result = await rankListApi(newPayload)
|
||||||
|
data.value = result ?? []
|
||||||
|
} catch (error) {}
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}, 500)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -120,21 +112,17 @@ export function useRank({ width }: { width: number }) {
|
||||||
export function usePositions() {
|
export function usePositions() {
|
||||||
const positionList = ref()
|
const positionList = ref()
|
||||||
const postId = ref()
|
const postId = ref()
|
||||||
const fetchPositions = () => {
|
const fetchPositions = async () => {
|
||||||
try {
|
try {
|
||||||
positionList.value = [
|
const result = await postLists()
|
||||||
{
|
positionList.value = result.lists.map(item => {
|
||||||
label: '电销老师',
|
return {
|
||||||
id: 5,
|
label: item.name,
|
||||||
value: 5
|
id: item.id,
|
||||||
},
|
value: item.id
|
||||||
{
|
|
||||||
label: '招生老师',
|
|
||||||
id: 6,
|
|
||||||
value: 6
|
|
||||||
}
|
}
|
||||||
]
|
})
|
||||||
if (positionList.value.length > 0) postId.value = positionList.value
|
if (positionList.value.length > 0) postId.value = positionList.value[0].id
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,8 @@
|
||||||
{
|
{
|
||||||
"path": "pages/admin/my/index",
|
"path": "pages/admin/my/index",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": ""
|
"navigationBarTitleText": "",
|
||||||
|
"navigationStyle": "custom"
|
||||||
},
|
},
|
||||||
"auth": true
|
"auth": true
|
||||||
},
|
},
|
||||||
|
|
|
@ -10,8 +10,8 @@
|
||||||
@change="handleChangeTab"
|
@change="handleChangeTab"
|
||||||
></u-tabs>
|
></u-tabs>
|
||||||
<view class="flex-1 bg-gray3">
|
<view class="flex-1 bg-gray3">
|
||||||
<admin-team v-if="activeTab === AdminTabEnum.TEAM" />
|
<admin-team v-if="activeTab === AdminTabEnum.TEAM" :activeTab="activeTab" />
|
||||||
<personally v-if="activeTab === AdminTabEnum.PERSONALLY" />
|
<personally v-if="activeTab === AdminTabEnum.PERSONALLY" :activeTab="activeTab" />
|
||||||
</view>
|
</view>
|
||||||
</TContainer>
|
</TContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -15,6 +15,7 @@ export default {
|
||||||
percentage: 0,
|
percentage: 0,
|
||||||
showText: true,
|
showText: true,
|
||||||
height: 12,
|
height: 12,
|
||||||
shape: 'circle'
|
shape: 'circle',
|
||||||
|
thresholdValue: 10
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,10 @@ export const props = defineMixin({
|
||||||
shape: {
|
shape: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => defProps.lineProgress.shape
|
default: () => defProps.lineProgress.shape
|
||||||
|
},
|
||||||
|
thresholdValue: {
|
||||||
|
type: Number,
|
||||||
|
default: () => defProps.lineProgress.thresholdValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -13,7 +13,11 @@
|
||||||
></view>
|
></view>
|
||||||
<view class="u-line-progress__line" :style="[progressStyle]">
|
<view class="u-line-progress__line" :style="[progressStyle]">
|
||||||
<slot>
|
<slot>
|
||||||
<text v-if="showText && percentage >= 10" class="u-line-progress__text">
|
<text
|
||||||
|
v-if="showText && percentage >= thresholdValue"
|
||||||
|
class="u-line-progress__text"
|
||||||
|
:style="{ right: textGap }"
|
||||||
|
>
|
||||||
{{ innserPercentage + '%' }}
|
{{ innserPercentage + '%' }}
|
||||||
</text>
|
</text>
|
||||||
</slot>
|
</slot>
|
||||||
|
@ -66,6 +70,14 @@ export default {
|
||||||
innserPercentage() {
|
innserPercentage() {
|
||||||
// 控制范围在0-100之间
|
// 控制范围在0-100之间
|
||||||
return range(0, 100, this.percentage)
|
return range(0, 100, this.percentage)
|
||||||
|
},
|
||||||
|
textGap() {
|
||||||
|
let value =
|
||||||
|
typeof this.lineWidth == 'string'
|
||||||
|
? Math.ceil(parseFloat(this.lineWidth))
|
||||||
|
: Math.ceil(this.lineWidth)
|
||||||
|
const newVal = value + 17
|
||||||
|
return '-' + newVal + 'px'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -128,18 +140,20 @@ export default {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@include flex(row);
|
@include flex(row);
|
||||||
color: #ffffff;
|
// color: #ffffff;
|
||||||
// border-radius: 100px;
|
// border-radius: 100px;
|
||||||
transition: width 0.5s ease;
|
transition: width 0.5s ease;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__text {
|
&__text {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
text-align: right;
|
// text-align: right;
|
||||||
color: #ffffff;
|
// color: #ffffff;
|
||||||
margin-right: 5px;
|
// margin-right: 5px;
|
||||||
transform: scale(0.9);
|
transform: scale(0.9);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue