✨ 增加快捷选择本地端口
This commit is contained in:
parent
80d0b53759
commit
ef1b8c186e
70
electron/api/local.ts
Normal file
70
electron/api/local.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import {ipcMain} from "electron";
|
||||||
|
import log from "electron-log";
|
||||||
|
|
||||||
|
const {exec, spawn} = require("child_process");
|
||||||
|
|
||||||
|
type LocalPort = {
|
||||||
|
protocol: string;
|
||||||
|
ip: string;
|
||||||
|
port: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const initLocalApi = () => {
|
||||||
|
const command = process.platform === 'win32'
|
||||||
|
? 'netstat -a -n'
|
||||||
|
: 'netstat -tuln';
|
||||||
|
|
||||||
|
ipcMain.on("local.getLocalPorts", async (event, args) => {
|
||||||
|
log.info("开始获取本地端口")
|
||||||
|
// 执行命令
|
||||||
|
exec(command, (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
log.error(`getLocalPorts - error ${error.message}`)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (stderr) {
|
||||||
|
log.error(`getLocalPorts - stderr ${stderr}`)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug(`sc ${stdout}`)
|
||||||
|
let ports = [];
|
||||||
|
if (stdout) {
|
||||||
|
ports = stdout.split('\r\n')
|
||||||
|
.filter(f => f.indexOf('TCP') > 0 || f.indexOf('UDP') > 0)
|
||||||
|
.map(m => {
|
||||||
|
const cols = m.split(' ')
|
||||||
|
.filter(f => f != '')
|
||||||
|
const local = cols[1]
|
||||||
|
const s = local.lastIndexOf(":")
|
||||||
|
let localIP = local.slice(0, s);
|
||||||
|
let localPort = local.slice(s - local.length + 1);
|
||||||
|
console.log(1)
|
||||||
|
// if (local.indexOf('[') == -1) {
|
||||||
|
// // ipv4
|
||||||
|
// const tmp = cols[1].split(":")
|
||||||
|
// localIP = tmp[0]
|
||||||
|
// localPort = tmp[1]
|
||||||
|
// } else {
|
||||||
|
// // ipv6
|
||||||
|
// console.log(1)
|
||||||
|
// }
|
||||||
|
|
||||||
|
const singe: LocalPort = {
|
||||||
|
protocol: cols[0],
|
||||||
|
ip: localIP,
|
||||||
|
port: localPort
|
||||||
|
}
|
||||||
|
|
||||||
|
return singe;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ports.sort((a, b) => a.port - b.port);
|
||||||
|
|
||||||
|
event.reply("local.getLocalPorts.hook", {
|
||||||
|
data: ports
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -7,10 +7,10 @@ import {initProxyApi} from "../api/proxy";
|
|||||||
import {initFrpcApi, startFrpWorkerProcess, stopFrpcProcess} from "../api/frpc";
|
import {initFrpcApi, startFrpWorkerProcess, stopFrpcProcess} from "../api/frpc";
|
||||||
import {initLoggerApi} from "../api/logger";
|
import {initLoggerApi} from "../api/logger";
|
||||||
import {initFileApi} from "../api/file";
|
import {initFileApi} from "../api/file";
|
||||||
import {initUpdaterApi} from "../api/update";
|
|
||||||
import {getConfig} from "../storage/config";
|
import {getConfig} from "../storage/config";
|
||||||
import log from "electron-log";
|
import log from "electron-log";
|
||||||
import {initCommonApi} from "../api/common";
|
import {initCommonApi} from "../api/common";
|
||||||
|
import {initLocalApi} from "../api/local";
|
||||||
// The built directory structure
|
// The built directory structure
|
||||||
//
|
//
|
||||||
// ├─┬ dist-electron
|
// ├─┬ dist-electron
|
||||||
@ -172,6 +172,7 @@ app.whenReady().then(() => {
|
|||||||
initLoggerApi();
|
initLoggerApi();
|
||||||
initFileApi();
|
initFileApi();
|
||||||
initCommonApi();
|
initCommonApi();
|
||||||
|
initLocalApi();
|
||||||
// initUpdaterApi(win);
|
// initUpdaterApi(win);
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@ -814,20 +814,22 @@ onUnmounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-dialog v-model="visibles.copyServerConfig" title="复制链接" width="500">
|
<el-dialog v-model="visibles.copyServerConfig" title="复制链接" width="500" top="5%">
|
||||||
<el-alert class="mb-4" title="生成内容包含服务器密钥等内容 请妥善保管 且链接仅在Frpc-Desktop中可用" type="warning"
|
<el-alert class="mb-4" title="生成内容包含服务器密钥等内容 请妥善保管 且链接仅在Frpc-Desktop中可用" type="warning"
|
||||||
:closable="false"/>
|
:closable="false"/>
|
||||||
<el-input class="h-30" v-model="copyServerConfigBase64" type="textarea" :rows="8"></el-input>
|
<el-input class="h-30" v-model="copyServerConfigBase64" type="textarea" :rows="8"></el-input>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<el-dialog v-model="visibles.pasteServerConfig" title="导入链接" width="500">
|
<el-dialog v-model="visibles.pasteServerConfig" title="导入链接" width="500" top="5%">
|
||||||
<el-input class="h-30"
|
<el-input class="h-30"
|
||||||
v-model="pasteServerConfigBase64"
|
v-model="pasteServerConfigBase64"
|
||||||
type="textarea" placeholder="frp://......"
|
type="textarea" placeholder="frp://......"
|
||||||
:rows="8"></el-input>
|
:rows="8"></el-input>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="dialog-footer">
|
<div class="dialog-footer">
|
||||||
<el-button plain type="primary" @click="handlePasteServerConfigBase64">导入</el-button>
|
|
||||||
|
<el-button plain type="primary" @click="handlePasteServerConfigBase64">
|
||||||
|
<Icon class="cursor-pointer mr-2" icon="material-symbols:label-important-rounded"/>导 入</el-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import Breadcrumb from "@/layout/compoenets/Breadcrumb.vue";
|
|||||||
import {ElMessage, FormInstance, FormRules} from "element-plus";
|
import {ElMessage, FormInstance, FormRules} from "element-plus";
|
||||||
import {ipcRenderer} from "electron";
|
import {ipcRenderer} from "electron";
|
||||||
import {clone} from "@/utils/clone";
|
import {clone} from "@/utils/clone";
|
||||||
|
import {useDebounceFn} from "@vueuse/core";
|
||||||
|
|
||||||
defineComponent({
|
defineComponent({
|
||||||
name: "Proxy"
|
name: "Proxy"
|
||||||
@ -20,6 +21,12 @@ type Proxy = {
|
|||||||
customDomains: string[];
|
customDomains: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type LocalPort = {
|
||||||
|
protocol: string;
|
||||||
|
ip: string;
|
||||||
|
port: number;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 代理列表
|
* 代理列表
|
||||||
*/
|
*/
|
||||||
@ -29,9 +36,13 @@ const proxys = ref<Array<Proxy>>([]);
|
|||||||
*/
|
*/
|
||||||
const loading = ref({
|
const loading = ref({
|
||||||
list: 1,
|
list: 1,
|
||||||
form: 0
|
form: 0,
|
||||||
|
localPorts: 1
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const localPorts = ref<Array<LocalPort>>([]);
|
||||||
|
const listPortsVisible = ref(false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 弹出层属性
|
* 弹出层属性
|
||||||
*/
|
*/
|
||||||
@ -170,6 +181,12 @@ const handleInitHook = () => {
|
|||||||
ipcRenderer.on("Proxy.updateProxy.hook", (event, args) => {
|
ipcRenderer.on("Proxy.updateProxy.hook", (event, args) => {
|
||||||
InsertOrUpdateHook("修改成功", args);
|
InsertOrUpdateHook("修改成功", args);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcRenderer.on("local.getLocalPorts.hook", (event, args) => {
|
||||||
|
loading.value.localPorts--;
|
||||||
|
localPorts.value = args.data;
|
||||||
|
console.log('本地端口', localPorts.value)
|
||||||
|
})
|
||||||
// ipcRenderer.on("Proxy.updateProxy.hook", (event, args) => {
|
// ipcRenderer.on("Proxy.updateProxy.hook", (event, args) => {
|
||||||
// loading.value.form--;
|
// loading.value.form--;
|
||||||
// const { err } = args;
|
// const { err } = args;
|
||||||
@ -216,6 +233,26 @@ const handleOpenUpdate = (proxy: Proxy) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleLoadLocalPorts = () => {
|
||||||
|
loading.value.localPorts = 1;
|
||||||
|
ipcRenderer.send("local.getLocalPorts")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const handleSelectLocalPort = useDebounceFn((port: number) => {
|
||||||
|
editForm.value.localPort = port;
|
||||||
|
handleCloseLocalPortDialog();
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleCloseLocalPortDialog = () => {
|
||||||
|
listPortsVisible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOpenLocalPortDialog = () => {
|
||||||
|
listPortsVisible.value = true;
|
||||||
|
handleLoadLocalPorts();
|
||||||
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
handleInitHook();
|
handleInitHook();
|
||||||
handleLoadProxys();
|
handleLoadProxys();
|
||||||
@ -226,6 +263,7 @@ onUnmounted(() => {
|
|||||||
ipcRenderer.removeAllListeners("Proxy.updateProxy.hook");
|
ipcRenderer.removeAllListeners("Proxy.updateProxy.hook");
|
||||||
ipcRenderer.removeAllListeners("Proxy.deleteProxyById.hook");
|
ipcRenderer.removeAllListeners("Proxy.deleteProxyById.hook");
|
||||||
ipcRenderer.removeAllListeners("Proxy.getProxys.hook");
|
ipcRenderer.removeAllListeners("Proxy.getProxys.hook");
|
||||||
|
ipcRenderer.removeAllListeners("local.getLocalPorts.hook");
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
@ -330,8 +368,8 @@ onUnmounted(() => {
|
|||||||
<el-dialog
|
<el-dialog
|
||||||
v-model="edit.visible"
|
v-model="edit.visible"
|
||||||
:title="edit.title"
|
:title="edit.title"
|
||||||
class="w-[400px]"
|
width="400"
|
||||||
top="30px"
|
top="5%"
|
||||||
>
|
>
|
||||||
<el-form
|
<el-form
|
||||||
v-loading="loading.form"
|
v-loading="loading.form"
|
||||||
@ -357,21 +395,25 @@ onUnmounted(() => {
|
|||||||
<el-input v-model="editForm.name" placeholder="代理名称"/>
|
<el-input v-model="editForm.name" placeholder="代理名称"/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="16">
|
<el-col :span="24">
|
||||||
<el-form-item label="内网地址:" prop="localIp">
|
<el-form-item label="内网地址:" prop="localIp">
|
||||||
<el-input v-model="editForm.localIp" placeholder="127.0.0.1"/>
|
<el-input v-model="editForm.localIp" placeholder="127.0.0.1"/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="8">
|
<el-col :span="24">
|
||||||
<el-form-item label="内网端口:" prop="localPort">
|
<el-form-item label="内网端口:" prop="localPort">
|
||||||
<el-input-number
|
<el-input-number
|
||||||
placeholder="8080"
|
placeholder="8080"
|
||||||
class="!w-full"
|
class="local-port-input"
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="65535"
|
:max="65535"
|
||||||
v-model="editForm.localPort"
|
v-model="editForm.localPort"
|
||||||
controls-position="right"
|
controls-position="right"
|
||||||
/>
|
/>
|
||||||
|
<el-button class="ml-[10px]" plain type="primary" @click="handleOpenLocalPortDialog">
|
||||||
|
<Icon class="cursor-pointer mr-2" icon="material-symbols:bring-your-own-ip-rounded"/>
|
||||||
|
本地端口
|
||||||
|
</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<template v-if="editForm.type === 'tcp' || editForm.type === 'udp'">
|
<template v-if="editForm.type === 'tcp' || editForm.type === 'udp'">
|
||||||
@ -443,9 +485,12 @@ onUnmounted(() => {
|
|||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<div class="w-full flex justify-end">
|
<div class="w-full flex justify-end">
|
||||||
<el-button @click="edit.visible = false">关 闭</el-button>
|
<el-button @click="edit.visible = false">
|
||||||
|
<Icon class="cursor-pointer mr-2" icon="material-symbols:cancel-presentation"/>
|
||||||
|
关 闭</el-button>
|
||||||
<el-button plain type="primary" @click="handleSubmit"
|
<el-button plain type="primary" @click="handleSubmit"
|
||||||
>保 存
|
><Icon class="cursor-pointer mr-2" icon="material-symbols:save-rounded"/>
|
||||||
|
保 存
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -453,6 +498,31 @@ onUnmounted(() => {
|
|||||||
</el-row>
|
</el-row>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog v-model="listPortsVisible"
|
||||||
|
title="本地端口"
|
||||||
|
width="600"
|
||||||
|
top="5%">
|
||||||
|
<el-table :data="localPorts" stripe
|
||||||
|
v-loading="loading.localPorts"
|
||||||
|
border height="400">
|
||||||
|
<el-table-column label="协议" :width="60" prop="protocol"/>
|
||||||
|
<el-table-column label="IP" prop="ip"/>
|
||||||
|
<el-table-column label="端口" :width="80" prop="port"/>
|
||||||
|
<el-table-column label="操作" :width="80">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button type="text" @click="handleSelectLocalPort(scope.row.port)">
|
||||||
|
<Icon class="cursor-pointer mr-2" icon="material-symbols:gesture-select"/>
|
||||||
|
选择
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<!-- <div class="h-[400px] overflow-y-scroll">-->
|
||||||
|
<!-- -->
|
||||||
|
<!-- </div>-->
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -477,6 +547,10 @@ onUnmounted(() => {
|
|||||||
width: calc(100% - 115px);
|
width: calc(100% - 115px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.local-port-input {
|
||||||
|
width: calc(100% - 120px);
|
||||||
|
}
|
||||||
|
|
||||||
.domain-input-button {
|
.domain-input-button {
|
||||||
background: #5f3bb0;
|
background: #5f3bb0;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
Loading…
Reference in New Issue
Block a user