<!-- @format -->

<template>
  <!-- 容器 -->
  <view class="container">
    <!-- 标题 -->
    <view class="title-container">
      <view class="title">
        <image class="back-homepage-btn" src="/static/marketSituation-image/back.png" mode="返回按钮" @click="backToHomepage()"> </image>
        <view class="mid-title">
          <view class="arrow-left" @click="toLeftPage()"> </view>
          <view class="stock-id">
            <view class="stock-name"> {{ stockInformation.stockName }} </view>
            <view class="stock-code"> {{ stockInformation.stockCode }} </view>
          </view>
          <view class="arrow-right" @click="toRightPage()"> </view>
        </view>
        <image class="search" src="/static/marketSituation-image/search.png" mode="搜索" @click="search()"> </image>
        <view class="more" @click="getMore()">···</view>
      </view>
    </view>

    <view class="body">
      <!-- 股票信息栏 -->
      <view class="stock-information" @click="openStockDetail()">
        <view v-if="isStockDetail" class="stock-detail-container" @click.stop @click="closeStockDetail()">
          <view class="stock-detail" @click.stop>
            <view class="first-column">
              <view class="first-column-data" v-for="item in firstColumData" :key="item">
                <view class="stock-detail-title">
                  {{ item.title }}
                </view>
                <view class="stock-detail-value">
                  {{ item.value }}
                </view>
              </view>
            </view>
            <view class="second-column">
              <view class="second-column-data" v-for="item in secondColumnData" :key="item">
                <view class="stock-detail-title">
                  {{ item.title }}
                </view>
                <view class="stock-detail-value">
                  {{ item.value }}
                </view>
              </view>
            </view>
          </view>
        </view>
        <view class="stock-current-data">
          <view class="stock-current-price" :class="confirmStockColor(stockInformation.currentPrice, stockInformation.lastDayStockClosePrice)">
            {{ Number(stockInformation.currentPrice).toFixed(2) }}
          </view>
          <view class="stock-current-other">
            <view class="stock-current-value" :class="confirmStockColor(stockInformation.currentValue)">
              {{ Number(stockInformation.currentValue).toFixed(2) }}
            </view>
            <view class="stock-current-ratio" :class="confirmStockColor(stockInformation.currentRatio)"> {{ Number(stockInformation.currentRatio).toFixed(2) }}% </view>
          </view>
        </view>
        <view class="stock-other-data">
          <view class="first-line">
            <view class="high-price">
              高
              <view class="value" :class="confirmStockColor(stockInformation.highPrice, stockInformation.lastDayStockClosePrice)">
                {{ Number(stockInformation.highPrice).toFixed(2) }}
              </view>
            </view>

            <view class="volume">
              量
              <view class="value">
                {{ utils.formatStockNumber(stockInformation.volume, 2) }}
              </view>
            </view>
            <view class="volume-ratio">
              量比
              <view class="value">
                {{ Number(stockInformation.volumeRatio).toFixed(2) }}
              </view>
            </view>
          </view>
          <view class="second-line">
            <view class="low-price">
              低
              <view class="value" :class="confirmStockColor(stockInformation.lowPrice, stockInformation.lastDayStockClosePrice)">
                {{ Number(stockInformation.lowPrice).toFixed(2) }}
              </view>
            </view>
            <view class="amount">
              额
              <view class="value">
                {{ utils.formatStockNumber(stockInformation.amount, 2) }}
              </view>
            </view>
            <view class="market-earn">
              市盈
              <view class="value">
                {{ Number(stockInformation.marketEarn).toFixed(2) }}
              </view>
            </view>
          </view>
          <view class="third-line">
            <view class="open-price">
              开
              <view class="value" :class="confirmStockColor(stockInformation.openPrice, stockInformation.lastDayStockClosePrice)">
                {{ Number(stockInformation.openPrice).toFixed(2) }}
              </view>
            </view>
            <view class="turnover-ratio">
              换
              <view class="value"> {{ Number(stockInformation.turnoverRatio).toFixed(2) }}% </view>
            </view>
            <view class="market-value">
              市值
              <view class="value">
                {{ utils.formatStockNumber(stockInformation.marketValue, 2) }}
              </view>
            </view>
          </view>
        </view>
      </view>
      <!-- 股票图表 -->
      <view class="stock-chart">
        <view class="stock-kline-tab">
          <!-- 1:分时 2:日K 3:周K 4:月K -->
          <view class="tab-time" :class="{ 'tab-selected': klineTab == 1 }" @click="selectKlineTab(1)"> 分时 </view>
          <view class="tab-day" :class="{ 'tab-selected': klineTab == 2 }" @click="selectKlineTab(2)"> 日K </view>
          <view class="tab-week" :class="{ 'tab-selected': klineTab == 3 }" @click="selectKlineTab(3)"> 周K </view>
          <view class="tab-month" :class="{ 'tab-selected': klineTab == 4 }" @click="selectKlineTab(4)"> 月K </view>
          <view class="tab-more" :class="{ 'tab-selected': klineTab != 1 && klineTab != 2 && klineTab != 3 && klineTab != 4 }" @click="isMoreTabs ? closeMoreTabs() : openMoreTabs()">
            <view class="more-no-choose" v-if="klineTab == 1 || klineTab == 2 || klineTab == 3 || klineTab == 4"> 更多 </view>
            <view class="more-choose" v-else>
              {{ chooseTabName(klineTab) }}
            </view>
            <view :class="[isMoreTabs ? 'arrow-down' : 'arrow-up']"> </view>
          </view>
          <view class="tab-setting">
            <image class="tab-setting-img" src="/static/marketSituation-image/marketCondition-image/setting2.png" mode="设置"></image>
          </view>
        </view>
        <view v-if="isMoreTabs" class="moreTabsContainer">
          <view v-for="item in moreTabsData" :key="item" class="moreTabItem" :class="{ 'tab-selected': klineTab == item.value }" @click="selectMoreTab(item.value)">
            {{ item.title }}
          </view>
        </view>
        <view class="stock-kline">
          <view v-if="klineTab === 1 || klineTab === 2 || klineTab === 3 || klineTab === 4 || klineTab === 5 || klineTab === 6 || klineTab === 7 || klineTab === 8 || klineTab === 9" class="time-chart-container" style="position: relative">
            <!-- 主图Canvas -->
            <canvas
              canvas-id="stockChart"
              class="stock-chart"
              :width="canvasWidth"
              :height="canvasHeight"
              :style="{
                width: canvasWidth + 'px',
                height: canvasHeight + 'px',
                pointerEvents: 'none',
              }"
              will-read-frequently="true"
            ></canvas>
            <!-- 动态数据Canvas层 -->
            <canvas
              canvas-id="dynamicCanvas"
              id="dynamicCanvas"
              :width="canvasWidth"
              :height="canvasHeight"
              :style="{
                width: canvasWidth + 'px',
                height: canvasHeight + 'px',
                pointerEvents: 'none',
                position: 'absolute',
                top: '0',
                left: '0',
              }"
              will-read-frequently="true"
            ></canvas>
            <!-- 十字准心Canvas层 -->
            <canvas
              canvas-id="crosshairCanvas"
              id="crosshairCanvas"
              :width="canvasWidth"
              :height="canvasHeight"
              :style="{
                width: canvasWidth + 'px',
                height: canvasHeight + 'px',
                position: 'absolute',
                top: '0',
                left: '0',
              }"
              will-read-frequently="true"
              @touchstart="touchStart"
              @touchmove="touchMove"
              @touchend="touchEnd"
            ></canvas>
          </view>
          <!-- K线图区域 -->
          <view class="test" v-else-if="klineTab === 10">
            <button @click="startTcp()">接收消息</button>
            <button @click="sendStopTimeData()">停止消息</button>
            <button @click="sendTcpMessage('real_time')">实时行情推送</button>
            <button @click="sendTcpMessage('init_real_time')">初始化获取行情历史数据</button>
            <button @click="sendTcpMessage('stop_real_time')">停止实时推送</button>
          </view>
          <view v-else class="kline-chart-container">
            <text>K线图开发中...</text>
          </view>
        </view>
      </view>
    </view>

    <view class="bottomTool">
      <view class="index">
        <image class="icon" src="/static/marketSituation-image/marketCondition-image/index.png" mode="指标仓库图标"> </image>
        指标仓库
      </view>
      <view class="function">
        <image class="icon" src="/static/marketSituation-image/marketCondition-image/function.png" mode="功能图标"> </image>
        功能
      </view>
      <view class="favorites">
        <image class="icon" src="/static/marketSituation-image/marketCondition-image/favorites.png" mode="加自选图标"></image>
        加自选
      </view>
    </view>
  </view>
</template>

<script setup>
import { ref, reactive, computed, onMounted, watch, nextTick, onUnmounted, getCurrentInstance } from "vue";
import { onLoad } from "@dcloudio/uni-app";
const instance = getCurrentInstance();
import { prevClosePrice, timeData as testTimeData, klineData as testKlineData } from "@/common/stockTimeInformation.js";
import { throttle } from "@/common/util.js";
import { HCharts } from "@/common/canvasMethod.js";
import tcpConnection, { TCPConnection, TCP_CONFIG } from "@/api/tcpConnection.js";

// TCP相关响应式变量
const tcpConnected = ref(false);
const connectionListener = ref(null);
const messageListener = ref(null);

// 股票来源
const currentStockFrom = ref();
// 当前股票位置
const currentStockIndex = ref(-1);
// 股票信息栏变量
const stockInformation = ref({
  stockName: "----", //股票名称
  stockCode: "------", //股票代码
  lastDayStockClosePrice: 0.0, //前一日收盘价
  currentPrice: 0.0, //当前股价
  currentValue: 0.0, //涨跌额度
  currentRatio: 0.0, //涨跌幅度
  highPrice: 0.0, //最高价
  lowPrice: 0.0, //最低价
  openPrice: 0.0, //开盘价
  closePrice: 0.0, //收盘价
  volume: 0.0, //成交量
  volumeRatio: 0.0, //成交量比
  amount: 0.0, //成交额
  marketEarn: 0.0, //市盈
  turnoverRatio: 0.0, //换手率
  marketValue: 0.0, //市值
});

