Forráskód Böngészése

fix: 【公共】详情页逻辑增加流程阶段

hanxiaohui 7 hónapja
szülő
commit
34db7083ad

+ 19 - 4
src/components/business/page-detail-layout/index.vue

@@ -22,7 +22,9 @@
         </div>
       </div>
 
-      <div class="header-flow" v-if="false"></div>
+      <div class="header-flow" v-if="!isEmpty(stepsData)">
+        <steps :doing-node-index="doingNodeIndex" @change-doing-node-index="handleChangeDoingNodeIndex" :steps="stepsData"></steps>
+      </div>
     </div>
 
     <div class="header-tabs">
@@ -38,9 +40,9 @@
 <script setup>
   import { BsTabs } from '/@/components/BsUi/index.js';
   import { useSlots } from 'vue';
-
+  import steps from './steps/index.vue';
+  import { isEmpty } from 'lodash';
   const slots = useSlots();
-
   const props = defineProps({
     title: {
       required: false,
@@ -66,13 +68,25 @@
         valueKey: 'value',
       },
     },
+    doingNodeIndex: {
+      required: false,
+      default: 0,
+    },
+    stepsData: {
+      required: false,
+      default: [],
+    },
   });
 
-  const emits = defineEmits(['update:tabActiveKey']);
+  const emits = defineEmits(['update:tabActiveKey', 'update:doingNodeIndex']);
 
   const handleChangeTabActiveKey = (val) => {
     emits('update:tabActiveKey', val);
   };
+
+  const handleChangeDoingNodeIndex = (val) => {
+    emits('update:doingNodeIndex', val);
+  };
 </script>
 
 <style lang="scss" scoped>
@@ -153,6 +167,7 @@
         margin-top: 20px;
         width: 100%;
         height: 100%;
+        overflow-x: auto;
       }
     }
 

+ 170 - 0
src/components/business/page-detail-layout/steps/index.vue

@@ -0,0 +1,170 @@
+<template>
+  <div class="steps-progress-container">
+    <div v-for="(step, index) in steps" :key="index" class="step-item">
+      <div class="step-item-part" @click="handleClickNode(step)">
+        <!-- 步骤圆圈及勾选/数字状态 -->
+        <div
+          :class="[
+            'step-circle',
+            {
+              completed: step.status === 'completed',
+              current: step.status === 'current',
+            },
+          ]"
+        >
+          <span v-if="step.status === 'completed'">✔</span>
+          <span v-else-if="step.status === 'current'">{{ step.number }}</span>
+          <span v-else>{{ step.number }}</span>
+        </div>
+        <!-- 步骤文字及时间 -->
+        <div class="step-text">
+          <div :class="`step-title ${step.status === 'completed' ? 'step-title_completed' : ''}`">{{ step.title }}</div>
+        </div>
+
+        <div class="step-time" v-if="step.time">{{ step.time }}</div>
+      </div>
+
+      <!-- 连接线条,最后一个步骤不需要线条 -->
+      <div
+        class="step-line"
+        v-if="index < steps.length - 1"
+        :style="{
+          width: '100%',
+          background: step.status === 'completed' ? lineColor_complete : lineColor_un_complete,
+        }"
+      ></div>
+
+      <div class="line-top" v-if="index < steps.length - 1">{{ step.lineTopNum }}天</div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+  import { onMounted, ref, watch } from 'vue';
+
+  const props = defineProps({
+    doingNodeIndex: {
+      required: false,
+      default: 0,
+    },
+    steps: {
+      required: false,
+      default: [],
+    },
+  });
+
+  const doingIndex = ref(0);
+
+  // 可根据需求动态调整线条宽度和颜色等样式
+  const lineColor_complete = '#1677ff';
+  const lineColor_un_complete = '#e5e6eb';
+
+  const emits = defineEmits(['clickNode', 'changeDoingNodeIndex']);
+
+  const handleClickNode = (currentNode) => {
+    emits('clickNode', { node: currentNode });
+  };
+
+  onMounted(() => {
+    doingIndex.value = props.doingNodeIndex;
+    //   TODO 动态来处理steps中的complete和current,todo的状态
+  });
+
+  watch(doingIndex, (val) => {
+    emits('changeDoingNodeIndex', val);
+  });
+</script>
+
+<style scoped>
+  .steps-progress-container {
+    min-width: 1250px;
+    display: flex;
+    align-items: flex-start;
+    padding: 10px 0;
+    justify-content: center;
+  }
+
+  .step-item {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    position: relative;
+    flex: 1;
+    //width: 150px;
+  }
+
+  .step-circle {
+    width: 30px;
+    height: 30px;
+    border-radius: 50%;
+    border: 2px solid #ccc;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    font-size: 14px;
+    color: #fff;
+    background-color: #ccc;
+  }
+
+  .step-circle.completed {
+    background-color: #fff;
+    border-color: var(--vxe-ui-font-primary-color);
+    color: var(--vxe-ui-font-primary-color);
+  }
+
+  .step-circle.current {
+    background-color: var(--vxe-ui-font-primary-color);
+    border-color: var(--vxe-ui-font-primary-color);
+  }
+
+  .step-text {
+    text-align: center;
+    margin-top: 5px;
+    font-size: 16px;
+    color: #86909c;
+    position: relative;
+  }
+
+  .step-time {
+    margin-top: 2px;
+    font-size: 14px;
+    color: #86909c;
+    position: absolute;
+    bottom: 0;
+    padding-left: 40px;
+  }
+
+  .step-line {
+    width: 100%;
+    height: 2px;
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    margin-left: 50px;
+    color: var(--vxe-ui-font-primary-color);
+  }
+
+  .step-item-part {
+    display: flex;
+    z-index: 100;
+    background: #fff;
+    gap: 10px;
+    padding: 20px;
+    align-items: center;
+    cursor: pointer;
+    position: relative;
+  }
+
+  .line-top {
+    position: absolute;
+    right: 0;
+    top: 10px;
+    transform: translateX(50%);
+    z-index: 100;
+    color: var(--vxe-ui-font-primary-color);
+  }
+
+  .step-title_completed {
+    color: #1d2129;
+  }
+</style>