From 73d112ddc9bdfeb7eaaf3b7308cfb4dbeb470c89 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期五, 07 八月 2026 17:28:10 +0800
Subject: [PATCH] 天津_意博凯信 1.备件管理-新增独立备件出库功能入口,支持直接完成备件领用出库操作。 2.设备保养-支持按日期分组折叠展示,分组条目可展开查看明细;单条明细行末尾增加【保养完成】操作按钮,可单独办结单条保养任务;折叠汇总状态下,新增【一键保养完成】批量操作按钮,快速办结当前分组全部保养项。 3.设备巡检-按日期分组折叠展示,分组支持展开查看明细;每条巡检明细后增设【巡检完成】按钮,支持单独办结单条巡检记录;分组折叠未展开状态下,提供【一键巡检完成】批量操作,统一办结该分组下所有巡检任务。 4.增值税对比-优化数据展示逻辑,列表展示各订单增值税明细;页面右侧新增柱状可视化图表,直观对比进销项增值税数据。 5.管理驾驶舱-指标修改:将顶部「销售产品数」调整为「销售订单数」;时间筛选:页面左上角新增日期范围下拉筛选控件,所有统计指标标注当前筛选时段;

---
 src/views/reportAnalysis/PSIDataAnalysis/index.vue |  458 ++++++++++++++++++++++++++++++++-------------------------
 1 files changed, 258 insertions(+), 200 deletions(-)

diff --git a/src/views/reportAnalysis/PSIDataAnalysis/index.vue b/src/views/reportAnalysis/PSIDataAnalysis/index.vue
index f81e482..ab6840c 100644
--- a/src/views/reportAnalysis/PSIDataAnalysis/index.vue
+++ b/src/views/reportAnalysis/PSIDataAnalysis/index.vue
@@ -1,50 +1,66 @@
 <template>
-  <div class="scale-container">
-    <div class="data-dashboard" :style="{ transform: `scale(${scaleRatio})` }">
-    <!-- 鍏ㄥ睆鎸夐挳 - 绉诲姩鍒板乏涓婅 -->
-    <button class="fullscreen-btn" @click="toggleFullscreen" :title="isFullscreen ? '閫�鍑哄叏灞�' : '鍏ㄥ睆鏄剧ず'">
-      <svg v-if="!isFullscreen" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-        <path d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3"/>
-      </svg>
-      <svg v-else width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
-        <path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/>
-      </svg>
-    </button>
-
-    <!-- 椤堕儴鏍囬鏍� -->
-    <div class="dashboard-header">
-      <div class="factory-name">PSI 鏁版嵁鍒嗘瀽</div>
-    </div>
-
-    <!-- 涓昏鍐呭鍖哄煙 -->
-    <div class="dashboard-content">
-      <!-- 宸︿晶鍖哄煙 -->
-      <div class="left-panel">
-        <LeftTop />
-
-        <LeftBottom />
-      </div>
-
-      <!-- 涓棿鍖哄煙 -->
-      <div class="center-panel">
-        <CenterTop />
-        <CenterCenter/>
-        <CenterBottom />
-      </div>
-
-      <!-- 鍙充晶鍖哄煙 -->
-      <div class="right-panel">
-        <RightBottom />
-        <RightTop />
-      </div>
-    </div>
-    </div>
-  </div>
+	<div class="scale-container">
+		<div class="data-dashboard" :style="{ transform: `scale(${scaleRatio})` }">
+			<!-- 鍏ㄥ睆鎸夐挳 - 绉诲姩鍒板乏涓婅 -->
+			<button class="fullscreen-btn" @click="toggleFullscreen" :title="isFullscreen ? '閫�鍑哄叏灞�' : '鍏ㄥ睆鏄剧ず'">
+				<svg v-if="!isFullscreen" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+					<path d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3"/>
+				</svg>
+				<svg v-else width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+					<path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/>
+				</svg>
+			</button>
+			
+			<!-- 椤堕儴鏍囬鏍� -->
+			<div class="dashboard-header">
+				<div class="header-left"></div>
+				<div class="factory-name">PSI 鏁版嵁鍒嗘瀽</div>
+				<div class="header-right">
+					<div class="date-picker-wrapper">
+						<el-date-picker
+							v-model="psiDateRange"
+							type="daterange"
+							range-separator="鑷�"
+							start-placeholder="寮�濮嬫棩鏈�"
+							end-placeholder="缁撴潫鏃ユ湡"
+							value-format="YYYY-MM-DD"
+							:shortcuts="dateShortcuts"
+							@change="onDateRangeChange"
+						/>
+					</div>
+				</div>
+			</div>
+			
+			<!-- 涓昏鍐呭鍖哄煙 -->
+			<div class="dashboard-content">
+				<!-- 宸︿晶鍖哄煙 -->
+				<div class="left-panel">
+					<LeftTop />
+					
+					<LeftBottom />
+				</div>
+				
+				<!-- 涓棿鍖哄煙 -->
+				<div class="center-panel">
+					<CenterTop />
+					<CenterCenter/>
+					<CenterBottom />
+				</div>
+				
+				<!-- 鍙充晶鍖哄煙 -->
+				<div class="right-panel">
+					<RightBottom />
+					<RightTop />
+				</div>
+			</div>
+		</div>
+	</div>
 </template>
 
 <script setup>