// 是否展开股票信息细节栏的判断变量
const isStockDetail = ref(false);

// 股票信息细节内容变量
const firstColumData = [
  {
    title: "振幅",
    value: 0,
  },
  {
    title: "现手",
    value: 0,
  },
  {
    title: "内盘",
    value: 0,
  },
  {
    title: "外盘",
    value: 0,
  },
  {
    title: "流通股",
    value: 0,
  },
  {
    title: "每股净资产",
    value: 0,
  },
  {
    title: "净资产收益率",
    value: 0,
  },
  {
    title: "总股本",
    value: 0,
  },
  {
    title: "总资本",
    value: 0,
  },
  {
    title: "总市值",
    value: 0,
  },
  {
    title: "一个月最高",
    value: 0,
  },
  {
    title: "一个月最低",
    value: 0,
  },
];
const secondColumnData = [
  {
    title: "均价",
    value: 0,
  },
  {
    title: "昨收",
    value: 0,
  },
  {
    title: "委比",
    value: 0,
  },
  {
    title: "委买",
    value: 0,
  },
  {
    title: "委卖",
    value: 0,
  },
  {
    title: "市盈利(静)",
    value: 0,
  },
  {
    title: "市盈利(动)",
    value: 0,
  },
  {
    title: "市净率",
    value: 0,
  },
  {
    title: "涨停价",
    value: 0,
  },
  {
    title: "跌停价",
    value: 0,
  },
  {
    title: "一年最高",
    value: 0,
  },
  {
    title: "一年最低",
    value: 0,
  },
];

// 是否展开更多Tab的判断变量
const isMoreTabs = ref(false);

const moreTabsData = ref([
  {
    title: "1分",
    value: 5,
  },
  {
    title: "5分",
    value: 6,
  },
  {
    title: "15分",
    value: 7,
  },
  {
    title: "30分",
    value: 8,
  },
  {
    title: "60分",
    value: 9,
  },
  {
    title: "季K",
    value: 10,
  },
  {
    title: "年K",
    value: 11,
  },
]);

// 股票当前选中的K线类型Tab
// 1:分时 2:日K 3:周K 4:月K
const klineTab = ref(1);
const startTcp = () => {
  try {
    removeTcpListeners();
    disconnectTcp();
    initTcpListeners();
    connectTcp();
  } catch (error) {
    console.error("建立连接并设置监听:", error);
    uni.showToast({
      title: "建立连接并设置监听",
      icon: "none",
      duration: 1500,
    });
  }
};

const sendStopTimeData = () => {
  disconnectTcp();
  removeTcpListeners();
};

// 确定股票数据的颜色方法
const confirmStockColor = (price, lastDayStockClosePrice) => {
  if (typeof lastDayStockClosePrice === "undefined") {
    if (price == 0) {
      return "price-none";
    } else if (price > 0) {
      return "price-up";
    } else {
      return "price-down";
    }
  } else {
    if (price == lastDayStockClosePrice) {
      return "price-none";
    } else if (price > lastDayStockClosePrice) {
      return "price-up";
    } else {
      return "price-down";
    }
  }
};

// 股票K线类型方法
const selectKlineTab = (tabId) => {
  klineTab.value = tabId;
  if (klineTab.value) {
    sendTcpMessage("stop_real_time");
  }
  switch (klineTab.value) {
    case 1:
      sendTcpMessage("init_real_time");
      break;
    case 2:
      sendTcpMessage("daily_data");
      break;
    case 3:
      sendTcpMessage("weekly_data");
      break;
    case 4:
      sendTcpMessage("monthly_data");
      break;
    case 5:
      sendTcpMessage("daily_one_minutes_data");
      break;
    case 6:
      sendTcpMessage("daily_five_minutes_data");
      break;
    case 7:
      sendTcpMessage("daily_fifteen_minutes_data");
      break;
    case 8:
      sendTcpMessage("daily_thirty_minutes_data");
      break;
    case 9:
      sendTcpMessage("daily_sixty_minutes_data");
      break;
    case 10:
      uni.showToast({
        title: "暂无季K数据",
        icon: "none",
        duration: 2000,
      });
      break;
    case 11:
      uni.showToast({
        title: "暂无年K数据",
        icon: "none",
        duration: 2000,
      });
      break;
    default:
      break;
  }
  initCanvas();
  //   startAddDataTimer();
};
// 返回按钮
const backToHomepage = () => {
  const pages = getCurrentPages();
  if (pages.length > 1) {
    uni.navigateBack();
  } else {
    // 如果没有上一页，跳转到首页
    uni.reLaunch({
      url: "/pages/home/home",
    });
  }
};

const toLeftPage = () => {
	if(currentStockFrom.value == "marketOverview"){
		return;
	}
  if (currentStockIndex.value > 0) {
    currentStockIndex.value--;
    // updateStockInformation();
  } else {
    uni.showToast({
      title: "没有更多股票了",
      icon: "none",
      duration: 1000,
    });
  }
};

const toRightPage = () => {
  if (currentStockIndex.value < stockList.length - 1) {
    currentStockIndex.value++;
    // updateStockInformation();
  } else {
    uni.showToast({
      title: "没有更多股票了",
      icon: "none",
      duration: 1000,
    });
  }
};

const openStockDetail = () => {
  isStockDetail.value = true;
};

const closeStockDetail = () => {
  isStockDetail.value = false;
};

const openMoreTabs = () => {
  isMoreTabs.value = true;
};

const closeMoreTabs = () => {
  isMoreTabs.value = false;
};

const selectMoreTab = (value) => {
  selectKlineTab(value);
};

const chooseTabName = () => {
  if (klineTab.value === 5) {
    return "1分";
  } else if (klineTab.value === 6) {
    return "5分";
  } else if (klineTab.value === 7) {
    return "15分";
  } else if (klineTab.value === 8) {
    return "30分";
  } else if (klineTab.value === 9) {
    return "60分";
  } else if (klineTab.value === 10) {
    return "季K";
  } else if (klineTab.value === 11) {
    return "年K";
  }
};

// 画布对象
const canvasWidth = ref(100);
const canvasHeight = ref(100);
const CANVAS_BACKGROUND_COLOR = "#fff";
const TEXT_COLOR1 = "#000";
const TEXT_COLOR2 = "red";
const LINE_COLOR = "#e5e5e5";
const timeChartObject = ref({
  min: 0,
  max: 0,
});
const klineChartObject = ref({
  min: 0,
  max: 0,
});

const chartRange = ref();

// 开盘时间
const openTime = "09:30";
// 收盘时间
const closeTime = "15:00";

const ctx = ref(null);
const dynamicCtx = ref(null); // 动态Canvas上下文
const crosshairCtx = ref(null); // 十字准心Canvas上下文
const pixelRatio = ref(1);
// 触屏对象
const touchState = reactive({
  startX: 0,
  startY: 0,
  isMoving: false,
  moveDistance: 0,
  moveThreshold: 10, //移动阈值(用于判断是否在点击)
  scale: 1, // 当前缩放比例
  minScale: 0.3, // 最小缩放比例
  maxScale: 5, // 最大缩放比例
  baseVisibleCount: 40, // 基准可见K线数量
  offset: 0, // 数据偏移量
  isZooming: false, // 是否正在缩放
  initialDistance: 0, // 初始两指距离
  initialScale: 1, // 初始缩放比例
});
// 计算当前可见的K线数量
const visibleCount = computed(() => {
  return Math.floor(touchState.baseVisibleCount / touchState.scale);
});
// 计算当前显示的数据范围
const visibleDataRange = computed(() => {
  const start = Math.max(0, klineData.value.length - visibleCount.value - touchState.offset);
  const end = Math.min(klineData.value.length, start + visibleCount.value);
  return {
    start,
    end,
  };
});
// 获取当前可见的数据
const visibleKlineData = computed(() => {
  const { start, end } = visibleDataRange.value;
  return klineData.value.slice(start, end);
});
// 计算两点之间的距离
const getDistance = (touch1, touch2) => {
  const dx = touch1.x - touch2.x;
  const dy = touch1.y - touch2.y;
  return Math.sqrt(dx * dx + dy * dy);
};

// 十字准线相关状态
const crosshair = reactive({
  show: false,
  x: 0,
  y: 0,
  currentData: null,
  snapToData: true,
});
// 绘制网格和坐标轴
const grid = [
  {
    top: 20,
    bottom: canvasHeight.value * 0.5,
    left: 5,
    right: 5,
    lineColor: LINE_COLOR,
    lineWidth: 1,
    horizontalLineNum: 5,
    verticalLineNum: 5,
  },
  {
    top: canvasHeight.value * 0.5 + 20,
    bottom: 20,
    left: 5,
    right: 5,
    lineColor: LINE_COLOR,
    lineWidth: 1,
    horizontalLineNum: 3,
    verticalLineNum: 5,
  },
];

