652 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
		
		
			
		
	
	
			652 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| 
								 | 
							
								<template>
							 | 
						|||
| 
								 | 
							
								  <div class="graph-container">
							 | 
						|||
| 
								 | 
							
								    <div class="echarts-container">
							 | 
						|||
| 
								 | 
							
								      <div class="zoom-controls">
							 | 
						|||
| 
								 | 
							
								        <button class="zoom-btn" title="확대" @click="zoomIn">
							 | 
						|||
| 
								 | 
							
								          <span>+</span>
							 | 
						|||
| 
								 | 
							
								        </button>
							 | 
						|||
| 
								 | 
							
								        <button class="zoom-btn" title="축소" @click="zoomOut">
							 | 
						|||
| 
								 | 
							
								          <span>−</span>
							 | 
						|||
| 
								 | 
							
								        </button>
							 | 
						|||
| 
								 | 
							
								        <button class="zoom-btn reset" title="초기화" @click="resetZoom">
							 | 
						|||
| 
								 | 
							
								          <span>⟲</span>
							 | 
						|||
| 
								 | 
							
								        </button>
							 | 
						|||
| 
								 | 
							
								      </div>
							 | 
						|||
| 
								 | 
							
								      <div ref="growthChartRef" class="echarts-graph"></div>
							 | 
						|||
| 
								 | 
							
								      <div class="yaxis-scroll-bar-overlay">
							 | 
						|||
| 
								 | 
							
								        <input
							 | 
						|||
| 
								 | 
							
								          v-model.number="yAxisScrollIndex"
							 | 
						|||
| 
								 | 
							
								          type="range"
							 | 
						|||
| 
								 | 
							
								          min="0"
							 | 
						|||
| 
								 | 
							
								          :max="Math.max(0, filteredYAxisList.length - 2)"
							 | 
						|||
| 
								 | 
							
								          :disabled="filteredYAxisList.length <= 2"
							 | 
						|||
| 
								 | 
							
								        />
							 | 
						|||
| 
								 | 
							
								      </div>
							 | 
						|||
| 
								 | 
							
								    </div>
							 | 
						|||
| 
								 | 
							
								  </div>
							 | 
						|||
| 
								 | 
							
								</template>
							 | 
						|||
| 
								 | 
							
								<script setup lang="ts">
							 | 
						|||
| 
								 | 
							
								import { ref, onMounted, watch, computed, onUnmounted } from "vue";
							 | 
						|||
| 
								 | 
							
								import * as echarts from "echarts";
							 | 
						|||
| 
								 | 
							
								const props = defineProps<{ batchIndex: number }>();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								// --- 기존 culture-graph.vue에서 필요한 상수/데이터 복사 ---
							 | 
						|||
| 
								 | 
							
								const pastelColors = [
							 | 
						|||
| 
								 | 
							
								  "#A3D8F4",
							 | 
						|||
| 
								 | 
							
								  "#F7B7A3",
							 | 
						|||
| 
								 | 
							
								  "#B5EAD7",
							 | 
						|||
| 
								 | 
							
								  "#FFDAC1",
							 | 
						|||
| 
								 | 
							
								  "#C7CEEA",
							 | 
						|||
| 
								 | 
							
								  "#FFF1BA",
							 | 
						|||
| 
								 | 
							
								  "#FFB7B2",
							 | 
						|||
| 
								 | 
							
								  "#B4A7D6",
							 | 
						|||
| 
								 | 
							
								  "#AED9E0",
							 | 
						|||
| 
								 | 
							
								  "#FFC3A0",
							 | 
						|||
| 
								 | 
							
								  "#E2F0CB",
							 | 
						|||
| 
								 | 
							
								  "#FFB347",
							 | 
						|||
| 
								 | 
							
								  "#C1C8E4",
							 | 
						|||
| 
								 | 
							
								  "#FFFACD",
							 | 
						|||
| 
								 | 
							
								  "#FFD1DC",
							 | 
						|||
| 
								 | 
							
								];
							 | 
						|||
