|
@@ -1,18 +1,70 @@
|
|
|
<script setup>
|
|
|
import {downloadByFlow} from '@/utils/common-methods'
|
|
|
import {getSellerTeamList} from '@/api/crm'
|
|
|
- import {getIncomeChartData} from '@/api/financialStatistics'
|
|
|
+ import {getIncomeChartData,getIncomeDetailData} from '@/api/financialStatistics'
|
|
|
import chart from '@/components/echart/index.vue'
|
|
|
import {baseOptions} from '@/components/echart/baseOptions'
|
|
|
import {getSimpleServiceList} from '@/api/financialMana'
|
|
|
+ import { Search } from '@element-plus/icons-vue'
|
|
|
|
|
|
const basicOptions=baseOptions()
|
|
|
-
|
|
|
+
|
|
|
+ const statisticalMethod=[{label:'开票统计',value:0},{label:'到款统计',value:1}]
|
|
|
const dimensionArray=[{label:'月度',tag:0},{label:'季度',tag:1},{label:'半年度',tag:2},{label:'年度',tag:3},{label:'月度累计',tag:4}]
|
|
|
const customerTypeArray=[{label:'全部',value:0},{label:'新客户',value:1},{label:'老客户',value:2},{label:'未续约',value:3}]
|
|
|
const sellerArray=ref(null)
|
|
|
+ const detailData = ref([])
|
|
|
+ const tableRef = ref(null)
|
|
|
+
|
|
|
+ const toolTipText = computed(() => {
|
|
|
+ return searchParams.income_type == 0 ?
|
|
|
+ (searchParams.company_type == 3 ? `未续约收入金额:当期未续约收入金额=去年同期总收入金额(新客户+老客户)-当期老客户收入金额<br />移动平均值:未续约收入金额的移动平均值,移动间隔长度为3个月<br />注:该处的收入金额为开票金额。`
|
|
|
+ : `业务收入金额:此处指开票金额,含已开票已到款、已开票未到款、未开票已到款<br />同比值:今年同期/去年同期-1`
|
|
|
+ ) :
|
|
|
+ (searchParams.company_type == 3 ? `未续约收入金额:当期未续约收入金额=去年同期总收入金额(新客户+老客户)-当期老客户收入金额<br />移动平均值:未续约收入金额的移动平均值,移动间隔长度为3个月<br />注:该处的收入金额为到款金额。`
|
|
|
+ : `业务收入金额:此处指到款金额,含已开票已到款、未开票已到款<br />同比值:今年同期/去年同期-1`
|
|
|
+ );
|
|
|
+ })
|
|
|
+
|
|
|
+ const detailColumns = computed(() => {
|
|
|
+ return [{
|
|
|
+ label: '客户名称',
|
|
|
+ key: 'company_name',
|
|
|
+ minwidthsty: '206px',
|
|
|
+ },{
|
|
|
+ label: '是否新客户',
|
|
|
+ key: 'contract_type',
|
|
|
+ minwidthsty: '100px',
|
|
|
+ },{
|
|
|
+ label: searchParams.income_type?'到款日期':'开票日期',
|
|
|
+ key: 'invoice_time',
|
|
|
+ minwidthsty: '140px',
|
|
|
+ sortable:'custom',
|
|
|
+ },{
|
|
|
+ label: searchParams.income_type?'到款金额':'开票金额',
|
|
|
+ key: 'origin_amount',
|
|
|
+ minwidthsty: '140px',
|
|
|
+ },{
|
|
|
+ label: searchParams.income_type?'到款单位':'开票单位',
|
|
|
+ key: 'unit_name',
|
|
|
+ minwidthsty: '120px',
|
|
|
+ },{
|
|
|
+ label: searchParams.income_type?'到款金额(换算后)':'开票金额(换算后)',
|
|
|
+ key: 'amount',
|
|
|
+ minwidthsty: '140px',
|
|
|
+ },{
|
|
|
+ label: '销售',
|
|
|
+ key: 'seller_name',
|
|
|
+ minwidthsty: '140px',
|
|
|
+ },{
|
|
|
+ label: '套餐',
|
|
|
+ key: 'services_name',
|
|
|
+ minwidthsty: '182px',
|
|
|
+ }]
|
|
|
+ })
|
|
|
|
|
|
const searchParams=reactive({
|
|
|
+ income_type:0,
|
|
|
list_param:0,
|
|
|
company_type:0,
|
|
|
start_date:'',
|
|
@@ -23,10 +75,18 @@
|
|
|
// 不是后端所需要的值,用于axios的请求拦截器,具体请看@/utils/request.js文件
|
|
|
takeLastRequestResult:true
|
|
|
})
|
|
|
+ const detailParams = reactive({
|
|
|
+ page_size:10,
|
|
|
+ current:1,
|
|
|
+ keyword:'',
|
|
|
+ sort_type:'',//排序
|
|
|
+ })
|
|
|
+ const detailTotal = ref(0)
|
|
|
// 加载状态
|
|
|
const loading=reactive({
|
|
|
list:true,
|
|
|
- export:false
|
|
|
+ export:false,
|
|
|
+ detail:false
|
|
|
})
|
|
|
const chartOptions=ref(basicOptions)
|
|
|
|
|
@@ -38,7 +98,7 @@
|
|
|
searchParams.start_date = value[0]
|
|
|
searchParams.end_date=value[1]
|
|
|
}
|
|
|
- getList()
|
|
|
+ changeChartDetail()
|
|
|
})
|
|
|
|
|
|
const sellersCheckList=ref(null)
|
|
@@ -48,7 +108,7 @@
|
|
|
}else{
|
|
|
searchParams.seller_ids = value.join(',')
|
|
|
}
|
|
|
- getList()
|
|
|
+ changeChartDetail()
|
|
|
})
|
|
|
|
|
|
const getSellerData=()=>{
|
|
@@ -169,9 +229,26 @@
|
|
|
})
|
|
|
}
|
|
|
|
|
|
+ //获取数据详情列表
|
|
|
+ const getDetailList = () => {
|
|
|
+ if(searchParams.company_type == 3) { //未续约
|
|
|
+ detailData.value = [];
|
|
|
+ detailParams.keyword = '';
|
|
|
+ detailParams.sort_type = '';
|
|
|
+ return
|
|
|
+ };
|
|
|
+ loading.detail=true;
|
|
|
+ getIncomeDetailData({...searchParams,...detailParams}).then(res=>{
|
|
|
+ loading.detail=false;
|
|
|
+ if(res.code != 200) return;
|
|
|
+ detailData.value = res.data.list || [];
|
|
|
+ detailTotal.value = res.data.page?.total || 0;
|
|
|
+ }).catch(err=>{loading.detail=false;})
|
|
|
+ }
|
|
|
+
|
|
|
const exportData=()=>{
|
|
|
loading.export=true
|
|
|
- getIncomeChartData({...searchParams,is_export:1}).then(res=>{
|
|
|
+ getIncomeDetailData({...searchParams,...detailParams,is_export:1}).then(res=>{
|
|
|
downloadByFlow(res,'xlxs','业务收入金额统计')
|
|
|
}).finally(()=>{
|
|
|
loading.export=false
|
|
@@ -188,25 +265,67 @@
|
|
|
// 选择套餐改变
|
|
|
const serviceTypeChange=(value)=>{
|
|
|
searchParams.service_types=value.join(',')
|
|
|
- getList()
|
|
|
+ changeChartDetail()
|
|
|
+ }
|
|
|
+ //初始化detailParams
|
|
|
+ const initDetailParams = () => {
|
|
|
+ detailParams.current = 1;
|
|
|
+ detailParams.keyword = '';
|
|
|
+ detailParams.sort_type = '';
|
|
|
+ tableRef.value?.clearSort();
|
|
|
+ };
|
|
|
+ //更新数据
|
|
|
+ const changeChartDetail = () => {
|
|
|
+ initDetailParams()
|
|
|
+ getList();
|
|
|
+ getDetailList();
|
|
|
+ }
|
|
|
+ //切换统计种类
|
|
|
+ const changeStatisticalMethod = () => {
|
|
|
+ changeChartDetail()
|
|
|
+ }
|
|
|
+ //表格客户名称搜索
|
|
|
+ const handleSearch = () => {
|
|
|
+ changePageNo(1);
|
|
|
+ };
|
|
|
+ //表格切换page_size
|
|
|
+ const changePageSize = (pageSize) => {
|
|
|
+ detailParams.page_size = pageSize;
|
|
|
+ getDetailList();
|
|
|
+ }
|
|
|
+ //表格切换current
|
|
|
+ const changePageNo = (pageNo) => {
|
|
|
+ detailParams.current = pageNo;
|
|
|
+ getDetailList()
|
|
|
+ }
|
|
|
+ //表格排序
|
|
|
+ const sortChange = ({order}) => {
|
|
|
+ detailParams.sort_type = order? order=="descending"?"desc":"asc" :""
|
|
|
+ getDetailList();
|
|
|
}
|
|
|
|
|
|
initOptions()
|
|
|
getSellerData()
|
|
|
getList()
|
|
|
+ getDetailList()
|
|
|
getServiceListFun()
|
|
|
</script>
|
|
|
|
|
|
<template>
|
|
|
<div class="business-income-container" id="business-income-container">
|
|
|
+ <el-radio-group v-model="searchParams.income_type" size="large"
|
|
|
+ style="margin: 0 0 30px 0;">
|
|
|
+ <el-radio-button v-for="item in statisticalMethod" :key="item.value" class="title-dimension-radio" @change="changeStatisticalMethod"
|
|
|
+ :label="item.value" >{{ item.label }}</el-radio-button>
|
|
|
+ </el-radio-group>
|
|
|
<div class="business-income-top">
|
|
|
<div class="business-income-search-zone">
|
|
|
- <el-radio-group v-model="searchParams.list_param" size="large" @change="getList"
|
|
|
+ <el-radio-group v-model="searchParams.list_param" size="large" @change="changeChartDetail"
|
|
|
style="margin: 0 30px 8px 0;">
|
|
|
<el-radio-button v-for="item in dimensionArray" :key="item.tag" class="dimension-radio"
|
|
|
:label="item.tag" >{{ item.label }}</el-radio-button>
|
|
|
</el-radio-group>
|
|
|
- <el-radio-group v-model="searchParams.company_type" size="large" @change="getList"
|
|
|
+ <el-radio-group v-model="searchParams.company_type" size="large" @change="changeChartDetail"
|
|
|
style="margin: 0 30px 8px 0;">
|
|
|
<el-radio-button v-for="item in customerTypeArray" :key="item.value" class="dimension-radio"
|
|
|
:label="item.value" >{{ item.label }}</el-radio-button>
|
|
@@ -218,30 +337,74 @@
|
|
|
<el-cascader :options="serviceTypeArray" style="width: 240px;margin-bottom: 8px;margin-right: 30px;"
|
|
|
@change="serviceTypeChange" placeholder="请选择套餐" clearable collapse-tags :show-all-levels="false"
|
|
|
:props="{multiple:true,label:'title',value:'service_template_id',children:'children',emitPath:false}"></el-cascader>
|
|
|
- <el-date-picker v-model="invoiceTime" type="monthrange"
|
|
|
+ <el-date-picker v-model="invoiceTime" type="monthrange" unlink-panels
|
|
|
start-placeholder="开始日期" end-placeholder="结束日期" style="max-width: 238px;margin:0 30px 8px 0;"
|
|
|
value-format="YYYY-MM"></el-date-picker>
|
|
|
</div>
|
|
|
- <el-button @click="exportData" type="primary" size="large" style="width: 118px;" :loading="loading.export"
|
|
|
- v-if="searchParams.company_type!=3">导出数据</el-button>
|
|
|
</div>
|
|
|
- <div class="business-income-chart" v-loading="loading.list" element-loading-text="图表加载中……">
|
|
|
- <chart :options="chartOptions" style="width: 75%;height: 80%;"/>
|
|
|
- <el-tooltip placement="top-end">
|
|
|
- <span class="notRenewed-legend-hint" v-show="searchParams.company_type==3">
|
|
|
- <el-icon size="16px" style="margin-right:4px ;">
|
|
|
- <svg-icon name="svgIcon-common-infoReverse" color="var(--themeColor)"></svg-icon>
|
|
|
- </el-icon>
|
|
|
- <span>图例说明</span>
|
|
|
- </span>
|
|
|
- <template #content>
|
|
|
- <div>
|
|
|
- 未续约收入金额:当期未续约收入金额=去年同期总收入金额(新客户+老客户)-当期老客户收入金额
|
|
|
- <br />
|
|
|
- 移动平均值:未续约收入金额的移动平均值,移动间隔长度3个月。
|
|
|
+ <div class="chart-detail-wrap">
|
|
|
+ <div class="business-income-chart" v-loading="loading.list" element-loading-text="图表加载中……">
|
|
|
+ <chart :options="chartOptions" style="width: 75%;height: 80%;"/>
|
|
|
+ <el-tooltip placement="top-end">
|
|
|
+ <span class="notRenewed-legend-hint">
|
|
|
+ <el-icon size="16px" style="margin-right:4px ;">
|
|
|
+ <svg-icon name="svgIcon-common-infoReverse" color="var(--themeColor)"></svg-icon>
|
|
|
+ </el-icon>
|
|
|
+ <span>图例说明</span>
|
|
|
+ </span>
|
|
|
+ <template #content>
|
|
|
+ <div v-html="toolTipText"></div>
|
|
|
+ </template>
|
|
|
+ </el-tooltip>
|
|
|
+ </div>
|
|
|
+ <div class="business-income-detail" v-if="searchParams.company_type!=3" v-loading="loading.detail" element-loading-text="数据加载中……">
|
|
|
+ <div class="detail-title">
|
|
|
+ <div class="title-text">数据详情</div>
|
|
|
+ <div style="display: flex;align-items: center;">
|
|
|
+ <el-input
|
|
|
+ v-model="detailParams.keyword"
|
|
|
+ style="width: 240px;margin-right: 25px;"
|
|
|
+ placeholder="请输入客户名称"
|
|
|
+ :prefix-icon="Search"
|
|
|
+ @input="handleSearch"
|
|
|
+ clearable/>
|
|
|
+ <el-button @click="exportData" type="primary" size="large" style="width: 118px;" :loading="loading.export"
|
|
|
+ v-if="searchParams.company_type!=3">导出数据</el-button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </template>
|
|
|
- </el-tooltip>
|
|
|
+ <el-table :data="detailData" border style="width: 100%;margin-top: 20px;" max-height="600px" @sort-change="sortChange" ref="tableRef">
|
|
|
+ <el-table-column
|
|
|
+ v-for="item in detailColumns"
|
|
|
+ :key="item.label"
|
|
|
+ :label="item.label"
|
|
|
+ :width="item.widthsty"
|
|
|
+ :min-width="item.minwidthsty"
|
|
|
+ show-overflow-tooltip
|
|
|
+ :sortable="item.sortable || false"
|
|
|
+ :prop="item.key"
|
|
|
+ >
|
|
|
+ <template #header>
|
|
|
+ <el-tooltip
|
|
|
+ :content="searchParams.income_type == 0 ? `开票金额:已开票已到款、已开票未到款、未开票已到款<br/>注:未开票已到款的也算作开票金额进行统计。` : `到款金额:已开票已到款、未开票已到款`"
|
|
|
+ placement="top"
|
|
|
+ v-if="item.key == 'origin_amount'"
|
|
|
+ raw-content>
|
|
|
+ <span style="display: inline-flex;align-items: center;">{{ item.label }}
|
|
|
+ <svg-Icon name="svgIcon-financial-info" size="18" style="margin-left: 5px;color: white;" />
|
|
|
+ </span>
|
|
|
+ </el-tooltip>
|
|
|
+ <span v-else>{{ item.label }}</span>
|
|
|
+ </template>
|
|
|
+ <template #default="{row}">
|
|
|
+ <span v-if="item.key == 'contract_type'">{{ row.contract_type==0?'/':row.contract_type==1?'是':'否' }}</span>
|
|
|
+ <span v-else>{{ row[item.key] }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ <m-page :pageSize="detailParams.page_size" :page_no="detailParams.current"
|
|
|
+ style="display: flex;justify-content: flex-end;margin-top: 30px;"
|
|
|
+ :total="detailTotal" @handleCurrentChange="changePageNo" @handleSizeChange="changePageSize"/>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
@@ -259,25 +422,43 @@
|
|
|
flex-wrap: wrap;
|
|
|
}
|
|
|
}
|
|
|
- .business-income-chart{
|
|
|
+ .chart-detail-wrap{
|
|
|
+ width: 100%;
|
|
|
border: 1px solid #DCDFE6;
|
|
|
background-color: white;
|
|
|
- height: calc(100vh - 190px);
|
|
|
- padding: 20px 30px;
|
|
|
- box-sizing: border-box;
|
|
|
- display: flex;
|
|
|
- justify-content: center;
|
|
|
- align-items: center;
|
|
|
- position: relative;
|
|
|
- .notRenewed-legend-hint{
|
|
|
- position: absolute;
|
|
|
- right: 30px;
|
|
|
- top: 20px;
|
|
|
- cursor: pointer;
|
|
|
- font-size: 14px;
|
|
|
- color: $themeColor;
|
|
|
+ .business-income-chart{
|
|
|
+ width: 100%;
|
|
|
+ height: calc(100vh - 280px);
|
|
|
+ padding: 0 30px;
|
|
|
+ box-sizing: border-box;
|
|
|
display: flex;
|
|
|
+ justify-content: center;
|
|
|
align-items: center;
|
|
|
+ position: relative;
|
|
|
+ .notRenewed-legend-hint{
|
|
|
+ position: absolute;
|
|
|
+ right: 30px;
|
|
|
+ top: 20px;
|
|
|
+ cursor: pointer;
|
|
|
+ font-size: 14px;
|
|
|
+ color: $themeColor;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .business-income-detail{
|
|
|
+ box-sizing: border-box;
|
|
|
+ padding: 0 30px 40px;
|
|
|
+ .detail-title{
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ .title-text{
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 400;
|
|
|
+ color: #333333;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -285,6 +466,17 @@
|
|
|
</style>
|
|
|
<style lang="scss">
|
|
|
#business-income-container{
|
|
|
+ .title-dimension-radio{
|
|
|
+ span{
|
|
|
+ box-sizing: border-box;
|
|
|
+ border: 1px solid #AB9523;
|
|
|
+ font-size: 16px;
|
|
|
+ display: inline-block;
|
|
|
+ color: #AB9523;
|
|
|
+ padding: 12px 30px;
|
|
|
+ font-weight: 500;
|
|
|
+ }
|
|
|
+ }
|
|
|
.dimension-radio{
|
|
|
span{
|
|
|
display: inline-block;
|
|
@@ -297,5 +489,10 @@
|
|
|
color:white
|
|
|
}
|
|
|
}
|
|
|
+ .business-income-detail{
|
|
|
+ .el-table td.el-table__cell, .el-table th.el-table__cell.is-leaf{
|
|
|
+ height: 50px;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|