Files
FT501-WMS/ruoyi-ui/src/views/index.vue
2025-08-05 16:40:19 +08:00

465 lines
14 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>
<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>