// 绘制网格和坐标轴
const dayGrid = [
  {
    top: 20,
    bottom: canvasHeight.value * 0.5,
    left: 5,
    right: 5,
    lineColor: LINE_COLOR,
    lineWidth: 1,
    horizontalLineNum: 5,
    verticalLineNum: 2,
  },
  {
    top: canvasHeight.value * 0.5 + 20,
    bottom: 20,
    left: 5,
    right: 5,
    lineColor: LINE_COLOR,
    lineWidth: 1,
    horizontalLineNum: 3,
    verticalLineNum: 2,
  },
  {
    top: canvasHeight.value * 0.5 + 20,
    bottom: 20,
    left: 5,
    right: 5,
    lineColor: LINE_COLOR,
    lineWidth: 1,
    horizontalLineNum: 3,
    verticalLineNum: 2,
  },
];
// 工具函数
const utils = {
  // 格式化价格
  formatPrice(price) {
    return price.toFixed(2);
  },

  // 计算数据范围
  calculateDataRange(data, key) {
    if (!data || data.length === 0) {
      return {
        min: 0,
        max: 0,
      };
    }
    const values = data.map((item) => item[key]);
    return {
      min: Math.min(...values),
      max: Math.max(...values),
    };
  },
  // 计算标签
  calculateLabel(data, type = 2, preClosePrice = 0, key, num) {
    let label = [];
    if (key === "price") {
      // 分时价格区间
      if (type == 1) {
        const priceRange = utils.calculateDataRange(data, "price");
        const theMost = Math.max(priceRange.max - preClosePrice, preClosePrice - priceRange.min);
        const mid = (num - 1) / 2;
        // 计算分时价格标签
        label[mid] = {
          value: utils.formatPrice(preClosePrice),
          ratio: utils.formatPrice(0) + "%",
        };
        for (let i = 0; i < mid; i++) {
          label[i] = {
            value: utils.formatPrice(preClosePrice + (theMost * (mid - i)) / mid),
            ratio: utils.formatPrice((100 * (theMost * (mid - i))) / mid / preClosePrice) + "%",
          };

          label[num - 1 - i] = {
            value: utils.formatPrice(preClosePrice - (theMost * (mid - i)) / mid),
            ratio: utils.formatPrice((-1 * 100 * (theMost * (mid - i))) / mid / preClosePrice) + "%",
          };
        }
        chartRange.value.push({
          max: preClosePrice + theMost,
          min: preClosePrice - theMost,
        });
        // timeChartObject.value.max = preClosePrice + theMost;
        // timeChartObject.value.min = preClosePrice - theMost;
        return label;
      } else {
        const highPriceRange = utils.calculateDataRange(data, "high");
        const lowPriceRange = utils.calculateDataRange(data, "low");
        const priceRange = {
          max: highPriceRange.max * 1.01,
          min: lowPriceRange.min * 0.99,
        };
        const priceDiff = priceRange.max - priceRange.min;
        for (let i = 0; i < num; ++i) {
          label[i] = {
            value: utils.formatPrice(priceRange.max - (i * priceDiff) / (num - 1)),
          };
        }
        chartRange.value.push(priceRange);
        // klineChartObject.value.max = highPriceRange.max * 1.01;
        // klineChartObject.value.min = lowPriceRange.min * 0.99;
        return label;
      }
    } else if (key === "volume") {
      const volumeRange = utils.calculateDataRange(data, "volume");
      chartRange.value.push({
        max: volumeRange.max,
        min: 0,
      });
      label[0] = {
        value: utils.formatPrice(volumeRange.max),
      };
      label[1] = {
        value: utils.formatPrice(0),
      };
      return label;
    }
    return null;
  },
  // 线性插值
  lerp(start, end, factor) {
    return start + (end - start) * factor;
  },

  // 根据X坐标找到最近的数据点
  findNearestDataPoint(x, pointLen, data, grid, offset) {
    if (!data.length) return null;
    const width = canvasWidth.value;
    const height = canvasHeight.value;
    // 计算每个数据点的X坐标间隔
    // 倒推 const x=5+(index*(width-10)/pointLen)  已知x求index
    const xStep = width - grid[0].left - grid[0].right;

    // 计算触摸点对应的数据索引
    const touchX = (x - grid[0].left - offset) * pointLen;
    let nearestIndex = Math.round(touchX / xStep);
    let dataX;
    // 确保索引在有效范围内
    if (nearestIndex >= 0 && nearestIndex <= data.length - 1) {
      dataX = offset + grid[0].left + (nearestIndex * (width - grid[0].left - grid[0].right)) / pointLen;
    } else {
      dataX = x;
    }
    nearestIndex = Math.max(0, Math.min(nearestIndex, data.length - 1));

    return {
      ...data[nearestIndex],
      index: nearestIndex,
      x: dataX,
    };
  },

  // 根据Y坐标计算价格
  calculatePriceFromY(y, data, grid) {
    if (!data.length) return 0;

    const width = canvasWidth.value;
    const height = canvasHeight.value;
    // 上下边距1
    const topPadding1 = 20;
    const bottomPadding1 = height * 0.4;
    //   上下边距2
    const topPadding2 = height - bottomPadding1 + 40;
    const bottomPadding2 = 5;
    // 左右边距
    const verticalPadding = 5;

    let chartY;
    let price;

    for (let i = 0; i < grid.length; i++) {
      if (y >= grid[i].top && y <= height - grid[i].bottom) {
        const chartDiff = chartRange.value[i].max - chartRange.value[i].min;
        chartY = y - grid[i].top;
        price = chartRange.value[i].max - (chartY / (height - grid[i].bottom - grid[i].top)) * chartDiff;
        break;
      }
    }

    // if (y >= topPadding1 && y <= height - bottomPadding1) {
    //   const priceDiff = priceRange.max - priceRange.min;
    //   chartY = y - topPadding1;
    //   price = priceRange.max - (chartY / (height - topPadding1 - bottomPadding1)) * priceDiff;
    // } else if (y >= topPadding2 && y <= height - bottomPadding2) {
    //   const volumeRange = utils.calculateDataRange(data, "volume");
    //   const volumeDiff = volumeRange.max - 0;
    //   chartY = y - topPadding2;
    //   price = volumeRange.max - (chartY / (height - topPadding2 - bottomPadding2)) * volumeDiff;
    // }

    return price;
  },
  // 股市数值格式化方法
  formatStockNumber(value, decimalPlaces = 2) {
    const num = Number(value);
    if (isNaN(num)) return "0";

    const absNum = Math.abs(num);
    const sign = num < 0 ? "-" : "";

    if (absNum >= 1000000000000) {
      // 万亿级别
      return sign + (absNum / 1000000000000).toFixed(decimalPlaces) + "万亿";
    } else if (absNum >= 100000000) {
      // 亿级别
      return sign + (absNum / 100000000).toFixed(decimalPlaces) + "亿";
    } else if (absNum >= 10000) {
      // 万级别
      return sign + (absNum / 10000).toFixed(decimalPlaces) + "万";
    } else {
      // 小于万的直接显示
      return sign + absNum.toFixed(decimalPlaces);
    }
  },
};

let text = [
  [
    {
      name: "领先",
      value: "暂无数据",
      color: "red",
    },
    {
      name: "价",
      value: utils.formatPrice(stockInformation.value.currentPrice),
      color: "black",
    },
  ],
  [
    {
      name: "量",
      value: utils.formatStockNumber(stockInformation.value.volume),
      color: "green",
    },
    {
      name: "额",
      value: "暂无数据",
      color: "black",
    },
  ],
];
// 示例数据
const timeData = ref([]);

const klineData = ref([]);

const initCanvas = async () => {
  try {
    crosshair.show = false;

    grid[0].bottom = canvasHeight.value * 0.4;
    grid[1].top = canvasHeight.value * 0.6 + 30;

    dayGrid[0].top = 20;
    dayGrid[0].bottom = canvasHeight.value * 0.6;
    dayGrid[1].top = canvasHeight.value * 0.4 + 30;
    dayGrid[1].bottom = canvasHeight.value * 0.3;
    dayGrid[2].top = canvasHeight.value * 0.7 + 20;
    dayGrid[2].bottom = 20;

    // 等待DOM更新
    await nextTick();
    ctx.value = uni.createCanvasContext("stockChart", instance.proxy);
    // 初始化动态数据Canvas上下文
    dynamicCtx.value = uni.createCanvasContext("dynamicCanvas", instance.proxy);
    // 初始化十字准心Canvas上下文
    crosshairCtx.value = uni.createCanvasContext("crosshairCanvas", instance.proxy);

    if (ctx.value) {
      // 设置Canvas的内部绘图区域尺寸
      ctx.value.canvas = {
        width: canvasWidth.value,
        height: canvasHeight.value,
      };
    } else {
      console.warn("Canvas上下文未初始化，跳过绘制");
    }
    // 确保Canvas上下文知道正确的尺寸
    if (dynamicCtx.value) {
      // 设置Canvas的内部绘图区域尺寸
      dynamicCtx.value.canvas = {
        width: canvasWidth.value,
        height: canvasHeight.value,
      };
    } else {
      console.warn("动态Canvas上下文未初始化，跳过绘制");
    }
    if (crosshairCtx.value) {
      // 设置Canvas的内部绘图区域尺寸
      crosshairCtx.value.canvas = {
        width: canvasWidth.value,
        height: canvasHeight.value,
      };
    } else {
      console.warn("十字准心Canvas上下文未初始化，跳过绘制");
    }
    // 等待DOM更新
    await nextTick();
    console.log("ctx", ctx.value);
    drawChart();
  } catch (error) {
    console.error("初始化Canvas失败:", error);
  }
};

// 绘制图表主函数-设置基础数据
const drawChart = () => {
  if (!ctx.value || !dynamicCtx.value || !crosshairCtx.value) {
    console.warn("Canvas上下文未初始化，跳过绘制");
    return;
  }
  const data = klineTab.value == 1 ? timeData.value : klineData.value;
  console.log("data", data);
  chartRange.value = [];
  // 清除画布
  // HCharts.setCanvasColor(ctx.value, width, height, CANVAS_BACKGROUND_COLOR);
  // HCharts.setCanvasColor(dynamicCtx.value, width, height, CANVAS_BACKGROUND_COLOR);
  // HCharts.setCanvasColor(crosshairCtx.value, width, height, CANVAS_BACKGROUND_COLOR);

  // 根据当前标签绘制对应图表
  if (klineTab.value == 1) {
    console.log("stockInfomaton.lastDayStockClosePrice", stockInformation.value.lastDayStockClosePrice);
    //   设置标签样式
    const label = [
      {
        text: utils.calculateLabel(data, 1, stockInformation.value.lastDayStockClosePrice, "price", grid[0].horizontalLineNum),
        color: [TEXT_COLOR1, TEXT_COLOR1, TEXT_COLOR1, TEXT_COLOR1, TEXT_COLOR2],
        fontSize: 12,
        lineStyle: ["solid", "solid", "solid", "solid", "solid"],
        onlyTwo: false,
      },
      {
        text: utils.calculateLabel(data, 2, stockInformation.value.lastDayStockClosePrice, "volume", grid[1].horizontalLineNum),
        color: [TEXT_COLOR1, TEXT_COLOR1],
        lineStyle: ["solid", "solid", "solid"],
        fontSize: 12,
        onlyTwo: true,
      },
    ];
    // 把label加进grid中
    grid[0].label = label[0];
    grid[1].label = label[1];

    drawTimeChart();
  } else {
    //   设置标签样式
    const label = [
      {
        text: utils.calculateLabel(data, 2, stockInformation.value.lastDayStockClosePrice, "price", grid[0].horizontalLineNum),
        color: [TEXT_COLOR1, TEXT_COLOR1, TEXT_COLOR1, TEXT_COLOR1, TEXT_COLOR2],
        lineStyle: ["solid", "dash", "dash", "dash", "solid"],
        fontSize: 12,
        onlyTwo: false,
      },
      {
        text: utils.calculateLabel(data, 2, stockInformation.value.lastDayStockClosePrice, "volume", grid[1].horizontalLineNum),
        color: [TEXT_COLOR1, TEXT_COLOR1],
        lineStyle: ["solid", "dash", "solid"],
        fontSize: 12,
        onlyTwo: true,
      },
      {
        text: utils.calculateLabel(data, 2, stockInformation.value.lastDayStockClosePrice, "volume", grid[1].horizontalLineNum),
        color: [TEXT_COLOR1, TEXT_COLOR1],
        lineStyle: ["solid", "dash", "solid"],
        fontSize: 12,
        onlyTwo: true,
      },
    ];
    // 把label加进grid中
    dayGrid[0].label = label[0];
    dayGrid[1].label = label[1];
    dayGrid[2].label = label[2];
    // HCharts.drawGrid(ctx.value, canvasWidth.value, canvasHeight.value, dayGrid, data[0].date, data[data.length - 1].date);
    drawKLineChart();
  }

  // crosshairCtx.value.draw();
};
const throttledDrawChart = throttle(drawChart, 50);

