465 lines
14 KiB
Vue
465 lines
14 KiB
Vue
<template>
|
||
<div class="app-container panel">
|
||
<!-- 顶部统计面板 -->
|
||
<el-row :gutter="40" class="panel-group">
|
||
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
|
||
<div class="card-panel">
|
||
<div class="card-panel-icon-wrapper icon-people">
|
||
<svg-icon icon-class="clipboard" class-name="card-panel-icon"/>
|
||
</div>
|
||
<div class="card-panel-description">
|
||
<div class="card-panel-text">{{ $t('viewsMain.inboundOrders') }}</div> <!-- 入库单数 -->
|
||
<count-to :start-val="0" :end-val="Number(quantity.inbound_order) || 0" :duration="2600" class="card-panel-num"/>
|
||
</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
|
||
<div class="card-panel">
|
||
<div class="card-panel-icon-wrapper icon-message">
|
||
<svg-icon icon-class="documentation" class-name="card-panel-icon"/>
|
||
</div>
|
||
<div class="card-panel-description">
|
||
<div class="card-panel-text">{{ $t('viewsMain.outboundOrders') }}</div> <!-- 出库单数 -->
|
||
<count-to :start-val="0" :end-val="Number(quantity.outbound_order) || 0" :duration="3000" class="card-panel-num"/>
|
||
</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
|
||
<div class="card-panel">
|
||
<div class="card-panel-icon-wrapper icon-money">
|
||
<svg-icon icon-class="chart" class-name="card-panel-icon"/>
|
||
</div>
|
||
<div class="card-panel-description">
|
||
<div class="card-panel-text">{{ $t('viewsMain.inventoryOrders') }}</div> <!-- 盘点单数 -->
|
||
<count-to :start-val="0" :end-val="Number(quantity.inventory_sheet)|| 0" :duration="3200" class="card-panel-num"/>
|
||
</div>
|
||
</div>
|
||
</el-col>
|
||
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
|
||
<div class="card-panel">
|
||
<div class="card-panel-icon-wrapper icon-shopping">
|
||
<svg-icon icon-class="excel" class-name="card-panel-icon"/>
|
||
</div>
|
||
<div class="card-panel-description">
|
||
<div class="card-panel-text">{{ $t('viewsMain.totalOrders') }}</div> <!-- 总单据数 -->
|
||
<count-to :start-val="0" :end-val="Number(quantity.total_documents)|| 0" :duration="3600" class="card-panel-num"/>
|
||
</div>
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 中间图表区域 -->
|
||
<el-row :gutter="32" style="margin-bottom: 0px;">
|
||
<el-col :xs="24" :sm="24" :lg="16">
|
||
<div class="chart-wrapper">
|
||
<div class="chart-header">
|
||
<span>{{ $t('viewsMain.weeklyStats') }}</span> <!-- 近一周入库出库统计 -->
|
||
</div>
|
||
<line-chart :chart-data="lineChartData"/>
|
||
</div>
|
||
</el-col>
|
||
<el-col :xs="24" :sm="24" :lg="8">
|
||
<div class="chart-wrapper">
|
||
<div class="chart-header">
|
||
<span>{{ $t('viewsMain.locationStats') }}</span> <!-- 库位统计 -->
|
||
</div>
|
||
<pie-chart :chart-data="pieChartData"/>
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- 底部列表区域 -->
|
||
<el-row :gutter="32">
|
||
<el-col :xs="24" :sm="24" :lg="8">
|
||
<div class="list-wrapper">
|
||
<div class="list-header">
|
||
<span>{{ $t('viewsMain.documentList') }}</span> <!-- 单据列表 -->
|
||
</div>
|
||
<el-table :data="documentList" style="width: 100%" size="mini" height="300"
|
||
:header-cell-style="{backgroundColor: '#f5f7fa'}">
|
||
<el-table-column prop="id" :label="$t('viewsMain.documentId')"/> <!-- 单据编号 -->
|
||
<el-table-column prop="type" :formatter="type_" :label="$t('viewsMain.type')" width="80"/> <!-- 类型 -->
|
||
<el-table-column prop="status" :formatter="status_" :label="$t('viewsMain.status')" width="80"/> <!-- 状态 -->
|
||
</el-table>
|
||
</div>
|
||
</el-col>
|
||
<el-col :xs="24" :sm="24" :lg="8">
|
||
<div class="list-wrapper">
|
||
<div class="list-header">
|
||
<span>{{ $t('viewsMain.executingTasks') }}</span> <!-- 正执行任务 -->
|
||
</div>
|
||
<el-table :data="executingTasks" style="width: 100%" size="mini" height="300"
|
||
:header-cell-style="{backgroundColor: '#f5f7fa'}">
|
||
<el-table-column prop="jobId" :label="$t('viewsMain.taskId')"/> <!-- 任务编号 -->
|
||
<el-table-column prop="inOrOut" :label="$t('viewsMain.type')" width="80"/> <!-- 类型 -->
|
||
<el-table-column prop="statusLabel" :label="$t('viewsMain.status')" width="80"/> <!-- 状态 -->
|
||
</el-table>
|
||
</div>
|
||
</el-col>
|
||
<el-col :xs="24" :sm="24" :lg="8">
|
||
<div class="list-wrapper">
|
||
<div class="list-header">
|
||
<span>{{ $t('viewsMain.pendingTasks') }}</span> <!-- 待执行任务 -->
|
||
</div>
|
||
<el-table :data="pendingTasks" style="width: 100%" size="mini" height="300"
|
||
:header-cell-style="{backgroundColor: '#f5f7fa'}">
|
||
<el-table-column prop="jobId" :label="$t('viewsMain.taskId')"/> <!-- 任务编号 -->
|
||
<el-table-column prop="inOrOut" :label="$t('viewsMain.type')" width="80"/> <!-- 类型 -->
|
||
<el-table-column prop="priority" :label="$t('viewsMain.priority')" width="80"/> <!-- 优先级 -->
|
||
</el-table>
|
||
</div>
|
||
</el-col>
|
||
</el-row>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import CountTo from 'vue-count-to'
|
||
import LineChart from './dashboard/LineChart'
|
||
import PieChart from './dashboard/PieChart'
|
||
import {finishCount, weekjob, cargospace, billStatus, jobStatus} from "@/api/index";
|
||
|
||
export default {
|
||
name: 'Index',
|
||
components: {
|
||
CountTo,
|
||
LineChart,
|
||
PieChart
|
||
},
|
||
data() {
|
||
return {
|
||
quantity: {
|
||
inbound_order: '',
|
||
outbound_order: '',
|
||
inventory_sheet: '',
|
||
total_documents: ''
|
||
},
|
||
chartType: '入库',
|
||
lineChartData: {
|
||
expectedData: [],
|
||
actualData: []
|
||
},
|
||
pieChartData: {
|
||
labels: [
|
||
this.$t('viewsMain.emptyLocation'), // 空货位
|
||
this.$t('viewsMain.fullLocation'), // 满货位
|
||
this.$t('viewsMain.inboundAllocated'), // 入库已分配
|
||
this.$t('viewsMain.outboundAllocated'), // 出库已分配
|
||
this.$t('viewsMain.inboundNeedCheck'), // 入库需盘点
|
||
this.$t('viewsMain.outboundNeedCheck') // 出库需盘点
|
||
],
|
||
datasets: [{
|
||
data: [],
|
||
backgroundColor: ['#00d083', '#fdd100', '#fd7546', '#3f5cfc', '#1d99fd', '#2bf6ff']
|
||
}]
|
||
},
|
||
documentList: [],
|
||
executingTasks: [],
|
||
pendingTasks: [],
|
||
timer: null,
|
||
isCurrentPageActive: true // 当前页面是否激活
|
||
}
|
||
},
|
||
created() {
|
||
this.getListData()
|
||
this.startTimer()
|
||
},
|
||
mounted() {
|
||
// 监听页面可见性变化
|
||
document.addEventListener('visibilitychange', this.handleVisibilityChange)
|
||
// 监听窗口焦点变化
|
||
window.addEventListener('focus', this.handleWindowFocus)
|
||
window.addEventListener('blur', this.handleWindowBlur)
|
||
// 初始检查页面状态
|
||
this.checkPageStatus()
|
||
},
|
||
activated() {
|
||
// 当组件被激活时(如从keep-alive缓存中恢复)
|
||
this.checkPageStatus()
|
||
},
|
||
deactivated() {
|
||
// 当组件被停用时(如被keep-alive缓存)
|
||
this.stopTimer()
|
||
},
|
||
beforeDestroy() {
|
||
this.stopTimer()
|
||
// 移除事件监听器
|
||
document.removeEventListener('visibilitychange', this.handleVisibilityChange)
|
||
window.removeEventListener('focus', this.handleWindowFocus)
|
||
window.removeEventListener('blur', this.handleWindowBlur)
|
||
},
|
||
// 监听路由变化
|
||
watch: {
|
||
'$route'(to, from) {
|
||
// 当路由变化时,检查是否为首页
|
||
this.checkPageStatus()
|
||
}
|
||
},
|
||
methods: {
|
||
// 启动定时器
|
||
startTimer() {
|
||
if (!this.timer && this.isCurrentPageActive) {
|
||
this.timer = setInterval(() => {
|
||
if (this.isCurrentPageActive) {
|
||
this.getListData()
|
||
}
|
||
}, 8000)
|
||
}
|
||
},
|
||
// 停止定时器
|
||
stopTimer() {
|
||
if (this.timer) {
|
||
clearInterval(this.timer)
|
||
this.timer = null
|
||
}
|
||
},
|
||
// 检查页面状态
|
||
checkPageStatus() {
|
||
// 检查当前路由是否为首页
|
||
const isHomePage = this.$route.path === '/' || this.$route.name === 'Index'
|
||
// 检查页面是否可见
|
||
const isPageVisible = !document.hidden
|
||
// 检查窗口是否有焦点
|
||
const isWindowFocused = document.hasFocus()
|
||
|
||
const shouldBeActive = isHomePage && isPageVisible && isWindowFocused
|
||
|
||
if (shouldBeActive !== this.isCurrentPageActive) {
|
||
this.isCurrentPageActive = shouldBeActive
|
||
|
||
if (this.isCurrentPageActive) {
|
||
console.log('首页激活,启动定时器')
|
||
this.startTimer()
|
||
// 立即刷新一次数据
|
||
this.getListData()
|
||
} else {
|
||
console.log('首页非激活状态,停止定时器')
|
||
this.stopTimer()
|
||
}
|
||
}
|
||
},
|
||
// 处理页面可见性变化
|
||
handleVisibilityChange() {
|
||
this.checkPageStatus()
|
||
},
|
||
// 处理窗口获得焦点
|
||
handleWindowFocus() {
|
||
this.checkPageStatus()
|
||
},
|
||
// 处理窗口失去焦点
|
||
handleWindowBlur() {
|
||
this.checkPageStatus()
|
||
},
|
||
getListData() {
|
||
// 所有单据数量
|
||
finishCount().then(response => {
|
||
console.log(response)
|
||
this.quantity.inbound_order = response.data.stockinCount
|
||
this.quantity.outbound_order = response.data.stockoutCount
|
||
this.quantity.inventory_sheet = response.data.stockcheckCount
|
||
this.quantity.total_documents = Number(response.data.stockinCount) + Number(response.data.stockoutCount) + Number(response.data.stockcheckCount)
|
||
})
|
||
// 周入库出库作业
|
||
weekjob().then(response => {
|
||
this.lineChartData.expectedData = response.data.inJobCounts
|
||
this.lineChartData.actualData = response.data.outJobCounts
|
||
})
|
||
// 库位统计
|
||
cargospace().then(response => {
|
||
this.pieChartData.datasets[0].data = response.data.statusCounts
|
||
})
|
||
// 单据列表
|
||
billStatus().then(response => {
|
||
this.documentList = response.data
|
||
})
|
||
// 正执行待执行作业
|
||
jobStatus().then(response => {
|
||
this.executingTasks = response.data.status3
|
||
this.pendingTasks = response.data.status2
|
||
})
|
||
|
||
},
|
||
type_(row, column) {
|
||
const type = row.type
|
||
if (type == 1) {
|
||
return this.$t('viewsMain.inbound') // 入库
|
||
} else if (type == 2) {
|
||
return this.$t('viewsMain.outbound') // 出库
|
||
} else if (type == 3) {
|
||
return this.$t('viewsMain.inventory') // 盘点
|
||
}
|
||
return ""
|
||
},
|
||
status_(row, column) {
|
||
const status = row.status
|
||
if (status == 1) {
|
||
return this.$t('viewsMain.unexecuted') // 未执行
|
||
} else if (status == 2) {
|
||
return this.$t('viewsMain.reviewing') // 审核中
|
||
} else if (status == 3) {
|
||
return this.$t('viewsMain.executing') // 正执行
|
||
}
|
||
return ""
|
||
}
|
||
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.panel{
|
||
background: #fafafa;
|
||
}
|
||
.panel-group {
|
||
margin-top: 18px;
|
||
|
||
.card-panel-col {
|
||
margin-bottom: 32px;
|
||
}
|
||
|
||
.card-panel {
|
||
height: 108px;
|
||
cursor: pointer;
|
||
font-size: 12px;
|
||
position: relative;
|
||
overflow: hidden;
|
||
color: #666;
|
||
background: #fff;
|
||
box-shadow: 4px 4px 40px rgba(0, 0, 0, .05);
|
||
border-color: rgba(0, 0, 0, .05);
|
||
|
||
&:hover {
|
||
.card-panel-icon-wrapper {
|
||
color: #fff;
|
||
}
|
||
|
||
.icon-people {
|
||
background: #27c3cc;
|
||
}
|
||
|
||
.icon-message {
|
||
background: #ffd202;
|
||
}
|
||
|
||
.icon-money {
|
||
background: #00cf7f;
|
||
}
|
||
|
||
.icon-shopping {
|
||
background: #2298f8;
|
||
}
|
||
}
|
||
|
||
.icon-people {
|
||
color: #27c3cc;
|
||
}
|
||
|
||
.icon-message {
|
||
color: #ffd202;
|
||
}
|
||
|
||
.icon-money {
|
||
color: #00cf7f;
|
||
}
|
||
|
||
.icon-shopping {
|
||
color: #2298f8;
|
||
}
|
||
|
||
.card-panel-icon-wrapper {
|
||
float: left;
|
||
margin: 14px 0 0 14px;
|
||
padding: 16px;
|
||
transition: all 0.38s ease-out;
|
||
border-radius: 6px;
|
||
}
|
||
|
||
.card-panel-icon {
|
||
float: left;
|
||
font-size: 48px;
|
||
}
|
||
|
||
.card-panel-description {
|
||
float: right;
|
||
font-weight: bold;
|
||
margin: 26px;
|
||
margin-left: 0px;
|
||
|
||
.card-panel-text {
|
||
line-height: 18px;
|
||
color: rgba(0, 0, 0, 0.45);
|
||
font-size: 16px;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.card-panel-num {
|
||
font-size: 20px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.chart-wrapper {
|
||
background: #fff;
|
||
padding: 16px;
|
||
margin-bottom: 32px;
|
||
border-radius: 4px;
|
||
box-shadow: 4px 4px 40px rgba(0, 0, 0, .05);
|
||
}
|
||
|
||
.chart-header {
|
||
border-bottom: 1px solid #eee;
|
||
padding-bottom: 16px;
|
||
margin-bottom: 16px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
|
||
span {
|
||
font-size: 16px;
|
||
font-weight: bold;
|
||
color: #303133;
|
||
}
|
||
}
|
||
|
||
.list-wrapper {
|
||
background: #fff;
|
||
padding: 16px;
|
||
margin-bottom: 32px;
|
||
border-radius: 4px;
|
||
box-shadow: 4px 4px 40px rgba(0, 0, 0, .05);
|
||
height: 380px;
|
||
|
||
.list-header {
|
||
border-bottom: 1px solid #eee;
|
||
padding-bottom: 16px;
|
||
margin-bottom: 16px;
|
||
|
||
span {
|
||
font-size: 16px;
|
||
font-weight: bold;
|
||
color: #303133;
|
||
}
|
||
}
|
||
|
||
.el-table {
|
||
::v-deep .el-table__body-wrapper {
|
||
&::-webkit-scrollbar {
|
||
width: 6px;
|
||
height: 6px;
|
||
}
|
||
|
||
&::-webkit-scrollbar-thumb {
|
||
border-radius: 3px;
|
||
background: #e0e0e0;
|
||
}
|
||
|
||
&::-webkit-scrollbar-track {
|
||
border-radius: 3px;
|
||
background: #f5f5f5;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|