4 次代碼提交 d2671e48fd ... 8e8724faab

作者 SHA1 備註 提交日期
  jhaoG 8e8724faab 今日工作12.9 接口联调 1,邀请中心及邀请好友分享页面 2,首页-热门理财 3,ICO,认购,配售 4,C2C,商家列表 页面修改 1,邀请中心 2,交易-币币 3 周之前
  jhaoG 560c4a1e9f 登录接口联调及问题修改 3 周之前
  jhaoG 07e330ec2d Merge branch 'main' into jiahao 3 周之前
  jhaoG cb7d721dcb 1,提的问题修改 功能开发 1,钱包断开连接(使用disconnect方法尝试断开连接,发现disconnect方法 不起作用,查看官方文档后发现已经弃用,选择accountsChanged方法解决) 2,钱包连接,获取钱包地址及整体逻辑开发 3 周之前

+ 19 - 11
src/App.vue

@@ -1,19 +1,27 @@
 <template>
-  <router-view/>
+  <router-view />
 </template>
 
 <script setup>
-/*import { useI18n } from 'vue-i18n';
-const { locale } = useI18n();*/
-// 必要时加上去
+  import { l_removeItem } from "@/utils/storage";
+  // 必要时加上去
+  // import { useI18n } from 'vue-i18n';
+  // const { locale } = useI18n();
+
+  // 监控钱包是否断开连接
+  ethereum.on("accountsChanged", (accounts) => {
+    if (accounts.length === 0) {
+      l_removeItem("address");
+    }
+  });
 </script>
 
 <style>
-* {
-  -webkit-overflow-scrolling: touch;
-}
+  * {
+    -webkit-overflow-scrolling: touch;
+  }
 
-* {
-  -webkit-tap-highlight-color: transparent;
-}
-</style>
+  * {
+    -webkit-tap-highlight-color: transparent;
+  }
+</style>

+ 38 - 22
src/api/index.js

@@ -1,44 +1,60 @@
-import request from '@/utils/request'
+import request from "@/utils/request";
 
 // 登录接口
 export function login(data) {
   return request({
-    url: '/user/login',
-    method: 'post',
-    data
-  })
+    url: "/custom/login/",
+    method: "post",
+    data,
+  });
 }
 
 // 获取币币
 export function GetCoins() {
   return request({
-    url: '/finance/trading_pair/get_display_coin/',
-    method: 'get'
-  })
+    url: "/finance/trading_pair/get_display_coin/",
+    method: "get",
+  });
 }
 
 //获取K线
 export function GetCandlestickChart(id) {
   return request({
     url: `/finance/trading_pair/${id?.symbol}/get_kline/`,
-    method: 'get',
+    method: "get",
     params: {
-        interval: id.period,
-        limit: 150,
-        end_time: id.to || undefined,
-    }
-  })
+      interval: id.period,
+      limit: 150,
+      end_time: id.to || undefined,
+    },
+  });
 }
 
-
 //交易对
 export function TradingPair(id) {
-    return request({
+  return request({
     url: `/finance/trading_pair/`,
-    method: 'get',
+    method: "get",
     params: {
-        pageSize: id.pageSize,
-        pageNum: id.pageNum,
-    }
-  })
-}
+      pageSize: id.pageSize,
+      pageNum: id.pageNum,
+    },
+  });
+}
+
+// 获取理财产品
+export function GetWealthProducts(params) {
+  return request({
+    url: "/wealth/products/",
+    method: "get",
+    params,
+  });
+}
+
+// ICO
+export function GetIcoIco() {
+  return request({
+    url: "/ico/ico/",
+    method: "get",
+  });
+}

+ 19 - 0
src/api/otc.js

@@ -0,0 +1,19 @@
+import request from "@/utils/request";
+
+// 挂单列表
+export function GetOTCPendingOrder(params) {
+  return request({
+    url: "/otc/otc_pending_order/",
+    method: "get",
+    params,
+  });
+}
+
+// 添加银行卡
+export function AddBankManager(params) {
+  return request({
+    url: "/custom/bank_manager/",
+    method: "post",
+    params,
+  });
+}

+ 10 - 0
src/api/user.js

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

二進制
src/assets/icon/user/copy.png


二進制
src/assets/icon/user/gift.png


二進制
src/assets/img/index/Bank_transfer_2_.png


+ 1 - 1
src/router/index.js

@@ -224,7 +224,7 @@ const routes = [
     component: () => import("@/views/user/InviteCenter.vue"), // 上面的组件
   },
   {
-    path: "/invite/poster",
+    path: "/invite/poster/:code",
     name: "InvitePoster",
     component: () => import("@/views/user/InvitePoster.vue"),
   },

+ 43 - 43
src/utils/request.js

@@ -1,83 +1,83 @@
 /* src/utils/request.js */
-import axios from 'axios'
+import axios from "axios";
 // 如果你用了 Vant UI,可以把下面这行解开,用来弹窗提示错误
