seeder-st2110-components 1.7.11 → 1.7.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import React, { memo, useState, useCallback, useMemo, useEffect, useRef } from 'react';
2
- import { Tooltip, Modal, App, Form, Input, Alert, message, Dropdown, Spin, Divider, Typography, InputNumber, ConfigProvider, Badge, Switch, Select, List, Empty, Button, Space, Flex, Checkbox, Row, Col, Result } from 'antd';
2
+ import { Tooltip, Modal, App, Alert, Form, Input, message, Dropdown, Spin, Divider, Typography, InputNumber, ConfigProvider, Badge, Switch, Select, List, Empty, Button, Space, Flex, Checkbox, Row, Col, Result } from 'antd';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
4
  import { useWebSocket, useInterval } from 'ahooks';
5
5
  import { LoadingOutlined, ExclamationCircleFilled, PlusOutlined } from '@ant-design/icons';
@@ -297,1279 +297,1708 @@ const StyledModal$2 = props => {
297
297
  };
298
298
  var StyledModal$3 = StyledModal$2;
299
299
 
300
- const AuthorizationModal = _ref => {
301
- let {
302
- onCancel,
303
- onOk,
304
- authData,
305
- title = 'Register License',
306
- okText,
307
- cancelText = 'Ignore',
308
- width = 600
309
- } = _ref;
310
- const {
311
- message
312
- } = App.useApp();
313
- const [code, setCode] = useState('');
314
- const {
315
- accredit_status: isActivated,
316
- message: statusMessage = 'Unactivated',
317
- bios_id: uuid,
318
- expires_time: expiresTime
319
- } = authData || {};
320
- const handleOk = () => {
321
- const trimmedCode = code.trim();
322
- if (!trimmedCode) {
323
- message.error('License key cannot be empty');
324
- return;
325
- }
326
- onOk(trimmedCode);
327
- };
328
- const statusAlert = isActivated ? /*#__PURE__*/jsx(Alert, {
329
- message: "".concat(statusMessage, ", will expire on ").concat(expiresTime),
330
- type: "success",
331
- showIcon: true
332
- }) : /*#__PURE__*/jsx(Alert, {
333
- message: statusMessage,
334
- type: "warning",
335
- showIcon: true
336
- });
337
- const defaultOkText = isActivated ? "Reactivate" : "Activate Now";
338
- return /*#__PURE__*/jsx(StyledModal$3, {
339
- title: title,
340
- width: width,
341
- open: true,
342
- onCancel: onCancel,
343
- onOk: handleOk,
344
- okText: okText || defaultOkText,
345
- cancelText: cancelText,
346
- cancelButtonProps: {
347
- disabled: !isActivated
348
- },
349
- maskClosable: false,
350
- closable: false,
351
- children: /*#__PURE__*/jsxs(Form, {
352
- name: "auth_form",
353
- labelCol: {
354
- span: 6
355
- },
356
- wrapperCol: {
357
- span: 18
358
- },
359
- autoComplete: "off",
360
- children: [/*#__PURE__*/jsx("div", {
361
- style: {
362
- marginBottom: 16
363
- },
364
- children: statusAlert
365
- }), /*#__PURE__*/jsx(Form.Item, {
366
- label: "Machine Code",
367
- required: true,
368
- children: /*#__PURE__*/jsx(Input, {
369
- value: uuid,
370
- readOnly: true
371
- })
372
- }), /*#__PURE__*/jsx(Form.Item, {
373
- label: "License Key",
374
- required: true,
375
- children: /*#__PURE__*/jsx(Input.TextArea, {
376
- value: code,
377
- onChange: e => setCode(e.target.value),
378
- autoSize: {
379
- minRows: 6,
380
- maxRows: 6
381
- },
382
- placeholder: "Enter your license key"
383
- })
384
- })]
385
- })
386
- });
387
- };
388
- var AuthorizationModal$1 = AuthorizationModal;
389
-
390
- const DEFAULT_AUTH = {
391
- accredit_status: false,
392
- // 授权状态
393
- bios_id: '',
394
- // 主机id
395
- message: 'Unactivated',
396
- // 激活信息
397
- expires_time: '' // 授权到期时间
398
- };
399
- const useAuth = options => {
400
- const {
401
- fetchAuthInfo,
402
- authorize,
403
- defaultAuthData = DEFAULT_AUTH,
404
- autoShowModal = true
405
- } = options || {};
406
- const [messageApi, contextHolder] = message.useMessage();
407
- const [showModal, setShowModal] = useState(false); // 默认隐藏
408
- const [authData, setAuthData] = useState(defaultAuthData);
409
- const [isRegistered, setIsRegistered] = useState(false);
410
- const [loading, setLoading] = useState(false);
411
- const openModal = useCallback(() => setShowModal(true), []);
412
- const closeModal = useCallback(() => setShowModal(false), []);
413
- useEffect(() => {
414
- async function loadAuthInfo() {
415
- try {
416
- setLoading(true);
417
- const result = await fetchAuthInfo();
418
- if (result !== null && result !== void 0 && result.commands) {
419
- const commands = result.commands;
420
- setAuthData(commands);
421
- setIsRegistered(commands.accredit_status);
422
- if (autoShowModal) {
423
- commands.accredit_status ? closeModal() : openModal();
424
- }
425
- }
426
- } catch (error) {
427
- console.error('Failed to fetch auth info:', error);
428
- } finally {
429
- setLoading(false);
430
- }
431
- }
432
- loadAuthInfo();
433
- }, [fetchAuthInfo, autoShowModal, openModal, closeModal]);
434
- const auth = async code => {
435
- try {
436
- setLoading(true);
437
- const result = await authorize({
438
- type: "accredit_command",
439
- commands: {
440
- id: "LICENSE",
441
- license_data: code
442
- }
443
- });
444
- if (result !== null && result !== void 0 && result.commands) {
445
- const commands = result.commands;
446
- // type accredit_message
447
- if (commands !== null && commands !== void 0 && commands.accredit_status) {
448
- setIsRegistered(true);
449
- setAuthData(commands);
450
- closeModal();
451
- } else {
452
- messageApi.error(commands.ciphertext_status || 'Authorization failed');
453
- }
454
- }
455
- } catch (error) {
456
- console.error('Authorization error:', error);
457
- } finally {
458
- setLoading(false);
459
- }
460
- };
461
- const modal = showModal && /*#__PURE__*/jsxs(Fragment, {
462
- children: [/*#__PURE__*/jsx(AuthorizationModal$1, {
463
- onOk: auth,
464
- onCancel: closeModal,
465
- authData: authData
466
- }), contextHolder]
467
- });
468
- return {
469
- AuthModal: modal,
470
- openAuthModal: openModal,
471
- closeAuthModal: closeModal,
472
- isRegistered,
473
- authData,
474
- loading
475
- };
476
- };
300
+ /**
301
+ * 国际化调试工具
302
+ * 在浏览器控制台运行 window.debugI18n() 查看当前国际化状态
303
+ */
477
304
 
478
- const useUpgrade = _ref => {
479
- let {
480
- menuItems = [],
481
- onMenuClick,
482
- downloadFiles,
483
- upgradeExecute,
484
- // 上传接口:(formData, config) => Promise<response>
485
- upgradeStatus,
486
- // 状态轮询接口:(config) => Promise<response>
487
- acceptFileTypes = "application/octet-stream",
488
- uploadCompleteDelay = 3000,
489
- statusPollingInterval = 1000
490
- } = _ref;
491
- const [isSpinning, setIsSpinning] = useState(false);
492
- const [pollingInterval, setPollingInterval] = useState(undefined); // 间隔时间,当设置值为 undefined 时会停止计时器
493
- const [currentStatus, setCurrentStatus] = useState('idle'); // 'idle' | 'uploading' | 'Upload complete, starting upgrade...' | 'upgrading'
494
- const [uploadProgress, setUploadProgress] = useState(0);
495
- const inputRef = useRef(null);
496
- // 控制并发上传
497
- const isUploadingRef = useRef(false);
498
- // 标记组件是否已卸载
499
- const isMountedRef = useRef(true);
305
+ const debugI18n = () => {
306
+ var _window$g_initialProp, _window$g_initialProp2, _window$g_initialProp3, _window$g_initialProp4, _window$g_initialProp5, _window$g_initialProp6;
307
+ if (typeof window === 'undefined') {
308
+ console.log('[i18n debug] Not in browser environment');
309
+ return;
310
+ }
311
+ console.group('🌍 I18n Debug Information');
500
312
 
501
- // 分别创建独立的取消令牌
502
- const uploadCancelToken = useRef(axios.CancelToken.source());
503
- const statusCancelToken = useRef(axios.CancelToken.source());
504
- const showLoader = () => setIsSpinning(true);
505
- const hideLoader = () => setIsSpinning(false);
313
+ // 1. 当前语言环境
314
+ console.log('📍 Current Locale Sources:');
315
+ console.log(' - window.g_initialProps?.locale:', (_window$g_initialProp = window.g_initialProps) === null || _window$g_initialProp === void 0 ? void 0 : _window$g_initialProp.locale);
316
+ console.log(' - window.g_initialProps?.___g_initialPropsFromServer?.locale:', (_window$g_initialProp2 = window.g_initialProps) === null || _window$g_initialProp2 === void 0 || (_window$g_initialProp2 = _window$g_initialProp2.___g_initialPropsFromServer) === null || _window$g_initialProp2 === void 0 ? void 0 : _window$g_initialProp2.locale);
317
+ try {
318
+ console.log(' - localStorage.umi-locale:', localStorage.getItem('umi-locale'));
319
+ } catch (e) {
320
+ console.log(' - localStorage: not available');
321
+ }
322
+ console.log(' - window.__COMPONENT_LOCALE__:', window.__COMPONENT_LOCALE__);
506
323
 
507
- // 构建菜单项 - 确保至少包含download和upload
508
- // 构建菜单项 - 最终完美版本
509
- const finalMenuItems = useMemo(() => {
510
- const hasDownload = menuItems.some(item => item.key === 'download');
511
- const hasUpload = menuItems.some(item => item.key === 'upload');
512
- if (hasDownload && hasUpload) {
513
- return menuItems;
514
- }
515
- const licenseIndex = menuItems.findIndex(item => item.key === 'license');
324
+ // 2. 语言包
325
+ console.log('\n📦 Available Message Sources:');
326
+ console.log(' - window.__COMPONENT_I18N_MESSAGES__:', window.__COMPONENT_I18N_MESSAGES__);
327
+ console.log(' - window.__PROJECT_I18N_MESSAGES__:', window.__PROJECT_I18N_MESSAGES__);
328
+ console.log(' - window.g_initialProps?.messages:', (_window$g_initialProp3 = window.g_initialProps) === null || _window$g_initialProp3 === void 0 ? void 0 : _window$g_initialProp3.messages);
516
329
 
517
- // 如果没有license,在末尾添加
518
- if (licenseIndex === -1) {
519
- const itemsToAdd = [];
520
- if (menuItems.length > 0) itemsToAdd.push({
521
- type: 'divider'
522
- });
523
- // if (!hasDownload) itemsToAdd.push({ key: "download", label: "Download Config File" });
524
- if (!hasUpload) itemsToAdd.push({
525
- key: "upload",
526
- label: "Software Update"
527
- });
528
- return [...menuItems, ...itemsToAdd];
529
- }
330
+ // 3. 合并后的语言包
331
+ const componentMessages = window.__COMPONENT_I18N_MESSAGES__ || {};
332
+ const projectMessages = window.__PROJECT_I18N_MESSAGES__ || {};
333
+ const umiMessages = ((_window$g_initialProp4 = window.g_initialProps) === null || _window$g_initialProp4 === void 0 ? void 0 : _window$g_initialProp4.messages) || {};
334
+ const allLocales = new Set([...Object.keys(componentMessages), ...Object.keys(projectMessages), ...Object.keys(umiMessages)]);
335
+ console.log('\n🔧 Merged Locales:', Array.from(allLocales));
336
+ allLocales.forEach(locale => {
337
+ const merged = _objectSpread2$1(_objectSpread2$1(_objectSpread2$1({}, umiMessages[locale] || {}), projectMessages[locale] || {}), componentMessages[locale] || {});
338
+ console.log("\n \uD83D\uDCC4 ".concat(locale, " (").concat(Object.keys(merged).length, " keys):"), merged);
339
+ });
530
340
 
531
- // 有license,在license前面插入
532
- const beforeLicense = menuItems.slice(0, licenseIndex);
533
- const licenseItem = menuItems[licenseIndex];
534
- const afterLicense = menuItems.slice(licenseIndex + 1);
535
- const itemsToInsert = [];
341
+ // 4. 测试几个关键的 key
342
+ console.log('\n🧪 Test Translation:');
343
+ const testKeys = ['networkSettings.title', 'button.ok', 'button.cancel', 'button.apply', 'button.close'];
344
+ const currentLocale = ((_window$g_initialProp5 = window.g_initialProps) === null || _window$g_initialProp5 === void 0 ? void 0 : _window$g_initialProp5.locale) || ((_window$g_initialProp6 = window.g_initialProps) === null || _window$g_initialProp6 === void 0 || (_window$g_initialProp6 = _window$g_initialProp6.___g_initialPropsFromServer) === null || _window$g_initialProp6 === void 0 ? void 0 : _window$g_initialProp6.locale) || localStorage.getItem('umi-locale') || 'zh-CN';
345
+ const allMessages = {};
346
+ allLocales.forEach(locale => {
347
+ allMessages[locale] = _objectSpread2$1(_objectSpread2$1(_objectSpread2$1({}, umiMessages[locale] || {}), projectMessages[locale] || {}), componentMessages[locale] || {});
348
+ });
349
+ testKeys.forEach(key => {
350
+ var _allMessages$currentL, _allMessages$enUS;
351
+ const translation = ((_allMessages$currentL = allMessages[currentLocale]) === null || _allMessages$currentL === void 0 ? void 0 : _allMessages$currentL[key]) || ((_allMessages$enUS = allMessages['en-US']) === null || _allMessages$enUS === void 0 ? void 0 : _allMessages$enUS[key]) || '❌ NOT FOUND';
352
+ console.log(" - ".concat(key, ": ").concat(translation));
353
+ });
354
+ console.groupEnd();
355
+ };
536
356
 
537
- // 1. 前面的分隔符(如果beforeLicense不为空且最后一项不是分隔符)
538
- if (beforeLicense.length > 0 && beforeLicense[beforeLicense.length - 1].type !== 'divider') {
539
- itemsToInsert.push({
540
- type: 'divider'
541
- });
542
- }
357
+ // 自动在开发环境注册到 window
358
+ if (process.env.NODE_ENV === 'development' && typeof window !== 'undefined') {
359
+ window.debugI18n = debugI18n;
360
+ console.log('💡 I18n debug tool available. Run window.debugI18n() in console to check i18n status.');
361
+ }
543
362
 
544
- // 2. 添加缺少的项
545
- // if (!hasDownload) itemsToInsert.push({ key: "download", label: "Download Config File" });
546
- if (!hasUpload) itemsToInsert.push({
547
- key: "upload",
548
- label: "Software Update"
549
- });
363
+ /**
364
+ * seeder-st2110-components 组件库国际化 Hook
365
+ *
366
+ * 设计目标:
367
+ * 1. 不依赖特定的国际化库(react-intl, i18next 等)
368
+ * 2. 可以与主项目(Umi)的国际化集成
369
+ * 3. 支持简单的变量替换
370
+ * 4. 降级处理:如果没有翻译,显示 key 本身
371
+ */
550
372
 
551
- // 3. 后面的分隔符(与license之间)
552
- itemsToInsert.push({
553
- type: 'divider'
554
- });
555
- return [...beforeLicense, ...itemsToInsert, licenseItem, ...afterLicense];
556
- }, [menuItems]);
557
- const handleMenuClick = _ref2 => {
558
- let {
559
- key
560
- } = _ref2;
561
- switch (key) {
562
- case 'download':
563
- onDownload();
564
- return;
565
- case 'upload':
566
- onUpload();
567
- return;
568
- default:
569
- // 其他菜单项交给外部回调处理
570
- if (onMenuClick) {
571
- onMenuClick(key);
572
- } else {
573
- console.warn("Unknown menu key: ".concat(key, " and no onMenuClick provided"));
574
- }
373
+ /**
374
+ * 获取当前语言环境
375
+ * 优先从 Umi 全局变量读取,其次使用默认值
376
+ */
377
+ const getLocale = () => {
378
+ // 尝试从 Umi 获取语言设置
379
+ if (typeof window !== 'undefined') {
380
+ var _window$g_initialProp, _window$g_initialProp2;
381
+ // 方式 1:Umi 的 g_initialProps
382
+ if ((_window$g_initialProp = window.g_initialProps) !== null && _window$g_initialProp !== void 0 && _window$g_initialProp.locale) {
383
+ return window.g_initialProps.locale;
575
384
  }
576
- };
577
- const onDownload = async () => {
578
- if (!downloadFiles) {
579
- console.error('downloadFiles function is required for download operation');
580
- message.error('Download functionality not configured');
581
- return;
385
+ // 方式 2:Umi 的全局语言标记
386
+ if ((_window$g_initialProp2 = window.g_initialProps) !== null && _window$g_initialProp2 !== void 0 && (_window$g_initialProp2 = _window$g_initialProp2.___g_initialPropsFromServer) !== null && _window$g_initialProp2 !== void 0 && _window$g_initialProp2.locale) {
387
+ return window.g_initialProps.___g_initialPropsFromServer.locale;
582
388
  }
389
+ // 方式 3:从 localStorage 读取(Umi 默认行为)
583
390
  try {
584
- const res = await downloadFiles();
585
- if (res.status !== 200) {
586
- throw new Error("Unexpected status code: ".concat(res.status));
391
+ const storedLocale = localStorage.getItem('umi-locale');
392
+ if (storedLocale) {
393
+ return storedLocale;
587
394
  }
395
+ } catch (e) {
396
+ // localStorage 不可用时忽略
397
+ }
398
+ // 方式 4:组件库自定义语言标记
399
+ if (window.__COMPONENT_LOCALE__) {
400
+ return window.__COMPONENT_LOCALE__;
401
+ }
402
+ }
403
+ // 默认语言
404
+ return 'zh-CN';
405
+ };
588
406
 
589
- // 默认值,兼容接口响应头没有定义文件名
590
- let fileName = 'config.tar.gz';
591
- const contentDisposition = res.headers['content-disposition'] || '';
407
+ /**
408
+ * 获取语言包
409
+ * 优先从主项目读取,其次使用组件库自带的语言包
410
+ */
411
+ const getMessages = () => {
412
+ if (typeof window !== 'undefined') {
413
+ var _window$g_initialProp3;
414
+ // 尝试从多个来源读取语言包
415
+ const messages = {};
592
416
 
593
- // 获取接口响应的content-disposition字段值,以便获取到文件名
594
- if (contentDisposition) {
595
- const filenameMatch = contentDisposition.match(/filename\s*=\s*"?([^";]+)"?/i);
596
- if (filenameMatch !== null && filenameMatch !== void 0 && filenameMatch[1]) {
597
- fileName = decodeURIComponent(filenameMatch[1]); // 处理编码后的文件名(如 %20 转空格)
598
- }
599
- }
417
+ // 1. 组件库注册的语言包
418
+ const componentMessages = window.__COMPONENT_I18N_MESSAGES__ || {};
600
419
 
601
- // 创建并下载文件
602
- const blob = new Blob([res.data], {
603
- type: 'application/octet-stream'
604
- });
605
- const url = window.URL.createObjectURL(blob);
420
+ // 2. 主项目的语言包
421
+ const projectMessages = window.__PROJECT_I18N_MESSAGES__ || {};
606
422
 
607
- // 下面就是创建一个a标签并触发click事件,来下载该文件
608
- const link = document.createElement('a');
609
- link.style.display = 'none';
610
- link.href = url;
611
- link.target = '_blank';
612
- link.download = fileName;
613
- document.body.appendChild(link);
614
- link.click();
423
+ // 3. Umi 的语言包
424
+ const umiMessages = ((_window$g_initialProp3 = window.g_initialProps) === null || _window$g_initialProp3 === void 0 ? void 0 : _window$g_initialProp3.messages) || {};
615
425
 
616
- // 最后移除a标签并删除创建的ObjectURL对象,防止内存泄漏
617
- link.parentNode.removeChild(link);
618
- window.URL.revokeObjectURL(url);
619
- } catch (error) {
620
- console.error('Download failed:', error);
621
- message.error('Download failed');
622
- }
426
+ // 合并语言包(组件库的优先级最高,其次是主项目,最后是 Umi)
427
+ const allLocales = new Set([...Object.keys(componentMessages), ...Object.keys(projectMessages), ...Object.keys(umiMessages)]);
428
+ allLocales.forEach(locale => {
429
+ messages[locale] = _objectSpread2$1(_objectSpread2$1(_objectSpread2$1({}, umiMessages[locale] || {}), projectMessages[locale] || {}), componentMessages[locale] || {});
430
+ });
431
+ return messages;
432
+ }
433
+ return {};
434
+ };
435
+
436
+ /**
437
+ * 格式化消息
438
+ * @param {string} id - 国际化 key
439
+ * @param {object} values - 变量替换值
440
+ * @param {string} locale - 语言环境
441
+ * @param {object} messages - 语言包
442
+ */
443
+ const formatMessage = function (_ref) {
444
+ var _messages$locale, _messages$enUS;
445
+ let {
446
+ id
447
+ } = _ref;
448
+ let values = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
449
+ let locale = arguments.length > 2 ? arguments[2] : undefined;
450
+ let messages = arguments.length > 3 ? arguments[3] : undefined;
451
+ const message = ((_messages$locale = messages[locale]) === null || _messages$locale === void 0 ? void 0 : _messages$locale[id]) || ((_messages$enUS = messages['en-US']) === null || _messages$enUS === void 0 ? void 0 : _messages$enUS[id]) || id;
452
+
453
+ // 支持变量替换 {min}, {max}, {value} 等
454
+ return message.replace(/\{(\w+)\}/g, (match, key) => {
455
+ return values[key] !== undefined ? values[key] : match;
456
+ });
457
+ };
458
+
459
+ /**
460
+ * 国际化 Hook
461
+ *
462
+ * @returns {{
463
+ * locale: string,
464
+ * formatMessage: function,
465
+ * messages: object
466
+ * }}
467
+ *
468
+ * @example
469
+ * const intl = useIntl();
470
+ * const title = intl.formatMessage({ id: 'networkSettings.title' });
471
+ * const message = intl.formatMessage({ id: 'validation.minLength' }, { min: 3 });
472
+ */
473
+ const useIntl = () => {
474
+ const locale = getLocale();
475
+ const messages = getMessages();
476
+ const formatMessageFn = function (_ref2) {
477
+ let {
478
+ id
479
+ } = _ref2;
480
+ let values = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
481
+ return formatMessage({
482
+ id
483
+ }, values, locale, messages);
623
484
  };
624
- const onUpload = () => {
625
- if (inputRef !== null && inputRef !== void 0 && inputRef.current && !isUploadingRef.current) {
626
- inputRef.current.click();
627
- }
485
+ return {
486
+ locale,
487
+ formatMessage: formatMessageFn,
488
+ messages
628
489
  };
629
- const updatePackage = async event => {
630
- var _event$target$files;
631
- if (isUploadingRef.current) return; // 防止重复上传
490
+ };
632
491
 
633
- const file = (_event$target$files = event.target.files) === null || _event$target$files === void 0 ? void 0 : _event$target$files[0];
634
- if (!file || !upgradeExecute) {
635
- if (inputRef.current) inputRef.current.value = "";
636
- return;
637
- }
638
- isUploadingRef.current = true;
639
- showLoader();
640
- setCurrentStatus('Uploading...');
641
- setUploadProgress(0);
492
+ /**
493
+ * 设置语言环境(可选)
494
+ * 如果需要在运行时切换语言,可以调用此函数
495
+ *
496
+ * @param {string} locale - 语言代码,如 'zh-CN' 或 'en-US'
497
+ */
498
+ const setLocale = locale => {
499
+ if (typeof window !== 'undefined') {
500
+ window.__COMPONENT_LOCALE__ = locale;
501
+ }
502
+ };
642
503
 
643
- // 重置取消令牌
644
- uploadCancelToken.current.cancel();
645
- uploadCancelToken.current = axios.CancelToken.source();
646
- try {
647
- const formData = new FormData();
648
- formData.append('file', file);
504
+ /**
505
+ * 注册语言包(可选)
506
+ * 如果需要在运行时注册语言包,可以调用此函数
507
+ *
508
+ * @param {string} locale - 语言代码
509
+ * @param {object} messages - 语言包对象
510
+ */
511
+ const addMessages = (locale, messages) => {
512
+ if (typeof window !== 'undefined') {
513
+ if (!window.__COMPONENT_I18N_MESSAGES__) {
514
+ window.__COMPONENT_I18N_MESSAGES__ = {};
515
+ }
516
+ window.__COMPONENT_I18N_MESSAGES__[locale] = messages;
517
+ }
518
+ };
649
519
 
650
- // 清除文件选择
651
- if (inputRef.current) inputRef.current.value = "";
520
+ /**
521
+ * 初始化国际化
522
+ * 在组件库初始化时调用,注册默认语言包
523
+ */
524
+ const initI18n = () => {
525
+ // 注册中文语言包
526
+ addMessages('zh-CN', {
527
+ 'button.ok': '确定',
528
+ 'button.cancel': '取消',
529
+ 'button.save': '保存',
530
+ 'button.delete': '删除',
531
+ 'button.edit': '编辑',
532
+ 'button.add': '添加',
533
+ 'button.confirm': '确认',
534
+ 'button.close': '关闭',
535
+ 'button.apply': '应用',
536
+ 'menu.networkSettings': '网络设置',
537
+ 'menu.ptp': 'PTP',
538
+ 'menu.nmos': 'NMOS',
539
+ 'menu.preset': '预设',
540
+ 'menu.license': '许可证'
541
+ // ... 其他默认翻译
542
+ });
652
543
 
653
- // 上传后立即检查业务状态码
654
- // upgradeExecute 使用 umi request 返回的数据是过滤过的
655
- const response = await upgradeExecute(formData, {
656
- onUploadProgress: progressEvent => {
657
- if (!isMountedRef.current) return;
658
- const percentCompleted = Math.round(progressEvent.loaded * 100 / progressEvent.total);
659
- setUploadProgress(percentCompleted); // 更新进度状态
660
- },
661
- cancelToken: uploadCancelToken.current.token
662
- });
544
+ // 注册英文语言包
545
+ addMessages('en-US', {
546
+ 'button.ok': 'OK',
547
+ 'button.cancel': 'Cancel',
548
+ 'button.save': 'Save',
549
+ 'button.delete': 'Delete',
550
+ 'button.edit': 'Edit',
551
+ 'button.add': 'Add',
552
+ 'button.confirm': 'Confirm',
553
+ 'button.close': 'Close',
554
+ 'button.apply': 'Apply',
555
+ 'menu.networkSettings': 'Network Settings',
556
+ 'menu.ptp': 'PTP',
557
+ 'menu.nmos': 'NMOS',
558
+ 'menu.preset': 'Preset',
559
+ 'menu.license': 'License'
560
+ // ... 其他默认翻译
561
+ });
562
+ };
663
563
 
664
- // 检查业务 code
665
- if ((response === null || response === void 0 ? void 0 : response.code) !== 200) {
666
- // 即使上传成功,但如果服务器返回非200,说明有问题
667
- if (isMountedRef.current) {
668
- const errorMsg = (response === null || response === void 0 ? void 0 : response.message) || 'Upload failed due to invalid file format';
669
- message.error(errorMsg);
670
- cancelRequest();
671
- }
672
- return;
673
- }
564
+ const AuthorizationModal = _ref => {
565
+ let {
566
+ onCancel,
567
+ onOk,
568
+ authData,
569
+ title = 'license.title',
570
+ okText,
571
+ cancelText = 'license.button.ignore',
572
+ width = 600
573
+ } = _ref;
574
+ const intl = useIntl();
575
+ const {
576
+ message
577
+ } = App.useApp();
578
+ const [code, setCode] = useState('');
579
+ const {
580
+ accredit_status: isActivated,
581
+ message: statusMessage,
582
+ bios_id: uuid,
583
+ expires_time: expiresTime
584
+ } = authData || {};
674
585
 
675
- // 上传成功,进入等待升级阶段
676
- if (!isMountedRef.current) return;
677
- setCurrentStatus('Upload complete, starting upgrade...');
586
+ // 服务端返回的状态文本映射到国际化 key
587
+ const getStatusMessageKey = msg => {
588
+ if (!msg) return 'license.status.unactivated';
678
589
 
679
- // 延迟后启动轮询
680
- setTimeout(() => {
681
- if (!isMountedRef.current) return;
682
- setPollingInterval(statusPollingInterval);
683
- setCurrentStatus('Upgrading...');
684
- }, uploadCompleteDelay);
685
- } catch (error) {
686
- if (!isMountedRef.current) return;
687
- if (!axios.isCancel(error)) {
688
- console.error("Upload error:", error);
689
- message.error('Upload failed');
690
- }
691
- cancelRequest();
692
- } finally {
693
- if (isMountedRef.current) {
694
- isUploadingRef.current = false;
695
- }
696
- }
590
+ // 根据服务端返回的英文文本映射
591
+ const statusMap = {
592
+ 'Already activated': 'license.status.activated',
593
+ 'Unactivated': 'license.status.unactivated',
594
+ 'Expired': 'license.status.expired',
595
+ 'Invalid': 'license.status.invalid'
596
+ };
597
+ return statusMap[msg] || 'license.status.unactivated';
697
598
  };
698
- const fetchUpgradeStatus = async () => {
699
- if (!upgradeStatus || !isMountedRef.current) return;
700
- try {
701
- // upgradeStatus 使用 axios request 返回的数据没有过滤
702
- const response = await upgradeStatus({
703
- cancelToken: statusCancelToken.current.token
704
- });
705
- if ((response === null || response === void 0 ? void 0 : response.status) === 200) {
706
- const {
707
- code,
708
- message: statusMessage
709
- } = response.data;
710
-
711
- // 状态处理
712
- switch (code) {
713
- case 200:
714
- // 升级成功
715
- message.success(statusMessage, 2.5, () => {
716
- if (isMountedRef.current) {
717
- cancelRequest();
718
- }
719
- window.location.reload();
720
- });
721
- break;
722
- case 201:
723
- // 升级中 — 继续轮询
724
- break;
725
- case 202: // 升级失败
726
- case 203:
727
- // 升级异常
728
- if (isMountedRef.current) {
729
- message.error(statusMessage);
730
- cancelRequest();
731
- }
732
- break;
733
- default:
734
- // 其他 code 如 500、400 等
735
- if (isMountedRef.current) {
736
- message.error(statusMessage || 'Upgrade process failed');
737
- cancelRequest();
738
- }
739
- break;
740
- }
741
- }
742
- } catch (error) {
743
- if (!isMountedRef.current) return;
744
- if (!axios.isCancel(error)) {
745
- console.error('Status check failed:', error);
746
- }
599
+ const statusMessageKey = getStatusMessageKey(statusMessage);
600
+ const handleOk = () => {
601
+ const trimmedCode = code.trim();
602
+ if (!trimmedCode) {
603
+ message.error(intl.formatMessage({
604
+ id: 'license.validation.keyRequired'
605
+ }));
606
+ return;
747
607
  }
608
+ onOk(trimmedCode);
748
609
  };
749
- const cancelRequest = () => {
750
- // 取消状态轮询
751
- uploadCancelToken.current.cancel('Operation canceled');
752
- statusCancelToken.current.cancel('Operation canceled');
753
- uploadCancelToken.current = axios.CancelToken.source();
754
- statusCancelToken.current = axios.CancelToken.source();
755
-
756
- // 重置所有状态
757
- setPollingInterval(undefined);
758
- setCurrentStatus('idle');
759
- setUploadProgress(0);
760
- isUploadingRef.current = false;
761
- hideLoader();
762
- };
763
-
764
- // 轮询状态
765
- useInterval(fetchUpgradeStatus, pollingInterval);
766
-
767
- // 组件卸载时清理
768
- useEffect(() => {
769
- return () => {
770
- isMountedRef.current = false;
771
- uploadCancelToken.current.cancel();
772
- statusCancelToken.current.cancel();
773
- };
774
- }, []);
775
- const upgradeElement = /*#__PURE__*/jsxs("div", {
776
- children: [/*#__PURE__*/jsx(Dropdown, {
777
- menu: {
778
- items: finalMenuItems,
779
- onClick: handleMenuClick
610
+ const statusAlert = isActivated ? /*#__PURE__*/jsx(Alert, {
611
+ message: "".concat(intl.formatMessage({
612
+ id: statusMessageKey
613
+ }), ", ").concat(intl.formatMessage({
614
+ id: 'license.status.expiresOn'
615
+ }), " ").concat(expiresTime),
616
+ type: "success",
617
+ showIcon: true
618
+ }) : /*#__PURE__*/jsx(Alert, {
619
+ message: intl.formatMessage({
620
+ id: statusMessageKey
621
+ }),
622
+ type: "warning",
623
+ showIcon: true
624
+ });
625
+ const defaultOkText = isActivated ? intl.formatMessage({
626
+ id: 'license.button.reactivate'
627
+ }) : intl.formatMessage({
628
+ id: 'license.button.activate'
629
+ });
630
+ return /*#__PURE__*/jsx(StyledModal$3, {
631
+ title: intl.formatMessage({
632
+ id: title
633
+ }),
634
+ width: width,
635
+ open: true,
636
+ onCancel: onCancel,
637
+ onOk: handleOk,
638
+ okText: okText || defaultOkText,
639
+ cancelText: intl.formatMessage({
640
+ id: cancelText
641
+ }),
642
+ cancelButtonProps: {
643
+ disabled: !isActivated
644
+ },
645
+ maskClosable: false,
646
+ closable: false,
647
+ children: /*#__PURE__*/jsxs(Form, {
648
+ name: "auth_form",
649
+ labelCol: {
650
+ span: 6
780
651
  },
781
- trigger: ["hover"],
782
- children: /*#__PURE__*/jsx("a", {
783
- onClick: e => e.preventDefault(),
784
- children: /*#__PURE__*/jsx("i", {
785
- className: "seeder-iconfont seeder-icon-liebiao2 text-xl text-neutral-400"
786
- })
787
- })
788
- }), /*#__PURE__*/jsx("input", {
789
- ref: inputRef,
790
- type: "file",
791
- onChange: updatePackage,
792
- className: "hidden",
793
- accept: acceptFileTypes
794
- }), /*#__PURE__*/jsx(Spin, {
795
- spinning: isSpinning,
796
- indicator: /*#__PURE__*/jsx(LoadingOutlined, {
652
+ wrapperCol: {
653
+ span: 18
654
+ },
655
+ autoComplete: "off",
656
+ children: [/*#__PURE__*/jsx("div", {
797
657
  style: {
798
- fontSize: 60
658
+ marginBottom: 16
799
659
  },
800
- spin: true
801
- }),
802
- tip: currentStatus === "Uploading..." ? "".concat(currentStatus, " ").concat(uploadProgress, "%") : currentStatus,
803
- size: "large",
804
- fullscreen: true
805
- })]
660
+ children: statusAlert
661
+ }), /*#__PURE__*/jsx(Form.Item, {
662
+ label: intl.formatMessage({
663
+ id: 'license.machineCode'
664
+ }),
665
+ required: true,
666
+ children: /*#__PURE__*/jsx(Input, {
667
+ value: uuid,
668
+ readOnly: true
669
+ })
670
+ }), /*#__PURE__*/jsx(Form.Item, {
671
+ label: intl.formatMessage({
672
+ id: 'license.key'
673
+ }),
674
+ required: true,
675
+ children: /*#__PURE__*/jsx(Input.TextArea, {
676
+ value: code,
677
+ onChange: e => setCode(e.target.value),
678
+ autoSize: {
679
+ minRows: 6,
680
+ maxRows: 6
681
+ },
682
+ placeholder: intl.formatMessage({
683
+ id: 'license.placeholder.enterKey'
684
+ })
685
+ })
686
+ })]
687
+ })
806
688
  });
807
- return [upgradeElement];
808
689
  };
809
- var useUpgrade$1 = useUpgrade;
810
-
811
- const useSystemOperations = function () {
812
- let {
813
- onPowerOff,
814
- onRestart,
815
- confirmTitle = "Confirm",
816
- cancelText = "No",
817
- okText = "Yes",
818
- run
819
- } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
820
- const {
821
- modal: AntdModal
822
- } = App.useApp();
823
- const doAction = useCallback(action => {
824
- try {
825
- AntdModal.confirm({
826
- icon: /*#__PURE__*/jsx(ExclamationCircleFilled, {}),
827
- title: "".concat(confirmTitle, " ").concat(action, "?"),
828
- cancelText,
829
- okText,
830
- onOk: () => {
831
- if (action === 'poweroff' && onPowerOff) {
832
- onPowerOff();
833
- } else if (action === 'restart' && onRestart) {
834
- onRestart();
835
- }
690
+ var AuthorizationModal$1 = AuthorizationModal;
836
691
 
837
- // Call the run callback after successful operation
838
- if (typeof run === 'function') {
839
- run();
692
+ const DEFAULT_AUTH = {
693
+ accredit_status: false,
694
+ // 授权状态
695
+ bios_id: '',
696
+ // 主机id
697
+ message: 'Unactivated',
698
+ // 激活信息
699
+ expires_time: '' // 授权到期时间
700
+ };
701
+ const useAuth = options => {
702
+ const {
703
+ fetchAuthInfo,
704
+ authorize,
705
+ defaultAuthData = DEFAULT_AUTH,
706
+ autoShowModal = true
707
+ } = options || {};
708
+ const [messageApi, contextHolder] = message.useMessage();
709
+ const [showModal, setShowModal] = useState(false); // 默认隐藏
710
+ const [authData, setAuthData] = useState(defaultAuthData);
711
+ const [isRegistered, setIsRegistered] = useState(false);
712
+ const [loading, setLoading] = useState(false);
713
+ const openModal = useCallback(() => setShowModal(true), []);
714
+ const closeModal = useCallback(() => setShowModal(false), []);
715
+ useEffect(() => {
716
+ async function loadAuthInfo() {
717
+ try {
718
+ setLoading(true);
719
+ const result = await fetchAuthInfo();
720
+ if (result !== null && result !== void 0 && result.commands) {
721
+ const commands = result.commands;
722
+ setAuthData(commands);
723
+ setIsRegistered(commands.accredit_status);
724
+ if (autoShowModal) {
725
+ commands.accredit_status ? closeModal() : openModal();
840
726
  }
841
727
  }
728
+ } catch (error) {
729
+ console.error('Failed to fetch auth info:', error);
730
+ } finally {
731
+ setLoading(false);
732
+ }
733
+ }
734
+ loadAuthInfo();
735
+ }, [fetchAuthInfo, autoShowModal, openModal, closeModal]);
736
+ const auth = async code => {
737
+ try {
738
+ setLoading(true);
739
+ const result = await authorize({
740
+ type: "accredit_command",
741
+ commands: {
742
+ id: "LICENSE",
743
+ license_data: code
744
+ }
842
745
  });
746
+ if (result !== null && result !== void 0 && result.commands) {
747
+ const commands = result.commands;
748
+ // type accredit_message
749
+ if (commands !== null && commands !== void 0 && commands.accredit_status) {
750
+ setIsRegistered(true);
751
+ setAuthData(commands);
752
+ closeModal();
753
+ } else {
754
+ messageApi.error(commands.ciphertext_status || 'Authorization failed');
755
+ }
756
+ }
843
757
  } catch (error) {
844
- console.error("".concat(action.toUpperCase(), " ERROR: "), error);
758
+ console.error('Authorization error:', error);
759
+ } finally {
760
+ setLoading(false);
845
761
  }
846
- }, [AntdModal, confirmTitle, cancelText, okText, onPowerOff, onRestart]);
847
- const getMenuItems = useCallback(() => [{
848
- key: "poweroff",
849
- label: "Power Off"
850
- }, {
851
- key: "restart",
852
- label: "Restart"
853
- }], []);
854
- const handleMenuClick = useCallback(_ref => {
855
- let {
856
- key
857
- } = _ref;
858
- doAction(key);
859
- }, [doAction]);
762
+ };
763
+ const modal = showModal && /*#__PURE__*/jsxs(Fragment, {
764
+ children: [/*#__PURE__*/jsx(AuthorizationModal$1, {
765
+ onOk: auth,
766
+ onCancel: closeModal,
767
+ authData: authData
768
+ }), contextHolder]
769
+ });
860
770
  return {
861
- menuItems: getMenuItems(),
862
- handleMenuClick
771
+ AuthModal: modal,
772
+ openAuthModal: openModal,
773
+ closeAuthModal: closeModal,
774
+ isRegistered,
775
+ authData,
776
+ loading
863
777
  };
864
778
  };
865
- var useSystemOperations$1 = useSystemOperations;
866
-
867
- const usePageReload = () => {
868
- const [reloading, setReloading] = useState(false);
869
- const startReload = useCallback(function () {
870
- let delay = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 800;
871
- if (reloading) return; // 防止重复触发
872
-
873
- // 显示遮罩
874
- setReloading(true);
875
-
876
- // 设置背景色
877
- document.body.style.backgroundColor = '#282828';
878
- document.body.style.transition = 'background-color 0.1s';
879
- document.body.offsetHeight;
880
779
 
881
- // 延迟刷新
882
- const timer = setTimeout(() => {
883
- window.location.reload();
884
- }, delay);
780
+ const useUpgrade = _ref => {
781
+ let {
782
+ menuItems = [],
783
+ onMenuClick,
784
+ downloadFiles,
785
+ upgradeExecute,
786
+ // 上传接口:(formData, config) => Promise<response>
787
+ upgradeStatus,
788
+ // 状态轮询接口:(config) => Promise<response>
789
+ acceptFileTypes = "application/octet-stream",
790
+ uploadCompleteDelay = 3000,
791
+ statusPollingInterval = 1000
792
+ } = _ref;
793
+ const intl = useIntl();
794
+ const [isSpinning, setIsSpinning] = useState(false);
795
+ const [pollingInterval, setPollingInterval] = useState(undefined); // 间隔时间,当设置值为 undefined 时会停止计时器
796
+ const [currentStatus, setCurrentStatus] = useState(intl.formatMessage({
797
+ id: 'upgrade.status.idle'
798
+ })); // 'idle' | 'uploading' | 'Upload complete, starting upgrade...' | 'upgrading'
799
+ const [uploadProgress, setUploadProgress] = useState(0);
800
+ const inputRef = useRef(null);
801
+ // 控制并发上传
802
+ const isUploadingRef = useRef(false);
803
+ // 标记组件是否已卸载
804
+ const isMountedRef = useRef(true);
885
805
 
886
- // 清理函数(防止内存泄漏)
887
- return () => clearTimeout(timer);
888
- }, [reloading]);
889
- const ReloadOverlay = () => {
890
- if (!reloading) return null;
891
- return /*#__PURE__*/jsxs("div", {
892
- style: {
893
- position: 'fixed',
894
- top: 0,
895
- left: 0,
896
- right: 0,
897
- bottom: 0,
898
- backgroundColor: '#282828',
899
- display: 'flex',
900
- flexDirection: 'column',
901
- alignItems: 'center',
902
- justifyContent: 'center',
903
- zIndex: 999999,
904
- color: 'white'
905
- },
906
- children: [/*#__PURE__*/jsx(Spin, {
907
- size: "large"
908
- }), /*#__PURE__*/jsx("div", {
909
- style: {
910
- marginTop: 20,
911
- fontSize: 16
912
- },
913
- children: "Loading preset configuration..."
914
- }), /*#__PURE__*/jsx("div", {
915
- style: {
916
- marginTop: 10,
917
- fontSize: 12,
918
- color: '#aaa'
919
- },
920
- children: "Page will refresh shortly"
921
- })]
922
- });
923
- };
924
- return {
925
- startReload,
926
- ReloadOverlay
927
- };
928
- };
929
- var usePageReload$1 = usePageReload;
806
+ // 分别创建独立的取消令牌
807
+ const uploadCancelToken = useRef(axios.CancelToken.source());
808
+ const statusCancelToken = useRef(axios.CancelToken.source());
809
+ const showLoader = () => setIsSpinning(true);
810
+ const hideLoader = () => setIsSpinning(false);
930
811
 
931
- const getReadyStateText = state => {
932
- const states = {
933
- 0: 'CONNECTING',
934
- 1: 'OPEN',
935
- 2: 'CLOSING',
936
- 3: 'CLOSED'
937
- };
938
- return states[state] || "UNKNOWN(".concat(state, ")");
939
- };
812
+ // 构建菜单项 - 确保至少包含download和upload
813
+ // 构建菜单项 - 最终完美版本
814
+ const finalMenuItems = useMemo(() => {
815
+ const hasDownload = menuItems.some(item => item.key === 'download');
816
+ const hasUpload = menuItems.some(item => item.key === 'upload');
817
+ if (hasDownload && hasUpload) {
818
+ return menuItems;
819
+ }
820
+ const licenseIndex = menuItems.findIndex(item => item.key === 'license');
940
821
 
941
- // 创建空日志器
942
- const createDummyLogger = () => {
943
- const dummyFn = () => {};
944
- return {
945
- log: dummyFn,
946
- exportLogs: dummyFn,
947
- getLogStats: () => ({
948
- total: 0,
949
- errors: 0,
950
- warnings: 0,
951
- info: 0,
952
- lastTimestamp: null
953
- }),
954
- clearLogs: dummyFn,
955
- logs: []
956
- };
957
- };
822
+ // 如果没有 license,在末尾添加
823
+ if (licenseIndex === -1) {
824
+ const itemsToAdd = [];
825
+ if (menuItems.length > 0) itemsToAdd.push({
826
+ type: 'divider'
827
+ });
828
+ // if (!hasDownload) itemsToAdd.push({ key: "download", label: intl.formatMessage({ id: 'upgrade.menu.download' }) });
829
+ if (!hasUpload) itemsToAdd.push({
830
+ key: "upload",
831
+ label: intl.formatMessage({
832
+ id: 'upgrade.menu.softwareUpdate'
833
+ })
834
+ });
835
+ return [...menuItems, ...itemsToAdd];
836
+ }
958
837
 
959
- // 创建通用的日志器
960
- const createGlobalLogger = function () {
961
- let webSocketUrl = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
962
- const logs = [];
963
- const maxLogs = 1000;
838
+ // 有 license,在 license 前面插入
839
+ const beforeLicense = menuItems.slice(0, licenseIndex);
840
+ const licenseItem = menuItems[licenseIndex];
841
+ const afterLicense = menuItems.slice(licenseIndex + 1);
842
+ const itemsToInsert = [];
964
843
 
965
- // 判断是否为开发环境
966
- const isDevelopment = process.env.NODE_ENV === 'development';
967
- const log = function (level, message) {
968
- let data = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
969
- const timestamp = new Date().toISOString();
970
- const logEntry = {
971
- timestamp,
972
- level,
973
- message,
974
- data,
975
- timestampDisplay: new Date().toLocaleString('zh-CN', {
976
- year: 'numeric',
977
- month: '2-digit',
978
- day: '2-digit',
979
- hour: '2-digit',
980
- minute: '2-digit',
981
- second: '2-digit',
982
- hour12: false
844
+ // 1. 前面的分隔符(如果 beforeLicense 不为空且最后一项不是分隔符)
845
+ if (beforeLicense.length > 0 && beforeLicense[beforeLicense.length - 1].type !== 'divider') {
846
+ itemsToInsert.push({
847
+ type: 'divider'
848
+ });
849
+ }
850
+
851
+ // 2. 添加缺少的项
852
+ // if (!hasDownload) itemsToInsert.push({ key: "download", label: intl.formatMessage({ id: 'upgrade.menu.download' }) });
853
+ if (!hasUpload) itemsToInsert.push({
854
+ key: "upload",
855
+ label: intl.formatMessage({
856
+ id: 'upgrade.menu.softwareUpdate'
983
857
  })
984
- };
985
- logs.push(logEntry);
986
- if (logs.length > maxLogs) logs.shift();
858
+ });
987
859
 
988
- // 只在开发环境下输出到控制台
989
- if (isDevelopment) {
990
- const consoleMessage = "[".concat(logEntry.timestampDisplay, "] [WebSocket] [").concat(level, "] ").concat(message);
991
- const consoleArgs = [consoleMessage];
992
- if (Object.keys(data).length > 0) consoleArgs.push(data);
993
- switch (level) {
994
- case 'ERROR':
995
- console.error(...consoleArgs);
996
- break;
997
- case 'WARN':
998
- console.warn(...consoleArgs);
999
- break;
1000
- case 'INFO':
1001
- console.info(...consoleArgs);
1002
- break;
1003
- default:
1004
- console.log(...consoleArgs);
1005
- }
860
+ // 3. 后面的分隔符(与 license 之间)
861
+ itemsToInsert.push({
862
+ type: 'divider'
863
+ });
864
+ return [...beforeLicense, ...itemsToInsert, licenseItem, ...afterLicense];
865
+ }, [menuItems, intl]);
866
+ const handleMenuClick = _ref2 => {
867
+ let {
868
+ key
869
+ } = _ref2;
870
+ switch (key) {
871
+ case 'download':
872
+ onDownload();
873
+ return;
874
+ case 'upload':
875
+ onUpload();
876
+ return;
877
+ default:
878
+ // 其他菜单项交给外部回调处理
879
+ if (onMenuClick) {
880
+ onMenuClick(key);
881
+ } else {
882
+ console.warn("Unknown menu key: ".concat(key, " and no onMenuClick provided"));
883
+ }
1006
884
  }
1007
- return logEntry;
1008
885
  };
1009
- const exportLogs = function () {
1010
- let filenamePrefix = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'websocket';
1011
- if (logs.length === 0) {
1012
- log('WARN', '没有日志可导出', {
1013
- filenamePrefix
1014
- });
886
+ const onDownload = async () => {
887
+ if (!downloadFiles) {
888
+ console.error('downloadFiles function is required for download operation');
889
+ message.error(intl.formatMessage({
890
+ id: 'upgrade.error.downloadNotConfigured'
891
+ }));
1015
892
  return;
1016
893
  }
1017
- const logText = logs.map(entry => "[".concat(entry.timestampDisplay, "] [").concat(entry.level, "] ").concat(entry.message, "\n\u6570\u636E: ").concat(JSON.stringify(entry.data, null, 2))).join('\n' + '='.repeat(50) + '\n');
1018
- const blob = new Blob([logText], {
1019
- type: 'text/plain;charset=utf-8'
1020
- });
1021
- const blobUrl = URL.createObjectURL(blob);
1022
- let hostname = 'unknown';
1023
- let pathname = 'unknown';
1024
894
  try {
1025
- if (webSocketUrl) {
1026
- const urlObj = new URL(webSocketUrl.replace(/^ws/, 'http'));
1027
- hostname = urlObj.hostname.replace(/\./g, '-');
1028
- pathname = urlObj.pathname.replace(/\//g, '-').replace(/^-|-$/g, '') || 'root';
895
+ const res = await downloadFiles();
896
+ if (res.status !== 200) {
897
+ throw new Error("Unexpected status code: ".concat(res.status));
898
+ }
899
+
900
+ // 默认值,兼容接口响应头没有定义文件名
901
+ let fileName = 'config.tar.gz';
902
+ const contentDisposition = res.headers['content-disposition'] || '';
903
+
904
+ // 获取接口响应的content-disposition字段值,以便获取到文件名
905
+ if (contentDisposition) {
906
+ const filenameMatch = contentDisposition.match(/filename\s*=\s*"?([^";]+)"?/i);
907
+ if (filenameMatch !== null && filenameMatch !== void 0 && filenameMatch[1]) {
908
+ fileName = decodeURIComponent(filenameMatch[1]); // 处理编码后的文件名(如 %20 转空格)
909
+ }
1029
910
  }
911
+
912
+ // 创建并下载文件
913
+ const blob = new Blob([res.data], {
914
+ type: 'application/octet-stream'
915
+ });
916
+ const url = window.URL.createObjectURL(blob);
917
+
918
+ // 下面就是创建一个 a 标签并触发 click 事件,来下载该文件
919
+ const link = document.createElement('a');
920
+ link.style.display = 'none';
921
+ link.href = url;
922
+ link.target = '_blank';
923
+ link.download = fileName;
924
+ document.body.appendChild(link);
925
+ link.click();
926
+
927
+ // 最后移除 a 标签并删除创建的 ObjectURL 对象,防止内存泄漏
928
+ link.parentNode.removeChild(link);
929
+ window.URL.revokeObjectURL(url);
1030
930
  } catch (error) {
1031
- console.warn('无法解析WebSocket URL:', error);
931
+ console.error('Download failed:', error);
932
+ message.error(intl.formatMessage({
933
+ id: 'upgrade.error.downloadFailed'
934
+ }));
1032
935
  }
1033
- const readableTime = new Date().toLocaleString('zh-CN', {
1034
- year: 'numeric',
1035
- month: '2-digit',
1036
- day: '2-digit',
1037
- hour: '2-digit',
1038
- minute: '2-digit',
1039
- second: '2-digit',
1040
- hour12: false
1041
- }).replace(/[\/:\s]/g, '-');
1042
- const filename = "".concat(filenamePrefix, "-").concat(hostname, "-").concat(pathname, "-").concat(readableTime, ".log");
1043
- const a = document.createElement('a');
1044
- a.href = blobUrl;
1045
- a.download = filename;
1046
- document.body.appendChild(a);
1047
- a.click();
1048
- document.body.removeChild(a);
1049
- URL.revokeObjectURL(blobUrl);
1050
- log('INFO', '日志文件已导出', {
1051
- count: logs.length,
1052
- filename
1053
- });
1054
- };
1055
- const getLogStats = () => {
1056
- var _logs;
1057
- return {
1058
- total: logs.length,
1059
- errors: logs.filter(l => l.level === 'ERROR').length,
1060
- warnings: logs.filter(l => l.level === 'WARN').length,
1061
- info: logs.filter(l => l.level === 'INFO').length,
1062
- lastTimestamp: (_logs = logs[logs.length - 1]) === null || _logs === void 0 ? void 0 : _logs.timestampDisplay
1063
- };
1064
- };
1065
- const clearLogs = () => {
1066
- logs.length = 0;
1067
- log('INFO', '日志已清空');
1068
936
  };
1069
- return {
1070
- log,
1071
- exportLogs,
1072
- getLogStats,
1073
- clearLogs,
1074
- logs
937
+ const onUpload = () => {
938
+ if (inputRef !== null && inputRef !== void 0 && inputRef.current && !isUploadingRef.current) {
939
+ inputRef.current.click();
940
+ }
1075
941
  };
1076
- };
942
+ const updatePackage = async event => {
943
+ var _event$target$files;
944
+ if (isUploadingRef.current) return; // 防止重复上传
1077
945
 
1078
- // 心跳管理器
1079
- const useHeartbeat = function (sendMessage, readyState, config, logger) {
1080
- let enabled = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
1081
- const heartbeatTimerRef = useRef(null);
1082
- const lastHeartbeatTimeRef = useRef(Date.now());
1083
- const isMountedRef = useRef(true);
1084
- const configRef = useRef({
1085
- interval: (config === null || config === void 0 ? void 0 : config.interval) || 20000,
1086
- message: (config === null || config === void 0 ? void 0 : config.message) || {
1087
- type: 'ping'
1088
- }
1089
- });
1090
- const stateRef = useRef({
1091
- sendMessage,
1092
- readyState,
1093
- enabled
1094
- });
1095
- useEffect(() => {
1096
- configRef.current = {
1097
- interval: (config === null || config === void 0 ? void 0 : config.interval) || 20000,
1098
- message: (config === null || config === void 0 ? void 0 : config.message) || {
1099
- type: 'ping'
1100
- }
1101
- };
1102
- }, [config === null || config === void 0 ? void 0 : config.interval, config === null || config === void 0 ? void 0 : config.message]);
1103
- useEffect(() => {
1104
- stateRef.current = {
1105
- sendMessage,
1106
- readyState,
1107
- enabled
1108
- };
1109
- }, [sendMessage, readyState, enabled]);
1110
- useEffect(() => {
1111
- return () => {
1112
- isMountedRef.current = false;
1113
- if (heartbeatTimerRef.current) {
1114
- clearInterval(heartbeatTimerRef.current);
1115
- }
1116
- };
1117
- }, []);
1118
- const stopHeartbeat = useCallback(() => {
1119
- if (heartbeatTimerRef.current) {
1120
- clearInterval(heartbeatTimerRef.current);
1121
- heartbeatTimerRef.current = null;
1122
- if (stateRef.current.enabled) {
1123
- logger.log('INFO', '停止心跳机制');
1124
- }
946
+ const file = (_event$target$files = event.target.files) === null || _event$target$files === void 0 ? void 0 : _event$target$files[0];
947
+ if (!file || !upgradeExecute) {
948
+ if (inputRef.current) inputRef.current.value = "";
949
+ return;
1125
950
  }
1126
- }, [logger]);
1127
- const startHeartbeat = useCallback(() => {
1128
- stopHeartbeat();
1129
- const currentState = stateRef.current;
1130
- const currentConfig = configRef.current;
1131
- if (!currentState.enabled) return;
1132
- logger.log('INFO', '启动心跳机制', {
1133
- interval: currentConfig.interval
1134
- });
1135
- heartbeatTimerRef.current = setInterval(() => {
1136
- // 检查组件是否挂载
1137
- if (!isMountedRef.current || !heartbeatTimerRef.current) {
1138
- return;
1139
- }
951
+ isUploadingRef.current = true;
952
+ showLoader();
953
+ setCurrentStatus(intl.formatMessage({
954
+ id: 'upgrade.status.uploading'
955
+ }));
956
+ setUploadProgress(0);
1140
957
 
1141
- // 目的: 总是获取最新的值
1142
- const latestState = stateRef.current;
1143
- const latestConfig = configRef.current;
1144
- if (latestState.sendMessage && latestState.readyState === 1) {
1145
- try {
1146
- latestState.sendMessage(JSON.stringify(latestConfig.message));
1147
- lastHeartbeatTimeRef.current = Date.now();
1148
- logger.log('DEBUG', '发送心跳包', {
1149
- timeSinceLastHeartbeat: Date.now() - lastHeartbeatTimeRef.current,
1150
- readyState: getReadyStateText(latestState.readyState)
1151
- });
1152
- } catch (error) {
1153
- logger.log('ERROR', '发送心跳失败', {
1154
- error: error.message,
1155
- readyState: getReadyStateText(latestState.readyState)
958
+ // 重置取消令牌
959
+ uploadCancelToken.current.cancel();
960
+ uploadCancelToken.current = axios.CancelToken.source();
961
+ try {
962
+ const formData = new FormData();
963
+ formData.append('file', file);
964
+
965
+ // 清除文件选择
966
+ if (inputRef.current) inputRef.current.value = "";
967
+
968
+ // 上传后立即检查业务状态码
969
+ // upgradeExecute 使用 umi request 返回的数据是过滤过的
970
+ const response = await upgradeExecute(formData, {
971
+ onUploadProgress: progressEvent => {
972
+ if (!isMountedRef.current) return;
973
+ const percentCompleted = Math.round(progressEvent.loaded * 100 / progressEvent.total);
974
+ setUploadProgress(percentCompleted); // 更新进度状态
975
+ },
976
+ cancelToken: uploadCancelToken.current.token
977
+ });
978
+
979
+ // 检查业务 code
980
+ if ((response === null || response === void 0 ? void 0 : response.code) !== 200) {
981
+ // 即使上传成功,但如果服务器返回非 200,说明有问题
982
+ if (isMountedRef.current) {
983
+ const errorMsg = (response === null || response === void 0 ? void 0 : response.message) || intl.formatMessage({
984
+ id: 'upgrade.error.invalidFileFormat'
1156
985
  });
986
+ message.error(errorMsg);
987
+ cancelRequest();
1157
988
  }
989
+ return;
1158
990
  }
1159
- }, currentConfig.interval);
1160
- }, [stopHeartbeat, logger]);
1161
991
 
1162
- // 自动管理心跳
1163
- useEffect(() => {
1164
- const currentState = stateRef.current;
1165
- if (currentState.enabled) {
1166
- if (currentState.readyState === 1) {
1167
- startHeartbeat();
1168
- } else {
1169
- stopHeartbeat();
992
+ // 上传成功,进入等待升级阶段
993
+ if (!isMountedRef.current) return;
994
+ setCurrentStatus(intl.formatMessage({
995
+ id: 'upgrade.status.uploadComplete'
996
+ }));
997
+
998
+ // 延迟后启动轮询
999
+ setTimeout(() => {
1000
+ if (!isMountedRef.current) return;
1001
+ setPollingInterval(statusPollingInterval);
1002
+ setCurrentStatus(intl.formatMessage({
1003
+ id: 'upgrade.status.upgrading'
1004
+ }));
1005
+ }, uploadCompleteDelay);
1006
+ } catch (error) {
1007
+ if (!isMountedRef.current) return;
1008
+ if (!axios.isCancel(error)) {
1009
+ console.error("Upload error:", error);
1010
+ message.error(intl.formatMessage({
1011
+ id: 'upgrade.error.uploadFailed'
1012
+ }));
1013
+ }
1014
+ cancelRequest();
1015
+ } finally {
1016
+ if (isMountedRef.current) {
1017
+ isUploadingRef.current = false;
1170
1018
  }
1171
- } else {
1172
- stopHeartbeat();
1173
1019
  }
1174
- return () => {
1175
- if (heartbeatTimerRef.current) {
1176
- clearInterval(heartbeatTimerRef.current);
1177
- heartbeatTimerRef.current = null;
1020
+ };
1021
+ const fetchUpgradeStatus = async () => {
1022
+ if (!upgradeStatus || !isMountedRef.current) return;
1023
+ try {
1024
+ // upgradeStatus 使用 axios request 返回的数据没有过滤
1025
+ const response = await upgradeStatus({
1026
+ cancelToken: statusCancelToken.current.token
1027
+ });
1028
+ if ((response === null || response === void 0 ? void 0 : response.status) === 200) {
1029
+ const {
1030
+ code,
1031
+ message: statusMessage
1032
+ } = response.data;
1033
+
1034
+ // 状态处理
1035
+ switch (code) {
1036
+ case 200:
1037
+ // 升级成功
1038
+ message.success(statusMessage, 2.5, () => {
1039
+ if (isMountedRef.current) {
1040
+ cancelRequest();
1041
+ }
1042
+ window.location.reload();
1043
+ });
1044
+ break;
1045
+ case 201:
1046
+ // 升级中 — 继续轮询
1047
+ break;
1048
+ case 202: // 升级失败
1049
+ case 203:
1050
+ // 升级异常
1051
+ if (isMountedRef.current) {
1052
+ message.error(statusMessage);
1053
+ cancelRequest();
1054
+ }
1055
+ break;
1056
+ default:
1057
+ // 其他 code 如 500、400 等
1058
+ if (isMountedRef.current) {
1059
+ message.error(statusMessage || intl.formatMessage({
1060
+ id: 'upgrade.error.upgradeFailed'
1061
+ }));
1062
+ cancelRequest();
1063
+ }
1064
+ break;
1065
+ }
1066
+ }
1067
+ } catch (error) {
1068
+ if (!isMountedRef.current) return;
1069
+ if (!axios.isCancel(error)) {
1070
+ console.error('Status check failed:', error);
1178
1071
  }
1179
- };
1180
- }, [readyState, enabled, startHeartbeat, stopHeartbeat]);
1181
- const heartbeatImpl = useMemo(() => {
1182
- if (!enabled) {
1183
- return {
1184
- startHeartbeat: () => {},
1185
- stopHeartbeat: () => {},
1186
- getLastHeartbeatTime: () => null,
1187
- isEnabled: false
1188
- };
1189
1072
  }
1190
- return {
1191
- startHeartbeat,
1192
- stopHeartbeat,
1193
- getLastHeartbeatTime: () => lastHeartbeatTimeRef.current,
1194
- isEnabled: true
1073
+ };
1074
+ const cancelRequest = () => {
1075
+ // 取消状态轮询
1076
+ uploadCancelToken.current.cancel('Operation canceled');
1077
+ statusCancelToken.current.cancel('Operation canceled');
1078
+ uploadCancelToken.current = axios.CancelToken.source();
1079
+ statusCancelToken.current = axios.CancelToken.source();
1080
+
1081
+ // 重置所有状态
1082
+ setPollingInterval(undefined);
1083
+ setCurrentStatus('idle');
1084
+ setUploadProgress(0);
1085
+ isUploadingRef.current = false;
1086
+ hideLoader();
1087
+ };
1088
+
1089
+ // 轮询状态
1090
+ useInterval(fetchUpgradeStatus, pollingInterval);
1091
+
1092
+ // 组件卸载时清理
1093
+ useEffect(() => {
1094
+ return () => {
1095
+ isMountedRef.current = false;
1096
+ uploadCancelToken.current.cancel();
1097
+ statusCancelToken.current.cancel();
1195
1098
  };
1196
- }, [enabled, startHeartbeat, stopHeartbeat]);
1197
- return heartbeatImpl;
1099
+ }, []);
1100
+ const upgradeElement = /*#__PURE__*/jsxs("div", {
1101
+ children: [/*#__PURE__*/jsx(Dropdown, {
1102
+ menu: {
1103
+ items: finalMenuItems,
1104
+ onClick: handleMenuClick
1105
+ },
1106
+ trigger: ["hover"],
1107
+ children: /*#__PURE__*/jsx("a", {
1108
+ onClick: e => e.preventDefault(),
1109
+ children: /*#__PURE__*/jsx("i", {
1110
+ className: "seeder-iconfont seeder-icon-liebiao2 text-xl text-neutral-400"
1111
+ })
1112
+ })
1113
+ }), /*#__PURE__*/jsx("input", {
1114
+ ref: inputRef,
1115
+ type: "file",
1116
+ onChange: updatePackage,
1117
+ className: "hidden",
1118
+ accept: acceptFileTypes
1119
+ }), /*#__PURE__*/jsx(Spin, {
1120
+ spinning: isSpinning,
1121
+ indicator: /*#__PURE__*/jsx(LoadingOutlined, {
1122
+ style: {
1123
+ fontSize: 60
1124
+ },
1125
+ spin: true
1126
+ }),
1127
+ tip: currentStatus === intl.formatMessage({
1128
+ id: 'upgrade.status.uploading'
1129
+ }) ? "".concat(currentStatus, " ").concat(uploadProgress, "%") : currentStatus,
1130
+ size: "large",
1131
+ fullscreen: true
1132
+ })]
1133
+ });
1134
+ return [upgradeElement];
1198
1135
  };
1199
- const useWebSocketWithFeatures = config => {
1200
- const {
1201
- url,
1202
- options = {},
1203
- heartbeat: heartbeatConfig = false,
1204
- enableLog = true
1205
- } = config;
1206
- const [isConnected, setIsConnected] = useState(false);
1136
+ var useUpgrade$1 = useUpgrade;
1207
1137
 
1208
- // 创建 logger 实例
1209
- const logger = useMemo(() => {
1210
- if (enableLog) {
1211
- return createGlobalLogger(url);
1212
- }
1213
- return createDummyLogger();
1214
- }, [url, enableLog]);
1215
- const websocketOptions = useMemo(() => _objectSpread2$1(_objectSpread2$1({}, options), {}, {
1216
- onOpen: event => {
1217
- var _options$onOpen;
1218
- logger.log('INFO', 'WebSocket 连接成功', {
1219
- url,
1220
- readyState: getReadyStateText(1),
1221
- // 连接成功时 readyState 为 1
1222
- eventCode: event === null || event === void 0 ? void 0 : event.code
1223
- });
1224
- setIsConnected(true);
1225
- (_options$onOpen = options.onOpen) === null || _options$onOpen === void 0 || _options$onOpen.call(options, event);
1226
- },
1227
- onError: error => {
1228
- var _options$onError;
1229
- logger.log('ERROR', 'WebSocket 连接错误', {
1230
- url,
1231
- error: error.message,
1232
- errorType: error.type,
1233
- readyState: getReadyStateText(3) // 错误时 readyState 为 3
1234
- });
1235
- setIsConnected(false);
1236
- (_options$onError = options.onError) === null || _options$onError === void 0 || _options$onError.call(options, error);
1237
- },
1238
- onClose: event => {
1239
- var _options$onClose;
1240
- logger.log('WARN', 'WebSocket 连接断开', {
1241
- url,
1242
- code: event.code,
1243
- reason: event.reason,
1244
- wasClean: event.wasClean,
1245
- readyState: getReadyStateText(3) // 关闭时 readyState 为 3
1246
- });
1247
- setIsConnected(false);
1248
- (_options$onClose = options.onClose) === null || _options$onClose === void 0 || _options$onClose.call(options, event);
1249
- },
1250
- onReconnect: count => {
1251
- var _options$onReconnect;
1252
- logger.log('INFO', '尝试重新连接', {
1253
- url,
1254
- attempt: count,
1255
- maxAttempts: options.reconnectLimit || 10
1256
- });
1257
- (_options$onReconnect = options.onReconnect) === null || _options$onReconnect === void 0 || _options$onReconnect.call(options, count);
1258
- }
1259
- }), [url, options, logger]);
1138
+ const useSystemOperations = function () {
1139
+ let {
1140
+ onPowerOff,
1141
+ onRestart,
1142
+ confirmTitle = "Confirm",
1143
+ cancelText = "No",
1144
+ okText = "Yes",
1145
+ run
1146
+ } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1260
1147
  const {
1261
- latestMessage,
1262
- readyState,
1263
- sendMessage,
1264
- connect,
1265
- disconnect
1266
- } = useWebSocket(url, websocketOptions);
1267
- const heartbeatOptions = useMemo(() => heartbeatConfig === true ? {} : heartbeatConfig, [heartbeatConfig]);
1268
- const heartbeatEnabled = useMemo(() => heartbeatConfig !== false, [heartbeatConfig]);
1269
- const heartbeat = useHeartbeat(sendMessage, readyState, heartbeatOptions, logger, heartbeatEnabled);
1148
+ modal: AntdModal
1149
+ } = App.useApp();
1150
+ const doAction = useCallback(action => {
1151
+ try {
1152
+ AntdModal.confirm({
1153
+ icon: /*#__PURE__*/jsx(ExclamationCircleFilled, {}),
1154
+ title: "".concat(confirmTitle, " ").concat(action, "?"),
1155
+ cancelText,
1156
+ okText,
1157
+ onOk: () => {
1158
+ if (action === 'poweroff' && onPowerOff) {
1159
+ onPowerOff();
1160
+ } else if (action === 'restart' && onRestart) {
1161
+ onRestart();
1162
+ }
1270
1163
 
1271
- // 在全局暴露日志方法
1272
- useEffect(() => {
1273
- if (typeof window !== 'undefined' && enableLog) {
1274
- // 生成唯一的键名:基于URL进行base64编码
1275
- const key = "exportWebSocketLogs_".concat(btoa(url));
1276
- // 在window对象上添加方法
1277
- window[key] = () => logger.exportLogs();
1164
+ // Call the run callback after successful operation
1165
+ if (typeof run === 'function') {
1166
+ run();
1167
+ }
1168
+ }
1169
+ });
1170
+ } catch (error) {
1171
+ console.error("".concat(action.toUpperCase(), " ERROR: "), error);
1278
1172
  }
1279
- return () => {
1280
- if (typeof window !== 'undefined' && enableLog) {
1281
- const key = "exportWebSocketLogs_".concat(btoa(url));
1282
- delete window[key];
1283
- }
1284
- };
1285
- }, [url, enableLog, logger]);
1286
- const loggerMethods = useMemo(() => ({
1287
- log: (level, message, data) => logger.log(level, message, data),
1288
- exportLogs: () => logger.exportLogs(),
1289
- getLogStats: () => logger.getLogStats(),
1290
- clearLogs: () => logger.clearLogs()
1291
- }), [logger]);
1173
+ }, [AntdModal, confirmTitle, cancelText, okText, onPowerOff, onRestart]);
1174
+ const getMenuItems = useCallback(() => [{
1175
+ key: "poweroff",
1176
+ label: "Power Off"
1177
+ }, {
1178
+ key: "restart",
1179
+ label: "Restart"
1180
+ }], []);
1181
+ const handleMenuClick = useCallback(_ref => {
1182
+ let {
1183
+ key
1184
+ } = _ref;
1185
+ doAction(key);
1186
+ }, [doAction]);
1292
1187
  return {
1293
- latestMessage,
1294
- readyState,
1295
- sendMessage,
1296
- connect,
1297
- disconnect,
1298
- isConnected,
1299
- readyStateText: getReadyStateText(readyState),
1300
- // 心跳功能
1301
- heartbeat,
1302
- // 日志功能
1303
- logger: enableLog ? loggerMethods : null
1188
+ menuItems: getMenuItems(),
1189
+ handleMenuClick
1304
1190
  };
1305
1191
  };
1306
- var useWebSocketWithFeatures$1 = useWebSocketWithFeatures;
1192
+ var useSystemOperations$1 = useSystemOperations;
1307
1193
 
1308
- // 在控制台中直接查看所有可用的导出方法 Object.keys(window).filter(key => key.startsWith('exportWebSocketLogs_'))
1309
- // 导出特定连接的日志 window['exportWebSocketLogs_d3M6Ly8xOTIuMTY4LjEyMy4yMDQvd3MvZHZyL3ZpZGVvX3N0YXR1c19jaGFuZ2U=']();
1194
+ const usePageReload = () => {
1195
+ const [reloading, setReloading] = useState(false);
1196
+ const startReload = useCallback(function () {
1197
+ let delay = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 800;
1198
+ if (reloading) return; // 防止重复触发
1310
1199
 
1311
- /**
1312
- * 国际化调试工具
1313
- * 在浏览器控制台运行 window.debugI18n() 查看当前国际化状态
1314
- */
1200
+ // 显示遮罩
1201
+ setReloading(true);
1315
1202
 
1316
- const debugI18n = () => {
1317
- var _window$g_initialProp, _window$g_initialProp2, _window$g_initialProp3, _window$g_initialProp4, _window$g_initialProp5, _window$g_initialProp6;
1318
- if (typeof window === 'undefined') {
1319
- console.log('[i18n debug] Not in browser environment');
1320
- return;
1321
- }
1322
- console.group('🌍 I18n Debug Information');
1203
+ // 设置背景色
1204
+ document.body.style.backgroundColor = '#282828';
1205
+ document.body.style.transition = 'background-color 0.1s';
1206
+ document.body.offsetHeight;
1323
1207
 
1324
- // 1. 当前语言环境
1325
- console.log('📍 Current Locale Sources:');
1326
- console.log(' - window.g_initialProps?.locale:', (_window$g_initialProp = window.g_initialProps) === null || _window$g_initialProp === void 0 ? void 0 : _window$g_initialProp.locale);
1327
- console.log(' - window.g_initialProps?.___g_initialPropsFromServer?.locale:', (_window$g_initialProp2 = window.g_initialProps) === null || _window$g_initialProp2 === void 0 || (_window$g_initialProp2 = _window$g_initialProp2.___g_initialPropsFromServer) === null || _window$g_initialProp2 === void 0 ? void 0 : _window$g_initialProp2.locale);
1328
- try {
1329
- console.log(' - localStorage.umi-locale:', localStorage.getItem('umi-locale'));
1330
- } catch (e) {
1331
- console.log(' - localStorage: not available');
1332
- }
1333
- console.log(' - window.__COMPONENT_LOCALE__:', window.__COMPONENT_LOCALE__);
1208
+ // 延迟刷新
1209
+ const timer = setTimeout(() => {
1210
+ window.location.reload();
1211
+ }, delay);
1334
1212
 
1335
- // 2. 语言包
1336
- console.log('\n📦 Available Message Sources:');
1337
- console.log(' - window.__COMPONENT_I18N_MESSAGES__:', window.__COMPONENT_I18N_MESSAGES__);
1338
- console.log(' - window.__PROJECT_I18N_MESSAGES__:', window.__PROJECT_I18N_MESSAGES__);
1339
- console.log(' - window.g_initialProps?.messages:', (_window$g_initialProp3 = window.g_initialProps) === null || _window$g_initialProp3 === void 0 ? void 0 : _window$g_initialProp3.messages);
1213
+ // 清理函数(防止内存泄漏)
1214
+ return () => clearTimeout(timer);
1215
+ }, [reloading]);
1216
+ const ReloadOverlay = () => {
1217
+ if (!reloading) return null;
1218
+ return /*#__PURE__*/jsxs("div", {
1219
+ style: {
1220
+ position: 'fixed',
1221
+ top: 0,
1222
+ left: 0,
1223
+ right: 0,
1224
+ bottom: 0,
1225
+ backgroundColor: '#282828',
1226
+ display: 'flex',
1227
+ flexDirection: 'column',
1228
+ alignItems: 'center',
1229
+ justifyContent: 'center',
1230
+ zIndex: 999999,
1231
+ color: 'white'
1232
+ },
1233
+ children: [/*#__PURE__*/jsx(Spin, {
1234
+ size: "large"
1235
+ }), /*#__PURE__*/jsx("div", {
1236
+ style: {
1237
+ marginTop: 20,
1238
+ fontSize: 16
1239
+ },
1240
+ children: "Loading preset configuration..."
1241
+ }), /*#__PURE__*/jsx("div", {
1242
+ style: {
1243
+ marginTop: 10,
1244
+ fontSize: 12,
1245
+ color: '#aaa'
1246
+ },
1247
+ children: "Page will refresh shortly"
1248
+ })]
1249
+ });
1250
+ };
1251
+ return {
1252
+ startReload,
1253
+ ReloadOverlay
1254
+ };
1255
+ };
1256
+ var usePageReload$1 = usePageReload;
1340
1257
 
1341
- // 3. 合并后的语言包
1342
- const componentMessages = window.__COMPONENT_I18N_MESSAGES__ || {};
1343
- const projectMessages = window.__PROJECT_I18N_MESSAGES__ || {};
1344
- const umiMessages = ((_window$g_initialProp4 = window.g_initialProps) === null || _window$g_initialProp4 === void 0 ? void 0 : _window$g_initialProp4.messages) || {};
1345
- const allLocales = new Set([...Object.keys(componentMessages), ...Object.keys(projectMessages), ...Object.keys(umiMessages)]);
1346
- console.log('\n🔧 Merged Locales:', Array.from(allLocales));
1347
- allLocales.forEach(locale => {
1348
- const merged = _objectSpread2$1(_objectSpread2$1(_objectSpread2$1({}, umiMessages[locale] || {}), projectMessages[locale] || {}), componentMessages[locale] || {});
1349
- console.log("\n \uD83D\uDCC4 ".concat(locale, " (").concat(Object.keys(merged).length, " keys):"), merged);
1350
- });
1258
+ const getReadyStateText = state => {
1259
+ const states = {
1260
+ 0: 'CONNECTING',
1261
+ 1: 'OPEN',
1262
+ 2: 'CLOSING',
1263
+ 3: 'CLOSED'
1264
+ };
1265
+ return states[state] || "UNKNOWN(".concat(state, ")");
1266
+ };
1351
1267
 
1352
- // 4. 测试几个关键的 key
1353
- console.log('\n🧪 Test Translation:');
1354
- const testKeys = ['networkSettings.title', 'button.ok', 'button.cancel', 'button.apply', 'button.close'];
1355
- const currentLocale = ((_window$g_initialProp5 = window.g_initialProps) === null || _window$g_initialProp5 === void 0 ? void 0 : _window$g_initialProp5.locale) || ((_window$g_initialProp6 = window.g_initialProps) === null || _window$g_initialProp6 === void 0 || (_window$g_initialProp6 = _window$g_initialProp6.___g_initialPropsFromServer) === null || _window$g_initialProp6 === void 0 ? void 0 : _window$g_initialProp6.locale) || localStorage.getItem('umi-locale') || 'zh-CN';
1356
- const allMessages = {};
1357
- allLocales.forEach(locale => {
1358
- allMessages[locale] = _objectSpread2$1(_objectSpread2$1(_objectSpread2$1({}, umiMessages[locale] || {}), projectMessages[locale] || {}), componentMessages[locale] || {});
1359
- });
1360
- testKeys.forEach(key => {
1361
- var _allMessages$currentL, _allMessages$enUS;
1362
- const translation = ((_allMessages$currentL = allMessages[currentLocale]) === null || _allMessages$currentL === void 0 ? void 0 : _allMessages$currentL[key]) || ((_allMessages$enUS = allMessages['en-US']) === null || _allMessages$enUS === void 0 ? void 0 : _allMessages$enUS[key]) || '❌ NOT FOUND';
1363
- console.log(" - ".concat(key, ": ").concat(translation));
1364
- });
1365
- console.groupEnd();
1268
+ // 创建空日志器
1269
+ const createDummyLogger = () => {
1270
+ const dummyFn = () => {};
1271
+ return {
1272
+ log: dummyFn,
1273
+ exportLogs: dummyFn,
1274
+ getLogStats: () => ({
1275
+ total: 0,
1276
+ errors: 0,
1277
+ warnings: 0,
1278
+ info: 0,
1279
+ lastTimestamp: null
1280
+ }),
1281
+ clearLogs: dummyFn,
1282
+ logs: []
1283
+ };
1366
1284
  };
1367
1285
 
1368
- // 自动在开发环境注册到 window
1369
- if (process.env.NODE_ENV === 'development' && typeof window !== 'undefined') {
1370
- window.debugI18n = debugI18n;
1371
- console.log('💡 I18n debug tool available. Run window.debugI18n() in console to check i18n status.');
1372
- }
1286
+ // 创建通用的日志器
1287
+ const createGlobalLogger = function () {
1288
+ let webSocketUrl = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
1289
+ const logs = [];
1290
+ const maxLogs = 1000;
1373
1291
 
1374
- /**
1375
- * seeder-st2110-components 组件库国际化 Hook
1376
- *
1377
- * 设计目标:
1378
- * 1. 不依赖特定的国际化库(react-intl, i18next 等)
1379
- * 2. 可以与主项目(Umi)的国际化集成
1380
- * 3. 支持简单的变量替换
1381
- * 4. 降级处理:如果没有翻译,显示 key 本身
1382
- */
1292
+ // 判断是否为开发环境
1293
+ const isDevelopment = process.env.NODE_ENV === 'development';
1294
+ const log = function (level, message) {
1295
+ let data = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
1296
+ const timestamp = new Date().toISOString();
1297
+ const logEntry = {
1298
+ timestamp,
1299
+ level,
1300
+ message,
1301
+ data,
1302
+ timestampDisplay: new Date().toLocaleString('zh-CN', {
1303
+ year: 'numeric',
1304
+ month: '2-digit',
1305
+ day: '2-digit',
1306
+ hour: '2-digit',
1307
+ minute: '2-digit',
1308
+ second: '2-digit',
1309
+ hour12: false
1310
+ })
1311
+ };
1312
+ logs.push(logEntry);
1313
+ if (logs.length > maxLogs) logs.shift();
1383
1314
 
1384
- /**
1385
- * 获取当前语言环境
1386
- * 优先从 Umi 全局变量读取,其次使用默认值
1387
- */
1388
- const getLocale = () => {
1389
- // 尝试从 Umi 获取语言设置
1390
- if (typeof window !== 'undefined') {
1391
- var _window$g_initialProp, _window$g_initialProp2;
1392
- // 方式 1:Umi 的 g_initialProps
1393
- if ((_window$g_initialProp = window.g_initialProps) !== null && _window$g_initialProp !== void 0 && _window$g_initialProp.locale) {
1394
- return window.g_initialProps.locale;
1315
+ // 只在开发环境下输出到控制台
1316
+ if (isDevelopment) {
1317
+ const consoleMessage = "[".concat(logEntry.timestampDisplay, "] [WebSocket] [").concat(level, "] ").concat(message);
1318
+ const consoleArgs = [consoleMessage];
1319
+ if (Object.keys(data).length > 0) consoleArgs.push(data);
1320
+ switch (level) {
1321
+ case 'ERROR':
1322
+ console.error(...consoleArgs);
1323
+ break;
1324
+ case 'WARN':
1325
+ console.warn(...consoleArgs);
1326
+ break;
1327
+ case 'INFO':
1328
+ console.info(...consoleArgs);
1329
+ break;
1330
+ default:
1331
+ console.log(...consoleArgs);
1332
+ }
1395
1333
  }
1396
- // 方式 2:Umi 的全局语言标记
1397
- if ((_window$g_initialProp2 = window.g_initialProps) !== null && _window$g_initialProp2 !== void 0 && (_window$g_initialProp2 = _window$g_initialProp2.___g_initialPropsFromServer) !== null && _window$g_initialProp2 !== void 0 && _window$g_initialProp2.locale) {
1398
- return window.g_initialProps.___g_initialPropsFromServer.locale;
1334
+ return logEntry;
1335
+ };
1336
+ const exportLogs = function () {
1337
+ let filenamePrefix = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'websocket';
1338
+ if (logs.length === 0) {
1339
+ log('WARN', '没有日志可导出', {
1340
+ filenamePrefix
1341
+ });
1342
+ return;
1399
1343
  }
1400
- // 方式 3:从 localStorage 读取(Umi 默认行为)
1344
+ const logText = logs.map(entry => "[".concat(entry.timestampDisplay, "] [").concat(entry.level, "] ").concat(entry.message, "\n\u6570\u636E: ").concat(JSON.stringify(entry.data, null, 2))).join('\n' + '='.repeat(50) + '\n');
1345
+ const blob = new Blob([logText], {
1346
+ type: 'text/plain;charset=utf-8'
1347
+ });
1348
+ const blobUrl = URL.createObjectURL(blob);
1349
+ let hostname = 'unknown';
1350
+ let pathname = 'unknown';
1401
1351
  try {
1402
- const storedLocale = localStorage.getItem('umi-locale');
1403
- if (storedLocale) {
1404
- return storedLocale;
1352
+ if (webSocketUrl) {
1353
+ const urlObj = new URL(webSocketUrl.replace(/^ws/, 'http'));
1354
+ hostname = urlObj.hostname.replace(/\./g, '-');
1355
+ pathname = urlObj.pathname.replace(/\//g, '-').replace(/^-|-$/g, '') || 'root';
1405
1356
  }
1406
- } catch (e) {
1407
- // localStorage 不可用时忽略
1408
- }
1409
- // 方式 4:组件库自定义语言标记
1410
- if (window.__COMPONENT_LOCALE__) {
1411
- return window.__COMPONENT_LOCALE__;
1357
+ } catch (error) {
1358
+ console.warn('无法解析WebSocket URL:', error);
1412
1359
  }
1413
- }
1414
- // 默认语言
1415
- return 'zh-CN';
1360
+ const readableTime = new Date().toLocaleString('zh-CN', {
1361
+ year: 'numeric',
1362
+ month: '2-digit',
1363
+ day: '2-digit',
1364
+ hour: '2-digit',
1365
+ minute: '2-digit',
1366
+ second: '2-digit',
1367
+ hour12: false
1368
+ }).replace(/[\/:\s]/g, '-');
1369
+ const filename = "".concat(filenamePrefix, "-").concat(hostname, "-").concat(pathname, "-").concat(readableTime, ".log");
1370
+ const a = document.createElement('a');
1371
+ a.href = blobUrl;
1372
+ a.download = filename;
1373
+ document.body.appendChild(a);
1374
+ a.click();
1375
+ document.body.removeChild(a);
1376
+ URL.revokeObjectURL(blobUrl);
1377
+ log('INFO', '日志文件已导出', {
1378
+ count: logs.length,
1379
+ filename
1380
+ });
1381
+ };
1382
+ const getLogStats = () => {
1383
+ var _logs;
1384
+ return {
1385
+ total: logs.length,
1386
+ errors: logs.filter(l => l.level === 'ERROR').length,
1387
+ warnings: logs.filter(l => l.level === 'WARN').length,
1388
+ info: logs.filter(l => l.level === 'INFO').length,
1389
+ lastTimestamp: (_logs = logs[logs.length - 1]) === null || _logs === void 0 ? void 0 : _logs.timestampDisplay
1390
+ };
1391
+ };
1392
+ const clearLogs = () => {
1393
+ logs.length = 0;
1394
+ log('INFO', '日志已清空');
1395
+ };
1396
+ return {
1397
+ log,
1398
+ exportLogs,
1399
+ getLogStats,
1400
+ clearLogs,
1401
+ logs
1402
+ };
1416
1403
  };
1417
1404
 
1418
- /**
1419
- * 获取语言包
1420
- * 优先从主项目读取,其次使用组件库自带的语言包
1421
- */
1422
- const getMessages = () => {
1423
- if (typeof window !== 'undefined') {
1424
- var _window$g_initialProp3;
1425
- // 尝试从多个来源读取语言包
1426
- const messages = {};
1427
-
1428
- // 1. 组件库注册的语言包
1429
- const componentMessages = window.__COMPONENT_I18N_MESSAGES__ || {};
1430
-
1431
- // 2. 主项目的语言包
1432
- const projectMessages = window.__PROJECT_I18N_MESSAGES__ || {};
1433
-
1434
- // 3. Umi 的语言包
1435
- const umiMessages = ((_window$g_initialProp3 = window.g_initialProps) === null || _window$g_initialProp3 === void 0 ? void 0 : _window$g_initialProp3.messages) || {};
1436
-
1437
- // 合并语言包(组件库的优先级最高,其次是主项目,最后是 Umi)
1438
- const allLocales = new Set([...Object.keys(componentMessages), ...Object.keys(projectMessages), ...Object.keys(umiMessages)]);
1439
- allLocales.forEach(locale => {
1440
- messages[locale] = _objectSpread2$1(_objectSpread2$1(_objectSpread2$1({}, umiMessages[locale] || {}), projectMessages[locale] || {}), componentMessages[locale] || {});
1405
+ // 心跳管理器
1406
+ const useHeartbeat = function (sendMessage, readyState, config, logger) {
1407
+ let enabled = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
1408
+ const heartbeatTimerRef = useRef(null);
1409
+ const lastHeartbeatTimeRef = useRef(Date.now());
1410
+ const isMountedRef = useRef(true);
1411
+ const configRef = useRef({
1412
+ interval: (config === null || config === void 0 ? void 0 : config.interval) || 20000,
1413
+ message: (config === null || config === void 0 ? void 0 : config.message) || {
1414
+ type: 'ping'
1415
+ }
1416
+ });
1417
+ const stateRef = useRef({
1418
+ sendMessage,
1419
+ readyState,
1420
+ enabled
1421
+ });
1422
+ useEffect(() => {
1423
+ configRef.current = {
1424
+ interval: (config === null || config === void 0 ? void 0 : config.interval) || 20000,
1425
+ message: (config === null || config === void 0 ? void 0 : config.message) || {
1426
+ type: 'ping'
1427
+ }
1428
+ };
1429
+ }, [config === null || config === void 0 ? void 0 : config.interval, config === null || config === void 0 ? void 0 : config.message]);
1430
+ useEffect(() => {
1431
+ stateRef.current = {
1432
+ sendMessage,
1433
+ readyState,
1434
+ enabled
1435
+ };
1436
+ }, [sendMessage, readyState, enabled]);
1437
+ useEffect(() => {
1438
+ return () => {
1439
+ isMountedRef.current = false;
1440
+ if (heartbeatTimerRef.current) {
1441
+ clearInterval(heartbeatTimerRef.current);
1442
+ }
1443
+ };
1444
+ }, []);
1445
+ const stopHeartbeat = useCallback(() => {
1446
+ if (heartbeatTimerRef.current) {
1447
+ clearInterval(heartbeatTimerRef.current);
1448
+ heartbeatTimerRef.current = null;
1449
+ if (stateRef.current.enabled) {
1450
+ logger.log('INFO', '停止心跳机制');
1451
+ }
1452
+ }
1453
+ }, [logger]);
1454
+ const startHeartbeat = useCallback(() => {
1455
+ stopHeartbeat();
1456
+ const currentState = stateRef.current;
1457
+ const currentConfig = configRef.current;
1458
+ if (!currentState.enabled) return;
1459
+ logger.log('INFO', '启动心跳机制', {
1460
+ interval: currentConfig.interval
1441
1461
  });
1442
- return messages;
1443
- }
1444
- return {};
1445
- };
1462
+ heartbeatTimerRef.current = setInterval(() => {
1463
+ // 检查组件是否挂载
1464
+ if (!isMountedRef.current || !heartbeatTimerRef.current) {
1465
+ return;
1466
+ }
1446
1467
 
1447
- /**
1448
- * 格式化消息
1449
- * @param {string} id - 国际化 key
1450
- * @param {object} values - 变量替换值
1451
- * @param {string} locale - 语言环境
1452
- * @param {object} messages - 语言包
1453
- */
1454
- const formatMessage = function (_ref) {
1455
- var _messages$locale, _messages$enUS;
1456
- let {
1457
- id
1458
- } = _ref;
1459
- let values = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
1460
- let locale = arguments.length > 2 ? arguments[2] : undefined;
1461
- let messages = arguments.length > 3 ? arguments[3] : undefined;
1462
- const message = ((_messages$locale = messages[locale]) === null || _messages$locale === void 0 ? void 0 : _messages$locale[id]) || ((_messages$enUS = messages['en-US']) === null || _messages$enUS === void 0 ? void 0 : _messages$enUS[id]) || id;
1468
+ // 目的: 总是获取最新的值
1469
+ const latestState = stateRef.current;
1470
+ const latestConfig = configRef.current;
1471
+ if (latestState.sendMessage && latestState.readyState === 1) {
1472
+ try {
1473
+ latestState.sendMessage(JSON.stringify(latestConfig.message));
1474
+ lastHeartbeatTimeRef.current = Date.now();
1475
+ logger.log('DEBUG', '发送心跳包', {
1476
+ timeSinceLastHeartbeat: Date.now() - lastHeartbeatTimeRef.current,
1477
+ readyState: getReadyStateText(latestState.readyState)
1478
+ });
1479
+ } catch (error) {
1480
+ logger.log('ERROR', '发送心跳失败', {
1481
+ error: error.message,
1482
+ readyState: getReadyStateText(latestState.readyState)
1483
+ });
1484
+ }
1485
+ }
1486
+ }, currentConfig.interval);
1487
+ }, [stopHeartbeat, logger]);
1463
1488
 
1464
- // 支持变量替换 {min}, {max}, {value} 等
1465
- return message.replace(/\{(\w+)\}/g, (match, key) => {
1466
- return values[key] !== undefined ? values[key] : match;
1467
- });
1489
+ // 自动管理心跳
1490
+ useEffect(() => {
1491
+ const currentState = stateRef.current;
1492
+ if (currentState.enabled) {
1493
+ if (currentState.readyState === 1) {
1494
+ startHeartbeat();
1495
+ } else {
1496
+ stopHeartbeat();
1497
+ }
1498
+ } else {
1499
+ stopHeartbeat();
1500
+ }
1501
+ return () => {
1502
+ if (heartbeatTimerRef.current) {
1503
+ clearInterval(heartbeatTimerRef.current);
1504
+ heartbeatTimerRef.current = null;
1505
+ }
1506
+ };
1507
+ }, [readyState, enabled, startHeartbeat, stopHeartbeat]);
1508
+ const heartbeatImpl = useMemo(() => {
1509
+ if (!enabled) {
1510
+ return {
1511
+ startHeartbeat: () => {},
1512
+ stopHeartbeat: () => {},
1513
+ getLastHeartbeatTime: () => null,
1514
+ isEnabled: false
1515
+ };
1516
+ }
1517
+ return {
1518
+ startHeartbeat,
1519
+ stopHeartbeat,
1520
+ getLastHeartbeatTime: () => lastHeartbeatTimeRef.current,
1521
+ isEnabled: true
1522
+ };
1523
+ }, [enabled, startHeartbeat, stopHeartbeat]);
1524
+ return heartbeatImpl;
1468
1525
  };
1526
+ const useWebSocketWithFeatures = config => {
1527
+ const {
1528
+ url,
1529
+ options = {},
1530
+ heartbeat: heartbeatConfig = false,
1531
+ enableLog = true
1532
+ } = config;
1533
+ const [isConnected, setIsConnected] = useState(false);
1469
1534
 
1470
- /**
1471
- * 国际化 Hook
1472
- *
1473
- * @returns {{
1474
- * locale: string,
1475
- * formatMessage: function,
1476
- * messages: object
1477
- * }}
1478
- *
1479
- * @example
1480
- * const intl = useIntl();
1481
- * const title = intl.formatMessage({ id: 'networkSettings.title' });
1482
- * const message = intl.formatMessage({ id: 'validation.minLength' }, { min: 3 });
1483
- */
1484
- const useIntl = () => {
1485
- const locale = getLocale();
1486
- const messages = getMessages();
1487
- const formatMessageFn = function (_ref2) {
1488
- let {
1489
- id
1490
- } = _ref2;
1491
- let values = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
1492
- return formatMessage({
1493
- id
1494
- }, values, locale, messages);
1495
- };
1535
+ // 创建 logger 实例
1536
+ const logger = useMemo(() => {
1537
+ if (enableLog) {
1538
+ return createGlobalLogger(url);
1539
+ }
1540
+ return createDummyLogger();
1541
+ }, [url, enableLog]);
1542
+ const websocketOptions = useMemo(() => _objectSpread2$1(_objectSpread2$1({}, options), {}, {
1543
+ onOpen: event => {
1544
+ var _options$onOpen;
1545
+ logger.log('INFO', 'WebSocket 连接成功', {
1546
+ url,
1547
+ readyState: getReadyStateText(1),
1548
+ // 连接成功时 readyState 为 1
1549
+ eventCode: event === null || event === void 0 ? void 0 : event.code
1550
+ });
1551
+ setIsConnected(true);
1552
+ (_options$onOpen = options.onOpen) === null || _options$onOpen === void 0 || _options$onOpen.call(options, event);
1553
+ },
1554
+ onError: error => {
1555
+ var _options$onError;
1556
+ logger.log('ERROR', 'WebSocket 连接错误', {
1557
+ url,
1558
+ error: error.message,
1559
+ errorType: error.type,
1560
+ readyState: getReadyStateText(3) // 错误时 readyState 为 3
1561
+ });
1562
+ setIsConnected(false);
1563
+ (_options$onError = options.onError) === null || _options$onError === void 0 || _options$onError.call(options, error);
1564
+ },
1565
+ onClose: event => {
1566
+ var _options$onClose;
1567
+ logger.log('WARN', 'WebSocket 连接断开', {
1568
+ url,
1569
+ code: event.code,
1570
+ reason: event.reason,
1571
+ wasClean: event.wasClean,
1572
+ readyState: getReadyStateText(3) // 关闭时 readyState 为 3
1573
+ });
1574
+ setIsConnected(false);
1575
+ (_options$onClose = options.onClose) === null || _options$onClose === void 0 || _options$onClose.call(options, event);
1576
+ },
1577
+ onReconnect: count => {
1578
+ var _options$onReconnect;
1579
+ logger.log('INFO', '尝试重新连接', {
1580
+ url,
1581
+ attempt: count,
1582
+ maxAttempts: options.reconnectLimit || 10
1583
+ });
1584
+ (_options$onReconnect = options.onReconnect) === null || _options$onReconnect === void 0 || _options$onReconnect.call(options, count);
1585
+ }
1586
+ }), [url, options, logger]);
1587
+ const {
1588
+ latestMessage,
1589
+ readyState,
1590
+ sendMessage,
1591
+ connect,
1592
+ disconnect
1593
+ } = useWebSocket(url, websocketOptions);
1594
+ const heartbeatOptions = useMemo(() => heartbeatConfig === true ? {} : heartbeatConfig, [heartbeatConfig]);
1595
+ const heartbeatEnabled = useMemo(() => heartbeatConfig !== false, [heartbeatConfig]);
1596
+ const heartbeat = useHeartbeat(sendMessage, readyState, heartbeatOptions, logger, heartbeatEnabled);
1597
+
1598
+ // 在全局暴露日志方法
1599
+ useEffect(() => {
1600
+ if (typeof window !== 'undefined' && enableLog) {
1601
+ // 生成唯一的键名:基于URL进行base64编码
1602
+ const key = "exportWebSocketLogs_".concat(btoa(url));
1603
+ // 在window对象上添加方法
1604
+ window[key] = () => logger.exportLogs();
1605
+ }
1606
+ return () => {
1607
+ if (typeof window !== 'undefined' && enableLog) {
1608
+ const key = "exportWebSocketLogs_".concat(btoa(url));
1609
+ delete window[key];
1610
+ }
1611
+ };
1612
+ }, [url, enableLog, logger]);
1613
+ const loggerMethods = useMemo(() => ({
1614
+ log: (level, message, data) => logger.log(level, message, data),
1615
+ exportLogs: () => logger.exportLogs(),
1616
+ getLogStats: () => logger.getLogStats(),
1617
+ clearLogs: () => logger.clearLogs()
1618
+ }), [logger]);
1496
1619
  return {
1497
- locale,
1498
- formatMessage: formatMessageFn,
1499
- messages
1620
+ latestMessage,
1621
+ readyState,
1622
+ sendMessage,
1623
+ connect,
1624
+ disconnect,
1625
+ isConnected,
1626
+ readyStateText: getReadyStateText(readyState),
1627
+ // 心跳功能
1628
+ heartbeat,
1629
+ // 日志功能
1630
+ logger: enableLog ? loggerMethods : null
1500
1631
  };
1501
1632
  };
1633
+ var useWebSocketWithFeatures$1 = useWebSocketWithFeatures;
1502
1634
 
1503
- /**
1504
- * 设置语言环境(可选)
1505
- * 如果需要在运行时切换语言,可以调用此函数
1506
- *
1507
- * @param {string} locale - 语言代码,如 'zh-CN' 或 'en-US'
1508
- */
1509
- const setLocale = locale => {
1510
- if (typeof window !== 'undefined') {
1511
- window.__COMPONENT_LOCALE__ = locale;
1512
- }
1513
- };
1635
+ // 在控制台中直接查看所有可用的导出方法 Object.keys(window).filter(key => key.startsWith('exportWebSocketLogs_'))
1636
+ // 导出特定连接的日志 window['exportWebSocketLogs_d3M6Ly8xOTIuMTY4LjEyMy4yMDQvd3MvZHZyL3ZpZGVvX3N0YXR1c19jaGFuZ2U=']();
1514
1637
 
1515
- /**
1516
- * 注册语言包(可选)
1517
- * 如果需要在运行时注册语言包,可以调用此函数
1518
- *
1519
- * @param {string} locale - 语言代码
1520
- * @param {object} messages - 语言包对象
1521
- */
1522
- const addMessages = (locale, messages) => {
1523
- if (typeof window !== 'undefined') {
1524
- if (!window.__COMPONENT_I18N_MESSAGES__) {
1525
- window.__COMPONENT_I18N_MESSAGES__ = {};
1526
- }
1527
- window.__COMPONENT_I18N_MESSAGES__[locale] = messages;
1528
- }
1638
+ // seeder-st2110-components 组件库中文语言包
1639
+ var zhCN = {
1640
+ // 通用按钮
1641
+ 'button.ok': '确定',
1642
+ 'button.cancel': '取消',
1643
+ 'button.save': '保存',
1644
+ 'button.delete': '删除',
1645
+ 'button.edit': '编辑',
1646
+ 'button.add': '添加',
1647
+ 'button.confirm': '确认',
1648
+ 'button.close': '关闭',
1649
+ 'button.apply': '应用',
1650
+ 'button.submit': '提交',
1651
+ 'button.next': '下一步',
1652
+ 'button.previous': '上一步',
1653
+ 'button.refresh': '刷新',
1654
+ 'button.retry': '重试',
1655
+ // 菜单
1656
+ 'menu.networkSettings': '网络设置',
1657
+ 'menu.ptp': 'PTP',
1658
+ 'menu.nmos': 'NMOS',
1659
+ 'menu.preset': '预设',
1660
+ 'menu.license': '许可证',
1661
+ 'menu.maintenance': '维护',
1662
+ 'menu.system': '系统',
1663
+ // Network Settings Modal
1664
+ 'networkSettings.title': '网络设置',
1665
+ 'networkSettings.ipAddress': 'IP 地址',
1666
+ 'networkSettings.subnetMask': '子网掩码',
1667
+ 'networkSettings.restartRequired': '配置已修改。是否重启以应用更改?',
1668
+ 'networkSettings.restartNow': '立即重启',
1669
+ 'networkSettings.restartLater': '稍后重启',
1670
+ 'networkSettings.saveSuccess': '保存成功',
1671
+ // PTP Modal
1672
+ 'ptp.title': 'PTP 设置',
1673
+ 'ptp.domainNumber': '域编号',
1674
+ 'ptp.priority1': '优先级 1',
1675
+ 'ptp.priority2': '优先级 2',
1676
+ 'ptp.clockClass': '时钟等级',
1677
+ 'ptp.clockClass.atomic': '原子钟 ({value})',
1678
+ 'ptp.clockClass.gps': 'GPS ({value})',
1679
+ 'ptp.clockClass.slaveOnly': '仅从时钟 ({value})',
1680
+ 'ptp.clockAccuracy': '时钟精度',
1681
+ 'ptp.offsetScaledLogVariance': '时钟稳定性',
1682
+ 'ptp.portIdentity': '端口标识',
1683
+ 'ptp.grandmasterIdentity': '主时钟标识',
1684
+ 'ptp.utcOffset': 'UTC 偏移量',
1685
+ 'ptp.connected': '已连接',
1686
+ 'ptp.disconnected': '未连接',
1687
+ 'ptp.locked': '已锁定',
1688
+ 'ptp.unlocked': '未锁定',
1689
+ 'ptp.saveSuccess': '保存成功',
1690
+ // NMOS Modal
1691
+ 'nmos.title': 'NMOS 设置',
1692
+ 'nmos.hostAddress': '主机地址',
1693
+ 'nmos.domain': '域',
1694
+ 'nmos.registrationPort': '注册端口',
1695
+ 'nmos.registryAddress': '注册地址',
1696
+ 'nmos.registryVersion': '注册版本',
1697
+ 'nmos.loggingLevel': '日志级别',
1698
+ 'nmos.placeholder.selectHostAddress': '选择主机地址',
1699
+ 'nmos.placeholder.selectVersion': '选择版本',
1700
+ 'nmos.saveSuccess': '保存成功',
1701
+ // Preset Modal
1702
+ 'preset.title': '预设管理',
1703
+ 'preset.name': '名称',
1704
+ 'preset.categories': '分类',
1705
+ 'preset.description': '描述',
1706
+ 'preset.placeholder.enterName': '请输入名称',
1707
+ 'preset.button.new': '新建预设',
1708
+ 'preset.button.delete': '删除',
1709
+ 'preset.button.load': '加载',
1710
+ 'preset.button.save': '保存',
1711
+ 'preset.button.edit': '编辑',
1712
+ 'preset.button.cancel': '取消',
1713
+ 'preset.empty.noData': '从列表中选择一个预设查看详情',
1714
+ 'preset.untitled': '未命名预设',
1715
+ 'preset.delete.title': '删除预设',
1716
+ 'preset.delete.confirmMessage': '确定要删除预设',
1717
+ 'preset.load.title': '加载预设',
1718
+ 'preset.load.confirmMessage': '确定要加载预设',
1719
+ 'preset.load.loading': '加载中...',
1720
+ 'preset.success': '成功',
1721
+ 'preset.validation.nameRequired': '名称是必填项',
1722
+ 'preset.validation.categoryRequired': '请选择分类',
1723
+ 'preset.error.noSelectionOrIdMissing': '未选择预设或预设 ID 缺失',
1724
+ // License/Auth Modal
1725
+ 'license.title': '注册许可证',
1726
+ 'license.machineCode': '机器码',
1727
+ 'license.key': '许可证密钥',
1728
+ 'license.placeholder.enterKey': '请输入许可证密钥',
1729
+ 'license.button.activate': '立即激活',
1730
+ 'license.button.reactivate': '重新激活',
1731
+ 'license.button.ignore': '忽略',
1732
+ 'license.status.unactivated': '未激活',
1733
+ 'license.status.activated': '已激活',
1734
+ 'license.status.expired': '已过期',
1735
+ 'license.status.invalid': '无效',
1736
+ 'license.status.expiresOn': '到期时间',
1737
+ 'license.validation.keyRequired': '许可证密钥不能为空',
1738
+ // 升级相关
1739
+ 'upgrade.menu.download': '下载配置文件',
1740
+ 'upgrade.menu.softwareUpdate': '软件升级',
1741
+ 'upgrade.status.idle': '空闲',
1742
+ 'upgrade.status.uploading': '上传中',
1743
+ 'upgrade.status.uploadComplete': '上传完成,开始升级...',
1744
+ 'upgrade.status.upgrading': '升级中...',
1745
+ 'upgrade.error.downloadNotConfigured': '下载功能未配置',
1746
+ 'upgrade.error.downloadFailed': '下载失败',
1747
+ 'upgrade.error.uploadFailed': '上传失败',
1748
+ 'upgrade.error.invalidFileFormat': '上传失败:文件格式无效',
1749
+ 'upgrade.error.upgradeFailed': '升级失败',
1750
+ // 系统操作
1751
+ 'system.poweroff': '关机',
1752
+ 'system.restart': '重启',
1753
+ 'system.action.powerOff': '关闭',
1754
+ 'system.action.restart': '重启',
1755
+ 'system.confirm.title': '确认操作',
1756
+ 'system.confirm.message': '确定要{action}系统吗?此操作无法撤销。',
1757
+ 'system.button.cancel': '取消',
1758
+ 'system.button.confirm': '确认',
1759
+ // 维护页面
1760
+ 'maintenance.title': '系统维护',
1761
+ 'maintenance.description': '设备正在维护中,请稍候...',
1762
+ 'maintenance.restart': '设备正在重启',
1763
+ 'maintenance.poweroff': '设备已关闭',
1764
+ 'maintenance.redirecting': '页面跳转中...',
1765
+ 'maintenance.backToHome': '返回首页',
1766
+ // 表单验证
1767
+ 'validation.required': '此项为必填项',
1768
+ 'validation.invalidIP': 'IP 地址格式不正确',
1769
+ 'validation.invalidPort': '端口号范围应为 1-65535',
1770
+ 'validation.invalidEmail': '邮箱格式不正确',
1771
+ 'validation.minLength': '最少需要 {min} 个字符',
1772
+ 'validation.maxLength': '最多只能有 {max} 个字符',
1773
+ 'validation.min': '最小值为 {min}',
1774
+ 'validation.max': '最大值为 {max}',
1775
+ 'validation.pattern': '格式不正确',
1776
+ 'validation.unique': '该值已存在',
1777
+ // 状态消息
1778
+ 'status.loading': '加载中...',
1779
+ 'status.saving': '保存中...',
1780
+ 'status.deleting': '删除中...',
1781
+ 'status.updating': '更新中...',
1782
+ 'status.submitting': '提交中...',
1783
+ 'status.success': '成功',
1784
+ 'status.failed': '失败',
1785
+ 'status.error': '错误',
1786
+ 'status.pending': '等待中',
1787
+ 'status.processing': '处理中',
1788
+ 'status.completed': '已完成',
1789
+ 'status.cancelled': '已取消',
1790
+ // 确认对话框
1791
+ 'confirm.title': '确认操作',
1792
+ 'confirm.message': '确定要执行此操作吗?',
1793
+ 'confirm.cancel': '取消',
1794
+ 'confirm.ok': '确定',
1795
+ // 空状态
1796
+ 'empty.noData': '暂无数据',
1797
+ 'empty.noResults': '暂无结果',
1798
+ 'empty.noItems': '暂无项目',
1799
+ // 通用标签
1800
+ 'label.name': '名称',
1801
+ 'label.description': '描述',
1802
+ 'label.status': '状态',
1803
+ 'label.type': '类型',
1804
+ 'label.action': '操作',
1805
+ 'label.actions': '操作',
1806
+ 'label.settings': '设置',
1807
+ 'label.configuration': '配置',
1808
+ 'label.information': '信息',
1809
+ 'label.version': '版本',
1810
+ 'label.time': '时间',
1811
+ 'label.date': '日期',
1812
+ 'label.enable': '启用',
1813
+ 'label.disable': '禁用',
1814
+ // 占位符
1815
+ 'placeholder.select': '请选择',
1816
+ 'placeholder.enter': '请输入',
1817
+ 'placeholder.search': '搜索',
1818
+ 'placeholder.filter': '筛选'
1529
1819
  };
1530
1820
 
1531
- /**
1532
- * 初始化国际化
1533
- * 在组件库初始化时调用,注册默认语言包
1534
- */
1535
- const initI18n = () => {
1536
- // 注册中文语言包
1537
- addMessages('zh-CN', {
1538
- 'button.ok': '确定',
1539
- 'button.cancel': '取消',
1540
- 'button.save': '保存',
1541
- 'button.delete': '删除',
1542
- 'button.edit': '编辑',
1543
- 'button.add': '添加',
1544
- 'button.confirm': '确认',
1545
- 'button.close': '关闭',
1546
- 'button.apply': '应用',
1547
- 'menu.networkSettings': '网络设置',
1548
- 'menu.ptp': 'PTP',
1549
- 'menu.nmos': 'NMOS',
1550
- 'menu.preset': '预设',
1551
- 'menu.license': '许可证'
1552
- // ... 其他默认翻译
1553
- });
1554
-
1555
- // 注册英文语言包
1556
- addMessages('en-US', {
1557
- 'button.ok': 'OK',
1558
- 'button.cancel': 'Cancel',
1559
- 'button.save': 'Save',
1560
- 'button.delete': 'Delete',
1561
- 'button.edit': 'Edit',
1562
- 'button.add': 'Add',
1563
- 'button.confirm': 'Confirm',
1564
- 'button.close': 'Close',
1565
- 'button.apply': 'Apply',
1566
- 'menu.networkSettings': 'Network Settings',
1567
- 'menu.ptp': 'PTP',
1568
- 'menu.nmos': 'NMOS',
1569
- 'menu.preset': 'Preset',
1570
- 'menu.license': 'License'
1571
- // ... 其他默认翻译
1572
- });
1821
+ // seeder-st2110-components component library English language pack
1822
+ var enUS = {
1823
+ // General buttons
1824
+ 'button.ok': 'OK',
1825
+ 'button.cancel': 'Cancel',
1826
+ 'button.save': 'Save',
1827
+ 'button.delete': 'Delete',
1828
+ 'button.edit': 'Edit',
1829
+ 'button.add': 'Add',
1830
+ 'button.confirm': 'Confirm',
1831
+ 'button.close': 'Close',
1832
+ 'button.apply': 'Apply',
1833
+ 'button.submit': 'Submit',
1834
+ 'button.next': 'Next',
1835
+ 'button.previous': 'Previous',
1836
+ 'button.refresh': 'Refresh',
1837
+ 'button.retry': 'Retry',
1838
+ // Menu
1839
+ 'menu.networkSettings': 'Network Settings',
1840
+ 'menu.ptp': 'PTP',
1841
+ 'menu.nmos': 'NMOS',
1842
+ 'menu.preset': 'Preset',
1843
+ 'menu.license': 'License',
1844
+ 'menu.maintenance': 'Maintenance',
1845
+ 'menu.system': 'System',
1846
+ // Network Settings Modal
1847
+ 'networkSettings.title': 'Network Settings',
1848
+ 'networkSettings.ipAddress': 'IP Address',
1849
+ 'networkSettings.subnetMask': 'Netmask',
1850
+ 'networkSettings.restartRequired': 'Configuration modified. Restart to apply changes?',
1851
+ 'networkSettings.restartNow': 'Restart Now',
1852
+ 'networkSettings.restartLater': 'Restart Later',
1853
+ 'networkSettings.saveSuccess': 'Success',
1854
+ // PTP Modal
1855
+ 'ptp.title': 'PTP Settings',
1856
+ 'ptp.domainNumber': 'Domain Number',
1857
+ 'ptp.priority1': 'Priority 1',
1858
+ 'ptp.priority2': 'Priority 2',
1859
+ 'ptp.clockClass': 'Clock Class',
1860
+ 'ptp.clockClass.atomic': 'Atomic Clock ({value})',
1861
+ 'ptp.clockClass.gps': 'GPS ({value})',
1862
+ 'ptp.clockClass.slaveOnly': 'Slave-Only ({value})',
1863
+ 'ptp.clockAccuracy': 'Clock Accuracy',
1864
+ 'ptp.offsetScaledLogVariance': 'Offset Scaled Log Variance',
1865
+ 'ptp.portIdentity': 'Port Identity',
1866
+ 'ptp.grandmasterIdentity': 'Grandmaster Identity',
1867
+ 'ptp.utcOffset': 'UTC Offset',
1868
+ 'ptp.connected': 'Connected',
1869
+ 'ptp.disconnected': 'Disconnected',
1870
+ 'ptp.locked': 'Locked',
1871
+ 'ptp.unlocked': 'Unlocked',
1872
+ 'ptp.saveSuccess': 'Success',
1873
+ // NMOS Modal
1874
+ 'nmos.title': 'NMOS Settings',
1875
+ 'nmos.hostAddress': 'Host Address',
1876
+ 'nmos.domain': 'Domain',
1877
+ 'nmos.registryAddress': 'Registry Address',
1878
+ 'nmos.registrationPort': 'Registry Port',
1879
+ 'nmos.registryVersion': 'Registry Version',
1880
+ 'nmos.loggingLevel': 'Logging Level',
1881
+ 'nmos.placeholder.selectHostAddress': 'Select host address',
1882
+ 'nmos.placeholder.selectVersion': 'Select version',
1883
+ 'nmos.saveSuccess': 'Success',
1884
+ // Preset Modal
1885
+ 'preset.title': 'Preset Management',
1886
+ 'preset.name': 'Name',
1887
+ 'preset.categories': 'Categories',
1888
+ 'preset.description': 'Description',
1889
+ 'preset.placeholder.enterName': 'Enter name',
1890
+ 'preset.button.new': 'New Preset',
1891
+ 'preset.button.delete': 'Delete',
1892
+ 'preset.button.load': 'Load',
1893
+ 'preset.button.save': 'Save',
1894
+ 'preset.button.edit': 'Edit',
1895
+ 'preset.button.cancel': 'Cancel',
1896
+ 'preset.empty.noData': 'Select a preset from the list to view details',
1897
+ 'preset.untitled': 'Untitled Preset',
1898
+ 'preset.delete.title': 'Delete Preset',
1899
+ 'preset.delete.confirmMessage': 'Are you sure you want to delete preset',
1900
+ 'preset.load.title': 'Load Preset',
1901
+ 'preset.load.confirmMessage': 'Are you sure you want to load preset',
1902
+ 'preset.load.loading': 'Loading...',
1903
+ 'preset.success': 'Success',
1904
+ 'preset.validation.nameRequired': 'Name is required',
1905
+ 'preset.validation.categoryRequired': 'No category selected',
1906
+ 'preset.error.noSelectionOrIdMissing': 'No preset selected or preset ID is missing',
1907
+ // License/Auth Modal
1908
+ 'license.title': 'Register License',
1909
+ 'license.machineCode': 'Machine Code',
1910
+ 'license.key': 'License Key',
1911
+ 'license.placeholder.enterKey': 'Enter your license key',
1912
+ 'license.button.activate': 'Activate Now',
1913
+ 'license.button.reactivate': 'Reactivate',
1914
+ 'license.button.ignore': 'Ignore',
1915
+ 'license.status.unactivated': 'Unactivated',
1916
+ 'license.status.activated': 'Already activated',
1917
+ 'license.status.expired': 'Expired',
1918
+ 'license.status.invalid': 'Invalid',
1919
+ 'license.status.expiresOn': 'Expires on',
1920
+ 'license.validation.keyRequired': 'License key cannot be empty',
1921
+ // Upgrade related
1922
+ 'upgrade.menu.download': 'Download Config File',
1923
+ 'upgrade.menu.softwareUpdate': 'Software Update',
1924
+ 'upgrade.status.idle': 'Idle',
1925
+ 'upgrade.status.uploading': 'Uploading...',
1926
+ 'upgrade.status.uploadComplete': 'Upload complete, starting upgrade...',
1927
+ 'upgrade.status.upgrading': 'Upgrading...',
1928
+ 'upgrade.error.downloadNotConfigured': 'Download functionality not configured',
1929
+ 'upgrade.error.downloadFailed': 'Download failed',
1930
+ 'upgrade.error.uploadFailed': 'Upload failed',
1931
+ 'upgrade.error.invalidFileFormat': 'Upload failed: Invalid file format',
1932
+ 'upgrade.error.upgradeFailed': 'Upgrade failed',
1933
+ // System operations
1934
+ 'system.poweroff': 'Power Off',
1935
+ 'system.restart': 'Restart',
1936
+ 'system.action.powerOff': 'power off',
1937
+ 'system.action.restart': 'restart',
1938
+ 'system.confirm.title': 'Confirmation Required',
1939
+ 'system.confirm.message': 'Are you sure you want to {action} the system? This action cannot be undone.',
1940
+ 'system.button.cancel': 'Cancel',
1941
+ 'system.button.confirm': 'Confirm',
1942
+ // Maintenance page
1943
+ 'maintenance.title': 'System Maintenance',
1944
+ 'maintenance.description': 'Device is under maintenance, please wait...',
1945
+ 'maintenance.restart': 'Device is restarting',
1946
+ 'maintenance.poweroff': 'Device is powered off',
1947
+ 'maintenance.redirecting': 'Redirecting...',
1948
+ 'maintenance.backToHome': 'Back to Home',
1949
+ // Form validation
1950
+ 'validation.required': 'This field is required',
1951
+ 'validation.invalidIP': 'Invalid IP address format',
1952
+ 'validation.invalidPort': 'Port number should be 1-65535',
1953
+ 'validation.invalidEmail': 'Invalid email format',
1954
+ 'validation.minLength': 'Minimum {min} characters required',
1955
+ 'validation.maxLength': 'Maximum {max} characters allowed',
1956
+ 'validation.min': 'Minimum value is {min}',
1957
+ 'validation.max': 'Maximum value is {max}',
1958
+ 'validation.pattern': 'Invalid format',
1959
+ 'validation.unique': 'This value already exists',
1960
+ // Status messages
1961
+ 'status.loading': 'Loading...',
1962
+ 'status.saving': 'Saving...',
1963
+ 'status.deleting': 'Deleting...',
1964
+ 'status.updating': 'Updating...',
1965
+ 'status.submitting': 'Submitting...',
1966
+ 'status.success': 'Success',
1967
+ 'status.failed': 'Failed',
1968
+ 'status.error': 'Error',
1969
+ 'status.pending': 'Pending',
1970
+ 'status.processing': 'Processing',
1971
+ 'status.completed': 'Completed',
1972
+ 'status.cancelled': 'Cancelled',
1973
+ // Confirmation dialogs
1974
+ 'confirm.title': 'Confirm Action',
1975
+ 'confirm.message': 'Are you sure you want to proceed?',
1976
+ 'confirm.cancel': 'Cancel',
1977
+ 'confirm.ok': 'OK',
1978
+ // Empty states
1979
+ 'empty.noData': 'No Data',
1980
+ 'empty.noResults': 'No Results',
1981
+ 'empty.noItems': 'No Items',
1982
+ // General labels
1983
+ 'label.name': 'Name',
1984
+ 'label.description': 'Description',
1985
+ 'label.status': 'Status',
1986
+ 'label.type': 'Type',
1987
+ 'label.action': 'Action',
1988
+ 'label.actions': 'Actions',
1989
+ 'label.settings': 'Settings',
1990
+ 'label.configuration': 'Configuration',
1991
+ 'label.information': 'Information',
1992
+ 'label.version': 'Version',
1993
+ 'label.time': 'Time',
1994
+ 'label.date': 'Date',
1995
+ 'label.enable': 'Enable',
1996
+ 'label.disable': 'Disable',
1997
+ // Placeholders
1998
+ 'placeholder.select': 'Please select',
1999
+ 'placeholder.enter': 'Please enter',
2000
+ 'placeholder.search': 'Search',
2001
+ 'placeholder.filter': 'Filter'
1573
2002
  };
1574
2003
 
1575
2004
  const NetworkFieldGroup = _ref => {
@@ -1604,8 +2033,8 @@ const NetworkFieldGroup = _ref => {
1604
2033
  };
1605
2034
  const mergedFieldConfig = _objectSpread2$1(_objectSpread2$1(_objectSpread2$1({}, defaultFieldConfig), fieldConfig), {}, {
1606
2035
  netmask: {
1607
- label: "Netmask",
1608
- enabled: (_fieldConfig$netmask$ = (_fieldConfig$netmask = fieldConfig.netmask) === null || _fieldConfig$netmask === void 0 ? void 0 : _fieldConfig$netmask.enabled) !== null && _fieldConfig$netmask$ !== void 0 ? _fieldConfig$netmask$ : defaultFieldConfig.netmask.enabled // 合并 enabled
2036
+ label: defaultFieldConfig.netmask.label,
2037
+ enabled: (_fieldConfig$netmask$ = (_fieldConfig$netmask = fieldConfig.netmask) === null || _fieldConfig$netmask === void 0 ? void 0 : _fieldConfig$netmask.enabled) !== null && _fieldConfig$netmask$ !== void 0 ? _fieldConfig$netmask$ : defaultFieldConfig.netmask.enabled
1609
2038
  }
1610
2039
  });
1611
2040
  return /*#__PURE__*/jsxs(Fragment, {
@@ -2007,86 +2436,86 @@ var NetworkSettingsModal$1 = /*#__PURE__*/memo(NetworkSettingsModal);
2007
2436
 
2008
2437
  const defaultFieldConfigs = {
2009
2438
  clock_class: {
2010
- label: 'Clock Class',
2439
+ label: 'ptp.clockClass',
2011
2440
  formType: 'select',
2012
2441
  options: [{
2013
2442
  value: 6,
2014
- label: 'Atomic Clock (6)'
2443
+ label: 'ptp.clockClass.atomic'
2015
2444
  }, {
2016
2445
  value: 7,
2017
- label: 'GPS (7)'
2446
+ label: 'ptp.clockClass.gps'
2018
2447
  }, {
2019
2448
  value: 248,
2020
- label: 'Slave-Only (248)'
2449
+ label: 'ptp.clockClass.slaveOnly'
2021
2450
  }],
2022
2451
  readOnly: true
2023
2452
  },
2024
2453
  clock_accuracy: {
2025
- label: 'Clock Accuracy',
2454
+ label: 'ptp.clockAccuracy',
2026
2455
  formType: 'number',
2027
2456
  readOnly: true
2028
2457
  },
2029
2458
  offset_scaled_log_variance: {
2030
- label: 'Offset Scaled Log Variance',
2459
+ label: 'ptp.offsetScaledLogVariance',
2031
2460
  formType: 'number',
2032
2461
  readOnly: true
2033
2462
  },
2034
2463
  grandmaster_priority1: {
2035
- label: 'Priority 1',
2464
+ label: 'ptp.priority1',
2036
2465
  formType: 'number',
2037
2466
  readOnly: true
2038
2467
  },
2039
2468
  grandmaster_priority2: {
2040
- label: 'Priority 2',
2469
+ label: 'ptp.priority2',
2041
2470
  formType: 'number',
2042
2471
  readOnly: true
2043
2472
  },
2044
2473
  grandmaster_identity: {
2045
- label: 'Grandmaster Identity',
2474
+ label: 'ptp.grandmasterIdentity',
2046
2475
  formType: 'text',
2047
2476
  readOnly: true
2048
2477
  },
2049
2478
  master_port_id: {
2050
- label: 'Port Identity',
2479
+ label: 'ptp.portIdentity',
2051
2480
  formType: 'text',
2052
2481
  readOnly: true
2053
2482
  },
2054
2483
  t1_domain_number: {
2055
- label: 'Domain',
2484
+ label: 'ptp.domainNumber',
2056
2485
  formType: 'number',
2057
2486
  min: 0,
2058
2487
  max: 127,
2059
2488
  readOnly: false
2060
2489
  },
2061
2490
  master_utc_offset: {
2062
- label: 'UTC Offset',
2491
+ label: 'ptp.utcOffset',
2063
2492
  formType: 'number',
2064
2493
  readOnly: true
2065
2494
  },
2066
2495
  is_connected: {
2067
- label: 'Connected Status',
2496
+ label: 'ptp.connected',
2068
2497
  formType: 'badge',
2069
2498
  statusMap: {
2070
2499
  0: {
2071
- text: 'Disconnected',
2500
+ text: 'ptp.disconnected',
2072
2501
  color: 'red'
2073
2502
  },
2074
2503
  1: {
2075
- text: 'Connected',
2504
+ text: 'ptp.connected',
2076
2505
  color: 'green'
2077
2506
  }
2078
2507
  }
2079
2508
  },
2080
2509
  is_locked: {
2081
- label: 'Locked Status',
2510
+ label: 'ptp.locked',
2082
2511
  formType: 'badge',
2083
2512
  statusMap: {
2084
2513
  0: {
2085
- text: 'Unlocked',
2514
+ text: 'ptp.unlocked',
2086
2515
  color: 'red'
2087
2516
  },
2088
2517
  1: {
2089
- text: 'Locked',
2518
+ text: 'ptp.locked',
2090
2519
  color: 'green'
2091
2520
  }
2092
2521
  }
@@ -2129,6 +2558,7 @@ const PtpModal = _ref => {
2129
2558
  modalProps = {},
2130
2559
  formProps = {}
2131
2560
  } = _ref;
2561
+ const intl = useIntl();
2132
2562
  const [ptpStatus, setPtpStatus] = useState(null);
2133
2563
  const [loading, setLoading] = useState(false);
2134
2564
  const [form] = Form.useForm();
@@ -2159,13 +2589,26 @@ const PtpModal = _ref => {
2159
2589
  if (!ptpStatus) return [];
2160
2590
  return convertPtpStatusToArray(ptpStatus, fieldConfigs, fieldOrder);
2161
2591
  }, [ptpStatus, fieldConfigs, fieldOrder]);
2592
+
2593
+ // 渲染 Form.Item 的 label
2594
+ const renderLabel = item => {
2595
+ // 如果 label 是国际化 key(包含点号),则使用 formatMessage
2596
+ if (item.label && typeof item.label === 'string' && item.label.includes('.')) {
2597
+ return intl.formatMessage({
2598
+ id: item.label
2599
+ });
2600
+ }
2601
+ return item.label;
2602
+ };
2162
2603
  const handleSubmit = async () => {
2163
2604
  const values = await form.getFieldsValue();
2164
2605
  setLoading(true);
2165
2606
  try {
2166
2607
  const response = await updatePtpInfo(values);
2167
2608
  if (response) {
2168
- message.success('Success');
2609
+ message.success(intl.formatMessage({
2610
+ id: 'ptp.saveSuccess'
2611
+ }));
2169
2612
  setTimeout(() => {
2170
2613
  onClose();
2171
2614
  }, 1500);
@@ -2177,11 +2620,18 @@ const PtpModal = _ref => {
2177
2620
  }
2178
2621
  };
2179
2622
  const renderFormItem = item => {
2180
- var _item$statusMap, _item$min, _item$max;
2623
+ var _item$options, _item$statusMap, _item$min, _item$max;
2181
2624
  switch (item.formType) {
2182
2625
  case 'select':
2626
+ const selectOptions = (_item$options = item.options) === null || _item$options === void 0 ? void 0 : _item$options.map(opt => _objectSpread2$1(_objectSpread2$1({}, opt), {}, {
2627
+ label: intl.formatMessage({
2628
+ id: opt.label
2629
+ }, {
2630
+ value: opt.value
2631
+ })
2632
+ }));
2183
2633
  return /*#__PURE__*/jsx(Select, {
2184
- options: item.options,
2634
+ options: selectOptions,
2185
2635
  disabled: item.readOnly
2186
2636
  });
2187
2637
  case 'switch':
@@ -2202,7 +2652,9 @@ const PtpModal = _ref => {
2202
2652
  },
2203
2653
  children: /*#__PURE__*/jsx(Badge, {
2204
2654
  color: status.color,
2205
- text: status.text
2655
+ text: intl.formatMessage({
2656
+ id: status.text
2657
+ })
2206
2658
  })
2207
2659
  });
2208
2660
  case 'number':
@@ -2223,11 +2675,17 @@ const PtpModal = _ref => {
2223
2675
 
2224
2676
  // 合并默认模态框属性和传入的属性
2225
2677
  const mergedModalProps = _objectSpread2$1({
2226
- title: "PTP",
2678
+ title: intl.formatMessage({
2679
+ id: 'ptp.title'
2680
+ }),
2227
2681
  width: 650,
2228
2682
  open,
2229
- okText: "Apply",
2230
- cancelText: "Close",
2683
+ okText: intl.formatMessage({
2684
+ id: 'button.apply'
2685
+ }),
2686
+ cancelText: intl.formatMessage({
2687
+ id: 'button.close'
2688
+ }),
2231
2689
  onCancel: onClose,
2232
2690
  onOk: handleSubmit,
2233
2691
  confirmLoading: loading
@@ -2248,7 +2706,7 @@ const PtpModal = _ref => {
2248
2706
  return /*#__PURE__*/jsx(StyledModal$3, _objectSpread2$1(_objectSpread2$1({}, mergedModalProps), {}, {
2249
2707
  children: /*#__PURE__*/jsx(Form, _objectSpread2$1(_objectSpread2$1({}, mergedFormProps), {}, {
2250
2708
  children: ptpStatusArray.map(item => /*#__PURE__*/jsx(Form.Item, {
2251
- label: item.label,
2709
+ label: renderLabel(item),
2252
2710
  name: item.key,
2253
2711
  initialValue: item.value,
2254
2712
  children: renderFormItem(item)
@@ -2629,24 +3087,24 @@ const Preset = _ref => {
2629
3087
  // 字段配置
2630
3088
  fields = {
2631
3089
  name: {
2632
- label: "Name",
2633
- placeholder: "Enter name",
3090
+ label: "preset.name",
3091
+ placeholder: "preset.placeholder.enterName",
2634
3092
  required: true
2635
3093
  }
2636
3094
  },
2637
3095
  texts = {
2638
- title: "Preset Management",
2639
- emptyText: "Select a preset from the list to view details",
2640
- deleteConfirm: "Are you sure you want to delete preset",
2641
- loadConfirm: "Are you sure you want to load preset",
2642
- loadText: "Loading...",
2643
- successText: "Success",
2644
- newButton: "New Preset",
2645
- removeButton: "Delete",
2646
- loadButton: "Load",
2647
- saveButton: "Save",
2648
- editButton: "Edit",
2649
- cancelButton: "Cancel"
3096
+ title: "preset.title",
3097
+ emptyText: "preset.empty.noData",
3098
+ deleteConfirm: "preset.delete.confirmMessage",
3099
+ loadConfirm: "preset.load.confirmMessage",
3100
+ loadText: "preset.load.loading",
3101
+ successText: "preset.success",
3102
+ newButton: "preset.button.new",
3103
+ removeButton: "preset.button.delete",
3104
+ loadButton: "preset.button.load",
3105
+ saveButton: "preset.button.save",
3106
+ editButton: "preset.button.edit",
3107
+ cancelButton: "preset.button.cancel"
2650
3108
  },
2651
3109
  // 样式定制
2652
3110
  width = 1000,
@@ -2655,6 +3113,7 @@ const Preset = _ref => {
2655
3113
  // 功能配置
2656
3114
  enableEdit = true
2657
3115
  } = _ref;
3116
+ const intl = useIntl();
2658
3117
  const {
2659
3118
  message,
2660
3119
  modal
@@ -2664,6 +3123,16 @@ const Preset = _ref => {
2664
3123
  const [loading, setLoading] = useState(false);
2665
3124
  const [presetChanged, setPresetChanged] = useState(0);
2666
3125
  const [form] = Form.useForm();
3126
+
3127
+ // 格式化文本的辅助函数
3128
+ const fmt = useCallback(key => {
3129
+ if (typeof key === 'string' && key.includes('.')) {
3130
+ return intl.formatMessage({
3131
+ id: key
3132
+ });
3133
+ }
3134
+ return key;
3135
+ }, [intl]);
2667
3136
  const fetchPresetList = useCallback(async () => {
2668
3137
  try {
2669
3138
  const data = await getPresetList();
@@ -2711,16 +3180,16 @@ const Preset = _ref => {
2711
3180
  const handleRemove = useCallback(async () => {
2712
3181
  if (!selectedPreset) return;
2713
3182
 
2714
- // 检查是否为新建的未保存数据(无id)
3183
+ // 检查是否为新建的未保存数据(无 id)
2715
3184
  const isUnsavedPreset = !selectedPreset.id;
2716
- const presetName = selectedPreset.name || 'Untitled Preset';
3185
+ const presetName = selectedPreset.name || fmt('preset.untitled');
2717
3186
  try {
2718
3187
  modal.confirm({
2719
3188
  icon: /*#__PURE__*/jsx(ExclamationCircleFilled, {}),
2720
- title: 'Delete Preset',
2721
- content: "".concat(texts.deleteConfirm, " \"").concat(presetName, "\"?"),
2722
- cancelText: 'Cancel',
2723
- okText: 'Delete',
3189
+ title: fmt('preset.delete.title'),
3190
+ content: "".concat(fmt('preset.delete.confirmMessage'), " \"").concat(presetName, "\"?"),
3191
+ cancelText: fmt('button.cancel'),
3192
+ okText: fmt('button.delete'),
2724
3193
  onOk: async () => {
2725
3194
  // 在删除前记录当前选中项的位置
2726
3195
  const currentIndex = presetList.findIndex(item => isUnsavedPreset ? !item.id : item.id === selectedPreset.id);
@@ -2728,7 +3197,7 @@ const Preset = _ref => {
2728
3197
  await removePreset({
2729
3198
  id: selectedPreset.id
2730
3199
  });
2731
- message.success(texts.successText);
3200
+ message.success(fmt('preset.success'));
2732
3201
  } else {
2733
3202
  // 移除未保存的预设
2734
3203
  setPresetList(prev => prev.filter(item => !!item.id));
@@ -2767,17 +3236,17 @@ const Preset = _ref => {
2767
3236
  } catch (error) {
2768
3237
  console.error('Failed to delete preset:', error);
2769
3238
  }
2770
- }, [selectedPreset, form, modal, message, texts, removePreset, getPresetList]);
3239
+ }, [selectedPreset, form, modal, message, removePreset, getPresetList, fmt]);
2771
3240
  const handleLoadPreset = useCallback(async loadData => {
2772
3241
  if (!loadData) return;
2773
3242
  let modalInstance = null;
2774
3243
  let resolveOk = null; // 用于控制 confirm 关闭时机
2775
3244
 
2776
3245
  modalInstance = modal.confirm({
2777
- title: 'Load Preset',
2778
- content: "".concat(texts.loadConfirm, " \"").concat(loadData.name, "\"?"),
2779
- cancelText: 'Cancel',
2780
- okText: 'Load',
3246
+ title: fmt('preset.load.title'),
3247
+ content: "".concat(fmt('preset.load.confirmMessage'), " \"").concat(loadData.name, "\"?"),
3248
+ cancelText: fmt('button.cancel'),
3249
+ okText: fmt('preset.button.load'),
2781
3250
  // onOk 返回一个 pending Promise
2782
3251
  onOk: () => {
2783
3252
  return new Promise((resolve, reject) => {
@@ -2785,7 +3254,7 @@ const Preset = _ref => {
2785
3254
 
2786
3255
  // 立即切换为 loading 状态
2787
3256
  modalInstance.update({
2788
- title: texts.loadText,
3257
+ title: fmt('preset.load.loading'),
2789
3258
  content: /*#__PURE__*/jsx(Spin, {
2790
3259
  size: "large",
2791
3260
  className: "block mx-auto"
@@ -2811,11 +3280,11 @@ const Preset = _ref => {
2811
3280
  category_list: loadData.category_list
2812
3281
  }));
2813
3282
 
2814
- // 成功:1秒后自动关闭
3283
+ // 成功:1 秒后自动关闭
2815
3284
  setTimeout(() => {
2816
3285
  var _resolveOk;
2817
3286
  (_resolveOk = resolveOk) === null || _resolveOk === void 0 || _resolveOk();
2818
- message.success(texts.successText);
3287
+ message.success(fmt('preset.success'));
2819
3288
  // 加载成功的外部回调
2820
3289
  if (onLoadSuccess) {
2821
3290
  onLoadSuccess(loadData);
@@ -2837,7 +3306,7 @@ const Preset = _ref => {
2837
3306
  // 用户取消
2838
3307
  }
2839
3308
  });
2840
- }, [loadPreset, texts, message, modal, onLoadSuccess, onLoadError]);
3309
+ }, [loadPreset, message, modal, onLoadSuccess, onLoadError, fmt]);
2841
3310
  const handleSave = useCallback(async () => {
2842
3311
  setLoading(true);
2843
3312
  try {
@@ -2847,7 +3316,7 @@ const Preset = _ref => {
2847
3316
  // 验证预设名称
2848
3317
  if ((_fields$name = fields.name) !== null && _fields$name !== void 0 && _fields$name.required) {
2849
3318
  if (!values.name || values.name.trim() === '') {
2850
- message.error('Name is required.');
3319
+ message.error(fmt('preset.validation.nameRequired'));
2851
3320
  return; // 直接返回 不执行
2852
3321
  }
2853
3322
  }
@@ -2855,12 +3324,12 @@ const Preset = _ref => {
2855
3324
  // 验证分类列表
2856
3325
  if ((_fields$category_list2 = fields.category_list) !== null && _fields$category_list2 !== void 0 && _fields$category_list2.required) {
2857
3326
  if (!values.category_list || values.category_list.length === 0) {
2858
- message.error('No category selected.');
3327
+ message.error(fmt('preset.validation.categoryRequired'));
2859
3328
  return;
2860
3329
  }
2861
3330
  }
2862
3331
  await savePreset(values);
2863
- message.success(texts.successText);
3332
+ message.success(fmt('preset.success'));
2864
3333
  const savedPresetName = values.name;
2865
3334
 
2866
3335
  // 刷新列表
@@ -2882,10 +3351,10 @@ const Preset = _ref => {
2882
3351
  } finally {
2883
3352
  setLoading(false);
2884
3353
  }
2885
- }, [form, message, texts, savePreset, getPresetList]);
3354
+ }, [form, message, fields, savePreset, getPresetList, fmt]);
2886
3355
  const handleUpdate = useCallback(async () => {
2887
3356
  if (!selectedPreset || !selectedPreset.id) {
2888
- message.error('No preset selected or preset ID is missing.');
3357
+ message.error(fmt('preset.error.noSelectionOrIdMissing'));
2889
3358
  return;
2890
3359
  }
2891
3360
  setLoading(true);
@@ -2896,7 +3365,7 @@ const Preset = _ref => {
2896
3365
  // 验证预设名称
2897
3366
  if ((_fields$name2 = fields.name) !== null && _fields$name2 !== void 0 && _fields$name2.required) {
2898
3367
  if (!values.name || values.name.trim() === '') {
2899
- message.error('Name is required.');
3368
+ message.error(fmt('preset.validation.nameRequired'));
2900
3369
  return; // 直接返回 不执行
2901
3370
  }
2902
3371
  }
@@ -2916,21 +3385,21 @@ const Preset = _ref => {
2916
3385
  if (updatedPreset) {
2917
3386
  setSelectedPreset(updatedPreset);
2918
3387
  }
2919
- message.success(texts.successText);
3388
+ message.success(fmt('preset.success'));
2920
3389
  } catch (error) {
2921
3390
  console.error('Failed to update preset:', error);
2922
3391
  throw error;
2923
3392
  } finally {
2924
3393
  setLoading(false);
2925
3394
  }
2926
- }, [form, message, texts, updatePreset, selectedPreset]);
3395
+ }, [form, message, fields, updatePreset, selectedPreset, fmt]);
2927
3396
 
2928
3397
  // 初始化数据
2929
3398
  useEffect(() => {
2930
3399
  fetchPresetList();
2931
3400
  }, [fetchPresetList]);
2932
3401
  return /*#__PURE__*/jsx(StyledModal$3, {
2933
- title: texts.title,
3402
+ title: fmt(texts.title),
2934
3403
  width: width,
2935
3404
  open: open,
2936
3405
  wrapClassName: "preset-management ".concat(className),
@@ -2956,8 +3425,8 @@ const Preset = _ref => {
2956
3425
  onAddNew: handleAddNew,
2957
3426
  onRemove: handleRemove,
2958
3427
  texts: {
2959
- newButton: texts.newButton,
2960
- removeButton: texts.removeButton
3428
+ newButton: fmt(texts.newButton),
3429
+ removeButton: fmt(texts.removeButton)
2961
3430
  }
2962
3431
  })
2963
3432
  }), /*#__PURE__*/jsx(Col, {
@@ -2974,10 +3443,10 @@ const Preset = _ref => {
2974
3443
  ,
2975
3444
  fields: fields,
2976
3445
  texts: {
2977
- loadButton: texts.loadButton,
2978
- saveButton: texts.saveButton,
2979
- editButton: texts.editButton,
2980
- cancelButton: texts.cancelButton
3446
+ loadButton: fmt(texts.loadButton),
3447
+ saveButton: fmt(texts.saveButton),
3448
+ editButton: fmt(texts.editButton),
3449
+ cancelButton: fmt(texts.cancelButton)
2981
3450
  },
2982
3451
  presetChanged: presetChanged,
2983
3452
  enableEdit: enableEdit
@@ -2988,7 +3457,7 @@ const Preset = _ref => {
2988
3457
  className: "h-full text-gray-400",
2989
3458
  children: /*#__PURE__*/jsx(Empty, {
2990
3459
  image: Empty.PRESENTED_IMAGE_SIMPLE,
2991
- description: texts.emptyText
3460
+ description: fmt(texts.emptyText)
2992
3461
  })
2993
3462
  })
2994
3463
  })]
@@ -4243,20 +4712,23 @@ const SystemOperations = _ref => {
4243
4712
  onRestartSuccess,
4244
4713
  beforeAction,
4245
4714
  // 在执行关机/重启前调用
4246
- powerOffLabel = "Power Off",
4247
- restartLabel = "Restart",
4715
+ powerOffLabel = "system.powerOff",
4716
+ restartLabel = "system.restart",
4248
4717
  iconClassName = "seeder-iconfont seeder-icon-guanji1 text-xl text-neutral-400",
4249
- confirmMessage = "Are you sure you want to {action} the system? This action cannot be undone.",
4250
- confirmTitle = "Confirmation Required",
4251
- cancelText = "Cancel",
4252
- okText = "Confirm"
4718
+ confirmMessage = "system.confirm.message",
4719
+ confirmTitle = "system.confirm.title",
4720
+ cancelText = "system.button.cancel",
4721
+ okText = "system.button.confirm"
4253
4722
  } = _ref;
4723
+ const intl = useIntl();
4254
4724
  const {
4255
4725
  modal
4256
4726
  } = App.useApp();
4257
4727
  const menuItems = [{
4258
4728
  key: "poweroff",
4259
- label: powerOffLabel
4729
+ label: typeof powerOffLabel === 'string' && powerOffLabel.includes('.') ? intl.formatMessage({
4730
+ id: powerOffLabel
4731
+ }) : powerOffLabel
4260
4732
  },
4261
4733
  // {
4262
4734
  // key: "reboot",
@@ -4264,22 +4736,45 @@ const SystemOperations = _ref => {
4264
4736
  // },
4265
4737
  {
4266
4738
  key: "restart",
4267
- label: restartLabel // 软重启 服务重启
4739
+ label: typeof restartLabel === 'string' && restartLabel.includes('.') ? intl.formatMessage({
4740
+ id: restartLabel
4741
+ }) : restartLabel
4268
4742
  }];
4269
4743
  const doAction = action => {
4270
4744
  try {
4271
- // 根据action决定显示的确认信息
4745
+ // 根据 action 决定显示的确认信息
4272
4746
  const actionLabels = {
4273
- poweroff: "power off",
4274
- restart: "restart"
4747
+ poweroff: intl.formatMessage({
4748
+ id: 'system.action.powerOff'
4749
+ }),
4750
+ restart: intl.formatMessage({
4751
+ id: 'system.action.restart'
4752
+ })
4275
4753
  };
4276
4754
  const actionText = actionLabels[action] || action;
4755
+
4756
+ // 处理标题和消息的国际化
4757
+ const titleText = typeof confirmTitle === 'string' && confirmTitle.includes('.') ? intl.formatMessage({
4758
+ id: confirmTitle
4759
+ }) : confirmTitle;
4760
+
4761
+ // 如果是国际化 key,使用 intl.formatMessage 处理参数
4762
+ // 如果是普通文本,使用 replace 替换占位符
4763
+ const messageText = typeof confirmMessage === 'string' && confirmMessage.includes('.') ? intl.formatMessage({
4764
+ id: confirmMessage
4765
+ }, {
4766
+ action: actionText
4767
+ }) : typeof confirmMessage === 'string' ? confirmMessage.replace('{action}', actionText) : confirmMessage;
4277
4768
  modal.confirm({
4278
4769
  icon: /*#__PURE__*/jsx(ExclamationCircleFilled, {}),
4279
- title: confirmTitle,
4280
- content: confirmMessage.replace('{action}', actionText),
4281
- cancelText,
4282
- okText,
4770
+ title: titleText,
4771
+ content: messageText,
4772
+ cancelText: typeof cancelText === 'string' && cancelText.includes('.') ? intl.formatMessage({
4773
+ id: cancelText
4774
+ }) : cancelText,
4775
+ okText: typeof okText === 'string' && okText.includes('.') ? intl.formatMessage({
4776
+ id: okText
4777
+ }) : okText,
4283
4778
  onOk: async () => {
4284
4779
  // 先执行 beforeAction(用于 enterMaintenanceMode)
4285
4780
  if (typeof beforeAction === 'function') {
@@ -12978,6 +13473,7 @@ const NmosModal = _ref => {
12978
13473
  modalProps = {},
12979
13474
  formProps = {}
12980
13475
  } = _ref;
13476
+ const intl = useIntl();
12981
13477
  const [nmosSettings, setNmosSettings] = useState(null);
12982
13478
  const [loading, setLoading] = useState(false);
12983
13479
  const [form] = Form.useForm();
@@ -13018,7 +13514,9 @@ const NmosModal = _ref => {
13018
13514
  try {
13019
13515
  const response = await updateNmosSettings(_objectSpread2$1(_objectSpread2$1({}, nmosSettings), values));
13020
13516
  if (response) {
13021
- message.success('Success');
13517
+ message.success(intl.formatMessage({
13518
+ id: 'nmos.saveSuccess'
13519
+ }));
13022
13520
  setTimeout(() => {
13023
13521
  onClose();
13024
13522
  }, 1500);
@@ -13030,9 +13528,17 @@ const NmosModal = _ref => {
13030
13528
  }
13031
13529
  };
13032
13530
  return /*#__PURE__*/jsx(StyledModal$1, _objectSpread2$1(_objectSpread2$1({
13033
- title: "NMOS",
13531
+ title: intl.formatMessage({
13532
+ id: 'nmos.title'
13533
+ }),
13034
13534
  width: 650,
13035
13535
  open: open,
13536
+ okText: intl.formatMessage({
13537
+ id: 'button.apply'
13538
+ }),
13539
+ cancelText: intl.formatMessage({
13540
+ id: 'button.close'
13541
+ }),
13036
13542
  onOk: handleSubmit,
13037
13543
  onCancel: onClose,
13038
13544
  confirmLoading: loading,
@@ -13051,7 +13557,9 @@ const NmosModal = _ref => {
13051
13557
  disabled: loading
13052
13558
  }, formProps), {}, {
13053
13559
  children: [/*#__PURE__*/jsx(Form.Item, {
13054
- label: "Host Addresses",
13560
+ label: intl.formatMessage({
13561
+ id: 'nmos.hostAddress'
13562
+ }),
13055
13563
  name: "host_addresses",
13056
13564
  children: /*#__PURE__*/jsx(Select, {
13057
13565
  mode: "multiple",
@@ -13060,19 +13568,27 @@ const NmosModal = _ref => {
13060
13568
  label: "display_name",
13061
13569
  value: "ip_address"
13062
13570
  },
13063
- placeholder: "Select host address",
13571
+ placeholder: intl.formatMessage({
13572
+ id: 'nmos.placeholder.selectHostAddress'
13573
+ }),
13064
13574
  allowClear: true
13065
13575
  })
13066
13576
  }), /*#__PURE__*/jsx(Form.Item, {
13067
- label: "Domain",
13577
+ label: intl.formatMessage({
13578
+ id: 'nmos.domain'
13579
+ }),
13068
13580
  name: "domain",
13069
13581
  children: /*#__PURE__*/jsx(Input, {})
13070
13582
  }), /*#__PURE__*/jsx(Form.Item, {
13071
- label: "Registry Address",
13583
+ label: intl.formatMessage({
13584
+ id: 'nmos.registryAddress'
13585
+ }),
13072
13586
  name: "registry_address",
13073
13587
  children: /*#__PURE__*/jsx(Input, {})
13074
13588
  }), /*#__PURE__*/jsx(Form.Item, {
13075
- label: "Registry Port",
13589
+ label: intl.formatMessage({
13590
+ id: 'nmos.registrationPort'
13591
+ }),
13076
13592
  name: "registration_port",
13077
13593
  children: /*#__PURE__*/jsx(InputNumber, _objectSpread2$1(_objectSpread2$1({}, numberProps), {}, {
13078
13594
  min: 0,
@@ -13082,7 +13598,9 @@ const NmosModal = _ref => {
13082
13598
  }
13083
13599
  }))
13084
13600
  }), /*#__PURE__*/jsx(Form.Item, {
13085
- label: "Registry Version",
13601
+ label: intl.formatMessage({
13602
+ id: 'nmos.registryVersion'
13603
+ }),
13086
13604
  name: "registry_version",
13087
13605
  children: /*#__PURE__*/jsx(Select, {
13088
13606
  options: [{
@@ -13098,10 +13616,14 @@ const NmosModal = _ref => {
13098
13616
  label: "v1.3",
13099
13617
  value: "v1.3"
13100
13618
  }],
13101
- placeholder: "Select version"
13619
+ placeholder: intl.formatMessage({
13620
+ id: 'nmos.placeholder.selectVersion'
13621
+ })
13102
13622
  })
13103
13623
  }), /*#__PURE__*/jsx(Form.Item, {
13104
- label: "Logging Level",
13624
+ label: intl.formatMessage({
13625
+ id: 'nmos.loggingLevel'
13626
+ }),
13105
13627
  name: "logging_level",
13106
13628
  children: /*#__PURE__*/jsx(InputNumber, _objectSpread2$1(_objectSpread2$1({}, numberProps), {}, {
13107
13629
  min: -40,
@@ -13723,5 +14245,5 @@ const PayloadInput = props => /*#__PURE__*/jsx(BoundedInput, _objectSpread2$1({
13723
14245
  placeholder: "Enter payload (96-127)"
13724
14246
  }, props));
13725
14247
 
13726
- export { AuthorizationModal$1 as AuthorizationModal, CommonHeader$1 as CommonHeader, DraggableNumberInput, LSMLabelField$1 as LSMLabelField, MaintenancePage, NetworkSettingsModal$1 as NetworkSettingsModal, NmosModal$1 as NmosModal, PayloadInput, PortInput, PresetModal, PtpModal$1 as PtpModal, StyledModal$3 as StyledModal, SystemOperations$1 as SystemOperations, UpgradeManager$1 as UpgradeManager, addMessages, debugI18n, initI18n, setLocale, useAuth, useHardwareUsage$1 as useHardwareUsage, useIntl, usePageReload$1 as usePageReload, useSystemOperations$1 as useSystemOperations, useUpgrade$1 as useUpgrade, useWebSocketWithFeatures$1 as useWebSocketWithFeatures };
14248
+ export { AuthorizationModal$1 as AuthorizationModal, CommonHeader$1 as CommonHeader, DraggableNumberInput, LSMLabelField$1 as LSMLabelField, MaintenancePage, NetworkSettingsModal$1 as NetworkSettingsModal, NmosModal$1 as NmosModal, PayloadInput, PortInput, PresetModal, PtpModal$1 as PtpModal, StyledModal$3 as StyledModal, SystemOperations$1 as SystemOperations, UpgradeManager$1 as UpgradeManager, addMessages, debugI18n, initI18n, enUS as localesEnUS, zhCN as localesZhCN, setLocale, useAuth, useHardwareUsage$1 as useHardwareUsage, useIntl, usePageReload$1 as usePageReload, useSystemOperations$1 as useSystemOperations, useUpgrade$1 as useUpgrade, useWebSocketWithFeatures$1 as useWebSocketWithFeatures };
13727
14249
  //# sourceMappingURL=index.esm.js.map