Переглянути джерело

套利价差,更新图表

DESKTOP-NE65RNK\Citrus_limon 3 місяців тому
батько
коміт
bdb72f8014

+ 1 - 1
.env.development

@@ -1 +1 @@
-VITE_API_BASE_URL = "http://cc.skyfffire.com:80"
+VITE_API_BASE_URL = "http://192.168.31.95:82"

+ 1 - 1
.env.production

@@ -1 +1 @@
-VITE_API_BASE_URL = "http://cc.skyfffire.com:80"
+VITE_API_BASE_URL = ""

+ 2 - 0
.gitignore

@@ -9,6 +9,7 @@ lerna-debug.log*
 
 node_modules
 dist
+dist.*
 dist-ssr
 *.local
 
@@ -22,3 +23,4 @@ dist-ssr
 *.njsproj
 *.sln
 *.sw?
+

+ 1 - 1
index.html

@@ -4,7 +4,7 @@
     <meta charset="UTF-8" />
     <link rel="icon" type="image/svg+xml" href="/vite.svg" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <title>两江资本</title>
+    <title>4L CAPITAL</title>
   </head>
   <body>
     <div id="app"></div>

+ 17 - 0
src/assets/images/bg.svg

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="1457px" height="651px" viewBox="0 0 1457 651" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 54 (76480) - https://sketchapp.com -->
+    <title>bg</title>
+    <desc>Created with Sketch.</desc>
+    <g id="页面1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="body">
+            <rect id="矩形" fill="#E0E3F5" transform="translate(1111.405592, 620.405592) rotate(45.000000) translate(-1111.405592, -620.405592) " x="1089.90559" y="598.905592" width="43" height="43"></rect>
+            <rect id="矩形" fill="#DEEFF6" transform="translate(127.254834, 230.254834) rotate(40.000000) translate(-127.254834, -230.254834) " x="95.254834" y="198.254834" width="64" height="64"></rect>
+            <circle id="Oval-7" fill="#C9CFED" fill-rule="nonzero" opacity="0.45" cx="1286.5" cy="27.5" r="23.5"></circle>
+            <circle id="Oval-7" fill="#E7E6E6" fill-rule="nonzero" opacity="0.45" cx="191" cy="51" r="18"></circle>
+            <circle id="Oval-7" fill="#EFEFEF" fill-rule="nonzero" opacity="0.45" cx="888" cy="499" r="18"></circle>
+            <ellipse id="Oval-7" stroke="#E4E4F2" stroke-width="10" fill-opacity="0" fill="#FFFFFF" fill-rule="nonzero" opacity="0.45" cx="1373.5" cy="331.5" rx="78.5" ry="79.5"></ellipse>
+            <ellipse id="Oval-7" stroke="#CEECF8" stroke-width="10" fill-opacity="0" fill="#FFFFFF" fill-rule="nonzero" opacity="0.45" cx="235" cy="481.5" rx="44" ry="44.5"></ellipse>
+        </g>
+    </g>
+</svg>

