瀏覽代碼

通用功能3个点-完成
交易规则-完成
交易设置 -完成
仓位模式切换弹窗-完成
交易设置-完成
交易通知-完成
资金费率触发设置-完成
交易设置界面-完成
交易颜色设置-完成
主题模式切换-完成
资产冻结弹窗-完成
设置支付密码-完成

Hexinkui 1 月之前
父節點
當前提交
2a096f07d4
共有 26 個文件被更改,包括 1734 次插入50 次删除
  1. 1 1
      src/App.vue
  2. 6 0
      src/assets/icon/bitcoin/feilv.svg
  3. 6 0
      src/assets/icon/bitcoin/guizhe.svg
  4. 6 0
      src/assets/icon/bitcoin/huazhuan.svg
  5. 6 0
      src/assets/icon/bitcoin/jiaoyi.svg
  6. 6 0
      src/assets/icon/bitcoin/jilu.svg
  7. 4 4
      src/assets/icon/bitcoin/jisuan.svg
  8. 6 0
      src/assets/icon/bitcoin/jisuanqi.svg
  9. 6 0
      src/assets/icon/bitcoin/shezhi.svg
  10. 33 0
      src/assets/icon/bitcoin/xialadaka.svg
  11. 57 0
      src/composables/useBodyScrollLock.js
  12. 28 5
      src/router/index.js
  13. 8 3
      src/views/HomeIndex.vue
  14. 21 5
      src/views/bitcoin/Calculator.vue
  15. 168 0
      src/views/bitcoin/CommonFunctionsPopup/CommonFunctionsPopup.vue
  16. 110 0
      src/views/bitcoin/CommonFunctionsPopup/GeneralLevel2/TradeRules.vue
  17. 511 0
      src/views/bitcoin/CommonFunctionsPopup/GeneralLevel2/TradeSettings.vue
  18. 171 0
      src/views/bitcoin/CommonFunctionsPopup/GeneralLevel2/components/PositionMode.vue
  19. 132 0
      src/views/bitcoin/CommonFunctionsPopup/GeneralLevel2/components/SelectionSheet.vue
  20. 194 0
      src/views/bitcoin/CommonFunctionsPopup/GeneralLevel2/components/TradeNotificationPopup.vue
  21. 93 0
      src/views/bitcoin/CommonFunctionsPopup/GeneralLevel2/components/TriggerValueSheet.vue
  22. 29 14
      src/views/bitcoin/Index.vue
  23. 118 0
      src/views/bitcoin/calculator/FundingRateReminder.vue
  24. 0 11
      src/views/bitcoin/calculator/LimitOrderModal.vue
  25. 11 6
      src/views/bitcoin/components/FundingOptions.vue
  26. 3 1
      src/views/bitcoin/components/OrderTimeSheet.vue

+ 1 - 1
src/App.vue

@@ -1,5 +1,5 @@
 <template>
-  <router-view :key="$route.fullPath"/>
+  <router-view/>
 </template>
 
 <style lang="less"></style>

文件差異過大導致無法顯示
+ 6 - 0
src/assets/icon/bitcoin/feilv.svg


文件差異過大導致無法顯示
+ 6 - 0
src/assets/icon/bitcoin/guizhe.svg


文件差異過大導致無法顯示
+ 6 - 0
src/assets/icon/bitcoin/huazhuan.svg


文件差異過大導致無法顯示
+ 6 - 0
src/assets/icon/bitcoin/jiaoyi.svg


文件差異過大導致無法顯示
+ 6 - 0
src/assets/icon/bitcoin/jilu.svg


文件差異過大導致無法顯示
+ 4 - 4
src/assets/icon/bitcoin/jisuan.svg


文件差異過大導致無法顯示
+ 6 - 0
src/assets/icon/bitcoin/jisuanqi.svg


文件差異過大導致無法顯示
+ 6 - 0
src/assets/icon/bitcoin/shezhi.svg


+ 33 - 0
src/assets/icon/bitcoin/xialadaka.svg

@@ -0,0 +1,33 @@
+<svg width="79" height="99" viewBox="0 0 79 99" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect x="48" y="39" width="31" height="60" rx="6" fill="#F5F5F5"/>
+<rect y="39" width="46" height="42" rx="6" fill="#F5F5F5"/>
+<rect y="83" width="46" height="7" rx="3.5" fill="#45B26B"/>
+<rect y="92" width="46" height="7" rx="3.5" fill="#DF384C"/>
+<rect width="79" height="36" rx="6" fill="#F5F5F5"/>
+<rect x="8.77344" y="15.2587" width="0.516132" height="13.8754" rx="0.258066" fill="#45B26B"/>
+<rect x="8" y="17.1088" width="2.06453" height="10.1753" rx="1.03226" fill="#45B26B"/>
+<rect x="14.1934" y="10.6335" width="0.516132" height="8.63359" rx="0.258066" fill="#DF384C"/>
+<rect x="13.4199" y="12.792" width="2.06453" height="4.62514" rx="1.03226" fill="#DF384C"/>
+<rect x="39.7422" y="8.78357" width="0.516132" height="7.09187" rx="0.258066" fill="#DF384C"/>
+<rect x="38.9688" y="10.0168" width="2.06453" height="3.23759" rx="1.03226" fill="#DF384C"/>
+<rect x="44.9023" y="2" width="0.516132" height="11.5628" rx="0.258066" fill="#45B26B"/>
+<rect x="44.1289" y="6.1626" width="2.06453" height="3.08342" rx="1.03226" fill="#45B26B"/>
+<rect x="65.5488" y="22.0422" width="0.516132" height="8.63359" rx="0.258066" fill="#DF384C"/>
+<rect x="64.7734" y="24.2007" width="2.06453" height="4.62514" rx="1.03226" fill="#DF384C"/>
+<rect x="70.7109" y="18.188" width="0.516132" height="5.70433" rx="0.258066" fill="#45B26B"/>
+<rect x="69.9355" y="19.5754" width="2.06453" height="3.08342" rx="1.03226" fill="#45B26B"/>
+<rect x="19.0977" y="18.8047" width="0.516132" height="6.9377" rx="0.258066" fill="#45B26B"/>
+<rect x="18.3223" y="20.6547" width="2.06453" height="3.23759" rx="1.03226" fill="#45B26B"/>
+<rect x="60.3867" y="18.8047" width="0.516132" height="11.4087" rx="0.258066" fill="#DF384C"/>
+<rect x="59.6133" y="19.5754" width="2.06453" height="5.24182" rx="1.03226" fill="#DF384C"/>
+<rect x="34.8398" y="11.8668" width="0.516132" height="6.9377" rx="0.258066" fill="#45B26B"/>
+<rect x="34.0645" y="13.7169" width="2.06453" height="3.23759" rx="1.03226" fill="#45B26B"/>
+<rect x="50.0645" y="7.55005" width="0.516132" height="6.9377" rx="0.258066" fill="#DF384C"/>
+<rect x="49.291" y="9.40027" width="2.06453" height="4.00845" rx="1.03226" fill="#DF384C"/>
+<rect x="24.2598" y="24.8174" width="0.516132" height="9.55861" rx="0.258066" fill="#DF384C"/>
+<rect x="23.4844" y="26.6672" width="2.06453" height="5.39599" rx="1.03226" fill="#DF384C"/>
+<rect x="29.4199" y="14.4878" width="0.516132" height="11.2545" rx="0.258066" fill="#45B26B"/>
+<rect x="28.6465" y="16.4919" width="2.06453" height="7.86273" rx="1.03226" fill="#45B26B"/>
+<rect x="55.4844" y="11.2502" width="0.516132" height="11.2545" rx="0.258066" fill="#45B26B"/>
+<rect x="54.7109" y="13.2544" width="2.06453" height="7.86273" rx="1.03226" fill="#45B26B"/>
+</svg>

+ 57 - 0
src/composables/useBodyScrollLock.js

@@ -0,0 +1,57 @@
+/*// src/composables/useBodyScrollLock.js*/
+import { watch, onUnmounted, onActivated, onDeactivated } from 'vue'
+
+// 全局变量
+let lockCount = 0
+let scrollTop = 0 // 记录页面滚动到了哪里
+
+export function useBodyScrollLock(visibleRef) {
+
+  const lock = () => {
+    lockCount++
+    if (lockCount === 1) {
+      // 1. 记住当前滚到了哪里
+      scrollTop = window.scrollY || document.documentElement.scrollTop
+
+      // 2. 把 body 变成 fixed,强制钉在当前位置
+      // 这样浏览器想滚也滚不动了,物理锁死
+      document.body.style.position = 'fixed'
+      document.body.style.top = `-${scrollTop}px`
+      document.body.style.width = '100%'
+      document.body.style.overflow = 'hidden'
+    }
+  }
+
+  const unlock = () => {
+    if (lockCount > 0) lockCount--
+    if (lockCount === 0) {
+      // 3. 恢复原样
+      document.body.style.position = ''
+      document.body.style.top = ''
+      document.body.style.width = ''
+      document.body.style.overflow = ''
+
+      // 4. 瞬间滚回刚才的位置(无缝衔接)
+      window.scrollTo(0, scrollTop)
+    }
+  }
+
+  // 监听变化
+  watch(visibleRef, (val) => {
+    if (val) lock()
+    else unlock()
+  }, { immediate: true })
+
+  // 清理逻辑
+  onUnmounted(() => {
+    if (visibleRef.value) unlock()
+  })
+
+  onActivated(() => {
+    if (visibleRef.value) lock()
+  })
+
+  onDeactivated(() => {
+    if (visibleRef.value) unlock()
+  })
+}

