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

914 lines
25 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="gross-margin-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 margin"></view>
<text class="metric-title">平均毛利率</text>
<view class="metric-value-container">
<text class="metric-value">{{ avgMargin }}%</text>
<text class="metric-unit">良好</text>
</view>
</view>
<view class="metric-card">
<view class="metric-icon revenue"></view>
<text class="metric-title">毛利总额</text>
<view class="metric-value-container">
<text class="metric-value">¥{{ formatMoney(totalMargin) }}</text>
<text class="metric-unit">万元</text>
</view>
</view>
<view class="metric-card">
<view class="metric-icon profit"></view>
<text class="metric-title">净利润率</text>
<view class="metric-value-container">
<text class="metric-value">{{ netProfitRate }}%</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="pieChartOpts"
:chartData="pieChartData"
:canvas2d="true"
:inScrollView="true"
canvasId="marginPieChart"
/>
<view class="pie-legend">
<view class="legend-item" v-for="(item, index) in marginDistributionData" :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">最近12个月毛利率变化趋势</text>
</view>
<view class="chart-content">
<view class="line-chart-container">
<QiunDataCharts
type="line"
:opts="lineChartOpts"
:chartData="lineChartData"
:canvas2d="true"
:inScrollView="true"
canvasId="marginTrendChart"
/>
<view class="chart-legend">
<view class="legend-item">
<view class="legend-dot margin"></view>
<text class="legend-text">毛利率</text>
</view>
<view class="legend-item">
<view class="legend-dot revenue"></view>
<text class="legend-text">销售额</text>
</view>
</view>
</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="bar-chart-container">
<QiunDataCharts
type="column"
:opts="barChartOpts"
:chartData="barChartData"
:canvas2d="true"
:inScrollView="true"
canvasId="categoryMarginChart"
/>
</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="ranking-list">
<view class="ranking-item" v-for="(item, index) in topMarginProducts" :key="index">
<view class="ranking-medal" :class="[getMedalClass(index)]">
<text class="medal-text">{{ getMedalText(index) }}</text>
</view>
<view class="ranking-content">
<text class="product-name">{{ item.name }}</text>
<text class="product-category">{{ item.category }}</text>
</view>
<view class="ranking-metrics">
<view class="metric-item">
<text class="metric-label">毛利率</text>
<text class="metric-value">{{ item.marginRate }}%</text>
</view>
<view class="metric-divider"></view>
<view class="metric-item">
<text class="metric-label">毛利额</text>
<text class="metric-value">¥{{ formatMoney(item.marginAmount) }}</text>
</view>
<view class="metric-divider"></view>
<view class="metric-item">
<text class="metric-label">销售额</text>
<text class="metric-value">¥{{ formatMoney(item.revenue) }}</text>
</view>
</view>
</view>
</view>
</view>
<!-- 低毛利商品分析 -->
<view class="chart-card">
<view class="chart-header">
<text class="chart-title">低毛利商品分析</text>
<text class="chart-subtitle">需要优化的低毛利商品</text>
</view>
<view class="low-margin-list">
<view class="low-margin-item" v-for="(item, index) in lowMarginProducts" :key="index" :class="[getMarginLevel(item.marginRate)]">
<view class="margin-icon">{{ getMarginIcon(item.marginRate) }}</view>
<view class="margin-content">
<text class="product-name">{{ item.name }}</text>
<text class="product-info">{{ item.category }} | 成本:¥{{ item.cost }} | 售价:¥{{ item.price }}</text>
</view>
<view class="margin-data">
<text class="margin-rate">{{ item.marginRate }}%</text>
<text class="margin-amount">毛利:¥{{ item.unitMargin }}</text>
</view>
<view class="margin-action">
<text class="action-btn">优化建议</text>
</view>
</view>
</view>
</view>
<!-- 毛利优化建议 -->
<view class="chart-card">
<view class="chart-header">
<text class="chart-title">毛利优化建议</text>
</view>
<view class="suggestions">
<view class="suggestion-item" v-for="(item, index) in optimizationSuggestions" :key="index">
<view class="suggestion-icon">{{ item.icon }}</view>
<view class="suggestion-content">
<text class="suggestion-title">{{ item.title }}</text>
<text class="suggestion-desc">{{ item.description }}</text>
</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月',
avgMargin: 42.8,
totalMargin: 1256.78,
netProfitRate: 18.5,
// 毛利率分布数据
marginDistributionData: [
{ name: '高毛利(>50%)', value: 156234, percentage: 32.2, color: '#52C41A' },
{ name: '良好毛利(30-50%)', value: 189456, percentage: 39.0, color: '#1890FF' },
{ name: '一般毛利(20-30%)', value: 98765, percentage: 20.3, color: '#FAAD14' },
{ name: '低毛利(10-20%)', value: 34567, percentage: 7.1, color: '#FF7A45' },
{ name: '微毛利(<10%)', value: 5678, percentage: 1.4, color: '#FF4D4F' }
],
// 毛利率趋势数据
marginTrendData: {
categories: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
marginData: [38.5, 39.2, 40.1, 41.3, 42.1, 41.8, 42.5, 43.1, 42.7, 42.8, 43.2, 42.8],
revenueData: [234, 256, 278, 289, 312, 298, 324, 345, 332, 338, 356, 345]
},
// 各类别毛利数据
categoryMarginData: [
{ category: '工艺品', marginRate: 68.5, revenue: 345.67 },
{ category: '保健品', marginRate: 52.3, revenue: 456.78 },
{ category: '化妆品', marginRate: 48.7, revenue: 234.56 },
{ category: '纺织品', marginRate: 45.2, revenue: 123.45 },
{ category: '茶叶', marginRate: 38.9, revenue: 567.89 },
{ category: '食品', marginRate: 35.6, revenue: 234.56 },
{ category: '饮品', marginRate: 28.4, revenue: 345.67 },
{ category: '其他', marginRate: 22.1, revenue: 89.12 }
],
// 高毛利商品TOP 10
topMarginProducts: [
{ name: '玉石手镯', category: '工艺品', marginRate: 75.8, marginAmount: 156.78, revenue: 345.67 },
{ name: '银饰项链', category: '工艺品', marginRate: 72.3, marginAmount: 134.56, revenue: 234.56 },
{ name: '三七粉', category: '保健品', marginRate: 68.9, marginAmount: 123.45, revenue: 234.56 },
{ name: '手工织锦', category: '纺织品', marginRate: 65.2, marginAmount: 98.76, revenue: 189.45 },
{ name: '鲜花精油', category: '化妆品', marginRate: 62.1, marginAmount: 87.65, revenue: 178.90 },
{ name: '普洱茶饼', category: '茶叶', marginRate: 58.7, marginAmount: 156.78, revenue: 345.67 },
{ name: '云南白药', category: '保健品', marginRate: 56.4, marginAmount: 145.67, revenue: 312.45 },
{ name: '民族刺绣', category: '纺织品', marginRate: 54.3, marginAmount: 76.54, revenue: 189.76 },
{ name: '玉石吊坠', category: '工艺品', marginRate: 52.8, marginAmount: 98.76, revenue: 234.56 },
{ name: '手工皂', category: '化妆品', marginRate: 51.2, marginAmount: 67.89, revenue: 189.45 }
],
// 低毛利商品
lowMarginProducts: [
{ name: '瓶装水', category: '饮品', cost: 1.2, price: 2.5, marginRate: 52.0, unitMargin: 1.3 },
{ name: '普通茶叶', category: '茶叶', cost: 45.0, price: 68.0, marginRate: 33.8, unitMargin: 23.0 },
{ name: '小食品', category: '食品', cost: 12.0, price: 18.5, marginRate: 35.1, unitMargin: 6.5 },
{ name: '简单工艺品', category: '工艺品', cost: 25.0, price: 38.0, marginRate: 34.2, unitMargin: 13.0 },
{ name: '基础化妆品', category: '化妆品', cost: 35.0, price: 52.0, marginRate: 32.7, unitMargin: 17.0 },
{ name: '普通保健品', category: '保健品', cost: 68.0, price: 95.0, marginRate: 28.4, unitMargin: 27.0 }
],
// 优化建议
optimizationSuggestions: [
{
icon: '💰',
title: '优化产品结构',
description: '增加高毛利商品的比重,减少低毛利商品的库存占比,提升整体毛利率'
},
{
icon: '📊',
title: '动态定价策略',
description: '根据市场需求和竞争情况,实施差异化定价策略,最大化单品毛利'
},
{
icon: '🏭',
title: '优化供应链成本',
description: '通过批量采购、优化物流等方式降低采购成本,提升毛利率空间'
},
{
icon: '🎯',
title: '精准营销推广',
description: '重点推广高毛利商品,通过营销策略提升高毛利商品的销售占比'
}
]
}
},
computed: {
// 饼图数据
pieChartData() {
return {
series: [{
data: this.marginDistributionData.map(item => ({
name: item.name,
value: item.value
}))
}]
}
},
// 饼图配置
pieChartOpts() {
return {
color: this.marginDistributionData.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'
}
}
}
},
// 折线图数据
lineChartData() {
return {
categories: this.marginTrendData.categories,
series: [
{
name: '毛利率',
data: this.marginTrendData.marginData
},
{
name: '销售额',
data: this.marginTrendData.revenueData.map(v => v / 10) // 缩放显示
}
]
}
},
// 折线图配置
lineChartOpts() {
return {
color: ['#52C41A', '#576EFF'],
padding: [15, 15, 15, 15],
dataLabel: false,
legend: {
show: false
},
xAxis: {
disableGrid: true
},
yAxis: {
gridType: 'dash',
dashLength: 2,
data: [
{
min: 0,
max: 50,
title: '毛利率(%)'
},
{
min: 0,
position: 'right',
title: '销售额(÷10)'
}
]
},
extra: {
line: {
type: 'curve',
width: 2,
activeType: 'hollow'
}
}
}
},
// 柱状图数据
barChartData() {
return {
categories: this.categoryMarginData.map(item => item.category),
series: [
{
name: '毛利率',
data: this.categoryMarginData.map(item => item.marginRate)
},
{
name: '销售额',
data: this.categoryMarginData.map(item => item.revenue / 10) // 缩放显示
}
]
}
},
// 柱状图配置
barChartOpts() {
return {
color: ['#52C41A', '#576EFF'],
padding: [15, 15, 15, 15],
dataLabel: false,
enableScroll: false,
xAxis: {
disableGrid: false,
gridType: 'dash',
rotateLabel: false,
itemCount: 8
},
yAxis: {
gridType: 'dash',
dashLength: 2,
data: [
{
min: 0,
max: 80,
title: '毛利率(%)'
},
{
min: 0,
position: 'right',
title: '销售额(÷10)'
}
]
},
extra: {
column: {
type: 'group',
width: 20,
activeBgColor: '#000000',
activeBgOpacity: 0.08,
linearType: 'custom',
barBorderCircle: true
}
}
}
}
},
methods: {
formatMoney(amount) {
return amount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")
},
getMedalClass(index) {
if (index === 0) return 'gold'
if (index === 1) return 'silver'
if (index === 2) return 'bronze'
return ''
},
getMedalText(index) {
if (index === 0) return '🥇'
if (index === 1) return '🥈'
if (index === 2) return '🥉'
return index + 1
},
getMarginLevel(marginRate) {
if (marginRate < 15) return 'severe'
if (marginRate < 25) return 'high'
if (marginRate < 35) return 'medium'
return 'low'
},
getMarginIcon(marginRate) {
if (marginRate < 15) return '🚨'
if (marginRate < 25) return '⚠️'
if (marginRate < 35) return '📉'
return '💹'
}
}
}
</script>
<style scoped lang="less">
@primary-color: #667eea;
@secondary-color: #764ba2;
@success-color: #52c41a;
@warning-color: #faad14;
@error-color: #ff4d4f;
@gold: #FFD700;
@silver: #C0C0C0;
@bronze: #CD7F32;
@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);
.gross-margin-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: 32rpx;
.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;
&.margin {
background: linear-gradient(135deg, #52c41a, #73d13d);
&::after { content: '💰'; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 24rpx; }
}
&.revenue {
background: linear-gradient(135deg, #1890ff, #40a9ff);
&::after { content: '📈'; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 24rpx; }
}
&.profit {
background: linear-gradient(135deg, #faad14, #ffc53d);
&::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: flex-start;
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;
}
}
.chart-content {
.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;
}
}
}
}
.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%;
&.margin {
background: #52C41A;
}
&.revenue {
background: #576EFF;
}
}
.legend-text {
font-size: 22rpx;
color: @text-secondary;
}
}
}
}
.bar-chart-container {
width: 100%;
}
}
.ranking-list {
.ranking-item {
display: flex;
align-items: center;
padding: 16rpx;
margin-bottom: 12rpx;
background: #fafafa;
border-radius: 12rpx;
border: 1rpx solid #f0f0f0;
.ranking-medal {
width: 48rpx;
height: 48rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 16rpx;
font-size: 24rpx;
&.gold {
background: linear-gradient(135deg, @gold, #FFA500);
}
&.silver {
background: linear-gradient(135deg, @silver, #D3D3D3);
}
&.bronze {
background: linear-gradient(135deg, @bronze, #D2691E);
}
&:not(.gold):not(.silver):not(.bronze) {
background: #f0f0f0;
color: @text-secondary;
}
.medal-text {
font-size: 20rpx;
font-weight: 600;
}
}
.ranking-content {
flex: 1;
.product-name {
font-size: 28rpx;
color: @text-primary;
font-weight: 500;
display: block;
margin-bottom: 4rpx;
}
.product-category {
font-size: 22rpx;
color: @text-light;
display: block;
}
}
.ranking-metrics {
display: flex;
align-items: center;
gap: 16rpx;
.metric-item {
text-align: center;
.metric-label {
font-size: 20rpx;
color: @text-secondary;
display: block;
margin-bottom: 4rpx;
}
.metric-value {
font-size: 22rpx;
color: @text-primary;
font-weight: 600;
display: block;
}
}
.metric-divider {
width: 1rpx;
height: 40rpx;
background: #e0e0e0;
}
}
}
}
.low-margin-list {
.low-margin-item {
display: flex;
align-items: center;
padding: 16rpx;
margin-bottom: 12rpx;
border-radius: 12rpx;
border: 1rpx solid #f0f0f0;
&.severe {
background: rgba(255, 77, 79, 0.05);
border-color: rgba(255, 77, 79, 0.2);
}
&.high {
background: rgba(255, 122, 69, 0.05);
border-color: rgba(255, 122, 69, 0.2);
}
&.medium {
background: rgba(250, 173, 20, 0.05);
border-color: rgba(250, 173, 20, 0.2);
}
&.low {
background: rgba(82, 196, 26, 0.05);
border-color: rgba(82, 196, 26, 0.2);
}
.margin-icon {
font-size: 32rpx;
margin-right: 16rpx;
}
.margin-content {
flex: 1;
.product-name {
font-size: 28rpx;
color: @text-primary;
font-weight: 500;
display: block;
margin-bottom: 4rpx;
}
.product-info {
font-size: 22rpx;
color: @text-light;
display: block;
}
}
.margin-data {
text-align: right;
margin-right: 16rpx;
.margin-rate {
font-size: 24rpx;
color: @error-color;
font-weight: 600;
display: block;
margin-bottom: 4rpx;
}
.margin-amount {
font-size: 22rpx;
color: @text-secondary;
display: block;
}
}
.margin-action {
.action-btn {
font-size: 22rpx;
color: @primary-color;
padding: 8rpx 16rpx;
border: 1rpx solid @primary-color;
border-radius: 8rpx;
}
}
}
}
.suggestions {
.suggestion-item {
display: flex;
align-items: flex-start;
padding: 16rpx;
margin-bottom: 12rpx;
background: #fafafa;
border-radius: 12rpx;
.suggestion-icon {
font-size: 32rpx;
margin-right: 16rpx;
margin-top: 4rpx;
}
.suggestion-content {
flex: 1;
.suggestion-title {
font-size: 26rpx;
color: @text-primary;
font-weight: 500;
display: block;
margin-bottom: 8rpx;
}
.suggestion-desc {
font-size: 22rpx;
color: @text-secondary;
line-height: 1.5;
display: block;
}
}
}
}
}
}
</style>