From 9d485123b8c947f61c94aee67a0ceec8953a510d Mon Sep 17 00:00:00 2001
From: yaowanxin <3588231647@qq.com>
Date: 星期五, 30 一月 2026 17:36:56 +0800
Subject: [PATCH] 用印管理、规章制度管理页面添加分页功能
---
src/views/reportAnalysis/productionAnalysis/components/center-bottom.vue | 351 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 351 insertions(+), 0 deletions(-)
diff --git a/src/views/reportAnalysis/productionAnalysis/components/center-bottom.vue b/src/views/reportAnalysis/productionAnalysis/components/center-bottom.vue
new file mode 100644
index 0000000..46a870a
--- /dev/null
+++ b/src/views/reportAnalysis/productionAnalysis/components/center-bottom.vue
@@ -0,0 +1,351 @@
+<template>
+ <div>
+ <PanelHeader title="鐢熶骇璁㈠崟瀹屾垚杩涘害" />
+ <div class="main-panel">
+ <div class="panel-item-customers">
+ <CarouselCards :items="cardItems" :visible-count="4" />
+ <div
+ class="progress-table-container"
+ ref="progressTableRef"
+ style="margin-top: 0px;"
+ @scroll="handleTableScroll"
+ >
+ <table class="progress-table">
+ <thead>
+ <tr>
+ <th>鐢熶骇璁㈠崟鍙�</th>
+ <th>浜у搧鍚嶇О</th>
+ <th>瑙勬牸</th>
+ <th>闇�姹傛暟閲�</th>
+ <th>瀹屾垚鏁伴噺</th>
+ <th>瀹屾垚杩涘害</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr
+ v-for="(item, index) in progressTableData"
+ :key="index"
+ :ref="(el) => setRowRef(el, index)"
+ :class="{ 'row-under-header': isRowUnderHeader(index) }"
+ >
+ <td>{{ item.npsNo || '-' }}</td>
+ <td>{{ item.productCategory || '-' }}</td>
+ <td>{{ item.specificationModel || '-' }}</td>
+ <td>{{ item.quantity || 0 }}</td>
+ <td>{{ item.completeQuantity || 0 }}</td>
+ <td>
+ <el-progress
+ :percentage="calculateProgress(item)"
+ :color="progressColor(calculateProgress(item))"
+ :status="calculateProgress(item) >= 100 ? 'success' : ''"
+ :stroke-width="8"
+ />
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue'
+import { getProgressStatistics } from '@/api/viewIndex.js'
+import PanelHeader from './PanelHeader.vue'
+import CarouselCards from './CarouselCards.vue'
+
+const progressTableRef = ref(null)
+const progressTableScrollTimer = ref(null)
+const tableScrollTimeout = ref(null)
+const tableRowRefs = ref([])
+const rowsUnderHeader = ref(new Set())
+
+// 璁㈠崟缁熻瀵硅薄
+const orderStatisticsObject = ref({
+ totalOrderCount: 0,
+ uncompletedOrderCount: 0,
+ partialCompletedOrderCount: 0,
+ completedOrderCount: 0,
+})
+
+// 杞挱鍗$墖鏁版嵁锛堢敱 orderStatisticsObject 鍚屾锛�
+const cardItems = ref([])
+
+// 鐢熶骇璁㈠崟瀹屾垚杩涘害琛ㄦ牸鏁版嵁
+const progressTableData = ref([])
+
+// 璁$畻瀹屾垚杩涘害鐧惧垎姣�
+const calculateProgress = (item) => {
+ if (!item) return 0
+ if (item.completionStatus !== undefined && item.completionStatus !== null) {
+ const percentage = Number(item.completionStatus)
+ if (isNaN(percentage)) return 0
+ return Math.min(Math.max(Math.round(percentage), 0), 100)
+ }
+ if (!item.quantity || item.quantity === 0) return 0
+ const percentage = ((item.completeQuantity || 0) / item.quantity) * 100
+ return Math.min(Math.max(Math.round(percentage), 0), 100)
+}
+
+// 鏍规嵁杩涘害鐧惧垎姣旇繑鍥為鑹�
+const progressColor = (percentage) => {
+ const p = percentage || 0
+ if (p < 30) return '#f56c6c'
+ if (p < 50) return '#e6a23c'
+ if (p < 80) return '#409eff'
+ return '#67c23a'
+}
+
+const setRowRef = (el, index) => {
+ if (el) {
+ tableRowRefs.value[index] = el
+ }
+}
+
+const isRowUnderHeader = (index) => rowsUnderHeader.value.has(index)
+
+const handleTableScroll = () => {
+ const tableContainer = progressTableRef.value
+ if (!tableContainer) return
+ const thead = tableContainer.querySelector('thead')
+ if (!thead) return
+ const theadHeight = thead.offsetHeight
+ const containerRect = tableContainer.getBoundingClientRect()
+ const containerTop = containerRect.top
+ const theadBottom = containerTop + theadHeight
+ rowsUnderHeader.value.clear()
+ tableRowRefs.value.forEach((row, index) => {
+ if (row) {
+ const rowRect = row.getBoundingClientRect()
+ const rowTop = rowRect.top
+ const rowBottom = rowRect.bottom
+ if (rowTop < theadBottom && rowBottom > containerTop) {
+ rowsUnderHeader.value.add(index)
+ }
+ }
+ })
+ if (tableScrollTimeout.value) clearTimeout(tableScrollTimeout.value)
+ tableScrollTimeout.value = setTimeout(() => {
+ rowsUnderHeader.value.clear()
+ }, 150)
+}
+
+const initProgressTableScroll = () => {
+ const tableContainer = progressTableRef.value
+ if (!tableContainer) return
+ if (progressTableScrollTimer.value) {
+ cancelAnimationFrame(progressTableScrollTimer.value)
+ progressTableScrollTimer.value = null
+ }
+ if (tableContainer._pauseTimer) {
+ clearInterval(tableContainer._pauseTimer)
+ tableContainer._pauseTimer = null
+ }
+ const tbody = tableContainer.querySelector('tbody')
+ if (!tbody) return
+ const originalCount = progressTableData.value.length
+ const allRows = Array.from(tbody.querySelectorAll('tr'))
+ if (allRows.length > originalCount) {
+ for (let i = originalCount; i < allRows.length; i++) {
+ allRows[i].remove()
+ }
+ }
+ const scrollItems = Array.from(tbody.querySelectorAll('tr'))
+ if (scrollItems.length === 0) return
+ const originalItemCount = scrollItems.length
+ const thead = tableContainer.querySelector('thead')
+ const theadHeight = thead ? thead.offsetHeight : 40
+ const containerHeight = tableContainer.clientHeight
+ const visibleHeight = containerHeight - theadHeight
+ const itemHeight = scrollItems[0]?.offsetHeight || 40
+ const totalContentHeight = itemHeight * originalItemCount
+ if (totalContentHeight <= visibleHeight) return
+ const cloneCount = Math.ceil(visibleHeight / itemHeight) + 2
+ for (let i = 0; i < cloneCount; i++) {
+ const clone = scrollItems[i % originalItemCount].cloneNode(true)
+ tbody.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)
+ const maxScroll = itemHeight * originalItemCount
+ if (scrollPosition >= maxScroll) {
+ scrollPosition = 0
+ tableContainer.scrollTop = 0
+ } else {
+ tableContainer.scrollTop = scrollPosition
+ }
+ }
+ progressTableScrollTimer.value = requestAnimationFrame(scrollAnimation)
+ }
+ progressTableScrollTimer.value = requestAnimationFrame(scrollAnimation)
+ const pauseTimer = setInterval(() => {
+ isPaused = !isPaused
+ }, pauseTime)
+ tableContainer._pauseTimer = pauseTimer
+}
+
+const progressStatisticsInfo = () => {
+ getProgressStatistics()
+ .then((res) => {
+ if (!res || !res.data) return
+ const obj = {
+ totalOrderCount: res.data.totalOrderCount || 0,
+ uncompletedOrderCount: res.data.uncompletedOrderCount || 0,
+ partialCompletedOrderCount: res.data.partialCompletedOrderCount || 0,
+ completedOrderCount: res.data.completedOrderCount || 0,
+ }
+ orderStatisticsObject.value = obj
+ cardItems.value = [
+ { label: '鎬昏鍗曟暟', value: obj.totalOrderCount, unit: '浠�' },
+ { label: '鏈畬鎴愯鍗曟暟', value: obj.uncompletedOrderCount, unit: '浠�' },
+ { label: '閮ㄥ垎瀹屾垚璁㈠崟鏁�', value: obj.partialCompletedOrderCount, unit: '浠�' },
+ { label: '宸插畬鎴愯鍗曟暟', value: obj.completedOrderCount, unit: '浠�' },
+ ]
+ progressTableData.value = res.data.completedOrderDetails || []
+ tableRowRefs.value = []
+ rowsUnderHeader.value.clear()
+ nextTick(() => {
+ initProgressTableScroll()
+ })
+ })
+ .catch((err) => {
+ console.error('鑾峰彇鐢熶骇璁㈠崟瀹屾垚杩涘害缁熻澶辫触:', err)
+ })
+}
+
+onMounted(() => {
+ progressStatisticsInfo()
+})
+
+onBeforeUnmount(() => {
+ if (progressTableScrollTimer.value) {
+ cancelAnimationFrame(progressTableScrollTimer.value)
+ }
+ if (tableScrollTimeout.value) clearTimeout(tableScrollTimeout.value)
+ const tableContainer = progressTableRef.value
+ if (tableContainer?._pauseTimer) {
+ clearInterval(tableContainer._pauseTimer)
+ }
+})
+</script>
+
+<style scoped>
+.main-panel {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+}
+
+.panel-item-customers {
+ border: 1px solid #1a58b0;
+ padding: 18px;
+ width: 100%;
+ height: 428px;
+}
+
+.progress-table-container {
+ height: 320px;
+ overflow-y: auto;
+ overflow-x: hidden;
+ margin-top: 10px;
+ scrollbar-width: none;
+ -ms-overflow-style: none;
+}
+
+.progress-table-container::-webkit-scrollbar {
+ display: none;
+}
+
+.progress-table {
+ width: 100%;
+ border-collapse: collapse;
+ color: #b8c8e0;
+ font-size: 12px;
+ table-layout: fixed;
+}
+
+.progress-table thead {
+ position: sticky;
+ top: 0;
+ background-color: rgba(26, 88, 176, 0.9);
+ z-index: 10;
+}
+
+.progress-table th {
+ padding: 8px 6px;
+ text-align: left;
+ font-weight: 500;
+ border-bottom: 1px solid rgba(184, 200, 224, 0.3);
+ color: #b8c8e0;
+ font-size: 12px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.progress-table th:nth-child(1) {
+ width: 15%;
+}
+
+.progress-table th:nth-child(2) {
+ width: 15%;
+}
+
+.progress-table th:nth-child(3) {
+ width: 15%;
+}
+
+.progress-table th:nth-child(4) {
+ width: 12%;
+}
+
+.progress-table th:nth-child(5) {
+ width: 12%;
+}
+
+.progress-table th:nth-child(6) {
+ width: 31%;
+}
+
+.progress-table td {
+ padding: 8px 6px;
+ border-bottom: 1px solid rgba(184, 200, 224, 0.1);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ font-size: 12px;
+ transition: opacity 0.3s ease;
+}
+
+.progress-table tbody tr:hover {
+ background-color: rgba(184, 200, 224, 0.1);
+}
+
+.progress-table tbody tr.row-under-header {
+ opacity: 0.5;
+}
+
+.progress-table :deep(.el-progress) {
+ width: 100%;
+}
+
+.progress-table :deep(.el-progress-bar__outer) {
+ background-color: rgba(184, 200, 224, 0.2);
+}
+
+.progress-table :deep(.el-progress__text) {
+ color: #b8c8e0;
+ font-size: 11px;
+}
+</style>
--
Gitblit v1.9.3