refactor(分析模块): 优化组件结构和搜索功能

- 合并和简化模板中的header部分,提升代码可读性
- 统一搜索条件的布局,增强用户体验
- 更新数据绑定逻辑,确保搜索参数的正确处理
- 精简和优化表格及分页组件的实现,提升性能和可维护性
This commit is contained in:
2026-04-08 17:36:56 +08:00
parent f89063af6f
commit 93cb47deaf
8 changed files with 1104 additions and 1917 deletions

View File

@@ -6,29 +6,6 @@
<span>分销效果核算</span>
</div>
</template>
<!-- 搜索条件 -->
<div class="search-container">
<el-form :model="searchParams" :inline="true" class="search-form">
<el-form-item label="时间范围">
<el-date-picker
v-model="searchParams.dateRange"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
/>
</el-form-item>
<el-form-item label="达人名称">
<el-input v-model="searchParams.anchorName" placeholder="请输入达人名称" clearable />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</div>
<!-- 分销效果趋势 -->
<div class="chart-container">
<el-card>
<template #header>
@@ -37,32 +14,25 @@
<div ref="effectChartRef" class="chart"></div>
</el-card>
</div>
<!-- 核心指标 -->
<div class="stats-cards">
<el-card class="stats-card">
<div class="stats-card-title">销售</div>
<div class="stats-card-value">{{ statsData.totalSales || 0 }}</div>
</el-card>
<el-card class="stats-card">
<div class="stats-card-title">总佣金金额</div>
<div class="stats-card-value">{{ statsData.totalCommission || 0 }}</div>
</el-card>
<el-card class="stats-card">
<div class="stats-card-title">平均佣金率</div>
<div class="stats-card-value">{{ statsData.avgCommissionRate || 0 }}%</div>
</el-card>
<el-card class="stats-card">
<div class="stats-card-title">达人数量</div>
<div class="stats-card-value">{{ statsData.anchorCount || 0 }}</div>
</el-card>
<el-card class="stats-card"><div class="stats-card-title">总销售额</div><div class="stats-card-value">{{ statsData.totalSales || 0 }}</div></el-card>
<el-card class="stats-card"><div class="stats-card-title">佣金金</div><div class="stats-card-value">{{ statsData.totalCommission || 0 }}</div></el-card>
<el-card class="stats-card"><div class="stats-card-title">平均佣金率</div><div class="stats-card-value">{{ statsData.avgCommissionRate || 0 }}%</div></el-card>
<el-card class="stats-card"><div class="stats-card-title">达人数量</div><div class="stats-card-value">{{ statsData.anchorCount || 0 }}</div></el-card>
</div>
<!-- 达人推广效果 -->
<div class="anchor-effect">
<el-card>
<template #header>
<div class="card-header">达人推广效果</div>
</template>
<el-table :data="anchorList" style="width: 100%">
<template #header><div class="card-header">达人推广效果</div></template>
<div class="search-container">
<el-form :model="searchParams" :inline="true" class="search-form">
<el-form-item label="时间范围">
<el-date-picker v-model="searchParams.dateRange" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD" />
</el-form-item>
<el-form-item label="达人名称"><el-input v-model="searchParams.anchorName" placeholder="请输入达人名称" clearable /></el-form-item>
<el-form-item><el-button type="primary" @click="handleSearch">查询</el-button><el-button @click="handleReset">重置</el-button></el-form-item>
</el-form>
</div>
<el-table :data="pagedAnchorList" style="width: 100%">
<el-table-column prop="rank" label="排名" width="80" />
<el-table-column prop="anchorName" label="达人名称" />
<el-table-column prop="sales" label="销售额" />
@@ -72,15 +42,7 @@
<el-table-column prop="conversionRate" label="转化率" />
</el-table>
<div class="pagination-container">
<el-pagination
v-model:current-page="pagination.currentPage"
v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
:total="pagination.total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
<el-pagination v-model:current-page="pagination.currentPage" v-model:page-size="pagination.pageSize" :page-sizes="[10, 20, 50, 100]" layout="total, sizes, prev, pager, next, jumper" :total="pagination.total" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
</div>
</el-card>
</div>
@@ -89,223 +51,74 @@
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue';
import { computed, onMounted, reactive, ref } from 'vue';
import * as echarts from 'echarts';
const searchParams = reactive({
dateRange: [],
anchorName: '',
});
const statsData = reactive({
totalSales: 0,
totalCommission: 0,
avgCommissionRate: 0,
anchorCount: 0,
});
interface AnchorItem {
rank: number;
anchorName: string;
sales: number;
commission: number;
commissionRate: number;
orderCount: number;
conversionRate: number;
}
const searchParams = reactive({ dateRange: [], anchorName: '' });
const statsData = reactive({ totalSales: 0, totalCommission: 0, avgCommissionRate: 0, anchorCount: 0 });
interface AnchorItem { rank: number; anchorName: string; sales: number; commission: number; commissionRate: number; orderCount: number; conversionRate: number }
const anchorList = ref<AnchorItem[]>([]);
const pagination = reactive({
currentPage: 1,
pageSize: 10,
total: 0,
});
const pagination = reactive({ currentPage: 1, pageSize: 10, total: 0 });
const pagedAnchorList = computed(() => anchorList.value.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize));
const effectChartRef = ref();
let effectChart: echarts.ECharts | null = null;
// 模拟分销效果趋势数据
const getMockEffectTrend = () => {
const dates = ['1月', '2月', '3月', '4月', '5月', '6月'];
return dates.map((date) => ({
date,
sales: 500000 + Math.random() * 1000000,
commission: 50000 + Math.random() * 200000,
}));
};
// 模拟达人推广效果数据
const getMockAnchorList = () => {
const anchors: AnchorItem[] = [];
for (let i = 1; i <= 20; i++) {
anchors.push({
rank: i,
anchorName: `达人${i}`,
sales: 100000 + Math.random() * 900000,
commission: 10000 + Math.random() * 180000,
commissionRate: 10 + Math.random() * 10,
orderCount: 100 + Math.floor(Math.random() * 900),
conversionRate: parseFloat((1 + Math.random() * 9).toFixed(2)),
});
}
return anchors;
const dates = searchParams.anchorName ? ['本期'] : ['1月', '2月', '3月', '4月', '5月', '6月'];
return dates.map((date) => ({ date, sales: 500000 + Math.random() * 1000000, commission: 50000 + Math.random() * 200000 }));
};
const getMockAnchorList = () => Array.from({ length: 24 }, (_, i) => ({ rank: i + 1, anchorName: `达人${i + 1}`, sales: 100000 + Math.random() * 900000, commission: 10000 + Math.random() * 180000, commissionRate: +(10 + Math.random() * 10).toFixed(2), orderCount: 100 + Math.floor(Math.random() * 900), conversionRate: +(1 + Math.random() * 9).toFixed(2) }));
const handleSearch = () => {
// 使用模拟数据
statsData.totalSales = 5000000 + Math.random() * 5000000;
statsData.totalCommission = 500000 + Math.random() * 500000;
statsData.avgCommissionRate = 15 + Math.random() * 5;
statsData.avgCommissionRate = +(15 + Math.random() * 5).toFixed(2);
statsData.anchorCount = 50 + Math.floor(Math.random() * 50);
anchorList.value = getMockAnchorList();
pagination.total = 20;
pagination.total = anchorList.value.length;
pagination.currentPage = 1;
initEffectChart(getMockEffectTrend());
};
const handleReset = () => {
searchParams.dateRange = [];
searchParams.anchorName = '';
pagination.currentPage = 1;
pagination.pageSize = 10;
};
const handleSizeChange = (size: number) => {
pagination.pageSize = size;
handleSearch();
};
const handleCurrentChange = (current: number) => {
pagination.currentPage = current;
handleSearch();
};
const handleReset = () => { searchParams.dateRange = []; searchParams.anchorName = ''; pagination.currentPage = 1; pagination.pageSize = 10; handleSearch(); };
const handleSizeChange = (size: number) => { pagination.pageSize = size; pagination.currentPage = 1; };
const handleCurrentChange = (current: number) => { pagination.currentPage = current; };
const initEffectChart = (effectTrend: any[]) => {
if (!effectChartRef.value) return;
if (effectChart) {
effectChart.dispose();
}
if (effectChart) effectChart.dispose();
effectChart = echarts.init(effectChartRef.value);
const option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
label: {
backgroundColor: '#6a7985',
},
},
},
legend: {
data: ['销售额', '佣金金额'],
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true,
},
xAxis: {
type: 'category',
boundaryGap: false,
data: effectTrend.map((item) => item.date),
},
yAxis: [
{
type: 'value',
name: '金额',
position: 'left',
},
],
const isSingle = effectTrend.length <= 1;
effectChart.setOption({
tooltip: { trigger: 'axis', axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } } },
legend: { data: ['销售额', '佣金金额'] },
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
xAxis: { type: 'category', boundaryGap: isSingle, data: effectTrend.map((item) => item.date) },
yAxis: [{ type: 'value', name: '金额', position: 'left' }],
series: [
{
name: '销售额',
type: 'line',
data: effectTrend.map((item) => item.sales),
smooth: true,
},
{
name: '佣金金额',
type: 'line',
data: effectTrend.map((item) => item.commission),
smooth: true,
},
{ name: '销售额', type: 'line', data: effectTrend.map((item) => item.sales), smooth: true, showSymbol: true, symbolSize: isSingle ? 10 : 6 },
{ name: '佣金金额', type: 'line', data: effectTrend.map((item) => item.commission), smooth: true, showSymbol: true, symbolSize: isSingle ? 10 : 6 },
],
};
effectChart.setOption(option);
});
};
onMounted(() => {
handleSearch();
window.addEventListener('resize', () => {
effectChart?.resize();
});
window.addEventListener('resize', () => effectChart?.resize());
});
</script>
<style scoped>
.trade-operation-distribution-effect {
padding: 20px;
}
.card-header {
font-size: 16px;
font-weight: bold;
}
.search-container {
margin-bottom: 20px;
}
.search-form {
display: flex;
align-items: center;
}
.stats-cards {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
margin-bottom: 20px;
}
.stats-card {
text-align: center;
padding: 20px;
}
.stats-card-title {
font-size: 14px;
color: #606266;
margin-bottom: 10px;
}
.stats-card-value {
font-size: 24px;
font-weight: bold;
}
.anchor-effect {
margin-bottom: 20px;
}
.pagination-container {
margin-top: 20px;
display: flex;
justify-content: flex-end;
}
.chart-container {
margin: 20px 0;
}
.chart {
width: 100%;
height: 400px;
}
.trade-operation-distribution-effect { padding: 20px; }
.card-header { display: flex; align-items: center; justify-content: space-between; font-size: 16px; font-weight: 600; }
.search-container { margin-bottom: 16px; }
.search-form { display: flex; align-items: center; flex-wrap: wrap; }
.search-form :deep(.el-form-item) { margin-right: 12px; margin-bottom: 12px; }
.stats-cards { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; margin-bottom: 20px; }
.stats-card { text-align: center; padding: 20px; }
.stats-card-title { font-size: 14px; color: #606266; margin-bottom: 10px; }
.stats-card-value { font-size: 24px; font-weight: bold; }
.anchor-effect { margin-bottom: 20px; }
.pagination-container { margin-top: 16px; display: flex; justify-content: flex-end; }
.chart-container { margin: 0 0 20px; }
.chart { width: 100%; height: 400px; }
</style>