<template> <div class="app-container"> <!-- 筛选项 --> <div class="seachData" ref="seachDatas"> <el-form :inline="true"> <div class="seachData-left"> <el-form-item label="日期"> <el-date-picker v-model="searchDateRange" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :picker-options="pickerOptions"></el-date-picker> </el-form-item> <el-form-item label="媒体"> <el-select v-model="queryParams.filtering.mediaCode" :placeholder="mediaCode.placeholder" clearable size="small" style="width: 220px"> <el-option v-for="dict in mediaCode.data" :key="dict.value" :label="dict.name" :value="dict.value" /> </el-select> </el-form-item> <el-form-item label="客户区域"> <el-select v-model="queryParams.filtering.customerArea" clearable placeholder="请选择客户区域"> <el-option v-for="item in customerAreaOptions" :key="item.value" :label="item.label" :value="item.value"> </el-option> </el-select> </el-form-item> <el-form-item label="客户类型"> <el-select v-model="queryParams.filtering.customerType" clearable placeholder="请选择客户类型"> <el-option v-for="item in customerTypeOptions" :key="item.value" :label="item.label" :value="item.value"> </el-option> </el-select> </el-form-item> <el-form-item label="数据来源"> <el-select v-model="queryParams.filtering.source" clearable :placeholder="sourceOptions.placeholder"> <el-option v-for="item in sourceOptions.data" :key="item.value" :label="item.label" :value="item.value"> </el-option> </el-select> </el-form-item> <el-form-item label="应用包名"> <el-input v-model="queryParams.filtering.appPackageName" placeholder="请输入应用包名" clearable size="small" style="width: 220px" @keyup.enter.native="handleQuery" /> </el-form-item> <el-form-item label="应用名称"> <el-input v-model="queryParams.filtering.appName" placeholder="请输入应用名称" clearable size="small" style="width: 220px" @keyup.enter.native="handleQuery" /> </el-form-item> <el-form-item label="客户编号"> <el-input v-model="customerId" placeholder="输入多个客户编号时用,隔开" clearable size="small" style="width: 220px" @keyup.enter.native="handleQuery" /> </el-form-item> <el-form-item label="客户名称"> <el-input v-model="queryParams.filtering.customerName" placeholder="请输入客户名称" clearable size="small" style="width: 220px" @keyup.enter.native="handleQuery" /> </el-form-item> <el-form-item label="细分项"> <el-select v-model="groupBy" multiple collapse-tags :placeholder="groupByOptions.placeholder"> <el-option v-for="item in groupByOptions.data" :key="item.value" :label="item.name" :value="item.value"> </el-option> </el-select> </el-form-item> </div> <div class="seachData-right"> <el-form-item class="active"> <el-button type="primary" size="mini" @click="handleQuery">筛选</el-button> <el-button type="text" size="mini" @click="resetHandleQuery">重置筛选</el-button> </el-form-item> </div> <div class="seachData-next-line"> <div class="seachData-next-div left-div"> </div> <div class="seachData-next-div right-div"> <el-button type="primary" size="mini" :disabled="downloadLoading" :loading="downloadLoading" @click="handleDownload">下载报表</el-button> </div> </div> </el-form> </div> <el-table class="seachTable" v-loading="loading" :data="customerList" :height="tableHeightCount" :default-sort="{prop: 'asc', order: 'desc'}" ref="customerListHeight" border> <el-table-column fixed="left" label="日期" align="left" prop="dateInt" :key="'dateInt'" sortable width="200"> <template slot-scope="scope"> <span>{{ scope.row.dateInt ? scope.row.dateInt : '-' }}</span> </template> </el-table-column> <el-table-column fixed="left" label="媒体" align="left" prop="mediaCode" :key="Math.random()" v-if="queryParams.groupBy.mediaCode" min-width="90"> <template slot-scope="scope"> <span>{{ scope.row.mediaCode }}</span> </template> </el-table-column> <el-table-column fixed="left" label="客户名称" align="left" prop="customerName" :key="Math.random()" v-if="queryParams.groupBy.customerName" :show-overflow-tooltip="true" min-width="200"> <template slot-scope="scope"> <div class="ellipsis-hidden">{{ scope.row.customerName ? scope.row.customerName : '-' }}</div> </template> </el-table-column> <el-table-column label="客户编号" align="left" prop="customerId" :key="Math.random()" v-if="queryParams.groupBy.customerId" :show-overflow-tooltip="true" min-width="200"> <template slot-scope="scope"> <div class="ellipsis-hidden">{{ scope.row.customerId ? scope.row.customerId : '-' }}</div> </template> </el-table-column> <el-table-column label="客户区域" align="left" prop="customerArea" :key="Math.random()" v-if="queryParams.groupBy.customerArea" :show-overflow-tooltip="true" min-width="90"> <template slot-scope="scope"> <div class="ellipsis-hidden">{{ scope.row.customerArea ? scope.row.customerArea : '-' }}</div> </template> </el-table-column> <el-table-column label="客户类型" align="left" prop="customerType" :key="Math.random()" v-if="queryParams.groupBy.customerType" :show-overflow-tooltip="true" min-width="90"> <template slot-scope="scope"> <div class="ellipsis-hidden">{{ typeText(scope.row.customerType) ? typeText(scope.row.customerType) : '-'}}</div> </template> </el-table-column> <el-table-column label="资质主体名称" align="left" prop="corporationName" :key="Math.random()" v-if="queryParams.groupBy.corporationName" :show-overflow-tooltip="true" min-width="90"> <template slot-scope="scope"> <div class="ellipsis-hidden">{{ scope.row.corporationName ? scope.row.corporationName : '-'}}</div> </template> </el-table-column> <el-table-column label="一级行业" align="left" prop="firstIndustryName" :key="Math.random()" v-if="queryParams.groupBy.firstIndustryName" :show-overflow-tooltip="true" min-width="90"> <template slot-scope="scope"> <div class="ellipsis-hidden">{{ scope.row.firstIndustryName ? scope.row.firstIndustryName : '-'}}</div> </template> </el-table-column> <el-table-column label="二级行业" align="left" prop="secondIndustryName" :key="Math.random()" v-if="queryParams.groupBy.secondIndustryName" :show-overflow-tooltip="true" min-width="90"> <template slot-scope="scope"> <div class="ellipsis-hidden">{{ scope.row.secondIndustryName ? scope.row.secondIndustryName : '-'}}</div> </template> </el-table-column> <el-table-column label="应用包名" align="left" prop="appPackageName" :key="Math.random()" v-if="queryParams.groupBy.appPackageName" :show-overflow-tooltip="true" min-width="90"> <template slot-scope="scope"> <div class="ellipsis-hidden">{{ scope.row.appPackageName ? scope.row.appPackageName : '-'}}</div> </template> </el-table-column> <el-table-column label="应用名称" align="left" prop="appName" :key="Math.random()" v-if="queryParams.groupBy.appName" :show-overflow-tooltip="true" min-width="90"> <template slot-scope="scope"> <div class="ellipsis-hidden">{{ scope.row.appName ? scope.row.appName : '-'}}</div> </template> </el-table-column> <el-table-column label="数据来源" align="left" prop="source" :key="Math.random()" v-if="queryParams.groupBy.source" :show-overflow-tooltip="true" min-width="90"> <template slot-scope="scope"> <div class="ellipsis-hidden">{{ typeTextSource(scope.row.source)}}</div> </template> </el-table-column> <el-table-column label="有消耗计划总数" align="right" prop="costAboveZeroPlanNum" :key="'costAboveZeroPlanNum'" sortable :show-overflow-tooltip="true" min-width="140"> <template slot-scope="scope"> <div class="ellipsis-hidden">{{ scope.row.costAboveZeroPlanNum ? scope.row.costAboveZeroPlanNum : '-' }}</div> </template> </el-table-column> <el-table-column label="新增计划数" align="right" prop="newPlanNum" :key="'newPlanNum'" sortable :show-overflow-tooltip="true" min-width="140"> <template slot-scope="scope"> <div class="ellipsis-hidden">{{ scope.row.newPlanNum ? scope.row.newPlanNum : '-' }}</div> </template> </el-table-column> <el-table-column label="新增且有消耗的计划数" align="right" prop="costAboveZeroNewPlanNum" :key="'costAboveZeroNewPlanNum'" sortable :show-overflow-tooltip="true" min-width="140"> <template slot-scope="scope"> <div class="ellipsis-hidden">{{ scope.row.costAboveZeroNewPlanNum ? scope.row.costAboveZeroNewPlanNum : '-' }}</div> </template> </el-table-column> <el-table-column label="消耗" align="right" prop="cost" :key="'cost'" sortable :show-overflow-tooltip="true" min-width="140"> <template slot-scope="scope"> <div class="ellipsis-hidden">{{ scope.row.cost ? scope.row.cost : '-' }}</div> </template> </el-table-column> <el-table-column label="展示" align="right" prop="show" :key="'show'" sortable :show-overflow-tooltip="true" min-width="140"> <template slot-scope="scope"> <div class="ellipsis-hidden">{{ scope.row.show ? scope.row.show : '-' }}</div> </template> </el-table-column> <el-table-column label="点击" align="right" prop="click" :key="'click'" sortable :show-overflow-tooltip="true" min-width="140"> <template slot-scope="scope"> <div class="ellipsis-hidden">{{ scope.row.click ? scope.row.click : '-' }}</div> </template> </el-table-column> <el-table-column label="点击率" align="right" prop="clickRate" :key="'clickRate'" sortable :show-overflow-tooltip="true" min-width="140"> <template slot-scope="scope"> <div class="ellipsis-hidden">{{ scope.row.clickRate ? scope.row.clickRate : '-' }}</div> </template> </el-table-column> <el-table-column label="点击成本" align="right" prop="cpc" :key="'cpc'" sortable :show-overflow-tooltip="true" min-width="140"> <template slot-scope="scope"> <div class="ellipsis-hidden">{{ scope.row.cpc ? scope.row.cpc : '-' }}</div> </template> </el-table-column> <el-table-column label="平均千次展示成本" align="right" prop="cpm" :key="'cpm'" sortable :show-overflow-tooltip="true" min-width="140"> <template slot-scope="scope"> <div class="ellipsis-hidden">{{ scope.row.cpm ? scope.row.cpm : '-' }}</div> </template> </el-table-column> <el-table-column label="转化数" align="right" prop="convert" :key="'convert'" sortable :show-overflow-tooltip="true" min-width="140"> <template slot-scope="scope"> <div class="ellipsis-hidden">{{ scope.row.convert ? scope.row.convert : '-' }}</div> </template> </el-table-column> <el-table-column label="平均转化成本" align="right" prop="convertCost" :key="'convertCost'" sortable :show-overflow-tooltip="true" min-width="140"> <template slot-scope="scope"> <div class="ellipsis-hidden">{{ scope.row.convertCost ? scope.row.convertCost : '-' }}</div> </template> </el-table-column> <el-table-column label="转化率" align="right" prop="convertRate" :key="'convertRate'" sortable :show-overflow-tooltip="true" min-width="140"> <template slot-scope="scope"> <div class="ellipsis-hidden">{{ scope.row.convertRate ? scope.row.convertRate : '-' }}</div> </template> </el-table-column> <el-table-column label="深度转化数" align="right" prop="deepConvert" :key="'deepConvert'" sortable :show-overflow-tooltip="true" min-width="140"> <template slot-scope="scope"> <div class="ellipsis-hidden">{{ scope.row.deepConvert ? scope.row.deepConvert : '-' }}</div> </template> </el-table-column> <el-table-column label="深度转化成本" align="right" prop="deepConvertCost" :key="'deepConvertCost'" sortable :show-overflow-tooltip="true" min-width="140"> <template slot-scope="scope"> <div class="ellipsis-hidden">{{ scope.row.deepConvertCost ? scope.row.deepConvertCost : '-' }}</div> </template> </el-table-column> <el-table-column label="深度转化率" align="right" prop="deepConvertRate" :key="'deepConvertRate'" sortable :show-overflow-tooltip="true" min-width="140"> <template slot-scope="scope"> <div class="ellipsis-hidden">{{ scope.row.deepConvertRate ? scope.row.deepConvertRate : '-' }}</div> </template> </el-table-column> </el-table> <!-- 分页 --> <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" @pagination="searchCustomer" /> </div> </template> <script> // 接口调用方法 import { planPage, planExport } from "@/api/system/product"; // 克隆方法 import { deepClone } from "@/utils/index"; // 下载方法 import { hackDownloadFileNew } from "@/utils/ruoyi"; export default { data() { return { // 搜索日期范围 searchDateRange: [], // 表格数据 customerList: [], // 搜索数据 queryParams: { pageNo: 1, pageSize: 10, sortBy: ['dateInt;desc', 'adsCost;desc'], filtering: { startDate: '', endDate: '', mediaCode: '', customerArea: '', customerType: '', source: '', customerId: [], customerName: '', appPackageName: '', appName: '' }, groupBy: { "dateInt": true, "mediaCode": true, "customerArea": false, "customerType": false, "customerId": false, "customerName": false, "source": false, "appPackageName": false, "appName": false, "corporationName": false, "firstIndustryName": false, "secondIndustryName": false } }, // 用户ID customerId: '', // 细分项记录 groupBy: ["dateInt", "mediaCode"], // 细分项枚举值 groupByOptions: { placeholder: '请选择细分项', data: [ { name: "日期", value: "dateInt" }, { name: "媒体", value: "mediaCode" }, { name: "客户区域", value: "customerArea" }, { name: "客户类型", value: "customerType" }, { name: "客户编号", value: "customerId" }, { name: "数据来源", value: "source" }, { name: "应用包名", value: "appPackageName" }, { name: "应用名称", value: "appName" }, { name: "资质主体名称", value: "corporationName" }, { name: "一级行业", value: "firstIndustryName" }, { name: "二级行业", value: "secondIndustryName" }, { name: "客户名称", value: "customerName" } ] }, // 数据来源 sourceOptions: { placeholder: '请选择数据来源', data: [ { name: "ADS计划", value: "ADS" }, { name: "非ADS计划", value: "SYNC" } ] }, // 选择日期范围 pickerOptions: { shortcuts: [{ text: '最近一周', onClick(picker) { const end = new Date(); const start = new Date(); start.setTime(start.getTime() - 3600 * 1000 * 24 * 7); end.setTime(end.getTime() - 3600 * 1000 * 24); picker.$emit('pick', [start, end]); } }], disabledDate: time => { let a = new Date().toLocaleDateString() + " " + "23:59:59"; let c = new Date(new Date() - 24 * 60 * 60 * 1000 * 7).getTime(); let b = new Date(a).getTime() - 3600 * 1000 * 24; return c > time.getTime() || time.getTime() > b; } }, // 媒体 mediaCode: { placeholder: '请选择媒体', data: [ { name: "头条", value: "toutiao" }, { name: "快手", value: "kuaishou" }, { name: "广点通", value: "gdt" }, { name: '百度', value: 'baidu' } ] }, // 客户区域 customerAreaOptions: [ { label: "华北", value: "华北" }, { label: "华南", value: "华南" }, { label: "华东", value: "华东" } ], // 客户类型 customerTypeOptions: [ { label: "试用", value: "TRIAL" }, { label: "签约", value: "SIGNED" }, { label: "其他", value: "OTHER" } ], // 列表加载等待 loading: false, // 总条数 total: 0, // 下载loading downloadLoading: false, tableHeights: 0, // table高度 customerListHeights: 0 // table真实高度 } }, watch: { customerId(newVal, oldVal) { if (newVal !== oldVal) { this.customerId = newVal.replace(/[^0-9\,]/,'') } } }, created() { this.init() this.$nextTick(() => { this.tableHeight(); }) }, computed: { // 计算过的height tableHeightCount() { return this.tableHeights > this.customerListHeights ? this.customerListHeights : this.tableHeights } }, methods: { // 初始化 init() { const end = this.formatDate(1); const start = this.formatDate(1); this.$set(this, 'searchDateRange', [start, end]) this.searchCustomer() }, // 初始化日期格式化 formatDate(date) { let d = new Date(), month = '' + (d.getMonth() + 1), day = '' + d.getDate() - date, year = d.getFullYear(); if (month.length < 2) month = '0' + month; if (day.length < 2) day = '0' + day; return [year, month, day].join('-'); }, /** 搜索按钮操作 */ handleQuery() { this.queryParams.pageNo = 1; this.disposeData() this.searchCustomer() }, // 搜索数据处理 disposeData() { let customerId = this.customerId ? this.customerId.split(',').filter(val => {if (val) return val}) : [] let groupBy = this.groupBy let groupByData = { "dateInt": false, "mediaCode": false, "customerArea": false, "customerType": false, "customerId": false, "customerName": false, "source": false, "appPackageName": false, "appName": false, "corporationName": false, "firstIndustryName": false, "secondIndustryName": false } if (groupBy.length) { groupBy.forEach(item => { if (item) { groupByData[item] = true } }) } this.$refs.customerListHeight.clearSort() this.$set(this.queryParams, 'sortBy', ['dateInt;desc', 'cost;desc']) this.queryParams.groupBy = groupByData this.queryParams.filtering.customerId = customerId }, searchCustomer() { this.loading = true; if (!this.searchDateRange) { this.$message({ message: '请选择日期在进行查询', type: 'warning' }); return false } if (this.searchDateRange) { this.queryParams.filtering.startDate = this.searchDateRange[0]; this.queryParams.filtering.endDate = this.searchDateRange[1]; } planPage(this.queryParams) .then(response => { this.total = response.data.total; this.loading = false; this.$nextTick(() => { this.$set(this, 'customerList', response.data.records); this.customerListHeights = this.$refs.customerListHeight.offsetHeight }) }) .catch(error => { this.loading = false; this.customerList = []; }); }, // 计算高度 tableHeight() { let height = document.documentElement.clientHeight - 222 - this.$refs.seachDatas.offsetHeight this.tableHeights = height < 580 ? 580 : height }, /** 重置筛选 */ resetHandleQuery() { this.queryParams = { pageNo: 1, pageSize: 10, sortBy: ['dateInt;desc', 'adsCost;desc'], filtering: { startDate: '', endDate: '', mediaCode: '', customerArea: '', customerType: '', customerId: [], customerName: '', source: '', appPackageName: '', appName: '' }, groupBy: { "dateInt": true, "mediaCode": true, "customerArea": false, "customerType": false, "customerId": false, "customerName": false, "source": false, "appPackageName": false, "appName": false, "corporationName": false, "firstIndustryName": false, "secondIndustryName": false } }; const end = this.formatDate(1); const start = this.formatDate(1); this.$set(this, 'searchDateRange', [start, end]) this.$set(this, 'customerId', '') this.$set(this, 'groupBy', ["dateInt", "mediaCode"]) this.disposeData(); this.searchCustomer(); }, // 下载报表 handleDownload() { this.downloadLoading = true; if (!this.searchDateRange) { this.$message({ message: '请选择日期在进行下载', type: 'warning' }); return false } if (this.searchDateRange) { this.queryParams.filtering.startDate = this.searchDateRange[0]; this.queryParams.filtering.endDate = this.searchDateRange[1]; } let queryParams = deepClone(this.queryParams) delete queryParams.pageNo delete queryParams.pageSize planExport(queryParams) .then(res => { hackDownloadFileNew(res, '客户投放数据_' + new Date().getTime()); this.msgSuccess("下载成功"); this.downloadLoading = false; }) .catch(() => { this.downloadLoading = false; }); }, // 排序 sortChange(column, prop, order) { if (!column.order) { this.$set(this.queryParams, 'sortBy', ['dateInt;desc', 'adsCost;desc']) } else { let text = column.prop + ';' if (column.order === 'descending') { text = text + 'desc' } else if (column.order === 'ascending') { text = text + 'asc' } this.$set(this.queryParams, 'sortBy', [text]) } this.searchCustomer(); }, // 客户类型 typeText(type) { const opt = this.customerTypeOptions.find(item => item.value === type); return opt && opt.label; }, // 数据来源 typeTextSource(type) { let types = { 'SYNC': '非ADS计划', 'ADS': 'ADS计划' } return types[type] ? types[type] : '-'; } } } </script> <style scoped lang="scss"> @import '@/views/main.scss'; </style>