Kaynağa Gözat

今日工作12.10 页面修改 1,首页与闪屏页路由修改 2,请求接口文件修改 3,个人中心svg修改 接口联调 1,理财产品详情 2,用户余额 3,理财下单 4,理财列表(我的理财) 5,ICO,规则 6,获取OTC商家支付方式(还差联调)

jhaoG 3 hafta önce
ebeveyn
işleme
8bae307c8b

BIN
dist.zip


+ 42 - 0
src/api/index.js

@@ -51,6 +51,40 @@ export function GetWealthProducts(params) {
   });
 }
 
+// 获取理财产品单条信息
+export function GetWealthProductsDetails(params) {
+  return request({
+    url: "/wealth/products/",
+    method: "get",
+    params,
+  });
+}
+
+// 理财下单
+export function wealthSubmitOrder(data) {
+  return request({
+    url: "/wealth/products/submit_order/",
+    method: "post",
+    data,
+  });
+}
+
+// 理财列表
+export function GetWealthOrderList() {
+  return request({
+    url: "/wealth/order/get_order_list/",
+    method: "get",
+  });
+}
+
+// 获取余额
+export function GetUsdtBalance() {
+  return request({
+    url: "/custom/custom/get_usdt_balance/",
+    method: "get",
+  });
+}
+
 // ICO
 export function GetIcoIco() {
   return request({
@@ -58,3 +92,11 @@ export function GetIcoIco() {
     method: "get",
   });
 }
+
+// ICO规则
+export function GetIcoDesc() {
+  return request({
+    url: "/ico/ico/get_ico_desc/",
+    method: "get",
+  });
+}

+ 8 - 0
src/api/otc.js

@@ -17,3 +17,11 @@ export function AddBankManager(params) {
     params,
   });
 }
+
+// 获取OTC商家支付方式
+export function GetPayList(id) {
+  return request({
+    url: `/otc/otc/${id}/get_pay_list/`,
+    method: "get",
+  });
+}

+ 3 - 4
src/api/user.js

@@ -1,10 +1,9 @@
 import request from "@/utils/request";
 
 // 邀请推广
