ylj20011123 2900c384eb update
2025-10-29 10:02:45 +08:00

911 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="inventory-turnover-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 turnover"></view>
<text class="metric-title">平均周转率</text>
<view class="metric-value-container">
<text class="metric-value">{{ avgTurnoverRate }}</text>
<text class="metric-unit">/</text>
</view>
</view>
<view class="metric-card">
<view class="metric-icon days"></view>
<text class="metric-title">平均周转天数</text>
<view class="metric-value-container">
<text class="metric-value">{{ avgTurnoverDays }}</text>
<text class="metric-unit"></text>
</view>
</view>
<view class="metric-card">
<view class="metric-icon efficiency"></view>
<text class="metric-title">周转效率</text>
<view class="metric-value-container">
<text class="metric-value">{{ efficiencyScore }}%</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="turnoverPieChart"
/>
<view class="pie-legend">
<view class="legend-item" v-for="(item, index) in turnoverDistributionData" :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="turnoverTrendChart"
/>
<view class="chart-legend">
<view class="legend-item">
<view class="legend-dot rate"></view>
<text class="legend-text">周转率</text>
</view>
<view class="legend-item">
<view class="legend-dot days"></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="categoryTurnoverChart"
/>
</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 topTurnoverProducts" :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.turnoverRate }}</text>
</view>
<view class="metric-divider"></view>
<view class="metric-item">
<text class="metric-label">周转天数</text>
<text class="metric-value">{{ item.turnoverDays }}天</text>
</view>
<view class="metric-divider"></view>
<view class="metric-item">
<text class="metric-label">年销量</text>
<text class="metric-value">{{ formatNumber(item.annualSales) }}</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="warning-list">
<view class="warning-item" v-for="(item, index) in lowTurnoverProducts" :key="index" :class="[getWarningLevel(item.turnoverDays)]">
<view class="warning-icon">⚠️</view>
<view class="warning-content">
<text class="product-name">{{ item.name }}</text>
<text class="product-info">{{ item.category }} | 库存:{{ item.stock }}件</text>
</view>
<view class="warning-data">
<text class="turnover-days">{{ item.turnoverDays }}天</text>
<text class="turnover-rate">{{ item.turnoverRate }}次/年</text>
</view>
<view class="warning-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月',
avgTurnoverRate: 8.5,
avgTurnoverDays: 43,
efficiencyScore: 76.8,
// 周转率分布数据
turnoverDistributionData: [
{ name: '快速周转(>12次)', value: 156234, percentage: 32.2, color: '#52C41A' },
{ name: '正常周转(6-12次)', value: 234567, percentage: 48.3, color: '#1890FF' },
{ name: '较慢周转(3-6次)', value: 78901, percentage: 16.2, color: '#FAAD14' },
{ name: '缓慢周转(1-3次)', value: 12345, percentage: 2.5, color: '#FF7A45' },
{ name: '滞销(<1次)', value: 3456, percentage: 0.8, color: '#FF4D4F' }
],
// 周转趋势数据
turnoverTrendData: {
categories: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
turnoverRateData: [7.2, 6.8, 7.5, 8.1, 8.3, 7.9, 8.2, 8.5, 8.1, 8.5, 8.7, 8.5],
turnoverDaysData: [51, 54, 49, 45, 44, 46, 44, 43, 45, 43, 42, 43]
},
// 各类别周转数据
categoryTurnoverData: [
{ category: '饮品', turnoverRate: 12.5, turnoverDays: 29 },
{ category: '食品', turnoverRate: 10.2, turnoverDays: 36 },
{ category: '保健品', turnoverRate: 8.7, turnoverDays: 42 },
{ category: '茶叶', turnoverRate: 7.8, turnoverDays: 47 },
{ category: '化妆品', turnoverRate: 6.5, turnoverDays: 56 },
{ category: '纺织品', turnoverRate: 5.2, turnoverDays: 70 },
{ category: '工艺品', turnoverRate: 3.4, turnoverDays: 107 },
{ category: '其他', turnoverRate: 2.1, turnoverDays: 174 }
],
// 高周转商品TOP 10
topTurnoverProducts: [
{ name: '云南咖啡', category: '饮品', turnoverRate: 18.5, turnoverDays: 20, annualSales: 12345 },
{ name: '鲜花饼', category: '食品', turnoverRate: 16.2, turnoverDays: 23, annualSales: 15678 },
{ name: '云南白药牙膏', category: '保健品', turnoverRate: 14.8, turnoverDays: 25, annualSales: 23456 },
{ name: '普洱茶饼', category: '茶叶', turnoverRate: 13.5, turnoverDays: 27, annualSales: 18900 },
{ name: '手工皂', category: '化妆品', turnoverRate: 12.8, turnoverDays: 29, annualSales: 9876 },
{ name: '鲜花精油', category: '化妆品', turnoverRate: 11.7, turnoverDays: 31, annualSales: 7654 },
{ name: '三七粉', category: '保健品', turnoverRate: 10.9, turnoverDays: 33, annualSales: 8765 },
{ name: '民族围巾', category: '纺织品', turnoverRate: 9.8, turnoverDays: 37, annualSales: 5432 },
{ name: '玉石吊坠', category: '工艺品', turnoverRate: 8.5, turnoverDays: 43, annualSales: 4321 },
{ name: '银饰', category: '工艺品', turnoverRate: 7.2, turnoverDays: 51, annualSales: 3210 }
],
// 低周转商品
lowTurnoverProducts: [
{ name: '大型玉石摆件', category: '工艺品', stock: 23, turnoverDays: 365, turnoverRate: 1.0 },
{ name: '手工织锦', category: '纺织品', stock: 45, turnoverDays: 280, turnoverRate: 1.3 },
{ name: '古董茶具', category: '工艺品', stock: 12, turnoverDays: 240, turnoverRate: 1.5 },
{ name: '民族乐器', category: '工艺品', stock: 8, turnoverDays: 180, turnoverRate: 2.0 },
{ name: '限量版银饰', category: '工艺品', stock: 15, turnoverDays: 150, turnoverRate: 2.4 },
{ name: '手工刺绣画', category: '纺织品', stock: 34, turnoverDays: 120, turnoverRate: 3.0 }
],
// 优化建议
optimizationSuggestions: [
{
icon: '📊',
title: '优化采购策略',
description: '根据历史销售数据调整采购量,减少滞销商品的库存,增加热销商品的供应'
},
{
icon: '💰',
title: '实施促销活动',
description: '对低周转商品开展针对性促销活动,如折扣、捆绑销售等,加速库存周转'
},
{
icon: '🔄',
title: '建立快速反应机制',
description: '实时监控销售数据,及时调整商品结构,快速响应市场需求变化'
},
{
icon: '📱',
title: '加强营销推广',
description: '通过线上线下多渠道营销,提升产品知名度,刺激销售增长'
}
]
}
},
computed: {
// 饼图数据
pieChartData() {
return {
series: [{
data: this.turnoverDistributionData.map(item => ({
name: item.name,
value: item.value
}))
}]
}
},
// 饼图配置
pieChartOpts() {
return {
color: this.turnoverDistributionData.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.turnoverTrendData.categories,
series: [
{
name: '周转率',
data: this.turnoverTrendData.turnoverRateData
},
{
name: '周转天数',
data: this.turnoverTrendData.turnoverDaysData.map(v => v / 2) // 缩放显示
}
]
}
},
// 折线图配置
lineChartOpts() {
return {
color: ['#576EFF', '#52C41A'],
padding: [15, 15, 15, 15],
dataLabel: false,
legend: {
show: false
},
xAxis: {
disableGrid: true,
itemCount: 6,
scrollShow: true,
scrollAlign: 'right'
},
yAxis: {
gridType: 'dash',
dashLength: 2,
data: [
{
min: 0,
title: '周转率'
},
{
min: 0,
position: 'right',
title: '周转天数(÷2)'
}
]
},
extra: {
line: {
type: 'curve',
width: 2,
activeType: 'hollow'
}
}
}
},
// 柱状图数据
barChartData() {
return {
categories: this.categoryTurnoverData.map(item => item.category),
series: [
{
name: '周转率',
data: this.categoryTurnoverData.map(item => item.turnoverRate)
},
{
name: '周转天数',
data: this.categoryTurnoverData.map(item => item.turnoverDays / 5) // 缩放显示
}
]
}
},
// 柱状图配置
barChartOpts() {
return {
color: ['#576EFF', '#52C41A'],
padding: [15, 15, 15, 15],
dataLabel: false,
enableScroll: true,
xAxis: {
itemCount: 4,
scrollShow: true,
scrollAlign: 'right',
scrollColor: '#576EFF',
scrollBackgroundColor: 'rgba(87, 110, 255, 0.1)',
scrollWidth: 4,
scrollHeight: 8
},
yAxis: {
gridType: 'dash',
dashLength: 2,
data: [
{
min: 0,
title: '周转率'
},
{
min: 0,
position: 'right',
title: '周转天数(÷5)'
}
]
},
extra: {
column: {
type: 'group',
width: 20,
activeBgColor: '#000000',
activeBgOpacity: 0.08,
linearType: 'custom',
barBorderCircle: true
}
}
}
}
},
methods: {
formatNumber(num) {
return num.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
},
getWarningLevel(turnoverDays) {
if (turnoverDays >= 300) return 'severe'
if (turnoverDays >= 180) return 'high'
if (turnoverDays >= 120) return 'medium'
return 'low'
}
}
}
</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);
.inventory-turnover-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;
&.turnover {
background: linear-gradient(135deg, #52c41a, #73d13d);
&::after { content: '🔄'; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 24rpx; }
}
&.days {
background: linear-gradient(135deg, #1890ff, #40a9ff);
&::after { content: '📅'; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 24rpx; }
}
&.efficiency {
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%;
&.rate {
background: #576EFF;
}
&.days {
background: #52C41A;
}
}
.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;
}
}
}
}
.warning-list {
.warning-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);
}
.warning-icon {
font-size: 32rpx;
margin-right: 16rpx;
}
.warning-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;
}
}
.warning-data {
text-align: right;
margin-right: 16rpx;
.turnover-days {
font-size: 24rpx;
color: @error-color;
font-weight: 600;
display: block;
margin-bottom: 4rpx;
}
.turnover-rate {
font-size: 22rpx;
color: @text-secondary;
display: block;
}
}
.warning-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>