// // 绘制分时图
const drawTimeChart = () => {
  drawCtxChart(grid);
  drawDynamicCtxChart();
};

// // 绘制K线图
const drawKLineChart = () => {
  drawCtxChart(dayGrid);
  drawDynamicCtxChart();
};

const drawCtxChart = (paramGrid) => {
  // 检查Canvas上下文是否已初始化
  if (!ctx.value) {
    console.warn("Canvas上下文未初始化，跳过绘制");
    return;
  }
  // 绘制网格
  HCharts.drawGrid(ctx.value, canvasWidth.value, canvasHeight.value, paramGrid, openTime, closeTime);
  //执行绘制
  ctx.value.draw();
};
const drawDynamicCtxChart = () => {
  if (klineTab.value == 1) {
    //绘制价格曲线
    HCharts.drawPriceLine(dynamicCtx.value, canvasWidth.value, canvasHeight.value, timeData.value, grid, chartRange.value[0]);
    //绘制成交量
    HCharts.drawVolume(
      dynamicCtx.value,
      canvasWidth.value,
      canvasHeight.value,
      timeData.value,
      2,
      240,
      grid,
      {
        max: utils.calculateDataRange(timeData.value, "volume").max,
        min: 0,
      },
      0
    );
    HCharts.drawAxisLabels(dynamicCtx.value, canvasWidth.value, canvasHeight.value, grid);
    HCharts.drawTopPriceDisplay(crosshairCtx.value, grid, text);
  } else {
    drawKLine(dynamicCtx.value);
    //绘制成交量
    HCharts.drawVolume(
      dynamicCtx.value,
      canvasWidth.value,
      canvasHeight.value,
      klineData.value,
      2,
      klineData.value.length,
      dayGrid,
      {
        max: utils.calculateDataRange(klineData.value, "volume").max,
        min: 0,
      },
      touchState.offset
    );
    //绘制成交量
    HCharts.drawVolume(
      dynamicCtx.value,
      canvasWidth.value,
      canvasHeight.value,
      klineData.value,
      3,
      klineData.value.length,
      dayGrid,
      {
        max: utils.calculateDataRange(klineData.value, "volume").max,
        min: 0,
      },
      touchState.offset
    );
    HCharts.drawAxisLabels(dynamicCtx.value, canvasWidth.value, canvasHeight.value, dayGrid);
    // drawMovingAverage();
    HCharts.drawTopPriceDisplay(crosshairCtx.value, dayGrid, []);
  }
  //执行绘制
  dynamicCtx.value.draw(false);
  crosshairCtx.value.draw();
};

const throttledDrawDynamicCtxChart = throttle(drawDynamicCtxChart, 50);
// const throttledDrawKLineChart = throttle(drawKLineChart, 10);

// 绘制K线
const drawKLine = (ctx) => {
  const data = klineData.value;
  if (!data.length) return;

  const width = canvasWidth.value;
  const height = canvasHeight.value;

  // 计算价格范围
  const priceRange = chartRange.value[0];
  const priceDiff = priceRange.max - priceRange.min;

  const areaWidth = (width - dayGrid[0].left - dayGrid[0].right) / data.length;
  const candleWidth = areaWidth * 0.6;

  data.forEach((item, index) => {
    const x = touchState.offset + dayGrid[0].left + (index * (width - dayGrid[0].left - dayGrid[0].right)) / data.length;

    // 计算坐标
    const highY = dayGrid[0].top + (height - dayGrid[0].top - dayGrid[0].bottom) * (1 - (item.high - priceRange.min) / priceDiff);
    const lowY = dayGrid[0].top + (height - dayGrid[0].top - dayGrid[0].bottom) * (1 - (item.low - priceRange.min) / priceDiff);
    const openY = dayGrid[0].top + (height - dayGrid[0].top - dayGrid[0].bottom) * (1 - (item.open - priceRange.min) / priceDiff);
    const closeY = dayGrid[0].top + (height - dayGrid[0].top - dayGrid[0].bottom) * (1 - (item.close - priceRange.min) / priceDiff);

    // 判断涨跌
    const isRise = item.close >= item.open;
    const color = isRise ? "green" : "red";

    // 绘制上下影线
    ctx.setStrokeStyle(color);
    ctx.setLineWidth(1);
    ctx.beginPath();
    ctx.moveTo(x, highY);
    ctx.lineTo(x, lowY);
    ctx.stroke();

    // 绘制K线实体
    const entityTop = isRise ? closeY : openY;
    const entityBottom = isRise ? openY : closeY;
    const entityHeight = Math.max(Math.abs(entityBottom - entityTop), 1);

    ctx.setFillStyle(color);
    ctx.fillRect(x - candleWidth / 2, entityTop, candleWidth, entityHeight);
  });
};

// 绘制移动平均线
const drawMovingAverage = () => {
  const data = klineData.value;
  if (!data.length) return;

  const ma5 = calculateMA(data, 5);
  const ma10 = calculateMA(data, 10);

  drawMALine(ma5, "#fadb14", "MA5"); // 黄色
  drawMALine(ma10, "#eb2f96", "MA10"); // 粉色
};

// 计算移动平均线
const calculateMA = (data, period) => {
  const result = [];
  for (let i = period - 1; i < data.length; i++) {
    let sum = 0;
    for (let j = 0; j < period; j++) {
      sum += data[i - j].close;
    }
    result.push({
      index: i,
      value: sum / period,
    });
  }
  return result;
};

// 绘制均线
const drawMALine = (maData, color, label) => {
  if (!maData.length) return;

  const width = canvasWidth.value;
  const height = canvasHeight.value;
  const data = klineData.value;

  // 计算价格范围
  const highs = data.map((item) => item.high);
  const lows = data.map((item) => item.low);
  const priceRange = chartRange.value[0];
  const priceDiff = priceRange.max - priceRange.min;

  ctx.value.setStrokeStyle(color);
  ctx.value.setLineWidth(1.5);
  ctx.value.beginPath();

  maData.forEach((point, idx) => {
    const x = 40 + (point.index * (width - 60)) / (data.length - 1);
    const y = 20 + (height - 60) * (1 - (point.value - priceRange.min) / priceDiff);

    if (idx === 0) {
      ctx.value.moveTo(x, y);
    } else {
      ctx.value.lineTo(x, y);
    }
  });

  ctx.value.stroke();

  // 绘制图例
  if (maData.length > 0) {
    const lastPoint = maData[maData.length - 1];
    const x = 40 + (lastPoint.index * (width - 60)) / (data.length - 1);
    const y = 20 + (height - 60) * (1 - (lastPoint.value - priceRange.min) / priceDiff);

    HCharts.drawText(ctx.value`${label}: ${utils.formatPrice(lastPoint.value)}`, x + 10, y - 5, 12, color);
  }
};
// 触摸事件处理
const touchStart = (e) => {
  e.preventDefault(); // 阻止页面滚动等默认行为

  if (typeof e.touches[1] === "undefined") {
    touchState.startX = e.touches[0].x;
    touchState.startY = e.touches[0].y;
    touchState.isMoving = false;
  } else if (typeof e.touches[1] !== "undefined" && !crosshair.show) {
    // touchState.startX1 = e.touches[0].x;
    // touchState.startY1 = e.touches[0].y;
    // touchState.startX2 = e.touches[1].x;
    // touchState.startY2 = e.touches[1].y;
    // touchState.isMoving = false;
  }
};

const touchMove = (e) => {
  touchState.isMoving = true;
  // 计算移动距离
  const currentX = e.touches[0].x;
  const currentY = e.touches[0].y;
  const deltaX = currentX - touchState.startX;
  const deltaY = currentY - touchState.startY;
  touchState.moveDistance = Math.max(touchState.moveDistance, Math.sqrt(deltaX * deltaX + deltaY * deltaY));
  if (crosshair.show) {
    if (isInChartArea(currentX, currentY, klineTab.value === 1 ? grid : dayGrid)) {
      throttledUpdateCrosshair(currentX, currentY);
    } else {
      // 如果移出图表区域，隐藏十字准线
      // crosshair.show = false;
      if (klineTab.value === 1) {
        HCharts.drawCrosshair(crosshairCtx.value, canvasWidth.value, canvasHeight.value, grid, crosshair, text);
      } else {
        HCharts.drawCrosshair(crosshairCtx.value, canvasWidth.value, canvasHeight.value, dayGrid, crosshair, []);
      }
    }
  } else {
    return;
    if (klineTab.value === 2) {
      // if(currentY)
      if (currentX < touchState.startX) {
        touchState.offset += currentX - touchState.startX;
        throttledDrawDynamicCtxChart();
        touchState.startX = currentX;
      } else {
        touchState.offset += currentX - touchState.startX;
        throttledDrawDynamicCtxChart();
        touchState.startX = currentX;
      }
      if (canvasWidth.value + touchState.offset < 30) {
        touchState.offset = 0;
      }
    }
  }
};

