Переглянути джерело

feat(StepsDisplay): 优化步骤展示组件功能和样式

- 新增步骤折叠功能,提高长步骤的可读性
- 添加计划执行和删除相关 API 调用
- 优化步骤进度显示,采用非线性增长动画
- 调整步骤图标和进度条样式,提升视觉效果
- 修复了一些组件初始化和数据解析的问题
chd 2 місяців тому
батько
коміт
3120e3affa

+ 21 - 0
src/api/advance.js

@@ -6,3 +6,24 @@ export function getPlan(data) {
         data: data
     })
 }
+export function executePlanByTemplateId(data) {
+    return request({
+        url: '/plan-template/executePlanByTemplateId',
+        method: 'post',
+        data: data
+    })
+}  
+
+export function executorDetail(data) {
+    return request({
+        url: `/executor/details/${data}`,
+        method: 'get',
+    })
+}  
+
+export function deleteExecutor(data) {
+    return request({
+        url: `/executor/details/${data}`,
+        method: 'delete',
+    })
+}  

+ 185 - 29
src/entrypoints/sidepanel/component/StepsDisplay.vue

@@ -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>

+ 1 - 1
src/utils/request2.js

@@ -7,7 +7,7 @@ axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
 
 // 创建axios实例
 const service = axios.create({
-  baseURL: 'http://192.168.1.118:18080/api',
+  baseURL: 'http://192.168.1.123:18080/api',
 })
 
 let cancel