zhangwencui
10 小时以前 617ad108a3c7f4e676229b1495185e66fac644b7
销售统计看板自适应,以及中心背景替换
已添加2个文件
已修改3个文件
1169 ■■■■■ 文件已修改
src/assets/BI/imageSS@2x.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/BI/imageSStop.png 补丁 | 查看 | 原始文档 | blame | 历史
src/views/reportAnalysis/PSIDataAnalysis/components/PanelHeader.vue 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/reportAnalysis/qualityAnalysis/components/CarouselCards.vue 524 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/reportAnalysis/salesStatistics/index.vue 591 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/BI/imageSS@2x.png
src/assets/BI/imageSStop.png
src/views/reportAnalysis/PSIDataAnalysis/components/PanelHeader.vue
@@ -1,33 +1,43 @@
<template>
  <div class="panel-header">
    <span class="panel-title">{{ title }}</span>
    <span :class="{'panel-title': !isFullscreen, 'panel-title-fullscreen': isFullscreen}">{{ title }}</span>
  </div>
</template>
<script setup>
defineProps({
  title: {
    type: String,
    required: true,
    default: ''
  }
})
  defineProps({
    title: {
      type: String,
      required: true,
      default: "",
    },
    isFullscreen: {
      type: Boolean,
      default: false,
    },
  });
</script>
<style scoped>
.panel-header {
  background-image: url("@/assets/BI/kehuhetongback@2x.png");
  background-size: 100% 100%;
  background-position: center;
  background-repeat: no-repeat;
}
  .panel-header {
    background-image: url("@/assets/BI/kehuhetongback@2x.png");
    background-size: 100% 100%;
    background-position: center;
    background-repeat: no-repeat;
  }
.panel-title {
  width: 100%;
  font-weight: 500;
  font-size: 16px;
  color: #D9ECFF;
  padding-left: 46px;
  line-height: 36px;
}
  .panel-title {
    width: 100%;
    font-weight: 500;
    font-size: 16px;
    color: #d9ecff;
    padding-left: 46px;
    line-height: 36px;
  }
  .panel-title-fullscreen {
    font-size: 1.6vh;
    line-height: 4vh;
    padding-left: 4.6vh;
  }
</style>
src/views/reportAnalysis/qualityAnalysis/components/CarouselCards.vue
@@ -1,306 +1,324 @@
<template>
  <div class="carousel-cards">
    <button
      v-if="canScrollLeft"
      class="nav-button nav-button-left"
      @click="scrollLeftFn"
    >
      <img src="@/assets/BI/jiantou.png" alt="左箭头" />
    <button v-if="canScrollLeft"
            class="nav-button nav-button-left"
            @click="scrollLeftFn">
      <img src="@/assets/BI/jiantou.png"
           alt="左箭头" />
    </button>
    <div
      class="cards-container"
      :style="{ '--visible-count': visibleCount }"
      ref="cardsContainerRef"
    >
      <div
        v-for="(item, index) in items"
        :key="index"
        class="card-item"
      >
        <div v-if="item.icon" class="card-icon" :style="{ backgroundImage: `url(${item.icon})` }"></div>
    <div class="cards-container"
         :style="{ '--visible-count': visibleCount }"
         ref="cardsContainerRef">
      <div v-for="(item, index) in items"
           :key="index"
           class="card-item">
        <div v-if="item.icon"
             class="card-icon"
             :style="{ backgroundImage: `url(${item.icon})` }"></div>
        <div class="card-title">
          <div class="card-label">{{ item.label }}</div>
          <div class="card-value">
            <span class="value-number">{{ item.value }}</span>
            <span class="value-unit">{{ item.unit }}</span>
          </div>
          <div v-if="item.rate ?? item.ratio ?? item.percent" class="card-rate">
          <div v-if="item.rate ?? item.ratio ?? item.percent"
               class="card-rate">
            <span class="rate-value">{{ item.rate ?? item.ratio ?? item.percent }}%</span>
          </div>
        </div>
      </div>
    </div>
    <button
      v-if="canScrollRight"
      class="nav-button nav-button-right"
      @click="scrollRightFn"
    >
      <img src="@/assets/BI/jiantou.png" alt="右箭头" />
    <button v-if="canScrollRight"
            class="nav-button nav-button-right"
            @click="scrollRightFn">
      <img src="@/assets/BI/jiantou.png"
           alt="右箭头" />
    </button>
  </div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount, nextTick, watch, computed } from 'vue'
  import {
    ref,
    onMounted,
    onBeforeUnmount,
    nextTick,
    watch,
    computed,
  } from "vue";
const props = defineProps({
  items: {
    type: Array,
    default: () => [],
    validator: (value) => {
      return value.every(item =>
        item && typeof item.label !== 'undefined' &&
        typeof item.value !== 'undefined' &&
        typeof item.unit !== 'undefined'
      )
    }
  },
  visibleCount: {
    type: Number,
    default: 3
  }
})
  const props = defineProps({
    items: {
      type: Array,
      default: () => [],
      validator: value => {
        return value.every(
          item =>
            item &&
            typeof item.label !== "undefined" &&
            typeof item.value !== "undefined" &&
            typeof item.unit !== "undefined"
        );
      },
    },
    visibleCount: {
      type: Number,
      default: 3,
    },
  });
const cardsContainerRef = ref(null)
const currentScrollLeft = ref(0)
const maxScrollLeft = ref(0)
  const cardsContainerRef = ref(null);
  const currentScrollLeft = ref(0);
  const maxScrollLeft = ref(0);
// 检查是否可以向左滚动
const canScrollLeft = computed(() => {
  return currentScrollLeft.value > 0
})
  // 检查是否可以向左滚动
  const canScrollLeft = computed(() => {
    return currentScrollLeft.value > 0;
  });
// 检查是否可以向右滚动
const canScrollRight = computed(() => {
  return currentScrollLeft.value < maxScrollLeft.value
})
  // 检查是否可以向右滚动
  const canScrollRight = computed(() => {
    return currentScrollLeft.value < maxScrollLeft.value;
  });
// 更新滚动状态
const updateScrollState = () => {
  const container = cardsContainerRef.value
  if (!container) return
  currentScrollLeft.value = container.scrollLeft
  maxScrollLeft.value = container.scrollWidth - container.clientWidth
}
  // 更新滚动状态
  const updateScrollState = () => {
    const container = cardsContainerRef.value;
    if (!container) return;
// 向左滚动
const scrollLeftFn = () => {
  const container = cardsContainerRef.value
  if (!container) return
  const scrollItems = Array.from(container.querySelectorAll('.card-item'))
  if (scrollItems.length === 0) return
  const itemWidth = scrollItems[0]?.offsetWidth || 0
  const gap = 12
  const scrollDistance = itemWidth + gap
  container.scrollBy({
    left: -scrollDistance,
    behavior: 'smooth'
  })
  // 延迟更新状态,等待滚动动画完成
  setTimeout(() => {
    updateScrollState()
  }, 300)
}
    currentScrollLeft.value = container.scrollLeft;
    maxScrollLeft.value = container.scrollWidth - container.clientWidth;
  };