const touchEnd = (e) => {
  // 触摸结束，隐藏十字准线

  if (touchState.moveDistance < touchState.moveThreshold) {
    // 移动距离小于阈值，认为是点击事件
    crosshair.show = !crosshair.show;
  }

  if (crosshair.show) {
    const currentX = e.changedTouches[0].x;
    const currentY = e.changedTouches[0].y;
    if (isInChartArea(currentX, currentY, klineTab.value === 1 ? grid : dayGrid)) {
      throttledUpdateCrosshair(currentX, currentY);
    }
  }
  if (klineTab.value === 1) {
    HCharts.drawCrosshair(crosshairCtx.value, canvasWidth.value, canvasHeight.value, grid, crosshair, text);
  } else {
    HCharts.drawCrosshair(crosshairCtx.value, canvasWidth.value, canvasHeight.value, dayGrid, crosshair, []);
  }
  // 重置移动距离
  touchState.moveDistance = 0;
};

// 检查坐标是否在图表区域内
const isInChartArea = (x, y, grid) => {
  const width = canvasWidth.value;
  const height = canvasHeight.value;

  if (x < grid[0].left || x > width - grid[0].right) return false;

  for (let i = 0; i < grid.length; i++) {
    if (y >= grid[i].top && y <= height - grid[i].bottom) return true;
  }
  return false;
};

// 更新十字准线位置和数据
const updateCrosshair = (x, y) => {
  if (!crosshair.show) return;
  // 更新Y坐标以匹配实际价格
  const data = klineTab.value === 1 ? timeData.value : klineData.value;

  const width = canvasWidth.value;
  const height = canvasHeight.value;

  crosshair.x = x;
  crosshair.y = y;
  const nearestData = utils.findNearestDataPoint(x, klineTab.value === 1 ? 240 : data.length, data, klineTab.value === 1 ? grid : dayGrid, klineTab.value === 1 ? 0 : touchState.offset);
  crosshair.x = nearestData.x;
  stockInformation.value.currentPrice = nearestData.price || nearestData.close;
  stockInformation.value.volume = nearestData.volume;
  // 涨跌额度
  if (klineTab.value == 1) {
    stockInformation.value.currentValue = stockInformation.value.currentPrice - stockInformation.value.lastDayStockClosePrice;
    stockInformation.value.currentRatio = ((stockInformation.value.currentPrice - stockInformation.value.lastDayStockClosePrice) / stockInformation.value.lastDayStockClosePrice) * 100;
  } else {
    stockInformation.value.openPrice = nearestData.open;
    stockInformation.value.highPrice = nearestData.high;
    stockInformation.value.lowPrice = nearestData.low;
    if (nearestData.index != 0) {
      stockInformation.value.lastDayStockClosePrice = data[nearestData.index - 1].close;
      stockInformation.value.currentValue = stockInformation.value.currentPrice - stockInformation.value.lastDayStockClosePrice;
      stockInformation.value.currentRatio = ((stockInformation.value.currentPrice - stockInformation.value.lastDayStockClosePrice) / stockInformation.value.lastDayStockClosePrice) * 100;
    }
  }
  if (klineTab.value === 1 && crosshair.snapToData) {
    // 吸附到最近的数据点

    if (nearestData) {
      crosshair.currentData = nearestData;
      const priceDiff = chartRange.value[0].max - chartRange.value[0].min;
      crosshair.y = grid[0].top + (height - grid[0].bottom - grid[0].top) * (1 - (nearestData.price - chartRange.value[0].min) / priceDiff);
    }
  } else {
    // 自由移动模式
    const currentPrice = utils.calculatePriceFromY(y, data, klineTab.value === 1 ? grid : dayGrid);
    crosshair.currentData = {
      ...nearestData,
      price: currentPrice,
    };
  }

  if (crosshair?.currentData?.volume) {
    text[0][1].value = crosshair.currentData.price;
    text[1][0].value = crosshair.currentData.volume;
  }
  // 只重绘十字准心，不重绘整个图表
  if (klineTab.value === 1) {
    HCharts.drawCrosshair(crosshairCtx.value, canvasWidth.value, canvasHeight.value, grid, crosshair, text);
  } else {
    HCharts.drawCrosshair(crosshairCtx.value, canvasWidth.value, canvasHeight.value, dayGrid, crosshair, []);
  }
};

const throttledUpdateCrosshair = throttle(updateCrosshair, 10);

// 缩放控制函数
const zoomIn = () => {
  touchState.scale = Math.min(touchState.maxScale, touchState.scale * 1.2);
  drawChart();
};

const zoomOut = () => {
  touchState.scale = Math.max(touchState.minScale, touchState.scale / 1.2);
  drawChart();
};

// 监听数据变化
// watch([timeData, klineData], () => {
//   console.log("数据变化");
//   drawChart();
// });

watch(klineTab, () => {
  console.log("标签页变化");
  drawChart();
});

// 初始化TCP监听器
const initTcpListeners = () => {
  // 创建连接状态监听器并保存引用
  connectionListener.value = (status, result) => {
    tcpConnected.value = status === "connected";
    console.log("TCP连接状态变化:", status, tcpConnected.value);

    // 显示连接状态提示
    uni.showToast({
      title: status === "connected" ? "TCP连接成功" : "TCP连接断开",
      icon: status === "connected" ? "success" : "none",
      duration: 2000,
    });

    if (status === "connected") {
      if (klineTab.value == 1) {
        sendTcpMessage("init_real_time");
      }
    }
  };

  // 创建消息监听器并保存引用
  messageListener.value = (type, message, parsedArray) => {
    const messageObj = {
      type: type,
      content: message,
      parsedArray: parsedArray,
      timestamp: new Date().toLocaleTimeString(),
      direction: "received",
    };

    // 解析股票数据
    parseStockData(message);
  };

  // 注册监听器
  tcpConnection.onConnectionChange(connectionListener.value);
  tcpConnection.onMessage(messageListener.value);
};

// 连接TCP服务器
const connectTcp = () => {
  console.log("开始连接TCP服务器...");
  tcpConnection.connect();
};

// 断开TCP连接
const disconnectTcp = () => {
  console.log("断开TCP连接...");
  tcpConnection.disconnect();
  tcpConnected.value = false;
};

// 发送TCP消息
const sendTcpMessage = (command) => {
  let messageData;
  switch (command) {
    // 实时行情推送
    case "real_time":
      messageData = {
        command: "real_time",
        stock_code: "SH.000001",
      };
      break;
    // 初始化获取行情历史数据
    case "init_real_time":
      messageData = {
        command: "init_real_time",
        stock_code: "SH.000001",
      };
      break;
    case "stop_real_time":
      messageData = {
        command: "stop_real_time",
      };
      break;
    // 股票列表
    case "stock_list":
      messageData = {
        command: "stock_list",
      };
      break;
    // 日线数据
    case "daily_data":
      messageData = {
        command: "daily_data",
        stock_code: "GBPAUD.FXCM",
        start_date: "20250801",
        end_date: "20251029",
      };
      break;
    // 周线数据
    case "weekly_data":
      messageData = {
        command: "weekly_data",
        stock_code: "000001.SZ",
        start_date: "2024912",
        end_date: "20251029",
      };
      break;
    // 周线数据
    case "monthly_data":
      messageData = {
        command: "monthly_data",
        stock_code: "000001.SZ",
        start_date: "20201130",
        end_date: "20251029",
      };
      break;
    // 1分钟线数据
    case "daily_one_minutes_data":
      messageData = {
        command: "daily_one_minutes_data",
        stock_code: "000001.SZ",
      };
      break;
    // 5分钟线数据
    case "daily_five_minutes_data":
      messageData = {
        command: "daily_five_minutes_data",
        stock_code: "000001.SZ",
      };
      break;
    // 15分钟线数据
    case "daily_fifteen_minutes_data":
      messageData = {
        command: "daily_fifteen_minutes_data",
        stock_code: "000001.SZ",
      };
      break;
    // 30分钟线数据
    case "daily_thirty_minutes_data":
      messageData = {
        command: "daily_thirty_minutes_data",
        stock_code: "000001.SZ",
      };
      break;
    // 60分钟线数据
    case "daily_sixty_minutes_data":
      messageData = {
        command: "daily_sixty_minutes_data",
        stock_code: "000001.SZ",
      };
      break;
    case "batch_real_time":
      messageData = {
        command: "batch_real_time",
        stock_codes: ["SH.000001", "SH.000002", "SH.000003", "SH.000004", "SH.000005"],
      };
      break;
    case "help":
      messageData = {
        command: "help",
      };
      break;
  }
  if (!messageData) {
    uni.showToast({
      title: "命令不存在",
      icon: "none",
      duration: 1000,
    });
    return;
  } else {
    try {
      // 发送消息
      const success = tcpConnection.send(messageData);
      if (success) {
        console.log("home发送TCP消息:", messageData);
        uni.showToast({
          title: "消息发送成功",
          icon: "success",
          duration: 1000,
        });
      }
    } catch (error) {
      console.error("发送TCP消息时出错:", error);
      uni.showToast({
        title: "消息发送失败",
        icon: "none",
        duration: 1000,
      });
    }
  }
};

// 获取TCP连接状态
const getTcpStatus = () => {
  const status = tcpConnection.getConnectionStatus();
  uni.showModal({
    title: "TCP连接状态",
    content: `当前状态: ${status ? "已连接" : "未连接"}`,
    showCancel: false,
  });
};

