207207 </section >
208208 </div >
209209 </el-tab-pane >
210+
211+ <!-- Tab 3: MaiBot 控制 -->
212+ <el-tab-pane name =" maibot" >
213+ <template #label >
214+ <span class =" tab-label" >
215+ <el-icon ><Pointer /></el-icon >
216+ MaiBot 控制
217+ </span >
218+ </template >
219+
220+ <div class =" tab-content-wrapper" >
221+ <div class =" inject-layout" >
222+ <!-- 控制表单 -->
223+ <section class =" inject-panel" >
224+ <div class =" panel-header" >
225+ <div class =" panel-title" >
226+ <el-icon class =" title-icon" ><Pointer /></el-icon >
227+ <span >MaiBot 动作控制</span >
228+ </div >
229+ <el-tag size =" small" type =" success" effect =" plain" >外部控制</el-tag >
230+ </div >
231+
232+ <el-form
233+ :model =" maibotForm"
234+ label-position =" top"
235+ class =" inject-form"
236+ @submit.prevent =" triggerMaibotAction"
237+ >
238+ <el-form-item label =" 动作类型" >
239+ <el-select
240+ v-model =" maibotForm.action"
241+ placeholder =" 选择动作类型"
242+ clearable
243+ style =" width : 100% "
244+ >
245+ <el-option label =" hotkey (热键)" value =" hotkey" />
246+ <el-option label =" expression (表情)" value =" expression" />
247+ <el-option label =" motion (动作)" value =" motion" />
248+ </el-select >
249+ </el-form-item >
250+
251+ <!-- 热键参数 -->
252+ <el-form-item v-if =" maibotForm.action === 'hotkey'" label =" 选择热键" >
253+ <el-select
254+ v-model =" maibotForm.hotkey"
255+ placeholder =" 选择预设热键"
256+ style =" width : 100% "
257+ >
258+ <el-option label =" smile (微笑)" value =" smile" />
259+ <el-option label =" wave (挥手)" value =" wave" />
260+ <el-option label =" nod (点头)" value =" nod" />
261+ <el-option label =" shake (摇头)" value =" shake" />
262+ <el-option label =" clap (鼓掌)" value =" clap" />
263+ <el-option label =" dance (跳舞)" value =" dance" />
264+ <el-option label =" jump (跳跃)" value =" jump" />
265+ <el-option label =" sit (坐下)" value =" sit" />
266+ <el-option label =" lie (躺下)" value =" lie" />
267+ <el-option label =" run (跑步)" value =" run" />
268+ </el-select >
269+ </el-form-item >
270+
271+ <!-- 表情参数 -->
272+ <el-form-item v-if =" maibotForm.action === 'expression'" label =" 选择表情" >
273+ <el-select
274+ v-model =" maibotForm.expression"
275+ placeholder =" 选择预设表情"
276+ style =" width : 100% "
277+ >
278+ <el-option label =" happy (开心)" value =" happy" />
279+ <el-option label =" sad (难过)" value =" sad" />
280+ <el-option label =" angry (生气)" value =" angry" />
281+ <el-option label =" surprised (惊讶)" value =" surprised" />
282+ <el-option label =" scared (害怕)" value =" scared" />
283+ <el-option label =" embarrassed (尴尬)" value =" embarrassed" />
284+ <el-option label =" cry (哭泣)" value =" cry" />
285+ <el-option label =" laugh (大笑)" value =" laugh" />
286+ </el-select >
287+ </el-form-item >
288+
289+ <!-- 动作参数 -->
290+ <el-form-item v-if =" maibotForm.action === 'motion'" label =" 选择动作" >
291+ <el-select
292+ v-model =" maibotForm.motion"
293+ placeholder =" 选择预设动作"
294+ style =" width : 100% "
295+ >
296+ <el-option label =" wave (挥手)" value =" wave" />
297+ <el-option label =" nod (点头)" value =" nod" />
298+ <el-option label =" shake_head (摇头)" value =" shake_head" />
299+ <el-option label =" dance (跳舞)" value =" dance" />
300+ <el-option label =" jump (跳跃)" value =" jump" />
301+ <el-option label =" run (跑步)" value =" run" />
302+ <el-option label =" sit_down (坐下)" value =" sit_down" />
303+ <el-option label =" stand_up (站起来)" value =" stand_up" />
304+ <el-option label =" walk (走路)" value =" walk" />
305+ <el-option label =" attack (攻击)" value =" attack" />
306+ </el-select >
307+ </el-form-item >
308+
309+ <el-form-item label =" 情绪类型" >
310+ <el-select
311+ v-model =" maibotForm.emotion"
312+ placeholder =" 选择情绪"
313+ clearable
314+ style =" width : 100% "
315+ >
316+ <el-option label =" happy (开心)" value =" happy" />
317+ <el-option label =" neutral (中性)" value =" neutral" />
318+ <el-option label =" sad (难过)" value =" sad" />
319+ <el-option label =" angry (生气)" value =" angry" />
320+ <el-option label =" excited (兴奋)" value =" excited" />
321+ <el-option label =" shy (害羞)" value =" shy" />
322+ </el-select >
323+ </el-form-item >
324+
325+ <el-form-item label =" 优先级" >
326+ <div class =" importance-slider" >
327+ <el-slider
328+ v-model =" maibotForm.priority"
329+ :min =" 0"
330+ :max =" 100"
331+ :show-tooltip =" false"
332+ />
333+ <span class =" importance-value" >{{ maibotForm.priority }}</span >
334+ </div >
335+ </el-form-item >
336+
337+ <el-form-item label =" 回复文本 (可选)" >
338+ <el-input
339+ v-model =" maibotForm.text"
340+ type =" textarea"
341+ :rows =" 2"
342+ placeholder =" 可选的回复文本..."
343+ resize =" none"
344+ />
345+ </el-form-item >
346+
347+ <el-form-item >
348+ <el-button
349+ type =" primary"
350+ :loading =" maibotLoading"
351+ :disabled =" !maibotForm.action && !maibotForm.emotion"
352+ @click =" triggerMaibotAction"
353+ >
354+ <el-icon ><Pointer /></el-icon >
355+ 触发动作/情绪
356+ </el-button >
357+ </el-form-item >
358+ </el-form >
359+ </section >
360+
361+ <!-- 控制历史 -->
362+ <section class =" history-panel" >
363+ <div class =" panel-header" >
364+ <div class =" panel-title" >
365+ <el-icon class =" title-icon" ><Clock /></el-icon >
366+ <span >控制历史</span >
367+ <el-badge :value =" maibotHistory.length" :max =" 99" class =" history-badge" />
368+ </div >
369+ <el-button
370+ size =" small"
371+ :icon =" Delete"
372+ :disabled =" maibotHistory.length === 0"
373+ @click =" maibotHistory = []"
374+ >
375+ 清空
376+ </el-button >
377+ </div >
378+
379+ <el-table
380+ v-if =" maibotHistory.length > 0"
381+ :data =" maibotHistory"
382+ size =" small"
383+ empty-text =" 暂无控制记录"
384+ class =" history-table"
385+ >
386+ <el-table-column prop =" time" label =" 时间" width =" 100" >
387+ <template #default =" { row } " >
388+ <span class =" time-text" >{{ row.time }}</span >
389+ </template >
390+ </el-table-column >
391+ <el-table-column label =" 动作/情绪" >
392+ <template #default =" { row } " >
393+ <div class =" message-preview" >
394+ <el-tag v-if =" row.action" size =" small" type =" warning" >{{
395+ row.action
396+ }}</el-tag >
397+ <el-tag v-if =" row.emotion" size =" small" type =" success" >{{
398+ row.emotion
399+ }}</el-tag >
400+ </div >
401+ </template >
402+ </el-table-column >
403+ <el-table-column label =" 状态" width =" 70" align =" center" >
404+ <template #default =" { row } " >
405+ <el-tag
406+ :type =" row.success ? 'success' : 'danger'"
407+ size =" small"
408+ effect =" plain"
409+ >
410+ {{ row.success ? '成功' : '失败' }}
411+ </el-tag >
412+ </template >
413+ </el-table-column >
414+ </el-table >
415+
416+ <el-empty v-else description =" 暂无控制记录" :image-size =" 80" />
417+ </section >
418+ </div >
419+ </div >
420+ </el-tab-pane >
210421 </el-tabs >
211422 </div >
212423 </div >
@@ -223,8 +434,9 @@ import {
223434 Refresh ,
224435 Delete ,
225436 RefreshRight ,
437+ Pointer ,
226438} from ' @element-plus/icons-vue' ;
227- import { debugApi } from ' @/api' ;
439+ import { debugApi , maibotApi } from ' @/api' ;
228440import type { EventBusStatsResponse , InjectMessageRequest } from ' @/types' ;
229441
230442// Tab state
@@ -301,6 +513,96 @@ async function retryInject(item: InjectHistoryItem) {
301513 await injectMessage ();
302514}
303515
516+ // ============ MaiBot 控制 Tab ============
517+ interface MaibotHistoryItem {
518+ time: string ;
519+ action? : string ;
520+ emotion? : string ;
521+ priority: number ;
522+ text? : string ;
523+ success: boolean ;
524+ error? : string ;
525+ }
526+
527+ const maibotForm = ref ({
528+ action: ' ' ,
529+ hotkey: ' ' ,
530+ expression: ' ' ,
531+ motion: ' ' ,
532+ emotion: ' ' ,
533+ priority: 50 ,
534+ text: ' ' ,
535+ });
536+
537+ const maibotLoading = ref (false );
538+ const maibotHistory = ref <MaibotHistoryItem []>([]);
539+
540+ async function triggerMaibotAction() {
541+ if (! maibotForm .value .action && ! maibotForm .value .emotion ) {
542+ ElMessage .warning (' 请选择动作或情绪' );
543+ return ;
544+ }
545+
546+ maibotLoading .value = true ;
547+ const time = new Date ().toLocaleString (' zh-CN' , {
548+ hour: ' 2-digit' ,
549+ minute: ' 2-digit' ,
550+ second: ' 2-digit' ,
551+ });
552+
553+ // 根据动作类型构建参数
554+ let actionParams = {};
555+ if (maibotForm .value .action === ' hotkey' && maibotForm .value .hotkey ) {
556+ actionParams = { hotkey: maibotForm .value .hotkey };
557+ } else if (maibotForm .value .action === ' expression' && maibotForm .value .expression ) {
558+ actionParams = { expression: maibotForm .value .expression };
559+ } else if (maibotForm .value .action === ' motion' && maibotForm .value .motion ) {
560+ actionParams = { motion: maibotForm .value .motion };
561+ }
562+
563+ const requestData = {
564+ action: maibotForm .value .action || undefined ,
565+ action_params: Object .keys (actionParams ).length > 0 ? actionParams : undefined ,
566+ emotion: maibotForm .value .emotion || undefined ,
567+ priority: maibotForm .value .priority ,
568+ text: maibotForm .value .text || undefined ,
569+ };
570+
571+ try {
572+ const response = await maibotApi .triggerAction (requestData );
573+
574+ maibotHistory .value .unshift ({
575+ time ,
576+ action: maibotForm .value .action || undefined ,
577+ emotion: maibotForm .value .emotion || undefined ,
578+ priority: maibotForm .value .priority ,
579+ text: maibotForm .value .text || undefined ,
580+ success: response .data .success ,
581+ error: response .data .error ,
582+ });
583+
584+ if (response .data .success ) {
585+ ElMessage .success (' 动作/情绪触发成功' );
586+ } else {
587+ ElMessage .error (response .data .error || ' 触发失败' );
588+ }
589+ } catch (error ) {
590+ console .error (' Maibot API error:' , error );
591+ maibotHistory .value .unshift ({
592+ time ,
593+ action: maibotForm .value .action || undefined ,
594+ emotion: maibotForm .value .emotion || undefined ,
595+ priority: maibotForm .value .priority ,
596+ text: maibotForm .value .text || undefined ,
597+ success: false ,
598+ error: String (error ),
599+ });
600+ ElMessage .error (' 请求失败' );
601+ } finally {
602+ maibotLoading .value = false ;
603+ }
604+ }
605+
304606// ============ EventBus 统计 Tab ============
305607const statsLoading = ref (false );
306608const eventBusStats = ref <EventBusStatsResponse | null >(null );
0 commit comments