diff --git a/client/src/sections/wuyuanbiaoba/components/AdvancedConfigAuth.jsx b/client/src/sections/wuyuanbiaoba/components/AdvancedConfigAuth.jsx
new file mode 100644
index 0000000..d64a25f
--- /dev/null
+++ b/client/src/sections/wuyuanbiaoba/components/AdvancedConfigAuth.jsx
@@ -0,0 +1,114 @@
+import React, { useState } from 'react';
+import { Card, Input, Button, message } from 'antd';
+import { LockOutlined } from '@ant-design/icons';
+
+/**
+ * 高级配置密码验证组件
+ */
+const AdvancedConfigAuth = ({ verifyPassword, onUnlock }) => {
+ const [password, setPassword] = useState('');
+ const [loading, setLoading] = useState(false);
+
+ const handleVerify = async () => {
+ if (!password) {
+ message.warning('请输入密码');
+ return;
+ }
+
+ setLoading(true);
+ try {
+ const isValid = await verifyPassword(password);
+
+ if (isValid) {
+ message.success('密码正确,已解锁高级配置');
+ onUnlock && onUnlock();
+ } else {
+ message.error('密码错误,请重试');
+ setPassword('');
+ }
+ } catch (error) {
+ console.error('密码验证失败:', error);
+ message.error('验证过程出错,请重试');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const handleKeyPress = (e) => {
+ if (e.key === 'Enter') {
+ handleVerify();
+ }
+ };
+
+ return (
+
+
+
+
+
+
+ 内部高级配置
+
+ 请输入管理员访问密码以继续
+
+
+ setPassword(e.target.value)}
+ onKeyPress={handleKeyPress}
+ prefix={}
+ style={{ marginBottom: 16 }}
+ />
+
+
+
+
+ 视觉位移计配置工具 v{window.env?.FS_VERSION || ''} Build {new Date().getFullYear()}
+
+
+
+ );
+};
+
+export default AdvancedConfigAuth;
diff --git a/client/src/sections/wuyuanbiaoba/components/index.js b/client/src/sections/wuyuanbiaoba/components/index.js
index 072da63..a55d6d2 100644
--- a/client/src/sections/wuyuanbiaoba/components/index.js
+++ b/client/src/sections/wuyuanbiaoba/components/index.js
@@ -5,3 +5,4 @@ export { default as RealtimeCharts } from './RealtimeCharts';
export { default as RealtimeDataTable } from './RealtimeDataTable';
export { default as TemplateModal } from './TemplateModal';
export { default as TargetDetailModal } from './TargetDetailModal';
+export { default as AdvancedConfigAuth } from './AdvancedConfigAuth';
diff --git a/client/src/sections/wuyuanbiaoba/container/index.jsx b/client/src/sections/wuyuanbiaoba/container/index.jsx
index 7934030..ffacc0d 100644
--- a/client/src/sections/wuyuanbiaoba/container/index.jsx
+++ b/client/src/sections/wuyuanbiaoba/container/index.jsx
@@ -1,5 +1,6 @@
import React, { useState, useEffect } from "react";
-import { Tabs, Typography } from "antd";
+import { Tabs, Typography, Menu } from "antd";
+import { MonitorOutlined, LockOutlined } from "@ant-design/icons";
import ExcelJS from "exceljs";
import {
@@ -10,6 +11,7 @@ import {
RealtimeDataTable,
TemplateModal,
TargetDetailModal,
+ AdvancedConfigAuth,
} from "../components";
import {
WebSocketProvider,
@@ -18,6 +20,7 @@ import {
} from "../actions/websocket.jsx";
import { useTemplateStorage } from "../hooks/useTemplateStorage.js";
import { useTargetStorage } from "../hooks/useTargetStorage.js";
+import { useAuth } from "../hooks/useAuth.js";
import { useRef } from "react";
const { Title } = Typography;
@@ -93,6 +96,12 @@ const WuyuanbiaobaContent = () => {
// 添加选中标靶的状态
const [selectedTargetId, setSelectedTargetId] = useState(null);
+ // 菜单状态
+ const [currentMenu, setCurrentMenu] = useState("monitor");
+
+ // 权限验证 Hook
+ const { isUnlocked, verifyPassword, logout } = useAuth();
+
// 处理实时数据并转换为表格格式
const processRealtimeData = (data) => {
if (!data || !data.data || !Array.isArray(data.data)) {
@@ -483,91 +492,156 @@ const WuyuanbiaobaContent = () => {
backgroundColor: "#f0f2f5",
}}
>
- {/* Header 区域 */}
+ {/* Header 区域 - 固定在顶部 */}
视觉位移计配置工具
-
-
- {/* 中间区域 - 固定视口剩余高度 */}
-
- {/* Camera 区域 */}
-
-
- {/* 右侧 Target List / Temp List 区域 */}
-
setCurrentMenu(key)}
style={{
- flex: 1,
- backgroundColor: "white",
- display: "flex",
- flexDirection: "column",
+ border: "none",
+ fontSize: "18px",
+ justifyContent: "flex-end"
}}
- >
-
-
-
-
- {/* 底部区域 - 在视口下方,需要滚动查看 */}
-
- {/* Charts 区域 */}
- ,
+ label: "实时监控",
+ },
+ {
+ key: "advanced",
+ icon: ,
+ label: "高级配置",
+ },
+ ]}
/>
+
- {/* Table 区域 - 使用采样数据显示 */}
-
+ {/* 内容区域 - 添加顶部padding以避免被固定header遮挡 */}
+
+ {/* 实时监控页面 */}
+ {currentMenu === "monitor" && (
+ <>
+ {/* 中间区域 - 固定视口剩余高度 */}
+
+ {/* Camera 区域 */}
+
+
+ {/* 右侧 Target List / Temp List 区域 */}
+
+
+
+
+
+ {/* 底部区域 - 在视口下方,需要滚动查看 */}
+
+ {/* Charts 区域 */}
+
+
+ {/* Table 区域 - 使用采样数据显示 */}
+
+
+ >
+ )}
+
+ {/* 高级配置页面 */}
+ {currentMenu === "advanced" && (
+
+ {isUnlocked ? (
+
+ ) : (
+
{}}
+ />
+ )}
+
+ )}
{/* 模板编辑模态框 */}
diff --git a/client/src/sections/wuyuanbiaoba/hooks/useAuth.js b/client/src/sections/wuyuanbiaoba/hooks/useAuth.js
new file mode 100644
index 0000000..0779766
--- /dev/null
+++ b/client/src/sections/wuyuanbiaoba/hooks/useAuth.js
@@ -0,0 +1 @@
+import { useState, useEffect, useCallback } from "react"; const CORRECT_PASSWORD_HASH = "77796ac7e66ecc44954287ed7de7096c4016dd6ffb2763091c4eb3bc4d28b6dc", AUTH_STATUS_KEY = "advanced_config_unlocked"; async function generatePasswordHash(e) { try { var t = (new TextEncoder).encode(e), c = await crypto.subtle.digest("SHA-256", t); return Array.from(new Uint8Array(c)).map(e => e.toString(16).padStart(2, "0")).join("") } catch (e) { throw console.error("密码哈希生成失败:", e), e } } function checkUnlockStatus() { return "true" === sessionStorage.getItem(AUTH_STATUS_KEY) } function setUnlockStatus(e) { e ? sessionStorage.setItem(AUTH_STATUS_KEY, "true") : sessionStorage.removeItem(AUTH_STATUS_KEY) } function useAuth() { let [e, t] = useState(checkUnlockStatus); return useEffect(() => { t(checkUnlockStatus()) }, []), { isUnlocked: e, verifyPassword: useCallback(async e => { try { return e ? await generatePasswordHash(e) === CORRECT_PASSWORD_HASH && (setUnlockStatus(!0), t(!0), !0) : !1 } catch (e) { return console.error("密码验证失败:", e), !1 } }, []), logout: useCallback(() => { setUnlockStatus(!1), t(!1) }, []) } } export { useAuth, generatePasswordHash, checkUnlockStatus };
diff --git a/config.cjs b/config.cjs
index dc7d654..825b80a 100644
--- a/config.cjs
+++ b/config.cjs
@@ -1,8 +1,11 @@
const path = require('path')
+const packageJson = require('./package.json')
+
const flag = process.env.npm_lifecycle_script.includes('--mode localdev') ? 'localdev' : null;
if (flag) {
process.env.FS_FLAG = flag;
}
+process.env.FS_VERSION = packageJson.version;
module.exports = {
env: process.env.NODE_ENV || 'development', // 运行环境 development | production
port: process.env.PORT || 5000, // 服务端口
@@ -12,6 +15,7 @@ module.exports = {
'/client/assets/script/peace.js'
],
flag: flag, // 自定义环境标识
+ version: packageJson.version, // 版本号
proxy: [{ // 代理配置
path: '/_api',
target: process.env.API,