-import { ref, onMounted, onBeforeUnmount, nextTick, provide } from 'vue'
+import { ref, onMounted, onBeforeUnmount, nextTick, provide, reactive } from 'vue'
 import autofit from 'autofit.js'
+import dayjs from 'dayjs'
 import LeftBottom from './components/left-bottom.vue'
 import CenterCenter from './components/center-center.vue'
 import RightTop from '../dataDashboard/components/basic/right-top.vue'
@@ -66,6 +82,27 @@
 // 鐢ㄦ埛store
 const userStore = useUserStore()
 
+// 鏃ユ湡鑼冨洿绛涢��
+const psiDateRange = ref([])
+const psiDateRangeParams = ref({})
+const dateShortcuts = [
+	{ text: '鏈湀', value: () => { const start = dayjs().startOf('month').format('YYYY-MM-DD'); const end = dayjs().format('YYYY-MM-DD'); return [start, end] } },
+	{ text: '杩�7澶�', value: () => { const start = dayjs().subtract(6, 'day').format('YYYY-MM-DD'); const end = dayjs().format('YYYY-MM-DD'); return [start, end] } },
+	{ text: '杩�30澶�', value: () => { const start = dayjs().subtract(29, 'day').format('YYYY-MM-DD'); const end = dayjs().format('YYYY-MM-DD'); return [start, end] } },
+	{ text: '杩�90澶�', value: () => { const start = dayjs().subtract(89, 'day').format('YYYY-MM-DD'); const end = dayjs().format('YYYY-MM-DD'); return [start, end] } },
+]
+
+const onDateRangeChange = (val) => {
+	if (val && val.length === 2) {
+		psiDateRangeParams.value = { startDate: val[0], endDate: val[1] }
+	} else {
+		psiDateRangeParams.value = {}
+	}
+	dataDashboardRefreshTick.value++
+}
+
+provide('psiDateRange', psiDateRangeParams)
+
 /** 涓� dataDashboard 鍏辩敤娉ㄥ叆鍚嶏紝瀛愮粍浠讹紙鍚鐢ㄧ殑 right-top/right-bottom锛夋瘡鍒嗛挓鍒锋柊 */
 const DASHBOARD_REFRESH_MS = 60 * 1000
 const dataDashboardRefreshTick = ref(0)
