mirror of
https://github.com/yaklang/yaklang-chrome-extension.git
synced 2026-09-22 03:10:43 +08:00
fix ui
This commit is contained in:
@@ -8,7 +8,7 @@
|
|||||||
"open_in_tab": true
|
"open_in_tab": true
|
||||||
},
|
},
|
||||||
"action": {
|
"action": {
|
||||||
"default_title": "Click to open panel",
|
"default_popup": "index.html",
|
||||||
"default_icon": {
|
"default_icon": {
|
||||||
"16": "/images/icon16.png",
|
"16": "/images/icon16.png",
|
||||||
"48": "/images/icon48.png",
|
"48": "/images/icon48.png",
|
||||||
|
|||||||
@@ -27,12 +27,20 @@ export class ProxySettings {
|
|||||||
const configs = await proxyStore.getProxyConfigs();
|
const configs = await proxyStore.getProxyConfigs();
|
||||||
if (!configs || configs.length === 0) {
|
if (!configs || configs.length === 0) {
|
||||||
// 设置默认的直接连接配置
|
// 设置默认的直接连接配置
|
||||||
await proxyStore.saveProxyConfigs([{
|
await proxyStore.saveProxyConfigs([
|
||||||
id: 'direct',
|
{
|
||||||
name: '直接连接',
|
id: 'direct',
|
||||||
proxyType: 'direct',
|
name: '直接连接',
|
||||||
enabled: false
|
proxyType: 'direct',
|
||||||
}]);
|
enabled: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'system',
|
||||||
|
name: '系统代理',
|
||||||
|
proxyType: 'system',
|
||||||
|
enabled: false
|
||||||
|
}
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
// 确保日志存储已初始化
|
// 确保日志存储已初始化
|
||||||
const logs = await proxyStore.getLogs(); // 使用 getLogs 而不是 getProxyLogs
|
const logs = await proxyStore.getLogs(); // 使用 getLogs 而不是 getProxyLogs
|
||||||
|
|||||||
-44
@@ -1,44 +0,0 @@
|
|||||||
.App {
|
|
||||||
width: 420px;
|
|
||||||
border-radius: 0px 0px 4px 4px;
|
|
||||||
border-right: 1px solid #EAECF3;
|
|
||||||
border-bottom: 1px solid #EAECF3;
|
|
||||||
border-left: 1px solid #EAECF3;
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.App-logo {
|
|
||||||
height: 40vmin;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
|
||||||
.App-logo {
|
|
||||||
animation: App-logo-spin infinite 20s linear;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.App-header {
|
|
||||||
background-color: #282c34;
|
|
||||||
min-height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: calc(10px + 2vmin);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.App-link {
|
|
||||||
color: #61dafb;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes App-logo-spin {
|
|
||||||
from {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+2
-1
@@ -1,10 +1,11 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import "./App.css";
|
// import "./App.css";
|
||||||
import {ConfigProvider} from "antd";
|
import {ConfigProvider} from "antd";
|
||||||
import {Contro} from "@components/Contro";
|
import {Contro} from "@components/Contro";
|
||||||
import {Proxifier} from "@components/Proxifier";
|
import {Proxifier} from "@components/Proxifier";
|
||||||
import {EvalInTab} from "@components/EvalInTab";
|
import {EvalInTab} from "@components/EvalInTab";
|
||||||
import {ProxySwitch} from "@components/ProxySwitch";
|
import {ProxySwitch} from "@components/ProxySwitch";
|
||||||
|
import './styles/global.css';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,68 +1,180 @@
|
|||||||
.proxy-switch {
|
.proxy-container {
|
||||||
padding: 16px;
|
min-width: 200px;
|
||||||
background: #fff;
|
background: white;
|
||||||
border-radius: 4px;
|
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.proxy-switch-header {
|
.proxy-menu {
|
||||||
display: flex;
|
border: none !important;
|
||||||
justify-content: space-between;
|
box-shadow: none !important;
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.proxy-switch .ant-select {
|
.menu-item {
|
||||||
margin-right: 8px;
|
height: 40px !important;
|
||||||
|
line-height: 40px !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
padding: 0 16px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.proxy-switch .ant-space {
|
.menu-item .anticon {
|
||||||
gap: 8px;
|
font-size: 16px;
|
||||||
|
color: var(--yakit-primary);
|
||||||
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.proxy-switch-title {
|
.menu-item-label {
|
||||||
display: flex;
|
font-size: 14px;
|
||||||
align-items: center;
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
.proxy-switch-title-text {
|
.menu-item:hover {
|
||||||
color: #31343F;
|
background-color: var(--yakit-primary-5) !important;
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.proxy-switch-content {
|
.menu-item:hover .anticon,
|
||||||
display: flex;
|
.menu-item:hover .menu-item-label {
|
||||||
align-items: center;
|
color: var(--yakit-primary) !important;
|
||||||
justify-content: space-between;
|
|
||||||
padding: 12px 16px;
|
|
||||||
background: #F8F8F8;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.proxy-switch-info {
|
/* 选中状态 */
|
||||||
display: flex;
|
.menu-item.ant-menu-item-selected {
|
||||||
flex-direction: column;
|
background-color: var(--yakit-primary) !important;
|
||||||
gap: 4px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.proxy-switch-status {
|
.menu-item.ant-menu-item-selected .anticon,
|
||||||
color: #85899E;
|
.menu-item.ant-menu-item-selected .menu-item-label {
|
||||||
font-size: 12px;
|
color: white !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.proxy-switch-proxy {
|
.menu-item.ant-menu-item-selected:hover {
|
||||||
color: #31343F;
|
background-color: var(--yakit-primary-hover) !important;
|
||||||
font-size: 13px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.proxy-switch-status-active {
|
/* 分隔线 */
|
||||||
color: var(--yakit-primary);
|
.ant-menu-item-divider {
|
||||||
|
margin: 4px 0 !important;
|
||||||
|
border-color: #EAECF3 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.proxy-switch-actions {
|
/* 设置选项 */
|
||||||
display: flex;
|
.menu-item-setting {
|
||||||
align-items: center;
|
border-top: 1px solid #EAECF3;
|
||||||
gap: 8px;
|
margin-top: 4px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item-setting .anticon {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item-setting:hover {
|
||||||
|
background-color: var(--yakit-primary-5) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item-setting:hover .anticon,
|
||||||
|
.menu-item-setting:hover .menu-item-label {
|
||||||
|
color: var(--yakit-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 调整图标大小和对齐 */
|
||||||
|
.anticon {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 添加以下样式来确保下拉菜单显示在正确的位置 */
|
||||||
|
.ant-dropdown {
|
||||||
|
position: absolute !important;
|
||||||
|
top: 100% !important;
|
||||||
|
left: 0 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
min-width: 200px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-content {
|
||||||
|
background: white;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 3px 6px -4px rgba(0,0,0,0.12),
|
||||||
|
0 6px 16px 0 rgba(0,0,0,0.08),
|
||||||
|
0 9px 28px 8px rgba(0,0,0,0.05);
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 确保容器不会限制弹出层 */
|
||||||
|
.proxy-container {
|
||||||
|
min-width: 200px;
|
||||||
|
background: white;
|
||||||
|
border-radius: 4px;
|
||||||
|
position: static;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 添加这个样式来确保下拉菜单显示在正确的位置 */
|
||||||
|
body {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-menu {
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.15) !important;
|
||||||
|
padding: 4px 0 !important;
|
||||||
|
width: 180px !important;
|
||||||
|
background: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-menu-item {
|
||||||
|
height: 28px !important;
|
||||||
|
line-height: 28px !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
padding: 0 16px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-menu-item:hover {
|
||||||
|
background-color: #f5f5f5 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-icon {
|
||||||
|
margin-right: 8px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item-selected {
|
||||||
|
background-color: #e6f7ff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-menu-item-divider {
|
||||||
|
margin: 4px 0 !important;
|
||||||
|
height: 1px !important;
|
||||||
|
background-color: #f0f0f0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-menu-item:last-child {
|
||||||
|
margin-top: 4px !important;
|
||||||
|
border-top: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 移除多余的样式 */
|
||||||
|
.ant-menu-root {
|
||||||
|
background: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 调整整体容器大小 */
|
||||||
|
.ant-menu-root {
|
||||||
|
width: 180px !important;
|
||||||
|
min-height: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 添加代理按钮样式 */
|
||||||
|
.menu-item-add {
|
||||||
|
color: #666 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item-add:hover {
|
||||||
|
background-color: #f5f5f5 !important;
|
||||||
|
color: var(--yakit-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item-add .anticon {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item-add:hover .anticon {
|
||||||
|
color: var(--yakit-primary);
|
||||||
}
|
}
|
||||||
@@ -1,206 +1,54 @@
|
|||||||
import React, {useEffect, useState} from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import {Select, Button, Tooltip, Modal, Form, Input, Radio, Space, message, Switch} from "antd";
|
import {Menu} from "antd";
|
||||||
import {PlusOutlined, SettingOutlined} from "@ant-design/icons";
|
import {GlobalOutlined, DisconnectOutlined, SettingOutlined, EditOutlined, PlusOutlined} from "@ant-design/icons";
|
||||||
import {ProxyConfig} from "@/types/proxy";
|
|
||||||
import {StorageChanges} from "@/types/chrome";
|
|
||||||
import {ProxyActionType} from '@/types/action';
|
import {ProxyActionType} from '@/types/action';
|
||||||
import "./index.css";
|
import "./index.css";
|
||||||
|
import type { MenuProps } from 'antd';
|
||||||
|
import type { ProxyConfig } from '@/types/proxy';
|
||||||
|
|
||||||
interface ProxySwitchProps {
|
// 固定的代理模式
|
||||||
onChange?: (checked: boolean) => void;
|
const FIXED_MODES = [
|
||||||
|
{
|
||||||
|
key: 'direct',
|
||||||
|
name: '[直接连接]',
|
||||||
|
icon: <DisconnectOutlined />,
|
||||||
|
color: '#666',
|
||||||
|
config: {
|
||||||
|
id: 'direct',
|
||||||
|
name: '[直接连接]',
|
||||||
|
proxyType: 'direct'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'system',
|
||||||
|
name: '[系统代理]',
|
||||||
|
icon: <SettingOutlined />,
|
||||||
|
color: '#666',
|
||||||
|
config: {
|
||||||
|
id: 'system',
|
||||||
|
name: '[系统代理]',
|
||||||
|
proxyType: 'system'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
interface CustomProxy {
|
||||||
|
key: string;
|
||||||
|
name: string;
|
||||||
|
color: string;
|
||||||
|
config: ProxyConfig;
|
||||||
|
enabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ProxySwitch: React.FC<ProxySwitchProps> = ({ onChange }) => {
|
export const ProxySwitch: React.FC = () => {
|
||||||
const [currentMode, setCurrentMode] = useState<string>("direct");
|
const [currentMode, setCurrentMode] = useState<string>('direct');
|
||||||
const [proxyConfigs, setProxyConfigs] = useState<ProxyConfig[]>([]);
|
const [customProxies, setCustomProxies] = useState<CustomProxy[]>([]);
|
||||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
|
||||||
const [form] = Form.useForm();
|
|
||||||
const [proxyHost, setProxyHost] = useState('');
|
|
||||||
const [proxyPort, setProxyPort] = useState('');
|
|
||||||
const [enabled, setEnabled] = useState(false);
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
loadConfigs().catch(error => {
|
|
||||||
console.error("Failed to load configs:", error);
|
|
||||||
// 可以在这里添加错误提示
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (proxyConfigs.length > 0) {
|
|
||||||
applyProxySettings(currentMode).catch(error => {
|
|
||||||
console.error("Failed to apply proxy settings:", error);
|
|
||||||
// 可以在这里添加错误提示
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [proxyConfigs, currentMode]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleStorageChange = (changes: StorageChanges) => {
|
|
||||||
if (changes.proxyConfigs) {
|
|
||||||
const newConfigs = changes.proxyConfigs.newValue;
|
|
||||||
setProxyConfigs(newConfigs);
|
|
||||||
const currentConfig = newConfigs.find((c: ProxyConfig) => c.id === currentMode);
|
|
||||||
if (!currentConfig || !currentConfig.enabled) {
|
|
||||||
setCurrentMode("direct");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
chrome.storage.onChanged.addListener(handleStorageChange);
|
|
||||||
return () => chrome.storage.onChanged.removeListener(handleStorageChange);
|
|
||||||
}, [currentMode]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadProxyStatus();
|
loadProxyStatus();
|
||||||
|
loadCustomProxies();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const loadConfigs = async () => {
|
|
||||||
try {
|
|
||||||
const result = await chrome.storage.local.get('proxyConfigs');
|
|
||||||
if (!result.proxyConfigs) {
|
|
||||||
const defaultConfigs: ProxyConfig[] = [
|
|
||||||
{
|
|
||||||
id: "direct",
|
|
||||||
name: "直接连接",
|
|
||||||
proxyType: "direct" as const,
|
|
||||||
enabled: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "default",
|
|
||||||
name: "默认代理",
|
|
||||||
proxyType: "fixed_server" as const,
|
|
||||||
host: "127.0.0.1",
|
|
||||||
port: 8080,
|
|
||||||
scheme: "http",
|
|
||||||
enabled: false
|
|
||||||
}
|
|
||||||
];
|
|
||||||
await chrome.storage.local.set({proxyConfigs: defaultConfigs});
|
|
||||||
setProxyConfigs(defaultConfigs);
|
|
||||||
setCurrentMode("direct");
|
|
||||||
} else {
|
|
||||||
setProxyConfigs(result.proxyConfigs);
|
|
||||||
const currentConfig = result.proxyConfigs.find((c: ProxyConfig) => c.id === currentMode);
|
|
||||||
if (!currentConfig || !currentConfig.enabled) {
|
|
||||||
setCurrentMode("direct");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error in loadConfigs:", error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const applyProxySettings = async (modeId: string) => {
|
|
||||||
try {
|
|
||||||
const config = proxyConfigs.find((c: ProxyConfig) => c.id === modeId);
|
|
||||||
if (!config || !config.enabled) return false;
|
|
||||||
|
|
||||||
if (config.proxyType === "direct") {
|
|
||||||
chrome.proxy.settings.clear({scope: 'regular'});
|
|
||||||
return true;
|
|
||||||
} else if (config.proxyType === "fixed_server") {
|
|
||||||
chrome.proxy.settings.set({
|
|
||||||
value: {
|
|
||||||
mode: "fixed_servers",
|
|
||||||
rules: {
|
|
||||||
singleProxy: {
|
|
||||||
scheme: config.scheme,
|
|
||||||
host: config.host,
|
|
||||||
port: Number(config.port)
|
|
||||||
},
|
|
||||||
bypassList: ["<-loopback>"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
scope: "regular"
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
} else if (config.proxyType === "pac_script") {
|
|
||||||
chrome.proxy.settings.set({
|
|
||||||
value: {
|
|
||||||
mode: "pac_script",
|
|
||||||
pacScript: {
|
|
||||||
data: config.pacScript
|
|
||||||
}
|
|
||||||
},
|
|
||||||
scope: "regular"
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Failed to apply proxy settings:", error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleAddProxy = () => {
|
|
||||||
form.resetFields();
|
|
||||||
setIsModalVisible(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleModalOk = () => {
|
|
||||||
form.validateFields().then(async values => {
|
|
||||||
try {
|
|
||||||
const newConfig: ProxyConfig = {
|
|
||||||
id: Date.now().toString(),
|
|
||||||
name: values.name,
|
|
||||||
proxyType: values.proxyType as "direct" | "fixed_server" | "pac_script" | "auto_detect",
|
|
||||||
host: values.host,
|
|
||||||
port: values.port ? parseInt(values.port) : undefined,
|
|
||||||
scheme: values.scheme,
|
|
||||||
enabled: true,
|
|
||||||
};
|
|
||||||
const updatedConfigs = [...proxyConfigs, newConfig];
|
|
||||||
await chrome.storage.local.set({proxyConfigs: updatedConfigs});
|
|
||||||
setProxyConfigs(updatedConfigs);
|
|
||||||
setIsModalVisible(false);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Failed to save new proxy config:", error);
|
|
||||||
// 可以在这里添加错误提示
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSettingClick = () => {
|
|
||||||
if (chrome.runtime.openOptionsPage) {
|
|
||||||
chrome.runtime.openOptionsPage();
|
|
||||||
} else {
|
|
||||||
chrome.tabs.create({
|
|
||||||
url: chrome.runtime.getURL('proxy/options.html')
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleApplyProxy = async () => {
|
|
||||||
try {
|
|
||||||
const response = await chrome.runtime.sendMessage({
|
|
||||||
action: 'SET_PROXY_CONFIG',
|
|
||||||
config: {
|
|
||||||
host: proxyHost,
|
|
||||||
port: parseInt(proxyPort),
|
|
||||||
scheme: 'http',
|
|
||||||
proxyType: 'fixed_server',
|
|
||||||
id: 'default',
|
|
||||||
name: '默认代理'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
message.success('代理设置已应用');
|
|
||||||
} else {
|
|
||||||
message.error(response.error || '代理设置失败');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error applying proxy:', error);
|
|
||||||
message.error('应用代理设置时发生错误');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const proxyType: ProxyConfig['proxyType'] = proxyConfigs.find((c: ProxyConfig) => c.id === currentMode)?.proxyType || 'direct';
|
|
||||||
|
|
||||||
const loadProxyStatus = async () => {
|
const loadProxyStatus = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await chrome.runtime.sendMessage({
|
const response = await chrome.runtime.sendMessage({
|
||||||
@@ -208,157 +56,124 @@ export const ProxySwitch: React.FC<ProxySwitchProps> = ({ onChange }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
setEnabled(response.data.enabled);
|
const activeMode = response.data.mode;
|
||||||
|
setCurrentMode(activeMode);
|
||||||
|
|
||||||
|
if (FIXED_MODES.some(mode => mode.key === activeMode)) {
|
||||||
|
setCurrentMode(activeMode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setLoading(false);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading proxy status:', error);
|
console.error('Error loading proxy status:', error);
|
||||||
setLoading(false);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChange = async (checked: boolean) => {
|
const loadCustomProxies = async () => {
|
||||||
try {
|
try {
|
||||||
if (checked) {
|
const response = await chrome.runtime.sendMessage({
|
||||||
// 获取配置列表
|
action: ProxyActionType.GET_PROXY_CONFIGS
|
||||||
const configResponse = await chrome.runtime.sendMessage({
|
});
|
||||||
action: ProxyActionType.GET_PROXY_CONFIGS
|
|
||||||
});
|
if (response.success && response.data) {
|
||||||
|
const proxies = response.data
|
||||||
if (!configResponse.success) {
|
.filter((proxy: ProxyConfig) => !FIXED_MODES.some(mode => mode.key === proxy.id))
|
||||||
throw new Error(configResponse.error || '获取代理配置失败');
|
.map((proxy: ProxyConfig): CustomProxy => ({
|
||||||
}
|
key: proxy.id,
|
||||||
|
name: proxy.name,
|
||||||
|
color: '#1890ff',
|
||||||
|
config: proxy,
|
||||||
|
enabled: proxy.enabled
|
||||||
|
}));
|
||||||
|
setCustomProxies(proxies);
|
||||||
|
|
||||||
const configs = configResponse.data || [];
|
const enabledProxy = proxies.find((proxy: ProxyConfig) => proxy.enabled);
|
||||||
const defaultConfig = configs.find((c: ProxyConfig) => c.id === 'direct');
|
if (enabledProxy) {
|
||||||
|
setCurrentMode(enabledProxy.key);
|
||||||
if (defaultConfig) {
|
|
||||||
const response = await chrome.runtime.sendMessage({
|
|
||||||
action: ProxyActionType.SET_PROXY_CONFIG,
|
|
||||||
config: defaultConfig
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setEnabled(true);
|
|
||||||
onChange?.(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const response = await chrome.runtime.sendMessage({
|
|
||||||
action: ProxyActionType.CLEAR_PROXY_CONFIG
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setEnabled(false);
|
|
||||||
onChange?.(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error toggling proxy:', error);
|
console.error('Error loading custom proxies:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleApplyConfig = async (mode: string) => {
|
||||||
|
try {
|
||||||
|
const fixedMode = FIXED_MODES.find(fixed => fixed.key === mode);
|
||||||
|
const customProxy = customProxies.find(proxy => proxy.key === mode);
|
||||||
|
|
||||||
|
const config = fixedMode?.config || customProxy?.config;
|
||||||
|
|
||||||
|
if (!config) {
|
||||||
|
console.error('No config found for mode:', mode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await chrome.runtime.sendMessage({
|
||||||
|
action: ProxyActionType.SET_PROXY_CONFIG,
|
||||||
|
config
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
setCurrentMode(mode);
|
||||||
|
await loadCustomProxies();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to apply proxy config:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleModeChange = async (mode: string) => {
|
||||||
|
if (mode === 'setting') {
|
||||||
|
chrome.runtime.openOptionsPage?.();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode === 'add') {
|
||||||
|
chrome.runtime.openOptionsPage?.();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await handleApplyConfig(mode);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to change proxy mode:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const menuItems: MenuProps['items'] = [
|
||||||
|
...FIXED_MODES.map(mode => ({
|
||||||
|
key: mode.key,
|
||||||
|
icon: <span className="menu-icon" style={{ color: mode.color }}>{mode.icon}</span>,
|
||||||
|
label: mode.name,
|
||||||
|
className: currentMode === mode.key ? 'menu-item-selected' : ''
|
||||||
|
})),
|
||||||
|
{ type: 'divider' },
|
||||||
|
...customProxies.map(proxy => ({
|
||||||
|
key: proxy.key,
|
||||||
|
icon: <span className="menu-icon" style={{ color: proxy.color }}><GlobalOutlined /></span>,
|
||||||
|
label: <span style={{ color: proxy.enabled ? 'var(--yakit-primary)' : 'inherit' }}>{proxy.name}</span>,
|
||||||
|
className: proxy.enabled ? 'menu-item-selected' : ''
|
||||||
|
})),
|
||||||
|
{
|
||||||
|
key: 'add',
|
||||||
|
icon: <PlusOutlined />,
|
||||||
|
label: '添加代理...',
|
||||||
|
className: 'menu-item-add'
|
||||||
|
},
|
||||||
|
{ type: 'divider' },
|
||||||
|
{
|
||||||
|
key: 'setting',
|
||||||
|
icon: <EditOutlined />,
|
||||||
|
label: '选项'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="proxy-switch">
|
<Menu
|
||||||
<div className="proxy-switch-header">
|
items={menuItems}
|
||||||
<Select
|
selectedKeys={[currentMode]}
|
||||||
value={currentMode}
|
onClick={({ key }) => handleModeChange(key)}
|
||||||
onChange={setCurrentMode}
|
style={{ width: 180 }}
|
||||||
style={{width: 200}}
|
/>
|
||||||
options={proxyConfigs.filter((config: ProxyConfig) => config.enabled).map((config: ProxyConfig) => ({
|
|
||||||
label: config.name,
|
|
||||||
value: config.id
|
|
||||||
}))}
|
|
||||||
/>
|
|
||||||
<Space>
|
|
||||||
<Tooltip title="添加代理">
|
|
||||||
<Button
|
|
||||||
icon={<PlusOutlined/>}
|
|
||||||
onClick={handleAddProxy}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip title="代理设置">
|
|
||||||
<Button
|
|
||||||
icon={<SettingOutlined/>}
|
|
||||||
onClick={handleSettingClick}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</Space>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Modal
|
|
||||||
title="添加代理配置"
|
|
||||||
open={isModalVisible}
|
|
||||||
onOk={handleModalOk}
|
|
||||||
onCancel={() => setIsModalVisible(false)}
|
|
||||||
>
|
|
||||||
<Form
|
|
||||||
form={form}
|
|
||||||
layout="vertical"
|
|
||||||
>
|
|
||||||
<Form.Item
|
|
||||||
name="name"
|
|
||||||
label="配置名称"
|
|
||||||
rules={[{required: true}]}
|
|
||||||
>
|
|
||||||
<Input/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name="proxyType"
|
|
||||||
label="代理类型"
|
|
||||||
rules={[{required: true}]}
|
|
||||||
>
|
|
||||||
<Radio.Group>
|
|
||||||
<Radio value="direct">直接连接</Radio>
|
|
||||||
<Radio value="fixed_server">代理服务器</Radio>
|
|
||||||
<Radio value="pac_script">PAC 脚本</Radio>
|
|
||||||
</Radio.Group>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
noStyle
|
|
||||||
shouldUpdate={(prev, curr) => prev.proxyType !== curr.proxyType}
|
|
||||||
>
|
|
||||||
{({getFieldValue}) =>
|
|
||||||
getFieldValue('proxyType') === 'fixed_server' && (
|
|
||||||
<>
|
|
||||||
<Form.Item
|
|
||||||
name="scheme"
|
|
||||||
label="代理协议"
|
|
||||||
rules={[{required: true}]}
|
|
||||||
>
|
|
||||||
<Select>
|
|
||||||
<Select.Option value="http">HTTP</Select.Option>
|
|
||||||
<Select.Option value="https">HTTPS</Select.Option>
|
|
||||||
<Select.Option value="socks4">SOCKS4</Select.Option>
|
|
||||||
<Select.Option value="socks5">SOCKS5</Select.Option>
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name="host"
|
|
||||||
label="代理服务器"
|
|
||||||
rules={[{required: true}]}
|
|
||||||
>
|
|
||||||
<Input/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name="port"
|
|
||||||
label="端口"
|
|
||||||
rules={[{required: true}]}
|
|
||||||
>
|
|
||||||
<Input type="number"/>
|
|
||||||
</Form.Item>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
<Switch
|
|
||||||
checked={enabled}
|
|
||||||
onChange={handleChange}
|
|
||||||
loading={loading}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
+7
-19
@@ -4,11 +4,13 @@ html:root {
|
|||||||
|
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||||
sans-serif;
|
sans-serif;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
*,
|
*,
|
||||||
@@ -17,23 +19,9 @@ body {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
#root {
|
||||||
/*滚动条整体样式*/
|
width: 180px;
|
||||||
width: 8px;
|
height: fit-content;
|
||||||
/*高宽分别对应横竖滚动条的尺寸*/
|
background: white;
|
||||||
height: 8px;
|
overflow: hidden;
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
|
||||||
/*滚动条里面小方块*/
|
|
||||||
border-radius: 10px;
|
|
||||||
box-shadow: inset 0 0 5px rgba(193, 193, 193);
|
|
||||||
background: #c1c1c1;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-track {
|
|
||||||
/*滚动条里面轨道*/
|
|
||||||
box-shadow: inset 0 0 5px rgba(193, 193, 193);
|
|
||||||
border-radius: 10px;
|
|
||||||
background: #ededed;
|
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-1
@@ -2,8 +2,18 @@
|
|||||||
--yakit-primary: #F28B44;
|
--yakit-primary: #F28B44;
|
||||||
--yakit-primary-hover: #f4a061;
|
--yakit-primary-hover: #f4a061;
|
||||||
--yakit-primary-active: #e87633;
|
--yakit-primary-active: #e87633;
|
||||||
--yakit-primary-5: rgba(242, 139, 68, 0.05);
|
--yakit-primary-5: #fff5eb;
|
||||||
--yakit-primary-10: rgba(242, 139, 68, 0.1);
|
--yakit-primary-10: rgba(242, 139, 68, 0.1);
|
||||||
|
|
||||||
|
/* 添加其他全局变量 */
|
||||||
|
--border-color: #f0f0f0;
|
||||||
|
--text-color: #333;
|
||||||
|
--icon-color: #666;
|
||||||
|
|
||||||
|
/* 菜单相关变量 */
|
||||||
|
--menu-item-height: 28px;
|
||||||
|
--menu-padding: 4px;
|
||||||
|
--menu-width: 180px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-btn-primary {
|
.ant-btn-primary {
|
||||||
@@ -34,4 +44,15 @@
|
|||||||
.ant-btn:not(.ant-btn-primary):hover {
|
.ant-btn:not(.ant-btn-primary):hover {
|
||||||
color: var(--yakit-primary) !important;
|
color: var(--yakit-primary) !important;
|
||||||
border-color: var(--yakit-primary) !important;
|
border-color: var(--yakit-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 移除所有滚动条 */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 确保所有内容都在视口内 */
|
||||||
|
html, body {
|
||||||
|
overflow: hidden;
|
||||||
|
height: fit-content;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user