ylj20011123 c78652a8d1 update
2025-10-23 18:35:54 +08:00

925 lines
26 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="order-refund-analysis">
<!-- 报表标题 -->
<view class="report-header">
<text class="report-title">订单和退单数据交叉分析</text>
<view class="report-period">
<text class="period-label">分析周期</text>
<text class="period-value">{{ analysisPeriod }}</text>
</view>
</view>
<!-- 关键指标卡片 -->
<view class="metrics-row">
<view class="metric-card">
<view class="metric-icon refund-amount"></view>
<text class="metric-title">退款总金额</text>
<view class="metric-value-container">
<text class="metric-value">¥{{ formatMoney(totalRefundAmount) }}</text>
<text class="metric-unit"></text>
</view>
</view>
<view class="metric-card">
<view class="metric-icon refund-count"></view>
<text class="metric-title">退款总笔数</text>
<view class="metric-value-container">
<text class="metric-value">{{ formatNumber(totalRefundCount) }}</text>
<text class="metric-unit"></text>
</view>
</view>
<view class="metric-card">
<view class="metric-icon refund-rate"></view>
<text class="metric-title">退款率</text>
<view class="metric-value-container">
<text class="metric-value">{{ refundRate }}%</text>
<text class="metric-unit"></text>
</view>
</view>
</view>
<!-- 退款原因分析 -->
<view class="chart-card">
<view class="chart-header">
<text class="chart-title">退款原因分析</text>
<text class="chart-subtitle">各退款原因占比情况</text>
</view>
<view class="chart-content">
<view class="pie-chart-container">
<QiunDataCharts
type="pie"
:opts="refundReasonOpts"
:chartData="refundReasonChartData"
:canvas2d="true"
:inScrollView="true"
canvasId="refundReasonChart"
/>
<view class="pie-legend">
<view class="legend-item" v-for="(item, index) in refundReasonList" :key="index">
<view class="legend-color" :style="{ backgroundColor: item.color }"></view>
<text class="legend-name">{{ item.name }}</text>
<text class="legend-value">{{ item.percentage }}%</text>
</view>
</view>
</view>
</view>
</view>
<!-- 退款商品分析 -->
<view class="chart-card">
<view class="chart-header">
<text class="chart-title">退款商品分析</text>
<text class="chart-subtitle">退款商品TOP 10排行</text>
</view>
<view class="chart-content">
<view class="bar-chart-container">
<QiunDataCharts
type="column"
:opts="refundProductOpts"
:chartData="refundProductChartData"
:canvas2d="true"
:inScrollView="true"
canvasId="refundProductChart"
/>
</view>
</view>
</view>
<!-- 退款店铺分析 -->
<view class="chart-card">
<view class="chart-header">
<text class="chart-title">退款店铺分析</text>
<text class="chart-subtitle">退款金额TOP 8店铺</text>
</view>
<view class="chart-content">
<view class="bar-chart-container">
<QiunDataCharts
type="column"
:opts="refundShopOpts"
:chartData="refundShopChartData"
:canvas2d="true"
:inScrollView="true"
canvasId="refundShopChart"
/>
</view>
</view>
</view>
<!-- 退款服务区分析 -->
<view class="chart-card">
<view class="chart-header">
<text class="chart-title">退款服务区分析</text>
<text class="chart-subtitle">各服务区退款金额分布</text>
</view>
<view class="chart-content">
<view class="column-chart-container">
<QiunDataCharts
type="column"
:opts="refundServiceAreaOpts"
:chartData="refundServiceAreaChartData"
:canvas2d="true"
:inScrollView="true"
canvasId="refundServiceAreaChart"
/>
</view>
</view>
</view>
<!-- 退款地域分布 -->
<view class="chart-card">
<view class="chart-header">
<text class="chart-title">退款地域分布</text>
<text class="chart-subtitle">各省份退款金额占比</text>
</view>
<view class="chart-content">
<view class="pie-chart-container">
<QiunDataCharts
type="pie"
:opts="refundRegionOpts"
:chartData="refundRegionChartData"
:canvas2d="true"
:inScrollView="true"
canvasId="refundRegionChart"
/>
<view class="pie-legend">
<view class="legend-item" v-for="(item, index) in refundRegionList" :key="index">
<view class="legend-color" :style="{ backgroundColor: item.color }"></view>
<text class="legend-name">{{ item.name }}</text>
<text class="legend-value">¥{{ formatMoney(item.amount) }}</text>
</view>
</view>
</view>
</view>
</view>
<!-- 退款趋势分析 -->
<view class="chart-card">
<view class="chart-header">
<text class="chart-title">退款趋势分析</text>
<text class="chart-subtitle">最近30天退款金额和笔数变化</text>
</view>
<view class="chart-content">
<view class="line-chart-container">
<QiunDataCharts
type="line"
:opts="refundTrendOpts"
:chartData="refundTrendChartData"
:canvas2d="true"
:inScrollView="true"
canvasId="refundTrendChart"
/>
<view class="chart-legend">
<view class="legend-item">
<view class="legend-dot amount"></view>
<text class="legend-text">退款金额(÷1000)</text>
</view>
<view class="legend-item">
<view class="legend-dot count"></view>
<text class="legend-text">退款笔数</text>
</view>
</view>
</view>
</view>
</view>
<!-- 退款明细数据 -->
<view class="chart-card">
<view class="chart-header">
<text class="chart-title">退款明细数据</text>
<view class="table-actions">
<text class="action-btn" @click="toggleFilter">筛选</text>
<text class="action-btn" @click="exportData">导出</text>
</view>
</view>
<view class="refund-cards">
<view class="refund-card" v-for="(item, index) in refundList" :key="index">
<view class="refund-header">
<view class="refund-reason-tag" :style="{
backgroundColor: item.reason === '质量问题' ? '#ff4d4f' :
item.reason === '物流问题' ? '#faad14' :
item.reason === '服务态度' ? '#1890ff' :
item.reason === '商品不符' ? '#722ed1' :
item.reason === '个人原因' ? '#52c41a' : '#666666'
}">
{{ item.reason }}
</view>
<text class="refund-date">{{ item.date }}</text>
</view>
<view class="refund-body">
<text class="refund-product">{{ item.product }}</text>
<text class="refund-shop">店铺{{ item.shop }}</text>
<view class="refund-metrics">
<view class="metric-item">
<text class="metric-label">退款金额</text>
<text class="metric-value">¥{{ formatMoney(item.amount) }}</text>
</view>
<view class="metric-divider"></view>
<view class="metric-item">
<text class="metric-label">退款地区</text>
<text class="metric-value">{{ item.region }}</text>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import QiunDataCharts from './qiun-data-charts/components/qiun-data-charts/qiun-data-charts.vue'
export default {
components: {
QiunDataCharts
},
data() {
return {
analysisPeriod: '2024年10月',
totalRefundAmount: 5678901,
totalRefundCount: 12345,
refundRate: 8.5,
// 退款原因数据
refundReasonList: [
{ name: '质量问题', amount: 2345678, percentage: 41.3, color: '#ff4d4f' },
{ name: '物流问题', amount: 1567890, percentage: 27.6, color: '#faad14' },
{ name: '商品不符', amount: 987654, percentage: 17.4, color: '#722ed1' },
{ name: '个人原因', amount: 456789, percentage: 8.0, color: '#52c41a' },
{ name: '服务态度', amount: 321890, percentage: 5.7, color: '#1890ff' }
],
// 退款商品数据
refundProductData: [
{ name: '云南白药套装', refundAmount: 567890 },
{ name: '普洱茶礼盒', refundAmount: 456789 },
{ name: '鲜花饼组合', refundAmount: 345678 },
{ name: '三七粉精品', refundAmount: 298765 },
{ name: '云南咖啡豆', refundAmount: 234567 },
{ name: '玉石手镯', refundAmount: 198765 },
{ name: '民族服饰', refundAmount: 167890 },
{ name: '鲜花精油', refundAmount: 145678 },
{ name: '手工皂套装', refundAmount: 123456 },
{ name: '银饰项链', refundAmount: 98765 }
],
// 退款店铺数据
refundShopData: [
{ name: '云南特产旗舰店', refundAmount: 1234567 },
{ name: '茶叶专营店', refundAmount: 987654 },
{ name: '手工艺品店', refundAmount: 789012 },
{ name: '健康养生店', refundAmount: 654321 },
{ name: '咖啡专营店', refundAmount: 543210 },
{ name: '珠宝玉石店', refundAmount: 456789 },
{ name: '食品特产店', refundAmount: 345678 },
{ name: '文创产品店', refundAmount: 234567 }
],
// 退款服务区数据
refundServiceAreaData: {
categories: ['昆明服务区', '大理服务区', '丽江服务区', '西双版纳服务区', '香格里拉服务区', '曲靖服务区', '玉溪服务区', '其他服务区'],
refundData: [234567, 198765, 167890, 145678, 123456, 98765, 87654, 65432]
},
// 退款地域数据
refundRegionList: [
{ name: '云南省', amount: 3456789, percentage: 60.8, color: '#ff4d4f' },
{ name: '广东省', amount: 789012, percentage: 13.9, color: '#faad14' },
{ name: '北京市', amount: 567890, percentage: 10.0, color: '#722ed1' },
{ name: '上海市', amount: 456789, percentage: 8.0, color: '#52c41a' },
{ name: '四川省', amount: 234567, percentage: 4.1, color: '#1890ff' },
{ name: '其他', amount: 177854, percentage: 3.2, color: '#999999' }
],
// 退款趋势数据
refundTrendData: {
categories: ['1日', '5日', '10日', '15日', '20日', '25日', '30日'],
amountData: [123456, 156789, 187654, 234567, 198765, 156789, 123456],
countData: [234, 345, 456, 567, 456, 345, 234]
},
// 退款明细数据
refundList: [
{ reason: '质量问题', date: '2024-10-28', product: '云南白药套装', shop: '云南特产旗舰店', amount: 56789, region: '云南省' },
{ reason: '物流问题', date: '2024-10-28', product: '普洱茶礼盒', shop: '茶叶专营店', amount: 34567, region: '广东省' },
{ reason: '商品不符', date: '2024-10-27', product: '鲜花饼组合', shop: '食品特产店', amount: 23456, region: '北京市' },
{ reason: '个人原因', date: '2024-10-27', product: '三七粉精品', shop: '健康养生店', amount: 19876, region: '上海市' },
{ reason: '服务态度', date: '2024-10-26', product: '云南咖啡豆', shop: '咖啡专营店', amount: 15678, region: '四川省' },
{ reason: '质量问题', date: '2024-10-26', product: '玉石手镯', shop: '珠宝玉石店', amount: 13456, region: '云南省' }
]
}
},
computed: {
// 退款原因图表数据
refundReasonChartData() {
return {
series: [{
data: this.refundReasonList.map(item => ({
name: item.name,
value: item.amount
}))
}]
}
},
// 退款原因图表配置
refundReasonOpts() {
return {
color: this.refundReasonList.map(item => item.color),
padding: [5, 5, 5, 5],
dataLabel: true,
legend: {
show: false
},
extra: {
pie: {
activeOpacity: 0.5,
activeRadius: 10,
offsetAngle: 0,
labelWidth: 15,
border: false,
borderWidth: 3,
borderColor: '#FFFFFF'
}
}
}
},
// 退款商品图表数据
refundProductChartData() {
return {
categories: this.refundProductData.map(item =>
item.name.length > 6 ? item.name.substring(0, 6) + '...' : item.name
),
series: [{
name: '退款金额',
data: this.refundProductData.map(item => item.refundAmount)
}]
}
},
// 退款商品图表配置
refundProductOpts() {
return {
color: ['#ff4d4f'],
padding: [15, 15, 15, 15],
dataLabel: false,
enableScroll: true,
xAxis: {
itemCount: 4,
scrollShow: true,
scrollAlign: 'right',
scrollColor: '#ff4d4f',
scrollBackgroundColor: 'rgba(255, 77, 79, 0.1)',
scrollWidth: 4,
scrollHeight: 8
},
yAxis: {
gridType: 'dash',
dashLength: 2,
data: [{
min: 0
}]
},
extra: {
column: {
type: 'group',
width: 30,
activeBgColor: '#000000',
activeBgOpacity: 0.08,
linearType: 'custom',
barBorderCircle: true
}
}
}
},
// 退款店铺图表数据
refundShopChartData() {
return {
categories: this.refundShopData.map(item =>
item.name.length > 6 ? item.name.substring(0, 6) + '...' : item.name
),
series: [{
name: '退款金额',
data: this.refundShopData.map(item => item.refundAmount)
}]
}
},
// 退款店铺图表配置
refundShopOpts() {
return {
color: ['#faad14'],
padding: [15, 15, 15, 15],
dataLabel: false,
enableScroll: true,
xAxis: {
itemCount: 3,
scrollShow: true,
scrollAlign: 'right',
scrollColor: '#faad14',
scrollBackgroundColor: 'rgba(250, 173, 20, 0.1)',
scrollWidth: 4,
scrollHeight: 8
},
yAxis: {
gridType: 'dash',
dashLength: 2,
data: [{
min: 0
}]
},
extra: {
column: {
type: 'group',
width: 40,
activeBgColor: '#000000',
activeBgOpacity: 0.08,
linearType: 'custom',
barBorderCircle: true
}
}
}
},
// 退款服务区图表数据
refundServiceAreaChartData() {
return {
categories: this.refundServiceAreaData.categories,
series: [{
name: '退款金额',
data: this.refundServiceAreaData.refundData
}]
}
},
// 退款服务区图表配置
refundServiceAreaOpts() {
return {
color: ['#722ed1'],
padding: [15, 15, 15, 15],
dataLabel: false,
enableScroll: true,
xAxis: {
itemCount: 4,
scrollShow: true,
scrollAlign: 'right',
scrollColor: '#722ed1',
scrollBackgroundColor: 'rgba(114, 46, 209, 0.1)',
scrollWidth: 4,
scrollHeight: 8
},
yAxis: {
gridType: 'dash',
dashLength: 2,
data: [{
min: 0
}]
},
extra: {
column: {
type: 'group',
width: 30,
activeBgColor: '#000000',
activeBgOpacity: 0.08,
linearType: 'custom',
barBorderCircle: true
}
}
}
},
// 退款地域图表数据
refundRegionChartData() {
return {
series: [{
data: this.refundRegionList.map(item => ({
name: item.name,
value: item.amount
}))
}]
}
},
// 退款地域图表配置
refundRegionOpts() {
return {
color: this.refundRegionList.map(item => item.color),
padding: [5, 5, 5, 5],
dataLabel: true,
legend: {
show: false
},
extra: {
pie: {
activeOpacity: 0.5,
activeRadius: 10,
offsetAngle: 0,
labelWidth: 15,
border: false,
borderWidth: 3,
borderColor: '#FFFFFF'
}
}
}
},
// 退款趋势图表数据
refundTrendChartData() {
return {
categories: this.refundTrendData.categories,
series: [
{
name: '退款金额',
data: this.refundTrendData.amountData.map(v => v / 1000)
},
{
name: '退款笔数',
data: this.refundTrendData.countData
}
]
}
},
// 退款趋势图表配置
refundTrendOpts() {
return {
color: ['#ff4d4f', '#faad14'],
padding: [15, 15, 15, 15],
dataLabel: false,
legend: {
show: false
},
xAxis: {
disableGrid: true
},
yAxis: {
gridType: 'dash',
dashLength: 2,
data: [
{
min: 0
},
{
min: 0,
position: 'right'
}
]
},
extra: {
line: {
type: 'curve',
width: 2,
activeType: 'hollow'
}
}
}
}
},
methods: {
formatNumber(num) {
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")
},
formatMoney(amount) {
return amount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")
},
toggleFilter() {
console.log('打开筛选器')
},
exportData() {
console.log('导出数据')
}
}
}
</script>
<style scoped lang="less">
@primary-color: #667eea;
@secondary-color: #764ba2;
@success-color: #52c41a;
@warning-color: #faad14;
@error-color: #ff4d4f;
@text-primary: #333;
@text-secondary: #666;
@text-light: #999;
@bg-white: #ffffff;
@border-radius: 16rpx;
@shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
.order-refund-analysis {
.report-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 32rpx;
padding: 0 8rpx;
.report-title {
font-size: 36rpx;
font-weight: 600;
color: @text-primary;
}
.report-period {
display: flex;
align-items: center;
.period-label {
font-size: 24rpx;
color: @text-secondary;
margin-right: 8rpx;
}
.period-value {
font-size: 24rpx;
color: @primary-color;
font-weight: 500;
}
}
}
.metrics-row {
display: flex;
gap: 24rpx;
margin-bottom: 24rpx;
.metric-card {
flex: 1;
background: @bg-white;
border-radius: @border-radius;
padding: 32rpx 24rpx;
box-shadow: @shadow;
text-align: center;
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4rpx;
background: linear-gradient(90deg, @primary-color, @secondary-color);
}
.metric-icon {
width: 48rpx;
height: 48rpx;
margin: 0 auto 16rpx;
border-radius: 50%;
position: relative;
&.refund-amount {
background: linear-gradient(135deg, #ff4d4f, #ff7875);
&::after { content: '💸'; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 24rpx; }
}
&.refund-count {
background: linear-gradient(135deg, #faad14, #ffc53d);
&::after { content: '📊'; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 24rpx; }
}
&.refund-rate {
background: linear-gradient(135deg, #722ed1, #9254de);
&::after { content: '📉'; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 24rpx; }
}
}
.metric-title {
font-size: 24rpx;
color: @text-secondary;
margin-bottom: 12rpx;
}
.metric-value-container {
display: flex;
flex-direction: column;
align-items: center;
gap: 4rpx;
}
.metric-value {
font-size: 32rpx;
font-weight: 600;
color: @text-primary;
font-family: 'DINAlternate-Bold', sans-serif;
line-height: 1;
}
.metric-unit {
font-size: 20rpx;
color: @text-light;
line-height: 1;
}
}
}
.chart-card {
background: @bg-white;
border-radius: @border-radius;
padding: 24rpx;
box-shadow: @shadow;
margin-bottom: 24rpx;
.chart-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
.chart-title {
font-size: 28rpx;
font-weight: 600;
color: @text-primary;
}
.chart-subtitle {
font-size: 22rpx;
color: @text-light;
margin-top: 4rpx;
}
.table-actions {
display: flex;
gap: 16rpx;
.action-btn {
font-size: 24rpx;
color: @primary-color;
padding: 8rpx 16rpx;
border: 1rpx solid @primary-color;
border-radius: 8rpx;
}
}
}
.chart-content {
.line-chart-container,
.column-chart-container,
.bar-chart-container {
width: 100%;
}
.line-chart-container {
.chart-legend {
display: flex;
justify-content: center;
gap: 32rpx;
margin-top: 16rpx;
.legend-item {
display: flex;
align-items: center;
gap: 8rpx;
.legend-dot {
width: 12rpx;
height: 12rpx;
border-radius: 50%;
&.amount {
background: #ff4d4f;
}
&.count {
background: #faad14;
}
}
.legend-text {
font-size: 22rpx;
color: @text-secondary;
}
}
}
}
.pie-chart-container {
display: flex;
flex-direction: column;
align-items: center;
.pie-legend {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 16rpx;
margin-top: 16rpx;
.legend-item {
display: flex;
align-items: center;
gap: 8rpx;
.legend-color {
width: 12rpx;
height: 12rpx;
border-radius: 2rpx;
}
.legend-name {
font-size: 22rpx;
color: @text-secondary;
}
.legend-value {
font-size: 22rpx;
color: @text-primary;
font-weight: 600;
}
}
}
}
}
.refund-cards {
display: flex;
flex-direction: column;
gap: 16rpx;
.refund-card {
background: #fafafa;
border-radius: 12rpx;
padding: 20rpx;
border: 1rpx solid #f0f0f0;
.refund-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
.refund-reason-tag {
padding: 4rpx 12rpx;
border-radius: 12rpx;
font-size: 20rpx;
color: white;
font-weight: 500;
}
.refund-date {
font-size: 20rpx;
color: @text-light;
font-family: monospace;
}
}
.refund-body {
.refund-product {
font-size: 28rpx;
color: @text-primary;
font-weight: 600;
margin-bottom: 4rpx;
display: block;
}
.refund-shop {
font-size: 22rpx;
color: @text-secondary;
margin-bottom: 12rpx;
display: block;
}
.refund-metrics {
display: flex;
align-items: center;
.metric-item {
flex: 1;
text-align: center;
.metric-label {
font-size: 20rpx;
color: @text-secondary;
display: block;
margin-bottom: 4rpx;
}
.metric-value {
font-size: 24rpx;
color: @text-primary;
font-weight: 600;
display: block;
}
}
.metric-divider {
width: 1rpx;
height: 40rpx;
background: #e0e0e0;
margin: 0 16rpx;
}
}
}
}
}
}
}
</style>