+ 28 - 5
src/router/index.js

@@ -17,6 +17,10 @@ import LoanRules from "../views/index/loan/Rules.vue";
 import UserLoanIndex from "../views/user/loan/Index.vue";
 import FootprintIndex from "../views/bitcoin/Index.vue";
 import Calculator from '../views/bitcoin/Calculator.vue' // 新建的计算器页面
+import CommonFunctionsPopup from '@/views/bitcoin/CommonFunctionsPopup/CommonFunctionsPopup.vue' // 子组件路径
+import TradeSettings from '@/views/bitcoin/CommonFunctionsPopup/GeneralLevel2/TradeSettings.vue'// 新建
+import TradeRules from '@/views/bitcoin/CommonFunctionsPopup/GeneralLevel2/TradeRules.vue'       // 新建
+
 
 
 const routes = [
@@ -29,11 +33,30 @@ const routes = [
         path: "/bitcoin",
         name: "bitcoin",
         component: FootprintIndex,
-    },
-    {
-      path: '/calculator',
-      name: 'calculator',
-      component: Calculator
+        children: [{
+            path: 'functions', // 访问地址变为 /bitcoin/functions
+            name: 'BitcoinFunctions',
+            component: CommonFunctionsPopup,
+            meta: { showTabbar: false }, // 如果有底部导航栏,可能需要隐藏
+            children: [
+                {
+                  path: 'settings', // 全屏页
+                  name: 'TradeSettings',
+                  component: TradeSettings
+                },
+                {
+                  path: 'rules',   // 二级弹窗
+                  name: 'TradeRules',
+                  component: TradeRules
+                },
+                {
+                  path: 'calculator',
+                  name: 'calculator',
+                  component: Calculator
+                },
+            ]
+            },
+        ]
     },
     {
         path: "/applyPermission",

+ 8 - 3
src/views/HomeIndex.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="index">
     <component :is="currentComponent" />
-    <div class="footer-tabbar">
+    <div class=" footer-tabbar">
       <div
         class="tabbar-item"
         v-for="(item, index) in tabbarList"
@@ -19,12 +19,17 @@
   </div>
 </template>
 <script setup>
+  // @click="$router.push({ name: 'bitcoin' })"
   import { ref, computed } from "vue";
   import CalendarIndex from "./index/Index.vue";
   import NoticeIndex from "./market/Index.vue";
   import FootprintIndex from "./bitcoin/Index.vue";
   import AiIndex from "./asset/Index.vue";
   import UserIndex from "./user/Index.vue";
+   import { useRouter } from "vue-router";
+
+  const router = useRouter();
+
 
   const current = ref("index");
   const componentsMap = {
@@ -71,13 +76,13 @@
   ];
 
   const tabbarChange = (key) => {
-    current.value = key;
+      current.value=key
+
   };
 </script>
 <style lang="less" scoped>
   .index {
     width: 100%;
-
     .footer-tabbar {
       position: fixed;
       left: 0;

+ 21 - 5
src/views/bitcoin/Calculator.vue

@@ -56,7 +56,7 @@
 
     <div v-if="currentTab === 5" class="funding-rate-display">
       <div class="rate-value">0.0030%</div>
-      <div class="rate-label">当前资金费率
+      <div class="rate-label" @click="showModal=true">当前资金费率
         <VanIcon name="question-o" />
       </div>
     </div>
@@ -229,14 +229,24 @@
       <button class="btn-reset" @click="reset">重置</button>
       <button class="btn-calc" :style="{ background: themeColor }" @click="calculate">计算</button>
     </div>
+    <div>
+      <ProfitAndLossPrompt
+          v-model:visible="showModal"
+      ></ProfitAndLossPrompt>
+    </div>
   </div>
 </template>
 
 <script setup>
 import { Icon as VanIcon } from 'vant';
-import { ref, computed } from 'vue';
+import {ref, computed, defineAsyncComponent} from 'vue';
 // 请确保路径正确,指向刚才那个更新过的子组件
-import LeverageSlider from './calculator/LeverageSlider.vue';
+const LeverageSlider = defineAsyncComponent(()=>import('./calculator/LeverageSlider.vue'));
+const ProfitAndLossPrompt = defineAsyncComponent(() => import('./calculator/FundingRateReminder.vue'));
+
+//资金费率提示
+const showModal = ref(false);
+
 
 // 状态定义
 const tabs = ['收益', '目标价格', '强平价格', '可开', '开仓均价', '资金费用'];
@@ -337,13 +347,20 @@ const reset = () => {
   min-height: 100vh;
   padding-bottom: 90px;
   color: #333;
+  z-index: 200;
+  position: fixed;       /* 固定定位 */
+  top: 0;
+  left: 0;
+  display: flex;
+  flex-direction: column;
+  width: 100%;
 }
 
 /* 导航 & 头部 */
 .nav-bar { display: flex; justify-content: space-between; align-items: center;
   height: 44px; padding: 0 16px; }
 .nav-left { font-size: 24px; cursor: pointer; }
-.nav-title { font-size: 17px; font-weight: 600; }
+.nav-title { font-size: 18px; font-weight: 600; }
 .nav-right { width: 20px; }
 
 .header-info {
@@ -407,7 +424,6 @@ const reset = () => {
   display: flex;
   align-items: center;
   background: #f7f8fa;
-  border: 1px solid #2979ff; /* 蓝色边框 */
   border-radius: 6px;
   height: 44px;
   margin-bottom: 10px;

+ 168 - 0
src/views/bitcoin/CommonFunctionsPopup/CommonFunctionsPopup.vue

@@ -0,0 +1,168 @@
+<template>
+  <Teleport to="body">
+    <Transition name="fade">
+      <div v-if="visible" class="modal-mask" @click="handleClose"></div>
+    </Transition>
+
+    <Transition name="slide-up">
+      <div v-if="visible" class="modal-panel" @click.stop >
+
+        <div class="panel-handle-wrap">
+          <div class="panel-handle"></div>
+        </div>
+
+        <div class="panel-title">常用功能</div>
+
+        <div class="grid-container">
+          <div
+            v-for="(item, index) in menuItems"
+            :key="index"
+            class="grid-item"
+            @click="handleItemClick(item)"
+          >
+            <div class="icon-box">
+              <img
+                :src="getIconPath(item.icon)"
+                class="grid-icon-img"
+                loading="lazy"
+              />
+            </div>
+            <span class="label">{{ item.name }}</span>
+          </div>
+        </div>
+
+        <div class="safe-area-bottom"></div>
+      </div>
+    </Transition>
+    <router-view></router-view>
+<!--    <router-view v-slot="{ Component }">-->
+<!--      <transition name="fade">-->
+<!--        <component :is="Component" />-->
+<!--      </transition>-->
+<!--    </router-view>-->
+  </Teleport>
+</template>
+
+<script setup>
+import { ref, onMounted, watch, onUnmounted } from 'vue' // 引入 watch 和 onUnmounted
+import { useRouter,useRoute } from 'vue-router'
+
+const route = useRoute() // 获取当前路由信息
+const router = useRouter()
+// 定义一个内部变量控制显示,用于触发 Transition 动画
+const visible = ref(false)
+const lockScroll = () => {
+  // 强制隐藏 body 的滚动条
+  document.body.style.overflow = 'hidden'
+  // 如果是 iOS,有些情况需要锁 html
+  document.documentElement.style.overflow = 'hidden'
+}
+
+const unlockScroll = () => {
+  // 恢复滚动
+  document.body.style.overflow = ''
+  document.documentElement.style.overflow = ''
+}
+
+// 监听弹窗显示状态
+watch(visible, (newVal) => {
+  if (newVal) {
+    lockScroll() // 弹窗开 -> 锁死
+  } else {
+    unlockScroll() // 弹窗关 -> 解锁
+  }
+})
+
+// 1. 进场动画:组件挂载后,立即设为 true
+onMounted(() => {
+  if (route.name === 'BitcoinFunctions') {
+    visible.value = true
+  }
+})
+
+// 2. 离场逻辑:先关动画,等动画播完再路由回退
+const handleClose = () => {
+  visible.value = false
+  // 300ms 对应 CSS 中的 transition 时间
+  setTimeout(() => {
+    router.back()
+    // router.push({ name: 'bitcoin' })
+  }, 300)
+}
+
+const handleItemClick = (item) => {
+  console.log('选中:', item.name)
+  if (item.name === '交易设置') {
+    router.push({ name: 'TradeSettings' })
+  } else if (item.name === '交易规则') {
+    router.push({ name: 'TradeRules' })
+
+  } else if (item.name === '计算器') {
+    router.push({ name: 'calculator' })
+  } else {
+    console.log('点击了其他:', item.name)
+  }
+  // 可以在这里处理业务逻辑,然后关闭
+  // handleClose()
+}
+
+// Webpack 图片加载逻辑
+const getIconPath = (iconName) => {
+  try {
+    return require(`../../../assets/icon/bitcoin/${iconName}`)
+  } catch (e) {
+    return ''
+  }
+}
+
+const menuItems = [
+  { name: '交易设置', icon: 'shezhi.svg' },
+  { name: '交易记录', icon: 'jilu.svg' },
+  { name: '计算器', icon: 'jisuan.svg' },
+  { name: '费率', icon: 'feilv.svg' },
+  { name: '资金划转', icon: 'huazhuan.svg' },
+  { name: 'OTC交易', icon: 'jiaoyi.svg' },
+  { name: '交易规则', icon: 'guizhe.svg' },
+]
+</script>
+
+<style scoped>
+
+/* 样式完全保持不变 */
+.modal-mask {
+  position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;
+  background-color: rgba(0, 0, 0, 0.5); z-index: 199;
+}
+.modal-panel {
+  position: fixed; bottom: 0; left: 50%; transform: translateX(-50%);
+  width: 100%; max-width: 600px; background: white;
+  border-radius: 16px 16px 0 0; padding: 10px 0 0 0; z-index: 199;
+}
+.panel-handle-wrap { display: flex; justify-content: center; padding-bottom: 20px; }
+.panel-handle { width: 36px; height: 4px; background: #E0E0E0; border-radius: 2px; }
+.panel-title { font-size: 18px; font-weight: bold; color: #333; padding: 0 20px 20px 20px; }
+.safe-area-bottom { height: 30px; }
+
+/* 动画 */
+.fade-enter-active, .fade-leave-active { transition: opacity 0.3s ease; }
+.fade-enter-from, .fade-leave-to { opacity: 0; }
+.slide-up-enter-active, .slide-up-leave-active { transition: transform 0.3s ease; }
+.slide-up-enter-from, .slide-up-leave-to { transform: translate(-50%, 100%); }
+
+/* Grid */
+.grid-container {
+  display: grid; grid-template-columns: repeat(4, 1fr);
+  row-gap: 25px; padding: 0 10px 20px 10px;
+}
+.grid-item {
+  display: flex; flex-direction: column; align-items: center; cursor: pointer;
+}
+.icon-box {
+  width: 40px; height: 40px; display: flex; align-items: center; justify-content: center;
+  margin-bottom: 8px;
+}
+.grid-icon-img {
+  width: 100%; height: 100%; object-fit: contain; display: block;
+}
+.label { font-size: 12px; color: #666; text-align: center; }
+</style>

+ 110 - 0
src/views/bitcoin/CommonFunctionsPopup/GeneralLevel2/TradeRules.vue

@@ -0,0 +1,110 @@
+<template>
+  <Teleport to="body">
+    <div class="mask-level-2" @click="$router.back()"></div>
+    
+    <div class="popup-level-2 slide-up-animation">
+      <div class="handle-bar-wrap">
+        <div class="handle-bar"></div>
+      </div>
+      
+      <div class="popup-title">交易规则</div>
+
+      <div class="rules-content">
+        <div class="rule-row border-bottom">
+          <span class="label">最小价格波动</span>
+          <span class="value">0.01 USDT</span>
+        </div>
+        <div class="rule-row spacer-bottom">
+          <span class="label">价格倍数</span>
+          <span class="value">0.2 - 5</span>
+        </div>
+
+        <div class="rule-row">
+          <span class="label">订单规模</span>
+          <span class="value">5 - 9000000 USDT</span>
+        </div>
+        <div class="rule-row">
+          <span class="label">交易数量</span>
+          <span class="value">00001 - 9000 BTC</span>
+        </div>
+        <div class="rule-row">
+          <span class="label">最小数量波动</span>
+          <span class="value">0.00001 BTC</span>
+        </div>
+        <div class="rule-row">
+          <span class="label">市价单单笔最大数量</span>
+          <span class="value">75.45007539 BTC</span>
+        </div>
+        <div class="rule-row">
+          <span class="label">限价单单笔挂单数量</span>
+          <span class="value">200</span>
+        </div>
+        <div class="rule-row">
+          <span class="label">最大条件单挂单数量</span>
+          <span class="value">5</span>
+        </div>
+      </div>
+    </div>
+  </Teleport>
+</template>
+
+<style scoped>
+/* 二级遮罩:稍微深一点,或者透明,看你想不想要双重遮罩效果 */
+.mask-level-2 {
+  position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;
+  background: rgba(0,0,0,0.2); 
+  z-index: 2001; /* 比一级高 */
+}
+
+.popup-level-2 {
+  position: fixed; bottom: 0; left: 0; width: 100%;
+  background: white;
+  border-radius: 16px 16px 0 0;
+  z-index: 2002; /* 最高层级 */
+  padding-bottom: calc(20px + env(safe-area-inset-bottom));
+  max-height: 85vh;
+  overflow-y: auto;
+}
+
+.handle-bar-wrap {
+  display: flex; justify-content: center; padding: 10px 0;
+}
+.handle-bar {
+  width: 36px; height: 4px; background: #E0E0E0; border-radius: 2px;
+}
+
+.popup-title {
+  font-size: 18px; font-weight: bold; color: #333;
+  padding: 0 20px 20px 20px;
+}
+
+.rules-content {
+  padding: 0 20px;
+}
+
+.rule-row {
+  display: flex; justify-content: space-between;
+  margin-bottom: 16px;
+  font-size: 14px;
+}
+.label { color: #888; }
+.value { color: #333; font-weight: 500; text-align: right;}
+
+.border-bottom {
+  padding-bottom: 16px;
+  border-bottom: 1px solid #f5f5f5;
+  margin-bottom: 16px;
+}
+.spacer-bottom {
+  margin-bottom: 30px; /* 两组数据之间的间距 */
+}
+
+/* 简单的进场动画 */
+.slide-up-animation {
+  animation: slideUp 0.3s ease-out;
+}
+@keyframes slideUp {
+  from { transform: translateY(100%); }
+  to { transform: translateY(0); }
+}
+</style>

+ 511 - 0
src/views/bitcoin/CommonFunctionsPopup/GeneralLevel2/TradeSettings.vue

@@ -0,0 +1,511 @@
+<template>
+  <div class="settings-page">
+    <div class="nav-bar">
+      <div class="back-icon" @click="$router.back()">
+        <div>
+          <VanIcon size="18" name="arrow-left"/>
+        </div>
+      </div>
+      <div class="nav-title">交易设置</div>
+      <div class="nav-right"></div>
+    </div>
+
+    <div class="tabs-header">
+      <div
+        class="tab-item"
+        :class="{ active: currentTab === 'mode' }"
+        @click="currentTab = 'mode'"
+      >
+        交易模式
+        <div class="active-line" v-if="currentTab === 'mode'"></div>
+      </div>
+      <div
+        class="tab-item"
+        :class="{ active: currentTab === 'pref' }"
+        @click="currentTab = 'pref'"
+      >
+        偏好
+        <div class="active-line" v-if="currentTab === 'pref'"></div>
+      </div>
+      <div
+        class="tab-item"
+        :class="{ active: currentTab === 'ui' }"
+        @click="currentTab = 'ui'"
+      >
+        界面
+        <div class="active-line" v-if="currentTab === 'ui'"></div>
+      </div>
+    </div>
+
+    <div class="content-scroll">
+
+      <div v-if="currentTab === 'mode'" class="tab-content fade-in">
+        <div class="list-item">
+          <div class="item-label">账户模式</div>
+          <div class="item-value">合约模式</div>
+        </div>
+
+        <div class="list-item clickable">
+          <div class="item-label">仓位模式</div>
+          <div class="item-value" @click="showModal1 = true">{{selectedLabel1}}
+            <div style="margin: 3px 0 0 2px;">
+              <VanIcon size="16" name="arrow"/>
+            </div>
+          </div>
+        </div>
+
+        <div class="list-item">
+          <div class="item-label">合约交易单位</div>
+          <div class="item-value">数量</div>
+        </div>
+
+        <div class="list-item">
+          <div class="item-label">期权交易单位</div>
+          <div class="item-value">币</div>
+        </div>
+
+        <div class="list-item">
+          <div class="item-label">稳定币偏好</div>
+          <div class="item-value">USDT</div>
+        </div>
+      </div>
+
+      <div v-if="currentTab === 'pref'" class="tab-content fade-in">
+        <div class="list-item clickable">
+          <div class="item-label">交易通知</div>
+          <div class="item-value" @click="showNotifyPopup = true">
+            <div class="arrow1" style="margin: 3px 0 0 2px;">
+              <VanIcon size="16" name="arrow"/>
+            </div>
+<!--            <span class="arrow">›</span>-->
+          </div>
+        </div>
+
+        <div class="list-item">
+          <div class="item-label">点击K线带入价格</div>
+          <div class="switch-box" :class="{ active: prefState.klinePrice }" @click="toggle('klinePrice')">
+            <div class="switch-circle"></div>
+          </div>
+        </div>
+
+        <div class="list-item">
+          <div class="item-label">点击订单表带入数量</div>
+          <div class="switch-box" :class="{ active: prefState.orderBookQty }" @click="toggle('orderBookQty')">
+            <div class="switch-circle"></div>
+          </div>
+        </div>
+
+        <div class="group-title">二次下单确认</div>
+
+        <div class="list-item">
+          <div class="item-label">下单确认</div>
+          <div class="switch-box" :class="{ active: prefState.orderConfirm }" @click="toggle('orderConfirm')">
+            <div class="switch-circle"></div>
+          </div>
+        </div>
+
+        <div class="list-item">
+          <div class="item-label">限价单</div>
+          <div class="switch-box" :class="{ active: prefState.limitOrder }" @click="toggle('limitOrder')">
+            <div class="switch-circle"></div>
+          </div>
+        </div>
+
+        <div class="list-item">
+          <div class="item-label">市价单</div>
+          <div class="switch-box" :class="{ active: prefState.marketOrder }" @click="toggle('marketOrder')">
+            <div class="switch-circle"></div>
+          </div>
+        </div>
+
+        <div class="list-item">
+          <div class="item-label">限价止盈止损订单</div>
+          <div class="switch-box" :class="{ active: prefState.limitTPSL }" @click="toggle('limitTPSL')">
+            <div class="switch-circle"></div>
+          </div>
+        </div>
+
+        <div class="list-item">
+          <div class="item-label">市价止盈止损</div>
+          <div class="switch-box" :class="{ active: prefState.marketTPSL }" @click="toggle('marketTPSL')">
+            <div class="switch-circle"></div>
+          </div>
+        </div>
+
+        <div class="list-item">
+          <div class="item-label">杠杆交易自动借还款</div>
+          <div class="switch-box" :class="{ active: prefState.autoBorrow }" @click="toggle('autoBorrow')">
+            <div class="switch-circle"></div>
+          </div>
+        </div>
+      </div>
+
+      <div v-if="currentTab === 'ui'" class="tab-content fade-in">
+        <div class="list-item clickable">
+          <div class="item-label">主题模式</div>
+          <div class="item-value text-gray" @click="showThemeModal = true">{{ currentThemeLabel }}
+             <div class="arrow1" style="margin: 3px 0 0 2px;">
+              <VanIcon size="16" name="arrow"/>
+            </div>
+          </div>
+        </div>
+
+        <div class="list-item clickable">
+          <div class="item-label">颜色设置</div>
+          <div class="item-value text-gray" @click="showColorModal = true">
+            <span v-if="currentColor === 'green_up'">
+                 <span class="green">绿涨</span><span class="red">红跌</span> <span class="green">↑</span><span class="red">↓</span>
+            </span>
+              <span v-else>
+                 <span class="red">红涨</span><span class="green">绿跌</span> <span class="red">↑</span><span class="green">↓</span>
+              </span>
+             <div class="arrow1" style="margin: 3px 0 0 2px;">
+              <VanIcon size="16" name="arrow"/>
+            </div>
+          </div>
+        </div>
+
+        <div class="layout-section">
+          <div class="section-label">交易页K线</div>
+          <div class="layout-grid">
+
+            <div class="layout-card" :class="{ selected: uiState.klineLayout === 'pull' }" @click="uiState.klineLayout = 'pull'">
+              <div class="preview-box">
+                <img src="../../../../assets/icon/bitcoin/xialadaka.svg" alt="">
+<!--                <div class="draw-kline top-part">-->
+<!--                  <div class="draw-candle red"></div>-->
+<!--                  <div class="draw-candle green"></div>-->
+<!--                </div>-->
+<!--                <div class="draw-bar bottom-part">-->
+<!--                  <div class="draw-btn red"></div>-->
+<!--                  <div class="draw-btn green"></div>-->
+<!--                </div>-->
+              </div>
+              <div class="card-text">下拉打开</div>
+            </div>
+
+            <div class="layout-card" :class="{ selected: uiState.klineLayout === 'bottom' }" @click="uiState.klineLayout = 'bottom'">
+              <div class="preview-box">
+                <div class="draw-bar top-part compact">
+                  <div class="draw-btn red"></div>
+                  <div class="draw-btn green"></div>
+                </div>
+                <div class="draw-kline bottom-part">
+                  <div class="draw-candle red"></div>
+                  <div class="draw-candle green"></div>
+                </div>
+              </div>
+              <div class="card-text">底部显示</div>
+            </div>
+
+            <div class="layout-card" :class="{ selected: uiState.klineLayout === 'none' }" @click="uiState.klineLayout = 'none'">
+              <div class="preview-box row-mode">
+                 <div class="draw-col left">
+                   <div class="draw-btn red full"></div>
+                 </div>
+                 <div class="draw-col right"></div>
+              </div>
+              <div class="card-text">不显示</div>
+            </div>
+          </div>
+        </div>
+
+        <div class="layout-section">
+          <div class="section-label">下单布局</div>
+          <div class="layout-grid two-col">
+
+            <div class="layout-card" :class="{ selected: uiState.orderLayout === 'left' }" @click="uiState.orderLayout = 'left'">
+              <div class="preview-box row-mode">
+                <div class="draw-col left selected-border">
+                  <div class="draw-btn green full"></div>
+                  <div class="draw-btn red full"></div>
+                </div>
+                <div class="draw-col right"></div>
+              </div>
+              <div class="card-text">左侧下单</div>
+            </div>
+
+            <div class="layout-card" :class="{ selected: uiState.orderLayout === 'right' }" @click="uiState.orderLayout = 'right'">
+              <div class="preview-box row-mode">
+                <div class="draw-col left"></div>
+                <div class="draw-col right selected-border">
+                   <div class="draw-btn green full"></div>
+                   <div class="draw-btn red full"></div>
+                </div>
+              </div>
+              <div class="card-text">右侧下单</div>
+            </div>
+          </div>
+        </div>
+      </div>
+
+    </div>
+    <div>
+      <FundingOptions
+          v-model:visible="showModal1"
+          :selected-id="currentId1"
+          @confirm="handleConfirm1"
+      ></FundingOptions>
+    </div>
+
+    <SelectionSheet
+      v-model:visible="showThemeModal"
+      title="主题模式"
+      :options="themeOptions"
+      v-model="currentTheme"
+      @confirm="onThemeConfirm"
+    />
+
+    <SelectionSheet
+      v-model:visible="showColorModal"
+      title="颜色设置"
+      :options="colorOptions"
+      v-model="currentColor"
+      @confirm="onColorConfirm"
+    >
+      <template #option="{ item }">
+        <div v-if="item.value === 'green_up'">
+          <span class="green">绿涨</span><span class="red">红跌</span>
+          <span class="green icon-arrow">↑</span><span class="red icon-arrow">↓</span>
+        </div>
+        <div v-else>
+           <span class="red">红涨</span><span class="green">绿跌</span>
+           <span class="red icon-arrow">↑</span><span class="green icon-arrow">↓</span>
+        </div>
+      </template>
+    </SelectionSheet>
+    <div>
+      <TradeNotificationPopup v-model:visible="showNotifyPopup" />
+    </div>
+  </div>
+</template>
+
+<script setup>
+        import {ref, reactive,computed,} from 'vue'
+        import { Icon as VanIcon } from 'vant';
+        import FundingOptions from './components/PositionMode.vue'
+        import SelectionSheet from './components/SelectionSheet.vue' // 引入上面的组件
+        import TradeNotificationPopup from './components/TradeNotificationPopup.vue'
+        import TriggerValueSheet from './components/TriggerValueSheet.vue'
+        const showNotifyPopup = ref(false)
+                // --- 状态管理 ---
+        const showThemeModal = ref(false)
+        const showColorModal = ref(false)
+
+        const currentTheme = ref('system') // 默认值
+        const currentColor = ref('green_up') // 默认值
+
+        // --- 数据配置 ---
+        const themeOptions = [
+          { label: '跟随系统', value: 'system' },
+          { label: '日间模式', value: 'light' },
+          { label: '夜间模式', value: 'dark' },
+        ]
+
+        const colorOptions = [
+          { label: '绿涨红跌', value: 'green_up' },
+          { label: '红涨绿跌', value: 'red_up' },
+        ]
+
+        // --- 计算属性:用于在列表显示当前选中的文字 ---
+        const currentThemeLabel = computed(() => {
+          const item = themeOptions.find(opt => opt.value === currentTheme.value)
+          return item ? item.label : ''
+        })
+
+        // --- 回调函数 ---
+        const onThemeConfirm = (val) => {
+          console.log('主题已确认修改为:', val)
+          // 这里可以调用 API 或者修改全局 CSS 变量
+        }
+
+        const onColorConfirm = (val) => {
+          console.log('颜色已确认修改为:', val)
+          // 这里处理红绿涨跌的全局逻辑
+        }
+
+        /*账户模式*/
+        const showModal1 = ref(false);
+
+        // 定义状态用于存储
+        const currentId1 = ref(1); // 默认选中第一个
+        const selectedLabel1 = ref('单向持仓'); // 默认值
+        const selectedUnit1 = ref('%');      // 默认值
+
+        // 回调函数:子组件选中后触发
+        const handleConfirm1 = (item) => {
+          // console.log('子组件返回的对象:', item);
+
+          // 保存ID用于下次打开时回显高亮
+          currentId1.value = item.id;
+          // const a = item.label.slice(-2)
+
+          // 核心需求:在这里将值“拆开”为两个字符串
+          selectedLabel1.value = item.label.slice(0); // 字符串1: "涨跌幅"
+          selectedUnit1.value = item.unit;   // 字符串2: "%"
+        };
+
+        // 状态管理
+        const currentTab = ref('mode') // 默认显示偏好
+
+        // 偏好设置数据 (对应图1)
+        const prefState = reactive({
+          klinePrice: false,
+          orderBookQty: true, // 默认开启
+          orderConfirm: false,
+          limitOrder: false,
+          marketOrder: true,
+          limitTPSL: false,
+          marketTPSL: true,
+          autoBorrow: false
+        })
+
+        // 界面设置数据 (对应图3)
+        const uiState = reactive({
+          klineLayout: 'pull', // pull | bottom | none
+          orderLayout: 'left', // left | right
+        })
+
+      // 切换开关
+      const toggle = (key) => {
+        prefState[key] = !prefState[key]
+      }
+</script>
+
+<style scoped>
+
+/* 定义红绿颜色,用于颜色设置弹窗 */
+.green { color: #2EBD85; }
+.red { color: #E54755; }
+.icon-arrow { font-weight: bold; margin-left: 2px; }
+/* ================== 全局布局 ================== */
+.settings-page {
+  position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;
+  background-color: #fff; z-index: 2000;
+  display: flex; flex-direction: column;
+}
+
+.content-scroll {
+  flex: 1; overflow-y: auto; padding-bottom: 40px;
+}
+
+/* 简单的淡入动画 */
+.fade-in {
+  animation: fadeIn 0.3s ease;
+}
+@keyframes fadeIn {
+  from { opacity: 0; transform: translateY(5px); }
+  to { opacity: 1; transform: translateY(0); }
+}
+
+/* ================== 顶部导航 & Tab ================== */
+.nav-bar {
+  height: 44px; display: flex; align-items: center; justify-content: space-between; padding: 0 15px;
+  background: #fff;
+  margin-top: 12px;
+}
+.nav-title { font-size: 17px; font-weight: 600; color: #333; }
+
+.tabs-header {
+  display: flex; padding-left: 16px; margin-top: 5px;
+  border-bottom: 1px solid #f9f9f9;
+}
+.tab-item {
+  margin-right: 24px; font-size: 16px; color: #999;
+  position: relative; padding-bottom: 10px; cursor: pointer;
+  transition: all 0.2s;
+}
+.tab-item.active { color: #333; font-weight: bold; font-size: 19px; }
+.active-line {
+  position: absolute; bottom: 0; left: 50%; transform: translateX(-50%);
+  width: 18px; height: 3px; background-color: #333; border-radius: 2px;
+}
+
+/* ================== 列表通用样式 ================== */
+.tab-content { padding: 0 16px; }
+.list-item {
+  display: flex; justify-content: space-between; align-items: center;
+  height: 56px; font-size: 15px; color: #333;
+}
+.item-value { display: flex; align-items: center; }
+.text-gray { color: #999; font-size: 14px; }
+.arrow { color: #ccc; margin-left: 8px; font-size: 18px; font-family: serif; position: relative; top: -1px; }
+
+.group-title {
+  margin-top: 24px; margin-bottom: 8px; font-size: 14px; color: #333; font-weight: 600;
+}
+
+/* ================== 开关组件 (Switch) ================== */
+.switch-box {
+  width: 50px; height: 30px; border-radius: 15px; background-color: #F0F0F0;
+  position: relative; cursor: pointer; transition: background-color 0.3s;
+}
+.switch-circle {
+  width: 26px; height: 26px; background: white; border-radius: 50%;
+  position: absolute; top: 2px; left: 2px; transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
+  box-shadow: 0 2px 4px rgba(0,0,0,0.2);
+}
+/* 激活状态:使用你截图中的红色 */
+.switch-box.active { background-color: #E54755; }
+.switch-box.active .switch-circle { transform: translateX(20px); }
+
+/* ================== 界面布局选择 (CSS 绘图核心) ================== */
+.layout-section { margin-top: 30px; }
+.section-label { font-size: 14px; font-weight: 600; color: #333; margin-bottom: 12px; }
+
+.layout-grid { display: flex; gap: 12px; }
+.layout-grid.two-col { justify-content: flex-start; }
+
+.layout-card {
+  width: 100px; cursor: pointer;
+  display: flex; flex-direction: column; align-items: center;
+}
+.card-text { margin-top: 8px; font-size: 12px; color: #999; }
+.layout-card.selected .card-text { color: #E54755; }
+
+/* 预览盒子 */
+.preview-box {
+  width: 100%; height: 120px; border-radius: 8px; border: 1px solid #eee;
+  padding: 6px; background: #fff;
+  display: flex; flex-direction: column; justify-content: space-between;
+  box-sizing: border-box; overflow: hidden;
+}
+/* 选中状态:红框 */
+.layout-card.selected .preview-box { border: 1px solid #E54755; background: rgba(229, 71, 85, 0.02); }
+
+/* --- CSS 绘图元件 --- */
+
+/* 1. K线图部分 (灰色背景 + 红绿柱子) */
+.draw-kline {
+  background: #F5F7FA; border-radius: 4px; position: relative;
+  display: flex; align-items: center; justify-content: center; gap: 4px;
+}
+.draw-candle { width: 4px; border-radius: 1px; }
+.draw-candle.red { height: 60%; background: #E54755; margin-top: 10%; }
+.draw-candle.green { height: 40%; background: #2EBD85; margin-bottom: 10%; }
+
+/* 2. 操作区部分 (灰色背景 + 红绿按钮条) */
+.draw-bar {
+  background: #F5F7FA; border-radius: 4px; padding: 4px;
+  display: flex; flex-direction: column; justify-content: flex-end; gap: 3px;
+}
+.draw-btn { height: 4px; width: 60%; border-radius: 2px; }
+.draw-btn.red { background: #E54755; }
+.draw-btn.green { background: #2EBD85; width: 80%; }
+.draw-btn.full { width: 100%; height: 6px; margin-bottom: 4px; }
+
+/* 上下结构高度调整 */
+.top-part { height: 60%; }
+.bottom-part { height: 35%; }
+.compact { height: 45%; } /* 底部布局时中间部分变矮 */
+
+/* 3. 左右布局模式 */
+.row-mode { flex-direction: row; gap: 4px; height: 100%; }
+.draw-col { flex: 1; background: #F5F7FA; border-radius: 4px; height: 100%; display: flex; flex-direction: column; justify-content: flex-end; padding: 4px; box-sizing: border-box; }
+.selected-border { border: 1px solid #1989fa; } /* 模拟选中边框 */
+
+/* 细节微调 */
+.layout-card:hover .preview-box { border-color: #ccc; }
+.layout-card.selected:hover .preview-box { border-color: #E54755; }
+
+</style>

+ 171 - 0
src/views/bitcoin/CommonFunctionsPopup/GeneralLevel2/components/PositionMode.vue

@@ -0,0 +1,171 @@
+<template>
+  <Teleport to="body">
+    <div v-if="visible" class="modal-mask" @click="close">
+      <div class="modal-content" @click.stop>
+        <div class="handle-bar"></div>
+<!--        <div class="trigger fs18 fc333333 pf600">选择触发类型</div>-->
+        <div class="options-list">
+          <div
+            v-for="item in options"
+            :key="item.id"
+            class="option-card"
+            :class="{ 'active': selectedId === item.id }"
+            @click="handleSelect(item)"
+          >
+            <div class="card-header">
+              <span class="title">{{ item.label }}</span>
+              <div v-if="selectedId === item.id" class="check-icon">
+                <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor"
+                     stroke-width="3" fill="none" stroke-linecap="round" stroke-linejoin="round"
+                     class="css-i6dzq1"><polyline points="20 6 9 17 4 12"></polyline></svg>
+              </div>
+            </div>
+            <div class="description">
+              {{ item.desc }}
+            </div>
+
+          </div>
+        </div>
+        <div class="fs12 fc666666">该设置对所有交易品种生效。若存在持仓或挂单,则不支持调整仓位模式。</div>
+      </div>
+    </div>
+
+  </Teleport>
+</template>
+
+<script setup>
+import { defineProps, defineEmits ,toRef} from 'vue';
+import { useBodyScrollLock } from '@/composables/useBodyScrollLock' // 2. 引入 Hook
+
+// 接收父组件传来的 modelValue (控制显示) 和 当前选中的ID
+const props = defineProps({
+  visible: {
+    type: Boolean,
+    default: false
+  },
+  selectedId: {
+    type: [String, Number],
+    default: 1
+  }
+});
+useBodyScrollLock(toRef(props, 'visible'))
+const emit = defineEmits(['update:visible', 'confirm']);
+
+// 静态数据源 (保持不变)
+const options = [
+  {
+    id: 1,
+    label: '单向持仓',
+    desc: '一个交易品种仅可持有多或空单一方向的仓位。如果您已持有某一交易品种的仓位,买入或卖出时将增减原有仓位,' +
+          '而非开新的仓位。所有仓位共用保证金盈亏互抵可有效降低强平风险但强平时可能损失全部仓位'
+  },
+
+  {
+    id: 2,
+    label: '双向持仓' ,
+    desc: '一个交易品种可同时持有多和空两个方向\n' +
+          '的仓位。',
+  },
+];
+
+const close = () => {
+  emit('update:visible', false);
+};
+
+const handleSelect = (item) => {
+  // 1. 触发 confirm 事件,把整个对象带回去
+  emit('confirm', item);
+  // 2. 关闭弹窗
+  close();
+};
+</script>
+
+<style lang="less" scoped>
+.modal-mask {
+  position: fixed;
+  top: 0; left: 0; width: 100%; height: 100%;
+  background: rgba(0, 0, 0, 0.5);
+  display: flex;
+  align-items: flex-end; /* 底部对齐 */
+  z-index: 2001; /* 极高的 Z-index */
+}
+
+.modal-content {
+  width: 100%;
+  background: white;
+  border-radius: 16px 16px 0 0;
+  padding: 20px;
+  padding-bottom: 40px;
+  max-height: 80vh;
+  overflow-y: auto;
+  animation: slideUp 0.3s ease-out;
+  .trigger{margin-bottom: 15px}
+}
+
+.handle-bar {
+  width: 40px;
+  height: 4px;
+  background: #E0E0E0;
+  border-radius: 2px;
+  margin: 0 auto 20px auto;
+}
+
+.option-card {
+  border: 1px solid #E0E0E0;
+  border-radius: 12px;
+  padding: 16px;
+  margin-bottom: 16px;
+  cursor: pointer;
+  transition: all 0.2s;
+  position: relative;
+}
+
+.card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 8px;
+}
+
+.title {
+  font-size: 16px;
+  font-weight: bold;
+  color: #333;
+}
+
+.description {
+  font-size: 13px;
+  color: #666;
+  line-height: 1.5;
+}
+
+/* 选中状态样式 */
+.option-card.active {
+  border-color: #F53F3F; /* 红色边框 */
+  background-color: #FFF9F9; /* 极淡的红色背景可选 */
+}
+
+.option-card.active .title {
+  color: #D92828; /* 红色标题 */
+}
+
+.option-card.active .description {
+  color: #D92828; /* 红色描述 */
+}
+
+.check-icon {
+  width: 24px;
+  height: 24px;
+  background: #D92828;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  color: white;
+}
+
+@keyframes slideUp {
+  from { transform: translateY(100%); }
+  to { transform: translateY(0); }
+}
+</style>

+ 132 - 0
src/views/bitcoin/CommonFunctionsPopup/GeneralLevel2/components/SelectionSheet.vue

@@ -0,0 +1,132 @@
+<template>
+  <Teleport to="body">
+    <Transition name="fade">
+      <div v-if="visible" class="modal-mask" @click="close"></div>
+    </Transition>
+
+    <Transition name="slide-up">
+      <div v-if="visible" class="modal-panel" @click.stop>
+        <div class="handle-bar-wrap">
+          <div class="handle-bar"></div>
+        </div>
+
+        <div class="panel-title">{{ title }}</div>
+
+        <div class="options-list">
+          <div 
+            v-for="item in options" 
+            :key="item.value" 
+            class="option-item"
+            @click="selectItem(item.value)"
+          >
+            <div class="option-label" :class="{ active: tempValue === item.value }">
+              <slot name="option" :item="item">
+                {{ item.label }}
+              </slot>
+            </div>
+
+            <div v-if="tempValue === item.value" class="check-icon">
+              <svg viewBox="0 0 24 24" width="20" height="20" fill="none">
+                <circle cx="12" cy="12" r="10" fill="#E54755"/>
+                <path d="M7 12L10 15L17 8" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+              </svg>
+            </div>
+          </div>
+        </div>
+
+        <div class="btn-area">
+          <button class="confirm-btn" @click="handleConfirm">确认</button>
+        </div>
+        
+        <div class="safe-area-bottom"></div>
+      </div>
+    </Transition>
+  </Teleport>
+</template>
+
+<script setup>
+import { ref, watch } from 'vue'
+
+const props = defineProps({
+  visible: Boolean,
+  title: String,
+  options: Array,   // 格式: [{ label: 'xxx', value: 'xxx' }]
+  modelValue: [String, Number] // 当前选中的值
+})
+
+const emit = defineEmits(['update:visible', 'update:modelValue', 'confirm'])
+
+// 临时选中的值(还没点确认前,不改变父组件的值)
+const tempValue = ref(props.modelValue)
+
+// 监听弹窗打开,每次打开时重置 tempValue 为父组件当前的值
+watch(() => props.visible, (val) => {
+  if (val) {
+    tempValue.value = props.modelValue
+  }
+})
+
+const selectItem = (val) => {
+  tempValue.value = val
+}
+
+const handleConfirm = () => {
+  emit('update:modelValue', tempValue.value) // 更新父组件 v-model
+  emit('confirm', tempValue.value) // 触发确认事件,带回值
+  close()
+}
+
+const close = () => {
+  emit('update:visible', false)
+}
+</script>
+
+<style scoped>
+.modal-mask {
+  position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;
+  background-color: rgba(0, 0, 0, 0.5); z-index: 3000;
+}
+.modal-panel {
+  position: fixed; bottom: 0; left: 0; width: 100%;
+  background: white; border-radius: 16px 16px 0 0;
+  z-index: 3001; padding-bottom: 20px;
+}
+
+.handle-bar-wrap { display: flex; justify-content: center; padding-top: 10px; }
+.handle-bar { width: 36px; height: 4px; background: #E0E0E0; border-radius: 2px; }
+
+.panel-title {
+  font-size: 18px; font-weight: bold; color: #333;
+  padding: 20px 16px;
+}
+
+.options-list { padding: 0 16px; margin-bottom: 20px; }
+
+.option-item {
+  display: flex; justify-content: space-between; align-items: center;
+  height: 54px; cursor: pointer;
+}
+
+.option-label {
+  font-size: 16px; color: #333; flex: 1;
+  /* 选中时文字加粗 */
+}
+.option-label.active { font-weight: 600; }
+
+.btn-area { padding: 0 16px; }
+.confirm-btn {
+  width: 100%; height: 48px;
+  background-color: #E54755; color: white;
+  border: none; border-radius: 24px;
+  font-size: 16px; font-weight: 600;
+}
+.confirm-btn:active { opacity: 0.9; }
+
+.safe-area-bottom { height: env(safe-area-inset-bottom); }
+
+/* 动画 */
+.fade-enter-active, .fade-leave-active { transition: opacity 0.3s; }
+.fade-enter-from, .fade-leave-to { opacity: 0; }
+.slide-up-enter-active, .slide-up-leave-active { transition: transform 0.3s; }
+.slide-up-enter-from, .slide-up-leave-to { transform: translateY(100%); }
+</style>

+ 194 - 0
src/views/bitcoin/CommonFunctionsPopup/GeneralLevel2/components/TradeNotificationPopup.vue

@@ -0,0 +1,194 @@
+<template>
+  <Teleport to="body">
+    <Transition name="fade">
+      <div v-if="visible" class="modal-mask" @click="close"></div>
+    </Transition>
+
+    <Transition name="slide-up">
+      <div v-if="visible" class="modal-panel" @click.stop>
+
+        <div class="handle-bar-wrap">
+          <div class="handle-bar"></div>
+        </div>
+
+        <div class="content-list">
+
+          <div class="notify-item">
+            <div class="item-header">
+              <span class="item-title">止盈止损通知</span>
+              <div class="switch-box" :class="{ active: state.tpsl }" @click="toggle('tpsl')">
+                <div class="switch-circle"></div>
+              </div>
+            </div>
+            <div class="item-desc">当止盈止损委托触发或成交时,您将收到通知</div>
+          </div>
+
+          <div class="notify-item">
+            <div class="item-header">
+              <span class="item-title">市价委托成交通知</span>
+              <div class="switch-box" :class="{ active: state.market }" @click="toggle('market')">
+                <div class="switch-circle"></div>
+              </div>
+            </div>
+            <div class="item-desc">当市价委托完全成交时,您将收到通知。极端情况下,通知可能会延迟</div>
+          </div>
+
+          <div class="notify-item">
+            <div class="item-header">
+              <span class="item-title">限价委托成交通知</span>
+              <div class="switch-box" :class="{ active: state.limit }" @click="toggle('limit')">
+                <div class="switch-circle"></div>
+              </div>
+            </div>
+            <div class="item-desc">当限价委托完全成交时,您将收到通知</div>
+          </div>
+
+          <div class="notify-item">
+            <div class="item-header">
+              <span class="item-title">只减仓改撤单通知</span>
+              <div class="switch-box" :class="{ active: state.reduce }" @click="toggle('reduce')">
+                <div class="switch-circle"></div>
+              </div>
+            </div>
+            <div class="item-desc">当只减仓委托被系统修改或撤单时,您将收到通知</div>
+          </div>
+
+          <div class="notify-item">
+            <div class="item-header">
+              <span class="item-title">资金费率通知</span>
+              <div class="switch-box" :class="{ active: state.funding }" @click="toggle('funding')">
+                <div class="switch-circle"></div>
+              </div>
+            </div>
+            <div class="item-desc">
+              当预估资金费率达到+/-{{ triggerValue }}%时,您将在结算前的30分钟收到通知
+              <span class="red-link" @click="showTriggerSheet = true">调整触发值</span>
+            </div>
+          </div>
+
+          <div class="safe-area-bottom"></div>
+        </div>
+
+        <TriggerValueSheet
+          v-model:visible="showTriggerSheet"
+          v-model="triggerValue"
+          @confirm="onTriggerConfirm"
+        />
+
+      </div>
+    </Transition>
+  </Teleport>
+</template>
+
+<script setup>
+import { ref, reactive } from 'vue'
+import TriggerValueSheet from './TriggerValueSheet.vue'
+
+const props = defineProps({
+  visible: Boolean
+})
+
+const emit = defineEmits(['update:visible'])
+
+const close = () => {
+  emit('update:visible', false)
+}
+
+// 内部开关状态
+const state = reactive({
+  tpsl: false,
+  market: true,
+  limit: false,
+  reduce: true,
+  funding: true
+})
+
+const toggle = (key) => {
+  state[key] = !state[key]
+}
+
+// 资金费率弹窗逻辑
+const showTriggerSheet = ref(false)
+const triggerValue = ref('0.375')
+
+const onTriggerConfirm = (val) => {
+  triggerValue.value = val
+  // 这里可以调用 API 保存设置
+}
+</script>
+
+<style scoped>
+/* ================== 弹窗结构 ================== */
+.modal-mask {
+  position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;
+  background-color: rgba(0, 0, 0, 0.5); z-index: 2100; /* 层级高于设置页(2000) */
+}
+
+.modal-panel {
+  position: fixed; bottom: 0; left: 0; width: 100%;
+  max-height: 85vh; /* 限制最大高度,大概占屏幕 85% */
+  background: white; border-radius: 16px 16px 0 0;
+  z-index: 2101; /* 比遮罩高 */
+  display: flex; flex-direction: column;
+}
+
+/* 顶部灰色把手 */
+.handle-bar-wrap {
+  display: flex; justify-content: center; padding: 10px 0;
+}
+.handle-bar {
+  width: 36px; height: 4px; background: #E0E0E0; border-radius: 2px;
+}
+
+/* 内容列表区域 */
+.content-list {
+  flex: 1; overflow-y: auto; padding: 10px 16px 20px 16px;
+}
+
+/* ================== 列表项样式 ================== */
+.notify-item {
+  margin-bottom: 24px;
+}
+
+.item-header {
+  display: flex; justify-content: space-between; align-items: center;
+  margin-bottom: 8px;
+}
+
+.item-title {
+  font-size: 16px; font-weight: 600; color: #333;
+}
+
+.item-desc {
+  font-size: 13px; color: #888; line-height: 1.5;
+  text-align: justify; /* 两端对齐更整齐 */
+}
+
+/* 红色链接 */
+.red-link {
+  color: #DF384C; margin-left: 5px; cursor: pointer;
+}
+
+/* 底部安全区适配 */
+.safe-area-bottom { height: env(safe-area-inset-bottom); }
+
+/* ================== 开关组件 (红色) ================== */
+.switch-box {
+  width: 50px; height: 30px; border-radius: 15px; background-color: #F5F5F5;
+  position: relative; cursor: pointer; transition: background-color 0.3s;
+}
+.switch-circle {
+  width: 26px; height: 26px; background: white; border-radius: 50%;
+  position: absolute; top: 2px; left: 2px; transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
+  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+}
+.switch-box.active { background-color: #DF384C; } /* 红色激活态 */
+.switch-box.active .switch-circle { transform: translateX(20px); }
+
+/* ================== 动画效果 ================== */
+.fade-enter-active, .fade-leave-active { transition: opacity 0.3s; }
+.fade-enter-from, .fade-leave-to { opacity: 0; }
+
+.slide-up-enter-active, .slide-up-leave-active { transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.5, 1); }
+.slide-up-enter-from, .slide-up-leave-to { transform: translateY(100%); }
+</style>

+ 93 - 0
src/views/bitcoin/CommonFunctionsPopup/GeneralLevel2/components/TriggerValueSheet.vue

@@ -0,0 +1,93 @@
+<template>
+  <Teleport to="body">
+    <Transition name="fade">
+      <div v-if="visible" class="modal-mask" @click="close"></div>
+    </Transition>
+
+    <Transition name="slide-up">
+      <div v-if="visible" class="modal-panel" @click.stop>
+        <div class="handle-bar-wrap"><div class="handle-bar"></div></div>
+
+        <div class="panel-title">资金费率触发值设置</div>
+
+        <div class="panel-content">
+          <div class="input-wrapper">
+            <input
+              type="text"
+              class="custom-input"
+              v-model="localValue"
+              placeholder="0.000"
+            />
+            <span class="suffix">触发值(%)</span>
+          </div>
+
+          <p class="desc-text">
+            资金费率为正,表做多仓位;资金费率为负,表做空仓位。您将在对应方向仓位的资金费率达到触发值时收到通知。
+          </p>
+
+          <button class="confirm-btn" @click="handleConfirm">确认</button>
+        </div>
+        <div class="safe-area-bottom"></div>
+      </div>
+    </Transition>
+  </Teleport>
+</template>
+
+<script setup>
+import { ref, watch } from 'vue'
+
+const props = defineProps({
+  visible: Boolean,
+  modelValue: [String, Number]
+})
+const emit = defineEmits(['update:visible', 'update:modelValue', 'confirm'])
+
+const localValue = ref(props.modelValue)
+
+watch(() => props.visible, (val) => {
+  if (val) localValue.value = props.modelValue
+})
+
+const close = () => emit('update:visible', false)
+const handleConfirm = () => {
+  emit('update:modelValue', localValue.value)
+  emit('confirm', localValue.value)
+  close()
+}
+</script>
+
+<style scoped>
+.modal-mask {
+  position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;
+  background: rgba(0,0,0,0.5); z-index: 3000; /* 最高层级 */
+}
+.modal-panel {
+  position: fixed; bottom: 0; left: 0; width: 100%;
+  background: white; border-radius: 16px 16px 0 0;
+  z-index: 3001; padding-bottom: 20px;
+}
+.handle-bar-wrap { display: flex; justify-content: center; padding-top: 10px; }
+.handle-bar { width: 36px; height: 4px; background: #E0E0E0; border-radius: 2px; }
+.panel-title { font-size: 18px; font-weight: bold; color: #333; padding: 20px 16px 10px; }
+.panel-content { padding: 0 16px; }
+
+.input-wrapper {
+  background: #F7F8FA; border-radius: 4px; height: 48px;
+  display: flex; align-items: center; padding: 0 12px; margin-bottom: 12px;
+}
+.custom-input { flex: 1; background: transparent; border: none; font-size: 16px; outline: none; }
+.suffix { color: #999; font-size: 14px; }
+.desc-text { font-size: 13px; color: #888; line-height: 1.5; margin-bottom: 30px; text-align: justify; }
+
+.confirm-btn {
+  width: 100%; height: 48px; background: #DF384C; color: white;
+  border: none; border-radius: 24px; font-size: 16px; font-weight: 600;
+}
+.safe-area-bottom { height: env(safe-area-inset-bottom); }
+
+/* 动画 */
+.fade-enter-active, .fade-leave-active { transition: opacity 0.3s; }
+.fade-enter-from, .fade-leave-to { opacity: 0; }
+.slide-up-enter-active, .slide-up-leave-active { transition: transform 0.3s; }
+.slide-up-enter-from, .slide-up-leave-to { transform: translateY(100%); }
+</style>

+ 29 - 14
src/views/bitcoin/Index.vue

@@ -26,9 +26,9 @@
       </div>
       <div class="menu-right fc333333">
 
-        <img src="../../assets/icon/bitcoin/jisuan.svg" alt="" @click="$router.push('/calculator')">
+        <img src="../../assets/icon/bitcoin/jisuanqi.svg" alt="" @click="$router.push({name: 'calculator'})">
         <img src="../../assets/icon/bitcoin/hangqing.svg" alt="">
-        <img src="../../assets/icon/bitcoin/den.svg" alt="">
+        <img src="../../assets/icon/bitcoin/den.svg" alt="" @click="$router.push({ name: 'BitcoinFunctions' })">
       </div>
     </div>
     <div class="menu-bottom">
@@ -304,13 +304,21 @@
     <!--          @confirm="handleGoRecharge"-->
     <!--      ></InsufficientBalance>-->
     <!--    </div>-->
-
   </div>
-
+<!--  <router-view v-slot="{ Component }">-->
+<!--    <keep-alive :include="['BitcoinFunctions', 'Calculator']">-->
+<!--      <component :is="Component" />-->
+<!--    </keep-alive>-->
+<!--  </router-view>-->
+  <router-view></router-view>
 </template>
 <script setup>
         import {Checkbox as VanCheckbox, Icon as VanIcon} from 'vant';
         import {computed, defineAsyncComponent, ref} from 'vue';
+        import { useRouter } from 'vue-router'
+
+        const router = useRouter()
+
         // 懒加载多个组件
         const priceLimit = defineAsyncComponent(() => import("./components/priceLimit.vue"));
         const assetlessState = defineAsyncComponent(() => import("./components/assetlessState.vue"));
@@ -324,14 +332,20 @@
         const LeveragePopup = defineAsyncComponent(() => import('./components/LeveragePopup.vue'));
         const OrderConfirmPopup = defineAsyncComponent(() => import('./components/OrderConfirmPopup.vue'));
         const OrderTimeSheet = defineAsyncComponent(() => import('./components/OrderTimeSheet.vue'));
+        //全仓逐仓组件
         const MarginInfoSheet = defineAsyncComponent(() => import('./components/MarginInfoSheet.vue'));
         const FundingOptions = defineAsyncComponent(() => import('./components/FundingOptions.vue'));
         const OrderType = defineAsyncComponent(() => import('./components/OrderType.vue'));
         //余额不足提示
         // const InsufficientBalance = defineAsyncComponent(() => import('./StatusComponent/InsufficientBalance.vue'));
         const LimitOrderModal = defineAsyncComponent(() => import('./components/LimitOrderModal.vue'));
+        /*常用功能*/
+        // 跳转到子路由
+        const openFunctions = () => {
+          router.push({ name: 'BitcoinFunctions' })
+        }
 
-        //市价说明
+       /*市价说明*/
         const showModal3 = ref(false);
 
         // //余额不足提示
@@ -344,7 +358,7 @@
         //   // 这里写你的跳转逻辑,例如: router.push('/recharge')
         // };
 
-        //订单类型选项
+        /*订单类型选项*/
         const showModal2 = ref(false);
 
         // 定义状态用于存储
@@ -352,7 +366,7 @@
         const selectedLabel2 = ref('市价'); // 默认值
         const selectedUnit2 = ref('%');      // 默认值
 
-        // 回调函数:子组件选中后触发
+        /*回调函数:子组件选中后触发*/
         const handleConfirm2 = (item) => {
           // console.log('子组件返回的对象:', item);
 
@@ -365,7 +379,7 @@
           selectedUnit2.value = item.unit;   // 字符串2: "%"
         };
 
-        //全仓逐仓选项
+        /* 全仓逐仓选项*/
         const showModal1 = ref(false);
 
         // 定义状态用于存储
@@ -386,10 +400,10 @@
           selectedUnit1.value = item.unit;   // 字符串2: "%"
         };
 
-        //全仓逐仓说明
+       /* // 全仓逐仓说明*/
         const showInfo = ref(false);
 
-        //GTC弹框
+        /*//GTC弹框*/
         // 控制弹窗显示
         const showModal = ref(false);
 
@@ -397,7 +411,7 @@
         const currentType = ref('GTC');
 
 
-        // --- 深度弹窗 ---
+       /* // --- 深度弹窗 ---*/
         const isPickerVisible = ref(false); // 控制弹窗开关
         const currentDepth = ref('depth1'); // 当前选中的深度
         const depthMap = {
@@ -407,10 +421,10 @@
         };
         const displayLabel = computed(() => depthMap[currentDepth.value] || '请选择');
 
-        //控制止盈止损
+        /*控制止盈止损*/
         const isEnabled = ref(false);
 
-        //控制倍数
+        /*控制倍数*/
         // 控制弹窗显示
         const showLeverageModal = ref(false);
         // 存储当前选中的倍数,默认 100
@@ -422,7 +436,7 @@
           // 这里可以继续添加发送 API 请求的逻辑
         };
 
-        //做多买入
+        /*做多买入*/
         const showConfirm = ref(false);
         const onOrderConfirmed = () => {
           // console.log('订单已提交');
@@ -446,6 +460,7 @@
   align-items: center;
   margin-bottom: 100px;
   width: 100%;
+  z-index: 1;
 
   .market-nav {
     display: flex;

+ 118 - 0
src/views/bitcoin/calculator/FundingRateReminder.vue

@@ -0,0 +1,118 @@
+<template>
+  <Teleport to="body">
+    <transition name="fade">
+      <div v-if="visible" class="modal-mask" @click="close">
+
+        <div class="modal-container" @click.stop>
+
+          <div class="modal-title">资金费率</div>
+
+          <div class="modal-text">
+                  费率为正,做多仓位支付做空仓位
+                  费率为负,做空仓位支付做多仓位
+          </div>
+
+          <button class="main-btn" @click="close">
+            我知道了
+          </button>
+
+        </div>
+      </div>
+    </transition>
+  </Teleport>
+</template>
+
+<script setup>
+import { defineProps, defineEmits } from 'vue';
+
+defineProps({
+  visible: Boolean
+});
+
+const emit = defineEmits(['update:visible']);
+
+const close = () => {
+  emit('update:visible', false);
+};
+</script>
+
+<style scoped>
+/* --- 布局与遮罩 --- */
+.modal-mask {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100vw;
+  height: 100vh;
+  background: rgba(0, 0, 0, 0.6); /* 深色遮罩 */
+  display: flex;
+  align-items: center; /* 垂直居中 */
+  justify-content: center; /* 水平居中 */
+  z-index: 999;
+}
+
+/* --- 弹窗卡片 --- */
+.modal-container {
+  width: 310px; /* 稍微宽一点,适配文字换行 */
+  background: #fff;
+  border-radius: 16px; /* 大圆角 */
+  padding: 24px 24px;
+  box-sizing: border-box;
+  text-align: center;
+  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1);
+}
+
+/* --- 标题 --- */
+.modal-title {
+  font-size: 18px;
+  font-weight: 600;
+  color: #111;
+  margin-bottom: 16px;
+}
+
+/* --- 正文 --- */
+.modal-text {
+  font-size: 15px;
+  color: #555; /* 稍微柔和的深灰色 */
+  line-height: 1.6; /* 增加行高,易读 */
+  text-align: justify; /* 两端对齐,像报纸一样整齐 */
+  max-width: 226px;
+  margin: 24px auto;
+}
+
+/* --- 红色按钮 --- */
+.main-btn {
+  width: 100%;
+  height: 46px;
+  background: #DE3545; /* 截图里的标准红 */
+  color: #fff;
+  font-size: 16px;
+  font-weight: 500;
+  border: none;
+  border-radius: 23px; /* 高度的一半,形成胶囊形状 */
+  cursor: pointer;
+}
+.main-btn:active {
+  opacity: 0.8; /* 点击反馈 */
+}
+
+/* --- 动画效果: 缩放弹入 --- */
+.fade-enter-active, .fade-leave-active {
+  transition: opacity 0.2s ease;
+}
+.fade-enter-active .modal-container {
+  animation: pop-in 0.3s cubic-bezier(0.18, 0.89, 0.32, 1.28); /* 弹性动画 */
+}
+.fade-leave-active .modal-container {
+  animation: pop-in 0.3s reverse;
+}
+
+.fade-enter-from, .fade-leave-to {
+  opacity: 0;
+}
+
+@keyframes pop-in {
+  0% { transform: scale(0.8); opacity: 0; }
+  100% { transform: scale(1); opacity: 1; }
+}
+</style>

+ 0 - 11
src/views/bitcoin/calculator/LimitOrderModal.vue

@@ -1,11 +0,0 @@
-<script setup>
-
-</script>
-
-<template>
-
-</template>
-
-<style scoped lang="less">
-
-</style>

+ 11 - 6
src/views/bitcoin/components/FundingOptions.vue

@@ -23,15 +23,19 @@
             <div class="description">
               {{ item.desc }}
             </div>
+
           </div>
         </div>
+        <div class="fs12 fc666666">该设置对所有交易品种生效。若存在持仓或挂单,则不支持调整仓位模式。</div>
       </div>
     </div>
+
   </Teleport>
 </template>
 
 <script setup>
-import { defineProps, defineEmits } from 'vue';
+import { defineProps, defineEmits ,toRef} from 'vue';
+import { useBodyScrollLock } from '@/composables/useBodyScrollLock' // 2. 引入 Hook
 
 // 接收父组件传来的 modelValue (控制显示) 和 当前选中的ID
 const props = defineProps({
@@ -44,7 +48,7 @@ const props = defineProps({
     default: 1
   }
 });
-
+useBodyScrollLock(toRef(props, 'visible'))
 const emit = defineEmits(['update:visible', 'confirm']);
 
 // 静态数据源 (保持不变)
@@ -52,14 +56,15 @@ const options = [
   {
     id: 1,
     label: '全仓',
-    desc: '所有仓位共用保证金盈亏互抵可有效降低强平风险但强平时可能损失全部仓位'
+    desc: '所有仓位共用保证金盈亏互抵可有效降低强平风险但强平时可能损失全部仓位',
   },
 
   {
     id: 2,
     label: '逐仓',
-    desc: '各仓位的保证金与盈亏单独核算强平时仅损失当前仓位'
-  }
+    desc: '各仓位的保证金与盈亏单独核算强平时仅损失当前仓位' ,
+
+  },
 ];
 
 const close = () => {
@@ -81,7 +86,7 @@ const handleSelect = (item) => {
   background: rgba(0, 0, 0, 0.5);
   display: flex;
   align-items: flex-end; /* 底部对齐 */
-  z-index: 1000; /* 极高的 Z-index */
+  z-index: 2001; /* 极高的 Z-index */
 }
 
 .modal-content {

+ 3 - 1
src/views/bitcoin/components/OrderTimeSheet.vue

@@ -40,6 +40,8 @@
 
 <script setup>
 import { defineProps, defineEmits } from 'vue';
+import { toRef } from 'vue' // 1. 引入 toRef
+import { useBodyScrollLock } from '@/composables/useBodyScrollLock' // 2. 引入 Hook
 
 // 定义接收的属性
 const props = defineProps({
@@ -52,7 +54,7 @@ const props = defineProps({
     default: ''
   }
 });
-
+useBodyScrollLock(toRef(props, 'visible'))
 // 定义抛出的事件
 const emit = defineEmits(['update:visible', 'update:modelValue']);
 

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