feat(task): 添加任务过程编辑功能

- 新增 ProcessCard 组件用于展示和编辑任务流程
- 实现双击编辑任务描述功能
- 添加编辑状态下的卡片式输入界面
- 支持保存和取消编辑操作
- 实现鼠标悬停高亮效果
- 添加颜色处理函数用于界面美化
- 集成到 TaskResult 组件中展示任务过程
- 支持动态创建和管理任务流程连接线
- 添加额外产物编辑功能
- 实现按钮交互状态管理
- 添加滚动和折叠面板事件处理
- 集成 AgentAllocation 组件用于智能体分配
- 实现椭圆框交互效果展示选中状态
- 添加智能体等级颜色配置
- 支持智能体选中状态切换和排序
This commit is contained in:
zhaoweijie
2025-12-15 20:47:51 +08:00
parent 5dace5f788
commit 907310365a
4 changed files with 1933 additions and 0 deletions

View File

@@ -0,0 +1,283 @@
<script setup lang="ts">
import { ref, computed } from 'vue'
import { getActionTypeDisplay } from '@/layout/components/config.ts'
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
}>()
// 当前正在编辑的process ID
const editingProcessId = ref<string | null>(null)
// 编辑框的值
const editValue = ref('')
// 鼠标悬停的process ID
const hoverProcessId = ref<string | null>(null)
// 获取颜色浅两号的函数
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 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">
<div class="process-content">
<!-- 显示模式 -->
<div class="display-content">
<span
v-for="process in step.TaskProcess"
: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">
<el-input
v-model="editValue"
type="textarea"
:autosize="{ minRows: 3, maxRows: 6 }"
placeholder="请输入描述内容"
autofocus
/>
<div class="edit-buttons">
<el-button
type="success"
size="small"
icon="Check"
@click="handleSave(process.ID)"
title="保存"
>
</el-button>
<el-button
type="danger"
size="small"
icon="Close"
@click="handleCancel"
title="取消"
>
×
</el-button>
</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
? getLightColor(getActionTypeDisplay(process.ActionType)?.color || '#909399')
: 'transparent'
}"
@dblclick="handleDblClick(process.ID, process.Description)"
>
{{ process.Description }}
</span>
<span class="separator" v-if="!process.Description.endsWith('。')"></span>
</span>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.process-card {
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);
.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 {
position: relative;
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;
}
}
.edit-buttons {
position: absolute;
right: 12px;
bottom: 8px;
display: flex;
gap: 8px;
.el-button {
width: 32px;
height: 32px;
padding: 0;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
&:first-child {
background-color: #67c23a;
border-color: #67c23a;
}
&:last-child {
background-color: #f56c6c;
border-color: #f56c6c;
}
&:hover {
transform: scale(1.05);
}
}
}
}
}
.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>