| 
								 | 
							
								const yAxisList = [
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "ORP",
							 | 
						|||
| 
								 | 
							
								    unit: "",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[0],
							 | 
						|||
| 
								 | 
							
								    min: 0,
							 | 
						|||
| 
								 | 
							
								    max: 1000,
							 | 
						|||
| 
								 | 
							
								    ticks: [1000, 800, 600, 400, 200, 0],
							 | 
						|||
| 
								 | 
							
								    fontWeight: "bold",
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "Air flow",
							 | 
						|||
| 
								 | 
							
								    unit: "(L/min)",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[1],
							 | 
						|||
| 
								 | 
							
								    min: 0,
							 | 
						|||
| 
								 | 
							
								    max: 30,
							 | 
						|||
| 
								 | 
							
								    ticks: [30, 25, 20, 15, 10, 5, 0],
							 | 
						|||
| 
								 | 
							
								    fontWeight: "bold",
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "DO",
							 | 
						|||
| 
								 | 
							
								    unit: "",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[2],
							 | 
						|||
| 
								 | 
							
								    min: 0,
							 | 
						|||
| 
								 | 
							
								    max: 200,
							 | 
						|||
| 
								 | 
							
								    ticks: [200, 150, 100, 50, 0],
							 | 
						|||
| 
								 | 
							
								    fontWeight: "bold",
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "Feed TK1",
							 | 
						|||
| 
								 | 
							
								    unit: "(L)",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[3],
							 | 
						|||
| 
								 | 
							
								    min: 0,
							 | 
						|||
| 
								 | 
							
								    max: 10,
							 | 
						|||
| 
								 | 
							
								    ticks: [10, 8, 6, 4, 2, 0],
							 | 
						|||
| 
								 | 
							
								    fontWeight: "bold",
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "Feed TK2",
							 | 
						|||
| 
								 | 
							
								    unit: "(L)",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[4],
							 | 
						|||
| 
								 | 
							
								    min: 0,
							 | 
						|||
| 
								 | 
							
								    max: 10,
							 | 
						|||
| 
								 | 
							
								    ticks: [10, 8, 6, 4, 2, 0],
							 | 
						|||
| 
								 | 
							
								    fontWeight: "bold",
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "pH",
							 | 
						|||
| 
								 | 
							
								    unit: "",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[5],
							 | 
						|||
| 
								 | 
							
								    min: 6.0,
							 | 
						|||
| 
								 | 
							
								    max: 8.0,
							 | 
						|||
| 
								 | 
							
								    ticks: [8.0, 7.5, 7.0, 6.5, 6.0],
							 | 
						|||
| 
								 | 
							
								    fontWeight: "bold",
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "Pressure",
							 | 
						|||
| 
								 | 
							
								    unit: "(bar)",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[6],
							 | 
						|||
| 
								 | 
							
								    min: 0,
							 | 
						|||
| 
								 | 
							
								    max: 2,
							 | 
						|||
| 
								 | 
							
								    ticks: [2, 1.5, 1, 0.5, 0],
							 | 
						|||
| 
								 | 
							
								    fontWeight: "bold",
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "RPM",
							 | 
						|||
| 
								 | 
							
								    unit: "",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[7],
							 | 
						|||
| 
								 | 
							
								    min: 0,
							 | 
						|||
| 
								 | 
							
								    max: 3000,
							 | 
						|||
| 
								 | 
							
								    ticks: [3000, 2400, 1800, 1200, 600, 0],
							 | 
						|||
| 
								 | 
							
								    fontWeight: "bold",
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "CO2",
							 | 
						|||
| 
								 | 
							
								    unit: "(%)",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[8],
							 | 
						|||
| 
								 | 
							
								    min: 0,
							 | 
						|||
| 
								 | 
							
								    max: 10,
							 | 
						|||
| 
								 | 
							
								    ticks: [10, 8, 6, 4, 2, 0],
							 | 
						|||
| 
								 | 
							
								    fontWeight: "bold",
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "JAR Vol",
							 | 
						|||
| 
								 | 
							
								    unit: "(L)",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[9],
							 | 
						|||
| 
								 | 
							
								    min: 0,
							 | 
						|||
| 
								 | 
							
								    max: 20,
							 | 
						|||
| 
								 | 
							
								    ticks: [20, 16, 12, 8, 4, 0],
							 | 
						|||
| 
								 | 
							
								    fontWeight: "bold",
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "WEIGHT",
							 | 
						|||
| 
								 | 
							
								    unit: "(kg)",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[10],
							 | 
						|||
| 
								 | 
							
								    min: 0,
							 | 
						|||
| 
								 | 
							
								    max: 100,
							 | 
						|||
| 
								 | 
							
								    ticks: [100, 80, 60, 40, 20, 0],
							 | 
						|||
| 
								 | 
							
								    fontWeight: "bold",
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "O2",
							 | 
						|||
| 
								 | 
							
								    unit: "(%)",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[11],
							 | 
						|||
| 
								 | 
							
								    min: 0,
							 | 
						|||
| 
								 | 
							
								    max: 100,
							 | 
						|||
| 
								 | 
							
								    ticks: [100, 80, 60, 40, 20, 0],
							 | 
						|||
| 
								 | 
							
								    fontWeight: "bold",
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "NV",
							 | 
						|||
| 
								 | 
							
								    unit: "",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[12],
							 | 
						|||
| 
								 | 
							
								    min: 0,
							 | 
						|||
| 
								 | 
							
								    max: 10,
							 | 
						|||
| 
								 | 
							
								    ticks: [10, 8, 6, 4, 2, 0],
							 | 
						|||
| 
								 | 
							
								    fontWeight: "bold",
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "NIR",
							 | 
						|||
| 
								 | 
							
								    unit: "",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[13],
							 | 
						|||
| 
								 | 
							
								    min: 0,
							 | 
						|||
| 
								 | 
							
								    max: 10,
							 | 
						|||
| 
								 | 
							
								    ticks: [10, 8, 6, 4, 2, 0],
							 | 
						|||
| 
								 | 
							
								    fontWeight: "bold",
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "Temperature",
							 | 
						|||
| 
								 | 
							
								    unit: "(℃)",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[14],
							 | 
						|||
| 
								 | 
							
								    min: 0,
							 | 
						|||
| 
								 | 
							
								    max: 50,
							 | 
						|||
| 
								 | 
							
								    ticks: [50, 40, 30, 20, 10, 0],
							 | 
						|||
| 
								 | 
							
								    fontWeight: "bold",
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								];
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								const intervalValue = ref(60); // 1분
							 | 
						|||
