Browse Source

所有大屏管理新增演示模式

yutianhao 3 days ago
parent
commit
bd0c52e0d2

+ 12 - 0
src/main/resources/static/screen/exec-screen.css

@@ -62,6 +62,18 @@ body {
 	background: rgba(8, 28, 54, 0.7);
 }
 
+.demo-toggle {
+	cursor: pointer;
+	font-family: inherit;
+	color: #9ec9ee;
+}
+
+.demo-toggle.is-on {
+	color: #ffd56a;
+	border-color: rgba(255, 184, 0, 0.75);
+	background: rgba(92, 58, 0, 0.72);
+}
+
 .hero-grid,
 .main-grid,
 .bottom-grid {

+ 91 - 2
src/main/resources/static/screen/exec-screen.js

@@ -277,13 +277,96 @@
 		renderBars("shiftBars", data.shifts, "今天没有班次数据");
 	}
 
+	var demoOn = false;
+
+	function statDayText(date) {
+		var day = new Date(date.getTime());
+		if (day.getHours() < 8) {
+			day.setDate(day.getDate() - 1);
+		}
+		return day.getFullYear() + "-" + pad(day.getMonth() + 1) + "-" + pad(day.getDate());
+	}
+
+	function trendLabel(date, offset) {
+		var day = new Date(date.getTime());
+		if (day.getHours() < 8) {
+			day.setDate(day.getDate() - 1);
+		}
+		day.setDate(day.getDate() - offset);
+		return (day.getMonth() + 1) + "/" + day.getDate();
+	}
+
+	function buildDemoData() {
+		var now = new Date();
+		var labels = [];
+		for (var i = 6; i >= 0; i--) {
+			labels.push(trendLabel(now, i));
+		}
+		return {
+			day: statDayText(now),
+			window: "08:00-次日08:00",
+			shiftTitle: "白班",
+			summary: { plan: 480, actual: 436, ok: 860, ng: 28, repair: 11, openAlarm: 1 },
+			kpi: {
+				achieve: 90.8,
+				fpy: 96.8,
+				oee: 85.4,
+				availability: 97.2,
+				performance: 90.8,
+				quality: 96.8,
+				shiftAchieve: 94.2
+			},
+			trend: {
+				labels: labels,
+				achieve: [88.2, 91.0, 86.4, 93.1, 89.5, 92.4, 90.8],
+				fpy: [96.1, 97.2, 95.4, 97.8, 96.6, 97.0, 96.8],
+				oee: [82.1, 85.4, 80.6, 88.2, 84.0, 86.7, 85.4],
+				output: [410, 455, 398, 468, 442, 460, 436],
+				planQty: [480, 480, 460, 500, 480, 480, 480]
+			},
+			shifts: [
+				{ title: "白班", range: "08:00-20:00", plan: 260, actual: 245, ok: 470, ng: 12, repair: 5, achieve: 94.2, fpy: 97.5, current: true },
+				{ title: "夜班", range: "20:00-08:00", plan: 220, actual: 191, ok: 390, ng: 16, repair: 6, achieve: 86.8, fpy: 96.1, current: false }
+			],
+			products: [
+				{ title: "冷板总成", plan: 180, actual: 172, achieve: 95.6 },
+				{ title: "框架总成", plan: 160, actual: 146, achieve: 91.3 },
+				{ title: "箱体总成", plan: 140, actual: 118, achieve: 84.3 }
+			]
+		};
+	}
+
+	function syncDemoButton() {
+		$("#demoToggle").toggleClass("is-on", demoOn).text(demoOn ? "退出演示" : "演示模式");
+	}
+
+	function setDemo(on) {
+		demoOn = !!on;
+		syncDemoButton();
+		if (refreshTimer) {
+			clearTimeout(refreshTimer);
+			refreshTimer = null;
+		}
+		if (demoOn) {
+			applyData(buildDemoData());
+			return;
+		}
+		loadData();
+	}
+
 	function loadData() {
+		if (demoOn) {
+			return;
+		}
 		$.ajax({
 			url: config.apiUrl,
 			type: "GET",
 			dataType: "json",
 			timeout: 20000
 		}).done(function (res) {
+			if (demoOn) {
+				return;
+			}
 			if (res && (res.result === "true" || res.result === true) && res.data) {
 				applyData(res.data);
 				scheduleRefresh(config.refreshInterval || 60000);
@@ -291,7 +374,9 @@
 				scheduleRefresh(5000);
 			}
 		}).fail(function () {
-			scheduleRefresh(5000);
+			if (!demoOn) {
+				scheduleRefresh(5000);
+			}
 		});
 	}
 
