diff --git a/src/api/home/reportChannel.js b/src/api/home/reportChannel.js new file mode 100644 index 0000000..0e1d268 --- /dev/null +++ b/src/api/home/reportChannel.js @@ -0,0 +1,4 @@ +import request from '@/config/axios' +export const getList = async (data) => { + return await request.post({ url: '/admin-api/crm/sch-clue/clueQuality/report', data }) +} diff --git a/src/api/home/reportSignRate.js b/src/api/home/reportSignRate.js index 3ceb4af..0b515fe 100644 --- a/src/api/home/reportSignRate.js +++ b/src/api/home/reportSignRate.js @@ -1,5 +1,4 @@ import request from '@/config/axios' -// 线索情况 export const getList = async (data) => { return await request.post({ url: '/admin-api/crm/sch-clue/signRate/report', data }) } diff --git a/src/router/modules/remaining.ts b/src/router/modules/remaining.ts index 85ab24e..13f762a 100644 --- a/src/router/modules/remaining.ts +++ b/src/router/modules/remaining.ts @@ -130,27 +130,27 @@ const remainingRouter: AppRouteRecordRaw[] = [ // } // ] // }, - { - path: '/Basic', - component: Layout, - name: 'Basic', - meta: { title: '菜单管理' }, - redirect: '/Basic/menu', - children: [ - { - path: 'menu', - component: () => import('@/views/Basic/Menu/index.vue'), - name: 'Menu', - meta: { - canTo: true, - hidden: true, - noTagsView: false, - icon: 'ep:user', - title: '菜单管理' - } - } - ] - }, + // { + // path: '/Basic', + // component: Layout, + // name: 'Basic', + // meta: { title: '菜单管理' }, + // redirect: '/Basic/menu', + // children: [ + // { + // path: 'menu', + // component: () => import('@/views/Basic/Menu/index.vue'), + // name: 'Menu', + // meta: { + // canTo: true, + // hidden: true, + // noTagsView: false, + // icon: 'ep:user', + // title: '菜单管理' + // } + // } + // ] + // }, { path: '/login', component: () => import('@/views/Login/Login.vue'), diff --git a/src/views/Home/ChannelReport.vue b/src/views/Home/ChannelReport.vue new file mode 100644 index 0000000..c38c3b2 --- /dev/null +++ b/src/views/Home/ChannelReport.vue @@ -0,0 +1,140 @@ +<template> + <div> + <ContentWrap> + <el-form :model="searchForm" label-width="0" inline> + <el-form-item> + <el-date-picker + v-model="searchForm.period" + type="month" + format="YYYY-MM" + value-format="YYYY-MM" + placeholder="选择年月" + :clearable="false" + /> + </el-form-item> + <el-form-item> + <el-tree-select + v-model="searchForm.sourceId" + :data="sourceOptions" + :props="defaultProps" + check-strictly + node-key="sourceId" + placeholder="请选择渠道" + /> + </el-form-item> + <el-form-item> + <el-button @click="handleSearch">查询</el-button> + <el-button @click="handleReset">重置</el-button> + </el-form-item> + </el-form> + + <el-table + v-loading="loading" + :data="tableList" + border + stripe + :summary-method="getSummaries" + show-summary + > + <el-table-column prop="sourceName" label="渠道名称" /> + <el-table-column prop="newClueSignNum" label="新线索成交数" sortable /> + <el-table-column prop="newClueNum" label="新线索总数" sortable /> + <el-table-column prop="rate" label="新线索成交率" sortable :formatter="parseRate" /> + <el-table-column prop="reallyClueSignNum" sortable> + <template #header> <Tooltip message="新线索+老线索" /> <span>实际成交数</span> </template> + </el-table-column> + </el-table> + </ContentWrap> + </div> +</template> + +<script setup name="ChannelReport"> +import * as reportApi from '@/api/home/reportChannel' +import { getSimpleSourceList } from '@/api/clue/source' +import { removeNullField } from '@/utils' +import { formatDate } from '@/utils/formatTime' +import { handleTree } from '@/utils/tree' + +const defaultProps = { + children: 'children', + label: 'sourceName', + value: 'sourceId', + isLeaf: 'leaf' +} + +onMounted(() => { + getOptions() + handleReset() + handleSearch() +}) + +const searchForm = ref({}) + +function handleReset() { + searchForm.value = { + sourceName: undefined, + period: formatDate(new Date(), 'YYYY-MM') + } +} + +const sourceOptions = ref([]) + +function getOptions() { + getSimpleSourceList().then((data) => { + sourceOptions.value = handleTree(data, 'sourceId') + }) +} + +const loading = ref(false) +const tableList = ref([]) +async function handleSearch() { + loading.value = true + try { + const data = await reportApi.getList(removeNullField(searchForm.value)) + tableList.value = data + } finally { + loading.value = false + } +} + +function parseRate(row) { + return Number(row.rate * 100).toFixed(2) + '%' +} + +function getSummaries({ columns, data }) { + let sums = [] + columns.forEach((column, index) => { + if (index == 0) { + sums[index] = '合计' + return + } + const values = data.map((item) => Number(item[column.property])) + if (!values.every((value) => Number.isNaN(value))) { + if (column.property == 'rate') { + const sum = data.reduce( + (pre, cur) => ({ + clueNum: pre.clueNum + cur.newClueNum, + signNum: pre.signNum + cur.newClueSignNum + }), + { clueNum: 0, signNum: 0 } + ) + sums[index] = sum.clueNum > 0 ? ((sum.signNum * 100) / sum.clueNum).toFixed(2) + '%' : 0 + } else { + sums[index] = values.reduce((prev, curr) => { + const value = Number(curr) + if (!Number.isNaN(value)) { + return prev + curr + } else { + return prev + } + }, 0) + } + } else { + sums[index] = '' + } + }) + return sums +} +</script> + +<style lang="scss" scoped></style> diff --git a/src/views/Home/CloseRate.vue b/src/views/Home/CloseRate.vue index f873d6c..aa9aa68 100644 --- a/src/views/Home/CloseRate.vue +++ b/src/views/Home/CloseRate.vue @@ -21,7 +21,7 @@ </el-form-item> </el-form> - <el-table :data="tableList" border stripe :default-sort="{ prop: 'totalRate' }"> + <el-table v-loading="loading" :data="tableList" border stripe> <el-table-column type="index" width="50" fixed="left" /> <el-table-column prop="nickname" label="姓名" width="80" /> <el-table-column diff --git a/src/views/Home/channel.vue b/src/views/Home/SignReport.vue similarity index 76% rename from src/views/Home/channel.vue rename to src/views/Home/SignReport.vue index 082b0bf..b780bce 100644 --- a/src/views/Home/channel.vue +++ b/src/views/Home/SignReport.vue @@ -1,5 +1,5 @@ <template> - <div> </div> + <div>成交报表</div> </template> <script setup></script>