680 lines
17 KiB
Vue
680 lines
17 KiB
Vue
<template>
|
||
<view class="inventory-structure-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 categories"></view>
|
||
<text class="metric-title">商品类别</text>
|
||
<view class="metric-value-container">
|
||
<text class="metric-value">{{ totalCategories }}</text>
|
||
<text class="metric-unit">类</text>
|
||
</view>
|
||
</view>
|
||
<view class="metric-card">
|
||
<view class="metric-icon quantity"></view>
|
||
<text class="metric-title">库存总量</text>
|
||
<view class="metric-value-container">
|
||
<text class="metric-value">{{ formatNumber(totalQuantity) }}</text>
|
||
<text class="metric-unit">件</text>
|
||
</view>
|
||
</view>
|
||
<view class="metric-card">
|
||
<view class="metric-icon value"></view>
|
||
<text class="metric-title">库存总值</text>
|
||
<view class="metric-value-container">
|
||
<text class="metric-value">¥{{ formatMoney(totalValue) }}</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="inventoryStructurePieChart"
|
||
/>
|
||
<view class="pie-legend">
|
||
<view class="legend-item" v-for="(item, index) in categoryData" :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">各类别库存数量与价值对比</text>
|
||
</view>
|
||
<view class="chart-content">
|
||
<view class="bar-chart-container">
|
||
<QiunDataCharts
|
||
type="column"
|
||
:opts="barChartOpts"
|
||
:chartData="barChartData"
|
||
:canvas2d="true"
|
||
:inScrollView="true"
|
||
canvasId="inventoryStructureBarChart"
|
||
/>
|
||
</view>
|
||
<view class="chart-legend">
|
||
<view class="legend-item">
|
||
<view class="legend-dot quantity"></view>
|
||
<text class="legend-text">库存数量</text>
|
||
</view>
|
||
<view class="legend-item">
|
||
<view class="legend-dot value"></view>
|
||
<text class="legend-text">库存价值(÷1000)</text>
|
||
</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="category-cards">
|
||
<view class="category-card" v-for="(item, index) in categoryData" :key="index">
|
||
<view class="category-header">
|
||
<view class="category-type-tag" :style="{ backgroundColor: item.color }">
|
||
{{ item.name }}
|
||
</view>
|
||
<text class="category-percentage">{{ item.percentage }}%</text>
|
||
</view>
|
||
<view class="category-body">
|
||
<view class="category-metrics">
|
||
<view class="metric-item">
|
||
<text class="metric-label">库存数量</text>
|
||
<text class="metric-value">{{ formatNumber(item.quantity) }}件</text>
|
||
</view>
|
||
<view class="metric-divider"></view>
|
||
<view class="metric-item">
|
||
<text class="metric-label">库存价值</text>
|
||
<text class="metric-value">¥{{ formatMoney(item.value) }}万</text>
|
||
</view>
|
||
<view class="metric-divider"></view>
|
||
<view class="metric-item">
|
||
<text class="metric-label">平均单价</text>
|
||
<text class="metric-value">¥{{ formatMoney(item.avgPrice) }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="category-trend">
|
||
<text class="trend-label">环比变化</text>
|
||
<text class="trend-value" :class="{ 'positive': item.trend > 0, 'negative': item.trend < 0 }">
|
||
{{ item.trend > 0 ? '+' : '' }}{{ item.trend }}%
|
||
</text>
|
||
</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月',
|
||
totalCategories: 8,
|
||
totalQuantity: 485672,
|
||
totalValue: 3847.56,
|
||
|
||
// 商品类别数据
|
||
categoryData: [
|
||
{
|
||
name: '保健品',
|
||
quantity: 156234,
|
||
value: 1256.78,
|
||
percentage: 32.2,
|
||
color: '#576EFF',
|
||
avgPrice: 80.45,
|
||
trend: 5.2
|
||
},
|
||
{
|
||
name: '茶叶',
|
||
quantity: 128456,
|
||
value: 987.34,
|
||
percentage: 26.5,
|
||
color: '#52C41A',
|
||
avgPrice: 76.84,
|
||
trend: -2.1
|
||
},
|
||
{
|
||
name: '食品',
|
||
quantity: 98567,
|
||
value: 654.23,
|
||
percentage: 20.3,
|
||
color: '#FAAD14',
|
||
avgPrice: 66.38,
|
||
trend: 8.7
|
||
},
|
||
{
|
||
name: '饮品',
|
||
quantity: 56789,
|
||
value: 456.78,
|
||
percentage: 11.7,
|
||
color: '#FF7875',
|
||
avgPrice: 80.46,
|
||
trend: 3.4
|
||
},
|
||
{
|
||
name: '工艺品',
|
||
quantity: 23456,
|
||
value: 345.67,
|
||
percentage: 4.8,
|
||
color: '#B37FEB',
|
||
avgPrice: 147.43,
|
||
trend: -1.5
|
||
},
|
||
{
|
||
name: '纺织品',
|
||
quantity: 12345,
|
||
value: 98.76,
|
||
percentage: 2.5,
|
||
color: '#13C2C2',
|
||
avgPrice: 80.00,
|
||
trend: 12.3
|
||
},
|
||
{
|
||
name: '化妆品',
|
||
quantity: 8765,
|
||
value: 34.56,
|
||
percentage: 1.8,
|
||
color: '#FF85C0',
|
||
avgPrice: 39.45,
|
||
trend: 6.8
|
||
},
|
||
{
|
||
name: '其他',
|
||
quantity: 560,
|
||
value: 13.44,
|
||
percentage: 0.2,
|
||
color: '#8C8C8C',
|
||
avgPrice: 240.00,
|
||
trend: -8.9
|
||
}
|
||
]
|
||
}
|
||
},
|
||
|
||
computed: {
|
||
// 饼图数据
|
||
pieChartData() {
|
||
return {
|
||
series: [{
|
||
data: this.categoryData.map(item => ({
|
||
name: item.name,
|
||
value: item.quantity
|
||
}))
|
||
}]
|
||
}
|
||
},
|
||
|
||
// 饼图配置
|
||
pieChartOpts() {
|
||
return {
|
||
color: this.categoryData.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'
|
||
}
|
||
}
|
||
}
|
||
},
|
||
|
||
// 柱状图数据
|
||
barChartData() {
|
||
return {
|
||
categories: this.categoryData.map(item => item.name),
|
||
series: [
|
||
{
|
||
name: '库存数量',
|
||
data: this.categoryData.map(item => item.quantity / 1000) // 转换为千件
|
||
},
|
||
{
|
||
name: '库存价值',
|
||
data: this.categoryData.map(item => item.value) // 万元
|
||
}
|
||
]
|
||
}
|
||
},
|
||
|
||
// 柱状图配置
|
||
barChartOpts() {
|
||
return {
|
||
color: ['#576EFF', '#52C41A'],
|
||
padding: [15, 15, 15, 15],
|
||
dataLabel: false,
|
||
enableScroll: false,
|
||
xAxis: {
|
||
disableGrid: false,
|
||
gridType: 'dash'
|
||
},
|
||
yAxis: {
|
||
gridType: 'dash',
|
||
dashLength: 2,
|
||
data: [
|
||
{
|
||
min: 0,
|
||
title: '数量(千件)'
|
||
},
|
||
{
|
||
min: 0,
|
||
position: 'right',
|
||
title: '价值(万元)'
|
||
}
|
||
]
|
||
},
|
||
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, ",")
|
||
},
|
||
|
||
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);
|
||
|
||
.inventory-structure-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;
|
||
|
||
&.categories {
|
||
background: linear-gradient(135deg, #1890ff, #40a9ff);
|
||
&::after { content: '📊'; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 24rpx; }
|
||
}
|
||
|
||
&.quantity {
|
||
background: linear-gradient(135deg, #52c41a, #73d13d);
|
||
&::after { content: '📦'; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 24rpx; }
|
||
}
|
||
|
||
&.value {
|
||
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: 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 {
|
||
.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;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.bar-chart-container {
|
||
width: 100%;
|
||
}
|
||
|
||
.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%;
|
||
|
||
&.quantity {
|
||
background: #576EFF;
|
||
}
|
||
|
||
&.value {
|
||
background: #52C41A;
|
||
}
|
||
}
|
||
|
||
.legend-text {
|
||
font-size: 22rpx;
|
||
color: @text-secondary;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.category-cards {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 16rpx;
|
||
|
||
.category-card {
|
||
background: #fafafa;
|
||
border-radius: 12rpx;
|
||
padding: 20rpx;
|
||
border: 1rpx solid #f0f0f0;
|
||
|
||
.category-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 16rpx;
|
||
|
||
.category-type-tag {
|
||
padding: 4rpx 12rpx;
|
||
border-radius: 12rpx;
|
||
font-size: 20rpx;
|
||
color: white;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.category-percentage {
|
||
font-size: 28rpx;
|
||
color: @primary-color;
|
||
font-weight: 600;
|
||
}
|
||
}
|
||
|
||
.category-body {
|
||
.category-metrics {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 12rpx;
|
||
|
||
.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;
|
||
}
|
||
}
|
||
|
||
.category-trend {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding-top: 12rpx;
|
||
border-top: 1rpx solid #e0e0e0;
|
||
|
||
.trend-label {
|
||
font-size: 20rpx;
|
||
color: @text-secondary;
|
||
}
|
||
|
||
.trend-value {
|
||
font-size: 22rpx;
|
||
font-weight: 600;
|
||
|
||
&.positive {
|
||
color: @success-color;
|
||
}
|
||
|
||
&.negative {
|
||
color: @error-color;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style> |