+ 30 - 0
src/assets/images/logo.svg

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg id="_图层_2" data-name="图层 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 669.77 121.77">
+  <defs>
+    <style>
+      .cls-1, .cls-2 {
+        stroke-width: 0px;
+      }
+
+      .cls-2 {
+        fill: #2467e9;
+      }
+    </style>
+  </defs>
+  <g id="_图层_1-2" data-name="图层 1">
+    <g>
+      <path class="cls-1" d="m199.71,76.15v16.57h-14.62v-16.57h-39.47l25.83-51.66h28.26v39.28h8.28v12.38h-8.28Zm-14.62-12.38V24.49l-19.59,39.28h19.59Z"/>
+      <path class="cls-1" d="m216.76,24.49h14.62v55.55h33.14v12.67h-47.76V24.49Z"/>
+      <path class="cls-1" d="m286.94,58.6c0-5,.94-9.68,2.83-14.03,1.88-4.35,4.43-8.12,7.65-11.31,3.22-3.18,6.98-5.68,11.31-7.5,4.32-1.82,8.92-2.73,13.79-2.73,3.7,0,7.24.54,10.62,1.61,3.38,1.07,6.5,2.55,9.36,4.43v59.06c-2.92,1.89-6.06,3.36-9.41,4.43-3.35,1.07-6.87,1.61-10.58,1.61-4.87,0-9.47-.91-13.79-2.73-4.32-1.82-8.09-4.32-11.31-7.5-3.22-3.18-5.77-6.95-7.65-11.31-1.89-4.35-2.83-9.03-2.83-14.04Zm15.59,0c0,2.86.52,5.51,1.56,7.94,1.04,2.44,2.47,4.55,4.29,6.33,1.82,1.79,3.95,3.18,6.38,4.19,2.44,1.01,5.02,1.51,7.75,1.51s5.31-.5,7.75-1.51c2.44-1.01,4.56-2.4,6.38-4.19,1.82-1.79,3.25-3.9,4.29-6.33,1.04-2.44,1.56-5.08,1.56-7.94s-.52-5.51-1.56-7.94c-1.04-2.44-2.47-4.55-4.29-6.33-1.82-1.79-3.95-3.18-6.38-4.19-2.44-1.01-5.02-1.51-7.75-1.51s-5.31.5-7.75,1.51c-2.44,1.01-4.57,2.4-6.38,4.19-1.82,1.79-3.25,3.9-4.29,6.33-1.04,2.44-1.56,5.08-1.56,7.94Z"/>
+      <path class="cls-1" d="m399.6,92.71l-5.65-19.79h-27.78l-5.65,19.79h-13.64l19.59-68.22h13.65l-10.23,35.77h20.47l-10.23-35.77h14.62l19.49,68.22h-14.62Z"/>
+      <path class="cls-1" d="m442.1,24.49c4.81,0,9.11.46,12.91,1.36,3.8.91,7.02,2.31,9.65,4.19,2.63,1.89,4.64,4.32,6.04,7.31,1.4,2.99,2.09,6.56,2.09,10.72,0,3.57-.71,6.84-2.14,9.8-1.43,2.96-3.46,5.49-6.09,7.6-2.63,2.11-5.85,3.75-9.65,4.92-3.8,1.17-8.07,1.75-12.82,1.75h-8.87v20.56h-14.62V24.49h23.49Zm-8.87,12.67v22.32h11.31c4.48,0,7.96-1.05,10.43-3.17,2.47-2.11,3.7-4.86,3.7-8.24,0-3.7-1.24-6.45-3.7-8.24-2.47-1.79-5.95-2.68-10.43-2.68h-11.31Z"/>
+      <path class="cls-1" d="m494.24,24.49v68.22h-14.62V24.49h14.62Z"/>
+      <path class="cls-1" d="m500.09,24.49h56.53v12.67h-20.96v55.55h-14.62v-55.55h-20.96v-12.67Z"/>
+      <path class="cls-1" d="m603.01,92.71l-5.65-19.79h-27.78l-5.65,19.79h-13.64l19.59-68.22h13.65l-10.23,35.77h20.47l-10.23-35.77h14.62l19.49,68.22h-14.62Z"/>
+      <path class="cls-1" d="m622.01,24.49h14.62v55.55h33.14v12.67h-47.76V24.49Z"/>
+    </g>
+    <path class="cls-2" d="m72.62,98.3v23.47H0V0h24.21v77.73c0,11.36,9.21,20.57,20.57,20.57h27.85Z"/>
+    <path class="cls-2" d="m72.62,0v74.09h-17.46c-3.72,0-6.74-3.02-6.74-6.74V0h24.21Z"/>
+    <rect class="cls-2" x="96.83" width="24.94" height="121.77"/>
+  </g>
+</svg>

BIN
src/assets/images/logo_192x192.png


+ 17 - 8
src/components/PageLayout/Header.vue

@@ -1,8 +1,10 @@
 <template>
   <div class="header-wp">
     <div class="header-left-wp">
