| | |
| | | <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 class="card-compare" :class="compareClass(staffYoY)"> |
| | | <span>同比</span> |
| | | <span class="compare-value">{{ formatPercent(staffYoY) }}</span> |
| | | <span class="compare-icon">{{ staffYoY >= 0 ? '↑' : '↓' }}</span> |
| | | </div> |
| | | </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 class="card-compare" :class="compareClass(customersYoY)"> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue' |
| | | import { ref, onMounted, onBeforeUnmount, nextTick, inject, watch } from 'vue' |
| | | import { homeTodos, summaryStatistics } from '@/api/viewIndex.js' |
| | | import { getLedgerPage } from '@/api/equipmentManagement/ledger.js' |
| | | import { getRepairPage } from '@/api/equipmentManagement/repair.js' |
| | |
| | | import { measuringInstrumentListPage } from '@/api/equipmentManagement/measurementEquipment.js' |
| | | |
| | | // 统计数据 |
| | | const totalStaff = ref(0) |
| | | const totalCustomers = ref(0) |
| | | const totalSuppliers = ref(0) |
| | | // 同比 |
| | | const staffYoY = ref(0) |
| | | const customersYoY = ref(0) |
| | | const suppliersYoY = ref(0) |
| | | const equipmentNum = ref(0) |
| | |
| | | |
| | | const compareClass = (val) => (val >= 0 ? 'compare-up' : 'compare-down') |
| | | |
| | | // 获取员工、客户、供应商数量 |
| | | // 获取客户、供应商数量 |
| | | const getNum = () => { |
| | | summaryStatistics().then((res) => { |
| | | totalStaff.value = res.data.totalStaff |
| | | staffYoY.value = res.data.staffGrowthRate |
| | | totalCustomers.value = res.data.totalCustomer |
| | | customersYoY.value = res.data.customerGrowthRate |
| | | totalSuppliers.value = res.data.totalSupplier |
| | |
| | | }) |
| | | } |
| | | |
| | | const destroyTodoListScroll = () => { |
| | | const todoListEl = refTodoList.value |
| | | if (todoListEl) { |
| | | if (todoListEl._animationFrame) { |
| | | cancelAnimationFrame(todoListEl._animationFrame) |
| | | todoListEl._animationFrame = null |
| | | } |
| | | if (todoListEl._pauseTimer) { |
| | | clearInterval(todoListEl._pauseTimer) |
| | | todoListEl._pauseTimer = null |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 初始化待办事项列表滚动功能 |
| | | const initTodoListScroll = () => { |
| | | destroyTodoListScroll() |
| | | const todoListEl = refTodoList.value |
| | | // 强制启用滚动,不检查任何条件 |
| | | if (todoListEl) { |
| | |
| | | |
| | | // 待办事项 |
| | | const todoInfoS = () => { |
| | | destroyTodoListScroll() |
| | | homeTodos().then((res) => { |
| | | todoList.value = res.data |
| | | // 在获取到待办事项数据后,初始化滚动功能 |
| | |
| | | }) |
| | | } |
| | | |
| | | onMounted(() => { |
| | | const refreshCenterTopData = () => { |
| | | getNum() |
| | | getLedgerNum() |
| | | todoInfoS() |
| | | } |
| | | |
| | | const dataDashboardRefreshTick = inject('dataDashboardRefreshTick', null) |
| | | if (dataDashboardRefreshTick) { |
| | | watch(dataDashboardRefreshTick, () => { |
| | | refreshCenterTopData() |
| | | }) |
| | | } |
| | | |
| | | onMounted(() => { |
| | | refreshCenterTopData() |
| | | }) |
| | | |
| | | 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 |
| | | } |
| | | } |
| | | destroyTodoListScroll() |
| | | }) |
| | | </script> |
| | | |
| | |
| | | padding: 18px; |
| | | height: 240px; |
| | | padding-top: 0px; |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .equipment-header { |
| | |
| | | background-position: center; |
| | | background-repeat: no-repeat; |
| | | padding: 20px; |
| | | height: 186px; |
| | | padding-top: 10px; |
| | | height: 480px; |
| | | flex: 1; |
| | | } |
| | | |
| | | .event-header { |
| | |
| | | list-style: none; |
| | | padding: 0; |
| | | margin: 0; |
| | | height: 120px; /* 按用户要求调整高度 */ |
| | | height: 210px; |
| | | overflow: hidden; |
| | | font-size: 15px; |
| | | } |