// 向右滚动
const scrollRightFn = () => {
  const container = cardsContainerRef.value
  if (!container) return
  const scrollItems = Array.from(container.querySelectorAll('.card-item'))
  if (scrollItems.length === 0) return
  const itemWidth = scrollItems[0]?.offsetWidth || 0
  const gap = 12
  const scrollDistance = itemWidth + gap
  container.scrollBy({
    left: scrollDistance,
    behavior: 'smooth'
  })
  // 延迟更新状态,等待滚动动画完成
  setTimeout(() => {
    updateScrollState()
  }, 300)
}
  // 向左滚动
  const scrollLeftFn = () => {
    const container = cardsContainerRef.value;
    if (!container) return;
// 监听 items 变化,更新滚动状态
watch(() => props.items, () => {
  nextTick(() => {
    updateScrollState()
  })
}, { deep: true })
    const scrollItems = Array.from(container.querySelectorAll(".card-item"));
    if (scrollItems.length === 0) return;
onMounted(() => {
  nextTick(() => {
    updateScrollState()
    // 监听滚动事件
    const container = cardsContainerRef.value
    const itemWidth = scrollItems[0]?.offsetWidth || 0;
    const gap = 12;
    const scrollDistance = itemWidth + gap;
    container.scrollBy({
      left: -scrollDistance,
      behavior: "smooth",
    });
    // 延迟更新状态,等待滚动动画完成
    setTimeout(() => {
      updateScrollState();
    }, 300);
  };
  // 向右滚动
  const scrollRightFn = () => {
    const container = cardsContainerRef.value;
    if (!container) return;
    const scrollItems = Array.from(container.querySelectorAll(".card-item"));
    if (scrollItems.length === 0) return;
    const itemWidth = scrollItems[0]?.offsetWidth || 0;
    const gap = 12;
    const scrollDistance = itemWidth + gap;
    container.scrollBy({
      left: scrollDistance,
      behavior: "smooth",
    });
    // 延迟更新状态,等待滚动动画完成
    setTimeout(() => {
      updateScrollState();
    }, 300);
  };
  // 监听 items 变化,更新滚动状态
  watch(
    () => props.items,
    () => {
      nextTick(() => {
        updateScrollState();
      });
    },
    { deep: true }
  );
  onMounted(() => {
    nextTick(() => {
      updateScrollState();
      // 监听滚动事件
      const container = cardsContainerRef.value;
      if (container) {
        container.addEventListener("scroll", updateScrollState);
      }
    });
  });
  onBeforeUnmount(() => {
    // 清理滚动事件监听器
    const container = cardsContainerRef.value;
    if (container) {
      container.addEventListener('scroll', updateScrollState)
      container.removeEventListener("scroll", updateScrollState);
    }
  })
})
onBeforeUnmount(() => {
  // 清理滚动事件监听器
  const container = cardsContainerRef.value
  if (container) {
    container.removeEventListener('scroll', updateScrollState)
  }
})
  });
</script>
<style scoped>
.carousel-cards {
  width: 100%;
  overflow: hidden;
  position: relative;
  display: flex;
  align-items: center;
}
  .carousel-cards {
    width: 100%;
    overflow: hidden;
    position: relative;
    display: flex;
    align-items: center;
  }
.cards-container {
  display: flex;
  gap: 12px;
  width: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none; /* Firefox */
  -ms-overflow-style: none; /* IE and Edge */
  padding-bottom: 4px;
  scroll-behavior: smooth;
}
  .cards-container {
    display: flex;
    gap: 12px;
    width: 100%;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none; /* Firefox */
    -ms-overflow-style: none; /* IE and Edge */
    padding-bottom: 4px;
    scroll-behavior: smooth;
  }
.cards-container::-webkit-scrollbar {
  display: none; /* Chrome, Safari, Opera */
}
  .cards-container::-webkit-scrollbar {
    display: none; /* Chrome, Safari, Opera */
  }
.nav-button {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 32px;
  height: 32px;
  background: rgba(26, 88, 176, 0.6);
  border: 1px solid rgba(26, 88, 176, 0.8);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  z-index: 10;
  transition: all 0.3s ease;
  padding: 0;
}
  .nav-button {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 32px;
    height: 32px;
    background: rgba(26, 88, 176, 0.6);
    border: 1px solid rgba(26, 88, 176, 0.8);
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    z-index: 10;
    transition: all 0.3s ease;
    padding: 0;
  }
.nav-button:hover {
  background: rgba(26, 88, 176, 0.8);
  transform: translateY(-50%) scale(1.1);
}
  .nav-button:hover {
    background: rgba(26, 88, 176, 0.8);
    transform: translateY(-50%) scale(1.1);
  }
.nav-button-left {
  left: -16px;
}
  .nav-button-left {
    left: -16px;
  }
.nav-button-left img {
  width: 16px;
  height: 16px;
  transform: rotate(180deg);
}
  .nav-button-left img {
    width: 16px;
    height: 16px;
    transform: rotate(180deg);
  }
.nav-button-right {
  right: -16px;
}
  .nav-button-right {
    right: -16px;
  }
.nav-button-right img {
  width: 16px;
  height: 16px;
}
  .nav-button-right img {
    width: 16px;
    height: 16px;
  }
