feat: 新增批量操作实例进行代理设置或者删除 (closes #29)

This commit is contained in:
foxhui
2026-03-22 23:58:38 +08:00
Unverified
parent 09bdfd95fa
commit b9f4fafec5
3 changed files with 156 additions and 6 deletions
+4
View File
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [3.5.8] - 2026-03-22
### ✨ Added
- **WebUI**
- 增加批量操作实例设置代理或者删除
### 🐛 Fixed
- **适配器**
- 修复豆包适配器无法点击选择模型的问题
+1 -1
View File
File diff suppressed because one or more lines are too long
+151 -5
View File
@@ -1,6 +1,7 @@
<script setup>
import { ref, onMounted, computed } from 'vue';
import { useSettingsStore } from '@/stores/settings';
import { Modal } from 'ant-design-vue';
const settingsStore = useSettingsStore();
@@ -86,6 +87,81 @@ const instanceData = computed({
set: (val) => { settingsStore.workerConfig = val; }
});
// 批量选择
const selectedRowKeys = ref([]);
const rowSelection = computed(() => ({
selectedRowKeys: selectedRowKeys.value,
onChange: (keys) => { selectedRowKeys.value = keys; }
}));
// 批量代理设置
const batchProxyVisible = ref(false);
const batchProxyForm = ref({
proxy: true,
proxyType: 'socks5',
proxyHost: '',
proxyPort: 1080,
proxyAuth: false,
proxyUsername: '',
proxyPassword: ''
});
const openBatchProxy = () => {
batchProxyForm.value = {
proxy: true,
proxyType: 'socks5',
proxyHost: '',
proxyPort: 1080,
proxyAuth: false,
proxyUsername: '',
proxyPassword: ''
};
batchProxyVisible.value = true;
};
const handleBatchProxySave = async () => {
const newList = (instanceData.value || []).map(inst => {
if (!selectedRowKeys.value.includes(inst.id)) return inst;
return {
...inst,
proxy: batchProxyForm.value.proxy ? {
enable: true,
type: batchProxyForm.value.proxyType,
host: batchProxyForm.value.proxyHost,
port: batchProxyForm.value.proxyPort,
auth: batchProxyForm.value.proxyAuth,
username: batchProxyForm.value.proxyUsername,
password: batchProxyForm.value.proxyPassword
} : null
};
});
const success = await settingsStore.saveWorkerConfig(newList);
if (success) {
batchProxyVisible.value = false;
selectedRowKeys.value = [];
}
};
// 批量删除
const handleBatchDelete = () => {
Modal.confirm({
title: '批量删除实例',
content: `确定要删除选中的 ${selectedRowKeys.value.length} 个实例吗?此操作不可撤销。`,
okText: '删除',
okType: 'danger',
cancelText: '取消',
async onOk() {
const newList = (instanceData.value || []).filter(
inst => !selectedRowKeys.value.includes(inst.id)
);
const success = await settingsStore.saveWorkerConfig(newList);
if (success) {
selectedRowKeys.value = [];
}
}
});
};
// 抽屉状态
const drawerOpen = ref(false);
const editingInstance = ref(null);
@@ -295,7 +371,8 @@ const handleRemoveWorker = (index) => {
故障转移时最大重试次数范围 1-10
</div>
<a-input-number v-model:value="poolConfig.failover.maxRetries" :min="1" :max="10"
:disabled="!poolConfig.failover.enabled" style="width: 100%" placeholder="请输入重试次数" />
:disabled="!poolConfig.failover.enabled" style="width: 100%"
placeholder="请输入重试次数" />
</div>
</a-col>
</a-row>
@@ -317,14 +394,23 @@ const handleRemoveWorker = (index) => {
<template #title>
<div style="display: flex; justify-content: space-between; align-items: center;">
<span>实例列表</span>
<a-button type="primary" @click="handleCreateInstance">
创建实例
</a-button>
<a-space>
<a-button v-if="selectedRowKeys.length > 0" @click="openBatchProxy">
批量设置代理 ({{ selectedRowKeys.length }})
</a-button>
<a-button v-if="selectedRowKeys.length > 0" danger @click="handleBatchDelete">
批量删除 ({{ selectedRowKeys.length }})
</a-button>
<a-button type="primary" @click="handleCreateInstance">
创建实例
</a-button>
</a-space>
</div>
</template>
<!-- 实例表格 -->
<a-table :columns="columns" :data-source="instanceData" :pagination="false">
<a-table :columns="columns" :data-source="instanceData" :pagination="false" :row-selection="rowSelection"
row-key="id">
<template #bodyCell="{ column, record }">
<!-- 实例名称 -->
<template v-if="column.key === 'name'">
@@ -521,5 +607,65 @@ const handleRemoveWorker = (index) => {
</div>
</template>
</a-modal>
<!-- 批量代理设置弹窗 -->
<a-modal v-model:open="batchProxyVisible" title="批量设置代理" okText="应用" cancelText="取消" @ok="handleBatchProxySave">
<div style="margin-bottom: 16px;">
<div style="font-size: 12px; color: #8c8c8c; margin-bottom: 16px;">
将对选中的 {{ selectedRowKeys.length }} 个实例统一设置代理
</div>
<!-- 是否启用代理 -->
<div style="margin-bottom: 16px;">
<a-switch v-model:checked="batchProxyForm.proxy" />
<span style="margin-left: 8px;">
{{ batchProxyForm.proxy ? '启用代理' : '关闭代理(将清除选中实例的代理配置)' }}
</span>
</div>
<!-- 代理类型 -->
<div style="margin-bottom: 16px;" v-if="batchProxyForm.proxy">
<div style="font-weight: 600; margin-bottom: 8px;">代理类型</div>
<a-segmented v-model:value="batchProxyForm.proxyType" block :options="[
{ label: 'SOCKS5', value: 'socks5' },
{ label: 'HTTP', value: 'http' }
]" style="width: 100%" />
</div>
<!-- 服务器地址 -->
<div style="margin-bottom: 16px;" v-if="batchProxyForm.proxy">
<div style="font-weight: 600; margin-bottom: 8px;">服务器地址</div>
<a-input v-model:value="batchProxyForm.proxyHost" placeholder="例如: 127.0.0.1" />
</div>
<!-- 端口 -->
<div style="margin-bottom: 16px;" v-if="batchProxyForm.proxy">
<div style="font-weight: 600; margin-bottom: 8px;">端口</div>
<a-input-number v-model:value="batchProxyForm.proxyPort" :min="1" :max="65535" style="width: 100%"
placeholder="例如: 1080" />
</div>
<!-- 身份验证 -->
<div style="margin-bottom: 16px;" v-if="batchProxyForm.proxy">
<div style="font-weight: 600; margin-bottom: 8px;">身份验证</div>
<a-switch v-model:checked="batchProxyForm.proxyAuth" />
<span style="margin-left: 8px;">
{{ batchProxyForm.proxyAuth ? '需要验证' : '无需验证' }}
</span>
</div>
<!-- 用户名 -->
<div style="margin-bottom: 16px;" v-if="batchProxyForm.proxy && batchProxyForm.proxyAuth">
<div style="font-weight: 600; margin-bottom: 8px;">用户名</div>
<a-input v-model:value="batchProxyForm.proxyUsername" placeholder="请输入用户名" />
</div>
<!-- 密码 -->
<div style="margin-bottom: 16px;" v-if="batchProxyForm.proxy && batchProxyForm.proxyAuth">
<div style="font-weight: 600; margin-bottom: 8px;">密码</div>
<a-input-password v-model:value="batchProxyForm.proxyPassword" placeholder="请输入密码" />
</div>
</div>
</a-modal>
</a-layout>
</template>