let isMorePacket = {
  init_real_time: false,
  daily_data: false,
  weekly_data: false,
  monthly_data: false,
  daily_one_minutes_data: false,
  daily_five_minutes_data: false,
  daily_fifteen_minutes_data: false,
  daily_thirty_minutes_data: false,
  daily_sixty_minutes_data: false,
};
let receivedMessage;
const findJsonPacket = (message, command) => {
  let jsonStartIndex = 0;
  let jsonEndIndex = message.indexOf(command);
  let jsonStartCount = 0;
  let jsonEndCount = 0;

  for (let i = 0; i < message.length - 1; ++i) {
    if (message[i] == "{") {
      jsonStartCount++;
      if (jsonStartCount == 2) {
        jsonStartIndex = i;
        break;
      }
    }
  }
  for (let i = message.indexOf(command); i >= 0; --i) {
    if (message[i] == "}" || i == jsonStartIndex) {
      jsonEndCount++;
      if (jsonEndCount == 1) {
        jsonEndIndex = i;
        break;
      }
    }
  }

  // 检查JSON字符串是否有效
  if (jsonStartIndex >= jsonEndIndex) {
    return { error: true };
  }

  return { json: JSON.parse(message.substring(jsonStartIndex, jsonEndIndex + 1)) };
};

// 根据timeData中最后一个时间生成下一个时间
const generateNextTime = () => {
  if (timeData.value.length === 0) {
    return "09:30"; // 如果没有数据，返回开盘时间
  }

  const lastTime = timeData.value[timeData.value.length - 1].time;
  if (!lastTime) {
    return "09:30";
  }

  // 解析时间字符串，格式为 "HH:MM"
  const [hours, minutes] = lastTime.split(":").map(Number);

  // 计算下一分钟
  let nextMinutes = minutes + 1;
  let nextHours = hours;

  // 处理分钟进位
  if (nextMinutes >= 60) {
    nextMinutes = 0;
    nextHours += 1;
  }

  // 处理小时进位（24小时制）
  if (nextHours >= 24) {
    nextHours = 0;
  }

  // 格式化为 "HH:MM" 格式
  const formattedHours = nextHours.toString().padStart(2, "0");
  const formattedMinutes = nextMinutes.toString().padStart(2, "0");

  return `${formattedHours}:${formattedMinutes}`;
};
// 解析TCP股票数据
const parseStockData = (message) => {
  try {
    console.log("进入parseStockData, message类型:", typeof message);

    let parsedMessage;
    // 如果isMorePacket是true，说明正在接受分包数据，无条件接收
    // 如果message是字符串且以{开头，说明是JSON字符串，需要解析
    // 如果不属于以上两种情况，说明是普通字符串，不预解析
    if (message.includes("欢迎连接到股票数据服务器")) {
      console.log("服务器命令列表，不予处理");
      return;
    }
    if (message.includes("real_time")) {
      let startIndex = 0;
      let endIndex = message.length;
      for (let i = 0; i < message.length - 1; ++i) {
        if (message[i] == "{") {
          startIndex = i;
          break;
        }
      }
      for (let i = message.length - 1; i >= 0; --i) {
        if (message[i] == "}") {
          endIndex = i;
          break;
        }
      }
      parsedMessage = JSON.parse(message.substring(startIndex, endIndex + 1));
      console.log("实时数据解析", parsedMessage);
      // 处理实时数据
      timeData.value.push({
        time: generateNextTime(),
        price: parsedMessage.current_price,
        volume: parsedMessage.volume,
        amount: parsedMessage.amount,
      });
      // 实时更新股票信息
      stockInformation.value.currentPrice = parsedMessage.current_price;
      stockInformation.value.openPrice = parsedMessage.open_price;
      stockInformation.value.closePrice = parsedMessage.close_price;
      stockInformation.value.highPrice = parsedMessage.high_price;
      stockInformation.value.lowPrice = parsedMessage.low_price;
      stockInformation.value.volume = parsedMessage.volume;
      stockInformation.value.amount = parsedMessage.amount;
      stockInformation.value.turnoverRatio = parsedMessage.turnover_ratio;
      stockInformation.value.marketValue = parsedMessage.total_market_value;
      stockInformation.value.currentValue = stockInformation.value.currentPrice - stockInformation.value.lastDayStockClosePrice;
      stockInformation.value.currentRatio = ((stockInformation.value.currentPrice - stockInformation.value.lastDayStockClosePrice) / stockInformation.value.lastDayStockClosePrice) * 100;
      console.log("重绘画面");
      drawChart();
      if (timeData.value.length >= 240) {
        sendTcpMessage("stop_real_time");
      }
      return;
    } else if ((typeof message === "string" && message.includes("init_real_data_start")) || isMorePacket.init_real_time) {
      if (typeof message === "string" && message.includes("init_real_data_start")) {
        console.log("开始接受分包数据");
        receivedMessage = "";
      } else {
        console.log("接收分包数据过程中");
      }
      isMorePacket.init_real_time = true;
      receivedMessage += message;
      // 如果当前消息包含}，说明收到JSON字符串结尾，结束接收，开始解析
      if (receivedMessage.includes("init_real_data_complete")) {
        console.log("接受分包数据结束");
        isMorePacket.init_real_time = false;

        console.log("展示数据", receivedMessage);

        const result = findJsonPacket(receivedMessage, "init_real_data_complete");
        if (result.error) {
          throw new Error("解析JSON字符串失败");
        } else {
          parsedMessage = result.json;
          console.log("JSON解析成功，解析后类型:", typeof parsedMessage, parsedMessage);
          if (parsedMessage.type === "daily_data") {
            timeData.value = parsedMessage.data;
            stockInformation.value.lastDayStockClosePrice = parsedMessage.pre_close;
            console.log("lastDayStockClosePrice", stockInformation.value.lastDayStockClosePrice);
            drawChart();
            sendTcpMessage("stop_real_time");
            sendTcpMessage("real_time");
          }
        }
      }
    } else if ((typeof message === "string" && message.includes("daily_data_start")) || isMorePacket.daily_data) {
      if (typeof message === "string" && message.includes("daily_data_start")) {
        console.log("开始接受分包数据");
        receivedMessage = "";
      } else {
        console.log("接收分包数据过程中");
      }
      isMorePacket.daily_data = true;
      receivedMessage += message;
      // 如果当前消息包含}，说明收到JSON字符串结尾，结束接收，开始解析
      if (receivedMessage.includes("daily_data_complete")) {
        console.log("接受分包数据结束");
        isMorePacket.daily_data = false;

        console.log("展示数据", receivedMessage);

        const result = findJsonPacket(receivedMessage, "daily_data_complete");
        if (result.error) {
          throw new Error("解析JSON字符串失败");
        } else {
          parsedMessage = result.json;
          console.log("JSON解析成功，解析后类型:", typeof parsedMessage, parsedMessage);
          if (parsedMessage.type === "daily_data") {
            klineData.value = parsedMessage.data.map((item) => ({
              open: item.ask_open,
              close: item.ask_close,
              high: item.ask_high,
              low: item.ask_low,
              volume: item.tick_qty,
              date: item.trade_date ? `${item.trade_date.slice(0, 4)}-${item.trade_date.slice(4, 6)}-${item.trade_date.slice(6, 8)}` : item.trade_date,
            }));
            stockInformation.value.lastDayStockClosePrice = klineData.value[klineData.value.length - 2].close;
            touchState.offset = canvasWidth.value / klineData.value.length / 2;
            console.log("lastDayStockClosePrice", stockInformation.value.lastDayStockClosePrice);
            drawChart();
          }
        }
      }
    } else if ((typeof message === "string" && message.includes("weekly_data_start")) || isMorePacket.weekly_data) {
      if (typeof message === "string" && message.includes("weekly_data_start")) {
        console.log("开始接受分包数据");
        receivedMessage = "";
      } else {
        console.log("接收分包数据过程中");
      }
      isMorePacket.weekly_data = true;
      receivedMessage += message;
      // 如果当前消息包含}，说明收到JSON字符串结尾，结束接收，开始解析
      if (receivedMessage.includes("weekly_data_complete")) {
        console.log("接受分包数据结束");
        isMorePacket.weekly_data = false;

        console.log("展示数据", receivedMessage);

        const result = findJsonPacket(receivedMessage, "weekly_data_complete");
        if (result.error) {
          throw new Error("解析JSON字符串失败");
        } else {
          parsedMessage = result.json;
          console.log("JSON解析成功，解析后类型:", typeof parsedMessage, parsedMessage);
          if (parsedMessage.type === "weekly_data") {
            klineData.value = parsedMessage.data.map((item) => ({
              open: item.bid_open,
              close: item.bid_close,
              high: item.bid_high,
              low: item.bid_low,
              volume: item.vol,
              amount: item.amount,
              date: item.trade_date ? `${item.trade_date.slice(0, 4)}-${item.trade_date.slice(4, 6)}-${item.trade_date.slice(6, 8)}` : item.trade_date,
            }));
            stockInformation.value.lastDayStockClosePrice = klineData.value[klineData.value.length - 2].close;
            touchState.offset = canvasWidth.value / klineData.value.length / 2;
            console.log("lastDayStockClosePrice", stockInformation.value.lastDayStockClosePrice);
            drawChart();
          }
        }
      }
    } else if ((typeof message === "string" && message.includes("monthly_data_start")) || isMorePacket.monthly_data) {
      if (typeof message === "string" && message.includes("monthly_data_start")) {
        console.log("开始接受分包数据");
        receivedMessage = "";
      } else {
        console.log("接收分包数据过程中");
      }
      isMorePacket.monthly_data = true;
      receivedMessage += message;
      // 如果当前消息包含}，说明收到JSON字符串结尾，结束接收，开始解析
      if (receivedMessage.includes("monthly_data_complete")) {
        console.log("接受分包数据结束");
        isMorePacket.monthly_data = false;

        console.log("展示数据", receivedMessage);

        const result = findJsonPacket(receivedMessage, "monthly_data_complete");
        if (result.error) {
          throw new Error("解析JSON字符串失败");
        } else {
          parsedMessage = result.json;
          console.log("JSON解析成功，解析后类型:", typeof parsedMessage, parsedMessage);
          if (parsedMessage.type === "monthly_data") {
            klineData.value = parsedMessage.data.map((item) => ({
              open: item.bid_open,
              close: item.bid_close,
              high: item.bid_high,
              low: item.bid_low,
              volume: item.vol,
              amount: item.amount,
              date: item.trade_date ? `${item.trade_date.slice(0, 4)}-${item.trade_date.slice(4, 6)}-${item.trade_date.slice(6, 8)}` : item.trade_date,
            }));
            stockInformation.value.lastDayStockClosePrice = klineData.value[klineData.value.length - 2].close;
            touchState.offset = canvasWidth.value / klineData.value.length / 2;
            console.log("lastDayStockClosePrice", stockInformation.value.lastDayStockClosePrice);
            drawChart();
          }
        }
      }
    } else if ((typeof message === "string" && message.includes("daily_one_minutes_data_start")) || isMorePacket.daily_one_minutes_data) {
      if (typeof message === "string" && message.includes("daily_one_minutes_data_start")) {
        console.log("开始接受分包数据");
        receivedMessage = "";
      } else {
        console.log("接收分包数据过程中");
      }
      isMorePacket.daily_one_minutes_data = true;
      receivedMessage += message;
      // 如果当前消息包含}，说明收到JSON字符串结尾，结束接收，开始解析
      if (receivedMessage.includes("daily_one_minutes_data_complete")) {
        console.log("接受分包数据结束");
        isMorePacket.daily_one_minutes_data = false;

        console.log("展示数据", receivedMessage);

        const result = findJsonPacket(receivedMessage, "daily_one_minutes_data_complete");
        if (result.error) {
          throw new Error("解析JSON字符串失败");
        } else {
          parsedMessage = result.json;
          console.log("JSON解析成功，解析后类型:", typeof parsedMessage, parsedMessage);
          if (parsedMessage.type === "daily_one_minutes_data") {
            klineData.value = parsedMessage.data.map((item) => ({
              open: item.open,
              close: item.close,
              high: Math.max(item.low, item.high),
              low: Math.min(item.low, item.high),
              volume: item.volume,
              amount: item.amount,
              date: item.timestamp,
            }));
            stockInformation.value.lastDayStockClosePrice = klineData.value[klineData.value.length - 2].close;
            touchState.offset = canvasWidth.value / klineData.value.length / 2;
            console.log("lastDayStockClosePrice", stockInformation.value.lastDayStockClosePrice);
            drawChart();
          }
        }
      }
    } else if ((typeof message === "string" && message.includes("daily_five_minutes_data_start")) || isMorePacket.daily_five_minutes_data) {
      if (typeof message === "string" && message.includes("daily_five_minutes_data_start")) {
        console.log("开始接受分包数据");
        receivedMessage = "";
      } else {
        console.log("接收分包数据过程中");
      }
      isMorePacket.daily_five_minutes_data = true;
      receivedMessage += message;
      // 如果当前消息包含}，说明收到JSON字符串结尾，结束接收，开始解析
      if (receivedMessage.includes("daily_five_minutes_data_complete")) {
        console.log("接受分包数据结束");
        isMorePacket.daily_five_minutes_data = false;

        console.log("展示数据", receivedMessage);

        const result = findJsonPacket(receivedMessage, "daily_five_minutes_data_complete");
        if (result.error) {
          throw new Error("解析JSON字符串失败");
        } else {
          parsedMessage = result.json;
          console.log("JSON解析成功，解析后类型:", typeof parsedMessage, parsedMessage);
          if (parsedMessage.type === "daily_five_minutes_data") {
            klineData.value = parsedMessage.data.map((item) => ({
              open: item.open,
              close: item.close,
              high: Math.max(item.low, item.high),
              low: Math.min(item.low, item.high),
              volume: item.volume,
              amount: item.amount,
              date: item.timestamp,
            }));
            stockInformation.value.lastDayStockClosePrice = klineData.value[klineData.value.length - 2].close;
            touchState.offset = canvasWidth.value / klineData.value.length / 2;
            console.log("lastDayStockClosePrice", stockInformation.value.lastDayStockClosePrice);
            drawChart();
          }
        }
      }
    } else if ((typeof message === "string" && message.includes("daily_fifteen_minutes_data_start")) || isMorePacket.daily_fifteen_minutes_data) {
      if (typeof message === "string" && message.includes("daily_fifteen_minutes_data_start")) {
        console.log("开始接受分包数据");
        receivedMessage = "";
      } else {
        console.log("接收分包数据过程中");
      }
      isMorePacket.daily_fifteen_minutes_data = true;
      receivedMessage += message;
      // 如果当前消息包含}，说明收到JSON字符串结尾，结束接收，开始解析
      if (receivedMessage.includes("daily_fifteen_minutes_data_complete")) {
        console.log("接受分包数据结束");
        isMorePacket.daily_fifteen_minutes_data = false;

        console.log("展示数据", receivedMessage);

        const result = findJsonPacket(receivedMessage, "daily_fifteen_minutes_data_complete");
        if (result.error) {
          throw new Error("解析JSON字符串失败");
        } else {
          parsedMessage = result.json;
          console.log("JSON解析成功，解析后类型:", typeof parsedMessage, parsedMessage);
          if (parsedMessage.type === "daily_fifteen_minutes_data") {
            klineData.value = parsedMessage.data.map((item) => ({
              open: item.open,
              close: item.close,
              high: Math.max(item.low, item.high),
              low: Math.min(item.low, item.high),
              volume: item.volume,
              amount: item.amount,
              date: item.timestamp,
            }));
            stockInformation.value.lastDayStockClosePrice = klineData.value[klineData.value.length - 2].close;
            touchState.offset = canvasWidth.value / klineData.value.length / 2;
            console.log("lastDayStockClosePrice", stockInformation.value.lastDayStockClosePrice);
            drawChart();
          }
        }
      }
    } else if ((typeof message === "string" && message.includes("daily_thirty_minutes_data_start")) || isMorePacket.daily_thirty_minutes_data) {
      if (typeof message === "string" && message.includes("daily_thirty_minutes_data_start")) {
        console.log("开始接受分包数据");
        receivedMessage = "";
      } else {
        console.log("接收分包数据过程中");
      }
      isMorePacket.daily_thirty_minutes_data = true;
      receivedMessage += message;
      // 如果当前消息包含}，说明收到JSON字符串结尾，结束接收，开始解析
      if (receivedMessage.includes("daily_thirty_minutes_data_complete")) {
        console.log("接受分包数据结束");
        isMorePacket.daily_thirty_minutes_data = false;

        console.log("展示数据", receivedMessage);

        const result = findJsonPacket(receivedMessage, "daily_thirty_minutes_data_complete");
        if (result.error) {
          throw new Error("解析JSON字符串失败");
        } else {
          parsedMessage = result.json;
          console.log("JSON解析成功，解析后类型:", typeof parsedMessage, parsedMessage);
          if (parsedMessage.type === "daily_thirty_minutes_data") {
            klineData.value = parsedMessage.data.map((item) => ({
              open: item.open,
              close: item.close,
              high: Math.max(item.low, item.high),
              low: Math.min(item.low, item.high),
              volume: item.volume,
              amount: item.amount,
              date: item.timestamp,
            }));
            stockInformation.value.lastDayStockClosePrice = klineData.value[klineData.value.length - 2].close;
            touchState.offset = canvasWidth.value / klineData.value.length / 2;
            console.log("lastDayStockClosePrice", stockInformation.value.lastDayStockClosePrice);
            drawChart();
          }
        }
      }
    } else if ((typeof message === "string" && message.includes("daily_sixty_minutes_data_start")) || isMorePacket.daily_sixty_minutes_data) {
      if (typeof message === "string" && message.includes("daily_sixty_minutes_data_start")) {
        console.log("开始接受分包数据");
        receivedMessage = "";
      } else {
        console.log("接收分包数据过程中");
      }
      isMorePacket.daily_sixty_minutes_data = true;
      receivedMessage += message;
      // 如果当前消息包含}，说明收到JSON字符串结尾，结束接收，开始解析
      if (receivedMessage.includes("daily_sixty_minutes_data_complete")) {
        console.log("接受分包数据结束");
        isMorePacket.daily_sixty_minutes_data = false;

        console.log("展示数据", receivedMessage);

        const result = findJsonPacket(receivedMessage, "daily_sixty_minutes_data_complete");
        if (result.error) {
          throw new Error("解析JSON字符串失败");
        } else {
          parsedMessage = result.json;
          console.log("JSON解析成功，解析后类型:", typeof parsedMessage, parsedMessage);
          if (parsedMessage.type === "daily_sixty_minutes_data") {
            klineData.value = parsedMessage.data.map((item) => ({
              open: item.open,
              close: item.close,
              high: Math.max(item.low, item.high),
              low: Math.min(item.low, item.high),
              volume: item.volume,
              amount: item.amount,
              date: item.timestamp,
            }));
            stockInformation.value.lastDayStockClosePrice = klineData.value[klineData.value.length - 2].close;
            touchState.offset = canvasWidth.value / klineData.value.length / 2;
            console.log("lastDayStockClosePrice", stockInformation.value.lastDayStockClosePrice);
            drawChart();
          }
        }
      }
    } else {
      // 没有通过JSON解析判断，说明不是需要的数据
      console.log("不是需要的数据，不做处理");
    }
  } catch (error) {
    console.error("解析TCP股票数据失败:", error.message);
    console.error("错误详情:", error);
  }
};