@@ -306,7 +391,11 @@
 		$("#screenTitle").text(config.title || "生产简报");
 		updateClock();
 		setInterval(updateClock, 1000);
-		loadData();
+		$("#demoToggle").on("click", function () {
+			setDemo(!demoOn);
+		});
+		var query = window.location.search || "";
+		setDemo(/(?:^|[?&])demo=1(?:&|$)/.test(query));
 		$(window).on("resize", function () {
 			if (chart) {
 				chart.resize();

+ 29 - 0
src/main/resources/static/screen/gp12-issue-screen.css

@@ -40,6 +40,35 @@ body {
 	height: 86px;
 }
 
+.header-meta {
+	justify-self: start;
+	display: flex;
+	align-items: center;
+	gap: 8px;
+}
+
+.meta-chip {
+	height: 32px;
+	padding: 0 14px;
+	border: 1px solid rgba(0, 143, 253, 0.4);
+	border-radius: 16px;
+	background: rgba(4, 24, 52, 0.72);
+	color: #9ad8ff;
+	font-size: 16px;
+	line-height: 32px;
+}
+
+.demo-toggle {
+	cursor: pointer;
+	font-family: inherit;
+}
+
+.demo-toggle.is-on {
+	color: #ffd56a;
+	border-color: rgba(255, 184, 0, 0.75);
+	background: rgba(92, 58, 0, 0.72);
+}
+
 .screen-title-new {
 	grid-column: 2;
 	color: #fff;

+ 95 - 1
src/main/resources/static/screen/gp12-issue-screen.js

@@ -114,7 +114,91 @@
 		startCarousel();
 	}
 
+	var demoOn = false;
+
+	function buildDemoData() {
+		return [
+			{
+				checkItem: "外观表面",
+				standard: "无划伤、无凹坑、无锈蚀,边角无明显毛刺",
+				method: "目视检查 + 手套触摸边角",
+				ngImages: [],
+				okImages: []
+			},
+			{
+				checkItem: "铭牌/标签",
+				standard: "内容正确、粘贴牢固、位置居中无歪斜",
+				method: "目视核对型号与工单信息",
+				ngImages: [],
+				okImages: []
+			},
+			{
+				checkItem: "紧固件扭矩",
+				standard: "关键螺丝扭矩符合工艺卡要求,无松动",
+				method: "扭矩扳手抽检",
+				ngImages: [],
+				okImages: []
+			},
+			{
+				checkItem: "密封装配",
+				standard: "密封圈无破损、无翻转,装配到位",
+				method: "目视 + 轻拉检查",
+				ngImages: [],
+				okImages: []
+			},
+			{
+				checkItem: "线束走向",
+				standard: "线束无压伤、无外露铜线,扎带间距规范",
+				method: "目视检查走线路径",
+				ngImages: [],
+				okImages: []
+			},
+			{
+				checkItem: "功能通断",
+				standard: "开关动作顺畅,指示灯状态与工艺一致",
+				method: "通电功能测试",
+				ngImages: [],
+				okImages: []
+			},
+			{
+				checkItem: "尺寸抽检",
+				standard: "关键尺寸在图纸公差范围内",
+				method: "卡尺/量具测量",
+				ngImages: [],
+				okImages: []
+			},
+			{
+				checkItem: "清洁度",
+				standard: "无油污、无铁屑、无异物残留",
+				method: "目视 + 白布擦拭抽检",
+				ngImages: [],
+				okImages: []
+			}
+		];
+	}
+
+	function syncDemoButton() {
+		$("#demoToggle").toggleClass("is-on", demoOn).text(demoOn ? "退出演示" : "演示模式");
+	}
+
+	function setDemo(on) {
+		demoOn = !!on;
+		syncDemoButton();
+		if (state.refreshTimer) {
+			clearTimeout(state.refreshTimer);
+			state.refreshTimer = null;
+		}
+		if (demoOn) {
+			applyData(buildDemoData());
+			return;
+		}
+		loadData();
+	}
+
 	function loadData() {
+		if (demoOn) {
+			return;
+		}
 		var apiUrl = config.apiUrl;
 		if (!apiUrl) {
 			applyData([]);
@@ -127,6 +211,9 @@
 			dataType: "json",
 			timeout: 20000
 		}).done(function (res) {
+			if (demoOn) {
+				return;
+			}
 			if (res && res.result === "true" && $.isArray(res.data)) {
 				applyData(res.data);
 				scheduleRefresh(config.refreshInterval || 60000);
@@ -137,6 +224,9 @@
 				scheduleRefresh(5000);
 			}
 		}).fail(function () {
+			if (demoOn) {
+				return;
+			}
 			// 保留上次成功数据,服务恢复后自动重试
 			scheduleRefresh(5000);
 		});
@@ -189,7 +279,11 @@
 		updateClock();
 		setInterval(updateClock, 1000);
 		bindPreview();
-		loadData();
+		$("#demoToggle").on("click", function () {
+			setDemo(!demoOn);
+		});
+		var query = window.location.search || "";
+		setDemo(/(?:^|[?&])demo=1(?:&|$)/.test(query));
 		fitScreen();
 		$(window).on("resize", fitScreen);
 	}

+ 71 - 2
src/main/resources/static/screen/oprno-record-screen.js

@@ -19,7 +19,8 @@
 		refreshing: false,
 		failCount: 0,
 		pollTimer: null,
-		hasLoaded: false
+		hasLoaded: false,
+		demoOn: false
 	};
 
 	$(function () {
@@ -27,7 +28,11 @@
 		initPage();
 		bindDelegatedEvents();
 		ensureConnTip();
-		refreshData();
+		$("#demoToggle").on("click", function () {
+			setDemo(!state.demoOn);
+		});
+		var query = window.location.search || "";
+		setDemo(/(?:^|[?&])demo=1(?:&|$)/.test(query));
 		setInterval(updateTime, 1000);
 		$(window).resize(function () {
 			changeZoom();
@@ -58,6 +63,10 @@
 			state.oprnos = normalizeOprnos($("#oprnoInput").val());
 			renderInput();
 			renderTags();
+			if (state.demoOn) {
+				applyDemoData();
+				return;
+			}
 			refreshData(true);
 		});
 		$("#clearBtn").on("click", function () {
@@ -93,6 +102,48 @@
 		state.scope = scope;
 		syncScopeTabs();
 		updateScopeLabels();
+		if (state.demoOn) {
+			applyDemoData();
+			return;
+		}
+		refreshData(true);
+	}
+
+	function buildDemoData() {
+		var base = state.scope === "today" ? 40 : 320;
+		var seed = [1.18, 0.92, 1.05, 0.78, 1.32, 0.88, 1.12, 0.95, 1.25, 0.82, 1.08, 0.9, 1.15, 0.86, 1.22, 0.98, 1.1];
+		return state.oprnos.map(function (oprno, index) {
+			var factor = seed[index % seed.length];
+			return {
+				oprno: oprno,
+				recordCount: Math.max(8, Math.round(base * factor + index * (state.scope === "today" ? 3 : 12)))
+			};
+		});
+	}
+
+	function applyDemoData() {
+		state.data = buildDemoData();
+		state.hasLoaded = true;
+		hideConnTip();
+		renderData();
+	}
+
+	function syncDemoButton() {
+		$("#demoToggle").toggleClass("is-on", state.demoOn).text(state.demoOn ? "退出演示" : "演示模式");
+	}
+
+	function setDemo(on) {
+		state.demoOn = !!on;
+		syncDemoButton();
+		if (state.pollTimer) {
+			clearTimeout(state.pollTimer);
+			state.pollTimer = null;
+		}
+		if (state.demoOn) {
+			state.refreshing = false;
+			applyDemoData();
+			return;
+		}
 		refreshData(true);
 	}
 
@@ -176,6 +227,10 @@
 		$("#addOprno").val("");
 		renderInput();
 		renderTags();
+		if (state.demoOn) {
+			applyDemoData();
+			return;
+		}
 		refreshData(true);
 	}
 
@@ -185,6 +240,10 @@
 		});
 		renderInput();
 		renderTags();
+		if (state.demoOn) {
+			applyDemoData();
+			return;
+		}
 		refreshData(true);
 	}
 
