| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 |
- <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>
|