14 changed files with 630 additions and 60 deletions
Split 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