// 移除TCP监听器
const removeTcpListeners = () => {
  if (connectionListener.value) {
    tcpConnection.removeConnectionListener(connectionListener.value);
    connectionListener.value = null;
    console.log("已移除TCP连接状态监听器");
  }

  if (messageListener.value) {
    tcpConnection.removeMessageListener(messageListener.value);
    messageListener.value = null;
    console.log("已移除TCP消息监听器");
  }
};

// 定时器标识（用于清除定时器）
let timer = null;
let index = 0;
// 定时添加数据的函数
const startAddDataTimer = () => {
  if (timer) {
    console.log("存在旧定时器，卸载旧定时器");
    clearInterval(timer);
  }
  console.log("开始定时任务");
  // 每隔5秒执行一次
  timer = setInterval(() => {
    if (index < testTimeData.length) {
      timeData.value.push(testTimeData[index]);
      console.log("新增数据：", testTimeData[index]);
      // 触发图表重新绘制
      drawChart();
      index++;
    } else {
      clearInterval(timer);
    }
  }, 2000); // 5000毫秒 = 5秒
};

onLoad((options) => {
  console.log("页面接收到的参数：", options);

  // 处理通过stockInformation参数传递的复杂对象
  if (options.stockInformation) {
    try {
      const stockData = JSON.parse(decodeURIComponent(options.stockInformation));
      console.log("解析的股票数据：", stockData);

      // 更新stockInformation
      if (stockData) {
        stockInformation.value.stockName = stockData.stockName;
        stockInformation.value.stockCode = stockData.stockCode;
      }
    } catch (error) {
      console.error("解析股票数据失败：", error);
    }
  }

  // 处理index参数（股票在列表中的位置）
  if (options.index !== undefined) {
    const stockIndex = parseInt(options.index);
    console.log("股票在列表中的索引：", stockIndex);
    // 将index保存到响应式变量中，用于后续的左右切换功能
    currentStockIndex.value = stockIndex;
  }

  // 处理stockFrom参数（股票来源）
  if (options.stockFrom) {
    currentStockFrom.value = options.stockFrom;
  }
});