| 
								 | 
							
								const totalSeconds = 100 * 60 * 60;
							 | 
						|||
| 
								 | 
							
								const pointsPerLine = computed(
							 | 
						|||
| 
								 | 
							
								  () => Math.floor(totalSeconds / intervalValue.value) + 1
							 | 
						|||
| 
								 | 
							
								);
							 | 
						|||
| 
								 | 
							
								const xLabels = computed(() =>
							 | 
						|||
| 
								 | 
							
								  Array.from({ length: pointsPerLine.value }, (_, i) => i * intervalValue.value)
							 | 
						|||
| 
								 | 
							
								);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								// --- batchIndex에 따라 곡선 패턴이 다르게 나오도록 smoothData 수정 ---
							 | 
						|||
| 
								 | 
							
								function smoothData(
							 | 
						|||
| 
								 | 
							
								  min: number,
							 | 
						|||
| 
								 | 
							
								  max: number,
							 | 
						|||
| 
								 | 
							
								  xLabels: number[],
							 | 
						|||
| 
								 | 
							
								  phase = 0,
							 | 
						|||
| 
								 | 
							
								  _amp = 1,
							 | 
						|||
| 
								 | 
							
								  offset = 0,
							 | 
						|||
| 
								 | 
							
								  seriesIndex: number
							 | 
						|||
| 
								 | 
							
								) {
							 | 
						|||
| 
								 | 
							
								  let _prevValue = 0.5;
							 | 
						|||
| 
								 | 
							
								  const values = [];
							 | 
						|||
| 
								 | 
							
								  // batchIndex를 곡선 패턴에 반영 (phase, ampVar, randomFactor 등)
							 | 
						|||
| 
								 | 
							
								  const batchPhase = phase + (props.batchIndex * Math.PI) / 5;
							 | 
						|||
| 
								 | 
							
								  const ampVar = 0.2 + ((seriesIndex + props.batchIndex) % 5) * 0.1;
							 | 
						|||
| 
								 | 
							
								  const randomFactor = 0.01 + 0.01 * ((seriesIndex + props.batchIndex) % 4);
							 | 
						|||
| 
								 | 
							
								  const trendType = (seriesIndex + props.batchIndex) % 4;
							 | 
						|||
| 
								 | 
							
								  const rangeMin = min + (max - min) * 0.1;
							 | 
						|||
| 
								 | 
							
								  const rangeMax = max - (max - min) * 0.1;
							 | 
						|||
| 
								 | 
							
								  for (let i = 0; i < xLabels.length; i++) {
							 | 
						|||
| 
								 | 
							
								    const t = i / (xLabels.length - 1);
							 | 
						|||
| 
								 | 
							
								    let base;
							 | 
						|||
| 
								 | 
							
								    if (trendType === 0) {
							 | 
						|||
| 
								 | 
							
								      base =
							 | 
						|||
| 
								 | 
							
								        0.2 + 0.6 * t + 0.13 * Math.sin(batchPhase + t * Math.PI * ampVar * 8);
							 | 
						|||
| 
								 | 
							
								    } else if (trendType === 1) {
							 | 
						|||
| 
								 | 
							
								      base =
							 | 
						|||
| 
								 | 
							
								        0.8 - 0.6 * t + 0.13 * Math.cos(batchPhase + t * Math.PI * ampVar * 8);
							 | 
						|||
| 
								 | 
							
								    } else if (trendType === 2) {
							 | 
						|||
| 
								 | 
							
								      base = 0.5 + 0.22 * Math.sin(batchPhase + t * Math.PI * ampVar * 12);
							 | 
						|||
| 
								 | 
							
								    } else {
							 | 
						|||
| 
								 | 
							
								      base =
							 | 
						|||
| 
								 | 
							
								        0.5 + 0.08 * Math.sin(batchPhase + t * Math.PI * ampVar * 6) + 0.2 * t;
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								    // 노이즈도 batchIndex에 따라 다르게
							 | 
						|||
| 
								 | 
							
								    const noise =
							 | 
						|||
| 
								 | 
							
								      (Math.sin(i + props.batchIndex * 13) * 0.5 + 0.5) *
							 | 
						|||
| 
								 | 
							
								      randomFactor *
							 | 
						|||
| 
								 | 
							
								      (Math.random() - 0.5);
							 | 
						|||
| 
								 | 
							
								    base += noise;
							 | 
						|||
| 
								 | 
							
								    base = Math.max(0, Math.min(1, base));
							 | 
						|||
| 
								 | 
							
								    const value = +(rangeMin + (rangeMax - rangeMin) * base + offset).toFixed(
							 | 
						|||
| 
								 | 
							
								      2
							 | 
						|||
| 
								 | 
							
								    );
							 | 
						|||
| 
								 | 
							
								    _prevValue = base;
							 | 
						|||
| 
								 | 
							
								    values.push([xLabels[i], value]);
							 | 
						|||
| 
								 | 
							
								  }
							 | 
						|||
| 
								 | 
							
								  return values;
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								const checkedList = ref(yAxisList.map(y => y.name));
							 | 
						|||
| 
								 | 
							
								const filteredYAxisList = computed(() =>
							 | 
						|||
| 
								 | 
							
								  yAxisList.filter(y => checkedList.value.includes(y.name))
							 | 
						|||
| 
								 | 
							
								);
							 | 
						|||
| 
								 | 
							
								const filteredSeriesList = computed(() =>
							 | 
						|||
| 
								 | 
							
								  seriesList.value.filter(s => checkedList.value.includes(s.name))
							 | 
						|||
| 
								 | 
							
								);
							 | 
						|||
| 
								 | 
							
								const yAxisScrollIndex = ref(0);
							 | 
						|||
| 
								 | 
							
								const visibleYAxisList = computed(() =>
							 | 
						|||
| 
								 | 
							
								  filteredYAxisList.value.slice(
							 | 
						|||
| 
								 | 
							
								    yAxisScrollIndex.value,
							 | 
						|||
| 
								 | 
							
								    yAxisScrollIndex.value + 2
							 | 
						|||
| 
								 | 
							
								  )
							 | 
						|||
| 
								 | 
							
								);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								const seriesList = computed(() => [
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "ORP",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[0],
							 | 
						|||
| 
								 | 
							
								    yAxisIndex: 0,
							 | 
						|||
| 
								 | 
							
								    data: smoothData(400, 900, xLabels.value, 0, 1, 0, 0),
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "Air flow",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[1],
							 | 
						|||
| 
								 | 
							
								    yAxisIndex: 1,
							 | 
						|||
| 
								 | 
							
								    data: smoothData(5, 25, xLabels.value, Math.PI / 2, 1, 0, 1),
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "DO",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[2],
							 | 
						|||
| 
								 | 
							
								    yAxisIndex: 2,
							 | 
						|||
| 
								 | 
							
								    data: smoothData(80, 180, xLabels.value, Math.PI / 3, 1, 0, 2),
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "Feed TK1",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[3],
							 | 
						|||
| 
								 | 
							
								    yAxisIndex: 3,
							 | 
						|||
| 
								 | 
							
								    data: smoothData(2, 8, xLabels.value, Math.PI / 4, 1, 0, 3),
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "Feed TK2",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[4],
							 | 
						|||
| 
								 | 
							
								    yAxisIndex: 4,
							 | 
						|||
| 
								 | 
							
								    data: smoothData(2, 8, xLabels.value, Math.PI / 5, 1, 0, 4),
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "pH",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[5],
							 | 
						|||
| 
								 | 
							
								    yAxisIndex: 5,
							 | 
						|||
| 
								 | 
							
								    data: smoothData(6.7, 7.6, xLabels.value, Math.PI / 6, 1, 0, 5),
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "Pressure",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[6],
							 | 
						|||
| 
								 | 
							
								    yAxisIndex: 6,
							 | 
						|||
| 
								 | 
							
								    data: smoothData(0.5, 1.5, xLabels.value, Math.PI / 7, 1, 0, 6),
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "RPM",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[7],
							 | 
						|||
| 
								 | 
							
								    yAxisIndex: 7,
							 | 
						|||
| 
								 | 
							
								    data: smoothData(1000, 2500, xLabels.value, Math.PI / 8, 1, 0, 7),
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "CO2",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[8],
							 | 
						|||
| 
								 | 
							
								    yAxisIndex: 8,
							 | 
						|||
| 
								 | 
							
								    data: smoothData(2, 8, xLabels.value, Math.PI / 9, 1, 0, 8),
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "JAR Vol",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[9],
							 | 
						|||
| 
								 | 
							
								    yAxisIndex: 9,
							 | 
						|||
| 
								 | 
							
								    data: smoothData(5, 18, xLabels.value, Math.PI / 10, 1, 0, 9),
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "WEIGHT",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[10],
							 | 
						|||
| 
								 | 
							
								    yAxisIndex: 10,
							 | 
						|||
| 
								 | 
							
								    data: smoothData(20, 90, xLabels.value, Math.PI / 11, 1, 0, 10),
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "O2",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[11],
							 | 
						|||
| 
								 | 
							
								    yAxisIndex: 11,
							 | 
						|||
| 
								 | 
							
								    data: smoothData(20, 90, xLabels.value, Math.PI / 12, 1, 0, 11),
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "NV",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[12],
							 | 
						|||
| 
								 | 
							
								    yAxisIndex: 12,
							 | 
						|||
| 
								 | 
							
								    data: smoothData(2, 8, xLabels.value, Math.PI / 13, 1, 0, 12),
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "NIR",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[13],
							 | 
						|||
| 
								 | 
							
								    yAxisIndex: 13,
							 | 
						|||
| 
								 | 
							
								    data: smoothData(2, 8, xLabels.value, Math.PI / 14, 1, 0, 13),
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								  {
							 | 
						|||
| 
								 | 
							
								    name: "Temperature",
							 | 
						|||
| 
								 | 
							
								    color: pastelColors[14],
							 | 
						|||
| 
								 | 
							
								    yAxisIndex: 14,
							 | 
						|||
| 
								 | 
							
								    data: smoothData(30, 38, xLabels.value, Math.PI / 15, 1, 0, 14),
							 | 
						|||
| 
								 | 
							
								  },
							 | 
						|||
| 
								 | 
							
								]);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								const batchMarkLines = [
							 | 
						|||
| 
								 | 
							
								  // 배치1
							 | 
						|||
| 
								 | 
							
								  [
							 | 
						|||
| 
								 | 
							
								    { xAxis: 6 * 3600, name: "1F" },
							 | 
						|||
| 
								 | 
							
								    { xAxis: 10 * 3600, name: "2F" },
							 | 
						|||
| 
								 | 
							
								    { xAxis: 12 * 3600, name: "Seed" },
							 | 
						|||
| 
								 | 
							
								    { xAxis: 22 * 3600, name: "Main" },
							 | 
						|||
| 
								 | 
							
								  ],
							 | 
						|||
| 
								 | 
							
								  // 배치2
							 | 
						|||
| 
								 | 
							
								  [
							 | 
						|||
| 
								 | 
							
								    { xAxis: 12 * 3600, name: "1F" },
							 | 
						|||
| 
								 | 
							
								    { xAxis: 18 * 3600, name: "2F" },
							 | 
						|||
| 
								 | 
							
								    { xAxis: 16 * 3600, name: "Seed" },
							 | 
						|||
| 
								 | 
							
								    { xAxis: 36 * 3600, name: "Main" },
							 | 
						|||
| 
								 | 
							
								  ],
							 | 
						|||
| 
								 | 
							
								  // 배치3
							 | 
						|||
| 
								 | 
							
								  [
							 | 
						|||
| 
								 | 
							
								    { xAxis: 15 * 3600, name: "1F" },
							 | 
						|||
| 
								 | 
							
								    { xAxis: 30 * 3600, name: "2F" },
							 | 
						|||
| 
								 | 
							
								    { xAxis: 45 * 3600, name: "Seed" },
							 | 
						|||
| 
								 | 
							
								    { xAxis: 60 * 3600, name: "Main" },
							 | 
						|||
| 
								 | 
							
								  ],
							 | 
						|||
| 
								 | 
							
								  // 배치4
							 | 
						|||
| 
								 | 
							
								  [
							 | 
						|||
| 
								 | 
							
								    { xAxis: 8 * 3600, name: "1F" },
							 | 
						|||
| 
								 | 
							
								    { xAxis: 16 * 3600, name: "2F" },
							 | 
						|||
| 
								 | 
							
								    { xAxis: 24 * 3600, name: "Seed" },
							 | 
						|||
| 
								 | 
							
								    { xAxis: 50 * 3600, name: "Main" },
							 | 
						|||
| 
								 | 
							
								  ],
							 | 
						|||
| 
								 | 
							
								];
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								// --- 차트 렌더링 로직 (간단화) ---
							 | 
						|||
| 
								 | 
							
								const growthChartRef = ref<HTMLDivElement | null>(null);
							 | 
						|||
| 
								 | 
							
								let chartInstance: echarts.ECharts | null = null;
							 | 
						|||
| 
								 | 
							
								const renderChart = () => {
							 | 
						|||
| 
								 | 
							
								  if (!growthChartRef.value) return;
							 | 
						|||
| 
								 | 
							
								  if (!chartInstance) {
							 | 
						|||
| 
								 | 
							
								    chartInstance = echarts.init(growthChartRef.value);
							 | 
						|||
| 
								 | 
							
								  }
							 | 
						|||
| 
								 | 
							
								  const yAxis = filteredYAxisList.value.map((y, i) => {
							 | 
						|||
| 
								 | 
							
								    const visibleIdx = visibleYAxisList.value.findIndex(
							 | 
						|||
| 
								 | 
							
								      vy => vy.name === y.name
							 | 
						|||
| 
								 | 
							
								    );
							 | 
						|||
| 
								 | 
							
								    return {
							 | 
						|||
| 
								 | 
							
								      type: "value",
							 | 
						|||
| 
								 | 
							
								      min: y.min,
							 | 
						|||
| 
								 | 
							
								      max: y.max,
							 | 
						|||
| 
								 | 
							
								      show: visibleIdx !== -1,
							 | 
						|||
| 
								 | 
							
								      position: "left",
							 | 
						|||
| 
								 | 
							
								      offset: visibleIdx === 1 ? 80 : 0,
							 | 
						|||
| 
								 | 
							
								      z: 10 + i,
							 | 
						|||
| 
								 | 
							
								      name: visibleIdx !== -1 ? y.name + (y.unit ? ` ${y.unit}` : "") : "",
							 | 
						|||
| 
								 | 
							
								      nameLocation: "middle",
							 | 
						|||
| 
								 | 
							
								      nameGap: 50,
							 | 
						|||
| 
								 | 
							
								      nameTextStyle: {
							 | 
						|||
| 
								 | 
							
								        color: y.color,
							 | 
						|||
| 
								 | 
							
								        fontWeight: "bold",
							 | 
						|||
| 
								 | 
							
								        fontSize: 11,
							 | 
						|||
| 
								 | 
							
								        writing: "tb-rl",
							 | 
						|||
| 
								 | 
							
								      },
							 | 
						|||
| 
								 | 
							
								      axisLabel: {
							 | 
						|||
| 
								 | 
							
								        show: visibleIdx !== -1,
							 | 
						|||
| 
								 | 
							
								        fontSize: 12,
							 | 
						|||
| 
								 | 
							
								        color: y.color,
							 | 
						|||
| 
								 | 
							
								        fontWeight: "bold",
							 | 
						|||
| 
								 | 
							
								      },
							 | 
						|||
| 
								 | 
							
								      splitLine: {
							 | 
						|||
| 
								 | 
							
								        show: visibleIdx === 1 || visibleIdx === 0,
							 | 
						|||
| 
								 | 
							
								        lineStyle: { color: "#f0f0f0", width: 1 },
							 | 
						|||
| 
								 | 
							
								      },
							 | 
						|||
| 
								 | 
							
								      axisLine: {
							 | 
						|||
| 
								 | 
							
								        show: visibleIdx !== -1,
							 | 
						|||
| 
								 | 
							
								        lineStyle: { color: y.color, width: 2 },
							 | 
						|||
| 
								 | 
							
								      },
							 | 
						|||
| 
								 | 
							
								      axisTick: {
							 | 
						|||
| 
								 | 
							
								        show: visibleIdx !== -1,
							 | 
						|||
| 
								 | 
							
								        lineStyle: { color: y.color },
							 | 
						|||
| 
								 | 
							
								      },
							 | 
						|||
| 
								 | 
							
								    };
							 | 
						|||
| 
								 | 
							
								  });
							 | 
						|||
| 
								 | 
							
								  const markLine = {
							 | 
						|||
| 
								 | 
							
								    symbol: "none",
							 | 
						|||
| 
								 | 
							
								    label: {
							 | 
						|||
| 
								 | 
							
								      show: true,
							 | 
						|||
| 
								 | 
							
								      position: "insideEndTop",
							 | 
						|||
| 
								 | 
							
								      fontWeight: "bold",
							 | 
						|||
| 
								 | 
							
								      fontSize: 13,
							 | 
						|||
| 
								 | 
							
								      color: "#222",
							 | 
						|||
| 
								 | 
							
								      formatter: function (params: { data?: { name?: string } }) {
							 | 
						|||
| 
								 | 
							
								        return params.data && params.data.name ? params.data.name : "";
							 | 
						|||
| 
								 | 
							
								      },
							 | 
						|||
| 
								 | 
							
								    },
							 | 
						|||
| 
								 | 
							
								    lineStyle: { color: "#888", width: 2, type: "solid" },
							 | 
						|||
| 
								 | 
							
								    data: batchMarkLines[props.batchIndex] || [],
							 | 
						|||
| 
								 | 
							
								  };
							 | 
						|||
| 
								 | 
							
								  const option = {
							 | 
						|||
| 
								 | 
							
								    grid: { left: 120, right: 60, top: 40, bottom: 120, containLabel: true },
							 | 
						|||
| 
								 | 
							
								    xAxis: [
							 | 
						|||
| 
								 | 
							
								      {
							 | 
						|||
| 
								 | 
							
								        type: "value",
							 | 
						|||
| 
								 | 
							
								        splitNumber: 20,
							 | 
						|||
| 
								 | 
							
								        position: "bottom",
							 | 
						|||
| 
								 | 
							
								        axisLabel: {
							 | 
						|||
| 
								 | 
							
								          fontSize: 12,
							 | 
						|||
| 
								 | 
							
								          color: "#666",
							 | 
						|||
| 
								 | 
							
								          fontWeight: "normal",
							 | 
						|||
| 
								 | 
							
								          interval: 0,
							 | 
						|||
| 
								 | 
							
								          hideOverlap: true,
							 | 
						|||
| 
								 | 
							
								          formatter: function (value: number) {
							 | 
						|||
| 
								 | 
							
								            const totalSeconds = Math.round(value);
							 | 
						|||
| 
								 | 
							
								            const hours = Math.floor(totalSeconds / 3600);
							 | 
						|||
| 
								 | 
							
								            const minutes = Math.floor((totalSeconds % 3600) / 60);
							 | 
						|||
| 
								 | 
							
								            const seconds = totalSeconds % 60;
							 | 
						|||
| 
								 | 
							
								            return `${hours}:${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
							 | 
						|||
| 
								 | 
							
								          },
							 | 
						|||
| 
								 | 
							
								        },
							 | 
						|||
| 
								 | 
							
								        splitLine: { show: true, lineStyle: { color: "#e0e0e0", width: 1 } },
							 | 
						|||
| 
								 | 
							
								        axisLine: { show: true, lineStyle: { color: "#ccc", width: 1 } },
							 | 
						|||
| 
								 | 
							
								        axisTick: { show: true, lineStyle: { color: "#ccc" } },
							 | 
						|||
| 
								 | 
							
								      },
							 | 
						|||
| 
								 | 
							
								    ],
							 | 
						|||
| 
								 | 
							
								    yAxis,
							 | 
						|||
| 
								 | 
							
								    series: filteredSeriesList.value.map((s, idx) => ({
							 | 
						|||
| 
								 | 
							
								      ...s,
							 | 
						|||
| 
								 | 
							
								      yAxisIndex: filteredYAxisList.value.findIndex(y => y.name === s.name),
							 | 
						|||
| 
								 | 
							
								      type: "line",
							 | 
						|||
| 
								 | 
							
								      symbol: "none",
							 | 
						|||
| 
								 | 
							
								      sampling: "lttb",
							 | 
						|||
| 
								 | 
							
								      lineStyle: { width: 0.5 },
							 | 
						|||
| 
								 | 
							
								      ...(idx === 0 ? { markLine } : {}),
							 | 
						|||
| 
								 | 
							
								    })),
							 | 
						|||
| 
								 | 
							
								    legend: { show: false },
							 | 
						|||
| 
								 | 
							
								    animation: false,
							 | 
						|||
| 
								 | 
							
								  };
							 | 
						|||
| 
								 | 
							
								  chartInstance.setOption(option, true);
							 | 
						|||
| 
								 | 
							
								};
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								onMounted(() => {
							 | 
						|||
| 
								 | 
							
								  renderChart();
							 | 
						|||
| 
								 | 
							
								});
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								watch([checkedList, yAxisScrollIndex, () => props.batchIndex], () => {
							 | 
						|||
| 
								 | 
							
								  renderChart();
							 | 
						|||
| 
								 | 
							
								});
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								onUnmounted(() => {
							 | 
						|||
| 
								 | 
							
								  if (chartInstance) {
							 | 
						|||
| 
								 | 
							
								    chartInstance.dispose();
							 | 
						|||
| 
								 | 
							
								  }
							 | 
						|||
| 
								 | 
							
								});
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								// 줌 컨트롤 함수
							 | 
						|||
| 
								 | 
							
								const currentZoomLevel = ref(1);
							 | 
						|||
| 
								 | 
							
								const zoomStep = 0.2;
							 | 
						|||
| 
								 | 
							
								const zoomIn = () => {
							 | 
						|||
| 
								 | 
							
								  if (chartInstance) {
							 | 
						|||
| 
								 | 
							
								    currentZoomLevel.value = Math.min(currentZoomLevel.value + zoomStep, 3);
							 | 
						|||
| 
								 | 
							
								    applyZoom();
							 | 
						|||
| 
								 | 
							
								  }
							 | 
						|||
| 
								 | 
							
								};
							 | 
						|||
| 
								 | 
							
								const zoomOut = () => {
							 | 
						|||
| 
								 | 
							
								  if (chartInstance) {
							 | 
						|||
| 
								 | 
							
								    currentZoomLevel.value = Math.max(currentZoomLevel.value - zoomStep, 0.5);
							 | 
						|||
| 
								 | 
							
								    applyZoom();
							 | 
						|||
| 
								 | 
							
								  }
							 | 
						|||
| 
								 | 
							
								};
							 | 
						|||
| 
								 | 
							
								const resetZoom = () => {
							 | 
						|||
| 
								 | 
							
								  if (chartInstance) {
							 | 
						|||
| 
								 | 
							
								    currentZoomLevel.value = 1;
							 | 
						|||
| 
								 | 
							
								    applyZoom();
							 | 
						|||
| 
								 | 
							
								  }
							 | 
						|||
| 
								 | 
							
								};
							 | 
						|||
| 
								 | 
							
								const applyZoom = () => {
							 | 
						|||
| 
								 | 
							
								  if (!chartInstance) return;
							 | 
						|||
| 
								 | 
							
								  const option = chartInstance.getOption();
							 | 
						|||
| 
								 | 
							
								  const xDataZoom = {
							 | 
						|||
| 
								 | 
							
								    type: "inside",
							 | 
						|||
| 
								 | 
							
								    xAxisIndex: 0,
							 | 
						|||
| 
								 | 
							
								    start: 0,
							 | 
						|||
| 
								 | 
							
								    end: 100 / currentZoomLevel.value,
							 | 
						|||
| 
								 | 
							
								    zoomOnMouseWheel: true,
							 | 
						|||
| 
								 | 
							
								    moveOnMouseMove: true,
							 | 
						|||
| 
								 | 
							
								    moveOnMouseWheel: false,
							 | 
						|||
| 
								 | 
							
								    preventDefaultMouseMove: true,
							 | 
						|||
| 
								 | 
							
								  };
							 | 
						|||
| 
								 | 
							
								  const yDataZoom = visibleYAxisList.value.map((_, index) => ({
							 | 
						|||
| 
								 | 
							
								    type: "inside",
							 | 
						|||
| 
								 | 
							
								    yAxisIndex: index,
							 | 
						|||
| 
								 | 
							
								    start: 0,
							 | 
						|||
| 
								 | 
							
								    end: 100 / currentZoomLevel.value,
							 | 
						|||
| 
								 | 
							
								    zoomOnMouseWheel: true,
							 | 
						|||
| 
								 | 
							
								    moveOnMouseWheel: false,
							 | 
						|||
| 
								 | 
							
								    preventDefaultMouseMove: true,
							 | 
						|||
| 
								 | 
							
								  }));
							 | 
						|||
| 
								 | 
							
								  option.dataZoom = [xDataZoom, ...yDataZoom];
							 | 
						|||
| 
								 | 
							
								  chartInstance.setOption(option, false);
							 | 
						|||
| 
								 | 
							
								};
							 | 
						|||
| 
								 | 
							
								</script>
							 | 
						|||
| 
								 | 
							
								<style scoped>
							 | 
						|||
| 
								 | 
							
								.graph-container {
							 | 
						|||
| 
								 | 
							
								  width: 100%;
							 | 
						|||
| 
								 | 
							
								  height: 1000px;
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								.main-graph-area {
							 | 
						|||
| 
								 | 
							
								  flex: 1;
							 | 
						|||
| 
								 | 
							
								  display: flex;
							 | 
						|||
| 
								 | 
							
								  flex-direction: column;
							 | 
						|||
| 
								 | 
							
								  overflow: hidden;
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								.echarts-container {
							 | 
						|||
| 
								 | 
							
								  flex: 1;
							 | 
						|||
| 
								 | 
							
								  position: relative;
							 | 
						|||
| 
								 | 
							
								  background: #fff;
							 | 
						|||
| 
								 | 
							
								  width: 100%;
							 | 
						|||
| 
								 | 
							
								  height: 100%;
							 | 
						|||
| 
								 | 
							
								  min-height: 1000px;
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								.echarts-graph {
							 | 
						|||
| 
								 | 
							
								  width: 100%;
							 | 
						|||
| 
								 | 
							
								  height: 100%;
							 | 
						|||
| 
								 | 
							
								  min-height: 1000px;
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								.zoom-controls {
							 | 
						|||
| 
								 | 
							
								  position: absolute;
							 | 
						|||
| 
								 | 
							
								  top: 60px;
							 | 
						|||
| 
								 | 
							
								  right: 10px;
							 | 
						|||
| 
								 | 
							
								  display: flex;
							 | 
						|||
| 
								 | 
							
								  flex-direction: column;
							 | 
						|||
| 
								 | 
							
								  gap: 4px;
							 | 
						|||
| 
								 | 
							
								  z-index: 1000;
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								.zoom-btn {
							 | 
						|||
| 
								 | 
							
								  width: 32px;
							 | 
						|||
| 
								 | 
							
								  height: 32px;
							 | 
						|||
| 
								 | 
							
								  border: 1px solid #ddd;
							 | 
						|||
| 
								 | 
							
								  background: #fff;
							 | 
						|||
| 
								 | 
							
								  border-radius: 4px;
							 | 
						|||
| 
								 | 
							
								  cursor: pointer;
							 | 
						|||
| 
								 | 
							
								  display: flex;
							 | 
						|||
| 
								 | 
							
								  align-items: center;
							 | 
						|||
| 
								 | 
							
								  justify-content: center;
							 | 
						|||
| 
								 | 
							
								  font-size: 16px;
							 | 
						|||
| 
								 | 
							
								  font-weight: bold;
							 | 
						|||
| 
								 | 
							
								  color: #666;
							 | 
						|||
| 
								 | 
							
								  transition: all 0.2s ease;
							 | 
						|||
| 
								 | 
							
								  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								.zoom-btn:hover {
							 | 
						|||
| 
								 | 
							
								  background: #f5f5f5;
							 | 
						|||
| 
								 | 
							
								  border-color: #bbb;
							 | 
						|||
| 
								 | 
							
								  color: #333;
							 | 
						|||
| 
								 | 
							
								  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								.zoom-btn.reset {
							 | 
						|||
| 
								 | 
							
								  font-size: 14px;
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								.zoom-btn:active {
							 | 
						|||
| 
								 | 
							
								  transform: translateY(1px);
							 | 
						|||
| 
								 | 
							
								  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								.yaxis-scroll-bar-overlay {
							 | 
						|||
| 
								 | 
							
								  position: absolute;
							 | 
						|||
| 
								 | 
							
								  left: 60px;
							 | 
						|||
| 
								 | 
							
								  bottom: 70px;
							 | 
						|||
| 
								 | 
							
								  width: 120px;
							 | 
						|||
| 
								 | 
							
								  height: 24px;
							 | 
						|||
| 
								 | 
							
								  display: flex;
							 | 
						|||
| 
								 | 
							
								  align-items: flex-start;
							 | 
						|||
| 
								 | 
							
								  justify-content: center;
							 | 
						|||
| 
								 | 
							
								  transform: translateY(50%);
							 | 
						|||
| 
								 | 
							
								  background: none;
							 | 
						|||
| 
								 | 
							
								  border-radius: 12px 12px 0 0;
							 | 
						|||
| 
								 | 
							
								  box-shadow: none;
							 | 
						|||
| 
								 | 
							
								  z-index: 30;
							 | 
						|||
| 
								 | 
							
								  padding: 0 8px;
							 | 
						|||
| 
								 | 
							
								  pointer-events: auto;
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								.yaxis-scroll-bar-overlay input[type="range"] {
							 | 
						|||
| 
								 | 
							
								  width: 100%;
							 | 
						|||
| 
								 | 
							
								  height: 8px;
							 | 
						|||
| 
								 | 
							
								  accent-color: #bbb;
							 | 
						|||
| 
								 | 
							
								  background: rgba(180, 180, 180, 0.1);
							 | 
						|||
| 
								 | 
							
								  border-radius: 8px;
							 | 
						|||
| 
								 | 
							
								  opacity: 0.4;
							 | 
						|||
| 
								 | 
							
								  transition: box-shadow 0.2s;
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								.yaxis-scroll-bar-overlay input[type="range"]::-webkit-slider-thumb {
							 | 
						|||
| 
								 | 
							
								  background: #eee;
							 | 
						|||
| 
								 | 
							
								  border: 1.5px solid #ccc;
							 | 
						|||
| 
								 | 
							
								  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
							 | 
						|||
| 
								 | 
							
								  opacity: 0.5;
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								.yaxis-scroll-bar-overlay input[type="range"]:hover {
							 | 
						|||
| 
								 | 
							
								  box-shadow: 0 0 0 2px #bbb2;
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								</style>
							 |