<template>
|
<div>
|
<!-- 顶部统计卡片 -->
|
<div class="stats-cards">
|
<div class="stat-card">
|
<img src="@/assets/BI/icon@2x.png" alt="图标" class="card-icon" />
|
<div class="card-content">
|
<span class="card-label">员工总数</span>
|
<span class="card-value">{{ totalStaff }}</span>
|
</div>
|
</div>
|
<div class="stat-card">
|
<img src="@/assets/BI/icon@2x.png" alt="图标" class="card-icon" />
|
<div class="card-content">
|
<span class="card-label">客户总数</span>
|
<span class="card-value">{{ totalCustomers }}</span>
|
</div>
|
</div>
|
<div class="stat-card">
|
<img src="@/assets/BI/icon@2x.png" alt="图标" class="card-icon" />
|
<div class="card-content">
|
<span class="card-label">供应商总数</span>
|
<span class="card-value">{{ totalSuppliers }}</span>
|
</div>
|
</div>
|
</div>
|
|
<!-- 设备统计 -->
|
<div class="equipment-stats">
|
<div class="equipment-header">
|
<img
|
src="@/assets/BI/shujutongjiicon@2x.png"
|
alt="图标"
|
class="equipment-icon"
|
/>
|
<span class="equipment-title">设备统计</span>
|
</div>
|
<div class="equipment-items">
|
<div class="equipment-item">
|
<span class="equipment-value">{{ equipmentNum }}</span>
|
<span class="equipment-label">设备总数</span>
|
</div>
|
<div class="equipment-item">
|
<span class="equipment-value">{{ equipmentRepair }}</span>
|
<span class="equipment-label">待维修设备</span>
|
</div>
|
<div class="equipment-item">
|
<span class="equipment-value">{{ equipmentMaintain }}</span>
|
<span class="equipment-label">待保养设备</span>
|
</div>
|
<div class="equipment-item">
|
<span class="equipment-value">{{ totalMeasuring }}</span>
|
<span class="equipment-label">计量器具总数</span>
|
</div>
|
</div>
|
</div>
|
|
<!-- 事件名称 -->
|
<div class="event-info">
|
<div class="event-header">
|
<img
|
src="@/assets/BI/shijianmingxiicon@2x.png"
|
alt="图标"
|
class="event-icon"
|
/>
|
<span class="event-title">事件名称</span>
|
</div>
|
<div class="event-content">
|
<ul class="todo-list" v-if="todoList.length > 0" ref="refTodoList">
|
<li v-for="item in todoList" :key="item.id">
|
<div
|
style="
|
display: flex;
|
flex-direction: column;
|
justify-content: space-between;
|
width: 100%;
|
gap: 20px;
|
"
|
>
|
<div class="todo-division">待办事由:{{ item.approveReason }}</div>
|
<div style="display: flex;justify-content: space-between;align-items: center;"
|
>
|
<div class="todo-title">申请类型:{{ item.approveId }}</div>
|
<div class="todo-division">申请部门:{{ item.approveDeptName }}</div>
|
<div class="todo-time">{{ item.approveTime }}</div>
|
</div>
|
|
</div>
|
</li>
|
</ul>
|
<div v-else style="text-align: center">暂无数据</div>
|
</div>
|
</div>
|
</div>
|
</template>
|
|
<script setup>
|
import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue'
|
import { homeTodos } from '@/api/viewIndex.js'
|
import { staffOnJobListPage } from '@/api/personnelManagement/employeeRecord.js'
|
import { listCustomer } from '@/api/basicData/customerFile.js'
|
import { listSupplier } from '@/api/basicData/supplierManageFile.js'
|
import { getLedgerPage } from '@/api/equipmentManagement/ledger.js'
|
import { getRepairPage } from '@/api/equipmentManagement/repair.js'
|
import { getUpkeepPage } from '@/api/equipmentManagement/upkeep.js'
|
import { measuringInstrumentListPage } from '@/api/equipmentManagement/measurementEquipment.js'
|
|
// 统计数据
|
const totalStaff = ref(0)
|
const totalCustomers = ref(0)
|
const totalSuppliers = ref(0)
|
const equipmentNum = ref(0)
|
const equipmentRepair = ref(0)
|
const equipmentMaintain = ref(0)
|
const totalMeasuring = ref(0)
|
|
// 待办事项
|
const todoList = ref([])
|
const refTodoList = ref(null)
|
|
// 获取员工、客户、供应商数量
|
const getNum = () => {
|
const params = {
|
pageNum: -1,
|
pageSize: -1,
|
}
|
staffOnJobListPage({ ...params, staffState: 1 }).then((res) => {
|
totalStaff.value = res.data.total
|
})
|
listCustomer(params).then((res) => {
|
totalCustomers.value = res.total
|
})
|
listSupplier(params).then((res) => {
|
totalSuppliers.value = res.data.total
|
})
|
}
|
|
// 获取设备相关数量
|
const getLedgerNum = () => {
|
const params = {
|
pageNum: -1,
|
pageSize: -1,
|
}
|
getLedgerPage(params).then((res) => {
|
equipmentNum.value = res.data.total
|
})
|
getRepairPage({ ...params, status: 0 }).then((res) => {
|
equipmentRepair.value = res.data.total
|
})
|
getUpkeepPage({ ...params, status: 0 }).then((res) => {
|
equipmentMaintain.value = res.data.total
|
})
|
measuringInstrumentListPage(params).then((res) => {
|
totalMeasuring.value = res.data.total
|
})
|
}
|
|
// 初始化待办事项列表滚动功能
|
const initTodoListScroll = () => {
|
const todoListEl = refTodoList.value
|
// 强制启用滚动,不检查任何条件
|
if (todoListEl) {
|
// 创建一个克隆项,用于实现无缝滚动
|
const scrollItems = Array.from(todoListEl.querySelectorAll('li'))
|
if (scrollItems.length > 0) {
|
// 确保有足够的项目用于滚动
|
// 如果项目太少,多复制几次以确保滚动效果
|
if (scrollItems.length < 4) {
|
const originalItems = [...scrollItems]
|
for (let i = 0; i < 4; i++) {
|
originalItems.forEach((item) => {
|
const clone = item.cloneNode(true)
|
todoListEl.appendChild(clone)
|
})
|
}
|
// 重新获取所有项目
|
scrollItems.push(
|
...Array.from(todoListEl.querySelectorAll('li')).slice(
|
scrollItems.length
|
)
|
)
|
}
|
const itemHeight = scrollItems[0]?.offsetHeight || 0
|
const containerHeight = todoListEl.clientHeight
|
const cloneCount = Math.ceil(containerHeight / itemHeight) + 2
|
|
// 克隆前几个项目并添加到列表末尾,实现无缝滚动
|
for (let i = 0; i < cloneCount; i++) {
|
const clone = scrollItems[i % scrollItems.length].cloneNode(true)
|
todoListEl.appendChild(clone)
|
}
|
|
let scrollPosition = 0
|
const scrollSpeed = 1.5 // 增加滚动速度,使滚动更加明显
|
const pauseTime = 3000 // 滚动暂停时间
|
let isPaused = false
|
let lastTimestamp = 0
|
|
// 连续滚动动画函数
|
function scrollAnimation(timestamp) {
|
if (!lastTimestamp) lastTimestamp = timestamp
|
const deltaTime = timestamp - lastTimestamp
|
lastTimestamp = timestamp
|
|
if (!isPaused) {
|
scrollPosition += scrollSpeed * (deltaTime / 16) // 标准化为60fps的速度
|
|
// 当滚动超过原始内容长度时,重置位置实现无缝滚动
|
const maxScroll = Math.max(
|
todoListEl.scrollHeight -
|
containerHeight -
|
cloneCount * itemHeight,
|
itemHeight * scrollItems.length
|
)
|
if (scrollPosition >= maxScroll) {
|
scrollPosition = 0
|
todoListEl.scrollTop = 0
|
} else {
|
todoListEl.scrollTop = scrollPosition
|
}
|
}
|
|
todoListEl._animationFrame = requestAnimationFrame(scrollAnimation)
|
}
|
|
// 启动滚动动画
|
todoListEl._animationFrame = requestAnimationFrame(scrollAnimation)
|
|
// 设置滚动-暂停-滚动的循环效果
|
const pauseTimer = setInterval(() => {
|
isPaused = !isPaused
|
}, pauseTime)
|
|
// 清理定时器
|
todoListEl._pauseTimer = pauseTimer
|
}
|
}
|
}
|
|
// 待办事项
|
const todoInfoS = () => {
|
homeTodos().then((res) => {
|
todoList.value = res.data
|
// 在获取到待办事项数据后,初始化滚动功能
|
nextTick(() => {
|
initTodoListScroll()
|
})
|
})
|
}
|
|
onMounted(() => {
|
getNum()
|
getLedgerNum()
|
todoInfoS()
|
})
|
|
onBeforeUnmount(() => {
|
// 清理待办事项列表的动画和定时器
|
const todoListEl = refTodoList.value
|
if (todoListEl) {
|
if (todoListEl._animationFrame) {
|
cancelAnimationFrame(todoListEl._animationFrame)
|
todoListEl._animationFrame = null
|
}
|
if (todoListEl._pauseTimer) {
|
clearInterval(todoListEl._pauseTimer)
|
todoListEl._pauseTimer = null
|
}
|
}
|
})
|
</script>
|
|
<style scoped>
|
.stats-cards {
|
display: flex;
|
gap: 30px;
|
}
|
|
.stat-card {
|
flex: 1;
|
display: flex;
|
align-items: center;
|
background-image: url('@/assets/BI/border@2x.png');
|
background-size: 100% 100%;
|
background-position: center;
|
background-repeat: no-repeat;
|
height: 142px;
|
}
|
|
.card-icon {
|
width: 100px;
|
height: 100px;
|
margin: 20px 20px 0 10px;
|
}
|
|
.card-content {
|
display: flex;
|
flex-direction: column;
|
gap: 10px;
|
}
|
|
.card-value {
|
font-weight: 500;
|
font-size: 40px;
|
background: linear-gradient(360deg, #008bfd 0%, #ffffff 100%);
|
-webkit-background-clip: text;
|
-webkit-text-fill-color: transparent;
|
background-clip: text;
|
}
|
|
.card-label {
|
font-weight: 400;
|
font-size: 19px;
|
color: rgba(208, 231, 255, 0.7);
|
}
|
|
.equipment-stats {
|
border: 1px solid #1a58b0;
|
padding: 18px;
|
height: 240px;
|
}
|
|
.equipment-header {
|
font-weight: 500;
|
font-size: 21px;
|
display: flex;
|
border-bottom: 1px solid;
|
border-image: linear-gradient(
|
270deg,
|
rgba(0, 126, 255, 0) 0%,
|
rgba(0, 126, 255, 0.4549) 35%,
|
#007eff 78%,
|
#007eff 100%
|
)
|
1;
|
padding-bottom: 2px;
|
}
|
|
.equipment-title {
|
font-weight: 500;
|
font-size: 18px;
|
background: linear-gradient(360deg, #056dff 0%, #43e8fc 100%);
|
-webkit-background-clip: text;
|
-webkit-text-fill-color: transparent;
|
background-clip: text;
|
line-height: 50px;
|
}
|
|
.equipment-icon {
|
width: 50px;
|
height: 50px;
|
}
|
|
.equipment-items {
|
display: flex;
|
justify-content: space-around;
|
gap: 30px;
|
}
|
|
.equipment-item {
|
text-align: center;
|
}
|
|
.equipment-value {
|
display: block;
|
font-weight: 500;
|
font-size: 40px;
|
color: #ffffff;
|
width: 120px;
|
height: 110px;
|
line-height: 110px;
|
background-image: url('@/assets/BI/shujutongji@2x.png');
|
background-size: 100% 100%;
|
background-position: center;
|
background-repeat: no-repeat;
|
margin-bottom: 8px;
|
}
|
|
.equipment-label {
|
font-weight: 500;
|
font-size: 16px;
|
color: #fffffe;
|
}
|
|
.event-info {
|
background-image: url('@/assets/BI/shijianmingchengbeijing@2x.png');
|
background-size: 100% 100%;
|
background-position: center;
|
background-repeat: no-repeat;
|
padding: 20px;
|
height: 186px;
|
}
|
|
.event-header {
|
display: flex;
|
align-items: center;
|
}
|
|
.event-icon {
|
width: 40px;
|
height: 40px;
|
}
|
|
.event-title {
|
font-weight: 500;
|
font-size: 18px;
|
color: #fffffe;
|
line-height: 30px;
|
}
|
|
.todo-list {
|
list-style: none;
|
padding: 0;
|
margin: 0;
|
height: 120px; /* 按用户要求调整高度 */
|
overflow: hidden;
|
font-size: 15px;
|
}
|
|
.todo-list li {
|
border-radius: 8px;
|
margin-bottom: 12px;
|
padding: 12px 40px;
|
height: 74px;
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
}
|
|
.todo-title {
|
font-weight: 400;
|
font-size: 16px;
|
color: #fffffe;
|
position: relative;
|
}
|
|
|
|
.todo-division {
|
font-weight: 400;
|
font-size: 16px;
|
color: #fffffe;
|
position: relative;
|
}
|
|
.todo-division::before {
|
content: '';
|
position: absolute;
|
left: -20px;
|
top: 50%;
|
transform: translateY(-50%);
|
width: 6px;
|
height: 6px;
|
background: #498ceb;
|
border-radius: 50%;
|
}
|
|
.todo-time {
|
font-weight: 400;
|
font-size: 16px;
|
color: #fffffe;
|
background: rgba(24, 93, 190, 0.4);
|
border-radius: 5px 5px 5px 5px;
|
padding: 5px 10px;
|
}
|
</style>
|