opt:富佳项目看板优化
This commit is contained in:
@@ -40,7 +40,7 @@ export const constantRouterMap = [
|
||||
children: [
|
||||
{
|
||||
path: 'dashboard',
|
||||
component: (resolve) => require(['@/views/system/redis/index'], resolve),
|
||||
component: (resolve) => require(['@/views/dashboard/Dashboard2'], resolve),
|
||||
name: 'Dashboard',
|
||||
meta: { title: i18n.t('common.home'), icon: 'index', affix: true, noCache: true }
|
||||
// meta: { title: '首页', icon: 'index', affix: true, noCache: true }
|
||||
|
||||
712
nladmin-ui/src/views/dashboard/Dashboard.vue
Normal file
712
nladmin-ui/src/views/dashboard/Dashboard.vue
Normal file
@@ -0,0 +1,712 @@
|
||||
<template>
|
||||
<div class="agv-dashboard">
|
||||
<!-- 顶部导航栏 -->
|
||||
<el-header class="header">
|
||||
<div class="header-left">
|
||||
<i class="el-icon-truck"></i>
|
||||
<span class="header-title">库位状态看板</span>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-refresh"
|
||||
@click="refreshData"
|
||||
:loading="isRefreshing"
|
||||
>
|
||||
手动刷新
|
||||
</el-button>
|
||||
<el-button
|
||||
type="text"
|
||||
icon="el-icon-pause"
|
||||
@click="toggleRefresh"
|
||||
class="refresh-toggle"
|
||||
>
|
||||
{{ isAutoRefresh ? '暂停刷新' : '开启刷新' }}
|
||||
</el-button>
|
||||
<span class="last-update">
|
||||
上次更新: {{ lastUpdateTime }}
|
||||
</span>
|
||||
</div>
|
||||
</el-header>
|
||||
|
||||
<!-- 主要内容区域 -->
|
||||
<el-main class="main-content">
|
||||
<!-- 数据概览卡片 -->
|
||||
<div class="overview-cards">
|
||||
<el-card class="stat-card" shadow="hover">
|
||||
<div class="stat-title">库位总数</div>
|
||||
<div class="stat-value">{{ overview.totalLocations }}</div>
|
||||
</el-card>
|
||||
|
||||
<el-card class="stat-card" shadow="hover">
|
||||
<div class="stat-title">货架数量</div>
|
||||
<div class="stat-value">{{ overview.totalShelves }}</div>
|
||||
</el-card>
|
||||
|
||||
<el-card class="stat-card" shadow="hover">
|
||||
<div class="stat-title">当前库存</div>
|
||||
<div class="stat-value">{{ overview.currentInventory }}</div>
|
||||
</el-card>
|
||||
|
||||
<el-card class="stat-card" shadow="hover">
|
||||
<div class="stat-title">剩余库位</div>
|
||||
<div class="stat-value">{{ overview.availableLocations }}</div>
|
||||
</el-card>
|
||||
</div>
|
||||
<div class="overview-cards">
|
||||
<el-card class="stat-card" shadow="hover">
|
||||
<div class="stat-title">入库中库位</div>
|
||||
<div class="stat-value">{{ overview.inboundQuantity }}</div>
|
||||
</el-card>
|
||||
|
||||
<el-card class="stat-card" shadow="hover">
|
||||
<div class="stat-title">出库中库位</div>
|
||||
<div class="stat-value">{{ overview.outboundQuantity }}</div>
|
||||
</el-card>
|
||||
|
||||
<el-card class="stat-card" shadow="hover">
|
||||
<div class="stat-title">移入中库位</div>
|
||||
<div class="stat-value">{{ overview.moveToQuantity }}</div>
|
||||
</el-card>
|
||||
|
||||
<el-card class="stat-card" shadow="hover">
|
||||
<div class="stat-title">移出中库位</div>
|
||||
<div class="stat-value">{{ overview.moveFromQuantity }}</div>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<!-- 楼层分布图表 -->
|
||||
<div class="floor-charts">
|
||||
<el-card class="chart-card" shadow="hover">
|
||||
<div class="chart-header">
|
||||
<span class="chart-title">各楼层库位分布</span>
|
||||
<el-select
|
||||
v-model="selectedFloor"
|
||||
class="floor-select"
|
||||
@change="handleFloorChange"
|
||||
>
|
||||
<el-option label="全部楼层" value="all"></el-option>
|
||||
<el-option label="一层" value="1"></el-option>
|
||||
<el-option label="二层" value="2"></el-option>
|
||||
<el-option label="三层" value="3"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="chart-container">
|
||||
<div id="floorPieChart" class="chart"></div>
|
||||
<div id="floorBarChart" class="chart"></div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</el-main>
|
||||
|
||||
<!-- 底部信息 -->
|
||||
<el-footer class="footer">
|
||||
<div class="footer-content">
|
||||
<span class="refresh-interval">
|
||||
自动刷新间隔: {{ refreshInterval }}秒
|
||||
</span>
|
||||
</div>
|
||||
</el-footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
import dashboard, { getDashboardData } from '@/views/dashboard/mixins/dashboard'
|
||||
|
||||
export default {
|
||||
name: 'AGVDashboard',
|
||||
data() {
|
||||
return {
|
||||
storCode : 'FJ',
|
||||
// 概览数据
|
||||
overview: {
|
||||
totalLocations: 0,
|
||||
totalShelves: 0,
|
||||
currentInventory: 0,
|
||||
availableLocations: 0,
|
||||
inboundQuantity: 0,
|
||||
outboundQuantity: 0,
|
||||
moveToQuantity: 0,
|
||||
moveFromQuantity: 0
|
||||
},
|
||||
|
||||
// 库位数据
|
||||
locations: [],
|
||||
filteredLocations: [],
|
||||
searchCode: '',
|
||||
|
||||
// 分页信息
|
||||
currentPage: 1,
|
||||
pageSize: 20,
|
||||
|
||||
// 图表相关
|
||||
selectedFloor: 'all',
|
||||
floorPieChart: null,
|
||||
floorBarChart: null,
|
||||
|
||||
// 刷新相关
|
||||
isAutoRefresh: true,
|
||||
isRefreshing: false,
|
||||
refreshInterval: 30,
|
||||
lastUpdateTime: '',
|
||||
refreshTimer: null,
|
||||
|
||||
// 详情弹窗
|
||||
isDetailDialogOpen: false,
|
||||
selectedLocation: {}
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
// 初始化数据
|
||||
this.fetchData()
|
||||
// 启动定时刷新
|
||||
this.startRefreshTimer()
|
||||
},
|
||||
|
||||
destroyed() {
|
||||
// 清除定时器
|
||||
if (this.refreshTimer) {
|
||||
clearInterval(this.refreshTimer)
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
// 监听搜索条件变化
|
||||
searchCode(val) {
|
||||
this.filterLocations()
|
||||
},
|
||||
|
||||
// 监听选中楼层变化
|
||||
selectedFloor(val) {
|
||||
this.updateCharts()
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 获取数据
|
||||
fetchData() {
|
||||
this.isRefreshing = true
|
||||
|
||||
// 模拟API请求
|
||||
setTimeout(() => {
|
||||
// 查询数据
|
||||
this.generateMockData()
|
||||
// 停止刷新状态
|
||||
this.isRefreshing = false
|
||||
}, 1000)
|
||||
},
|
||||
|
||||
// 生成模拟数据
|
||||
generateMockData() {
|
||||
const param = {}
|
||||
param.storCode = this.storCode
|
||||
// 查询概览数据
|
||||
dashboard.getDashboardData(param).then(res => {
|
||||
this.overview = res.overview
|
||||
this.locations = res.locations
|
||||
// 更新图表
|
||||
this.locations.forEach(item => {
|
||||
item.floor = parseInt(item.floor)
|
||||
})
|
||||
this.updateCharts()
|
||||
// 更新最后更新时间
|
||||
this.lastUpdateTime = new Date().toLocaleString()
|
||||
// 过滤库位数据
|
||||
this.filterLocations()
|
||||
})
|
||||
},
|
||||
|
||||
// 过滤库位数据
|
||||
filterLocations() {
|
||||
if (this.searchCode) {
|
||||
this.filteredLocations = this.locations.filter(location =>
|
||||
location.structCode.includes(this.searchCode) ||
|
||||
location.shelfCode.includes(this.searchCode)
|
||||
)
|
||||
} else {
|
||||
this.filteredLocations = [...this.locations]
|
||||
}
|
||||
},
|
||||
|
||||
// 更新图表
|
||||
updateCharts() {
|
||||
// 初始化饼图
|
||||
if (!this.floorPieChart) {
|
||||
this.floorPieChart = echarts.init(document.getElementById('floorPieChart'))
|
||||
}
|
||||
|
||||
// 初始化柱状图
|
||||
if (!this.floorBarChart) {
|
||||
this.floorBarChart = echarts.init(document.getElementById('floorBarChart'))
|
||||
}
|
||||
|
||||
// 准备图表数据
|
||||
let pieData, barData
|
||||
|
||||
if (this.selectedFloor === 'all') {
|
||||
// 全部楼层数据
|
||||
pieData = [
|
||||
{ name: '一层', value: this.calculateFloorCount(1) },
|
||||
{ name: '二层', value: this.calculateFloorCount(2) },
|
||||
{ name: '三层', value: this.calculateFloorCount(3) }
|
||||
]
|
||||
|
||||
|
||||
barData = this.prepareBarData([1, 2, 3])
|
||||
} else {
|
||||
// 特定楼层数据
|
||||
const floor = parseInt(this.selectedFloor)
|
||||
pieData = this.prepareFloorStatusData(floor)
|
||||
barData = this.prepareBarData([floor])
|
||||
}
|
||||
|
||||
// 更新饼图
|
||||
this.floorPieChart.setOption({
|
||||
title: {
|
||||
text: this.selectedFloor === 'all' ? '各楼层库位总数' : `${this.selectedFloor}层库位状态分布`,
|
||||
left: 'center'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b}: {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left'
|
||||
},
|
||||
// 添加更鲜艳的配色方案
|
||||
color: ['#1890ff', '#52c41a', '#ff7875', '#faad14', '#722ed1', '#13c2c2'],
|
||||
series: [
|
||||
{
|
||||
name: '库位数量',
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'],
|
||||
avoidLabelOverlap: false,
|
||||
itemStyle: {
|
||||
borderRadius: 10,
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2
|
||||
},
|
||||
label: {
|
||||
show: false,
|
||||
position: 'center'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
data: pieData
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
// 更新柱状图
|
||||
this.floorBarChart.setOption({
|
||||
title: {
|
||||
text: '各楼层库位状态统计',
|
||||
left: 'center'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: ['空闲', '占用', '入库中', '出库中', '异常'],
|
||||
bottom: 0
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '15%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: barData.categories
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
// 添加更鲜艳的配色方案
|
||||
color: ['#1890ff', '#52c41a', '#ff7875', '#faad14', '#722ed1', '#13c2c2', '#fa8c16'],
|
||||
|
||||
series: barData.series
|
||||
})
|
||||
},
|
||||
|
||||
// 计算特定楼层的库位总数
|
||||
calculateFloorCount(floor) {
|
||||
return this.locations.filter(location => location.floor === floor).length
|
||||
},
|
||||
|
||||
// 准备特定楼层的状态数据
|
||||
prepareFloorStatusData(floor) {
|
||||
const statuses = ['空闲', '占用', '入库中', '出库中', '移入中', '移出中','其他占用']
|
||||
const floorLocations = this.locations.filter(location => location.floor === floor)
|
||||
return statuses.map(status => {
|
||||
return {
|
||||
name: status,
|
||||
value: floorLocations.filter(location => location.status === status).length
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 准备柱状图数据
|
||||
prepareBarData(floors) {
|
||||
const statuses = ['空闲', '占用', '入库中', '出库中', '移入中', '移出中', '其他占用']
|
||||
const categories = floors.map(floor => `${floor}层`)
|
||||
|
||||
const series = statuses.map(status => {
|
||||
return {
|
||||
name: status,
|
||||
type: 'bar',
|
||||
stack: 'total',
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
data: floors.map(floor => {
|
||||
return this.locations.filter(
|
||||
location => location.floor === floor && location.status === status
|
||||
).length
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
categories: categories,
|
||||
series: series
|
||||
}
|
||||
},
|
||||
|
||||
// 手动刷新数据
|
||||
refreshData() {
|
||||
this.fetchData()
|
||||
},
|
||||
|
||||
// 切换自动刷新状态
|
||||
toggleRefresh() {
|
||||
this.isAutoRefresh = !this.isAutoRefresh
|
||||
|
||||
if (this.isAutoRefresh) {
|
||||
this.startRefreshTimer()
|
||||
} else {
|
||||
this.clearRefreshTimer()
|
||||
}
|
||||
},
|
||||
|
||||
// 启动刷新定时器
|
||||
startRefreshTimer() {
|
||||
this.clearRefreshTimer()
|
||||
this.refreshTimer = setInterval(() => {
|
||||
this.fetchData()
|
||||
}, this.refreshInterval * 1000)
|
||||
},
|
||||
|
||||
// 清除刷新定时器
|
||||
clearRefreshTimer() {
|
||||
if (this.refreshTimer) {
|
||||
clearInterval(this.refreshTimer)
|
||||
this.refreshTimer = null
|
||||
}
|
||||
},
|
||||
|
||||
// 获取楼层标签类型
|
||||
getFloorTagType(floor) {
|
||||
const types = ['', 'primary', 'success', 'warning']
|
||||
return types[floor] || 'info'
|
||||
},
|
||||
|
||||
// 获取状态标签类型
|
||||
getStatusTagType(status) {
|
||||
switch (status) {
|
||||
case '空闲':
|
||||
return 'success'
|
||||
case '占用':
|
||||
return 'info'
|
||||
case '入库中':
|
||||
return 'primary'
|
||||
case '出库中':
|
||||
return 'warning'
|
||||
case '异常':
|
||||
return 'danger'
|
||||
default:
|
||||
return 'info'
|
||||
}
|
||||
},
|
||||
|
||||
// 处理楼层选择变化
|
||||
handleFloorChange() {
|
||||
this.updateCharts()
|
||||
},
|
||||
|
||||
// 处理分页大小变化
|
||||
handleSizeChange(val) {
|
||||
this.pageSize = val
|
||||
this.currentPage = 1
|
||||
},
|
||||
|
||||
// 处理当前页变化
|
||||
handleCurrentChange(val) {
|
||||
this.currentPage = val
|
||||
},
|
||||
|
||||
// 处理行点击事件
|
||||
handleRowClick(row) {
|
||||
this.selectedLocation = row
|
||||
this.isDetailDialogOpen = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 全局样式 */
|
||||
.agv-dashboard {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
/* 头部样式 */
|
||||
.header {
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 20px;
|
||||
height: 64px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-left i {
|
||||
font-size: 24px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.last-update {
|
||||
margin-left: 20px;
|
||||
font-size: 14px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.refresh-toggle {
|
||||
color: #fff;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
/* 主要内容区域 */
|
||||
.main-content {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/* 概览卡片样式 */
|
||||
.overview-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.stat-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.stat-title {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
color: #1890ff;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.stat-change {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.increase {
|
||||
color: #52c41a;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.decrease {
|
||||
color: #ff4d4f;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.compared {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 图表区域样式 */
|
||||
.floor-charts {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.chart-card {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.chart-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.floor-select {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.chart {
|
||||
height: 350px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 表格区域样式 */
|
||||
.location-table {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.table-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.table-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin-top: 15px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
/* 详情弹窗样式 */
|
||||
.detail-info {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.detail-row {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
width: 100px;
|
||||
text-align: right;
|
||||
padding-right: 10px;
|
||||
font-weight: bold;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
flex: 1;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* 底部样式 */
|
||||
.footer {
|
||||
background-color: #fff;
|
||||
text-align: center;
|
||||
padding: 10px 0;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
border-top: 1px solid #e8e8e8;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.refresh-interval {
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
/* 响应式样式 */
|
||||
@media (max-width: 768px) {
|
||||
.overview-cards {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.detail-info {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
789
nladmin-ui/src/views/dashboard/Dashboard2.vue
Normal file
789
nladmin-ui/src/views/dashboard/Dashboard2.vue
Normal file
@@ -0,0 +1,789 @@
|
||||
<template>
|
||||
<div class="transport-dashboard">
|
||||
<!-- 顶部导航栏 -->
|
||||
<el-header class="header">
|
||||
<div class="header-left">
|
||||
<i class="el-icon-s-tools"></i>
|
||||
<span class="header-title">任务完成情况看板</span>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-refresh"
|
||||
@click="refreshData"
|
||||
:loading="isRefreshing"
|
||||
>
|
||||
手动刷新
|
||||
</el-button>
|
||||
<el-button
|
||||
type="text"
|
||||
icon="el-icon-pause"
|
||||
@click="toggleRefresh"
|
||||
class="refresh-toggle"
|
||||
>
|
||||
{{ isAutoRefresh ? '暂停刷新' : '开启刷新' }}
|
||||
</el-button>
|
||||
<span class="last-update">
|
||||
上次更新: {{ lastUpdateTime }}
|
||||
</span>
|
||||
</div>
|
||||
</el-header>
|
||||
|
||||
<!-- 主要内容区域 -->
|
||||
<el-main class="main-content">
|
||||
<!-- 数据概览卡片 - 第一行 -->
|
||||
<div class="overview-cards">
|
||||
<el-card class="stat-card" shadow="hover">
|
||||
<div class="stat-title">总任务数</div>
|
||||
<div class="stat-value">{{ stats.totalTasks }}</div>
|
||||
</el-card>
|
||||
|
||||
<el-card class="stat-card" shadow="hover">
|
||||
<div class="stat-title">今日任务数</div>
|
||||
<div class="stat-value">{{ stats.todayTasks }}</div>
|
||||
<div class="stat-change">
|
||||
<span :class="weeklyComparison.growthRate >= 0 ? 'positive' : 'negative'">
|
||||
<i :class="weeklyComparison.growthRate >= 0 ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"></i>
|
||||
{{ stats.dayOnDay }}%
|
||||
</span>
|
||||
<span class="compared">较昨日</span>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-card class="stat-card" shadow="hover">
|
||||
<div class="stat-title">本月任务数</div>
|
||||
<div class="stat-value">{{ stats.monthTasks }}</div>
|
||||
</el-card>
|
||||
|
||||
<el-card class="stat-card" shadow="hover">
|
||||
<div class="stat-title">库位数量</div>
|
||||
<div class="stat-value">{{ stats.locationCount }}</div>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<!-- 数据概览卡片 - 第二行 -->
|
||||
<div class="overview-cards">
|
||||
<!-- <el-card class="stat-card" shadow="hover">
|
||||
<div class="stat-title">月均任务数</div>
|
||||
<div class="stat-value">{{ stats.monthlyAvg }}</div>
|
||||
</el-card>-->
|
||||
|
||||
<el-card class="stat-card" shadow="hover">
|
||||
<div class="stat-title">在库数量</div>
|
||||
<div class="stat-value">{{ stats.inventoryCount }}</div>
|
||||
</el-card>
|
||||
<el-card class="stat-card" shadow="hover">
|
||||
<div class="stat-title">入库数量</div>
|
||||
<div class="stat-value">{{ stats.inboundCount }}</div>
|
||||
</el-card>
|
||||
<el-card class="stat-card" shadow="hover">
|
||||
<div class="stat-title">出库数量</div>
|
||||
<div class="stat-value">{{ stats.outboundCount }}</div>
|
||||
</el-card>
|
||||
|
||||
<el-card class="stat-card" shadow="hover">
|
||||
<div class="stat-title">运转数量</div>
|
||||
<div class="stat-value">{{ stats.operationCount }}</div>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<!-- 任务统计图表 - 合并为一个组件 -->
|
||||
<div class="task-charts">
|
||||
<el-card class="chart-card" shadow="hover">
|
||||
<div class="chart-header">
|
||||
<span class="chart-title">{{ viewType === 'daily' ? '每日任务完成情况' : '周任务数据对比' }}</span>
|
||||
<el-select
|
||||
v-model="viewType"
|
||||
class="view-type-select"
|
||||
@change="handleViewTypeChange"
|
||||
>
|
||||
<el-option label="每日任务完成情况" value="daily"></el-option>
|
||||
<el-option label="周任务数据对比" value="weekly"></el-option>
|
||||
</el-select>
|
||||
<div class="growth-rate" v-if="viewType === 'weekly' && weeklyComparison.growthRate !== 0">
|
||||
<span :class="weeklyComparison.growthRate >= 0 ? 'positive' : 'negative'">
|
||||
<i :class="weeklyComparison.growthRate >= 0 ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"></i>
|
||||
{{ weeklyComparison.growthRate }}%
|
||||
</span>
|
||||
<span class="compared">较上周</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart-container">
|
||||
<div id="taskChart" class="chart"></div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</el-main>
|
||||
|
||||
<!-- 底部信息 -->
|
||||
<el-footer class="footer">
|
||||
<div class="footer-content">
|
||||
<span class="refresh-interval">
|
||||
自动刷新间隔: {{ refreshInterval }}秒
|
||||
</span>
|
||||
</div>
|
||||
</el-footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
import dashboard2 from './mixins/dashboard2.js'
|
||||
import axios from 'axios'
|
||||
|
||||
export default {
|
||||
mixins: [dashboard2],
|
||||
data() {
|
||||
return {
|
||||
isRefreshing: false,
|
||||
isAutoRefresh: true,
|
||||
lastUpdateTime: '2025-11-19 15:30:22',
|
||||
refreshInterval: 30,
|
||||
searchKeyword: '',
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
// 视图类型:'daily' - 每日任务完成情况,'weekly' - 周任务数据对比
|
||||
viewType: 'daily',
|
||||
// 近三周数据
|
||||
threeWeeksData: {
|
||||
weeks: [], // 周标签
|
||||
inboundData: [], // 入库任务数据
|
||||
outboundData: [], // 出库任务数据
|
||||
otherData: [] // 其他搬运数据
|
||||
},
|
||||
stats: {
|
||||
totalTasks: 0,
|
||||
todayTasks: 0,
|
||||
dayOnDay: 0,
|
||||
monthTasks: 0,
|
||||
monthlyAvg: 0,
|
||||
locationCount: 0,
|
||||
inventoryCount: 0,
|
||||
outboundCount: 0,
|
||||
operationCount: 0
|
||||
},
|
||||
// 每日详细数据
|
||||
historyList: [],
|
||||
// 周对比数据
|
||||
weeklyComparison: {
|
||||
thisWeek: {
|
||||
totalTasks: 0,
|
||||
completedTasks: 0,
|
||||
completionRate: 0
|
||||
},
|
||||
lastWeek: {
|
||||
totalTasks: 0,
|
||||
completedTasks: 0,
|
||||
completionRate: 0
|
||||
},
|
||||
growthRate: 0
|
||||
},
|
||||
// 定时刷新的定时器
|
||||
refreshTimer: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filteredHistory() {
|
||||
return this.historyList.filter(item =>
|
||||
item.date.includes(this.searchKeyword) ||
|
||||
item.inbound.toString().includes(this.searchKeyword) ||
|
||||
item.outbound.toString().includes(this.searchKeyword) ||
|
||||
item.other.toString().includes(this.searchKeyword)
|
||||
)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 初始化时获取数据
|
||||
this.refreshData()
|
||||
// 初始化图表
|
||||
this.initChart()
|
||||
// 设置自动刷新
|
||||
this.setupAutoRefresh()
|
||||
// 在窗口大小改变时,重置图表大小
|
||||
window.addEventListener('resize', () => {
|
||||
this.initChart()
|
||||
})
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 移除事件监听
|
||||
window.removeEventListener('resize', () => {
|
||||
this.initChart()
|
||||
})
|
||||
// 清除定时器
|
||||
if (this.refreshTimer) {
|
||||
clearInterval(this.refreshTimer)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 设置自动刷新
|
||||
setupAutoRefresh() {
|
||||
if (this.refreshTimer) {
|
||||
clearInterval(this.refreshTimer)
|
||||
}
|
||||
if (this.isAutoRefresh) {
|
||||
this.refreshTimer = setInterval(() => {
|
||||
this.refreshData()
|
||||
}, this.refreshInterval * 1000)
|
||||
}
|
||||
},
|
||||
|
||||
// 获取报表数据
|
||||
async refreshData() {
|
||||
this.isRefreshing = true
|
||||
try {
|
||||
// 使用mixin中的接口方法获取数据
|
||||
const params = {
|
||||
currentDate: new Date().toLocaleDateString('zh-CN')
|
||||
}
|
||||
|
||||
// 直接解构从mixin返回的数据
|
||||
const { stats, historyList, weeklyComparison } = await dashboard2.refreshDashboardData(params)
|
||||
|
||||
// 更新统计数据
|
||||
this.stats = stats
|
||||
|
||||
// 更新历史数据
|
||||
if (historyList) {
|
||||
this.historyList = historyList
|
||||
} else {
|
||||
// 保留原有的模拟数据逻辑作为降级方案
|
||||
this.historyList = Array.from({ length: 7 }, (_, i) => {
|
||||
const date = new Date()
|
||||
date.setDate(date.getDate() - 6 + i)
|
||||
const dateStr = date.toISOString().split('T')[0]
|
||||
return {
|
||||
date: dateStr,
|
||||
inbound: Math.floor(Math.random() * 20) + 40,
|
||||
outbound: Math.floor(Math.random() * 20) + 30,
|
||||
other: Math.floor(Math.random() * 15) + 25,
|
||||
total: 100
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 更新周对比数据
|
||||
if (weeklyComparison) {
|
||||
this.weeklyComparison = weeklyComparison
|
||||
this.updateTaskChart()
|
||||
} else {
|
||||
// 如果没有从mixin获取到周对比数据,则调用原方法
|
||||
await this.getWeeklyData()
|
||||
}
|
||||
|
||||
// 更新任务完成情况图表
|
||||
this.updateChart()
|
||||
|
||||
// 更新最后更新时间
|
||||
this.lastUpdateTime = new Date().toLocaleString('zh-CN')
|
||||
} catch (error) {
|
||||
console.error('获取报表数据失败:', error)
|
||||
this.$message.error('获取数据失败,请稍后重试')
|
||||
} finally {
|
||||
this.isRefreshing = false
|
||||
}
|
||||
},
|
||||
|
||||
// 获取周对比数据
|
||||
async getWeeklyData() {
|
||||
try {
|
||||
// 使用mixin中的接口方法
|
||||
const weekData = await this.getWeekData()
|
||||
|
||||
// 直接使用weekData,假设mixin返回的数据格式符合需求
|
||||
const data = weekData || {}
|
||||
|
||||
// 更新周对比数据
|
||||
this.weeklyComparison = {
|
||||
thisWeek: {
|
||||
totalTasks: data.thisWeek && data.thisWeek.totalTasks !== undefined ? data.thisWeek.totalTasks : 0,
|
||||
completedTasks: data.thisWeek && data.thisWeek.completedTasks !== undefined ? data.thisWeek.completedTasks : 0,
|
||||
completionRate: data.thisWeek && data.thisWeek.completionRate !== undefined ? data.thisWeek.completionRate : 0
|
||||
},
|
||||
lastWeek: {
|
||||
totalTasks: data.lastWeek && data.lastWeek.totalTasks !== undefined ? data.lastWeek.totalTasks : 0,
|
||||
completedTasks: data.lastWeek && data.lastWeek.completedTasks !== undefined ? data.lastWeek.completedTasks : 0,
|
||||
completionRate: data.lastWeek && data.lastWeek.completionRate !== undefined ? data.lastWeek.completionRate : 0
|
||||
},
|
||||
growthRate: data.growthRate || 0
|
||||
}
|
||||
|
||||
// 更新周对比图表
|
||||
this.updateTaskChart()
|
||||
} catch (error) {
|
||||
console.error('获取周对比数据失败:', error)
|
||||
// 如果接口调用失败,使用模拟数据
|
||||
this.weeklyComparison = {
|
||||
thisWeek: {
|
||||
totalTasks: 650,
|
||||
completedTasks: 585,
|
||||
completionRate: 90
|
||||
},
|
||||
lastWeek: {
|
||||
totalTasks: 580,
|
||||
completedTasks: 493,
|
||||
completionRate: 85
|
||||
},
|
||||
growthRate: 12.1
|
||||
}
|
||||
this.updateTaskChart()
|
||||
}
|
||||
}
|
||||
,
|
||||
toggleRefresh() {
|
||||
this.isAutoRefresh = !this.isAutoRefresh
|
||||
this.setupAutoRefresh()
|
||||
},
|
||||
initChart() {
|
||||
// 初始化任务统计图表(合并后的单一图表)
|
||||
const taskChartDom = document.getElementById('taskChart')
|
||||
if (taskChartDom.myChart) {
|
||||
taskChartDom.myChart.dispose()
|
||||
}
|
||||
const taskChart = echarts.init(taskChartDom)
|
||||
taskChartDom.myChart = taskChart
|
||||
|
||||
// 根据当前视图类型更新图表
|
||||
this.updateTaskChart(taskChart)
|
||||
},
|
||||
|
||||
// 处理视图类型切换
|
||||
handleViewTypeChange() {
|
||||
this.updateTaskChart()
|
||||
},
|
||||
|
||||
// 更新任务统计图表(合并后的方法)
|
||||
async updateTaskChart(myChart) {
|
||||
// 如果没有传入图表实例,则从DOM获取
|
||||
if (!myChart) {
|
||||
const chartDom = document.getElementById('taskChart')
|
||||
if (!chartDom) return
|
||||
myChart = chartDom.myChart || echarts.init(chartDom)
|
||||
}
|
||||
|
||||
// 根据当前视图类型渲染不同的图表
|
||||
if (this.viewType === 'daily') {
|
||||
this.renderDailyChart(myChart)
|
||||
} else {
|
||||
await this.renderWeeklyChart(myChart)
|
||||
}
|
||||
},
|
||||
|
||||
// 渲染每日任务完成情况图表
|
||||
renderDailyChart(myChart) {
|
||||
// 直接使用每日数据
|
||||
const xAxisData = this.historyList.map(item => item.date.slice(5)) // 提取 "11-13" 格式的日期
|
||||
const inboundData = this.historyList.map(item => item.inbound)
|
||||
const outboundData = this.historyList.map(item => item.outbound)
|
||||
const otherData = this.historyList.map(item => item.other)
|
||||
|
||||
const option = {
|
||||
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
|
||||
legend: { data: ['入库任务', '出库任务', '其他搬运'], top: 10 },
|
||||
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
|
||||
xAxis: { type: 'category', data: xAxisData },
|
||||
yAxis: { type: 'value' },
|
||||
series: [
|
||||
{ name: '入库任务', type: 'bar', data: inboundData, itemStyle: { color: '#1890ff' } },
|
||||
{ name: '出库任务', type: 'bar', data: outboundData, itemStyle: { color: '#52c41a' } },
|
||||
{ name: '其他搬运', type: 'bar', data: otherData, itemStyle: { color: '#fa8c16' } }
|
||||
]
|
||||
}
|
||||
myChart.setOption(option)
|
||||
},
|
||||
|
||||
// 渲染周任务数据对比图表
|
||||
async renderWeeklyChart(myChart) {
|
||||
// 准备近3周数据(异步获取)
|
||||
await this.prepareThreeWeeksData()
|
||||
|
||||
const option = {
|
||||
tooltip: { trigger: 'axis' },
|
||||
legend: { data: ['入库任务', '出库任务', '其他搬运'], top: 10 },
|
||||
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: this.threeWeeksData.weeks
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{ name: '入库任务', type: 'bar', data: this.threeWeeksData.inboundData, itemStyle: { color: '#1890ff' } },
|
||||
{ name: '出库任务', type: 'bar', data: this.threeWeeksData.outboundData, itemStyle: { color: '#52c41a' } },
|
||||
{ name: '其他搬运', type: 'bar', data: this.threeWeeksData.otherData, itemStyle: { color: '#fa8c16' } }
|
||||
]
|
||||
}
|
||||
|
||||
myChart.setOption(option)
|
||||
},
|
||||
|
||||
// 准备近3周的数据
|
||||
async prepareThreeWeeksData() {
|
||||
try {
|
||||
// 从后台API获取真实的周任务数据
|
||||
const response = await axios.get('/api/bigScreen/getWeeklyData')
|
||||
const weeklyData = response.data
|
||||
|
||||
// 从响应数据中提取近3周的数据
|
||||
// 如果后台返回的数据格式不符合预期,则使用默认值
|
||||
if (weeklyData && weeklyData.success && weeklyData.data) {
|
||||
const { weeks, inboundData, outboundData, otherData } = weeklyData.data
|
||||
|
||||
this.threeWeeksData = {
|
||||
weeks: weeks || this.generateWeekDateLabels(),
|
||||
inboundData: inboundData || [0, 0, 0],
|
||||
outboundData: outboundData || [0, 0, 0],
|
||||
otherData: otherData || [0, 0, 0]
|
||||
}
|
||||
} else {
|
||||
// 后备方案:生成日期标签并使用空数据
|
||||
this.threeWeeksData = {
|
||||
weeks: this.generateWeekDateLabels(),
|
||||
inboundData: [0, 0, 0],
|
||||
outboundData: [0, 0, 0],
|
||||
otherData: [0, 0, 0]
|
||||
}
|
||||
console.warn('未获取到有效的周任务数据')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取周任务数据失败:', error)
|
||||
// 出错时使用后备方案
|
||||
this.threeWeeksData = {
|
||||
weeks: this.generateWeekDateLabels(),
|
||||
inboundData: [0, 0, 0],
|
||||
outboundData: [0, 0, 0],
|
||||
otherData: [0, 0, 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 生成周日期标签(MM-DD-MM-DD格式)
|
||||
generateWeekDateLabels() {
|
||||
const weeks = []
|
||||
const now = new Date()
|
||||
|
||||
for (let i = 2; i >= 0; i--) {
|
||||
// 计算本周的开始日期(周一)
|
||||
const weekDate = new Date(now)
|
||||
weekDate.setDate(now.getDate() - i * 7)
|
||||
|
||||
// 获取本周周一
|
||||
const day = weekDate.getDay() || 7 // 将周日的0转换为7
|
||||
const monday = new Date(weekDate)
|
||||
monday.setDate(weekDate.getDate() - day + 1)
|
||||
|
||||
// 获取本周周日
|
||||
const sunday = new Date(monday)
|
||||
sunday.setDate(monday.getDate() + 6)
|
||||
|
||||
// 格式化为 MM-DD 格式
|
||||
const formatDate = (date) => {
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
return `${month}-${day}`
|
||||
}
|
||||
|
||||
weeks.push(`${formatDate(monday)}-${formatDate(sunday)}`)
|
||||
}
|
||||
|
||||
return weeks
|
||||
}
|
||||
,
|
||||
|
||||
// 更新周对比图表
|
||||
updateWeeklyChart(myChart) {
|
||||
// 如果没有传入图表实例,则从DOM获取
|
||||
if (!myChart) {
|
||||
const chartDom = document.getElementById('weeklyComparisonChart')
|
||||
if (!chartDom) return
|
||||
myChart = chartDom.myChart || echarts.init(chartDom)
|
||||
}
|
||||
|
||||
const option = {
|
||||
tooltip: { trigger: 'axis' },
|
||||
legend: { data: ['本周', '上周'], top: 10 },
|
||||
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: ['总任务数', '已完成任务数', '完成率(%)']
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '本周',
|
||||
type: 'bar',
|
||||
data: [
|
||||
this.weeklyComparison.thisWeek.totalTasks,
|
||||
this.weeklyComparison.thisWeek.completedTasks,
|
||||
this.weeklyComparison.thisWeek.completionRate
|
||||
],
|
||||
itemStyle: { color: '#1890ff' }
|
||||
},
|
||||
{
|
||||
name: '上周',
|
||||
type: 'bar',
|
||||
data: [
|
||||
this.weeklyComparison.lastWeek.totalTasks,
|
||||
this.weeklyComparison.lastWeek.completedTasks,
|
||||
this.weeklyComparison.lastWeek.completionRate
|
||||
],
|
||||
itemStyle: { color: '#52c41a' }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
myChart.setOption(option)
|
||||
},
|
||||
updateChart(myChart) {
|
||||
// 如果没有传入图表实例,则从DOM获取
|
||||
if (!myChart) {
|
||||
const chartDom = document.getElementById('taskChart')
|
||||
if (!chartDom) return
|
||||
myChart = chartDom.myChart || echarts.init(chartDom)
|
||||
}
|
||||
|
||||
// 直接使用每日数据
|
||||
const xAxisData = this.historyList.map(item => item.date.slice(5)) // 提取 "11-13" 格式的日期
|
||||
const inboundData = this.historyList.map(item => item.inbound)
|
||||
const outboundData = this.historyList.map(item => item.outbound)
|
||||
const otherData = this.historyList.map(item => item.other)
|
||||
|
||||
const option = {
|
||||
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
|
||||
legend: { data: ['入库任务', '出库任务', '其他搬运'], top: 10 },
|
||||
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
|
||||
xAxis: { type: 'category', data: xAxisData },
|
||||
yAxis: { type: 'value' },
|
||||
series: [
|
||||
{ name: '入库任务', type: 'bar', data: inboundData, itemStyle: { color: '#1890ff' } },
|
||||
{ name: '出库任务', type: 'bar', data: outboundData, itemStyle: { color: '#52c41a' } },
|
||||
{ name: '其他搬运', type: 'bar', data: otherData, itemStyle: { color: '#fa8c16' } }
|
||||
]
|
||||
}
|
||||
myChart.setOption(option)
|
||||
}
|
||||
,
|
||||
handleSizeChange(val) {
|
||||
this.pageSize = val
|
||||
}
|
||||
,
|
||||
handleCurrentChange(val) {
|
||||
this.currentPage = val
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 全局样式 */
|
||||
.transport-dashboard {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
/* 头部样式 */
|
||||
.header {
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 20px;
|
||||
height: 64px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-left i {
|
||||
font-size: 24px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.last-update {
|
||||
margin-left: 20px;
|
||||
font-size: 14px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.refresh-toggle {
|
||||
color: #fff;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
/* 主要内容区域 */
|
||||
.main-content {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/* 概览卡片样式 */
|
||||
.overview-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.stat-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.stat-title {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
color: #1890ff;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.stat-change {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.increase,
|
||||
.positive {
|
||||
color: #52c41a;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.decrease,
|
||||
.negative {
|
||||
color: #ff4d4f;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.compared {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.growth-rate {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
/* 图表区域样式 */
|
||||
.task-charts {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.chart-card {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.chart-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
height: 350px;
|
||||
}
|
||||
|
||||
.chart {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 表格区域样式 */
|
||||
.history-table {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.table-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.table-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin-top: 15px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
/* 底部样式 */
|
||||
.footer {
|
||||
background-color: #fff;
|
||||
text-align: center;
|
||||
padding: 10px 0;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
border-top: 1px solid #e8e8e8;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.refresh-interval {
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
/* 响应式样式 */
|
||||
@media (max-width: 768px) {
|
||||
.overview-cards {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
10
nladmin-ui/src/views/dashboard/mixins/dashboard.js
Normal file
10
nladmin-ui/src/views/dashboard/mixins/dashboard.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function getDashboardData(data) {
|
||||
return request({
|
||||
url: 'api/bigScreen/getDashboardData',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
export default { getDashboardData }
|
||||
69
nladmin-ui/src/views/dashboard/mixins/dashboard2.js
Normal file
69
nladmin-ui/src/views/dashboard/mixins/dashboard2.js
Normal file
@@ -0,0 +1,69 @@
|
||||
import axios from 'axios'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 获取仪表板报表数据
|
||||
* @param {Object} params - 查询参数
|
||||
* @returns {Promise} 返回数据Promise
|
||||
*/
|
||||
async getReportData(params = {}) {
|
||||
try {
|
||||
const response = await axios.get('/api/bigScreen/getReportData', {
|
||||
params: {
|
||||
dateRange: params.dateRange || '',
|
||||
searchKey: params.searchKey || '',
|
||||
...params
|
||||
}
|
||||
})
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('获取报表数据失败:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取周对比数据
|
||||
* @param {Object} params - 查询参数
|
||||
* @returns {Promise} 返回数据Promise
|
||||
*/
|
||||
async getWeekData(params = {}) {
|
||||
try {
|
||||
const response = await axios.get('/api/bigScreen/getWeeklyData', {
|
||||
params: {
|
||||
currentWeek: params.currentWeek || '',
|
||||
lastWeek: params.lastWeek || '',
|
||||
...params
|
||||
}
|
||||
})
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('获取周对比数据失败:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 刷新所有仪表板数据
|
||||
* @param {Object} params - 查询参数
|
||||
* @returns {Promise<Object>} 返回包含报表数据和周对比数据的对象
|
||||
*/
|
||||
async refreshDashboardData(params = {}) {
|
||||
try {
|
||||
const [reportData, weekData] = await Promise.all([
|
||||
this.getReportData(params),
|
||||
this.getWeekData(params)
|
||||
])
|
||||
|
||||
// 返回Dashboard2.vue中期望的格式
|
||||
return {
|
||||
stats: reportData && reportData.stats ? reportData.stats : undefined,
|
||||
historyList: reportData && reportData.historyList ? reportData.historyList : undefined,
|
||||
weeklyComparison: weekData
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('刷新仪表板数据失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -179,7 +179,6 @@
|
||||
<el-table-column prop="stor_name" :label="$t('wms.st.inbill.warehouse')" width="100px;" />
|
||||
<el-table-column prop="struct_code" :label="$t('wms.st.inbill.warehouse_location')" width="100px;" />
|
||||
<el-table-column prop="vehicle_code" :label="$t('wms.st.inbill.vehicle_code')" width="100px;" />
|
||||
<el-table-column prop="barcode" :label="$t('wms.st.inbill.material_qrcode')" width="100px;" />
|
||||
<el-table-column show-overflow-tooltip prop="bill_type" min-width="120" :formatter="bill_typeFormat" :label="$t('wms.st.inbill.bill_type')" />
|
||||
<el-table-column show-overflow-tooltip min-width="120" prop="biz_date" :label="$t('wms.st.inbill.business_date')" />
|
||||
<el-table-column show-overflow-tooltip prop="create_mode" :formatter="create_modeFormat" :label="$t('wms.st.inbill.create_mode')" />
|
||||
|
||||
@@ -98,9 +98,9 @@
|
||||
<el-table-column prop="pcsn" :label="$t('wms.statement.structivt.batch_number')" :min-width="flexWidth('pcsn',crud.data,'批次号')" />
|
||||
<el-table-column prop="qty" :label="$t('wms.statement.structivt.stock_quantity')" :formatter="crud.formatNum3" :min-width="100" />
|
||||
<el-table-column prop="frozen_qty" :label="$t('wms.statement.structivt.frozen_quantity')" :formatter="crud.formatNum3" :min-width="100" />
|
||||
<el-table-column prop="update_time" :label="$t('wms.statement.structivt.storage_time')" :min-width="flexWidth('create_time',crud.data,'入库时间')" />
|
||||
<el-table-column prop="qty_unit_name" :label="$t('wms.statement.structivt.measurement_unit')" :min-width="flexWidth('qty_unit_name',crud.data,'计量单位')" />
|
||||
<el-table-column prop="remark" :label="$t('wms.statement.structivt.remark')" :min-width="flexWidth('remark',crud.data,'备注')" />
|
||||
<el-table-column prop="update_time" :label="$t('wms.statement.structivt.storage_time')" :min-width="flexWidth('create_time',crud.data,'入库时间')" />
|
||||
</el-table>
|
||||
<!--分页组件-->
|
||||
<pagination />
|
||||
|
||||
Reference in New Issue
Block a user