feat: 更新前端页面和agent应用
- Agents.vue: 更新agent列表和创建功能 - Skill.vue: 更新skill页面 - skill.ts: 更新skill编辑逻辑 - agent/app/main.py: 更新agent应用 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -142,6 +142,31 @@ class TeamChatRequest(BaseModel):
|
|||||||
strategy: str = "parallel"
|
strategy: str = "parallel"
|
||||||
|
|
||||||
|
|
||||||
|
class CreateAgentRequest(BaseModel):
|
||||||
|
"""创建智能体请求"""
|
||||||
|
name: str
|
||||||
|
description: Optional[str] = None
|
||||||
|
avatar: str = "🤖"
|
||||||
|
# 技能配置
|
||||||
|
skills_mode: str = "all" # all / include / exclude
|
||||||
|
skills: list[str] = [] # 技能ID列表
|
||||||
|
# 知识库
|
||||||
|
knowledge: str = "general" # general / codebase / docs / api
|
||||||
|
# 自定义提示词
|
||||||
|
prompt: Optional[str] = None
|
||||||
|
# 模型配置
|
||||||
|
model_provider: Optional[str] = None
|
||||||
|
model_name: Optional[str] = None
|
||||||
|
user_id: int = 1
|
||||||
|
|
||||||
|
|
||||||
|
class CreateAgentResponse(BaseModel):
|
||||||
|
"""创建智能体响应"""
|
||||||
|
agent_id: int
|
||||||
|
name: str
|
||||||
|
message: str = "Agent created successfully"
|
||||||
|
|
||||||
|
|
||||||
class ChatResponse(BaseModel):
|
class ChatResponse(BaseModel):
|
||||||
"""对话响应"""
|
"""对话响应"""
|
||||||
agent_id: int
|
agent_id: int
|
||||||
@@ -443,6 +468,73 @@ async def team_chat(request: TeamChatRequest):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/agent/create", response_model=CreateAgentResponse)
|
||||||
|
async def create_agent(request: CreateAgentRequest):
|
||||||
|
"""
|
||||||
|
创建新的智能体
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
# 生成唯一的 agent_id
|
||||||
|
agent_id = int(datetime.now().timestamp() * 1000) % 100000
|
||||||
|
|
||||||
|
# 构建 Agent 配置
|
||||||
|
agent_config = {
|
||||||
|
"id": agent_id,
|
||||||
|
"name": request.name,
|
||||||
|
"description": request.description or "",
|
||||||
|
"avatar": request.avatar,
|
||||||
|
"skills_mode": request.skills_mode,
|
||||||
|
"skills": request.skills,
|
||||||
|
"knowledge": request.knowledge,
|
||||||
|
"role_description": request.prompt or f"You are {request.name}. {request.description or ''}",
|
||||||
|
"model_provider": request.model_provider or "anthropic",
|
||||||
|
"model_name": request.model_name or "claude-sonnet-4-20250514",
|
||||||
|
}
|
||||||
|
|
||||||
|
# 保存到 agents 目录
|
||||||
|
agents_dir = os.path.join(os.path.dirname(__file__), "agents")
|
||||||
|
os.makedirs(agents_dir, exist_ok=True)
|
||||||
|
|
||||||
|
config_file = os.path.join(agents_dir, f"agent_{agent_id}.json")
|
||||||
|
with open(config_file, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(agent_config, f, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
|
logger.info(f"Agent created: {request.name} (ID: {agent_id})")
|
||||||
|
|
||||||
|
return CreateAgentResponse(
|
||||||
|
agent_id=agent_id,
|
||||||
|
name=request.name,
|
||||||
|
message="Agent created successfully"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/agent/list")
|
||||||
|
async def list_agents():
|
||||||
|
"""
|
||||||
|
获取智能体列表
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
|
||||||
|
agents_dir = os.path.join(os.path.dirname(__file__), "agents")
|
||||||
|
if not os.path.exists(agents_dir):
|
||||||
|
return {"agents": []}
|
||||||
|
|
||||||
|
agents = []
|
||||||
|
for file in os.listdir(agents_dir):
|
||||||
|
if file.endswith(".json"):
|
||||||
|
config_file = os.path.join(agents_dir, file)
|
||||||
|
try:
|
||||||
|
with open(config_file, "r", encoding="utf-8") as f:
|
||||||
|
agent = json.load(f)
|
||||||
|
agents.append(agent)
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
|
||||||
|
return {"agents": agents}
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import uvicorn
|
import uvicorn
|
||||||
port = int(os.getenv("AGENT_PORT", "8081"))
|
port = int(os.getenv("AGENT_PORT", "8081"))
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
const API_BASE = 'http://localhost:8082'
|
const API_BASE = 'http://localhost:8082'
|
||||||
|
|
||||||
@@ -14,13 +15,35 @@ interface Skill {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Agents 数据
|
// Agents 数据
|
||||||
const agents = ref([
|
const agents = ref<any[]>([])
|
||||||
{ id: 1, name: 'Claude Agent', avatar: '🧠', description: 'General purpose AI assistant', accentColor: '#f97316', gradient: 'from-orange-500/20 to-amber-500/20', status: 'running' as const, framework: 'Google ADK', model: 'gemini-2.0-flash', mcpServers: 2, createdAt: '2025-04-10' },
|
|
||||||
{ id: 2, name: 'Code Assistant', avatar: '💻', description: 'Specialized in code generation', accentColor: '#3b82f6', gradient: 'from-blue-500/20 to-cyan-500/20', status: 'running' as const, framework: 'OpenAI', model: 'gpt-4o', mcpServers: 1, createdAt: '2025-04-08' },
|
// 获取智能体列表
|
||||||
{ id: 3, name: 'Data Analyst', avatar: '📊', description: 'Data analysis and visualization', accentColor: '#10b981', gradient: 'from-emerald-500/20 to-green-500/20', status: 'stopped' as const, framework: 'PydanticAI', model: 'gpt-4o-mini', mcpServers: 3, createdAt: '2025-04-05' },
|
const fetchAgents = async () => {
|
||||||
{ id: 4, name: 'Research Bot', avatar: '🔬', description: 'Academic research assistant', accentColor: '#8b5cf6', gradient: 'from-violet-500/20 to-purple-500/20', status: 'running' as const, framework: 'LangChain', model: 'claude-3-5-sonnet', mcpServers: 2, createdAt: '2025-04-12' },
|
try {
|
||||||
{ id: 5, name: '客服助手', avatar: '🎧', description: 'Customer support agent', accentColor: '#ec4899', gradient: 'from-pink-500/20 to-rose-500/20', status: 'running' as const, framework: 'Google ADK', model: 'gemini-1.5-pro', mcpServers: 4, createdAt: '2025-04-11' },
|
const response = await fetch('http://localhost:8082/api/agent/list')
|
||||||
])
|
if (!response.ok) throw new Error('Failed to fetch agents')
|
||||||
|
|
||||||
|
const data = await response.json()
|
||||||
|
|
||||||
|
// 转换后端数据为前端格式
|
||||||
|
agents.value = (data.agents || []).map((agent: any) => ({
|
||||||
|
id: agent.id,
|
||||||
|
name: agent.name,
|
||||||
|
avatar: agent.avatar || '🤖',
|
||||||
|
description: agent.description || '',
|
||||||
|
accentColor: '#f97316',
|
||||||
|
gradient: 'from-orange-500/20 to-amber-500/20',
|
||||||
|
status: 'stopped' as const,
|
||||||
|
framework: agent.skills?.length > 0 ? agent.skills.join(', ') : 'None',
|
||||||
|
model: agent.model_name || 'claude-sonnet-4-20250514',
|
||||||
|
mcpServers: 0,
|
||||||
|
createdAt: new Date().toISOString().split('T')[0],
|
||||||
|
}))
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch agents:', error)
|
||||||
|
// 保持空数组,不使用假数据
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 创建智能体弹窗状态
|
// 创建智能体弹窗状态
|
||||||
const showCreateModal = ref(false)
|
const showCreateModal = ref(false)
|
||||||
@@ -65,8 +88,10 @@ const selectSkillsMode = (mode: 'all' | 'include' | 'exclude') => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 处理技能模式点击(用于模板)
|
// 处理技能模式点击(用于模板)
|
||||||
const handleSkillsModeClick = (mode: 'all' | 'include' | 'exclude') => {
|
const handleSkillsModeClick = (mode: string) => {
|
||||||
selectSkillsMode(mode)
|
if (mode === 'all' || mode === 'include' || mode === 'exclude') {
|
||||||
|
selectSkillsMode(mode)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 切换子下拉框
|
// 切换子下拉框
|
||||||
@@ -214,9 +239,10 @@ const handleClickOutside = (e: MouseEvent) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 页面加载时获取 skills
|
// 页面加载时获取 skills 和 agents
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
fetchSkills()
|
fetchSkills()
|
||||||
|
fetchAgents()
|
||||||
document.addEventListener('click', handleClickOutside)
|
document.addEventListener('click', handleClickOutside)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -246,13 +272,35 @@ const createAgent = async () => {
|
|||||||
|
|
||||||
isCreating.value = true
|
isCreating.value = true
|
||||||
try {
|
try {
|
||||||
|
// 调用后端 API 创建智能体
|
||||||
|
const response = await fetch('http://localhost:8082/api/agent/create', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
name: newAgent.value.name,
|
||||||
|
description: newAgent.value.description,
|
||||||
|
avatar: newAgent.value.avatar,
|
||||||
|
skills_mode: newAgent.value.skillsMode,
|
||||||
|
skills: newAgent.value.selectedSkills,
|
||||||
|
knowledge: newAgent.value.knowledge,
|
||||||
|
prompt: newAgent.value.prompt,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Failed to create agent: ${response.status}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json()
|
||||||
|
|
||||||
// 处理 skills 标签
|
// 处理 skills 标签
|
||||||
const skillsLabels = newAgent.value.selectedSkills.map((id: string) => getSkillLabel(id)).join(', ')
|
const skillsLabels = newAgent.value.selectedSkills.map((id: string) => getSkillLabel(id)).join(', ')
|
||||||
|
|
||||||
// 模拟创建
|
// 添加到列表
|
||||||
const newId = Math.max(...agents.value.map(a => a.id)) + 1
|
|
||||||
agents.value.unshift({
|
agents.value.unshift({
|
||||||
id: newId,
|
id: result.agent_id,
|
||||||
name: newAgent.value.name,
|
name: newAgent.value.name,
|
||||||
avatar: newAgent.value.avatar,
|
avatar: newAgent.value.avatar,
|
||||||
description: newAgent.value.description,
|
description: newAgent.value.description,
|
||||||
@@ -264,7 +312,12 @@ const createAgent = async () => {
|
|||||||
mcpServers: 0,
|
mcpServers: 0,
|
||||||
createdAt: new Date().toISOString().split('T')[0],
|
createdAt: new Date().toISOString().split('T')[0],
|
||||||
})
|
})
|
||||||
|
|
||||||
showCreateModal.value = false
|
showCreateModal.value = false
|
||||||
|
ElMessage.success('Agent created successfully')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to create agent:', error)
|
||||||
|
ElMessage.error('Failed to create agent')
|
||||||
} finally {
|
} finally {
|
||||||
isCreating.value = false
|
isCreating.value = false
|
||||||
}
|
}
|
||||||
@@ -345,7 +398,7 @@ const deleteAgent = (id: number) => {
|
|||||||
|
|
||||||
<!-- Agents 列表 -->
|
<!-- Agents 列表 -->
|
||||||
<div class="bg-dark-700 rounded-xl overflow-hidden">
|
<div class="bg-dark-700 rounded-xl overflow-hidden">
|
||||||
<table class="w-full">
|
<table v-if="filteredAgents.length > 0" class="w-full">
|
||||||
<thead class="bg-dark-600">
|
<thead class="bg-dark-600">
|
||||||
<tr>
|
<tr>
|
||||||
<th class="text-left px-5 py-3 text-sm font-medium text-gray-400">Agent Name</th>
|
<th class="text-left px-5 py-3 text-sm font-medium text-gray-400">Agent Name</th>
|
||||||
@@ -416,9 +469,12 @@ const deleteAgent = (id: number) => {
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
<!-- 空状态 -->
|
<!-- 空状态 -->
|
||||||
<div v-if="filteredAgents.length === 0" class="py-12 text-center text-gray-500">
|
<div v-if="filteredAgents.length === 0" class="empty-box">
|
||||||
<i class="fa-solid fa-robot text-4xl mb-3"></i>
|
<div class="empty-icon">
|
||||||
<p>No agents found</p>
|
<i class="fa-solid fa-robot"></i>
|
||||||
|
</div>
|
||||||
|
<p class="empty-text">No agents found</p>
|
||||||
|
<p class="empty-tip">Click "New Agent" to create one</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -506,7 +562,7 @@ const deleteAgent = (id: number) => {
|
|||||||
:key="option.value"
|
:key="option.value"
|
||||||
class="skills-mode-item"
|
class="skills-mode-item"
|
||||||
:class="{ 'active': newAgent.skillsMode === option.value }"
|
:class="{ 'active': newAgent.skillsMode === option.value }"
|
||||||
@click="handleSkillsModeClick(option.value as 'all' | 'include' | 'exclude')"
|
@click="handleSkillsModeClick(option.value)"
|
||||||
>
|
>
|
||||||
<div class="mode-radio">
|
<div class="mode-radio">
|
||||||
<div v-if="newAgent.skillsMode === option.value" class="radio-dot"></div>
|
<div v-if="newAgent.skillsMode === option.value" class="radio-dot"></div>
|
||||||
@@ -655,7 +711,7 @@ const deleteAgent = (id: number) => {
|
|||||||
/* Skills Mode Options */
|
/* Skills Mode Options */
|
||||||
.skills-mode-options {
|
.skills-mode-options {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
border-bottom: 1px solid #4b5563;
|
border-bottom: 1px solid #2a2c36;
|
||||||
}
|
}
|
||||||
|
|
||||||
.skills-mode-item {
|
.skills-mode-item {
|
||||||
@@ -669,7 +725,7 @@ const deleteAgent = (id: number) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.skills-mode-item:hover {
|
.skills-mode-item:hover {
|
||||||
background: #374151;
|
background: #1a1c25;
|
||||||
}
|
}
|
||||||
|
|
||||||
.skills-mode-item.active {
|
.skills-mode-item.active {
|
||||||
@@ -734,8 +790,8 @@ const deleteAgent = (id: number) => {
|
|||||||
left: 100%;
|
left: 100%;
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
width: 280px;
|
width: 280px;
|
||||||
background: #374151;
|
background: #171922;
|
||||||
border: 1px solid #4b5563;
|
border: 1px solid #2a2c36;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.4);
|
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.4);
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
@@ -747,8 +803,8 @@ const deleteAgent = (id: number) => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
border-bottom: 1px solid #4b5563;
|
border-bottom: 1px solid #2a2c36;
|
||||||
background: #4b5563;
|
background: #1a1c25;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sub-dropdown-title {
|
.sub-dropdown-title {
|
||||||
@@ -794,8 +850,8 @@ const deleteAgent = (id: number) => {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
min-height: 44px;
|
min-height: 44px;
|
||||||
padding: 0 12px;
|
padding: 0 12px;
|
||||||
background: #374151;
|
background: #171922;
|
||||||
border: 1px solid #4b5563;
|
border: 1px solid #2a2c36;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
@@ -856,8 +912,8 @@ const deleteAgent = (id: number) => {
|
|||||||
top: calc(100% + 8px);
|
top: calc(100% + 8px);
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
background: #374151;
|
background: #171922;
|
||||||
border: 1px solid #4b5563;
|
border: 1px solid #2a2c36;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4);
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4);
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
@@ -868,7 +924,7 @@ const deleteAgent = (id: number) => {
|
|||||||
.search-box {
|
.search-box {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
border-bottom: 1px solid #4b5563;
|
border-bottom: 1px solid #2a2c36;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-icon {
|
.search-icon {
|
||||||
@@ -957,7 +1013,7 @@ const deleteAgent = (id: number) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.options-list::-webkit-scrollbar-track {
|
.options-list::-webkit-scrollbar-track {
|
||||||
background: #374151;
|
background: #171922;
|
||||||
}
|
}
|
||||||
|
|
||||||
.options-list::-webkit-scrollbar-thumb {
|
.options-list::-webkit-scrollbar-thumb {
|
||||||
@@ -1029,4 +1085,42 @@ const deleteAgent = (id: number) => {
|
|||||||
input[type="checkbox"]:indeterminate {
|
input[type="checkbox"]:indeterminate {
|
||||||
accent-color: #f97316;
|
accent-color: #f97316;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Empty Box */
|
||||||
|
.empty-box {
|
||||||
|
min-height: 340px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-icon {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: linear-gradient(135deg, #1f2937, #111827);
|
||||||
|
border-radius: 24px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-icon i {
|
||||||
|
font-size: 40px;
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
color: #d1d5db;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-tip {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #9ca3af;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ const {
|
|||||||
openEdit,
|
openEdit,
|
||||||
closeEdit,
|
closeEdit,
|
||||||
goToEditStep2,
|
goToEditStep2,
|
||||||
|
goBackToEditStep1,
|
||||||
saveEditStep2,
|
saveEditStep2,
|
||||||
closeEditStep2,
|
closeEditStep2,
|
||||||
toggleStatus,
|
toggleStatus,
|
||||||
@@ -266,7 +267,7 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center justify-between gap-3 p-5 border-t border-dark-600 bg-dark-700/50">
|
<div class="flex items-center justify-between gap-3 p-5 border-t border-dark-600 bg-dark-700/50">
|
||||||
<button @click="closeEditStep2" class="btn-secondary">Back</button>
|
<button @click="goBackToEditStep1" class="btn-secondary">Back</button>
|
||||||
<button @click="saveEditStep2" class="btn-primary">Save Changes</button>
|
<button @click="saveEditStep2" class="btn-primary">Save Changes</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -348,12 +348,20 @@ Example 1:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 关闭编辑第二步弹窗
|
// 返回编辑第一步
|
||||||
const closeEditStep2 = () => {
|
const goBackToEditStep1 = () => {
|
||||||
isEditingStep2.value = false
|
isEditingStep2.value = false
|
||||||
isEditing.value = true
|
isEditing.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 关闭编辑第二步弹窗(完全关闭)
|
||||||
|
const closeEditStep2 = () => {
|
||||||
|
isEditingStep2.value = false
|
||||||
|
isEditing.value = false
|
||||||
|
editingSkill.value = null
|
||||||
|
editSkillContent.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
// 切换状态
|
// 切换状态
|
||||||
const toggleStatus = async (skill: Skill) => {
|
const toggleStatus = async (skill: Skill) => {
|
||||||
const newStatus = skill.status === 'active' ? 'inactive' : 'active'
|
const newStatus = skill.status === 'active' ? 'inactive' : 'active'
|
||||||
@@ -444,11 +452,14 @@ Example 1:
|
|||||||
|
|
||||||
const file = input.files[0]
|
const file = input.files[0]
|
||||||
importFile.value = file
|
importFile.value = file
|
||||||
importFileName.value = file.name
|
|
||||||
|
// 获取纯文件名,去除路径
|
||||||
|
const fileName = file.name.split(/[/\\]/).pop()?.replace('.md', '') || 'untitled'
|
||||||
|
importFileName.value = fileName
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const content = await file.text()
|
const content = await file.text()
|
||||||
const { skillName, skillDesc } = parseSkillContent(content, file.name.replace('.md', ''))
|
const { skillName, skillDesc } = parseSkillContent(content, fileName)
|
||||||
|
|
||||||
importSkillName.value = skillName
|
importSkillName.value = skillName
|
||||||
importSkillDesc.value = skillDesc
|
importSkillDesc.value = skillDesc
|
||||||
@@ -577,6 +588,7 @@ Example 1:
|
|||||||
openEdit,
|
openEdit,
|
||||||
closeEdit,
|
closeEdit,
|
||||||
goToEditStep2,
|
goToEditStep2,
|
||||||
|
goBackToEditStep1,
|
||||||
saveEditStep2,
|
saveEditStep2,
|
||||||
closeEditStep2,
|
closeEditStep2,
|
||||||
toggleStatus,
|
toggleStatus,
|
||||||
|
|||||||
Reference in New Issue
Block a user