Commit fd59473d authored by 黄同智's avatar 黄同智

视频数据分析页面的布局

parent 6f5ecf40
<template>
<div class="p-6">
<el-card class="bg-fff round-12" shadow="hover">
<div class="flex-items-center gap-34 filter-header">
<div v-for="item in platformTabs" :key="item.value"
class="pb-18 flex-items-center gap-10 pointer platform-tab"
:class="{ active: platform === item.value }" @click="platform = item.value">
<span class="w-24 h-24 round-6 flex-center platform-icon">{{ item.icon }}</span>
<span class="fs-16 font-bold">{{ item.label }}</span>
</div>
</div>
<div class="py-20">
<div class="flex-items-center mb-18">
<el-button v-for="item in scopes" :key="item" :type="scope === item ? 'primary' : ''"
class="round-6" @click="scope = item">
{{ item }}
</el-button>
</div>
<div class="flex-items-center">
<el-select v-model="category" class="w-140" placeholder="请选择分类">
<el-option label="全部分类" value="all" />
<el-option label="服饰鞋包" value="clothes" />
</el-select>
<div class="w-240 mx-10">
<el-date-picker v-model="dateRange" type="daterange" range-separator="至"
start-placeholder="开始日期" end-placeholder="结束日期" style="width: 100% !important;" />
</div>
<el-button type="primary" class="round-6" @click="query">查询</el-button>
<el-button class=" round-6" @click="reset">重置</el-button>
</div>
</div>
</el-card>
<el-card class="bg-fff round-12 mt-16" shadow="hover">
<div class="pb-16 flex-between ratio-title">
<span class="fs-16 font-bold color-000">占比分析</span>
<el-button type="primary" link @click="collapsed = !collapsed">{{ collapsed ? '展开' : '收起'
}}</el-button>
</div>
<div v-show="!collapsed" class="p-20 flex gap-26">
<div class="flex-1 gap-14 card-grid">
<div v-for="item in ratioList" :key="item.name" class="p-18 round-6 flex-between metric-card">
<div class="flex-items-center">
<span class="w-42 h-42 round-8 flex-center fs-20"
:style="{ color: item.color, backgroundColor: item.bg }">
{{ item.icon }}
</span>
<div class="ml-16">
<div class="font-bold color-000">{{ item.name }}</div>
<div class="mt-8">数量: <span class="font-bold">{{ item.count.toLocaleString() }}</span>
</div>
<div class="mt-6">消耗: <span class="font-bold">¥{{ item.cost.toLocaleString() }}</span>
</div>
</div>
</div>
<div class="lh-24">
<div>数量占比: <span class="font-bold">{{ item.countRate }}%</span></div>
<div>消耗占比: <span class="font-bold">{{ item.costRate }}%</span></div>
</div>
</div>
</div>
<div class="flex-1 minw-400 flex-items-center justify-center chart-panel">
<div class="w-240 h-240">
<v-chart class="w-full h-full" :option="pieOption" autoresize />
</div>
<div class="ml-40 flex-1">
<div v-for="item in ratioList" :key="item.name" class="flex-between mb-18">
<div class="flex-items-center gap-10">
<span class="w-10 h-10 round-2" :style="{ backgroundColor: item.color }"></span>
<span>{{ item.name }}</span>
</div>
<span class="fs-12 color-666">{{ item.count }} ({{ item.countRate }}%)</span>
</div>
</div>
</div>
</div>
</el-card>
<el-card class="bg-fff round-12 mt-16" shadow="hover">
<div class="pb-16 flex-between table-title">
<div class="flex-items-center gap-8">
<span class="fs-16 font-bold color-000">详细数据</span>
<span class="color-888"></span>
</div>
<el-button class="round-6" @click="query">⇩ 导出表格</el-button>
</div>
<el-table :data="pagedTableData" class="analysis-table">
<el-table-column prop="account" label="广告账户" min-width="210" />
<el-table-column prop="subject" label="主体" min-width="180" />
<el-table-column prop="department" label="部门" width="130" />
<el-table-column prop="group" label="分组" width="120" />
<el-table-column prop="user" label="用户" width="100" />
<el-table-column prop="category1" label="一级分类" width="110" />
<el-table-column prop="category2" label="二级分类" width="110" />
<el-table-column prop="total" label="总素材数" width="110" align="center" />
<el-table-column prop="first" label="首发素材" width="120" align="center">
<template #default="{ row }">
<MetricText :value="row.first" sub="28.5%" />
</template>
</el-table-column>
<el-table-column prop="firstCost" label="首发素材消耗" width="140" align="center" />
<el-table-column prop="good" label="优质素材" width="110" align="center">
<template #default="{ row }"><span class="font-bold color-green">{{ row.good }}</span></template>
</el-table-column>
<el-table-column prop="goodCost" label="优质素材消耗" width="140" align="center">
<template #default="{ row }"><span class="font-bold color-green">{{ row.goodCost
}}</span></template>
</el-table-column>
<el-table-column prop="low" label="低效素材" width="110" align="center">
<template #default="{ row }"><span class="font-bold color-main">{{ row.low }}</span></template>
</el-table-column>
<el-table-column prop="bad" label="低质素材" width="110" align="center">
<template #default="{ row }"><span class="font-bold color-orange">{{ row.bad }}</span></template>
</el-table-column>
<el-table-column prop="same" label="同质化挤压严重素材" width="160" align="center">
<template #default="{ row }"><span class="font-bold color-pink">{{ row.same }}</span></template>
</el-table-column>
<el-table-column prop="risk" label="同质化素材风险-排队投放素材" width="210" align="center">
<template #default="{ row }"><span class="font-bold color-green">{{ row.risk }}</span></template>
</el-table-column>
</el-table>
<div class="py-12 flex justify-end table-footer">
<el-pagination
v-model:current-page="pageData.currentPage"
v-model:page-size="pageData.pageSize"
background
layout="total, sizes, prev, pager, next, jumper"
:total="tableData.length"
:page-sizes="pageData.pageSizes"
@size-change="handleSizeChange"
/>
</div>
</el-card>
</div>
</template>
<script setup lang="ts">
import { computed, defineComponent, h, reactive, ref } from 'vue';
import VChart from 'vue-echarts';
import { use } from 'echarts/core';
import { CanvasRenderer } from 'echarts/renderers';
import { PieChart } from 'echarts/charts';
import { GraphicComponent, LegendComponent, TooltipComponent } from 'echarts/components';
import { ElMessage } from 'element-plus';
use([CanvasRenderer, PieChart, TooltipComponent, LegendComponent, GraphicComponent]);
const MetricText = defineComponent({
props: { value: [Number, String], sub: String },
setup(props) {
return () => h('div', [h('div', { class: 'font-bold color-000' }, String(props.value)), h('div', { class: 'fs-12 color-888' }, props.sub)]);
},
});
const platform = ref('qianchuan');
const scope = ref('汇总');
const category = ref('');
const dateRange = ref('');
const collapsed = ref(false);
const pageData = reactive({
currentPage: 1,
pageSize: 10,
pageSizes: [10, 20, 50, 100],
});
const platformTabs = [
{ label: '巨量千川', value: 'qianchuan', icon: 'Ⅲ' },
{ label: '巨量广告', value: 'ad', icon: '⊘' },
];
const scopes = ['汇总', '部门', '分组', '个人', '广告账户'];
const ratioList = [
{ name: '首发素材', count: 1280, cost: 342800, countRate: 28.5, costRate: 32.4, color: '#5046e5', bg: '#f0efff', icon: '⌘' },
{ name: '优质素材', count: 860, cost: 415200, countRate: 19.1, costRate: 39.2, color: '#ef3d3d', bg: '#fff0f0', icon: '♙' },
{ name: '低效素材', count: 940, cost: 182500, countRate: 20.9, costRate: 17.2, color: '#8656ee', bg: '#f5efff', icon: '!' },
{ name: '低质素材', count: 520, cost: 68100, countRate: 11.6, costRate: 6.4, color: '#f59e0b', bg: '#fff7e8', icon: '△' },
{ name: '同质化挤压严重素材', count: 480, cost: 32400, countRate: 10.7, costRate: 3.1, color: '#ec4899', bg: '#fff0f8', icon: '▱' },
{ name: '同质化素材风险-排队投放素材', count: 410, cost: 17900, countRate: 9.2, costRate: 1.7, color: '#10b981', bg: '#ecfdf5', icon: '♢' },
];
const tableData = [
{ account: '总计', subject: '--', department: '--', group: '--', user: '--', category1: '--', category2: '--', total: '4,490', first: '1,280', firstCost: '¥342,800', good: 860, goodCost: '¥415,200', low: 940, bad: 520, same: 480, risk: 410 },
{ account: '广州千川官方号-01 (8821941)', subject: '广州某某网络科技有限公司', department: '华南电商一队', group: '直播二组', user: '张伟', category1: '服饰鞋包', category2: '女装短袖', total: 1250, first: 380, firstCost: '¥112,000', good: 240, goodCost: '¥145,000', low: 280, bad: 150, same: 120, risk: 80 },
{ account: '杭州巨量千川-旗舰02 (9120482)', subject: '杭州电商服务有限公司', department: '华东数码二队', group: '信息流一组', user: '李娜', category1: '3C数码', category2: '智能耳机', total: 980, first: 290, firstCost: '¥89,500', good: 195, goodCost: '¥118,200', low: 210, bad: 115, same: 95, risk: 75 },
{ account: '上海巨量广告-品牌01 (7739102)', subject: '上海传媒网络科技公司', department: '华东美妆一队', group: '品牌推广组', user: '王强', category1: '美妆护肤', category2: '精华面霜', total: 1120, first: 310, firstCost: '¥95,000', good: 220, goodCost: '¥102,000', low: 245, bad: 135, same: 110, risk: 100 },
{ account: '北京千川测试户-05 (6652011)', subject: '北京互联网有限公司', department: '华北综合队', group: '效果提升组', user: '刘洋', category1: '食品饮料', category2: '休闲零食', total: 640, first: 180, firstCost: '¥32,300', good: 125, goodCost: '¥38,000', low: 135, bad: 80, same: 75, risk: 45 },
];
const pagedTableData = computed(() => {
const start = (pageData.currentPage - 1) * pageData.pageSize;
return tableData.slice(start, start + pageData.pageSize);
});
const pieOption = computed(() => ({
tooltip: { trigger: 'item' },
legend: { show: false },
series: [{
type: 'pie',
radius: ['42%', '78%'],
label: { show: false },
data: ratioList.map((item) => ({ name: item.name, value: item.count, itemStyle: { color: item.color } })),
}],
graphic: [
{ type: 'text', left: 'center', top: '42%', style: { text: '总素材数', textAlign: 'center', fill: '#6b7c93', fontSize: 12 } },
{ type: 'text', left: 'center', top: '51%', style: { text: '4,490', textAlign: 'center', fill: '#0f172a', fontSize: 20, fontWeight: 700 } },
{ type: 'text', left: 'center', top: '63%', style: { text: '六维类型', textAlign: 'center', fill: '#6b7c93', fontSize: 12 } },
],
}));
const query = () => ElMessage.success('查询完成');
const reset = () => {
category.value = '';
dateRange.value = '';
};
const handleSizeChange = () => {
pageData.currentPage = 1;
};
</script>
<style scoped lang="scss">
.filter-header,
.ratio-title,
.table-title,
.table-footer {
border-bottom: 1px solid #edf0f5;
}
.table-footer {
border-top: 1px solid #edf0f5;
border-bottom: none;
}
.platform-tab {
color: #607086;
&.active {
color: rgba(var(--main-color), 1);
border-bottom: 2px solid rgba(var(--main-color), 1);
}
}
.platform-icon,
.work-tag {
background-color: rgba(var(--main-color), .08);
}
.card-grid {
display: grid;
grid-template-columns: repeat(2, minmax(280px, 1fr));
}
.analysis-table {
:deep(.el-table__header th) {
background: #f8fbff;
color: #48607f;
font-weight: bold;
}
}
.color-green {
color: #00a86b;
}
.color-orange {
color: #f59e0b;
}
.color-pink {
color: #ec4899;
}
</style>
<template>
<div class="p-6">
<el-card class="round-12 mt-10 filter-card" shadow="hover">
<div class="flex-items-center gap-34 header-tabs">
<div v-for="item in platforms" :key="item" class="pb-16 font-bold pointer"
:class="{ active: platform === item }" @click="platform = item">{{ item }}</div>
</div>
<div class="py-14 sub-tabs">
<div class="flex-items-center gap-24">
<span v-for="item in scopes" :key="item" class="font-bold pointer"
:class="{ 'color-main': scope === item }" @click="scope = item">{{ item }}</span>
<div class="flex-items-center gap-10">
<div class="w-300 flex-items-center mr-10">
<div class="w-90">上传时间:</div>
<el-date-picker v-model="uploadRange" type="daterange" range-separator="至"
start-placeholder="开始日期" end-placeholder="结束日期" style="width: 100% !important;" />
</div>
<div class="w-300 flex-items-center mr-10">
<div class="w-90">投放时间:</div>
<el-date-picker v-model="deliveryRange" type="daterange" range-separator="至"
start-placeholder="开始日期" end-placeholder="结束日期" style="width: 100% !important;" />
</div>
</div>
</div>
<div class="mt-16">
<el-button type="primary" class="round-6">视频作者</el-button>
<el-button class="round-6 mr-10">下单运营</el-button>
<el-select v-model="department" class="w-180 mr-10" placeholder="请选择部门" />
<el-select v-model="category" class="w-180 mr-10" placeholder="请选择分类" />
<el-button type="primary" class="w-66 round-6">查询</el-button>
<el-button class="round-6">导出数据</el-button>
<el-button class="w-34 h-34 round-6"></el-button>
</div>
</div>
</el-card>
<el-card class="round-12 mt-16" shadow="hover">
<el-table :data="pagedTableData" class="analysis-table">
<el-table-column prop="department" label="部门" min-width="160" />
<el-table-column prop="cost" label="消耗" width="120" align="center" />
<el-table-column prop="roi" label="roi" width="90" align="center" />
<el-table-column prop="totalAmount" label="总成交金额" width="130" align="center" />
<el-table-column prop="amount" label="成交金额" width="130" align="center" />
<el-table-column prop="coupon" label="智能优惠券" width="130" align="center" />
<el-table-column prop="conversions" label="转化数" width="110" align="center" />
<el-table-column prop="rate" label="转化率" width="110" align="center" />
<el-table-column prop="costRate" label="转化成本" width="110" align="center" />
<el-table-column prop="show" label="展示数" width="130" align="center" />
<el-table-column prop="avgCost" label="平均千次展现费用" width="160" align="center" />
<el-table-column prop="click" label="点击数" width="120" align="center" />
<el-table-column prop="clickRate" label="点击率" width="110" align="center" />
<el-table-column prop="avgPrice" label="平均点击单价" width="140" align="center" />
<el-table-column prop="play" label="播放量" width="130" align="center" />
</el-table>
<div class="py-12 flex justify-end table-footer">
<el-pagination
v-model:current-page="pageData.currentPage"
v-model:page-size="pageData.pageSize"
background
layout="total, sizes, prev, pager, next, jumper"
:total="tableData.length"
:page-sizes="pageData.pageSizes"
@size-change="handleSizeChange"
/>
</div>
</el-card>
</div>
</template>
<script setup lang="ts">
import { computed, reactive, ref } from 'vue';
const platform = ref('巨量广告');
const scope = ref('部门');
const department = ref('');
const category = ref('');
const uploadRange = ref('');
const deliveryRange = ref('');
const pageData = reactive({
currentPage: 1,
pageSize: 10,
pageSizes: [10, 20, 50, 100],
});
const platforms = ['巨量广告', '巨量千川', '磁力智投', '磁力金牛', '百度营销', '小红书'];
const scopes = ['部门', '分组', '个人', '明细', '分日', '分月'];
const tableData = [
{ department: '总计', cost: '1375201.50', roi: '3.69', totalAmount: '¥5,074,860', amount: '¥4,740,000', coupon: '¥334,860', conversions: '44,240', rate: '4.54%', costRate: '¥31.09', show: '26,040,000', avgCost: '¥52.81', click: '974,510', clickRate: '3.74%', avgPrice: '¥1.41', play: '22,300,000' },
{ department: '项目1-爆款电商', cost: '342100.80', roi: '4.12', totalAmount: '¥1,409,450', amount: '¥1,320,000', coupon: '¥89,450', conversions: '11,400', rate: '5.10%', costRate: '¥30.01', show: '6,450,000', avgCost: '¥53.03', click: '223,500', clickRate: '3.46%', avgPrice: '¥1.53', play: '5,600,000' },
{ department: '达人测试部门', cost: '285400.00', roi: '3.85', totalAmount: '¥1,098,790', amount: '¥1,020,000', coupon: '¥78,790', conversions: '9,240', rate: '4.82%', costRate: '¥30.88', show: '5,120,000', avgCost: '¥55.74', click: '191,980', clickRate: '3.75%', avgPrice: '¥1.48', play: '4,320,000' },
{ department: 'xx素颜霜项目', cost: '220500.00', roi: '3.68', totalAmount: '¥811,440', amount: '¥760,000', coupon: '¥51,440', conversions: '7,350', rate: '4.50%', costRate: '¥30.00', show: '4,100,000', avgCost: '¥53.78', click: '163,330', clickRate: '3.98%', avgPrice: '¥1.35', play: '3,680,000' },
{ department: '小真测试部门', cost: '196200.50', roi: '3.42', totalAmount: '¥671,000', amount: '¥630,000', coupon: '¥41,000', conversions: '6,150', rate: '4.25%', costRate: '¥31.90', show: '3,820,000', avgCost: '¥51.36', click: '144,700', clickRate: '3.78%', avgPrice: '¥1.35', play: '3,100,000' },
{ department: '抖音投放一组', cost: '189000.20', roi: '3.52', totalAmount: '¥665,280', amount: '¥620,000', coupon: '¥45,280', conversions: '5,980', rate: '4.15%', costRate: '¥31.60', show: '3,600,000', avgCost: '¥52.50', click: '144,000', clickRate: '4.00%', avgPrice: '¥1.31', play: '3,150,000' },
];
const pagedTableData = computed(() => {
const start = (pageData.currentPage - 1) * pageData.pageSize;
return tableData.slice(start, start + pageData.pageSize);
});
const handleSizeChange = () => {
pageData.currentPage = 1;
};
</script>
<style scoped lang="scss">
.filter-card,
.table-card {
border: 1px solid #dfe7f2;
}
.header-tabs,
.sub-tabs,
.table-footer {
border-bottom: 1px solid #edf0f5;
}
.table-footer {
border-top: 1px solid #edf0f5;
border-bottom: none;
}
.header-tabs .active {
color: rgba(var(--main-color), 1);
border-bottom: 2px solid rgba(var(--main-color), 1);
}
.analysis-table {
:deep(.el-table__header th) {
background: #f8fbff;
color: #48607f;
font-weight: bold;
}
}
</style>
<template>
<div class="p-6">
<el-card class="bg-fff round-12" shadow="hover">
<div class="flex-items-center gap-34 header-tabs">
<div v-for="item in topTabs" :key="item" class="pb-16 font-bold pointer"
:class="{ active: topTab === item }" @click="topTab = item">
{{ item }}
</div>
</div>
<div class="py-20">
<div class="flex mb-18 round-6 platform-box">
<div v-for="item in platforms" :key="item" class="px-22 py-8 round-6 font-bold pointer"
:class="{ active: platform === item }" @click="platform = item">
{{ item }}
</div>
</div>
<div class="flex-items-center">
<el-select v-model="category" class="w-240 mr-10" placeholder="请选择分类">
<el-option label="全部分类" value="all" />
<el-option label="美妆护肤" value="beauty" />
</el-select>
<el-select v-model="tag" class="w-220 mr-10" placeholder="请选择标签">
<el-option label="全部标签" value="all" />
<el-option label="主打模特" value="model" />
</el-select>
<div class="w-300 flex-items-center mr-10">
<div class="w-90">上传时间:</div>
<el-date-picker v-model="uploadRange" type="daterange" range-separator="至"
start-placeholder="开始日期" end-placeholder="结束日期" style="width: 100% !important;" />
</div>
<div class="w-300 flex-items-center mr-10">
<div class="w-90">消耗时间:</div>
<el-date-picker v-model="costRange" type="daterange" range-separator="至"
start-placeholder="开始日期" end-placeholder="结束日期" style="width: 100% !important;" />
</div>
<el-button type="primary" class="w-66 round-6">查询</el-button>
<el-button class="w-76 round-6">重置</el-button>
</div>
</div>
</el-card>
<el-card class="bg-fff round-12 mt-16" shadow="hover">
<div class="flex-between pb-16 section-title">
<div class="flex-items-center gap-10">
<span class="fs-24 color-main"></span>
<span class="font-bold fs-16">占比分析</span>
</div>
<span class="color-888">数据汇总范围: 2025-10-06 ~ 2025-10-21</span>
</div>
<div class="flex minh-400 chart-area">
<div class="w-400 h-400">
<v-chart class="w-full h-full" :option="pieOption" autoresize />
</div>
<div class="flex-1 legend-grid gap-10 ml-60 pt-20">
<div v-for="item in chartData" :key="item.name" class="px-14 py-12 round-6 flex-between border-eee">
<div class="flex-items-center gap-10">
<span class="w-12 h-12 round-2" :style="{ backgroundColor: item.color }"></span>
<span class="font-bold">{{ item.name }}</span>
</div>
<div class="text-right">
<div class="font-bold">¥{{ item.value.toLocaleString() }}</div>
<div class="fs-12 color-888">{{ item.rate }}%</div>
</div>
</div>
</div>
</div>
</el-card>
<el-card class="bg-fff round-12 mt-16" shadow="hover">
<div class="pb-16 flex-between table-title">
<span class="fs-16 font-bold">详细数据</span>
<div class="flex-items-center gap-8">
<el-button class="round-6">导出</el-button>
<el-button class="w-34 h-34 round-6"></el-button>
</div>
</div>
<el-table :data="pagedTableData" class="analysis-table">
<el-table-column prop="name" label="模特姓名 / 标签" width="130" />
<el-table-column prop="videos" label="视频数量" width="100" align="center" />
<el-table-column prop="cost" label="消耗" width="130" align="center" />
<el-table-column prop="roi" label="roi" width="90" align="center" />
<el-table-column prop="amount" label="成交金额" width="130" align="center" />
<el-table-column prop="coupon" label="智能优惠券" width="120" align="center" />
<el-table-column prop="subsidy" label="电商平台补贴金额" width="150" align="center" />
<el-table-column prop="conversions" label="转化数" width="100" align="center" />
<el-table-column prop="rate" label="转化率" width="100" align="center" />
<el-table-column prop="costRate" label="转化成本" width="110" align="center" />
<el-table-column prop="show" label="展示数" width="120" align="center" />
<el-table-column prop="avgCost" label="平均千次展现费用" width="150" align="center" />
<el-table-column prop="click" label="点击数" width="110" align="center" />
<el-table-column prop="clickRate" label="点击率" width="100" align="center" />
<el-table-column prop="price" label="平均点击单价" width="130" align="center" />
<el-table-column prop="play" label="播放量" width="130" align="center" />
<el-table-column prop="complete" label="3S完播率" width="110" align="center" />
<el-table-column prop="deal" label="净成交金额" width="140" align="center" />
<el-table-column prop="orders" label="净成交订单数" width="130" align="center" />
<el-table-column prop="netRoi" label="净成交ROI" width="110" align="center" />
<el-table-column prop="unit" label="净成交订单成本" width="140" align="center" />
</el-table>
<div class="py-12 flex justify-end table-footer">
<el-pagination
v-model:current-page="pageData.currentPage"
v-model:page-size="pageData.pageSize"
background
layout="total, sizes, prev, pager, next, jumper"
:total="tableData.length"
:page-sizes="pageData.pageSizes"
@size-change="handleSizeChange"
/>
</div>
</el-card>
</div>
</template>
<script setup lang="ts">
import { computed, reactive, ref } from 'vue';
import VChart from 'vue-echarts';
import { use } from 'echarts/core';
import { CanvasRenderer } from 'echarts/renderers';
import { PieChart } from 'echarts/charts';
import { GraphicComponent, TooltipComponent } from 'echarts/components';
use([CanvasRenderer, PieChart, TooltipComponent, GraphicComponent]);
const topTab = ref('标签分析');
const platform = ref('抖音');
const category = ref('');
const tag = ref('');
const uploadRange = ref('');
const costRange = ref('');
const pageData = reactive({
currentPage: 1,
pageSize: 10,
pageSizes: [10, 20, 50, 100],
});
const topTabs = ['标签分析', '潜力 / 当月爆款', '使用数据'];
const platforms = ['抖音', '腾讯', 'TikTok'];
const chartData = [
{ name: '谭明珠(主打模特)', value: 128450.5, rate: 25.3, color: '#5572f6' },
{ name: '林启珊(高ROI)', value: 86200.2, rate: 17.0, color: '#8cc97d' },
{ name: '吴雪妮(痛点对撞)', value: 68300, rate: 13.5, color: '#f6b827' },
{ name: '李银凤(口播种草)', value: 52140.1, rate: 10.3, color: '#f87171' },
{ name: '阮丽琼(情景短剧)', value: 43200, rate: 8.5, color: '#4bb9e9' },
{ name: '马依卓(测评体验)', value: 38100, rate: 7.5, color: '#4bd0a0' },
{ name: '无模特(纯产品)', value: 32000, rate: 6.3, color: '#ff823b' },
{ name: '绿款二创标签', value: 24500, rate: 4.8, color: '#a78bfa' },
{ name: '沉浸拆箱标签', value: 18400, rate: 3.6, color: '#f472b6' },
{ name: '高反差对比', value: 16150.57, rate: 3.2, color: '#60a5fa' },
];
const pieOption = computed(() => ({
tooltip: { trigger: 'item' },
series: [{ type: 'pie', radius: ['48%', '82%'], label: { show: false }, data: chartData.map((item) => ({ value: item.value, name: item.name, itemStyle: { color: item.color } })) }],
graphic: [
{ type: 'text', left: 'center', top: '42%', style: { text: '总消耗金额(元)', fill: '#8492a6', textAlign: 'center', fontSize: 12 } },
{ type: 'text', left: 'center', top: '50%', style: { text: '507441.37', fill: '#0f172a', textAlign: 'center', fontSize: 28, fontWeight: 700 } },
{ type: 'text', left: 'center', top: '61%', style: { text: '多维对比看板', fill: '#7c3aed', textAlign: 'center', fontSize: 12, fontWeight: 700 } },
],
}));
const tableData = [
{ name: '总计', videos: 768, cost: '¥507,441.37', roi: '2.85', amount: '¥1,446,202.00', coupon: '¥12,300.00', subsidy: '¥8,200.00', conversions: '16,200', rate: '3.65%', costRate: '¥31.32', show: '9,740,000', avgCost: '¥52.10', click: '355,501', clickRate: '3.65%', price: '¥1.43', play: '8,200,000', complete: '19.2%', deal: '¥1,380,500.00', orders: '15,350', netRoi: '2.72', unit: '¥33.05' },
{ name: '谭明珠', videos: 185, cost: '¥128,450.50', roi: '3.25', amount: '¥417,464.00', coupon: '¥3,850.00', subsidy: '¥2,400.00', conversions: '4,210', rate: '4.12%', costRate: '¥30.50', show: '2,450,000', avgCost: '¥52.42', click: '98,200', clickRate: '4.01%', price: '¥1.31', play: '2,100,000', complete: '22.5%', deal: '¥398,500.00', orders: '4,010', netRoi: '3.10', unit: '¥32.03' },
{ name: '林启珊', videos: 142, cost: '¥86,200.20', roi: '2.98', amount: '¥256,876.00', coupon: '¥2,100.00', subsidy: '¥1,800.00', conversions: '2,840', rate: '3.85%', costRate: '¥30.35', show: '1,680,000', avgCost: '¥51.30', click: '64,680', clickRate: '3.85%', price: '¥1.33', play: '1,420,000', complete: '20.8%', deal: '¥245,200.00', orders: '2,710', netRoi: '2.84', unit: '¥31.80' },
{ name: '吴雪妮', videos: 118, cost: '¥68,300.00', roi: '2.82', amount: '¥192,606.00', coupon: '¥1,650.00', subsidy: '¥1,200.00', conversions: '2,150', rate: '3.62%', costRate: '¥31.76', show: '1,320,000', avgCost: '¥51.74', click: '47,784', clickRate: '3.62%', price: '¥1.43', play: '1,110,000', complete: '19.5%', deal: '¥183,900.00', orders: '2,050', netRoi: '2.69', unit: '¥33.31' },
{ name: '李银凤', videos: 95, cost: '¥52,140.10', roi: '2.75', amount: '¥143,385.00', coupon: '¥1,200.00', subsidy: '¥950.00', conversions: '1,620', rate: '3.45%', costRate: '¥32.18', show: '1,050,000', avgCost: '¥49.65', click: '36,225', clickRate: '3.45%', price: '¥1.44', play: '890,000', complete: '18.2%', deal: '¥136,800.00', orders: '1,540', netRoi: '2.62', unit: '¥33.85' },
];
const pagedTableData = computed(() => {
const start = (pageData.currentPage - 1) * pageData.pageSize;
return tableData.slice(start, start + pageData.pageSize);
});
const handleSizeChange = () => {
pageData.currentPage = 1;
};
</script>
<style scoped lang="scss">
.header-tabs,
.section-title,
.table-title,
.table-footer {
border-bottom: 1px solid #edf0f5;
}
.table-footer {
border-top: 1px solid #edf0f5;
border-bottom: none;
}
.header-tabs .active {
color: rgba(var(--main-color), 1);
border-bottom: 2px solid rgba(var(--main-color), 1);
}
.platform-box {
width: fit-content;
border: 1px solid #dfe7f2;
overflow: hidden;
}
.platform-box .active {
background: rgba(var(--main-color), 1);
color: #fff;
}
.legend-grid {
display: grid;
grid-template-columns: repeat(2, 300px);
}
.analysis-table {
:deep(.el-table__header th) {
background: #f8fbff;
color: #48607f;
font-weight: bold;
}
}
</style>
<template>
<div class="p-6">
<el-card class="bg-fff round-12 mt-10" shadow="hover">
<div class="flex-items-center gap-30 header-tabs">
<div v-for="item in dataTabs" :key="item" class="pb-16 font-bold pointer" :class="{ active: dataTab === item }" @click="dataTab = item">{{ item }}</div>
</div>
<div class="pt-16 flex-between">
<div class="flex-items-center">
<el-button type="primary" class="round-6">汇总</el-button>
<el-button class="round-6 mr-10">明细</el-button>
<el-select v-model="department" class="w-180 mr-10" placeholder="请选择部门"><el-option label="默认部门" value="默认部门" /></el-select>
<el-select v-model="group" class="w-180 mr-10" placeholder="请选择"><el-option label="华南电商一队" value="华南电商一队" /></el-select>
<div class="w-240 mr-10">
<el-date-picker v-model="range" type="daterange" range-separator="至" start-placeholder="2022/04/27" end-placeholder="2025/05/12" style="width: 100% !important;" />
</div>
<el-button type="primary" class="w-66 round-6 mr-10">查询</el-button>
</div>
</div>
</el-card>
<el-card class="bg-fff round-12 mt-16" shadow="hover">
<div class="pb-12 flex-between table-title">
<span class="fs-16 font-bold">汇总数据</span>
<el-button type="default" plain class="round-6">导出数据</el-button>
</div>
<el-table :data="pagedTableData" class="analysis-table">
<el-table-column prop="department" label="部门" min-width="180" />
<el-table-column prop="cost" label="消耗" align="center" />
<el-table-column prop="conversions" label="转化数" align="center" />
<el-table-column prop="rate" label="转化率" align="center" />
<el-table-column prop="costRate" label="转化成本" align="center" />
<el-table-column prop="show" label="展示数" align="center" />
<el-table-column prop="click" label="点击数" align="center" />
<el-table-column prop="clickRate" label="点击率" align="center" />
<el-table-column prop="avgPrice" label="平均点击单价" align="center" />
<el-table-column prop="play" label="播放量" align="center" />
<el-table-column prop="complete" label="3S完播率" align="center" />
</el-table>
<div class="px-20 py-12 flex justify-end table-footer">
<el-pagination
v-model:current-page="pageData.currentPage"
v-model:page-size="pageData.pageSize"
background
layout="total, sizes, prev, pager, next, jumper"
:total="tableData.length"
:page-sizes="pageData.pageSizes"
@size-change="handleSizeChange"
/>
</div>
</el-card>
</div>
</template>
<script setup lang="ts">
import { computed, reactive, ref } from 'vue';
const dataTab = ref('部门数据');
const department = ref('');
const group = ref('');
const range = ref('');
const pageData = reactive({
currentPage: 1,
pageSize: 10,
pageSizes: [10, 20, 50, 100],
});
const dataTabs = ['部门数据', '分组数据', '个人数据'];
const tableData = [
{ department: '总计', cost: '361051.50', conversions: '11,550', rate: '4.27%', costRate: '¥31.26', show: '6,880,000', click: '270,530', clickRate: '3.93%', avgPrice: '1.33', play: '5,860,000', complete: '20.6%' },
{ department: '默认部门', cost: '128450.50', conversions: '4,210', rate: '4.12%', costRate: '¥30.50', show: '2,450,000', click: '98,200', clickRate: '4.01%', avgPrice: '1.31', play: '2,100,000', complete: '22.5%' },
{ department: '华南电商一队', cost: '96200.80', conversions: '3,120', rate: '3.95%', costRate: '¥30.83', show: '1,820,000', click: '74,620', clickRate: '4.10%', avgPrice: '1.29', play: '1,560,000', complete: '21.2%' },
{ department: '华东投放二队', cost: '84300.00', conversions: '2,680', rate: '3.78%', costRate: '¥31.45', show: '1,590,000', click: '62,010', clickRate: '3.90%', avgPrice: '1.36', play: '1,320,000', complete: '20.4%' },
{ department: '海外品牌推广组', cost: '52100.20', conversions: '1,540', rate: '3.45%', costRate: '¥33.83', show: '1,020,000', click: '35,700', clickRate: '3.50%', avgPrice: '1.46', play: '880,000', complete: '18.5%' },
];
const pagedTableData = computed(() => {
const start = (pageData.currentPage - 1) * pageData.pageSize;
return tableData.slice(start, start + pageData.pageSize);
});
const handleSizeChange = () => {
pageData.currentPage = 1;
};
</script>
<style scoped lang="scss">
.header-tabs,.table-title,.table-footer { border-bottom: 1px solid #edf0f5; }
.table-footer { border-top: 1px solid #edf0f5; border-bottom: none; }
.header-tabs .active { color: rgba(var(--main-color), 1); border-bottom: 2px solid rgba(var(--main-color), 1); }
.analysis-table { :deep(.el-table__header th) { background: #f8fbff; color: #48607f; font-weight: bold; } }
</style>
<template> <template>
<div> <div class="bgmr vh100 over-y-auto">
<section class="bg-fff px-20 py-10 analysis-tabs">
<div class="flex-items-center gap-16">
<div
v-for="item in tabs"
:key="item.value"
class="px-12 py-8 round-6 font-bold pointer"
:class="activeTab === item.value ? 'bg-main color-fff' : 'color-000'"
@click="activeTab = item.value"
>
{{ item.label }}
</div>
</div>
</section>
<div class="p-16">
<component :is="currentComponent" />
</div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from 'vue';
import AdPlatformTags from './components/AdPlatformTags.vue';
import TagAnalysis from './components/TagAnalysis.vue';
import TencentDeliveryReport from './components/TencentDeliveryReport.vue';
import DeliveryReport from './components/DeliveryReport.vue';
const activeTab = ref('platform');
const tabs = [
{ label: '广告平台标签', value: 'platform', component: AdPlatformTags },
{ label: '标签分析', value: 'tag', component: TagAnalysis },
{ label: '腾讯投放报表', value: 'tencent', component: TencentDeliveryReport },
{ label: '投放报表', value: 'delivery', component: DeliveryReport },
];
const currentComponent = computed(() => tabs.find((item) => item.value === activeTab.value)?.component);
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.analysis-tabs {
border-bottom: 1px solid #dfe7f2;
}
</style> </style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment