refactor: 统一扩展消息机制并优化组件结构

1、统一消息系统:将 background、content 和 popup 的通信方式从原生的 chrome.runtime 迁移至 @webext-core/messaging,修复了消息回调返回 undefined 的问题。
2、优化组件结构:将通用组件从 entrypoints/popup/components 迁移至根目录 components,并使用 MUI 重构了 Navbar。
3、同步更新:调整了录制工具 (useRecorder) 和各页面组件(RecordeReplayPage, TestPage等),以适配新的消息协议和组件路径。
This commit is contained in:
雨霖铃
2026-02-13 20:38:15 +08:00
parent 22985f4c18
commit 50034331bc
16 changed files with 252 additions and 271 deletions
+176
View File
@@ -0,0 +1,176 @@
import { useState, useCallback } from 'react';
import dayjs from 'dayjs';
import {
Button,
TextField,
Select,
MenuItem,
FormControl,
InputLabel,
Paper,
Stack,
Box,
SelectChangeEvent,
} from '@mui/material';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';
dayjs.extend(utc);
dayjs.extend(timezone);
const TIME_ZONE_LIST = [
'America/New_York',
'America/Chicago',
'America/Denver',
'America/Los_Angeles',
'America/Anchorage',
'America/Honolulu',
'Europe/London',
'Europe/Paris',
'Europe/Berlin',
'Europe/Moscow',
'Asia/Tokyo',
'Asia/Shanghai',
'Asia/Hong_Kong',
'Asia/Singapore',
'Asia/Dubai',
'Asia/Kolkata',
'Australia/Sydney',
'Pacific/Auckland',
];
const TIMESTAMP_UNITS = [
{ value: 'milliseconds', label: '毫秒 (ms)' },
{ value: 'seconds', label: '秒 (s)' },
];
export function TimestampToDatetime() {
const [timestampValue, setTimestampValue] = useState(() => dayjs().valueOf().toString());
const [timestampResult, setTimestampResult] = useState('');
const [unit, setUnit] = useState('milliseconds');
const [selectedZone, setSelectedZone] = useState('Asia/Shanghai');
const [error, setError] = useState('');
const performConversion = useCallback((val: string, zone: string, u: string) => {
if (!val || val.trim() === '') {
setError('请输入有效的时间戳');
return '';
}
const numberValue = Number(val);
if (isNaN(numberValue)) {
setError('时间戳必须是数字');
return '';
}
const d = u === 'milliseconds' ? dayjs(numberValue) : dayjs.unix(numberValue);
if (!d.isValid()) {
setError('无效的时间戳格式');
return '';
}
setError('');
return d.tz(zone).format('YYYY/MM/DD HH:mm:ss');
}, []);
const handleConvert = useCallback(() => {
const newResult = performConversion(timestampValue, selectedZone, unit);
setTimestampResult(newResult);
}, [timestampValue, selectedZone, unit, performConversion]);
const handleInputChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
setTimestampValue(e.target.value);
if (error) setError('');
},
[error],
);
const handleZoneChange = useCallback(
(e: SelectChangeEvent<string>) => {
const newZone = e.target.value;
setSelectedZone(newZone);
if (timestampResult) {
const newResult = performConversion(timestampValue, newZone, unit);
setTimestampResult(newResult || '');
}
},
[performConversion, timestampResult, timestampValue, unit],
);
const handleUnitChange = useCallback(
(e: SelectChangeEvent<string>) => {
const newUnit = e.target.value;
setUnit(newUnit);
if (timestampResult) {
const newResult = performConversion(timestampValue, selectedZone, newUnit);
setTimestampResult(newResult || '');
}
},
[performConversion, timestampResult, timestampValue, selectedZone],
);
return (
<Paper elevation={3} sx={{ p: 2, my: 2, borderRadius: 2 }}>
<Stack spacing={2} sx={{ mt: 2 }}>
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={2}>
<TextField
label="输入时间戳"
placeholder="如: 1704067200000"
value={timestampValue}
onChange={handleInputChange}
error={!!error}
helperText={error}
fullWidth
variant="outlined"
/>
<FormControl fullWidth>
<InputLabel></InputLabel>
<Select
value={unit}
label="单位"
onChange={handleUnitChange}
MenuProps={{ disableScrollLock: true }}
>
{TIMESTAMP_UNITS.map(({ value, label }) => (
<MenuItem key={value} value={value}>
{label}
</MenuItem>
))}
</Select>
</FormControl>
</Stack>
<Box sx={{ display: 'flex', justifyContent: 'center' }}>
<Button variant="contained" size="medium" color="primary" onClick={handleConvert}>
</Button>
</Box>
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={2}>
<TextField
label="转换结果"
value={timestampResult}
fullWidth
variant="outlined"
InputProps={{
readOnly: true,
}}
/>
<FormControl fullWidth>
<InputLabel></InputLabel>
<Select
value={selectedZone}
label="时区"
onChange={handleZoneChange}
MenuProps={{ disableScrollLock: true }}
>
{TIME_ZONE_LIST.map((zone) => (
<MenuItem key={zone} value={zone}>
{zone}
</MenuItem>
))}
</Select>
</FormControl>
</Stack>
</Stack>
</Paper>
);
}