ccy_DIB/pages/DigitalIntelligenceDashboard/components/InventoryStructureAnalysis.vue
ylj20011123 188282c4f0 update
2025-10-24 11:50:00 +08:00

629 lines
15 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-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>
</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">库存价值</text>
</view>
</view>
</view>
</view>
<!-- 库存详细数据表 -->
<view class="chart-card">
<view class="chart-header">
<text class="chart-title">库存详细数据</text>
</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">
{{ item.name }}
</view>
<text class="category-percentage">{{ item.value }}%</text>
</view>
<view class="category-body">
<view class="category-metrics">
<view class="metric-item">
<text class="metric-label">库存数量</text>
<text class="metric-value"></text>
</view>
<view class="metric-divider"></view>
<view class="metric-item">
<text class="metric-label">库存价值</text>
<text class="metric-value">¥</text>
</view>
<view class="metric-divider"></view>
<view class="metric-item">
<text class="metric-label">平均单价</text>
<text class="metric-value">¥</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'
import request from "@/util/index.js";
export default {
components: {
QiunDataCharts
},
data() {
return {
analysisPeriod: '2024年10月',
totalCategories: 8,
totalQuantity: 485672,
totalValue: 3847.56,
// 商品类别数据
categoryData: []
}
},
computed: {
// 饼图数据
pieChartData() {
return {
series: [{
data: this.categoryData.map(item => ({
name: item.name,
value: Number(item.value)
}))
}]
}
},
// 饼图配置
pieChartOpts() {
return {
padding: [5, 5, 5, 5],
dataLabel: true,
legend: {
show: true
},
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
}
}
}
}
},
onReady() {
// 获取整个组件的数据
this.handleGetAllData()
},
methods: {
// 获取整个组件的数据
handleGetAllData() {
// 获取商品类别数量占比
this.handleGetShopTypeNumberRate()
},
// 获取商品类别数量占比
async handleGetShopTypeNumberRate() {
let req = {
ProvinceCode: "530000",
type: "encryption"
}
const data = await request.$webPost(
"CommercialApi/BaseInfo/GetBrandStructureAnalysis",
req
);
let list = data.Result_Data.List
console.log('商品类别数量占比', list);
this.categoryData = list
},
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>