@@ -271,6 +330,10 @@
 	}
 
 	function refreshData(immediate) {
+		if (state.demoOn) {
+			applyDemoData();
+			return;
+		}
 		if (state.pollTimer) {
 			clearTimeout(state.pollTimer);
 			state.pollTimer = null;
@@ -298,6 +361,9 @@
 			timeout: 20000,
 			success: function (ret) {
 				state.refreshing = false;
+				if (state.demoOn) {
+					return;
+				}
 				if (ret && ret.result === "true") {
 					state.failCount = 0;
 					state.hasLoaded = true;
@@ -318,6 +384,9 @@
 			},
 			error: function (xhr) {
 				state.refreshing = false;
+				if (state.demoOn) {
+					return;
+				}
 				state.failCount += 1;
 				if (isAuthFailure(xhr)) {
 					tryRecoverSession();

+ 29 - 0
src/main/resources/static/screen/repair-screen.css

@@ -40,6 +40,35 @@ body {
 	height: 86px;
 }
 
+.header-meta {
+	justify-self: start;
+	display: flex;
+	align-items: center;
+	gap: 8px;
+}
+
+.meta-chip {
+	height: 32px;
+	padding: 0 14px;
+	border: 1px solid rgba(0, 143, 253, 0.4);
+	border-radius: 16px;
+	background: rgba(4, 24, 52, 0.72);
+	color: #9ad8ff;
+	font-size: 16px;
+	line-height: 32px;
+}
+
+.demo-toggle {
+	cursor: pointer;
+	font-family: inherit;
+}
+
+.demo-toggle.is-on {
+	color: #ffd56a;
+	border-color: rgba(255, 184, 0, 0.75);
+	background: rgba(92, 58, 0, 0.72);
+}
+
 .screen-title-new {
 	grid-column: 2;
 	color: #fff;

+ 57 - 1
src/main/resources/static/screen/repair-screen.js

@@ -129,6 +129,49 @@
 		startCarousel();
 	}
 
+	var demoOn = false;
+
+	function buildDemoData() {
+		var now = new Date();
+		function timeText(minutesAgo) {
+			var d = new Date(now.getTime() - minutesAgo * 60000);
+			return d.getFullYear() + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate()) +
+				" " + pad(d.getHours()) + ":" + pad(d.getMinutes()) + ":" + pad(d.getSeconds());
+		}
+		return {
+			summary: { pending: 6, repairing: 3, todayDone: 18, todayFail: 2 },
+			list: [
+				{ sn: "SN20261014001", oprno: "OP080", itemTitle: "外观划伤", typeText: "外观", remark: "左侧钣金轻微划痕", state: "1", stateText: "待返修", createBy: "张三", createDate: timeText(35) },
+				{ sn: "SN20261014002", oprno: "OP120", itemTitle: "螺丝松动", typeText: "装配", remark: "固定螺丝未拧紧", state: "1", stateText: "待返修", createBy: "李四", createDate: timeText(52) },
+				{ sn: "SN20261014003", oprno: "OP150", itemTitle: "尺寸超差", typeText: "尺寸", remark: "孔位偏差 0.3mm", state: "2", stateText: "待检查", createBy: "王五", createDate: timeText(78) },
+				{ sn: "SN20261014004", oprno: "OP060", itemTitle: "焊接气孔", typeText: "焊接", remark: "焊缝局部气孔", state: "1", stateText: "待返修", createBy: "赵六", createDate: timeText(95) },
+				{ sn: "SN20261014005", oprno: "OP200", itemTitle: "标签错贴", typeText: "标识", remark: "型号标签贴反", state: "2", stateText: "待检查", createBy: "钱七", createDate: timeText(110) },
+				{ sn: "SN20261014006", oprno: "OP090", itemTitle: "镀层脱落", typeText: "表面", remark: "边角镀层局部脱落", state: "1", stateText: "待返修", createBy: "孙八", createDate: timeText(128) },
+				{ sn: "SN20261014007", oprno: "OP170", itemTitle: "功能异常", typeText: "功能", remark: "开关通断不稳定", state: "2", stateText: "待检查", createBy: "周九", createDate: timeText(145) },
+				{ sn: "SN20261014008", oprno: "OP040", itemTitle: "毛刺残留", typeText: "外观", remark: "冲裁边毛刺未清理", state: "1", stateText: "待返修", createBy: "吴十", createDate: timeText(160) },
+				{ sn: "SN20261014009", oprno: "OP210", itemTitle: "密封不良", typeText: "装配", remark: "密封圈安装不到位", state: "1", stateText: "待返修", createBy: "郑一", createDate: timeText(175) }
+			]
+		};
+	}
+
+	function syncDemoButton() {
+		$("#demoToggle").toggleClass("is-on", demoOn).text(demoOn ? "退出演示" : "演示模式");
+	}
+
+	function setDemo(on) {
+		demoOn = !!on;
+		syncDemoButton();
+		if (state.refreshTimer) {
+			clearTimeout(state.refreshTimer);
+			state.refreshTimer = null;
+		}
+		if (demoOn) {
+			applyData(buildDemoData());
+			return;
+		}
+		loadData();
+	}
+
 	function showLoadError(message) {
 		renderSummary({});
 		state.items = [];
@@ -141,6 +184,9 @@
 	}
 
 	function loadData() {
+		if (demoOn) {
+			return;
+		}
 		var apiUrl = config.apiUrl;
 		if (!apiUrl) {
 			showLoadError("未配置数据接口");
@@ -153,6 +199,9 @@
 			dataType: "json",
 			timeout: 20000
 		}).done(function (res) {
+			if (demoOn) {
+				return;
+			}
 			if (res && (res.result === "true" || res.result === true) && res.data) {
 				applyData(res.data);
 				scheduleRefresh(config.refreshInterval || 60000);
@@ -161,6 +210,9 @@
 				scheduleRefresh(5000);
 			}
 		}).fail(function (xhr) {
+			if (demoOn) {
+				return;
+			}
 			var msg = "数据加载失败";
 			if (xhr && xhr.status === 401) {
 				msg = "接口未授权,请检查匿名访问配置";
@@ -210,7 +262,11 @@
 		$("#screenTitle").text(config.title || "返修区看板");
 		updateClock();
 		setInterval(updateClock, 1000);
-		loadData();
+		$("#demoToggle").on("click", function () {
+			setDemo(!demoOn);
+		});
+		var query = window.location.search || "";
+		setDemo(/(?:^|[?&])demo=1(?:&|$)/.test(query));
 		fitScreen();
 		$(window).on("resize", fitScreen);
 	}

+ 3 - 2
src/main/resources/views/modules/mes/mesScreen11.html

@@ -5,7 +5,7 @@
 	<meta name="viewport" content="width=device-width, initial-scale=1.0">
 	<meta http-equiv="X-UA-Compatible" content="ie=edge">
 	<title>生产简报</title>
-	<link rel="stylesheet" href="${ctxStatic}/screen/exec-screen.css?v=202609303">
+	<link rel="stylesheet" href="${ctxStatic}/screen/exec-screen.css?v=20261014">
 </head>
 <body>
 <div class="exec-page">
@@ -13,6 +13,7 @@
 		<div class="header-side">
 			<span class="meta-chip" id="statDay">统计日 --</span>
 			<span class="meta-chip" id="shiftChip">当前班次 --</span>
+			<button type="button" class="meta-chip demo-toggle" id="demoToggle">演示模式</button>
 		</div>
 		<h1 id="screenTitle">生产简报</h1>
 		<div class="header-side is-right">
@@ -78,7 +79,7 @@
 		apiUrl: "${ctx}/mes/mesProduct/screenKpiData"
 	};
 </script>
-<script src="${ctxStatic}/screen/exec-screen.js?v=202609303"></script>
+<script src="${ctxStatic}/screen/exec-screen.js?v=20261014"></script>
 <script src="${ctxStatic}/screen/screen-fullscreen.js?v=20261010"></script>
 </body>
 </html>

+ 31 - 2
src/main/resources/views/modules/mes/mesScreen5.html

@@ -123,6 +123,32 @@
 			flex: 1;
 			min-width: 0;
 		}
+
+		.header-right-tools {
+			justify-self: end;
+			display: flex;
+			align-items: center;
+			gap: 10px;
+		}
+
+		.meta-chip.demo-toggle {
+			height: 32px;
+			padding: 0 14px;
+			border: 1px solid rgba(0, 143, 253, 0.4);
+			border-radius: 16px;
+			background: rgba(4, 24, 52, 0.72);
+			color: #9ad8ff;
+			font-size: 16px;
+			line-height: 32px;
+			cursor: pointer;
+			font-family: inherit;
+		}
+
+		.meta-chip.demo-toggle.is-on {
+			color: #ffd56a;
+			border-color: rgba(255, 184, 0, 0.75);
+			background: rgba(92, 58, 0, 0.72);
+		}
 	</style>
 </head>
 <body>
@@ -137,7 +163,10 @@
 				</div>
 			</div>
 			<div class="screen-title-new" id="screenTitle"></div>
-			<div class="screen-time" id="screenTime"></div>
+			<div class="header-right-tools">
+				<button type="button" class="meta-chip demo-toggle" id="demoToggle">演示模式</button>
+				<div class="screen-time" id="screenTime"></div>
+			</div>
 		</div>
 		<div class="summary-row">
 			<div class="summary-item"><div class="summary-label">工位数量</div><div class="summary-value" id="oprnoTotal">0</div></div>
@@ -186,7 +215,7 @@
 		defaultOprnos: ["OP010", "OP050", "OP060", "OP070", "OP080", "OP090", "OP100"]
 	};
 </script>
-<script src="${ctxStatic}/screen/oprno-record-screen.js?v=20250731"></script>
+<script src="${ctxStatic}/screen/oprno-record-screen.js?v=20261014"></script>
 <script src="${ctxStatic}/screen/screen-fullscreen.js?v=20261010"></script>
 </body>
 </html>

+ 31 - 2
src/main/resources/views/modules/mes/mesScreen6.html

@@ -123,6 +123,32 @@
 			flex: 1;
 			min-width: 0;
 		}
+
+		.header-right-tools {
+			justify-self: end;
+			display: flex;
+			align-items: center;
+			gap: 10px;
+		}
+
+		.meta-chip.demo-toggle {
+			height: 32px;
+			padding: 0 14px;
+			border: 1px solid rgba(0, 143, 253, 0.4);
+			border-radius: 16px;
+			background: rgba(4, 24, 52, 0.72);
+			color: #9ad8ff;
+			font-size: 16px;
+			line-height: 32px;
+			cursor: pointer;
+			font-family: inherit;
+		}
+
+		.meta-chip.demo-toggle.is-on {
+			color: #ffd56a;
+			border-color: rgba(255, 184, 0, 0.75);
+			background: rgba(92, 58, 0, 0.72);
+		}
 	</style>
 </head>
 <body>
@@ -137,7 +163,10 @@
 				</div>
 			</div>
 			<div class="screen-title-new" id="screenTitle"></div>
-			<div class="screen-time" id="screenTime"></div>
+			<div class="header-right-tools">
+				<button type="button" class="meta-chip demo-toggle" id="demoToggle">演示模式</button>
+				<div class="screen-time" id="screenTime"></div>
+			</div>
 		</div>
 		<div class="summary-row">
 			<div class="summary-item"><div class="summary-label">工位数量</div><div class="summary-value" id="oprnoTotal">0</div></div>
@@ -186,7 +215,7 @@
 		defaultOprnos: ["OP110", "OP120", "OP130", "OP140", "OP150", "OP160", "OP170", "OP180", "OP190"]
 	};
 </script>
-<script src="${ctxStatic}/screen/oprno-record-screen.js?v=20250731"></script>
+<script src="${ctxStatic}/screen/oprno-record-screen.js?v=20261014"></script>
 <script src="${ctxStatic}/screen/screen-fullscreen.js?v=20261010"></script>
 </body>
 </html>

+ 31 - 2
src/main/resources/views/modules/mes/mesScreen7.html

@@ -123,6 +123,32 @@
 			flex: 1;
 			min-width: 0;
 		}