-      <div class="title">两江资本</div>
-      <div class="sub-title">无敌中控</div>
+      <div class="logo">
+        <img class="logo-svg" src="../../assets/images/logo.svg" />
+        <img class="logo-png" src="../../assets/images/logo_192x192.png" />
+      </div>
     </div>
   </div>
 </template>
@@ -17,6 +19,7 @@
   border-bottom: 1px solid rgba(0, 0, 0, 0.1);
   display: flex;
   .header-left-wp {
+    height: 64px;
     box-sizing: border-box;
     display: flex;
     justify-content: center;
@@ -24,12 +27,18 @@
     text-align: center;
     width: 256px;
     border-right: 1px solid rgba(0, 0, 0, 0.1);
-    .title {
-      font-size: 18px;
-      font-weight: bold;
-    }
-    .sub-title {
-      font-weight: bold;
+    font-size: 0;
+    .logo {
+      display: flex;
+      justify-content: center;
+      .logo-svg {
+        width: 60%;
+        display: block;
+      }
+      .logo-png {
+        width: 40%;
+        display: none;
+      }
     }
   }
 }

+ 18 - 18
src/components/PageLayout/Layout.vue

@@ -40,24 +40,24 @@ interface MenuList {
 const menuCollapse = ref<boolean>(JSON.parse(window.sessionStorage.getItem("_4L_MENU_COLLAPSE") || "false"));
 
 const menuList = ref<Array<MenuList>>([
-  {
-    icon: "",
-    id: 1,
-    name: "热点币信息",
-    parentId: 0,
-    path: "/hot",
-    type: 1,
-    childMenu: [],
-  },
-  {
-    icon: "",
-    id: 2,
-    name: "热点币监控",
-    parentId: 0,
-    path: "/hot_monitoring",
-    type: 1,
-    childMenu: [],
-  },
+  // {
+  //   icon: "",
+  //   id: 1,
+  //   name: "热点币信息",
+  //   parentId: 0,
+  //   path: "/hot",
+  //   type: 1,
+  //   childMenu: [],
+  // },
+  // {
+  //   icon: "",
+  //   id: 2,
+  //   name: "热点币监控",
+  //   parentId: 0,
+  //   path: "/hot_monitoring",
+  //   type: 1,
+  //   childMenu: [],
+  // },
   {
     icon: "",
     id: 3,

+ 1 - 1
src/router/routes.ts

@@ -3,7 +3,7 @@ import { RouteRecordRaw } from "vue-router";
 const routes: Array<RouteRecordRaw> = [
   {
     path: "/",
-    redirect: "/hot",
+    redirect: "/depth",
   },
   {
     path: "/",

+ 112 - 33
src/views/depth/index.vue

@@ -4,19 +4,34 @@
       <div class="page_wp">
         <div class="content_wp">
           <div class="content_top_wp">
-            <div class="exchange_wp">
-              <div :class="{ exchange_item: true, selected: selectedExchangeList.includes(item) }" v-for="item in exchangeList" @click="handleExchange(item)">
-                {{ item }}
+            <div class="custom-form-layout">
+              <div class="custom-checkbox">
+                <div class="label">交易所</div>
+                <div class="checkbox-group">
+                  <div class="checkbox-wp">
+                    <lay-checkcard-group v-model="selectedExchangeList">
+                      <lay-checkcard v-for="item of exchangeList" :value="item" :title="item" @click="handleExchange(item)" />
+                    </lay-checkcard-group>
+                  </div>
+                </div>
               </div>
             </div>
-            <div class="symbol_wp">
-              <div class="symbol_label">币对:</div>
-              <lay-select v-model="selectedSymbol" :show-search="true" @change="handleSelectedSymbol">
-                <lay-select-option v-for="item in symbolList" :keyword="item.toLocaleLowerCase()" :value="item" :label="item" />
-              </lay-select>
+            <div class="custom-form-layout">
+              <div class="custom-checkbox">
+                <div class="label">时间(小时):</div>
+                <lay-input-number :step-strictly="true" v-model="dayNum" placeholder="单位(小时)" />
+              </div>
+            </div>
+            <div class="custom-form-layout">
+              <div class="custom-checkbox">
+                <div class="label">币对:</div>
+                <lay-select v-model="selectedSymbol" :show-search="true" @change="handleSelectedSymbol">
+                  <lay-select-option v-for="item in symbolList" :keyword="item.toLocaleLowerCase()" :value="item" :label="item" />
+                </lay-select>
+              </div>
             </div>
           </div>
-          <div class="operator_wp" v-show="false">
+          <div class="operator_wp" v-show="true">
             <lay-space>
               <div class="item_wp">
                 <div class="label">自动刷新</div>
@@ -27,7 +42,7 @@
           <div class="content_bottom_wp">
             <lay-loading :loading="chartLoading">
               <div class="chart_wp">
-                <div id="chart"></div>
+                <div id="chart" ref="chartRef"></div>
               </div>
             </lay-loading>
           </div>
@@ -38,10 +53,12 @@
 </template>
 <script lang="ts" setup name="Depth">
 import { ref, shallowRef, reactive, watch, onUnmounted } from "vue";
-import { init, getInstanceByDom } from "echarts";
+import * as echarts from "echarts";
 import dayjs from "dayjs";
 import { get_exchange, get_coin, get_depth, get_incremental_depth } from "@/api";
 
+let chartRef = ref();
+
 let pageLoading = ref(true);
 let chartLoading = ref(true);
 
@@ -50,6 +67,7 @@ let selectedExchangeList = ref<Array<string>>([]);
 
 let symbolList = ref<Array<string>>([]);
 let selectedSymbol = ref();
+let dayNum = ref(2);
 let isUpdate = ref(false);
 
 interface SymbolInfo {
@@ -70,7 +88,7 @@ watch(isUpdate, (value) => {
     clearInterval(timer.value);
     timer.value = setInterval(() => {
       getIncrementalDepth();
-    }, 1000);
+    }, 5000);
   } else {
     clearInterval(timer.value);
   }
@@ -138,7 +156,7 @@ const handleSelectedSymbol = (value: any) => {
 
 // 获取深度信息
 const getDepthInfo = (symbol: string) => {
-  const params = { exchange: selectedExchangeList.value.join(","), coin: symbol };
+  const params = { exchange: selectedExchangeList.value.join(","), coin: symbol, dayNum: dayNum.value };
   chartLoading.value = true;
   get_depth(params, (data: any) => {
     chartLoading.value = false;
@@ -158,7 +176,7 @@ const getIncrementalDepth = () => {
   get_incremental_depth(params, (data: any) => {
     if (data.code == 200) {
       if (data.data.values.length > 0) {
-        let newSeries = handleSeries(data.data.values);
+        let newSeries = handleSeries(data.data.values, data.data.time.length);
         let isUpdate = false;
         series.value = newSeries.map((item: any) => {
           let oldData = series.value.find((items: any) => {
@@ -167,7 +185,6 @@ const getIncrementalDepth = () => {
           });
           if (oldData) {
             oldData.data = oldData.data.slice(item.data.length);
-            // oldData.data = oldData.data.slice(0, oldData.data.length - 5);
             oldData.data.push(...item.data);
             isUpdate = true;
           }
@@ -175,7 +192,6 @@ const getIncrementalDepth = () => {
         });
         if (isUpdate) {
           times.value = times.value.slice(data.data.time.length);
-          // times.value = times.value.slice(0, times.value.length - 5);
           times.value.push(...data.data.time);
           isUpdate = false;
         }
@@ -195,49 +211,58 @@ const getIncrementalDepth = () => {
   });
 };
 
-const handleSeries = (data: any) => {
-  let series: Array<any> = [];
+const handleSeries = (data: any, length: any = -1) => {
+  let newSeries: Array<any> = [];
+  let lastSeries = (series.value || [])
+    .map((item: any) => ({ [item.remark]: item.data[item.data.length - 1].value }))
+    .reduce((acc: any, current: any) => {
+      return { ...acc, ...current };
+    }, {});
+
   for (let item of data) {
     let ask = item.ask.map((price: any) => ({ value: price, name: `${item.exchange}_ASK` }));
     let bid = item.bid.map((price: any) => ({ value: price, name: `${item.exchange}_BID` }));
-    series.push({
+    if ((ask.length == 0 || bid.length == 0) && length > 0) {
+      ask = Array.from({ length }, () => ({ value: lastSeries[`${item.exchange}_ASK`], name: `${item.exchange}_ASK` }));
+      bid = Array.from({ length }, () => ({ value: lastSeries[`${item.exchange}_BID`], name: `${item.exchange}_BID` }));
+    }
+    newSeries.push({
       name: `${item.exchange}`,
       type: "line",
+      silent: true,
+      showSymbol: false,
       data: ask,
       remark: `${item.exchange}_ASK`,
       smooth: true,
     });
 
-    series.push({
+    newSeries.push({
       name: `${item.exchange}`,
       type: "line",
+      silent: true,
+      showSymbol: false,
       data: bid,
       remark: `${item.exchange}_BID`,
       smooth: true,
     });
   }
-  return series;
+  return newSeries;
 };
 
 const initChart = (data: any) => {
-  const chartContainer = document.getElementById("chart");
+  if (myChart.value != null && !myChart.value.isDisposed()) echarts.dispose(myChart.value);
+  myChart.value = echarts.init(chartRef.value);
+  window.removeEventListener("resize", () => myChart.value.resize());
+  window.addEventListener("resize", () => myChart.value.resize());
+
   times.value = data.time;
   series.value = handleSeries(data.values);
 
-  const isChart = getInstanceByDom(chartContainer!);
-  if (isChart) myChart.value.dispose();
-  myChart.value = init(chartContainer);
-  window.removeEventListener("resize", () => {
-    myChart.value.resize();
-  });
-  window.addEventListener("resize", () => {
-    myChart.value.resize();
-  });
-
   const option = {
     title: {
       text: "kline_viewer",
     },
+    animation: false,
     tooltip: {
       trigger: "axis",
       formatter: (params: any) => {
@@ -286,6 +311,8 @@ const initChart = (data: any) => {
 };
 onUnmounted(() => {
   window.removeEventListener("resize", myChart.value.resize());
+  clearInterval(timer.value);
+  myChart.value.dispose();
 });
 </script>
 <style lang="scss" scoped>
@@ -302,8 +329,8 @@ onUnmounted(() => {
   .content_top_wp {
     display: flex;
     align-items: center;
-    justify-content: space-between;
     .exchange_wp {
+      align-items: center;
       display: flex;
       .exchange_item {
         cursor: pointer;
@@ -354,3 +381,55 @@ onUnmounted(() => {
   }
 }
 </style>
+<style lang="scss" scoped>
+.custom-form-layout {
+  .custom-card-checkbox,
+  .custom-checkbox {
+    word-break: keep-all;
+    display: inline-flex;
+    align-items: center;
+    margin-bottom: 16px;
+    padding-right: 20px;
+    .label {
+      word-break: keep-all;
+      display: flex;
+      padding-right: 15px;
+    }
+    .checkbox-group {
+      display: flex;
+      .checkbox-wp {
+        display: flex;
+        align-items: center;
+        margin-right: 10px;
+        line-height: 38px;
+        :deep(.layui-checkbox-label) {
+          padding: 0;
+        }
+        :deep(.layui-form-radio) {
+          margin-top: 0;
+        }
+        :deep(.layui-checkcard) {
+          padding: 0 10px;
+          width: auto;
+          margin: 0 10px 0 0;
+          .layui-checkcard-content {
+            padding: 0;
+          }
+        }
+        .checkbox-input {
+          width: 40px;
+        }
+        &:last-child {
+          margin-right: 0;
+        }
+      }
+    }
+  }
+  .custom-card-checkbox {
+    .checkbox-wp {
+      padding: 0 20px;
+      border: 1px solid #d9d9d9;
+    }
+  }
+}
+</style>