mirror of
https://github.com/yaklang/yaklang-chrome-extension.git
synced 2026-09-22 03:10:43 +08:00
ui v0.0.2-beta
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
"jest": "^27.4.3",
|
||||
"jest-resolve": "^27.4.2",
|
||||
"jest-watch-typeahead": "^1.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"mini-css-extract-plugin": "^2.4.5",
|
||||
"postcss": "^8.4.4",
|
||||
"postcss-flexbugs-fixes": "^5.0.2",
|
||||
@@ -139,6 +140,7 @@
|
||||
"@babel/preset-env": "^7.24.1",
|
||||
"@babel/preset-react": "^7.24.1",
|
||||
"@types/chrome": "^0.0.268",
|
||||
"@types/lodash": "^4.17.15",
|
||||
"babel-loader": "^9.1.3",
|
||||
"copy-webpack-plugin": "^12.0.2",
|
||||
"cross-env": "^7.0.3",
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
"storage",
|
||||
"sidePanel",
|
||||
"webRequest",
|
||||
"declarativeNetRequest"
|
||||
"declarativeNetRequest",
|
||||
"tabs"
|
||||
],
|
||||
"host_permissions": [
|
||||
"<all_urls>"
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
.add-proxy-form {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.form-buttons {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
margin-top: 24px;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import React from 'react';
|
||||
import { Form, Input, Select, InputNumber, Button, message } from 'antd';
|
||||
import { ProxyActionType } from '@/types/action';
|
||||
import './index.css';
|
||||
|
||||
interface EditFormData {
|
||||
name: string;
|
||||
proxyType: string;
|
||||
scheme?: string;
|
||||
host?: string;
|
||||
port?: number;
|
||||
pacScript?: string;
|
||||
}
|
||||
|
||||
export const AddProxyForm: React.FC = () => {
|
||||
const [form] = Form.useForm<EditFormData>();
|
||||
|
||||
// 初始化表单
|
||||
React.useEffect(() => {
|
||||
form.setFieldsValue({
|
||||
name: '',
|
||||
proxyType: 'fixed_servers',
|
||||
scheme: 'http',
|
||||
host: '127.0.0.1',
|
||||
port: 8080
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
const values = await form.validateFields();
|
||||
const newConfig = {
|
||||
id: Date.now().toString(),
|
||||
...values,
|
||||
enabled: false
|
||||
};
|
||||
|
||||
const response = await chrome.runtime.sendMessage({
|
||||
action: ProxyActionType.ADD_PROXY_CONFIG,
|
||||
config: newConfig
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
message.success('添加成功');
|
||||
window.close(); // 关闭窗口
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to add proxy:', error);
|
||||
message.error('添加失败');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="add-proxy-form">
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
onFinish={handleSave}
|
||||
>
|
||||
{/* 表单项与之前相同 */}
|
||||
<Form.Item className="form-buttons">
|
||||
<Button type="primary" htmlType="submit">
|
||||
确定
|
||||
</Button>
|
||||
<Button onClick={() => window.close()}>
|
||||
取消
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -42,7 +42,17 @@ interface CustomProxy {
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
export const ProxySwitch: React.FC = () => {
|
||||
interface ProxySwitchProps {
|
||||
proxyConfigs: ProxyConfig[];
|
||||
currentProxy: ProxyConfig | null;
|
||||
onProxyChange: (config: ProxyConfig) => void;
|
||||
}
|
||||
|
||||
export const ProxySwitch: React.FC<ProxySwitchProps> = ({
|
||||
proxyConfigs,
|
||||
currentProxy,
|
||||
onProxyChange,
|
||||
}) => {
|
||||
const [currentMode, setCurrentMode] = useState<string>('direct');
|
||||
const [customProxies, setCustomProxies] = useState<CustomProxy[]>([]);
|
||||
|
||||
@@ -126,12 +136,48 @@ export const ProxySwitch: React.FC = () => {
|
||||
|
||||
const handleModeChange = async (mode: string) => {
|
||||
if (mode === 'setting') {
|
||||
chrome.runtime.openOptionsPage?.();
|
||||
await chrome.runtime.openOptionsPage?.();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode === 'add') {
|
||||
chrome.runtime.openOptionsPage?.();
|
||||
try {
|
||||
// 获取当前活动标签页
|
||||
const [activeTab] = await chrome.tabs.query({
|
||||
active: true,
|
||||
currentWindow: true
|
||||
});
|
||||
const optionsUrl = chrome.runtime.getURL('/proxy/options.html');
|
||||
|
||||
if (activeTab?.url === optionsUrl) {
|
||||
// 如果当前就在 options 页面,直接发消息触发添加代理
|
||||
chrome.tabs.sendMessage(activeTab.id!, {
|
||||
action: 'TRIGGER_ADD_PROXY'
|
||||
});
|
||||
} else {
|
||||
// 如果不在 options 页面,创建新的
|
||||
const tab = await chrome.tabs.create({
|
||||
url: optionsUrl
|
||||
});
|
||||
|
||||
// 等待页面加载完成
|
||||
const listener = (tabId: number, changeInfo: chrome.tabs.TabChangeInfo) => {
|
||||
if (tabId === tab.id && changeInfo.status === 'complete') {
|
||||
chrome.tabs.onUpdated.removeListener(listener);
|
||||
// 给页面一点时间完全初始化
|
||||
// setTimeout(() => {
|
||||
chrome.tabs.sendMessage(tab.id!, {
|
||||
action: 'TRIGGER_ADD_PROXY'
|
||||
});
|
||||
// }, 500); // 减少延迟时间
|
||||
}
|
||||
};
|
||||
|
||||
chrome.tabs.onUpdated.addListener(listener);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to get current tab:', error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/* 表格样式 */
|
||||
.proxy-table {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
/* 表头样式 */
|
||||
.proxy-table .ant-table-thead > tr > th {
|
||||
background: white !important;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
/* 斑马纹样式 */
|
||||
.proxy-table .ant-table-tbody > tr:nth-child(even) {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
/* 单元格样式 */
|
||||
.proxy-table .ant-table-tbody > tr > td {
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
/* 操作列图标样式 */
|
||||
.action-icon {
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
color: #666;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.action-icon:hover {
|
||||
color: var(--yakit-primary);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.action-icon.enabled {
|
||||
color: var(--yakit-primary);
|
||||
}
|
||||
|
||||
.action-icon.delete:hover {
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
/* 间距调整 */
|
||||
.ant-space-middle {
|
||||
gap: 16px !important;
|
||||
}
|
||||
|
||||
/* 添加按钮样式 */
|
||||
.add-proxy-btn {
|
||||
background-color: var(--yakit-primary);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.add-proxy-btn:hover {
|
||||
background-color: var(--yakit-primary-hover) !important;
|
||||
color: white !important;
|
||||
}
|
||||
@@ -1,18 +1,32 @@
|
||||
import React, { useRef } from 'react';
|
||||
import { Card, Input, Space, Button, Select, InputNumber, Form, Table, Tooltip, Popover } from 'antd';
|
||||
import React, { useRef, useState, useEffect, useCallback } from 'react';
|
||||
import { Card, Input, Space, Button, Select, InputNumber, Form, Table, Tooltip, Popover, Modal, message } from 'antd';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { DeleteOutlined, PlusOutlined, EditOutlined, CheckOutlined } from '@ant-design/icons';
|
||||
import { ProxyConfig } from '@/types/proxy';
|
||||
import './index.css';
|
||||
|
||||
interface ProxySettingsProps {
|
||||
proxyConfigs: ProxyConfig[];
|
||||
onAdd: () => void;
|
||||
onChange: (configId: string, field: keyof ProxyConfig, value: any) => void;
|
||||
onAdd: (config: ProxyConfig) => void;
|
||||
onChange: (configId: string, field: keyof ProxyConfig | 'config', value: any) => void;
|
||||
onDelete: (configId: string) => void;
|
||||
onApply: (configId: string) => Promise<void>;
|
||||
onClear: (configId: string) => Promise<void>;
|
||||
}
|
||||
|
||||
// 添加编辑表单的接口
|
||||
interface EditFormData {
|
||||
name: string;
|
||||
proxyType: "direct" | "system" | "fixed_servers" | "pac_script" | "auto_detect";
|
||||
scheme?: "http" | "https" | "socks4" | "socks5";
|
||||
host?: string;
|
||||
port?: number;
|
||||
pacScript?: string;
|
||||
}
|
||||
|
||||
// 或者更好的方式是创建一个专门的类型
|
||||
type ProxyConfigField = keyof ProxyConfig | 'config';
|
||||
|
||||
export const ProxySettings: React.FC<ProxySettingsProps> = ({
|
||||
proxyConfigs,
|
||||
onAdd,
|
||||
@@ -21,120 +35,141 @@ export const ProxySettings: React.FC<ProxySettingsProps> = ({
|
||||
onApply,
|
||||
onClear
|
||||
}) => {
|
||||
const [editingConfig, setEditingConfig] = useState<ProxyConfig | null>(null);
|
||||
const [editModalVisible, setEditModalVisible] = useState(false);
|
||||
const [form] = Form.useForm<EditFormData>();
|
||||
|
||||
// 添加 useEffect 来监听表单值变化
|
||||
useEffect(() => {
|
||||
if (editModalVisible && editingConfig) {
|
||||
form.setFieldsValue({
|
||||
name: editingConfig.name,
|
||||
proxyType: editingConfig.proxyType,
|
||||
scheme: editingConfig.scheme,
|
||||
host: editingConfig.host,
|
||||
port: editingConfig.port,
|
||||
pacScript: editingConfig.pacScript
|
||||
});
|
||||
}
|
||||
}, [editModalVisible, editingConfig, form]);
|
||||
|
||||
// 处理添加按钮点击
|
||||
const handleAdd = useCallback(() => {
|
||||
setEditingConfig({
|
||||
id: Date.now().toString(),
|
||||
name: '',
|
||||
proxyType: 'fixed_servers',
|
||||
scheme: 'http' as "http" | "https" | "socks4" | "socks5",
|
||||
host: '127.0.0.1',
|
||||
port: 8080,
|
||||
enabled: false
|
||||
});
|
||||
setEditModalVisible(true);
|
||||
}, []);
|
||||
|
||||
// 处理编辑按钮点击
|
||||
const handleEdit = (record: ProxyConfig) => {
|
||||
setEditingConfig(record);
|
||||
setEditModalVisible(true);
|
||||
};
|
||||
|
||||
// 处理编辑保存
|
||||
const handleEditSave = async () => {
|
||||
try {
|
||||
const values = await form.validateFields();
|
||||
if (editingConfig) {
|
||||
if (editingConfig.enabled) {
|
||||
await onClear(editingConfig.id);
|
||||
}
|
||||
|
||||
const updatedConfig: ProxyConfig = {
|
||||
...editingConfig,
|
||||
...values,
|
||||
proxyType: values.proxyType as "direct" | "system" | "fixed_servers" | "pac_script" | "auto_detect",
|
||||
scheme: values.scheme as "http" | "https" | "socks4" | "socks5"
|
||||
};
|
||||
|
||||
if (!proxyConfigs.find(config => config.id === editingConfig.id)) {
|
||||
await onAdd(updatedConfig);
|
||||
} else {
|
||||
await onChange(editingConfig.id, 'config', updatedConfig);
|
||||
}
|
||||
|
||||
setEditModalVisible(false);
|
||||
setEditingConfig(null);
|
||||
form.resetFields();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Validate Failed:', error);
|
||||
message.error('保存失败,请检查表单');
|
||||
}
|
||||
};
|
||||
|
||||
// 处理模态框关闭
|
||||
const handleModalClose = () => {
|
||||
form.resetFields();
|
||||
setEditModalVisible(false);
|
||||
setEditingConfig(null);
|
||||
};
|
||||
|
||||
const columns: ColumnsType<ProxyConfig> = [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
render: (text: string, record: ProxyConfig) => (
|
||||
<Input
|
||||
value={text}
|
||||
onChange={e => onChange(record.id, 'name', e.target.value)}
|
||||
disabled={record.id === 'direct'}
|
||||
/>
|
||||
)
|
||||
render: (text: string) => text
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
dataIndex: 'proxyType',
|
||||
key: 'proxyType',
|
||||
render: (text: string, record: ProxyConfig) => (
|
||||
<Select
|
||||
value={text}
|
||||
onChange={value => onChange(record.id, 'proxyType', value)}
|
||||
style={{ width: 120 }}
|
||||
disabled={record.id === 'direct'}
|
||||
options={[
|
||||
{ label: '直接连接', value: 'direct' },
|
||||
{ label: '代理服务器', value: 'fixed_servers' },
|
||||
{ label: 'PAC 脚本', value: 'pac_script' }
|
||||
]}
|
||||
/>
|
||||
)
|
||||
render: (text: string) => {
|
||||
const typeMap = {
|
||||
direct: '直接连接',
|
||||
fixed_servers: '代理服务器',
|
||||
pac_script: 'PAC 脚本'
|
||||
};
|
||||
return typeMap[text as keyof typeof typeMap] || text;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '协议',
|
||||
dataIndex: 'scheme',
|
||||
key: 'scheme',
|
||||
render: (text: string, record: ProxyConfig) => (
|
||||
record.proxyType === 'fixed_servers' && (
|
||||
<Select
|
||||
value={text}
|
||||
onChange={value => onChange(record.id, 'scheme', value)}
|
||||
style={{ width: 100 }}
|
||||
options={[
|
||||
{ label: 'HTTP', value: 'http' },
|
||||
{ label: 'HTTPS', value: 'https' },
|
||||
{ label: 'SOCKS4', value: 'socks4' },
|
||||
{ label: 'SOCKS5', value: 'socks5' }
|
||||
]}
|
||||
/>
|
||||
)
|
||||
)
|
||||
key: 'scheme'
|
||||
},
|
||||
{
|
||||
title: '主机',
|
||||
dataIndex: 'host',
|
||||
key: 'host',
|
||||
render: (text: string, record: ProxyConfig) => (
|
||||
record.proxyType === 'fixed_servers' && (
|
||||
<Input
|
||||
value={text}
|
||||
onChange={e => onChange(record.id, 'host', e.target.value)}
|
||||
placeholder="127.0.0.1"
|
||||
/>
|
||||
)
|
||||
)
|
||||
key: 'host'
|
||||
},
|
||||
{
|
||||
title: '端口',
|
||||
dataIndex: 'port',
|
||||
key: 'port',
|
||||
render: (text: number, record: ProxyConfig) => (
|
||||
record.proxyType === 'fixed_servers' && (
|
||||
<InputNumber
|
||||
value={text}
|
||||
onChange={value => onChange(record.id, 'port', value)}
|
||||
min={1}
|
||||
max={65535}
|
||||
/>
|
||||
)
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'PAC 脚本',
|
||||
dataIndex: 'pacScript',
|
||||
key: 'pacScript',
|
||||
render: (text: string, record: ProxyConfig) => (
|
||||
record.proxyType === 'pac_script' && (
|
||||
<Input.TextArea
|
||||
value={text}
|
||||
onChange={e => onChange(record.id, 'pacScript', e.target.value)}
|
||||
rows={4}
|
||||
placeholder="输入 PAC 脚本"
|
||||
/>
|
||||
)
|
||||
)
|
||||
key: 'port'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 120,
|
||||
render: (_, record: ProxyConfig) => (
|
||||
<Space>
|
||||
<Button
|
||||
type={record.enabled ? "primary" : "default"}
|
||||
danger={record.enabled}
|
||||
onClick={() => record.enabled ?
|
||||
onClear(record.id) :
|
||||
onApply(record.id)
|
||||
}
|
||||
>
|
||||
{record.enabled ? '取消应用' : '应用选项'}
|
||||
</Button>
|
||||
<Space size="middle">
|
||||
<CheckOutlined
|
||||
className={`action-icon ${record.enabled ? 'enabled' : ''}`}
|
||||
onClick={async () => {
|
||||
if (record.enabled) {
|
||||
await onClear(record.id);
|
||||
} else {
|
||||
await onApply(record.id);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<EditOutlined
|
||||
className="action-icon"
|
||||
onClick={() => handleEdit(record)}
|
||||
/>
|
||||
{record.id !== 'direct' && (
|
||||
<Button
|
||||
danger
|
||||
icon={<DeleteOutlined />}
|
||||
<DeleteOutlined
|
||||
className="action-icon delete"
|
||||
onClick={() => onDelete(record.id)}
|
||||
/>
|
||||
)}
|
||||
@@ -145,21 +180,129 @@ export const ProxySettings: React.FC<ProxySettingsProps> = ({
|
||||
|
||||
const buttonRef = useRef(null);
|
||||
|
||||
// 过滤掉固定模式的代理
|
||||
const filteredProxyConfigs = proxyConfigs.filter(
|
||||
config => !['direct', 'system'].includes(config.id)
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Space style={{ marginBottom: 16, justifyContent: 'flex-end', width: '100%' }}>
|
||||
<Popover content="添加代理" trigger="hover">
|
||||
<Button ref={buttonRef} onClick={onAdd}>
|
||||
添加代理
|
||||
</Button>
|
||||
</Popover>
|
||||
<Button
|
||||
className="add-proxy-btn"
|
||||
onClick={handleAdd}
|
||||
icon={<PlusOutlined />}
|
||||
>
|
||||
添加代理
|
||||
</Button>
|
||||
</Space>
|
||||
<Table
|
||||
dataSource={proxyConfigs}
|
||||
className="proxy-table"
|
||||
dataSource={filteredProxyConfigs}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
pagination={false}
|
||||
bordered={false}
|
||||
size="middle"
|
||||
/>
|
||||
|
||||
<Modal
|
||||
title="编辑代理配置"
|
||||
open={editModalVisible}
|
||||
onOk={handleEditSave}
|
||||
onCancel={handleModalClose}
|
||||
destroyOnClose
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
preserve={false}
|
||||
>
|
||||
<Form.Item
|
||||
name="name"
|
||||
label="名称"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="proxyType"
|
||||
label="类型"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Select
|
||||
options={[
|
||||
{ label: '直接连接', value: 'direct' },
|
||||
{ label: '代理服务器', value: 'fixed_servers' },
|
||||
{ label: 'PAC 脚本', value: 'pac_script' }
|
||||
]}
|
||||
onChange={(value) => {
|
||||
// 当类型改变时,清除相关字段
|
||||
if (value !== 'fixed_servers') {
|
||||
form.setFieldsValue({
|
||||
scheme: undefined,
|
||||
host: undefined,
|
||||
port: undefined
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
noStyle
|
||||
shouldUpdate={(prevValues, currentValues) => prevValues.proxyType !== currentValues.proxyType}
|
||||
>
|
||||
{({ getFieldValue }) => {
|
||||
const proxyType = getFieldValue('proxyType');
|
||||
return proxyType === 'fixed_servers' ? (
|
||||
<>
|
||||
<Form.Item
|
||||
name="scheme"
|
||||
label="协议"
|
||||
rules={[{ required: true, message: '请选择协议' }]}
|
||||
>
|
||||
<Select
|
||||
options={[
|
||||
{ label: 'HTTP', value: 'http' },
|
||||
{ label: 'HTTPS', value: 'https' },
|
||||
{ label: 'SOCKS4', value: 'socks4' },
|
||||
{ label: 'SOCKS5', value: 'socks5' }
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="host"
|
||||
label="主机"
|
||||
rules={[{ required: true, message: '请输入主机地址' }]}
|
||||
>
|
||||
<Input placeholder="127.0.0.1" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="port"
|
||||
label="端口"
|
||||
rules={[{ required: true, message: '请输入端口号' }]}
|
||||
>
|
||||
<InputNumber
|
||||
min={1}
|
||||
max={65535}
|
||||
placeholder="8080"
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
) : proxyType === 'pac_script' ? (
|
||||
<Form.Item
|
||||
name="pacScript"
|
||||
label="PAC 脚本"
|
||||
rules={[{ required: true, message: '请输入 PAC 脚本' }]}
|
||||
>
|
||||
<Input.TextArea rows={4} />
|
||||
</Form.Item>
|
||||
) : null;
|
||||
}}
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,35 +1,104 @@
|
||||
import React from 'react';
|
||||
import { Layout, Tabs } from 'antd';
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { Layout, Tabs, message } from 'antd';
|
||||
import { ProxySettings } from './components/ProxySettings';
|
||||
import { ProxyLogs } from './components/ProxyLogs';
|
||||
import { useProxyConfigs } from './hooks/useProxyConfigs';
|
||||
import { useProxyLogs } from './hooks/useProxyLogs';
|
||||
import { ProxyConfig } from '@/types/proxy';
|
||||
import { ProxyActionType } from '@/types/action';
|
||||
|
||||
const { Content } = Layout;
|
||||
|
||||
interface ProxySettingsProps {
|
||||
proxyConfigs: ProxyConfig[];
|
||||
onAdd: (config: ProxyConfig) => void;
|
||||
onChange: (configId: string, field: keyof ProxyConfig | 'config', value: any) => void;
|
||||
onDelete: (configId: string) => void;
|
||||
onApply: (configId: string) => Promise<void>;
|
||||
onClear: (configId: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export const OptionsPage: React.FC = () => {
|
||||
const {
|
||||
proxyConfigs,
|
||||
handleAddProxy,
|
||||
handleConfigChange,
|
||||
handleConfigChange: handleConfigChangeHook,
|
||||
handleDeleteProxy,
|
||||
handleApplyConfig,
|
||||
handleClearProxy
|
||||
} = useProxyConfigs();
|
||||
const { proxyLogs, handleClearLogs } = useProxyLogs();
|
||||
const [proxyConfigsState, setProxyConfigs] = useState<ProxyConfig[]>([]);
|
||||
|
||||
const handleAdd = () => {
|
||||
const newConfig: ProxyConfig = {
|
||||
id: Date.now().toString(),
|
||||
name: '新建代理',
|
||||
proxyType: 'fixed_servers',
|
||||
scheme: 'http',
|
||||
host: '127.0.0.1',
|
||||
port: 8080,
|
||||
enabled: false
|
||||
useEffect(() => {
|
||||
setProxyConfigs(proxyConfigs);
|
||||
}, [proxyConfigs]);
|
||||
|
||||
// 通知 background 页面已准备就绪
|
||||
useEffect(() => {
|
||||
chrome.runtime.sendMessage({ action: 'OPTIONS_PAGE_READY' });
|
||||
|
||||
const messageListener = (
|
||||
message: any,
|
||||
sender: chrome.runtime.MessageSender,
|
||||
sendResponse: (response?: any) => void
|
||||
) => {
|
||||
if (message.action === 'TRIGGER_ADD_PROXY') {
|
||||
const proxySettingsElement = document.querySelector('.add-proxy-btn');
|
||||
if (proxySettingsElement) {
|
||||
(proxySettingsElement as HTMLElement).click();
|
||||
}
|
||||
}
|
||||
sendResponse();
|
||||
};
|
||||
handleAddProxy(newConfig);
|
||||
|
||||
chrome.runtime.onMessage.addListener(messageListener);
|
||||
return () => {
|
||||
chrome.runtime.onMessage.removeListener(messageListener);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleAdd = async (config: ProxyConfig) => {
|
||||
try {
|
||||
await handleAddProxy(config);
|
||||
} catch (error) {
|
||||
console.error('Failed to add proxy:', error);
|
||||
message.error('添加代理失败');
|
||||
}
|
||||
};
|
||||
|
||||
const handleConfigChange = async (configId: string, field: keyof ProxyConfig | 'config', value: any) => {
|
||||
try {
|
||||
const updatedConfigs = proxyConfigsState.map(config => {
|
||||
if (config.id === configId) {
|
||||
if (field === 'config') {
|
||||
// 如果是整个配置更新
|
||||
return value;
|
||||
} else {
|
||||
// 如果是单个字段更新
|
||||
return {
|
||||
...config,
|
||||
[field]: value
|
||||
};
|
||||
}
|
||||
}
|
||||
return config;
|
||||
});
|
||||
|
||||
// 更新 IndexedDB
|
||||
await chrome.runtime.sendMessage({
|
||||
action: ProxyActionType.UPDATE_PROXY_CONFIG,
|
||||
configs: updatedConfigs
|
||||
});
|
||||
|
||||
// 更新本地状态
|
||||
setProxyConfigs(updatedConfigs);
|
||||
|
||||
message.success('更新配置成功');
|
||||
} catch (error) {
|
||||
console.error('Failed to update config:', error);
|
||||
message.error('更新配置失败');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -43,7 +112,7 @@ export const OptionsPage: React.FC = () => {
|
||||
label: '代理设置',
|
||||
children: (
|
||||
<ProxySettings
|
||||
proxyConfigs={proxyConfigs}
|
||||
proxyConfigs={proxyConfigsState}
|
||||
onAdd={handleAdd}
|
||||
onChange={handleConfigChange}
|
||||
onDelete={handleDeleteProxy}
|
||||
|
||||
@@ -2261,6 +2261,11 @@
|
||||
resolved "https://mirrors.cloud.tencent.com/npm/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
|
||||
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
|
||||
|
||||
"@types/lodash@^4.17.15":
|
||||
version "4.17.15"
|
||||
resolved "https://mirrors.cloud.tencent.com/npm/@types/lodash/-/lodash-4.17.15.tgz#12d4af0ed17cc7600ce1f9980cec48fc17ad1e89"
|
||||
integrity sha512-w/P33JFeySuhN6JLkysYUK2gEmy9kHHFN7E8ro0tkfmlDOgxBDzWEZ/J8cWA+fHqFevpswDTFZnDx+R9lbL6xw==
|
||||
|
||||
"@types/mime@^1":
|
||||
version "1.3.5"
|
||||
resolved "https://mirrors.cloud.tencent.com/npm/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690"
|
||||
|
||||
Reference in New Issue
Block a user