|
@@ -1,20 +1,21 @@
|
|
|
<template>
|
|
|
<div class="steps-display-container">
|
|
|
<div v-if="parsedContent" class="steps-content">
|
|
|
+ <button>删除</button>
|
|
|
+
|
|
|
<h2 class="steps-title">{{ parsedContent.title }}</h2>
|
|
|
- <p class="steps-id">计划ID: {{ parsedContent.planId }}</p>
|
|
|
+ <!-- <p class="steps-id">计划ID: {{ parsedContent.planId }}</p> -->
|
|
|
<el-steps
|
|
|
:active="activeStep"
|
|
|
finish-status="success"
|
|
|
class="custom-steps"
|
|
|
- :space="80"
|
|
|
+ :space="60"
|
|
|
direction="vertical"
|
|
|
>
|
|
|
<el-step
|
|
|
v-for="(step, index) in parsedContent.steps"
|
|
|
:key="index"
|
|
|
:title="'步骤 ' + (index + 1)"
|
|
|
- :description="step.stepRequirement"
|
|
|
>
|
|
|
<template #icon>
|
|
|
<div class="step-icon-container">
|
|
@@ -29,18 +30,35 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
+ <template #title>
|
|
|
+ <div class="step-title-container" @click.stop="toggleCollapse(index)">
|
|
|
+ <span>步骤 {{ index + 1 }}</span>
|
|
|
+ <el-button
|
|
|
+ type="text"
|
|
|
+ class="collapse-button"
|
|
|
+
|
|
|
+ >
|
|
|
+ <el-icon>
|
|
|
+ <component :is="collapsedSteps[index] ? 'ArrowDown' : 'ArrowUp'"></component>
|
|
|
+ </el-icon>
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
<template #description>
|
|
|
- <div>
|
|
|
- {{ step.stepRequirement }}
|
|
|
- <!-- <div v-if="index === activeStep" class="step-progress">
|
|
|
- <el-progress :percentage="stepProgress" :show-text="false" :stroke-width="8" />
|
|
|
- <span class="progress-text">{{ stepProgress }}%</span>
|
|
|
- </div> -->
|
|
|
+ <div :class="['step-description', { 'collapsed': collapsedSteps[index] }]">
|
|
|
+ <div class="step-content">
|
|
|
+ {{ step.stepRequirement }}
|
|
|
+ <div>22312312</div>
|
|
|
+ <div>22312312</div>
|
|
|
+ <div>22312312</div>
|
|
|
+ <div>22312312</div>
|
|
|
+
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</template>
|
|
|
</el-step>
|
|
|
</el-steps>
|
|
|
-
|
|
|
+<!--
|
|
|
<div class="steps-controls">
|
|
|
<el-button
|
|
|
type="primary"
|
|
@@ -66,7 +84,10 @@
|
|
|
>
|
|
|
完成所有
|
|
|
</el-button>
|
|
|
- </div>
|
|
|
+ </div> -->
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
</div>
|
|
|
<div v-else class="error-message">
|
|
|
<el-alert
|
|
@@ -80,8 +101,9 @@
|
|
|
</template>
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
-import { ref, computed, onMounted, watch } from 'vue'
|
|
|
-import { ChatDotRound, Check, Loading } from '@element-plus/icons-vue'
|
|
|
+import { ref, computed, onMounted, watch, onBeforeUnmount } from 'vue'
|
|
|
+import { ChatDotRound, Check, Loading, ArrowDown, ArrowUp } from '@element-plus/icons-vue'
|
|
|
+import { deleteExecutor, executePlanByTemplateId, executorDetail } from '@/api/advance'
|
|
|
|
|
|
// 定义props
|
|
|
const props = defineProps({
|
|
@@ -101,19 +123,32 @@ const emit = defineEmits(['step-change', 'complete'])
|
|
|
// 当前激活的步骤
|
|
|
const activeStep = ref(props.initialStep)
|
|
|
|
|
|
-// 步骤进度(0-100)
|
|
|
-const stepProgress = ref(0)
|
|
|
-
|
|
|
-// 进度条更新定时器
|
|
|
-let progressTimer = null
|
|
|
-
|
|
|
+// 步骤折叠状态
|
|
|
+const collapsedSteps = ref([])
|
|
|
+const planTemplateId = ref('')
|
|
|
+const planId = ref('')
|
|
|
+const currentStepIndex = ref()
|
|
|
+const response = ref()
|
|
|
+const handleExecute = async () => {
|
|
|
+ const res = await executorDetail(planId)
|
|
|
+ response.value = res
|
|
|
+ if (!res.completed) handleExecute()
|
|
|
+ else deleteExecutor(planId)
|
|
|
+}
|
|
|
// 解析内容
|
|
|
const parsedContent = computed(() => {
|
|
|
try {
|
|
|
- if (typeof props.content === 'string') {
|
|
|
- const obj = JSON.parse(props.content)
|
|
|
- console.log(obj);
|
|
|
- obj.steps = obj.steps.map(_ => ({..._,stepRequirement:_.stepRequirement.split(' ')[1]}))
|
|
|
+ if (typeof props.content === 'string') {
|
|
|
+ const obj = JSON.parse(props.content)
|
|
|
+ console.log(obj);
|
|
|
+ obj.steps = obj.steps.map(_ => ({ ..._, stepRequirement: _.stepRequirement.split(' ')[1] }))
|
|
|
+ planTemplateId.value = obj.planTemplateId
|
|
|
+ currentStepIndex.value = 0
|
|
|
+ setTimeout(() => {
|
|
|
+ executePlanByTemplateId({ planTemplateId: planTemplateId.value }).then(res => {
|
|
|
+ planId.value = res.planId
|
|
|
+ })
|
|
|
+ }, 1000);
|
|
|
return obj
|
|
|
} else {
|
|
|
return props.content
|
|
@@ -124,6 +159,19 @@ const parsedContent = computed(() => {
|
|
|
}
|
|
|
})
|
|
|
|
|
|
+// 初始化折叠状态
|
|
|
+watch(() => parsedContent.value, (newContent) => {
|
|
|
+ if (newContent && newContent.steps) {
|
|
|
+ // 默认所有步骤都展开,当前步骤之外的都折叠
|
|
|
+ collapsedSteps.value = newContent.steps.map((_, index) => index !== activeStep.value)
|
|
|
+ }
|
|
|
+}, { immediate: true })
|
|
|
+
|
|
|
+// 切换步骤折叠状态
|
|
|
+const toggleCollapse = (index) => {
|
|
|
+ collapsedSteps.value[index] = !collapsedSteps.value[index]
|
|
|
+}
|
|
|
+
|
|
|
// 获取步骤类型
|
|
|
const getStepType = (requirement) => {
|
|
|
if (requirement.includes('[BROWSER_AGENT]')) {
|
|
@@ -148,17 +196,26 @@ const startProgressAnimation = () => {
|
|
|
// 创建新的定时器,模拟进度增长
|
|
|
progressTimer = setInterval(() => {
|
|
|
if (stepProgress.value < 100) {
|
|
|
- // 进度增长速度可以根据需要调整
|
|
|
- stepProgress.value += 1
|
|
|
+ // 使用非线性增长,开始快,接近100%时变慢
|
|
|
+ const increment = Math.max(1, 5 * Math.pow(1 - stepProgress.value / 100, 1.5))
|
|
|
+ stepProgress.value = Math.min(100, stepProgress.value + increment)
|
|
|
} else {
|
|
|
clearInterval(progressTimer)
|
|
|
}
|
|
|
- }, 300) // 每300毫秒更新一次进度
|
|
|
+ }, 200) // 每200毫秒更新一次进度,更加流畅
|
|
|
}
|
|
|
|
|
|
// 上一步
|
|
|
const prevStep = () => {
|
|
|
if (activeStep.value > 0) {
|
|
|
+ // 折叠当前步骤,展开上一步骤
|
|
|
+ if (collapsedSteps.value[activeStep.value - 1]) {
|
|
|
+ collapsedSteps.value[activeStep.value - 1] = false
|
|
|
+ }
|
|
|
+ if (!collapsedSteps.value[activeStep.value]) {
|
|
|
+ collapsedSteps.value[activeStep.value] = true
|
|
|
+ }
|
|
|
+
|
|
|
activeStep.value--
|
|
|
emit('step-change', activeStep.value)
|
|
|
startProgressAnimation()
|
|
@@ -168,6 +225,14 @@ const prevStep = () => {
|
|
|
// 下一步
|
|
|
const nextStep = () => {
|
|
|
if (parsedContent.value && activeStep.value < parsedContent.value.steps.length) {
|
|
|
+ // 折叠当前步骤,展开下一步骤
|
|
|
+ if (!collapsedSteps.value[activeStep.value]) {
|
|
|
+ collapsedSteps.value[activeStep.value] = true
|
|
|
+ }
|
|
|
+ if (activeStep.value + 1 < parsedContent.value.steps.length && collapsedSteps.value[activeStep.value + 1]) {
|
|
|
+ collapsedSteps.value[activeStep.value + 1] = false
|
|
|
+ }
|
|
|
+
|
|
|
activeStep.value++
|
|
|
emit('step-change', activeStep.value)
|
|
|
if (activeStep.value < parsedContent.value.steps.length) {
|
|
@@ -278,6 +343,39 @@ onBeforeUnmount(() => {
|
|
|
color: white;
|
|
|
}
|
|
|
|
|
|
+/* 步骤标题容器 */
|
|
|
+.step-title-container {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ width: 100%;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+/* 折叠按钮 */
|
|
|
+.collapse-button {
|
|
|
+ margin-left: 8px;
|
|
|
+ padding: 2px;
|
|
|
+ font-size: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 步骤描述 */
|
|
|
+.step-description {
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ overflow: hidden;
|
|
|
+ max-height: 500px;
|
|
|
+}
|
|
|
+
|
|
|
+.step-description.collapsed {
|
|
|
+ max-height: 30px;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+/* 步骤内容 */
|
|
|
+.step-content {
|
|
|
+ padding: 8px 0;
|
|
|
+}
|
|
|
+
|
|
|
/* 步骤进度条样式 */
|
|
|
.step-progress {
|
|
|
margin-top: 10px;
|
|
@@ -289,6 +387,8 @@ onBeforeUnmount(() => {
|
|
|
margin-left: 8px;
|
|
|
font-size: 12px;
|
|
|
color: #409eff;
|
|
|
+ font-weight: bold;
|
|
|
+ transition: all 0.3s;
|
|
|
}
|
|
|
|
|
|
/* 自定义步骤图标 */
|
|
@@ -314,7 +414,6 @@ onBeforeUnmount(() => {
|
|
|
.step-icon.current {
|
|
|
background-color: #409eff;
|
|
|
color: white;
|
|
|
- /* animation: ; */
|
|
|
animation: rotate 2s linear infinite, pulse 1.5s infinite;
|
|
|
}
|
|
|
|
|
@@ -323,6 +422,49 @@ onBeforeUnmount(() => {
|
|
|
color: white;
|
|
|
}
|
|
|
|
|
|
+/* 自定义进度条 */
|
|
|
+.progress-bar-container {
|
|
|
+ position: relative;
|
|
|
+ width: 100%;
|
|
|
+ height: 8px;
|
|
|
+ background-color: #f5f7fa;
|
|
|
+ border-radius: 4px;
|
|
|
+ overflow: hidden;
|
|
|
+ margin-right: 10px;
|
|
|
+ flex-grow: 1;
|
|
|
+}
|
|
|
+
|
|
|
+.progress-bar-background {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ background: linear-gradient(90deg, #f5f7fa 0%, #e4e7ed 100%);
|
|
|
+ opacity: 0.5;
|
|
|
+}
|
|
|
+
|
|
|
+.progress-bar-fill {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ height: 100%;
|
|
|
+ background: linear-gradient(90deg, #409eff 0%, #95d0ff 100%);
|
|
|
+ border-radius: 4px;
|
|
|
+ transition: width 0.2s ease-out;
|
|
|
+ box-shadow: 0 0 5px rgba(64, 158, 255, 0.5);
|
|
|
+}
|
|
|
+
|
|
|
+.progress-bar-glow {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ width: 20px;
|
|
|
+ height: 100%;
|
|
|
+ background: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.8) 50%, rgba(255, 255, 255, 0) 100%);
|
|
|
+ transform: translateX(-50%);
|
|
|
+ animation: shimmer 1.5s infinite;
|
|
|
+}
|
|
|
+
|
|
|
@keyframes pulse {
|
|
|
0% {
|
|
|
box-shadow: 0 0 0 0 rgba(64, 158, 255, 0.4);
|
|
@@ -334,6 +476,7 @@ onBeforeUnmount(() => {
|
|
|
box-shadow: 0 0 0 0 rgba(64, 158, 255, 0);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
@keyframes rotate {
|
|
|
0% {
|
|
|
transform: rotate(0);
|
|
@@ -342,7 +485,20 @@ onBeforeUnmount(() => {
|
|
|
transform: rotate(360deg);
|
|
|
}
|
|
|
}
|
|
|
-:deep(.el-step__icon ){
|
|
|
- border-radius: 50%;
|
|
|
+
|
|
|
+@keyframes shimmer {
|
|
|
+ 0% {
|
|
|
+ opacity: 0.3;
|
|
|
+ }
|
|
|
+ 50% {
|
|
|
+ opacity: 0.7;
|
|
|
+ }
|
|
|
+ 100% {
|
|
|
+ opacity: 0.3;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.el-step__icon) {
|
|
|
+ border-radius: 50%;
|
|
|
}
|
|
|
</style>
|