+
+		.header-right-tools {
+			justify-self: end;
+			display: flex;
+			align-items: center;
+			gap: 10px;
+		}
+
+		.meta-chip.demo-toggle {
+			height: 32px;
+			padding: 0 14px;
+			border: 1px solid rgba(0, 143, 253, 0.4);
+			border-radius: 16px;
+			background: rgba(4, 24, 52, 0.72);
+			color: #9ad8ff;
+			font-size: 16px;
+			line-height: 32px;
+			cursor: pointer;
+			font-family: inherit;
+		}
+
+		.meta-chip.demo-toggle.is-on {
+			color: #ffd56a;
+			border-color: rgba(255, 184, 0, 0.75);
+			background: rgba(92, 58, 0, 0.72);
+		}
 	</style>
 </head>
 <body>
@@ -137,7 +163,10 @@
 				</div>
 			</div>
 			<div class="screen-title-new" id="screenTitle"></div>
-			<div class="screen-time" id="screenTime"></div>
+			<div class="header-right-tools">
+				<button type="button" class="meta-chip demo-toggle" id="demoToggle">演示模式</button>
+				<div class="screen-time" id="screenTime"></div>
+			</div>
 		</div>
 		<div class="summary-row">
 			<div class="summary-item"><div class="summary-label">工位数量</div><div class="summary-value" id="oprnoTotal">0</div></div>