@@ -74,232 +111,253 @@
 
 // 璁$畻缂╂斁姣斾緥
 const calculateScale = () => {
-  const container = document.querySelector('.scale-container')
-  if (!container) return
-
-  // 鑾峰彇瀹瑰櫒鐨勫疄闄呭昂瀵�
-  const rect = container.getBoundingClientRect?.()
-  const containerWidth = container.clientWidth || rect?.width || window.innerWidth
-  const containerHeight = container.clientHeight || rect?.height || window.innerHeight
-
-  // 璁$畻瀹介珮缂╂斁姣斾緥锛屽彇杈冨皬鍊间互淇濊瘉鍐呭瀹屾暣鏄剧ず锛堢瓑姣旂缉鏀撅級
-  const scaleX = containerWidth / designWidth
-  const scaleY = containerHeight / designHeight
-  scaleRatio.value = Math.min(scaleX, scaleY)
+	const container = document.querySelector('.scale-container')
+	if (!container) return
+	
+	// 鑾峰彇瀹瑰櫒鐨勫疄闄呭昂瀵�
+	const rect = container.getBoundingClientRect?.()
+	const containerWidth = container.clientWidth || rect?.width || window.innerWidth
+	const containerHeight = container.clientHeight || rect?.height || window.innerHeight
+	
+	// 璁$畻瀹介珮缂╂斁姣斾緥锛屽彇杈冨皬鍊间互淇濊瘉鍐呭瀹屾暣鏄剧ず锛堢瓑姣旂缉鏀撅級
+	const scaleX = containerWidth / designWidth
+	const scaleY = containerHeight / designHeight
+	scaleRatio.value = Math.min(scaleX, scaleY)
 }
 
 // 绐楀彛澶у皬鍙樺寲澶勭悊
 const handleResize = () => {
-  // 寤惰繜鎵ц锛岀‘淇滵OM鏇存柊瀹屾垚
-  setTimeout(() => {
-    calculateScale()
-  }, 100)
+	// 寤惰繜鎵ц锛岀‘淇滵OM鏇存柊瀹屾垚
+	setTimeout(() => {
+		calculateScale()
+	}, 100)
 }
 
 // 鍏ㄥ睆鍔熻兘瀹炵幇 - 閽堝scale-container鍏冪礌
 const toggleFullscreen = () => {
-  const element = document.querySelector('.scale-container')
-
-  if (!element) return
-
-  if (!isFullscreen.value) {
-    if (element.requestFullscreen) {
-      element.requestFullscreen()
-    } else if (element.webkitRequestFullscreen) {
-      element.webkitRequestFullscreen()
-    } else if (element.msRequestFullscreen) {
-      element.msRequestFullscreen()
-    }
-  } else {
-    if (document.exitFullscreen) {
-      document.exitFullscreen()
-    } else if (document.webkitExitFullscreen) {
-      document.webkitExitFullscreen()
-    } else if (document.msExitFullscreen) {
-      document.msExitFullscreen()
-    }
-  }
+	const element = document.querySelector('.scale-container')
+	
+	if (!element) return
+	
+	if (!isFullscreen.value) {
+		if (element.requestFullscreen) {
+			element.requestFullscreen()
+		} else if (element.webkitRequestFullscreen) {
+			element.webkitRequestFullscreen()
+		} else if (element.msRequestFullscreen) {
+			element.msRequestFullscreen()
+		}
+	} else {
+		if (document.exitFullscreen) {
+			document.exitFullscreen()
+		} else if (document.webkitExitFullscreen) {
+			document.webkitExitFullscreen()
+		} else if (document.msExitFullscreen) {
+			document.msExitFullscreen()
+		}
+	}
 }
 
 // 鐩戝惉鍏ㄥ睆鍙樺寲浜嬩欢
 const handleFullscreenChange = () => {
-  const fullscreenElement = document.fullscreenElement || 
-                           document.webkitFullscreenElement || 
-                           document.msFullscreenElement
-  isFullscreen.value = fullscreenElement && fullscreenElement.classList.contains('scale-container')
-
-  // 鍏ㄥ睆鐘舵�佸彉鍖栨椂锛屽欢杩熼噸鏂拌绠楃缉鏀炬瘮渚嬶紙纭繚DOM鏇存柊瀹屾垚锛�
-  setTimeout(() => {
-    calculateScale()
-  }, 200)
+	const fullscreenElement = document.fullscreenElement ||
+		document.webkitFullscreenElement ||
+		document.msFullscreenElement
+	isFullscreen.value = fullscreenElement && fullscreenElement.classList.contains('scale-container')
+	
+	// 鍏ㄥ睆鐘舵�佸彉鍖栨椂锛屽欢杩熼噸鏂拌绠楃缉鏀炬瘮渚嬶紙纭繚DOM鏇存柊瀹屾垚锛�
+	setTimeout(() => {
+		calculateScale()
+	}, 200)
 }
 
 // 鐢熷懡鍛ㄦ湡閽╁瓙
 onMounted(() => {
-  // 浣跨敤nextTick纭繚DOM瀹屽叏娓叉煋鍚庡啀鍒濆鍖�
-  nextTick(() => {
-    // 璁$畻鍒濆缂╂斁姣斾緥
-    calculateScale()
-  })
-
-  window.addEventListener('resize', handleResize)
-  window.addEventListener('fullscreenchange', handleFullscreenChange)
-  window.addEventListener('webkitfullscreenchange', handleFullscreenChange)
-  window.addEventListener('MSFullscreenChange', handleFullscreenChange)
-
-  dashboardPollTimer = setInterval(() => {
-    dataDashboardRefreshTick.value++
-  }, DASHBOARD_REFRESH_MS)
+	// 浣跨敤nextTick纭繚DOM瀹屽叏娓叉煋鍚庡啀鍒濆鍖�
+	nextTick(() => {
+		// 璁$畻鍒濆缂╂斁姣斾緥
+		calculateScale()
+	})
+	
+	window.addEventListener('resize', handleResize)
+	window.addEventListener('fullscreenchange', handleFullscreenChange)
+	window.addEventListener('webkitfullscreenchange', handleFullscreenChange)
+	window.addEventListener('MSFullscreenChange', handleFullscreenChange)
+	
+	dashboardPollTimer = setInterval(() => {
+		dataDashboardRefreshTick.value++
+	}, DASHBOARD_REFRESH_MS)
 })
 
 onBeforeUnmount(() => {
-  if (dashboardPollTimer) {
-    clearInterval(dashboardPollTimer)
-    dashboardPollTimer = null
-  }
-  window.removeEventListener('resize', handleResize)
-  window.removeEventListener('fullscreenchange', handleFullscreenChange)
-  window.removeEventListener('webkitfullscreenchange', handleFullscreenChange)
-  window.removeEventListener('MSFullscreenChange', handleFullscreenChange)
-  // 绉婚櫎鎴戜滑娣诲姞鐨刟utofit鍔ㄦ�佽皟鏁寸洃鍚櫒
-  if (window._autofitUpdateHandler) {
-    window.removeEventListener('resize', window._autofitUpdateHandler)
-    delete window._autofitUpdateHandler
-  }
-  // 鍏抽棴autofit
-  autofit.off()
+	if (dashboardPollTimer) {
+		clearInterval(dashboardPollTimer)
+		dashboardPollTimer = null
+	}
+	window.removeEventListener('resize', handleResize)
+	window.removeEventListener('fullscreenchange', handleFullscreenChange)
+	window.removeEventListener('webkitfullscreenchange', handleFullscreenChange)
+	window.removeEventListener('MSFullscreenChange', handleFullscreenChange)
+	// 绉婚櫎鎴戜滑娣诲姞鐨刟utofit鍔ㄦ�佽皟鏁寸洃鍚櫒
+	if (window._autofitUpdateHandler) {
+		window.removeEventListener('resize', window._autofitUpdateHandler)
+		delete window._autofitUpdateHandler
+	}
+	// 鍏抽棴autofit
+	autofit.off()
 })
 </script>
 
 <style scoped>
 /* 澶栭儴缂╂斁瀹瑰櫒 - 鍗犳嵁鏁翠釜瑙嗗彛 */
 .scale-container {
-position: relative;
-width: 100%;
-/* 椤甸潰鍦ㄥ父瑙勫竷灞�涓嬶紙鏈夐《鏍忥級榛樿鍑忓幓 84px锛岄伩鍏嶅唴瀹硅瑁佸垏 */
-height: calc(100vh - 84px);
-display: flex;
-align-items: center;
-justify-content: center;
-background-color: #000;
-overflow: hidden;
+	position: relative;
+	width: 100%;
+	/* 椤甸潰鍦ㄥ父瑙勫竷灞�涓嬶紙鏈夐《鏍忥級榛樿鍑忓幓 84px锛岄伩鍏嶅唴瀹硅瑁佸垏 */
+	height: calc(100vh - 84px);
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	background-color: #000;
+	overflow: hidden;
 }
 
 /* 鍐呴儴鍐呭鍖哄煙 - 鍥哄畾璁捐灏哄 */
 .data-dashboard {
-position: relative;
-width: 1920px;
-height: 1080px;
-background-image: url("@/assets/BI/backImage@2x.png");
-background-size: cover;
-background-position: center;
-background-repeat: no-repeat;
-transform-origin: center center;
+	position: relative;
+	width: 1920px;
+	height: 1080px;
+	background-image: url("@/assets/BI/backImage@2x.png");
+	background-size: cover;
+	background-position: center;
+	background-repeat: no-repeat;
+	transform-origin: center center;
 }
 
 /* 鍏ㄥ睆鐘舵�佺殑鏍峰紡 - 浣滅敤浜巗cale-container */
 .scale-container:fullscreen {
-width: 100vw;
-height: 100vh;
-margin: 0;
-padding: 0;
-background-color: #000;
-z-index: 9999;
+	width: 100vw;
+	height: 100vh;
+	margin: 0;
+	padding: 0;
+	background-color: #000;
+	z-index: 9999;
 }
 
 /* Webkit娴忚鍣ㄥ墠缂� */
 .scale-container:-webkit-full-screen {
-width: 100vw;
-height: 100vh;
-margin: 0;
-padding: 0;
-background-color: #000;
-z-index: 9999;
+	width: 100vw;
+	height: 100vh;
+	margin: 0;
+	padding: 0;
+	background-color: #000;
+	z-index: 9999;
 }
 
 /* MS娴忚鍣ㄥ墠缂� */
 .scale-container:-ms-fullscreen {
-width: 100vw;
-height: 100vh;
-margin: 0;
-padding: 0;
-background-color: #000;
-z-index: 9999;
+	width: 100vw;
+	height: 100vh;
+	margin: 0;
+	padding: 0;
+	background-color: #000;
+	z-index: 9999;
 }
 
 
 .dashboard-header {
-position: relative;
-z-index: 1;
-height: 86px;
-background-image: url("@/assets/BI/biaoti.png");
-background-size: cover;
-background-repeat: no-repeat;
-display: flex;
-align-items: center;
-justify-content: center;
+	position: relative;
+	z-index: 1;
+	height: 86px;
+	background-image: url("@/assets/BI/biaoti.png");
+	background-size: cover;
+	background-repeat: no-repeat;
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+}
+
+.header-left {
+	width: 300px;
+	padding-left: 20px;
+	display: flex;
+	align-items: center;
+}
+
+.header-right {
+	width: 300px;
+	display: flex;
+	align-items: center;
+	justify-content: flex-end;
+	padding-right: 20px;
+}
+
+.date-picker-wrapper {
+	/* 鏃ユ湡閫夋嫨鍣ㄥ湪澶у睆涓婄殑鏍峰紡 */
+	--el-fill-color-blank: rgba(0, 20, 60, 0.8);
+	--el-border-color: rgba(0, 212, 255, 0.3);
+	--el-text-color-regular: #00d4ff;
+	--el-color-primary: #00d4ff;
 }
 
 .factory-name {
-font-weight: 600;
-font-size: 52px;
-color: #FFFFFF;
-top: 16px;
-position: absolute;
+	font-weight: 600;
+	font-size: 52px;
+	color: #FFFFFF;
 }
 
 .fullscreen-btn {
-position: absolute;
-top: 10px;
-left: 20px;
-width: 40px;
-height: 40px;
-background: rgba(0, 20, 60, 0.8);
-border: 1px solid rgba(0, 212, 255, 0.3);
-border-radius: 6px;
-color: #00d4ff;
-cursor: pointer;
-display: flex;
-align-items: center;
-justify-content: center;
-transition: all 0.3s;
-z-index: 10000;
+	position: absolute;
+	top: 10px;
+	left: 20px;
+	width: 40px;
+	height: 40px;
+	background: rgba(0, 20, 60, 0.8);
+	border: 1px solid rgba(0, 212, 255, 0.3);
+	border-radius: 6px;
+	color: #00d4ff;
+	cursor: pointer;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	transition: all 0.3s;
+	z-index: 10000;
 }
 
 .fullscreen-btn:hover {
-background: rgba(0, 30, 90, 0.9);
-border-color: rgba(0, 212, 255, 0.5);
+	background: rgba(0, 30, 90, 0.9);
+	border-color: rgba(0, 212, 255, 0.5);
 }
 
 .dashboard-content {
-position: relative;
-z-index: 1;
-display: flex;
-gap: 30px;
-padding: 0 30px;
-height: calc(100% - 86px);
-overflow: hidden;
+	position: relative;
+	z-index: 1;
+	display: flex;
+	gap: 30px;
+	padding: 0 30px;
+	height: calc(100% - 86px);
+	overflow: hidden;
 }
 
 /* 纭繚鍚勯潰鏉胯兘澶熸纭樉绀� */
 .left-panel, .center-panel, .right-panel {
-overflow: hidden;
+	overflow: hidden;
 }
 
 .left-panel,
 .right-panel {
-flex: 1;
-display: flex;
-flex-direction: column;
-gap: 24px;
-width: 520px;
+	flex: 1;
+	display: flex;
+	flex-direction: column;
+	gap: 24px;
+	width: 520px;
 }
 
 .center-panel {
-flex: 1.5;
-display: flex;
-flex-direction: column;
-gap: 20px;
+	flex: 1.5;
+	display: flex;
+	flex-direction: column;
+	gap: 20px;
 }
 
 </style>
\ No newline at end of file

--
Gitblit v1.9.3