mirror of
https://github.com/ZHANGTIANYAO1/teamspeak-music-bot.git
synced 2026-10-02 04:52:50 +08:00
feat: 机器人管理 — 删除、编辑配置、完整创建表单
- PUT /api/bot/:id 更新机器人配置(保存到数据库,重启生效) - GET /api/bot/:id/config 获取保存的配置 - BotManager.updateBot/getBotConfig 方法 - Settings 页面:删除按钮(带确认)、编辑弹窗(修改所有字段) - 创建表单:完整字段(名称、地址、端口、昵称、默认频道) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
bc9b2366d0
commit
067a30233f
3 files changed
+250
-6
No files matched your search
+199
-6
@@ -36,17 +36,79 @@
|
||||
<button class="btn-sm" @click="toggleBot(bot.id, bot.connected)">
|
||||
{{ bot.connected ? '停止' : '启动' }}
|
||||
</button>
|
||||
<button class="btn-sm btn-edit" @click="openEditBot(bot)">
|
||||
<Icon icon="mdi:pencil" />
|
||||
</button>
|
||||
<button class="btn-sm btn-delete" @click="deleteBot(bot.id, bot.name)">
|
||||
<Icon icon="mdi:delete" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit Bot Modal -->
|
||||
<div v-if="editingBot" class="edit-modal-overlay" @click.self="editingBot = null">
|
||||
<div class="edit-modal">
|
||||
<h3 class="modal-title">编辑机器人</h3>
|
||||
<div class="form-group">
|
||||
<label>名称</label>
|
||||
<input v-model="editForm.name" class="input" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>服务器地址</label>
|
||||
<input v-model="editForm.serverAddress" class="input" placeholder="ts.example.com" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group" style="flex:1">
|
||||
<label>端口</label>
|
||||
<input v-model.number="editForm.serverPort" type="number" class="input" />
|
||||
</div>
|
||||
<div class="form-group" style="flex:2">
|
||||
<label>昵称</label>
|
||||
<input v-model="editForm.nickname" class="input" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>默认频道(可选)</label>
|
||||
<input v-model="editForm.defaultChannel" class="input" placeholder="音乐频道" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>频道密码(可选)</label>
|
||||
<input v-model="editForm.channelPassword" class="input" type="password" />
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
<button class="btn-secondary" @click="editingBot = null">取消</button>
|
||||
<button class="btn-primary" @click="saveEditBot">保存(需重启机器人生效)</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Create Bot -->
|
||||
<div class="create-bot">
|
||||
<h3 class="subsection-title">创建新实例</h3>
|
||||
<div class="form-row">
|
||||
<input v-model="newBotName" class="input" placeholder="机器人名称" />
|
||||
<input v-model="newBotServer" class="input" placeholder="TS服务器地址" />
|
||||
<button class="btn-primary" @click="createBot">创建</button>
|
||||
<div class="form-group">
|
||||
<label>名称</label>
|
||||
<input v-model="newBotName" class="input" placeholder="我的音乐机器人" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group" style="flex:2">
|
||||
<label>服务器地址</label>
|
||||
<input v-model="newBotServer" class="input" placeholder="localhost 或 ts.example.com" />
|
||||
</div>
|
||||
<div class="form-group" style="flex:1">
|
||||
<label>端口</label>
|
||||
<input v-model.number="newBotPort" type="number" class="input" placeholder="9987" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>昵称</label>
|
||||
<input v-model="newBotNickname" class="input" placeholder="MusicBot" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>默认频道(可选)</label>
|
||||
<input v-model="newBotChannel" class="input" placeholder="音乐频道" />
|
||||
</div>
|
||||
<button class="btn-primary" @click="createBot">创建</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -315,6 +377,21 @@ const store = usePlayerStore();
|
||||
|
||||
const newBotName = ref('');
|
||||
const newBotServer = ref('');
|
||||
const newBotPort = ref(9987);
|
||||
const newBotNickname = ref('MusicBot');
|
||||
const newBotChannel = ref('');
|
||||
|
||||
// Edit bot
|
||||
const editingBot = ref<string | null>(null);
|
||||
const editForm = reactive({
|
||||
name: '',
|
||||
serverAddress: '',
|
||||
serverPort: 9987,
|
||||
nickname: '',
|
||||
defaultChannel: '',
|
||||
channelPassword: '',
|
||||
});
|
||||
|
||||
const neteaseCookie = ref('');
|
||||
const qqCookie = ref('');
|
||||
const bilibiliCookie = ref('');
|
||||
@@ -465,12 +542,59 @@ async function createBot() {
|
||||
await axios.post('/api/bot', {
|
||||
name: newBotName.value,
|
||||
serverAddress: newBotServer.value,
|
||||
serverPort: 9987,
|
||||
nickname: newBotName.value,
|
||||
serverPort: newBotPort.value || 9987,
|
||||
nickname: newBotNickname.value || newBotName.value,
|
||||
defaultChannel: newBotChannel.value || undefined,
|
||||
autoStart: false,
|
||||
});
|
||||
newBotName.value = '';
|
||||
newBotServer.value = '';
|
||||
newBotPort.value = 9987;
|
||||
newBotNickname.value = 'MusicBot';
|
||||
newBotChannel.value = '';
|
||||
await store.fetchBots();
|
||||
} catch {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteBot(botId: string, botName: string) {
|
||||
if (!confirm(`确认删除机器人 "${botName}"?此操作不可撤销。`)) return;
|
||||
try {
|
||||
await axios.delete(`/api/bot/${botId}`);
|
||||
await store.fetchBots();
|
||||
} catch {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
function openEditBot(bot: any) {
|
||||
editingBot.value = bot.id;
|
||||
// We need to fetch the saved config from server
|
||||
// For now use the status info we have
|
||||
editForm.name = bot.name;
|
||||
editForm.serverAddress = '';
|
||||
editForm.serverPort = 9987;
|
||||
editForm.nickname = '';
|
||||
editForm.defaultChannel = '';
|
||||
editForm.channelPassword = '';
|
||||
// Try to get saved config
|
||||
axios.get(`/api/bot/${bot.id}/config`).then(res => {
|
||||
if (res.data) {
|
||||
editForm.serverAddress = res.data.serverAddress ?? '';
|
||||
editForm.serverPort = res.data.serverPort ?? 9987;
|
||||
editForm.nickname = res.data.nickname ?? '';
|
||||
editForm.defaultChannel = res.data.defaultChannel ?? '';
|
||||
editForm.channelPassword = res.data.channelPassword ?? '';
|
||||
}
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
async function saveEditBot() {
|
||||
if (!editingBot.value) return;
|
||||
try {
|
||||
await axios.put(`/api/bot/${editingBot.value}`, editForm);
|
||||
editingBot.value = null;
|
||||
await store.fetchBots();
|
||||
} catch {
|
||||
// Ignore
|
||||
@@ -848,6 +972,75 @@ onUnmounted(() => {
|
||||
&:hover { background: var(--color-primary); color: white; }
|
||||
}
|
||||
|
||||
.btn-edit {
|
||||
padding: 6px 8px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.btn-delete {
|
||||
padding: 6px 8px;
|
||||
font-size: 14px;
|
||||
&:hover { background: #f44336; color: white; }
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
padding: 10px 20px;
|
||||
background: var(--hover-bg);
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.bot-actions {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 12px;
|
||||
label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 4px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
.edit-modal-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 200;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.edit-modal {
|
||||
background: var(--bg-secondary);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 28px;
|
||||
width: 480px;
|
||||
max-width: 90vw;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.modal-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
justify-content: flex-end;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.spin {
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user