Commit 504490492f83482f32d3af280bd281c1415c6a41

Authored by Yi Bo
1 parent 562e85dc

feat: add hire modal and fire agent action to overview page

web/src/views/CompanyDashboard.vue
... ... @@ -111,8 +111,11 @@
111 111 <!-- Agent Status Table -->
112 112 <div class="overview-card agents-card">
113 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 119 </div>
117 120 <div v-if="companyStatus?.agents?.length > 0" class="agent-status-table">
118 121 <div class="table-header">
... ... @@ -168,6 +171,7 @@
168 171 <button class="btn-action-chat" @click="goToAgentChat(agent.id)">💬 聊天</button>
169 172 <button class="btn-action-skills" @click="toggleAgentSkills(agent.id)">📋 技能</button>
170 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 175 </div>
172 176 </span>
173 177 </div>
... ... @@ -517,6 +521,37 @@
517 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 555 <!-- ═══ Templates Panel ═══ -->
521 556 <div v-if="showTemplates" class="templates-panel">
522 557 <div class="panel-header">
... ... @@ -569,6 +604,49 @@ const showDropdown = ref(false)
569 604 const expandedAgent = ref(null)
570 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 650 // Files tab
573 651 const companyFiles = ref(null)
574 652 const selectedFilePath = ref(null)
... ... @@ -1539,6 +1617,7 @@ function closeDropdown() {
1539 1617 .btn-action-chat { color: var(--accent-light); }
1540 1618 .btn-action-skills { color: var(--blue-text, #60a5fa); }
1541 1619 .btn-action-train { color: var(--purple-text, #a78bfa); }
  1620 +.btn-action-fire { color: var(--red-text, #ef4444); }
1542 1621  
1543 1622 /* Expandable Skills Panel */
1544 1623 .agent-expand-panel {
... ...