.card-item {
  flex: 0 0 calc((100% - (var(--visible-count) - 1) * 12px) / var(--visible-count));
  min-width: calc((100% - (var(--visible-count) - 1) * 12px) / var(--visible-count));
  display: flex;
  align-items: center;
  background: linear-gradient(269deg, rgba(27,57,126,0.13) 0%, rgba(33,137,206,0.33) 98.13%, #24AFF4 100%);
  border-radius: 8px 8px 8px 8px;
  padding: 12px 16px;
  transition: all 0.3s ease;
}
  .card-item {
    flex: 0 0
      calc((100% - (var(--visible-count) - 1) * 12px) / var(--visible-count));
    min-width: calc(
      (100% - (var(--visible-count) - 1) * 12px) / var(--visible-count)
    );
    display: flex;
    align-items: center;
    background: linear-gradient(
      269deg,
      rgba(27, 57, 126, 0.13) 0%,
      rgba(33, 137, 206, 0.33) 98.13%,
      #24aff4 100%
    );
    border-radius: 8px 8px 8px 8px;
    padding: 12px 16px;
    transition: all 0.3s ease;
  }
.card-item:hover {
  transform: translateY(-2px);
}
  .card-item:hover {
    transform: translateY(-2px);
  }
.card-icon {
  width: 80px;
  height: 60px;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  flex-shrink: 0;
  margin-right: 12px;
}
  .card-icon {
    width: 80px;
    height: 60px;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    flex-shrink: 0;
    margin-right: 12px;
  }
.card-title {
  display: flex;
  align-items: flex-start;
  flex-direction: column;
  flex: 1;
}
  .card-title {
    display: flex;
    align-items: flex-start;
    flex-direction: column;
    flex: 1;
  }
.card-label {
  font-weight: 400;
  font-size: 14px;
  color: #FFFFFF;
  margin-bottom: 4px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  width: 100%;
}
  .card-label {
    font-weight: 400;
    font-size: 14px;
    color: #ffffff;
    margin-bottom: 4px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    width: 100%;
  }
.card-value {
  display: flex;
  align-items: baseline;
  gap: 4px;
}
  .card-value {
    display: flex;
    align-items: baseline;
    gap: 4px;
  }
.card-rate {
  margin-top: 4px;
  display: flex;
  align-items: center;
  gap: 6px;
  font-weight: 400;
  font-size: 12px;
  color: rgba(255, 255, 255, 0.85);
}
  .card-rate {
    margin-top: 4px;
    display: flex;
    align-items: center;
    gap: 6px;
    font-weight: 400;
    font-size: 12px;
    color: rgba(255, 255, 255, 0.85);
  }
.rate-label {
  opacity: 0.85;
}
  .rate-label {
    opacity: 0.85;
  }
.rate-value {
  font-weight: 500;
}
  .rate-value {
    font-weight: 500;
  }
.value-number {
  font-weight: 400;
  font-size: 14px;
  color: #FFFFFF;
  line-height: 1;
}
  .value-number {
    font-weight: 400;
    font-size: 14px;
    color: #ffffff;
    line-height: 1;
  }
.value-unit {
  font-size: 14px;
  color: #FFFFFF;
  font-weight: 400;
}
  .value-unit {
    font-size: 14px;
    color: #ffffff;
    font-weight: 400;
  }
</style>
src/views/reportAnalysis/salesStatistics/index.vue
@@ -1,14 +1,39 @@
<template>
  <div ref="screenRoot" class="sales-statistics-container" :class="{ 'is-fullscreen': isFullscreen }">
  <div ref="screenRoot"
       class="sales-statistics-container"
       :class="{ 'is-fullscreen': isFullscreen }">
    <div class="bi-bg"></div>
    <div class="bi-topbar">
      <img class="bi-topbar-title-bg" src="@/assets/BI/biaoti.png" alt="销售看板统计" />
      <img class="bi-topbar-title-bg"
           src="@/assets/BI/biaoti.png"
           alt="销售看板统计" />
      <div class="bi-topbar-content">
        <div class="bi-topbar-left">
          <span class="status-sun">☀</span>
          <button class="fullscreen-btn"
                  @click="toggleFullscreen"
                  :title="isFullscreen ? '退出全屏' : '全屏显示'">
            <svg v-if="!isFullscreen"
                 width="1.6vh"
                 height="1.6vh"
                 viewBox="0 0 24 24"
                 fill="none"
                 stroke="currentColor"
                 stroke-width="2">
              <path d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3" />
            </svg>
            <svg v-else
                 width="1.6vh"
                 height="1.6vh"
                 viewBox="0 0 24 24"
                 fill="none"
                 stroke="currentColor"
                 stroke-width="2">
              <path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3" />
            </svg>
          </button>
          <!-- <span class="status-sun">☀</span>
          <span>26℃</span>
          <span class="bi-topbar-sep">湿度:1</span>
          <span class="bi-topbar-sep">湿度:1</span> -->
        </div>
        <div class="bi-topbar-title">销售看板统计</div>
        <div class="bi-topbar-meta">
@@ -16,21 +41,13 @@
          <span class="bi-topbar-sep">|</span>
          <span class="bi-topbar-date">{{ currentDateText }}</span>
        </div>
        <button class="fullscreen-btn" @click="toggleFullscreen" :title="isFullscreen ? '退出全屏' : '全屏显示'">
          <svg v-if="!isFullscreen" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <path d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3"/>
          </svg>
          <svg v-else width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/>
          </svg>
        </button>
      </div>
    </div>
    <div class="bi-dashboard-grid">
      <!-- 左上:销量趋势 -->
      <div class="bi-panel bi-panel-top-left">
        <PanelHeader title="销售分析-砌块" />
        <PanelHeader :isFullscreen="true"
                     title="销售分析-砌块" />
        <div class="panel-tabs">
          <span class="tab-item active">年</span>
          <span class="tab-item">月</span>
@@ -47,13 +64,14 @@
            <span>单位:立方米</span>
            <span class="dot-legend">板材</span>
          </div>
          <div ref="salesVolumeChart" class="echart-fill"></div>
          <div ref="salesVolumeChart"
               class="echart-fill"></div>
        </div>
      </div>
      <!-- 右上:销售金额 -->
      <div class="bi-panel bi-panel-top-right">
        <PanelHeader title="销售分析-板材" />
        <PanelHeader :isFullscreen="true"
                     title="销售分析-板材" />
        <div class="panel-tabs">
          <span class="tab-item active">年</span>
          <span class="tab-item">月</span>
@@ -70,38 +88,54 @@
            <span>单位:件</span>
            <span class="dot-legend">板材</span>
          </div>
          <div ref="salesAmountChart" class="echart-fill"></div>
          <div ref="salesAmountChart"
               class="echart-fill"></div>
        </div>
      </div>
      <!-- 中间中心环 -->
      <div class="center-ring">
        <img
          class="center-ring-bg"
          src="@/assets/BI/zonghetongbingtubiankuang@2x.png"
          alt=""
        />
        <div class="center-ring-content">
          <div class="center-ring-title">销售<br />中心</div>
        <!-- <img class="center-ring-bg"
             src="@/assets/BI/zonghetongbingtubiankuang@2x.png"
             alt="" /> -->
        <!-- <div class="center-ring-content"> -->
        <!-- <div class="center-ring-title">销售<br />中心</div>
          <div class="center-metric m1">
            <div class="center-metric-label">新增客户</div>
            <div class="center-metric-value">{{ centerNewCustomerCount }}</div>
            <div class="center-metric-unit">人</div>
          </div>
          <div class="center-metric m2">
            <div class="center-metric-label">成交总订单</div>
            <div class="center-metric-value">{{ completedOrders }}</div>
            <div class="center-metric-unit">单</div>
          </div>
          <div class="center-metric m3">
            <div class="center-metric-label">新增订单</div>
            <div class="center-metric-value">{{ salesOrderCount }}</div>
            <div class="center-metric-unit">单</div>
          </div>
          <div class="center-metric m4">
            <div class="center-metric-label">总销售区</div>
            <div class="center-metric-value">{{ totalSalesAreaCount }}</div>
            <div class="center-metric-unit">区</div>
          </div> -->
        <!-- </div> -->
        <div class="center-ring-box">
          <div class="center-metric m1">
            <div class="center-metric-label">新增客户</div>
            <div class="center-metric-value">{{ centerNewCustomerCount }}</div>
            <div class="center-metric-unit">人</div>
          </div>
          <div class="center-metric m2">
            <div class="center-metric-label">成交总订单</div>
            <div class="center-metric-value">{{ completedOrders }}</div>
            <div class="center-metric-unit">单</div>
          </div>
          <div class="center-metric m3">
            <div class="center-metric-label">新增订单</div>
            <div class="center-metric-value">{{ salesOrderCount }}</div>
            <div class="center-metric-unit">单</div>
          </div>
          <div class="center-metric m4">
            <div class="center-metric-label">总销售区</div>
            <div class="center-metric-value">{{ totalSalesAreaCount }}</div>
@@ -109,10 +143,10 @@
          </div>
        </div>
      </div>
      <!-- 左下:产品类型销量 -->
      <div class="bi-panel bi-panel-bottom-left">
        <PanelHeader title="客户销量排名分析-砌块" />
        <PanelHeader :isFullscreen="true"
                     title="客户销量排名分析-砌块" />
        <div class="panel-tabs">
          <span class="tab-item active">年</span>
          <span class="tab-item">月</span>
@@ -125,13 +159,14 @@
            <span class="cf-tab">xxx销售区</span>
            <span class="cf-tab">xxx销售区</span>
          </div>
          <div ref="productTypeChart" class="echart-fill"></div>
          <div ref="productTypeChart"
               class="echart-fill"></div>
        </div>
      </div>
      <!-- 中下:新增客户分析(分产品类型趋势) -->
      <div class="bi-panel bi-panel-bottom-center">
        <PanelHeader title="新增客户趋势分析" />
        <PanelHeader :isFullscreen="true"
                     title="新增客户趋势分析" />
        <div class="panel-tabs">
          <span class="tab-item active">年</span>
          <span class="tab-item">月</span>
@@ -144,13 +179,14 @@
          <div class="chart-unit-row chart-unit-single">
            <span>单位:人</span>
          </div>
          <div ref="productTypeTrendChart" class="echart-fill"></div>
          <div ref="productTypeTrendChart"
               class="echart-fill"></div>
        </div>
      </div>
      <!-- 右下:销售区域销量 -->
      <div class="bi-panel bi-panel-bottom-right">
        <PanelHeader title="客户销量排名分析-板材" />
        <PanelHeader :isFullscreen="true"
                     title="客户销量排名分析-板材" />
        <div class="panel-tabs">
          <span class="tab-item active">年</span>
          <span class="tab-item">月</span>
@@ -163,7 +199,8 @@
            <span class="cf-tab">xxx销售区</span>
            <span class="cf-tab">xxx销售区</span>
          </div>
          <div ref="salesAreaChart" class="echart-fill"></div>
          <div ref="salesAreaChart"
               class="echart-fill"></div>
        </div>
      </div>
    </div>
@@ -189,8 +226,8 @@
  const isFullscreen = ref(false);
  // 顶部栏时间(用于匹配BI大屏效果图)
  const now = ref(dayjs())
  const currentTime = computed(() => now.value.format("HH:mm:ss"))
  const now = ref(dayjs());
  const currentTime = computed(() => now.value.format("HH:mm:ss"));
  const currentDateText = computed(() => {
    const weekMap = {
      0: "星期日",
@@ -200,10 +237,10 @@
      4: "星期四",
      5: "星期五",
      6: "星期六",
    }
    return `${now.value.format("YYYY-MM-DD")} ${weekMap[now.value.day()] || ""}`
  })
  let timeTicker = null
    };
    return `${now.value.format("YYYY-MM-DD")} ${weekMap[now.value.day()] || ""}`;
  });
  let timeTicker = null;
  const handleFullscreenChange = () => {
    isFullscreen.value = !!document.fullscreenElement;
@@ -566,8 +603,8 @@
        trigger: "axis",
        backgroundColor: "rgba(0,0,0,0.55)",
        borderColor: "rgba(64,158,255,0.25)",
        borderWidth: 1,
        textStyle: { color: "#B8C8E0" },
        borderWidth: getResponsiveValue(1),
        textStyle: { color: "#B8C8E0", fontSize: getResponsiveValue(11) },
        formatter: "{b}: {c} 立方米",
      },
      grid: {
@@ -582,14 +619,22 @@
        data: periods,
        axisLine: { lineStyle: { color: "rgba(184,200,224,0.25)" } },
        axisTick: { show: false },
        axisLabel: { color: "#B8C8E0", fontSize: 11, margin: 10 },
        axisLabel: {
          color: "#B8C8E0",
          fontSize: getResponsiveValue(11),
          margin: getResponsiveValue(10),
        },
        splitLine: { show: false },
      },
      yAxis: {
        type: "value",
        name: "",
        axisLine: { lineStyle: { color: "rgba(184,200,224,0.25)" } },
        axisLabel: { color: "#B8C8E0", fontSize: 11, margin: 8 },
        axisLabel: {
          color: "#B8C8E0",
          fontSize: getResponsiveValue(11),
          margin: getResponsiveValue(8),
        },
        splitLine: { lineStyle: { color: "rgba(184,200,224,0.12)" } },
      },
      series: [
@@ -597,8 +642,8 @@
          data: values,
          type: "line",
          smooth: true,
          symbolSize: 8,
          lineStyle: { width: 3, color: "#00A4ED" },
          symbolSize: getResponsiveValue(8),
          lineStyle: { width: getResponsiveValue(3), color: "#00A4ED" },
          itemStyle: { color: "#00A4ED" },
          areaStyle: {
            opacity: 1,
@@ -623,8 +668,8 @@
        trigger: "axis",
        backgroundColor: "rgba(0,0,0,0.55)",
        borderColor: "rgba(64,158,255,0.25)",
        borderWidth: 1,
        textStyle: { color: "#B8C8E0" },
        borderWidth: getResponsiveValue(1),
        textStyle: { color: "#B8C8E0", fontSize: getResponsiveValue(11) },
        formatter: "{b}: {c} 万元",
      },
      grid: {
@@ -639,13 +684,21 @@
        data: periods,
        axisLine: { lineStyle: { color: "rgba(184,200,224,0.25)" } },
        axisTick: { show: false },
        axisLabel: { color: "#B8C8E0", fontSize: 11, margin: 10 },
        axisLabel: {
          color: "#B8C8E0",
          fontSize: getResponsiveValue(11),
          margin: getResponsiveValue(10),
        },
      },
      yAxis: {
        type: "value",
        name: "",
        axisLine: { lineStyle: { color: "rgba(184,200,224,0.25)" } },
        axisLabel: { color: "#B8C8E0", fontSize: 11, margin: 8 },
        axisLabel: {
          color: "#B8C8E0",
          fontSize: getResponsiveValue(11),
          margin: getResponsiveValue(8),
        },
        splitLine: { lineStyle: { color: "rgba(184,200,224,0.12)" } },
      },
      series: [
@@ -653,11 +706,11 @@
          data: values,
          type: "line",
          smooth: true,
          symbolSize: 8,
          symbolSize: getResponsiveValue(8),
          itemStyle: {
            color: "#00A4ED",
          },
          lineStyle: { width: 3, color: "#00A4ED" },
          lineStyle: { width: getResponsiveValue(3), color: "#00A4ED" },
          areaStyle: {
            opacity: 1,
            color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
@@ -674,7 +727,14 @@
  const productTypeChartOption = computed(() => {
    const types = ["客户BB", "客户AA", "客户CC", "客户DD", "客户DD", "客户DD"];
    const values = [130, 120, 102, 90, 90, 70];
    const barColors = ["#34D8F7", "#4A8BFF", "#8A6BFF", "#C8C447", "#C8C447", "#C8C447"];
    const barColors = [
      "#34D8F7",
      "#4A8BFF",
      "#8A6BFF",
      "#C8C447",
      "#C8C447",
      "#C8C447",
    ];
    return {
      backgroundColor: "transparent",
@@ -683,8 +743,8 @@
        axisPointer: { type: "shadow" },
        backgroundColor: "rgba(0,0,0,0.55)",
        borderColor: "rgba(64,158,255,0.25)",
        borderWidth: 1,
        textStyle: { color: "#B8C8E0" },
        borderWidth: getResponsiveValue(1),
        textStyle: { color: "#B8C8E0", fontSize: getResponsiveValue(11) },
        formatter: "{b}: {c} 立方米",
      },
      grid: {
@@ -697,7 +757,7 @@
      xAxis: {
        type: "value",
        axisLine: { show: false },
        axisLabel: { color: "#B8C8E0", fontSize: 11 },
        axisLabel: { color: "#B8C8E0", fontSize: getResponsiveValue(11) },
        splitLine: { lineStyle: { color: "rgba(184,200,224,0.12)" } },
      },
      yAxis: {
@@ -705,17 +765,26 @@
        data: types,
        axisTick: { show: false },
        axisLine: { show: false },
        axisLabel: { color: "#B8C8E0", fontSize: 11, margin: 8 },
        axisLabel: {
          color: "#B8C8E0",
          fontSize: getResponsiveValue(11),
          margin: getResponsiveValue(8),
        },
      },
      series: [
        {
          name: "销量(立方米)",
          type: "bar",
          barWidth: 14,
          barWidth: getResponsiveValue(14),
          data: values,
          itemStyle: {
            color: params => barColors[params.dataIndex] || "#00A4ED",
            borderRadius: [6, 6, 6, 6],
            borderRadius: [
              getResponsiveValue(6),
              getResponsiveValue(6),
              getResponsiveValue(6),
              getResponsiveValue(6),
            ],
          },
          label: {
            show: false,
@@ -729,7 +798,14 @@
  const salesAreaChartOption = computed(() => {
    const areas = ["客户BB", "客户AA", "客户CC", "客户DD", "客户DD", "客户DD"];
    const values = [130, 120, 102, 90, 90, 70];
    const barColors = ["#34D8F7", "#4A8BFF", "#8A6BFF", "#C8C447", "#C8C447", "#C8C447"];
    const barColors = [
      "#34D8F7",
      "#4A8BFF",
      "#8A6BFF",
      "#C8C447",
      "#C8C447",
      "#C8C447",
    ];
    return {
      backgroundColor: "transparent",
@@ -738,8 +814,8 @@
        axisPointer: { type: "shadow" },
        backgroundColor: "rgba(0,0,0,0.55)",
        borderColor: "rgba(64,158,255,0.25)",
        borderWidth: 1,
        textStyle: { color: "#B8C8E0" },
        borderWidth: getResponsiveValue(1),
        textStyle: { color: "#B8C8E0", fontSize: getResponsiveValue(11) },
        formatter: "{b}: {c} 立方米",
      },
      grid: {
@@ -752,7 +828,7 @@
      xAxis: {
        type: "value",
        axisLine: { show: false },
        axisLabel: { color: "#B8C8E0", fontSize: 11 },
        axisLabel: { color: "#B8C8E0", fontSize: getResponsiveValue(11) },
        splitLine: { lineStyle: { color: "rgba(184,200,224,0.12)" } },
      },
      yAxis: {
@@ -760,17 +836,26 @@
        data: areas,
        axisTick: { show: false },
        axisLine: { show: false },
        axisLabel: { color: "#B8C8E0", fontSize: 11, margin: 8 },
        axisLabel: {
          color: "#B8C8E0",
          fontSize: getResponsiveValue(11),
          margin: getResponsiveValue(8),
        },
      },
      series: [
        {
          name: "销量(立方米)",
          type: "bar",
          barWidth: 14,
          barWidth: getResponsiveValue(14),
          data: values,
          itemStyle: {
            color: params => barColors[params.dataIndex] || "#00A4ED",
            borderRadius: [6, 6, 6, 6],
            borderRadius: [
              getResponsiveValue(6),
              getResponsiveValue(6),
              getResponsiveValue(6),
              getResponsiveValue(6),
            ],
          },
        },
      ],
@@ -781,33 +866,42 @@
  const productTypeTrendChartOption = computed(() => {
    const typeOrder = ["AAA销售区", "BBB销售区", "CCC销售区", "DDD销售区"];
    const colorMap = {
      "AAA销售区": "#65A0FF",
      "BBB销售区": "#33F5FF",
      "CCC销售区": "#FFD54A",
      "DDD销售区": "#EE52FF",
      AAA销售区: "#65A0FF",
      BBB销售区: "#33F5FF",
      CCC销售区: "#FFD54A",
      DDD销售区: "#EE52FF",
    };
    const areaColorMap = {
      "AAA销售区": "rgba(101,160,255,0.28)",
      "BBB销售区": "rgba(51,245,255,0.30)",
      "CCC销售区": "rgba(255,213,74,0.25)",
      "DDD销售区": "rgba(238,82,255,0.25)",
      AAA销售区: "rgba(101,160,255,0.28)",
      BBB销售区: "rgba(51,245,255,0.30)",
      CCC销售区: "rgba(255,213,74,0.25)",
      DDD销售区: "rgba(238,82,255,0.25)",
    };
    const periods = ["6/9", "6/10", "6/11", "6/12", "6/12", "6/13", "6/14", "6/15"];
    const periods = [
      "6/9",
      "6/10",
      "6/11",
      "6/12",
      "6/12",
      "6/13",
      "6/14",
      "6/15",
    ];
    const map = {
      "AAA销售区": [85, 112, 112, 112, 140, 112, 112, 140],
      "BBB销售区": [140, 180, 180, 180, 230, 180, 180, 230],
      "CCC销售区": [112, 140, 140, 140, 180, 140, 140, 180],
      "DDD销售区": [200, 165, 200, 200, 165, 165, 140, 140],
      AAA销售区: [85, 112, 112, 112, 140, 112, 112, 140],
      BBB销售区: [140, 180, 180, 180, 230, 180, 180, 230],
      CCC销售区: [112, 140, 140, 140, 180, 140, 140, 180],
      DDD销售区: [200, 165, 200, 200, 165, 165, 140, 140],
    };
    const series = typeOrder.map((t) => ({
    const series = typeOrder.map(t => ({
      name: t,
      type: "line",
      smooth: true,
      symbolSize: 7,
      symbolSize: getResponsiveValue(7),
      showSymbol: true,
      data: map[t] || [],
      lineStyle: { width: 3, color: colorMap[t] },
      lineStyle: { width: getResponsiveValue(3), color: colorMap[t] },
      itemStyle: { color: colorMap[t] },
      areaStyle: {
        opacity: 0.25,
@@ -820,20 +914,25 @@
    return {
      backgroundColor: "transparent",
      legend: {
        top: 10,
        top: getResponsiveValue(10),
        left: "center",
        textStyle: { color: "#B8C8E0", fontSize: 11, padding: [0, 0, 0, 2] },
        itemWidth: 12,
        itemHeight: 10,
        itemGap: 18,
        textStyle: {
          color: "#B8C8E0",
          fontSize: getResponsiveValue(11),
          padding: [0, 0, 0, getResponsiveValue(2)],
        },
        itemWidth: getResponsiveValue(12),
        itemHeight: getResponsiveValue(10),
        itemGap: getResponsiveValue(18),
      },
      tooltip: {
        trigger: "axis",
        backgroundColor: "rgba(0,0,0,0.55)",
        borderColor: "rgba(64,158,255,0.25)",
        borderWidth: 1,
        textStyle: { color: "#B8C8E0" },
        borderWidth: getResponsiveValue(1),
        textStyle: { color: "#B8C8E0", fontSize: getResponsiveValue(11) },
      },
      grid: {
        left: "10%",
@@ -847,13 +946,21 @@
        data: periods,
        axisLine: { lineStyle: { color: "rgba(184,200,224,0.25)" } },
        axisTick: { show: false },
        axisLabel: { color: "#B8C8E0", fontSize: 11, margin: 10 },
        axisLabel: {
          color: "#B8C8E0",
          fontSize: getResponsiveValue(11),
          margin: getResponsiveValue(10),
        },
      },
      yAxis: {
        type: "value",
        name: "",
        axisLine: { lineStyle: { color: "rgba(184,200,224,0.25)" } },
        axisLabel: { color: "#B8C8E0", fontSize: 11, margin: 8 },
        axisLabel: {
          color: "#B8C8E0",
          fontSize: getResponsiveValue(11),
          margin: getResponsiveValue(8),
        },
        splitLine: { lineStyle: { color: "rgba(184,200,224,0.12)" } },
      },
      series,
@@ -883,14 +990,17 @@
      tooltip: {
        trigger: "axis",
        formatter: "{b}: {c} 立方米",
        textStyle: { fontSize: getResponsiveValue(11) },
      },
      xAxis: {
        type: "category",
        data: periods,
        axisLabel: { fontSize: getResponsiveValue(11) },
      },
      yAxis: {
        type: "value",
        name: "累计销量(立方米)",
        axisLabel: { fontSize: getResponsiveValue(11) },
      },
      series: [
        {
@@ -904,7 +1014,7 @@
            color: "#E6A23C",
          },
          lineStyle: {
            width: 3,
            width: getResponsiveValue(3),
          },
        },
      ],
@@ -934,14 +1044,17 @@
      tooltip: {
        trigger: "axis",
        formatter: "{b}: {c} 万元",
        textStyle: { fontSize: getResponsiveValue(11) },
      },
      xAxis: {
        type: "category",
        data: periods,
        axisLabel: { fontSize: getResponsiveValue(11) },
      },
      yAxis: {
        type: "value",
        name: "累计销售金额(万元)",
        axisLabel: { fontSize: getResponsiveValue(11) },
      },
      series: [
        {
@@ -968,6 +1081,11 @@
  const handleFilterChange = () => {
    // 处理筛选条件变化
    updateCharts();
  };
  const baseWidth = ref(1650);
  // 计算响应式值
  const getResponsiveValue = baseValue => {
    return Math.round((baseValue * window.innerWidth) / baseWidth.value);
  };
  // 初始化图表
@@ -1013,12 +1131,13 @@
    updateCharts();
  };
  // 更新图表
  const updateCharts = () => {
    // 更新销量趋势图表
    if (salesVolumeChartInstance) {
      salesVolumeChartInstance.setOption(salesVolumeChartOption.value);
      salesVolumeChartInstance.setOption(
        JSON.parse(JSON.stringify(salesVolumeChartOption.value))
      );
    }
    // 更新销售金额趋势图表
@@ -1058,6 +1177,10 @@
  // 监听窗口大小变化
  const handleResize = () => {
    console.log("resize");
    // 先更新图表选项,重新计算响应式值
    updateCharts();
    // 然后调整图表大小
    if (salesVolumeChartInstance) {
      salesVolumeChartInstance.resize();
    }
@@ -1170,9 +1293,9 @@
  .sales-statistics-container {
    position: relative;
    width: 100%;
    min-height: calc(100vh - 84px);
    min-height: calc(100vh - 8.4vh);
    overflow: hidden;
    color: #B8C8E0;
    color: #b8c8e0;
    background: #041026;
  }
@@ -1185,7 +1308,7 @@
  .bi-bg {
    position: absolute;
    inset: 0;
    background-image: url("@/assets/BI/backImage@2x.png");
    /* background-image: url("@/assets/BI/backImage@2x.png"); */
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
@@ -1196,7 +1319,7 @@
  .bi-topbar {
    position: relative;
    z-index: 2;
    height: 58px;
    height: 5.8vh;
    display: flex;
    align-items: center;
    justify-content: center;
@@ -1206,7 +1329,7 @@
    position: absolute;
    top: 0;
    left: 0;
    height: 58px;
    height: 8vh;
    width: 100%;
    object-fit: cover;
    z-index: 0;
@@ -1217,7 +1340,7 @@
    position: relative;
    z-index: 1;
    width: 100%;
    padding: 0 28px;
    padding: 0 2.8vh;
    display: flex;
    align-items: center;
    justify-content: center;
@@ -1227,58 +1350,57 @@
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    font-size: 26px;
    font-size: 2.6vh;
    font-weight: 800;
    letter-spacing: 1px;
    background: linear-gradient(180deg, #ffffff 0%, #B8DFFF 100%);
    letter-spacing: 0.1vh;
    background: linear-gradient(180deg, #ffffff 0%, #b8dfff 100%);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
    text-shadow: 0 0 26px rgba(0, 164, 237, 0.55);
    text-shadow: 0 0 2.6vh rgba(0, 164, 237, 0.55);
  }
  .bi-topbar-left {
    position: absolute;
    left: 10px;
    left: 1vh;
    display: flex;
    align-items: center;
    gap: 8px;
    gap: 0.8vh;
    color: rgba(208, 231, 255, 0.85);
    font-size: 13px;
    font-size: 1.3vh;
  }
  .status-sun {
    color: #ffd85e;
    text-shadow: 0 0 10px rgba(255, 216, 94, 0.8);
    font-size: 13px;
    text-shadow: 0 0 1vh rgba(255, 216, 94, 0.8);
    font-size: 1.3vh;
    line-height: 1;
  }
  .bi-topbar-meta {
    position: absolute;
    right: 52px;
    top: 16px;
    font-size: 12px;
    right: 5.2vh;
    /* top: 1.6vh; */
    font-size: 1.2vh;
    font-weight: 500;
    letter-spacing: 0.5px;
    letter-spacing: 0.05vh;
    color: rgba(208, 231, 255, 0.85);
    display: flex;
    align-items: center;
    gap: 10px;
    gap: 1vh;
  }
  .fullscreen-btn {
    position: absolute;
    right: 10px;
    top: 12px;
    bottom: -1vh;
    transform: none;
    border: 1px solid rgba(64, 158, 255, 0.45);
    border: 0.1vh solid rgba(64, 158, 255, 0.45);
    background: rgba(0, 164, 237, 0.14);
    color: #d0e7ff;
    width: 34px;
    height: 34px;
    border-radius: 6px;
    width: 3.4vh;
    height: 3.4vh;
    border-radius: 0.6vh;
    padding: 0;
    cursor: pointer;
    transition: all 0.2s ease;
@@ -1290,7 +1412,7 @@
  .fullscreen-btn:hover {
    background: rgba(0, 164, 237, 0.24);
    box-shadow: 0 0 12px rgba(0, 164, 237, 0.3);
    box-shadow: 0 0 1.2vh rgba(0, 164, 237, 0.3);
  }
  .bi-topbar-sep {
@@ -1301,61 +1423,61 @@
  .bi-dashboard-grid {
    position: relative;
    z-index: 2;
    height: calc(100vh - 84px - 58px);
    min-height: 450px;
    padding: 10px 18px 14px;
    height: calc(100vh - 8.4vh - 5.8vh);
    min-height: 45vh;
    padding: 1vh 1.8vh 1.4vh;
    display: grid;
    grid-template-columns: 1fr 1.05fr 1fr;
    grid-template-rows: 1fr 1fr;
    gap: 12px;
    gap: 1.2vh;
  }
  .sales-statistics-container.is-fullscreen .bi-dashboard-grid {
    height: calc(100vh - 58px);
    height: calc(100vh - 5.8vh);
  }
  .bi-panel {
    background: rgba(3, 18, 46, 0.62);
    border: 1px solid rgba(64, 158, 255, 0.35);
    border-radius: 4px;
    border: 0.1vh solid rgba(64, 158, 255, 0.35);
    border-radius: 0.4vh;
    overflow: hidden;
    box-shadow: 0 0 22px rgba(0, 164, 237, 0.12);
    box-shadow: 0 0 2.2vh rgba(0, 164, 237, 0.12);
    display: flex;
    flex-direction: column;
    position: relative;
  }
  .bi-panel-title {
    height: 44px;
    height: 4.4vh;
    display: flex;
    align-items: center;
    padding: 0 18px;
    font-size: 15px;
    padding: 0 1.8vh;
    font-size: 1.5vh;
    font-weight: 700;
    color: #B8C8E0;
    color: #b8c8e0;
    background: linear-gradient(
      90deg,
      rgba(0, 164, 237, 0.2),
      rgba(0, 164, 237, 0.04)
    );
    border-bottom: 1px solid rgba(64, 158, 255, 0.25);
    border-bottom: 0.1vh solid rgba(64, 158, 255, 0.25);
  }
  .panel-tabs {
    position: absolute;
    top: 8px;
    right: 12px;
    top: 0.8vh;
    right: 1.2vh;
    display: flex;
    gap: 6px;
    gap: 0.6vh;
    z-index: 4;
  }
  .tab-item {
    font-size: 12px;
    font-size: 1.2vh;
    color: rgba(184, 200, 224, 0.75);
    padding: 1px 5px;
    border: 1px solid rgba(64, 158, 255, 0.25);
    border-radius: 3px;
    padding: 0.1vh 0.5vh;
    border: 0.1vh solid rgba(64, 158, 255, 0.25);
    border-radius: 0.3vh;
    line-height: 1.4;
  }
@@ -1367,7 +1489,7 @@
  .bi-panel-body {
    flex: 1;
    padding: 8px 10px;
    padding: 0.8vh 1vh;
  }
  .echart-fill {
@@ -1377,21 +1499,21 @@
  .chart-filter-tabs {
    display: flex;
    gap: 6px;
    margin: 0 0 5px 0;
    gap: 0.6vh;
    margin: 0 0 0.5vh 0;
  }
  .cf-tab {
    font-size: 11px;
    font-size: 1.1vh;
    color: rgba(184, 200, 224, 0.68);
    background: rgba(18, 56, 106, 0.65);
    border: 1px solid rgba(64, 158, 255, 0.25);
    padding: 3px 9px;
    border: 0.1vh solid rgba(64, 158, 255, 0.25);
    padding: 0.3vh 0.9vh;
    line-height: 1;
  }
  .cf-tab.active {
    color: #D9ECFF;
    color: #d9ecff;
    background: rgba(0, 108, 208, 0.85);
    border-color: rgba(64, 158, 255, 0.65);
  }
@@ -1399,57 +1521,57 @@
  .chart-unit-row {
    display: flex;
    justify-content: space-between;
    font-size: 12px;
    font-size: 1.2vh;
    color: rgba(208, 231, 255, 0.88);
    margin-bottom: 4px;
    padding: 0 2px;
    margin-bottom: 0.4vh;
    padding: 0 0.2vh;
  }
  .dot-legend::before {
    content: "";
    display: inline-block;
    width: 8px;
    height: 8px;
    background: #65A0FF;
    margin-right: 6px;
    width: 0.8vh;
    height: 0.8vh;
    background: #65a0ff;
    margin-right: 0.6vh;
  }
  .chart-mini-title {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 18px;
    color: #D9ECFF;
    gap: 0.8vh;
    font-size: 1.8vh;
    color: #d9ecff;
    font-weight: 700;
    margin: 0 0 8px 0;
    margin: 0 0 0.8vh 0;
    line-height: 1;
  }
  .diamond {
    width: 10px;
    height: 10px;
    background: #1E8BFF;
    width: 1vh;
    height: 1vh;
    background: #1e8bff;
    transform: rotate(45deg);
    display: inline-block;
  }
  .chart-unit-single {
    justify-content: flex-start;
    margin-bottom: 2px;
    margin-bottom: 0.2vh;
  }
  .bi-panel-top-left .echart-fill,
  .bi-panel-top-right .echart-fill {
    height: calc(100% - 44px);
    height: calc(100% - 4.4vh);
  }
  .bi-panel-bottom-left .echart-fill,
  .bi-panel-bottom-right .echart-fill {
    height: calc(100% - 28px);
    height: calc(100% - 2.8vh);
  }
  .bi-panel-bottom-center .echart-fill {
    height: calc(100% - 44px);
    height: calc(100% - 4.4vh);
  }
  .bi-panel-top-left {
@@ -1484,13 +1606,24 @@
    grid-column: 2;
    grid-row: 1 / span 2;
    position: absolute;
    left:25%;
    background: url("@/assets/BI/imageSS@2x.png") no-repeat bottom center;
    background-size: 100% 30%;
    left: 25%;
    top: 25%;
    transform: translate(-50%, -50%);
    width: 400px;
    height: 275px;
    width: 60vh;
    height: 40.5vh;
    z-index: 3;
    pointer-events: none;
  }
  .center-ring-box {
    position: absolute;
    /* inset: 0; */
    height: 100%;
    width: 100%;
    /* background-color: #fff; */
    background: url("@/assets/BI/imageSStop.png") no-repeat center center;
    background-size: 80% 90%;
  }
  .center-ring-bg {
@@ -1511,18 +1644,18 @@
    position: absolute;
    left: 50%;
    top: 56%;
    width: 370px;
    height: 146px;
    width: 37vh;
    height: 14.6vh;
    transform: translate(-50%, -50%) rotate(-18deg);
    border: 2px solid rgba(40, 186, 255, 0.45);
    border: 0.2vh solid rgba(40, 186, 255, 0.45);
    border-radius: 50%;
    filter: drop-shadow(0 0 8px rgba(0, 164, 237, 0.35));
    filter: drop-shadow(0 0 0.8vh rgba(0, 164, 237, 0.35));
    opacity: 0.7;
  }
  .center-ring-content::after {
    width: 360px;
    height: 150px;
    width: 36vh;
    height: 15vh;
    transform: translate(-50%, -50%) rotate(26deg);
    border-color: rgba(80, 220, 255, 0.35);
    opacity: 0.55;
@@ -1533,12 +1666,12 @@
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-size: 36px;
    font-size: 3.6vh;
    line-height: 1.05;
    text-align: center;
    font-weight: 900;
    color: #EAF6FF;
    text-shadow: 0 0 22px rgba(0, 164, 237, 0.55);
    color: #eaf6ff;
    text-shadow: 0 0 2.2vh rgba(0, 164, 237, 0.55);
    z-index: 2;
  }
@@ -1547,121 +1680,111 @@
    position: absolute;
    left: 50%;
    top: 50%;
    width: 155px;
    height: 155px;
    width: 15.5vh;
    height: 15.5vh;
    transform: translate(-50%, -50%);
    background: radial-gradient(circle, rgba(43, 199, 255, 0.26) 0%, rgba(8, 28, 61, 0.86) 70%);
    border: 2px solid rgba(39, 198, 255, 0.46);
    background: radial-gradient(
      circle,
      rgba(43, 199, 255, 0.26) 0%,
      rgba(8, 28, 61, 0.86) 70%
    );
    border: 0.2vh solid rgba(39, 198, 255, 0.46);
    border-radius: 50%;
    box-shadow: 0 0 20px rgba(0, 164, 237, 0.45), inset 0 0 26px rgba(0, 164, 237, 0.2);
    box-shadow: 0 0 2vh rgba(0, 164, 237, 0.45),
      inset 0 0 2.6vh rgba(0, 164, 237, 0.2);
    z-index: -1;
  }
  .center-metric {
    position: absolute;
    width: 155px;
    width: 15.5vh;
    z-index: 3;
    text-align: center;
    height: 120px;
    height: 12vh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
  }
  .center-metric::before {
    content: "";
    position: absolute;
    left: 50%;
    top: 50%;
    width: 104px;
    height: 104px;
    transform: translate(-50%, -50%);
    border-radius: 50%;
    border: 2px solid rgba(71, 223, 255, 0.4);
    background: radial-gradient(circle, rgba(37, 177, 255, 0.25) 0%, rgba(8, 33, 69, 0.55) 70%);
    box-shadow: 0 0 18px rgba(0, 164, 237, 0.35), inset 0 0 18px rgba(0, 164, 237, 0.2);
    z-index: -1;
  }
  .center-metric-label {
    font-size: 12px;
    font-size: 1.2vh;
    font-weight: 500;
    color: rgba(234, 246, 255, 0.9);
    margin-top: 0;
  }
  .center-metric-value {
    font-size: 34px;
    font-size: 3.4vh;
    font-weight: 800;
    color: #EAF6FF;
    text-shadow: 0 0 8px rgba(0, 229, 255, 0.22);
    line-height: 1.0;
    color: #eaf6ff;
    text-shadow: 0 0 0.8vh rgba(0, 229, 255, 0.22);
    line-height: 1;
  }
  .center-metric-unit {
    margin-top: 0;
    font-size: 12px;
    font-size: 1.2vh;
    color: rgba(208, 231, 255, 0.85);
  }
  .m1 {
    top: -6px;
    left: -26px;
    top: 2.5vh;
    left: 2.3vw;
    text-align: left;
  }
  .m2 {
    top: -6px;
    right: -26px;
    top: 4.1vh;
    right: 4.3vw;
    text-align: right;
  }
  .m3 {
    bottom: 66px;
    left: -30px;
    bottom: 7.9vh;
    left: 4vh;
    text-align: left;
  }
  .m4 {
    bottom: 66px;
    right: -30px;
    bottom: 7vh;
    right: 5.4vh;
    text-align: right;
  }
  @media (max-width: 1100px) {
    .bi-topbar-content {
      padding: 0 14px;
      padding: 0 1.4vh;
    }
    .center-ring {
      left: 45.2%;
      width: 330px;
      height: 245px;
      top: 24px;
      width: 33vh;
      height: 24.5vh;
      top: 2.4vh;
    }
    .center-ring-title {
      top: 50%;
      font-size: 28px;
      font-size: 2.8vh;
      transform: translate(-50%, -50%);
    }
    .center-metric {
      height: 105px;
      height: 10.5vh;
    }
    .m1 {
      top: 52px;
      left: 42px;
      top: 5.2vh;
      left: 4.2vh;
    }
    .m2 {
      top: 54px;
      right: 42px;
      top: 5.4vh;
      right: 4.2vh;
    }
    .m3 {
      bottom: 62px;
      left: 48px;
      bottom: 6.2vh;
      left: 4.8vh;
    }
    .m4 {
      bottom: 68px;
      right: 44px;
      bottom: 6.8vh;
      right: 4.4vh;
    }
  }
</style>