@@ -186,7 +215,7 @@
 		defaultOprnos: ["OP200", "OP210", "OP220", "OP230", "OP240", "OP250", "OP260", "OP270", "OP280", "OP290", "OP300", "OP310", "OP320", "OP330", "OP340", "OP350", "OP360"]
 	};
 </script>
-<script src="${ctxStatic}/screen/oprno-record-screen.js?v=20250731"></script>
+<script src="${ctxStatic}/screen/oprno-record-screen.js?v=20261014"></script>
 <script src="${ctxStatic}/screen/screen-fullscreen.js?v=20261010"></script>
 </body>
 </html>

+ 5 - 3
src/main/resources/views/modules/mes/mesScreen8.html

@@ -5,14 +5,16 @@
 	<meta name="viewport" content="width=device-width, initial-scale=1.0">
 	<meta http-equiv="X-UA-Compatible" content="ie=edge">
 	<title>GP12不良问题点看板</title>
-	<link rel="stylesheet" href="${ctxStatic}/screen/gp12-issue-screen.css?v=202607154">
+	<link rel="stylesheet" href="${ctxStatic}/screen/gp12-issue-screen.css?v=20261014">
 </head>
 <body>
 <div class="screen-page">
 	<img class="bg" src="${ctxStatic}/screen/imgs/bg3.png" alt="">
 	<div class="screen-content">
 		<div class="screen-header">
