Compare commits
25 Commits
main
...
45314b7be6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45314b7be6 | ||
|
|
c5848410c1 | ||
|
|
571b5101ff | ||
|
|
029df6b5a5 | ||
|
|
edb39d4c1f | ||
|
|
0e87777ae8 | ||
|
|
244deceb91 | ||
|
|
69587c0481 | ||
|
|
e0cc11647f | ||
|
|
59fd94e783 | ||
|
|
3ff70463ca | ||
|
|
82e92f12aa | ||
|
|
920588b063 | ||
|
|
5847365eee | ||
|
|
d42554ce03 | ||
|
|
bcc0c53ba1 | ||
|
|
7da5e82d40 | ||
|
|
cc22655a1e | ||
|
|
f0db3c88e4 | ||
|
|
b987fe70ad | ||
|
|
b42ab5aedd | ||
|
|
5ef86c6fa9 | ||
|
|
907310365a | ||
|
|
5dace5f788 | ||
|
|
77530c49f8 |
@@ -1,5 +1,7 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
from openai import OpenAI, AsyncOpenAI
|
|
||||||
|
import httpx
|
||||||
|
from openai import OpenAI, AsyncOpenAI, max_retries
|
||||||
import yaml
|
import yaml
|
||||||
from termcolor import colored
|
from termcolor import colored
|
||||||
import os
|
import os
|
||||||
@@ -21,6 +23,9 @@ OPENAI_API_BASE = os.getenv("OPENAI_API_BASE") or yaml_data.get(
|
|||||||
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") or yaml_data.get(
|
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") or yaml_data.get(
|
||||||
"OPENAI_API_KEY", ""
|
"OPENAI_API_KEY", ""
|
||||||
)
|
)
|
||||||
|
OPENAI_API_MODEL = os.getenv("OPENAI_API_MODEL") or yaml_data.get(
|
||||||
|
"OPENAI_API_MODEL", ""
|
||||||
|
)
|
||||||
|
|
||||||
# Initialize OpenAI clients
|
# Initialize OpenAI clients
|
||||||
client = OpenAI(api_key=OPENAI_API_KEY, base_url=OPENAI_API_BASE)
|
client = OpenAI(api_key=OPENAI_API_KEY, base_url=OPENAI_API_BASE)
|
||||||
@@ -41,8 +46,11 @@ MISTRAL_API_KEY = os.getenv("MISTRAL_API_KEY") or yaml_data.get(
|
|||||||
|
|
||||||
# for LLM completion
|
# for LLM completion
|
||||||
def LLM_Completion(
|
def LLM_Completion(
|
||||||
messages: list[dict], stream: bool = True, useGroq: bool = True
|
messages: list[dict], stream: bool = True, useGroq: bool = True,model_config: dict = None
|
||||||
) -> str:
|
) -> str:
|
||||||
|
if model_config:
|
||||||
|
print_colored(f"Using model config: {model_config}", "blue")
|
||||||
|
return _call_with_custom_config(messages,stream,model_config)
|
||||||
if not useGroq or not FAST_DESIGN_MODE:
|
if not useGroq or not FAST_DESIGN_MODE:
|
||||||
force_gpt4 = True
|
force_gpt4 = True
|
||||||
useGroq = False
|
useGroq = False
|
||||||
@@ -75,6 +83,82 @@ def LLM_Completion(
|
|||||||
return _chat_completion(messages=messages)
|
return _chat_completion(messages=messages)
|
||||||
|
|
||||||
|
|
||||||
|
def _call_with_custom_config(messages: list[dict], stream: bool, model_config: dict) ->str:
|
||||||
|
"使用自定义配置调用API"
|
||||||
|
api_url = model_config.get("apiUrl", OPENAI_API_BASE)
|
||||||
|
api_key = model_config.get("apiKey", OPENAI_API_KEY)
|
||||||
|
api_model = model_config.get("apiModel", OPENAI_API_MODEL)
|
||||||
|
|
||||||
|
temp_client = OpenAI(api_key=api_key, base_url=api_url)
|
||||||
|
temp_async_client = AsyncOpenAI(api_key=api_key, base_url=api_url)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if stream:
|
||||||
|
try:
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
except RuntimeError as ex:
|
||||||
|
if "There is no current event loop in thread" in str(ex):
|
||||||
|
loop = asyncio.new_event_loop()
|
||||||
|
asyncio.set_event_loop(loop)
|
||||||
|
return loop.run_until_complete(
|
||||||
|
_achat_completion_stream_custom(messages=messages, temp_async_client=temp_async_client, api_model=api_model)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
response = temp_client.chat.completions.create(
|
||||||
|
messages=messages,
|
||||||
|
model=api_model,
|
||||||
|
temperature=0.3,
|
||||||
|
max_tokens=4096,
|
||||||
|
timeout=180
|
||||||
|
|
||||||
|
)
|
||||||
|
full_reply_content = response.choices[0].message.content
|
||||||
|
print(colored(full_reply_content, "blue", "on_white"), end="")
|
||||||
|
return full_reply_content
|
||||||
|
except Exception as e:
|
||||||
|
print_colored(f"Custom API error for model {api_model} :{str(e)}","red")
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
async def _achat_completion_stream_custom(messages:list[dict], temp_async_client, api_model: str ) -> str:
|
||||||
|
max_retries=3
|
||||||
|
for attempt in range(max_retries):
|
||||||
|
try:
|
||||||
|
response = await temp_async_client.chat.completions.create(
|
||||||
|
messages=messages,
|
||||||
|
model=api_model,
|
||||||
|
temperature=0.3,
|
||||||
|
max_tokens=4096,
|
||||||
|
stream=True,
|
||||||
|
timeout=180
|
||||||
|
)
|
||||||
|
|
||||||
|
collected_chunks = []
|
||||||
|
collected_messages = []
|
||||||
|
async for chunk in response:
|
||||||
|
collected_chunks.append(chunk)
|
||||||
|
choices = chunk.choices
|
||||||
|
if len(choices) > 0:
|
||||||
|
chunk_message = chunk.choices[0].delta
|
||||||
|
collected_messages.append(chunk_message)
|
||||||
|
if chunk_message.content:
|
||||||
|
print(colored(chunk_message.content, "blue", "on_white"), end="")
|
||||||
|
print()
|
||||||
|
full_reply_content = "".join(
|
||||||
|
[m.content or "" for m in collected_messages if m is not None]
|
||||||
|
)
|
||||||
|
return full_reply_content
|
||||||
|
except httpx.RemoteProtocolError as e:
|
||||||
|
if attempt < max_retries - 1:
|
||||||
|
wait_time = (attempt + 1) *2
|
||||||
|
print_colored(f"⚠️ Stream connection interrupted (attempt {attempt+1}/{max_retries}). Retrying in {wait_time}s...", text_color="yellow")
|
||||||
|
await asyncio.sleep(wait_time)
|
||||||
|
continue
|
||||||
|
except Exception as e:
|
||||||
|
print_colored(f"Custom API stream error for model {api_model} :{str(e)}","red")
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
async def _achat_completion_stream_groq(messages: list[dict]) -> str:
|
async def _achat_completion_stream_groq(messages: list[dict]) -> str:
|
||||||
from groq import AsyncGroq
|
from groq import AsyncGroq
|
||||||
groq_client = AsyncGroq(api_key=GROQ_API_KEY)
|
groq_client = AsyncGroq(api_key=GROQ_API_KEY)
|
||||||
@@ -144,7 +228,7 @@ async def _achat_completion_stream_gpt35(messages: list[dict]) -> str:
|
|||||||
messages=messages,
|
messages=messages,
|
||||||
max_tokens=4096,
|
max_tokens=4096,
|
||||||
temperature=0.3,
|
temperature=0.3,
|
||||||
timeout=30,
|
timeout=600,
|
||||||
model="gpt-3.5-turbo-16k",
|
model="gpt-3.5-turbo-16k",
|
||||||
stream=True,
|
stream=True,
|
||||||
)
|
)
|
||||||
@@ -172,16 +256,15 @@ async def _achat_completion_stream_gpt35(messages: list[dict]) -> str:
|
|||||||
return full_reply_content
|
return full_reply_content
|
||||||
|
|
||||||
|
|
||||||
async def _achat_completion_json(messages: list[dict]) -> str:
|
def _achat_completion_json(messages: list[dict] ) -> str:
|
||||||
max_attempts = 5
|
max_attempts = 5
|
||||||
|
|
||||||
for attempt in range(max_attempts):
|
for attempt in range(max_attempts):
|
||||||
try:
|
try:
|
||||||
response = await async_client.chat.completions.create(
|
response = async_client.chat.completions.create(
|
||||||
messages=messages,
|
messages=messages,
|
||||||
max_tokens=4096,
|
max_tokens=4096,
|
||||||
temperature=0.3,
|
temperature=0.3,
|
||||||
timeout=30,
|
timeout=600,
|
||||||
model=MODEL,
|
model=MODEL,
|
||||||
response_format={"type": "json_object"},
|
response_format={"type": "json_object"},
|
||||||
)
|
)
|
||||||
@@ -245,7 +328,7 @@ def _cons_kwargs(messages: list[dict]) -> dict:
|
|||||||
"messages": messages,
|
"messages": messages,
|
||||||
"max_tokens": 2000,
|
"max_tokens": 2000,
|
||||||
"temperature": 0.3,
|
"temperature": 0.3,
|
||||||
"timeout": 15,
|
"timeout": 600,
|
||||||
}
|
}
|
||||||
kwargs_mode = {"model": MODEL}
|
kwargs_mode = {"model": MODEL}
|
||||||
kwargs.update(kwargs_mode)
|
kwargs.update(kwargs_mode)
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ PROMPT_ABILITY_REQUIREMENT_GENERATION = """
|
|||||||
## Instruction
|
## Instruction
|
||||||
Based on "General Goal" and "Current Task", output a formatted "Ability Requirement" which lists at least 3 different ability requirement that is required by the "Current Task". The ability should be summarized concisely within a few words.
|
Based on "General Goal" and "Current Task", output a formatted "Ability Requirement" which lists at least 3 different ability requirement that is required by the "Current Task". The ability should be summarized concisely within a few words.
|
||||||
|
|
||||||
|
**IMPORTANT LANGUAGE REQUIREMENT: You must respond in Chinese (中文) for all ability requirements.**
|
||||||
|
|
||||||
## General Goal (The general goal for the collaboration plan, "Current Task" is just one of its substep)
|
## General Goal (The general goal for the collaboration plan, "Current Task" is just one of its substep)
|
||||||
{General_Goal}
|
{General_Goal}
|
||||||
|
|
||||||
@@ -49,6 +51,8 @@ PROMPT_AGENT_ABILITY_SCORING = """
|
|||||||
## Instruction
|
## Instruction
|
||||||
Based on "Agent Board" and "Ability Requirement", output a score for each agent to estimate the possibility that the agent can fulfil the "Ability Requirement". The score should be 1-5. Provide a concise reason before you assign the score.
|
Based on "Agent Board" and "Ability Requirement", output a score for each agent to estimate the possibility that the agent can fulfil the "Ability Requirement". The score should be 1-5. Provide a concise reason before you assign the score.
|
||||||
|
|
||||||
|
**IMPORTANT LANGUAGE REQUIREMENT: You must respond in Chinese (中文) for all reasons and explanations.**
|
||||||
|
|
||||||
## AgentBoard
|
## AgentBoard
|
||||||
{Agent_Board}
|
{Agent_Board}
|
||||||
|
|
||||||
@@ -133,5 +137,6 @@ def AgentSelectModify_init(stepTask, General_Goal, Agent_Board):
|
|||||||
|
|
||||||
|
|
||||||
def AgentSelectModify_addAspect(aspectList, Agent_Board):
|
def AgentSelectModify_addAspect(aspectList, Agent_Board):
|
||||||
scoreTable = agentAbilityScoring(Agent_Board, aspectList)
|
newAspect = aspectList[-1]
|
||||||
|
scoreTable = agentAbilityScoring(Agent_Board, [newAspect])
|
||||||
return scoreTable
|
return scoreTable
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ PROMPT_AGENT_SELECTION_GENERATION = """
|
|||||||
## Instruction
|
## Instruction
|
||||||
Based on "General Goal", "Current Task" and "Agent Board", output a formatted "Agent Selection Plan". Your selection should consider the ability needed for "Current Task" and the profile of each agent in "Agent Board".
|
Based on "General Goal", "Current Task" and "Agent Board", output a formatted "Agent Selection Plan". Your selection should consider the ability needed for "Current Task" and the profile of each agent in "Agent Board".
|
||||||
|
|
||||||
|
**IMPORTANT LANGUAGE REQUIREMENT: You must respond in Chinese (中文) for all explanations and reasoning, though agent names should remain in their original form.**
|
||||||
|
|
||||||
## General Goal (Specify the general goal for the collaboration plan)
|
## General Goal (Specify the general goal for the collaboration plan)
|
||||||
{General_Goal}
|
{General_Goal}
|
||||||
|
|
||||||
|
|||||||
@@ -1,55 +1,53 @@
|
|||||||
from AgentCoord.PlanEngine.planOutline_Generator import generate_PlanOutline
|
from AgentCoord.PlanEngine.planOutline_Generator import generate_PlanOutline
|
||||||
from AgentCoord.PlanEngine.AgentSelection_Generator import (
|
# from AgentCoord.PlanEngine.AgentSelection_Generator import (
|
||||||
generate_AgentSelection,
|
# generate_AgentSelection,
|
||||||
)
|
# )
|
||||||
from AgentCoord.PlanEngine.taskProcess_Generator import generate_TaskProcess
|
|
||||||
import AgentCoord.util as util
|
|
||||||
|
|
||||||
|
|
||||||
def generate_basePlan(
|
def generate_basePlan(
|
||||||
General_Goal, Agent_Board, AgentProfile_Dict, InitialObject_List
|
General_Goal, Agent_Board, AgentProfile_Dict, InitialObject_List
|
||||||
):
|
):
|
||||||
basePlan = {
|
"""
|
||||||
"Initial Input Object": InitialObject_List,
|
优化模式:生成大纲 + 智能体选择,但不生成任务流程
|
||||||
"Collaboration Process": [],
|
优化用户体验:
|
||||||
}
|
1. 快速生成大纲和分配智能体
|
||||||
|
2. 用户可以看到完整的大纲和智能体图标
|
||||||
|
3. TaskProcess由前端通过 fillStepTask API 异步填充
|
||||||
|
|
||||||
|
"""
|
||||||
|
# 参数保留以保持接口兼容性
|
||||||
|
_ = AgentProfile_Dict
|
||||||
PlanOutline = generate_PlanOutline(
|
PlanOutline = generate_PlanOutline(
|
||||||
InitialObject_List=[], General_Goal=General_Goal
|
InitialObject_List=InitialObject_List, General_Goal=General_Goal
|
||||||
)
|
)
|
||||||
|
|
||||||
|
basePlan = {
|
||||||
|
"General Goal": General_Goal,
|
||||||
|
"Initial Input Object": InitialObject_List,
|
||||||
|
"Collaboration Process": []
|
||||||
|
}
|
||||||
|
|
||||||
for stepItem in PlanOutline:
|
for stepItem in PlanOutline:
|
||||||
Current_Task = {
|
# # 为每个步骤分配智能体
|
||||||
"TaskName": stepItem["StepName"],
|
# Current_Task = {
|
||||||
"InputObject_List": stepItem["InputObject_List"],
|
# "TaskName": stepItem["StepName"],
|
||||||
"OutputObject": stepItem["OutputObject"],
|
# "InputObject_List": stepItem["InputObject_List"],
|
||||||
"TaskContent": stepItem["TaskContent"],
|
# "OutputObject": stepItem["OutputObject"],
|
||||||
|
# "TaskContent": stepItem["TaskContent"],
|
||||||
|
# }
|
||||||
|
# AgentSelection = generate_AgentSelection(
|
||||||
|
# General_Goal=General_Goal,
|
||||||
|
# Current_Task=Current_Task,
|
||||||
|
# Agent_Board=Agent_Board,
|
||||||
|
# )
|
||||||
|
|
||||||
|
# 添加智能体选择,但不添加任务流程
|
||||||
|
stepItem["AgentSelection"] = []
|
||||||
|
stepItem["TaskProcess"] = [] # 空数组,由前端异步填充
|
||||||
|
stepItem["Collaboration_Brief_frontEnd"] = {
|
||||||
|
"template": "",
|
||||||
|
"data": {}
|
||||||
}
|
}
|
||||||
AgentSelection = generate_AgentSelection(
|
|
||||||
General_Goal=General_Goal,
|
|
||||||
Current_Task=Current_Task,
|
|
||||||
Agent_Board=Agent_Board,
|
|
||||||
)
|
|
||||||
Current_Task_Description = {
|
|
||||||
"TaskName": stepItem["StepName"],
|
|
||||||
"AgentInvolved": [
|
|
||||||
{"Name": name, "Profile": AgentProfile_Dict[name]}
|
|
||||||
for name in AgentSelection
|
|
||||||
],
|
|
||||||
"InputObject_List": stepItem["InputObject_List"],
|
|
||||||
"OutputObject": stepItem["OutputObject"],
|
|
||||||
"CurrentTaskDescription": util.generate_template_sentence_for_CollaborationBrief(
|
|
||||||
stepItem["InputObject_List"],
|
|
||||||
stepItem["OutputObject"],
|
|
||||||
AgentSelection,
|
|
||||||
stepItem["TaskContent"],
|
|
||||||
),
|
|
||||||
}
|
|
||||||
TaskProcess = generate_TaskProcess(
|
|
||||||
General_Goal=General_Goal,
|
|
||||||
Current_Task_Description=Current_Task_Description,
|
|
||||||
)
|
|
||||||
# add the generated AgentSelection and TaskProcess to the stepItem
|
|
||||||
stepItem["AgentSelection"] = AgentSelection
|
|
||||||
stepItem["TaskProcess"] = TaskProcess
|
|
||||||
basePlan["Collaboration Process"].append(stepItem)
|
basePlan["Collaboration Process"].append(stepItem)
|
||||||
basePlan["General Goal"] = General_Goal
|
|
||||||
return basePlan
|
return basePlan
|
||||||
@@ -9,6 +9,8 @@ PROMPT_PLAN_OUTLINE_BRANCHING = """
|
|||||||
Based on "Existing Steps", your task is to comeplete the "Remaining Steps" for the plan for "General Goal".
|
Based on "Existing Steps", your task is to comeplete the "Remaining Steps" for the plan for "General Goal".
|
||||||
Note: "Modification Requirement" specifies how to modify the "Baseline Completion" for a better/alternative solution.
|
Note: "Modification Requirement" specifies how to modify the "Baseline Completion" for a better/alternative solution.
|
||||||
|
|
||||||
|
**IMPORTANT LANGUAGE REQUIREMENT: You must respond in Chinese (中文) for all content, including StepName, TaskContent, and OutputObject fields.**
|
||||||
|
|
||||||
## General Goal (Specify the general goal for the plan)
|
## General Goal (Specify the general goal for the plan)
|
||||||
{General_Goal}
|
{General_Goal}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ PROMPT_TASK_PROCESS_BRANCHING = """
|
|||||||
Based on "Existing Steps", your task is to comeplete the "Remaining Steps" for the "Task for Current Step".
|
Based on "Existing Steps", your task is to comeplete the "Remaining Steps" for the "Task for Current Step".
|
||||||
Note: "Modification Requirement" specifies how to modify the "Baseline Completion" for a better/alternative solution.
|
Note: "Modification Requirement" specifies how to modify the "Baseline Completion" for a better/alternative solution.
|
||||||
|
|
||||||
|
**IMPORTANT LANGUAGE REQUIREMENT: You must respond in Chinese (中文) for the Description field and all explanations, while keeping ID, ActionType, and AgentName in their original format.**
|
||||||
|
|
||||||
## General Goal (The general goal for the collaboration plan, you just design the plan for one of its step (i.e. "Task for Current Step"))
|
## General Goal (The general goal for the collaboration plan, you just design the plan for one of its step (i.e. "Task for Current Step"))
|
||||||
{General_Goal}
|
{General_Goal}
|
||||||
|
|
||||||
@@ -55,27 +57,40 @@ Note: "Modification Requirement" specifies how to modify the "Baseline Completio
|
|||||||
"ID": "Action4",
|
"ID": "Action4",
|
||||||
"ActionType": "Propose",
|
"ActionType": "Propose",
|
||||||
"AgentName": "Mia",
|
"AgentName": "Mia",
|
||||||
"Description": "Propose psychological theories on love and attachment that could be applied to AI's emotional development.",
|
"Description": "提议关于人工智能情感发展的心理学理论,重点关注爱与依恋的概念。",
|
||||||
"ImportantInput": [
|
"ImportantInput": [
|
||||||
"InputObject:Story Outline"
|
"InputObject:Story Outline"
|
||||||
]
|
]
|
||||||
}},
|
}},
|
||||||
{{
|
{{
|
||||||
"ID": "Action5",
|
"ID": "Action5",
|
||||||
"ActionType": "Propose",
|
"ActionType": "Critique",
|
||||||
"AgentName": "Noah",
|
"AgentName": "Noah",
|
||||||
"Description": "Propose ethical considerations and philosophical questions regarding AI's capacity for love.",
|
"Description": "对Mia提出的心理学理论进行批判性评估,分析其在AI情感发展场景中的适用性和局限性。",
|
||||||
"ImportantInput": []
|
"ImportantInput": [
|
||||||
|
"ActionResult:Action4"
|
||||||
|
]
|
||||||
}},
|
}},
|
||||||
{{
|
{{
|
||||||
"ID": "Action6",
|
"ID": "Action6",
|
||||||
"ActionType": "Finalize",
|
"ActionType": "Improve",
|
||||||
"AgentName": "Liam",
|
"AgentName": "Liam",
|
||||||
"Description": "Combine the poetic elements and ethical considerations into a cohesive set of core love elements for the story.",
|
"Description": "基于Noah的批判性反馈,改进和完善心理学理论框架,使其更贴合AI情感发展的实际需求。",
|
||||||
"ImportantInput": [
|
"ImportantInput": [
|
||||||
"ActionResult:Action1",
|
"ActionResult:Action4",
|
||||||
"ActionResult:Action5"
|
"ActionResult:Action5"
|
||||||
]
|
]
|
||||||
|
}},
|
||||||
|
{{
|
||||||
|
"ID": "Action7",
|
||||||
|
"ActionType": "Finalize",
|
||||||
|
"AgentName": "Mia",
|
||||||
|
"Description": "综合所有提议、批判和改进意见,整合并提交最终的AI情感发展心理学理论框架。",
|
||||||
|
"ImportantInput": [
|
||||||
|
"ActionResult:Action4",
|
||||||
|
"ActionResult:Action5",
|
||||||
|
"ActionResult:Action6"
|
||||||
|
]
|
||||||
}}
|
}}
|
||||||
]
|
]
|
||||||
}}
|
}}
|
||||||
@@ -84,7 +99,12 @@ Note: "Modification Requirement" specifies how to modify the "Baseline Completio
|
|||||||
ImportantInput: Specify if there is any previous result that should be taken special consideration during the execution the action. Should be of format "InputObject:xx" or "ActionResult:xx".
|
ImportantInput: Specify if there is any previous result that should be taken special consideration during the execution the action. Should be of format "InputObject:xx" or "ActionResult:xx".
|
||||||
InputObject_List: List existing objects that should be utilized in current step.
|
InputObject_List: List existing objects that should be utilized in current step.
|
||||||
AgentName: Specify the agent who will perform the action, You CAN ONLY USE THE NAME APPEARS IN "AgentInvolved".
|
AgentName: Specify the agent who will perform the action, You CAN ONLY USE THE NAME APPEARS IN "AgentInvolved".
|
||||||
ActionType: Specify the type of action, note that only the last action can be of type "Finalize", and the last action must be "Finalize".
|
ActionType: Specify the type of action. **CRITICAL REQUIREMENTS:**
|
||||||
|
1. The "Remaining Steps" MUST include ALL FOUR action types in the following order: Propose -> Critique -> Improve -> Finalize
|
||||||
|
2. Each action type (Propose, Critique, Improve, Finalize) MUST appear at least once
|
||||||
|
3. The actions must follow the sequence: Propose actions first, then Critique actions, then Improve actions, and Finalize must be the last action
|
||||||
|
4. Even if only one agent is involved in a phase, that phase must still have its corresponding action type
|
||||||
|
5. The last action must ALWAYS be of type "Finalize"
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ PROMPT_PLAN_OUTLINE_GENERATION = """
|
|||||||
## Instruction
|
## Instruction
|
||||||
Based on "Output Format Example", "General Goal", and "Initial Key Object List", output a formatted "Plan_Outline".
|
Based on "Output Format Example", "General Goal", and "Initial Key Object List", output a formatted "Plan_Outline".
|
||||||
|
|
||||||
|
**IMPORTANT LANGUAGE REQUIREMENT: You must respond in Chinese (中文) for all content, including StepName, TaskContent, and OutputObject fields.**
|
||||||
|
|
||||||
## Initial Key Object List (Specify the list of initial key objects available, each initial key object should be the input object of at least one Step)
|
## Initial Key Object List (Specify the list of initial key objects available, each initial key object should be the input object of at least one Step)
|
||||||
{InitialObject_List}
|
{InitialObject_List}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ PROMPT_TASK_PROCESS_GENERATION = """
|
|||||||
## Instruction
|
## Instruction
|
||||||
Based on "General Goal", "Task for Current Step", "Action Set" and "Output Format Example", design a plan for "Task for Current Step", output a formatted "Task_Process_Plan".
|
Based on "General Goal", "Task for Current Step", "Action Set" and "Output Format Example", design a plan for "Task for Current Step", output a formatted "Task_Process_Plan".
|
||||||
|
|
||||||
|
**IMPORTANT LANGUAGE REQUIREMENT: You must respond in Chinese (中文) for the Description field and all explanations, while keeping ID, ActionType, and AgentName in their original format.**
|
||||||
|
|
||||||
## General Goal (The general goal for the collaboration plan, you just design the plan for one of its step (i.e. "Task for Current Step"))
|
## General Goal (The general goal for the collaboration plan, you just design the plan for one of its step (i.e. "Task for Current Step"))
|
||||||
{General_Goal}
|
{General_Goal}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ You are within a multi-agent collaboration for the "Current Task".
|
|||||||
Now it's your turn to take action. Read the "Context Information" and take your action following "Instruction for Your Current Action".
|
Now it's your turn to take action. Read the "Context Information" and take your action following "Instruction for Your Current Action".
|
||||||
Note: Important Input for your action are marked with *Important Input*
|
Note: Important Input for your action are marked with *Important Input*
|
||||||
|
|
||||||
|
**IMPORTANT LANGUAGE REQUIREMENT: You must respond in Chinese (中文) for all your answers and outputs.**
|
||||||
|
|
||||||
## Context Information
|
## Context Information
|
||||||
|
|
||||||
### General Goal (The "Current Task" is indeed a substep of the general goal)
|
### General Goal (The "Current Task" is indeed a substep of the general goal)
|
||||||
@@ -81,16 +83,33 @@ class BaseAction():
|
|||||||
action_Record += PROMPT_TEMPLATE_ACTION_RECORD.format(AgentName = actionInfo["AgentName"], Action_Description = actionInfo["AgentName"], Action_Result = actionInfo["Action_Result"], Important_Mark = Important_Mark)
|
action_Record += PROMPT_TEMPLATE_ACTION_RECORD.format(AgentName = actionInfo["AgentName"], Action_Description = actionInfo["AgentName"], Action_Result = actionInfo["Action_Result"], Important_Mark = Important_Mark)
|
||||||
|
|
||||||
# Handle missing agent profiles gracefully
|
# Handle missing agent profiles gracefully
|
||||||
|
model_config = None
|
||||||
if agentName not in AgentProfile_Dict:
|
if agentName not in AgentProfile_Dict:
|
||||||
print_colored(text=f"Warning: Agent '{agentName}' not found in AgentProfile_Dict. Using default profile.", text_color="yellow")
|
print_colored(text=f"Warning: Agent '{agentName}' not found in AgentProfile_Dict. Using default profile.", text_color="yellow")
|
||||||
agentProfile = f"AI Agent named {agentName}"
|
agentProfile = f"AI Agent named {agentName}"
|
||||||
else:
|
else:
|
||||||
agentProfile = AgentProfile_Dict[agentName]
|
# agentProfile = AgentProfile_Dict[agentName]
|
||||||
|
agent_config = AgentProfile_Dict[agentName]
|
||||||
prompt = PROMPT_TEMPLATE_TAKE_ACTION_BASE.format(agentName = agentName, agentProfile = agentProfile, General_Goal = General_Goal, Current_Task_Description = TaskDescription, Input_Objects = inputObject_Record, History_Action = action_Record, Action_Description = self.info["Description"], Action_Custom_Note = self.Action_Custom_Note)
|
agentProfile = agent_config.get("profile",f"AI Agent named {agentName}")
|
||||||
|
if agent_config.get("useCustomAPI",False):
|
||||||
|
model_config = {
|
||||||
|
"apiModel":agent_config.get("apiModel"),
|
||||||
|
"apiUrl":agent_config.get("apiUrl"),
|
||||||
|
"apiKey":agent_config.get("apiKey"),
|
||||||
|
}
|
||||||
|
prompt = PROMPT_TEMPLATE_TAKE_ACTION_BASE.format(
|
||||||
|
agentName = agentName,
|
||||||
|
agentProfile = agentProfile,
|
||||||
|
General_Goal = General_Goal,
|
||||||
|
Current_Task_Description = TaskDescription,
|
||||||
|
Input_Objects = inputObject_Record,
|
||||||
|
History_Action = action_Record,
|
||||||
|
Action_Description = self.info["Description"],
|
||||||
|
Action_Custom_Note = self.Action_Custom_Note
|
||||||
|
)
|
||||||
print_colored(text = prompt, text_color="red")
|
print_colored(text = prompt, text_color="red")
|
||||||
messages = [{"role":"system", "content": prompt}]
|
messages = [{"role":"system", "content": prompt}]
|
||||||
ActionResult = LLM_Completion(messages,True,False)
|
ActionResult = LLM_Completion(messages,True,False,model_config=model_config)
|
||||||
ActionInfo_with_Result = copy.deepcopy(self.info)
|
ActionInfo_with_Result = copy.deepcopy(self.info)
|
||||||
ActionInfo_with_Result["Action_Result"] = ActionResult
|
ActionInfo_with_Result["Action_Result"] = ActionResult
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from AgentCoord.RehearsalEngine_V2.Action import BaseAction
|
from AgentCoord.RehearsalEngine_V2.Action import BaseAction
|
||||||
|
|
||||||
ACTION_CUSTOM_NOTE = '''
|
ACTION_CUSTOM_NOTE = '''
|
||||||
Note: Since you are in a conversation, your critique must be concise, clear and easy to read, don't overwhelm others. If you want to list some points, list at most 2 points.
|
注意:由于你在对话中,你的批评必须简洁、清晰且易于阅读,不要让人感到压力过大。如果你要列出一些观点,最多列出2点。
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ from AgentCoord.util.converter import read_outputObject_content
|
|||||||
from AgentCoord.RehearsalEngine_V2.Action import BaseAction
|
from AgentCoord.RehearsalEngine_V2.Action import BaseAction
|
||||||
|
|
||||||
ACTION_CUSTOM_NOTE = '''
|
ACTION_CUSTOM_NOTE = '''
|
||||||
Note: You can say something before you give the final content of {OutputName}. When you decide to give the final content of {OutputName}, it should be enclosed like this:
|
注意:你可以在给出{OutputName}的最终内容之前先说一些话。当你决定给出{OutputName}的最终内容时,应该这样包含:
|
||||||
```{OutputName}
|
```{OutputName}
|
||||||
(the content of {OutputName})
|
({OutputName}的内容)
|
||||||
```
|
```
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
from AgentCoord.RehearsalEngine_V2.Action import BaseAction
|
from AgentCoord.RehearsalEngine_V2.Action import BaseAction
|
||||||
|
|
||||||
ACTION_CUSTOM_NOTE = '''
|
ACTION_CUSTOM_NOTE = '''
|
||||||
Note: You can say something before you provide the improved version of the content.
|
注意:你可以在提供改进版本的内容之前先说一些话。
|
||||||
The improved version you provide must be a completed version (e.g. if you provide a improved story, you should give completed story content, rather than just reporting where you have improved).
|
你提供的改进版本必须是完整的版本(例如,如果你提供改进的故事,你应该给出完整的故事内容,而不仅仅是报告你在哪里改进了)。
|
||||||
When you decide to give the improved version of the content, it should be start like this:
|
当你决定提供内容的改进版本时,应该这样开始:
|
||||||
|
|
||||||
## Improved version of xxx
|
## xxx的改进版本
|
||||||
(the improved version of the content)
|
(改进版本的内容)
|
||||||
```
|
```
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
## config for default LLM
|
## config for default LLM
|
||||||
OPENAI_API_BASE: ""
|
OPENAI_API_BASE: "https://ai.gitee.com/v1"
|
||||||
OPENAI_API_KEY: ""
|
OPENAI_API_KEY: "HYCNGM39GGFNSB1F8MBBMI9QYJR3P1CRSYS2PV1A"
|
||||||
OPENAI_API_MODEL: "gpt-4-turbo-preview"
|
OPENAI_API_MODEL: "DeepSeek-V3"
|
||||||
|
|
||||||
## config for fast mode
|
## config for fast mode
|
||||||
FAST_DESIGN_MODE: True
|
FAST_DESIGN_MODE: False
|
||||||
GROQ_API_KEY: ""
|
GROQ_API_KEY: ""
|
||||||
MISTRAL_API_KEY: ""
|
MISTRAL_API_KEY: ""
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
from flask import Flask, request, jsonify
|
from flask import Flask, request, jsonify, Response, stream_with_context
|
||||||
import json
|
import json
|
||||||
from DataProcess import Add_Collaboration_Brief_FrontEnd
|
from DataProcess import Add_Collaboration_Brief_FrontEnd
|
||||||
from AgentCoord.RehearsalEngine_V2.ExecutePlan import executePlan
|
from AgentCoord.RehearsalEngine_V2.ExecutePlan import executePlan
|
||||||
|
from AgentCoord.RehearsalEngine_V2.ExecutePlan_Optimized import executePlan_streaming
|
||||||
from AgentCoord.PlanEngine.basePlan_Generator import generate_basePlan
|
from AgentCoord.PlanEngine.basePlan_Generator import generate_basePlan
|
||||||
from AgentCoord.PlanEngine.fill_stepTask import fill_stepTask
|
from AgentCoord.PlanEngine.fill_stepTask import fill_stepTask
|
||||||
from AgentCoord.PlanEngine.fill_stepTask_TaskProcess import (
|
from AgentCoord.PlanEngine.fill_stepTask_TaskProcess import (
|
||||||
@@ -257,6 +258,45 @@ def Handle_executePlan():
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/executePlanOptimized", methods=["post"])
|
||||||
|
def Handle_executePlanOptimized():
|
||||||
|
"""
|
||||||
|
优化版流式执行计划(阶段1+2:步骤级流式 + 动作级智能并行)
|
||||||
|
|
||||||
|
返回 SSE 流,每完成一个动作就返回结果
|
||||||
|
- 无依赖关系的动作并行执行
|
||||||
|
- 有依赖关系的动作串行执行
|
||||||
|
|
||||||
|
前端使用 EventSource 接收
|
||||||
|
"""
|
||||||
|
incoming_data = request.get_json()
|
||||||
|
|
||||||
|
def generate():
|
||||||
|
try:
|
||||||
|
for chunk in executePlan_streaming(
|
||||||
|
plan=incoming_data["plan"],
|
||||||
|
num_StepToRun=incoming_data.get("num_StepToRun"),
|
||||||
|
RehearsalLog=incoming_data.get("RehearsalLog", []),
|
||||||
|
AgentProfile_Dict=AgentProfile_Dict,
|
||||||
|
):
|
||||||
|
yield chunk
|
||||||
|
except Exception as e:
|
||||||
|
error_event = json.dumps({
|
||||||
|
"type": "error",
|
||||||
|
"message": str(e)
|
||||||
|
}, ensure_ascii=False)
|
||||||
|
yield f"data: {error_event}\n\n"
|
||||||
|
|
||||||
|
return Response(
|
||||||
|
stream_with_context(generate()),
|
||||||
|
mimetype="text/event-stream",
|
||||||
|
headers={
|
||||||
|
"Cache-Control": "no-cache",
|
||||||
|
"X-Accel-Buffering": "no",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/_saveRequestCashe", methods=["post"])
|
@app.route("/_saveRequestCashe", methods=["post"])
|
||||||
def Handle_saveRequestCashe():
|
def Handle_saveRequestCashe():
|
||||||
with open(
|
with open(
|
||||||
@@ -271,13 +311,29 @@ def Handle_saveRequestCashe():
|
|||||||
|
|
||||||
@app.route("/setAgents", methods=["POST"])
|
@app.route("/setAgents", methods=["POST"])
|
||||||
def set_agents():
|
def set_agents():
|
||||||
global AgentBoard, AgentProfile_Dict
|
global AgentBoard, AgentProfile_Dict,yaml_data
|
||||||
AgentBoard = request.json
|
AgentBoard = request.json
|
||||||
AgentProfile_Dict = {}
|
AgentProfile_Dict = {}
|
||||||
for item in AgentBoard:
|
for item in AgentBoard:
|
||||||
name = item["Name"]
|
name = item["Name"]
|
||||||
profile = item["Profile"]
|
if all(item.get(field) for field in ["apiUrl","apiKey","apiModel"]):
|
||||||
AgentProfile_Dict[name] = profile
|
agent_config = {
|
||||||
|
"profile": item["Profile"],
|
||||||
|
"apiUrl": item["apiUrl"],
|
||||||
|
"apiKey": item["apiKey"],
|
||||||
|
"apiModel": item["apiModel"],
|
||||||
|
"useCustomAPI":True
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
agent_config = {
|
||||||
|
"profile": item["Profile"],
|
||||||
|
"apiUrl": yaml_data.get("OPENAI_API_BASE"),
|
||||||
|
"apiKey": yaml_data.get("OPENAI_API_KEY"),
|
||||||
|
"apiModel": yaml_data.get("OPENAI_API_MODEL"),
|
||||||
|
"useCustomAPI":False
|
||||||
|
}
|
||||||
|
AgentProfile_Dict[name] = agent_config
|
||||||
|
|
||||||
return jsonify({"code": 200, "content": "set agentboard successfully"})
|
return jsonify({"code": 200, "content": "set agentboard successfully"})
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
1
frontend/.env
Normal file
@@ -0,0 +1 @@
|
|||||||
|
API_BASE=http://127.0.0.1:8000
|
||||||
6
frontend/components.d.ts
vendored
@@ -16,7 +16,13 @@ declare module 'vue' {
|
|||||||
ElCard: typeof import('element-plus/es')['ElCard']
|
ElCard: typeof import('element-plus/es')['ElCard']
|
||||||
ElCollapse: typeof import('element-plus/es')['ElCollapse']
|
ElCollapse: typeof import('element-plus/es')['ElCollapse']
|
||||||
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
|
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
|
||||||
|
ElDialog: typeof import('element-plus/es')['ElDialog']
|
||||||
|
ElDivider: typeof import('element-plus/es')['ElDivider']
|
||||||
|
ElDrawer: typeof import('element-plus/es')['ElDrawer']
|
||||||
|
ElEmpty: typeof import('element-plus/es')['ElEmpty']
|
||||||
|
ElInput: typeof import('element-plus/es')['ElInput']
|
||||||
ElPopover: typeof import('element-plus/es')['ElPopover']
|
ElPopover: typeof import('element-plus/es')['ElPopover']
|
||||||
|
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
|
||||||
ElTooltip: typeof import('element-plus/es')['ElTooltip']
|
ElTooltip: typeof import('element-plus/es')['ElTooltip']
|
||||||
MultiLineTooltip: typeof import('./src/components/MultiLineTooltip/index.vue')['default']
|
MultiLineTooltip: typeof import('./src/components/MultiLineTooltip/index.vue')['default']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<html lang="">
|
<html lang="">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<link rel="icon" href="/logo.jpg">
|
<link rel="icon" href="/logo.png">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>多智能体协同平台</title>
|
<title>多智能体协同平台</title>
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@@ -17,8 +17,13 @@
|
|||||||
"format": "prettier --write src/"
|
"format": "prettier --write src/"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@element-plus/icons-vue": "^2.3.2",
|
||||||
"@jsplumb/browser-ui": "^6.2.10",
|
"@jsplumb/browser-ui": "^6.2.10",
|
||||||
"@types/markdown-it": "^14.1.2",
|
"@types/markdown-it": "^14.1.2",
|
||||||
|
"@vue-flow/background": "^1.3.2",
|
||||||
|
"@vue-flow/controls": "^1.1.3",
|
||||||
|
"@vue-flow/core": "^1.48.1",
|
||||||
|
"@vue-flow/minimap": "^1.5.4",
|
||||||
"@vueuse/core": "^14.0.0",
|
"@vueuse/core": "^14.0.0",
|
||||||
"axios": "^1.12.2",
|
"axios": "^1.12.2",
|
||||||
"dompurify": "^3.3.0",
|
"dompurify": "^3.3.0",
|
||||||
|
|||||||
9052
frontend/pnpm-lock.yaml
generated
@@ -15,5 +15,6 @@
|
|||||||
],
|
],
|
||||||
"agentRepository": {
|
"agentRepository": {
|
||||||
"storageVersionIdentifier": "1"
|
"storageVersionIdentifier": "1"
|
||||||
}
|
},
|
||||||
|
"dev": true
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
frontend/public/logo.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
42
frontend/src/ directive/devOnly/index.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import type { Directive, DirectiveBinding } from 'vue'
|
||||||
|
import { useConfigStoreHook } from '@/stores'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开发模式专用指令
|
||||||
|
* 只在开发模式下显示元素,生产模式下会移除该元素
|
||||||
|
*
|
||||||
|
* \@param binding.value - 是否开启该功能,默认为 true
|
||||||
|
* @example
|
||||||
|
* \!-- 默认开启,开发模式显示 --
|
||||||
|
* \!div v-dev-only开发模式内容</div>
|
||||||
|
*
|
||||||
|
* \!-- 传入参数控制 --
|
||||||
|
* <div v-dev-only="true">开启指令</div>
|
||||||
|
* <div v-dev-only="false">取消指令</div>
|
||||||
|
*/
|
||||||
|
export const devOnly: Directive = {
|
||||||
|
mounted(el: HTMLElement, binding: DirectiveBinding) {
|
||||||
|
checkAndRemoveElement(el, binding)
|
||||||
|
},
|
||||||
|
|
||||||
|
updated(el: HTMLElement, binding: DirectiveBinding) {
|
||||||
|
checkAndRemoveElement(el, binding)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const configStore = useConfigStoreHook()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查并移除元素的逻辑
|
||||||
|
*/
|
||||||
|
function checkAndRemoveElement(el: HTMLElement, binding: DirectiveBinding) {
|
||||||
|
const isDev = typeof configStore.config.dev === 'boolean' ? configStore.config.dev : import.meta.env.DEV
|
||||||
|
// 默认值为 true,如果没有传值或者传值为 true 都启用
|
||||||
|
const shouldEnable = binding.value !== false
|
||||||
|
// 如果不是开发模式或者明确禁用,移除该元素
|
||||||
|
if (!isDev && shouldEnable) {
|
||||||
|
if (el.parentNode) {
|
||||||
|
el.parentNode.removeChild(el)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
frontend/src/ directive/index.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import type { App } from 'vue'
|
||||||
|
|
||||||
|
import { devOnly } from './devOnly'
|
||||||
|
|
||||||
|
|
||||||
|
// 全局注册 directive
|
||||||
|
export function setupDirective(app: App<Element>) {
|
||||||
|
app.directive('dev-only', devOnly)
|
||||||
|
}
|
||||||
@@ -1,15 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted } from 'vue'
|
|
||||||
|
|
||||||
import Layout from './layout/index.vue'
|
import Layout from './layout/index.vue'
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
document.documentElement.classList.add('dark')
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<layout />
|
<Layout />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@@ -29,11 +23,12 @@ onMounted(() => {
|
|||||||
.el-card {
|
.el-card {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background: var(--color-bg-tertiary);
|
background: var(--color-bg-tertiary);
|
||||||
border: 2px solid var(--color-bg-tertiary);
|
border: 2px solid var(--color-card-border);
|
||||||
|
box-shadow: var(--color-card-border-hover);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #171B22;
|
background: var(--color-bg-content-hover);
|
||||||
box-shadow: none !important;
|
box-shadow: none;
|
||||||
transition: background-color 0.3s ease-in-out;
|
transition: background-color 0.3s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,14 +45,16 @@ onMounted(() => {
|
|||||||
.active-card {
|
.active-card {
|
||||||
border: 2px solid transparent;
|
border: 2px solid transparent;
|
||||||
$bg: var(--el-input-bg-color, var(--el-fill-color-blank));
|
$bg: var(--el-input-bg-color, var(--el-fill-color-blank));
|
||||||
background:
|
background: linear-gradient(
|
||||||
linear-gradient(var(--color-bg-tertiary), var(--color-bg-tertiary)) padding-box,
|
var(--color-agent-list-selected-bg),
|
||||||
|
var(--color-agent-list-selected-bg)
|
||||||
|
)
|
||||||
|
padding-box,
|
||||||
linear-gradient(to right, #00c8d2, #315ab4) border-box;
|
linear-gradient(to right, #00c8d2, #315ab4) border-box;
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* 1. 定义流动动画:让虚线沿路径移动 */
|
/* 1. 定义流动动画:让虚线沿路径移动 */
|
||||||
@keyframes flowAnimation {
|
@keyframes flowAnimation {
|
||||||
to {
|
to {
|
||||||
@@ -71,14 +68,13 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* 2. 为jsPlumb连线绑定动画:作用于SVG的path元素 */
|
/* 2. 为jsPlumb连线绑定动画:作用于SVG的path元素 */
|
||||||
/* jtk-connector是jsPlumb连线的默认SVG类,path是实际的线条元素 */
|
/* jtk-connector是jsPlumb连线的默认SVG类,path是实际的线条元素 */
|
||||||
.jtk-connector-output path {
|
.jtk-connector-output path {
|
||||||
/* 定义虚线规则:线段长度5px + 间隙3px(总长度8px,与动画偏移量匹配) */
|
/* 定义虚线规则:线段长度5px + 间隙3px(总长度8px,与动画偏移量匹配) */
|
||||||
stroke-dasharray: 5 3;
|
stroke-dasharray: 5 3;
|
||||||
/* 应用动画:名称+时长+线性速度+无限循环 */
|
/* 应用动画:名称+时长+线性速度+无限循环 */
|
||||||
animation: flowAnimationReverse .5s linear infinite;
|
animation: flowAnimationReverse 0.5s linear infinite;
|
||||||
/* 可选:设置线条基础样式(颜色、宽度) */
|
/* 可选:设置线条基础样式(颜色、宽度) */
|
||||||
stroke-width: 2;
|
stroke-width: 2;
|
||||||
}
|
}
|
||||||
@@ -89,7 +85,7 @@ onMounted(() => {
|
|||||||
/* 定义虚线规则:线段长度5px + 间隙3px(总长度8px,与动画偏移量匹配) */
|
/* 定义虚线规则:线段长度5px + 间隙3px(总长度8px,与动画偏移量匹配) */
|
||||||
stroke-dasharray: 5 3;
|
stroke-dasharray: 5 3;
|
||||||
/* 应用动画:名称+时长+线性速度+无限循环 */
|
/* 应用动画:名称+时长+线性速度+无限循环 */
|
||||||
animation: flowAnimationReverse .5s linear infinite;
|
animation: flowAnimationReverse 0.5s linear infinite;
|
||||||
/* 可选:设置线条基础样式(颜色、宽度) */
|
/* 可选:设置线条基础样式(颜色、宽度) */
|
||||||
stroke-width: 2;
|
stroke-width: 2;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,20 @@
|
|||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
import type { Agent, IRawPlanResponse } from '@/stores'
|
import type { Agent, IApiStepTask, IRawPlanResponse, IRawStepTask } from '@/stores'
|
||||||
|
import {
|
||||||
|
mockBackendAgentSelectModifyInit,
|
||||||
|
mockBackendAgentSelectModifyAddAspect,
|
||||||
|
type BackendAgentScoreResponse,
|
||||||
|
} from '@/layout/components/Main/TaskTemplate/TaskSyllabus/components/mock/AgentAssignmentBackendMock'
|
||||||
|
import {
|
||||||
|
mockBackendFillAgentTaskProcess,
|
||||||
|
type RawAgentTaskProcessResponse,
|
||||||
|
} from '@/layout/components/Main/TaskTemplate/TaskProcess/components/mock/AgentTaskProcessBackendMock'
|
||||||
|
import { mockBranchPlanOutlineAPI } from '@/layout/components/Main/TaskTemplate/TaskSyllabus/Branch/mock/branchPlanOutlineMock'
|
||||||
|
import { mockFillStepTaskAPI } from '@/layout/components/Main/TaskTemplate/TaskSyllabus/Branch/mock/fill-step-task-mock'
|
||||||
|
import {
|
||||||
|
mockBranchTaskProcessAPI,
|
||||||
|
type BranchAction,
|
||||||
|
} from '@/layout/components/Main/TaskTemplate/TaskSyllabus/Branch/mock/branchTaskProcessMock'
|
||||||
|
|
||||||
export interface ActionHistory {
|
export interface ActionHistory {
|
||||||
ID: string
|
ID: string
|
||||||
@@ -19,9 +34,55 @@ export type IExecuteRawResponse = {
|
|||||||
ActionHistory: ActionHistory[]
|
ActionHistory: ActionHistory[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSE 流式事件类型
|
||||||
|
*/
|
||||||
|
export type StreamingEvent =
|
||||||
|
| {
|
||||||
|
type: 'step_start'
|
||||||
|
step_index: number
|
||||||
|
total_steps: number
|
||||||
|
step_name: string
|
||||||
|
task_description?: string
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'action_complete'
|
||||||
|
step_index: number
|
||||||
|
step_name: string
|
||||||
|
action_index: number
|
||||||
|
total_actions: number
|
||||||
|
completed_actions: number
|
||||||
|
action_result: ActionHistory
|
||||||
|
batch_info?: {
|
||||||
|
batch_index: number
|
||||||
|
batch_size: number
|
||||||
|
is_parallel: boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'step_complete'
|
||||||
|
step_index: number
|
||||||
|
step_name: string
|
||||||
|
step_log_node: any
|
||||||
|
object_log_node: any
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'execution_complete'
|
||||||
|
total_steps: number
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'error'
|
||||||
|
message: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IFillAgentSelectionRequest {
|
||||||
|
goal: string
|
||||||
|
stepTask: IApiStepTask
|
||||||
|
agents: string[]
|
||||||
|
}
|
||||||
|
|
||||||
class Api {
|
class Api {
|
||||||
// 智能体信息
|
setAgents = (data: Pick<Agent, 'Name' | 'Profile' | 'apiUrl' | 'apiKey' | 'apiModel'>[]) => {
|
||||||
setAgents = (data: Pick<Agent, 'Name' | 'Profile'>[]) => {
|
|
||||||
return request({
|
return request({
|
||||||
url: '/setAgents',
|
url: '/setAgents',
|
||||||
data,
|
data,
|
||||||
@@ -29,13 +90,22 @@ class Api {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
generateBasePlan = (data: { goal: string; inputs: string[] }) => {
|
generateBasePlan = (data: {
|
||||||
|
goal: string
|
||||||
|
inputs: string[]
|
||||||
|
apiUrl?: string
|
||||||
|
apiKey?: string
|
||||||
|
apiModel?: string
|
||||||
|
}) => {
|
||||||
return request<unknown, IRawPlanResponse>({
|
return request<unknown, IRawPlanResponse>({
|
||||||
url: '/generate_basePlan',
|
url: '/generate_basePlan',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: {
|
data: {
|
||||||
'General Goal': data.goal,
|
'General Goal': data.goal,
|
||||||
'Initial Input Object': data.inputs,
|
'Initial Input Object': data.inputs,
|
||||||
|
apiUrl: data.apiUrl,
|
||||||
|
apiKey: data.apiKey,
|
||||||
|
apiModel: data.apiModel,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -50,14 +120,14 @@ class Api {
|
|||||||
plan: {
|
plan: {
|
||||||
'Initial Input Object': plan['Initial Input Object'],
|
'Initial Input Object': plan['Initial Input Object'],
|
||||||
'General Goal': plan['General Goal'],
|
'General Goal': plan['General Goal'],
|
||||||
'Collaboration Process': plan['Collaboration Process']?.map(step => ({
|
'Collaboration Process': plan['Collaboration Process']?.map((step) => ({
|
||||||
StepName: step.StepName,
|
StepName: step.StepName,
|
||||||
TaskContent: step.TaskContent,
|
TaskContent: step.TaskContent,
|
||||||
InputObject_List: step.InputObject_List,
|
InputObject_List: step.InputObject_List,
|
||||||
OutputObject: step.OutputObject,
|
OutputObject: step.OutputObject,
|
||||||
AgentSelection: step.AgentSelection,
|
AgentSelection: step.AgentSelection,
|
||||||
Collaboration_Brief_frontEnd: step.Collaboration_Brief_FrontEnd,
|
Collaboration_Brief_frontEnd: step.Collaboration_Brief_frontEnd,
|
||||||
TaskProcess: step.TaskProcess.map(action => ({
|
TaskProcess: step.TaskProcess.map((action) => ({
|
||||||
ActionType: action.ActionType,
|
ActionType: action.ActionType,
|
||||||
AgentName: action.AgentName,
|
AgentName: action.AgentName,
|
||||||
Description: action.Description,
|
Description: action.Description,
|
||||||
@@ -69,6 +139,546 @@ class Api {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 优化版流式执行计划(阶段1+2:步骤级流式 + 动作级智能并行)
|
||||||
|
* 无依赖关系的动作并行执行,有依赖关系的动作串行执行
|
||||||
|
*/
|
||||||
|
executePlanOptimized = (
|
||||||
|
plan: IRawPlanResponse,
|
||||||
|
onMessage: (event: StreamingEvent) => void,
|
||||||
|
onError?: (error: Error) => void,
|
||||||
|
onComplete?: () => void,
|
||||||
|
) => {
|
||||||
|
const data = {
|
||||||
|
RehearsalLog: [],
|
||||||
|
num_StepToRun: null,
|
||||||
|
plan: {
|
||||||
|
'Initial Input Object': plan['Initial Input Object'],
|
||||||
|
'General Goal': plan['General Goal'],
|
||||||
|
'Collaboration Process': plan['Collaboration Process']?.map((step) => ({
|
||||||
|
StepName: step.StepName,
|
||||||
|
TaskContent: step.TaskContent,
|
||||||
|
InputObject_List: step.InputObject_List,
|
||||||
|
OutputObject: step.OutputObject,
|
||||||
|
AgentSelection: step.AgentSelection,
|
||||||
|
Collaboration_Brief_frontEnd: step.Collaboration_Brief_frontEnd,
|
||||||
|
TaskProcess: step.TaskProcess.map((action) => ({
|
||||||
|
ActionType: action.ActionType,
|
||||||
|
AgentName: action.AgentName,
|
||||||
|
Description: action.Description,
|
||||||
|
ID: action.ID,
|
||||||
|
ImportantInput: action.ImportantInput,
|
||||||
|
})),
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch('/api/executePlanOptimized', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
})
|
||||||
|
.then(async (response) => {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const reader = response.body?.getReader()
|
||||||
|
const decoder = new TextDecoder()
|
||||||
|
|
||||||
|
if (!reader) {
|
||||||
|
throw new Error('Response body is null')
|
||||||
|
}
|
||||||
|
|
||||||
|
let buffer = ''
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read()
|
||||||
|
|
||||||
|
if (done) {
|
||||||
|
onComplete?.()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer += decoder.decode(value, { stream: true })
|
||||||
|
|
||||||
|
const lines = buffer.split('\n')
|
||||||
|
buffer = lines.pop() || ''
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.startsWith('data: ')) {
|
||||||
|
const data = line.slice(6)
|
||||||
|
try {
|
||||||
|
const event = JSON.parse(data)
|
||||||
|
onMessage(event)
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to parse SSE data:', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
onError?.(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分支任务大纲
|
||||||
|
*/
|
||||||
|
branchPlanOutline = (data: {
|
||||||
|
branch_Number: number
|
||||||
|
Modification_Requirement: string
|
||||||
|
Existing_Steps: IRawStepTask[]
|
||||||
|
Baseline_Completion: number
|
||||||
|
initialInputs: string[]
|
||||||
|
goal: string
|
||||||
|
}) => {
|
||||||
|
return request<unknown, IRawPlanResponse>({
|
||||||
|
url: '/branch_PlanOutline',
|
||||||
|
method: 'POST',
|
||||||
|
data: {
|
||||||
|
branch_Number: data.branch_Number,
|
||||||
|
Modification_Requirement: data.Modification_Requirement,
|
||||||
|
Existing_Steps: data.Existing_Steps,
|
||||||
|
Baseline_Completion: data.Baseline_Completion,
|
||||||
|
'Initial Input Object': data.initialInputs,
|
||||||
|
'General Goal': data.goal,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分支任务流程
|
||||||
|
*/
|
||||||
|
branchTaskProcess = (data: {
|
||||||
|
branch_Number: number
|
||||||
|
Modification_Requirement: string
|
||||||
|
Existing_Steps: BranchAction[]
|
||||||
|
Baseline_Completion: number
|
||||||
|
stepTaskExisting: any
|
||||||
|
goal: string
|
||||||
|
}) => {
|
||||||
|
return request<unknown, BranchAction[][]>({
|
||||||
|
url: '/branch_TaskProcess',
|
||||||
|
method: 'POST',
|
||||||
|
data: {
|
||||||
|
branch_Number: data.branch_Number,
|
||||||
|
Modification_Requirement: data.Modification_Requirement,
|
||||||
|
Existing_Steps: data.Existing_Steps,
|
||||||
|
Baseline_Completion: data.Baseline_Completion,
|
||||||
|
stepTaskExisting: data.stepTaskExisting,
|
||||||
|
'General Goal': data.goal,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fillStepTask = async (data: { goal: string; stepTask: any }): Promise<IRawStepTask> => {
|
||||||
|
const response = await request<
|
||||||
|
{
|
||||||
|
'General Goal': string
|
||||||
|
stepTask: any
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AgentSelection?: string[]
|
||||||
|
Collaboration_Brief_FrontEnd?: {
|
||||||
|
template: string
|
||||||
|
data: Record<string, { text: string; color: number[] }>
|
||||||
|
}
|
||||||
|
InputObject_List?: string[]
|
||||||
|
OutputObject?: string
|
||||||
|
StepName?: string
|
||||||
|
TaskContent?: string
|
||||||
|
TaskProcess?: Array<{
|
||||||
|
ID: string
|
||||||
|
ActionType: string
|
||||||
|
AgentName: string
|
||||||
|
Description: string
|
||||||
|
ImportantInput: string[]
|
||||||
|
}>
|
||||||
|
}
|
||||||
|
>({
|
||||||
|
url: '/fill_stepTask',
|
||||||
|
method: 'POST',
|
||||||
|
data: {
|
||||||
|
'General Goal': data.goal,
|
||||||
|
stepTask: data.stepTask,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const vec2Hsl = (color: number[]): string => {
|
||||||
|
const [h, s, l] = color
|
||||||
|
return `hsl(${h}, ${s}%, ${l}%)`
|
||||||
|
}
|
||||||
|
|
||||||
|
const briefData: Record<string, { text: string; style?: Record<string, string> }> = {}
|
||||||
|
if (response.Collaboration_Brief_FrontEnd?.data) {
|
||||||
|
for (const [key, value] of Object.entries(response.Collaboration_Brief_FrontEnd.data)) {
|
||||||
|
briefData[key] = {
|
||||||
|
text: value.text,
|
||||||
|
style: {
|
||||||
|
background: vec2Hsl(value.color),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建前端格式的 IRawStepTask
|
||||||
|
*/
|
||||||
|
return {
|
||||||
|
StepName: response.StepName || '',
|
||||||
|
TaskContent: response.TaskContent || '',
|
||||||
|
InputObject_List: response.InputObject_List || [],
|
||||||
|
OutputObject: response.OutputObject || '',
|
||||||
|
AgentSelection: response.AgentSelection || [],
|
||||||
|
Collaboration_Brief_frontEnd: {
|
||||||
|
template: response.Collaboration_Brief_FrontEnd?.template || '',
|
||||||
|
data: briefData,
|
||||||
|
},
|
||||||
|
TaskProcess: response.TaskProcess || [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fillStepTaskTaskProcess = async (data: {
|
||||||
|
goal: string
|
||||||
|
stepTask: IApiStepTask
|
||||||
|
agents: string[]
|
||||||
|
}): Promise<IApiStepTask> => {
|
||||||
|
const response = await request<
|
||||||
|
{
|
||||||
|
'General Goal': string
|
||||||
|
stepTask_lackTaskProcess: {
|
||||||
|
StepName: string
|
||||||
|
TaskContent: string
|
||||||
|
InputObject_List: string[]
|
||||||
|
OutputObject: string
|
||||||
|
AgentSelection: string[]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StepName?: string
|
||||||
|
TaskContent?: string
|
||||||
|
InputObject_List?: string[]
|
||||||
|
OutputObject?: string
|
||||||
|
AgentSelection?: string[]
|
||||||
|
TaskProcess?: Array<{
|
||||||
|
ID: string
|
||||||
|
ActionType: string
|
||||||
|
AgentName: string
|
||||||
|
Description: string
|
||||||
|
ImportantInput: string[]
|
||||||
|
}>
|
||||||
|
Collaboration_Brief_FrontEnd?: {
|
||||||
|
template: string
|
||||||
|
data: Record<string, { text: string; color: number[] }>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>({
|
||||||
|
url: '/fill_stepTask_TaskProcess',
|
||||||
|
method: 'POST',
|
||||||
|
data: {
|
||||||
|
'General Goal': data.goal,
|
||||||
|
stepTask_lackTaskProcess: {
|
||||||
|
StepName: data.stepTask.name,
|
||||||
|
TaskContent: data.stepTask.content,
|
||||||
|
InputObject_List: data.stepTask.inputs,
|
||||||
|
OutputObject: data.stepTask.output,
|
||||||
|
AgentSelection: data.agents,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const vec2Hsl = (color: number[]): string => {
|
||||||
|
const [h, s, l] = color
|
||||||
|
return `hsl(${h}, ${s}%, ${l}%)`
|
||||||
|
}
|
||||||
|
|
||||||
|
const briefData: Record<string, { text: string; style: { background: string } }> = {}
|
||||||
|
if (response.Collaboration_Brief_FrontEnd?.data) {
|
||||||
|
for (const [key, value] of Object.entries(response.Collaboration_Brief_FrontEnd.data)) {
|
||||||
|
briefData[key] = {
|
||||||
|
text: value.text,
|
||||||
|
style: {
|
||||||
|
background: vec2Hsl(value.color),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const process = (response.TaskProcess || []).map((action) => ({
|
||||||
|
id: action.ID,
|
||||||
|
type: action.ActionType,
|
||||||
|
agent: action.AgentName,
|
||||||
|
description: action.Description,
|
||||||
|
inputs: action.ImportantInput,
|
||||||
|
}))
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: response.StepName || '',
|
||||||
|
content: response.TaskContent || '',
|
||||||
|
inputs: response.InputObject_List || [],
|
||||||
|
output: response.OutputObject || '',
|
||||||
|
agents: response.AgentSelection || [],
|
||||||
|
brief: {
|
||||||
|
template: response.Collaboration_Brief_FrontEnd?.template || '',
|
||||||
|
data: briefData,
|
||||||
|
},
|
||||||
|
process,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为每个智能体评分
|
||||||
|
*/
|
||||||
|
agentSelectModifyInit = async (data: {
|
||||||
|
goal: string
|
||||||
|
stepTask: any
|
||||||
|
}): Promise<Record<string, Record<string, { reason: string; score: number }>>> => {
|
||||||
|
const response = await request<
|
||||||
|
{
|
||||||
|
'General Goal': string
|
||||||
|
stepTask: any
|
||||||
|
},
|
||||||
|
Record<string, Record<string, { Reason: string; Score: number }>>
|
||||||
|
>({
|
||||||
|
url: '/agentSelectModify_init',
|
||||||
|
method: 'POST',
|
||||||
|
data: {
|
||||||
|
'General Goal': data.goal,
|
||||||
|
stepTask: {
|
||||||
|
StepName: data.stepTask.StepName || data.stepTask.name,
|
||||||
|
TaskContent: data.stepTask.TaskContent || data.stepTask.content,
|
||||||
|
InputObject_List: data.stepTask.InputObject_List || data.stepTask.inputs,
|
||||||
|
OutputObject: data.stepTask.OutputObject || data.stepTask.output,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const transformedData: Record<string, Record<string, { reason: string; score: number }>> = {}
|
||||||
|
|
||||||
|
for (const [aspect, agents] of Object.entries(response)) {
|
||||||
|
for (const [agentName, scoreInfo] of Object.entries(agents)) {
|
||||||
|
if (!transformedData[agentName]) {
|
||||||
|
transformedData[agentName] = {}
|
||||||
|
}
|
||||||
|
transformedData[agentName][aspect] = {
|
||||||
|
reason: scoreInfo.Reason,
|
||||||
|
score: scoreInfo.Score,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return transformedData
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加新的评估维度
|
||||||
|
*/
|
||||||
|
agentSelectModifyAddAspect = async (data: {
|
||||||
|
aspectList: string[]
|
||||||
|
}): Promise<{
|
||||||
|
aspectName: string
|
||||||
|
agentScores: Record<string, { score: number; reason: string }>
|
||||||
|
}> => {
|
||||||
|
const response = await request<
|
||||||
|
{
|
||||||
|
aspectList: string[]
|
||||||
|
},
|
||||||
|
Record<string, Record<string, { Reason: string; Score: number }>>
|
||||||
|
>({
|
||||||
|
url: '/agentSelectModify_addAspect',
|
||||||
|
method: 'POST',
|
||||||
|
data: {
|
||||||
|
aspectList: data.aspectList,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取新添加的维度
|
||||||
|
*/
|
||||||
|
const newAspect = data.aspectList[data.aspectList.length - 1]
|
||||||
|
if (!newAspect) {
|
||||||
|
throw new Error('aspectList is empty')
|
||||||
|
}
|
||||||
|
|
||||||
|
const newAspectAgents = response[newAspect]
|
||||||
|
const agentScores: Record<string, { score: number; reason: string }> = {}
|
||||||
|
|
||||||
|
if (newAspectAgents) {
|
||||||
|
for (const [agentName, scoreInfo] of Object.entries(newAspectAgents)) {
|
||||||
|
agentScores[agentName] = {
|
||||||
|
score: scoreInfo.Score,
|
||||||
|
reason: scoreInfo.Reason,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
aspectName: newAspect,
|
||||||
|
agentScores,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* ==================== Mock API(开发阶段使用)====================
|
||||||
|
*为每个智能体评分
|
||||||
|
*/
|
||||||
|
mockAgentSelectModifyInit = async (): Promise<
|
||||||
|
Record<string, Record<string, { reason: string; score: number }>>
|
||||||
|
> => {
|
||||||
|
const response: BackendAgentScoreResponse = await mockBackendAgentSelectModifyInit()
|
||||||
|
|
||||||
|
const transformedData: Record<string, Record<string, { reason: string; score: number }>> = {}
|
||||||
|
|
||||||
|
for (const [aspect, agents] of Object.entries(response)) {
|
||||||
|
for (const [agentName, scoreInfo] of Object.entries(agents)) {
|
||||||
|
if (!transformedData[agentName]) {
|
||||||
|
transformedData[agentName] = {}
|
||||||
|
}
|
||||||
|
transformedData[agentName][aspect] = {
|
||||||
|
reason: scoreInfo.Reason,
|
||||||
|
score: scoreInfo.Score,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return transformedData
|
||||||
|
}
|
||||||
|
|
||||||
|
mockAgentSelectModifyAddAspect = async (data: {
|
||||||
|
aspectList: string[]
|
||||||
|
}): Promise<{
|
||||||
|
aspectName: string
|
||||||
|
agentScores: Record<string, { score: number; reason: string }>
|
||||||
|
}> => {
|
||||||
|
const response: BackendAgentScoreResponse = await mockBackendAgentSelectModifyAddAspect(
|
||||||
|
data.aspectList,
|
||||||
|
)
|
||||||
|
|
||||||
|
const newAspect = data.aspectList[data.aspectList.length - 1]
|
||||||
|
if (!newAspect) {
|
||||||
|
throw new Error('aspectList is empty')
|
||||||
|
}
|
||||||
|
|
||||||
|
const newAspectAgents = response[newAspect]
|
||||||
|
const agentScores: Record<string, { score: number; reason: string }> = {}
|
||||||
|
|
||||||
|
if (newAspectAgents) {
|
||||||
|
for (const [agentName, scoreInfo] of Object.entries(newAspectAgents)) {
|
||||||
|
agentScores[agentName] = {
|
||||||
|
score: scoreInfo.Score,
|
||||||
|
reason: scoreInfo.Reason,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
aspectName: newAspect,
|
||||||
|
agentScores,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mockFillStepTaskTaskProcess = async (data: {
|
||||||
|
goal: string
|
||||||
|
stepTask: IApiStepTask
|
||||||
|
agents: string[]
|
||||||
|
}): Promise<IApiStepTask> => {
|
||||||
|
const response: RawAgentTaskProcessResponse = await mockBackendFillAgentTaskProcess(
|
||||||
|
data.goal,
|
||||||
|
data.stepTask,
|
||||||
|
data.agents,
|
||||||
|
)
|
||||||
|
|
||||||
|
const vec2Hsl = (color: number[]): string => {
|
||||||
|
const [h, s, l] = color
|
||||||
|
return `hsl(${h}, ${s}%, ${l}%)`
|
||||||
|
}
|
||||||
|
|
||||||
|
const briefData: Record<string, { text: string; style: { background: string } }> = {}
|
||||||
|
if (response.Collaboration_Brief_frontEnd?.data) {
|
||||||
|
for (const [key, value] of Object.entries(response.Collaboration_Brief_frontEnd.data)) {
|
||||||
|
briefData[key] = {
|
||||||
|
text: value.text,
|
||||||
|
style: {
|
||||||
|
background: vec2Hsl(value.color),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const process = (response.TaskProcess || []).map((action) => ({
|
||||||
|
id: action.ID,
|
||||||
|
type: action.ActionType,
|
||||||
|
agent: action.AgentName,
|
||||||
|
description: action.Description,
|
||||||
|
inputs: action.ImportantInput,
|
||||||
|
}))
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: response.StepName || '',
|
||||||
|
content: response.TaskContent || '',
|
||||||
|
inputs: response.InputObject_List || [],
|
||||||
|
output: response.OutputObject || '',
|
||||||
|
agents: response.AgentSelection || [],
|
||||||
|
brief: {
|
||||||
|
template: response.Collaboration_Brief_frontEnd?.template || '',
|
||||||
|
data: briefData,
|
||||||
|
},
|
||||||
|
process,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mockBranchPlanOutline = async (data: {
|
||||||
|
branch_Number: number
|
||||||
|
Modification_Requirement: string
|
||||||
|
Existing_Steps: IRawStepTask[]
|
||||||
|
Baseline_Completion: number
|
||||||
|
initialInputs: string[]
|
||||||
|
goal: string
|
||||||
|
}): Promise<IRawPlanResponse> => {
|
||||||
|
const response = await mockBranchPlanOutlineAPI({
|
||||||
|
branch_Number: data.branch_Number,
|
||||||
|
Modification_Requirement: data.Modification_Requirement,
|
||||||
|
Existing_Steps: data.Existing_Steps,
|
||||||
|
Baseline_Completion: data.Baseline_Completion,
|
||||||
|
InitialObject_List: data.initialInputs,
|
||||||
|
General_Goal: data.goal,
|
||||||
|
})
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
mockFillStepTask = async (data: { goal: string; stepTask: any }): Promise<any> => {
|
||||||
|
const response = await mockFillStepTaskAPI({
|
||||||
|
General_Goal: data.goal,
|
||||||
|
stepTask: data.stepTask,
|
||||||
|
})
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
mockBranchTaskProcess = async (data: {
|
||||||
|
branch_Number: number
|
||||||
|
Modification_Requirement: string
|
||||||
|
Existing_Steps: BranchAction[]
|
||||||
|
Baseline_Completion: number
|
||||||
|
stepTaskExisting: any
|
||||||
|
goal: string
|
||||||
|
}): Promise<BranchAction[][]> => {
|
||||||
|
const response = await mockBranchTaskProcessAPI({
|
||||||
|
branch_Number: data.branch_Number,
|
||||||
|
Modification_Requirement: data.Modification_Requirement,
|
||||||
|
Existing_Steps: data.Existing_Steps,
|
||||||
|
Baseline_Completion: data.Baseline_Completion,
|
||||||
|
stepTaskExisting: data.stepTaskExisting,
|
||||||
|
General_Goal: data.goal,
|
||||||
|
})
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new Api()
|
export default new Api()
|
||||||
|
|||||||
20
frontend/src/assets/icons/Cancel.svg
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||||
|
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg t="1766247549273" class="icon"
|
||||||
|
viewBox="0 0 1024 1024" version="1.1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
p-id="10050" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
width="16" height="16">
|
||||||
|
<path d="M512 1024C229.248 1024
|
||||||
|
0 794.752 0 512S229.248 0 512 0s512
|
||||||
|
229.248 512 512-229.248 512-512 512z
|
||||||
|
m0-938.666667C276.352 85.333333 85.333333
|
||||||
|
276.352 85.333333 512s191.018667 426.666667
|
||||||
|
426.666667 426.666667 426.666667-191.018667
|
||||||
|
426.666667-426.666667S747.648 85.333333 512
|
||||||
|
85.333333z m198.698667 625.365334a42.666667
|
||||||
|
42.666667 0 0 1-60.330667
|
||||||
|
0L512 572.330667l-138.368 138.368a42.666667 42.666667 0 0 1-60.330667-60.330667L451.669333 512 313.301333 373.632a42.666667 42.666667 0 0 1 60.330667-60.330667L512 451.669333l138.368-138.368a42.624 42.624 0 1 1 60.330667 60.330667L572.330667 512l138.368 138.368a42.666667 42.666667 0 0 1 0 60.330667z"
|
||||||
|
fill="#8e0707" p-id="10051">
|
||||||
|
</path></svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
7
frontend/src/assets/icons/Check.svg
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg t="1766247454540" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9049"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16">
|
||||||
|
<path d="M512 1024C230.4 1024 0 793.6 0 512S230.4 0 512 0s512 230.4 512 512-230.4 512-512 512z m0-938.666667C277.333333 85.333333 85.333333 277.333333 85.333333 512s192 426.666667 426.666667 426.666667 426.666667-192 426.666667-426.666667S746.666667 85.333333 512 85.333333z" p-id="9050">
|
||||||
|
</path>
|
||||||
|
<path d="M708.266667 375.466667c-17.066667-17.066667-44.8-17.066667-59.733334 0l-181.333333 181.333333-91.733333-91.733333c-14.933333-14.933333-40.533333-14.933333-55.466667 0l-6.4 4.266666c-14.933333 14.933333-14.933333 40.533333 0 55.466667l125.866667 125.866667c14.933333 14.933333 40.533333 14.933333 55.466666 0l4.266667-4.266667 209.066667-209.066667c17.066667-17.066667 17.066667-44.8 0-61.866666z" p-id="9051">
|
||||||
|
</path></svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
1
frontend/src/assets/icons/Edit.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1766136633767" class="icon" viewBox="0 0 1028 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7819" width="32.125" height="32" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M1018.319924 112.117535q4.093748 9.210934 6.652341 21.492179t2.558593 25.585928-5.117186 26.609365-16.374994 25.585928q-12.281245 12.281245-22.003898 21.492179t-16.886712 16.374994q-8.187497 8.187497-15.351557 14.32812l-191.382739-191.382739q12.281245-11.257808 29.167958-27.121083t28.144521-25.074209q14.32812-11.257808 29.679676-15.863275t30.191395-4.093748 28.656239 4.605467 24.050772 9.210934q21.492179 11.257808 47.589826 39.402329t40.425766 58.847634zM221.062416 611.554845q6.140623-6.140623 28.656239-29.167958t56.289041-56.80076l74.710909-74.710909 82.898406-82.898406 220.038979-220.038979 191.382739 192.406177-220.038979 220.038979-81.874969 82.898406q-40.937484 39.914047-73.687472 73.175753t-54.242167 54.753885-25.585928 24.562491q-10.234371 9.210934-23.539054 19.445305t-27.632802 16.374994q-14.32812 7.16406-41.960921 17.398431t-57.824197 19.957024-57.312478 16.886712-40.425766 9.210934q-27.632802 3.070311-36.843736-8.187497t-5.117186-37.867173q2.046874-14.32812 9.722653-41.449203t16.374994-56.289041 16.886712-53.730448 13.304682-33.773425q6.140623-14.32812 13.816401-26.097646t22.003898-26.097646z" p-id="7820" fill="#000000"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
1
frontend/src/assets/icons/agent-change.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1767084779395" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="21382" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M304.79872 108.70784c-118.75328 2.56-194.5856 51.74784-259.92192 144.73216-69.6832 99.1232-37.41696 233.31328-37.41696 233.31328s31.7696-100.06528 88.81664-147.74784c50.53952-42.22976 99.60448-78.11072 208.52224-82.97984v54.68672l141.69088-126.94016-141.69088-129.024v53.95968zM719.19616 915.25632c118.75328-2.56512 194.5856-51.712 259.96288-144.6912 69.64224-99.1232 37.37088-233.31328 37.37088-233.31328s-31.7696 100.06528-88.81152 147.74784c-50.51904 42.20928-99.6096 78.08512-208.52736 82.95936v-54.66624l-141.66528 126.93504 141.66528 129.024v-53.99552zM794.82368 304.37376h-88.64256c-72.77056 0-131.712 58.96192-131.712 131.712v110.96064c54.29248 22.21056 113.75616 34.49856 176.06656 34.49856 62.26944 0 121.728-12.288 176.02048-34.49856V436.08064c-0.00512-72.75008-58.96192-131.70688-131.73248-131.70688zM863.34464 167.58272c0 62.336-50.49856 112.87552-112.80896 112.87552-62.37696 0-112.87552-50.53952-112.87552-112.87552s50.49856-112.83456 112.87552-112.83456c62.30528 0 112.80896 50.49856 112.80896 112.83456z" fill="#ffffff" p-id="21383"></path><path d="M333.27616 692.08576H244.6336c-72.77056 0-131.73248 58.9824-131.73248 131.73248v110.94016c54.30784 22.23104 113.75104 34.49856 176.06656 34.49856 62.28992 0 121.74848-12.26752 176.02048-34.49856v-110.94016c0-72.75008-58.96192-131.73248-131.712-131.73248zM401.80224 555.31008c0 62.31552-50.47808 112.88064-112.83456 112.88064-62.37696 0-112.88064-50.56512-112.88064-112.88064 0-62.35136 50.50368-112.85504 112.88064-112.85504 62.35648 0 112.83456 50.5088 112.83456 112.85504z" fill="#ffffff" p-id="21384"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.9 KiB |
1
frontend/src/assets/icons/branch.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1766289101374" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2562" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M801.796646 348.507817c-6.599924 7.099918-9.599889 16.799806-8.299904 26.399694 1.499983 10.899874 2.399972 21.799748 2.699969 32.299627 1.399984 51.499404-11.099872 96.098888-37.09957 132.698464-45.099478 63.199269-117.398641 83.499034-170.098032 98.298863-35.799586 9.999884-61.599287 12.599854-82.399047 14.599831-22.799736 2.299973-34.299603 3.399961-50.599414 12.799851-3.199963 1.899978-6.399926 3.899955-9.49989 6.09993-29.499659 21.199755-46.399463 55.699355-46.399463 91.998935v28.599669c0 10.699876 5.499936 20.599762 14.399833 26.599692 30.299649 20.399764 50.199419 55.199361 49.599426 94.598906-0.799991 60.299302-49.999421 109.598732-110.398722 110.398722C291.002557 1024.89999 240.003148 974.400574 240.003148 912.001296c0-38.89955 19.799771-73.199153 49.899422-93.198921 8.799898-5.899932 14.099837-15.899816 14.099837-26.499694V231.60917c0-10.699876-5.499936-20.599762-14.399833-26.599692-30.299649-20.399764-50.199419-55.199361-49.599426-94.598906C240.803138 50.11127 290.002569 0.911839 350.40187 0.01185 413.001146-0.88814 464.000555 49.611276 464.000555 112.010554c0 38.89955-19.799771 73.199153-49.899422 93.198921-8.799898 5.899932-14.099837 15.899816-14.099837 26.499694v346.095994c0 4.099953 4.399949 6.599924 7.999908 4.599947h0.099998c34.299603-19.699772 62.099281-22.399741 88.99897-25.099709 18.799782-1.799979 38.299557-3.799956 65.899238-11.499867 43.299499-12.09986 92.398931-25.8997 117.898635-61.599287 16.999803-23.799725 20.599762-53.399382 19.099779-79.599079-0.699992-12.599854-8.699899-23.499728-20.399763-28.099675-42.099513-16.299811-71.899168-57.299337-71.599172-105.298781 0.399995-61.699286 51.299406-111.698707 112.998692-111.198714 61.399289 0.499994 110.998715 50.499416 110.998716 111.998704 0 29.599657-11.499867 56.499346-30.199651 76.499115z" p-id="2563" fill="#ffffff"></path></svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
1
frontend/src/assets/icons/close.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1764755052115" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6234" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M886.784 746.496q29.696 30.72 43.52 56.32t-4.608 58.368q-4.096 6.144-11.264 14.848t-14.848 16.896-15.36 14.848-12.8 9.728q-25.6 15.36-60.416 8.192t-62.464-34.816l-43.008-43.008-57.344-57.344-67.584-67.584-73.728-73.728-131.072 131.072q-60.416 60.416-98.304 99.328-38.912 38.912-77.312 48.128t-68.096-17.408l-7.168-7.168-11.264-11.264-11.264-11.264q-6.144-6.144-7.168-8.192-11.264-14.336-13.312-29.184t2.56-29.184 13.824-27.648 20.48-24.576q9.216-8.192 32.768-30.72l55.296-57.344q33.792-32.768 75.264-73.728t86.528-86.016q-49.152-49.152-93.696-93.184t-79.872-78.848-57.856-56.832-27.648-27.136q-26.624-26.624-27.136-52.736t17.92-52.736q8.192-10.24 23.552-24.064t21.504-17.92q30.72-20.48 55.296-17.92t49.152 28.16l31.744 31.744q23.552 23.552 58.368 57.344t78.336 76.288 90.624 88.576q38.912-38.912 76.288-75.776t69.632-69.12 58.368-57.856 43.52-43.008q24.576-23.552 53.248-31.232t55.296 12.8q1.024 1.024 6.656 5.12t11.264 9.216 10.752 9.728 7.168 5.632q27.648 26.624 27.136 57.856t-27.136 57.856q-18.432 18.432-45.568 46.08t-60.416 60.416-70.144 69.632l-77.824 77.824q37.888 36.864 74.24 72.192t67.584 66.048 56.32 56.32 41.472 41.984z" p-id="6235"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
14
frontend/src/assets/icons/icons.svg
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<svg
|
||||||
|
className="icon"
|
||||||
|
viewBox="0 0 8960 1024"
|
||||||
|
version="1.1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
p-id="9634"
|
||||||
|
width="100%"
|
||||||
|
height="60"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M8960 0c-451.52 181.184-171.2 1024-992 1024H992C171.232 1024 451.392 181.184 0 0h8960z"
|
||||||
|
p-id="9635"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 336 B |
1
frontend/src/assets/icons/left.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1764757574909" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9019" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M248.88888875 558.88888844l401.7777778 401.77777782c28.44444469 28.44444469 74.66666625 28.44444469 99.55555594 0 28.44444469-28.44444469 28.44444469-74.66666625 1e-8-99.555555l-352.00000031-352.00000032 352.00000031-351.99999937c28.44444469-28.44444469 28.44444469-71.11111125 0-99.55555594s-74.66666625-28.44444469-99.55555594 0L248.88888875 459.33333344c-14.22222188 14.22222188-21.33333375 31.99999969-21.33333281 49.7777775 0 17.7777778 7.11111094 35.55555562 21.33333281 49.7777775" fill="#ffffff" p-id="9020"></path></svg>
|
||||||
|
After Width: | Height: | Size: 860 B |
1
frontend/src/assets/icons/moon.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1764820450888" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="23116" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M854.442667 725.376c-117.845333 204.117333-378.837333 274.048-582.954667 156.202667A425.173333 425.173333 0 0 1 133.546667 754.346667a32 32 0 0 1 15.573333-48.298667c160.725333-57.514667 246.826667-124.16 296.789333-219.562667 52.565333-100.394667 66.176-210.346667 29.397334-361.088a32 32 0 0 1 32.810666-39.552 425.002667 425.002667 0 0 1 190.165334 56.618667c204.117333 117.845333 274.048 378.837333 156.16 582.912z" fill="#ffffff" p-id="23117"></path></svg>
|
||||||
|
After Width: | Height: | Size: 793 B |
1
frontend/src/assets/icons/process.svg
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
1
frontend/src/assets/icons/right.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1764757483582" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7204" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M775.11111125 465.11111156l-401.7777778-401.77777782c-28.44444469-28.44444469-74.66666625-28.44444469-99.55555594 0-28.44444469 28.44444469-28.44444469 74.66666625-1e-8 99.555555l352.00000031 352.00000032-352.00000031 351.99999937c-28.44444469 28.44444469-28.44444469 71.11111125 0 99.55555594s74.66666625 28.44444469 99.55555594 0L775.11111125 564.66666656c14.22222188-14.22222188 21.33333375-31.99999969 21.33333281-49.7777775 0-17.7777778-7.11111094-35.55555562-21.33333281-49.7777775" fill="#d8d8d8" p-id="7205"></path></svg>
|
||||||
|
After Width: | Height: | Size: 860 B |
1
frontend/src/assets/icons/sunny.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1764820260653" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="13095" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M152.00256 792.00256l76.00128-77.99808 60.00128 60.00128-76.00128 77.99808z m317.99808 165.99552V832h83.99872v125.99808H470.00064zM512 233.99936c141.99808 0 256 114.00192 256 256s-114.00192 256-256 256-256-114.00192-256-256 114.00192-256 256-256zM854.00064 448h128v86.00064h-128V448z m-118.00064 326.00064l60.00128-57.99936 76.00128 76.00128-60.00128 60.00128z m136.00256-584.00256l-76.00128 76.00128-60.00128-60.00128 76.00128-76.00128z m-318.0032-165.99552v125.99808H470.00064V24.00256h83.99872z m-384 423.99744v86.00064h-128V448h128zM288 205.99808L227.99872 265.99936 151.99744 189.99808 211.99872 129.9968z" p-id="13096" fill="#ffa300"></path></svg>
|
||||||
|
After Width: | Height: | Size: 985 B |
@@ -1,5 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-tooltip :disabled="!isOverflow" effect="light" placement="top" :content="text">
|
<el-tooltip
|
||||||
|
:disabled="!isOverflow"
|
||||||
|
effect="light"
|
||||||
|
placement="top"
|
||||||
|
:content="text"
|
||||||
|
popper-class="multi-line-tooltip-popper"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
ref="containerRef"
|
ref="containerRef"
|
||||||
class="multi-line-ellipsis"
|
class="multi-line-ellipsis"
|
||||||
@@ -28,7 +34,7 @@ interface Props {
|
|||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
text: '',
|
text: '',
|
||||||
lines: 3,
|
lines: 3,
|
||||||
maxWidth: '100%',
|
maxWidth: '100%'
|
||||||
})
|
})
|
||||||
|
|
||||||
const isOverflow = ref(false)
|
const isOverflow = ref(false)
|
||||||
@@ -45,8 +51,8 @@ const containerStyle = computed(
|
|||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
textOverflow: 'ellipsis',
|
textOverflow: 'ellipsis',
|
||||||
lineHeight: '1.5',
|
lineHeight: '1.5',
|
||||||
wordBreak: 'break-all',
|
wordBreak: 'break-all'
|
||||||
}) as HTMLAttributes['style'],
|
} as HTMLAttributes['style'])
|
||||||
)
|
)
|
||||||
|
|
||||||
// 检查文字是否溢出
|
// 检查文字是否溢出
|
||||||
@@ -91,3 +97,9 @@ onMounted(() => {
|
|||||||
white-space: normal;
|
white-space: normal;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.multi-line-tooltip-popper {
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
iconClass: string
|
iconClass: string
|
||||||
|
|||||||
@@ -1,21 +1,71 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useConfigStore } from '@/stores'
|
import { useConfigStore } from '@/stores'
|
||||||
|
import { ref, onMounted, onUnmounted } from 'vue'
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'AppHeader'
|
name: 'AppHeader'
|
||||||
})
|
})
|
||||||
|
|
||||||
const configStore = useConfigStore()
|
const configStore = useConfigStore()
|
||||||
|
const isDark = ref(false)
|
||||||
|
|
||||||
|
// 检测当前主题
|
||||||
|
const checkDarkMode = () => {
|
||||||
|
isDark.value =
|
||||||
|
document.documentElement.classList.contains('dark') ||
|
||||||
|
window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
checkDarkMode()
|
||||||
|
|
||||||
|
// 监听系统主题变化
|
||||||
|
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
||||||
|
mediaQuery.addEventListener('change', checkDarkMode)
|
||||||
|
|
||||||
|
// 监听类名变化
|
||||||
|
const observer = new MutationObserver(checkDarkMode)
|
||||||
|
observer.observe(document.documentElement, {
|
||||||
|
attributes: true,
|
||||||
|
attributeFilter: ['class']
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
mediaQuery.removeEventListener('change', checkDarkMode)
|
||||||
|
observer.disconnect()
|
||||||
|
})
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="bg-[var(--color-bg-secondary)] h-[60px] relative pl-[27px] font-[900] text-[24px]">
|
<div
|
||||||
<div class="absolute left-0 h-full flex items-center">
|
class="bg-[var(--color-bg)] h-[60px] relative pl-[28px] font-[900] text-[24px] absolute left-1/2 transform -translate-x-1/2 w-[600px] h-[60px] dark:bg-black flex items-center justify-center transition-[top]"
|
||||||
<img class="w-[36.8px] h-[36.8px] rounded-full mr-[12px]" src="/logo.jpg" alt="logo" />
|
>
|
||||||
<span class="text-[#9E0000]">{{ configStore.config.title }}</span>{{ configStore.config.subTitle }}
|
<svg-icon
|
||||||
</div>
|
icon-class="icons"
|
||||||
<div class="text-center h-full w-full tracking-[8.5px] flex items-center justify-center">
|
class="header-icon"
|
||||||
{{ configStore.config.centerTitle }}
|
size="100% "
|
||||||
</div>
|
:style="{
|
||||||
|
filter: isDark
|
||||||
|
? 'drop-shadow(0px 0px 5px rgba(0, 0, 0, 0.5))'
|
||||||
|
: 'drop-shadow(0px 0px 5px rgba(161, 161, 161, 0.2))',
|
||||||
|
stroke: 'var(--color-border)',
|
||||||
|
strokeWidth: '1px',
|
||||||
|
strokeLinejoin: 'round'
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
<h2
|
||||||
|
className="flex items-center gap-3 text-xl font-bold text-zinc-700 dark:text-zinc-300 mr-3 absolute left-1/2 transform -translate-x-1/2"
|
||||||
|
>
|
||||||
|
<img src="/logo.png" alt="logo" className="w-8" />
|
||||||
|
<p>
|
||||||
|
<span className="text-[#9e0000]">数联网</span
|
||||||
|
><span className="text-[var(--color-text-title)]">多智能体协同平台</span>
|
||||||
|
</p>
|
||||||
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.header-icon {
|
||||||
|
color: var(--color-header-bg);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref, onMounted, computed, reactive, nextTick } from 'vue'
|
||||||
|
|
||||||
import SvgIcon from '@/components/SvgIcon/index.vue'
|
import SvgIcon from '@/components/SvgIcon/index.vue'
|
||||||
import { useAgentsStore, useConfigStore } from '@/stores'
|
import { useAgentsStore, useConfigStore } from '@/stores'
|
||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
import { changeBriefs } from '@/utils/collaboration_Brief_FrontEnd.ts'
|
import { changeBriefs } from '@/utils/collaboration_Brief_FrontEnd.ts'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
|
import AssignmentButton from './TaskTemplate/TaskSyllabus/components/AssignmentButton.vue'
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'search-start'): void
|
(e: 'search-start'): void
|
||||||
(e: 'search', value: string): void
|
(e: 'search', value: string): void
|
||||||
@@ -14,12 +13,80 @@ const emit = defineEmits<{
|
|||||||
|
|
||||||
const agentsStore = useAgentsStore()
|
const agentsStore = useAgentsStore()
|
||||||
const configStore = useConfigStore()
|
const configStore = useConfigStore()
|
||||||
|
|
||||||
const searchValue = ref('')
|
const searchValue = ref('')
|
||||||
const triggerOnFocus = ref(true)
|
const triggerOnFocus = ref(true)
|
||||||
const isFocus = ref(false)
|
const isFocus = ref(false)
|
||||||
|
const hasAutoSearched = ref(false)
|
||||||
|
const isExpanded = ref(false)
|
||||||
|
|
||||||
|
// 解析URL参数
|
||||||
|
function getUrlParam(param: string): string | null {
|
||||||
|
const urlParams = new URLSearchParams(window.location.search)
|
||||||
|
return urlParams.get(param)
|
||||||
|
}
|
||||||
|
|
||||||
|
const planReady = computed(() => {
|
||||||
|
return agentsStore.agentRawPlan.data !== undefined
|
||||||
|
})
|
||||||
|
const openAgentAllocationDialog = () => {
|
||||||
|
agentsStore.openAgentAllocationDialog()
|
||||||
|
}
|
||||||
|
// 自动搜索函数
|
||||||
|
async function autoSearchFromUrl() {
|
||||||
|
const query = getUrlParam('q')
|
||||||
|
if (query && !hasAutoSearched.value) {
|
||||||
|
// 解码URL参数
|
||||||
|
const decodedQuery = decodeURIComponent(query)
|
||||||
|
searchValue.value = decodedQuery
|
||||||
|
hasAutoSearched.value = true
|
||||||
|
|
||||||
|
// 延迟执行搜索,确保组件已完全渲染
|
||||||
|
setTimeout(() => {
|
||||||
|
handleSearch()
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理获取焦点事件
|
||||||
|
function handleFocus() {
|
||||||
|
isFocus.value = true
|
||||||
|
isExpanded.value = true // 搜索框展开
|
||||||
|
}
|
||||||
|
|
||||||
|
const taskContainerRef = ref<HTMLDivElement | null>(null)
|
||||||
|
|
||||||
|
// 处理失去焦点事件
|
||||||
|
function handleBlur() {
|
||||||
|
isFocus.value = false
|
||||||
|
// 延迟收起搜索框,以便点击按钮等操作
|
||||||
|
setTimeout(() => {
|
||||||
|
isExpanded.value = false
|
||||||
|
// 强制重置文本区域高度到最小行数
|
||||||
|
resetTextareaHeight()
|
||||||
|
}, 200)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置文本区域高度到最小行数
|
||||||
|
function resetTextareaHeight() {
|
||||||
|
nextTick(() => {
|
||||||
|
// 获取textarea元素
|
||||||
|
const textarea =
|
||||||
|
document.querySelector('#task-container .el-textarea__inner') ||
|
||||||
|
document.querySelector('#task-container textarea')
|
||||||
|
|
||||||
|
if (textarea instanceof HTMLElement) {
|
||||||
|
// 强制设置最小高度
|
||||||
|
textarea.style.height = 'auto'
|
||||||
|
textarea.style.minHeight = '56px'
|
||||||
|
textarea.style.overflowY = 'hidden'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
async function handleSearch() {
|
async function handleSearch() {
|
||||||
|
// 用于标记大纲是否成功加载
|
||||||
|
let outlineLoaded = false
|
||||||
|
|
||||||
try {
|
try {
|
||||||
triggerOnFocus.value = false
|
triggerOnFocus.value = false
|
||||||
if (!searchValue.value) {
|
if (!searchValue.value) {
|
||||||
@@ -29,25 +96,111 @@ async function handleSearch() {
|
|||||||
emit('search-start')
|
emit('search-start')
|
||||||
agentsStore.resetAgent()
|
agentsStore.resetAgent()
|
||||||
agentsStore.setAgentRawPlan({ loading: true })
|
agentsStore.setAgentRawPlan({ loading: true })
|
||||||
const data = await api.generateBasePlan({
|
|
||||||
|
// 获取大纲
|
||||||
|
const outlineData = await api.generateBasePlan({
|
||||||
goal: searchValue.value,
|
goal: searchValue.value,
|
||||||
inputs: [],
|
inputs: []
|
||||||
})
|
})
|
||||||
data['Collaboration Process'] = changeBriefs(data['Collaboration Process'])
|
|
||||||
agentsStore.setAgentRawPlan({ data })
|
// 处理简报数据格式
|
||||||
|
outlineData['Collaboration Process'] = changeBriefs(outlineData['Collaboration Process'])
|
||||||
|
|
||||||
|
// 立即显示大纲
|
||||||
|
agentsStore.setAgentRawPlan({ data: outlineData, loading: false })
|
||||||
|
outlineLoaded = true
|
||||||
emit('search', searchValue.value)
|
emit('search', searchValue.value)
|
||||||
|
|
||||||
|
// 并行填充所有步骤的详情
|
||||||
|
const steps = outlineData['Collaboration Process'] || []
|
||||||
|
|
||||||
|
// 带重试的填充函数
|
||||||
|
const fillStepWithRetry = async (step: any, retryCount = 0): Promise<void> => {
|
||||||
|
const maxRetries = 2 // 最多重试2次
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!step.StepName) {
|
||||||
|
console.warn('步骤缺少 StepName,跳过填充详情')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用现有的 fillStepTask API 填充每个步骤的详情
|
||||||
|
const detailedStep = await api.fillStepTask({
|
||||||
|
goal: searchValue.value,
|
||||||
|
stepTask: {
|
||||||
|
StepName: step.StepName,
|
||||||
|
TaskContent: step.TaskContent,
|
||||||
|
InputObject_List: step.InputObject_List,
|
||||||
|
OutputObject: step.OutputObject
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 更新该步骤的详情到 store
|
||||||
|
updateStepDetail(step.StepName, detailedStep)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
`填充步骤 ${step.StepName} 详情失败 (尝试 ${retryCount + 1}/${maxRetries + 1}):`,
|
||||||
|
error
|
||||||
|
)
|
||||||
|
|
||||||
|
// 如果未达到最大重试次数,延迟后重试
|
||||||
|
if (retryCount < maxRetries) {
|
||||||
|
console.log(`正在重试步骤 ${step.StepName}...`)
|
||||||
|
// 延迟1秒后重试,避免立即重试导致同样的问题
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||||
|
return fillStepWithRetry(step, retryCount + 1)
|
||||||
|
} else {
|
||||||
|
console.error(`步骤 ${step.StepName} 在 ${maxRetries + 1} 次尝试后仍然失败`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// // 为每个步骤并行填充详情(选人+过程)
|
||||||
|
// const fillPromises = steps.map(step => fillStepWithRetry(step))
|
||||||
|
// // 等待所有步骤填充完成(包括重试)
|
||||||
|
// await Promise.all(fillPromises)
|
||||||
|
|
||||||
|
// 串行填充所有步骤的详情(避免字段混乱)
|
||||||
|
for (const step of steps) {
|
||||||
|
await fillStepWithRetry(step)
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
triggerOnFocus.value = true
|
triggerOnFocus.value = true
|
||||||
|
// 如果大纲加载失败,确保关闭loading
|
||||||
|
if (!outlineLoaded) {
|
||||||
agentsStore.setAgentRawPlan({ loading: false })
|
agentsStore.setAgentRawPlan({ loading: false })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辅助函数:更新单个步骤的详情
|
||||||
|
function updateStepDetail(stepId: string, detailedStep: any) {
|
||||||
|
const planData = agentsStore.agentRawPlan.data
|
||||||
|
if (!planData) return
|
||||||
|
|
||||||
|
const collaborationProcess = planData['Collaboration Process']
|
||||||
|
if (!collaborationProcess) return
|
||||||
|
|
||||||
|
const index = collaborationProcess.findIndex((s: any) => s.StepName === stepId)
|
||||||
|
if (index !== -1 && collaborationProcess[index]) {
|
||||||
|
// 保持响应式更新 - 使用 Vue 的响应式系统
|
||||||
|
Object.assign(collaborationProcess[index], {
|
||||||
|
AgentSelection: detailedStep.AgentSelection || [],
|
||||||
|
TaskProcess: detailedStep.TaskProcess || [],
|
||||||
|
Collaboration_Brief_frontEnd: detailedStep.Collaboration_Brief_frontEnd || {
|
||||||
|
template: '',
|
||||||
|
data: {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const querySearch = (queryString: string, cb: (v: { value: string }[]) => void) => {
|
const querySearch = (queryString: string, cb: (v: { value: string }[]) => void) => {
|
||||||
const results = queryString
|
const results = queryString
|
||||||
? configStore.config.taskPromptWords.filter(createFilter(queryString))
|
? configStore.config.taskPromptWords.filter(createFilter(queryString))
|
||||||
: configStore.config.taskPromptWords
|
: configStore.config.taskPromptWords
|
||||||
// call callback function to return suggestions
|
// call callback function to return suggestions
|
||||||
cb(results.map((item) => ({ value: item })))
|
cb(results.map(item => ({ value: item })))
|
||||||
}
|
}
|
||||||
|
|
||||||
const createFilter = (queryString: string) => {
|
const createFilter = (queryString: string) => {
|
||||||
@@ -56,7 +209,10 @@ const createFilter = (queryString: string) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const taskContainerRef = ref<HTMLDivElement | null>(null)
|
// 组件挂载时检查URL参数
|
||||||
|
onMounted(() => {
|
||||||
|
autoSearchFromUrl()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -67,13 +223,20 @@ const taskContainerRef = ref<HTMLDivElement | null>(null)
|
|||||||
:disabled="agentsStore.agents.length > 0"
|
:disabled="agentsStore.agents.length > 0"
|
||||||
>
|
>
|
||||||
<div class="task-root-container">
|
<div class="task-root-container">
|
||||||
<div class="task-container" ref="taskContainerRef" id="task-container">
|
<div
|
||||||
<span class="text-[var(--color-text)] font-bold task-title">任务</span>
|
class="task-container"
|
||||||
|
ref="taskContainerRef"
|
||||||
|
id="task-container"
|
||||||
|
:class="{ expanded: isExpanded }"
|
||||||
|
>
|
||||||
|
<span class="text-[var(--color-text-task)] font-bold task-title">任务</span>
|
||||||
<el-autocomplete
|
<el-autocomplete
|
||||||
|
ref="autocompleteRef"
|
||||||
v-model.trim="searchValue"
|
v-model.trim="searchValue"
|
||||||
class="task-input"
|
class="task-input"
|
||||||
size="large"
|
size="large"
|
||||||
:rows="isFocus ? 3 : 1"
|
:rows="1"
|
||||||
|
:autosize="{ minRows: 1, maxRows: 10 }"
|
||||||
placeholder="请输入您的任务"
|
placeholder="请输入您的任务"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
:append-to="taskContainerRef"
|
:append-to="taskContainerRef"
|
||||||
@@ -81,9 +244,10 @@ const taskContainerRef = ref<HTMLDivElement | null>(null)
|
|||||||
@change="agentsStore.setSearchValue"
|
@change="agentsStore.setSearchValue"
|
||||||
:disabled="!(agentsStore.agents.length > 0)"
|
:disabled="!(agentsStore.agents.length > 0)"
|
||||||
:debounce="0"
|
:debounce="0"
|
||||||
|
:clearable="true"
|
||||||
:trigger-on-focus="triggerOnFocus"
|
:trigger-on-focus="triggerOnFocus"
|
||||||
@focus="isFocus = true"
|
@focus="handleFocus"
|
||||||
@blur="isFocus = false"
|
@blur="handleBlur"
|
||||||
@select="isFocus = false"
|
@select="isFocus = false"
|
||||||
>
|
>
|
||||||
</el-autocomplete>
|
</el-autocomplete>
|
||||||
@@ -101,10 +265,11 @@ const taskContainerRef = ref<HTMLDivElement | null>(null)
|
|||||||
v-if="!agentsStore.agentRawPlan.loading"
|
v-if="!agentsStore.agentRawPlan.loading"
|
||||||
icon-class="paper-plane"
|
icon-class="paper-plane"
|
||||||
size="18px"
|
size="18px"
|
||||||
color="var(--color-text)"
|
color="#ffffff"
|
||||||
/>
|
/>
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
<AssignmentButton v-if="planReady" @click="openAgentAllocationDialog" />
|
||||||
</div>
|
</div>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</template>
|
</template>
|
||||||
@@ -120,10 +285,9 @@ const taskContainerRef = ref<HTMLDivElement | null>(null)
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
border: 2px solid transparent;
|
border: 2px solid transparent;
|
||||||
$bg: var(--el-input-bg-color, var(--el-fill-color-blank));
|
$bg: var(--el-input-bg-color, var(--el-fill-color-blank));
|
||||||
background:
|
background: linear-gradient(var(--color-bg-taskbar), var(--color-bg-taskbar)) padding-box,
|
||||||
linear-gradient(var(--color-bg-tertiary), var(--color-bg-tertiary)) padding-box,
|
|
||||||
linear-gradient(to right, #00c8d2, #315ab4) border-box;
|
linear-gradient(to right, #00c8d2, #315ab4) border-box;
|
||||||
border-radius: 40px;
|
border-radius: 30px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
@@ -131,28 +295,49 @@ const taskContainerRef = ref<HTMLDivElement | null>(null)
|
|||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: 0 55px 0 47px;
|
padding: 0 55px 0 47px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
/* 搜索框展开时的样式 */
|
||||||
|
&.expanded {
|
||||||
|
box-shadow: var(--color-task-shadow);
|
||||||
|
:deep(.el-autocomplete .el-textarea .el-textarea__inner) {
|
||||||
|
overflow-y: auto !important;
|
||||||
|
min-height: 56px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 非展开状态时,确保文本区域高度固定 */
|
||||||
|
&:not(.expanded) {
|
||||||
|
:deep(.el-textarea__inner) {
|
||||||
|
height: 56px !important;
|
||||||
|
overflow-y: hidden !important;
|
||||||
|
min-height: 56px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
:deep(.el-popper) {
|
:deep(.el-popper) {
|
||||||
position: static !important;
|
position: static !important;
|
||||||
width: 100%;
|
width: calc(100% + 102px); /*增加左右padding的总和 */
|
||||||
min-width: 100%;
|
min-width: calc(100% + 102px); /* 确保最小宽度也增加 */
|
||||||
background: var(--color-bg-tertiary);
|
margin-left: -47px; /* 向左偏移左padding的值 */
|
||||||
box-shadow: none;
|
margin-right: -55px; /*向右偏移右padding的值 */
|
||||||
|
background: var(--color-bg-taskbar);
|
||||||
border: none;
|
border: none;
|
||||||
transition: height 0s ease-in-out;
|
transition: height 0s ease-in-out;
|
||||||
border-top: 1px solid #494B51;
|
border-top: 1px solid var(--color-border);
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
|
||||||
li {
|
li {
|
||||||
height: 45px;
|
height: 45px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
line-height: 45px;
|
line-height: 45px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
padding-left: 27px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #1C1E25;
|
background: var(--color-bg-hover);
|
||||||
color: #00F3FF;
|
color: var(--color-text-hover);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,8 +360,14 @@ const taskContainerRef = ref<HTMLDivElement | null>(null)
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
padding: 18px 0;
|
padding: 18px 0 0 18px;
|
||||||
resize: none;
|
resize: none;
|
||||||
|
color: var(--color-text-taskbar);
|
||||||
|
|
||||||
|
/* 聚焦时的样式 */
|
||||||
|
.expanded & {
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
&::placeholder {
|
&::placeholder {
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
@@ -192,11 +383,10 @@ const taskContainerRef = ref<HTMLDivElement | null>(null)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.task-title {
|
.task-title {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 28px;
|
top: 28px;
|
||||||
left: 10px;
|
left: 27px;
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
}
|
}
|
||||||
@@ -221,4 +411,58 @@ const taskContainerRef = ref<HTMLDivElement | null>(null)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.drawer-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
.title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.process-list {
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
.process-item {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: var(--color-bg-list);
|
||||||
|
border: 1px solid var(--color-border-default);
|
||||||
|
|
||||||
|
.process-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.agent-tag {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.process-text {
|
||||||
|
line-height: 1.6;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-container {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.process-item:hover {
|
||||||
|
border-color: var(--el-border-color);
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
import { getActionTypeDisplay, getAgentMapIcon } from '@/layout/components/config.ts'
|
import { getActionTypeDisplay, getAgentMapIcon } from '@/layout/components/config.ts'
|
||||||
import SvgIcon from '@/components/SvgIcon/index.vue'
|
import SvgIcon from '@/components/SvgIcon/index.vue'
|
||||||
import { type Agent, useAgentsStore } from '@/stores'
|
import { type Agent, useAgentsStore } from '@/stores'
|
||||||
@@ -10,9 +11,9 @@ const porps = defineProps<{
|
|||||||
|
|
||||||
const taskProcess = computed(() => {
|
const taskProcess = computed(() => {
|
||||||
const list = agentsStore.currentTask?.TaskProcess ?? []
|
const list = agentsStore.currentTask?.TaskProcess ?? []
|
||||||
return list.map((item) => ({
|
return list.map(item => ({
|
||||||
...item,
|
...item,
|
||||||
key: uuidv4(),
|
key: uuidv4()
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -26,35 +27,40 @@ const agentsStore = useAgentsStore()
|
|||||||
class="user-item"
|
class="user-item"
|
||||||
:class="agentsStore.currentTask?.AgentSelection?.includes(item.Name) ? 'active-card' : ''"
|
:class="agentsStore.currentTask?.AgentSelection?.includes(item.Name) ? 'active-card' : ''"
|
||||||
>
|
>
|
||||||
<div class="flex items-center justify-between relative h-[41px]">
|
<div class="flex items-center justify-between relative h-[43px]">
|
||||||
|
<!-- 图标区域 -->
|
||||||
<div
|
<div
|
||||||
class="w-[44px] h-[44px] rounded-full flex items-center justify-center flex-shrink-0 relative right-[2px] icon-container"
|
class="w-[44px] h-[44px] rounded-full flex items-center justify-center flex-shrink-0 relative right-[2px] icon-container"
|
||||||
:style="{ background: getAgentMapIcon(item.Name).color }"
|
:style="{ background: getAgentMapIcon(item.Name).color }"
|
||||||
>
|
>
|
||||||
<svg-icon
|
<svg-icon :icon-class="getAgentMapIcon(item.Name).icon" color="#fff" size="24px" />
|
||||||
:icon-class="getAgentMapIcon(item.Name).icon"
|
|
||||||
color="var(--color-text)"
|
|
||||||
size="24px"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 text-[14px] flex flex-col items-end justify-end truncate ml-1">
|
|
||||||
|
<div class="flex-1 text-[14px] textClass flex flex-col items-end justify-start truncate ml-1">
|
||||||
|
<div class="flex items-center justify-start gap-2 w-full">
|
||||||
<span
|
<span
|
||||||
class="w-full truncate text-right"
|
class="truncate"
|
||||||
:style="
|
:style="
|
||||||
agentsStore.currentTask?.AgentSelection?.includes(item.Name) ? 'color:#00F3FF' : ''
|
agentsStore.currentTask?.AgentSelection?.includes(item.Name)
|
||||||
|
? 'color:var(--color-accent)'
|
||||||
|
: ''
|
||||||
"
|
"
|
||||||
>{{ item.Name }}</span
|
>{{ item.Name }}</span
|
||||||
>
|
>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="agentsStore.currentTask?.AgentSelection?.includes(item.Name)"
|
v-if="agentsStore.currentTask?.AgentSelection?.includes(item.Name)"
|
||||||
class="flex items-center gap-[7px] h-[8px] mr-1"
|
class="flex items-center gap-[7px] h-[8px] mr-1"
|
||||||
>
|
>
|
||||||
<!-- 小圆点 -->
|
<!-- 小圆点 -->
|
||||||
<div
|
<div
|
||||||
v-for="item1 in taskProcess.filter((i) => i.AgentName === item.Name)"
|
v-for="item1 in taskProcess.filter(i => i.AgentName === item.Name)"
|
||||||
:key="item1.key"
|
:key="item1.key"
|
||||||
class="w-[6px] h-[6px] rounded-full"
|
class="w-[6px] h-[6px] rounded-full"
|
||||||
:style="{ background: getActionTypeDisplay(item1.ActionType)?.color }"
|
:style="{
|
||||||
|
background: getActionTypeDisplay(item1.ActionType)?.color,
|
||||||
|
border: `1px solid ${getActionTypeDisplay(item1.ActionType)?.border}`
|
||||||
|
}"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -71,7 +77,7 @@ const agentsStore = useAgentsStore()
|
|||||||
</div>
|
</div>
|
||||||
<div class="p-[8px] pt-0">
|
<div class="p-[8px] pt-0">
|
||||||
<div
|
<div
|
||||||
v-for="(item1, index1) in taskProcess.filter((i) => i.AgentName === item.Name)"
|
v-for="(item1, index1) in taskProcess.filter(i => i.AgentName === item.Name)"
|
||||||
:key="item1.key"
|
:key="item1.key"
|
||||||
class="text-[12px]"
|
class="text-[12px]"
|
||||||
>
|
>
|
||||||
@@ -89,9 +95,11 @@ const agentsStore = useAgentsStore()
|
|||||||
</div>
|
</div>
|
||||||
<!-- 分割线 -->
|
<!-- 分割线 -->
|
||||||
<div
|
<div
|
||||||
v-if="index1 !== taskProcess.filter((i) => i.AgentName === item.Name).length - 1"
|
v-if="index1 !== taskProcess.filter(i => i.AgentName === item.Name).length - 1"
|
||||||
class="h-[1px] w-full bg-[#494B51] my-[8px]"
|
class="h-[1px] w-full bg-[var(--color-border-default)] my-[8px]"
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
|
<AssignmentButton />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -100,13 +108,14 @@ const agentsStore = useAgentsStore()
|
|||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.user-item {
|
.user-item {
|
||||||
background: #1d222b;
|
background: var(--color-agent-list-bg);
|
||||||
border-radius: 40px;
|
border-radius: 40px;
|
||||||
padding-right: 12px;
|
padding-right: 12px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.25s ease;
|
transition: all 0.25s ease;
|
||||||
color: #969696;
|
color: var(--color-text-detail);
|
||||||
border: 2px solid transparent;
|
border: 1px solid var(--color-agent-list-border);
|
||||||
|
box-sizing: border-box;
|
||||||
.duty-info {
|
.duty-info {
|
||||||
transition: height 0.25s ease;
|
transition: height 0.25s ease;
|
||||||
height: 0;
|
height: 0;
|
||||||
@@ -118,16 +127,30 @@ const agentsStore = useAgentsStore()
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
|
box-shadow: var(--color-agent-list-hover-shadow);
|
||||||
color: #b8b8b8;
|
color: var(--color-text);
|
||||||
|
background: var(--color-agent-list-hover-bg);
|
||||||
|
border: 1px solid var(--color-agent-list-hover-border);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.textClass {
|
||||||
|
color: var(--color-text-agent-list);
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-text-agent-list-hover);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.active-card {
|
.active-card {
|
||||||
background:
|
background: linear-gradient(var(--color-bg-quaternary), var(--color-bg-quaternary)) padding-box,
|
||||||
linear-gradient(#171B22, #171B22) padding-box,
|
linear-gradient(to right, var(--color-accent), var(--color-accent-secondary)) border-box;
|
||||||
linear-gradient(to right, #00c8d2, #315ab4) border-box;
|
border: 1px solid var(--color-agent-list-border);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
box-shadow: var(--color-agent-list-hover-shadow);
|
||||||
|
color: var(--color-text);
|
||||||
|
background: var(--color-agent-list-hover-bg);
|
||||||
|
border: 1px solid var(--color-agent-list-hover-border);
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
.duty-info {
|
.duty-info {
|
||||||
height: auto;
|
height: auto;
|
||||||
@@ -138,4 +161,10 @@ const agentsStore = useAgentsStore()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加头像容器样式修复
|
||||||
|
.icon-container {
|
||||||
|
right: 0 !important;
|
||||||
|
margin-left: 0px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ElNotification } from 'element-plus'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
import { pick } from 'lodash'
|
import { pick } from 'lodash'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
import api from '@/api/index.ts'
|
import api from '@/api/index.ts'
|
||||||
|
|
||||||
import SvgIcon from '@/components/SvgIcon/index.vue'
|
import SvgIcon from '@/components/SvgIcon/index.vue'
|
||||||
import { agentMapDuty } from '@/layout/components/config.ts'
|
import { agentMapDuty } from '@/layout/components/config.ts'
|
||||||
import { type Agent, useAgentsStore } from '@/stores'
|
import { type Agent, useAgentsStore } from '@/stores'
|
||||||
import { onMounted } from 'vue'
|
|
||||||
import { readConfig } from '@/utils/readJson.ts'
|
import { readConfig } from '@/utils/readJson.ts'
|
||||||
import AgentRepoList from './AgentRepoList.vue'
|
import AgentRepoList from './AgentRepoList.vue'
|
||||||
|
|
||||||
@@ -19,7 +17,9 @@ onMounted(async () => {
|
|||||||
const res = await readConfig<Agent[]>('agent.json')
|
const res = await readConfig<Agent[]>('agent.json')
|
||||||
agentsStore.setAgents(res)
|
agentsStore.setAgents(res)
|
||||||
}
|
}
|
||||||
await api.setAgents(agentsStore.agents.map((item) => pick(item, ['Name', 'Profile'])))
|
await api.setAgents(
|
||||||
|
agentsStore.agents.map(item => pick(item, ['Name', 'Profile', 'apiUrl', 'apiKey', 'apiModel']))
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 上传agent文件
|
// 上传agent文件
|
||||||
@@ -37,50 +37,77 @@ const handleFileSelect = (event: Event) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const readFileContent = async (file: File) => {
|
// 验证API配置:三个字段必须同时存在或同时不存在
|
||||||
|
const validateApiConfig = (agent: any) => {
|
||||||
|
const hasApiUrl = 'apiUrl' in agent
|
||||||
|
const hasApiKey = 'apiKey' in agent
|
||||||
|
const hasApiModel = 'apiModel' in agent
|
||||||
|
|
||||||
|
return hasApiUrl === hasApiKey && hasApiKey === hasApiModel
|
||||||
|
}
|
||||||
|
|
||||||
|
const readFileContent = (file: File) => {
|
||||||
const reader = new FileReader()
|
const reader = new FileReader()
|
||||||
reader.onload = async (e) => {
|
reader.onload = e => {
|
||||||
if (!e.target?.result) {
|
try {
|
||||||
|
const content = e.target?.result as string
|
||||||
|
const jsonData = JSON.parse(content)
|
||||||
|
|
||||||
|
if (!Array.isArray(jsonData)) {
|
||||||
|
ElMessage.error('JSON格式错误: 必须为数组格式')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
const json = JSON.parse(e.target.result?.toString?.() ?? '{}')
|
const validAgents = jsonData.filter((agent) => {
|
||||||
// 处理 JSON 数据
|
// 验证必需字段
|
||||||
if (Array.isArray(json)) {
|
if (!agent.Name || typeof agent.Name !== 'string') {
|
||||||
const isValid = json.every(
|
return false
|
||||||
(item) =>
|
}
|
||||||
typeof item.Name === 'string' &&
|
if (!agent.Icon || typeof agent.Icon !== 'string') {
|
||||||
typeof item.Icon === 'string' &&
|
return false
|
||||||
typeof item.Profile === 'string',
|
}
|
||||||
)
|
if (!agent.Profile || typeof agent.Profile !== 'string') {
|
||||||
if (isValid) {
|
return false
|
||||||
// 处理有效的 JSON 数据
|
}
|
||||||
agentsStore.setAgents(
|
|
||||||
json.map((item) => ({
|
// 验证API配置
|
||||||
Name: item.Name,
|
if (!validateApiConfig(agent)) {
|
||||||
Icon: item.Icon.replace(/\.png$/, ''),
|
return false
|
||||||
Profile: item.Profile,
|
}
|
||||||
Classification: item.Classification,
|
|
||||||
})),
|
return true
|
||||||
)
|
|
||||||
await api.setAgents(json.map((item) => pick(item, ['Name', 'Profile'])))
|
|
||||||
} else {
|
|
||||||
ElNotification.error({
|
|
||||||
title: '错误',
|
|
||||||
message: 'JSON 格式错误',
|
|
||||||
})
|
})
|
||||||
}
|
// 修改发送到后端的数据
|
||||||
} else {
|
const processedAgents = validAgents.map(agent => ({
|
||||||
console.error('JSON is not an array')
|
Name: agent.Name,
|
||||||
ElNotification.error({
|
Profile: agent.Profile,
|
||||||
title: '错误',
|
Icon: agent.Icon,
|
||||||
message: 'JSON 格式错误',
|
Classification: agent.Classification || '',
|
||||||
|
apiUrl: agent.apiUrl,
|
||||||
|
apiKey: agent.apiKey,
|
||||||
|
apiModel: agent.apiModel
|
||||||
|
}))
|
||||||
|
|
||||||
|
agentsStore.setAgents(processedAgents)
|
||||||
|
|
||||||
|
// 调用API
|
||||||
|
api
|
||||||
|
.setAgents(processedAgents)
|
||||||
|
.then(() => {
|
||||||
|
ElMessage.success('智能体上传成功')
|
||||||
})
|
})
|
||||||
}
|
.catch(() => {
|
||||||
} catch (e) {
|
ElMessage.error('智能体上传失败')
|
||||||
console.error(e)
|
})
|
||||||
|
} catch {
|
||||||
|
ElMessage.error('JSON解析错误')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reader.onerror = () => {
|
||||||
|
ElMessage.error('文件读取错误')
|
||||||
|
}
|
||||||
|
|
||||||
reader.readAsText(file)
|
reader.readAsText(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +122,7 @@ const agentList = computed(() => {
|
|||||||
if (!agentsStore.agents.length) {
|
if (!agentsStore.agents.length) {
|
||||||
return {
|
return {
|
||||||
selected,
|
selected,
|
||||||
unselected,
|
unselected
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const agent of agentsStore.agents) {
|
for (const agent of agentsStore.agents) {
|
||||||
@@ -110,14 +137,14 @@ const agentList = computed(() => {
|
|||||||
obj[agent.Classification] = arr
|
obj[agent.Classification] = arr
|
||||||
unselected.push({
|
unselected.push({
|
||||||
title: agent.Classification,
|
title: agent.Classification,
|
||||||
data: arr,
|
data: arr
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
selected,
|
selected,
|
||||||
unselected: unselected,
|
unselected: unselected
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
@@ -126,7 +153,7 @@ const agentList = computed(() => {
|
|||||||
<div class="agent-repo h-full flex flex-col" id="agent-repo">
|
<div class="agent-repo h-full flex flex-col" id="agent-repo">
|
||||||
<!-- 头部 -->
|
<!-- 头部 -->
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-[18px] font-bold">智能体库</span>
|
<span class="text-[18px] font-bold text-[var(--color-text-title-header)]">智能体库</span>
|
||||||
<!-- 上传文件 -->
|
<!-- 上传文件 -->
|
||||||
<input type="file" accept=".json" @change="handleFileSelect" class="hidden" ref="fileInput" />
|
<input type="file" accept=".json" @change="handleFileSelect" class="hidden" ref="fileInput" />
|
||||||
<div class="plus-button" @click="triggerFileSelect">
|
<div class="plus-button" @click="triggerFileSelect">
|
||||||
@@ -144,14 +171,22 @@ const agentList = computed(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 底部提示栏 -->
|
<!-- 底部提示栏 -->
|
||||||
<div class="w-full grid grid-cols-3 gap-x-[10px] bg-[#1d222b] rounded-[20px] p-[8px] mt-[10px]">
|
<div
|
||||||
|
class="w-full grid grid-cols-3 gap-x-[10px] bg-[var(--color-bg-indicator)] rounded-[20px] p-[8px] mt-[10px]"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
v-for="item in Object.values(agentMapDuty)"
|
v-for="item in Object.values(agentMapDuty)"
|
||||||
:key="item.key"
|
:key="item.key"
|
||||||
class="flex items-center justify-center gap-x-1"
|
class="flex items-center justify-center gap-x-1"
|
||||||
>
|
>
|
||||||
|
<div
|
||||||
|
class="w-[8px] h-[8px] rounded-full"
|
||||||
|
:style="{
|
||||||
|
background: item.color,
|
||||||
|
border: `1px solid ${item.border}`
|
||||||
|
}"
|
||||||
|
></div>
|
||||||
<span class="text-[12px]">{{ item.name }}</span>
|
<span class="text-[12px]">{{ item.name }}</span>
|
||||||
<div class="w-[8px] h-[8px] rounded-full" :style="{ background: item.color }"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -162,7 +197,7 @@ const agentList = computed(() => {
|
|||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
|
|
||||||
.plus-button {
|
.plus-button {
|
||||||
background: #1d2128;
|
background: var(--color-bg-tertiary);
|
||||||
width: 24px;
|
width: 24px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@@ -174,7 +209,7 @@ const agentList = computed(() => {
|
|||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #374151;
|
background: var(--color-bg-quaternary);
|
||||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);
|
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,324 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed } from 'vue'
|
||||||
|
import { getActionTypeDisplay } from '@/layout/components/config.ts'
|
||||||
|
import { useAgentsStore } from '@/stores'
|
||||||
|
import BranchButton from './components/TaskButton.vue'
|
||||||
|
|
||||||
|
const agentsStore = useAgentsStore()
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
step: {
|
||||||
|
Id?: string
|
||||||
|
TaskProcess: Array<{
|
||||||
|
ID: string
|
||||||
|
ActionType: string
|
||||||
|
AgentName: string
|
||||||
|
Description: string
|
||||||
|
}>
|
||||||
|
}
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'open-edit', stepId: string, processId: string): void
|
||||||
|
(e: 'save-edit', stepId: string, processId: string, value: string): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
//从 currentTask 中获取数据
|
||||||
|
const currentTaskProcess = computed(() => {
|
||||||
|
const currentTask = agentsStore.currentTask
|
||||||
|
if (currentTask && currentTask.Id === props.step.Id && currentTask.TaskProcess) {
|
||||||
|
return currentTask.TaskProcess
|
||||||
|
}
|
||||||
|
|
||||||
|
//从 agentRawPlan 中获取原始数据
|
||||||
|
const collaborationProcess = agentsStore.agentRawPlan.data?.['Collaboration Process'] || []
|
||||||
|
const rawData = collaborationProcess.find((task: any) => task.Id === props.step.Id)
|
||||||
|
return rawData?.TaskProcess || []
|
||||||
|
})
|
||||||
|
|
||||||
|
// 当前正在编辑的process ID
|
||||||
|
const editingProcessId = ref<string | null>(null)
|
||||||
|
const editValue = ref('')
|
||||||
|
// 鼠标悬停的process ID
|
||||||
|
const hoverProcessId = ref<string | null>(null)
|
||||||
|
|
||||||
|
// 处理卡片点击事件
|
||||||
|
function handleCardClick() {
|
||||||
|
// 如果正在编辑,不处理点击
|
||||||
|
if (editingProcessId.value) return
|
||||||
|
|
||||||
|
// 设置当前任务,与任务大纲联动
|
||||||
|
if (props.step.Id) {
|
||||||
|
agentsStore.setCurrentTask(props.step as any)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测当前是否是深色模式
|
||||||
|
function isDarkMode(): boolean {
|
||||||
|
return document.documentElement.classList.contains('dark')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取颜色浅两号的函数
|
||||||
|
function getLightColor(color: string, level: number = 2): string {
|
||||||
|
if (!color || color.length !== 7 || color[0] !== '#') return color
|
||||||
|
|
||||||
|
const r = parseInt(color.substr(1, 2), 16)
|
||||||
|
const g = parseInt(color.substr(3, 2), 16)
|
||||||
|
const b = parseInt(color.substr(5, 2), 16)
|
||||||
|
|
||||||
|
// 增加亮度(浅两号)
|
||||||
|
const lightenAmount = level * 20
|
||||||
|
const newR = Math.min(255, r + lightenAmount)
|
||||||
|
const newG = Math.min(255, g + lightenAmount)
|
||||||
|
const newB = Math.min(255, b + lightenAmount)
|
||||||
|
|
||||||
|
return `#${Math.round(newR).toString(16).padStart(2, '0')}${Math.round(newG)
|
||||||
|
.toString(16)
|
||||||
|
.padStart(2, '0')}${Math.round(newB).toString(16).padStart(2, '0')}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取颜色深两号的函数
|
||||||
|
function getDarkColor(color: string, level: number = 2): string {
|
||||||
|
if (!color || color.length !== 7 || color[0] !== '#') return color
|
||||||
|
|
||||||
|
const r = parseInt(color.substr(1, 2), 16)
|
||||||
|
const g = parseInt(color.substr(3, 2), 16)
|
||||||
|
const b = parseInt(color.substr(5, 2), 16)
|
||||||
|
|
||||||
|
// 降低亮度(深两号)
|
||||||
|
const darkenAmount = level * 20
|
||||||
|
const newR = Math.max(0, r - darkenAmount)
|
||||||
|
const newG = Math.max(0, g - darkenAmount)
|
||||||
|
const newB = Math.max(0, b - darkenAmount)
|
||||||
|
|
||||||
|
return `#${Math.round(newR).toString(16).padStart(2, '0')}${Math.round(newG)
|
||||||
|
.toString(16)
|
||||||
|
.padStart(2, '0')}${Math.round(newB).toString(16).padStart(2, '0')}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据主题模式获取调整后的颜色
|
||||||
|
function getAdjustedColor(color: string, level: number = 2): string {
|
||||||
|
if (isDarkMode()) {
|
||||||
|
return getDarkColor(color, level)
|
||||||
|
} else {
|
||||||
|
return getLightColor(color, level)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理鼠标进入
|
||||||
|
function handleMouseEnter(processId: string) {
|
||||||
|
hoverProcessId.value = processId
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理鼠标离开
|
||||||
|
function handleMouseLeave() {
|
||||||
|
hoverProcessId.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理双击编辑(针对单个process)
|
||||||
|
function handleDblClick(processId: string, currentDescription: string) {
|
||||||
|
editingProcessId.value = processId
|
||||||
|
editValue.value = currentDescription
|
||||||
|
emit('open-edit', props.step.Id || '', processId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理保存编辑
|
||||||
|
function handleSave(processId: string) {
|
||||||
|
if (!editingProcessId.value) return
|
||||||
|
|
||||||
|
emit('save-edit', props.step.Id || '', processId, editValue.value)
|
||||||
|
editingProcessId.value = null
|
||||||
|
editValue.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理取消编辑
|
||||||
|
function handleCancel() {
|
||||||
|
editingProcessId.value = null
|
||||||
|
editValue.value = ''
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="process-card" @click="handleCardClick">
|
||||||
|
<div class="process-content">
|
||||||
|
<!-- 显示模式 -->
|
||||||
|
<div class="display-content">
|
||||||
|
<span
|
||||||
|
v-for="process in currentTaskProcess"
|
||||||
|
:key="process.ID"
|
||||||
|
class="process-segment"
|
||||||
|
@mouseenter="handleMouseEnter(process.ID)"
|
||||||
|
@mouseleave="handleMouseLeave"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="agent-name"
|
||||||
|
:style="{
|
||||||
|
backgroundColor: getActionTypeDisplay(process.ActionType)?.color || '#909399',
|
||||||
|
color: '#fff',
|
||||||
|
padding: '2px 6px',
|
||||||
|
borderRadius: '3px',
|
||||||
|
marginRight: '4px'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ process.AgentName }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- 编辑模式 - 修改为卡片样式 -->
|
||||||
|
<div v-if="editingProcessId === process.ID" class="edit-container">
|
||||||
|
<div class="edit-card">
|
||||||
|
<div class="flex flex-col gap-3">
|
||||||
|
<el-input
|
||||||
|
v-model="editValue"
|
||||||
|
type="textarea"
|
||||||
|
:autosize="{ minRows: 3, maxRows: 6 }"
|
||||||
|
placeholder="请输入描述内容"
|
||||||
|
autofocus
|
||||||
|
/>
|
||||||
|
<div class="flex justify-end">
|
||||||
|
<svg-icon
|
||||||
|
icon-class="Check"
|
||||||
|
size="20px"
|
||||||
|
color="#328621"
|
||||||
|
class="cursor-pointer mr-4"
|
||||||
|
@click="handleSave(process.ID)"
|
||||||
|
title="保存"
|
||||||
|
/>
|
||||||
|
<svg-icon
|
||||||
|
icon-class="Cancel"
|
||||||
|
size="20px"
|
||||||
|
color="#8e0707"
|
||||||
|
class="cursor-pointer mr-1"
|
||||||
|
@click="handleCancel"
|
||||||
|
title="取消"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 显示模式 -->
|
||||||
|
<span
|
||||||
|
v-else
|
||||||
|
class="process-description"
|
||||||
|
:class="{ hovered: hoverProcessId === process.ID }"
|
||||||
|
:style="{
|
||||||
|
border: `1px solid ${getActionTypeDisplay(process.ActionType)?.border}`,
|
||||||
|
backgroundColor:
|
||||||
|
hoverProcessId === process.ID
|
||||||
|
? getAdjustedColor(getActionTypeDisplay(process.ActionType)?.color || '#909399')
|
||||||
|
: 'transparent'
|
||||||
|
}"
|
||||||
|
@dblclick="handleDblClick(process.ID, process.Description)"
|
||||||
|
>
|
||||||
|
{{ process.Description }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="separator" v-if="process.Description && !process.Description.endsWith('。')"
|
||||||
|
>。</span
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 按钮点击不会冒泡到卡片 -->
|
||||||
|
<BranchButton :step="step" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.process-card {
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding: 16px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: var(--color-bg-list);
|
||||||
|
border: 1px solid var(--color-border-default);
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
.process-content {
|
||||||
|
min-height: 20px;
|
||||||
|
|
||||||
|
.display-content {
|
||||||
|
line-height: 1.6;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
|
||||||
|
.process-segment {
|
||||||
|
display: inline;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.agent-name {
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 13px;
|
||||||
|
margin-right: 4px;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-container {
|
||||||
|
display: block; // 改为块级元素,使其换行显示
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
.edit-card {
|
||||||
|
//background: #f0f2f5;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
width: 100%;
|
||||||
|
max-width: 600px;
|
||||||
|
|
||||||
|
:deep(.el-textarea) {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.el-textarea__inner {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.6;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
resize: vertical;
|
||||||
|
min-height: 60px;
|
||||||
|
color: var(--color-text-taskbar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.process-description {
|
||||||
|
display: inline;
|
||||||
|
white-space: normal;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 2px 4px;
|
||||||
|
border-radius: 3px;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
|
||||||
|
&.hovered {
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.separator {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child .separator {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.process-card:hover {
|
||||||
|
border-color: var(--el-border-color);
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { useAgentsStore, useSelectionStore } from '@/stores'
|
||||||
|
|
||||||
|
const agentsStore = useAgentsStore()
|
||||||
|
const selectionStore = useSelectionStore()
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'click'): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
step?: any
|
||||||
|
}>()
|
||||||
|
|
||||||
|
// 获取分支数量
|
||||||
|
const branchCount = computed(() => {
|
||||||
|
if (!props.step?.Id) return 1
|
||||||
|
|
||||||
|
// 获取该任务步骤的分支数据
|
||||||
|
const taskStepId = props.step.Id
|
||||||
|
// 获取该任务的 agent 组合
|
||||||
|
const agents = props.step.AgentSelection || []
|
||||||
|
const branches = selectionStore.getTaskProcessBranches(taskStepId, agents)
|
||||||
|
return branches.length || 1
|
||||||
|
})
|
||||||
|
|
||||||
|
// 判断按钮是否可点击
|
||||||
|
const isClickable = computed(() => {
|
||||||
|
if (!props.step?.Id || !agentsStore.currentTask?.Id) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return props.step.Id === agentsStore.currentTask.Id
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleClick = (event?: MouseEvent) => {
|
||||||
|
// 只有可点击时才执行操作
|
||||||
|
if (!isClickable.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 阻止冒泡,避免触发卡片点击
|
||||||
|
if (event) {
|
||||||
|
event.stopPropagation()
|
||||||
|
}
|
||||||
|
|
||||||
|
emit('click')
|
||||||
|
// 设置当前任务
|
||||||
|
if (props.step) {
|
||||||
|
agentsStore.setCurrentTask(props.step)
|
||||||
|
}
|
||||||
|
// 触发打开任务过程探索窗口
|
||||||
|
agentsStore.openPlanTask()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="task-button"
|
||||||
|
:class="{ 'has-branches': branchCount > 0, 'is-disabled': !isClickable }"
|
||||||
|
@click="handleClick"
|
||||||
|
:title="isClickable ? `${branchCount} 个分支` : '请先在任务大纲中选中此任务'"
|
||||||
|
>
|
||||||
|
<!-- 流程图标 -->
|
||||||
|
<svg-icon icon-class="branch" size="20px" class="task-icon" />
|
||||||
|
|
||||||
|
<!-- 分支数量显示 -->
|
||||||
|
<span class="branch-count">
|
||||||
|
{{ branchCount }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.task-button {
|
||||||
|
/* 定位 - 右下角 */
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
|
||||||
|
/* 尺寸 */
|
||||||
|
width: 36px;
|
||||||
|
height: 32px;
|
||||||
|
|
||||||
|
/* 样式 */
|
||||||
|
background-color: #43a8aa;
|
||||||
|
border-radius: 10px 0 0 0;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
z-index: 100;
|
||||||
|
|
||||||
|
/* 布局 */
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
/* 交互 */
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
filter: brightness(0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 禁用状态
|
||||||
|
&.is-disabled {
|
||||||
|
background-color: #bdc3c7;
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.6;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
filter: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.has-branches::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -2px;
|
||||||
|
right: -2px;
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
background: #ff6b6b;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2px solid white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-icon {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-count {
|
||||||
|
position: absolute;
|
||||||
|
right: 4px;
|
||||||
|
bottom: 2px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: white;
|
||||||
|
font-weight: 800;
|
||||||
|
text-align: right;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
// 模拟后端原始返回格式的 Mock 数据 - fill_stepTask_TaskProcess 接口
|
||||||
|
// 后端返回格式: IRawStepTask { StepName, TaskContent, InputObject_List, OutputObject, AgentSelection, TaskProcess, Collaboration_Brief_frontEnd }
|
||||||
|
|
||||||
|
import type { IRawStepTask } from '@/stores'
|
||||||
|
|
||||||
|
// TaskProcess 项格式
|
||||||
|
interface RawTaskProcessItem {
|
||||||
|
ID: string
|
||||||
|
ActionType: string
|
||||||
|
AgentName: string
|
||||||
|
Description: string
|
||||||
|
ImportantInput: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collaboration_Brief_frontEnd 数据项格式
|
||||||
|
interface RawBriefDataItem {
|
||||||
|
text: string
|
||||||
|
color: number[] // [h, s, l]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 后端返回的完整数据格式
|
||||||
|
export interface RawAgentTaskProcessResponse {
|
||||||
|
StepName: string
|
||||||
|
TaskContent: string
|
||||||
|
InputObject_List: string[]
|
||||||
|
OutputObject: string
|
||||||
|
AgentSelection: string[]
|
||||||
|
TaskProcess: RawTaskProcessItem[]
|
||||||
|
Collaboration_Brief_frontEnd?: {
|
||||||
|
template: string
|
||||||
|
data: Record<string, RawBriefDataItem>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模拟后端返回的原始数据结构(与后端缓存数据格式一致)
|
||||||
|
// 使用与 AgentAssignmentBackendMock 相同的 agent 列表
|
||||||
|
export const mockBackendAgentTaskProcessData: RawAgentTaskProcessResponse = {
|
||||||
|
StepName: '腐蚀类型识别',
|
||||||
|
TaskContent: '分析船舶制造中常见的材料腐蚀类型及其成因。',
|
||||||
|
InputObject_List: [],
|
||||||
|
OutputObject: '腐蚀类型及成因列表',
|
||||||
|
AgentSelection: ['腐蚀机理研究员', '实验材料学家', '防护工程专家'],
|
||||||
|
TaskProcess: [
|
||||||
|
{
|
||||||
|
ID: 'action_101',
|
||||||
|
ActionType: 'Propose',
|
||||||
|
AgentName: '腐蚀机理研究员',
|
||||||
|
Description: '分析海洋环境下的腐蚀机理,确定关键防护要素',
|
||||||
|
ImportantInput: ['海洋环境参数', '防护性能指标'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action_102',
|
||||||
|
ActionType: 'Critique',
|
||||||
|
AgentName: '实验材料学家',
|
||||||
|
Description: '基于腐蚀机理分析结果,设计涂层材料的基础配方',
|
||||||
|
ImportantInput: ['腐蚀机理分析结果', '涂层材料配方'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action_103',
|
||||||
|
ActionType: 'Improve',
|
||||||
|
AgentName: '防护工程专家',
|
||||||
|
Description: '筛选适用于防护涂层的二维材料,评估其性能潜力',
|
||||||
|
ImportantInput: ['材料配方设计', '涂层材料配方'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action_104',
|
||||||
|
ActionType: 'Finalize',
|
||||||
|
AgentName: '实验材料学家',
|
||||||
|
Description: '制定涂层材料性能测试实验方案,包括测试指标和方法',
|
||||||
|
ImportantInput: ['二维材料筛选结果', '防护性能指标'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action_105',
|
||||||
|
ActionType: 'Critique',
|
||||||
|
AgentName: '防护工程专家',
|
||||||
|
Description: '模拟海洋流体环境对涂层材料的影响,优化涂层结构',
|
||||||
|
ImportantInput: ['实验方案', '海洋环境参数'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action_106',
|
||||||
|
ActionType: 'Improve',
|
||||||
|
AgentName: '腐蚀机理研究员',
|
||||||
|
Description: '综合评估涂层材料的防护性能,提出改进建议',
|
||||||
|
ImportantInput: ['流体力学模拟结果', '实验材料学测试结果', '二维材料性能数据'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action_107',
|
||||||
|
ActionType: 'Improve',
|
||||||
|
AgentName: '实验材料学家',
|
||||||
|
Description: '整理研发数据和测试结果,撰写完整的研发报告',
|
||||||
|
ImportantInput: ['综合性能评估', '所有研发数据'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
Collaboration_Brief_frontEnd: {
|
||||||
|
template: '基于!<0>!、!<1>!和!<2>!,!<3>!、!<4>!、!<5>!和!<6>!执行!<7>!任务,以获得!<8>!。',
|
||||||
|
data: {
|
||||||
|
'0': {
|
||||||
|
text: '涂层材料配方',
|
||||||
|
color: [120, 60, 70], // hsl(120, 60%, 70%)
|
||||||
|
},
|
||||||
|
'1': {
|
||||||
|
text: '海洋环境参数',
|
||||||
|
color: [120, 60, 70], // hsl(120, 60%, 70%)
|
||||||
|
},
|
||||||
|
'2': {
|
||||||
|
text: '防护性能指标',
|
||||||
|
color: [120, 60, 70], // hsl(120, 60%, 70%)
|
||||||
|
},
|
||||||
|
'3': {
|
||||||
|
text: '腐蚀机理研究员',
|
||||||
|
color: [0, 0, 90], // hsl(0, 0%, 90%)
|
||||||
|
},
|
||||||
|
'4': {
|
||||||
|
text: '先进材料研发员',
|
||||||
|
color: [0, 0, 90], // hsl(0, 0%, 90%)
|
||||||
|
},
|
||||||
|
'5': {
|
||||||
|
text: '二维材料科学家',
|
||||||
|
color: [0, 0, 90], // hsl(0, 0%, 90%)
|
||||||
|
},
|
||||||
|
'6': {
|
||||||
|
text: '实验材料学家',
|
||||||
|
color: [0, 0, 90], // hsl(0, 0%, 90%)
|
||||||
|
},
|
||||||
|
'7': {
|
||||||
|
text: '研发适用于海洋环境的耐腐蚀防护涂层材料,并进行性能测试与评估',
|
||||||
|
color: [0, 0, 87], // hsl(0, 0%, 87%)
|
||||||
|
},
|
||||||
|
'8': {
|
||||||
|
text: '防护涂层材料研发报告',
|
||||||
|
color: [30, 100, 80], // hsl(30, 100%, 80%)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模拟后端API调用 - fill_stepTask_TaskProcess
|
||||||
|
export const mockBackendFillAgentTaskProcess = async (
|
||||||
|
goal: string,
|
||||||
|
stepTask: any,
|
||||||
|
agents: string[],
|
||||||
|
): Promise<RawAgentTaskProcessResponse> => {
|
||||||
|
// 模拟网络延迟 500ms
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 500))
|
||||||
|
|
||||||
|
// 在真实场景中,后端会根据传入的 goal、stepTask 和 agents 生成不同的 TaskProcess
|
||||||
|
// 这里我们直接返回预设的 Mock 数据
|
||||||
|
// 可以根据传入的 agents 动态修改 AgentSelection 和 TaskProcess
|
||||||
|
|
||||||
|
// 确保 agents 数组不为空
|
||||||
|
const safeAgents = agents.length > 0 ? agents : ['腐蚀机理研究员']
|
||||||
|
|
||||||
|
const responseData: RawAgentTaskProcessResponse = {
|
||||||
|
...mockBackendAgentTaskProcessData,
|
||||||
|
AgentSelection: agents,
|
||||||
|
TaskProcess: mockBackendAgentTaskProcessData.TaskProcess.map((action, index) => ({
|
||||||
|
...action,
|
||||||
|
AgentName: safeAgents[index % safeAgents.length],
|
||||||
|
})),
|
||||||
|
Collaboration_Brief_frontEnd: mockBackendAgentTaskProcessData.Collaboration_Brief_frontEnd
|
||||||
|
? {
|
||||||
|
template: mockBackendAgentTaskProcessData.Collaboration_Brief_frontEnd.template,
|
||||||
|
data: { ...mockBackendAgentTaskProcessData.Collaboration_Brief_frontEnd.data },
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新 Collaboration_Brief_frontEnd.data 中的 agent 引用
|
||||||
|
if (responseData.Collaboration_Brief_frontEnd?.data) {
|
||||||
|
const agentCount = Math.min(safeAgents.length, 4) // 最多4个agent
|
||||||
|
for (let i = 0; i < agentCount; i++) {
|
||||||
|
const key = String(i + 3) // agent从索引3开始
|
||||||
|
if (responseData.Collaboration_Brief_frontEnd.data[key]) {
|
||||||
|
responseData.Collaboration_Brief_frontEnd.data[key] = {
|
||||||
|
...responseData.Collaboration_Brief_frontEnd.data[key],
|
||||||
|
text: safeAgents[i]!,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return responseData
|
||||||
|
}
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
<!-- AdditionalOutputCard.vue -->
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, ref, nextTick } from 'vue'
|
||||||
|
import { useAgentsStore } from '@/stores'
|
||||||
|
import SvgIcon from '@/components/SvgIcon/index.vue'
|
||||||
|
|
||||||
|
const agentsStore = useAgentsStore()
|
||||||
|
const props = defineProps<{
|
||||||
|
index: number
|
||||||
|
}>()
|
||||||
|
|
||||||
|
// 获取产物名称
|
||||||
|
const currentOutput = computed(() => {
|
||||||
|
return agentsStore.additionalOutputs[props.index] || ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 编辑状态
|
||||||
|
const isEditing = ref(false)
|
||||||
|
const inputValue = ref('')
|
||||||
|
const originalValue = ref('')
|
||||||
|
const inputRef = ref<HTMLElement>()
|
||||||
|
|
||||||
|
// 点击编辑图标
|
||||||
|
const handleEditClick = () => {
|
||||||
|
isEditing.value = true
|
||||||
|
originalValue.value = inputValue.value
|
||||||
|
|
||||||
|
// 等待 DOM 更新后聚焦输入框
|
||||||
|
nextTick(() => {
|
||||||
|
if (inputRef.value) {
|
||||||
|
const inputEl = inputRef.value.querySelector('input')
|
||||||
|
if (inputEl) {
|
||||||
|
inputEl.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存编辑
|
||||||
|
const handleSave = () => {
|
||||||
|
if (isEditing.value) {
|
||||||
|
isEditing.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消编辑
|
||||||
|
const handleCancel = () => {
|
||||||
|
if (isEditing.value) {
|
||||||
|
inputValue.value = originalValue.value // 恢复原始值
|
||||||
|
isEditing.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理键盘事件
|
||||||
|
const handleKeydown = (event: KeyboardEvent) => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
event.preventDefault()
|
||||||
|
handleSave()
|
||||||
|
} else if (event.key === 'Escape') {
|
||||||
|
event.preventDefault()
|
||||||
|
handleCancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 输入框失去焦点处理
|
||||||
|
const handleBlur = () => {
|
||||||
|
// 延迟处理,避免点击按钮时立即触发
|
||||||
|
setTimeout(() => {
|
||||||
|
if (isEditing.value) {
|
||||||
|
handleSave()
|
||||||
|
}
|
||||||
|
}, 200)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<!-- 当产物存在时才显示 -->
|
||||||
|
<div v-if="currentOutput" class="card-item">
|
||||||
|
<el-card
|
||||||
|
class="card-item w-full relative output-object-card"
|
||||||
|
:shadow="true"
|
||||||
|
:id="`additional-output-${index}`"
|
||||||
|
>
|
||||||
|
<!-- 显示产物名称 -->
|
||||||
|
<div class="text-start w-[100%]">
|
||||||
|
<div class="text-[18px] font-bold text-[var(--color-text)] mb-2">
|
||||||
|
{{ currentOutput }}
|
||||||
|
</div>
|
||||||
|
<div ref="inputRef">
|
||||||
|
<el-input
|
||||||
|
v-model="inputValue"
|
||||||
|
:readonly="!isEditing"
|
||||||
|
:placeholder="isEditing ? '请输入内容...' : '点击编辑图标开始编辑...'"
|
||||||
|
@keydown="handleKeydown"
|
||||||
|
@blur="handleBlur"
|
||||||
|
:class="{ editing: isEditing }"
|
||||||
|
>
|
||||||
|
<template #suffix>
|
||||||
|
<!-- 只读状态:显示编辑图标 -->
|
||||||
|
<div v-if="!isEditing" class="flex items-center">
|
||||||
|
<svg-icon
|
||||||
|
icon-class="Edit"
|
||||||
|
size="20px"
|
||||||
|
class="cursor-pointer hover:text-[#409eff] transition-colors"
|
||||||
|
@click="handleEditClick"
|
||||||
|
title="点击编辑"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 编辑状态下的提示 -->
|
||||||
|
<div v-if="isEditing" class="mt-2 text-end text-xs text-gray-500">
|
||||||
|
<svg-icon
|
||||||
|
icon-class="Check"
|
||||||
|
size="20px"
|
||||||
|
color="#328621"
|
||||||
|
class="cursor-pointer mr-4"
|
||||||
|
@click="handleSave"
|
||||||
|
title="保存"
|
||||||
|
/>
|
||||||
|
<svg-icon
|
||||||
|
icon-class="Cancel"
|
||||||
|
size="20px"
|
||||||
|
color="#8e0707"
|
||||||
|
class="cursor-pointer mr-4"
|
||||||
|
@click="handleCancel"
|
||||||
|
title="取消"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.card-item {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.output-object-card {
|
||||||
|
:deep(.el-card__body) {
|
||||||
|
min-height: 80px;
|
||||||
|
display: flex;
|
||||||
|
align-items: start;
|
||||||
|
justify-content: start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 输入框样式 */
|
||||||
|
:deep(.el-input .el-input__wrapper) {
|
||||||
|
box-shadow: none;
|
||||||
|
background-color: var(--color-bg-three);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 编辑状态下的输入框样式 */
|
||||||
|
:deep(.el-input.editing .el-input__wrapper) {
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-input.editing .el-input__wrapper.is-focus) {
|
||||||
|
border-color: #c0c4cc;
|
||||||
|
box-shadow: 0 0 0 1px #c0c4cc;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -15,16 +15,14 @@ const md = new MarkdownIt({
|
|||||||
html: true,
|
html: true,
|
||||||
linkify: true,
|
linkify: true,
|
||||||
typographer: true,
|
typographer: true,
|
||||||
breaks: true,
|
breaks: true
|
||||||
})
|
})
|
||||||
|
|
||||||
function sanitize(str?: string) {
|
function sanitize(str?: string) {
|
||||||
if (!str) {
|
if (!str) {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
const cleanStr = str
|
const cleanStr = str.replace(/\\n/g, '\n').replace(/\n\s*\d+\./g, '\n$&')
|
||||||
.replace(/\\n/g, '\n')
|
|
||||||
.replace(/\n\s*\d+\./g, '\n$&')
|
|
||||||
const html = md.render(cleanStr)
|
const html = md.render(cleanStr)
|
||||||
return html
|
return html
|
||||||
// return DOMPurify.sanitize(html)
|
// return DOMPurify.sanitize(html)
|
||||||
@@ -43,7 +41,7 @@ const data = computed<Data | null>(() => {
|
|||||||
return {
|
return {
|
||||||
Description: props.nodeId,
|
Description: props.nodeId,
|
||||||
Content: sanitize(result.content),
|
Content: sanitize(result.content),
|
||||||
LogNodeType: result.LogNodeType,
|
LogNodeType: result.LogNodeType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,7 +54,7 @@ const data = computed<Data | null>(() => {
|
|||||||
return {
|
return {
|
||||||
Description: action.Description,
|
Description: action.Description,
|
||||||
Content: sanitize(action.Action_Result),
|
Content: sanitize(action.Action_Result),
|
||||||
LogNodeType: result.LogNodeType,
|
LogNodeType: result.LogNodeType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,7 +75,9 @@ const data = computed<Data | null>(() => {
|
|||||||
{{ data.Description }}
|
{{ data.Description }}
|
||||||
<Iod v-if="data.LogNodeType !== 'object'" />
|
<Iod v-if="data.LogNodeType !== 'object'" />
|
||||||
</div>
|
</div>
|
||||||
<div class="rounded-[8px] p-[15px] text-[14px] bg-[var(--color-bg-quaternary)]">
|
<div
|
||||||
|
class="rounded-[8px] p-[15px] text-[14px] bg-[var(--color-bg-result-detail)] text-[var(--color-text-detail)]"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
class="markdown-content max-h-[240px] overflow-y-auto max-w-full"
|
class="markdown-content max-h-[240px] overflow-y-auto max-w-full"
|
||||||
v-html="data.Content"
|
v-html="data.Content"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { readConfig } from '@/utils/readJson.ts'
|
import { readConfig } from '@/utils/readJson.ts'
|
||||||
import { onMounted } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
|
|
||||||
interface Iod {
|
interface Iod {
|
||||||
name: string
|
name: string
|
||||||
@@ -28,10 +28,22 @@ function handleNext() {
|
|||||||
displayIndex.value++
|
displayIndex.value++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handlePrev() {
|
||||||
|
if (displayIndex.value === 0) {
|
||||||
|
displayIndex.value = data.value.length - 1
|
||||||
|
} else {
|
||||||
|
displayIndex.value--
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<el-popover trigger="hover" width="440">
|
<el-popover
|
||||||
|
trigger="hover"
|
||||||
|
width="440"
|
||||||
|
popper-style="background-color: var(--color-bg-result); border: none;"
|
||||||
|
>
|
||||||
<template #reference>
|
<template #reference>
|
||||||
<div
|
<div
|
||||||
class="rounded-full w-[20px] h-[20px] bg-[var(--color-bg-quaternary)] flex justify-center items-center cursor-pointer"
|
class="rounded-full w-[20px] h-[20px] bg-[var(--color-bg-quaternary)] flex justify-center items-center cursor-pointer"
|
||||||
@@ -40,12 +52,14 @@ function handleNext() {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #default v-if="data.length">
|
<template #default v-if="data.length">
|
||||||
<div>
|
<div class="bg-[var(--color-bg-result)]">
|
||||||
<div class="flex justify-between items-center p-2 pb-0 rounded-[8px] text-[16px] font-bold">
|
<div class="flex justify-between items-center p-2 pb-0 rounded-[8px] text-[16px] font-bold">
|
||||||
<span>数联网搜索结果</span>
|
<span>数联网搜索结果</span>
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<div>{{ `${displayIndex + 1}/${data.length}` }}</div>
|
<!-- <div>{{ `${displayIndex + 1}/${data.length}` }}</div>
|
||||||
<el-button type="primary" size="small" @click="handleNext">下一个</el-button>
|
<el-button type="primary" size="small" @click="handleNext">下一个</el-button> -->
|
||||||
|
<!-- 关闭 -->
|
||||||
|
<!-- <SvgIcon icon-class="close" size="15px" /> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 分割线 -->
|
<!-- 分割线 -->
|
||||||
@@ -53,19 +67,50 @@ function handleNext() {
|
|||||||
<div class="p-2 pt-0">
|
<div class="p-2 pt-0">
|
||||||
<div class="flex items-center w-full gap-3">
|
<div class="flex items-center w-full gap-3">
|
||||||
<div class="font-bold w-[75px] text-right flex-shrink-0">名称:</div>
|
<div class="font-bold w-[75px] text-right flex-shrink-0">名称:</div>
|
||||||
<div class="text-[var(--color-text-secondary)] flex-1 break-words">{{ displayIod.name }}</div>
|
<div class="text-[var(--color-text-detail)] flex-1 break-words">
|
||||||
|
{{ displayIod.name }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center w-full gap-3">
|
<div class="flex items-center w-full gap-3">
|
||||||
<div class="font-bold w-[75px] text-right flex-shrink-0">数据空间:</div>
|
<div class="font-bold w-[75px] text-right flex-shrink-0">数据空间:</div>
|
||||||
<div class="text-[var(--color-text-secondary)] lex-1 break-words">{{ displayIod.data_space }}</div>
|
<div class="text-[var(--color-text-detail)] lex-1 break-words">
|
||||||
|
{{ displayIod.data_space }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center w-full gap-3">
|
<div class="flex items-center w-full gap-3">
|
||||||
<div class="font-bold w-[75px] text-right flex-shrink-0">DOID:</div>
|
<div class="font-bold w-[75px] text-right flex-shrink-0">DOID:</div>
|
||||||
<div class="text-[var(--color-text-secondary)] lex-1 break-words">{{ displayIod.doId }}</div>
|
<div class="text-[var(--color-text-detail)] lex-1 break-words">
|
||||||
|
{{ displayIod.doId }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center w-full gap-3">
|
<div class="flex items-center w-full gap-3">
|
||||||
<div class="font-bold w-[75px] text-right flex-shrink-0">来源仓库:</div>
|
<div class="font-bold w-[75px] text-right flex-shrink-0">来源仓库:</div>
|
||||||
<div class="text-[var(--color-text-secondary)] flex-1 break-words break-al">{{ displayIod.fromRepo }}</div>
|
<div class="text-[var(--color-text-detail)] flex-1 break-words break-al">
|
||||||
|
{{ displayIod.fromRepo }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="card-item w-[80px] h-[25px] flex justify-between items-center rounded-[25px] bg-[#b1b1b1] ml-auto px-2"
|
||||||
|
>
|
||||||
|
<div class="text-[14px] text-[#ffffff] font-medium">
|
||||||
|
{{ `${displayIndex + 1}/${data.length}` }}
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-1">
|
||||||
|
<svg-icon
|
||||||
|
icon-class="left"
|
||||||
|
size="15px"
|
||||||
|
@click="handlePrev"
|
||||||
|
class="cursor-pointer hover:opacity-70"
|
||||||
|
></svg-icon>
|
||||||
|
<svg-icon
|
||||||
|
icon-class="right"
|
||||||
|
size="15px"
|
||||||
|
@click="handleNext"
|
||||||
|
class="cursor-pointer hover:opacity-70"
|
||||||
|
></svg-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,31 +1,56 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
defineProps<{
|
||||||
|
isAdding?: boolean
|
||||||
|
}>()
|
||||||
|
|
||||||
|
defineEmits<{
|
||||||
|
(e: 'start-add-output'): void
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="absolute inset-0 flex items-start gap-[14%]">
|
<div class="absolute inset-0 flex items-start gap-[14%]">
|
||||||
<!-- 左侧元素 -->
|
<!-- 左侧元素 -->
|
||||||
<div class="flex-1 relative h-full flex justify-center">
|
<div class="flex-1 relative h-full flex justify-center">
|
||||||
<!-- 背景那一根线 -->
|
<!-- 背景那一根线 -->
|
||||||
<div
|
<div class="h-full bg-[var(--color-bg-flow)] w-[5px]">
|
||||||
class="h-full bg-[var(--color-bg-tertiary)] w-[5px]"
|
|
||||||
>
|
|
||||||
<!-- 线底部的小圆球 -->
|
<!-- 线底部的小圆球 -->
|
||||||
<div
|
<div
|
||||||
class="absolute bottom-0 left-1/2 transform -translate-x-1/2 bg-[var(--color-bg-tertiary)] w-[15px] h-[15px] rounded-full"
|
class="absolute bottom-0 left-1/2 transform -translate-x-1/2 bg-[var(--color-bg-flow)] w-[15px] h-[15px] rounded-full"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 右侧元素 -->
|
<!-- 右侧元素 -->
|
||||||
<div class="flex-1 relative h-full flex justify-center">
|
<div class="flex-1 relative h-full flex justify-center">
|
||||||
<!-- 背景那一根线 -->
|
<!-- 背景那一根线 -->
|
||||||
<div
|
<div class="h-full bg-[var(--color-bg-flow)] w-[5px]">
|
||||||
class="h-full bg-[var(--color-bg-tertiary)] w-[5px]"
|
<!-- 顶部加号区域 -->
|
||||||
>
|
<!-- <div
|
||||||
|
v-if="!isAdding"
|
||||||
|
v-dev-only
|
||||||
|
class="plus-area mt-[35px] ml-[-15px] w-[34px] h-[34px] flex items-center justify-center cursor-pointer rounded-full"
|
||||||
|
@click="$emit('start-add-output')"
|
||||||
|
> -->
|
||||||
|
<!-- 加号图标 -->
|
||||||
|
<!-- <svg-icon
|
||||||
|
icon-class="plus"
|
||||||
|
color="var(--color-text)"
|
||||||
|
size="20px"
|
||||||
|
class="plus-icon opacity-0 transition-opacity duration-200"
|
||||||
|
/>
|
||||||
|
</div> -->
|
||||||
<!-- 线底部的小圆球 -->
|
<!-- 线底部的小圆球 -->
|
||||||
<div
|
<div
|
||||||
class="absolute bottom-0 left-1/2 transform -translate-x-1/2 bg-[var(--color-bg-tertiary)] w-[15px] h-[15px] rounded-full"
|
class="absolute bottom-0 left-1/2 transform -translate-x-1/2 bg-[var(--color-bg-flow)] w-[15px] h-[15px] rounded-full"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<style scoped>
|
||||||
</script>
|
.plus-area:hover .plus-icon {
|
||||||
|
opacity: 1;
|
||||||
|
border: 1px dashed var(--color-text);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -0,0 +1,365 @@
|
|||||||
|
<template>
|
||||||
|
<div class="root-node-wrapper">
|
||||||
|
<!-- 左侧连接点(输入) -->
|
||||||
|
<Handle type="target" :position="Position.Left" id="left" />
|
||||||
|
<!-- 右侧连接点(输出) -->
|
||||||
|
<Handle type="source" :position="Position.Right" id="right" />
|
||||||
|
<!-- 底部连接点(用于分支) -->
|
||||||
|
<Handle type="source" :position="Position.Bottom" id="bottom" />
|
||||||
|
|
||||||
|
<el-card class="root-node-card" :shadow="true">
|
||||||
|
<!-- 目标内容 -->
|
||||||
|
<div class="goal-content">
|
||||||
|
<div class="goal-label">初始目标</div>
|
||||||
|
<div class="goal-text">{{ goal }}</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 底部添加按钮 -->
|
||||||
|
<div v-if="!isAddingBranch" class="external-add-btn" @click="startAddBranch">
|
||||||
|
<el-icon :size="20" color="#409eff">
|
||||||
|
<CirclePlus />
|
||||||
|
</el-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 取消按钮(输入框显示时) -->
|
||||||
|
<div v-else class="external-add-btn cancel-btn" @click="cancelAddBranch">
|
||||||
|
<el-icon :size="20" color="#f56c6c">
|
||||||
|
<Remove />
|
||||||
|
</el-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 外部输入框 -->
|
||||||
|
<div v-if="isAddingBranch" class="external-input-container">
|
||||||
|
<el-input
|
||||||
|
v-model="branchInput"
|
||||||
|
placeholder="输入分支需求..."
|
||||||
|
size="small"
|
||||||
|
@keydown="handleBranchKeydown"
|
||||||
|
ref="branchInputRef"
|
||||||
|
class="branch-input"
|
||||||
|
>
|
||||||
|
<template #suffix>
|
||||||
|
<svg-icon
|
||||||
|
icon-class="paper-plane"
|
||||||
|
size="16px"
|
||||||
|
color="#409eff"
|
||||||
|
class="submit-icon"
|
||||||
|
:class="{ 'is-disabled': !branchInput.trim() }"
|
||||||
|
@click="submitBranch"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed, nextTick } from 'vue'
|
||||||
|
import { CirclePlus, Remove } from '@element-plus/icons-vue'
|
||||||
|
import { Handle, Position } from '@vue-flow/core'
|
||||||
|
import SvgIcon from '@/components/SvgIcon/index.vue'
|
||||||
|
interface RootNodeData {
|
||||||
|
goal: string
|
||||||
|
initialInput?: string[] | string
|
||||||
|
isRoot?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
id: string
|
||||||
|
data: RootNodeData
|
||||||
|
isAddingBranch?: boolean
|
||||||
|
[key: string]: any
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'add-branch', nodeId: string, branchContent: string): void
|
||||||
|
(e: 'start-add-branch', nodeId: string): void
|
||||||
|
(e: 'cancel-add-branch'): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const goal = computed(() => props.data.goal || '')
|
||||||
|
|
||||||
|
const initialInput = computed(() => props.data.initialInput)
|
||||||
|
|
||||||
|
const displayInput = computed(() => {
|
||||||
|
if (!initialInput.value) return false
|
||||||
|
if (Array.isArray(initialInput.value)) {
|
||||||
|
return initialInput.value.length > 0
|
||||||
|
}
|
||||||
|
return initialInput.value.trim().length > 0
|
||||||
|
})
|
||||||
|
|
||||||
|
// 分支添加相关状态(使用父组件传入的 prop)
|
||||||
|
const branchInput = ref('')
|
||||||
|
const branchInputRef = ref<InstanceType<typeof HTMLInputElement>>()
|
||||||
|
|
||||||
|
// 计算属性,使用父组件传入的状态
|
||||||
|
const isAddingBranch = computed(() => props.isAddingBranch || false)
|
||||||
|
|
||||||
|
// 分支添加相关方法
|
||||||
|
const startAddBranch = () => {
|
||||||
|
emit('start-add-branch', props.id)
|
||||||
|
branchInput.value = ''
|
||||||
|
nextTick(() => {
|
||||||
|
branchInputRef.value?.focus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const cancelAddBranch = () => {
|
||||||
|
emit('cancel-add-branch')
|
||||||
|
branchInput.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitBranch = () => {
|
||||||
|
if (branchInput.value.trim()) {
|
||||||
|
emit('add-branch', props.id, branchInput.value.trim())
|
||||||
|
branchInput.value = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleBranchKeydown = (event: KeyboardEvent) => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
event.preventDefault()
|
||||||
|
submitBranch()
|
||||||
|
} else if (event.key === 'Escape') {
|
||||||
|
cancelAddBranch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.root-node-card {
|
||||||
|
width: 200px;
|
||||||
|
min-height: 100px;
|
||||||
|
background: var(--color-bg-three);
|
||||||
|
border: 2px solid #409eff;
|
||||||
|
box-sizing: border-box;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.2);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0 6px 16px rgba(64, 158, 255, 0.3);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-card__body) {
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-badge {
|
||||||
|
position: absolute;
|
||||||
|
top: -10px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goal-icon {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goal-content {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goal-label {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #409eff;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goal-text {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text-title-header);
|
||||||
|
line-height: 1.6;
|
||||||
|
word-break: break-word;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.initial-input {
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-divider {
|
||||||
|
height: 1px;
|
||||||
|
background: linear-gradient(to right, transparent, #409eff, transparent);
|
||||||
|
margin: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-label {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 4px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-text {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
text-align: center;
|
||||||
|
word-break: break-word;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 节点包装器
|
||||||
|
.root-node-wrapper {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 外部添加按钮
|
||||||
|
.external-add-btn {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -20px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #fff;
|
||||||
|
border: 2px solid #409eff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
z-index: 10;
|
||||||
|
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.2);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #409eff;
|
||||||
|
transform: translateX(-50%) scale(1.1);
|
||||||
|
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
|
||||||
|
|
||||||
|
.el-icon {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateX(-50%) scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消按钮样式
|
||||||
|
&.cancel-btn {
|
||||||
|
border-color: #f56c6c;
|
||||||
|
box-shadow: 0 2px 8px rgba(245, 108, 108, 0.2);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #f56c6c;
|
||||||
|
box-shadow: 0 4px 12px rgba(245, 108, 108, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 外部输入容器
|
||||||
|
.external-input-container {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -80px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 260px;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px;
|
||||||
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
|
||||||
|
z-index: 100;
|
||||||
|
animation: slideDown 0.2s ease-out;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -6px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
background: #fff;
|
||||||
|
border-left: 1px solid #dcdfe6;
|
||||||
|
border-top: 1px solid #dcdfe6;
|
||||||
|
transform: translateX(-50%) rotate(45deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideDown {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-50%) translateY(-10px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(-50%) translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-input {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
:deep(.el-input__wrapper) {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: #c0c4cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-focus {
|
||||||
|
border-color: #409eff;
|
||||||
|
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-input__inner {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #000;
|
||||||
|
padding-right: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交图标样式
|
||||||
|
.submit-icon {
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&:hover:not(.is-disabled) {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active:not(.is-disabled) {
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-disabled {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,518 @@
|
|||||||
|
<template>
|
||||||
|
<div class="task-node-wrapper">
|
||||||
|
<!-- 左侧连接点(输入) -->
|
||||||
|
<Handle type="target" :position="Position.Left" id="left" />
|
||||||
|
<!-- 右侧连接点(输出) -->
|
||||||
|
<Handle type="source" :position="Position.Right" id="right" />
|
||||||
|
<!-- 底部连接点(用于分支) -->
|
||||||
|
<Handle type="source" :position="Position.Bottom" id="bottom" />
|
||||||
|
|
||||||
|
<el-card
|
||||||
|
class="task-node-card"
|
||||||
|
:class="{
|
||||||
|
'is-editing': isEditing,
|
||||||
|
'is-active': isActive,
|
||||||
|
'is-branch-selected': props.isBranchSelected
|
||||||
|
}"
|
||||||
|
:shadow="true"
|
||||||
|
>
|
||||||
|
<!-- 任务名称 -->
|
||||||
|
<div class="task-name">{{ task.StepName }}</div>
|
||||||
|
|
||||||
|
<!-- 智能体列表 -->
|
||||||
|
<div class="agents-container">
|
||||||
|
<el-tooltip
|
||||||
|
v-for="agentSelection in task.AgentSelection"
|
||||||
|
:key="agentSelection"
|
||||||
|
effect="light"
|
||||||
|
placement="top"
|
||||||
|
:show-after="500"
|
||||||
|
popper-class="task-syllabus-tooltip-popper"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<div class="agent-tooltip">
|
||||||
|
<div class="text-[16px] font-bold">{{ agentSelection }}</div>
|
||||||
|
<div class="h-[1px] w-full bg-[#494B51] my-[4px]"></div>
|
||||||
|
<div class="text-[12px]">
|
||||||
|
{{ task.TaskProcess.find(i => i.AgentName === agentSelection)?.Description }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="agent-icon" :style="{ background: getAgentIcon(agentSelection).color }">
|
||||||
|
<svg-icon :icon-class="getAgentIcon(agentSelection).icon" color="#fff" size="20px" />
|
||||||
|
</div>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 底部添加按钮(卡片外部) -->
|
||||||
|
<div v-if="!isAddingBranch" class="external-add-btn" @click="startAddBranch">
|
||||||
|
<el-icon :size="20" color="#409eff">
|
||||||
|
<CirclePlus />
|
||||||
|
</el-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 取消按钮(输入框显示时) -->
|
||||||
|
<div v-else class="external-add-btn cancel-btn" @click="cancelAddBranch">
|
||||||
|
<el-icon :size="20" color="#f56c6c">
|
||||||
|
<Remove />
|
||||||
|
</el-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 外部输入框 -->
|
||||||
|
<div v-if="isAddingBranch" class="external-input-container">
|
||||||
|
<el-input
|
||||||
|
v-model="branchInput"
|
||||||
|
placeholder="输入分支需求..."
|
||||||
|
size="small"
|
||||||
|
@keydown="handleBranchKeydown"
|
||||||
|
ref="branchInputRef"
|
||||||
|
class="branch-input"
|
||||||
|
>
|
||||||
|
<template #suffix>
|
||||||
|
<svg-icon
|
||||||
|
icon-class="paper-plane"
|
||||||
|
size="16px"
|
||||||
|
color="#409eff"
|
||||||
|
class="submit-icon"
|
||||||
|
:class="{ 'is-disabled': !branchInput.trim() }"
|
||||||
|
@click="submitBranch"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed, nextTick } from 'vue'
|
||||||
|
import { CirclePlus, Remove } from '@element-plus/icons-vue'
|
||||||
|
import { Handle, Position } from '@vue-flow/core'
|
||||||
|
import { type IRawStepTask } from '@/stores'
|
||||||
|
import { getAgentMapIcon } from '@/layout/components/config'
|
||||||
|
import SvgIcon from '@/components/SvgIcon/index.vue'
|
||||||
|
|
||||||
|
interface TaskNodeData {
|
||||||
|
task: IRawStepTask
|
||||||
|
isEditing: boolean
|
||||||
|
editingContent: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用更宽松的类型定义来避免类型错误
|
||||||
|
const props = defineProps<{
|
||||||
|
id: string
|
||||||
|
data: TaskNodeData
|
||||||
|
isAddingBranch?: boolean
|
||||||
|
isBranchSelected?: boolean // 是否属于选中的分支路径
|
||||||
|
[key: string]: any
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'edit-task', nodeId: string): void
|
||||||
|
(e: 'save-task', nodeId: string, content: string): void
|
||||||
|
(e: 'cancel-edit', nodeId: string): void
|
||||||
|
(e: 'add-branch', nodeId: string, branchContent: string): void
|
||||||
|
(e: 'start-add-branch', nodeId: string): void
|
||||||
|
(e: 'cancel-add-branch'): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const editingContent = ref(props.data.task.TaskContent || '')
|
||||||
|
|
||||||
|
// 分支添加相关状态(使用父组件传入的 prop)
|
||||||
|
const branchInput = ref('')
|
||||||
|
const branchInputRef = ref<InstanceType<typeof HTMLInputElement>>()
|
||||||
|
|
||||||
|
// 计算属性,使用父组件传入的状态
|
||||||
|
const isAddingBranch = computed(() => props.isAddingBranch || false)
|
||||||
|
|
||||||
|
const isEditing = computed({
|
||||||
|
get: () => props.data.isEditing,
|
||||||
|
set: value => {
|
||||||
|
if (!value) {
|
||||||
|
emit('cancel-edit', props.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const isActive = computed(() => {
|
||||||
|
return props.data.task.StepName === props.data.task.StepName
|
||||||
|
})
|
||||||
|
|
||||||
|
const task = computed(() => props.data.task)
|
||||||
|
|
||||||
|
const getAgentIcon = (agentName: string) => {
|
||||||
|
return getAgentMapIcon(agentName)
|
||||||
|
}
|
||||||
|
|
||||||
|
const startEdit = () => {
|
||||||
|
emit('edit-task', props.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveEdit = () => {
|
||||||
|
emit('save-task', props.id, editingContent.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const cancelEdit = () => {
|
||||||
|
emit('cancel-edit', props.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleKeydown = (event: KeyboardEvent) => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
event.preventDefault()
|
||||||
|
saveEdit()
|
||||||
|
} else if (event.key === 'Escape') {
|
||||||
|
cancelEdit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分支添加相关方法
|
||||||
|
const startAddBranch = () => {
|
||||||
|
emit('start-add-branch', props.id)
|
||||||
|
branchInput.value = ''
|
||||||
|
nextTick(() => {
|
||||||
|
branchInputRef.value?.focus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const cancelAddBranch = () => {
|
||||||
|
emit('cancel-add-branch')
|
||||||
|
branchInput.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitBranch = () => {
|
||||||
|
if (branchInput.value.trim()) {
|
||||||
|
emit('add-branch', props.id, branchInput.value.trim())
|
||||||
|
branchInput.value = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleBranchKeydown = (event: KeyboardEvent) => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
event.preventDefault()
|
||||||
|
submitBranch()
|
||||||
|
} else if (event.key === 'Escape') {
|
||||||
|
cancelAddBranch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.task-node-card {
|
||||||
|
width: 150px;
|
||||||
|
min-height: 100px;
|
||||||
|
background-color: var(--color-card-bg-task);
|
||||||
|
border: 1px solid var(--color-card-border-task);
|
||||||
|
box-sizing: border-box;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--color-card-bg-task-hover);
|
||||||
|
border-color: var(--color-card-border-hover);
|
||||||
|
box-shadow: var(--color-card-shadow-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分支选中高亮样式(绿色)
|
||||||
|
&.is-branch-selected {
|
||||||
|
border-color: #67c23a;
|
||||||
|
box-shadow: 0 0 0 3px rgba(103, 194, 58, 0.3);
|
||||||
|
background-color: rgba(103, 194, 58, 0.05);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: #67c23a;
|
||||||
|
box-shadow: 0 0 0 3px rgba(103, 194, 58, 0.4);
|
||||||
|
background-color: rgba(103, 194, 58, 0.08);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-editing {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-card__body) {
|
||||||
|
padding: 12px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-name {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--color-text-title-header);
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
height: 1px;
|
||||||
|
width: 100%;
|
||||||
|
background: var(--color-border-separate);
|
||||||
|
margin: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-content {
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
min-height: 40px;
|
||||||
|
word-break: break-word;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-content-editing {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agents-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-icon {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-outputs {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-label {
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-tooltip {
|
||||||
|
padding: 8px;
|
||||||
|
max-width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑器样式
|
||||||
|
.task-content-editor {
|
||||||
|
:deep(.el-textarea__inner) {
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 4px;
|
||||||
|
resize: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 节点包装器(用于容纳外部按钮)
|
||||||
|
.task-node-wrapper {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 外部添加按钮
|
||||||
|
.external-add-btn {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -20px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #fff;
|
||||||
|
border: 2px solid #409eff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
z-index: 10;
|
||||||
|
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.2);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #409eff;
|
||||||
|
transform: translateX(-50%) scale(1.1);
|
||||||
|
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
|
||||||
|
|
||||||
|
.el-icon {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateX(-50%) scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消按钮样式
|
||||||
|
&.cancel-btn {
|
||||||
|
border-color: #f56c6c;
|
||||||
|
box-shadow: 0 2px 8px rgba(245, 108, 108, 0.2);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #f56c6c;
|
||||||
|
box-shadow: 0 4px 12px rgba(245, 108, 108, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 外部输入容器
|
||||||
|
.external-input-container {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -80px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 260px;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px;
|
||||||
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
|
||||||
|
z-index: 100;
|
||||||
|
animation: slideDown 0.2s ease-out;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -6px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
background: #fff;
|
||||||
|
border-left: 1px solid #dcdfe6;
|
||||||
|
border-top: 1px solid #dcdfe6;
|
||||||
|
transform: translateX(-50%) rotate(45deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideDown {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-50%) translateY(-10px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(-50%) translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-branch-section {
|
||||||
|
margin-top: 8px;
|
||||||
|
padding-top: 8px;
|
||||||
|
border-top: 1px dashed var(--color-border-separate);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-branch-btn {
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-input-container {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-input {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
:deep(.el-input__wrapper) {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: #c0c4cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-focus {
|
||||||
|
border-color: #409eff;
|
||||||
|
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-input__inner {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #000;
|
||||||
|
padding-right: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交图标样式
|
||||||
|
.submit-icon {
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&:hover:not(.is-disabled) {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active:not(.is-disabled) {
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-disabled {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-adding-branch {
|
||||||
|
.task-node-card {
|
||||||
|
border-color: #409eff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.task-syllabus-tooltip-popper {
|
||||||
|
z-index: 4000 !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
# Mock 数据说明
|
||||||
|
|
||||||
|
本目录包含用于分支功能的 mock 数据,支持在开发环境中测试分支逻辑,无需调用真实后端 API。
|
||||||
|
|
||||||
|
## 文件说明
|
||||||
|
|
||||||
|
### 1. `branchPlanOutlineMock.ts`
|
||||||
|
**用途**: 根节点级别的分支(任务大纲分支)
|
||||||
|
|
||||||
|
**类型**: `IApiStepTask[][]`
|
||||||
|
|
||||||
|
**说明**: 返回多个分支方案,每个方案是一个完整的任务流程(IApiStepTask[])
|
||||||
|
|
||||||
|
**示例结构**:
|
||||||
|
```typescript
|
||||||
|
[
|
||||||
|
// 第一个分支方案(瀑布流开发)
|
||||||
|
[task1, task2, ...],
|
||||||
|
// 第二个分支方案(敏捷开发)
|
||||||
|
[task1, task2, ...],
|
||||||
|
// 第三个分支方案(快速原型)
|
||||||
|
[task1, task2, ...]
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. `branchTaskProcessMock.ts`
|
||||||
|
**用途**: 任务节点级别的分支(任务流程分支)
|
||||||
|
|
||||||
|
**类型**: `IApiAgentAction[][]`
|
||||||
|
|
||||||
|
**说明**: 返回多个分支方案,每个方案是一系列动作(IApiAgentAction[]),这些动作会追加到现有任务的 TaskProcess 中
|
||||||
|
|
||||||
|
**示例结构**:
|
||||||
|
```typescript
|
||||||
|
[
|
||||||
|
// 第一个分支方案(标准开发流程)
|
||||||
|
[action1, action2, action3, ...],
|
||||||
|
// 第二个分支方案(快速原型流程)
|
||||||
|
[action1, action2, ...],
|
||||||
|
// ... 更多方案
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 如何使用
|
||||||
|
|
||||||
|
### 切换 Mock 数据开关
|
||||||
|
|
||||||
|
在 `PlanModification.vue` 文件中,找到以下配置:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 开关:控制是否使用 mock 数据(开发时设置为 true,生产时设置为 false)
|
||||||
|
const USE_MOCK_DATA = true
|
||||||
|
```
|
||||||
|
|
||||||
|
- **开发阶段**: 设置为 `true`,使用 mock 数据
|
||||||
|
- **生产环境**: 设置为 `false`,调用真实 API
|
||||||
|
|
||||||
|
### 数据转换流程
|
||||||
|
|
||||||
|
```
|
||||||
|
IApiStepTask (API 格式)
|
||||||
|
↓ convertToIRawStepTask()
|
||||||
|
IRawStepTask (内部格式)
|
||||||
|
↓
|
||||||
|
更新到 agentsStore.agentRawPlan.data
|
||||||
|
↓
|
||||||
|
Vue Flow 流程图自动更新
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mock 数据特点
|
||||||
|
|
||||||
|
1. **模拟网络延迟**: Mock 数据调用会模拟 800ms 的网络延迟
|
||||||
|
2. **多方案选择**: 每个分支接口提供多个备选方案(目前默认使用第一个)
|
||||||
|
3. **完整数据结构**: Mock 数据包含完整的字段,与真实 API 返回格式一致
|
||||||
|
4. **类型安全**: 使用 TypeScript 类型定义,确保类型正确
|
||||||
|
|
||||||
|
## 扩展 Mock 数据
|
||||||
|
|
||||||
|
### 添加新的分支方案
|
||||||
|
|
||||||
|
在对应的 mock 文件中添加新的数组元素:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// branchPlanOutlineMock.ts
|
||||||
|
const mockPlanBranchData: IApiStepTask[][] = [
|
||||||
|
// 现有方案...
|
||||||
|
[
|
||||||
|
// 新增方案
|
||||||
|
{
|
||||||
|
name: '新方案步骤1',
|
||||||
|
content: '...',
|
||||||
|
// ... 其他字段
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '新方案步骤2',
|
||||||
|
content: '...',
|
||||||
|
// ... 其他字段
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 随机选择方案
|
||||||
|
|
||||||
|
修改 `PlanModification.vue` 中的方案选择逻辑:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 当前:固定选择第一个
|
||||||
|
const mockBranchTasks = branchPlanOutlineMock[0]
|
||||||
|
|
||||||
|
// 改为:随机选择一个方案
|
||||||
|
const randomIndex = Math.floor(Math.random() * branchPlanOutlineMock.length)
|
||||||
|
const mockBranchTasks = branchPlanOutlineMock[randomIndex]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. ⚠️ **生产环境**: 发布前务必将 `USE_MOCK_DATA` 设置为 `false`
|
||||||
|
2. 🔍 **调试**: 查看控制台日志,以 `[Mock]` 开头的是 mock 数据相关日志
|
||||||
|
3. 📝 **数据一致性**: 确保 mock 数据结构与真实 API 返回格式一致
|
||||||
|
4. 🔄 **数据持久化**: Mock 数据仅存在于前端,刷新页面后会丢失
|
||||||
|
|
||||||
|
## 相关文件
|
||||||
|
|
||||||
|
- `PlanModification.vue`: 主要逻辑文件,包含分支添加和 mock 数据集成
|
||||||
|
- `api/index.ts`: 真实 API 接口定义
|
||||||
|
- `stores/modules/agents.ts`: 类型定义和数据存储
|
||||||
@@ -0,0 +1,246 @@
|
|||||||
|
// branch_PlanOutline 接口的 Mock 数据和 Mock API
|
||||||
|
// 模拟后端返回的原始数据格式(IRawPlanResponse)
|
||||||
|
|
||||||
|
import type { IRawPlanResponse, IRawStepTask } from '@/stores'
|
||||||
|
|
||||||
|
// 后端返回的数据格式
|
||||||
|
export type BranchPlanOutlineResponse = IRawPlanResponse
|
||||||
|
|
||||||
|
// Mock 数据:模拟后端返回的原始分支数据(不含 Collaboration_Brief_FrontEnd)
|
||||||
|
// 注意:这里模拟的是 branch_PlanOutline 函数返回的数据,不是前端转换后的数据
|
||||||
|
const mockBranchDataRaw: IRawStepTask[][] = [
|
||||||
|
// 第一个分支方案
|
||||||
|
[
|
||||||
|
{
|
||||||
|
StepName: '分析用户需求',
|
||||||
|
TaskContent: '分析用户需求,制定项目开发计划',
|
||||||
|
InputObject_List: ['腐蚀类型及成因列表'],
|
||||||
|
OutputObject: '项目开发计划书',
|
||||||
|
AgentSelection: ['腐蚀机理研究员', '实验材料学家'],
|
||||||
|
Collaboration_Brief_frontEnd: {
|
||||||
|
template: '',
|
||||||
|
data: {},
|
||||||
|
},
|
||||||
|
TaskProcess: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StepName: '系统设计与架构',
|
||||||
|
TaskContent: '设计系统架构和数据库结构',
|
||||||
|
InputObject_List: ['项目开发计划书'],
|
||||||
|
OutputObject: '系统设计文档',
|
||||||
|
AgentSelection: ['腐蚀机理研究员', '防护工程专家'],
|
||||||
|
Collaboration_Brief_frontEnd: {
|
||||||
|
template: '',
|
||||||
|
data: {},
|
||||||
|
},
|
||||||
|
TaskProcess: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// 第二个分支方案(快速原型方案)
|
||||||
|
[
|
||||||
|
{
|
||||||
|
StepName: '需求快速原型',
|
||||||
|
TaskContent: '构建快速原型验证核心功能',
|
||||||
|
InputObject_List: ['腐蚀类型及成因列表'],
|
||||||
|
OutputObject: '原型系统',
|
||||||
|
AgentSelection: ['实验材料学家', '防护工程专家'],
|
||||||
|
Collaboration_Brief_frontEnd: {
|
||||||
|
template: '',
|
||||||
|
data: {},
|
||||||
|
},
|
||||||
|
TaskProcess: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StepName: '原型测试与优化',
|
||||||
|
TaskContent: '测试原型并根据反馈快速迭代',
|
||||||
|
InputObject_List: ['原型系统'],
|
||||||
|
OutputObject: '优化后的原型',
|
||||||
|
AgentSelection: ['腐蚀机理研究员', '实验材料学家'],
|
||||||
|
Collaboration_Brief_frontEnd: {
|
||||||
|
template: '',
|
||||||
|
data: {},
|
||||||
|
},
|
||||||
|
TaskProcess: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// 第三个分支方案(质量优先方案)
|
||||||
|
[
|
||||||
|
{
|
||||||
|
StepName: '需求深度分析',
|
||||||
|
TaskContent: '深入分析用户需求和技术可行性',
|
||||||
|
InputObject_List: ['腐蚀类型及成因列表'],
|
||||||
|
OutputObject: '详细需求分析报告',
|
||||||
|
AgentSelection: ['腐蚀机理研究员', '防护工程专家'],
|
||||||
|
Collaboration_Brief_frontEnd: {
|
||||||
|
template: '',
|
||||||
|
data: {},
|
||||||
|
},
|
||||||
|
TaskProcess: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StepName: '质量保障设计',
|
||||||
|
TaskContent: '设计质量保障体系和测试方案',
|
||||||
|
InputObject_List: ['详细需求分析报告'],
|
||||||
|
OutputObject: '质量保障方案',
|
||||||
|
AgentSelection: ['实验材料学家', '防护工程专家'],
|
||||||
|
Collaboration_Brief_frontEnd: {
|
||||||
|
template: '',
|
||||||
|
data: {},
|
||||||
|
},
|
||||||
|
TaskProcess: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StepName: '系统开发与测试',
|
||||||
|
TaskContent: '按质量标准进行系统开发和测试',
|
||||||
|
InputObject_List: ['质量保障方案'],
|
||||||
|
OutputObject: '经过完整测试的系统',
|
||||||
|
AgentSelection: ['腐蚀机理研究员', '实验材料学家'],
|
||||||
|
Collaboration_Brief_frontEnd: {
|
||||||
|
template: '',
|
||||||
|
data: {},
|
||||||
|
},
|
||||||
|
TaskProcess: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// 第四个分支方案(敏捷开发方案)
|
||||||
|
[
|
||||||
|
{
|
||||||
|
StepName: '迭代规划',
|
||||||
|
TaskContent: '制定敏捷开发迭代计划',
|
||||||
|
InputObject_List: ['腐蚀类型及成因列表'],
|
||||||
|
OutputObject: '迭代计划',
|
||||||
|
AgentSelection: ['防护工程专家', '腐蚀机理研究员'],
|
||||||
|
Collaboration_Brief_frontEnd: {
|
||||||
|
template: '',
|
||||||
|
data: {},
|
||||||
|
},
|
||||||
|
TaskProcess: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StepName: '快速开发与验证',
|
||||||
|
TaskContent: '快速开发并持续验证功能',
|
||||||
|
InputObject_List: ['迭代计划'],
|
||||||
|
OutputObject: '可运行的功能模块',
|
||||||
|
AgentSelection: ['实验材料学家', '防护工程专家'],
|
||||||
|
Collaboration_Brief_frontEnd: {
|
||||||
|
template: '',
|
||||||
|
data: {},
|
||||||
|
},
|
||||||
|
TaskProcess: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模拟后端的 Add_Collaboration_Brief_FrontEnd 函数
|
||||||
|
* 为每个任务添加前端协作简报(Collaboration_Brief_frontEnd)
|
||||||
|
*/
|
||||||
|
function Add_Collaboration_Brief_FrontEnd(branchList: IRawStepTask[][]): IRawStepTask[][] {
|
||||||
|
return branchList.map((tasks) =>
|
||||||
|
tasks.map((task) => ({
|
||||||
|
...task,
|
||||||
|
Collaboration_Brief_frontEnd: generateCollaborationBrief(task),
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成协作简报(Collaboration_Brief_frontEnd)
|
||||||
|
* 根据 StepName、TaskContent、AgentSelection 生成模板和数据
|
||||||
|
*/
|
||||||
|
function generateCollaborationBrief(task: IRawStepTask): {
|
||||||
|
template: string
|
||||||
|
data: Record<string, any>
|
||||||
|
} {
|
||||||
|
const agents = task.AgentSelection || []
|
||||||
|
const stepName = task.StepName || ''
|
||||||
|
|
||||||
|
// 为每个 agent 生成颜色
|
||||||
|
const colors = [
|
||||||
|
'hsl(210, 70%, 50%)',
|
||||||
|
'hsl(30, 70%, 50%)',
|
||||||
|
'hsl(120, 70%, 50%)',
|
||||||
|
'hsl(270, 70%, 50%)',
|
||||||
|
]
|
||||||
|
|
||||||
|
// 生成 data 对象
|
||||||
|
const data: Record<string, any> = {}
|
||||||
|
agents.forEach((agent, index) => {
|
||||||
|
data[agent] = {
|
||||||
|
text: agent,
|
||||||
|
style: { background: colors[index % colors.length] },
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 生成 template(简化版本,实际应根据任务内容生成)
|
||||||
|
const template =
|
||||||
|
agents.length > 0
|
||||||
|
? agents.map((agent, i) => `!<${agent}>!负责!<${stepName}-${i}>!`).join(',')
|
||||||
|
: ''
|
||||||
|
|
||||||
|
return {
|
||||||
|
template,
|
||||||
|
data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mock API:模拟后端 branch_PlanOutline 接口调用
|
||||||
|
*
|
||||||
|
* @param branch_Number - 分支数量
|
||||||
|
* @param Modification_Requirement - 修改需求
|
||||||
|
* @param Existing_Steps - 现有步骤列表(包含完整信息的对象数组)
|
||||||
|
* @param Baseline_Completion - 基线完成度
|
||||||
|
* @param InitialObject_List - 初始对象列表
|
||||||
|
* @param General_Goal - 总体目标
|
||||||
|
* @returns Promise<IRawPlanResponse> - 返回包含 'Collaboration Process' 的响应
|
||||||
|
*/
|
||||||
|
export const mockBranchPlanOutlineAPI = async (params: {
|
||||||
|
branch_Number: number
|
||||||
|
Modification_Requirement: string
|
||||||
|
Existing_Steps: any[] // 临时使用 any[],因为这里没有 IRawStepTask 类型
|
||||||
|
Baseline_Completion: number
|
||||||
|
InitialObject_List: string[]
|
||||||
|
General_Goal: string
|
||||||
|
}): Promise<IRawPlanResponse> => {
|
||||||
|
// 模拟网络延迟 800ms
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 800))
|
||||||
|
|
||||||
|
console.log('[Mock API] branch_PlanOutline 调用参数:', params)
|
||||||
|
|
||||||
|
// 🆕 使用轮询方式选择分支方案(依次循环使用所有分支方案)
|
||||||
|
const totalBranches = mockBranchDataRaw.length
|
||||||
|
const sessionKey = `branch-plan-outline-index-${params.General_Goal || 'default'}`
|
||||||
|
|
||||||
|
// 获取上一次的选择索引
|
||||||
|
let lastIndex = parseInt(sessionStorage.getItem(sessionKey) || '0')
|
||||||
|
|
||||||
|
// 计算本次的选择索引(轮询到下一个分支)
|
||||||
|
const selectedBranchIndex = (lastIndex + 1) % totalBranches
|
||||||
|
|
||||||
|
// 保存本次的选择索引
|
||||||
|
sessionStorage.setItem(sessionKey, selectedBranchIndex.toString())
|
||||||
|
|
||||||
|
const rawBranchData = mockBranchDataRaw[selectedBranchIndex] || []
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
'[Mock API] branch_PlanOutline 选择分支方案:',
|
||||||
|
selectedBranchIndex + 1,
|
||||||
|
'/',
|
||||||
|
totalBranches,
|
||||||
|
)
|
||||||
|
|
||||||
|
// 模拟后端处理:添加 Collaboration_Brief_FrontEnd
|
||||||
|
const processedBranches = Add_Collaboration_Brief_FrontEnd([rawBranchData])
|
||||||
|
|
||||||
|
// 构造响应数据(符合后端返回格式)
|
||||||
|
const response: IRawPlanResponse = {
|
||||||
|
'Collaboration Process': processedBranches[0] || [],
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[Mock API] branch_PlanOutline 返回数据:', response)
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
export default mockBranchPlanOutlineAPI
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
// branch_TaskProcess 接口的 Mock 数据和 Mock API
|
||||||
|
export interface BranchAction {
|
||||||
|
ID: string
|
||||||
|
ActionType: string
|
||||||
|
AgentName: string
|
||||||
|
Description: string
|
||||||
|
ImportantInput: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type BranchTaskProcessResponse = BranchAction[][]
|
||||||
|
|
||||||
|
// Mock 数据:模拟后端返回的原始任务流程数据(2D 数组)
|
||||||
|
// 格式:[[action1, action2], [action3, action4]]
|
||||||
|
const mockBranchTaskProcessDataRaw: BranchAction[][] = [
|
||||||
|
// 第一个任务分支方案
|
||||||
|
[
|
||||||
|
{
|
||||||
|
ID: 'agent3',
|
||||||
|
ActionType: 'Critique',
|
||||||
|
AgentName: '实验材料学家',
|
||||||
|
Description: '详细分析用户需求文档',
|
||||||
|
ImportantInput: ['agent2'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'agent4',
|
||||||
|
ActionType: 'Critique',
|
||||||
|
AgentName: '腐蚀机理研究员',
|
||||||
|
Description: '设计系统整体架构',
|
||||||
|
ImportantInput: ['agent3'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'agent5',
|
||||||
|
ActionType: 'Improve',
|
||||||
|
AgentName: '防护工程专家',
|
||||||
|
Description: '实现系统核心功能',
|
||||||
|
ImportantInput: ['agent4'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'agent6',
|
||||||
|
ActionType: 'Finalize',
|
||||||
|
AgentName: '实验材料学家',
|
||||||
|
Description: '进行系统集成测试',
|
||||||
|
ImportantInput: ['agent5'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// 第二个任务分支方案
|
||||||
|
[
|
||||||
|
{
|
||||||
|
ID: 'agent7',
|
||||||
|
ActionType: 'Critique',
|
||||||
|
AgentName: '实验材料学家',
|
||||||
|
Description: '深入分析用户需求和技术约束',
|
||||||
|
ImportantInput: ['agent2'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'agent8',
|
||||||
|
ActionType: 'Critique',
|
||||||
|
AgentName: '防护工程专家',
|
||||||
|
Description: '设计系统技术架构和数据流',
|
||||||
|
ImportantInput: ['agent8'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'agent9',
|
||||||
|
ActionType: 'Improve',
|
||||||
|
AgentName: '腐蚀机理研究员',
|
||||||
|
Description: '评估系统安全性',
|
||||||
|
ImportantInput: ['agent4'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'agent10',
|
||||||
|
ActionType: 'Finalize',
|
||||||
|
AgentName: '实验材料学家',
|
||||||
|
Description: '完成系统安全测试',
|
||||||
|
ImportantInput: ['agent9'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
//第三个任务分支方案
|
||||||
|
[
|
||||||
|
{
|
||||||
|
ID: 'agent12',
|
||||||
|
ActionType: 'Critique',
|
||||||
|
AgentName: '腐蚀机理研究员',
|
||||||
|
Description: '设计系统整体架构',
|
||||||
|
ImportantInput: ['agent11'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'agent13',
|
||||||
|
ActionType: 'Improve',
|
||||||
|
AgentName: '防护工程专家',
|
||||||
|
Description: '实现系统核心功能',
|
||||||
|
ImportantInput: ['agent12'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'agent14',
|
||||||
|
ActionType: 'Finalize',
|
||||||
|
AgentName: '实验材料学家',
|
||||||
|
Description: '进行系统集成测试',
|
||||||
|
ImportantInput: ['agent13'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mock API:模拟后端 branch_TaskProcess 接口调用
|
||||||
|
*
|
||||||
|
* @param branch_Number - 分支数量
|
||||||
|
* @param Modification_Requirement - 修改需求
|
||||||
|
* @param Existing_Steps - 现有步骤列表(包含完整信息的对象数组)
|
||||||
|
* @param Baseline_Completion - 基线完成度
|
||||||
|
* @param stepTaskExisting - 现有任务
|
||||||
|
* @param General_Goal - 总体目标
|
||||||
|
* @returns Promise<BranchAction[][]> - 返回 2D 数组,与后端格式完全一致
|
||||||
|
*/
|
||||||
|
export const mockBranchTaskProcessAPI = async (params: {
|
||||||
|
branch_Number: number
|
||||||
|
Modification_Requirement: string
|
||||||
|
Existing_Steps: BranchAction[]
|
||||||
|
Baseline_Completion: number
|
||||||
|
stepTaskExisting: any
|
||||||
|
General_Goal: string
|
||||||
|
}): Promise<BranchAction[][]> => {
|
||||||
|
// 模拟网络延迟 800ms
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 800))
|
||||||
|
|
||||||
|
console.log('[Mock API] branch_TaskProcess 调用参数:', params)
|
||||||
|
|
||||||
|
// 🆕 使用轮询方式选择分支方案(依次循环使用所有分支方案)
|
||||||
|
const totalBranches = mockBranchTaskProcessDataRaw.length
|
||||||
|
const sessionKey = `branch-task-process-index-${params.stepTaskExisting?.Id || 'default'}`
|
||||||
|
|
||||||
|
// 获取上一次的选择索引
|
||||||
|
let lastIndex = parseInt(sessionStorage.getItem(sessionKey) || '0')
|
||||||
|
|
||||||
|
// 计算本次的选择索引(轮询到下一个分支)
|
||||||
|
const selectedBranchIndex = (lastIndex + 1) % totalBranches
|
||||||
|
|
||||||
|
// 保存本次的选择索引
|
||||||
|
sessionStorage.setItem(sessionKey, selectedBranchIndex.toString())
|
||||||
|
|
||||||
|
const rawBranchData = mockBranchTaskProcessDataRaw[selectedBranchIndex] || []
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
'[Mock API] branch_TaskProcess 选择分支方案:',
|
||||||
|
selectedBranchIndex + 1,
|
||||||
|
'/',
|
||||||
|
totalBranches,
|
||||||
|
)
|
||||||
|
console.log('[Mock API] branch_TaskProcess 返回数据:', rawBranchData)
|
||||||
|
|
||||||
|
// 直接返回 2D 数组,与后端格式完全一致
|
||||||
|
return [rawBranchData]
|
||||||
|
}
|
||||||
|
|
||||||
|
export default mockBranchTaskProcessAPI
|
||||||
@@ -0,0 +1,617 @@
|
|||||||
|
// fill_stepTask 接口的 Mock API
|
||||||
|
// 简化版本:硬编码多个流程卡片的填充内容(支持多个分支方案)
|
||||||
|
|
||||||
|
import type { IRawStepTask } from '@/stores'
|
||||||
|
|
||||||
|
// 后端返回的数据格式
|
||||||
|
export type FillStepTaskResponse = IRawStepTask
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mock API:模拟后端 fill_stepTask 接口调用
|
||||||
|
*
|
||||||
|
* 根据 stepTask 的 StepName 返回对应的填充内容
|
||||||
|
* 支持多个分支方案的轮询选择
|
||||||
|
*
|
||||||
|
* @param General_Goal - 总体目标
|
||||||
|
* @param stepTask - 待填充的任务(包含基本字段)
|
||||||
|
* @returns Promise<IRawStepTask> - 返回填充了 AgentSelection 和 TaskProcess 的完整任务
|
||||||
|
*/
|
||||||
|
export const mockFillStepTaskAPI = async (params: {
|
||||||
|
General_Goal: string
|
||||||
|
stepTask: any
|
||||||
|
}): Promise<IRawStepTask> => {
|
||||||
|
// 模拟网络延迟 300ms
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 300))
|
||||||
|
|
||||||
|
console.log('[Mock API] fill_stepTask 调用参数:', params)
|
||||||
|
|
||||||
|
const stepName = params.stepTask.StepName
|
||||||
|
|
||||||
|
// 🆕 使用轮询方式选择填充方案(依次循环使用所有分支方案)
|
||||||
|
const branchIndexKey = `fill-step-task-index-${params.stepTask?.Id || stepName || 'default'}`
|
||||||
|
let lastIndex = parseInt(sessionStorage.getItem(branchIndexKey) || '0')
|
||||||
|
const totalBranches = 4 // 总共4个分支方案
|
||||||
|
const selectedBranchIndex = (lastIndex + 1) % totalBranches
|
||||||
|
sessionStorage.setItem(branchIndexKey, selectedBranchIndex.toString())
|
||||||
|
|
||||||
|
console.log('[Mock API] fill_stepTask 选择分支方案:', selectedBranchIndex + 1, '/', totalBranches)
|
||||||
|
|
||||||
|
// ==================== 第一个任务组 ====================
|
||||||
|
if (stepName === '分析用户需求' || stepName === '需求快速原型' || stepName === '需求深度分析' || stepName === '迭代规划') {
|
||||||
|
let filledTask: IRawStepTask
|
||||||
|
|
||||||
|
if (selectedBranchIndex === 0) {
|
||||||
|
// 第一个分支方案:分析用户需求
|
||||||
|
filledTask = {
|
||||||
|
Id: params.stepTask.Id || 'task-1',
|
||||||
|
StepName: '分析用户需求',
|
||||||
|
TaskContent: '分析用户需求,制定项目开发计划',
|
||||||
|
InputObject_List: ['用户需求文档', '技术规范'],
|
||||||
|
OutputObject: '项目开发计划书',
|
||||||
|
AgentSelection: ['腐蚀机理研究员', '实验材料学家'],
|
||||||
|
Collaboration_Brief_frontEnd: {
|
||||||
|
template: '!腐蚀机理研究员!负责!腐蚀类型识别-0!,!实验材料学家!负责!腐蚀类型识别-1!',
|
||||||
|
data: {
|
||||||
|
腐蚀机理研究员: {
|
||||||
|
text: '腐蚀机理研究员',
|
||||||
|
style: { background: 'hsl(210, 70%, 50%)' },
|
||||||
|
},
|
||||||
|
实验材料学家: {
|
||||||
|
text: '实验材料学家',
|
||||||
|
style: { background: 'hsl(120, 70%, 50%)' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TaskProcess: [
|
||||||
|
{
|
||||||
|
ID: 'action-1-1',
|
||||||
|
ActionType: 'Propose',
|
||||||
|
AgentName: '腐蚀机理研究员',
|
||||||
|
Description: '分析腐蚀环境和介质特征,识别潜在腐蚀类型',
|
||||||
|
ImportantInput: ['InputObject:腐蚀类型及成因列表'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-1-2',
|
||||||
|
ActionType: 'Propose',
|
||||||
|
AgentName: '实验材料学家',
|
||||||
|
Description: '设计腐蚀实验方案,确定测试参数',
|
||||||
|
ImportantInput: ['ActionResult:action-1-1'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-1-3',
|
||||||
|
ActionType: 'Critique',
|
||||||
|
AgentName: '腐蚀机理研究员',
|
||||||
|
Description: '评估实验方案的可行性',
|
||||||
|
ImportantInput: [
|
||||||
|
'ActionResult:action-1-2',
|
||||||
|
'ActionResult:action-1-1',
|
||||||
|
'InputObject:腐蚀类型及成因列表',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-1-4',
|
||||||
|
ActionType: 'Improve',
|
||||||
|
AgentName: '实验材料学家',
|
||||||
|
Description: '优化实验设计,完善测试流程',
|
||||||
|
ImportantInput: ['ActionResult:action-1-3'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-1-5',
|
||||||
|
ActionType: 'Finalize',
|
||||||
|
AgentName: '腐蚀机理研究员',
|
||||||
|
Description: '确认最终的腐蚀类型识别方案',
|
||||||
|
ImportantInput: ['ActionResult:action-1-4', 'ActionResult:action-1-3'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
} else if (selectedBranchIndex === 1) {
|
||||||
|
// 第二个分支方案:需求快速原型
|
||||||
|
filledTask = {
|
||||||
|
Id: params.stepTask.Id || 'task-1',
|
||||||
|
StepName: '需求快速原型',
|
||||||
|
TaskContent: '构建快速原型验证核心功能',
|
||||||
|
InputObject_List: ['腐蚀类型及成因列表'],
|
||||||
|
OutputObject: '原型系统',
|
||||||
|
AgentSelection: ['实验材料学家', '防护工程专家'],
|
||||||
|
Collaboration_Brief_frontEnd: {
|
||||||
|
template: '!实验材料学家!负责!需求快速原型-0!,!防护工程专家!负责!需求快速原型-1!',
|
||||||
|
data: {
|
||||||
|
实验材料学家: {
|
||||||
|
text: '实验材料学家',
|
||||||
|
style: { background: 'hsl(120, 70%, 50%)' },
|
||||||
|
},
|
||||||
|
防护工程专家: {
|
||||||
|
text: '防护工程专家',
|
||||||
|
style: { background: 'hsl(30, 70%, 50%)' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TaskProcess: [
|
||||||
|
{
|
||||||
|
ID: 'action-1-1-branch2',
|
||||||
|
ActionType: 'Propose',
|
||||||
|
AgentName: '实验材料学家',
|
||||||
|
Description: '基于腐蚀类型列表,设计快速原型验证方案',
|
||||||
|
ImportantInput: ['InputObject:腐蚀类型及成因列表'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-1-2-branch2',
|
||||||
|
ActionType: 'Propose',
|
||||||
|
AgentName: '防护工程专家',
|
||||||
|
Description: '实现原型核心功能和用户界面',
|
||||||
|
ImportantInput: ['ActionResult:action-1-1-branch2'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-1-3-branch2',
|
||||||
|
ActionType: 'Critique',
|
||||||
|
AgentName: '实验材料学家',
|
||||||
|
Description: '评估原型功能完整性和用户体验',
|
||||||
|
ImportantInput: ['ActionResult:action-1-2-branch2'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-1-4-branch2',
|
||||||
|
ActionType: 'Improve',
|
||||||
|
AgentName: '防护工程专家',
|
||||||
|
Description: '根据反馈优化原型功能和界面',
|
||||||
|
ImportantInput: ['ActionResult:action-1-3-branch2'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-1-5-branch2',
|
||||||
|
ActionType: 'Finalize',
|
||||||
|
AgentName: '实验材料学家',
|
||||||
|
Description: '确认最终版本的原型系统',
|
||||||
|
ImportantInput: ['ActionResult:action-1-4-branch2'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
} else if (selectedBranchIndex === 2) {
|
||||||
|
// 第三个分支方案:需求深度分析
|
||||||
|
filledTask = {
|
||||||
|
Id: params.stepTask.Id || 'task-1',
|
||||||
|
StepName: '需求深度分析',
|
||||||
|
TaskContent: '深入分析用户需求和技术可行性',
|
||||||
|
InputObject_List: ['腐蚀类型及成因列表'],
|
||||||
|
OutputObject: '详细需求分析报告',
|
||||||
|
AgentSelection: ['腐蚀机理研究员', '防护工程专家'],
|
||||||
|
Collaboration_Brief_frontEnd: {
|
||||||
|
template: '!腐蚀机理研究员!负责!需求深度分析-0!,!防护工程专家!负责!需求深度分析-1!',
|
||||||
|
data: {
|
||||||
|
腐蚀机理研究员: {
|
||||||
|
text: '腐蚀机理研究员',
|
||||||
|
style: { background: 'hsl(210, 70%, 50%)' },
|
||||||
|
},
|
||||||
|
防护工程专家: {
|
||||||
|
text: '防护工程专家',
|
||||||
|
style: { background: 'hsl(30, 70%, 50%)' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TaskProcess: [
|
||||||
|
{
|
||||||
|
ID: 'action-1-1-branch3',
|
||||||
|
ActionType: 'Propose',
|
||||||
|
AgentName: '腐蚀机理研究员',
|
||||||
|
Description: '全面分析腐蚀环境和技术约束条件',
|
||||||
|
ImportantInput: ['InputObject:腐蚀类型及成因列表'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-1-2-branch3',
|
||||||
|
ActionType: 'Propose',
|
||||||
|
AgentName: '防护工程专家',
|
||||||
|
Description: '评估技术可行性和防护方案',
|
||||||
|
ImportantInput: ['ActionResult:action-1-1-branch3'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-1-3-branch3',
|
||||||
|
ActionType: 'Critique',
|
||||||
|
AgentName: '腐蚀机理研究员',
|
||||||
|
Description: '评审需求分析的完整性和准确性',
|
||||||
|
ImportantInput: ['ActionResult:action-1-2-branch3', 'ActionResult:action-1-1-branch3'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-1-4-branch3',
|
||||||
|
ActionType: 'Improve',
|
||||||
|
AgentName: '防护工程专家',
|
||||||
|
Description: '补充技术风险评估和应对策略',
|
||||||
|
ImportantInput: ['ActionResult:action-1-3-branch3'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-1-5-branch3',
|
||||||
|
ActionType: 'Finalize',
|
||||||
|
AgentName: '腐蚀机理研究员',
|
||||||
|
Description: '确认详细需求分析报告',
|
||||||
|
ImportantInput: ['ActionResult:action-1-4-branch3', 'ActionResult:action-1-3-branch3'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 第四个分支方案:迭代规划
|
||||||
|
filledTask = {
|
||||||
|
Id: params.stepTask.Id || 'task-1',
|
||||||
|
StepName: '迭代规划',
|
||||||
|
TaskContent: '制定敏捷开发迭代计划',
|
||||||
|
InputObject_List: ['腐蚀类型及成因列表'],
|
||||||
|
OutputObject: '迭代计划',
|
||||||
|
AgentSelection: ['防护工程专家', '腐蚀机理研究员'],
|
||||||
|
Collaboration_Brief_frontEnd: {
|
||||||
|
template: '!防护工程专家!负责!迭代规划-0!,!腐蚀机理研究员!负责!迭代规划-1!',
|
||||||
|
data: {
|
||||||
|
防护工程专家: {
|
||||||
|
text: '防护工程专家',
|
||||||
|
style: { background: 'hsl(30, 70%, 50%)' },
|
||||||
|
},
|
||||||
|
腐蚀机理研究员: {
|
||||||
|
text: '腐蚀机理研究员',
|
||||||
|
style: { background: 'hsl(210, 70%, 50%)' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TaskProcess: [
|
||||||
|
{
|
||||||
|
ID: 'action-1-1-branch4',
|
||||||
|
ActionType: 'Propose',
|
||||||
|
AgentName: '防护工程专家',
|
||||||
|
Description: '制定敏捷开发总体框架和迭代周期',
|
||||||
|
ImportantInput: ['InputObject:腐蚀类型及成因列表'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-1-2-branch4',
|
||||||
|
ActionType: 'Propose',
|
||||||
|
AgentName: '腐蚀机理研究员',
|
||||||
|
Description: '规划各迭代阶段的技术验证重点',
|
||||||
|
ImportantInput: ['ActionResult:action-1-1-branch4'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-1-3-branch4',
|
||||||
|
ActionType: 'Critique',
|
||||||
|
AgentName: '防护工程专家',
|
||||||
|
Description: '评审迭代计划的合理性和可执行性',
|
||||||
|
ImportantInput: ['ActionResult:action-1-2-branch4'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-1-4-branch4',
|
||||||
|
ActionType: 'Improve',
|
||||||
|
AgentName: '腐蚀机理研究员',
|
||||||
|
Description: '优化迭代节奏和里程碑设置',
|
||||||
|
ImportantInput: ['ActionResult:action-1-3-branch4'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-1-5-branch4',
|
||||||
|
ActionType: 'Finalize',
|
||||||
|
AgentName: '防护工程专家',
|
||||||
|
Description: '确认最终迭代计划',
|
||||||
|
ImportantInput: ['ActionResult:action-1-4-branch4', 'ActionResult:action-1-3-branch4'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[Mock API] fill_stepTask 返回数据:', filledTask)
|
||||||
|
return filledTask
|
||||||
|
}
|
||||||
|
// ==================== 第二个任务组 ====================
|
||||||
|
else if (stepName === '系统设计与架构' || stepName === '原型测试与优化' || stepName === '质量保障设计' || stepName === '快速开发与验证') {
|
||||||
|
let filledTask: IRawStepTask
|
||||||
|
|
||||||
|
if (selectedBranchIndex === 0) {
|
||||||
|
// 第一个分支方案:系统设计与架构
|
||||||
|
filledTask = {
|
||||||
|
Id: params.stepTask.Id || 'task-2',
|
||||||
|
StepName: '系统设计与架构',
|
||||||
|
TaskContent: '设计系统架构和数据库结构',
|
||||||
|
InputObject_List: ['项目开发计划书'],
|
||||||
|
OutputObject: '系统设计文档',
|
||||||
|
AgentSelection: ['腐蚀机理研究员', '防护工程专家'],
|
||||||
|
Collaboration_Brief_frontEnd: {
|
||||||
|
template: '!腐蚀机理研究员!负责!系统设计与架构-0!,!防护工程专家!负责!系统设计与架构-1!',
|
||||||
|
data: {
|
||||||
|
腐蚀机理研究员: {
|
||||||
|
text: '腐蚀机理研究员',
|
||||||
|
style: { background: 'hsl(210, 70%, 50%)' },
|
||||||
|
},
|
||||||
|
防护工程专家: {
|
||||||
|
text: '防护工程专家',
|
||||||
|
style: { background: 'hsl(30, 70%, 50%)' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TaskProcess: [
|
||||||
|
{
|
||||||
|
ID: 'action-2-1',
|
||||||
|
ActionType: 'Propose',
|
||||||
|
AgentName: '腐蚀机理研究员',
|
||||||
|
Description: '分析系统功能需求,提出整体架构方案',
|
||||||
|
ImportantInput: ['InputObject:项目开发计划书'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-2-2',
|
||||||
|
ActionType: 'Propose',
|
||||||
|
AgentName: '防护工程专家',
|
||||||
|
Description: '设计防护系统架构和数据库模型',
|
||||||
|
ImportantInput: ['ActionResult:action-2-1'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-2-3',
|
||||||
|
ActionType: 'Critique',
|
||||||
|
AgentName: '腐蚀机理研究员',
|
||||||
|
Description: '评审架构设计的合理性和可扩展性',
|
||||||
|
ImportantInput: ['ActionResult:action-2-2', 'InputObject:项目开发计划书'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-2-4',
|
||||||
|
ActionType: 'Improve',
|
||||||
|
AgentName: '防护工程专家',
|
||||||
|
Description: '优化架构设计,补充性能和安全方案',
|
||||||
|
ImportantInput: ['ActionResult:action-2-3', 'ActionResult:action-2-2'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-2-5',
|
||||||
|
ActionType: 'Finalize',
|
||||||
|
AgentName: '腐蚀机理研究员',
|
||||||
|
Description: '确认最终系统架构设计文档',
|
||||||
|
ImportantInput: ['ActionResult:action-2-4', 'ActionResult:action-2-3'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
} else if (selectedBranchIndex === 1) {
|
||||||
|
// 第二个分支方案:原型测试与优化
|
||||||
|
filledTask = {
|
||||||
|
Id: params.stepTask.Id || 'task-2',
|
||||||
|
StepName: '原型测试与优化',
|
||||||
|
TaskContent: '测试原型并根据反馈快速迭代',
|
||||||
|
InputObject_List: ['原型系统'],
|
||||||
|
OutputObject: '优化后的原型',
|
||||||
|
AgentSelection: ['腐蚀机理研究员', '实验材料学家'],
|
||||||
|
Collaboration_Brief_frontEnd: {
|
||||||
|
template: '!腐蚀机理研究员!负责!原型测试与优化-0!,!实验材料学家!负责!原型测试与优化-1!',
|
||||||
|
data: {
|
||||||
|
腐蚀机理研究员: {
|
||||||
|
text: '腐蚀机理研究员',
|
||||||
|
style: { background: 'hsl(210, 70%, 50%)' },
|
||||||
|
},
|
||||||
|
实验材料学家: {
|
||||||
|
text: '实验材料学家',
|
||||||
|
style: { background: 'hsl(120, 70%, 50%)' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TaskProcess: [
|
||||||
|
{
|
||||||
|
ID: 'action-2-1-branch2',
|
||||||
|
ActionType: 'Propose',
|
||||||
|
AgentName: '腐蚀机理研究员',
|
||||||
|
Description: '制定原型测试计划和验证标准',
|
||||||
|
ImportantInput: ['InputObject:原型系统'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-2-2-branch2',
|
||||||
|
ActionType: 'Propose',
|
||||||
|
AgentName: '实验材料学家',
|
||||||
|
Description: '执行原型测试并收集性能数据',
|
||||||
|
ImportantInput: ['ActionResult:action-2-1-branch2'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-2-3-branch2',
|
||||||
|
ActionType: 'Critique',
|
||||||
|
AgentName: '腐蚀机理研究员',
|
||||||
|
Description: '分析测试结果和用户反馈',
|
||||||
|
ImportantInput: ['ActionResult:action-2-2-branch2'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-2-4-branch2',
|
||||||
|
ActionType: 'Improve',
|
||||||
|
AgentName: '实验材料学家',
|
||||||
|
Description: '根据测试结果优化原型功能和性能',
|
||||||
|
ImportantInput: ['ActionResult:action-2-3-branch2'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-2-5-branch2',
|
||||||
|
ActionType: 'Finalize',
|
||||||
|
AgentName: '腐蚀机理研究员',
|
||||||
|
Description: '确认优化后的原型版本',
|
||||||
|
ImportantInput: ['ActionResult:action-2-4-branch2', 'ActionResult:action-2-3-branch2'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
} else if (selectedBranchIndex === 2) {
|
||||||
|
// 第三个分支方案:质量保障设计
|
||||||
|
filledTask = {
|
||||||
|
Id: params.stepTask.Id || 'task-2',
|
||||||
|
StepName: '质量保障设计',
|
||||||
|
TaskContent: '设计质量保障体系和测试方案',
|
||||||
|
InputObject_List: ['详细需求分析报告'],
|
||||||
|
OutputObject: '质量保障方案',
|
||||||
|
AgentSelection: ['实验材料学家', '防护工程专家'],
|
||||||
|
Collaboration_Brief_frontEnd: {
|
||||||
|
template: '!实验材料学家!负责!质量保障设计-0!,!防护工程专家!负责!质量保障设计-1!',
|
||||||
|
data: {
|
||||||
|
实验材料学家: {
|
||||||
|
text: '实验材料学家',
|
||||||
|
style: { background: 'hsl(120, 70%, 50%)' },
|
||||||
|
},
|
||||||
|
防护工程专家: {
|
||||||
|
text: '防护工程专家',
|
||||||
|
style: { background: 'hsl(30, 70%, 50%)' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TaskProcess: [
|
||||||
|
{
|
||||||
|
ID: 'action-2-1-branch3',
|
||||||
|
ActionType: 'Propose',
|
||||||
|
AgentName: '实验材料学家',
|
||||||
|
Description: '制定质量标准和测试指标体系',
|
||||||
|
ImportantInput: ['InputObject:详细需求分析报告'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-2-2-branch3',
|
||||||
|
ActionType: 'Propose',
|
||||||
|
AgentName: '防护工程专家',
|
||||||
|
Description: '设计质量保障流程和验证方案',
|
||||||
|
ImportantInput: ['ActionResult:action-2-1-branch3'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-2-3-branch3',
|
||||||
|
ActionType: 'Critique',
|
||||||
|
AgentName: '实验材料学家',
|
||||||
|
Description: '评审质量保障方案的完整性',
|
||||||
|
ImportantInput: ['ActionResult:action-2-2-branch3'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-2-4-branch3',
|
||||||
|
ActionType: 'Improve',
|
||||||
|
AgentName: '防护工程专家',
|
||||||
|
Description: '完善质量保障方案,补充风险控制',
|
||||||
|
ImportantInput: ['ActionResult:action-2-3-branch3'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-2-5-branch3',
|
||||||
|
ActionType: 'Finalize',
|
||||||
|
AgentName: '实验材料学家',
|
||||||
|
Description: '确认最终质量保障方案',
|
||||||
|
ImportantInput: ['ActionResult:action-2-4-branch3', 'ActionResult:action-2-3-branch3'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 第四个分支方案:快速开发与验证
|
||||||
|
filledTask = {
|
||||||
|
Id: params.stepTask.Id || 'task-2',
|
||||||
|
StepName: '快速开发与验证',
|
||||||
|
TaskContent: '快速开发并持续验证功能',
|
||||||
|
InputObject_List: ['迭代计划'],
|
||||||
|
OutputObject: '可运行的功能模块',
|
||||||
|
AgentSelection: ['实验材料学家', '防护工程专家'],
|
||||||
|
Collaboration_Brief_frontEnd: {
|
||||||
|
template: '!实验材料学家!负责!快速开发与验证-0!,!防护工程专家!负责!快速开发与验证-1!',
|
||||||
|
data: {
|
||||||
|
实验材料学家: {
|
||||||
|
text: '实验材料学家',
|
||||||
|
style: { background: 'hsl(120, 70%, 50%)' },
|
||||||
|
},
|
||||||
|
防护工程专家: {
|
||||||
|
text: '防护工程专家',
|
||||||
|
style: { background: 'hsl(30, 70%, 50%)' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TaskProcess: [
|
||||||
|
{
|
||||||
|
ID: 'action-2-1-branch4',
|
||||||
|
ActionType: 'Propose',
|
||||||
|
AgentName: '实验材料学家',
|
||||||
|
Description: '根据迭代计划快速开发功能模块',
|
||||||
|
ImportantInput: ['InputObject:迭代计划'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-2-2-branch4',
|
||||||
|
ActionType: 'Propose',
|
||||||
|
AgentName: '防护工程专家',
|
||||||
|
Description: '实现功能防护机制和验证逻辑',
|
||||||
|
ImportantInput: ['ActionResult:action-2-1-branch4'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-2-3-branch4',
|
||||||
|
ActionType: 'Critique',
|
||||||
|
AgentName: '实验材料学家',
|
||||||
|
Description: '验证功能模块的正确性和性能',
|
||||||
|
ImportantInput: ['ActionResult:action-2-2-branch4'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-2-4-branch4',
|
||||||
|
ActionType: 'Improve',
|
||||||
|
AgentName: '防护工程专家',
|
||||||
|
Description: '根据验证结果优化功能实现',
|
||||||
|
ImportantInput: ['ActionResult:action-2-3-branch4'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-2-5-branch4',
|
||||||
|
ActionType: 'Finalize',
|
||||||
|
AgentName: '实验材料学家',
|
||||||
|
Description: '确认可运行的功能模块',
|
||||||
|
ImportantInput: ['ActionResult:action-2-4-branch4', 'ActionResult:action-2-3-branch4'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[Mock API] fill_stepTask 返回数据:', filledTask)
|
||||||
|
return filledTask
|
||||||
|
}
|
||||||
|
// ==================== 第三个任务组(仅第三个分支方案) ====================
|
||||||
|
else if (stepName === '系统开发与测试') {
|
||||||
|
const filledTask: IRawStepTask = {
|
||||||
|
Id: params.stepTask.Id || 'task-3',
|
||||||
|
StepName: '系统开发与测试',
|
||||||
|
TaskContent: '按质量标准进行系统开发和测试',
|
||||||
|
InputObject_List: ['质量保障方案'],
|
||||||
|
OutputObject: '经过完整测试的系统',
|
||||||
|
AgentSelection: ['腐蚀机理研究员', '实验材料学家'],
|
||||||
|
Collaboration_Brief_frontEnd: {
|
||||||
|
template: '!腐蚀机理研究员!负责!系统开发与测试-0!,!实验材料学家!负责!系统开发与测试-1!',
|
||||||
|
data: {
|
||||||
|
腐蚀机理研究员: {
|
||||||
|
text: '腐蚀机理研究员',
|
||||||
|
style: { background: 'hsl(210, 70%, 50%)' },
|
||||||
|
},
|
||||||
|
实验材料学家: {
|
||||||
|
text: '实验材料学家',
|
||||||
|
style: { background: 'hsl(120, 70%, 50%)' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TaskProcess: [
|
||||||
|
{
|
||||||
|
ID: 'action-3-1',
|
||||||
|
ActionType: 'Propose',
|
||||||
|
AgentName: '腐蚀机理研究员',
|
||||||
|
Description: '根据质量标准制定系统开发计划',
|
||||||
|
ImportantInput: ['InputObject:质量保障方案'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-3-2',
|
||||||
|
ActionType: 'Propose',
|
||||||
|
AgentName: '实验材料学家',
|
||||||
|
Description: '按标准实施系统开发和单元测试',
|
||||||
|
ImportantInput: ['ActionResult:action-3-1'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-3-3',
|
||||||
|
ActionType: 'Critique',
|
||||||
|
AgentName: '腐蚀机理研究员',
|
||||||
|
Description: '评审系统开发质量和测试覆盖率',
|
||||||
|
ImportantInput: ['ActionResult:action-3-2'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-3-4',
|
||||||
|
ActionType: 'Improve',
|
||||||
|
AgentName: '实验材料学家',
|
||||||
|
Description: '根据评审结果完善系统功能和测试',
|
||||||
|
ImportantInput: ['ActionResult:action-3-3'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'action-3-5',
|
||||||
|
ActionType: 'Finalize',
|
||||||
|
AgentName: '腐蚀机理研究员',
|
||||||
|
Description: '确认经过完整测试的系统版本',
|
||||||
|
ImportantInput: ['ActionResult:action-3-4', 'ActionResult:action-3-3'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[Mock API] fill_stepTask 返回数据:', filledTask)
|
||||||
|
return filledTask
|
||||||
|
}
|
||||||
|
// ==================== 其他任务 ====================
|
||||||
|
else {
|
||||||
|
// 其他任务,返回空 TaskProcess
|
||||||
|
console.warn('[Mock API] 未知的 StepName:', stepName)
|
||||||
|
return {
|
||||||
|
...params.stepTask,
|
||||||
|
AgentSelection: [],
|
||||||
|
TaskProcess: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default mockFillStepTaskAPI
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import SvgIcon from '@/components/SvgIcon/index.vue'
|
||||||
|
import { useAgentsStore } from '@/stores'
|
||||||
|
|
||||||
|
const agentsStore = useAgentsStore()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'click'): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
// 判断是否禁用 - 必须先点击任务大纲中的卡片
|
||||||
|
const isDisabled = computed(() => {
|
||||||
|
return !agentsStore.currentTask
|
||||||
|
})
|
||||||
|
|
||||||
|
// 获取agent组合卡片数量
|
||||||
|
const agentGroupCount = computed(() => {
|
||||||
|
if (!agentsStore.currentTask?.Id) return 1
|
||||||
|
|
||||||
|
// 获取该任务的已确认agent组合
|
||||||
|
const confirmedGroups = agentsStore.getConfirmedAgentGroups(agentsStore.currentTask.Id)
|
||||||
|
|
||||||
|
// 当前任务agents(1) + 已确认的agent组合数量
|
||||||
|
return confirmedGroups.length || 1
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
if (isDisabled.value) return
|
||||||
|
emit('click')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="assignment-button"
|
||||||
|
:class="{ 'is-disabled': isDisabled, 'has-groups': agentGroupCount > 1 }"
|
||||||
|
@click="handleClick"
|
||||||
|
:title="isDisabled ? '请先点击任务大纲中的任务卡片' : `${agentGroupCount} 个agent组合`"
|
||||||
|
>
|
||||||
|
<!-- 智能体分配图标 -->
|
||||||
|
<SvgIcon icon-class="agent-change" size="24px" color="#fff" />
|
||||||
|
|
||||||
|
<!-- agent组合数量显示 -->
|
||||||
|
<span class="agent-group-count">
|
||||||
|
{{ agentGroupCount }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.assignment-button {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 40px;
|
||||||
|
height: 36px;
|
||||||
|
background-color: #43a8aa;
|
||||||
|
border-radius: 10px 0 0 0;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
z-index: 100;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
filter: brightness(0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
filter: brightness(0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 多个agent组合时显示红点指示器
|
||||||
|
&.has-groups::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -2px;
|
||||||
|
right: -2px;
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
background: #ff6b6b;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2px solid white;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 禁用状态
|
||||||
|
&.is-disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
filter: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
filter: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-group-count {
|
||||||
|
position: absolute;
|
||||||
|
right: 1px;
|
||||||
|
bottom: 2px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: white;
|
||||||
|
font-weight: 800;
|
||||||
|
text-align: right;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { useAgentsStore, useSelectionStore } from '@/stores'
|
||||||
|
|
||||||
|
const agentsStore = useAgentsStore()
|
||||||
|
const selectionStore = useSelectionStore()
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'click'): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
// 获取分支数量
|
||||||
|
const branchCount = computed(() => {
|
||||||
|
// flowBranches 包含所有通过 Branch 创建的分支
|
||||||
|
const extraBranches = selectionStore.flowBranches?.length || 1
|
||||||
|
|
||||||
|
return extraBranches
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
emit('click')
|
||||||
|
// 触发打开分支窗口
|
||||||
|
agentsStore.openPlanModification()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="branch-button"
|
||||||
|
:class="{ 'has-branches': branchCount > 0 }"
|
||||||
|
@click="handleClick"
|
||||||
|
:title="`${branchCount} 个分支`"
|
||||||
|
>
|
||||||
|
<!-- 分支图标 -->
|
||||||
|
<svg-icon icon-class="branch" size="24px" class="branch-icon" />
|
||||||
|
|
||||||
|
<!-- 分支数量显示 -->
|
||||||
|
<span class="branch-count">
|
||||||
|
{{ branchCount }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.branch-button {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
|
||||||
|
/* 尺寸 */
|
||||||
|
width: 36px;
|
||||||
|
height: 32px;
|
||||||
|
|
||||||
|
/* 样式 */
|
||||||
|
background-color: #43a8aa;
|
||||||
|
border-radius: 10px 0 0 0;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
z-index: 100;
|
||||||
|
|
||||||
|
/* 布局 */
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
/* 交互 */
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
filter: brightness(0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.has-branches::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -2px;
|
||||||
|
right: -2px;
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
background: #ff6b6b;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2px solid white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-icon {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-count {
|
||||||
|
position: absolute;
|
||||||
|
right: 4px;
|
||||||
|
bottom: 2px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: white;
|
||||||
|
font-weight: 800;
|
||||||
|
text-align: right;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,446 @@
|
|||||||
|
<template>
|
||||||
|
<div ref="rndContainer" class="rnd-container" :style="containerStyle">
|
||||||
|
<!-- 标题栏 -->
|
||||||
|
<div class="float-window-header" @mousedown="handleMouseDown">
|
||||||
|
<div v-if="typeof title === 'string'" class="header-title">
|
||||||
|
{{ title }}
|
||||||
|
</div>
|
||||||
|
<div v-else class="header-title-custom">
|
||||||
|
<slot name="title">
|
||||||
|
{{ title }}
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
<div class="header-actions">
|
||||||
|
<button v-if="onClose" class="close-btn" @click="handleClose">
|
||||||
|
<svg-icon icon-class="close" size="20px" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 内容区域 -->
|
||||||
|
<div
|
||||||
|
ref="contentContainer"
|
||||||
|
class="float-window-content"
|
||||||
|
@pointerenter="setResizeable(false)"
|
||||||
|
@pointerleave="setResizeable(true)"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 调整大小的手柄 -->
|
||||||
|
<div
|
||||||
|
v-for="handle in resizeHandles"
|
||||||
|
:key="handle"
|
||||||
|
:class="`resize-handle resize-handle-${handle}`"
|
||||||
|
@mousedown="e => startResize(e, handle)"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue'
|
||||||
|
import { debounce } from 'lodash-es'
|
||||||
|
export interface IFloatingWindowProps {
|
||||||
|
title?: string | any
|
||||||
|
onClose?: () => void
|
||||||
|
onResize?: () => void
|
||||||
|
minWidth?: number
|
||||||
|
minHeight?: number
|
||||||
|
defaultSize?: {
|
||||||
|
x: number
|
||||||
|
y: number
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<IFloatingWindowProps>(), {
|
||||||
|
title: '',
|
||||||
|
minWidth: 150,
|
||||||
|
minHeight: 60
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
'update:position': [value: { x: number; y: number }]
|
||||||
|
'update:size': [value: { width: number; height: number }]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
// 响应式状态
|
||||||
|
const position = ref({ x: 0, y: 0 })
|
||||||
|
const size = ref({ width: 400, height: 300 })
|
||||||
|
const isDragging = ref(false)
|
||||||
|
const isResizing = ref(false)
|
||||||
|
const resizeDirection = ref<string | null>(null)
|
||||||
|
const resizeable = ref(true)
|
||||||
|
const resizeStart = ref({ x: 0, y: 0, width: 0, height: 0, right: 0, bottom: 0 })
|
||||||
|
const dragStart = ref({ x: 0, y: 0 })
|
||||||
|
const containerRef = ref<HTMLElement>()
|
||||||
|
|
||||||
|
// 窗口管理
|
||||||
|
let windowsArrange: HTMLElement[] = []
|
||||||
|
|
||||||
|
const focusWindow = (element: HTMLElement) => {
|
||||||
|
if (!element) return
|
||||||
|
|
||||||
|
// 过滤掉已经不存在的元素
|
||||||
|
windowsArrange = windowsArrange.filter(ele => ele.isConnected && element !== ele)
|
||||||
|
|
||||||
|
// 将当前窗口移到最前面
|
||||||
|
windowsArrange.push(element)
|
||||||
|
|
||||||
|
// 更新所有窗口的z-index
|
||||||
|
windowsArrange.forEach((ele, index) => {
|
||||||
|
ele.style.zIndex = `${index + 3000}`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算默认尺寸
|
||||||
|
const defaultSize = computed(() => {
|
||||||
|
if (props.defaultSize) return props.defaultSize
|
||||||
|
|
||||||
|
const width = Math.min(1280, window.innerWidth - 20)
|
||||||
|
const height = Math.min(600, window.innerHeight - 20)
|
||||||
|
const x = (window.innerWidth - width) / 2
|
||||||
|
const y = (window.innerHeight - height) / 2
|
||||||
|
return { x, y, width, height }
|
||||||
|
})
|
||||||
|
|
||||||
|
// 容器样式
|
||||||
|
const containerStyle = computed(() => ({
|
||||||
|
position: 'fixed' as const,
|
||||||
|
left: `${position.value.x}px`,
|
||||||
|
top: `${position.value.y}px`,
|
||||||
|
width: `${size.value.width}px`,
|
||||||
|
height: `${size.value.height}px`,
|
||||||
|
border: '3px solid #43A8AA',
|
||||||
|
boxShadow: '3px 3px 20px rgba(0, 0, 0, 0.3)',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column' as const,
|
||||||
|
zIndex: '3000',
|
||||||
|
backgroundColor: 'var(--color-bg-three)',
|
||||||
|
overflow: 'hidden',
|
||||||
|
userSelect: (isDragging.value || isResizing.value ? 'none' : 'auto') as any
|
||||||
|
}))
|
||||||
|
|
||||||
|
// 调整大小的手柄位置
|
||||||
|
const resizeHandles = ['n', 'e', 's', 'w', 'ne', 'nw', 'se', 'sw']
|
||||||
|
|
||||||
|
// 事件处理
|
||||||
|
const handleMouseDown = (e: MouseEvent) => {
|
||||||
|
if (!resizeable.value || isResizing.value) return
|
||||||
|
|
||||||
|
isDragging.value = true
|
||||||
|
dragStart.value = {
|
||||||
|
x: e.clientX - position.value.x,
|
||||||
|
y: e.clientY - position.value.y
|
||||||
|
}
|
||||||
|
|
||||||
|
if (containerRef.value) {
|
||||||
|
focusWindow(containerRef.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('mousemove', handleDragMove)
|
||||||
|
document.addEventListener('mouseup', handleDragEnd)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDragMove = (e: MouseEvent) => {
|
||||||
|
if (!isDragging.value) return
|
||||||
|
|
||||||
|
let newX = e.clientX - dragStart.value.x
|
||||||
|
let newY = e.clientY - dragStart.value.y
|
||||||
|
|
||||||
|
// 边界检查
|
||||||
|
newX = Math.max(0, Math.min(newX, window.innerWidth - size.value.width))
|
||||||
|
newY = Math.max(0, Math.min(newY, window.innerHeight - size.value.height))
|
||||||
|
|
||||||
|
position.value = { x: newX, y: newY }
|
||||||
|
emit('update:position', position.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDragEnd = () => {
|
||||||
|
isDragging.value = false
|
||||||
|
document.removeEventListener('mousemove', handleDragMove)
|
||||||
|
document.removeEventListener('mouseup', handleDragEnd)
|
||||||
|
}
|
||||||
|
|
||||||
|
const startResize = (e: MouseEvent, direction: string) => {
|
||||||
|
if (!resizeable.value) return
|
||||||
|
|
||||||
|
e.stopPropagation()
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
isResizing.value = true
|
||||||
|
resizeDirection.value = direction
|
||||||
|
resizeStart.value = {
|
||||||
|
x: e.clientX,
|
||||||
|
y: e.clientY,
|
||||||
|
width: size.value.width,
|
||||||
|
height: size.value.height,
|
||||||
|
right: position.value.x + size.value.width,
|
||||||
|
bottom: position.value.y + size.value.height
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('mousemove', handleResizeMove)
|
||||||
|
document.addEventListener('mouseup', handleResizeEnd)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleResizeMove = (e: MouseEvent) => {
|
||||||
|
if (!isResizing.value || !resizeDirection.value) return
|
||||||
|
|
||||||
|
const deltaX = e.clientX - resizeStart.value.x
|
||||||
|
const deltaY = e.clientY - resizeStart.value.y
|
||||||
|
|
||||||
|
let newWidth = resizeStart.value.width
|
||||||
|
let newHeight = resizeStart.value.height
|
||||||
|
let newX = position.value.x
|
||||||
|
let newY = position.value.y
|
||||||
|
|
||||||
|
// 根据调整方向计算新尺寸和位置
|
||||||
|
switch (resizeDirection.value) {
|
||||||
|
// 右边调整 - 固定左边
|
||||||
|
case 'e':
|
||||||
|
newWidth = Math.max(props.minWidth, resizeStart.value.width + deltaX)
|
||||||
|
break
|
||||||
|
|
||||||
|
// 下边调整 - 固定上边
|
||||||
|
case 's':
|
||||||
|
newHeight = Math.max(props.minHeight, resizeStart.value.height + deltaY)
|
||||||
|
break
|
||||||
|
|
||||||
|
// 左边调整 - 固定右边
|
||||||
|
case 'w':
|
||||||
|
newWidth = Math.max(props.minWidth, resizeStart.value.width - deltaX)
|
||||||
|
newX = resizeStart.value.right - newWidth
|
||||||
|
break
|
||||||
|
|
||||||
|
// 上边调整 - 固定下边
|
||||||
|
case 'n':
|
||||||
|
newHeight = Math.max(props.minHeight, resizeStart.value.height - deltaY)
|
||||||
|
newY = resizeStart.value.bottom - newHeight
|
||||||
|
break
|
||||||
|
|
||||||
|
// 右上角调整 - 固定左下角
|
||||||
|
case 'ne':
|
||||||
|
newWidth = Math.max(props.minWidth, resizeStart.value.width + deltaX)
|
||||||
|
newHeight = Math.max(props.minHeight, resizeStart.value.height - deltaY)
|
||||||
|
newY = resizeStart.value.bottom - newHeight
|
||||||
|
break
|
||||||
|
|
||||||
|
// 左上角调整 - 固定右下角
|
||||||
|
case 'nw':
|
||||||
|
newWidth = Math.max(props.minWidth, resizeStart.value.width - deltaX)
|
||||||
|
newHeight = Math.max(props.minHeight, resizeStart.value.height - deltaY)
|
||||||
|
newX = resizeStart.value.right - newWidth
|
||||||
|
newY = resizeStart.value.bottom - newHeight
|
||||||
|
break
|
||||||
|
|
||||||
|
// 左下角调整 - 固定右上角
|
||||||
|
case 'sw':
|
||||||
|
newWidth = Math.max(props.minWidth, resizeStart.value.width - deltaX)
|
||||||
|
newHeight = Math.max(props.minHeight, resizeStart.value.height + deltaY)
|
||||||
|
newX = resizeStart.value.right - newWidth
|
||||||
|
break
|
||||||
|
|
||||||
|
// 右下角调整 - 固定左上角
|
||||||
|
case 'se':
|
||||||
|
default:
|
||||||
|
newWidth = Math.max(props.minWidth, resizeStart.value.width + deltaX)
|
||||||
|
newHeight = Math.max(props.minHeight, resizeStart.value.height + deltaY)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// 边界检查
|
||||||
|
newX = Math.max(0, Math.min(newX, window.innerWidth - newWidth))
|
||||||
|
newY = Math.max(0, Math.min(newY, window.innerHeight - newHeight))
|
||||||
|
newWidth = Math.min(newWidth, window.innerWidth - newX)
|
||||||
|
newHeight = Math.min(newHeight, window.innerHeight - newY)
|
||||||
|
|
||||||
|
size.value = { width: newWidth, height: newHeight }
|
||||||
|
position.value = { x: newX, y: newY }
|
||||||
|
|
||||||
|
emit('update:size', size.value)
|
||||||
|
emit('update:position', position.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleResizeEnd = debounce(() => {
|
||||||
|
isResizing.value = false
|
||||||
|
resizeDirection.value = null
|
||||||
|
document.removeEventListener('mousemove', handleResizeMove)
|
||||||
|
document.removeEventListener('mouseup', handleResizeEnd)
|
||||||
|
|
||||||
|
props.onResize?.()
|
||||||
|
}, 50)
|
||||||
|
|
||||||
|
const setResizeable = (value: boolean) => {
|
||||||
|
resizeable.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
if (props.onClose) {
|
||||||
|
props.onClose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
onMounted(() => {
|
||||||
|
position.value = { x: defaultSize.value.x, y: defaultSize.value.y }
|
||||||
|
size.value = { width: defaultSize.value.width, height: defaultSize.value.height }
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
if (containerRef.value) {
|
||||||
|
focusWindow(containerRef.value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
// 清理事件监听
|
||||||
|
document.removeEventListener('mousemove', handleDragMove)
|
||||||
|
document.removeEventListener('mouseup', handleDragEnd)
|
||||||
|
document.removeEventListener('mousemove', handleResizeMove)
|
||||||
|
document.removeEventListener('mouseup', handleResizeEnd)
|
||||||
|
|
||||||
|
// 从窗口管理数组中移除
|
||||||
|
if (containerRef.value) {
|
||||||
|
windowsArrange = windowsArrange.filter(ele => ele !== containerRef.value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.rnd-container {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.float-window-header {
|
||||||
|
background-color: #1976d2;
|
||||||
|
color: white;
|
||||||
|
height: 36px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 5px;
|
||||||
|
user-select: none;
|
||||||
|
cursor: move;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 800;
|
||||||
|
flex-grow: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-title-custom {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-btn {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-btn:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-btn:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.float-window-content {
|
||||||
|
flex-grow: 1;
|
||||||
|
background: var(--color-card);
|
||||||
|
overflow: auto;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 调整大小的手柄样式 */
|
||||||
|
.resize-handle {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resize-handle-n,
|
||||||
|
.resize-handle-s {
|
||||||
|
width: 100%;
|
||||||
|
height: 6px;
|
||||||
|
cursor: ns-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resize-handle-e,
|
||||||
|
.resize-handle-w {
|
||||||
|
width: 6px;
|
||||||
|
height: 100%;
|
||||||
|
cursor: ew-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resize-handle-ne,
|
||||||
|
.resize-handle-nw,
|
||||||
|
.resize-handle-se,
|
||||||
|
.resize-handle-sw {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resize-handle-n {
|
||||||
|
top: -3px;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resize-handle-s {
|
||||||
|
bottom: -3px;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resize-handle-e {
|
||||||
|
top: 0;
|
||||||
|
right: -3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resize-handle-w {
|
||||||
|
top: 0;
|
||||||
|
left: -3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resize-handle-ne {
|
||||||
|
top: -6px;
|
||||||
|
right: -6px;
|
||||||
|
cursor: ne-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resize-handle-nw {
|
||||||
|
top: -6px;
|
||||||
|
left: -6px;
|
||||||
|
cursor: nw-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resize-handle-se {
|
||||||
|
bottom: -6px;
|
||||||
|
right: -6px;
|
||||||
|
cursor: se-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resize-handle-sw {
|
||||||
|
bottom: -6px;
|
||||||
|
left: -6px;
|
||||||
|
cursor: sw-resize;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
// Mock数据 - 用于agentSelectModifyAddAspect接口
|
||||||
|
// 模拟用户输入新维度后,所有agent在该维度上的评分数据
|
||||||
|
|
||||||
|
import { vueAgentList } from './AgentAssignmentMock'
|
||||||
|
|
||||||
|
// 类型定义
|
||||||
|
export interface NewDimensionScore {
|
||||||
|
score: number
|
||||||
|
reason: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NewDimensionScoreData = Record<string, NewDimensionScore>
|
||||||
|
|
||||||
|
// 模拟接口返回的数据结构
|
||||||
|
export interface AgentAddAspectResponse {
|
||||||
|
aspectName: string // 新添加的维度名称
|
||||||
|
agentScores: NewDimensionScoreData // 所有agent在该维度上的评分
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成指定维度名称的mock评分数据
|
||||||
|
export const generateMockDimensionScores = (dimensionName: string): AgentAddAspectResponse => {
|
||||||
|
const agentScores: NewDimensionScoreData = {}
|
||||||
|
|
||||||
|
vueAgentList.forEach((agent) => {
|
||||||
|
// 随机生成1-5的评分
|
||||||
|
const score = Math.floor(Math.random() * 5) + 1
|
||||||
|
|
||||||
|
// 根据评分生成不同的原因描述
|
||||||
|
let reason = ''
|
||||||
|
switch (score) {
|
||||||
|
case 5:
|
||||||
|
reason = `在"${dimensionName}"方面表现卓越,展现出杰出的能力和深刻的理解`
|
||||||
|
break
|
||||||
|
case 4:
|
||||||
|
reason = `在"${dimensionName}"方面表现优秀,具有良好的专业能力和执行力`
|
||||||
|
break
|
||||||
|
case 3:
|
||||||
|
reason = `在"${dimensionName}"方面表现合格,能够完成相关任务`
|
||||||
|
break
|
||||||
|
case 2:
|
||||||
|
reason = `在"${dimensionName}"方面表现一般,仍有提升空间`
|
||||||
|
break
|
||||||
|
case 1:
|
||||||
|
reason = `在"${dimensionName}"方面需要加强,建议进一步提升相关能力`
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
agentScores[agent] = { score, reason }
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
aspectName: dimensionName,
|
||||||
|
agentScores,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预设的一些常用维度及其评分数据
|
||||||
|
export const presetDimensionScores: Record<string, AgentAddAspectResponse> = {
|
||||||
|
创新性: generateMockDimensionScores('创新性'),
|
||||||
|
技术能力: generateMockDimensionScores('技术能力'),
|
||||||
|
沟通技巧: generateMockDimensionScores('沟通技巧'),
|
||||||
|
问题解决: generateMockDimensionScores('问题解决'),
|
||||||
|
团队协作: generateMockDimensionScores('团队协作'),
|
||||||
|
学习能力: generateMockDimensionScores('学习能力'),
|
||||||
|
执行力: generateMockDimensionScores('执行力'),
|
||||||
|
责任心: generateMockDimensionScores('责任心'),
|
||||||
|
适应性: generateMockDimensionScores('适应性'),
|
||||||
|
领导力: generateMockDimensionScores('领导力'),
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模拟API调用函数(用于前端测试)
|
||||||
|
export const mockAgentAddAspectApi = async (
|
||||||
|
aspectList: string[],
|
||||||
|
): Promise<AgentAddAspectResponse[]> => {
|
||||||
|
// 获取新增的维度(最后一个)
|
||||||
|
const newAspect = aspectList[aspectList.length - 1]
|
||||||
|
|
||||||
|
// 模拟网络延迟 500ms
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 20000))
|
||||||
|
|
||||||
|
// 如果是预设维度,返回预设数据
|
||||||
|
if (presetDimensionScores[newAspect]) {
|
||||||
|
return [presetDimensionScores[newAspect]]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 否则动态生成新的评分数据
|
||||||
|
return [generateMockDimensionScores(newAspect)]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vue Composition API 兼容的hook
|
||||||
|
export const useAgentAddAspectMock = () => {
|
||||||
|
const addNewDimension = async (dimensionName: string) => {
|
||||||
|
const response = await mockAgentAddAspectApi([dimensionName])
|
||||||
|
return response[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
const getMultipleDimensions = async (dimensionNames: string[]) => {
|
||||||
|
const responses: AgentAddAspectResponse[] = []
|
||||||
|
|
||||||
|
for (const dimension of dimensionNames) {
|
||||||
|
if (presetDimensionScores[dimension]) {
|
||||||
|
responses.push(presetDimensionScores[dimension])
|
||||||
|
} else {
|
||||||
|
responses.push(generateMockDimensionScores(dimension))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return responses
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
addNewDimension,
|
||||||
|
getMultipleDimensions,
|
||||||
|
generateMockDimensionScores,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
// 模拟后端原始返回格式的Mock数据 - 维度 -> agent -> { Reason, Score }
|
||||||
|
import { vueAgentList, vueAspectList } from './AgentAssignmentMock'
|
||||||
|
|
||||||
|
// 后端返回的评分项格式
|
||||||
|
export interface BackendScoreItem {
|
||||||
|
Reason: string
|
||||||
|
Score: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// 后端返回的完整数据格式
|
||||||
|
export type BackendAgentScoreResponse = Record<string, Record<string, BackendScoreItem>>
|
||||||
|
|
||||||
|
// 模拟后端返回的原始数据结构(维度 -> agent -> { Reason, Score })
|
||||||
|
export const mockBackendAgentScoreData: BackendAgentScoreResponse = {
|
||||||
|
能力: {
|
||||||
|
船舶设计师: { Reason: '展现出卓越的创造力和创新思维', Score: 4 },
|
||||||
|
防护工程专家: { Reason: '展现出杰出的创造性问题解决能力', Score: 5 },
|
||||||
|
病理生理学家: { Reason: '具有中等创造技能,有待提升', Score: 3 },
|
||||||
|
药物化学家: { Reason: '在大多数情况下展现较强的创造性思维', Score: 4 },
|
||||||
|
制剂工程师: { Reason: '展现出胜任的创造能力', Score: 3 },
|
||||||
|
监管事务专家: { Reason: '具有较强的创造性表达能力', Score: 4 },
|
||||||
|
物理学家: { Reason: '擅长创新性思维方法', Score: 5 },
|
||||||
|
实验材料学家: { Reason: '展现出卓越的创造性思维和创新能力', Score: 5 },
|
||||||
|
计算模拟专家: { Reason: '展现出良好的创造性问题解决能力', Score: 4 },
|
||||||
|
腐蚀机理研究员: { Reason: '展现出卓越的创造性问题解决能力', Score: 5 },
|
||||||
|
先进材料研发员: { Reason: '展现出平衡的创造能力', Score: 4 },
|
||||||
|
肾脏病学家: { Reason: '展现出卓越的创造天赋', Score: 5 },
|
||||||
|
临床研究协调员: { Reason: '展现出胜任的创造性思维', Score: 3 },
|
||||||
|
中医药专家: { Reason: '展现出较强的创造性主动性', Score: 4 },
|
||||||
|
药物安全专家: { Reason: '具有发展中的创造技能', Score: 3 },
|
||||||
|
二维材料科学家: { Reason: '展现出卓越的创造愿景', Score: 5 },
|
||||||
|
光电物理学家: { Reason: '展现出卓越的创造性执行力', Score: 4 },
|
||||||
|
机器学习专家: { Reason: '具有较强的创造性问题解决能力', Score: 4 },
|
||||||
|
流体动力学专家: { Reason: '展现出胜任的创造能力', Score: 3 },
|
||||||
|
},
|
||||||
|
可用性: {
|
||||||
|
船舶设计师: { Reason: '展现出卓越的共情能力和社会意识', Score: 5 },
|
||||||
|
防护工程专家: { Reason: '具有较强的情绪调节和人际交往技能', Score: 4 },
|
||||||
|
病理生理学家: { Reason: '展现出卓越的情感智力', Score: 5 },
|
||||||
|
药物化学家: { Reason: '在大多数情况下展现平均的情感智力', Score: 3 },
|
||||||
|
制剂工程师: { Reason: '具有良好的情绪意识和沟通能力', Score: 4 },
|
||||||
|
监管事务专家: { Reason: '在情绪意识方面偶尔表现不足', Score: 3 },
|
||||||
|
物理学家: { Reason: '具有较强的情绪理解能力', Score: 4 },
|
||||||
|
实验材料学家: { Reason: '展现出卓越的共情能力和社交技能', Score: 5 },
|
||||||
|
计算模拟专家: { Reason: '具有良好的情绪调节能力', Score: 4 },
|
||||||
|
腐蚀机理研究员: { Reason: '展现出卓越的情感智力和社会意识', Score: 5 },
|
||||||
|
先进材料研发员: { Reason: '具有发展中的情绪意识', Score: 3 },
|
||||||
|
肾脏病学家: { Reason: '擅长人际交往和建立关系', Score: 5 },
|
||||||
|
临床研究协调员: { Reason: '展现出平衡的情感智力', Score: 4 },
|
||||||
|
中医药专家: { Reason: '具有基本的情绪理解能力', Score: 3 },
|
||||||
|
药物安全专家: { Reason: '展现出良好的情绪调节能力', Score: 4 },
|
||||||
|
二维材料科学家: { Reason: '展现出卓越的社会意识', Score: 5 },
|
||||||
|
光电物理学家: { Reason: '在情感智力方面需要提升', Score: 3 },
|
||||||
|
机器学习专家: { Reason: '具有较强的共情能力', Score: 4 },
|
||||||
|
流体动力学专家: { Reason: '具有良好的情绪沟通技能', Score: 4 },
|
||||||
|
},
|
||||||
|
专业性: {
|
||||||
|
船舶设计师: { Reason: '展现出胜任的哲学推理技能', Score: 3 },
|
||||||
|
防护工程专家: { Reason: '展现出卓越的逻辑推理和分析能力', Score: 5 },
|
||||||
|
病理生理学家: { Reason: '展现出深刻的哲学洞察力和批判性思维', Score: 2 },
|
||||||
|
药物化学家: { Reason: '展现出良好的哲学理解能力', Score: 1 },
|
||||||
|
制剂工程师: { Reason: '具有基础哲学推理能力,存在一些局限', Score: 3 },
|
||||||
|
监管事务专家: { Reason: '展现出较强的分析思维能力', Score: 4 },
|
||||||
|
物理学家: { Reason: '展现出卓越的哲学深度', Score: 5 },
|
||||||
|
实验材料学家: { Reason: '展现出卓越的专业分析和推理能力', Score: 5 },
|
||||||
|
计算模拟专家: { Reason: '具有良好的批判性思维能力', Score: 4 },
|
||||||
|
腐蚀机理研究员: { Reason: '具有较强的专业性分析和推理能力', Score: 4 },
|
||||||
|
先进材料研发员: { Reason: '展现出卓越的逻辑推理能力', Score: 5 },
|
||||||
|
肾脏病学家: { Reason: '具有基础的哲学理解能力', Score: 3 },
|
||||||
|
临床研究协调员: { Reason: '展现出平衡的哲学推理能力', Score: 4 },
|
||||||
|
中医药专家: { Reason: '需要在哲学思维方面发展', Score: 3 },
|
||||||
|
药物安全专家: { Reason: '展现出良好的分析技能', Score: 4 },
|
||||||
|
二维材料科学家: { Reason: '具有较强的哲学洞察力', Score: 4 },
|
||||||
|
光电物理学家: { Reason: '擅长批判性思维和分析', Score: 5 },
|
||||||
|
机器学习专家: { Reason: '具有基础哲学推理能力', Score: 3 },
|
||||||
|
流体动力学专家: { Reason: '展现出卓越的哲学才能', Score: 5 },
|
||||||
|
},
|
||||||
|
效率: {
|
||||||
|
船舶设计师: { Reason: '在任务完成方面展现出卓越的效率', Score: 4 },
|
||||||
|
防护工程专家: { Reason: '擅长高效的工作流程管理', Score: 5 },
|
||||||
|
病理生理学家: { Reason: '展现出平均的效率,有提升空间', Score: 3 },
|
||||||
|
药物化学家: { Reason: '具有良好的时间管理技能', Score: 4 },
|
||||||
|
制剂工程师: { Reason: '展现出胜任的效率', Score: 3 },
|
||||||
|
监管事务专家: { Reason: '展现出卓越的生产力', Score: 5 },
|
||||||
|
物理学家: { Reason: '具有强大的任务执行能力', Score: 4 },
|
||||||
|
实验材料学家: { Reason: '具有良好的工作效率和时间管理', Score: 4 },
|
||||||
|
计算模拟专家: { Reason: '展现出良好的工作流程优化能力', Score: 4 },
|
||||||
|
腐蚀机理研究员: { Reason: '展现出卓越的效率和工作流程管理', Score: 5 },
|
||||||
|
先进材料研发员: { Reason: '展现出足够的效率', Score: 3 },
|
||||||
|
肾脏病学家: { Reason: '擅长快速完成任务', Score: 5 },
|
||||||
|
临床研究协调员: { Reason: '展现出良好的生产力', Score: 4 },
|
||||||
|
中医药专家: { Reason: '具有中等的效率水平', Score: 3 },
|
||||||
|
药物安全专家: { Reason: '具有较强的任务效率', Score: 4 },
|
||||||
|
二维材料科学家: { Reason: '具有良好的执行速度', Score: 4 },
|
||||||
|
光电物理学家: { Reason: '展现出卓越的效率', Score: 5 },
|
||||||
|
机器学习专家: { Reason: '展现出平均的生产力', Score: 3 },
|
||||||
|
流体动力学专家: { Reason: '在执行方面具有良好的效率', Score: 4 },
|
||||||
|
},
|
||||||
|
准确性: {
|
||||||
|
船舶设计师: { Reason: '展现出卓越的细节关注度', Score: 5 },
|
||||||
|
防护工程专家: { Reason: '展现出卓越的准确性和精确度', Score: 5 },
|
||||||
|
病理生理学家: { Reason: '展现出卓越的精确度', Score: 5 },
|
||||||
|
药物化学家: { Reason: '展现出良好的准确性', Score: 4 },
|
||||||
|
制剂工程师: { Reason: '展现出中等的准确性,有提升空间', Score: 3 },
|
||||||
|
监管事务专家: { Reason: '具有较强的细节关注度', Score: 4 },
|
||||||
|
物理学家: { Reason: '在精确度和准确性方面表现卓越', Score: 5 },
|
||||||
|
实验材料学家: { Reason: '展现出卓越的细节导向和精确度', Score: 5 },
|
||||||
|
计算模拟专家: { Reason: '展现出平均的准确性', Score: 3 },
|
||||||
|
腐蚀机理研究员: { Reason: '展现出卓越的准确性和精确技能', Score: 5 },
|
||||||
|
先进材料研发员: { Reason: '展现出卓越的准确性', Score: 5 },
|
||||||
|
肾脏病学家: { Reason: '展现出较强的精确度', Score: 4 },
|
||||||
|
临床研究协调员: { Reason: '展现出中等的准确性', Score: 3 },
|
||||||
|
中医药专家: { Reason: '具有良好的细节导向能力', Score: 4 },
|
||||||
|
药物安全专家: { Reason: '在准确性和精确度方面表现卓越', Score: 5 },
|
||||||
|
二维材料科学家: { Reason: '展现出较强的细节关注度', Score: 4 },
|
||||||
|
光电物理学家: { Reason: '展现出平均的准确性水平', Score: 3 },
|
||||||
|
机器学习专家: { Reason: '在工作中具有良好的精确度', Score: 4 },
|
||||||
|
流体动力学专家: { Reason: '展现出卓越的准确性', Score: 5 },
|
||||||
|
},
|
||||||
|
协作性: {
|
||||||
|
船舶设计师: { Reason: '展现出卓越的协作技能', Score: 4 },
|
||||||
|
防护工程专家: { Reason: '在团队合作和协作方面表现卓越', Score: 5 },
|
||||||
|
病理生理学家: { Reason: '具有较强的协作能力', Score: 4 },
|
||||||
|
药物化学家: { Reason: '具有中等的协作技能', Score: 3 },
|
||||||
|
制剂工程师: { Reason: '展现出良好的团队合作精神', Score: 4 },
|
||||||
|
监管事务专家: { Reason: '具有较强的合作能力', Score: 4 },
|
||||||
|
物理学家: { Reason: '展现出平均的协作技能', Score: 3 },
|
||||||
|
实验材料学家: { Reason: '在团队协作方面表现卓越', Score: 5 },
|
||||||
|
计算模拟专家: { Reason: '展现出良好的合作工作能力', Score: 4 },
|
||||||
|
腐蚀机理研究员: { Reason: '在团队协作和合作方面表现卓越', Score: 5 },
|
||||||
|
先进材料研发员: { Reason: '具有中等的协作水平', Score: 3 },
|
||||||
|
肾脏病学家: { Reason: '展现出良好的协作技能', Score: 4 },
|
||||||
|
临床研究协调员: { Reason: '在协调和团队合作方面表现卓越', Score: 5 },
|
||||||
|
中医药专家: { Reason: '具有较强的合作能力', Score: 4 },
|
||||||
|
药物安全专家: { Reason: '展现出平均的协作水平', Score: 3 },
|
||||||
|
二维材料科学家: { Reason: '展现出良好的团队合作精神', Score: 4 },
|
||||||
|
光电物理学家: { Reason: '具有较强的协作技能', Score: 4 },
|
||||||
|
机器学习专家: { Reason: '在团队协作方面表现卓越', Score: 5 },
|
||||||
|
流体动力学专家: { Reason: '具有中等的协作能力', Score: 3 },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模拟后端API调用 - agentSelectModifyInit
|
||||||
|
export const mockBackendAgentSelectModifyInit = async (): Promise<BackendAgentScoreResponse> => {
|
||||||
|
// 模拟网络延迟 300ms
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 300))
|
||||||
|
return mockBackendAgentScoreData
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模拟后端API调用 - agentSelectModifyAddAspect(添加新维度)
|
||||||
|
export const mockBackendAgentSelectModifyAddAspect = async (
|
||||||
|
aspectList: string[]
|
||||||
|
): Promise<BackendAgentScoreResponse> => {
|
||||||
|
// 模拟网络延迟 500ms
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 500))
|
||||||
|
|
||||||
|
// 获取新添加的维度(最后一个)
|
||||||
|
const newAspect = aspectList[aspectList.length - 1]
|
||||||
|
if (!newAspect) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成该维度下所有agent的评分
|
||||||
|
const aspectData: Record<string, BackendScoreItem> = {}
|
||||||
|
|
||||||
|
vueAgentList.forEach(agent => {
|
||||||
|
const score = Math.floor(Math.random() * 5) + 1
|
||||||
|
let reason = ''
|
||||||
|
switch (score) {
|
||||||
|
case 5:
|
||||||
|
reason = `在"${newAspect}"方面表现卓越,展现出杰出的能力和深刻的理解`
|
||||||
|
break
|
||||||
|
case 4:
|
||||||
|
reason = `在"${newAspect}"方面表现优秀,具有良好的专业能力和执行力`
|
||||||
|
break
|
||||||
|
case 3:
|
||||||
|
reason = `在"${newAspect}"方面表现合格,能够完成相关任务`
|
||||||
|
break
|
||||||
|
case 2:
|
||||||
|
reason = `在"${newAspect}"方面表现一般,仍有提升空间`
|
||||||
|
break
|
||||||
|
case 1:
|
||||||
|
reason = `在"${newAspect}"方面需要加强,建议进一步提升相关能力`
|
||||||
|
break
|
||||||
|
}
|
||||||
|
aspectData[agent] = { Reason: reason, Score: score }
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
[newAspect]: aspectData
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,314 @@
|
|||||||
|
// Vue兼容的mock数据 - 6个维度,19个智能体
|
||||||
|
export const vueAgentList = [
|
||||||
|
'船舶设计师',
|
||||||
|
'防护工程专家',
|
||||||
|
'病理生理学家',
|
||||||
|
'药物化学家',
|
||||||
|
'制剂工程师',
|
||||||
|
'监管事务专家',
|
||||||
|
'物理学家',
|
||||||
|
'实验材料学家',
|
||||||
|
'计算模拟专家',
|
||||||
|
'腐蚀机理研究员',
|
||||||
|
'先进材料研发员',
|
||||||
|
'肾脏病学家',
|
||||||
|
'临床研究协调员',
|
||||||
|
'中医药专家',
|
||||||
|
'药物安全专家',
|
||||||
|
'二维材料科学家',
|
||||||
|
'光电物理学家',
|
||||||
|
'机器学习专家',
|
||||||
|
'流体动力学专家',
|
||||||
|
]
|
||||||
|
|
||||||
|
export const vueAspectList = ['能力', '可用性', '专业性', '效率', '准确性', '协作性']
|
||||||
|
|
||||||
|
// 类型定义
|
||||||
|
export type AgentName = (typeof vueAgentList)[number]
|
||||||
|
export type AspectName = (typeof vueAspectList)[number]
|
||||||
|
|
||||||
|
export interface AgentScore {
|
||||||
|
score: number
|
||||||
|
reason: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type IAgentSelectModifyAddRequest = Record<AspectName, Record<AgentName, AgentScore>>
|
||||||
|
|
||||||
|
// Vue友好的数据结构 - agent -> 维度 -> 评分(与后端返回格式一致)
|
||||||
|
export const vueAgentScoreData: Record<AgentName, Record<AspectName, AgentScore>> = {
|
||||||
|
船舶设计师: {
|
||||||
|
能力: { score: 4, reason: '展现出卓越的创造力和创新思维' },
|
||||||
|
可用性: { score: 5, reason: '展现出卓越的共情能力和社会意识' },
|
||||||
|
专业性: { score: 3, reason: '展现出胜任的哲学推理技能' },
|
||||||
|
效率: { score: 4, reason: '在任务完成方面展现出卓越的效率' },
|
||||||
|
准确性: { score: 5, reason: '展现出卓越的细节关注度' },
|
||||||
|
协作性: { score: 4, reason: '展现出卓越的协作技能' },
|
||||||
|
},
|
||||||
|
防护工程专家: {
|
||||||
|
能力: { score: 5, reason: '展现出杰出的创造性问题解决能力' },
|
||||||
|
可用性: { score: 4, reason: '具有较强的情绪调节和人际交往技能' },
|
||||||
|
专业性: { score: 5, reason: '展现出卓越的逻辑推理和分析能力' },
|
||||||
|
效率: { score: 5, reason: '擅长高效的工作流程管理' },
|
||||||
|
准确性: { score: 5, reason: '展现出卓越的准确性和精确度' },
|
||||||
|
协作性: { score: 5, reason: '在团队合作和协作方面表现卓越' },
|
||||||
|
},
|
||||||
|
病理生理学家: {
|
||||||
|
能力: { score: 3, reason: '具有中等创造技能,有待提升' },
|
||||||
|
可用性: { score: 5, reason: '展现出卓越的情感智力' },
|
||||||
|
专业性: { score: 2, reason: '展现出深刻的哲学洞察力和批判性思维' },
|
||||||
|
效率: { score: 3, reason: '展现出平均的效率,有提升空间' },
|
||||||
|
准确性: { score: 5, reason: '展现出卓越的精确度' },
|
||||||
|
协作性: { score: 4, reason: '具有较强的协作能力' },
|
||||||
|
},
|
||||||
|
药物化学家: {
|
||||||
|
能力: { score: 4, reason: '在大多数情况下展现较强的创造性思维' },
|
||||||
|
可用性: { score: 3, reason: '在大多数情况下展现平均的情感智力' },
|
||||||
|
专业性: { score: 1, reason: '展现出良好的哲学理解能力' },
|
||||||
|
效率: { score: 4, reason: '具有良好的时间管理技能' },
|
||||||
|
准确性: { score: 4, reason: '展现出良好的准确性' },
|
||||||
|
协作性: { score: 3, reason: '具有中等的协作技能' },
|
||||||
|
},
|
||||||
|
制剂工程师: {
|
||||||
|
能力: { score: 3, reason: '展现出胜任的创造能力' },
|
||||||
|
可用性: { score: 4, reason: '具有良好的情绪意识和沟通能力' },
|
||||||
|
专业性: { score: 3, reason: '具有基础哲学推理能力,存在一些局限' },
|
||||||
|
效率: { score: 3, reason: '展现出胜任的效率' },
|
||||||
|
准确性: { score: 3, reason: '展现出中等的准确性,有提升空间' },
|
||||||
|
协作性: { score: 4, reason: '展现出良好的团队合作精神' },
|
||||||
|
},
|
||||||
|
监管事务专家: {
|
||||||
|
能力: { score: 4, reason: '具有较强的创造性表达能力' },
|
||||||
|
可用性: { score: 3, reason: '在情绪意识方面偶尔表现不足' },
|
||||||
|
专业性: { score: 4, reason: '展现出较强的分析思维能力' },
|
||||||
|
效率: { score: 5, reason: '展现出卓越的生产力' },
|
||||||
|
准确性: { score: 4, reason: '具有较强的细节关注度' },
|
||||||
|
协作性: { score: 4, reason: '具有较强的合作能力' },
|
||||||
|
},
|
||||||
|
物理学家: {
|
||||||
|
能力: { score: 5, reason: '擅长创新性思维方法' },
|
||||||
|
可用性: { score: 4, reason: '具有较强的情绪理解能力' },
|
||||||
|
专业性: { score: 5, reason: '展现出卓越的哲学深度' },
|
||||||
|
效率: { score: 4, reason: '具有强大的任务执行能力' },
|
||||||
|
准确性: { score: 5, reason: '在精确度和准确性方面表现卓越' },
|
||||||
|
协作性: { score: 3, reason: '展现出平均的协作技能' },
|
||||||
|
},
|
||||||
|
实验材料学家: {
|
||||||
|
能力: { score: 5, reason: '展现出卓越的创造性思维和创新能力' },
|
||||||
|
可用性: { score: 5, reason: '展现出卓越的共情能力和社交技能' },
|
||||||
|
专业性: { score: 5, reason: '展现出卓越的专业分析和推理能力' },
|
||||||
|
效率: { score: 4, reason: '具有良好的工作效率和时间管理' },
|
||||||
|
准确性: { score: 5, reason: '展现出卓越的细节导向和精确度' },
|
||||||
|
协作性: { score: 5, reason: '在团队协作方面表现卓越' },
|
||||||
|
},
|
||||||
|
计算模拟专家: {
|
||||||
|
能力: { score: 4, reason: '展现出良好的创造性问题解决能力' },
|
||||||
|
可用性: { score: 4, reason: '具有良好的情绪调节能力' },
|
||||||
|
专业性: { score: 4, reason: '具有良好的批判性思维能力' },
|
||||||
|
效率: { score: 4, reason: '展现出良好的工作流程优化能力' },
|
||||||
|
准确性: { score: 3, reason: '展现出平均的准确性' },
|
||||||
|
协作性: { score: 4, reason: '展现出良好的合作工作能力' },
|
||||||
|
},
|
||||||
|
腐蚀机理研究员: {
|
||||||
|
能力: { score: 5, reason: '展现出卓越的创造性问题解决能力' },
|
||||||
|
可用性: { score: 5, reason: '展现出卓越的情感智力和社会意识' },
|
||||||
|
专业性: { score: 4, reason: '具有较强的专业性分析和推理能力' },
|
||||||
|
效率: { score: 5, reason: '展现出卓越的效率和工作流程管理' },
|
||||||
|
准确性: { score: 5, reason: '展现出卓越的准确性和精确技能' },
|
||||||
|
协作性: { score: 5, reason: '在团队协作和合作方面表现卓越' },
|
||||||
|
},
|
||||||
|
先进材料研发员: {
|
||||||
|
能力: { score: 4, reason: '展现出平衡的创造能力' },
|
||||||
|
可用性: { score: 3, reason: '具有发展中的情绪意识' },
|
||||||
|
专业性: { score: 5, reason: '展现出卓越的逻辑推理能力' },
|
||||||
|
效率: { score: 3, reason: '展现出足够的效率' },
|
||||||
|
准确性: { score: 5, reason: '展现出卓越的准确性' },
|
||||||
|
协作性: { score: 3, reason: '具有中等的协作水平' },
|
||||||
|
},
|
||||||
|
肾脏病学家: {
|
||||||
|
能力: { score: 5, reason: '展现出卓越的创造天赋' },
|
||||||
|
可用性: { score: 5, reason: '擅长人际交往和建立关系' },
|
||||||
|
专业性: { score: 3, reason: '具有基础的哲学理解能力' },
|
||||||
|
效率: { score: 5, reason: '擅长快速完成任务' },
|
||||||
|
准确性: { score: 4, reason: '展现出较强的精确度' },
|
||||||
|
协作性: { score: 4, reason: '展现出良好的协作技能' },
|
||||||
|
},
|
||||||
|
临床研究协调员: {
|
||||||
|
能力: { score: 3, reason: '展现出胜任的创造性思维' },
|
||||||
|
可用性: { score: 4, reason: '展现出平衡的情感智力' },
|
||||||
|
专业性: { score: 4, reason: '展现出平衡的哲学推理能力' },
|
||||||
|
效率: { score: 4, reason: '展现出良好的生产力' },
|
||||||
|
准确性: { score: 3, reason: '展现出中等的准确性' },
|
||||||
|
协作性: { score: 5, reason: '在协调和团队合作方面表现卓越' },
|
||||||
|
},
|
||||||
|
中医药专家: {
|
||||||
|
能力: { score: 4, reason: '展现出较强的创造性主动性' },
|
||||||
|
可用性: { score: 3, reason: '具有基本的情绪理解能力' },
|
||||||
|
专业性: { score: 3, reason: '需要在哲学思维方面发展' },
|
||||||
|
效率: { score: 3, reason: '具有中等的效率水平' },
|
||||||
|
准确性: { score: 4, reason: '具有良好的细节导向能力' },
|
||||||
|
协作性: { score: 4, reason: '具有较强的合作能力' },
|
||||||
|
},
|
||||||
|
药物安全专家: {
|
||||||
|
能力: { score: 3, reason: '具有发展中的创造技能' },
|
||||||
|
可用性: { score: 4, reason: '展现出良好的情绪调节能力' },
|
||||||
|
专业性: { score: 4, reason: '展现出良好的分析技能' },
|
||||||
|
效率: { score: 4, reason: '具有较强的任务效率' },
|
||||||
|
准确性: { score: 5, reason: '在准确性和精确度方面表现卓越' },
|
||||||
|
协作性: { score: 3, reason: '展现出平均的协作水平' },
|
||||||
|
},
|
||||||
|
二维材料科学家: {
|
||||||
|
能力: { score: 5, reason: '展现出卓越的创造愿景' },
|
||||||
|
可用性: { score: 5, reason: '展现出卓越的社会意识' },
|
||||||
|
专业性: { score: 4, reason: '具有较强的哲学洞察力' },
|
||||||
|
效率: { score: 4, reason: '具有良好的执行速度' },
|
||||||
|
准确性: { score: 4, reason: '展现出较强的细节关注度' },
|
||||||
|
协作性: { score: 4, reason: '展现出良好的团队合作精神' },
|
||||||
|
},
|
||||||
|
光电物理学家: {
|
||||||
|
能力: { score: 4, reason: '展现出卓越的创造性执行力' },
|
||||||
|
可用性: { score: 3, reason: '在情感智力方面需要提升' },
|
||||||
|
专业性: { score: 5, reason: '擅长批判性思维和分析' },
|
||||||
|
效率: { score: 5, reason: '展现出卓越的效率' },
|
||||||
|
准确性: { score: 3, reason: '展现出平均的准确性水平' },
|
||||||
|
协作性: { score: 4, reason: '具有较强的协作技能' },
|
||||||
|
},
|
||||||
|
机器学习专家: {
|
||||||
|
能力: { score: 4, reason: '具有较强的创造性问题解决能力' },
|
||||||
|
可用性: { score: 4, reason: '具有较强的共情能力' },
|
||||||
|
专业性: { score: 3, reason: '具有基础哲学推理能力' },
|
||||||
|
效率: { score: 3, reason: '展现出平均的生产力' },
|
||||||
|
准确性: { score: 4, reason: '在工作中具有良好的精确度' },
|
||||||
|
协作性: { score: 5, reason: '在团队协作方面表现卓越' },
|
||||||
|
},
|
||||||
|
流体动力学专家: {
|
||||||
|
能力: { score: 3, reason: '展现出胜任的创造能力' },
|
||||||
|
可用性: { score: 4, reason: '具有良好的情绪沟通技能' },
|
||||||
|
专业性: { score: 5, reason: '展现出卓越的哲学才能' },
|
||||||
|
效率: { score: 4, reason: '在执行方面具有良好的效率' },
|
||||||
|
准确性: { score: 5, reason: '展现出卓越的准确性' },
|
||||||
|
协作性: { score: 3, reason: '具有中等的协作能力' },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vue友好的智能体选择配置
|
||||||
|
export const vueAgentSelections = {
|
||||||
|
balanced: { agents: ['船舶设计师', '防护工程专家', '病理生理学家'] },
|
||||||
|
creative: { agents: ['防护工程专家', '物理学家', '二维材料科学家'] },
|
||||||
|
emotional: { agents: ['船舶设计师', '病理生理学家', '实验材料学家'] },
|
||||||
|
philosophical: { agents: ['病理生理学家', '物理学家', '光电物理学家'] },
|
||||||
|
mixed: { agents: ['药物化学家', '先进材料研发员', '肾脏病学家', '机器学习专家'] },
|
||||||
|
}
|
||||||
|
|
||||||
|
export const vueCurrentAgentSelection = 'balanced'
|
||||||
|
|
||||||
|
// Vue兼容的工具函数
|
||||||
|
export const vueCalculateAgentAverages = () => {
|
||||||
|
const averages: Record<string, number> = {}
|
||||||
|
|
||||||
|
vueAgentList.forEach((agent) => {
|
||||||
|
let total = 0
|
||||||
|
let count = 0
|
||||||
|
|
||||||
|
vueAspectList.forEach((aspect) => {
|
||||||
|
// 数据结构:agentScores[agent][aspect]
|
||||||
|
const scoreData = vueAgentScoreData[agent]?.[aspect]
|
||||||
|
if (scoreData) {
|
||||||
|
total += scoreData.score
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
averages[agent] = count > 0 ? Number((total / count).toFixed(2)) : 0
|
||||||
|
})
|
||||||
|
|
||||||
|
return averages
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取按平均分排序的智能体列表
|
||||||
|
export const vueGetSortedAgentsByAverage = () => {
|
||||||
|
const averages = vueCalculateAgentAverages()
|
||||||
|
return [...vueAgentList].sort((a, b) => {
|
||||||
|
return averages[b] - averages[a]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vue Composition API 兼容的hook
|
||||||
|
export const useAgentMockData = () => {
|
||||||
|
// 在Vue中可以使用ref或reactive包装数据
|
||||||
|
const agentScores = vueAgentScoreData
|
||||||
|
const agentSelections = vueAgentSelections
|
||||||
|
const currentSelection = vueCurrentAgentSelection
|
||||||
|
|
||||||
|
// 计算平均分的响应式函数
|
||||||
|
const calculateAverages = () => {
|
||||||
|
return vueCalculateAgentAverages()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取特定维度的评分(返回该维度下所有智能体的评分)
|
||||||
|
const getScoresByAspect = (aspect: AspectName) => {
|
||||||
|
const result: Record<AgentName, AgentScore> = {}
|
||||||
|
// 数据结构:agentScores[agent][aspect],需要遍历所有agent提取指定aspect
|
||||||
|
vueAgentList.forEach((agent) => {
|
||||||
|
if (agentScores[agent]?.[aspect]) {
|
||||||
|
result[agent] = agentScores[agent][aspect]!
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取特定智能体的所有维度评分
|
||||||
|
const getScoresByAgent = (agent: AgentName) => {
|
||||||
|
// 数据结构:agentScores[agent][aspect],直接返回agent的所有维度评分
|
||||||
|
return agentScores[agent] || {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
agentScores,
|
||||||
|
agentSelections,
|
||||||
|
currentSelection,
|
||||||
|
calculateAverages,
|
||||||
|
getScoresByAspect,
|
||||||
|
getScoresByAgent,
|
||||||
|
getSortedAgents: vueGetSortedAgentsByAverage,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vue 2.x 兼容的选项式API版本
|
||||||
|
export const vueMockMixin = {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
vueAgentScores: vueAgentScoreData,
|
||||||
|
vueAgentSelections: vueAgentSelections,
|
||||||
|
vueCurrentSelection: vueCurrentAgentSelection,
|
||||||
|
vueAgentList: vueAgentList,
|
||||||
|
vueAspectList: vueAspectList,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
vueAgentAverages() {
|
||||||
|
return vueCalculateAgentAverages()
|
||||||
|
},
|
||||||
|
vueSortedAgents() {
|
||||||
|
return vueGetSortedAgentsByAverage()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
vueGetScoresByAspect(aspect: AspectName) {
|
||||||
|
const agentScores = (this as any).vueAgentScores
|
||||||
|
const agentList = (this as any).vueAgentList
|
||||||
|
// 数据结构:agentScores[agent][aspect],遍历所有agent提取指定aspect
|
||||||
|
const result: Record<string, { score: number; reason: string }> = {}
|
||||||
|
agentList.forEach((agent: string) => {
|
||||||
|
if (agentScores[agent]?.[aspect]) {
|
||||||
|
result[agent] = agentScores[agent][aspect]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
},
|
||||||
|
vueGetScoresByAgent(agent: AgentName) {
|
||||||
|
const agentScores = (this as any).vueAgentScores
|
||||||
|
// 数据结构:agentScores[agent][aspect],直接返回agent的所有维度评分
|
||||||
|
return agentScores[agent] || {}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -3,15 +3,27 @@ import SvgIcon from '@/components/SvgIcon/index.vue'
|
|||||||
import { getAgentMapIcon } from '@/layout/components/config.ts'
|
import { getAgentMapIcon } from '@/layout/components/config.ts'
|
||||||
import { type ConnectArg, Jsplumb } from '@/layout/components/Main/TaskTemplate/utils.ts'
|
import { type ConnectArg, Jsplumb } from '@/layout/components/Main/TaskTemplate/utils.ts'
|
||||||
import { type IRawStepTask, useAgentsStore } from '@/stores'
|
import { type IRawStepTask, useAgentsStore } from '@/stores'
|
||||||
import { computed } from 'vue'
|
import { computed, ref, nextTick, watch, onMounted } from 'vue'
|
||||||
import { AnchorLocations } from '@jsplumb/browser-ui'
|
import { AnchorLocations } from '@jsplumb/browser-ui'
|
||||||
|
import { Loading } from '@element-plus/icons-vue'
|
||||||
import MultiLineTooltip from '@/components/MultiLineTooltip/index.vue'
|
import MultiLineTooltip from '@/components/MultiLineTooltip/index.vue'
|
||||||
|
|
||||||
import Bg from './Bg.vue'
|
import Bg from './Bg.vue'
|
||||||
|
import BranchButton from './components/BranchButton.vue'
|
||||||
|
|
||||||
|
// 判断计划是否就绪
|
||||||
|
const planReady = computed(() => {
|
||||||
|
return agentsStore.agentRawPlan.data !== undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
const openPlanModification = () => {
|
||||||
|
agentsStore.openPlanModification()
|
||||||
|
}
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(el: 'resetAgentRepoLine'): void
|
(el: 'resetAgentRepoLine'): void
|
||||||
(el: 'setCurrentTask', task: IRawStepTask): void
|
(el: 'setCurrentTask', task: IRawStepTask): void
|
||||||
|
(el: 'add-output', outputName: string): void
|
||||||
|
(el: 'click-branch'): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const jsplumb = new Jsplumb('task-syllabus')
|
const jsplumb = new Jsplumb('task-syllabus')
|
||||||
@@ -26,6 +38,138 @@ const collaborationProcess = computed(() => {
|
|||||||
return agentsStore.agentRawPlan.data?.['Collaboration Process'] ?? []
|
return agentsStore.agentRawPlan.data?.['Collaboration Process'] ?? []
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 检测是否正在填充详情(有步骤但没有 AgentSelection)
|
||||||
|
const isFillingDetails = computed(() => {
|
||||||
|
const process = agentsStore.agentRawPlan.data?.['Collaboration Process'] || []
|
||||||
|
return process.length > 0 && process.some(step => !step.AgentSelection || step.AgentSelection.length === 0)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 计算填充进度
|
||||||
|
const completedSteps = computed(() => {
|
||||||
|
const process = agentsStore.agentRawPlan.data?.['Collaboration Process'] || []
|
||||||
|
return process.filter(step => step.AgentSelection && step.AgentSelection.length > 0).length
|
||||||
|
})
|
||||||
|
|
||||||
|
const totalSteps = computed(() => {
|
||||||
|
return agentsStore.agentRawPlan.data?.['Collaboration Process']?.length || 0
|
||||||
|
})
|
||||||
|
|
||||||
|
// 编辑状态管理
|
||||||
|
const editingTaskId = ref<string | null>(null)
|
||||||
|
const editingContent = ref('')
|
||||||
|
|
||||||
|
// 添加新产物状态管理
|
||||||
|
const isAddingOutput = ref(false)
|
||||||
|
const newOutputInputRef = ref<HTMLElement>()
|
||||||
|
const newOutputName = ref('')
|
||||||
|
|
||||||
|
// 处理加号点击
|
||||||
|
const handleAddOutputClick = () => {
|
||||||
|
isAddingOutput.value = true
|
||||||
|
newOutputName.value = ''
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (newOutputInputRef.value) {
|
||||||
|
newOutputInputRef.value?.focus()
|
||||||
|
}
|
||||||
|
jsplumb.instance.repaintEverything()
|
||||||
|
}, 50)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存新产物
|
||||||
|
const saveNewOutput = () => {
|
||||||
|
if (newOutputName.value.trim()) {
|
||||||
|
const outputName = newOutputName.value.trim()
|
||||||
|
const success = agentsStore.addNewOutput(outputName)
|
||||||
|
if (success) {
|
||||||
|
emit('add-output', outputName)
|
||||||
|
isAddingOutput.value = false
|
||||||
|
newOutputName.value = ''
|
||||||
|
nextTick(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
jsplumb.instance.repaintEverything()
|
||||||
|
}, 50)
|
||||||
|
})
|
||||||
|
console.log('添加新产物成功', outputName)
|
||||||
|
} else {
|
||||||
|
// 退出编辑状态
|
||||||
|
isAddingOutput.value = false
|
||||||
|
newOutputName.value = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消添加产物
|
||||||
|
const cancelAddOutput = () => {
|
||||||
|
isAddingOutput.value = false
|
||||||
|
newOutputName.value = ''
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
jsplumb.instance.repaintEverything()
|
||||||
|
}, 50)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理新产物的键盘事件
|
||||||
|
const handleNewOutputKeydown = (event: KeyboardEvent) => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
event.preventDefault()
|
||||||
|
saveNewOutput()
|
||||||
|
} else if (event.key === 'Escape') {
|
||||||
|
cancelAddOutput()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新产物输入框失去焦点处理
|
||||||
|
const handleNewOutputBlur = () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (newOutputName.value.trim() === '') {
|
||||||
|
cancelAddOutput()
|
||||||
|
}
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始编辑
|
||||||
|
const startEditing = (task: IRawStepTask) => {
|
||||||
|
if (!task.Id) {
|
||||||
|
console.warn('Task ID is missing, cannot start editing')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
editingTaskId.value = task.Id
|
||||||
|
editingContent.value = task.TaskContent || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存编辑
|
||||||
|
const saveEditing = () => {
|
||||||
|
if (editingTaskId.value && editingContent.value.trim()) {
|
||||||
|
const taskToUpdate = collaborationProcess.value.find(item => item.Id === editingTaskId.value)
|
||||||
|
if (taskToUpdate) {
|
||||||
|
taskToUpdate.TaskContent = editingContent.value.trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
editingTaskId.value = null
|
||||||
|
editingContent.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消编辑
|
||||||
|
const cancelEditing = () => {
|
||||||
|
editingTaskId.value = null
|
||||||
|
editingContent.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理键盘事件
|
||||||
|
const handleKeydown = (event: KeyboardEvent) => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
event.preventDefault()
|
||||||
|
saveEditing()
|
||||||
|
} else if (event.key === 'Escape') {
|
||||||
|
cancelEditing()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function handleCurrentTask(task: IRawStepTask, transparent: boolean): ConnectArg[] {
|
function handleCurrentTask(task: IRawStepTask, transparent: boolean): ConnectArg[] {
|
||||||
// 创建当前流程与产出的连线
|
// 创建当前流程与产出的连线
|
||||||
const arr: ConnectArg[] = [
|
const arr: ConnectArg[] = [
|
||||||
@@ -34,14 +178,14 @@ function handleCurrentTask(task: IRawStepTask, transparent: boolean): ConnectArg
|
|||||||
targetId: `task-syllabus-output-object-${task.Id}`,
|
targetId: `task-syllabus-output-object-${task.Id}`,
|
||||||
anchor: [AnchorLocations.Right, AnchorLocations.Left],
|
anchor: [AnchorLocations.Right, AnchorLocations.Left],
|
||||||
config: {
|
config: {
|
||||||
transparent,
|
transparent
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
// 创建当前产出与流程的连线
|
// 创建当前产出与流程的连线
|
||||||
task.InputObject_List?.forEach((item) => {
|
task.InputObject_List?.forEach(item => {
|
||||||
const id = collaborationProcess.value.find((i) => i.OutputObject === item)?.Id
|
const id = collaborationProcess.value.find(i => i.OutputObject === item)?.Id
|
||||||
if (id) {
|
if (id) {
|
||||||
arr.push({
|
arr.push({
|
||||||
sourceId: `task-syllabus-output-object-${id}`,
|
sourceId: `task-syllabus-output-object-${id}`,
|
||||||
@@ -49,8 +193,8 @@ function handleCurrentTask(task: IRawStepTask, transparent: boolean): ConnectArg
|
|||||||
anchor: [AnchorLocations.Left, AnchorLocations.Right],
|
anchor: [AnchorLocations.Left, AnchorLocations.Right],
|
||||||
config: {
|
config: {
|
||||||
type: 'output',
|
type: 'output',
|
||||||
transparent,
|
transparent
|
||||||
},
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -61,7 +205,7 @@ function handleCurrentTask(task: IRawStepTask, transparent: boolean): ConnectArg
|
|||||||
function changeTask(task?: IRawStepTask, isEmit?: boolean) {
|
function changeTask(task?: IRawStepTask, isEmit?: boolean) {
|
||||||
jsplumb.reset()
|
jsplumb.reset()
|
||||||
const arr: ConnectArg[] = []
|
const arr: ConnectArg[] = []
|
||||||
agentsStore.agentRawPlan.data?.['Collaboration Process']?.forEach((item) => {
|
agentsStore.agentRawPlan.data?.['Collaboration Process']?.forEach(item => {
|
||||||
arr.push(...handleCurrentTask(item, item.Id !== task?.Id))
|
arr.push(...handleCurrentTask(item, item.Id !== task?.Id))
|
||||||
})
|
})
|
||||||
jsplumb.connects(arr)
|
jsplumb.connects(arr)
|
||||||
@@ -74,28 +218,86 @@ function clear() {
|
|||||||
jsplumb.reset()
|
jsplumb.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 封装连线重绘方法
|
||||||
|
const redrawConnections = () => {
|
||||||
|
// 等待 DOM 更新完成
|
||||||
|
nextTick(() => {
|
||||||
|
// 清除旧连线
|
||||||
|
jsplumb.reset()
|
||||||
|
|
||||||
|
// 等待 DOM 稳定后重新绘制
|
||||||
|
setTimeout(() => {
|
||||||
|
const arr: ConnectArg[] = []
|
||||||
|
const currentTaskId = agentsStore.currentTask?.Id
|
||||||
|
|
||||||
|
// 重新绘制所有连线
|
||||||
|
collaborationProcess.value.forEach(item => {
|
||||||
|
arr.push(...handleCurrentTask(item, item.Id !== currentTaskId))
|
||||||
|
})
|
||||||
|
|
||||||
|
jsplumb.connects(arr)
|
||||||
|
}, 100)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听 collaborationProcess 变化,自动重绘连线
|
||||||
|
watch(
|
||||||
|
() => collaborationProcess,
|
||||||
|
() => {
|
||||||
|
redrawConnections()
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
// 组件挂载后初始化连线
|
||||||
|
onMounted(() => {
|
||||||
|
// 初始化时绘制连线
|
||||||
|
nextTick(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
const arr: ConnectArg[] = []
|
||||||
|
collaborationProcess.value.forEach(item => {
|
||||||
|
arr.push(...handleCurrentTask(item, true))
|
||||||
|
})
|
||||||
|
jsplumb.connects(arr)
|
||||||
|
}, 100)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
changeTask,
|
changeTask,
|
||||||
clear,
|
clear
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="h-full flex flex-col">
|
<div class="h-full flex flex-col">
|
||||||
<div class="text-[18px] font-bold mb-[18px]">任务大纲</div>
|
<div class="text-[18px] font-bold mb-[18px] text-[var(--color-text-title-header)]">
|
||||||
|
任务大纲
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 加载详情提示 -->
|
||||||
|
<div v-if="isFillingDetails" class="detail-loading-hint">
|
||||||
|
<el-icon class="is-loading"><Loading /></el-icon>
|
||||||
|
<span>正在生成任务协作流程...</span>
|
||||||
|
<span class="progress">{{ completedSteps }}/{{ totalSteps }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-loading="agentsStore.agentRawPlan.loading"
|
v-loading="agentsStore.agentRawPlan.loading"
|
||||||
class="flex-1 w-full overflow-y-auto relative"
|
class="flex-1 w-full overflow-y-auto relative"
|
||||||
@scroll="handleScroll"
|
@scroll="handleScroll"
|
||||||
>
|
>
|
||||||
<div v-show="collaborationProcess.length > 0" class="w-full relative min-h-full" id="task-syllabus">
|
<div
|
||||||
<Bg />
|
v-show="collaborationProcess.length > 0"
|
||||||
|
class="w-full relative min-h-full"
|
||||||
|
id="task-syllabus"
|
||||||
|
>
|
||||||
|
<Bg :is-adding="isAddingOutput" @start-add-output="handleAddOutputClick" />
|
||||||
|
|
||||||
<div class="w-full flex items-center gap-[14%] mb-[35px]">
|
<div class="w-full flex items-center gap-[14%] mb-[35px]">
|
||||||
<div class="flex-1 flex justify-center">
|
<div class="flex-1 flex justify-center">
|
||||||
<div
|
<div
|
||||||
class="card-item w-[45%] h-[41px] flex justify-center relative z-99 items-center rounded-[20px] bg-[var(--color-bg-tertiary)]"
|
class="card-item w-[168px] h-[41px] flex justify-center relative z-99 items-center rounded-[20px] bg-[var(--color-bg-flow)]"
|
||||||
>
|
>
|
||||||
流程
|
流程
|
||||||
</div>
|
</div>
|
||||||
@@ -103,41 +305,112 @@ defineExpose({
|
|||||||
|
|
||||||
<div class="flex-1 flex justify-center">
|
<div class="flex-1 flex justify-center">
|
||||||
<div
|
<div
|
||||||
class="card-item w-[45%] h-[41px] flex justify-center relative z-99 items-center rounded-[20px] bg-[var(--color-bg-tertiary)]"
|
class="card-item w-[168px] h-[41px] flex justify-center relative z-99 items-center rounded-[20px] bg-[var(--color-bg-flow)]"
|
||||||
>
|
>
|
||||||
产物
|
产物
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 添加新产物卡片 -->
|
||||||
|
<div
|
||||||
|
v-if="isAddingOutput"
|
||||||
|
class="card-it w-full flex items-center gap-[14%] bg-[var(--color-card-bg)] add-output-form mb-[100px]"
|
||||||
|
>
|
||||||
|
<!-- 左侧空白的流程卡片占位 -->
|
||||||
|
<div class="w-[43%] relative z-99" style="height: 20px"></div>
|
||||||
|
|
||||||
|
<!-- 右侧可编辑的产物卡片 -->
|
||||||
|
<el-card
|
||||||
|
class="w-[43%] relative task-syllabus-output-object-card border-dashed border-2 border-[var(--color-primary)]"
|
||||||
|
>
|
||||||
|
<div class="h-full flex items-center justify-center">
|
||||||
|
<!-- 输入框 -->
|
||||||
|
<el-input
|
||||||
|
ref="newOutputInputRef"
|
||||||
|
v-model="newOutputName"
|
||||||
|
placeholder="Enter保存,ESC取消"
|
||||||
|
@keydown="handleNewOutputKeydown"
|
||||||
|
@blur="handleNewOutputBlur"
|
||||||
|
size="large"
|
||||||
|
class="w-full"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 显示临时产物卡片 -->
|
||||||
|
<div
|
||||||
|
v-for="output in agentsStore.additionalOutputs"
|
||||||
|
:key="output"
|
||||||
|
class="card-it w-full flex items-center gap-[14%] bg-[var(--color-card-bg)] mb-[100px]"
|
||||||
|
>
|
||||||
|
<!-- 左侧空白的流程卡片占位 -->
|
||||||
|
<div class="w-[43%] relative z-99" style="height: 100px"></div>
|
||||||
|
|
||||||
|
<!-- 右侧产物卡片 -->
|
||||||
|
<el-card class="w-[43%] relative task-syllabus-output-object-card" :shadow="true">
|
||||||
|
<div class="text-[18px] font-bold text-center">{{ output }}</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
v-for="item in collaborationProcess"
|
v-for="item in collaborationProcess"
|
||||||
:key="item.Id"
|
:key="item.Id"
|
||||||
class="card-item w-full flex items-center gap-[14%]"
|
class="card-it w-full flex items-center gap-[14%] bg-[var(--color-card-bg)] mb-[100px]"
|
||||||
>
|
>
|
||||||
<!-- 流程卡片 -->
|
<!-- 流程卡片 -->
|
||||||
<el-card
|
<el-card
|
||||||
class="w-[43%] overflow-y-auto relative z-99 task-syllabus-flow-card"
|
class="w-[43%] overflow-y-auto relative z-99 task-syllabus-flow-card"
|
||||||
:class="agentsStore.currentTask?.StepName === item.StepName ? 'active-card' : ''"
|
:class="agentsStore.currentTask?.StepName === item.StepName ? 'active-card' : ''"
|
||||||
shadow="hover"
|
:shadow="true"
|
||||||
:id="`task-syllabus-flow-${item.Id}`"
|
:id="`task-syllabus-flow-${item.Id}`"
|
||||||
@click="changeTask(item, true)"
|
@click="changeTask(item, true)"
|
||||||
>
|
>
|
||||||
<MultiLineTooltip placement="right" :text="item.StepName" :lines="2">
|
<MultiLineTooltip placement="right" :text="item.StepName" :lines="2">
|
||||||
<div class="text-[18px] font-bold text-center">
|
<div class="text-[18px] font-bold text-center">{{ item.StepName }}</div>
|
||||||
{{ item.StepName }}
|
|
||||||
</div>
|
|
||||||
</MultiLineTooltip>
|
</MultiLineTooltip>
|
||||||
<div class="h-[1px] w-full bg-[#494B51] my-[8px]"></div>
|
<div class="h-[1px] w-full bg-[var(--color-border-separate)] my-[8px]"></div>
|
||||||
<MultiLineTooltip placement="right" :text="item.StepName" :lines="3">
|
|
||||||
<div
|
<!-- 任务内容区域 - 支持双击编辑 -->
|
||||||
class="text-[14px] text-[var(--color-text-secondary)]"
|
<div v-if="editingTaskId === item.Id" class="w-full">
|
||||||
:title="item.TaskContent"
|
<div class="flex flex-col gap-3">
|
||||||
>
|
<el-input
|
||||||
|
v-model="editingContent"
|
||||||
|
type="textarea"
|
||||||
|
:autosize="{ minRows: 2, maxRows: 4 }"
|
||||||
|
placeholder="请输入任务内容"
|
||||||
|
@keydown="handleKeydown"
|
||||||
|
class="task-content-editor"
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
<div class="flex justify-end">
|
||||||
|
<svg-icon
|
||||||
|
icon-class="Check"
|
||||||
|
size="20px"
|
||||||
|
color="#328621"
|
||||||
|
class="cursor-pointer mr-4"
|
||||||
|
@click="saveEditing"
|
||||||
|
title="保存"
|
||||||
|
/>
|
||||||
|
<svg-icon
|
||||||
|
icon-class="Cancel"
|
||||||
|
size="20px"
|
||||||
|
color="#8e0707"
|
||||||
|
class="cursor-pointer mr-1"
|
||||||
|
@click="cancelEditing"
|
||||||
|
title="取消"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else @dblclick="startEditing(item)" class="w-full cursor-pointer">
|
||||||
|
<MultiLineTooltip placement="right" :text="item.TaskContent" :lines="3">
|
||||||
|
<div class="text-[14px] text-[var(--color-text-secondary)] task-content-display">
|
||||||
{{ item.TaskContent }}
|
{{ item.TaskContent }}
|
||||||
</div>
|
</div>
|
||||||
</MultiLineTooltip>
|
</MultiLineTooltip>
|
||||||
<div class="h-[1px] w-full bg-[#494B51] my-[8px]"></div>
|
</div>
|
||||||
|
|
||||||
|
<div class="h-[1px] w-full bg-[var(--color-border-separate)] my-[8px]"></div>
|
||||||
<div
|
<div
|
||||||
class="flex items-center gap-2 overflow-y-auto flex-wrap relative w-full max-h-[72px]"
|
class="flex items-center gap-2 overflow-y-auto flex-wrap relative w-full max-h-[72px]"
|
||||||
>
|
>
|
||||||
@@ -157,9 +430,7 @@ defineExpose({
|
|||||||
<div class="text-[18px] font-bold">{{ agentSelection }}</div>
|
<div class="text-[18px] font-bold">{{ agentSelection }}</div>
|
||||||
<div class="h-[1px] w-full bg-[#494B51] my-[8px]"></div>
|
<div class="h-[1px] w-full bg-[#494B51] my-[8px]"></div>
|
||||||
<div>
|
<div>
|
||||||
{{
|
{{ item.TaskProcess.find(i => i.AgentName === agentSelection)?.Description }}
|
||||||
item.TaskProcess.find((i) => i.AgentName === agentSelection)?.Description
|
|
||||||
}}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -169,7 +440,7 @@ defineExpose({
|
|||||||
>
|
>
|
||||||
<svg-icon
|
<svg-icon
|
||||||
:icon-class="getAgentMapIcon(agentSelection).icon"
|
:icon-class="getAgentMapIcon(agentSelection).icon"
|
||||||
color="var(--color-text)"
|
color="#fff"
|
||||||
size="24px"
|
size="24px"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -178,8 +449,8 @@ defineExpose({
|
|||||||
</el-card>
|
</el-card>
|
||||||
<!-- 产物卡片 -->
|
<!-- 产物卡片 -->
|
||||||
<el-card
|
<el-card
|
||||||
class="w-[43%] relative"
|
class="w-[43%] relative task-syllabus-output-object-card"
|
||||||
shadow="hover"
|
:shadow="true"
|
||||||
:class="agentsStore.currentTask?.StepName === item.StepName ? 'active-card' : ''"
|
:class="agentsStore.currentTask?.StepName === item.StepName ? 'active-card' : ''"
|
||||||
:id="`task-syllabus-output-object-${item.Id}`"
|
:id="`task-syllabus-output-object-${item.Id}`"
|
||||||
>
|
>
|
||||||
@@ -188,10 +459,20 @@ defineExpose({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<BranchButton v-if="planReady" @click="openPlanModification" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.task-syllabus-flow-card {
|
.task-syllabus-flow-card {
|
||||||
|
background-color: var(--color-card-bg-task);
|
||||||
|
border: 1px solid var(--color-card-border-task);
|
||||||
|
box-sizing: border-box;
|
||||||
|
transition: border-color 0.2s ease;
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--color-card-bg-task-hover);
|
||||||
|
border-color: var(--color-card-border-hover);
|
||||||
|
box-shadow: var(--color-card-shadow-hover);
|
||||||
|
}
|
||||||
:deep(.el-card__body) {
|
:deep(.el-card__body) {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -201,4 +482,199 @@ defineExpose({
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.task-syllabus-output-object-card {
|
||||||
|
background-color: var(--color-card-bg-task);
|
||||||
|
border: 1px solid var(--color-card-border-task);
|
||||||
|
box-sizing: border-box;
|
||||||
|
transition: border-color 0.2s ease;
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--color-card-bg-task-hover);
|
||||||
|
border-color: var(--color-card-border-hover);
|
||||||
|
box-shadow: var(--color-card-shadow-hover);
|
||||||
|
}
|
||||||
|
:deep(.el-card__body) {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-content-editor {
|
||||||
|
:deep(.el-textarea__inner) {
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 4px;
|
||||||
|
resize: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-content-display {
|
||||||
|
min-height: 40px;
|
||||||
|
word-break: break-word;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-output-btn {
|
||||||
|
opacity: 0.8;
|
||||||
|
transition: opacity 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(59, 130, 246, 0.05);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-output-form {
|
||||||
|
animation: slideDown 0.3s ease-out;
|
||||||
|
:deep(.el-card__body) {
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
:deep(.el-input__wrapper) {
|
||||||
|
border: 1px solid var(--color-text);
|
||||||
|
background: transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
|
||||||
|
&.is-focus {
|
||||||
|
border-color: var(--color-text);
|
||||||
|
box-shadow: 0 0 0 1px var(--color-primary-light);
|
||||||
|
}
|
||||||
|
:deep(.el-input__inner) {
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideDown {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-10px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载详情提示样式
|
||||||
|
.detail-loading-hint {
|
||||||
|
position: fixed;
|
||||||
|
top: 80px;
|
||||||
|
right: 20px;
|
||||||
|
background: var(--el-bg-color);
|
||||||
|
border: 1px solid var(--el-border-color);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
z-index: 1000;
|
||||||
|
animation: slideInRight 0.3s ease-out;
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
font-weight: bold;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideInRight {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 输入框样式
|
||||||
|
:deep(.el-input__wrapper) {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: none;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: #c0c4cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-focus {
|
||||||
|
border-color: #409eff;
|
||||||
|
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-input__inner) {
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
font-size: 14px;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 任务内容编辑按钮样式 - 匹配执行结果编辑按钮样式
|
||||||
|
.task-content-editor {
|
||||||
|
.el-button {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
&.el-button--small {
|
||||||
|
padding: 4px 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在深色模式下,按钮背景会自动适配为深色
|
||||||
|
html.dark & {
|
||||||
|
.el-button {
|
||||||
|
&.el-button--primary {
|
||||||
|
background-color: var(--color-bg-detail);
|
||||||
|
border-color: var(--color-border);
|
||||||
|
color: var(--color-text);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--color-bg-hover);
|
||||||
|
border-color: var(--color-text-hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(.el-button--primary) {
|
||||||
|
background-color: var(--color-bg-detail);
|
||||||
|
border-color: var(--color-border);
|
||||||
|
color: var(--color-text);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--color-bg-hover);
|
||||||
|
border-color: var(--color-text-hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import TaskResult from './TaskResult/index.vue'
|
|||||||
import { Jsplumb } from './utils.ts'
|
import { Jsplumb } from './utils.ts'
|
||||||
import { type IRawStepTask, useAgentsStore } from '@/stores'
|
import { type IRawStepTask, useAgentsStore } from '@/stores'
|
||||||
import { BezierConnector } from '@jsplumb/browser-ui'
|
import { BezierConnector } from '@jsplumb/browser-ui'
|
||||||
|
import { ref } from 'vue'
|
||||||
const agentsStore = useAgentsStore()
|
const agentsStore = useAgentsStore()
|
||||||
|
|
||||||
// 智能体库
|
// 智能体库
|
||||||
@@ -15,9 +15,9 @@ const agentRepoJsplumb = new Jsplumb('task-template', {
|
|||||||
options: {
|
options: {
|
||||||
curviness: 30, // 曲线弯曲程度
|
curviness: 30, // 曲线弯曲程度
|
||||||
stub: 20, // 添加连接点与端点的距离
|
stub: 20, // 添加连接点与端点的距离
|
||||||
alwaysRespectStubs: true,
|
alwaysRespectStubs: true
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 任务流程
|
// 任务流程
|
||||||
@@ -32,18 +32,16 @@ const taskResultRef = ref<{
|
|||||||
}>()
|
}>()
|
||||||
const taskResultJsplumb = new Jsplumb('task-template')
|
const taskResultJsplumb = new Jsplumb('task-template')
|
||||||
|
|
||||||
|
|
||||||
function scrollToElementTop(elementId: string) {
|
function scrollToElementTop(elementId: string) {
|
||||||
const element = document.getElementById(elementId);
|
const element = document.getElementById(elementId)
|
||||||
if (element) {
|
if (element) {
|
||||||
element.scrollIntoView({
|
element.scrollIntoView({
|
||||||
behavior: 'smooth',
|
behavior: 'smooth',
|
||||||
block: 'start'
|
block: 'start'
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function handleTaskSyllabusCurrentTask(task: IRawStepTask) {
|
function handleTaskSyllabusCurrentTask(task: IRawStepTask) {
|
||||||
scrollToElementTop(`task-results-${task.Id}-0`)
|
scrollToElementTop(`task-results-${task.Id}-0`)
|
||||||
agentsStore.setCurrentTask(task)
|
agentsStore.setCurrentTask(task)
|
||||||
@@ -73,16 +71,22 @@ function clear() {
|
|||||||
taskResultJsplumb.repaintEverything()
|
taskResultJsplumb.repaintEverything()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const additionalOutputs = ref<string[]>([])
|
||||||
|
const handleAddOutput = (outputName: string) => {
|
||||||
|
additionalOutputs.value.unshift(outputName)
|
||||||
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
changeTask,
|
changeTask,
|
||||||
resetAgentRepoLine,
|
resetAgentRepoLine,
|
||||||
clear,
|
clear
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<!-- 删除overflow-hidden -->
|
||||||
<div
|
<div
|
||||||
class="task-template flex gap-6 items-center h-[calc(100%-84px)] relative overflow-hidden"
|
class="task-template flex gap-6 items-center h-[calc(100%-84px)] relative"
|
||||||
id="task-template"
|
id="task-template"
|
||||||
>
|
>
|
||||||
<!-- 智能体库 -->
|
<!-- 智能体库 -->
|
||||||
@@ -95,6 +99,7 @@ defineExpose({
|
|||||||
ref="taskSyllabusRef"
|
ref="taskSyllabusRef"
|
||||||
@resetAgentRepoLine="resetAgentRepoLine"
|
@resetAgentRepoLine="resetAgentRepoLine"
|
||||||
@set-current-task="handleTaskSyllabusCurrentTask"
|
@set-current-task="handleTaskSyllabusCurrentTask"
|
||||||
|
@add-output="handleAddOutput"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<!-- 执行结果 -->
|
<!-- 执行结果 -->
|
||||||
@@ -103,6 +108,7 @@ defineExpose({
|
|||||||
ref="taskResultRef"
|
ref="taskResultRef"
|
||||||
@refresh-line="taskResultJsplumb.repaintEverything"
|
@refresh-line="taskResultJsplumb.repaintEverything"
|
||||||
@set-current-task="handleTaskResultCurrentTask"
|
@set-current-task="handleTaskResultCurrentTask"
|
||||||
|
:additional-outputs="additionalOutputs"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -111,10 +117,10 @@ defineExpose({
|
|||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.task-template {
|
.task-template {
|
||||||
& > div {
|
& > div {
|
||||||
box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.8);
|
box-shadow: var(--color-card-shadow-three);
|
||||||
border-radius: 24px;
|
border-radius: 24px;
|
||||||
border: 1px solid #414752;
|
border: 1px solid var(--color-card-border-three);
|
||||||
background: var(--color-bg-quinary);
|
background: var(--color-bg-three);
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
padding-bottom: 20px;
|
padding-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,8 +87,8 @@ export class Jsplumb {
|
|||||||
const stops = _config.stops ?? this.getStops(config.type)
|
const stops = _config.stops ?? this.getStops(config.type)
|
||||||
// 如果config.transparent为true,则将stops都加一些透明度
|
// 如果config.transparent为true,则将stops都加一些透明度
|
||||||
if (config.transparent) {
|
if (config.transparent) {
|
||||||
stops[0][1] = stops[0][1] + '30'
|
stops[0][1] = stops[0][1] + '80'
|
||||||
stops[1][1] = stops[1][1] + '30'
|
stops[1][1] = stops[1][1] + '80'
|
||||||
}
|
}
|
||||||
if (targetElement && sourceElement) {
|
if (targetElement && sourceElement) {
|
||||||
this.instance.connect({
|
this.instance.connect({
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Task from './Task.vue'
|
import Task from './Task.vue'
|
||||||
import TaskTemplate from './TaskTemplate/index.vue'
|
import TaskTemplate from './TaskTemplate/index.vue'
|
||||||
import { nextTick } from 'vue'
|
import { nextTick, ref } from 'vue'
|
||||||
|
|
||||||
const taskTemplateRef = ref<{ changeTask: () => void, clear: () => void }>()
|
const taskTemplateRef = ref<{ changeTask: () => void; clear: () => void }>()
|
||||||
|
|
||||||
function handleSearch() {
|
function handleSearch() {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ export interface AgentMapDuty {
|
|||||||
name: string
|
name: string
|
||||||
key: string
|
key: string
|
||||||
color: string
|
color: string
|
||||||
|
border: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 职责映射
|
// 职责映射
|
||||||
@@ -122,21 +123,25 @@ export const agentMapDuty: Record<string, AgentMapDuty> = {
|
|||||||
name: '提议',
|
name: '提议',
|
||||||
key: 'propose',
|
key: 'propose',
|
||||||
color: '#06A3FF',
|
color: '#06A3FF',
|
||||||
|
border:'#0f95e4'
|
||||||
},
|
},
|
||||||
Critique: {
|
Critique: {
|
||||||
name: '评审',
|
name: '评审',
|
||||||
key: 'review',
|
key: 'review',
|
||||||
color: '#FFFC08',
|
color: '#2cd235',
|
||||||
|
border:'#19ab21'
|
||||||
},
|
},
|
||||||
Improve: {
|
Improve: {
|
||||||
name: '改进',
|
name: '改进',
|
||||||
key: 'improve',
|
key: 'improve',
|
||||||
color: '#BF65FF',
|
color: '#ff8a01',
|
||||||
|
border:'#dc7700'
|
||||||
},
|
},
|
||||||
Finalize: {
|
Finalize: {
|
||||||
name: '总结',
|
name: '总结',
|
||||||
key: 'summary',
|
key: 'summary',
|
||||||
color: '#FFA236',
|
color: '#bf65ff',
|
||||||
|
border:'#a13de8'
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,112 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Header from './components/Header.vue'
|
import Header from './components/Header.vue'
|
||||||
import Main from './components/Main/index.vue'
|
import Main from './components/Main/index.vue'
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
import FloatWindow from './components/Main/TaskTemplate/TaskSyllabus/components/FloatWindow.vue'
|
||||||
|
import PlanModification from './components/Main/TaskTemplate/TaskSyllabus/Branch/PlanModification.vue'
|
||||||
|
import { useAgentsStore } from '@/stores/modules/agents'
|
||||||
|
import PlanTask from './components/Main/TaskTemplate/TaskProcess/components/PlanTask.vue'
|
||||||
|
import AgentAllocation from './components/Main/TaskTemplate/TaskSyllabus/components/AgentAllocation.vue'
|
||||||
|
const isDarkMode = ref(false)
|
||||||
|
const agentsStore = useAgentsStore()
|
||||||
|
// 初始化主题
|
||||||
|
const initTheme = () => {
|
||||||
|
const savedTheme = localStorage.getItem('theme')
|
||||||
|
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||||
|
|
||||||
|
if (savedTheme === 'dark' || (!savedTheme && prefersDark)) {
|
||||||
|
enableDarkMode()
|
||||||
|
} else {
|
||||||
|
enableLightMode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启用深色模式
|
||||||
|
const enableDarkMode = () => {
|
||||||
|
document.documentElement.classList.add('dark')
|
||||||
|
isDarkMode.value = true
|
||||||
|
localStorage.setItem('theme', 'dark')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启用浅色模式
|
||||||
|
const enableLightMode = () => {
|
||||||
|
document.documentElement.classList.remove('dark')
|
||||||
|
isDarkMode.value = false
|
||||||
|
localStorage.setItem('theme', 'light')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换主题
|
||||||
|
const toggleTheme = () => {
|
||||||
|
if (isDarkMode.value) {
|
||||||
|
enableLightMode()
|
||||||
|
} else {
|
||||||
|
enableDarkMode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组件挂载时初始化主题
|
||||||
|
onMounted(() => {
|
||||||
|
initTheme()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="h-full">
|
<div class="h-full relative">
|
||||||
|
<!-- 主题切换按钮 - 放置在页面右上角 -->
|
||||||
|
<div class="absolute top-4 right-4 z-50">
|
||||||
|
<button
|
||||||
|
@click="toggleTheme"
|
||||||
|
class="w-10 h-10 rounded-full transition-all duration-300 flex items-center justify-center"
|
||||||
|
:class="{
|
||||||
|
'bg-[#ebebeb] hover:bg-[var(--color-bg-hover)]': !isDarkMode,
|
||||||
|
'bg-[#0e131c] hover:bg-[var(--color-bg-hover)]': isDarkMode
|
||||||
|
}"
|
||||||
|
:title="isDarkMode ? '切换到浅色模式' : '切换到深色模式'"
|
||||||
|
>
|
||||||
|
<!-- 浅色模式图标 -->
|
||||||
|
<svg-icon
|
||||||
|
v-if="!isDarkMode"
|
||||||
|
icon-class="sunny"
|
||||||
|
size="20px"
|
||||||
|
:color="isDarkMode ? 'var(--color-text-title)' : 'var(--color-text-title)'"
|
||||||
|
class="transition-opacity duration-300"
|
||||||
|
/>
|
||||||
|
<!-- 深色模式图标 -->
|
||||||
|
<svg-icon
|
||||||
|
v-else
|
||||||
|
icon-class="moon"
|
||||||
|
size="20px"
|
||||||
|
:color="isDarkMode ? 'var(--color-text-title)' : 'var(--color-text-title)'"
|
||||||
|
class="transition-opacity duration-300"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Header />
|
<Header />
|
||||||
<Main />
|
<Main />
|
||||||
|
|
||||||
|
<FloatWindow
|
||||||
|
v-if="agentsStore.planModificationWindow"
|
||||||
|
title="任务大纲探索"
|
||||||
|
@close="agentsStore.closePlanModification"
|
||||||
|
>
|
||||||
|
<PlanModification />
|
||||||
|
</FloatWindow>
|
||||||
|
|
||||||
|
<FloatWindow
|
||||||
|
v-if="agentsStore.planTaskWindow"
|
||||||
|
title="任务过程探索"
|
||||||
|
@close="agentsStore.closePlanTask"
|
||||||
|
>
|
||||||
|
<PlanTask />
|
||||||
|
</FloatWindow>
|
||||||
|
|
||||||
|
<FloatWindow
|
||||||
|
v-if="agentsStore.agentAllocationDialog"
|
||||||
|
title="智能体探索"
|
||||||
|
@close="agentsStore.closeAgentAllocationDialog"
|
||||||
|
>
|
||||||
|
<AgentAllocation />
|
||||||
|
</FloatWindow>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -8,13 +8,18 @@ import 'element-plus/theme-chalk/dark/css-vars.css'
|
|||||||
import 'virtual:svg-icons-register'
|
import 'virtual:svg-icons-register'
|
||||||
import { initService } from '@/utils/request.ts'
|
import { initService } from '@/utils/request.ts'
|
||||||
import { setupStore, useConfigStore } from '@/stores'
|
import { setupStore, useConfigStore } from '@/stores'
|
||||||
|
import { setupDirective } from '@/ directive'
|
||||||
|
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
|
||||||
async function init() {
|
async function init() {
|
||||||
const app = createApp(App)
|
|
||||||
setupStore(app)
|
|
||||||
initService()
|
|
||||||
const configStore = useConfigStore()
|
const configStore = useConfigStore()
|
||||||
await configStore.initConfig()
|
await configStore.initConfig()
|
||||||
|
const app = createApp(App)
|
||||||
|
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
|
||||||
|
app.component(key, component)
|
||||||
|
}
|
||||||
|
setupStore(app)
|
||||||
|
setupDirective(app)
|
||||||
|
initService()
|
||||||
document.title = configStore.config.centerTitle
|
document.title = configStore.config.centerTitle
|
||||||
app.use(router)
|
app.use(router)
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|||||||
@@ -10,3 +10,4 @@ export function setupStore(app: App<Element>) {
|
|||||||
|
|
||||||
export * from './modules/agents.ts'
|
export * from './modules/agents.ts'
|
||||||
export * from './modules/config.ts'
|
export * from './modules/config.ts'
|
||||||
|
export * from './modules/selection.ts'
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
import { ref } from 'vue'
|
import { ref, watch, computed } from 'vue'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
|
import { getAgentMapIcon } from '@/layout/components/config'
|
||||||
import { store } from '../index'
|
import { store } from '../index'
|
||||||
import { useStorage } from '@vueuse/core'
|
import { useStorage } from '@vueuse/core'
|
||||||
import type { IExecuteRawResponse } from '@/api'
|
import type { IExecuteRawResponse } from '@/api'
|
||||||
import { useConfigStore } from '@/stores/modules/config.ts'
|
import { useConfigStore } from '@/stores/modules/config.ts'
|
||||||
|
|
||||||
export interface Agent {
|
export interface Agent {
|
||||||
Name: string
|
Name: string
|
||||||
Profile: string
|
Profile: string
|
||||||
Icon: string
|
Icon: string
|
||||||
Classification: string
|
Classification: string
|
||||||
|
apiUrl?: string
|
||||||
|
apiKey?: string
|
||||||
|
apiModel?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type HslColorVector = [number, number, number]
|
type HslColorVector = [number, number, number]
|
||||||
@@ -43,10 +45,44 @@ export interface IRawStepTask {
|
|||||||
InputObject_List?: string[]
|
InputObject_List?: string[]
|
||||||
OutputObject?: string
|
OutputObject?: string
|
||||||
AgentSelection?: string[]
|
AgentSelection?: string[]
|
||||||
Collaboration_Brief_FrontEnd: IRichText
|
Collaboration_Brief_frontEnd: IRichText
|
||||||
TaskProcess: TaskProcess[]
|
TaskProcess: TaskProcess[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IApiAgentAction {
|
||||||
|
id: string
|
||||||
|
type: string
|
||||||
|
agent: string
|
||||||
|
description: string
|
||||||
|
inputs: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IApiStepTask {
|
||||||
|
name: string
|
||||||
|
content: string
|
||||||
|
inputs: string[]
|
||||||
|
output: string
|
||||||
|
agents: string[]
|
||||||
|
brief: IRichText
|
||||||
|
process: IApiAgentAction[]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 智能体评分数据类型
|
||||||
|
export interface IScoreItem {
|
||||||
|
score: number
|
||||||
|
reason: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IAgentSelectModifyAddRequest {
|
||||||
|
aspectList: string[] // 维度列表
|
||||||
|
agentScores: Record<string, Record<string, IScoreItem>> // agent -> 维度 -> 评分(与后端返回格式一致)
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IAgentSelectModifyInitRequest {
|
||||||
|
goal: string
|
||||||
|
stepTask: IApiStepTask
|
||||||
|
}
|
||||||
|
|
||||||
export interface IRawPlanResponse {
|
export interface IRawPlanResponse {
|
||||||
'Initial Input Object'?: string[] | string
|
'Initial Input Object'?: string[] | string
|
||||||
'General Goal'?: string
|
'General Goal'?: string
|
||||||
@@ -64,19 +100,110 @@ function clearStorageByVersion() {
|
|||||||
|
|
||||||
export const useAgentsStore = defineStore('agents', () => {
|
export const useAgentsStore = defineStore('agents', () => {
|
||||||
const configStore = useConfigStore()
|
const configStore = useConfigStore()
|
||||||
|
|
||||||
const agents = useStorage<Agent[]>(`${storageKey}-repository`, [])
|
const agents = useStorage<Agent[]>(`${storageKey}-repository`, [])
|
||||||
function setAgents(agent: Agent[]) {
|
function setAgents(agent: Agent[]) {
|
||||||
agents.value = agent
|
agents.value = agent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 智能体评分数据存储
|
||||||
|
const IAgentSelectModifyAddRequest = useStorage<IAgentSelectModifyAddRequest | null>(
|
||||||
|
`${storageKey}-score-data`,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
|
||||||
|
// 设置智能体评分数据
|
||||||
|
function setAgentScoreData(data: IAgentSelectModifyAddRequest) {
|
||||||
|
IAgentSelectModifyAddRequest.value = data
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加新维度的评分数据(追加模式)
|
||||||
|
function addAgentScoreAspect(aspectName: string, scores: Record<string, IScoreItem>) {
|
||||||
|
if (!IAgentSelectModifyAddRequest.value) {
|
||||||
|
IAgentSelectModifyAddRequest.value = {
|
||||||
|
aspectList: [],
|
||||||
|
agentScores: {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查维度是否已存在
|
||||||
|
if (!IAgentSelectModifyAddRequest.value.aspectList.includes(aspectName)) {
|
||||||
|
IAgentSelectModifyAddRequest.value.aspectList.push(aspectName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加该维度的评分数据
|
||||||
|
// scores: { agentName: { score, reason } }
|
||||||
|
// 转换为 agentScores[agentName][aspectName] = { score, reason }
|
||||||
|
for (const [agentName, scoreItem] of Object.entries(scores)) {
|
||||||
|
if (!IAgentSelectModifyAddRequest.value.agentScores[agentName]) {
|
||||||
|
IAgentSelectModifyAddRequest.value.agentScores[agentName] = {}
|
||||||
|
}
|
||||||
|
IAgentSelectModifyAddRequest.value.agentScores[agentName][aspectName] = scoreItem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清除智能体评分数据
|
||||||
|
function clearAgentScoreData() {
|
||||||
|
IAgentSelectModifyAddRequest.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 智能体分配确认的组合列表存储 - 按任务ID分别存储(不持久化到localStorage)
|
||||||
|
const confirmedAgentGroupsMap = ref<Map<string, string[][]>>(new Map())
|
||||||
|
|
||||||
|
// 获取指定任务的确认的agent组合列表
|
||||||
|
function getConfirmedAgentGroups(taskId: string): string[][] {
|
||||||
|
return confirmedAgentGroupsMap.value.get(taskId) || []
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加确认的agent组合到指定任务
|
||||||
|
function addConfirmedAgentGroup(taskId: string, group: string[]) {
|
||||||
|
const groups = confirmedAgentGroupsMap.value.get(taskId) || []
|
||||||
|
groups.push(group)
|
||||||
|
confirmedAgentGroupsMap.value.set(taskId, groups)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清除指定任务的确认的agent组合列表
|
||||||
|
function clearConfirmedAgentGroups(taskId: string) {
|
||||||
|
confirmedAgentGroupsMap.value.delete(taskId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清除所有任务的确认的agent组合列表
|
||||||
|
function clearAllConfirmedAgentGroups() {
|
||||||
|
confirmedAgentGroupsMap.value.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
const planModificationWindow = ref(false)
|
||||||
|
const planTaskWindow = ref(false)
|
||||||
|
const agentAllocationDialog = ref(false)
|
||||||
|
|
||||||
|
function openPlanModification() {
|
||||||
|
planModificationWindow.value = true
|
||||||
|
}
|
||||||
|
function closePlanModification() {
|
||||||
|
planModificationWindow.value = false
|
||||||
|
}
|
||||||
|
function openPlanTask() {
|
||||||
|
planTaskWindow.value = true
|
||||||
|
}
|
||||||
|
function closePlanTask() {
|
||||||
|
planTaskWindow.value = false
|
||||||
|
}
|
||||||
|
function openAgentAllocationDialog() {
|
||||||
|
agentAllocationDialog.value = true
|
||||||
|
}
|
||||||
|
function closeAgentAllocationDialog() {
|
||||||
|
agentAllocationDialog.value = false
|
||||||
|
}
|
||||||
|
|
||||||
// 任务搜索的内容
|
// 任务搜索的内容
|
||||||
const searchValue = useStorage<string>(`${storageKey}-search-value`, '')
|
const searchValue = useStorage<string>(`${storageKey}-search-value`, '')
|
||||||
function setSearchValue(value: string) {
|
function setSearchValue(value: string) {
|
||||||
searchValue.value = value
|
searchValue.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
const storageVersionIdentifier = useStorage<string>(`${storageKey}-storage-version-identifier`, '')
|
const storageVersionIdentifier = useStorage<string>(
|
||||||
|
`${storageKey}-storage-version-identifier`,
|
||||||
|
'',
|
||||||
|
)
|
||||||
// 监听 configStore.config.agentRepository.storageVersionIdentifier 改变
|
// 监听 configStore.config.agentRepository.storageVersionIdentifier 改变
|
||||||
watch(
|
watch(
|
||||||
() => configStore.config.agentRepository.storageVersionIdentifier,
|
() => configStore.config.agentRepository.storageVersionIdentifier,
|
||||||
@@ -89,13 +216,119 @@ export const useAgentsStore = defineStore('agents', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
immediate: true,
|
immediate: true,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// 当前的展示的任务流程
|
// 当前的展示的任务流程
|
||||||
const currentTask = ref<IRawStepTask>()
|
const currentTask = ref<IRawStepTask>()
|
||||||
function setCurrentTask(task: IRawStepTask) {
|
function setCurrentTask(task: IRawStepTask) {
|
||||||
|
const existingTask = currentTask.value
|
||||||
|
|
||||||
|
// 🆕 智能判断:如果是同一个任务,保留用户修改过的数据(AgentSelection、TaskProcess、Collaboration_Brief_frontEnd)
|
||||||
|
if (existingTask && existingTask.Id === task.Id) {
|
||||||
|
currentTask.value = {
|
||||||
|
...task,
|
||||||
|
AgentSelection: existingTask.AgentSelection || task.AgentSelection,
|
||||||
|
TaskProcess: existingTask.TaskProcess || task.TaskProcess,
|
||||||
|
Collaboration_Brief_frontEnd:
|
||||||
|
existingTask.Collaboration_Brief_frontEnd || task.Collaboration_Brief_frontEnd,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🆕 同步更新主流程数据(让执行结果卡片和任务大纲都能联动)
|
||||||
|
syncCurrentTaskToMainProcess(currentTask.value)
|
||||||
|
|
||||||
|
console.log('🔄 setCurrentTask: 保留同一任务的分支数据', {
|
||||||
|
taskId: task.Id,
|
||||||
|
taskName: task.StepName,
|
||||||
|
preservedAgentSelection: currentTask.value.AgentSelection,
|
||||||
|
preservedTaskProcessLength: currentTask.value.TaskProcess?.length || 0,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 不同任务:使用新任务数据
|
||||||
currentTask.value = task
|
currentTask.value = task
|
||||||
|
console.log('🔄 setCurrentTask: 切换到不同任务', {
|
||||||
|
taskId: task.Id,
|
||||||
|
taskName: task.StepName,
|
||||||
|
agentSelection: task.AgentSelection,
|
||||||
|
taskProcessLength: task.TaskProcess?.length || 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🆕 同步 currentTask 到主流程数据
|
||||||
|
function syncCurrentTaskToMainProcess(updatedTask: IRawStepTask) {
|
||||||
|
const collaborationProcess = agentRawPlan.value.data?.['Collaboration Process']
|
||||||
|
if (!collaborationProcess) {
|
||||||
|
console.warn('⚠️ syncCurrentTaskToMainProcess: collaborationProcess 不存在')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const taskIndex = collaborationProcess.findIndex((t) => t.Id === updatedTask.Id)
|
||||||
|
if (taskIndex === -1) {
|
||||||
|
console.warn('⚠️ syncCurrentTaskToMainProcess: 未找到对应任务', updatedTask.Id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用 splice 确保 Vue 响应式更新
|
||||||
|
collaborationProcess.splice(taskIndex, 1, {
|
||||||
|
...collaborationProcess[taskIndex]!,
|
||||||
|
AgentSelection: updatedTask.AgentSelection || [],
|
||||||
|
TaskProcess: updatedTask.TaskProcess || [],
|
||||||
|
Collaboration_Brief_frontEnd: updatedTask.Collaboration_Brief_frontEnd,
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('✅ syncCurrentTaskToMainProcess: 已同步到主流程', {
|
||||||
|
taskName: collaborationProcess[taskIndex]!.StepName,
|
||||||
|
agentSelection: collaborationProcess[taskIndex]!.AgentSelection,
|
||||||
|
taskProcessLength: collaborationProcess[taskIndex]!.TaskProcess?.length || 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🆕 设置当前任务的 TaskProcess 数据(用于切换分支时更新显示)
|
||||||
|
function setCurrentTaskProcess(taskProcess: TaskProcess[]) {
|
||||||
|
if (currentTask.value) {
|
||||||
|
// 创建新的对象引用,确保响应式更新
|
||||||
|
currentTask.value = {
|
||||||
|
...currentTask.value,
|
||||||
|
TaskProcess: JSON.parse(JSON.stringify(taskProcess)),
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🆕 同步到主流程数据(让执行结果卡片和任务大纲都能联动)
|
||||||
|
syncCurrentTaskToMainProcess(currentTask.value)
|
||||||
|
|
||||||
|
console.log('✅ 已更新 currentTask.TaskProcess 并同步到主流程:', {
|
||||||
|
taskId: currentTask.value.Id,
|
||||||
|
agent数量: taskProcess.length,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🆕 更新当前任务的 AgentSelection 和 TaskProcess(用于在 AgentAllocation 中切换 agent 组合)
|
||||||
|
// 此函数专门用于强制更新,不会被 setCurrentTask 的"智能保留"逻辑阻止
|
||||||
|
function updateCurrentAgentSelection(
|
||||||
|
agentSelection: string[],
|
||||||
|
taskProcess: TaskProcess[],
|
||||||
|
collaborationBrief: any,
|
||||||
|
) {
|
||||||
|
if (currentTask.value) {
|
||||||
|
// 直接更新 currentTask,不保留旧数据
|
||||||
|
currentTask.value = {
|
||||||
|
...currentTask.value,
|
||||||
|
AgentSelection: agentSelection,
|
||||||
|
TaskProcess: taskProcess,
|
||||||
|
Collaboration_Brief_frontEnd: collaborationBrief,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 同步到主流程数据
|
||||||
|
syncCurrentTaskToMainProcess(currentTask.value)
|
||||||
|
|
||||||
|
console.log('✅ 已强制更新 currentTask 的 AgentSelection 并同步到主流程:', {
|
||||||
|
taskId: currentTask.value.Id,
|
||||||
|
taskName: currentTask.value.StepName,
|
||||||
|
newAgentSelection: agentSelection,
|
||||||
|
taskProcessLength: taskProcess.length,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const agentRawPlan = ref<{ data?: IRawPlanResponse; loading?: boolean }>({ loading: false })
|
const agentRawPlan = ref<{ data?: IRawPlanResponse; loading?: boolean }>({ loading: false })
|
||||||
@@ -104,7 +337,8 @@ export const useAgentsStore = defineStore('agents', () => {
|
|||||||
if (plan.data) {
|
if (plan.data) {
|
||||||
plan.data['Collaboration Process'] = plan.data['Collaboration Process']?.map((item) => ({
|
plan.data['Collaboration Process'] = plan.data['Collaboration Process']?.map((item) => ({
|
||||||
...item,
|
...item,
|
||||||
Id: uuidv4(),
|
// ✅ 只在任务没有 Id 时才生成新的 ID,保持已存在任务的 Id 不变
|
||||||
|
Id: item.Id || uuidv4(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
agentRawPlan.value = {
|
agentRawPlan.value = {
|
||||||
@@ -127,6 +361,60 @@ export const useAgentsStore = defineStore('agents', () => {
|
|||||||
executePlan.value = []
|
executePlan.value = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 额外的产物列表
|
||||||
|
const additionalOutputs = ref<string[]>([])
|
||||||
|
|
||||||
|
// 用户在 Assignment 中选择的 agent 组合(按任务ID分别存储)
|
||||||
|
const selectedAgentGroupMap = ref<Map<string, string[]>>(new Map())
|
||||||
|
|
||||||
|
// 设置指定任务的选择的 agent 组合
|
||||||
|
function setSelectedAgentGroup(taskId: string, agents: string[]) {
|
||||||
|
selectedAgentGroupMap.value.set(taskId, agents)
|
||||||
|
console.log('💾 保存任务选择的 agent 组合:', { taskId, agents })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取指定任务的选择的 agent 组合
|
||||||
|
function getSelectedAgentGroup(taskId: string): string[] | undefined {
|
||||||
|
return selectedAgentGroupMap.value.get(taskId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清除指定任务的选择的 agent 组合
|
||||||
|
function clearSelectedAgentGroup(taskId: string) {
|
||||||
|
selectedAgentGroupMap.value.delete(taskId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清除所有任务的选择的 agent 组合
|
||||||
|
function clearAllSelectedAgentGroups() {
|
||||||
|
selectedAgentGroupMap.value.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加新产物
|
||||||
|
function addNewOutput(outputObject: string) {
|
||||||
|
if (!outputObject.trim()) return false
|
||||||
|
|
||||||
|
const trimmedOutput = outputObject.trim()
|
||||||
|
if (!additionalOutputs.value.includes(trimmedOutput)) {
|
||||||
|
additionalOutputs.value.unshift(trimmedOutput)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除额外产物
|
||||||
|
function removeAdditionalOutput(outputObject: string) {
|
||||||
|
const index = additionalOutputs.value.indexOf(outputObject)
|
||||||
|
if (index > -1) {
|
||||||
|
additionalOutputs.value.splice(index, 1)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清除额外产物
|
||||||
|
function clearAdditionalOutputs() {
|
||||||
|
additionalOutputs.value = []
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
agents,
|
agents,
|
||||||
setAgents,
|
setAgents,
|
||||||
@@ -134,11 +422,44 @@ export const useAgentsStore = defineStore('agents', () => {
|
|||||||
setSearchValue,
|
setSearchValue,
|
||||||
currentTask,
|
currentTask,
|
||||||
setCurrentTask,
|
setCurrentTask,
|
||||||
|
setCurrentTaskProcess, // 🆕 设置当前任务的 TaskProcess
|
||||||
|
updateCurrentAgentSelection, // 🆕 强制更新 AgentSelection(用于 AgentAllocation 切换组合)
|
||||||
|
syncCurrentTaskToMainProcess, // 🆕 同步 currentTask 到主流程
|
||||||
agentRawPlan,
|
agentRawPlan,
|
||||||
setAgentRawPlan,
|
setAgentRawPlan,
|
||||||
executePlan,
|
executePlan,
|
||||||
setExecutePlan,
|
setExecutePlan,
|
||||||
resetAgent,
|
resetAgent,
|
||||||
|
additionalOutputs,
|
||||||
|
addNewOutput,
|
||||||
|
removeAdditionalOutput,
|
||||||
|
clearAdditionalOutputs,
|
||||||
|
// 用户选择的 agent 组合
|
||||||
|
selectedAgentGroupMap,
|
||||||
|
setSelectedAgentGroup,
|
||||||
|
getSelectedAgentGroup,
|
||||||
|
clearSelectedAgentGroup,
|
||||||
|
clearAllSelectedAgentGroups,
|
||||||
|
planModificationWindow,
|
||||||
|
openPlanModification,
|
||||||
|
closePlanModification,
|
||||||
|
planTaskWindow,
|
||||||
|
openPlanTask,
|
||||||
|
closePlanTask,
|
||||||
|
agentAllocationDialog,
|
||||||
|
openAgentAllocationDialog,
|
||||||
|
closeAgentAllocationDialog,
|
||||||
|
// 智能体评分数据
|
||||||
|
IAgentSelectModifyAddRequest,
|
||||||
|
setAgentScoreData,
|
||||||
|
addAgentScoreAspect,
|
||||||
|
clearAgentScoreData,
|
||||||
|
// 确认的agent组合列表(按任务ID分别存储)
|
||||||
|
confirmedAgentGroupsMap,
|
||||||
|
getConfirmedAgentGroups,
|
||||||
|
addConfirmedAgentGroup,
|
||||||
|
clearConfirmedAgentGroups,
|
||||||
|
clearAllConfirmedAgentGroups,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
import { ref } from 'vue'
|
||||||
import { readConfig } from '@/utils/readJson.ts'
|
import { readConfig } from '@/utils/readJson.ts'
|
||||||
import { store } from '@/stores'
|
import { store } from '@/stores'
|
||||||
|
|
||||||
@@ -10,15 +11,25 @@ export interface Config {
|
|||||||
agentRepository: {
|
agentRepository: {
|
||||||
storageVersionIdentifier: string
|
storageVersionIdentifier: string
|
||||||
}
|
}
|
||||||
|
// 是否是开发环境
|
||||||
|
dev?: boolean
|
||||||
|
// api base url
|
||||||
|
apiBaseUrl?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const defaultConfig: Config = {
|
||||||
|
apiBaseUrl: `${import.meta.env.BASE_URL || '/'}api`
|
||||||
|
} as Config
|
||||||
export const useConfigStore = defineStore('config', () => {
|
export const useConfigStore = defineStore('config', () => {
|
||||||
const config = ref<Config>({} as Config)
|
const config = ref<Config>({} as Config)
|
||||||
|
|
||||||
// 异步调用readConfig
|
// 异步调用readConfig
|
||||||
async function initConfig() {
|
async function initConfig() {
|
||||||
config.value = await readConfig<Config>('config.json')
|
const data = await readConfig<Config>('config.json')
|
||||||
|
config.value = {
|
||||||
|
...defaultConfig,
|
||||||
|
...data
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
463
frontend/src/stores/modules/selection.ts
Normal file
@@ -0,0 +1,463 @@
|
|||||||
|
import { ref } from 'vue'
|
||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
|
import { store } from '../index'
|
||||||
|
import type { IRawStepTask, IApiStepTask } from './agents'
|
||||||
|
import type { Node, Edge } from '@vue-flow/core'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分支数据接口
|
||||||
|
* @description 用于存储流程图中的分支节点和边数据
|
||||||
|
*/
|
||||||
|
export interface IBranchData {
|
||||||
|
/** 分支唯一 ID */
|
||||||
|
id: string
|
||||||
|
/** 父节点 ID(根节点或任务节点) */
|
||||||
|
parentNodeId: string
|
||||||
|
/** 分支需求内容 */
|
||||||
|
branchContent: string
|
||||||
|
/** 分支类型 */
|
||||||
|
branchType: 'root' | 'task'
|
||||||
|
/** 分支包含的所有节点 */
|
||||||
|
nodes: Node[]
|
||||||
|
/** 分支包含的所有边 */
|
||||||
|
edges: Edge[]
|
||||||
|
/** 分支的任务数据 */
|
||||||
|
tasks: IRawStepTask[]
|
||||||
|
/** 创建时间 */
|
||||||
|
createdAt: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useSelectionStore = defineStore('selection', () => {
|
||||||
|
// ==================== 任务大纲探索分支数据存储 ====================
|
||||||
|
|
||||||
|
/** 流程图分支列表 */
|
||||||
|
const flowBranches = ref<IBranchData[]>([])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加流程图分支
|
||||||
|
* @param data 分支数据
|
||||||
|
* @returns 分支 ID
|
||||||
|
*/
|
||||||
|
function addFlowBranch(data: {
|
||||||
|
parentNodeId: string
|
||||||
|
branchContent: string
|
||||||
|
branchType: 'root' | 'task'
|
||||||
|
nodes: Node[]
|
||||||
|
edges: Edge[]
|
||||||
|
tasks: IRawStepTask[]
|
||||||
|
}): string {
|
||||||
|
const branchId = `flow-branch-${uuidv4()}`
|
||||||
|
const newBranch: IBranchData = {
|
||||||
|
id: branchId,
|
||||||
|
parentNodeId: data.parentNodeId,
|
||||||
|
branchContent: data.branchContent,
|
||||||
|
branchType: data.branchType,
|
||||||
|
nodes: data.nodes,
|
||||||
|
edges: data.edges,
|
||||||
|
tasks: data.tasks,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
}
|
||||||
|
flowBranches.value.push(newBranch)
|
||||||
|
return branchId
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有流程图分支
|
||||||
|
* @returns 所有流程图分支数据
|
||||||
|
*/
|
||||||
|
function getAllFlowBranches(): IBranchData[] {
|
||||||
|
return flowBranches.value
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据父节点 ID 获取流程图分支
|
||||||
|
* @param parentNodeId 父节点 ID
|
||||||
|
* @returns 匹配的流程图分支列表
|
||||||
|
*/
|
||||||
|
function getFlowBranchesByParent(parentNodeId: string): IBranchData[] {
|
||||||
|
return flowBranches.value.filter((branch) => branch.parentNodeId === parentNodeId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除流程图分支
|
||||||
|
* @param branchId 分支 ID
|
||||||
|
* @returns 是否删除成功
|
||||||
|
*/
|
||||||
|
function removeFlowBranch(branchId: string): boolean {
|
||||||
|
const index = flowBranches.value.findIndex((branch) => branch.id === branchId)
|
||||||
|
if (index > -1) {
|
||||||
|
flowBranches.value.splice(index, 1)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 清除所有流程图分支 */
|
||||||
|
function clearFlowBranches() {
|
||||||
|
flowBranches.value = []
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据父节点 ID 清除流程图分支
|
||||||
|
* @param parentNodeId 父节点 ID
|
||||||
|
*/
|
||||||
|
function clearFlowBranchesByParent(parentNodeId: string) {
|
||||||
|
flowBranches.value = flowBranches.value.filter((branch) => branch.parentNodeId !== parentNodeId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 任务过程探索分支数据存储 ====================
|
||||||
|
/**
|
||||||
|
* 任务过程分支数据映射
|
||||||
|
* @description 存储结构: Map<taskStepId, Map<agentGroupKey, IBranchData[]>>
|
||||||
|
* - taskStepId: 任务步骤 ID
|
||||||
|
* - agentGroupKey: agent 组合的唯一标识(排序后的 JSON 字符串)
|
||||||
|
* - IBranchData[]: 该 agent 组合在该任务下的所有分支数据
|
||||||
|
*/
|
||||||
|
const taskProcessBranchesMap = ref<Map<string, Map<string, IBranchData[]>>>(new Map())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加任务过程分支
|
||||||
|
* @param taskStepId 任务步骤 ID
|
||||||
|
* @param agents Agent 列表
|
||||||
|
* @param data 分支数据
|
||||||
|
* @returns 分支 ID
|
||||||
|
*/
|
||||||
|
function addTaskProcessBranch(
|
||||||
|
taskStepId: string,
|
||||||
|
agents: string[],
|
||||||
|
data: {
|
||||||
|
parentNodeId: string
|
||||||
|
branchContent: string
|
||||||
|
branchType: 'root' | 'task'
|
||||||
|
nodes: Node[]
|
||||||
|
edges: Edge[]
|
||||||
|
tasks: IRawStepTask[]
|
||||||
|
},
|
||||||
|
): string {
|
||||||
|
const branchId = `task-process-branch-${uuidv4()}`
|
||||||
|
const agentGroupKey = getAgentGroupKey(agents)
|
||||||
|
|
||||||
|
const newBranch: IBranchData = {
|
||||||
|
id: branchId,
|
||||||
|
parentNodeId: data.parentNodeId,
|
||||||
|
branchContent: data.branchContent,
|
||||||
|
branchType: data.branchType,
|
||||||
|
nodes: data.nodes,
|
||||||
|
edges: data.edges,
|
||||||
|
tasks: data.tasks,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取或创建该任务步骤的 Map
|
||||||
|
if (!taskProcessBranchesMap.value.has(taskStepId)) {
|
||||||
|
taskProcessBranchesMap.value.set(taskStepId, new Map())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取或创建该 agent 组合的分支列表
|
||||||
|
const agentMap = taskProcessBranchesMap.value.get(taskStepId)!
|
||||||
|
if (!agentMap.has(agentGroupKey)) {
|
||||||
|
agentMap.set(agentGroupKey, [])
|
||||||
|
}
|
||||||
|
agentMap.get(agentGroupKey)!.push(newBranch)
|
||||||
|
|
||||||
|
return branchId
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定任务步骤和 agent 组合的所有分支
|
||||||
|
* @param taskStepId 任务步骤 ID
|
||||||
|
* @param agents Agent 列表
|
||||||
|
* @returns 匹配的任务过程分支列表
|
||||||
|
*/
|
||||||
|
function getTaskProcessBranches(taskStepId: string, agents: string[]): IBranchData[] {
|
||||||
|
const agentGroupKey = getAgentGroupKey(agents)
|
||||||
|
return taskProcessBranchesMap.value.get(taskStepId)?.get(agentGroupKey) || []
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有任务过程分支
|
||||||
|
* @returns 所有任务过程分支数据映射
|
||||||
|
*/
|
||||||
|
function getAllTaskProcessBranches(): Map<string, Map<string, IBranchData[]>> {
|
||||||
|
return taskProcessBranchesMap.value
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据父节点 ID 获取任务过程分支
|
||||||
|
* @param taskStepId 任务步骤 ID
|
||||||
|
* @param agents Agent 列表
|
||||||
|
* @param parentNodeId 父节点 ID
|
||||||
|
* @returns 匹配的任务过程分支列表
|
||||||
|
*/
|
||||||
|
function getTaskProcessBranchesByParent(
|
||||||
|
taskStepId: string,
|
||||||
|
agents: string[],
|
||||||
|
parentNodeId: string,
|
||||||
|
): IBranchData[] {
|
||||||
|
const agentGroupKey = getAgentGroupKey(agents)
|
||||||
|
const branches = taskProcessBranchesMap.value.get(taskStepId)?.get(agentGroupKey) || []
|
||||||
|
return branches.filter((branch) => branch.parentNodeId === parentNodeId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除任务过程分支
|
||||||
|
* @param taskStepId 任务步骤 ID
|
||||||
|
* @param agents Agent 列表
|
||||||
|
* @param branchId 分支 ID
|
||||||
|
* @returns 是否删除成功
|
||||||
|
*/
|
||||||
|
function removeTaskProcessBranch(
|
||||||
|
taskStepId: string,
|
||||||
|
agents: string[],
|
||||||
|
branchId: string,
|
||||||
|
): boolean {
|
||||||
|
const agentGroupKey = getAgentGroupKey(agents)
|
||||||
|
const branches = taskProcessBranchesMap.value.get(taskStepId)?.get(agentGroupKey)
|
||||||
|
if (branches) {
|
||||||
|
const index = branches.findIndex((branch) => branch.id === branchId)
|
||||||
|
if (index > -1) {
|
||||||
|
branches.splice(index, 1)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除指定任务步骤和 agent 组合的所有分支
|
||||||
|
* @param taskStepId 任务步骤 ID
|
||||||
|
* @param agents Agent 列表(可选,不传则清除该任务步骤的所有分支)
|
||||||
|
*/
|
||||||
|
function clearTaskProcessBranches(taskStepId: string, agents?: string[]) {
|
||||||
|
if (agents) {
|
||||||
|
// 清除指定 agent 组合的分支
|
||||||
|
const agentGroupKey = getAgentGroupKey(agents)
|
||||||
|
const agentMap = taskProcessBranchesMap.value.get(taskStepId)
|
||||||
|
if (agentMap) {
|
||||||
|
agentMap.delete(agentGroupKey)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 清除该任务步骤的所有分支(所有 agent 组合)
|
||||||
|
taskProcessBranchesMap.value.delete(taskStepId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== Agent 组合 TaskProcess 数据存储 ====================
|
||||||
|
/**
|
||||||
|
* Agent 组合 TaskProcess 数据映射
|
||||||
|
* @description 用于存储 fill_stepTask_TaskProcess 接口返回的数据
|
||||||
|
* 存储结构: Map<taskId, Map<agentGroupKey, IApiStepTask>>
|
||||||
|
* - taskId: 任务 ID
|
||||||
|
* - agentGroupKey: agent 组合的唯一标识(排序后的 JSON 字符串)
|
||||||
|
* - IApiStepTask: 该 agent 组合的完整 TaskProcess 数据
|
||||||
|
*/
|
||||||
|
const agentTaskProcessMap = ref<Map<string, Map<string, IApiStepTask>>>(new Map())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成 agent 组合的唯一 key
|
||||||
|
* @description 排序后保证一致性
|
||||||
|
* @param agents Agent 列表
|
||||||
|
* @returns Agent 组合的唯一标识
|
||||||
|
*/
|
||||||
|
function getAgentGroupKey(agents: string[]): string {
|
||||||
|
// 处理 undefined 或 null 的情况
|
||||||
|
if (!agents || !Array.isArray(agents)) {
|
||||||
|
return JSON.stringify([])
|
||||||
|
}
|
||||||
|
return JSON.stringify([...agents].sort())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存储 agent 组合的 TaskProcess 数据
|
||||||
|
* @param taskId 任务 ID
|
||||||
|
* @param agents Agent 列表
|
||||||
|
* @param taskProcess TaskProcess 数据
|
||||||
|
*/
|
||||||
|
function setAgentTaskProcess(taskId: string, agents: string[], taskProcess: IApiStepTask) {
|
||||||
|
const groupKey = getAgentGroupKey(agents)
|
||||||
|
|
||||||
|
// 获取或创建该任务的 Map
|
||||||
|
if (!agentTaskProcessMap.value.has(taskId)) {
|
||||||
|
agentTaskProcessMap.value.set(taskId, new Map())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 存储该 agent 组合的 TaskProcess 数据
|
||||||
|
agentTaskProcessMap.value.get(taskId)!.set(groupKey, taskProcess)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 agent 组合的 TaskProcess 数据
|
||||||
|
* @param taskId 任务 ID
|
||||||
|
* @param agents Agent 列表
|
||||||
|
* @returns TaskProcess 数据
|
||||||
|
*/
|
||||||
|
function getAgentTaskProcess(taskId: string, agents: string[]): IApiStepTask | undefined {
|
||||||
|
const groupKey = getAgentGroupKey(agents)
|
||||||
|
return agentTaskProcessMap.value.get(taskId)?.get(groupKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查 agent 组合是否已有 TaskProcess 数据
|
||||||
|
* @param taskId 任务 ID
|
||||||
|
* @param agents Agent 列表
|
||||||
|
* @returns 是否存在数据
|
||||||
|
*/
|
||||||
|
function hasAgentTaskProcess(taskId: string, agents: string[]): boolean {
|
||||||
|
const groupKey = getAgentGroupKey(agents)
|
||||||
|
return agentTaskProcessMap.value.get(taskId)?.has(groupKey) || false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除指定任务的所有 agent 组合 TaskProcess 数据
|
||||||
|
* @param taskId 任务 ID
|
||||||
|
*/
|
||||||
|
function clearAgentTaskProcess(taskId: string) {
|
||||||
|
agentTaskProcessMap.value.delete(taskId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 清除所有任务的 agent 组合 TaskProcess 数据 */
|
||||||
|
function clearAllAgentTaskProcess() {
|
||||||
|
agentTaskProcessMap.value.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 当前生效的任务过程分支 ====================
|
||||||
|
/**
|
||||||
|
* 当前生效的任务过程分支映射
|
||||||
|
* @description 记录每个任务步骤和 agent 组合当前生效的分支 ID(持久化选中状态)
|
||||||
|
* 存储结构: Map<taskStepId, Map<agentGroupKey, branchId>>
|
||||||
|
* - taskStepId: 任务步骤 ID
|
||||||
|
* - agentGroupKey: agent 组合的唯一标识
|
||||||
|
* - branchId: 当前选中的分支 ID
|
||||||
|
*/
|
||||||
|
const activeTaskProcessBranchMap = ref<Map<string, Map<string, string>>>(new Map())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前生效的 TaskProcess 数据映射
|
||||||
|
* @description 用于外部组件显示职责分配
|
||||||
|
* 存储结构: Map<taskStepId, Map<agentGroupKey, TaskProcess[]>>
|
||||||
|
*/
|
||||||
|
const activeTaskProcessDataMap = ref<Map<string, Map<string, any[]>>>(new Map())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置当前生效的分支
|
||||||
|
* @param taskStepId 任务步骤 ID
|
||||||
|
* @param agents Agent 列表
|
||||||
|
* @param branchId 分支 ID
|
||||||
|
*/
|
||||||
|
function setActiveTaskProcessBranch(taskStepId: string, agents: string[], branchId: string) {
|
||||||
|
const agentGroupKey = getAgentGroupKey(agents)
|
||||||
|
|
||||||
|
// 获取或创建该任务步骤的 Map
|
||||||
|
if (!activeTaskProcessBranchMap.value.has(taskStepId)) {
|
||||||
|
activeTaskProcessBranchMap.value.set(taskStepId, new Map())
|
||||||
|
}
|
||||||
|
|
||||||
|
activeTaskProcessBranchMap.value.get(taskStepId)!.set(agentGroupKey, branchId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置当前生效的 TaskProcess 数据
|
||||||
|
* @param taskStepId 任务步骤 ID
|
||||||
|
* @param agents Agent 列表
|
||||||
|
* @param taskProcess TaskProcess 数据
|
||||||
|
*/
|
||||||
|
function setActiveTaskProcessData(taskStepId: string, agents: string[], taskProcess: any[]) {
|
||||||
|
const agentGroupKey = getAgentGroupKey(agents)
|
||||||
|
|
||||||
|
// 获取或创建该任务步骤的 Map
|
||||||
|
if (!activeTaskProcessDataMap.value.has(taskStepId)) {
|
||||||
|
activeTaskProcessDataMap.value.set(taskStepId, new Map())
|
||||||
|
}
|
||||||
|
|
||||||
|
activeTaskProcessDataMap.value.get(taskStepId)!.set(agentGroupKey, taskProcess)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前生效的分支 ID
|
||||||
|
* @param taskStepId 任务步骤 ID
|
||||||
|
* @param agents Agent 列表
|
||||||
|
* @returns 分支 ID
|
||||||
|
*/
|
||||||
|
function getActiveTaskProcessBranch(taskStepId: string, agents: string[]): string | undefined {
|
||||||
|
const agentGroupKey = getAgentGroupKey(agents)
|
||||||
|
return activeTaskProcessBranchMap.value.get(taskStepId)?.get(agentGroupKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前生效的 TaskProcess 数据
|
||||||
|
* @param taskStepId 任务步骤 ID
|
||||||
|
* @param agents Agent 列表
|
||||||
|
* @returns TaskProcess 数据
|
||||||
|
*/
|
||||||
|
function getActiveTaskProcessData(taskStepId: string, agents: string[]): any[] | undefined {
|
||||||
|
const agentGroupKey = getAgentGroupKey(agents)
|
||||||
|
return activeTaskProcessDataMap.value.get(taskStepId)?.get(agentGroupKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除生效分支
|
||||||
|
* @param taskStepId 任务步骤 ID
|
||||||
|
* @param agents Agent 列表(可选,不传则清除该任务步骤的所有生效分支)
|
||||||
|
*/
|
||||||
|
function clearActiveTaskProcessBranch(taskStepId: string, agents?: string[]) {
|
||||||
|
if (agents) {
|
||||||
|
// 清除指定 agent 组合的生效分支
|
||||||
|
const agentGroupKey = getAgentGroupKey(agents)
|
||||||
|
activeTaskProcessBranchMap.value.get(taskStepId)?.delete(agentGroupKey)
|
||||||
|
activeTaskProcessDataMap.value.get(taskStepId)?.delete(agentGroupKey)
|
||||||
|
} else {
|
||||||
|
// 清除该任务步骤的所有生效分支(所有 agent 组合)
|
||||||
|
activeTaskProcessBranchMap.value.delete(taskStepId)
|
||||||
|
activeTaskProcessDataMap.value.delete(taskStepId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
// ==================== 状态 ====================
|
||||||
|
flowBranches,
|
||||||
|
taskProcessBranchesMap,
|
||||||
|
agentTaskProcessMap,
|
||||||
|
activeTaskProcessBranchMap,
|
||||||
|
activeTaskProcessDataMap,
|
||||||
|
|
||||||
|
// ==================== 任务大纲分支管理方法 ====================
|
||||||
|
addFlowBranch,
|
||||||
|
getAllFlowBranches,
|
||||||
|
getFlowBranchesByParent,
|
||||||
|
removeFlowBranch,
|
||||||
|
clearFlowBranches,
|
||||||
|
clearFlowBranchesByParent,
|
||||||
|
|
||||||
|
// ==================== 任务过程分支管理方法 ====================
|
||||||
|
addTaskProcessBranch,
|
||||||
|
getTaskProcessBranches,
|
||||||
|
getAllTaskProcessBranches,
|
||||||
|
getTaskProcessBranchesByParent,
|
||||||
|
removeTaskProcessBranch,
|
||||||
|
clearTaskProcessBranches,
|
||||||
|
|
||||||
|
// ==================== 任务过程分支生效状态管理方法 ====================
|
||||||
|
setActiveTaskProcessBranch,
|
||||||
|
setActiveTaskProcessData,
|
||||||
|
getActiveTaskProcessBranch,
|
||||||
|
getActiveTaskProcessData,
|
||||||
|
clearActiveTaskProcessBranch,
|
||||||
|
|
||||||
|
// ==================== Agent 组合 TaskProcess 数据管理方法 ====================
|
||||||
|
getAgentGroupKey,
|
||||||
|
setAgentTaskProcess,
|
||||||
|
getAgentTaskProcess,
|
||||||
|
hasAgentTaskProcess,
|
||||||
|
clearAgentTaskProcess,
|
||||||
|
clearAllAgentTaskProcess,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于在组件外部使用 Selection Store
|
||||||
|
* @returns Selection Store 实例
|
||||||
|
*/
|
||||||
|
export function useSelectionStoreHook() {
|
||||||
|
return useSelectionStore(store)
|
||||||
|
}
|
||||||
@@ -1,16 +1,222 @@
|
|||||||
|
// 浅色模式
|
||||||
:root {
|
:root {
|
||||||
--color-bg: #fff;
|
--color-bg: #ffffff;
|
||||||
--color-bg-secondary: #fafafa;
|
--color-bg-secondary: #ffffff;
|
||||||
--color-bg-tertiary: #f5f5f5;
|
--color-bg-tertiary: #f0f0f0; //正确
|
||||||
--color-text: #000;
|
--color-bg-quaternary: rgba(0, 0, 0, 11%);
|
||||||
|
--color-bg-quinary: #f7f7f7;
|
||||||
|
--color-text: #36404F;
|
||||||
|
// 标题文字颜色
|
||||||
|
--color-text-title-header: #262626;
|
||||||
|
--color-text-secondary: #4f4f4f;
|
||||||
|
--color-border-default: #e0e0e0;
|
||||||
|
--color-accent: #222222;
|
||||||
|
--color-accent-secondary: #315ab4;
|
||||||
|
// 鼠标悬浮背景色
|
||||||
|
--color-bg-hover: #f0f0f0;
|
||||||
|
// 鼠标悬浮字体颜色
|
||||||
|
--color-text-hover: #222222;
|
||||||
|
// 分割线颜色
|
||||||
|
--color-border: #D8D8d8;
|
||||||
|
// 内容区鼠标悬浮颜色
|
||||||
|
--color-bg-content-hover: #f0f0f0;
|
||||||
|
//列表背景颜色
|
||||||
|
--color-bg-list: #E1E1E1;
|
||||||
|
// 详情卡文字内容
|
||||||
|
--color-text-detail: #4f4f4f;
|
||||||
|
// 详情卡背景颜色
|
||||||
|
--color-bg-detail: #f7f7f7;
|
||||||
|
// 任务栏背景颜色
|
||||||
|
--color-bg-taskbar: #ffffff;
|
||||||
|
// 详情列表背景颜色
|
||||||
|
--color-bg-detail-list: rgba(230, 230, 230, 0.6) ;//#E6E6E6 60%
|
||||||
|
// 详情列表运行后背景颜色
|
||||||
|
--color-bg-detail-list-run: #e6e6e6; //运行后背景色#e6e6e6 100%
|
||||||
|
// 旋转图标背景颜色
|
||||||
|
--color-bg-icon-rotate: #a7a7a7;
|
||||||
|
// 智能体列表背景颜色
|
||||||
|
--color-agent-list-bg: #ededed; //背景色#fbfcff
|
||||||
|
// 智能体边框颜色
|
||||||
|
--color-agent-list-border: #E0E0E070; //边框#FFFFFF60 60%
|
||||||
|
//智能体鼠标悬浮颜色
|
||||||
|
--color-agent-list-hover-bg: #f7f7f7; //hover背景色#FFFFFF 100%
|
||||||
|
// 智能体鼠标悬浮边框颜色
|
||||||
|
--color-agent-list-hover-border: #e0e0e0; //hover边框#E0E0E0 100%
|
||||||
|
//智能体鼠标悬浮阴影
|
||||||
|
--color-agent-list-hover-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.2); //hover阴影 #FFFFFF 100%
|
||||||
|
//任务框下拉阴影
|
||||||
|
--color-task-shadow: 0px 10px 10px 5px rgba(0, 0, 0, 0.2);
|
||||||
|
// 卡片边框颜色
|
||||||
|
--color-card-border: #ececec;
|
||||||
|
// 卡片鼠标悬浮边框颜色
|
||||||
|
--color-card-border-hover: #ececec;
|
||||||
|
// 任务大纲卡片颜色
|
||||||
|
--color-card-bg-task: #ededed;
|
||||||
|
// 任务大纲卡片悬浮颜色
|
||||||
|
--color-card-bg-task-hover: #f7f7f7;
|
||||||
|
// 任务大纲卡片悬浮阴影效果
|
||||||
|
--color-card-shadow-hover: 0px 2px 4px 0px rgba(0, 0, 0, 0.2);
|
||||||
|
// 卡片阴影效果
|
||||||
|
--color-card-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.17);
|
||||||
|
// 任务大纲边框颜色
|
||||||
|
--color-card-border-task: #E0E0E0; //边框颜色#FFFFFF60
|
||||||
|
// 执行结果卡片颜色
|
||||||
|
--color-card-bg-result: #ececec;
|
||||||
|
// 执行结果边框颜色
|
||||||
|
--color-card-border-result: #ececec;
|
||||||
|
// 执行结果卡片悬浮颜色
|
||||||
|
--color-card-bg-result-hover: #ededed;
|
||||||
|
// 头部背景颜色
|
||||||
|
--color-header-bg: #ffffff;
|
||||||
|
// 头部字体颜色
|
||||||
|
--color-text-title: #2a3342;
|
||||||
|
//结果卡背景颜色
|
||||||
|
--color-bg-result: #eaedee;
|
||||||
|
// 任务字体背景颜色
|
||||||
|
--color-text-task: #36404F;
|
||||||
|
// 指示灯背景颜色
|
||||||
|
--color-bg-indicator: #f7f7f7;
|
||||||
|
// 三个卡片背景颜色
|
||||||
|
--color-bg-three: #fbfcff;
|
||||||
|
// 三个卡片边框颜色
|
||||||
|
--color-card-border-three: #FFFFFF60;
|
||||||
|
// 流程卡片背景颜色
|
||||||
|
--color-bg-flow: #f0f0f0;
|
||||||
|
// 三个卡片阴影
|
||||||
|
--color-card-shadow-three: 0px 0px 5px 0px rgba(0, 0, 0, 0.17);
|
||||||
|
// 头部阴影
|
||||||
|
--color-header-shadow: 0px 0px 5px 0px rgba(161, 161, 161, 0.5);
|
||||||
|
// 执行结果详情背景颜色
|
||||||
|
--color-bg-result-detail: #f7f7f7;
|
||||||
|
// 智能体库字体颜色
|
||||||
|
--color-text-agent-list: #636364;
|
||||||
|
// 智能体库字体悬浮颜色
|
||||||
|
--color-text-agent-list-hover: #222222;
|
||||||
|
// 任务栏字体颜色
|
||||||
|
--color-text-taskbar: #222222;
|
||||||
|
// 智能体卡片被选中后背景颜色
|
||||||
|
--color-agent-list-selected-bg: #f7f7f7; //选中后背景色#171b22 100%
|
||||||
|
// 结果卡片运行前颜色
|
||||||
|
--color-text-result-detail: rgba(0,0,0,0.6);
|
||||||
|
// 结果卡片运行后颜色
|
||||||
|
--color-text-result-detail-run: #000;
|
||||||
|
// 分割线
|
||||||
|
--color-border-separate: #D6D6D6;
|
||||||
|
// url颜色
|
||||||
|
--color-text-url: #FF0000;
|
||||||
|
// model颜色
|
||||||
|
--color-text-model: #0580ff;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 深色模式
|
||||||
html.dark {
|
html.dark {
|
||||||
--color-bg: #131A27;
|
--color-bg: #131A27;
|
||||||
--color-bg-secondary: #050505;
|
--color-bg-secondary: rgba(5, 5, 5, 0.4);
|
||||||
--color-bg-tertiary: #20222A;
|
--color-bg-tertiary: #20222A;
|
||||||
--color-bg-quaternary: #24252A;
|
--color-bg-quaternary: #24252A;
|
||||||
--color-bg-quinary: #29303c;
|
--color-bg-quinary: #29303c;
|
||||||
--color-text: #fff;
|
--color-text: #fff;
|
||||||
|
// 标题文字颜色
|
||||||
|
--color-text-title-header: #ffffff;
|
||||||
--color-text-secondary: #C9C9C9;
|
--color-text-secondary: #C9C9C9;
|
||||||
|
--color-border-default: #494B51;
|
||||||
|
--color-accent: #00F3FF;
|
||||||
|
--color-accent-secondary: #315ab4;
|
||||||
|
// 鼠标悬浮背景色
|
||||||
|
--color-bg-hover: #1c1e25;
|
||||||
|
// 鼠标悬浮字体颜色
|
||||||
|
--color-text-hover: #00f3ff;
|
||||||
|
// 分割线颜色
|
||||||
|
--color-border: #494B51;
|
||||||
|
// 内容区鼠标悬浮颜色
|
||||||
|
--color-bg-content-hover: #171b22;
|
||||||
|
// 列表背景颜色
|
||||||
|
--color-bg-list: #171B22;
|
||||||
|
// 详情卡文字内容
|
||||||
|
--color-text-detail: #ffffff;
|
||||||
|
// 详情卡背景颜色
|
||||||
|
--color-bg-detail: #20222a;
|
||||||
|
// 任务栏背景颜色
|
||||||
|
--color-bg-taskbar: #20222a;
|
||||||
|
// 详情列表背景颜色
|
||||||
|
--color-bg-detail-list: #050505; //运行后背景色#18191f 60%
|
||||||
|
//详情列表运行后背景颜色
|
||||||
|
--color-bg-detail-list-run: #050505;
|
||||||
|
// 旋转图标背景颜色
|
||||||
|
--color-bg-icon-rotate: #151619;
|
||||||
|
// 智能体列表背景颜色
|
||||||
|
--color-agent-list-bg: #1d222b; //背景色#181b20 100%
|
||||||
|
// 智能体边框颜色
|
||||||
|
--color-agent-list-border: rgba(8,8,8,0.6);
|
||||||
|
//智能体鼠标悬浮颜色
|
||||||
|
--color-agent-list-hover-bg: rgba(23,27,34,1); //hover背景色#171b22 100%
|
||||||
|
// 智能体鼠标悬浮边框颜色
|
||||||
|
--color-agent-list-hover-border: rgba(8,8,8,1); //hover边框#080808 100%
|
||||||
|
//智能体鼠标悬浮阴影
|
||||||
|
--color-agent-list-hover-shadow: 0 2 4 #00000050; //hover阴影 0 2 4 #00000050
|
||||||
|
//任务框下拉阴影
|
||||||
|
--color-task-shadow: 0px 10px 10px 5px rgba(0, 0, 0, 0.2);
|
||||||
|
// 卡片边框颜色
|
||||||
|
--color-card-border: #1a1a1a;
|
||||||
|
// 卡片鼠标悬浮边框颜色
|
||||||
|
--color-card-border-hover: #151515;
|
||||||
|
// 任务大纲卡片颜色
|
||||||
|
--color-card-bg-task: #20222a;
|
||||||
|
// 任务大纲卡片悬浮颜色
|
||||||
|
--color-card-bg-task-hover: #171b22;
|
||||||
|
// 任务大纲边框颜色
|
||||||
|
--color-card-border-task: #151515;
|
||||||
|
// 执行结果卡片颜色
|
||||||
|
--color-card-bg-result: #1a1a1a;
|
||||||
|
// 执行结果边框颜色
|
||||||
|
--color-card-border-result: #151515;
|
||||||
|
// 执行结果卡片悬浮颜色
|
||||||
|
--color-card-bg-result-hover: #171b22;
|
||||||
|
// 头部背景颜色
|
||||||
|
--color-header-bg: #050505;
|
||||||
|
// 标题头部字体颜色
|
||||||
|
--color-text-title: #2c72e7;
|
||||||
|
// 卡片阴影效果
|
||||||
|
--color-card-shadow: 0px 0px 5px 5px rgba(0, 0, 0, 0.17);
|
||||||
|
// 任务大纲卡片悬浮阴影效果
|
||||||
|
--color-card-shadow-hover: 0px 2px 4px 0px rgba(0, 0, 0, 0.2);
|
||||||
|
// 结果卡背景颜色
|
||||||
|
--color-bg-result: #0d1119;
|
||||||
|
// 任务字体背景颜色
|
||||||
|
--color-text-task: #d7d7d7;
|
||||||
|
// 指示灯背景颜色
|
||||||
|
--color-bg-indicator: #17181a;
|
||||||
|
// 三个卡片背景颜色
|
||||||
|
--color-bg-three: #29303c;
|
||||||
|
// 三个卡片边框颜色
|
||||||
|
--color-card-border-three: #393d42;
|
||||||
|
// 流程卡片背景颜色
|
||||||
|
--color-bg-flow: #1C222a;
|
||||||
|
// 三个卡片阴影
|
||||||
|
--color-card-shadow-three: 0px 0px 6px 0px rgba(0, 0, 0, 0.8);
|
||||||
|
// 头部阴影
|
||||||
|
--color-header-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.5);
|
||||||
|
// 执行结果详情背景颜色
|
||||||
|
--color-bg-result-detail: #24252a;
|
||||||
|
// 智能体库字体颜色
|
||||||
|
--color-text-agent-list: #969696;
|
||||||
|
// 智能体库字体悬浮颜色
|
||||||
|
--color-text-agent-list-hover: #b8b8b8;
|
||||||
|
// 任务栏字体颜色
|
||||||
|
--color-text-taskbar: #ffffff;
|
||||||
|
// 智能体卡片被选中后背景颜色
|
||||||
|
--color-agent-list-selected-bg: #20222a; //选中后背景色#171b22 100%
|
||||||
|
// 结果卡片运行前颜色
|
||||||
|
--color-text-result-detail: #6c6e72;
|
||||||
|
// 结果卡片运行后颜色
|
||||||
|
--color-text-result-detail-run: #fff;
|
||||||
|
// 分割线
|
||||||
|
--color-border-separate: rgba(255,255,255,0.18);
|
||||||
|
// url颜色
|
||||||
|
--color-text-url: #2cd235;
|
||||||
|
// model颜色
|
||||||
|
--color-text-model: #00c8d2;
|
||||||
|
|
||||||
|
--el-fill-color-blank: transparent !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,100 @@ $bg-tertiary: var(--color-bg-tertiary);
|
|||||||
$bg-quaternary: var(--color-bg-quaternary);
|
$bg-quaternary: var(--color-bg-quaternary);
|
||||||
$text: var(--color-text);
|
$text: var(--color-text);
|
||||||
$text-secondary: var(--color-text-secondary);
|
$text-secondary: var(--color-text-secondary);
|
||||||
|
// 鼠标悬浮
|
||||||
|
$bg-hover: var(--color-bg-hover);
|
||||||
|
// 字体悬浮
|
||||||
|
$text-hover: var(--color-text-hover);
|
||||||
|
// 分割线
|
||||||
|
$border: var(--color-border);
|
||||||
|
// 内容区鼠标悬浮
|
||||||
|
$bg-content-hover: var(--color-bg-content-hover);
|
||||||
|
// 列表背景颜色
|
||||||
|
$bg-list: var(--color-bg-list);
|
||||||
|
// 详情卡文字内容
|
||||||
|
$text-detail: var(--color-text-detail);
|
||||||
|
// 详情卡背景颜色
|
||||||
|
$bg-detail: var(--color-bg-detail);
|
||||||
|
// 任务栏背景颜色
|
||||||
|
$bg-taskbar: var(--color-bg-taskbar);
|
||||||
|
// 详情列表背景颜色
|
||||||
|
$bg-detail-list: var(--color-bg-detail-list);
|
||||||
|
// 旋转图标背景颜色
|
||||||
|
$bg-icon-rotate: var(--color-bg-icon-rotate);
|
||||||
|
// 智能体列表背景颜色
|
||||||
|
$agent-list-bg: var(--color-agent-list-bg);
|
||||||
|
// 智能体边框颜色
|
||||||
|
$agent-list-border: var(--color-agent-list-border);
|
||||||
|
//智能体鼠标悬浮颜色
|
||||||
|
$agent-list-hover-bg: var(--color-agent-list-hover-bg);
|
||||||
|
// 智能体鼠标悬浮边框颜色
|
||||||
|
$agent-list-hover-border: var(--color-agent-list-hover-border);
|
||||||
|
//智能体鼠标悬浮阴影
|
||||||
|
$agent-list-hover-shadow: var(--color-agent-list-hover-shadow);
|
||||||
|
//任务框下拉阴影
|
||||||
|
$task-shadow: var(--color-task-shadow);
|
||||||
|
// 卡片边框颜色
|
||||||
|
$card-border: var(--color-card-border);
|
||||||
|
// 卡片鼠标悬浮边框颜色
|
||||||
|
$card-border-hover: var(--color-card-border-hover);
|
||||||
|
// 标题文字颜色
|
||||||
|
$text-title: var(--color-text-title);
|
||||||
|
// 任务大纲卡片颜色
|
||||||
|
$card-bg-task: var(--color-card-bg-task);
|
||||||
|
// 任务大纲边框颜色
|
||||||
|
$card-border-task: var(--color-card-border-task);
|
||||||
|
// 执行结果卡片颜色
|
||||||
|
$card-bg-result: var(--color-card-bg-result);
|
||||||
|
// 执行结果边框颜色
|
||||||
|
$card-border-result: var(--color-card-border-result);
|
||||||
|
// 头部背景颜色
|
||||||
|
$header-bg: var(--color-header-bg);
|
||||||
|
// 头部字体颜色
|
||||||
|
$header-text-title: var(--color-text-title);
|
||||||
|
// 卡片阴影效果
|
||||||
|
$card-shadow: var(--color-card-shadow);
|
||||||
|
// 任务大纲卡片悬浮颜色
|
||||||
|
$card-bg-task-hover: var(--color-card-bg-task-hover);
|
||||||
|
// 任务大纲卡片悬浮阴影效果
|
||||||
|
$card-shadow-hover: var(--color-card-shadow-hover);
|
||||||
|
// 结果卡背景颜色
|
||||||
|
$bg-result: var(--color-bg-result);
|
||||||
|
// 执行结果卡片悬浮颜色
|
||||||
|
$card-bg-result-hover: var(--color-card-bg-result-hover);
|
||||||
|
// 详情列表运行后背景颜色
|
||||||
|
$bg-detail-list-run: var(--color-bg-detail-list-run);
|
||||||
|
// 任务字体背景颜色
|
||||||
|
$text-task: var(--color-text-task);
|
||||||
|
// 指示灯背景颜色
|
||||||
|
$bg-indicator: var(--color-bg-indicator);
|
||||||
|
// 三个卡片背景颜色
|
||||||
|
$bg-three: var(--color-bg-three);
|
||||||
|
// 流程卡片背景颜色
|
||||||
|
$bg-flow: var(--color-bg-flow);
|
||||||
|
// 三个卡片边框颜色
|
||||||
|
$card-border-three: var(--color-card-border-three);
|
||||||
|
// 三个卡片阴影
|
||||||
|
$card-shadow-three: var(--color-card-shadow-three);
|
||||||
|
// 头部阴影
|
||||||
|
$header-shadow: var(--color-header-shadow);
|
||||||
|
// 执行结果详情背景颜色
|
||||||
|
$bg-result-detail: var(--color-bg-result-detail);
|
||||||
|
// 智能体库字体颜色
|
||||||
|
$text-agent-list: var(--color-text-agent-list);
|
||||||
|
// 智能体库字体悬浮颜色
|
||||||
|
$text-agent-list-hover: var(--color-text-agent-list-hover);
|
||||||
|
// 任务栏字体颜色
|
||||||
|
$text-taskbar: var(--color-text-taskbar);
|
||||||
|
// 智能体卡片被选中后背景颜色
|
||||||
|
$agent-list-selected-bg: var(--color-agent-list-selected-bg); //选中后背景色#171b22 100%
|
||||||
|
// 结果卡片运行前颜色
|
||||||
|
$text-result-detail: var(--color-text-result-detail);
|
||||||
|
// url和model颜色
|
||||||
|
$text-url: var(--color-text-url);
|
||||||
|
$text-model: var(--color-text-model);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
:export {
|
:export {
|
||||||
bg: $bg;
|
bg: $bg;
|
||||||
@@ -12,4 +106,51 @@ $text-secondary: var(--color-text-secondary);
|
|||||||
bg-quaternary: $bg-quaternary;
|
bg-quaternary: $bg-quaternary;
|
||||||
text: $text;
|
text: $text;
|
||||||
text-secondary: $text-secondary;
|
text-secondary: $text-secondary;
|
||||||
|
bg-hover:$bg-hover;
|
||||||
|
text-hover:$text-hover;
|
||||||
|
border: $border;
|
||||||
|
bg-content-hover:$bg-content-hover;
|
||||||
|
bg-list: $bg-list;
|
||||||
|
text-detail: $text-detail;
|
||||||
|
bg-detail: $bg-detail;
|
||||||
|
bg-taskbar: $bg-taskbar;
|
||||||
|
bg-detail-list: $bg-detail-list;
|
||||||
|
bg-icon-rotate:$bg-icon-rotate;
|
||||||
|
agent-list-bg: $agent-list-bg;
|
||||||
|
agent-list-border: $agent-list-border;
|
||||||
|
agent-list-hover-bg: $agent-list-hover-bg;
|
||||||
|
agent-list-hover-border: $agent-list-hover-border;
|
||||||
|
agent-list-hover-shadow: $agent-list-hover-shadow;
|
||||||
|
task-shadow: $task-shadow;
|
||||||
|
card-border: $card-border;
|
||||||
|
card-border-hover: $card-border-hover;
|
||||||
|
text-title: $text-title;
|
||||||
|
card-border: $card-border;
|
||||||
|
card-bg-task: $card-bg-task;
|
||||||
|
card-border-task: $card-border-task;
|
||||||
|
card-bg-result: $card-bg-result;
|
||||||
|
card-border-result: $card-border-result;
|
||||||
|
header-bg: $header-bg;
|
||||||
|
header-text-title: $header-text-title;
|
||||||
|
card-shadow: $card-shadow;
|
||||||
|
card-bg-task-hover: $card-bg-task-hover;
|
||||||
|
card-shadow-hover: $card-shadow-hover;
|
||||||
|
bg-result: $bg-result;
|
||||||
|
card-bg-result-hover: $card-bg-result-hover;
|
||||||
|
bg-detail-list-run: $bg-detail-list-run;
|
||||||
|
text-task: $text-task;
|
||||||
|
bg-indicator: $bg-indicator;
|
||||||
|
bg-three: $bg-three;
|
||||||
|
bg-flow: $bg-flow;
|
||||||
|
card-border-three: $card-border-three;
|
||||||
|
card-shadow-three: $card-shadow-three;
|
||||||
|
header-shadow: $header-shadow;
|
||||||
|
bg-result-detail: $bg-result-detail;
|
||||||
|
text-agent-list: $text-agent-list;
|
||||||
|
text-agent-list-hover: $text-agent-list-hover;
|
||||||
|
text-taskbar: $text-taskbar;
|
||||||
|
agent-list-selected-bg: $agent-list-selected-bg;
|
||||||
|
text-result-detail: $text-result-detail;
|
||||||
|
text-url: $text-url;
|
||||||
|
text-model: $text-model;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export function changeBriefs(task?: IRawStepTask[]): IRawStepTask[] {
|
|||||||
return task.map((item) => {
|
return task.map((item) => {
|
||||||
const record = {
|
const record = {
|
||||||
...item,
|
...item,
|
||||||
Collaboration_Brief_FrontEnd: changeBrief(item),
|
Collaboration_Brief_frontEnd: changeBrief(item),
|
||||||
}
|
}
|
||||||
return record
|
return record
|
||||||
})
|
})
|
||||||
@@ -30,10 +30,10 @@ function changeBrief(task: IRawStepTask): IRichText {
|
|||||||
// 如果不存在AgentSelection直接返回
|
// 如果不存在AgentSelection直接返回
|
||||||
const agents = task.AgentSelection ?? []
|
const agents = task.AgentSelection ?? []
|
||||||
if (agents.length === 0) {
|
if (agents.length === 0) {
|
||||||
return task.Collaboration_Brief_FrontEnd
|
return task.Collaboration_Brief_frontEnd
|
||||||
}
|
}
|
||||||
const data: IRichText['data'] = {};
|
const data: IRichText['data'] = {}
|
||||||
let indexOffset = 0;
|
let indexOffset = 0
|
||||||
|
|
||||||
// 根据InputObject_List修改
|
// 根据InputObject_List修改
|
||||||
const inputs = task.InputObject_List ?? []
|
const inputs = task.InputObject_List ?? []
|
||||||
@@ -41,63 +41,62 @@ function changeBrief(task: IRawStepTask): IRichText {
|
|||||||
data[(index + indexOffset).toString()] = {
|
data[(index + indexOffset).toString()] = {
|
||||||
text,
|
text,
|
||||||
style: { background: '#ACDBA0' },
|
style: { background: '#ACDBA0' },
|
||||||
};
|
}
|
||||||
return `!<${index + indexOffset}>!`;
|
return `!<${index + indexOffset}>!`
|
||||||
});
|
})
|
||||||
const inputSentence = nameJoin(inputPlaceHolders);
|
const inputSentence = nameJoin(inputPlaceHolders)
|
||||||
indexOffset += inputs.length;
|
indexOffset += inputs.length
|
||||||
|
|
||||||
// 根据AgentSelection修改
|
// 根据AgentSelection修改
|
||||||
const namePlaceholders = agents.map((text, index) => {
|
const namePlaceholders = agents.map((text, index) => {
|
||||||
data[(index + indexOffset).toString()] = {
|
data[(index + indexOffset).toString()] = {
|
||||||
text,
|
text,
|
||||||
style: { background: '#E5E5E5', boxShadow: '1px 1px 4px 1px #0003' },
|
style: { background: '#E5E5E5', boxShadow: '1px 1px 4px 1px #0003' },
|
||||||
};
|
}
|
||||||
return `!<${index + indexOffset}>!`;
|
return `!<${index + indexOffset}>!`
|
||||||
});
|
})
|
||||||
const nameSentence = nameJoin(namePlaceholders);
|
const nameSentence = nameJoin(namePlaceholders)
|
||||||
indexOffset += agents.length;
|
indexOffset += agents.length
|
||||||
|
|
||||||
|
let actionSentence = task.TaskContent ?? ''
|
||||||
let actionSentence = task.TaskContent ?? '';
|
|
||||||
|
|
||||||
// delete the last '.' of actionSentence
|
// delete the last '.' of actionSentence
|
||||||
if (actionSentence[actionSentence.length - 1] === '.') {
|
if (actionSentence[actionSentence.length - 1] === '.') {
|
||||||
actionSentence = actionSentence.slice(0, -1);
|
actionSentence = actionSentence.slice(0, -1)
|
||||||
}
|
}
|
||||||
const actionIndex = indexOffset++;
|
const actionIndex = indexOffset++
|
||||||
|
|
||||||
data[actionIndex.toString()] = {
|
data[actionIndex.toString()] = {
|
||||||
text: actionSentence,
|
text: actionSentence,
|
||||||
style: { background: '#DDD', border: '1.5px solid #ddd' },
|
style: { background: '#DDD', border: '1.5px solid #ddd' },
|
||||||
};
|
}
|
||||||
|
|
||||||
let outputSentence = '';
|
let outputSentence = ''
|
||||||
const output = task.OutputObject ?? '';
|
const output = task.OutputObject ?? ''
|
||||||
if (output) {
|
if (output) {
|
||||||
data[indexOffset.toString()] = {
|
data[indexOffset.toString()] = {
|
||||||
text: output,
|
text: output,
|
||||||
style: { background: '#FFCA8C' },
|
style: { background: '#FFCA8C' },
|
||||||
};
|
}
|
||||||
outputSentence = `得到 !<${indexOffset}>!`;
|
outputSentence = `得到 !<${indexOffset}>!`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Join them togeter
|
// Join them togeter
|
||||||
let content = inputSentence;
|
let content = inputSentence
|
||||||
if (content) {
|
if (content) {
|
||||||
content = `基于${content}, ${nameSentence} 执行任务 !<${actionIndex}>!`;
|
content = `基于${content}, ${nameSentence} 执行任务 !<${actionIndex}>!`
|
||||||
} else {
|
} else {
|
||||||
content = `${nameSentence} 执行任务 !<${actionIndex}>!`;
|
content = `${nameSentence} 执行任务 !<${actionIndex}>!`
|
||||||
}
|
}
|
||||||
if (outputSentence) {
|
if (outputSentence) {
|
||||||
content = `${content}, ${outputSentence}.`;
|
content = `${content}, ${outputSentence}.`
|
||||||
} else {
|
} else {
|
||||||
content = `${content}.`;
|
content = `${content}.`
|
||||||
}
|
}
|
||||||
content = content.trim();
|
content = content.trim()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
template: content,
|
template: content,
|
||||||
data,
|
data,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
import type { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios'
|
import type {
|
||||||
|
AxiosError,
|
||||||
|
AxiosInstance,
|
||||||
|
AxiosRequestConfig,
|
||||||
|
AxiosResponse,
|
||||||
|
InternalAxiosRequestConfig,
|
||||||
|
} from 'axios'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import qs from 'qs'
|
import qs from 'qs'
|
||||||
import type { Ref } from 'vue'
|
import type { Ref } from 'vue'
|
||||||
import { ElNotification } from 'element-plus'
|
import { ElNotification } from 'element-plus'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
import { useConfigStoreHook } from '@/stores'
|
||||||
|
|
||||||
// 创建 axios 实例
|
// 创建 axios 实例
|
||||||
let service: AxiosInstance
|
let service: AxiosInstance
|
||||||
@@ -14,9 +21,9 @@ export interface AxiosResponseData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function initService() {
|
export function initService() {
|
||||||
|
const configStore = useConfigStoreHook()
|
||||||
service = axios.create({
|
service = axios.create({
|
||||||
baseURL: '/api',
|
baseURL: configStore.config.apiBaseUrl,
|
||||||
timeout: 50000,
|
|
||||||
headers: { 'Content-Type': 'application/json;charset=utf-8' },
|
headers: { 'Content-Type': 'application/json;charset=utf-8' },
|
||||||
paramsSerializer: (params) => {
|
paramsSerializer: (params) => {
|
||||||
return qs.stringify(params)
|
return qs.stringify(params)
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ const pathSrc = resolve(__dirname, 'src')
|
|||||||
|
|
||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
base: '',
|
||||||
plugins: [
|
plugins: [
|
||||||
vue(),
|
vue(),
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
@@ -38,7 +39,7 @@ export default defineConfig({
|
|||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
@@ -46,13 +47,14 @@ export default defineConfig({
|
|||||||
'/api': {
|
'/api': {
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
// 接口地址
|
// 接口地址
|
||||||
target: 'http://localhost:8000',
|
// target: 'http://82.157.183.212:21092',
|
||||||
rewrite: (path: string) =>
|
target: 'http://82.157.183.212:21097',
|
||||||
path.replace(/^\/api/, ''),
|
// target: 'http://localhost:8000',
|
||||||
configure: (proxy, options) => {
|
// rewrite: (path: string) => path.replace(/^\/api/, ''),
|
||||||
console.log('Proxy configured:', options)
|
// configure: (proxy, options) => {
|
||||||
}
|
// console.log('Proxy configured:', options)
|
||||||
|
// },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|||||||