// 保存定时器，用于页面卸载时清理
onUnmounted(() => {
  removeTcpListeners();
  disconnect();
  if (timer) {
    console.log("卸载定时器");
    clearInterval(timer);
  }
});

onMounted(async () => {
  try {
    console.log("步骤1: 初始化系统信息");
    const systemInfo = uni.getSystemInfoSync();
    pixelRatio.value = systemInfo.pixelRatio;
    // 设置Canvas实际像素（考虑pixelRatio以获得高清效果）
    // 1rpx = 设备屏幕宽度 / 750
    const rpxToPx = systemInfo.windowWidth / 750;
    const offsetHeight = (150 + 200 + 80 + 150 + 30) * rpxToPx; // 350rpx转换为px
    const calculatedHeight = systemInfo.windowHeight - offsetHeight;
    canvasWidth.value = systemInfo.windowWidth;
    canvasHeight.value = Math.max(calculatedHeight, canvasHeight.value);

    initTcpListeners();
    await nextTick();
    // 开始连接
    startTcp();

    if (timeData.value && timeData.value.length > 0) {
      // 当前股价
      stockInformation.value.currentPrice = timeData.value[timeData.value.length - 1].price;
      // 涨跌额度
      stockInformation.value.currentValue = stockInformation.value.currentPrice - stockInformation.value.lastDayStockClosePrice;
      // 涨跌幅度
      stockInformation.value.currentRatio = ((stockInformation.value.currentPrice - stockInformation.value.lastDayStockClosePrice) / stockInformation.value.lastDayStockClosePrice) * 100;
      // 成交量
      stockInformation.value.volume = timeData.value[timeData.value.length - 1].volume;
      text[0][1].value = utils.formatPrice(stockInformation.value.currentPrice);
      text[1][0].value = utils.formatStockNumber(stockInformation.value.volume);
    } else {
      console.warn("没有时间数据，跳过股票信息计算");
    }
    await nextTick();
    setTimeout(() => {
      initCanvas();
    }, 100);
    console.log("所有初始化步骤完成");
  } catch (error) {
    console.error("初始化过程中出现错误:", error);
  }
});
</script>

<style>
.container {
  width: 100%;
  min-height: 100vh;
  background-color: #f6f6f6;
}

.title-container {
  width: 100%;
  height: 150rpx;
  background-color: white;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}

.title {
  /* border: 1px solid #ff0000; */
  position: relative;
  /* 关键：作为绝对定位的父容器 */
  width: 100%;
  height: 80rpx;
  display: flex;
  justify-content: center;
  align-items: center;
}

.back-homepage-btn {
  margin-left: 40rpx;
  margin-right: auto;
  height: 30rpx;
  width: 20rpx;
  z-index: 1;
}

.mid-title {
  position: absolute;
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
  align-items: center;
}

.left-page {
  height: 40rpx;
  width: 40rpx;
}

.right-page {
  height: 40rpx;
  width: 40rpx;
}

.stock-id {
  margin: 0rpx 40rpx;
  display: flex;
  flex-direction: column;
  text-align: center;
}

.stock-name {
  font-weight: bold;
}

.stock-code {
  font-size: 0.8rem;
  font-weight: bold;
  color: #a1a1a1;
}

.search {
  height: 40rpx;
  width: 40rpx;
}

.more {
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  margin-right: 40rpx;
  margin-left: 20rpx;
}

.body {
  overflow: auto;
  height: calc(100vh - 305rpx);
  /* border: 1px solid red; */
}

.stock-information {
  background-color: white;
  width: 100%;
  height: 200rpx;
  margin: 10rpx 0rpx;
  display: flex;
  align-items: center;
  position: relative;
  /* 为伪元素定位做准备 */
}

/* 右下角黑色三角形 */
.stock-information::after {
  content: "";
  position: absolute;
  bottom: 0;
  right: 0;
  width: 0;
  height: 0;
  border-left: 20rpx solid transparent;
  border-bottom: 20rpx solid #6a6a6a;
}

.stock-detail-container {
  position: absolute;
  top: 100%;
  /* 在父容器下方 */
  left: 0;
  /* 从左边开始 */
  right: 0;
  /* 到右边结束 */
  width: 100%;
  /* height: 300rpx; 使用固定高度替代calc，避免计算问题 */
  height: calc(100vh - 515rpx);
  display: flex;
  flex-direction: column;
  background-color: rgba(0, 0, 0, 0.6);
  z-index: 100;
  box-sizing: border-box;
  pointer-events: auto;
}

.stock-detail {
  border: 1px solid #cacaca;
  width: 100%;
  display: flex;
  background-color: white;
  padding: 10rpx 0;
}

.first-column,
.second-column {
  width: 50%;
  height: 100%;
  display: flex;
  flex-direction: column;
  color: #6a6a6a;
  font-size: 0.8rem;
}

.first-column-data,
.second-column-data {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10rpx 40rpx;
}

.stock-detail-title {
  color: #6a6a6a;
}

.stock-detail-value {
  color: black;
}

.price-up {
  color: #10b981;
}

.price-down {
  color: #ef4444;
}

.price-none {
  color: black;
}

.stock-current-data {
  width: 30%;
  height: 70%;
  display: flex;
  flex-direction: column;
}

.stock-current-price {
  font-weight: bold;
  font-size: 1.2rem;
  width: 100%;
  height: 50%;
  display: flex;
  justify-content: center;
  align-items: center;
}

.stock-current-other {
  width: 100%;
  height: 50%;
  display: flex;
  font-weight: bold;
  font-size: 0.6rem;
}

.stock-current-value {
  width: 50%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
}

.stock-current-ratio {
  width: 50%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
}

.stock-other-data {
  height: 100%;
  width: 70%;
  display: flex;
  flex-direction: column;
}

.first-line,
.second-line,
.third-line {
  display: flex;
  align-items: center;
  width: 100%;
  height: 33%;
  /* font-weight: bold; */
  font-size: 0.8rem;
}

.value {
  margin-left: auto;
  margin-right: 20rpx;
}

.high-price,
.volume,
.volume-ratio,
.low-price,
.amount,
.market-earn,
.open-price,
.turnover-ratio,
.market-value {
  display: flex;
  flex: 1;
}

.stock-chart {
  width: 100%;
}

.stock-kline-tab {
  display: flex;
  width: 100%;
  height: 80rpx;
  /* border: 1px solid black; */
}

.tab-day,
.tab-month,
.tab-time,
.tab-week,
.tab-more,
.tab-setting {
  display: flex;
  justify-content: center;
  align-items: center;
  flex: 1;
  font-size: 0.8rem;
  color: #6a6a6a;
  position: relative;
}

/* 向下小三角样式 */
.arrow-down {
  width: 0;
  height: 0;
  border-left: 10rpx solid transparent;
  border-right: 10rpx solid transparent;
  border-bottom: 12rpx solid currentColor;
  margin-left: 8rpx;
  display: inline-block;
}

.arrow-up {
  width: 0;
  height: 0;
  border-left: 10rpx solid transparent;
  border-right: 10rpx solid transparent;
  border-top: 12rpx solid currentColor;
  margin-left: 8rpx;
  display: inline-block;
}

.arrow-left {
  width: 0;
  height: 0;
  border-top: 10rpx solid transparent;
  border-bottom: 10rpx solid transparent;
  border-right: 12rpx solid currentColor;
  margin-left: 8rpx;
  display: inline-block;
}

.arrow-right {
  width: 0;
  height: 0;
  border-top: 10rpx solid transparent;
  border-bottom: 10rpx solid transparent;
  border-left: 12rpx solid currentColor;
  margin-left: 8rpx;
  display: inline-block;
}

.moreTabsContainer {
  width: 100%;
  display: flex;
  gap: 5rpx;
  margin-bottom: 5rpx;
}

.moreTabItem {
  flex: 1;
  padding: 10rpx 20rpx;
  border-radius: 10rpx;
  background-color: white;
  font-size: 0.85rem;
  color: #6a6a6a;
  text-align: center;
}

.tab-selected {
  color: #db1f1d;
}

.tab-selected::after {
  content: "";
  position: absolute;
  bottom: 10rpx;
  left: 50%;
  transform: translateX(-50%);
  width: 40rpx;
  height: 5rpx;
  background-color: #db1f1d;
}

.tab-setting-img {
  width: 30rpx;
  height: 30rpx;
}

.time-chart-container {
  width: 100%;
  min-height: 400rpx;
  background-color: #ffffff;
  box-sizing: border-box;
}

.stock-chart {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.kline-chart-container {
  width: 100%;
  height: 400rpx;
  background-color: #ffffff;
  padding: 20rpx;
  box-sizing: border-box;
  display: flex;
  justify-content: center;
  align-items: center;
}

.bottomTool {
  width: 100%;
  height: 150rpx;
  position: fixed;
  bottom: 0;
  background-color: white;
  display: flex;
  box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.1);
}

.index,
.function,
.favorites {
  flex: 1;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  font-size: 12px;
  transition: all 0.2s ease;
}

.index:active,
.function:active,
.favorites:active {
  background-color: rgba(99, 99, 99, 0.5);
  transform: scale(0.95);
  transition: all 0.1s ease;
}

.icon {
  width: 40rpx;
  height: 40rpx;
}
</style>