-			<div></div>
+			<div class="header-meta">
+				<button type="button" class="meta-chip demo-toggle" id="demoToggle">演示模式</button>
+			</div>
 			<div class="screen-title-new" id="screenTitle">GP12不良问题点看板</div>
 			<div class="screen-time" id="screenTime"></div>
 		</div>
@@ -49,7 +51,7 @@
 		apiUrl: "${ctx}/mes/mesProduct/screenGp12IssueList"
 	};
 </script>
-<script src="${ctxStatic}/screen/gp12-issue-screen.js?v=202607153"></script>
+<script src="${ctxStatic}/screen/gp12-issue-screen.js?v=20261014"></script>
 <script src="${ctxStatic}/screen/screen-fullscreen.js?v=20261010"></script>
 </body>
 </html>

+ 5 - 3
src/main/resources/views/modules/mes/mesScreen9.html

@@ -5,14 +5,16 @@
 	<meta name="viewport" content="width=device-width, initial-scale=1.0">
 	<meta http-equiv="X-UA-Compatible" content="ie=edge">
 	<title>返修区看板</title>
-	<link rel="stylesheet" href="${ctxStatic}/screen/repair-screen.css?v=202607264">
+	<link rel="stylesheet" href="${ctxStatic}/screen/repair-screen.css?v=20261014">
 </head>
 <body>
 <div class="screen-page">
 	<img class="bg" src="${ctxStatic}/screen/imgs/bg3.png" alt="">
 	<div class="screen-content">
 		<div class="screen-header">
-			<div></div>
+			<div class="header-meta">
+				<button type="button" class="meta-chip demo-toggle" id="demoToggle">演示模式</button>
+			</div>
 			<div class="screen-title-new" id="screenTitle">返修区看板</div>
 			<div class="screen-time" id="screenTime"></div>
 		</div>
@@ -70,7 +72,7 @@
 		apiUrl: "${ctx}/mes/mesProduct/screenRepairData"
 	};
 </script>
-<script src="${ctxStatic}/screen/repair-screen.js?v=202607264"></script>
+<script src="${ctxStatic}/screen/repair-screen.js?v=20261014"></script>
 <script src="${ctxStatic}/screen/screen-fullscreen.js?v=20261010"></script>
 </body>
 </html>