-export function getInviteInfo(data) {
+export function getInviteInfo() {
   return request({
-    url: "/custom/custom/get_invite_info",
-    method: "post",
-    data,
+    url: "/custom/custom/get_invite_info/",
+    method: "get",
   });
 }

BIN
src/assets/icon/index/CopySimple.png


BIN
src/assets/img/index/user/email.png


BIN
src/assets/img/index/user/google.png


BIN
src/assets/img/index/user/key.png


BIN
src/assets/img/index/user/phone.png


+ 7 - 3
src/router/index.js

@@ -76,6 +76,11 @@ const routes = [
   {
     path: "/",
     name: "home",
+    component: SplashScreen,
+  },
+  {
+    path: "/index",
+    name: "index",
     component: HomeIndex,
     children: [
       {
@@ -84,7 +89,7 @@ const routes = [
         component: AppIndex,
       },
       {
-        path: "marketIndex",
+        path: "/marketIndex",
         name: "marketIndex",
         component: MarketIndex,
       },
@@ -119,7 +124,6 @@ const routes = [
             component: () => import("@/views/bitcoin/lever/CryptocurrencyTrading.vue"),
             meta: { title: "币币交易" },
           },
-
           {
             path: "margin",
             name: "TradeMargin",
@@ -504,7 +508,7 @@ const routes = [
     component: FinancialIndex,
   },
   {
-    path: "/financialBuy",
+    path: "/financialBuy/:id",
     name: "financialBuy",
     component: FinancialBuy,
   },

+ 1 - 12
src/utils/request.js

@@ -1,6 +1,4 @@
-/* src/utils/request.js */
 import axios from "axios";
-// 如果你用了 Vant UI,可以把下面这行解开,用来弹窗提示错误
 import { showToast, showFailToast } from "vant";
 import "vant/es/toast/style";
 
@@ -16,7 +14,6 @@ const service = axios.create({
 });
 
 // 2. 请求拦截器 (Request Interceptor)
-// 在发送请求之前做些什么,比如加 Token
 service.interceptors.request.use(
   (config) => {
     // 假设你的 token 存在 localStorage 里
@@ -42,14 +39,8 @@ service.interceptors.response.use(
   (response) => {
     const res = response.data;
 
-    // 这里根据后端返回的状态码来判断请求是否成功
     // 假设后端约定 code === 00000 为成功
     if (res.code !== "00000") {
-      // 如果不是 200,说明业务逻辑有错,比如“余额不足”
-
-      // showFailToast(res.msg || 'Error') // 如果用了 Vant,可以用这个提示
-      console.error("业务报错:", res.msg);
-
       // 特殊处理:比如 401 表示 Token 过期,需要跳回登录页
       if (res.code === 401) {
         //以此处逻辑为准:清除本地数据,强制刷新或跳转
@@ -57,16 +48,14 @@ service.interceptors.response.use(
         location.reload();
       }
 
-      return Promise.reject(new Error(res.msg || "Error"));
+      // return Promise.reject(new Error(res.msg || "Error"));
     } else {
       // 成功,直接把数据剥离出来
       return res.data;
     }
   },
   (error) => {
-    console.log("网络报错" + error); // for debug
     let message = error.message;
-
     if (message === "Network Error") {
       message = "后端接口连接异常";
     } else if (message.includes("timeout")) {

+ 273 - 0
src/views/asset/dialog/C2CBuy.vue

@@ -0,0 +1,273 @@
+<template>
+  <div class="sell-and-buy">
+    <div class="apply-mask" @click="emits('sellAndBuyClose')"></div>
+    <div class="apply-content">
+      <div class="slide-line"></div>
+      <div class="apply-text pf600 fs18 fc121212">购买</div>
+      <div class="goods-info">
+        <div class="info-left">
+          <div class="shangjia">
+            <img src="@/assets/img/index/user/default-head.png" alt="" />
+            <div class="left-name pf500 fs14 fc2C3131">商家昵称</div>
+            <div class="vip-flag pf500 fs10 fcDF384C">V2</div>
+          </div>
+          <div class="chengjiao pf400 fs10 fc999999">成交量 12550 · 98.95% 成交率</div>
+        </div>
+        <div class="info-right">
+          <div class="text pf400 fs10 fc999999">单价</div>
+          <div class="price-number pf400 fs10 fc999999">
+            ¥
+            <span class="pf500 fs16 fc333333">6.58</span>
+            /USDT
+          </div>
+        </div>
+      </div>
+      <div class="required-quantity pf500 fs14 fc999999">
+        <div>需求数量</div>
+        <div>150200.00 USDT</div>
+      </div>
+      <div class="required-quantity pf500 fs14 fc999999">
+        <div>单笔限额</div>
+        <div>100.00 - 252020.00 USDT</div>
+      </div>
+      <div class="number-input">
+        <input
+          type="text"
+          class="input pf400 fs14 fc333333"
+          placeholder="请输入购买数量" />
+        <div class="all pf400 fs14 fc333333">USDT <span class="fcDF384C">全部</span></div>
+      </div>
+      <div class="use-number pf400 fs12 fc333333">可买&nbsp; 215.0508 USDT</div>
+      <div class="account pf500 fs14 fc999999">
+        <div>付款方式</div>
+        <div class="account-right">
+          <div class="color"></div>
+          银行卡
+        </div>
+      </div>
+      <div class="account pf500 fs14 fc999999">
+        <div>付款账号</div>
+        <div class="account-right fcDF384C" @click="goAddBankAccount">
+          请选择
+          <img src="@/assets/icon/asset/right-arrow-black.svg" alt="" />
+        </div>
+      </div>
+      <div class="sure-btn pf600 fs14 fcFFFFFF" @click="goAddzfbAccount">确定购买</div>
+    </div>
+  </div>
+</template>
+<script setup>
+  import { useRoute, useRouter } from "vue-router";
+  import { GetPayList } from "@/api/otc";
+  import { watch } from "vue";
+
+  const router = useRouter();
+
+  const props = defineProps(["OTCId"]);
+
+  watch(
+    () => props.OTCId,
+    async (newVal, oldVal) => {
+      const data1 = await GetPayList(newVal);
+      console.log(12, data1);
+    }
+  );
+  const emits = defineEmits(["sellAndBuyClose"]);
+
+  const goAddzfbAccount = () => {
+    router.push("/addzfbAccount");
+  };
+
+  const goAddBankAccount = () => {
+    router.push("/addBankAccount");
+  };
+</script>
+<style lang="less" scoped>
+  .sell-and-buy {
+    position: fixed;
+    left: 0;
+    top: 0;
+    z-index: 1000;
+    display: flex;
+    flex-direction: column;
+    justify-content: flex-end;
+    width: 100%;
+    min-height: 100vh;
+
+    .apply-mask {
+      position: fixed;
+      left: 0;
+      top: 0;
+      z-index: -1;
+      width: 100%;
+      min-height: 100vh;
+      background: rgba(0, 0, 0, 0.5);
+    }
+
+    .apply-content {
+      display: flex;
+      flex-direction: column;
+      justify-content: flex-start;
+      align-items: center;
+      width: 375px;
+      background: #ffffff;
+      border-radius: 24px 24px 0px 0px;
+
+      .slide-line {
+        margin-top: 12px;
+        width: 32px;
+        height: 4px;
+        border-radius: 2px;
+        opacity: 0.4;
+        background: rgba(0, 0, 0, 0.5);
+      }
+
+      .apply-text {
+        margin-top: 20px;
+        height: 24px;
+        line-height: 24px;
+        letter-spacing: 0.2px;
+      }
+
+      .goods-info {
+        display: flex;
+        flex-direction: row;
+        justify-content: space-between;
+        margin-top: 11px;
+        width: 345px;
+
+        .info-left {
+          .shangjia {
+            display: flex;
+            flex-direction: row;
+            justify-content: flex-start;
+            align-items: center;
+            height: 24px;
+
+            img {
+              width: 24px;
+              height: 24px;
+            }
+
+            .left-name {
+              margin-left: 4px;
+            }
+
+            .vip-flag {
+              margin-left: 4px;
+              width: 18px;
+              height: 15px;
+              line-height: 15px;
+              text-align: center;
+              border-radius: 3px;
+              background: #df384c1a;
+            }
+          }
+
+          .chengjiao {
+            margin-top: 3px;
+          }
+        }
+
+        .info-right {
+          display: flex;
+          flex-direction: column;
+          justify-content: flex-start;
+          align-items: flex-end;
+
+          .text {
+            height: 24px;
+            line-height: 24px;
+          }
+
+          .price-number {
+            margin-top: -6px;
+          }
+        }
+      }
+
+      .required-quantity {
+        display: flex;
+        flex-direction: row;
+        justify-content: space-between;
+        align-items: center;
+        margin-top: 10px;
+        width: 345px;
+        height: 18px;
+      }
+
+      .number-input {
+        position: relative;
+        margin-top: 18px;
+        width: 345px;
+        height: 45px;
+
+        .input {
+          padding-left: 11px;
+          width: 100%;
+          height: 100%;
+          box-sizing: border-box;
+          border-radius: 6px;
+          border: none;
+          outline: none;
+          background: #f5f5f5;
+        }
+
+        .all {
+          position: absolute;
+          top: 13px;
+          right: 11px;
+        }
+      }
+
+      .use-number {
+        margin-top: 7px;
+        width: 345px;
+        height: 18px;
+        line-height: 18px;
+      }
+
+      .account {
+        display: flex;
+        flex-direction: row;
+        justify-content: space-between;
+        align-items: center;
+        margin-top: 8px;
+        width: 345px;
+        height: 18px;
+
+        .account-right {
+          display: flex;
+          flex-direction: row;
+          justify-content: flex-start;
+          align-items: center;
+          margin-top: 5px;
+          height: 12px;
+
+          .color {
+            margin-right: 3px;
+            width: 3px;
+            height: 10px;
+            border-radius: 2px;
+            background: #df384c;
+          }
+
+          img {
+            margin-left: 5px;
+          }
+        }
+      }
+
+      .sure-btn {
+        margin-top: 47px;
+        margin-bottom: 40px;
+        width: 311px;
+        height: 40px;
+        line-height: 40px;
+        text-align: center;
+        border-radius: 100px;
+        background: #45b26b;
+      }
+    }
+  }
+</style>

+ 0 - 0
src/views/asset/dialog/SellAndBuy.vue → src/views/asset/dialog/C2CSell.vue


+ 14 - 6
src/views/asset/otc/transaction/C2C.vue

@@ -81,9 +81,7 @@
           </div>
           <div class="func-main">
             <div class="func-chat pf500 fs12 fcDF384C">聊天</div>
-            <div class="func-buy pf500 fs12 fcFFFFFF" @click="sellAndBuyFlag = true">
-              购买
-            </div>
+            <div class="func-buy pf500 fs12 fcFFFFFF" @click="OTCBuy(item.otc)">购买</div>
           </div>
         </div>
       </div>
@@ -91,7 +89,10 @@
     <PaymentWay v-show="paymentWayFlag" @paymentWayClose="paymentWayClose"></PaymentWay>
     <Amount v-show="amountFlag" @amountClose="amountClose"></Amount>
     <Filter v-show="filterFlag" @filterClose="filterClose"></Filter>
-    <SellAndBuy v-show="sellAndBuyFlag" @sellAndBuyClose="sellAndBuyClose"></SellAndBuy>
+    <C2CBuy
+      v-show="sellAndBuyFlag"
+      :OTCId="OTCId"
+      @sellAndBuyClose="sellAndBuyClose"></C2CBuy>
     <!-- 暂无收款方式,还没点击的地方 -->
     <NotPaymentWay
       v-show="notPaymnetWayFlag"
@@ -117,7 +118,8 @@
   import PaymentWay from "../../dialog/NotPaymentWay.vue";
   import Amount from "../../dialog/Amount.vue";
   import Filter from "../../dialog/Filter.vue";
-  import SellAndBuy from "../../dialog/SellAndBuy.vue";
+  import C2CSell from "../../dialog/C2CSell.vue";
+  import C2CBuy from "../../dialog/C2CBuy.vue";
   import NotPaymentWay from "../../dialog/NotPaymentWay.vue";
   import PaymentAccount from "../../dialog/PaymentAccount.vue";
   import CancelOrder from "../../dialog/CancelOrder.vue";
@@ -125,6 +127,13 @@
 
   const router = useRouter();
 
+  const OTCId = ref();
+
+  const OTCBuy = (otcId) => {
+    OTCId.value = otcId;
+    sellAndBuyFlag.value = true;
+  };
+
   const goMerchantDetails = () => {
     router.push("/OTCMerchantDetails");
   };
@@ -145,7 +154,6 @@
     const data = await GetOTCPendingOrder(params);
     if (data) {
       OTCPendingOrderData.value = data.list;
-      console.log(1, data.list);
     }
   };
 

+ 2 - 2
src/views/index/ApplyPermission.vue

@@ -34,7 +34,7 @@
 
       l_setItem("token", data.accessToken);
       l_setItem("address", address);
-      router.push("/");
+      router.push("/index");
     }
   };
 
@@ -50,7 +50,7 @@
 
   onMounted(() => {
     if (l_getItem("address")) {
-      router.push("/");
+      router.push("/index");
     }
   });
 </script>

+ 1 - 1
src/views/index/Index.vue

@@ -17,7 +17,7 @@
     </div>
     <div class="asset-total">
       <div class="asset-title pf400 fs16 fc1F2937">
-        理财总资产(USDT)
+        总资产(USDT)
         <img :src="isHide ? eyeClose : eyeOpen" class="eye-close" @click="toggleEye" />
       </div>
       <div class="asset-number pf600 fs32 fc1F2937" :class="{ 'mask-style': isHide }">

+ 182 - 161
src/views/index/components/HotCoin.vue

@@ -36,185 +36,200 @@
         </div>
       </div>
     </div>
-    <div class="coin-body" >
-     <div class="body-item" v-for="(item, index) in coinList" :key="item.id || index">
-      <div  class="item-left" @click="router.push({ path: '/marketDetails', query: { id: item.id, type: item.symbol.toLowerCase()} })">
-        <div class="coin-img" >
-          <img :src="item.logo" alt="" />
-        </div>
-        <div class="coin-name">
-          <div class="upper-name pf500 fs14 fc2C3131">{{ formatSymbol(item.symbol) }}</div>
-          <div class="letter-name pf400 fs10 fcA9A9A9">{{ item.name }}</div>
+    <div class="coin-body">
+      <div class="body-item" v-for="(item, index) in coinList" :key="item.id || index">
+        <div
+          class="item-left"
+          @click="
+            router.push({
+              path: '/marketDetails',
+              query: { id: item.id, type: item.symbol.toLowerCase() },
+            })
+          ">
+          <div class="coin-img">
+            <img :src="item.logo" alt="" />
+          </div>
+          <div class="coin-name">
+            <div class="upper-name pf500 fs14 fc2C3131">
+              {{ formatSymbol(item.symbol) }}
+            </div>
+            <div class="letter-name pf400 fs10 fcA9A9A9">{{ item.name }}</div>
+          </div>
+          <div class="coin-echars"></div>
+          <div class="coin-price">
+            <div class="upper-price pf500 fs14 fc2C3131">
+              {{ formatPrice(item.current_price) }}
+            </div>
+            <div class="letter-price pf400 fs10 fcA9A9A9">
+              ≈ ${{ formatPrice(item.current_price) }}
+            </div>
+          </div>
         </div>
-        <div class="coin-echars"></div>
-        <div class="coin-price">
-          <div class="upper-price pf500 fs14 fc2C3131">{{ formatPrice(item.current_price) }}</div>
-          <div class="letter-price pf400 fs10 fcA9A9A9">≈ ${{ formatPrice(item.current_price) }}</div>
+        <div class="item-right pf500 fs12 fcFFFFFF" :class="getChangeColor(item.trend)">
+          {{ formatChange(item.trend) }}
         </div>
       </div>
-      <div  class="item-right pf500 fs12 fcFFFFFF" :class="getChangeColor(item.trend)">{{ formatChange(item.trend) }}</div>
-    </div>
     </div>
   </div>
 </template>
 
 <script setup>
-import { GetCoins } from '@/api/index.js'
-import { ref, onMounted, onUnmounted } from 'vue'
-import { useRoute, useRouter } from "vue-router";
-
-const router = useRouter();
-
-const coinList = ref([])
-let socket = null
-let heartbeatTimer = null
-let reconnectTimer = null
-let isUnmounted = false
-
-// --- 辅助函数 ---
-const formatSymbol = (symbol) => symbol ? symbol.replace('USDT', '') : ''
-const formatPrice = (price) => price ? parseFloat(price).toFixed(2) : '0.00'
-
-// 格式化涨跌幅
-const formatChange = (val) => {
-  if (!val) return '+0.00%'
-  const num = parseFloat(val)
-  return (num > 0 ? '+' : '') + num.toFixed(2) + '%'
-}
-
-// 获取颜色
-const getChangeColor = (val) => {
-  if (!val) return 'bg-gray'
-  return parseFloat(val) >= 0 ? 'bg-green' : 'bg-red'
-}
-
-// --- 1. WebSocket 核心逻辑 ---
-
-const initWebSocket = () => {
-  if (socket) {
-    socket.onclose = null
-    socket.close()
-    socket = null
-  }
-
-  if (coinList.value.length === 0) return
+  import { GetCoins } from "@/api/index.js";
+  import { ref, onMounted, onUnmounted } from "vue";
+  import { useRoute, useRouter } from "vue-router";
+
+  const router = useRouter();
+
+  const coinList = ref([]);
+  let socket = null;
+  let heartbeatTimer = null;
+  let reconnectTimer = null;
+  let isUnmounted = false;
+
+  // --- 辅助函数 ---
+  const formatSymbol = (symbol) => (symbol ? symbol.replace("USDT", "") : "");
+  const formatPrice = (price) => (price ? parseFloat(price).toFixed(2) : "0.00");
+
+  // 格式化涨跌幅
+  const formatChange = (val) => {
+    if (!val) return "+0.00%";
+    const num = parseFloat(val);
+    return (num > 0 ? "+" : "") + num.toFixed(2) + "%";
+  };
+
+  // 获取颜色
+  const getChangeColor = (val) => {
+    if (!val) return "bg-gray";
+    return parseFloat(val) >= 0 ? "bg-green" : "bg-red";
+  };
+
+  // --- 1. WebSocket 核心逻辑 ---
+
+  const initWebSocket = () => {
+    if (socket) {
+      socket.onclose = null;
+      socket.close();
+      socket = null;
+    }
 
-  const symbolsParam = coinList.value
-    .map(item => item.symbol.toLowerCase())
-    .join('/')
+    if (coinList.value.length === 0) return;
 
-  const query = `?symbol=${symbolsParam}`
+    const symbolsParam = coinList.value
+      .map((item) => item.symbol.toLowerCase())
+      .join("/");
 
-  const host = process.env.NODE_ENV === 'production'
-    ? 'backend.66linknow.com'
-    : 'localhost:8080'
-  const wsUrl = `ws://${host}/ws/kline/${query}`
+    const query = `?symbol=${symbolsParam}`;
 
-  try {
-    socket = new WebSocket(wsUrl)
-  } catch (err) {
-    reconnect()
-    return
-  }
+    const wsUrl = `wss://test2.66linknow.com/ws/kline/${query}`;
 
-  socket.onopen = () => {
-    startHeartbeat()
-    if (reconnectTimer) clearTimeout(reconnectTimer)
-  }
-
-  socket.onmessage = (event) => {
-    if (event.data === 'pong' || event.data === 'ping') return
     try {
-      const msg = JSON.parse(event.data)
-      if (msg.data) {
-        updateCoinData(msg.data)
-      } else {
-        updateCoinData(msg)
-      }
-    } catch (e) {}
-  }
-
-  socket.onerror = (err) => {
-    console.error('❌ WS 报错')
-  }
-
-  socket.onclose = (e) => {
-    if (e.code === 1000) return
-    socket = null
-    reconnect()
-  }
-}
-
-// --- 2. 更新数据 (核心适配) ---
-const updateCoinData = (ticker) => {
-  // WS 推送格式: { s: "XRPUSDT", c: "2.18", P: "9.01" }
-
-  if (!ticker || !ticker.s) return
-
-  const targetCoin = coinList.value.find(item =>
-    item.symbol.toUpperCase() === ticker.s.toUpperCase()
-  )
-
-  if (targetCoin) {
-    // 【修改点4】: WebSocket 更新时,赋值给新的字段名
-    // c = current price -> 赋值给 current_price
-    if (ticker.c) targetCoin.current_price = ticker.c
+      socket = new WebSocket(wsUrl);
+    } catch (err) {
+      reconnect();
+      return;
+    }
 
-    // P = percentage change -> 赋值给 trend
-    if (ticker.P) {targetCoin.trend = ticker.P; targetCoin.p = ticker.P}
-  }
-}
-
-// --- 3. 自动重连 ---
-const reconnect = () => {
-  if (isUnmounted) return
-  if (reconnectTimer) return
-  reconnectTimer = setTimeout(() => {
-    reconnectTimer = null
-    initWebSocket()
-  }, 3000)
-}
-
-// --- 4. 心跳保活 ---
-const startHeartbeat = () => {
-  if (heartbeatTimer) clearInterval(heartbeatTimer)
-  heartbeatTimer = setInterval(() => {
-    if (socket && socket.readyState === WebSocket.OPEN) {
-      socket.send('ping')
+    socket.onopen = () => {
+      startHeartbeat();
+      if (reconnectTimer) clearTimeout(reconnectTimer);
+    };
+
+    socket.onmessage = (event) => {
+      if (event.data === "pong" || event.data === "ping") return;
+      try {
+        const msg = JSON.parse(event.data);
+        if (msg.data) {
+          updateCoinData(msg.data);
+        } else {
+          updateCoinData(msg);
+        }
+      } catch (e) {}
+    };
+
+    socket.onerror = (err) => {
+      console.error("❌ WS 报错");
+    };
+
+    socket.onclose = (e) => {
+      if (e.code === 1000) return;
+      socket = null;
+      reconnect();
+    };
+  };
+
+  // --- 2. 更新数据 (核心适配) ---
+  const updateCoinData = (ticker) => {
+    // WS 推送格式: { s: "XRPUSDT", c: "2.18", P: "9.01" }
+
+    if (!ticker || !ticker.s) return;
+
+    const targetCoin = coinList.value.find(
+      (item) => item.symbol.toUpperCase() === ticker.s.toUpperCase()
+    );
+
+    if (targetCoin) {
+      // 【修改点4】: WebSocket 更新时,赋值给新的字段名
+      // c = current price -> 赋值给 current_price
+      if (ticker.c) targetCoin.current_price = ticker.c;
+
+      // P = percentage change -> 赋值给 trend
+      if (ticker.P) {
+        targetCoin.trend = ticker.P;
+        targetCoin.p = ticker.P;
+      }
     }
-  }, 10000)
-}
+  };
+
+  // --- 3. 自动重连 ---
+  const reconnect = () => {
+    if (isUnmounted) return;
+    if (reconnectTimer) return;
+    reconnectTimer = setTimeout(() => {
+      reconnectTimer = null;
+      initWebSocket();
+    }, 3000);
+  };
+
+  // --- 4. 心跳保活 ---
+  const startHeartbeat = () => {
+    if (heartbeatTimer) clearInterval(heartbeatTimer);
+    heartbeatTimer = setInterval(() => {
+      if (socket && socket.readyState === WebSocket.OPEN) {
+        socket.send("ping");
+      }
+    }, 10000);
+  };
 
-// --- 生命周期 ---
+  // --- 生命周期 ---
 
-onMounted(async () => {
-  try {
-    const res = await GetCoins()
-    // 注意:确保 res 已经是数组,或者取 res.data
-    coinList.value = Array.isArray(res) ? res : (res.data || [])
+  onMounted(async () => {
+    try {
+      const res = await GetCoins();
+      // 注意:确保 res 已经是数组,或者取 res.data
+      coinList.value = Array.isArray(res) ? res : res.data || [];
 
-    if (coinList.value.length > 0) {
-      initWebSocket()
+      if (coinList.value.length > 0) {
+        initWebSocket();
+      }
+    } catch (error) {
+      console.error("API 失败:", error);
     }
-  } catch (error) {
-    console.error('API 失败:', error)
-  }
-})
-
-onUnmounted(() => {
-  isUnmounted = true
-  if (heartbeatTimer) clearInterval(heartbeatTimer)
-  if (reconnectTimer) clearTimeout(reconnectTimer)
-  if (socket) {
-    socket.onclose = null
-    socket.close()
-    socket = null
-  }
-})
+  });
+
+  onUnmounted(() => {
+    isUnmounted = true;
+    if (heartbeatTimer) clearInterval(heartbeatTimer);
+    if (reconnectTimer) clearTimeout(reconnectTimer);
+    if (socket) {
+      socket.onclose = null;
+      socket.close();
+      socket = null;
+    }
+  });
 </script>
 
 <style lang="less" scoped>
-/* 样式保持不变,省略以节省空间 */
+  /* 样式保持不变,省略以节省空间 */
   .hot-coin {
     margin-top: 20px;
     width: 346px;
@@ -362,8 +377,14 @@ onUnmounted(() => {
     }
   }
 
-/* 颜色配置 */
-.bg-green { background-color: #2EBD85!important; }
-.bg-red { background-color: #F6465D!important; }
-.bg-gray { background-color: #C0C0C0; }
-</style>
+  /* 颜色配置 */
+  .bg-green {
+    background-color: #2ebd85 !important;
+  }
+  .bg-red {
+    background-color: #f6465d !important;
+  }
+  .bg-gray {
+    background-color: #c0c0c0;
+  }
+</style>

+ 10 - 2
src/views/index/components/HotFinancial.vue

@@ -6,7 +6,8 @@
       <div
         class="financial-item"
         v-for="(item, index) in WealthProductsData"
-        :key="index">
+        :key="index"
+        @click="financialBuy(item.id)">
         <div class="item-name pf400 fs12 fc2C3131">
           <img :src="item.icon" alt="" />
           {{ item.title }}
@@ -33,7 +34,7 @@
                 (item.details[0].amount / item.details[0].duration_days) *
                   item.details[0].daily_rate
               ).toFixed(4)
-            }}天
+            }}/
           </div>
         </div>
         <div class="item-buy pf500 fs10 fcFFFFFF">立即购买</div>
@@ -43,8 +44,11 @@
 </template>
 <script setup>
   import { ref, onMounted } from "vue";
+  import { useRoute, useRouter } from "vue-router";
   import { GetWealthProducts } from "@/api/index";
 
+  const router = useRouter();
+
   const WealthProductsData = ref();
 
   const getWealthProductsData = async () => {
@@ -58,6 +62,10 @@
     }
   };
 
+  const financialBuy = (id) => {
+    router.push({ name: "financialBuy", params: { id: id } });
+  };
+
   onMounted(() => {
     getWealthProductsData();
   });

+ 84 - 12
src/views/index/financial/Buy.vue

@@ -3,16 +3,20 @@
   <HeaderNav headerText="理财"></HeaderNav>
   <div class="financial-buy">
     <div class="item-name">
-      <img src="@/assets/icon/coin/bnb.svg" alt="" />
+      <img :src="productDataList.icon" alt="" />
       <div class="coin-name">
-        <div class="first pf500 fs14 fc1F2937">Bitcoin定期</div>
+        <div class="first pf500 fs14 fc1F2937">{{ productDataList.title }}</div>
         <div class="second pf400 fs10 fcA9A9A9">定期</div>
       </div>
     </div>
     <div class="product">
-      <div class="product-item pf500 fcFFFFFF" v-for="(item, index) in 3" :key="index">
-        <div class="rate fs14">4.50%</div>
-        <div class="date fs12">30天</div>
+      <div
+        class="product-item pf500 fcFFFFFF"
+        v-for="(item, index) in productDataList.details"
+        :key="index"
+        @click="getProductDetails(index)">
+        <div class="rate fs14">{{ Number(item.daily_rate * 100).toFixed(2) }}%</div>
+        <div class="date fs12">{{ item.duration_days }}天</div>
       </div>
     </div>
     <div class="number">
@@ -26,24 +30,32 @@
       <div class="all pf400 fs14 fc333333">USDT <span class="fcDF384C">全部</span></div>
     </div>
     <div class="use-number pf400 fs12 fc333333">
-      可用&nbsp; 215.0508 USDT
+      可用&nbsp; {{ usdtBalance }} USDT
       <img src="@/assets/icon/bitcoin/qianbao1.svg" alt="" class="qianbao" />
     </div>
     <div class="message margin-top20 pf500 fs14 fc666666">
       <div>理财周期</div>
-      <div>30天</div>
+      <div>{{ productDetails.duration_days }}天</div>
     </div>
     <div class="message margin-top28 pf500 fs14 fc666666">
       <div>预估每日收益</div>
-      <div>0.1215 USDT</div>
+      <div>
+        {{
+          Number(
+            (productDetails.amount / productDetails.duration_days) *
+              productDetails.daily_rate
+          ).toFixed(4)
+        }}
+        USDT
+      </div>
     </div>
     <div class="message margin-top28 pf500 fs14 fc666666">
       <div>预估总收益</div>
-      <div>20 USDT</div>
+      <div>{{ Number(productDetails.amount).toFixed(4) }} USDT</div>
     </div>
     <div class="message margin-top28 pf500 fs14 fc666666">
       <div>收益率</div>
-      <div>4.50%</div>
+      <div>{{ Number(productDetails.daily_rate * 100).toFixed(2) }}%</div>
     </div>
     <div class="profit-statement">
       <div class="title pf500 fs14 fc333333">收益说明</div>
@@ -52,15 +64,75 @@
         到期后本息自动发放到账户余额
       </div>
     </div>
-    <div class="submit pf600 fs14 fcFFFFFF">立即购买</div>
+    <div class="submit pf600 fs14 fcFFFFFF" @click="submitOrder()">立即购买</div>
   </div>
 </template>
 <script setup>
   import HeaderNav from "../components/HeaderNav.vue";
   import CheckBox from "../../../components/ui/CheckBox.vue";
-  import { ref } from "vue";
+  import { ref, onMounted } from "vue";
+  import { useRoute, useRouter } from "vue-router";
+  import {
+    GetWealthProductsDetails,
+    GetUsdtBalance,
+    wealthSubmitOrder,
+  } from "@/api/index";
+  import { showToast, showFailToast } from "vant";
+
+  const route = useRoute();
+  const router = useRouter();
 
   const autoBuyFlag = ref(true);
+  const productDataList = ref([]);
+  const productDetails = ref([]);
+  const usdtBalance = ref(0);
+  const productIndex = ref(0);
+
+  const GetWealthProductsDetailsData = async (id) => {
+    const params = {
+      id: id,
+    };
+    const data = await GetWealthProductsDetails(params);
+    if (data) {
+      productDataList.value = data.list[0];
+    }
+  };
+
+  const GetUsdtBalanceData = async () => {
+    const data = await GetUsdtBalance();
+    console.log(1, data);
+    if (data) {
+      usdtBalance.value = data;
+    }
+  };
+
+  const getProductDetails = (index) => {
+    if (index) {
+      productIndex.value = index;
+      productDetails.value = productDataList.value.details[index];
+    } else {
+      productIndex.value = 0;
+      productDetails.value = productDataList.value.details[0];
+    }
+  };
+
+  const submitOrder = async () => {
+    const params = {
+      detail_id: productDataList.value.details[productIndex.value].id,
+    };
+    const data = await wealthSubmitOrder(params);
+    if (data) {
+      router.push("/financialIndex");
+    } else {
+      showFailToast("余额不足请充币");
+    }
+  };
+
+  onMounted(async () => {
+    await GetWealthProductsDetailsData(route.params.id);
+    await GetUsdtBalanceData();
+    getProductDetails();
+  });
 </script>
 <style lang="less" scoped>
   .financial-buy {

+ 39 - 8
src/views/index/financial/MyFinancial.vue

@@ -2,43 +2,70 @@
   <!-- 我的理财 -->
   <HeaderNav headerText="我的理财"></HeaderNav>
   <div class="my-financial">
-    <div class="coin-item" v-for="(item, index) in 2" :key="index">
+    <div class="coin-item" v-for="(item, index) in WealthOrderList" :key="index">
       <div class="item-name">
         <img src="@/assets/icon/coin/bnb.svg" alt="" />
         <div class="item-info">
           <div class="info-top pf600 fs14 fc1F2937">
-            <div>Bitcoin定期</div>
+            <div>{{ item.wealth_title }}</div>
             <div class="pf400 fs12">申购数量</div>
           </div>
           <div class="info-bottom">
             <div class="pf400 fs10 fcA9A9A9">定期</div>
-            <div class="pf600 fs14 fc1F2937">1,125,158.00</div>
+            <div class="pf600 fs14 fc1F2937">
+              {{ Number(item.amount).toFixed(4) }}
+            </div>
           </div>
         </div>
       </div>
       <div class="item-line"></div>
       <div class="item-use pf500 fs12 margin-top10">
         <div class="fcA8A8A8">理财周期</div>
-        <div class="fcDF384C">15 天</div>
+        <div class="fcDF384C">{{ item.duration_days }} 天</div>
       </div>
       <div class="item-use pf500 fs12 margin-top4">
         <div class="fcA8A8A8">预估每日收益</div>
-        <div class="fc767676">0.45 USDT</div>
+        <div class="fc767676">
+          {{ Number((item.amount / item.duration_days) * item.daily_rate).toFixed(4) }}
+          USDT/天
+        </div>
       </div>
       <div class="item-use pf500 fs12 margin-top4">
         <div class="fcA8A8A8">预估总收益</div>
-        <div class="fc767676">0.45 USDT</div>
+        <!-- amount * 天数 * 日利率 -->
+        <div class="fc767676">
+          {{ Number(item.amount * item.duration_days * item.daily_rate).toFixed(2) }} USDT
+        </div>
       </div>
       <div class="item-use pf500 fs12 margin-top4">
         <div class="fcA8A8A8">收益率</div>
-        <div class="fc767676">0.16%</div>
+        <div class="fc767676">{{ Number(item.daily_rate * 100).toFixed(2) }}%</div>
+      </div>
+      <div
+        class="conduct-btn pf600 fs14 fcFFFFFF"
+        :class="item.status == 1 ? '' : 'conduct-finish'">
+        {{ item.status == 1 ? "进行中" : "已结束" }}
       </div>
-      <div class="conduct-btn pf600 fs14 fcFFFFFF">进行中</div>
     </div>
   </div>
 </template>
 <script setup>
   import HeaderNav from "../components/HeaderNav.vue";
+  import { GetWealthOrderList } from "@/api/index";
+  import { ref, onMounted } from "vue";
+
+  const WealthOrderList = ref();
+
+  const GetWealthOrderListData = async () => {
+    const data = await GetWealthOrderList();
+    if (data) {
+      WealthOrderList.value = data;
+    }
+  };
+
+  onMounted(() => {
+    GetWealthOrderListData();
+  });
 </script>
 <style lang="less" scoped>
   .my-financial {
@@ -120,6 +147,10 @@
         height: 18px;
       }
 
+      .conduct-finish {
+        background: #a8a8a8 !important;
+      }
+
       .conduct-btn {
         margin-top: 8px;
         width: 311px;

+ 19 - 13
src/views/index/ico/Relus.vue

@@ -1,25 +1,31 @@
 <template>
   <!-- 规则 -->
   <div class="rules pf400 fs14 fc666666">
-    ICO是区块链项目的众筹方式,通过出售代币(而非传统初创企业的股权)来筹集资金。开发团队通常在以太坊、BNB
-    Chain或Solana等智能合约平台上铸造固定数量的加密资产(多为实用型或治理型代币)。<br />
-    早期投资者可用ETH、BNB、USDC等主流加密货币以专属优惠价兑换新资产。这些资金将用于项目开发,而购入的代币可能随项目的成功而增值。<br />
-    ICO通常发生在项目未完成阶段,支持者可以押注未来的应用场景——如去中心化存储访问权、交易费折扣、治理投票权、质押收益等。自Filecoin和Bancor等项目瞬间募集数百万美元后,该模式已成为行业标准。<br />
-    尽管监管日益严格(尤其在美国,多数代币可能被认定为未注册证券),ICO在创新友好司法管辖区仍具热度。其核心特征包括:<br />
-    · 全球触达(任何互联网用户均可参与) <br />
-    · 通过现有智能合约网络实现快速结算<br />
-    · 资金流向透明可查(记录于公开区块链)<br />
-    但低准入门槛也伴随着骗局、过度承诺路线图及代码审计不足等风险。成功的ICO需要平衡大胆愿景与可验证进展,提供详尽的代币经济学模型及明确的法律披露。<br />
-    ICO如何运作? <br />
-    简而言之,ICO遵循以下流程:团队发布白皮书→部署智能合约销售合约→收取加密货币并兑换新代币。<br />
-    然而表层流程背后,涉及法律审查、持续营销、社区讨论与流动性规划等复杂的环节——这些要素最终决定了项目成败。<br />
+    {{ icoDescData }}
   </div>
 </template>
-<script setup></script>
+<script setup>
+  import { ref, onMounted } from "vue";
+  import { GetIcoDesc } from "@/api/index";
+
+  const icoDescData = ref();
+
+  const GetIcoDescData = async () => {
+    const data = await GetIcoDesc();
+    if (data) {
+      icoDescData.value = data;
+    }
+  };
+
+  onMounted(async () => {
+    await GetIcoDescData();
+  });
+</script>
 <style lang="less" scoped>
   .rules {
     margin-top: 10px;
     width: 349px;
     line-height: 24px;
+    letter-spacing: 0.2px;
   }
 </style>

+ 4 - 4
src/views/index/user/SafetySet.vue

@@ -14,7 +14,7 @@
       <div class="key">
         <div class="key-img">
           <div class="img-left">
-            <img src="../../../assets/img/index/user/key.svg" alt="" />
+            <img src="../../../assets/img/index/user/key.png" alt="" />
           </div>
           <div class="img-right">
             <img src="../../../assets/icon/index/right-arrow-gray.svg" alt="" />
@@ -26,7 +26,7 @@
       <div class="google-verify">
         <div class="key-img">
           <div class="img-left">
-            <img src="../../../assets/img/index/user/google.svg" alt="" />
+            <img src="../../../assets/img/index/user/google.png" alt="" />
           </div>
           <div class="img-right">
             <img src="../../../assets/icon/index/right-arrow-gray.svg" alt="" />
@@ -40,7 +40,7 @@
       <div class="key">
         <div class="key-img">
           <div class="img-left">
-            <img src="../../../assets/img/index/user/key.svg" alt="" />
+            <img src="../../../assets/img/index/user/phone.png" alt="" />
           </div>
           <div class="img-right">
             <img src="../../../assets/icon/index/right-arrow-gray.svg" alt="" />
@@ -52,7 +52,7 @@
       <div class="google-verify">
         <div class="key-img">
           <div class="img-left">
-            <img src="../../../assets/img/index/user/google.svg" alt="" />
+            <img src="../../../assets/img/index/user/email.png" alt="" />
           </div>
           <div class="img-right">
             <img src="../../../assets/icon/index/right-arrow-gray.svg" alt="" />

+ 1 - 1
src/views/index/user/UserInfo.vue

@@ -6,7 +6,7 @@
       <div class="uid-right pf400 fs14 fcA8A8A8">
         115615080805020850
         <div class="copy">
-          <img src="../../../assets/icon/index/CopySimple.svg" alt="" />
+          <img src="../../../assets/icon/index/CopySimple.png" alt="" />
         </div>
       </div>
     </div>

+ 1 - 1
src/views/market/details/MarketConditions.vue

@@ -262,7 +262,7 @@
   // --- 数据/WS ---
   const kLineData = ref([]);
   const socket = ref(null);
-  const WS_BASE_URL = "ws://backend.66linknow.com/ws/kline/";
+  const WS_BASE_URL = "wss://test2.66linknow.com/ws/kline/";
   const HEARTBEAT_INTERVAL = 15000;
   const RECONNECT_DELAY = 3000;
   let heartbeatTimer = null,

+ 2 - 2
vue.config.js

@@ -13,12 +13,12 @@ module.exports = defineConfig({
         // 如果是线上测试服,可能是 http://47.100.xx.xx
         //'http://63.141.230.43:57676',
         // 'http://backend.66linknow.com'
-        target: "http://63.141.230.43:57676", // ✅ 必须加上协议
+        target: "https://test2.66linknow.com", // ✅ 必须加上协议
         changeOrigin: true, // 允许跨域
       },
       // 2.【新增】WebSocket 代理配置
       "/ws/kline": {
-        target: "ws://backend.66linknow.com", // 后端 IP
+        target: "wss://test2.66linknow.com", // 后端 IP
         changeOrigin: true,
         ws: true, // ⚠️ 开启 WebSocket 支持
         // 这里是否需要 pathRewrite 取决于后端路径有没有 /ws