Commit 504490492f83482f32d3af280bd281c1415c6a41
1 parent
562e85dc
feat: add hire modal and fire agent action to overview page
Showing
1 changed file
with
81 additions
and
2 deletions
web/src/views/CompanyDashboard.vue
| @@ -111,8 +111,11 @@ | @@ -111,8 +111,11 @@ | ||
| 111 | <!-- Agent Status Table --> | 111 | <!-- Agent Status Table --> |
| 112 | <div class="overview-card agents-card"> | 112 | <div class="overview-card agents-card"> |
| 113 | <div class="overview-card-header"> | 113 | <div class="overview-card-header"> |
| 114 | - <h3>🤖 Agent 状态监控</h3> | ||
| 115 | - <span class="agent-count-badge">{{ companyStatus?.agents?.length || 0 }} 个</span> | 114 | + <div style="display:flex; align-items:center; gap:8px;"> |
| 115 | + <h3>🤖 Agent 状态监控</h3> | ||
| 116 | + <span class="agent-count-badge">{{ companyStatus?.agents?.length || 0 }} 个</span> | ||
| 117 | + </div> | ||
| 118 | + <button class="btn-sm" @click="openHireModal">+ 雇佣 Agent</button> | ||
| 116 | </div> | 119 | </div> |
| 117 | <div v-if="companyStatus?.agents?.length > 0" class="agent-status-table"> | 120 | <div v-if="companyStatus?.agents?.length > 0" class="agent-status-table"> |
| 118 | <div class="table-header"> | 121 | <div class="table-header"> |
| @@ -168,6 +171,7 @@ | @@ -168,6 +171,7 @@ | ||
| 168 | <button class="btn-action-chat" @click="goToAgentChat(agent.id)">💬 聊天</button> | 171 | <button class="btn-action-chat" @click="goToAgentChat(agent.id)">💬 聊天</button> |
| 169 | <button class="btn-action-skills" @click="toggleAgentSkills(agent.id)">📋 技能</button> | 172 | <button class="btn-action-skills" @click="toggleAgentSkills(agent.id)">📋 技能</button> |
| 170 | <button class="btn-action-train" @click="triggerTrain(agent.id)">🎓 训练</button> | 173 | <button class="btn-action-train" @click="triggerTrain(agent.id)">🎓 训练</button> |
| 174 | + <button class="btn-action-fire" @click="fireAgent(agent.id, agent.name)">🗑️ 解雇</button> | ||
| 171 | </div> | 175 | </div> |
| 172 | </span> | 176 | </span> |
| 173 | </div> | 177 | </div> |
| @@ -517,6 +521,37 @@ | @@ -517,6 +521,37 @@ | ||
| 517 | </section> | 521 | </section> |
| 518 | 522 | ||
| 519 | 523 | ||
| 524 | + <!-- Hire Modal --> | ||
| 525 | + <div v-if="showHireModal" class="modal-overlay" @click="showHireModal = false"> | ||
| 526 | + <div class="modal-content" style="max-width: 500px;" @click.stop> | ||
| 527 | + <div class="modal-header"> | ||
| 528 | + <h3>+ 雇佣新 Agent</h3> | ||
| 529 | + <button class="btn-close" @click="showHireModal = false">✕</button> | ||
| 530 | + </div> | ||
| 531 | + <div class="modal-body" style="display: flex; flex-direction: column; gap: 16px;"> | ||
| 532 | + <div> | ||
| 533 | + <label style="font-size: 13px; font-weight: 600; margin-bottom: 6px; display: block; color: var(--text-primary);">从模板库选择角色 (Role)</label> | ||
| 534 | + <select v-model="hireForm.role" class="form-input" style="width: 100%;"> | ||
| 535 | + <option value="">-- 请选择预置 Agent 模板 --</option> | ||
| 536 | + <option v-for="tpl in templates" :key="tpl.id" :value="tpl.id"> | ||
| 537 | + {{ agentEmoji(tpl.type) }} {{ tpl.name }} ({{ tpl.id }}) | ||
| 538 | + </option> | ||
| 539 | + </select> | ||
| 540 | + </div> | ||
| 541 | + <div v-if="hireForm.role"> | ||
| 542 | + <label style="font-size: 13px; font-weight: 600; margin-bottom: 6px; display: block; color: var(--text-primary);">自定义花名 (可选)</label> | ||
| 543 | + <input v-model="hireForm.name" class="form-input" style="width: 100%;" placeholder="留空则使用默认名称" /> | ||
| 544 | + </div> | ||
| 545 | + </div> | ||
| 546 | + <div class="modal-footer" style="padding-top: 16px; margin-top: 8px;"> | ||
| 547 | + <button class="btn-cancel" @click="showHireModal = false" style="padding: 6px 12px; border-radius: 6px; border: 1px solid var(--border-primary); background: transparent; color: var(--text-primary); cursor: pointer;">取消</button> | ||
| 548 | + <button class="btn-install-large" :disabled="!hireForm.role || hiring" @click="submitHire" style="padding: 6px 16px; font-size: 13px;"> | ||
| 549 | + {{ hiring ? '⏳ 办理入职中...' : '🎉 正式雇佣' }} | ||
| 550 | + </button> | ||
| 551 | + </div> | ||
| 552 | + </div> | ||
| 553 | + </div> | ||
| 554 | + | ||
| 520 | <!-- ═══ Templates Panel ═══ --> | 555 | <!-- ═══ Templates Panel ═══ --> |
| 521 | <div v-if="showTemplates" class="templates-panel"> | 556 | <div v-if="showTemplates" class="templates-panel"> |
| 522 | <div class="panel-header"> | 557 | <div class="panel-header"> |
| @@ -569,6 +604,49 @@ const showDropdown = ref(false) | @@ -569,6 +604,49 @@ const showDropdown = ref(false) | ||
| 569 | const expandedAgent = ref(null) | 604 | const expandedAgent = ref(null) |
| 570 | const agentSkills = ref([]) | 605 | const agentSkills = ref([]) |
| 571 | 606 | ||
| 607 | +const showHireModal = ref(false) | ||
| 608 | +const hiring = ref(false) | ||
| 609 | +const hireForm = ref({ role: '', name: '' }) | ||
| 610 | + | ||
| 611 | +function openHireModal() { | ||
| 612 | + hireForm.value = { role: '', name: '' }; | ||
| 613 | + showHireModal.value = true; | ||
| 614 | + if (templates.value.length === 0) loadTemplates(); | ||
| 615 | +} | ||
| 616 | + | ||
| 617 | +async function submitHire() { | ||
| 618 | + if (!hireForm.value.role || !selectedCompany.value) return; | ||
| 619 | + hiring.value = true; | ||
| 620 | + try { | ||
| 621 | + const tpl = templates.value.find(t => t.id === hireForm.value.role); | ||
| 622 | + await axios.post(`/api/company/${selectedCompany.value.id}/hire`, { | ||
| 623 | + role: hireForm.value.role, | ||
| 624 | + name: hireForm.value.name || undefined, | ||
| 625 | + type: tpl?.type, | ||
| 626 | + description: tpl?.description | ||
| 627 | + }); | ||
| 628 | + showHireModal.value = false; | ||
| 629 | + await loadCompanyStatus(selectedCompany.value.id); | ||
| 630 | + await loadCompanyAgents(selectedCompany.value.id); | ||
| 631 | + } catch (e) { | ||
| 632 | + alert(`❌ 雇佣失败: ${e.response?.data?.error || e.message}`); | ||
| 633 | + } finally { | ||
| 634 | + hiring.value = false; | ||
| 635 | + } | ||
| 636 | +} | ||
| 637 | + | ||
| 638 | +async function fireAgent(agentId, agentName) { | ||
| 639 | + if (!selectedCompany.value) return; | ||
| 640 | + if (!confirm(`⚠️ 确定要彻底解雇 ${agentName || agentId} 吗?将停止其所有运行进程并归档数据!`)) return; | ||
| 641 | + try { | ||
| 642 | + await axios.post(`/api/company/${selectedCompany.value.id}/fire`, { role: agentId }); | ||
| 643 | + await loadCompanyStatus(selectedCompany.value.id); | ||
| 644 | + await loadCompanyAgents(selectedCompany.value.id); | ||
| 645 | + } catch (e) { | ||
| 646 | + alert(`❌ 解雇失败: ${e.response?.data?.error || e.message}`); | ||
| 647 | + } | ||
| 648 | +} | ||
| 649 | + | ||
| 572 | // Files tab | 650 | // Files tab |
| 573 | const companyFiles = ref(null) | 651 | const companyFiles = ref(null) |
| 574 | const selectedFilePath = ref(null) | 652 | const selectedFilePath = ref(null) |
| @@ -1539,6 +1617,7 @@ function closeDropdown() { | @@ -1539,6 +1617,7 @@ function closeDropdown() { | ||
| 1539 | .btn-action-chat { color: var(--accent-light); } | 1617 | .btn-action-chat { color: var(--accent-light); } |
| 1540 | .btn-action-skills { color: var(--blue-text, #60a5fa); } | 1618 | .btn-action-skills { color: var(--blue-text, #60a5fa); } |
| 1541 | .btn-action-train { color: var(--purple-text, #a78bfa); } | 1619 | .btn-action-train { color: var(--purple-text, #a78bfa); } |
| 1620 | +.btn-action-fire { color: var(--red-text, #ef4444); } | ||
| 1542 | 1621 | ||
| 1543 | /* Expandable Skills Panel */ | 1622 | /* Expandable Skills Panel */ |
| 1544 | .agent-expand-panel { | 1623 | .agent-expand-panel { |