From 7ac000394220a33c48b81c5f3276f7ecb10255e2 Mon Sep 17 00:00:00 2001 From: chenyt Date: Fri, 22 Aug 2025 18:59:43 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=A1=8C=E9=9D=A2):=20DHCP+=E9=9D=99?= =?UTF-8?q?=E6=80=81IP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pc-fe/.umirc.ts | 5 + pc-fe/mock/configStep.js | 8 + pc-fe/package-lock.json | 1 + pc-fe/package.json | 1 + pc-fe/src/assets/stepIcon.png | Bin 0 -> 287 bytes pc-fe/src/main/ipc/platform.ts | 69 ++++++- pc-fe/src/main/utils/utils.ts | 102 ++++++++++ .../src/pages/components/ButtonCom/index.less | 66 +++++++ .../src/pages/components/ButtonCom/index.tsx | 53 +++++ pc-fe/src/pages/components/Layout/index.less | 7 +- pc-fe/src/pages/components/Layout/index.tsx | 26 ++- .../components/networkConfig/index.less | 178 +++++++++++++++++ .../components/networkConfig/index.tsx | 182 ++++++++++++++++++ .../components/terminalGetImage/index.less | 0 .../components/terminalGetImage/index.tsx | 11 ++ .../components/watchManagement/index.less | 0 .../components/watchManagement/index.tsx | 11 ++ pc-fe/src/pages/configSteps/index.less | 70 +++++++ pc-fe/src/pages/configSteps/index.tsx | 45 +++++ pc-fe/src/pages/welcome/index.less | 6 +- pc-fe/src/services/test.js | 2 +- pc-fe/src/types/configSteps.d.ts | 11 ++ pc-fe/tsconfig.json | 2 +- 23 files changed, 839 insertions(+), 17 deletions(-) create mode 100644 pc-fe/mock/configStep.js create mode 100644 pc-fe/src/assets/stepIcon.png create mode 100644 pc-fe/src/main/utils/utils.ts create mode 100644 pc-fe/src/pages/components/ButtonCom/index.less create mode 100644 pc-fe/src/pages/components/ButtonCom/index.tsx create mode 100644 pc-fe/src/pages/configSteps/components/networkConfig/index.less create mode 100644 pc-fe/src/pages/configSteps/components/networkConfig/index.tsx create mode 100644 pc-fe/src/pages/configSteps/components/terminalGetImage/index.less create mode 100644 pc-fe/src/pages/configSteps/components/terminalGetImage/index.tsx create mode 100644 pc-fe/src/pages/configSteps/components/watchManagement/index.less create mode 100644 pc-fe/src/pages/configSteps/components/watchManagement/index.tsx create mode 100644 pc-fe/src/pages/configSteps/index.less create mode 100644 pc-fe/src/pages/configSteps/index.tsx create mode 100644 pc-fe/src/types/configSteps.d.ts diff --git a/pc-fe/.umirc.ts b/pc-fe/.umirc.ts index e301eb6..3ae51a7 100644 --- a/pc-fe/.umirc.ts +++ b/pc-fe/.umirc.ts @@ -20,6 +20,7 @@ export default defineConfig({ "@assets": path.resolve(rootdir, "src/assets"), "@components": path.resolve(rootdir, "src/components"), "@utils": path.resolve(rootdir, "src/utils"), + "@types": path.resolve(rootdir, "src/types"), }, // 路由配置 routes: [ @@ -43,6 +44,10 @@ export default defineConfig({ path: '/login', component: '@/pages/login', }, + { + path: '/configSteps', + component: '@/pages/configSteps', + }, { path: '/', redirect: '/welcome', diff --git a/pc-fe/mock/configStep.js b/pc-fe/mock/configStep.js new file mode 100644 index 0000000..fb8b3e9 --- /dev/null +++ b/pc-fe/mock/configStep.js @@ -0,0 +1,8 @@ +export default { + 'POST /api/v1/sendMessage': (req,res)=>{ + res.send({ + code: 200, + data: '发送成功' + }) + } +} \ No newline at end of file diff --git a/pc-fe/package-lock.json b/pc-fe/package-lock.json index f4f3bd1..012669c 100644 --- a/pc-fe/package-lock.json +++ b/pc-fe/package-lock.json @@ -12,6 +12,7 @@ "@ant-design/icons": "^6.0.0", "antd": "^5.26.6", "axios": "^1.11.0", + "classnames": "^2.5.1", "umi": "^4.0.42" }, "devDependencies": { diff --git a/pc-fe/package.json b/pc-fe/package.json index 8c0a012..194955b 100644 --- a/pc-fe/package.json +++ b/pc-fe/package.json @@ -19,6 +19,7 @@ "@ant-design/icons": "^6.0.0", "antd": "^5.26.6", "axios": "^1.11.0", + "classnames": "^2.5.1", "umi": "^4.0.42" }, "devDependencies": { diff --git a/pc-fe/src/assets/stepIcon.png b/pc-fe/src/assets/stepIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..e0d82cbf33f601ffb7b70c0e2d0f0000d4f14929 GIT binary patch literal 287 zcmeAS@N?(olHy`uVBq!ia0vp^;y}#B!3HGbla4+GQk(@Ik;M!Q+`=Ht$S`Y;1W=H% zILO_JVcj{Imp~3nx}&cn1H;CC?mvmFK>ij_7srqa#9!yk@$)M6&*uy4IF?Z=MQ1zhk*nJ^J*Sez^k^ { if (window) { window.setFullScreen(false); } -}); \ No newline at end of file +}); + + + +ipcMain.handle('get-deviceid',async()=>{ + const deviceId = await getDeviceId(); + console.log(`Using device ID: ${deviceId}`); + // TODO:传给后端 +}) + +/* IPC 处理应用有线网络配置 */ +ipcMain.handle('apply-wired-config',async(event,config)=>{ + // return { + // success: true, + // message: '网络配置已成功应用' + // }; + try{ + console.log('应用网络配置:', config); + // 获取有线连接名称 + const connectionName = await getWiredConnectionName(); + console.log('有线连接名称:', connectionName); + + if(config.method==='static'){ + // 使用nmcli配置静态IP,需要使用sudo权限,一次性设置所有参数 + let modifyCmd = `echo "unis@123" | sudo -S nmcli connection modify "${connectionName}" ipv4.method manual ipv4.addresses "${config.ipv4}/${netmaskToCidr(config.subnetMask)}" ipv4.gateway "${config.ipv4Gateway}"`; + const dnsServers = [config.primaryDns, config.secondaryDns].filter(Boolean).join(','); + modifyCmd += ` ipv4.dns "${dnsServers}"`; + + // 添加 IPv6 配置(如果存在 ipv6Gateway)????ipv6和长度需要吗?ui只写了ipv6网关 + // ipv6PrefixLength 是 IPv6 地址的前缀长度,类似于 IPv4 中的子网掩码。???? + if (config.ipv6 && config.ipv6Gateway) { + modifyCmd += ` ipv6.method manual ipv6.addresses "${config.ipv6}/${config.ipv6PrefixLength || 64}" ipv6.gateway "${config.ipv6Gateway}"`; + } + + // 执行配置命令 + console.log('执行命令:', modifyCmd.replace('unis@123', '***')); + await execAsync(modifyCmd); + + // 重新激活连接 + await execAsync(`echo "unis@123" | sudo -S nmcli connection up "${connectionName}"`); + + }else{ + // DHCP配置,一次性设置所有参数 + const modifyCmd = `echo "unis@123" | sudo -S nmcli connection modify "${connectionName}" ipv4.method auto ipv4.addresses "" ipv4.gateway "" ipv4.dns ""`; + + // 执行配置命令 + console.log('执行命令:', modifyCmd.replace('unis@123', '***')); + await execAsync(modifyCmd); + + // 重新激活连接 + await execAsync(`echo "unis@123" | sudo -S nmcli connection up "${connectionName}"`); + } + return { + success: true, + message: '网络配置已成功应用' + }; + }catch(error:unknown){ + console.error('应用网络配置失败:', error); + return { + success: false, + message: `配置失败: ${error instanceof Error ? error.message : String(error || '未知错误')}` + }; + } +}) \ No newline at end of file diff --git a/pc-fe/src/main/utils/utils.ts b/pc-fe/src/main/utils/utils.ts new file mode 100644 index 0000000..bc7b818 --- /dev/null +++ b/pc-fe/src/main/utils/utils.ts @@ -0,0 +1,102 @@ + +const { exec } = require('child_process'); +const os = require('os'); +const { promisify } = require('util'); +const execAsync = promisify(exec); + +/** 获取设备ID(芯片序列号) + * TODO: 增加获取mac地址的逻辑,需要传给后端 + */ +export async function getDeviceId() { + try { + // 尝试多种方法获取唯一的设备标识 + const methods = [ + // 方法1: CPU序列号 + 'cat /proc/cpuinfo | grep Serial | head -1 | awk \'{print $3}\'', + // 方法2: 机器ID + 'cat /etc/machine-id 2>/dev/null || echo ""', + // 方法3: DMI产品UUID + 'cat /sys/class/dmi/id/product_uuid 2>/dev/null || echo ""', + // 方法4: 主板序列号 + 'cat /sys/class/dmi/id/board_serial 2>/dev/null || echo ""' + ]; + + for (const command of methods) { + try { + const { stdout } = await execAsync(command); + const deviceId = stdout.trim(); + + if (deviceId && deviceId !== '' && deviceId !== 'unknown' && deviceId !== '0000000000000000') { + console.log(`Device ID obtained using command: ${command}`); + console.log(`Device ID: ${deviceId}`); + return deviceId; + } + } catch (error) { + console.log(`Method failed: ${command}, error: ${(error as Error).message}`); + continue; + } + } + + // 如果所有方法都失败,生成一个基于MAC地址的fallback ID + const networkInterfaces = os.networkInterfaces(); + for (const interfaceName of Object.keys(networkInterfaces)) { + const interfaces = networkInterfaces[interfaceName]; + for (const iface of interfaces) { + if (iface.mac && iface.mac !== '00:00:00:00:00:00') { + const fallbackId = iface.mac.replace(/:/g, '').toUpperCase(); + console.log(`Using MAC address as fallback device ID: ${fallbackId}`); + return fallbackId; + } + } + } + + // 最后的fallback - 使用hostname + const hostname = os.hostname(); + console.log(`Using hostname as final fallback device ID: ${hostname}`); + return hostname; + + } catch (error) { + console.error('Error getting device ID:', error); + // 返回一个默认的设备ID + return 'UNKNOWN_DEVICE'; + } +} + +/** 获取有线网络连接名称 */ +export async function getWiredConnectionName() { + try { + // 首先尝试获取活动的有线连接 + const { stdout: activeConn } = await execAsync('nmcli -t -f NAME,TYPE connection show --active | grep ethernet | head -1 | cut -d: -f1'); + if (activeConn.trim()) { + return activeConn.trim(); + } + + // 如果没有活动连接,获取所有有线连接 + const { stdout: allConn } = await execAsync('nmcli -t -f NAME,TYPE connection show | grep ethernet | head -1 | cut -d: -f1'); + if (allConn.trim()) { + return allConn.trim(); + } + + // 默认连接名称 + return 'Wired connection 1'; + } catch (error) { + console.error('获取有线连接名称失败:', error); + return 'Wired connection 1'; + } +} + +/* 子网掩码转CIDR */ +export function netmaskToCidr(netmask:string) { + const netmaskMap: { [key: string]: string } = { + '255.255.255.0': '24', + '255.255.0.0': '16', + '255.0.0.0': '8', + '255.255.255.128': '25', + '255.255.255.192': '26', + '255.255.255.224': '27', + '255.255.255.240': '28', + '255.255.255.248': '29', + '255.255.255.252': '30' + }; + return netmaskMap[netmask] || '24'; +} \ No newline at end of file diff --git a/pc-fe/src/pages/components/ButtonCom/index.less b/pc-fe/src/pages/components/ButtonCom/index.less new file mode 100644 index 0000000..8a9dde7 --- /dev/null +++ b/pc-fe/src/pages/components/ButtonCom/index.less @@ -0,0 +1,66 @@ +.button-container { + display: flex; + justify-content: center; + padding: 15px 0; + flex-shrink: 0; + gap: 40px; + + .cancel-button { + width: 140px; + height: 64px; + border-radius: 32px; + background: transparent; + border: 1px solid rgba(134, 133, 158, 1); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + + span { + font-family: PingFang SC; + font-weight: 400; + font-style: Heavy; + font-size: 20px; + line-height: 32px; + letter-spacing: 0%; + color: rgba(229, 229, 229, 1); + } + + &:hover { + background: rgba(255, 255, 255, 0.05); + } + } + + .confirm-button { + height: 64px; + border-radius: 32px; + background: rgba(255, 255, 255, 0.3); + border: none; + cursor: pointer; + display: flex; + align-items: center; + padding: 0 30px; + + span { + font-family: PingFang SC; + font-weight: 400; + font-style: Heavy; + font-size: 20px; + line-height: 32px; + letter-spacing: 0%; + color: rgba(229, 229, 229, 1); + margin-right: 15px; + } + + .arrow-icon { + width: 20px; + height: 8px; + background-image: url('../../../assets/stepIcon.png'); + background-size: 100% 100%; + } + + &:hover { + background: rgba(255, 255, 255, 0.4); + } + } +} \ No newline at end of file diff --git a/pc-fe/src/pages/components/ButtonCom/index.tsx b/pc-fe/src/pages/components/ButtonCom/index.tsx new file mode 100644 index 0000000..070ca7f --- /dev/null +++ b/pc-fe/src/pages/components/ButtonCom/index.tsx @@ -0,0 +1,53 @@ +import React from 'react'; +import classNames from 'classnames'; +import styles from './index.less'; + +interface ButtonComProps { + // 取消按钮配置 + cancelText?: string; + onCancel?: () => void; + showCancel?: boolean; + + // 确认按钮配置 + confirmText?: string; + onConfirm?: () => void; + showConfirm?: boolean; + + // 样式类名 + className?: string; +} + +const ButtonCom: React.FC = ({ + cancelText = '取消', + onCancel, + showCancel = true, + confirmText = '确认', + onConfirm, + showConfirm = true, + className +}) => { + return ( +
+ {showCancel && ( + + )} + + {showConfirm && ( + + )} +
+ ); +}; + +export default ButtonCom; \ No newline at end of file diff --git a/pc-fe/src/pages/components/Layout/index.less b/pc-fe/src/pages/components/Layout/index.less index 50d4abe..3320c8f 100644 --- a/pc-fe/src/pages/components/Layout/index.less +++ b/pc-fe/src/pages/components/Layout/index.less @@ -1,9 +1,12 @@ .main-layout { - min-height: 100vh; + background-color: rgba(0, 9, 51, 0.9); } .main-content { - min-height: 100vh; + // background-size: 100% 100%; + background-color: rgba(0, 9, 51, 0.9); + width: 100vw; + height: 100vh; } \ No newline at end of file diff --git a/pc-fe/src/pages/components/Layout/index.tsx b/pc-fe/src/pages/components/Layout/index.tsx index fbecb4c..3549248 100644 --- a/pc-fe/src/pages/components/Layout/index.tsx +++ b/pc-fe/src/pages/components/Layout/index.tsx @@ -23,11 +23,21 @@ const MainLayout: React.FC = () => { // setUsername(currentUsername || ''); // }, []); - useEffect(() => { + useEffect(() => { // TODO: 第一次来:判断是否配置ip/DHCP、服务ip绑定终端 绑定:直接到版本更新页面 未绑定:到配置ip/DHCP页面 - setTimeout(() => { - history.push('/login'); - },9000) + // setTimeout(() => { + // history.push('/configSteps'); + // },1000) + // const fetchDeviceId = async () => { + // try { + // const res = await window.electronAPI.invoke('get-deviceid'); + // console.log('获取设备ID:', res); + // } catch (error) { + // console.error('获取设备ID失败:', error); + // } + // } + // fetchDeviceId() + }, []); const handleMenuClick = (key: string) => { @@ -43,11 +53,11 @@ const MainLayout: React.FC = () => { }; return ( - - +
+
- - +
+
); }; diff --git a/pc-fe/src/pages/configSteps/components/networkConfig/index.less b/pc-fe/src/pages/configSteps/components/networkConfig/index.less new file mode 100644 index 0000000..33891d0 --- /dev/null +++ b/pc-fe/src/pages/configSteps/components/networkConfig/index.less @@ -0,0 +1,178 @@ +.network-config { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + + .tab-container { + height: 70px; + flex-shrink: 0; + background: linear-gradient(180deg, rgba(229, 229, 229, 0.05) 0%, rgba(229, 229, 229, 0.05) 100%); + display: flex; + justify-content: center; + align-items: center; + + .tab-item { + font-family: PingFang SC; + font-weight: 400; + font-style: Heavy; + top: -5px; + font-size: 20px; + line-height: 30px; + letter-spacing: 0%; + padding: 0 30px; + cursor: pointer; + position: relative; + color: rgba(229, 229, 229, 0.5); + + &.active { + color: rgba(229, 229, 229, 1); + } + + .indicator { + position: absolute; + bottom: -15px; + left: 50%; + transform: translateX(-50%); + width: 8px; + height: 8px; + background: rgba(229, 229, 229, 1); + border-radius: 50%; + } + } + } + + .content-container { + flex: 1; + overflow: hidden; + } + + .dhcp-content { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + + h2 { + font-family: PingFang SC; + font-weight: 400; + font-style: Heavy; + font-size: 24px; + color: rgba(229, 229, 229, 1); + margin-bottom: 20px; + } + + p { + font-family: PingFang SC; + font-size: 18px; + color: rgba(229, 229, 229, 0.8); + } + } + + .static-ip-container { + display: flex; + flex-direction: column; + height: 100%; + + .form-container { + width: 500px; + margin: 0 auto; + flex: 1; + overflow-y: auto; + padding: 20px 0px; + padding-right: 60px; + + // 滚动条样式 + &::-webkit-scrollbar { + width: 10px; + } + + &::-webkit-scrollbar-track { + background: rgba(255, 255, 255, 0.05); + border-radius: 28px; + margin: 10px 0; + } + + &::-webkit-scrollbar-thumb { + background: rgba(255, 255, 255, 0.1); + border-radius: 28px; + min-height: 30px; + } + + // 表单项样式 + .ant-form-item { + margin-bottom: 24px; + } + + .ant-form-item-label { + padding: 0 0 12px 0; + } + + .label { + font-family: PingFang SC; + font-weight: 400; + font-style: Heavy; + font-size: 18px; + line-height: 32px; + letter-spacing: 0%; + color: rgba(229, 229, 229, 1); + margin-bottom: 12px; + } + + .input-field { + width: 100%; + height: 56px; + background: rgba(255, 255, 255, 0.1); + border-radius: 28px; + border: none; + padding: 0 24px; + box-sizing: border-box; + font-family: PingFang SC; + font-weight: 400; + font-style: Heavy; + font-size: 18px; + line-height: 32px; + letter-spacing: 0%; + color: rgba(229, 229, 229, 1); + + &::placeholder { + color: rgba(229, 229, 229, 0.5); + } + + &:focus { + outline: none; + background: rgba(255, 255, 255, 0.15); + } + } + + // 覆盖 Ant Design 的默认样式 + .ant-input { + background: rgba(255, 255, 255, 0.1); + border-radius: 28px; + border: none; + padding: 0 24px; + box-sizing: border-box; + font-family: PingFang SC; + font-weight: 400; + font-style: Heavy; + font-size: 18px; + line-height: 32px; + letter-spacing: 0%; + color: rgba(229, 229, 229, 1); + height: 56px; + + &::placeholder { + color: rgba(229, 229, 229, 0.5); + } + + &:focus { + outline: none; + background: rgba(255, 255, 255, 0.15); + box-shadow: none; + } + } + } + + } +} \ No newline at end of file diff --git a/pc-fe/src/pages/configSteps/components/networkConfig/index.tsx b/pc-fe/src/pages/configSteps/components/networkConfig/index.tsx new file mode 100644 index 0000000..1626580 --- /dev/null +++ b/pc-fe/src/pages/configSteps/components/networkConfig/index.tsx @@ -0,0 +1,182 @@ +import React, { useState } from 'react'; +import styles from './index.less'; +import cs from 'classnames'; +import { Form, Input, message } from 'antd'; +import ButtonCom from '../../../components/ButtonCom'; + +const staticIpFormFields: CONFIG_STEPS.StaticFormFieldConfig[] = [ + { + name: "ipv4", + label: "IPv4", + type: "input", + placeholder: "请输入", + rules: [ + { required: true, message: '请输入IPv4地址' }, + { + pattern: /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/, + message: '请输入正确的IPv4地址格式' + } + ] + }, + { + name: "subnetMask", + label: "子网掩码", + type: "input", + placeholder: "请输入", + rules: [ + { required: true, message: '请输入子网掩码' }, + { + pattern: /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/, + message: '请输入正确的子网掩码格式' + } + ] + }, + { + name: "ipv4Gateway", + label: "IPv4网关", + type: "input", + placeholder: "请输入", + rules: [ + { required: true, message: '请输入IPv4网关' }, + { + pattern: /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/, + message: '请输入正确的IPv4网关格式' + } + ] + }, + { + name: "ipv6Gateway", + label: "IPv6网关", + type: "input", + placeholder: "请输入", + rules: [ + { + pattern: /^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|^([0-9a-fA-F]{1,4}:)*::([0-9a-fA-F]{1,4}:)*[0-9a-fA-F]{1,4}$/, + message: '请输入正确的IPv6地址格式' + } + ] + }, + { + name: "primaryDns", + label: "首选DNS", + type: "input", + placeholder: "请输入", + rules: [ + { required: true, message: '请输入首选DNS' }, + { + pattern: /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/, + message: '请输入正确的DNS地址格式' + } + ] + }, + { + name: "secondaryDns", + label: "备用DNS", + type: "input", + placeholder: "请输入", + rules: [ + { + pattern: /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/, + message: '请输入正确的DNS地址格式' + } + ] + } +]; + +const NetworkConfig: React.FC = () => { + const [activeTab, setActiveTab] = useState<'dhcp' | 'static'>('dhcp'); + const [form] = Form.useForm(); + + const handleSubmit = async () => { + if (activeTab === 'dhcp') { + // 如果是 DHCP 模式,直接IPC 处理应用有线网络配置 + try { + const res = await window.electronAPI.invoke('apply-wired-config',{ method: 'dhcp' }); + console.log('网络配置返回信息成功:', res); + if(res.success){ + message.success('网络配置成功'); + }else{ + message.error(res.message || '网络配置失败'); + } + } catch (error) { + console.error('网络配置返回信息失败:', error); + } + } else { + // 如果是静态IP模式,进行表单校验,再IPC 处理应用有线网络配置 + try { + const values = await form.validateFields(); + console.log('表单提交数据:', values); + const res = await window.electronAPI.invoke('apply-wired-config',{ method: 'static', ...values }); + console.log('网络配置返回信息成功:', res); + if(res.success){ + message.success('网络配置成功'); + }else{ + message.error(res.message || '网络配置失败'); + } + } catch (errorInfo) { + console.log('网络配置返回信息失败:', errorInfo); + } + } + // TODO: 处理网络配置成功后的逻辑,跳转到下一个步骤或页面 + }; + + const DhcpComponent = () => ( +
+

DHCP 配置

+

正在使用 DHCP 自动获取网络配置

+
+ ); + + const StaticIpComponent = () => ( +
+
+ {staticIpFormFields.map(field => ( + {field.label}
} + rules={field.rules} + > + + + ))} + + + ); + + return ( +
+
+
setActiveTab('dhcp')} + > + DHCP + {activeTab === 'dhcp' &&
} +
+
setActiveTab('static')} + > + 静态IP + {activeTab === 'static' &&
} +
+
+ +
+ {activeTab === 'dhcp' ? : } +
+ +
+ ); +}; + +export default NetworkConfig; \ No newline at end of file diff --git a/pc-fe/src/pages/configSteps/components/terminalGetImage/index.less b/pc-fe/src/pages/configSteps/components/terminalGetImage/index.less new file mode 100644 index 0000000..e69de29 diff --git a/pc-fe/src/pages/configSteps/components/terminalGetImage/index.tsx b/pc-fe/src/pages/configSteps/components/terminalGetImage/index.tsx new file mode 100644 index 0000000..0cd9718 --- /dev/null +++ b/pc-fe/src/pages/configSteps/components/terminalGetImage/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +const Index = () => { + return ( +
+ 终端获取镜像信息 +
+ ); +} + +export default Index; diff --git a/pc-fe/src/pages/configSteps/components/watchManagement/index.less b/pc-fe/src/pages/configSteps/components/watchManagement/index.less new file mode 100644 index 0000000..e69de29 diff --git a/pc-fe/src/pages/configSteps/components/watchManagement/index.tsx b/pc-fe/src/pages/configSteps/components/watchManagement/index.tsx new file mode 100644 index 0000000..f38f4ce --- /dev/null +++ b/pc-fe/src/pages/configSteps/components/watchManagement/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +const Index = () => { + return ( +
+ 侦测管理平台 +
+ ); +} + +export default Index; diff --git a/pc-fe/src/pages/configSteps/index.less b/pc-fe/src/pages/configSteps/index.less new file mode 100644 index 0000000..b6df9d7 --- /dev/null +++ b/pc-fe/src/pages/configSteps/index.less @@ -0,0 +1,70 @@ +.config-step-container { + width: 100%; + height: 100%; + padding-top: 24px; + display: flex; + flex-direction: column; + + .tabs-container { + display: flex; + width: 100%; + height: 60px; + flex-shrink: 0; + // background: rgba(229, 229, 229, 0.2); + + .tab-item { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + cursor: pointer; + position: relative; + + .tab-label { + font-size: 22px; + color: rgba(255, 255, 255, 0.6); // 未激活tab文字透明 + transition: color 0.3s ease; + } + + .tab-indicator { + position: absolute; + bottom: 0; + width: 100%; + height: 4px; + background: rgba(229, 229, 229, 0.2); + transition: background-color 0.3s ease; + } + + &.active { + .tab-label { + color: white; // 激活tab文字为白色 + } + + .tab-indicator { + background: white; // 激活tab指示器为白色 + } + } + } + } + + .tab-content-container { + flex: 1; + overflow: hidden; + background: rgba(255, 255, 255, 0.1); + + .tab-content { + height: 100%; + max-height: 100%; + background: rgba(255, 255, 255, 0.1); + border-radius: 4px; + padding: 20px; + box-sizing: border-box; + } + } + + .emptyBox{ + height: 60px; + flex-shrink: 0; + } +} \ No newline at end of file diff --git a/pc-fe/src/pages/configSteps/index.tsx b/pc-fe/src/pages/configSteps/index.tsx new file mode 100644 index 0000000..7f2e697 --- /dev/null +++ b/pc-fe/src/pages/configSteps/index.tsx @@ -0,0 +1,45 @@ +// src/pages/configSteps/index.tsx +import React, { useState } from 'react'; +import styles from './index.less'; +import cs from 'classnames'; +import NetworkConfig from './components/networkConfig'; +import WatchManagement from './components/watchManagement'; +import TerminalGetImage from './components/terminalGetImage'; + +const Index: React.FC = () => { + const [activeTab, setActiveTab] = useState("networkConfig"); + + const tabs = [ + { key: "networkConfig", label: '平台网络配置', component: }, + { key: "watchManagement", label: '侦测管理平台', component: }, + { key: "terminalGetImage", label: '终端获取镜像信息', component: }, + ]; + + const activeTabItem = tabs.find(tab => tab.key === activeTab); + + return ( +
+
+ {tabs.map((tab) => ( +
setActiveTab(tab.key)} + > + {tab.label} +
+
+ ))} +
+ +
+ {activeTabItem?.component ||
未找到对应内容
} +
+
+
+ ); +}; + +export default Index; \ No newline at end of file diff --git a/pc-fe/src/pages/welcome/index.less b/pc-fe/src/pages/welcome/index.less index 2657624..4dc5d7b 100644 --- a/pc-fe/src/pages/welcome/index.less +++ b/pc-fe/src/pages/welcome/index.less @@ -1,8 +1,6 @@ .welcomeCon{ - width: 100vw; - height: 100vh; - background-color: rgba(0, 9, 51, 1); - background-size: 100% 100%; + width: 100%; + height: 100%; display: flex; flex-direction: column; justify-content: center; diff --git a/pc-fe/src/services/test.js b/pc-fe/src/services/test.js index 9968492..0c2d672 100644 --- a/pc-fe/src/services/test.js +++ b/pc-fe/src/services/test.js @@ -1,4 +1,4 @@ -import axios from '@/utils/axios'; +import axios from '@utils/axios'; export const BASE_URL = '/api/v1/test'; diff --git a/pc-fe/src/types/configSteps.d.ts b/pc-fe/src/types/configSteps.d.ts new file mode 100644 index 0000000..f702bbf --- /dev/null +++ b/pc-fe/src/types/configSteps.d.ts @@ -0,0 +1,11 @@ +declare namespace CONFIG_STEPS { + interface StaticFormFieldConfig { + name: string; + label: string; + type: 'input' | 'select'; // 可扩展更多类型 + rules?: any[]; + placeholder?: string; + options?: { label: string; value: string }[]; // select 专用 + required?: boolean; + } +} diff --git a/pc-fe/tsconfig.json b/pc-fe/tsconfig.json index 133cfd8..cab18d4 100644 --- a/pc-fe/tsconfig.json +++ b/pc-fe/tsconfig.json @@ -1,3 +1,3 @@ { - "extends": "./src/.umi/tsconfig.json" + "extends": "./src/.umi/tsconfig.json", }