2025-10-29 10:22:14 +08:00
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
import { computed, onUnmounted, ref } from 'vue'
|
|
|
|
|
|
import { throttle } from 'lodash'
|
|
|
|
|
|
import { AnchorLocations, BezierConnector } from '@jsplumb/browser-ui'
|
|
|
|
|
|
|
|
|
|
|
|
import SvgIcon from '@/components/SvgIcon/index.vue'
|
|
|
|
|
|
import { getActionTypeDisplay, getAgentMapIcon } from '@/layout/components/config.ts'
|
|
|
|
|
|
import { type ConnectArg, Jsplumb } from '@/layout/components/Main/TaskTemplate/utils.ts'
|
|
|
|
|
|
import variables from '@/styles/variables.module.scss'
|
|
|
|
|
|
import { type IRawStepTask, useAgentsStore } from '@/stores'
|
2025-10-31 18:42:31 +08:00
|
|
|
|
import api from '@/api'
|
2025-12-15 20:46:54 +08:00
|
|
|
|
import ProcessCard from '../TaskProcess/ProcessCard.vue'
|
2025-10-29 10:22:14 +08:00
|
|
|
|
import ExecutePlan from './ExecutePlan.vue'
|
|
|
|
|
|
|
|
|
|
|
|
const emit = defineEmits<{
|
|
|
|
|
|
(e: 'refreshLine'): void
|
|
|
|
|
|
(el: 'setCurrentTask', task: IRawStepTask): void
|
|
|
|
|
|
}>()
|
|
|
|
|
|
|
|
|
|
|
|
const agentsStore = useAgentsStore()
|
2025-12-15 20:46:54 +08:00
|
|
|
|
const drawerVisible = ref(false)
|
2025-10-29 10:22:14 +08:00
|
|
|
|
const collaborationProcess = computed(() => {
|
|
|
|
|
|
return agentsStore.agentRawPlan.data?.['Collaboration Process'] ?? []
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2025-12-15 20:46:54 +08:00
|
|
|
|
// 编辑逻辑
|
|
|
|
|
|
const editMode = ref(false) //全局编辑开关
|
|
|
|
|
|
const editMap = reactive<Record<string, boolean>>({}) //行级编辑状态
|
|
|
|
|
|
const editBuffer = reactive<Record<string, string | undefined>>({}) //临时输入
|
|
|
|
|
|
|
|
|
|
|
|
function getProcessDescription(stepId: string, processId: string) {
|
|
|
|
|
|
const step = collaborationProcess.value.find(s => s.Id === stepId)
|
|
|
|
|
|
if (step) {
|
|
|
|
|
|
const process = step.TaskProcess.find(p => p.ID === processId)
|
|
|
|
|
|
return process?.Description || ''
|
|
|
|
|
|
}
|
|
|
|
|
|
return ''
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function save() {
|
|
|
|
|
|
Object.keys(editMap).forEach(key => {
|
|
|
|
|
|
if (editMap[key]) {
|
|
|
|
|
|
const [stepId, processId] = key.split('-')
|
|
|
|
|
|
const value = editBuffer[key]
|
|
|
|
|
|
// 确保 value 是字符串类型
|
|
|
|
|
|
if (value !== undefined && value !== null) {
|
|
|
|
|
|
// @ts-ignore - TypeScript 无法正确推断类型,但运行时是安全的
|
|
|
|
|
|
handleSaveEdit(stepId, processId, value)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
editMode.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function handleOpenEdit(stepId: string, processId: string) {
|
|
|
|
|
|
if (!editMode.value) return
|
|
|
|
|
|
const key = `${stepId}-${processId}`
|
|
|
|
|
|
editMap[key] = true
|
|
|
|
|
|
editBuffer[key] = getProcessDescription(stepId, processId)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function handleSaveEdit(stepId: string, processId: string, value: string) {
|
|
|
|
|
|
const key = `${stepId}-${processId}`
|
|
|
|
|
|
const step = collaborationProcess.value.find(s => s.Id === stepId)
|
|
|
|
|
|
if (step) {
|
|
|
|
|
|
const process = step.TaskProcess.find(p => p.ID === processId)
|
|
|
|
|
|
if (process) {
|
|
|
|
|
|
process.Description = value
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
editMap[key] = false
|
|
|
|
|
|
ElMessage.success('已保存(前端内存)')
|
|
|
|
|
|
}
|
2025-10-29 10:22:14 +08:00
|
|
|
|
const jsplumb = new Jsplumb('task-results-main', {
|
|
|
|
|
|
connector: {
|
|
|
|
|
|
type: BezierConnector.type,
|
2025-12-15 20:46:54 +08:00
|
|
|
|
options: { curviness: 30, stub: 10 }
|
|
|
|
|
|
}
|
2025-10-29 10:22:14 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 操作折叠面板时要实时的刷新连线
|
2025-12-15 20:46:54 +08:00
|
|
|
|
let timer: ReturnType<typeof setInterval> | null = null
|
2025-10-29 10:22:14 +08:00
|
|
|
|
function handleCollapse() {
|
|
|
|
|
|
if (timer) {
|
|
|
|
|
|
clearInterval(timer)
|
|
|
|
|
|
}
|
|
|
|
|
|
timer = setInterval(() => {
|
|
|
|
|
|
jsplumb.repaintEverything()
|
|
|
|
|
|
emit('refreshLine')
|
2025-12-15 20:46:54 +08:00
|
|
|
|
}, 1) as ReturnType<typeof setInterval>
|
2025-10-29 10:22:14 +08:00
|
|
|
|
|
|
|
|
|
|
// 默认三秒后已经完全打开
|
|
|
|
|
|
const timer1 = setTimeout(() => {
|
2025-12-15 20:46:54 +08:00
|
|
|
|
if (timer) {
|
|
|
|
|
|
clearInterval(timer)
|
|
|
|
|
|
timer = null
|
|
|
|
|
|
}
|
2025-10-29 10:22:14 +08:00
|
|
|
|
}, 3000)
|
|
|
|
|
|
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
|
|
|
if (timer) {
|
|
|
|
|
|
clearInterval(timer)
|
|
|
|
|
|
}
|
|
|
|
|
|
if (timer1) {
|
|
|
|
|
|
clearInterval(timer1)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 创建内部连线
|
|
|
|
|
|
function createInternalLine(id?: string) {
|
|
|
|
|
|
const arr: ConnectArg[] = []
|
|
|
|
|
|
jsplumb.reset()
|
2025-12-15 20:46:54 +08:00
|
|
|
|
collaborationProcess.value.forEach(item => {
|
2025-10-29 10:22:14 +08:00
|
|
|
|
// 创建左侧流程与产出的连线
|
|
|
|
|
|
arr.push({
|
|
|
|
|
|
sourceId: `task-results-${item.Id}-0`,
|
|
|
|
|
|
targetId: `task-results-${item.Id}-1`,
|
2025-12-15 20:46:54 +08:00
|
|
|
|
anchor: [AnchorLocations.Left, AnchorLocations.Left]
|
2025-10-29 10:22:14 +08:00
|
|
|
|
})
|
2025-12-15 20:46:54 +08:00
|
|
|
|
collaborationProcess.value.forEach(jitem => {
|
2025-10-29 10:22:14 +08:00
|
|
|
|
// 创建左侧产出与上一步流程的连线
|
|
|
|
|
|
if (item.InputObject_List!.includes(jitem.OutputObject ?? '')) {
|
|
|
|
|
|
arr.push({
|
|
|
|
|
|
sourceId: `task-results-${jitem.Id}-1`,
|
|
|
|
|
|
targetId: `task-results-${item.Id}-0`,
|
|
|
|
|
|
anchor: [AnchorLocations.Left, AnchorLocations.Left],
|
|
|
|
|
|
config: {
|
2025-12-15 20:46:54 +08:00
|
|
|
|
type: 'output'
|
|
|
|
|
|
}
|
2025-10-29 10:22:14 +08:00
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
// 创建右侧任务程序与InputObject字段的连线
|
2025-12-15 20:46:54 +08:00
|
|
|
|
jitem.TaskProcess.forEach(i => {
|
2025-10-29 10:22:14 +08:00
|
|
|
|
if (i.ImportantInput?.includes(`InputObject:${item.OutputObject}`)) {
|
|
|
|
|
|
const color = getActionTypeDisplay(i.ActionType)?.color ?? ''
|
2025-11-03 09:44:14 +08:00
|
|
|
|
const sourceId = `task-results-${item.Id}-1`
|
|
|
|
|
|
const targetId = `task-results-${jitem.Id}-0-${i.ID}`
|
2025-10-29 10:22:14 +08:00
|
|
|
|
arr.push({
|
|
|
|
|
|
sourceId,
|
|
|
|
|
|
targetId,
|
|
|
|
|
|
anchor: [AnchorLocations.Right, AnchorLocations.Right],
|
|
|
|
|
|
config: {
|
|
|
|
|
|
stops: [
|
|
|
|
|
|
[0, color],
|
2025-12-15 20:46:54 +08:00
|
|
|
|
[1, color]
|
2025-10-29 10:22:14 +08:00
|
|
|
|
],
|
2025-12-15 20:46:54 +08:00
|
|
|
|
transparent: targetId !== id
|
|
|
|
|
|
}
|
2025-10-29 10:22:14 +08:00
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 创建右侧TaskProcess内部连线
|
2025-12-15 20:46:54 +08:00
|
|
|
|
item.TaskProcess?.forEach(i => {
|
2025-10-29 10:22:14 +08:00
|
|
|
|
if (!i.ImportantInput?.length) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-12-15 20:46:54 +08:00
|
|
|
|
item.TaskProcess?.forEach(i2 => {
|
2025-10-29 10:22:14 +08:00
|
|
|
|
if (i.ImportantInput.includes(`ActionResult:${i2.ID}`)) {
|
|
|
|
|
|
const color = getActionTypeDisplay(i.ActionType)?.color ?? ''
|
2025-11-03 09:44:14 +08:00
|
|
|
|
const sourceId = `task-results-${item.Id}-0-${i2.ID}`
|
|
|
|
|
|
const targetId = `task-results-${item.Id}-0-${i.ID}`
|
2025-10-29 10:22:14 +08:00
|
|
|
|
arr.push({
|
|
|
|
|
|
sourceId,
|
|
|
|
|
|
targetId,
|
|
|
|
|
|
anchor: [AnchorLocations.Right, AnchorLocations.Right],
|
|
|
|
|
|
config: {
|
|
|
|
|
|
stops: [
|
|
|
|
|
|
[0, color],
|
2025-12-15 20:46:54 +08:00
|
|
|
|
[1, color]
|
2025-10-29 10:22:14 +08:00
|
|
|
|
],
|
2025-12-15 20:46:54 +08:00
|
|
|
|
transparent: targetId !== id
|
|
|
|
|
|
}
|
2025-10-29 10:22:14 +08:00
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
jsplumb.connects(arr)
|
|
|
|
|
|
jsplumb.repaintEverything()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const loading = ref(false)
|
2025-12-15 20:46:54 +08:00
|
|
|
|
|
|
|
|
|
|
// 额外产物编辑状态
|
|
|
|
|
|
const editingOutputId = ref<string | null>(null)
|
|
|
|
|
|
const editingOutputContent = ref('')
|
|
|
|
|
|
|
|
|
|
|
|
// 额外产物内容存储
|
|
|
|
|
|
const additionalOutputContents = ref<Record<string, string>>({})
|
|
|
|
|
|
|
2025-10-29 10:22:14 +08:00
|
|
|
|
async function handleRun() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
loading.value = true
|
2025-10-31 18:42:31 +08:00
|
|
|
|
const d = await api.executePlan(agentsStore.agentRawPlan.data!)
|
|
|
|
|
|
agentsStore.setExecutePlan(d)
|
2025-10-29 10:22:14 +08:00
|
|
|
|
} finally {
|
|
|
|
|
|
loading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-15 20:46:54 +08:00
|
|
|
|
// 查看任务过程
|
|
|
|
|
|
async function handleTaskProcess() {
|
|
|
|
|
|
drawerVisible.value = true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 开始编辑额外产物内容
|
|
|
|
|
|
function startOutputEditing(output: string) {
|
|
|
|
|
|
editingOutputId.value = output
|
|
|
|
|
|
editingOutputContent.value = getAdditionalOutputContent(output) || ''
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 保存额外产物内容
|
|
|
|
|
|
function saveOutputEditing() {
|
|
|
|
|
|
if (editingOutputId.value && editingOutputContent.value.trim()) {
|
|
|
|
|
|
additionalOutputContents.value[editingOutputId.value] = editingOutputContent.value.trim()
|
|
|
|
|
|
}
|
|
|
|
|
|
editingOutputId.value = null
|
|
|
|
|
|
editingOutputContent.value = ''
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 取消编辑额外产物内容
|
|
|
|
|
|
function cancelOutputEditing() {
|
|
|
|
|
|
editingOutputId.value = null
|
|
|
|
|
|
editingOutputContent.value = ''
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取额外产物内容
|
|
|
|
|
|
function getAdditionalOutputContent(output: string) {
|
|
|
|
|
|
return additionalOutputContents.value[output] || ''
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理额外产物的键盘事件
|
|
|
|
|
|
function handleOutputKeydown(event: KeyboardEvent) {
|
|
|
|
|
|
if (event.key === 'Enter') {
|
|
|
|
|
|
event.preventDefault()
|
|
|
|
|
|
saveOutputEditing()
|
|
|
|
|
|
} else if (event.key === 'Escape') {
|
|
|
|
|
|
editingOutputId.value = null
|
|
|
|
|
|
editingOutputContent.value = ''
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-29 10:22:14 +08:00
|
|
|
|
// 添加滚动状态标识
|
|
|
|
|
|
const isScrolling = ref(false)
|
2025-12-15 20:46:54 +08:00
|
|
|
|
let scrollTimer: ReturnType<typeof setTimeout> | null = null
|
2025-10-29 10:22:14 +08:00
|
|
|
|
|
|
|
|
|
|
// 修改滚动处理函数
|
|
|
|
|
|
function handleScroll() {
|
|
|
|
|
|
isScrolling.value = true
|
|
|
|
|
|
emit('refreshLine')
|
|
|
|
|
|
// 清除之前的定时器
|
|
|
|
|
|
if (scrollTimer) {
|
|
|
|
|
|
clearTimeout(scrollTimer)
|
|
|
|
|
|
}
|
|
|
|
|
|
jsplumb.repaintEverything()
|
|
|
|
|
|
|
|
|
|
|
|
// 设置滚动结束检测
|
|
|
|
|
|
scrollTimer = setTimeout(() => {
|
|
|
|
|
|
isScrolling.value = false
|
2025-12-15 20:46:54 +08:00
|
|
|
|
}, 300) as ReturnType<typeof setTimeout>
|
2025-10-29 10:22:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 修改鼠标事件处理函数
|
2025-12-15 20:46:54 +08:00
|
|
|
|
const handleMouseEnter = throttle(id => {
|
2025-10-29 10:22:14 +08:00
|
|
|
|
if (!isScrolling.value) {
|
|
|
|
|
|
createInternalLine(id)
|
|
|
|
|
|
}
|
|
|
|
|
|
}, 100)
|
|
|
|
|
|
|
|
|
|
|
|
const handleMouseLeave = throttle(() => {
|
|
|
|
|
|
if (!isScrolling.value) {
|
|
|
|
|
|
createInternalLine()
|
|
|
|
|
|
}
|
|
|
|
|
|
}, 100)
|
|
|
|
|
|
|
2025-10-31 18:42:31 +08:00
|
|
|
|
function clear() {
|
|
|
|
|
|
jsplumb.reset()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-15 20:46:54 +08:00
|
|
|
|
// ========== 按钮交互状态管理 ==========
|
|
|
|
|
|
const buttonHoverState = ref<'process' | 'execute' | null>(null)
|
|
|
|
|
|
let buttonHoverTimer: ReturnType<typeof setTimeout> | null = null
|
|
|
|
|
|
const handleProcessMouseEnter = () => {
|
|
|
|
|
|
if (buttonHoverTimer) {
|
|
|
|
|
|
clearTimeout(buttonHoverTimer)
|
|
|
|
|
|
buttonHoverTimer = null
|
|
|
|
|
|
}
|
|
|
|
|
|
buttonHoverState.value = 'process'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleExecuteMouseEnter = () => {
|
|
|
|
|
|
if (buttonHoverTimer) {
|
|
|
|
|
|
clearTimeout(buttonHoverTimer)
|
|
|
|
|
|
buttonHoverTimer = null
|
|
|
|
|
|
}
|
|
|
|
|
|
if (agentsStore.agentRawPlan.data) {
|
|
|
|
|
|
buttonHoverState.value = 'execute'
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const handleButtonMouseLeave = () => {
|
|
|
|
|
|
// 添加防抖,防止快速切换时的抖动
|
|
|
|
|
|
if (buttonHoverTimer) {
|
|
|
|
|
|
clearTimeout(buttonHoverTimer)
|
|
|
|
|
|
}
|
|
|
|
|
|
buttonHoverTimer = setTimeout(() => {
|
|
|
|
|
|
buttonHoverState.value = null
|
|
|
|
|
|
}, 50) // 适当减少延迟时间
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 添加离开组件时的清理
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
|
|
|
if (buttonHoverTimer) {
|
|
|
|
|
|
clearTimeout(buttonHoverTimer)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
// 计算按钮类名
|
|
|
|
|
|
const processBtnClass = computed(() => {
|
|
|
|
|
|
return buttonHoverState.value === 'process' ? 'ellipse' : 'circle'
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const executeBtnClass = computed(() => {
|
|
|
|
|
|
// 鼠标悬停在过程按钮上时,执行按钮变圆形
|
|
|
|
|
|
if (buttonHoverState.value === 'process') {
|
|
|
|
|
|
return 'circle'
|
|
|
|
|
|
}
|
|
|
|
|
|
// 其他情况:如果有任务数据就显示椭圆形,否则显示圆形
|
|
|
|
|
|
return agentsStore.agentRawPlan.data ? 'ellipse' : 'circle'
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 计算按钮是否显示文字
|
|
|
|
|
|
const showProcessText = computed(() => {
|
|
|
|
|
|
return buttonHoverState.value === 'process'
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const showExecuteText = computed(() => {
|
|
|
|
|
|
// 鼠标悬停在过程按钮上时,执行按钮不显示文字
|
|
|
|
|
|
if (buttonHoverState.value === 'process') return false
|
|
|
|
|
|
// 其他情况:如果有任务数据就显示文字,否则不显示
|
|
|
|
|
|
return agentsStore.agentRawPlan.data
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 计算按钮标题
|
|
|
|
|
|
const processBtnTitle = computed(() => {
|
|
|
|
|
|
return buttonHoverState.value === 'process' ? '任务过程' : '点击查看任务过程'
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const executeBtnTitle = computed(() => {
|
|
|
|
|
|
return showExecuteText.value ? '任务执行' : '点击运行'
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2025-10-29 10:22:14 +08:00
|
|
|
|
defineExpose({
|
|
|
|
|
|
createInternalLine,
|
2025-12-15 20:46:54 +08:00
|
|
|
|
clear
|
2025-10-29 10:22:14 +08:00
|
|
|
|
})
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<template>
|
2025-12-15 20:46:54 +08:00
|
|
|
|
<div
|
|
|
|
|
|
class="h-full flex flex-col relative"
|
|
|
|
|
|
id="task-results"
|
|
|
|
|
|
:class="{ 'is-running': agentsStore.executePlan.length > 0 }"
|
|
|
|
|
|
>
|
2025-10-29 10:22:14 +08:00
|
|
|
|
<!-- 标题与执行按钮 -->
|
|
|
|
|
|
<div class="text-[18px] font-bold mb-[18px] flex justify-between items-center px-[20px]">
|
2025-12-15 20:46:54 +08:00
|
|
|
|
<span class="text-[var(--color-text-title-header)]">执行结果</span>
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="flex items-center gap-[14px] task-button-group"
|
|
|
|
|
|
@mouseleave="handleButtonMouseLeave"
|
|
|
|
|
|
>
|
|
|
|
|
|
<!-- 任务过程按钮 -->
|
|
|
|
|
|
<el-button
|
|
|
|
|
|
:class="processBtnClass"
|
|
|
|
|
|
:color="variables.tertiary"
|
|
|
|
|
|
:title="processBtnTitle"
|
|
|
|
|
|
@mouseenter="handleProcessMouseEnter"
|
|
|
|
|
|
@click="handleTaskProcess"
|
|
|
|
|
|
>
|
|
|
|
|
|
<svg-icon icon-class="process" />
|
|
|
|
|
|
<span v-if="showProcessText" class="btn-text">任务过程</span>
|
2025-10-29 10:22:14 +08:00
|
|
|
|
</el-button>
|
2025-12-15 20:46:54 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- 任务执行按钮 -->
|
|
|
|
|
|
<el-popover
|
|
|
|
|
|
:disabled="Boolean(agentsStore.agentRawPlan.data)"
|
|
|
|
|
|
title="请先输入要执行的任务"
|
|
|
|
|
|
:visible="showPopover"
|
|
|
|
|
|
@hide="showPopover = false"
|
|
|
|
|
|
>
|
2025-10-31 18:42:31 +08:00
|
|
|
|
<template #reference>
|
|
|
|
|
|
<el-button
|
2025-12-15 20:46:54 +08:00
|
|
|
|
:class="executeBtnClass"
|
2025-10-31 18:42:31 +08:00
|
|
|
|
:color="variables.tertiary"
|
2025-12-15 20:46:54 +08:00
|
|
|
|
:title="executeBtnTitle"
|
2025-10-31 18:42:31 +08:00
|
|
|
|
:disabled="!agentsStore.agentRawPlan.data"
|
2025-12-15 20:46:54 +08:00
|
|
|
|
@mouseenter="handleExecuteMouseEnter"
|
2025-10-31 18:42:31 +08:00
|
|
|
|
@click="handleRun"
|
|
|
|
|
|
>
|
|
|
|
|
|
<svg-icon icon-class="action" />
|
2025-12-15 20:46:54 +08:00
|
|
|
|
<span v-if="showExecuteText" class="btn-text">任务执行</span>
|
2025-10-31 18:42:31 +08:00
|
|
|
|
</el-button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-popover>
|
2025-12-15 20:46:54 +08:00
|
|
|
|
|
|
|
|
|
|
<el-drawer
|
|
|
|
|
|
v-model="drawerVisible"
|
|
|
|
|
|
title="任务过程"
|
|
|
|
|
|
direction="rtl"
|
|
|
|
|
|
size="30%"
|
|
|
|
|
|
:destroy-on-close="false"
|
|
|
|
|
|
>
|
|
|
|
|
|
<!-- 头部工具栏 -->
|
|
|
|
|
|
<template #header>
|
|
|
|
|
|
<div class="drawer-header">
|
|
|
|
|
|
<span class="title">任务过程</span>
|
|
|
|
|
|
<!-- <el-button v-if="!editMode" text icon="Edit" @click="editMode = true" />
|
|
|
|
|
|
<el-button v-else text icon="Check" @click="save" /> -->
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<el-scrollbar height="calc(100vh - 120px)">
|
|
|
|
|
|
<el-empty v-if="!collaborationProcess.length" description="暂无任务过程" />
|
|
|
|
|
|
<div v-else class="process-list">
|
|
|
|
|
|
<!-- 使用ProcessCard组件显示每个AgentSelection -->
|
|
|
|
|
|
<ProcessCard
|
|
|
|
|
|
v-for="step in collaborationProcess"
|
|
|
|
|
|
:key="step.Id"
|
|
|
|
|
|
:step="step"
|
|
|
|
|
|
@open-edit="handleOpenEdit"
|
|
|
|
|
|
@save-edit="handleSaveEdit"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-scrollbar>
|
|
|
|
|
|
</el-drawer>
|
2025-10-29 10:22:14 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<!-- 内容 -->
|
|
|
|
|
|
<div
|
|
|
|
|
|
v-loading="agentsStore.agentRawPlan.loading"
|
|
|
|
|
|
class="flex-1 overflow-auto relative"
|
|
|
|
|
|
@scroll="handleScroll"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div id="task-results-main" class="px-[40px] relative">
|
2025-12-15 20:46:54 +08:00
|
|
|
|
<!-- 原有的流程和产物 -->
|
2025-10-29 10:22:14 +08:00
|
|
|
|
<div v-for="item in collaborationProcess" :key="item.Id" class="card-item">
|
|
|
|
|
|
<el-card
|
|
|
|
|
|
class="card-item w-full relative"
|
2025-10-31 18:42:31 +08:00
|
|
|
|
:class="agentsStore.currentTask?.StepName === item.StepName ? 'active-card' : ''"
|
2025-12-15 20:46:54 +08:00
|
|
|
|
:shadow="true"
|
2025-10-29 10:22:14 +08:00
|
|
|
|
:id="`task-results-${item.Id}-0`"
|
|
|
|
|
|
@click="emit('setCurrentTask', item)"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="text-[18px] mb-[15px]">{{ item.StepName }}</div>
|
|
|
|
|
|
<!-- 折叠面板 -->
|
|
|
|
|
|
<el-collapse @change="handleCollapse">
|
|
|
|
|
|
<el-collapse-item
|
|
|
|
|
|
v-for="item1 in item.TaskProcess"
|
|
|
|
|
|
:key="`task-results-${item.Id}-${item1.ID}`"
|
|
|
|
|
|
:name="`task-results-${item.Id}-${item1.ID}`"
|
2025-10-31 18:42:31 +08:00
|
|
|
|
:disabled="Boolean(!agentsStore.executePlan.length || loading)"
|
2025-10-29 10:22:14 +08:00
|
|
|
|
@mouseenter="() => handleMouseEnter(`task-results-${item.Id}-0-${item1.ID}`)"
|
|
|
|
|
|
@mouseleave="handleMouseLeave"
|
|
|
|
|
|
>
|
|
|
|
|
|
<template v-if="loading" #icon>
|
|
|
|
|
|
<SvgIcon icon-class="loading" size="20px" class="animate-spin" />
|
|
|
|
|
|
</template>
|
2025-10-31 18:42:31 +08:00
|
|
|
|
<template v-else-if="!agentsStore.executePlan.length" #icon>
|
2025-10-29 10:22:14 +08:00
|
|
|
|
<span></span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<template #title>
|
2025-12-15 20:46:54 +08:00
|
|
|
|
<!-- 运行之前背景颜色是var(--color-bg-detail-list),运行之后背景颜色是var(--color-bg-detail-list-run) -->
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="flex items-center gap-[15px] rounded-[20px]"
|
|
|
|
|
|
:class="{
|
|
|
|
|
|
'bg-[var(--color-bg-detail-list)]': !agentsStore.executePlan.length,
|
|
|
|
|
|
'bg-[var(--color-bg-detail-list-run)]': agentsStore.executePlan.length
|
|
|
|
|
|
}"
|
|
|
|
|
|
>
|
2025-10-29 10:22:14 +08:00
|
|
|
|
<!-- 右侧链接点 -->
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="absolute right-0 top-1/2 transform -translate-y-1/2"
|
|
|
|
|
|
:id="`task-results-${item.Id}-0-${item1.ID}`"
|
|
|
|
|
|
></div>
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="w-[41px] h-[41px] rounded-full flex items-center justify-center"
|
|
|
|
|
|
:style="{ background: getAgentMapIcon(item1.AgentName).color }"
|
|
|
|
|
|
>
|
|
|
|
|
|
<svg-icon
|
|
|
|
|
|
:icon-class="getAgentMapIcon(item1.AgentName).icon"
|
2025-12-15 20:46:54 +08:00
|
|
|
|
color="#fff"
|
2025-10-29 10:22:14 +08:00
|
|
|
|
size="24px"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="text-[16px]">
|
2025-12-15 20:46:54 +08:00
|
|
|
|
<span
|
|
|
|
|
|
:class="{
|
|
|
|
|
|
'text-[var(--color-text-result-detail)]': !agentsStore.executePlan.length,
|
|
|
|
|
|
'text-[var(--color-text-result-detail-run)]':
|
|
|
|
|
|
agentsStore.executePlan.length
|
|
|
|
|
|
}"
|
|
|
|
|
|
>{{ item1.AgentName }}: </span
|
|
|
|
|
|
>
|
2025-10-29 10:22:14 +08:00
|
|
|
|
<span :style="{ color: getActionTypeDisplay(item1.ActionType)?.color }">
|
|
|
|
|
|
{{ getActionTypeDisplay(item1.ActionType)?.name }}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<ExecutePlan
|
|
|
|
|
|
:action-id="item1.ID"
|
|
|
|
|
|
:node-id="item.StepName"
|
2025-10-31 18:42:31 +08:00
|
|
|
|
:execute-plans="agentsStore.executePlan"
|
2025-10-29 10:22:14 +08:00
|
|
|
|
/>
|
|
|
|
|
|
</el-collapse-item>
|
|
|
|
|
|
</el-collapse>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
|
|
|
|
|
|
<el-card
|
2025-10-31 18:42:31 +08:00
|
|
|
|
class="card-item w-full relative output-object-card"
|
2025-12-15 20:46:54 +08:00
|
|
|
|
:shadow="true"
|
2025-10-31 18:42:31 +08:00
|
|
|
|
:class="agentsStore.currentTask?.StepName === item.StepName ? 'active-card' : ''"
|
2025-10-29 10:22:14 +08:00
|
|
|
|
:id="`task-results-${item.Id}-1`"
|
|
|
|
|
|
@click="emit('setCurrentTask', item)"
|
|
|
|
|
|
>
|
2025-10-31 18:42:31 +08:00
|
|
|
|
<!-- <div class="text-[18px]">{{ item.OutputObject }}</div>-->
|
|
|
|
|
|
<el-collapse @change="handleCollapse">
|
|
|
|
|
|
<el-collapse-item
|
|
|
|
|
|
class="output-object"
|
|
|
|
|
|
:disabled="Boolean(!agentsStore.executePlan.length || loading)"
|
|
|
|
|
|
>
|
|
|
|
|
|
<template v-if="loading" #icon>
|
|
|
|
|
|
<SvgIcon icon-class="loading" size="20px" class="animate-spin" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<template v-else-if="!agentsStore.executePlan.length" #icon>
|
|
|
|
|
|
<span></span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<template #title>
|
2025-12-15 20:46:54 +08:00
|
|
|
|
<div
|
|
|
|
|
|
class="text-[18px]"
|
|
|
|
|
|
:class="{
|
|
|
|
|
|
'text-[var(--color-text-result-detail)]': !agentsStore.executePlan.length,
|
|
|
|
|
|
'text-[var(--color-text-result-detail-run)]': agentsStore.executePlan.length
|
|
|
|
|
|
}"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ item.OutputObject }}
|
|
|
|
|
|
</div>
|
2025-10-31 18:42:31 +08:00
|
|
|
|
</template>
|
2025-12-15 20:46:54 +08:00
|
|
|
|
<ExecutePlan
|
|
|
|
|
|
:node-id="item.OutputObject"
|
|
|
|
|
|
:execute-plans="agentsStore.executePlan"
|
|
|
|
|
|
/>
|
2025-10-31 18:42:31 +08:00
|
|
|
|
</el-collapse-item>
|
|
|
|
|
|
</el-collapse>
|
2025-10-29 10:22:14 +08:00
|
|
|
|
</el-card>
|
|
|
|
|
|
</div>
|
2025-12-15 20:46:54 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- 额外产物的编辑卡片 -->
|
|
|
|
|
|
<div
|
|
|
|
|
|
v-for="(output, index) in agentsStore.additionalOutputs"
|
|
|
|
|
|
:key="`additional-output-${index}`"
|
|
|
|
|
|
class="card-item"
|
|
|
|
|
|
>
|
|
|
|
|
|
<!-- 空的流程卡片位置 -->
|
|
|
|
|
|
<div class="w-full"></div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 额外产物的编辑卡片 -->
|
|
|
|
|
|
<el-card
|
|
|
|
|
|
class="card-item w-full relative output-object-card additional-output-card"
|
|
|
|
|
|
:shadow="false"
|
|
|
|
|
|
:id="`additional-output-results-${index}`"
|
|
|
|
|
|
>
|
|
|
|
|
|
<!-- 产物名称行 -->
|
|
|
|
|
|
<div class="text-[18px] mb-3">
|
|
|
|
|
|
{{ output }}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 编辑区域行 -->
|
|
|
|
|
|
<div class="additional-output-editor">
|
|
|
|
|
|
<div v-if="editingOutputId === output" class="w-full">
|
|
|
|
|
|
<!-- 编辑状态:输入框 + 按钮 -->
|
|
|
|
|
|
<div class="flex flex-col gap-3">
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
v-model="editingOutputContent"
|
|
|
|
|
|
type="textarea"
|
|
|
|
|
|
:autosize="{ minRows: 3, maxRows: 6 }"
|
|
|
|
|
|
placeholder="请输入产物内容"
|
|
|
|
|
|
@keydown="handleOutputKeydown"
|
|
|
|
|
|
class="output-editor"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<div class="flex justify-end gap-2">
|
|
|
|
|
|
<el-button @click="saveOutputEditing" type="primary" size="small" class="px-3">
|
|
|
|
|
|
√
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
<el-button @click="cancelOutputEditing" size="small" class="px-3">
|
|
|
|
|
|
×
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div v-else class="w-full">
|
|
|
|
|
|
<!-- 非编辑状态:折叠区域 + 编辑按钮 -->
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="flex items-center justify-between p-3 bg-[var(--color-bg-quinary)] rounded-[8px]"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="text-[14px] text-[var(--color-text-secondary)] output-content-display"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ getAdditionalOutputContent(output) || '暂无内容,点击编辑' }}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<el-button
|
|
|
|
|
|
@click="startOutputEditing(output)"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
type="primary"
|
|
|
|
|
|
plain
|
|
|
|
|
|
class="flex items-center gap-1"
|
|
|
|
|
|
>
|
|
|
|
|
|
<svg-icon icon-class="action" size="12px" />
|
|
|
|
|
|
<span>编辑</span>
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
</div>
|
2025-10-29 10:22:14 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
2025-12-15 20:46:54 +08:00
|
|
|
|
#task-results.is-running {
|
|
|
|
|
|
--color-bg-detail-list: var(--color-bg-detail-list-run); // 直接指向 100 % 版本
|
|
|
|
|
|
}
|
2025-10-29 10:22:14 +08:00
|
|
|
|
#task-results {
|
|
|
|
|
|
:deep(.el-collapse) {
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
border-radius: 20px;
|
|
|
|
|
|
.el-collapse-item + .el-collapse-item {
|
|
|
|
|
|
margin-top: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.el-collapse-item__header {
|
|
|
|
|
|
border: none;
|
2025-12-15 20:46:54 +08:00
|
|
|
|
background: var(--color-bg-detail-list-run);
|
2025-10-29 10:22:14 +08:00
|
|
|
|
min-height: 41px;
|
|
|
|
|
|
line-height: 41px;
|
|
|
|
|
|
border-radius: 20px;
|
|
|
|
|
|
transition: border-radius 1ms;
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
|
|
|
|
|
|
.el-collapse-item__title {
|
2025-12-15 20:46:54 +08:00
|
|
|
|
background: var(--color-bg-detail-list);
|
2025-10-29 10:22:14 +08:00
|
|
|
|
border-radius: 20px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.el-icon {
|
|
|
|
|
|
font-size: 20px;
|
2025-12-15 20:46:54 +08:00
|
|
|
|
font-weight: 900;
|
|
|
|
|
|
background: var(--color-bg-icon-rotate);
|
|
|
|
|
|
border-radius: 50px;
|
|
|
|
|
|
color: #d8d8d8;
|
2025-10-29 10:22:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
&.is-active {
|
|
|
|
|
|
border-bottom-left-radius: 0;
|
|
|
|
|
|
border-bottom-right-radius: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-31 18:42:31 +08:00
|
|
|
|
.output-object {
|
|
|
|
|
|
.el-collapse-item__header {
|
|
|
|
|
|
background: none;
|
|
|
|
|
|
|
|
|
|
|
|
.el-collapse-item__title {
|
|
|
|
|
|
background: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.el-collapse-item__wrap {
|
|
|
|
|
|
background: none;
|
|
|
|
|
|
|
|
|
|
|
|
.card-item {
|
2025-12-15 20:46:54 +08:00
|
|
|
|
background: var(--color-bg-detail);
|
2025-10-31 18:42:31 +08:00
|
|
|
|
padding: 25px;
|
|
|
|
|
|
padding-top: 10px;
|
|
|
|
|
|
border-radius: 7px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-29 10:22:14 +08:00
|
|
|
|
.el-collapse-item__wrap {
|
|
|
|
|
|
border: none;
|
2025-12-15 20:46:54 +08:00
|
|
|
|
background: var(--color-bg-detail-list);
|
2025-10-29 10:22:14 +08:00
|
|
|
|
border-bottom-left-radius: 20px;
|
|
|
|
|
|
border-bottom-right-radius: 20px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.el-card) {
|
|
|
|
|
|
.el-card__body {
|
|
|
|
|
|
padding-right: 40px;
|
2025-12-15 20:46:54 +08:00
|
|
|
|
background-color: var(--color-bg-detail);
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
background-color: var(--color-card-bg-result-hover);
|
|
|
|
|
|
}
|
2025-10-29 10:22:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-31 18:42:31 +08:00
|
|
|
|
.output-object-card {
|
|
|
|
|
|
:deep(.el-card__body) {
|
|
|
|
|
|
padding-top: 0;
|
|
|
|
|
|
padding-bottom: 0;
|
|
|
|
|
|
padding-right: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.active-card {
|
2025-12-15 20:46:54 +08:00
|
|
|
|
background: linear-gradient(var(--color-bg-tertiary), var(--color-bg-tertiary)) padding-box,
|
2025-10-31 18:42:31 +08:00
|
|
|
|
linear-gradient(to right, #00c8d2, #315ab4) border-box;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-29 10:22:14 +08:00
|
|
|
|
.card-item + .card-item {
|
|
|
|
|
|
margin-top: 10px;
|
|
|
|
|
|
}
|
2025-12-15 20:46:54 +08:00
|
|
|
|
|
|
|
|
|
|
.additional-output-card {
|
|
|
|
|
|
border: 1px dashed #dcdfe6;
|
|
|
|
|
|
opacity: 0.9;
|
|
|
|
|
|
box-shadow: var(--color-agent-list-hover-shadow);
|
|
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
border-color: #409eff;
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.el-card__body) {
|
|
|
|
|
|
padding: 20px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 编辑区域样式调整
|
|
|
|
|
|
.el-collapse {
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
|
|
|
|
|
|
.el-collapse-item {
|
|
|
|
|
|
.el-collapse-item__header {
|
|
|
|
|
|
background: var(--color-bg-detail);
|
|
|
|
|
|
min-height: 36px;
|
|
|
|
|
|
line-height: 36px;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
|
|
|
|
|
|
.el-collapse-item__title {
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
padding-left: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.el-icon {
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.el-collapse-item__wrap {
|
|
|
|
|
|
background: var(--color-bg-detail);
|
|
|
|
|
|
border-radius: 0 0 8px 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 额外产物编辑区域样式
|
|
|
|
|
|
.additional-output-editor {
|
|
|
|
|
|
.output-editor {
|
|
|
|
|
|
:deep(.el-textarea__inner) {
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
color: var(--color-text-primary);
|
|
|
|
|
|
background: var(--color-bg-detail);
|
|
|
|
|
|
border: 1px solid #dcdfe6;
|
|
|
|
|
|
resize: none;
|
|
|
|
|
|
padding: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.output-content-display {
|
|
|
|
|
|
word-break: break-word;
|
|
|
|
|
|
white-space: pre-wrap;
|
|
|
|
|
|
transition: all 0.2s ease;
|
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
|
min-height: 20px;
|
|
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
background-color: rgba(0, 0, 0, 0.05);
|
|
|
|
|
|
border-color: #409eff;
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 编辑按钮样式
|
|
|
|
|
|
.el-button {
|
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
|
|
|
|
|
|
&.el-button--small {
|
|
|
|
|
|
padding: 4px 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ========== 新增:按钮交互样式 ==========
|
|
|
|
|
|
.task-button-group {
|
|
|
|
|
|
.el-button {
|
|
|
|
|
|
display: inline-flex !important;
|
|
|
|
|
|
align-items: center !important;
|
|
|
|
|
|
justify-content: center !important;
|
|
|
|
|
|
transition: all 0.35s cubic-bezier(0.175, 0.885, 0.32, 1.275) !important;
|
|
|
|
|
|
overflow: hidden !important;
|
|
|
|
|
|
white-space: nowrap !important;
|
|
|
|
|
|
border: none !important;
|
|
|
|
|
|
color: var(--color-text-primary) !important;
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
background-color: var(--color-bg-tertiary);
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
transform: translateY(-2px) !important;
|
|
|
|
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important;
|
|
|
|
|
|
filter: brightness(1.1) !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
&.is-disabled {
|
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
|
cursor: not-allowed !important;
|
|
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
transform: none !important;
|
|
|
|
|
|
box-shadow: none !important;
|
|
|
|
|
|
filter: none !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 圆形状态
|
|
|
|
|
|
.circle {
|
|
|
|
|
|
width: 40px !important;
|
|
|
|
|
|
height: 40px !important;
|
|
|
|
|
|
min-width: 40px !important;
|
|
|
|
|
|
padding: 0 !important;
|
|
|
|
|
|
border-radius: 50% !important;
|
|
|
|
|
|
|
|
|
|
|
|
.btn-text {
|
|
|
|
|
|
display: none !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 椭圆形状态
|
|
|
|
|
|
.ellipse {
|
|
|
|
|
|
height: 40px !important;
|
|
|
|
|
|
border-radius: 20px !important;
|
|
|
|
|
|
padding: 0 16px !important;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
|
|
|
|
|
|
.btn-text {
|
|
|
|
|
|
display: inline-block !important;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
margin-left: 4px;
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
animation: fadeIn 0.3s ease forwards;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.btn-text {
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
margin-left: 4px;
|
|
|
|
|
|
opacity: 0;
|
|
|
|
|
|
animation: fadeIn 0.3s ease forwards;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@keyframes fadeIn {
|
|
|
|
|
|
from {
|
|
|
|
|
|
opacity: 0;
|
|
|
|
|
|
transform: translateX(-5px);
|
|
|
|
|
|
}
|
|
|
|
|
|
to {
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
transform: translateX(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-29 10:22:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
</style>
|