Files
replypier/frontend/src/components/agent/CreateAgentModal.vue
T
WangShen007 2dd062787d feat(agent): 检索工具、模型接入与结构化回复
Agent 按组织与知识源范围调用检索工具,密钥加密保存,回复含来源与转人工意图。
2026-10-02 21:25:00 +08:00

358 lines
9.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!--
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script setup lang="ts">
import type { Agent } from '@/types/agent'
import { useAgentCreate } from '@/composables/useAgentCreate'
const {
agentName,
useWorkflow,
isCreating,
error,
createAgent
} = useAgentCreate()
const emit = defineEmits<{
(e: 'close'): void
(e: 'created', agent: Agent): void
(e: 'openWorkflowBuilder', agent: Agent): void
}>()
const handleCreateAgent = async () => {
const newAgent = await createAgent()
if (newAgent) {
emit('created', newAgent)
}
}
</script>
<template>
<div class="modal-overlay">
<div class="modal-content" role="dialog" aria-modal="true" aria-labelledby="create-agent-title">
<div class="modal-header">
<h3 id="create-agent-title">创建客服</h3>
<button type="button" class="close-button" aria-label="关闭" @click="$emit('close')">&times;</button>
</div>
<div class="modal-body">
<form @submit.prevent="handleCreateAgent">
<div class="form-group">
<label for="agent-name">客服名称</label>
<input
id="agent-name"
type="text"
v-model="agentName"
placeholder="例如:客户支持客服"
:disabled="isCreating"
required
>
<div class="help-text">
为客服起个名字,访客会看到这个名称。
</div>
</div>
<div class="form-group mode-selection">
<label>客服模式</label>
<div class="mode-options">
<div
class="mode-option"
:class="{ 'selected': !useWorkflow }"
@click="useWorkflow = false"
>
<div class="mode-icon">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 3l1.9 4.6L18.5 9l-4.6 1.4L12 15l-1.9-4.6L5.5 9l4.6-1.4z"/>
<path d="M19 14l.7 1.8L21.5 16.5l-1.8.7L19 19l-.7-1.8L16.5 16.5l1.8-.7z"/>
</svg>
</div>
<div class="mode-details">
<h4>AI 自动模式</h4>
<p>让 AI 依据指令自然应答</p>
</div>
</div>
<div
class="mode-option"
:class="{ 'selected': useWorkflow }"
@click="useWorkflow = true"
>
<div class="mode-icon">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
<circle cx="6" cy="6" r="2.4"/>
<circle cx="6" cy="18" r="2.4"/>
<circle cx="18" cy="9" r="2.4"/>
<path d="M8.4 6H13a3 3 0 0 1 3 3M6 8.4v7.2"/>
</svg>
</div>
<div class="mode-details">
<h4>工作流编排</h4>
<p>拖拽搭建自定义对话流程</p>
</div>
</div>
</div>
</div>
<div v-if="error" class="error-message">
{{ error }}
</div>
<div class="form-actions">
<button
type="button"
class="cancel-button"
@click="$emit('close')"
:disabled="isCreating"
>
取消
</button>
<button
type="submit"
class="create-button"
:disabled="isCreating"
>
{{ isCreating ? '创建中…' : '创建客服' }}
</button>
</div>
</form>
</div>
</div>
</div>
</template>
<style scoped>
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(255, 255, 255, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.modal-content {
background: var(--surface);
border: 1px solid var(--o10);
border-radius: 24px;
width: 90%;
max-width: 600px;
max-height: 90vh;
overflow-y: auto;
box-shadow: 0 12px 48px rgba(0, 0, 0, 0.2);
animation: modal-appear 0.3s ease-out;
}
@keyframes modal-appear {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--space-lg) var(--space-lg) var(--space-md);
border-bottom: 1px solid var(--border-color);
}
.modal-header h3 {
margin: 0;
font-size: 1.5rem;
color: var(--text-color);
font-weight: 600;
}
.close-button {
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
color: var(--text-muted);
line-height: 1;
padding: 0;
margin: 0;
}
.close-button:hover {
color: var(--text-color);
}
.modal-body {
padding: var(--space-lg);
}
.form-group {
margin-bottom: var(--space-lg);
}
label {
display: block;
margin-bottom: var(--space-xs);
font-weight: 500;
color: var(--text-color);
}
input {
width: 100%;
padding: var(--space-md);
border: 1px solid var(--border-color);
border-radius: var(--radius-md);
background: var(--background-soft);
color: var(--text-color);
font-size: 1rem;
transition: border-color 0.2s ease;
}
input:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 2px var(--primary-soft);
}
.help-text {
font-size: 0.875rem;
color: var(--text-muted);
margin-top: var(--space-xs);
}
.error-message {
background-color: var(--error-soft);
color: var(--error-color);
padding: var(--space-md);
border-radius: var(--radius-md);
margin-bottom: var(--space-lg);
font-size: 0.875rem;
}
.mode-options {
display: flex;
gap: var(--space-md);
flex-direction: column;
}
.mode-option {
display: flex;
align-items: center;
gap: var(--space-md);
padding: var(--space-md);
border: 2px solid var(--border-color);
border-radius: var(--radius-lg);
cursor: pointer;
transition: all 0.2s ease;
}
.mode-option.selected {
border-color: var(--primary-color);
background-color: var(--primary-soft);
}
.mode-icon {
display: flex;
align-items: center;
justify-content: center;
width: 44px;
height: 44px;
flex-shrink: 0;
border-radius: var(--radius-md);
background: var(--o05);
border: 1px solid var(--o10);
color: var(--muted);
transition: all 0.2s ease;
}
.mode-option.selected .mode-icon {
background: var(--accent-bg-08);
border-color: var(--accent-border);
color: var(--accent-ink);
}
.mode-details {
flex: 1;
}
.mode-details h4 {
margin: 0 0 var(--space-xs);
font-size: 1.1rem;
}
.mode-details p {
margin: 0;
color: var(--text-muted);
font-size: 0.875rem;
}
.form-actions {
display: flex;
justify-content: flex-end;
gap: var(--space-md);
margin-top: var(--space-xl);
}
.cancel-button {
padding: var(--space-md) var(--space-lg);
border: 1px solid var(--border-color);
border-radius: var(--radius-md);
background: var(--background-soft);
color: var(--text-color);
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
}
.cancel-button:hover {
background: var(--background-muted);
}
.create-button {
padding: var(--space-md) var(--space-lg);
border: none;
border-radius: var(--radius-md);
background: var(--accent-solid);
color: var(--on-accent-solid);
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
}
.create-button:hover {
background: var(--primary-dark);
}
.create-button:disabled,
.cancel-button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
@media (min-width: 768px) {
.mode-options {
flex-direction: row;
}
.mode-option {
flex: 1;
}
}
</style>