riku
2025-09-18 c1d2051abc8ca88cd07f0d7c56c0dbf8165d5c33
src/views/fysp/data-product/base-data-product/components/BaseProdProcess.vue
@@ -1,62 +1,338 @@
<template>
  <!-- <el-button type="primary" @click="changeActive">change</el-button> -->
  <!-- <el-row>
    <el-col
      :span="active == 1 ? 16 : 4"
      :class="active == 1 ? 'prod-active' : 'prod-inactive'"
    >step1</el-col>
    <el-col
      :span="active == 2 ? 16 : 4"
      :class="active == 2 ? 'prod-active' : 'prod-inactive'"
    >step2</el-col>
    <el-col
      :span="active == 3 ? 16 : 4"
      :class="active == 3 ? 'prod-active' : 'prod-inactive'"
    >step3</el-col>
  </el-row> -->
  <el-row>
    <div :class="active == 1 ? 'prod-active' : 'prod-inactive'">
        <slot name="step1"></slot>
    <!-- 步骤1 数据产品生成选项 -->
    <div :class="active == 1 ? 'prod-active' : 'prod-inactive'" ref="step1Ref">
      <transition
        name="el-fade-in"
        @after-leave="handleTransitionContentEnd(1)"
      >
        <div v-show="showStep1Content">
          <template v-if="$slots.step1">
            <slot name="step1"></slot>
          </template>
          <template v-else>
            <ProdQueryOpt :loading="loading" @submit="onSearch"> </ProdQueryOpt>
          </template>
        </div>
      </transition>
      <transition
        name="el-fade-in"
        @after-leave="handleTransitionThumbnailEnd(1)"
      >
        <div
          v-show="showStep1Thumbnail"
          class="prod-thumbnail-wrapper"
          :style="{ height: viewHeight + 'px' }"
          @click="changeActive(1)"
        >
          <div class="prod-thumbnail">①修改选项</div>
        </div>
      </transition>
    </div>
    <div :class="active == 2 ? 'prod-active' : 'prod-inactive'">
        <slot name="step2"></slot>
    <!-- 步骤2 数据产品结果预览 -->
    <div :class="active == 2 ? 'prod-active' : 'prod-inactive'" ref="step2Ref">
      <transition
        name="el-fade-in"
        @after-leave="handleTransitionContentEnd(2)"
      >
        <div v-show="showStep2Content">
          <div ref="titleRef" class="prod-title">
            <el-text tag="b" size="large">数据产品预览</el-text>
            <el-button type="primary" @click="$emit('onStep2')">
              下载数据产品
            </el-button>
          </div>
          <slot name="step2" :contentHeight="contentHeight"></slot>
        </div>
      </transition>
      <transition
        name="el-fade-in"
        @after-leave="handleTransitionThumbnailEnd(2)"
      >
        <div
          v-show="showStep2Thumbnail"
          class="prod-thumbnail-wrapper"
          :style="{ height: viewHeight + 'px' }"
          @click="changeActive(2)"
        >
          <div
            :class="
              'prod-thumbnail prod-thumbnail_middle ' +
              (active < 2 ? 'prod-thumbnail-disabled' : '')
            "
          >
            ②数据产品预览
          </div>
        </div>
      </transition>
    </div>
    <div :class="active == 3 ? 'prod-active' : 'prod-inactive'">
        <slot name="step3"></slot>
    <!-- 步骤3 数据产品表单下载 -->
    <div :class="active == 3 ? 'prod-active' : 'prod-inactive'" ref="step3Ref">
      <transition
        name="el-fade-in"
        @after-leave="handleTransitionContentEnd(3)"
      >
        <div v-show="showStep3Content">
          <template v-if="$slots.step3">
            <slot name="step3"></slot>
          </template>
          <template v-else>
            <ProdDownload
              :loading="loading"
              :queryOpt="queryOpt"
              @submit="onDownload"
            ></ProdDownload>
          </template>
        </div>
      </transition>
      <transition
        name="el-fade-in"
        @after-leave="handleTransitionThumbnailEnd(3)"
      >
        <div
          v-show="showStep3Thumbnail"
          class="prod-thumbnail-wrapper"
          :style="{ height: viewHeight + 'px' }"
          @click="changeActive(3)"
        >
          <div
            :class="
              'prod-thumbnail prod-thumbnail_end ' +
              (active < 3 ? 'prod-thumbnail-disabled' : '')
            "
          >
            ③数据产品下载
          </div>
        </div>
      </transition>
    </div>
  </el-row>
