14 changed files with 630 additions and 60 deletions
Unified View
Diff Options
-
1src/locales/lang/zh-CN/routes/clue.ts
-
1src/locales/lang/zh-CN/routes/invite.ts
-
16src/router/menu.ts
-
4src/router/menus/modules/clue.ts
-
4src/router/menus/modules/invite.ts
-
8src/router/routes/modules/clue.ts
-
8src/router/routes/modules/invite.ts
-
42src/views/clue/clueIndex/components/GrowCard.vue
-
49src/views/clue/clueIndex/data.ts
-
71src/views/clue/clueIndex/index.vue
-
42src/views/invite/statistics/components/GrowCard.vue
-
234src/views/invite/statistics/data.ts
-
90src/views/invite/statistics/index.vue
-
120src/views/sys/workbench/index.vue
@ -0,0 +1,42 @@ |
|||||
|
<template> |
||||
|
<div class="md:flex"> |
||||
|
<template v-for="(item, index) in dataList" :key="item.title"> |
||||
|
<Card |
||||
|
size="small" |
||||
|
:loading="loading" |
||||
|
:title="item.title" |
||||
|
:style="{flex: 1, marginRight: index < dataList.length - 1 ? '5px' : '0px', marginLeft: index > 0 ? '5px' : '0px'}" |
||||
|
> |
||||
|
<!-- <template #extra> |
||||
|
<Tag :color="item.color">{{ item.action }}</Tag> |
||||
|
</template> --> |
||||
|
|
||||
|
<div class="py-3 px-5 flex justify-between items-center"> |
||||
|
<CountTo :startVal="1" :endVal="item.value" class="text-3xl" /> |
||||
|
<!-- <Icon :icon="item.icon" :size="40" /> --> |
||||
|
</div> |
||||
|
|
||||
|
<!-- <div class="p-2 px-4 flex justify-between"> |
||||
|
<span>总{{ item.title }}</span> |
||||
|
<CountTo prefix="$" :startVal="1" :endVal="item.total" /> |
||||
|
</div> --> |
||||
|
</Card> |
||||
|
</template> |
||||
|
</div> |
||||
|
</template> |
||||
|
<script lang="ts" setup> |
||||
|
import { CountTo } from '/@/components/CountTo' |
||||
|
import Icon from '/@/components/Icon/Icon.vue' |
||||
|
import { Tag, Card } from 'ant-design-vue' |
||||
|
import { GrowCardItem } from '../data' |
||||
|
|
||||
|
defineProps({ |
||||
|
loading: { |
||||
|
type: Boolean, |
||||
|
}, |
||||
|
dataList: { |
||||
|
type: Array as () => GrowCardItem[], |
||||
|
default: [] |
||||
|
}, |
||||
|
}) |
||||
|
</script> |
||||
@ -0,0 +1,49 @@ |
|||||
|
export interface GrowCardItem { |
||||
|
icon: string; |
||||
|
title: string; |
||||
|
value: number; |
||||
|
color: string; |
||||
|
action: string; |
||||
|
} |
||||
|
|
||||
|
import { BasicColumn } from '/@/components/Table' |
||||
|
|
||||
|
export const growCardList: GrowCardItem[] = [ |
||||
|
{ |
||||
|
title: '新增数量', |
||||
|
icon: 'visit-count|svg', |
||||
|
value: 0, |
||||
|
color: 'green', |
||||
|
action: '天', |
||||
|
}, |
||||
|
{ |
||||
|
title: '预约数量', |
||||
|
icon: 'total-sales|svg', |
||||
|
value: 0, |
||||
|
color: 'blue', |
||||
|
action: '天', |
||||
|
}, |
||||
|
{ |
||||
|
title: '接通数量', |
||||
|
icon: 'download-count|svg', |
||||
|
value: 0, |
||||
|
color: 'orange', |
||||
|
action: '天', |
||||
|
}, |
||||
|
{ |
||||
|
title: '签约数量', |
||||
|
icon: 'transaction|svg', |
||||
|
value: 0, |
||||
|
color: 'purple', |
||||
|
action: '天', |
||||
|
}, |
||||
|
]; |
||||
|
|
||||
|
|
||||
|
export const tableColumns: BasicColumn[] = [ |
||||
|
{ title: '员工姓名', dataIndex: 'operatorName' }, |
||||
|
{ title: '新增数量', dataIndex: 'newQuantity' }, |
||||
|
{ title: '预约数量', dataIndex: 'appointmentQuantity' }, |
||||
|
{ title: '接通数量', dataIndex: 'connectionQuantity' }, |
||||
|
{ title: '签约数量', dataIndex: 'signQuantity' }, |
||||
|
] |
||||
@ -0,0 +1,71 @@ |
|||||
|
<template> |
||||
|
<div class="flex-col"> |
||||
|
<div class="flex-row-center-space" style="padding: 12px 16px 0px 16px;"> |
||||
|
<span style="color: #333;text-align: center;font-size: 22px;">流量数据统计</span> |
||||
|
<RangePicker v-model:value="value4" :format="dateFormat" :disabled-date="disabledDate" @change="fetchData"/> |
||||
|
</div> |
||||
|
<div class="p-4"> |
||||
|
<GrowCard :loading="loading" :data-list="growCardList" class="enter-y" /> |
||||
|
</div> |
||||
|
<div style="padding: 0px 16px;"> |
||||
|
<BasicTable @register="registerTable" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
<script lang="ts" setup> |
||||
|
import { onMounted, ref } from 'vue'; |
||||
|
import moment, {Moment} from 'moment'; |
||||
|
import { formatToDate } from '/@/utils/dateUtil' |
||||
|
import GrowCard from './components/GrowCard.vue'; |
||||
|
import { RangePicker } from 'ant-design-vue' |
||||
|
import { BasicTable, useTable } from '/@/components/Table' |
||||
|
import { tableColumns, growCardList } from './data' |
||||
|
import { getFlowStatic, getFlowData } from '/@/api/clue' |
||||
|
|
||||
|
const dateFormat = 'YYYY-MM-DD'; |
||||
|
const value4 = ref<any[]>([]) |
||||
|
const loading = ref(true); |
||||
|
onMounted(() => { |
||||
|
value4.value = [moment().format(dateFormat), moment().format(dateFormat)] |
||||
|
fetchData() |
||||
|
}) |
||||
|
|
||||
|
function disabledDate (current: Moment) { |
||||
|
return current && current >= moment().endOf('day'); |
||||
|
} |
||||
|
|
||||
|
async function fetchData(){ |
||||
|
loading.value = true; |
||||
|
const result = await getFlowStatic({startTime: formatToDate(value4.value[0]), endTime: formatToDate(value4.value[1])}) |
||||
|
if(result){ |
||||
|
growCardList[0].value = result.newQuantity |
||||
|
growCardList[1].value = result.appointmentQuantity |
||||
|
growCardList[2].value = result.connectionQuantity |
||||
|
growCardList[3].value = result.signQuantity |
||||
|
} else { |
||||
|
for (let index = 0; index < growCardList.length; index++) { |
||||
|
growCardList[index].value = 0 |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
await reload() |
||||
|
loading.value = false; |
||||
|
} |
||||
|
|
||||
|
const [registerTable, { reload }] = useTable({ |
||||
|
bordered: true, |
||||
|
immediate: false, |
||||
|
canResize: true, |
||||
|
useSearchForm: false, |
||||
|
showIndexColumn: true, |
||||
|
showTableSetting: false, |
||||
|
pagination: false, |
||||
|
columns: tableColumns, |
||||
|
api: getFlowData, |
||||
|
beforeFetch: (arg) => { |
||||
|
arg.startTime = formatToDate(value4.value[0]) |
||||
|
arg.endTime = formatToDate(value4.value[1]) |
||||
|
}, |
||||
|
}) |
||||
|
|
||||
|
</script> |
||||
@ -0,0 +1,42 @@ |
|||||
|
<template> |
||||
|
<div class="md:flex"> |
||||
|
<template v-for="(item, index) in dataList" :key="item.title"> |
||||
|
<Card |
||||
|
size="small" |
||||
|
:loading="loading" |
||||
|
:title="item.title" |
||||
|
:style="{flex: 1, marginRight: index < dataList.length - 1 ? '5px' : '0px', marginLeft: index > 0 ? '5px' : '0px'}" |
||||
|
> |
||||
|
<!-- <template #extra> |
||||
|
<Tag :color="item.color">{{ item.action }}</Tag> |
||||
|
</template> --> |
||||
|
|
||||
|
<div class="py-3 px-5 flex justify-between items-center"> |
||||
|
<CountTo :startVal="1" :endVal="item.value" class="text-3xl" /> |
||||
|
<!-- <Icon :icon="item.icon" :size="40" /> --> |
||||
|
</div> |
||||
|
|
||||
|
<!-- <div class="p-2 px-4 flex justify-between"> |
||||
|
<span>总{{ item.title }}</span> |
||||
|
<CountTo prefix="$" :startVal="1" :endVal="item.total" /> |
||||
|
</div> --> |
||||
|
</Card> |
||||
|
</template> |
||||
|
</div> |
||||
|
</template> |
||||
|
<script lang="ts" setup> |
||||
|
import { CountTo } from '/@/components/CountTo' |
||||
|
import Icon from '/@/components/Icon/Icon.vue' |
||||
|
import { Tag, Card } from 'ant-design-vue' |
||||
|
import { GrowCardItem } from '../data' |
||||
|
|
||||
|
defineProps({ |
||||
|
loading: { |
||||
|
type: Boolean, |
||||
|
}, |
||||
|
dataList: { |
||||
|
type: Array as () => GrowCardItem[], |
||||
|
default: [] |
||||
|
}, |
||||
|
}) |
||||
|
</script> |
||||
@ -0,0 +1,234 @@ |
|||||
|
export interface GrowCardItem { |
||||
|
icon: string; |
||||
|
title: string; |
||||
|
value: number; |
||||
|
color: string; |
||||
|
action: string; |
||||
|
dataIndex: string; |
||||
|
} |
||||
|
|
||||
|
export const growCardList: GrowCardItem[] = [ |
||||
|
{ |
||||
|
title: '获得数量', |
||||
|
icon: 'visit-count|svg', |
||||
|
value: 0, |
||||
|
color: 'green', |
||||
|
action: '天', |
||||
|
dataIndex: 'obtainedQuantity' |
||||
|
}, |
||||
|
{ |
||||
|
title: '分配数量', |
||||
|
icon: 'total-sales|svg', |
||||
|
value: 0, |
||||
|
color: 'blue', |
||||
|
action: '天', |
||||
|
dataIndex: 'allocationQuantity' |
||||
|
}, |
||||
|
{ |
||||
|
title: '呼出数量', |
||||
|
icon: 'download-count|svg', |
||||
|
value: 0, |
||||
|
color: 'orange', |
||||
|
action: '天', |
||||
|
dataIndex: 'followUpQuantity' |
||||
|
}, |
||||
|
{ |
||||
|
title: '有效沟通数量', |
||||
|
icon: 'transaction|svg', |
||||
|
value: 0, |
||||
|
color: 'purple', |
||||
|
action: '天', |
||||
|
dataIndex: 'effectiveTalkQuantity' |
||||
|
}, |
||||
|
{ |
||||
|
title: '被拒绝数量', |
||||
|
icon: 'test|svg', |
||||
|
value: 0, |
||||
|
color: 'purple', |
||||
|
action: '天', |
||||
|
dataIndex: 'rejectedQuantity' |
||||
|
}, |
||||
|
{ |
||||
|
title: '放弃数量', |
||||
|
icon: 'test|svg', |
||||
|
value: 0, |
||||
|
color: 'purple', |
||||
|
action: '天', |
||||
|
dataIndex: 'abandonedQuantity' |
||||
|
}, |
||||
|
{ |
||||
|
title: '接通数量', |
||||
|
icon: 'visit-count|svg', |
||||
|
value: 0, |
||||
|
color: 'green', |
||||
|
action: '天', |
||||
|
dataIndex: 'connectionQuantity' |
||||
|
}, |
||||
|
{ |
||||
|
title: '未接通数量', |
||||
|
icon: 'total-sales|svg', |
||||
|
value: 0, |
||||
|
color: 'blue', |
||||
|
action: '天', |
||||
|
dataIndex: 'unconnectedQuantity' |
||||
|
}, |
||||
|
{ |
||||
|
title: '无人接听', |
||||
|
icon: 'download-count|svg', |
||||
|
value: 0, |
||||
|
color: 'orange', |
||||
|
action: '天', |
||||
|
dataIndex: 'noAnsweredQuantity' |
||||
|
}, |
||||
|
{ |
||||
|
title: '无效信息', |
||||
|
icon: 'transaction|svg', |
||||
|
value: 0, |
||||
|
color: 'purple', |
||||
|
action: '天', |
||||
|
dataIndex: 'invalidInformationQuantity' |
||||
|
}, |
||||
|
{ |
||||
|
title: '空号', |
||||
|
icon: 'test|svg', |
||||
|
value: 0, |
||||
|
color: 'purple', |
||||
|
action: '天', |
||||
|
dataIndex: 'emptyNumberQuantity' |
||||
|
}, |
||||
|
{ |
||||
|
title: '预约数量', |
||||
|
icon: 'test|svg', |
||||
|
value: 0, |
||||
|
color: 'purple', |
||||
|
action: '天', |
||||
|
dataIndex: 'appointmentQuantity' |
||||
|
}, |
||||
|
]; |
||||
|
|
||||
|
|
||||
|
export const historyCardList: GrowCardItem[] = [ |
||||
|
{ |
||||
|
title: '存量放弃', |
||||
|
icon: 'visit-count|svg', |
||||
|
value: 0, |
||||
|
color: 'green', |
||||
|
action: '天', |
||||
|
dataIndex: 'historyAbandonedQuantity' |
||||
|
}, |
||||
|
{ |
||||
|
title: '存量已确认预约', |
||||
|
icon: 'total-sales|svg', |
||||
|
value: 0, |
||||
|
color: 'blue', |
||||
|
action: '天', |
||||
|
dataIndex: 'historyAppointmentQuantity' |
||||
|
}, |
||||
|
{ |
||||
|
title: '存量已接通', |
||||
|
icon: 'download-count|svg', |
||||
|
value: 0, |
||||
|
color: 'orange', |
||||
|
action: '天', |
||||
|
dataIndex: 'historyConnectionQuantity' |
||||
|
}, |
||||
|
{ |
||||
|
title: '存量有效沟通', |
||||
|
icon: 'transaction|svg', |
||||
|
value: 0, |
||||
|
color: 'purple', |
||||
|
action: '天', |
||||
|
dataIndex: 'historyEffectiveTalkQuantity' |
||||
|
}, |
||||
|
{ |
||||
|
title: '存量空号', |
||||
|
icon: 'test|svg', |
||||
|
value: 0, |
||||
|
color: 'purple', |
||||
|
action: '天', |
||||
|
dataIndex: 'historyEmptyNumberQuantity' |
||||
|
}, |
||||
|
{ |
||||
|
title: '存量呼出数量', |
||||
|
icon: 'test|svg', |
||||
|
value: 0, |
||||
|
color: 'purple', |
||||
|
action: '天', |
||||
|
dataIndex: 'historyFollowUpQuantity' |
||||
|
}, |
||||
|
{ |
||||
|
title: '存量无效信息', |
||||
|
icon: 'visit-count|svg', |
||||
|
value: 0, |
||||
|
color: 'green', |
||||
|
action: '天', |
||||
|
dataIndex: 'historyInvalidInformationQuantity' |
||||
|
}, |
||||
|
{ |
||||
|
title: '存量无人接听', |
||||
|
icon: 'total-sales|svg', |
||||
|
value: 0, |
||||
|
color: 'blue', |
||||
|
action: '天', |
||||
|
dataIndex: 'historyNoAnsweredQuantity' |
||||
|
}, |
||||
|
{ |
||||
|
title: '存量被拒绝', |
||||
|
icon: 'download-count|svg', |
||||
|
value: 0, |
||||
|
color: 'orange', |
||||
|
action: '天', |
||||
|
dataIndex: 'historyRejectedQuantity' |
||||
|
}, |
||||
|
{ |
||||
|
title: '存量未接通', |
||||
|
icon: 'transaction|svg', |
||||
|
value: 0, |
||||
|
color: 'purple', |
||||
|
action: '天', |
||||
|
dataIndex: 'historyUnconnectedQuantity' |
||||
|
}, |
||||
|
]; |
||||
|
|
||||
|
import { BasicColumn } from '/@/components/Table' |
||||
|
|
||||
|
export const tableColumns: BasicColumn[] = [ |
||||
|
{ title: '员工姓名', width: 60, dataIndex: 'operatorName' }, |
||||
|
{ title: '分配数量', width: 60, dataIndex: 'allocationQuantity' }, |
||||
|
// { title: '获得数量', width: 60, dataIndex: 'obtainedQuantity' },
|
||||
|
{ title: '呼出数量', width: 60, dataIndex: 'followUpQuantity' }, |
||||
|
{ title: '有效沟通', width: 60, dataIndex: 'effectiveTalkQuantity' }, |
||||
|
{ title: '被拒绝', width: 50, dataIndex: 'rejectedQuantity' }, |
||||
|
{ title: '放弃', width: 60, dataIndex: 'abandonedQuantity' }, |
||||
|
{ title: '接通数量', width: 60, dataIndex: 'connectionQuantity' }, |
||||
|
{ title: '未接通', width: 50, dataIndex: 'unconnectedQuantity' }, |
||||
|
{ title: '无人接听', width: 60, dataIndex: 'noAnsweredQuantity' }, |
||||
|
{ title: '无效信息', width: 60, dataIndex: 'invalidInformationQuantity' }, |
||||
|
{ title: '空号', width: 60, dataIndex: 'emptyNumberQuantity' }, |
||||
|
{ title: '预约数量', width: 60, dataIndex: 'appointmentQuantity' }, |
||||
|
|
||||
|
// { title: '存量放弃', width: 60, dataIndex: 'historyAbandonedQuantity' },
|
||||
|
// { title: '存量已确认预约', width: 90, dataIndex: 'historyAppointmentQuantity' },
|
||||
|
// { title: '存量已接通', width: 70, dataIndex: 'historyConnectionQuantity' },
|
||||
|
// { title: '存量有效沟通', width: 80, dataIndex: 'historyEffectiveTalkQuantity' },
|
||||
|
// { title: '存量空号', width: 60, dataIndex: 'historyEmptyNumberQuantity' },
|
||||
|
// { title: '存量呼出数量', width: 80, dataIndex: 'historyFollowUpQuantity' },
|
||||
|
// { title: '存量无效信息', width: 80, dataIndex: 'historyInvalidInformationQuantity' },
|
||||
|
// { title: '存量无人接听', width: 80, dataIndex: 'historyNoAnsweredQuantity' },
|
||||
|
// { title: '存量被拒绝', width: 80, dataIndex: 'historyRejectedQuantity' },
|
||||
|
// { title: '存量未接通', width: 80, dataIndex: 'historyUnconnectedQuantity' },
|
||||
|
] |
||||
|
|
||||
|
export const historyColumns: BasicColumn[] = [ |
||||
|
{ title: '员工姓名', width: 60, dataIndex: 'operatorName' }, |
||||
|
{ title: '存量放弃', width: 60, dataIndex: 'historyAbandonedQuantity' }, |
||||
|
{ title: '存量已确认预约', width: 90, dataIndex: 'historyAppointmentQuantity' }, |
||||
|
{ title: '存量已接通', width: 70, dataIndex: 'historyConnectionQuantity' }, |
||||
|
{ title: '存量有效沟通', width: 80, dataIndex: 'historyEffectiveTalkQuantity' }, |
||||
|
{ title: '存量空号', width: 60, dataIndex: 'historyEmptyNumberQuantity' }, |
||||
|
{ title: '存量呼出数量', width: 80, dataIndex: 'historyFollowUpQuantity' }, |
||||
|
{ title: '存量无效信息', width: 80, dataIndex: 'historyInvalidInformationQuantity' }, |
||||
|
{ title: '存量无人接听', width: 80, dataIndex: 'historyNoAnsweredQuantity' }, |
||||
|
{ title: '存量被拒绝', width: 80, dataIndex: 'historyRejectedQuantity' }, |
||||
|
{ title: '存量未接通', width: 80, dataIndex: 'historyUnconnectedQuantity' }, |
||||
|
] |
||||
@ -0,0 +1,90 @@ |
|||||
|
<template> |
||||
|
<div class="flex-col"> |
||||
|
<div class="flex-row-center-space" style="padding: 12px 16px 0px 16px;"> |
||||
|
<span style="color: #333;text-align: center;font-size: 22px;">电邀数据统计</span> |
||||
|
<RangePicker v-model:value="value4" :format="dateFormat" :disabled-date="disabledDate" @change="fetchData"/> |
||||
|
</div> |
||||
|
<div class="p-4"> |
||||
|
<GrowCard :loading="loading" :data-list="growCardList" class="enter-y" /> |
||||
|
</div> |
||||
|
<div style="padding: 0px 16px;"> |
||||
|
<BasicTable @register="registerTable" /> |
||||
|
</div> |
||||
|
<div class="p-4"> |
||||
|
<GrowCard :loading="loading" :data-list="historyCardList" class="enter-y" /> |
||||
|
</div> |
||||
|
<div style="padding: 0px 16px;"> |
||||
|
<BasicTable @register="historyTable" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
<script lang="ts" setup> |
||||
|
import { onMounted, ref } from 'vue'; |
||||
|
import moment, {Moment} from 'moment'; |
||||
|
import { formatToDate } from '/@/utils/dateUtil' |
||||
|
import GrowCard from './components/GrowCard.vue'; |
||||
|
import { RangePicker } from 'ant-design-vue' |
||||
|
import { BasicTable, useTable } from '/@/components/Table' |
||||
|
import { tableColumns, growCardList, historyCardList, historyColumns } from './data' |
||||
|
import { getInvitationStatic, getInvitationData } from '/@/api/clue' |
||||
|
|
||||
|
const dateFormat = 'YYYY-MM-DD'; |
||||
|
const value4 = ref<any[]>([]) |
||||
|
const loading = ref(true); |
||||
|
onMounted(() => { |
||||
|
value4.value = [moment().format(dateFormat), moment().format(dateFormat)] |
||||
|
fetchData() |
||||
|
}) |
||||
|
|
||||
|
function disabledDate (current: Moment) { |
||||
|
return current && current >= moment().endOf('day'); |
||||
|
} |
||||
|
|
||||
|
async function fetchData(){ |
||||
|
loading.value = true; |
||||
|
const result = await getInvitationStatic({startTime: formatToDate(value4.value[0]), endTime: formatToDate(value4.value[1])}) |
||||
|
if(result){ |
||||
|
for (let index = 0; index < growCardList.length; index++) { |
||||
|
growCardList[index].value = result[growCardList[index].dataIndex] |
||||
|
} |
||||
|
for (let index = 0; index < historyCardList.length; index++) { |
||||
|
historyCardList[index].value = result[historyCardList[index].dataIndex] |
||||
|
} |
||||
|
} else { |
||||
|
for (let index = 0; index < growCardList.length; index++) { |
||||
|
growCardList[index].value = 0 |
||||
|
} |
||||
|
for (let index = 0; index < historyCardList.length; index++) { |
||||
|
historyCardList[index].value = 0 |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const dataSource = await getInvitationData({startTime: formatToDate(value4.value[0]), endTime: formatToDate(value4.value[1])}) |
||||
|
setTableData(dataSource) |
||||
|
setHistoryTableData(dataSource) |
||||
|
loading.value = false; |
||||
|
} |
||||
|
|
||||
|
const [registerTable, { setTableData }] = useTable({ |
||||
|
bordered: true, |
||||
|
immediate: false, |
||||
|
canResize: false, |
||||
|
useSearchForm: false, |
||||
|
showIndexColumn: true, |
||||
|
showTableSetting: false, |
||||
|
pagination: false, |
||||
|
columns: tableColumns, |
||||
|
}) |
||||
|
|
||||
|
const [historyTable, { setTableData: setHistoryTableData }] = useTable({ |
||||
|
bordered: true, |
||||
|
immediate: false, |
||||
|
canResize: false, |
||||
|
useSearchForm: false, |
||||
|
showIndexColumn: true, |
||||
|
showTableSetting: false, |
||||
|
pagination: false, |
||||
|
columns: historyColumns, |
||||
|
}) |
||||
|
|
||||
|
</script> |
||||
Write
Preview
Loading…
Cancel
Save