-import { showToast, showFailToast } from 'vant';
-import 'vant/es/toast/style';
+import { showToast, showFailToast } from "vant";
+import "vant/es/toast/style";
 
 // 1. 创建 axios 实例
 const service = axios.create({
   // 基础 URL,通常在 .env 文件中配置 VUE_APP_BASE_API
-  baseURL: process.env.VUE_APP_BASE_API || '/api', 
+  baseURL: process.env.VUE_APP_BASE_API || "/api",
   // 请求超时时间 (毫秒),交易类应用建议设置短一点,比如 10秒
-  timeout: 10000, 
+  timeout: 10000,
   headers: {
-    'Content-Type': 'application/json;charset=utf-8'
-  }
-})
+    "Content-Type": "application/json;charset=utf-8",
+  },
+});
 
 // 2. 请求拦截器 (Request Interceptor)
 // 在发送请求之前做些什么,比如加 Token
 service.interceptors.request.use(
-  config => {
+  (config) => {
     // 假设你的 token 存在 localStorage 里
-    const token = localStorage.getItem('token')
-    
+    const token = localStorage.getItem("token");
+
     if (token) {
       // 让每个请求携带自定义 token 请根据实际情况修改
-      // 例如:config.headers['Authorization'] = 'Bearer ' + token
-      config.headers['token'] = token 
+      config.headers["Authorization"] = "JWT " + token;
+      // config.headers["token"] = token;
     }
-    return config
+    return config;
   },
-  error => {
+  (error) => {
     // 对请求错误做些什么
-    console.log(error) 
-    return Promise.reject(error)
+    console.log(error);
+    return Promise.reject(error);
   }
-)
+);
 
 // 3. 响应拦截器 (Response Interceptor)
 // 对响应数据做点什么
 service.interceptors.response.use(
-  response => {
-    const res = response.data
-    
+  (response) => {
+    const res = response.data;
+
     // 这里根据后端返回的状态码来判断请求是否成功
     // 假设后端约定 code === 00000 为成功
     if (res.code !== "00000") {
       // 如果不是 200,说明业务逻辑有错,比如“余额不足”
-      
+
       // showFailToast(res.msg || 'Error') // 如果用了 Vant,可以用这个提示
-      console.error('业务报错:', res.msg)
+      console.error("业务报错:", res.msg);
 
       // 特殊处理:比如 401 表示 Token 过期,需要跳回登录页
       if (res.code === 401) {
         //以此处逻辑为准:清除本地数据,强制刷新或跳转
-        localStorage.removeItem('token')
-        location.reload()
+        localStorage.removeItem("token");
+        location.reload();
       }
-      
-      return Promise.reject(new Error(res.msg || 'Error'))
+
+      return Promise.reject(new Error(res.msg || "Error"));
     } else {
       // 成功,直接把数据剥离出来
-      return res.data
+      return res.data;
     }
   },
-  error => {
-    console.log('网络报错' + error) // for debug
-    let message = error.message
-    
-    if (message === 'Network Error') {
-      message = '后端接口连接异常'
-    } else if (message.includes('timeout')) {
-      message = '系统接口请求超时'
-    } else if (message.includes('Request failed with status code')) {
-      message = '系统接口' + message.substr(message.length - 3) + '异常'
+  (error) => {
+    console.log("网络报错" + error); // for debug
+    let message = error.message;
+
+    if (message === "Network Error") {
+      message = "后端接口连接异常";
+    } else if (message.includes("timeout")) {
+      message = "系统接口请求超时";
+    } else if (message.includes("Request failed with status code")) {
+      message = "系统接口" + message.substr(message.length - 3) + "异常";
     }
-    
-    showFailToast(message)
-    return Promise.reject(error)
+
+    showFailToast(message);
+    return Promise.reject(error);
   }
-)
+);
 
-export default service
+export default service;

+ 4 - 4
src/views/asset/Index.vue

@@ -28,21 +28,21 @@
     <div class="asset-money">
       <div class="money-item" @click="goRecharge">
         <div class="item-img">
-          <img src="../../assets/img/index/Rectangle 8.svg" />
+          <img src="@/assets/icon/usercenter/chongbi.png" />
         </div>
         <div class="pf400 fs14 fc666666">充币</div>
         <img src="../../assets/icon/asset/divider.svg" alt="" class="divider-img" />
       </div>
       <div class="money-item" @click="goWithdraw">
         <div class="item-img">
-          <img src="../../assets/img/index/Rectangle 9.svg" />
+          <img src="@/assets/icon/usercenter/tibi.png" />
         </div>
         <div class="pf400 fs14 fc666666">提币</div>
         <img src="../../assets/icon/asset/divider.svg" alt="" class="divider-img" />
       </div>
       <div class="money-item" @click="goTransfer">
         <div class="item-img">
-          <img src="../../assets/img/index/user/huazhuan.svg" />
+          <img src="@/assets/icon/usercenter/huazhuan.png" />
         </div>
         <div class="pf400 fs14 fc666666">划转</div>
       </div>
@@ -74,7 +74,7 @@
         <div class="details-item" v-for="(item, index) in 2" :key="index">
           <div class="item-name">
             <div class="name-left">
-              <img src="../../assets/icon/coin/bnb.svg" alt="" class="left-coin" />
+              <img src="../../assets/icon/coin/Ethereum.png" alt="" class="left-coin" />
               <div class="name-area">
                 <div class="pf500 fs16 fc121212">BTC/USDT 永续</div>
                 <div class="name-flag">

+ 1 - 1
src/views/asset/otc/Index.vue

@@ -63,7 +63,7 @@
     .footer-tabbar {
       position: fixed;
       left: 15px;
-      bottom: 37px;
+      bottom: 10px;
       z-index: 99;
       display: flex;
       justify-content: space-around;

+ 0 - 1
src/views/asset/otc/order/All.vue

@@ -180,7 +180,6 @@
       flex-direction: row;
       justify-content: space-between;
       margin-top: 10px;
-      width: 345px;
       height: 38px;
 
       .kefu {

+ 41 - 4
src/views/asset/otc/transaction/AddBankAccount.vue

@@ -4,25 +4,41 @@
     <div class="account-item">
       <div class="item-title pf500 fs14 fc333333">持卡人姓名</div>
       <div class="left-input">
-        <input type="text" class="input pf400 fs14 fc333333" placeholder="请输入" />
+        <input
+          type="text"
+          class="input pf400 fs14 fc333333"
+          placeholder="请输入"
+          v-model="name" />
       </div>
     </div>
     <div class="account-item">
       <div class="item-title pf500 fs14 fc333333">手机号</div>
       <div class="left-input">
-        <input type="text" class="input pf400 fs14 fc333333" placeholder="请输入" />
+        <input
+          type="text"
+          class="input pf400 fs14 fc333333"
+          placeholder="请输入"
+          v-model="phone" />
       </div>
     </div>
     <div class="account-item">
       <div class="item-title pf500 fs14 fc333333">开户行</div>
       <div class="left-input">
-        <input type="text" class="input pf400 fs14 fc333333" placeholder="请输入" />
+        <input
+          type="text"
+          class="input pf400 fs14 fc333333"
+          placeholder="请输入"
+          v-model="bankOfDeposit" />
       </div>
     </div>
     <div class="account-item">
       <div class="item-title pf500 fs14 fc333333">银行卡号</div>
       <div class="left-input">
-        <input type="text" class="input pf400 fs14 fc333333" placeholder="请输入" />
+        <input
+          type="text"
+          class="input pf400 fs14 fc333333"
+          placeholder="请输入"
+          v-model="bankNumber" />
       </div>
     </div>
     <div class="submit pf600 fs14 fcFFFFFF" @click="submit">提交</div>
@@ -30,6 +46,27 @@
 </template>
 <script setup>
   import HeaderNav from "../../../index/components/HeaderNav.vue";
+  import { AddBankManager } from "@/api/otc";
+  import { ref } from "vue";
+
+  const name = ref();
+  const phone = ref();
+  const bankOfDeposit = ref();
+  const bankNumber = ref();
+
+  const submit = async () => {
+    const params = {
+      name: name.value,
+      phone: phone.value,
+      open_bank: bankOfDeposit.value,
+      card_number: bankNumber.value,
+    };
+    const data = await AddBankManager(params);
+    if (data) {
+      // 差一个跳转到购买的页面
+      console.log(data);
+    }
+  };
 </script>
 <style lang="less" scoped>
   .add-zfb-account {

+ 49 - 11
src/views/asset/otc/transaction/C2C.vue

@@ -31,42 +31,53 @@
     </div>
   </div>
   <div class="goods-area">
-    <div class="goods-item" v-for="(item, index) in 2" :key="index">
+    <div class="goods-item" v-for="(item, index) in OTCPendingOrderData" :key="index">
       <div class="item-merchant">
         <div class="merchant-left" @click="goMerchantDetails">
           <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 class="left-name pf500 fs14 fc2C3131">{{ item.otc_name }}</div>
+          <div class="vip-flag pf500 fs10 fcDF384C">{{ item.otc_level }}</div>
         </div>
         <div class="merchant-right pf400 fs10 fc999999">
-          入驻时间: 2025-11-07 12:25:10
+          入驻时间: {{ item.otc_create_time }}
         </div>
       </div>
       <div class="item-chengjiao-number pf400 fs10 fc999999">
-        成交量 12550 · 98.95% 成交率
+        成交量 {{ item.order_info.total }} · {{ item.order_info.rate }} 成交率
       </div>
       <div class="item-price">
         <div class="price-area">
           <div class="text pf400 fs10 fc999999">单价</div>
           <div class="price-number pf400 fs10 fc999999">
-            <span class="pf500 fs16 fc333333">6.58</span>
+            <span class="pf500 fs16 fc333333">{{ Number(item.price).toFixed(2) }}</span>
             /USDT
           </div>
           <div class="number pf400 fs10 fc999999">
-            数量 <span class="pf400 fs12 fc666666">1000.05 USDT</span>
+            数量
+            <span class="pf400 fs12 fc666666"
+              >{{ Number(item.left_count).toFixed(2) }} USDT</span
+            >
           </div>
           <div class="number pf400 fs10 fc999999">
-            限额 <span class="pf400 fs12 fc666666">20000 - 1000.05 CNY</span>
+            限额
+            <span class="pf400 fs12 fc666666">
+              {{ Number(item.min_limit).toFixed(2) }} -
+              {{ Number(item.max_limit).toFixed(2) }} CNY</span
+            >
           </div>
         </div>
         <div class="price-func">
           <div class="func-time pf400 fs10 fc999999">
-            <img src="@/assets/icon/asset/clock-gray.svg" alt="" />0m 45s
+            <img src="@/assets/icon/asset/clock-gray.svg" alt="" />
+            {{ item.order_info.avg_time }}
           </div>
           <div class="func-pay-way pf400 fs10 fc999999">
             <div class="color"></div>
-            银行卡
+            <!-- 1.银行卡 2.支付宝 3.微信 -->
+            <div v-if="item.pay_type == 1">银行卡</div>
+            <div v-if="item.pay_type == 2">支付宝</div>
+            <div v-if="item.pay_type == 3">微信</div>
           </div>
           <div class="func-main">
             <div class="func-chat pf500 fs12 fcDF384C">聊天</div>
@@ -100,8 +111,9 @@
   </div>
 </template>
 <script setup>
-  import { ref } from "vue";
+  import { ref, onMounted } from "vue";
   import { useRoute, useRouter } from "vue-router";
+  import { GetOTCPendingOrder } from "@/api/otc";
   import PaymentWay from "../../dialog/NotPaymentWay.vue";
   import Amount from "../../dialog/Amount.vue";
   import Filter from "../../dialog/Filter.vue";
@@ -117,6 +129,30 @@
     router.push("/OTCMerchantDetails");
   };
 
+  const OTCPendingOrderData = ref();
+  const GetOTCPendingOrderData = async () => {
+    const params = {
+      order_type: "1",
+      trans_type: "1",
+      coin_type: "",
+      price: "",
+      count: "",
+      min_limit: "",
+      min_amount: "",
+      pay_type: "",
+      left_count: "",
+    };
+    const data = await GetOTCPendingOrder(params);
+    if (data) {
+      OTCPendingOrderData.value = data.list;
+      console.log(1, data.list);
+    }
+  };
+
+  onMounted(() => {
+    GetOTCPendingOrderData();
+  });
+
   const completePaymentFlag = ref(false);
   const completePaymentClose = () => {
     completePaymentFlag.value = false;
@@ -303,6 +339,8 @@
   }
 
   .goods-area {
+    margin-top: 10px;
+    margin-bottom: 30px;
     width: 345px;
 
     .goods-item {

+ 520 - 288
src/views/bitcoin/Calculator.vue

@@ -3,7 +3,7 @@
     <div class="nav-bar">
       <div class="nav-left" @click="$router.back()">
         <div>
-          <VanIcon size="18" name="arrow-left"/>
+          <VanIcon size="18" name="arrow-left" />
         </div>
       </div>
       <div class="nav-title">计算器</div>
@@ -24,8 +24,7 @@
           :key="index"
           class="tab-item"
           :class="{ active: currentTab === index }"
-          @click="currentTab = index"
-        >
+          @click="currentTab = index">
           {{ tab }}
           <div class="active-bar" v-if="currentTab === index"></div>
         </div>
@@ -41,22 +40,21 @@
       <button
         class="dir-btn long"
         :class="{ active: direction === 'long' }"
-        @click="direction = 'long'"
-      >
-        买入(做多)
+        @click="direction = 'long'">
+        买入
       </button>
       <button
         class="dir-btn short"
         :class="{ active: direction === 'short' }"
-        @click="direction = 'short'"
-      >
-        卖出(做空)
+        @click="direction = 'short'">
+        卖出
       </button>
     </div>
 
     <div v-if="currentTab === 5" class="funding-rate-display">
       <div class="rate-value">0.0030%</div>
-      <div class="rate-label" @click="hyu">当前资金费率
+      <div class="rate-label" @click="hyu">
+        当前资金费率
         <VanIcon name="question-o" />
       </div>
     </div>
@@ -71,16 +69,10 @@
       </div>
 
       <div class="slider-area">
-        <LeverageSlider
-          v-model="leverage"
-          :marks="leverageMarks"
-          :color="themeColor"
-        />
+        <LeverageSlider v-model="leverage" :marks="leverageMarks" :color="themeColor" />
       </div>
 
-      <div class="leverage-tip">
-        当前杠杆倍数最高可持有头寸 1,800,000,000 USDT
-      </div>
+      <div class="leverage-tip">当前杠杆倍数最高可持有头寸 1,800,000,000 USDT</div>
     </div>
 
     <div class="form-group">
@@ -165,8 +157,11 @@
           <div class="input-container half">
             <input type="number" v-model="item.amount" placeholder="0.00" />
           </div>
-          <div style="display: flex; align-items: center;">
-            <div class="remove-btn" @click="removeRow(idx)" v-if="avgPriceList.length > 1">
+          <div style="display: flex; align-items: center">
+            <div
+              class="remove-btn"
+              @click="removeRow(idx)"
+              v-if="avgPriceList.length > 1">
               <span>-</span>
             </div>
           </div>
@@ -197,9 +192,15 @@
       <p class="sub-text">实际市场存在变动,计算价格仅供参考</p>
 
       <div v-if="currentTab === 0">
-        <div class="res-row"><span>保证金</span><span>{{ result.margin }} USDT</span></div>
-        <div class="res-row"><span>收益</span><span :class="resultClass">{{ result.pnl }} USDT</span></div>
-        <div class="res-row"><span>收益率</span><span :class="resultClass">{{ result.roe }}%</span></div>
+        <div class="res-row">
+          <span>保证金</span><span>{{ result.margin }} USDT</span>
+        </div>
+        <div class="res-row">
+          <span>收益</span><span :class="resultClass">{{ result.pnl }} USDT</span>
+        </div>
+        <div class="res-row">
+          <span>收益率</span><span :class="resultClass">{{ result.roe }}%</span>
+        </div>
       </div>
 
       <div v-if="currentTab === 1">
@@ -227,280 +228,511 @@
 
     <div class="footer-btns">
       <button class="btn-reset" @click="reset">重置</button>
-      <button class="btn-calc" :style="{ background: themeColor }" @click="calculate">计算</button>
+      <button class="btn-calc" :style="{ background: themeColor }" @click="calculate">
+        计算
+      </button>
     </div>
     <div>
-      <ProfitAndLossPrompt
-          v-model:visible="showModal"
-      ></ProfitAndLossPrompt>
+      <ProfitAndLossPrompt v-model:visible="showModal"></ProfitAndLossPrompt>
     </div>
   </div>
 </template>
 
 <script setup>
-import { Icon as VanIcon } from 'vant';
-import {ref, computed, defineAsyncComponent} from 'vue';
-// 请确保路径正确,指向刚才那个更新过的子组件
-const LeverageSlider = defineAsyncComponent(()=>import('./calculator/LeverageSlider.vue'));
-const ProfitAndLossPrompt = defineAsyncComponent(() => import('./calculator/FundingRateReminder.vue'));
-
-//资金费率提示
-const showModal = ref(false);
-const hyu = () => {
-  console.log("kkkkkkk")
-  showModal.value = true;
-}
-
-// 状态定义
-const tabs = ['收益', '目标价格', '强平价格', '可开', '开仓均价', '资金费用'];
-const currentTab = ref(0);
-const direction = ref('long');
-const leverage = ref(10); // 默认从 10x 开始
-
-// ★ 固定阶梯数组 (10, 50, 100, 500, 1000)
-const leverageMarks = [10, 50, 100, 500, 1000];
-
-const form = ref({
-  entryPrice: '',
-  closePrice: '',
-  amount: '',
-  margin: '',
-  balance: '',
-  pnl: ''
-});
-
-const avgPriceList = ref([{ price: '', amount: '' }, { price: '', amount: '' }]);
-const result = ref({ margin: '--', pnl: '--', roe: '--' });
-
-// 计算属性
-const themeColor = computed(() => direction.value === 'long' ? '#2ebd85' : '#f6465d');
-const resultClass = computed(() => {
-  if (parseFloat(result.value.pnl) > 0) return 'text-green';
-  if (parseFloat(result.value.pnl) < 0) return 'text-red';
-  return '';
-});
-
-// ★ 核心修改:步进式调整杠杆 (点击加减号时)
-const stepLeverage = (dir) => {
-  // 1. 找到当前值在数组中的索引
-  let currentIndex = leverageMarks.indexOf(leverage.value);
-
-  // 如果当前值不在数组里(比如手动改成了20),就找一个最近的索引
-  if (currentIndex === -1) {
-    // 简单逻辑:默认归位到 0
-    currentIndex = 0;
-  }
-
-  // 2. 计算下一个索引
-  let nextIndex = currentIndex + dir;
-
-  // 3. 边界限制
-  if (nextIndex < 0) nextIndex = 0;
-  if (nextIndex >= leverageMarks.length) nextIndex = leverageMarks.length - 1;
-
-  // 4. 更新值
-  leverage.value = leverageMarks[nextIndex];
-};
-
-// 简单计算逻辑 (Tab 0 收益)
-const calculate = () => {
-  if (currentTab.value === 0) {
-    const entry = parseFloat(form.value.entryPrice);
-    const close = parseFloat(form.value.closePrice);
-    const amt = parseFloat(form.value.amount);
-    const lev = leverage.value;
-
-    if (entry && close && amt && lev) {
-      const margin = (entry * amt) / lev;
-      let pnl = 0;
-      if (direction.value === 'long') {
-        pnl = (close - entry) * amt;
-      } else {
-        pnl = (entry - close) * amt;
-      }
-      const roe = (pnl / margin) * 100;
-
-      result.value = {
-        margin: margin.toFixed(2),
-        pnl: pnl.toFixed(2),
-        roe: roe.toFixed(2)
-      };
+  import { Icon as VanIcon } from "vant";
+  import { ref, computed, defineAsyncComponent } from "vue";
+  // 请确保路径正确,指向刚才那个更新过的子组件
+  const LeverageSlider = defineAsyncComponent(() =>
+    import("./calculator/LeverageSlider.vue")
+  );
+  const ProfitAndLossPrompt = defineAsyncComponent(() =>
+    import("./calculator/FundingRateReminder.vue")
+  );
+
+  //资金费率提示
+  const showModal = ref(false);
+  const hyu = () => {
+    console.log("kkkkkkk");
+    showModal.value = true;
+  };
+
+  // 状态定义
+  const tabs = ["收益", "目标价格", "强平价格", "可开", "开仓均价", "资金费用"];
+  const currentTab = ref(0);
+  const direction = ref("long");
+  const leverage = ref(10); // 默认从 10x 开始
+
+  // ★ 固定阶梯数组 (10, 50, 100, 500, 1000)
+  const leverageMarks = [10, 50, 100, 500, 1000];
+
+  const form = ref({
+    entryPrice: "",
+    closePrice: "",
+    amount: "",
+    margin: "",
+    balance: "",
+    pnl: "",
+  });
+
+  const avgPriceList = ref([
+    { price: "", amount: "" },
+    { price: "", amount: "" },
+  ]);
+  const result = ref({ margin: "--", pnl: "--", roe: "--" });
+
+  // 计算属性
+  const themeColor = computed(() => (direction.value === "long" ? "#2ebd85" : "#f6465d"));
+  const resultClass = computed(() => {
+    if (parseFloat(result.value.pnl) > 0) return "text-green";
+    if (parseFloat(result.value.pnl) < 0) return "text-red";
+    return "";
+  });
+
+  // ★ 核心修改:步进式调整杠杆 (点击加减号时)
+  const stepLeverage = (dir) => {
+    // 1. 找到当前值在数组中的索引
+    let currentIndex = leverageMarks.indexOf(leverage.value);
+
+    // 如果当前值不在数组里(比如手动改成了20),就找一个最近的索引
+    if (currentIndex === -1) {
+      // 简单逻辑:默认归位到 0
+      currentIndex = 0;
     }
-  }
-};
 
-// 其他辅助函数
-const addRow = () => avgPriceList.value.push({ price: '', amount: '' });
-const removeRow = (idx) => avgPriceList.value.splice(idx, 1);
-const reset = () => {
-  form.value = { entryPrice: '', closePrice: '', amount: '', margin: '', balance: '', pnl: '' };
-  result.value = { margin: '--', pnl: '--', roe: '--' };
-  leverage.value = 10;
-};
+    // 2. 计算下一个索引
+    let nextIndex = currentIndex + dir;
+
+    // 3. 边界限制
+    if (nextIndex < 0) nextIndex = 0;
+    if (nextIndex >= leverageMarks.length) nextIndex = leverageMarks.length - 1;
+
+    // 4. 更新值
+    leverage.value = leverageMarks[nextIndex];
+  };
+
+  // 简单计算逻辑 (Tab 0 收益)
+  const calculate = () => {
+    if (currentTab.value === 0) {
+      const entry = parseFloat(form.value.entryPrice);
+      const close = parseFloat(form.value.closePrice);
+      const amt = parseFloat(form.value.amount);
+      const lev = leverage.value;
+
+      if (entry && close && amt && lev) {
+        const margin = (entry * amt) / lev;
+        let pnl = 0;
+        if (direction.value === "long") {
+          pnl = (close - entry) * amt;
+        } else {
+          pnl = (entry - close) * amt;
+        }
+        const roe = (pnl / margin) * 100;
+
+        result.value = {
+          margin: margin.toFixed(2),
+          pnl: pnl.toFixed(2),
+          roe: roe.toFixed(2),
+        };
+      }
+    }
+  };
+
+  // 其他辅助函数
+  const addRow = () => avgPriceList.value.push({ price: "", amount: "" });
+  const removeRow = (idx) => avgPriceList.value.splice(idx, 1);
+  const reset = () => {
+    form.value = {
+      entryPrice: "",
+      closePrice: "",
+      amount: "",
+      margin: "",
+      balance: "",
+      pnl: "",
+    };
+    result.value = { margin: "--", pnl: "--", roe: "--" };
+    leverage.value = 10;
+  };
 </script>
 
 <style scoped>
-* {
-  box-sizing: border-box;
-}
-
-.calculator-page {
-  font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', sans-serif;
-  background: #fff;
-  min-height: 100vh;
-  /* 这里的 padding-bottom: 90px 很重要,为了防止底部内容被固定的 footer 遮挡,保留它 */
-  padding-bottom: 90px;
-  color: #333;
-  z-index: 200;
-
-  /* 删掉了 position: fixed/top/left */
-
-  display: flex;
-  flex-direction: column;
-  width: 100%;
-}
-/* 导航 & 头部 */
-.nav-bar { display: flex; justify-content: space-between; align-items: center;
-  height: 44px; padding: 0 16px;
-  /* 新增以下 3 行,实现吸顶效果 */
-  position: sticky;
-  top: 0;
-  background: #fff; /* 防止透明背景导致内容重叠 */
-  z-index: 100;
-}
-.nav-left { font-size: 24px; cursor: pointer; }
-.nav-title { font-size: 18px; font-weight: 600; }
-.nav-right { width: 20px; }
-
-.header-info {
-  display: flex;
-  align-items: center;
-  padding: 0px 15px;
-  background-color: #fff;
-  width: 100%;
-}
-
-.icon-wrapper {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  width: 24px;
-  height: 24px;
-  margin-bottom: 1px;
-}
-
-.pair-name {
-  margin: 0;
-  font-size: 18px;
-  font-weight: bold;
-  color: #333;
-  line-height: 1;
-}
-
-/* Tabs */
-.tabs-wrapper { overflow-x: auto; scrollbar-width: none; margin-bottom: 15px; }
-.tabs-content { display: flex; justify-content: space-between; padding: 0 15px;
-  border-bottom: 1px solid #f5f5f5; }
-.tab-item { white-space: nowrap; padding: 12px 0; font-size: 14px; color: #999; position: relative;
-  cursor: pointer; }
-.tab-item.active { color: #111; font-weight: 600; }
-.active-bar { position: absolute; bottom: 0; left: 50%; transform: translateX(-50%); width: 20px;
-  height: 3px; background: #111; border-radius: 2px; }
-
-/* 模式选择 */
-.mode-selectors { display: flex; gap: 10px; padding: 0 16px; margin-bottom: 15px; }
-.select-box { flex: 1; background: #f5f5f5; height: 36px; display: flex; align-items: center;
-  padding: 0 12px; border-radius: 4px; font-size: 14px; color: #333; }
-
-/* 做多做空 */
-.trade-direction { display: flex; gap: 10px; padding: 0 16px; margin-bottom: 20px; }
-.dir-btn { flex: 1; padding: 10px 0; border: none; border-radius: 4px; font-size: 14px;
-  cursor: pointer; background: #f5f5f5; color: #999; transition: all 0.2s; }
-.dir-btn.long.active { background: #2ebd85; color: #fff; font-weight: 600; }
-.dir-btn.short.active { background: #f6465d; color: #fff; font-weight: 600; }
-
-/* 资金费率 */
-.funding-rate-display { text-align: center; padding: 20px 0; }
-.rate-value { font-size: 32px; color: #666; font-weight: 500; }
-.rate-label { font-size: 12px; color: #999; margin-top: 5px; }
-
-/* 杠杆区域 */
-.leverage-section { padding: 0 16px; margin-bottom: 20px; }
-.label { font-size: 12px; color: #666; margin-bottom: 8px; }
-
-/* 蓝色边框输入框 */
-.leverage-control {
-  display: flex;
-  align-items: center;
-  background: #f7f8fa;
-  border-radius: 6px;
-  height: 44px;
-  margin-bottom: 10px;
-  overflow: hidden;
-}
-.ctrl-btn {
-  width: 50px;
-  height: 100%;
-  border: none;
-  background: transparent;
-  font-size: 22px;
-  color: #999;
-  cursor: pointer;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  padding-bottom: 4px;
-}
-.leverage-val {
-  flex: 1;
-  text-align: center;
-  font-size: 16px;
-  font-weight: 500;
-  color: #333;
-}
-.slider-area { padding: 0 10px; }
-.leverage-tip { font-size: 12px; color: #333; margin-top: 10px; }
-
-/* 表单 */
-.form-group { padding: 0 16px; }
-.input-row { margin-bottom: 12px; }
-.input-container { position: relative; display: flex; align-items: center; background: #f5f5f5;
-  height: 44px; border-radius: 4px; padding: 0 12px; }
-.placeholder { position: absolute; color: #bbb; font-size: 14px; pointer-events: none; }
-.input-container input { flex: 1; border: none; background: transparent; outline: none;
-  font-size: 14px; z-index: 1; height: 100%; width: 100%; }
-.suffix { font-size: 14px; color: #666; margin-left: 8px; z-index: 2; white-space: nowrap; }
-.tag-latest { color: #f6465d; margin-left: 4px; }
-
-/* 表格布局 */
-.grid-header { display: flex; font-size: 12px; color: #333; margin-bottom: 8px; }
-.grid-header span { flex: 1; }
-.dynamic-row { display: flex; gap: 10px; margin-bottom: 10px; align-items: center; }
-.input-container.half { flex: 1; }
-.remove-btn { width: 24px; height: 24px; border: 1px solid #f6465d; color: #f6465d;
-  border-radius: 50%; display: flex; align-items: center; justify-content: center;
-  font-size: 18px; cursor: pointer; margin-left: 5px; }
-.remove-btn span { margin-bottom: 5px; }
-.add-position-btn { width: 100%; height: 44px; background: #f6465d; color: white;
-  border: none; border-radius: 22px; margin-top: 10px; font-size: 14px; }
-
-/* 结果 */
-.result-section { padding: 20px 16px; }
-.result-section h3 { margin: 0 0 5px; font-size: 16px; font-weight: 600; }
-.sub-text { font-size: 12px; color: #999; margin-bottom: 15px; }
-.res-row { display: flex; justify-content: space-between; margin-bottom: 12px; font-size: 14px; }
-.res-row span:first-child { color: #666; }
-.res-row span:last-child { color: #333; font-weight: 500; }
-.text-green { color: #2ebd85; }
-.text-red { color: #f6465d; }
-.small-tip { font-size: 12px; color: #999; margin-top: 5px; }
-
-/* 底部按钮 */
-.footer-btns { position: fixed; bottom: 0; left: 0; width: 100%; background: #fff;
-  padding: 10px 16px 20px; display: flex; gap: 15px; box-shadow: 0 -2px 10px rgba(0,0,0,0.05);
-  box-sizing: border-box; z-index: 100; }
-.btn-reset { flex: 1; height: 44px; border: none; background: #bbb; color: #fff;
-  font-size: 16px; border-radius: 4px; }
-.btn-calc { flex: 2; height: 44px; border: none; color: #fff; font-size: 16px;
-  border-radius: 4px; font-weight: 600; transition: background 0.3s; }
-</style>
+  * {
+    box-sizing: border-box;
+  }
+
+  .calculator-page {
+    font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", sans-serif;
+    background: #fff;
+    min-height: 100vh;
+    /* 这里的 padding-bottom: 90px 很重要,为了防止底部内容被固定的 footer 遮挡,保留它 */
+    padding-bottom: 90px;
+    color: #333;
+    z-index: 200;
+
+    /* 删掉了 position: fixed/top/left */
+
+    display: flex;
+    flex-direction: column;
+    width: 100%;
+  }
+  /* 导航 & 头部 */
+  .nav-bar {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    height: 44px;
+    padding: 0 16px;
+    /* 新增以下 3 行,实现吸顶效果 */
+    position: sticky;
+    top: 0;
+    background: #fff; /* 防止透明背景导致内容重叠 */
+    z-index: 100;
+  }
+  .nav-left {
+    font-size: 24px;
+    cursor: pointer;
+  }
+  .nav-title {
+    font-size: 18px;
+    font-weight: 600;
+  }
+  .nav-right {
+    width: 20px;
+  }
+
+  .header-info {
+    display: flex;
+    align-items: center;
+    padding: 0px 15px;
+    background-color: #fff;
+    width: 100%;
+  }
+
+  .icon-wrapper {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 24px;
+    height: 24px;
+    margin-bottom: 1px;
+  }
+
+  .pair-name {
+    margin: 0;
+    font-size: 18px;
+    font-weight: bold;
+    color: #333;
+    line-height: 1;
+  }
+
+  /* Tabs */
+  .tabs-wrapper {
+    overflow-x: auto;
+    scrollbar-width: none;
+    margin-bottom: 15px;
+  }
+  .tabs-content {
+    display: flex;
+    justify-content: space-between;
+    padding: 0 15px;
+    border-bottom: 1px solid #f5f5f5;
+  }
+  .tab-item {
+    white-space: nowrap;
+    padding: 12px 0;
+    font-size: 14px;
+    color: #999;
+    position: relative;
+    cursor: pointer;
+  }
+  .tab-item.active {
+    color: #111;
+    font-weight: 600;
+  }
+  .active-bar {
+    position: absolute;
+    bottom: 0;
+    left: 50%;
+    transform: translateX(-50%);
+    width: 20px;
+    height: 3px;
+    background: #111;
+    border-radius: 2px;
+  }
+
+  /* 模式选择 */
+  .mode-selectors {
+    display: flex;
+    gap: 10px;
+    padding: 0 16px;
+    margin-bottom: 15px;
+  }
+  .select-box {
+    flex: 1;
+    background: #f5f5f5;
+    height: 36px;
+    display: flex;
+    align-items: center;
+    padding: 0 12px;
+    border-radius: 4px;
+    font-size: 14px;
+    color: #333;
+  }
+
+  /* 做多做空 */
+  .trade-direction {
+    display: flex;
+    gap: 10px;
+    padding: 0 16px;
+    margin-bottom: 20px;
+  }
+  .dir-btn {
+    flex: 1;
+    padding: 10px 0;
+    border: none;
+    border-radius: 4px;
+    font-size: 14px;
+    cursor: pointer;
+    background: #f5f5f5;
+    color: #999;
+    transition: all 0.2s;
+  }
+  .dir-btn.long.active {
+    background: #2ebd85;
+    color: #fff;
+    font-weight: 600;
+  }
+  .dir-btn.short.active {
+    background: #f6465d;
+    color: #fff;
+    font-weight: 600;
+  }
+
+  /* 资金费率 */
+  .funding-rate-display {
+    text-align: center;
+    padding: 20px 0;
+  }
+  .rate-value {
+    font-size: 32px;
+    color: #666;
+    font-weight: 500;
+  }
+  .rate-label {
+    font-size: 12px;
+    color: #999;
+    margin-top: 5px;
+  }
+
+  /* 杠杆区域 */
+  .leverage-section {
+    padding: 0 16px;
+    margin-bottom: 20px;
+  }
+  .label {
+    font-size: 12px;
+    color: #666;
+    margin-bottom: 8px;
+  }
+
+  /* 蓝色边框输入框 */
+  .leverage-control {
+    display: flex;
+    align-items: center;
+    background: #f7f8fa;
+    border-radius: 6px;
+    height: 44px;
+    margin-bottom: 10px;
+    overflow: hidden;
+  }
+  .ctrl-btn {
+    width: 50px;
+    height: 100%;
+    border: none;
+    background: transparent;
+    font-size: 22px;
+    color: #999;
+    cursor: pointer;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    padding-bottom: 4px;
+  }
+  .leverage-val {
+    flex: 1;
+    text-align: center;
+    font-size: 16px;
+    font-weight: 500;
+    color: #333;
+  }
+  .slider-area {
+    padding: 0 10px;
+  }
+  .leverage-tip {
+    font-size: 12px;
+    color: #333;
+    margin-top: 10px;
+  }
+
+  /* 表单 */
+  .form-group {
+    padding: 0 16px;
+  }
+  .input-row {
+    margin-bottom: 12px;
+  }
+  .input-container {
+    position: relative;
+    display: flex;
+    align-items: center;
+    background: #f5f5f5;
+    height: 44px;
+    border-radius: 4px;
+    padding: 0 12px;
+  }
+  .placeholder {
+    position: absolute;
+    color: #bbb;
+    font-size: 14px;
+    pointer-events: none;
+  }
+  .input-container input {
+    flex: 1;
+    border: none;
+    background: transparent;
+    outline: none;
+    font-size: 14px;
+    z-index: 1;
+    height: 100%;
+    width: 100%;
+  }
+  .suffix {
+    font-size: 14px;
+    color: #666;
+    margin-left: 8px;
+    z-index: 2;
+    white-space: nowrap;
+  }
+  .tag-latest {
+    color: #f6465d;
+    margin-left: 4px;
+  }
+
+  /* 表格布局 */
+  .grid-header {
+    display: flex;
+    font-size: 12px;
+    color: #333;
+    margin-bottom: 8px;
+  }
+  .grid-header span {
+    flex: 1;
+  }
+  .dynamic-row {
+    display: flex;
+    gap: 10px;
+    margin-bottom: 10px;
+    align-items: center;
+  }
+  .input-container.half {
+    flex: 1;
+  }
+  .remove-btn {
+    width: 24px;
+    height: 24px;
+    border: 1px solid #f6465d;
+    color: #f6465d;
+    border-radius: 50%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-size: 18px;
+    cursor: pointer;
+    margin-left: 5px;
+  }
+  .remove-btn span {
+    margin-bottom: 5px;
+  }
+  .add-position-btn {
+    width: 100%;
+    height: 44px;
+    background: #f6465d;
+    color: white;
+    border: none;
+    border-radius: 22px;
+    margin-top: 10px;
+    font-size: 14px;
+  }
+
+  /* 结果 */
+  .result-section {
+    padding: 20px 16px;
+  }
+  .result-section h3 {
+    margin: 0 0 5px;
+    font-size: 16px;
+    font-weight: 600;
+  }
+  .sub-text {
+    font-size: 12px;
+    color: #999;
+    margin-bottom: 15px;
+  }
+  .res-row {
+    display: flex;
+    justify-content: space-between;
+    margin-bottom: 12px;
+    font-size: 14px;
+  }
+  .res-row span:first-child {
+    color: #666;
+  }
+  .res-row span:last-child {
+    color: #333;
+    font-weight: 500;
+  }
+  .text-green {
+    color: #2ebd85;
+  }
+  .text-red {
+    color: #f6465d;
+  }
+  .small-tip {
+    font-size: 12px;
+    color: #999;
+    margin-top: 5px;
+  }
+
+  /* 底部按钮 */
+  .footer-btns {
+    position: fixed;
+    bottom: 0;
+    left: 0;
+    width: 100%;
+    background: #fff;
+    padding: 10px 16px 20px;
+    display: flex;
+    gap: 15px;
+    box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
+    box-sizing: border-box;
+    z-index: 100;
+  }
+  .btn-reset {
+    flex: 1;
+    height: 44px;
+    border: none;
+    background: #bbb;
+    color: #fff;
+    font-size: 16px;
+    border-radius: 4px;
+  }
+  .btn-calc {
+    flex: 2;
+    height: 44px;
+    border: none;
+    color: #fff;
+    font-size: 16px;
+    border-radius: 4px;
+    font-weight: 600;
+    transition: background 0.3s;
+  }
+</style>

文件差異過大導致無法顯示
+ 0 - 8
src/views/bitcoin/lever/CryptocurrencyTrading.vue


+ 348 - 306
src/views/bitcoin/lever/components/UserAssetsPanel.vue

@@ -5,38 +5,47 @@
         <div
           class="tab-item"
           :class="{ active: activeTab === 'assets' }"
-          @click="activeTab = 'assets'"
-        >
+          @click="activeTab = 'assets'">
           币种资产
         </div>
         <div
           class="tab-item"
           :class="{ active: activeTab === 'positions' }"
-          @click="activeTab = 'positions'"
-        >
-          持有仓位({{ positionsList.length }})
+          @click="activeTab = 'positions'">
+          <!-- 持有仓位({{ positionsList.length }}) -->
+          持有仓位
         </div>
         <div
           class="tab-item"
           :class="{ active: activeTab === 'orders' }"
-          @click="activeTab = 'orders'"
-        >
-          当前委托({{ ordersList.length }})
+          @click="activeTab = 'orders'">
+          <!-- 当前委托({{ ordersList.length }}) -->
+          当前委托
         </div>
       </div>
       <div class="tabs-right" @click="router.push('/historyIndex')">
         <span class="history-icon">
-          <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
-          <path d="M17.025 10.125L15.5254 8.625L14.025 10.125M15.75 9C15.75 12.7279 12.7279 15.75 9 15.75C5.27208 15.75 2.25 12.7279 2.25 9C2.25 5.27208 5.27208 2.25 9 2.25C11.4764 2.25 13.6414 3.5836 14.8159 5.57182M9 5.25V9L11.25 10.5" stroke="#A8A8A8" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+          <svg
+            width="18"
+            height="18"
+            viewBox="0 0 18 18"
+            fill="none"
+            xmlns="http://www.w3.org/2000/svg">
+            <path
+              d="M17.025 10.125L15.5254 8.625L14.025 10.125M15.75 9C15.75 12.7279 12.7279 15.75 9 15.75C5.27208 15.75 2.25 12.7279 2.25 9C2.25 5.27208 5.27208 2.25 9 2.25C11.4764 2.25 13.6414 3.5836 14.8159 5.57182M9 5.25V9L11.25 10.5"
+              stroke="#A8A8A8"
+              stroke-width="1.5"
+              stroke-linecap="round"
+              stroke-linejoin="round" />
           </svg>
-        </span> 全部
+        </span>
+        全部
       </div>
     </div>
 
     <div class="panel-content">
-
       <div v-if="activeTab === 'assets'" class="list-container">
-        <div v-for="(item, index) in assetsList" :key="index" class="card asset-card">
+        <!-- <div v-for="(item, index) in assetsList" :key="index" class="card asset-card">
           <div class="card-header">
             <div class="coin-info">
               <div class="coin-icon icon-btc">₿</div>
@@ -59,11 +68,11 @@
             <span class="label">冻结</span>
             <span class="value red-text">{{ item.frozen }}</span>
           </div>
-        </div>
+        </div> -->
       </div>
 
       <div v-else-if="activeTab === 'positions'" class="list-container">
-        <div v-for="(item, index) in positionsList" :key="index" class="card position-card">
+        <!-- <div v-for="(item, index) in positionsList" :key="index" class="card position-card">
           <div class="card-header">
             <div class="coin-info">
               <div class="coin-icon icon-btc">₿</div>
@@ -98,11 +107,11 @@
             </div>
           </div>
           <button class="btn-block-red">平仓</button>
-        </div>
+        </div> -->
       </div>
 
       <div v-else-if="activeTab === 'orders'" class="list-container">
-        <div v-for="(item, index) in ordersList" :key="index" class="card order-card">
+        <!-- <div v-for="(item, index) in ordersList" :key="index" class="card order-card">
           <div class="card-header">
             <div class="coin-info">
               <div class="coin-icon icon-btc">₿</div>
@@ -134,319 +143,352 @@
               <div class="val">{{ item.price }} {{ item.unit }}</div>
             </div>
           </div>
-        </div>
+        </div> -->
       </div>
-
     </div>
   </div>
 </template>
 
 <script setup>
-import { ref } from 'vue';
-import { useRouter } from 'vue-router'; // 【新增】 引入路由
-
-const router = useRouter(); // 【新增】 实例化路由
-
-// 当前选中的 Tab:'assets', 'positions', 'orders'
-const activeTab = ref('orders');
-
-// 模拟数据 - 委托
-const ordersList = ref([
-  {
-    pair: 'BTC/USDT',
-    type: 'buy',
-    leverage: 20,
-    amount: '0.215',
-    unit: 'USDT',
-    price: '0.215',
-    time: '2025-11-04, 16:30'
-  },
-  {
-    pair: 'BTC/USDT',
-    type: 'sell',
-    leverage: 20,
-    amount: '0.215',
-    unit: 'USDT',
-    price: '0.215',
-    time: '2025-11-04, 16:30'
-  }
-]);
-
-// 模拟数据 - 仓位
-const positionsList = ref([
-  {
-    pair: 'BTC/USDT',
-    type: 'buy',
-    leverage: 200,
-    amount: '0.215',
-    unit: 'USDT',
-    price: '0.215',
-    time: '2025-11-04, 16:30'
-  },
-  {
-    pair: 'ETH/USDT',
-    type: 'sell',
-    leverage: 50,
-    amount: '1.5',
-    unit: 'USDT',
-    price: '1800.00',
-    time: '2025-11-04, 14:20'
-  }
-]);
-
-// 模拟数据 - 资产
-const assetsList = ref([
-  {
-    symbol: 'BTC',
-    nameCn: '比特币',
-    totalValue: '1,125,158.00',
-    cnyValue: '2150508',
-    available: '18802.0850',
-    frozen: '151.2050'
-  },
-  {
-    symbol: 'BTC', // 重复模拟图3效果
-    nameCn: '比特币',
-    totalValue: '1,125,158.00',
-    cnyValue: '2150508',
-    available: '18802.0850',
-    frozen: '151.2050'
-  }
-]);
+  import { ref } from "vue";
+  import { useRouter } from "vue-router"; // 【新增】 引入路由
+
+  const router = useRouter(); // 【新增】 实例化路由
+
+  // 当前选中的 Tab:'assets', 'positions', 'orders'
+  const activeTab = ref("orders");
+
+  // 模拟数据 - 委托
+  const ordersList = ref([
+    {
+      pair: "BTC/USDT",
+      type: "buy",
+      leverage: 20,
+      amount: "0.215",
+      unit: "USDT",
+      price: "0.215",
+      time: "2025-11-04, 16:30",
+    },
+    {
+      pair: "BTC/USDT",
+      type: "sell",
+      leverage: 20,
+      amount: "0.215",
+      unit: "USDT",
+      price: "0.215",
+      time: "2025-11-04, 16:30",
+    },
+  ]);
+
+  // 模拟数据 - 仓位
+  const positionsList = ref([
+    {
+      pair: "BTC/USDT",
+      type: "buy",
+      leverage: 200,
+      amount: "0.215",
+      unit: "USDT",
+      price: "0.215",
+      time: "2025-11-04, 16:30",
+    },
+    {
+      pair: "ETH/USDT",
+      type: "sell",
+      leverage: 50,
+      amount: "1.5",
+      unit: "USDT",
+      price: "1800.00",
+      time: "2025-11-04, 14:20",
+    },
+  ]);
+
+  // 模拟数据 - 资产
+  const assetsList = ref([
+    {
+      symbol: "BTC",
+      nameCn: "比特币",
+      totalValue: "1,125,158.00",
+      cnyValue: "2150508",
+      available: "18802.0850",
+      frozen: "151.2050",
+    },
+    {
+      symbol: "BTC", // 重复模拟图3效果
+      nameCn: "比特币",
+      totalValue: "1,125,158.00",
+      cnyValue: "2150508",
+      available: "18802.0850",
+      frozen: "151.2050",
+    },
+  ]);
 </script>
 
 <style scoped lang="scss">
-/* 基础变量 */
-$c-bg: #fff;
-$c-white: #ffffff;
-$c-text-main: #333333;
-$c-text-sub: #969799;
-$c-green: #00b86b; /* 买入绿 */
-$c-red: #dc3545;   /* 卖出/撤单红 */
-$c-line: #f5f5f5;
-$c-orange: #f7931a; /* BTC颜色 */
-
-.panel-container {
-  width: 100%;
-  background-color: $c-bg;
-  font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Segoe UI, Arial, Roboto, "PingFang SC", "miui", "Hiragino Sans GB", "Microsoft Yahei", sans-serif;
-  padding-bottom: 20px;
-}
-
-/* Tab 头部样式 */
-.tabs-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  background: $c-white;
-  padding: 12px 16px;
-  position: sticky;
-  top: 0;
-  z-index: 10;
-}
-
-.tabs-left {
-  display: flex;
-  gap: 20px;
-}
-
-.tab-item {
-  font-size: 15px;
-  color: $c-text-sub;
-  font-weight: 400;
-  position: relative;
-  cursor: pointer;
-  padding-bottom: 4px;
-  transition: all 0.2s;
-
-  &.active {
-    color: $c-text-main;
-    font-weight: 600;
-    font-size: 16px;
+  /* 基础变量 */
+  $c-bg: #fff;
+  $c-white: #ffffff;
+  $c-text-main: #333333;
+  $c-text-sub: #969799;
+  $c-green: #00b86b; /* 买入绿 */
+  $c-red: #dc3545; /* 卖出/撤单红 */
+  $c-line: #f5f5f5;
+  $c-orange: #f7931a; /* BTC颜色 */
+
+  .panel-container {
+    width: 100%;
+    background-color: $c-bg;
+    font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Segoe UI,
+      Arial, Roboto, "PingFang SC", "miui", "Hiragino Sans GB", "Microsoft Yahei",
+      sans-serif;
+    padding-bottom: 20px;
+  }
+
+  /* Tab 头部样式 */
+  .tabs-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    background: $c-white;
+    padding: 12px 16px;
+    position: sticky;
+    top: 0;
+    z-index: 10;
+  }
+
+  .tabs-left {
+    display: flex;
+    gap: 20px;
+  }
 
-    &::after {
-      content: '';
-      position: absolute;
-      bottom: -2px;
-      left: 50%;
-      transform: translateX(-50%);
-      width: 20px;
-      height: 3px;
-      background-color: #000;
-      border-radius: 2px;
+  .tab-item {
+    font-size: 15px;
+    color: $c-text-sub;
+    font-weight: 400;
+    position: relative;
+    cursor: pointer;
+    padding-bottom: 4px;
+    transition: all 0.2s;
+
+    &.active {
+      color: $c-text-main;
+      font-weight: 600;
+      font-size: 16px;
+
+      &::after {
+        content: "";
+        position: absolute;
+        bottom: -2px;
+        left: 50%;
+        transform: translateX(-50%);
+        width: 20px;
+        height: 3px;
+        background-color: #000;
+        border-radius: 2px;
+      }
     }
   }
-}
-
-.tabs-right {
-  font-size: 13px;
-  color: $c-text-sub;
-  display: flex;
-  align-items: flex-start;
-  gap: 4px;
-  margin-top: 2px;
-}
-
-/* 列表容器 */
-.list-container {
-  padding: 10px 16px;
-}
-
-/* 卡片通用样式 */
-.card {
-  background: $c-white;
-  border-radius: 8px;
-  padding: 12px 12px;
-  margin-bottom: 12px;
-  box-shadow: 0 2px 8px rgba(0,0,0,0.02);
-  border: 1px solid #ebedf0;
-}
-
-.card-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: flex-start;
-  margin-bottom: 12px;
-}
-
-.coin-info {
-  display: flex;
-  align-items: flex-start;
-}
-
-.coin-icon {
-  width: 36px;
-  height: 36px;
-  border-radius: 50%;
-  background: $c-orange;
-  color: #fff;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  font-size: 20px;
-  font-weight: bold;
-  margin-right: 10px;
-}
-
-.coin-name-block {
-  display: flex;
-  flex-direction: column;
-  gap: 4px;
-}
-
-.symbol {
-  font-size: 16px;
-  font-weight: 600;
-  color: $c-text-main;
-}
-
-/* 标签样式 */
-.tags-row {
-  display: flex;
-  gap: 4px;
-}
-
-.tag {
-  font-size: 10px;
-  padding: 1px 4px;
-  border-radius: 2px;
-  color: #fff;
-  display: inline-block;
-  line-height: 1.4;
-}
-
-.tag-green { background: $c-green; }
-.tag-red { background: $c-red; }
-.tag-grey { background: #c8c9cc; color: #fff; }
-
-/* 分割线 */
-.divider {
-  height: 1px;
-  background: $c-line;
-  margin: 0 0 12px 0;
-}
-
-/* 数据网格 (三列) */
-.data-grid {
-  display: flex;
-  justify-content: space-between;
-  margin-bottom: 8px;
-}
-
-.data-col {
-  text-align: left;
-
-
-  &.center { text-align: center; width: 50%; }
-  &.right { text-align: right; }
-
-  .label {
-    font-size: 12px;
+
+  .tabs-right {
+    font-size: 13px;
     color: $c-text-sub;
-    margin-bottom: 4px;
+    display: flex;
+    align-items: flex-start;
+    gap: 4px;
+    margin-top: 2px;
   }
-  .val {
-    font-size: 14px;
-    color: $c-text-main;
-    font-weight: 500;
+
+  /* 列表容器 */
+  .list-container {
+    padding: 10px 16px;
+  }
+
+  /* 卡片通用样式 */
+  .card {
+    background: $c-white;
+    border-radius: 8px;
+    padding: 12px 12px;
+    margin-bottom: 12px;
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.02);
+    border: 1px solid #ebedf0;
   }
-}
-
-/* 按钮样式 */
-.btn-small-red {
-  background: $c-red;
-  color: #fff;
-  border: none;
-  padding: 6px 16px;
-  border-radius: 4px;
-  font-size: 13px;
-}
-
-.btn-block-red {
-  width: 100%;
-  background: $c-red;
-  color: #fff;
-  border: none;
-  padding: 10px 0;
-  border-radius: 20px; /* 圆角按钮 */
-  font-size: 15px;
-  margin-top: 8px;
-  font-weight: 500;
-}
-
-/* 特殊样式:Position 页面的时间 */
-.header-right-time {
-  text-align: right;
-  font-size: 12px;
-  color: $c-text-sub;
-  line-height: 1.4;
-}
-
-/* 特殊样式:Asset 页面 */
-.asset-card {
+
   .card-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: flex-start;
+    margin-bottom: 12px;
+  }
+
+  .coin-info {
+    display: flex;
+    align-items: flex-start;
+  }
+
+  .coin-icon {
+    width: 36px;
+    height: 36px;
+    border-radius: 50%;
+    background: $c-orange;
+    color: #fff;
+    display: flex;
     align-items: center;
+    justify-content: center;
+    font-size: 20px;
+    font-weight: bold;
+    margin-right: 10px;
   }
-  .coin-name {
+
+  .coin-name-block {
     display: flex;
     flex-direction: column;
-    .name-cn { font-size: 12px; color: $c-text-sub; }
+    gap: 4px;
   }
-  .asset-total {
-    text-align: right;
-    .main-val { font-size: 18px; font-weight: bold; color: $c-text-main; }
-    .sub-val { font-size: 12px; color: $c-text-sub; margin-top: 2px; }
+
+  .symbol {
+    font-size: 16px;
+    font-weight: 600;
+    color: $c-text-main;
+  }
+
+  /* 标签样式 */
+  .tags-row {
+    display: flex;
+    gap: 4px;
+  }
+
+  .tag {
+    font-size: 10px;
+    padding: 1px 4px;
+    border-radius: 2px;
+    color: #fff;
+    display: inline-block;
+    line-height: 1.4;
   }
-  .card-row {
+
+  .tag-green {
+    background: $c-green;
+  }
+  .tag-red {
+    background: $c-red;
+  }
+  .tag-grey {
+    background: #c8c9cc;
+    color: #fff;
+  }
+
+  /* 分割线 */
+  .divider {
+    height: 1px;
+    background: $c-line;
+    margin: 0 0 12px 0;
+  }
+
+  /* 数据网格 (三列) */
+  .data-grid {
     display: flex;
     justify-content: space-between;
+    margin-bottom: 8px;
+  }
+
+  .data-col {
+    text-align: left;
+
+    &.center {
+      text-align: center;
+      width: 50%;
+    }
+    &.right {
+      text-align: right;
+    }
+
+    .label {
+      font-size: 12px;
+      color: $c-text-sub;
+      margin-bottom: 4px;
+    }
+    .val {
+      font-size: 14px;
+      color: $c-text-main;
+      font-weight: 500;
+    }
+  }
+
+  /* 按钮样式 */
+  .btn-small-red {
+    background: $c-red;
+    color: #fff;
+    border: none;
+    padding: 6px 16px;
+    border-radius: 4px;
     font-size: 13px;
-    margin-bottom: 6px;
-    &:last-child { margin-bottom: 0; }
+  }
+
+  .btn-block-red {
+    width: 100%;
+    background: $c-red;
+    color: #fff;
+    border: none;
+    padding: 10px 0;
+    border-radius: 20px; /* 圆角按钮 */
+    font-size: 15px;
+    margin-top: 8px;
+    font-weight: 500;
+  }
+
+  /* 特殊样式:Position 页面的时间 */
+  .header-right-time {
+    text-align: right;
+    font-size: 12px;
+    color: $c-text-sub;
+    line-height: 1.4;
+  }
 
-    .label { color: $c-text-sub; }
-    .value { color: $c-text-sub; font-family: sans-serif; }
-    .red-text { color: $c-red; font-weight: 500; }
+  /* 特殊样式:Asset 页面 */
+  .asset-card {
+    .card-header {
+      align-items: center;
+    }
+    .coin-name {
+      display: flex;
+      flex-direction: column;
+      .name-cn {
+        font-size: 12px;
+        color: $c-text-sub;
+      }
+    }
+    .asset-total {
+      text-align: right;
+      .main-val {
+        font-size: 18px;
+        font-weight: bold;
+        color: $c-text-main;
+      }
+      .sub-val {
+        font-size: 12px;
+        color: $c-text-sub;
+        margin-top: 2px;
+      }
+    }
+    .card-row {
+      display: flex;
+      justify-content: space-between;
+      font-size: 13px;
+      margin-bottom: 6px;
+      &:last-child {
+        margin-bottom: 0;
+      }
+
+      .label {
+        color: $c-text-sub;
+      }
+      .value {
+        color: $c-text-sub;
+        font-family: sans-serif;
+      }
+      .red-text {
+        color: $c-red;
+        font-weight: 500;
+      }
+    }
   }
-}
-</style>
+</style>

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

@@ -10,15 +10,49 @@
         <div>你确认将钱包地址公开给此网站吗?</div>
       </div>
       <div class="sure-btn pf600 fs14 fcFFFFFF" @click="sure">确认</div>
-      <div class="cancel-btn pf400 fs14 fcDF384C">取消</div>
+      <div class="cancel-btn pf400 fs14 fcDF384C" @click="cancel">取消</div>
     </div>
   </div>
 </template>
 <script setup>
+  import { l_setItem, l_getItem } from "@/utils/storage";
+  import { login } from "@/api/index";
+  import { useRouter } from "vue-router";
+  import { onMounted } from "vue";
+
+  const router = useRouter();
+
   const sure = async () => {
     const accounts = await ethereum.request({ method: "eth_requestAccounts" });
-    console.log(1, accounts);
+    const address = accounts[0];
+
+    if (address) {
+      const params = {
+        address: address,
+      };
+      const data = await login(params);
+
+      l_setItem("token", data.accessToken);
+      l_setItem("address", address);
+      router.push("/");
+    }
   };
+
+  const cancel = () => {
+    // 强制退出当前 dApp WebView
+    window.location.replace("about:blank");
+
+    // 备用关闭(部分钱包生效)
+    setTimeout(() => {
+      window.close();
+    }, 80);
+  };
+
+  onMounted(() => {
+    if (l_getItem("address")) {
+      router.push("/");
+    }
+  });
 </script>
 <style lang="less" scoped>
   .apply-permission {

+ 40 - 6
src/views/index/components/HotFinancial.vue

@@ -3,31 +3,65 @@
   <div class="hot-financial">
     <div class="financial-title pf600 fs18 fc121212">热门理财</div>
     <div class="financial-main">
-      <div class="financial-item" v-for="(item, index) in 2" :key="index">
+      <div
+        class="financial-item"
+        v-for="(item, index) in WealthProductsData"
+        :key="index">
         <div class="item-name pf400 fs12 fc2C3131">
-          <img src="@/assets/icon/coin/bnb.svg" alt="" />理财产品名称
+          <img :src="item.icon" alt="" />
+          {{ item.title }}
         </div>
         <div class="item-info">
           <div class="info-left">
-            <div class="pf400 fs10 fcDF384C">0.23%</div>
+            <div class="pf400 fs10 fcDF384C">
+              {{ Number(item.details[0].daily_rate * 100).toFixed(2) }}%
+            </div>
             <div class="margin-top2 pf400 fs10 fcA8A8A8">收益率</div>
           </div>
           <div class="info-right">
-            <div class="pf400 fs10 fcDF384C">10天</div>
+            <div class="pf400 fs10 fcDF384C">{{ item.details[0].duration_days }}</div>
             <div class="margin-top2 pf400 fs10 fcA8A8A8">理财周期</div>
           </div>
         </div>
         <div class="item-line"></div>
         <div class="item-shouyi pf400 fs10 fcA8A8A8">
           <div>预估每日收益</div>
-          <div>0.2150天</div>
+          <div>
+            <!-- 总数 / 天数 * 日利率 -->
+            {{
+              Number(
+                (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>
       </div>
     </div>
   </div>
 </template>
-<script setup></script>
+<script setup>
+  import { ref, onMounted } from "vue";
+  import { GetWealthProducts } from "@/api/index";
+
+  const WealthProductsData = ref();
+
+  const getWealthProductsData = async () => {
+    // 是否首页
+    const params = {
+      is_visible: true,
+    };
+    const data = await GetWealthProducts(params);
+    if (data) {
+      WealthProductsData.value = data.list;
+    }
+  };
+
+  onMounted(() => {
+    getWealthProductsData();
+  });
+</script>
 <style lang="less" scoped>
   .hot-financial {
     margin-top: 30px;

+ 27 - 10
src/views/index/ico/Placement.vue

@@ -8,12 +8,11 @@
         <div class="head-operate">操作</div>
       </div>
       <div class="subscription-body">
-        <div
-          class="subscription-item"
-          v-for="(item, index) in subscriptionData"
-          :key="index">
-          <div class="item-name pf400 fs14 fc2C3131">MFEIANS</div>
-          <div class="item-price pf400 fs14 fc2C3131">13.3156</div>
+        <div class="subscription-item" v-for="(item, index) in icoData" :key="index">
+          <div class="item-name pf400 fs14 fc2C3131">{{ item.asset_name }}</div>
+          <div class="item-price pf400 fs14 fc2C3131">
+            {{ Number(item.issue_price).toFixed(2) }}
+          </div>
           <div class="item-operate">
             <div class="text pf500 fs12 fcFFFFFF">配售</div>
           </div>
@@ -29,9 +28,27 @@
   </div>
 </template>
 <script setup>
-  import { ref } from "vue";
+  import { ref, onMounted } from "vue";
+  import { GetIcoIco } from "@/api/index";
+
+  const icoData = ref();
+  const subscriptionData = ref();
+
+  const GetIcoIcoData = async () => {
+    // 1,认购 2,配售
+    const params = {
+      asset_type: "2",
+    };
+    const data = await GetIcoIco(params);
+    if (data) {
+      icoData.value = data.list;
+      subscriptionData.value = data.total;
+    }
+  };
 
-  const subscriptionData = ref(6);
+  onMounted(() => {
+    GetIcoIcoData();
+  });
 </script>
 <style lang="less" scoped>
   .placement {
@@ -80,8 +97,8 @@
           display: flex;
           flex-direction: row;
           justify-content: flex-start;
-          margin-top: 13px;
-          padding-bottom: 13px;
+          margin-top: 8px;
+          padding-bottom: 8px;
           width: 100%;
           border-bottom: 1px solid #f5f5f5;
 

+ 28 - 11
src/views/index/ico/Subscription.vue

@@ -8,12 +8,11 @@
         <div class="head-operate">操作</div>
       </div>
       <div class="subscription-body">
-        <div
-          class="subscription-item"
-          v-for="(item, index) in subscriptionData"
-          :key="index">
-          <div class="item-name pf400 fs14 fc2C3131">Bitcoin</div>
-          <div class="item-price pf400 fs14 fc2C3131">15.3156</div>
+        <div class="subscription-item" v-for="(item, index) in icoData" :key="index">
+          <div class="item-name pf400 fs14 fc2C3131">{{ item.asset_name }}</div>
+          <div class="item-price pf400 fs14 fc2C3131">
+            {{ Number(item.issue_price).toFixed(2) }}
+          </div>
           <div class="item-operate">
             <div class="text pf500 fs12 fcFFFFFF">认购</div>
           </div>
@@ -29,16 +28,34 @@
   </div>
 </template>
 <script setup>
-  import { ref } from "vue";
+  import { ref, onMounted } from "vue";
+  import { GetIcoIco } from "@/api/index";
+
+  const icoData = ref();
+  const subscriptionData = ref();
+
+  const GetIcoIcoData = async () => {
+    // 1,认购 2,配售
+    const params = {
+      asset_type: "1",
+    };
+    const data = await GetIcoIco(params);
+    if (data) {
+      icoData.value = data.list;
+      subscriptionData.value = data.total;
+    }
+  };
 
-  const subscriptionData = ref(6);
+  onMounted(() => {
+    GetIcoIcoData();
+  });
 </script>
 <style lang="less" scoped>
   .subscription {
     width: 349px;
 
     .subscription-content {
-      margin-top: 10px;
+      margin-top: 15px;
       width: 100%;
 
       .subscription-head {
@@ -80,8 +97,8 @@
           display: flex;
           flex-direction: row;
           justify-content: flex-start;
-          margin-top: 13px;
-          padding-bottom: 13px;
+          margin-top: 8px;
+          padding-bottom: 8px;
           width: 100%;
           border-bottom: 1px solid #f5f5f5;
 

+ 565 - 252
src/views/market/details/MarketConditions.vue

@@ -4,7 +4,9 @@
     <div class="market-price">
       <div class="price-left">
         <div class="left-price pf400 fs14 fc333333">实时价格</div>
-        <div class="left-number pf600 fs20 fc1F2937" :class="getPriceColor(marketInfo.change)">
+        <div
+          class="left-number pf600 fs20 fc1F2937"
+          :class="getPriceColor(marketInfo.change)">
           {{ formatNumber(marketInfo.price) }}
         </div>
         <div class="left-appro pf500 fs14 fcA8A8A8">
@@ -32,7 +34,9 @@
           </div>
           <div class="right-number-top-number">
             <div class="pf400 fs10 fcA8A8A8">24h 成交额</div>
-            <div class="pf400 fs10 fc2C3131">{{ abbreviateNumber(marketInfo.amount) }}</div>
+            <div class="pf400 fs10 fc2C3131">
+              {{ abbreviateNumber(marketInfo.amount) }}
+            </div>
           </div>
         </div>
       </div>
@@ -49,13 +53,20 @@
         {{ getTabLabel(tab) }}
       </div>
 
-      <div class="tab-item icon-btn" @click.stop="toggleMore" :style="isMoreActive ? { backgroundColor: '#F6465D', color: '#fff' } : {}">
-        <span :class="{ 'active-text': isMoreActive }">{{ isMoreActive ? getTabLabel(currentTab) : '更多' }}</span>
+      <div
+        class="tab-item icon-btn"
+        @click.stop="toggleMore"
+        :style="isMoreActive ? { backgroundColor: '#F6465D', color: '#fff' } : {}">
+        <span :class="{ 'active-text': isMoreActive }">{{
+          isMoreActive ? getTabLabel(currentTab) : "更多"
+        }}</span>
         <span class="triangle" :style="isMoreActive ? { color: '#fff' } : {}">◢</span>
         <div class="dropdown-menu" v-show="showMoreMenu">
           <div
-            v-for="mt in moreTabs" :key="mt"
-            class="drop-item" :class="{ active: currentTab === mt }"
+            v-for="mt in moreTabs"
+            :key="mt"
+            class="drop-item"
+            :class="{ active: currentTab === mt }"
             @click.stop="switchPeriod(mt)">
             {{ getTabLabel(mt) }}
           </div>
@@ -72,19 +83,36 @@
       <div class="panel-section">
         <div class="section-title">主图指标</div>
         <div class="btn-group">
-          <div v-for="m in ['MA', 'EMA', 'BOLL', 'SAR']" :key="m"
-               class="idx-btn" :class="{ active: mainIdx === m }"
-               @click="changeMain(m)">{{ m }}</div>
-          <div class="idx-btn" :class="{ active: mainIdx === 'Hide' }" @click="changeMain('Hide')">隐藏</div>
+          <div
+            v-for="m in ['MA', 'EMA', 'BOLL', 'SAR']"
+            :key="m"
+            class="idx-btn"
+            :class="{ active: mainIdx === m }"
+            @click="changeMain(m)">
+            {{ m }}
+          </div>
+          <div
+            class="idx-btn"
+            :class="{ active: mainIdx === 'Hide' }"
+            @click="changeMain('Hide')">
+            隐藏
+          </div>
         </div>
       </div>
       <div class="panel-section">
         <div class="section-title">副图指标 (可多选)</div>
         <div class="btn-group">
-          <div v-for="s in ['VOL', 'MACD', 'KDJ', 'RSI', 'WR', 'CCI', 'OBV']" :key="s"
-               class="idx-btn" :class="{ active: subIdx.includes(s) }"
-               @click="toggleSub(s)">{{ s }}</div>
-          <div class="idx-btn" :class="{ active: subIdx.length === 0 }" @click="clearSub">隐藏</div>
+          <div
+            v-for="s in ['VOL', 'MACD', 'KDJ', 'RSI', 'WR', 'CCI', 'OBV']"
+            :key="s"
+            class="idx-btn"
+            :class="{ active: subIdx.includes(s) }"
+            @click="toggleSub(s)">
+            {{ s }}
+          </div>
+          <div class="idx-btn" :class="{ active: subIdx.length === 0 }" @click="clearSub">
+            隐藏
+          </div>
         </div>
       </div>
     </div>
@@ -101,263 +129,548 @@
 
     <!-- 5. 底部原有的 委托挂单/最新成交 -->
     <div class="notifi-classifi">
-      <div class="pf600 fs14" :class="current === 'entrustingOrder' ? 'fc121212' : 'fcA8A8A8'" @click="messageChange('entrustingOrder')">委托挂单</div>
-      <div class="sys-notifi pf600 fs14" :class="current === 'latestTransactions' ? 'fc121212' : 'fcA8A8A8'" @click="messageChange('latestTransactions')">最新成交</div>
+      <div
+        class="pf600 fs14"
+        :class="current === 'entrustingOrder' ? 'fc121212' : 'fcA8A8A8'"
+        @click="messageChange('entrustingOrder')">
+        委托挂单
+      </div>
+      <div
+        class="sys-notifi pf600 fs14"
+        :class="current === 'latestTransactions' ? 'fc121212' : 'fcA8A8A8'"
+        @click="messageChange('latestTransactions')">
+        最新成交
+      </div>
     </div>
 
-    <component :is="currentComponent" :symbol-id="symbolId" :latestTransactionData="latestTransactionData" :orderPlacement="orderPlacement" />
+    <component
+      :is="currentComponent"
+      :symbol-id="symbolId"
+      :latestTransactionData="latestTransactionData"
+      :orderPlacement="orderPlacement" />
 
     <div
+      style="
+        box-shadow: 0 -0.05333rem 0.26667rem rgba(109 109 109 / 5%);
+        box-sizing: border-box;
+        margin-top: 20px;
+        width: 100%;
+        z-index: 99;
+        position: sticky;
+        bottom: 0;
+        background-color: #ffffff;
+        padding: 20px;
+      ">
+      <div
+        @click="router.push('/bitcoin/CryptocurrencyTrading')"
         style="
-    box-shadow: 0 -0.05333rem 0.26667rem rgba(109 109 109 / 5%);
-    box-sizing: border-box;margin-top: 20px;
-    width:100%;z-index: 99;position: sticky; bottom: 0;background-color: #FFFFFF;padding: 20px;">
-      <div @click="router.push('/bitcoin/CryptocurrencyTrading')"
-          style="font-size: 17px; margin:0 auto;border-radius:19px;color:#FFFFFF; text-align:center; width:80%; line-height: 38px; font-weight: 500; background-color: #F6465D; ">交易</div>
+          font-size: 17px;
+          margin: 0 auto;
+          border-radius: 19px;
+          color: #ffffff;
+          text-align: center;
+          width: 80%;
+          line-height: 38px;
+          font-weight: 500;
+          background-color: #f6465d;
+        ">
+        交易
+      </div>
     </div>
   </div>
 </template>
 
 <script setup>
-import { ref, computed, onMounted, onBeforeUnmount, watch } from "vue";
-import { useRoute,useRouter } from "vue-router";
-import { GetCandlestickChart } from "@/api/index.js";
-
-const router = useRouter(); // 【新增】 实例化路由
-
-// 引入组件
-import KLineChart from "@/views/bitcoin/lever/components/KLineChart.vue";
-import EntrustingOrder from "./EntrustingOrder.vue";
-import LatestTransactions from "./LatestTransactions.vue";
-
-const route = useRoute();
-const symbolId = computed(() => route.query.id || "6");
-
-// --- 周期 ---
-const currentTab = ref("1d");
-const visibleTabs = ["15m", "1h", "4h", "1d"];
-const moreTabs = ["1m", "5m", "30m", "1w", "1M"];
-const isMoreActive = computed(() => moreTabs.includes(currentTab.value));
-const getTabLabel = (t) => ({ '1m':'1分', '5m':'5分', '15m':'15分', '30m':'30分', '1h':'1小时', '4h':'4小时', '1d':'日线', '1w':'周线', '1M':'月线' }[t] || t);
-
-// --- 指标管理 ---
-const showMoreMenu = ref(false);
-const showIndicatorMenu = ref(false);
-const klineRef = ref(null);
-const mainIdx = ref('MA');
-const subIdx = ref(['VOL', 'MACD']);
-
-const changeMain = (name) => { mainIdx.value = name; if (klineRef.value) klineRef.value.setMainIndicator(name); };
-const toggleSub = (name) => {
-  const index = subIdx.value.indexOf(name);
-  if (index > -1) subIdx.value.splice(index, 1);
-  else {
-    if (subIdx.value.length >= 3) subIdx.value.shift();
-    subIdx.value.push(name);
-  }
-  if (klineRef.value) klineRef.value.setSubIndicators(subIdx.value);
-};
-const clearSub = () => { subIdx.value = []; if (klineRef.value) klineRef.value.setSubIndicators([]); };
-
-const toggleMore = () => { showIndicatorMenu.value = false; showMoreMenu.value = !showMoreMenu.value; };
-const toggleIndicators = () => { showMoreMenu.value = false; showIndicatorMenu.value = !showIndicatorMenu.value; };
-const closePopups = () => { showMoreMenu.value = false; showIndicatorMenu.value = false; };
-
-const switchPeriod = (period) => {
-  if (currentTab.value === period) return;
-  currentTab.value = period;
-  showMoreMenu.value = false;
-  kLineData.value = [];
-  getKlineData();
-};
-
-// --- 数据/WS ---
-const kLineData = ref([]);
-const socket = ref(null);
-const WS_BASE_URL = "ws://backend.66linknow.com/ws/kline/";
-const HEARTBEAT_INTERVAL = 15000;
-const RECONNECT_DELAY = 3000;
-let heartbeatTimer = null, reconnectTimer = null, isUnmounted = false;
-const isLoadingMore = ref(false);
-
-const marketInfo = ref({ price: "0.00", fiatPrice: "0.00", change: 0.0, high: "0.00", low: "0.00", vol: "0", amount: "0" });
-
-const orderPlacement = ref();
-const latestTransactionData = ref();
-const current = ref("entrustingOrder");
-const componentsMap = { entrustingOrder: EntrustingOrder, latestTransactions: LatestTransactions };
-const currentComponent = computed(() => componentsMap[current.value]);
-const messageChange = (key) => current.value = key;
-
-// --- 获取 K线数据 (核心) ---
-const getKlineData = async (endTime = null) => {
-  if (typeof GetCandlestickChart !== "function") return;
-
-  try {
-    const params = {
-      symbol: symbolId.value,
-      period: currentTab.value,
-      limit: 150,
-      to: endTime || undefined
-    };
-
-    const res = await GetCandlestickChart(params);
-
-    let rawList = Array.isArray(res) ? res : (res && res.data ? res.data : []);
-
-    if (rawList.length > 0) {
-      const formattedData = rawList.map((item) => ({
-        timestamp: Number(item[0]),
-        open: parseFloat(item[1]),
-        high: parseFloat(item[2]),
-        low: parseFloat(item[3]),
-        close: parseFloat(item[4]),
-        volume: parseFloat(item[5])
-      }));
-
-      // 保证按时间升序
-      formattedData.sort((a, b) => a.timestamp - b.timestamp);
-
-      if (endTime) {
-        // --- 加载历史模式 ---
-        if (klineRef.value) {
-          const hasMore = formattedData.length >= 150;
-          // 核心点:子组件 KLineChart 内部现在会自动处理 overlap
-          klineRef.value.applyHistoryData(formattedData, hasMore);
+  import { ref, computed, onMounted, onBeforeUnmount, watch } from "vue";
+  import { useRoute, useRouter } from "vue-router";
+  import { GetCandlestickChart } from "@/api/index.js";
+
+  const router = useRouter(); // 【新增】 实例化路由
+
+  // 引入组件
+  import KLineChart from "@/views/bitcoin/lever/components/KLineChart.vue";
+  import EntrustingOrder from "./EntrustingOrder.vue";
+  import LatestTransactions from "./LatestTransactions.vue";
+
+  const route = useRoute();
+  const symbolId = computed(() => route.query.id || "6");
+
+  // --- 周期 ---
+  const currentTab = ref("1d");
+  const visibleTabs = ["15m", "1h", "4h", "1d"];
+  const moreTabs = ["1m", "5m", "30m", "1w", "1M"];
+  const isMoreActive = computed(() => moreTabs.includes(currentTab.value));
+  const getTabLabel = (t) =>
+    ({
+      "1m": "1分",
+      "5m": "5分",
+      "15m": "15分",
+      "30m": "30分",
+      "1h": "1小时",
+      "4h": "4小时",
+      "1d": "日线",
+      "1w": "周线",
+      "1M": "月线",
+    }[t] || t);
+
+  // --- 指标管理 ---
+  const showMoreMenu = ref(false);
+  const showIndicatorMenu = ref(false);
+  const klineRef = ref(null);
+  const mainIdx = ref("MA");
+  const subIdx = ref(["VOL", "MACD"]);
+
+  const changeMain = (name) => {
+    mainIdx.value = name;
+    if (klineRef.value) klineRef.value.setMainIndicator(name);
+  };
+  const toggleSub = (name) => {
+    const index = subIdx.value.indexOf(name);
+    if (index > -1) subIdx.value.splice(index, 1);
+    else {
+      if (subIdx.value.length >= 3) subIdx.value.shift();
+      subIdx.value.push(name);
+    }
+    if (klineRef.value) klineRef.value.setSubIndicators(subIdx.value);
+  };
+  const clearSub = () => {
+    subIdx.value = [];
+    if (klineRef.value) klineRef.value.setSubIndicators([]);
+  };
+
+  const toggleMore = () => {
+    showIndicatorMenu.value = false;
+    showMoreMenu.value = !showMoreMenu.value;
+  };
+  const toggleIndicators = () => {
+    showMoreMenu.value = false;
+    showIndicatorMenu.value = !showIndicatorMenu.value;
+  };
+  const closePopups = () => {
+    showMoreMenu.value = false;
+    showIndicatorMenu.value = false;
+  };
+
+  const switchPeriod = (period) => {
+    if (currentTab.value === period) return;
+    currentTab.value = period;
+    showMoreMenu.value = false;
+    kLineData.value = [];
+    getKlineData();
+  };
+
+  // --- 数据/WS ---
+  const kLineData = ref([]);
+  const socket = ref(null);
+  const WS_BASE_URL = "ws://backend.66linknow.com/ws/kline/";
+  const HEARTBEAT_INTERVAL = 15000;
+  const RECONNECT_DELAY = 3000;
+  let heartbeatTimer = null,
+    reconnectTimer = null,
+    isUnmounted = false;
+  const isLoadingMore = ref(false);
+
+  const marketInfo = ref({
+    price: "0.00",
+    fiatPrice: "0.00",
+    change: 0.0,
+    high: "0.00",
+    low: "0.00",
+    vol: "0",
+    amount: "0",
+  });
+
+  const orderPlacement = ref();
+  const latestTransactionData = ref();
+  const current = ref("entrustingOrder");
+  const componentsMap = {
+    entrustingOrder: EntrustingOrder,
+    latestTransactions: LatestTransactions,
+  };
+  const currentComponent = computed(() => componentsMap[current.value]);
+  const messageChange = (key) => (current.value = key);
+
+  // --- 获取 K线数据 (核心) ---
+  const getKlineData = async (endTime = null) => {
+    if (typeof GetCandlestickChart !== "function") return;
+
+    try {
+      const params = {
+        symbol: symbolId.value,
+        period: currentTab.value,
+        limit: 150,
+        to: endTime || undefined,
+      };
+
+      const res = await GetCandlestickChart(params);
+
+      let rawList = Array.isArray(res) ? res : res && res.data ? res.data : [];
+
+      if (rawList.length > 0) {
+        const formattedData = rawList.map((item) => ({
+          timestamp: Number(item[0]),
+          open: parseFloat(item[1]),
+          high: parseFloat(item[2]),
+          low: parseFloat(item[3]),
+          close: parseFloat(item[4]),
+          volume: parseFloat(item[5]),
+        }));
+
+        // 保证按时间升序
+        formattedData.sort((a, b) => a.timestamp - b.timestamp);
+
+        if (endTime) {
+          // --- 加载历史模式 ---
+          if (klineRef.value) {
+            const hasMore = formattedData.length >= 150;
+            // 核心点:子组件 KLineChart 内部现在会自动处理 overlap
+            klineRef.value.applyHistoryData(formattedData, hasMore);
+          }
+        } else {
+          // --- 初始化/重置模式 ---
+          kLineData.value = formattedData;
+          if (formattedData.length > 0) {
+            const lastBar = formattedData[formattedData.length - 1];
+            marketInfo.value = {
+              ...marketInfo.value,
+              price: lastBar.close,
+              fiatPrice: lastBar.close,
+              high: lastBar.high,
+              low: lastBar.low,
+            };
+          }
         }
       } else {
-        // --- 初始化/重置模式 ---
-        kLineData.value = formattedData;
-        if (formattedData.length > 0) {
-           const lastBar = formattedData[formattedData.length - 1];
-           marketInfo.value = { ...marketInfo.value, price: lastBar.close, fiatPrice: lastBar.close, high: lastBar.high, low: lastBar.low };
+        // 没数据了
+        if (endTime && klineRef.value) {
+          klineRef.value.applyHistoryData([], false);
         }
       }
-    } else {
-      // 没数据了
+    } catch (error) {
+      console.error("API Error", error);
       if (endTime && klineRef.value) {
+        // 发生错误时也要告知 KLineChart 停止加载,否则 loading 动画会一直转
         klineRef.value.applyHistoryData([], false);
       }
+    } finally {
+      isLoadingMore.value = false;
     }
-  } catch (error) {
-    console.error("API Error", error);
-    if (endTime && klineRef.value) {
-      // 发生错误时也要告知 KLineChart 停止加载,否则 loading 动画会一直转
-      klineRef.value.applyHistoryData([], false);
+  };
+
+  const handleLoadMore = (timestamp) => {
+    if (isLoadingMore.value) return;
+    isLoadingMore.value = true;
+    getKlineData(timestamp);
+  };
+
+  // --- WebSocket ---
+  const connectWebSocket = () => {
+    closeWebSocket();
+    const symbolStr = String(route.query.type || "btcusdt").toLowerCase();
+    const url = `${WS_BASE_URL}?symbol=${symbolStr}`;
+    try {
+      socket.value = new WebSocket(url);
+      socket.value.onopen = () => startHeartbeat();
+      socket.value.onmessage = (event) => handleSocketMessage(event.data);
+      socket.value.onclose = () => {
+        stopHeartbeat();
+        if (!isUnmounted) {
+          clearTimeout(reconnectTimer);
+          reconnectTimer = setTimeout(() => connectWebSocket(), RECONNECT_DELAY);
+        }
+      };
+    } catch (e) {
+      if (!isUnmounted)
+        reconnectTimer = setTimeout(() => connectWebSocket(), RECONNECT_DELAY);
     }
-  } finally {
-    isLoadingMore.value = false;
-  }
-};
-
-const handleLoadMore = (timestamp) => {
-  if (isLoadingMore.value) return;
-  isLoadingMore.value = true;
-  getKlineData(timestamp);
-};
-
-// --- WebSocket ---
-const connectWebSocket = () => {
-  closeWebSocket();
-  const symbolStr = String(route.query.type || "btcusdt").toLowerCase();
-  const url = `${WS_BASE_URL}?symbol=${symbolStr}`;
-  try {
-    socket.value = new WebSocket(url);
-    socket.value.onopen = () => startHeartbeat();
-    socket.value.onmessage = (event) => handleSocketMessage(event.data);
-    socket.value.onclose = () => { stopHeartbeat(); if (!isUnmounted) { clearTimeout(reconnectTimer); reconnectTimer = setTimeout(() => connectWebSocket(), RECONNECT_DELAY); } };
-  } catch (e) { if (!isUnmounted) reconnectTimer = setTimeout(() => connectWebSocket(), RECONNECT_DELAY); }
-};
-
-const closeWebSocket = () => { if (socket.value) { socket.value.close(); socket.value = null; } stopHeartbeat(); clearTimeout(reconnectTimer); };
-const startHeartbeat = () => { stopHeartbeat(); heartbeatTimer = setInterval(() => { socket.value?.readyState === 1 && socket.value.send("ping"); }, HEARTBEAT_INTERVAL); };
-const stopHeartbeat = () => { clearInterval(heartbeatTimer); heartbeatTimer = null; };
-
-const handleSocketMessage = (msgStr) => {
-  try {
-    if (msgStr === "pong") return;
-    const rawData = JSON.parse(msgStr);
-    const msg = rawData.data || rawData;
-    if (msg.e === "kline") {
-      const k = msg.k;
-      if (k.i !== currentTab.value) return;
-      const newBar = { timestamp: Number(k.t), open: parseFloat(k.o), high: parseFloat(k.h), low: parseFloat(k.l), close: parseFloat(k.c), volume: parseFloat(k.v) };
-      updateKlineData(newBar);
-      marketInfo.value.price = newBar.close;
-      marketInfo.value.fiatPrice = newBar.close;
-    } else if (msg.e === "24hrTicker") {
-      marketInfo.value.change = parseFloat(msg.P).toFixed(2);
-      marketInfo.value.high = msg.h; marketInfo.value.low = msg.l;
-      marketInfo.value.vol = msg.v; marketInfo.value.amount = msg.q;
-    } else if (rawData.stream?.includes("@depth20")) orderPlacement.value = rawData.data;
-    else if (rawData.stream?.includes("@aggTrade")) latestTransactionData.value = rawData.data;
-  } catch (e) {}
-};
-
-const updateKlineData = (newBar) => {
-  if (!kLineData.value?.length) { kLineData.value = [newBar]; return; }
-  const lastIndex = kLineData.value.length - 1;
-  const lastBar = kLineData.value[lastIndex];
-  // 简单的 WebSocket 去重
-  if (newBar.timestamp === lastBar.timestamp) kLineData.value.splice(lastIndex, 1, newBar);
-  else if (newBar.timestamp > lastBar.timestamp) kLineData.value.push(newBar);
-};
-
-const handleVisibilityChange = () => {
-  if (document.visibilityState === "visible" && socket.value?.readyState !== 1)
-    connectWebSocket(); };
-onMounted(() => { isUnmounted = false;
-  getKlineData(); connectWebSocket();
-  document.addEventListener("visibilitychange", handleVisibilityChange); });
-onBeforeUnmount(() => { isUnmounted = true; closeWebSocket(); document.removeEventListener("visibilitychange", handleVisibilityChange); });
-watch(symbolId, () => { kLineData.value = []; getKlineData(); connectWebSocket(); });
-const formatNumber = (num) => num ? Number(num).toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 6 }) : "0.00";
-const abbreviateNumber = (v) => { if (!v) return "0.00"; let n = parseFloat(v);
-  if (n>=1e9) return (n/1e9).toFixed(2)+"B";
-  if (n>=1e6) return (n/1e6).toFixed(2)+"M";
-  if (n>=1e3) return (n/1e3).toFixed(2)+"K";
-  return n.toFixed(2); };
-const getPricePrecision = (p) => (p < 1 ? 6 : p < 10 ? 4 : 2);
-const getPriceColor = (c) => (c >= 0 ? "fc45B26B" : "fcF6465D");
-const getUpDownClass = (c) => (c >= 0 ? "fc45B26B" : "fcF6465D");
+  };
+
+  const closeWebSocket = () => {
+    if (socket.value) {
+      socket.value.close();
+      socket.value = null;
+    }
+    stopHeartbeat();
+    clearTimeout(reconnectTimer);
+  };
+  const startHeartbeat = () => {
+    stopHeartbeat();
+    heartbeatTimer = setInterval(() => {
+      socket.value?.readyState === 1 && socket.value.send("ping");
+    }, HEARTBEAT_INTERVAL);
+  };
+  const stopHeartbeat = () => {
+    clearInterval(heartbeatTimer);
+    heartbeatTimer = null;
+  };
+
+  const handleSocketMessage = (msgStr) => {
+    try {
+      if (msgStr === "pong") return;
+      const rawData = JSON.parse(msgStr);
+      const msg = rawData.data || rawData;
+      if (msg.e === "kline") {
+        const k = msg.k;
+        if (k.i !== currentTab.value) return;
+        const newBar = {
+          timestamp: Number(k.t),
+          open: parseFloat(k.o),
+          high: parseFloat(k.h),
+          low: parseFloat(k.l),
+          close: parseFloat(k.c),
+          volume: parseFloat(k.v),
+        };
+        updateKlineData(newBar);
+        marketInfo.value.price = newBar.close;
+        marketInfo.value.fiatPrice = newBar.close;
+      } else if (msg.e === "24hrTicker") {
+        marketInfo.value.change = parseFloat(msg.P).toFixed(2);
+        marketInfo.value.high = msg.h;
+        marketInfo.value.low = msg.l;
+        marketInfo.value.vol = msg.v;
+        marketInfo.value.amount = msg.q;
+      } else if (rawData.stream?.includes("@depth20"))
+        orderPlacement.value = rawData.data;
+      else if (rawData.stream?.includes("@aggTrade"))
+        latestTransactionData.value = rawData.data;
+    } catch (e) {}
+  };
+
+  const updateKlineData = (newBar) => {
+    if (!kLineData.value?.length) {
+      kLineData.value = [newBar];
+      return;
+    }
+    const lastIndex = kLineData.value.length - 1;
+    const lastBar = kLineData.value[lastIndex];
+    // 简单的 WebSocket 去重
+    if (newBar.timestamp === lastBar.timestamp)
+      kLineData.value.splice(lastIndex, 1, newBar);
+    else if (newBar.timestamp > lastBar.timestamp) kLineData.value.push(newBar);
+  };
+
+  const handleVisibilityChange = () => {
+    if (document.visibilityState === "visible" && socket.value?.readyState !== 1)
+      connectWebSocket();
+  };
+  onMounted(() => {
+    isUnmounted = false;
+    getKlineData();
+    connectWebSocket();
+    document.addEventListener("visibilitychange", handleVisibilityChange);
+  });
+  onBeforeUnmount(() => {
+    isUnmounted = true;
+    closeWebSocket();
+    document.removeEventListener("visibilitychange", handleVisibilityChange);
+  });
+  watch(symbolId, () => {
+    kLineData.value = [];
+    getKlineData();
+    connectWebSocket();
+  });
+  const formatNumber = (num) =>
+    num
+      ? Number(num).toLocaleString("en-US", {
+          minimumFractionDigits: 2,
+          maximumFractionDigits: 6,
+        })
+      : "0.00";
+  const abbreviateNumber = (v) => {
+    if (!v) return "0.00";
+    let n = parseFloat(v);
+    if (n >= 1e9) return (n / 1e9).toFixed(2) + "B";
+    if (n >= 1e6) return (n / 1e6).toFixed(2) + "M";
+    if (n >= 1e3) return (n / 1e3).toFixed(2) + "K";
+    return n.toFixed(2);
+  };
+  const getPricePrecision = (p) => (p < 1 ? 6 : p < 10 ? 4 : 2);
+  const getPriceColor = (c) => (c >= 0 ? "fc45B26B" : "fcF6465D");
+  const getUpDownClass = (c) => (c >= 0 ? "fc45B26B" : "fcF6465D");
 </script>
 
 <style lang="less" scoped>
-.fc45B26B { color: #2ebd85 !important; }
-.fcF6465D { color: #f6465d !important; }
-.fc1F2937 { color: #1f2937; }
-.time-tabs { display: flex; align-items: center; justify-content: space-between; width: 100%;
-  box-sizing: border-box; padding:5px 0 15px 15px; position: relative; z-index: 20; }
-.tab-item { font-size: 14px; color: #929aa5; padding: 4px 8px; border-radius: 4px; cursor: pointer;
-  font-weight: 500; display: flex; align-items: center; position: relative; }
-.tab-item.icon-btn { padding: 4px 4px; }
-.active-text { color: #fff; font-weight: 600; }
-.triangle { font-size: 8px; margin-left: 2px; transform: scale(0.9); }
-.tab-item img { display: block; height: 16px; width: auto; }
-.dropdown-menu { position: absolute; top: 30px; left: -20px; width: 80px; background: #fff;
-  box-shadow: 0 4px 12px rgba(0,0,0,0.15); border-radius: 6px; padding: 5px 0; z-index: 100;
-  display: flex; flex-direction: column; }
-.drop-item { padding: 8px 15px; font-size: 13px; color: #666; text-align: center; }
-.drop-item.active { color: #F6465D; background: #fff5f5; }
-.indicator-panel { position: absolute; top: 155px; left: 15px; right: 15px; background: #fff;
-  box-shadow: 0 4px 15px rgba(0,0,0,0.1); border-radius: 8px; padding: 15px; z-index: 99; }
-.panel-section { margin-bottom: 15px; }
-.section-title { font-size: 12px; color: #999; margin-bottom: 8px; }
-.btn-group { display: flex; flex-wrap: wrap; gap: 10px; }
-.idx-btn { padding: 4px 12px; border: 1px solid #eee; border-radius: 14px; font-size: 12px;
-  color: #666; cursor: pointer; }
-.idx-btn.active { border-color: #F6465D; color: #F6465D; background-color: #fff5f5; }
-.market-conditions { display: flex; flex-direction: column; align-items: center; width: 100%;
-  position: relative; min-height: 100vh; background: #fff; }
-.market-price { display: flex; justify-content: space-between; margin-top: 4px;
-  width: 100%; padding: 0 15px; box-sizing: border-box;
-  .price-left { display: flex;line-height: 20px; flex-direction: column; width: 144px;
-    .left-price { height: 18px; line-height: 18px; } .left-number { margin-top: 5px; }
-    .left-appro { margin-top: 3px; .appro { margin-left: 9px; } } }
-  .price-right { display: flex; flex-direction: column; height: 100%;
-    .right-number-top, .right-number-bottom { display: flex; justify-content: flex-end; width: 100%; height: 32px; .right-number-top-price, .right-number-top-number { margin-left: 10px; text-align: right; div { height: 16px; line-height: 16px; text-align: end; } } } } }
-.k-line-main { height: 50vh; min-height: 350px; width: 100%; padding: 0 15px; }
-.notifi-classifi { display: flex; align-items: flex-end; margin-top: 140px;
-  width: 100%; padding: 0 15px 0 15px; box-sizing: border-box; height: 24px;
-  border-bottom: 1px solid #f5f5f5; .sys-notifi { margin-left: 47px; } }
-</style>
+  .fc45B26B {
+    color: #2ebd85 !important;
+  }
+  .fcF6465D {
+    color: #f6465d !important;
+  }
+  .fc1F2937 {
+    color: #1f2937;
+  }
+  .time-tabs {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    width: 100%;
+    box-sizing: border-box;
+    padding: 5px 0 15px 15px;
+    position: relative;
+    z-index: 20;
+  }
+  .tab-item {
+    font-size: 14px;
+    color: #929aa5;
+    padding: 4px 8px;
+    border-radius: 4px;
+    cursor: pointer;
+    font-weight: 500;
+    display: flex;
+    align-items: center;
+    position: relative;
+  }
+  .tab-item.icon-btn {
+    padding: 4px 4px;
+  }
+  .active-text {
+    color: #fff;
+    font-weight: 600;
+  }
+  .triangle {
+    font-size: 8px;
+    margin-left: 2px;
+    transform: scale(0.9);
+  }
+  .tab-item img {
+    display: block;
+    height: 16px;
+    width: auto;
+  }
+  .dropdown-menu {
+    position: absolute;
+    top: 30px;
+    left: -20px;
+    width: 80px;
+    background: #fff;
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+    border-radius: 6px;
+    padding: 5px 0;
+    z-index: 100;
+    display: flex;
+    flex-direction: column;
+  }
+  .drop-item {
+    padding: 8px 15px;
+    font-size: 13px;
+    color: #666;
+    text-align: center;
+  }
+  .drop-item.active {
+    color: #f6465d;
+    background: #fff5f5;
+  }
+  .indicator-panel {
+    position: absolute;
+    top: 155px;
+    left: 15px;
+    right: 15px;
+    background: #fff;
+    box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
+    border-radius: 8px;
+    padding: 15px;
+    z-index: 99;
+  }
+  .panel-section {
+    margin-bottom: 15px;
+  }
+  .section-title {
+    font-size: 12px;
+    color: #999;
+    margin-bottom: 8px;
+  }
+  .btn-group {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 10px;
+  }
+  .idx-btn {
+    padding: 4px 12px;
+    border: 1px solid #eee;
+    border-radius: 14px;
+    font-size: 12px;
+    color: #666;
+    cursor: pointer;
+  }
+  .idx-btn.active {
+    border-color: #f6465d;
+    color: #f6465d;
+    background-color: #fff5f5;
+  }
+  .market-conditions {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    width: 100%;
+    position: relative;
+    min-height: 100vh;
+    background: #fff;
+  }
+  .market-price {
+    display: flex;
+    justify-content: space-between;
+    margin-top: 4px;
+    width: 100%;
+    padding: 0 15px;
+    box-sizing: border-box;
+    .price-left {
+      display: flex;
+      line-height: 20px;
+      flex-direction: column;
+      width: 144px;
+      .left-price {
+        height: 18px;
+        line-height: 18px;
+      }
+      .left-number {
+        margin-top: 5px;
+      }
+      .left-appro {
+        margin-top: 3px;
+        .appro {
+          margin-left: 9px;
+        }
+      }
+    }
+    .price-right {
+      display: flex;
+      flex-direction: column;
+      height: 100%;
+      .right-number-top,
+      .right-number-bottom {
+        display: flex;
+        justify-content: flex-end;
+        width: 100%;
+        height: 32px;
+        .right-number-top-price,
+        .right-number-top-number {
+          margin-left: 10px;
+          text-align: right;
+          div {
+            height: 16px;
+            line-height: 16px;
+            text-align: end;
+          }
+        }
+      }
+    }
+  }
+  .k-line-main {
+    height: 50vh;
+    min-height: 350px;
+    width: 100%;
+    padding: 0 15px;
+  }
+  .notifi-classifi {
+    display: flex;
+    align-items: flex-end;
+    margin-top: 140px;
+    width: 100%;
+    padding: 0 15px 0 15px;
+    box-sizing: border-box;
+    height: 24px;
+    .sys-notifi {
+      margin-left: 47px;
+    }
+  }
+</style>

+ 197 - 113
src/views/user/InviteCenter.vue

@@ -12,9 +12,7 @@
     <!-- 顶部礼物图标 -->
     <div class="gift-section">
       <div class="gift-circle">
-        <svg viewBox="0 0 1024 1024" width="80" height="80" fill="#DE3545">
-          <path d="M880 305H632c6.2-11.4 11-23.6 14.2-36.4 8.6-34 1.8-70.6-19-100.8-23.4-34-62-54.2-103.2-54.2-27.6 0-54.2 9-76 25.8-21.8-16.8-48.4-25.8-76-25.8-41.2 0-79.8 20.2-103.2 54.2-20.8 30.2-27.6 66.8-19 100.8 3.2 12.8 8 25 14.2 36.4H144c-17.6 0-32 14.4-32 32v152c0 17.6 14.4 32 32 32h24.8v344c0 17.6 14.4 32 32 32h622.4c17.6 0 32-14.4 32-32V497H880c17.6 0 32-14.4 32-32V337c0-17.6-14.4-32-32-32zM524 179.6c17.6 0 33.6 11 42.4 28.4 6 12 7.2 25.6 3.4 38.6-4 13.6-13.4 25.8-26.6 34.4-12.8 8.4-26.4 13-39.6 13-3.8 0-7.6-0.4-11.2-1.2-12.4-2.6-22.8-11.6-26.4-23.8-3.4-11.6 0-25.2 7-38.4 11.4-21.4 30.2-51 51-51zM293.6 208c8.8-17.4 24.8-28.4 42.4-28.4 20.8 0 39.6 29.6 51 51 7 13.2 10.4 26.8 7 38.4-3.6 12.2-14 21.2-26.4 23.8-3.6 0.8-7.4 1.2-11.2 1.2-13.2 0-26.8-4.6-39.6-13-13.2-8.6-22.6-20.8-26.6-34.4-3.8-13-2.6-26.6 3.4-38.6z m-65.6 161h228v128h-228V369z m228 365H232.8V561h223.2v173z m267.2 0H520V561h223.2v173z m0-237H520V369h203.2v128z"/>
-        </svg>
+        <img src="@/assets/icon/user/gift.png" alt="" />
       </div>
       <h2 class="invite-title">邀请好友赚取更多福利</h2>
     </div>
@@ -23,12 +21,13 @@
     <div class="code-section">
       <div class="code-label">邀请码</div>
       <div class="code-box">
-        <span class="code-text">{{ inviteCode }}</span>
+        <span class="code-text">{{ inviteData.code }}</span>
         <!-- 点击复制图标 -->
-        <svg @click="copyCode" class="copy-icon" viewBox="0 0 1024 1024" width="20" height="20">
-          <path d="M832 64H296c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h496v688c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V96c0-17.7-14.3-32-32-32z" fill="#DE3545"/>
-          <path d="M704 192H192c-17.7 0-32 14.3-32 32v530.7c0 8.5 3.4 16.6 9.4 22.6l173.3 173.3c6 6 14.1 9.4 22.6 9.4h338.7c17.7 0 32-14.3 32-32V224c0-17.7-14.3-32-32-32zM352 853.3V704h-149.3L352 853.3z m320-32V256H224v384h160c17.7 0 32 14.3 32 32v149.3h256z" fill="#DE3545"/>
-        </svg>
+        <img
+          src="@/assets/icon/user/copy.png"
+          @click="copyCode"
+          class="copy-icon"
+          alt="" />
       </div>
 
       <button class="share-btn" @click="handleShare">分享邀请</button>
@@ -38,11 +37,11 @@
     <div class="stats-row">
       <div class="stats-item">
         <div class="stats-label">累计返佣</div>
-        <div class="stats-num">158.00 USDT</div>
+        <div class="stats-num">{{ inviteData.reward }} USDT</div>
       </div>
       <div class="stats-item">
         <div class="stats-label">邀请人数</div>
-        <div class="stats-num">1515</div>
+        <div class="stats-num">{{ inviteData.inv_cnt }}</div>
       </div>
     </div>
 
@@ -58,116 +57,201 @@
 </template>
 
 <script setup>
-import { ref } from 'vue';
-import { useRouter } from "vue-router";
-// 引入 Vant 的 Toast
-import { showToast } from 'vant';
-// 如果项目中没有全局引入样式,可能需要取消下面这行的注释
-// import 'vant/es/toast/style';
-
-const router = useRouter();
-const inviteCode = ref('LANDCOIN444');
-
-// --- 核心:兼容性极强的复制功能 ---
-const copyCode = async () => {
-  const text = inviteCode.value;
-
-  // 1. 优先尝试标准 API (需要 HTTPS)
-  if (navigator.clipboard && navigator.clipboard.writeText) {
+  import { ref, onMounted } from "vue";
+  import { useRouter } from "vue-router";
+  // 引入 Vant 的 Toast
+  import { showToast } from "vant";
+  // 如果项目中没有全局引入样式,可能需要取消下面这行的注释
+  // import 'vant/es/toast/style';
+  import { getInviteInfo } from "@/api/user";
+
+  const router = useRouter();
+
+  const inviteData = ref({});
+
+  const getInviteInfoData = async () => {
+    const data = await getInviteInfo();
+    if (data) {
+      inviteData.value = data;
+    }
+  };
+
+  // --- 核心:兼容性极强的复制功能 ---
+  const copyCode = async () => {
+    const text = inviteCode.value;
+
+    // 1. 优先尝试标准 API (需要 HTTPS)
+    if (navigator.clipboard && navigator.clipboard.writeText) {
+      try {
+        await navigator.clipboard.writeText(text);
+        showToast({ type: "success", message: "复制成功" });
+        return;
+      } catch (err) {
+        console.warn("标准API复制失败,尝试降级方案", err);
+      }
+    }
+
+    // 2. 降级方案 (兼容所有浏览器/Webview)
     try {
-      await navigator.clipboard.writeText(text);
-      showToast({ type: 'success', message: '复制成功' });
-      return;
-    } catch (err) {
-      console.warn('标准API复制失败,尝试降级方案', err);
+      const input = document.createElement("input");
+      input.setAttribute("readonly", "readonly"); // 防止 iOS 拉起键盘
+      input.setAttribute("value", text);
+      input.style.position = "fixed";
+      input.style.left = "-9999px";
+      document.body.appendChild(input);
+
+      input.select();
+      input.setSelectionRange(0, 9999); // 兼容 iOS
+
+      if (document.execCommand("copy")) {
+        showToast({ type: "success", message: "复制成功" });
+      } else {
+        showToast({ type: "fail", message: "复制失败" });
+      }
+
+      document.body.removeChild(input);
+    } catch (e) {
+      showToast({ type: "fail", message: "浏览器不支持自动复制" });
     }
-  }
+  };
 
-  // 2. 降级方案 (兼容所有浏览器/Webview)
-  try {
-    const input = document.createElement('input');
-    input.setAttribute('readonly', 'readonly'); // 防止 iOS 拉起键盘
-    input.setAttribute('value', text);
-    input.style.position = 'fixed';
-    input.style.left = '-9999px';
-    document.body.appendChild(input);
+  // 点击分享,跳转到新的海报页面
+  const handleShare = () => {
+    router.push({ name: "InvitePoster", params: { code: inviteData.value.code } });
+  };
+
+  onMounted(() => {
+    getInviteInfoData();
+  });
+</script>
+
+<style scoped lang="less">
+  /* ========== 基础页面样式 ========== */
+  .invite-page {
+    min-height: 100vh;
+    background: #fff;
+  }
+  .nav-bar {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    height: 44px;
+    padding: 0 16px;
+  }
+  .nav-title {
+    font-size: 18px;
+    font-weight: 500;
+  }
+  .nav-left,
+  .nav-right {
+    width: 30px;
+    display: flex;
+    align-items: center;
+  }
 
-    input.select();
-    input.setSelectionRange(0, 9999); // 兼容 iOS
+  .gift-section {
+    text-align: center;
+    padding: 30px 0 20px;
+  }
+  .gift-circle {
+    width: 200px;
+    height: 200px;
+    background: #fff0f0; /* 浅粉色圆底 */
+    border-radius: 50%;
+    margin: 0 auto 20px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
 
-    if (document.execCommand('copy')) {
-      showToast({ type: 'success', message: '复制成功' });
-    } else {
-      showToast({ type: 'fail', message: '复制失败' });
+    img {
+      width: 100px;
+      height: 116px;
     }
+  }
+  .invite-title {
+    height: 32px;
+    line-height: 32px;
+    font-size: 22px;
+    font-weight: 600;
+    color: #333;
+  }
 
-    document.body.removeChild(input);
-  } catch (e) {
-    showToast({ type: 'fail', message: '浏览器不支持自动复制' });
+  .code-section {
+    padding: 0 20px;
+  }
+  .code-label {
+    font-size: 16px;
+    font-weight: 400;
+    color: #333;
+    margin-bottom: 10px;
+  }
+  .code-box {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    background: #fff;
+    border: 1px dashed #eee; /* 虚线边框 */
+    padding: 12px 16px;
+    border-radius: 8px;
+    margin-bottom: 10px;
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03);
+  }
+  .code-text {
+    font-size: 16px;
+    font-weight: 700;
+    color: #333;
+  }
+  .share-btn {
+    width: 100%;
+    height: 40px;
+    background: #de3545; /* 红色按钮 */
+    color: #fff;
+    border: none;
+    border-radius: 20px;
+    font-size: 14px;
+    font-weight: 600;
+  }
+  /* 点击反馈 */
+  .share-btn:active {
+    opacity: 0.9;
+  }
+  .copy-icon {
+    width: 24px;
+    height: 24px;
+    cursor: pointer;
+  }
+  .copy-icon:active {
+    opacity: 0.6;
   }
-};
 
-// 点击分享,跳转到新的海报页面
-const handleShare = () => {
-  router.push('/invite/poster');
-};
-</script>
+  .stats-row {
+    display: flex;
+    margin: 28px 0 15px 20px;
+  }
+  .stats-item {
+    flex: 1;
+    text-align: center;
+  }
+  .stats-label {
+    color: #666;
+    font-size: 12px;
+    margin-bottom: 6px;
+  }
+  .stats-num {
+    font-size: 18px;
+    font-weight: 600;
+    color: #333;
+  }
 
-<style scoped>
-/* ========== 基础页面样式 ========== */
-.invite-page {
-  min-height: 100vh;
-  background: #fff;
-}
-.nav-bar {
-  display: flex; justify-content: space-between; align-items: center;
-  height: 44px; padding: 0 16px;
-}
-.nav-title { font-size: 18px; font-weight: 500; }
-.nav-left, .nav-right { width: 30px; display: flex; align-items: center; }
-
-.gift-section {
-  text-align: center;
-  padding: 30px 0 20px;
-}
-.gift-circle {
-  width: 120px; height: 120px;
-  background: #FFF0F0; /* 浅粉色圆底 */
-  border-radius: 50%;
-  margin: 0 auto 20px;
-  display: flex; align-items: center; justify-content: center;
-}
-.invite-title {
-  font-size: 18px; font-weight: 400; color: #333;
-}
-
-.code-section { padding: 0 20px; }
-.code-label { font-size: 14px; font-weight: 500; color: #333; margin-bottom: 10px; }
-.code-box {
-  display: flex; justify-content: space-between; align-items: center;
-  background: #fff; border: 1px dashed #eee; /* 虚线边框 */
-  padding: 12px 16px; border-radius: 8px; margin-bottom: 20px;
-  box-shadow: 0 2px 8px rgba(0,0,0,0.03);
-}
-.code-text { font-size: 14px; font-weight: 500; color: #333; }
-.share-btn {
-  width: 100%; height: 40px;
-  background: #DE3545; /* 红色按钮 */
-  color: #fff; border: none; border-radius: 20px;
-  font-size: 14px; font-weight: 600;
-}
-/* 点击反馈 */
-.share-btn:active { opacity: 0.9; }
-.copy-icon { cursor: pointer; }
-.copy-icon:active { opacity: 0.6; }
-
-.stats-row {
-  display: flex; margin: 11px 20px;
-}
-.stats-item { flex: 1; text-align: center; }
-.stats-label { color: #666; font-size: 12px; margin-bottom: 6px; }
-.stats-num { font-size: 18px; font-weight: 600; color: #333; }
-
-.rules-section { padding: 0 20px; color: #999; font-size: 12px; line-height: 1.8; }
-.rules-section h3 { color: #333; font-size: 14px; margin-bottom: 10px; }
-</style>
+  .rules-section {
+    margin-top: 30px;
+    padding: 0 20px;
+    color: #999;
+    font-size: 12px;
+    line-height: 1.8;
+  }
+  .rules-section h3 {
+    color: #333;
+    font-size: 14px;
+  }
+</style>

+ 145 - 111
src/views/user/InvitePoster.vue

@@ -1,147 +1,181 @@
 <template>
   <div class="poster-container">
-
     <div class="poster-content" ref="posterRef">
-
       <div class="header-section">
         <h1 class="brand-name">BitWiseWorld</h1>
-        <div class="main-title">邀请好友<br>赚取更多福利</div>
+        <div class="main-title">邀请好友<br />赚取更多福利</div>
       </div>
-
       <div class="illustration-section">
-        <img src="@/assets/icon/bitcoin/yaoqing.svg" class="main-img" alt="Illustration" />
+        <img
+          src="@/assets/icon/bitcoin/yaoqing.svg"
+          class="main-img"
+          alt="Illustration" />
       </div>
-
       <div class="footer-info">
         <div class="info-left">
           <div class="label">邀请码</div>
-          <div class="code">LANDCOIN444</div>
+          <div class="code">{{ inviteCode }}</div>
         </div>
         <div class="info-right">
           <img src="@/assets/icon/bitcoin/erweima.svg" class="qr-img" alt="QR Code" />
         </div>
       </div>
-
       <div class="bg-pattern"></div>
     </div>
-
     <div class="action-area">
       <button class="save-btn" @click="handleSave">保存图片</button>
     </div>
-
   </div>
 </template>
 
 <script setup>
-import { ref } from 'vue';
-import html2canvas from 'html2canvas';
-
-const posterRef = ref(null);
-
-const handleSave = async () => {
-  if (!posterRef.value) return;
-  try {
-    const canvas = await html2canvas(posterRef.value, {
-      useCORS: true,
-      scale: 3, // 稍微提高一点清晰度
-      backgroundColor: '#D91E2D',
-    });
-    const imgUrl = canvas.toDataURL('image/png');
-
-    const link = document.createElement('a');
-    link.href = imgUrl;
-    link.download = 'invite_poster.png';
-    document.body.appendChild(link);
-    link.click();
-    document.body.removeChild(link);
-  } catch (error) {
-    console.error(error);
-  }
-};
+  import { ref, onMounted } from "vue";
+  import { useRoute } from "vue-router";
+  import html2canvas from "html2canvas";
+
+  const route = useRoute();
+  const posterRef = ref(null);
+  const inviteCode = ref();
+
+  const handleSave = async () => {
+    if (!posterRef.value) return;
+    try {
+      const canvas = await html2canvas(posterRef.value, {
+        useCORS: true,
+        scale: 3, // 稍微提高一点清晰度
+        backgroundColor: "#D91E2D",
+      });
+      const imgUrl = canvas.toDataURL("image/png");
+
+      const link = document.createElement("a");
+      link.href = imgUrl;
+      link.download = "invite_poster.png";
+      document.body.appendChild(link);
+      link.click();
+      document.body.removeChild(link);
+    } catch (error) {
+      console.error(error);
+    }
+  };
+
+  onMounted(() => {
+    inviteCode.value = route.params.code;
+  });
 </script>
 
 <style scoped>
-/* 页面容器:红色背景铺满全屏 */
-.poster-container {
-  min-height: 100vh;
-  background-color: #D91E2D;
-  display: flex;
-  flex-direction: column;
-  /* 如果内容很少,可以加这个让内容整体垂直居中,或者去掉它让内容顶对齐 */
-  /* justify-content: center; */
-}
+  /* 页面容器:红色背景铺满全屏 */
+  .poster-container {
+    min-height: 100vh;
+    background-color: #d91e2d;
+    display: flex;
+    flex-direction: column;
+    /* 如果内容很少,可以加这个让内容整体垂直居中,或者去掉它让内容顶对齐 */
+    /* justify-content: center; */
+  }
 
-/* 核心海报内容区:这里决定了截图的高度 */
-.poster-content {
-  position: relative;
-  background-color: #D91E2D; /* 截图需要背景色 */
-  padding: 30px 24px 10px;    /* 减少底部 padding */
-  overflow: hidden;           /* 防止装饰溢出 */
-}
+  /* 核心海报内容区:这里决定了截图的高度 */
+  .poster-content {
+    position: relative;
+    background-color: #d91e2d; /* 截图需要背景色 */
+    padding: 30px 24px 10px; /* 减少底部 padding */
+    overflow: hidden; /* 防止装饰溢出 */
+  }
 
-.header-section {
-  position: relative; z-index: 1;
-  margin-bottom: 20px; /* 减小间距 */
-}
-.brand-name {
-  font-size: 18px; font-weight: 800; color: #fff; opacity: 0.9;
-  margin-bottom: 10px;
-}
-.main-title {
-  font-size: 32px; font-weight: 600; color: #fff; line-height: 1.2;
-}
+  .header-section {
+    position: relative;
+    z-index: 1;
+    margin-bottom: 20px; /* 减小间距 */
+  }
+  .brand-name {
+    font-size: 18px;
+    font-weight: 800;
+    color: #fff;
+    opacity: 0.9;
+    margin-bottom: 10px;
+  }
+  .main-title {
+    font-size: 32px;
+    font-weight: 600;
+    color: #fff;
+    line-height: 1.2;
+  }
 
-/* 插画区:关键修改 */
-.illustration-section {
-  position: relative; z-index: 1;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  margin: 10px 0 20px; /* 上下留一点紧凑的间距 */
-}
+  /* 插画区:关键修改 */
+  .illustration-section {
+    position: relative;
+    z-index: 1;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    margin: 10px 0 20px; /* 上下留一点紧凑的间距 */
+  }
 
-.main-img {
-  width: 100%;
-  max-width: 280px; /* 限制最大宽度 */
-  height: auto;
-  /* 如果图还是很长,可以强制限制高度,用 object-fit 保持比例 */
-  max-height: 320px;
-  object-fit: contain;
-}
+  .main-img {
+    width: 100%;
+    max-width: 280px; /* 限制最大宽度 */
+    height: auto;
+    /* 如果图还是很长,可以强制限制高度,用 object-fit 保持比例 */
+    max-height: 320px;
+    object-fit: contain;
+  }
 
-/* 底部信息区 */
-.footer-info {
-  position: relative; z-index: 1;
-  display: flex;
-  justify-content: space-between;
-  align-items: flex-end;
-  padding-bottom: 20px;
-}
-.label { font-size: 14px; color: rgba(255,255,255,0.8); margin-bottom: 4px; }
-.code { font-size: 20px; font-weight: 700; color: #fff; letter-spacing: 0.5px; }
-.qr-img {
-  width: 72px; height: 72px;
-  background: #fff; padding: 3px; border-radius: 4px;
-}
+  /* 底部信息区 */
+  .footer-info {
+    position: relative;
+    z-index: 1;
+    display: flex;
+    justify-content: space-between;
+    align-items: flex-end;
+    padding-bottom: 20px;
+  }
+  .label {
+    font-size: 14px;
+    color: rgba(255, 255, 255, 0.8);
+    margin-bottom: 4px;
+  }
+  .code {
+    font-size: 20px;
+    font-weight: 700;
+    color: #fff;
+    letter-spacing: 0.5px;
+  }
+  .qr-img {
+    width: 72px;
+    height: 72px;
+    background: #fff;
+    padding: 3px;
+    border-radius: 4px;
+  }
 
-/* 背景装饰 */
-.bg-pattern {
-  position: absolute; top: 0; left: 0; right: 0; bottom: 0;
-  background-image: radial-gradient(rgba(255,255,255,0.1) 2px, transparent 2px);
-  background-size: 18px 18px;
-  pointer-events: none; z-index: 0;
-}
+  /* 背景装饰 */
+  .bg-pattern {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    background-image: radial-gradient(rgba(255, 255, 255, 0.1) 2px, transparent 2px);
+    background-size: 18px 18px;
+    pointer-events: none;
+    z-index: 0;
+  }
 
-/* 底部按钮区:脱离海报流,固定在底部或者紧跟内容 */
-.action-area {
-  padding: 0px 24px 30px;
-  margin-top: auto; /* 把按钮推到屏幕最底部,如果不想推到底部,去掉这行 */
-}
+  /* 底部按钮区:脱离海报流,固定在底部或者紧跟内容 */
+  .action-area {
+    padding: 0px 24px 30px;
+    margin-top: auto; /* 把按钮推到屏幕最底部,如果不想推到底部,去掉这行 */
+  }
 
-.save-btn {
-  width: 100%; height: 48px;
-  background: #fff; color: #D91E2D;
-  font-size: 16px; font-weight: 600; border: none; border-radius: 24px;
-  box-shadow: 0 4px 12px rgba(0,0,0,0.15);
-}
-</style>
+  .save-btn {
+    width: 100%;
+    height: 48px;
+    background: #fff;
+    color: #d91e2d;
+    font-size: 16px;
+    font-weight: 600;
+    border: none;
+    border-radius: 24px;
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+  }
+</style>

+ 23 - 26
vue.config.js

@@ -1,5 +1,5 @@
 // vue.config.js
-const { defineConfig } = require('@vue/cli-service')
+const { defineConfig } = require("@vue/cli-service");
 
 module.exports = defineConfig({
   transpileDependencies: true,
@@ -7,73 +7,70 @@ module.exports = defineConfig({
   // --- 核心配置开始 ---
   devServer: {
     proxy: {
-      '/api': {
+      "/api": {
         // ⚠️【重要】这里必须改成你真实的后端地址!
         // 如果后端在本地,可能是 http://localhost:8000
         // 如果是线上测试服,可能是 http://47.100.xx.xx
-          //'http://63.141.230.43:57676',
-          // 'http://backend.66linknow.com'
-        target:'http://backend.66linknow.com', // ✅ 必须加上协议
+        //'http://63.141.230.43:57676',
+        // 'http://backend.66linknow.com'
+        target: "http://63.141.230.43:57676", // ✅ 必须加上协议
         changeOrigin: true, // 允许跨域
-
       },
       // 2.【新增】WebSocket 代理配置
-      '/ws/kline': {
-        target: 'ws://backend.66linknow.com', // 后端 IP
+      "/ws/kline": {
+        target: "ws://backend.66linknow.com", // 后端 IP
         changeOrigin: true,
-        ws: true // ⚠️ 开启 WebSocket 支持
+        ws: true, // ⚠️ 开启 WebSocket 支持
         // 这里是否需要 pathRewrite 取决于后端路径有没有 /ws
-      }
-    }
+      },
+    },
   },
 
   // 1. 基础路径 (解决白屏问题)
-  publicPath: './',
+  publicPath: "./",
 
   // 2. 关闭 SourceMap (最简单粗暴的减体积)
   // 生产环境不生成 .map 文件,体积能减少 50% 以上,且防源码泄露
   productionSourceMap: false,
 
   // 3. Webpack 核心优化 (代码分割)
-  configureWebpack: config => {
+  configureWebpack: (config) => {
     // 只有在打包生产环境 (npm run build) 时才运行优化
-    if (process.env.NODE_ENV === 'production') {
-
+    if (process.env.NODE_ENV === "production") {
       // 开启代码分割 (SplitChunks)
       config.optimization.splitChunks = {
-        chunks: 'all', // 对所有代码进行分割 (无论是异步还是同步)
+        chunks: "all", // 对所有代码进行分割 (无论是异步还是同步)
 
         // 缓存组配置:决定什么代码打包到什么文件里
         cacheGroups: {
           // 组1: 第三方库 (node_modules)
           // 作用:把所有 node_modules 里的东西拆出来
           libs: {
-            name: 'chunk-libs',
+            name: "chunk-libs",
             test: /[\\/]node_modules[\\/]/,
             priority: 10,
-            chunks: 'initial' // 只打包初始依赖
+            chunks: "initial", // 只打包初始依赖
           },
 
           // 组2: Vant UI 组件库 (单独拆分)
           // 作用:Vant 体积较大,单独打包有利于缓存
           vant: {
-            name: 'chunk-vant',
+            name: "chunk-vant",
             priority: 20, // 优先级更高,会先被提取
-            test: /[\\/]node_modules[\\/]_?vant(.*)/
+            test: /[\\/]node_modules[\\/]_?vant(.*)/,
           },
 
           // 组3: 公共代码
           // 作用:如果你的多个页面都引用了同一个组件,提取出来公用
           commons: {
-            name: 'chunk-commons',
+            name: "chunk-commons",
             test: /[\\/]src[\\/]components[\\/]/, // 匹配 src/components 下的组件
             minChunks: 2, // 只要被引用2次及以上就提取
             priority: 5,
-            reuseExistingChunk: true
-          }
-        }
+            reuseExistingChunk: true,
+          },
+        },
       };
     }
   },
-
-})
+});

部分文件因文件數量過多而無法顯示