</template>
<script setup>
import { ref } from 'vue';
import { computed, inject, ref, watch, onMounted, onUnmounted } from 'vue';
import { unCalc } from '@/utils/css-util';
import ProdQueryOpt from '@/views/fysp/data-product/base-data-product/components/ProdQueryOpt.vue';
import ProdDownload from '@/views/fysp/data-product/base-data-product/components/ProdDownload.vue';
const props = defineProps({
  active: {
    type: Number,
    default: 1
  },
  loading: {
    type: Boolean,
    default: false
  }
});
// function changeActive() {
//   active.value++;
//   active.value = active.value > 3 ? 1 : active.value;
// }
const emit = defineEmits(['update:active', 'onStep1', 'onStep2', 'onStep3']);
const contentMaxHeight = inject('contentMaxHeight');
const viewHeight = inject('viewHeight', contentMaxHeight.value);
const btnDisabled = ref(false);
const titleRef = ref(null);
const contentHeight = ref('50vh');
function calContentHeight() {
  // console.log(titleRef.value.offsetHeight);
  contentHeight.value = viewHeight.value - (titleRef.value?.offsetHeight || 0);
  // console.log(contentHeight.value);
}
// 数据产品生成选项
const queryOpt = ref({});
// 步骤引用
const step1Ref = ref(null);
const step2Ref = ref(null);
const step3Ref = ref(null);
// 控制显示/隐藏的状态
const showStep1Content = ref(props.active === 1);
const showStep1Thumbnail = ref(props.active != 1);
const showStep2Content = ref(props.active === 2);
const showStep2Thumbnail = ref(props.active != 2);
const showStep3Content = ref(props.active === 3);
const showStep3Thumbnail = ref(props.active != 3);
// 记录动画是否正在进行中
const isAnimating = ref({});
// 监听active变化
watch(
  () => props.active,
  (newActive, oldActive) => {
    // 标记动画开始
    isAnimating.value[oldActive] = true;
    isAnimating.value[newActive] = true;
    // 先隐藏所有内容,等待动画结束后再显示正确的内容
    if (oldActive === 1) {
      showStep1Content.value = false;
    } else if (oldActive === 2) {
      showStep2Content.value = false;
    } else if (oldActive === 3) {
      showStep3Content.value = false;
    }
    if (newActive === 1) {
      showStep1Thumbnail.value = false;
    } else if (newActive === 2) {
      showStep2Thumbnail.value = false;
    } else if (newActive === 3) {
      showStep3Thumbnail.value = false;
    }
  }
);
// 处理动画结束事件
function handleTransitionThumbnailEnd(stepIndex) {
  // 检查动画是否确实结束(避免重复触发)
  if (isAnimating.value[stepIndex]) {
    isAnimating.value[stepIndex] = false;
    // setTimeout(() => {
    // 动画结束后,更新显示状态
    if (stepIndex === 1) {
      showStep1Content.value = props.active === 1;
    } else if (stepIndex === 2) {
      showStep2Content.value = props.active === 2;
    } else if (stepIndex === 3) {
      showStep3Content.value = props.active === 3;
    }
    // }, 50);
  }
}
function handleTransitionContentEnd(stepIndex) {
  // 检查动画是否确实结束(避免重复触发)
  if (isAnimating.value[stepIndex]) {
    isAnimating.value[stepIndex] = false;
    // setTimeout(() => {
    // 动画结束后,更新显示状态
    if (stepIndex === 1) {
      showStep1Thumbnail.value = props.active != 1;
    } else if (stepIndex === 2) {
      showStep2Thumbnail.value = props.active != 2;
    } else if (stepIndex === 3) {
      showStep3Thumbnail.value = props.active != 3;
    }
    // }, 50);
  }
}
function onSearch(opt) {
  queryOpt.value = opt;
  emit('onStep1', opt);
}
function onDownload(val) {
  emit('onStep3', val);
}
function changeActive(index) {
  let isAnimate = false;
  Object.values(isAnimating.value).forEach((item) => {
    isAnimate = isAnimate || item;
  });
  if (!isAnimate && !btnDisabled.value && props.active >= index) {
    emit('update:active', index);
    btnDisabled.value = true;
    setTimeout(() => {
      btnDisabled.value = false;
    }, 500);
  }
  // emit('update:active', index);
}
let resizeObserver = null;
onMounted(() => {
  if (titleRef.value) {
    resizeObserver = new ResizeObserver(() => {
      calContentHeight();
    });
    resizeObserver.observe(titleRef.value);
  }
});
// 在组件卸载时清理
onUnmounted(() => {
  if (resizeObserver && titleRef.value) {
    resizeObserver.unobserve(titleRef);
  }
});
</script>
<style scoped>
.prod-active {
  width: 66.667%;
  /* width: 66.667%; */
  width: 90%;
  transition:
    width 0.5s ease,
    box-shadow 0.3s ease;
  /* background-color: #409eff; */
  margin: 5px 0;
  border-radius: 4px;
  box-shadow:
    -3px 0 6px rgba(0, 0, 0, 0.1),
    3px 0 6px rgba(0, 0, 0, 0.1);
}
.prod-inactive {
  /* width: 16.667%; */
  width: 5%;
  transition: width 0.5s ease;
  background-color: #409eff;
  color: white;
  /* background-color: #e4e7ed; */
  margin: 5px 0;
  border-radius: 4px;
}
.prod-inactive {
  width: 16.667%;
  transition: width 0.5s ease;
.prod-title {
  padding: 20px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.prod-thumbnail-wrapper {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 0 2px;
}
.prod-thumbnail {
  height: 90%;
  width: 100%;
  background-color: #409eff;
  color: white;
  display: flex;
  justify-content: center;
  align-items: center;
  writing-mode: vertical-rl;
  text-orientation: upright;
  letter-spacing: 8px;
  font-size: 18px;
  font-weight: 600;
  border-top-left-radius: 4px;
  border-bottom-left-radius: 4px;
  cursor: pointer;
}
.prod-thumbnail_middle {
  border-radius: 0px;
}
.prod-thumbnail_end {
  border-top-left-radius: 0px;
  border-bottom-left-radius: 0px;
  border-top-right-radius: 4px;
  border-bottom-right-radius: 4px;
}
.prod-thumbnail-disabled {
  background-color: #e4e7ed;
  color: #606266;
  margin: 5px 0;
  border-radius: 4px;
  color: #c0c4cc;
  cursor: not-allowed;
}
</style>