4 changed files with 93 additions and 11 deletions
+2 -1
View File
@@ -27,4 +27,5 @@ dist/
node_modules/ node_modules/
design.md design.md
agent.md agent.md
api-backend.md api-backend.md
CLAUDE.md
+3 -1
View File
@@ -107,7 +107,9 @@ export const instanceApi = {
modifyProxy: (instanceID, proxyInfo) => modifyProxy: (instanceID, proxyInfo) =>
api.post('/frpcAct/proxyMgr/modify', { instanceID, proxyInfo }), api.post('/frpcAct/proxyMgr/modify', { instanceID, proxyInfo }),
deleteProxy: (instanceID, proxyName) => deleteProxy: (instanceID, proxyName) =>
api.post('/frpcAct/proxyMgr/delete', { instanceID, proxyName }) api.post('/frpcAct/proxyMgr/delete', { instanceID, proxyName }),
switchProxy: (instanceID, proxyName, action) =>
api.post('/frpcAct/proxyMgr/switch', { instanceID, proxyName, action })
}; };
export const logApi = { export const logApi = {
+87 -8
View File
@@ -172,20 +172,30 @@
</button> </button>
</h3> </h3>
<div v-if="proxies.length > 0" class="proxy-list"> <div v-if="proxies.length > 0" class="proxy-list">
<div v-for="proxy in proxies" :key="proxy.name" class="proxy-item"> <div v-for="proxy in proxies" :key="proxy.name" class="proxy-item" :class="{ 'proxy-disabled': !proxy.enabled }">
<div class="proxy-header"> <div class="proxy-header">
<span class="proxy-name">{{ proxy.name }}</span> <div class="proxy-header-left">
<span class="proxy-name">{{ proxy.name }}</span>
<label class="toggle-switch" @click.stop>
<input
type="checkbox"
:checked="proxy.enabled"
@change="toggleProxy(proxy)"
>
<span class="toggle-slider"></span>
</label>
</div>
<div class="proxy-actions-right"> <div class="proxy-actions-right">
<span class="proxy-type">{{ proxy.type }}</span> <span class="proxy-type">{{ proxy.type }}</span>
<div class="proxy-actions"> <div class="proxy-actions">
<button <button
class="common-btn edit-btn" class="common-btn edit-btn"
@click="editProxy(proxy)" @click="editProxy(proxy)"
> >
<i class="fas fa-edit"></i> Edit <i class="fas fa-edit"></i> Edit
</button> </button>
<button <button
class="common-btn delete-btn" class="common-btn delete-btn"
@click="deleteProxy(proxy)" @click="deleteProxy(proxy)"
> >
<i class="fas fa-trash"></i> Delete <i class="fas fa-trash"></i> Delete
@@ -442,7 +452,8 @@ export default {
type: proxy.type, type: proxy.type,
localIP: proxy.localIP, localIP: proxy.localIP,
localPort: proxy.localPort, localPort: proxy.localPort,
remotePort: proxy.remotePort remotePort: proxy.remotePort,
enabled: proxy.enabled !== undefined ? proxy.enabled : true
})); }));
} catch (error) { } catch (error) {
showNotification(error.message || 'Load proxy list failed', 'error'); showNotification(error.message || 'Load proxy list failed', 'error');
@@ -581,6 +592,17 @@ export default {
showDeleteProxyModal.value = true; showDeleteProxyModal.value = true;
} }
const toggleProxy = async (proxy) => {
const action = proxy.enabled ? 0 : 1;
try {
const result = await instanceApi.switchProxy(+instanceID.value, proxy.name, action);
showNotification(result.message || `Proxy "${proxy.name}" ${proxy.enabled ? 'disabled' : 'enabled'}`, 'success');
await loadProxies();
} catch (error) {
showNotification(error.message || 'Failed to toggle proxy', 'error');
}
};
const closeDeleteProxyModal = () => { const closeDeleteProxyModal = () => {
showDeleteProxyModal.value = false; showDeleteProxyModal.value = false;
selectedProxyName.value = ''; selectedProxyName.value = '';
@@ -601,8 +623,8 @@ export default {
try { try {
const result = await instanceApi.deleteProxy(instanceID.value, selectedProxyName.value); const result = await instanceApi.deleteProxy(instanceID.value, selectedProxyName.value);
showNotification(result.message || 'Proxy deleted successfully', 'success'); showNotification(result.message || 'Proxy deleted successfully', 'success');
closeDeleteProxyModal();
await loadProxies(); await loadProxies();
closeDeleteProxyModal();
} catch (error) { } catch (error) {
showNotification(error.message || 'Delete proxy failed', 'error'); showNotification(error.message || 'Delete proxy failed', 'error');
} finally { } finally {
@@ -751,6 +773,7 @@ export default {
addProxy, addProxy,
editProxy, editProxy,
deleteProxy, deleteProxy,
toggleProxy,
closeAddProxyModal, closeAddProxyModal,
closeDeleteProxyModal, closeDeleteProxyModal,
confirmDeleteProxy, confirmDeleteProxy,
@@ -810,6 +833,12 @@ export default {
gap: 12px; gap: 12px;
} }
.proxy-header-left {
display: flex;
align-items: center;
gap: 10px;
}
.proxy-name { .proxy-name {
font-family: 'SF Pro Display', 'SF Pro Icons', 'Helvetica Neue', Helvetica, Arial, sans-serif; font-family: 'SF Pro Display', 'SF Pro Icons', 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 18px; font-size: 18px;
@@ -1204,4 +1233,54 @@ export default {
.section:last-child .logs-container::-webkit-scrollbar-thumb:hover { .section:last-child .logs-container::-webkit-scrollbar-thumb:hover {
background: rgba(0, 0, 0, 0.3); background: rgba(0, 0, 0, 0.3);
} }
.toggle-switch {
position: relative;
display: inline-block;
width: 44px;
height: 24px;
cursor: pointer;
flex-shrink: 0;
}
.toggle-switch input {
opacity: 0;
width: 0;
height: 0;
}
.toggle-slider {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: var(--surface-hover);
border-radius: 24px;
transition: background 0.3s;
}
.toggle-slider::before {
content: '';
position: absolute;
height: 18px;
width: 18px;
left: 3px;
bottom: 3px;
background: white;
border-radius: 50%;
transition: transform 0.3s;
}
.toggle-switch input:checked + .toggle-slider {
background: var(--primary-color);
}
.toggle-switch input:checked + .toggle-slider::before {
transform: translateX(20px);
}
.proxy-disabled {
opacity: 0.48;
}
</style> </style>
+1 -1
View File
@@ -1,7 +1,7 @@
import { defineConfig } from 'vite' import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue' import vue from '@vitejs/plugin-vue'
const APP_TARGET = '192.168.1.4:7000' const APP_TARGET = '192.168.20.4:19090'
export default defineConfig({ export default defineConfig({
define: { define: {