Преглед изворни кода

我的页面增加悬浮按钮

ylong пре 9 месеци
родитељ
комит
1e8200d15a

+ 94 - 0
components/floating-activity.vue

@@ -0,0 +1,94 @@
+<template>
+  <floating-drag
+    :visible="visible"
+    :initialPosition="initialPosition"
+    :width="size"
+    :height="size"
+    @click="handleClick"
+    @position-change="onPositionChange"
+  >
+    <view class="floating-btn">
+      <image src="/static/img/activity.png" class="icon"></image>
+    </view>
+  </floating-drag>
+</template>
+
+<script>
+import FloatingDrag from "./floating-drag.vue";
+
+export default {
+  name: "FloatingButton",
+  components: {
+    FloatingDrag,
+  },
+  props: {
+    // 是否显示
+    visible: {
+      type: Boolean,
+      default: true,
+    },
+    // 初始位置
+    initialPosition: {
+      type: Object,
+      default: () => ({
+        left: "auto",
+        right: 0,
+        bottom: "30%",
+      }),
+    },
+    // 按钮大小
+    size: {
+      type: [Number, String],
+      default: 200,
+    },
+    // 背景颜色
+    bgColor: {
+      type: String,
+      default: "#007AFF",
+    },
+    // 图标
+    icon: {
+      type: String,
+      default: "",
+    },
+    // 文本
+    text: {
+      type: String,
+      default: "按钮",
+    },
+  },
+  methods: {
+    // 处理点击事件
+    handleClick() {
+      this.$emit("click");
+    },
+
+    // 处理位置变更事件
+    onPositionChange(position) {
+      this.$emit("position-change", position);
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.floating-btn {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+
+  .icon {
+    width: 100%;
+    height: 100%;
+  }
+
+  text {
+    color: #ffffff;
+    font-size: 28rpx;
+    text-align: center;
+    font-weight: 500;
+  }
+}
+</style>

+ 248 - 0
components/floating-drag.vue

@@ -0,0 +1,248 @@
+<template>
+  <view 
+    class="floating-drag"
+    :style="{
+      left: buttonPosition.left + 'px',
+      right: buttonPosition.right + 'px',
+      bottom: buttonPosition.bottom + 'px',
+      width: width + 'rpx',
+      height: height + 'rpx',
+      display: visible ? 'block' : 'none'
+    }"
+    @touchstart="touchStart"
+    @touchmove="touchMove"
+    @touchend="touchEnd"
+    @click="handleClick"
+    v-if="visible"
+  >
+    <slot>
+      <!-- Default content if no slot is provided -->
+      <view class="default-btn">
+        <text>按钮</text>
+      </view>
+    </slot>
+  </view>
+</template>
+
+<script>
+export default {
+  name: 'FloatingDrag',
+  props: {
+    // 初始位置
+    initialPosition: {
+      type: Object,
+      default: () => ({
+        left: 'auto',
+        right: 0,
+        bottom: '20%'
+      })
+    },
+    // 组件尺寸
+    width: {
+      type: [Number, String],
+      default: 120
+    },
+    height: {
+      type: [Number, String],
+      default: 120
+    },
+    // 是否显示
+    visible: {
+      type: Boolean,
+      default: true
+    },
+    // 点击事件
+    onClick: {
+      type: Function,
+      default: null
+    }
+  },
+  data() {
+    return {
+      // 悬浮按钮位置
+      buttonPosition: {
+        left: 'auto',
+        right: 0,
+        bottom: '20%',
+      },
+      // 触摸开始位置
+      startX: 0,
+      startY: 0,
+      // 屏幕宽度和高度
+      screenWidth: 0,
+      screenHeight: 0,
+      // 初始位置记录,用于计算拖动
+      initialLeft: 0,
+      initialBottom: 0,
+      // 是否正在更新位置,用于防止频繁更新
+      isUpdatingPosition: false,
+    };
+  },
+  created() {
+    // 使用传入的初始位置
+    this.buttonPosition = { ...this.initialPosition };
+  },
+  watch: {
+    initialPosition: {
+      handler(newVal) {
+        this.buttonPosition = { ...newVal };
+      },
+      immediate: true,
+    },
+  },
+  mounted() {
+    // 获取屏幕宽度和高度
+    uni.getSystemInfo({
+      success: (res) => {
+        this.screenWidth = res.windowWidth;
+        this.screenHeight = res.windowHeight;
+      },
+    });
+  },
+  methods: {
+    // 触摸开始
+    touchStart(e) {
+      const touch = e.touches[0];
+      this.startX = touch.clientX;
+      this.startY = touch.clientY;
+
+      // 记录初始位置,用于计算移动距离
+      if (this.buttonPosition.right !== 'auto') {
+        // 如果是靠右定位,记录当前位置但不立即改变显示位置
+        this.initialLeft = this.screenWidth - parseFloat(this.width) / 2;
+      } else {
+        this.initialLeft = parseFloat(this.buttonPosition.left);
+      }
+
+      // 如果bottom是百分比,转换为具体像素值
+      if (typeof this.buttonPosition.bottom === 'string' && this.buttonPosition.bottom.includes('%')) {
+        const percentage = parseFloat(this.buttonPosition.bottom) / 100;
+        this.initialBottom = this.screenHeight * percentage;
+      } else {
+        this.initialBottom = parseFloat(this.buttonPosition.bottom);
+      }
+    },
+
+    // 触摸移动
+    touchMove(e) {
+      // 阻止默认行为,防止页面滚动
+      e.preventDefault && e.preventDefault();
+      e.stopPropagation && e.stopPropagation();
+
+      const touch = e.touches[0];
+
+      // 计算移动距离
+      const deltaX = touch.clientX - this.startX;
+      const deltaY = touch.clientY - this.startY;
+
+      // 使用初始位置计算新位置,避免累积误差
+      let newLeft = this.initialLeft + deltaX;
+      let newBottom = this.initialBottom - deltaY; // 注意:y轴方向是相反的
+
+      // 获取按钮实际宽度(将rpx转换为px)
+      const buttonWidthPx = parseFloat(this.width) / 750 * this.screenWidth;
+      
+      // 确保按钮不超出屏幕边界
+      if (newLeft < 0) {
+        newLeft = 0;
+      } else if (newLeft > this.screenWidth - buttonWidthPx) {
+        newLeft = this.screenWidth - buttonWidthPx;
+      }
+
+      // 确保按钮不超出屏幕垂直边界
+      if (newBottom < 20) {
+        newBottom = 20;
+      } else if (newBottom > this.screenHeight - buttonWidthPx) {
+        newBottom = this.screenHeight - buttonWidthPx;
+      }
+
+      // 使用节流方式更新位置,避免过于频繁的更新
+      if (!this.isUpdatingPosition) {
+        this.isUpdatingPosition = true;
+
+        // 更新位置 - 第一次移动时才真正改变right为auto
+        this.buttonPosition = {
+          left: newLeft,
+          right: 'auto',
+          bottom: newBottom,
+        };
+
+        // 使用setTimeout代替requestAnimationFrame,在微信小程序中更兼容
+        setTimeout(() => {
+          this.isUpdatingPosition = false;
+        }, 16); // 约等于60fps的刷新率
+      }
+    },
+
+    // 触摸结束,实现吸附效果
+    touchEnd() {
+      // 确保不再有待处理的更新
+      this.isUpdatingPosition = false;
+
+      // 获取按钮实际宽度(将rpx转换为px)
+      const buttonWidthPx = parseFloat(this.width) / 750 * this.screenWidth;
+      const buttonCenter = this.buttonPosition.left + buttonWidthPx / 2; // 按钮中心位置
+      
+      const halfScreen = this.screenWidth / 2;
+
+      // 判断是吸附到左边还是右边
+      if (buttonCenter < halfScreen) {
+        // 吸附到左边
+        this.buttonPosition = {
+          left: 0,
+          right: 'auto',
+          bottom: this.buttonPosition.bottom,
+        };
+      } else {
+        // 吸附到右边
+        this.buttonPosition = {
+          left: 'auto',
+          right: 0,
+          bottom: this.buttonPosition.bottom,
+        };
+      }
+      
+      // 触发位置变更事件
+      this.$emit('position-change', { ...this.buttonPosition });
+    },
+    
+    // 处理点击事件
+    handleClick() {
+      this.$emit('click');
+      if (this.onClick) {
+        this.onClick();
+      }
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.floating-drag {
+  position: fixed;
+  z-index: 999;
+  transition: all 0.3s ease;
+  
+  .default-btn {
+    width: 100%;
+    height: 100%;
+    background-color: #4cd964;
+    border-radius: 50%;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    border: 4rpx solid #fff;
+    box-shadow: 0 0 10rpx rgba(0, 0, 0, 0.2);
+    
+    text {
+      color: #ffffff;
+      font-size: 34rpx;
+      text-align: center;
+      font-weight: 500;
+      padding: 0 10rpx;
+      line-height: 1.2;
+    }
+  }
+}
+</style> 

+ 77 - 0
components/withdrawal-confirm.vue

@@ -0,0 +1,77 @@
+<template>
+  <floating-drag
+    :visible="visible"
+    :initialPosition="initialPosition"
+    :width="120"
+    :height="120"
+    @click="handleClick"
+    @position-change="onPositionChange"
+  >
+    <view class="confirm-btn">
+      <text>提现</text>
+      <text>确认</text>
+    </view>
+  </floating-drag>
+</template>
+
+<script>
+import FloatingDrag from './floating-drag.vue';
+
+export default {
+  name: 'WithdrawalConfirm',
+  components: {
+    FloatingDrag
+  },
+  props: {
+    // 是否显示
+    visible: {
+      type: Boolean,
+      default: true
+    },
+    // 初始位置
+    initialPosition: {
+      type: Object,
+      default: () => ({
+        left: 'auto',
+        right: 0,
+        bottom: '20%'
+      })
+    }
+  },
+  methods: {
+    // 处理点击事件
+    handleClick() {
+      this.$emit('click');
+    },
+    
+    // 处理位置变更事件
+    onPositionChange(position) {
+      this.$emit('position-change', position);
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.confirm-btn {
+  width: 100%;
+  height: 100%;
+  background-color: #4cd964;
+  border-radius: 50%;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  border: 4rpx solid #fff;
+  box-shadow: 0 0 10rpx rgba(0, 0, 0, 0.2);
+
+  text {
+    color: #ffffff;
+    font-size: 34rpx;
+    text-align: center;
+    font-weight: 500;
+    padding: 0 10rpx;
+    line-height: 1.2;
+  }
+}
+</style> 

+ 50 - 153
pages/mine/index.vue

@@ -90,24 +90,19 @@
         </view>
 
         <!-- 悬浮提现确认按钮 -->
-        <view
-            class="withdrawal-confirm"
-            :style="{
-                left: buttonPosition.left + 'px',
-                right: buttonPosition.right + 'px',
-                bottom: buttonPosition.bottom + 'px',
-            }"
-            @touchstart="touchStart"
-            @touchmove="touchMove"
-            @touchend="touchEnd"
+        <withdrawal-confirm 
+            :initialPosition="buttonPosition"
             @click="navigateToWithdrawal"
-            v-if="withdrawalOrder && withdrawalOrder.length > 0"
-        >
-            <view class="confirm-btn">
-                <text>提现</text>
-                <text>确认</text>
-            </view>
-        </view>
+            @position-change="onPositionChange"
+        />
+
+        <!-- 活动悬浮按钮 -->
+        <floating-button 
+            :visible="true"
+            :initialPosition="activityPosition"
+            @click="contactCustomerService"
+            @position-change="onActivityButtonPositionChange"
+        />
 
         <!-- 提现进度弹窗 -->
         <withdrawal-progress
@@ -120,10 +115,14 @@
 
 <script>
 import WithdrawalProgress from "./components/withdrawal-progress.vue";
+import WithdrawalConfirm from "../../components/withdrawal-confirm.vue";
+import FloatingButton from "../../components/floating-activity.vue";
 
 export default {
     components: {
         WithdrawalProgress,
+        WithdrawalConfirm,
+        FloatingButton
     },
     data() {
         return {
@@ -241,17 +240,12 @@ export default {
                 right: 0,
                 bottom: "20%",
             },
-            // 触摸开始位置
-            startX: 0,
-            startY: 0,
-            // 屏幕宽度和高度
-            screenWidth: 0,
-            screenHeight: 0,
-            // 初始位置记录,用于计算拖动
-            initialLeft: 0,
-            initialBottom: 0,
-            // 是否正在更新位置,用于防止频繁更新
-            isUpdatingPosition: false,
+            // 客服按钮位置
+            activityPosition: {
+                left: "auto",
+                right: 0,
+                bottom: "40%",
+            },
             withdrawalOrder: [],
             // 提现进度弹窗相关
             showWithdrawalModal: false,
@@ -305,6 +299,7 @@ export default {
         },
         // 导航到提现确认页面
         navigateToWithdrawal() {
+            console.log('11111')
             if (this.withdrawalOrder && this.withdrawalOrder.length > 0) {
                 // 显示提现进度弹窗,使用第一个提现订单
                 this.currentWithdrawalOrder = this.withdrawalOrder[0];
@@ -352,102 +347,36 @@ export default {
                 });
             });
         },
-        // 触摸开始
-        touchStart(e) {
-            const touch = e.touches[0];
-            this.startX = touch.clientX;
-            this.startY = touch.clientY;
-
-            // 记录初始位置,用于计算移动距离
-            if (this.buttonPosition.right !== "auto") {
-                // 如果是靠右定位,记录当前位置但不立即改变显示位置
-                this.initialLeft = this.screenWidth - 120;
-            } else {
-                this.initialLeft = parseFloat(this.buttonPosition.left);
-            }
-
-            // 如果bottom是百分比,转换为具体像素值
-            if (typeof this.buttonPosition.bottom === "string" && this.buttonPosition.bottom.includes("%")) {
-                const percentage = parseFloat(this.buttonPosition.bottom) / 100;
-                this.initialBottom = this.screenHeight * percentage;
-            } else {
-                this.initialBottom = parseFloat(this.buttonPosition.bottom);
-            }
+        
+        // 更新悬浮按钮位置
+        onPositionChange(position) {
+            this.buttonPosition = position;
         },
-
-        // 触摸移动
-        touchMove(e) {
-            // 阻止默认行为,防止页面滚动
-            e.preventDefault && e.preventDefault();
-            e.stopPropagation && e.stopPropagation();
-
-            const touch = e.touches[0];
-
-            // 计算移动距离
-            const deltaX = touch.clientX - this.startX;
-            const deltaY = touch.clientY - this.startY;
-
-            // 使用初始位置计算新位置,避免累积误差
-            let newLeft = this.initialLeft + deltaX;
-            let newBottom = this.initialBottom - deltaY; // 注意:y轴方向是相反的
-
-            // 确保按钮不超出屏幕边界
-            if (newLeft < 0) {
-                newLeft = 0;
-            } else if (newLeft > this.screenWidth - 120) {
-                newLeft = this.screenWidth - 120;
-            }
-
-            // 确保按钮不超出屏幕垂直边界
-            if (newBottom < 20) {
-                newBottom = 20;
-            } else if (newBottom > this.screenHeight - 120) {
-                newBottom = this.screenHeight - 120;
-            }
-
-            // 使用节流方式更新位置,避免过于频繁的更新
-            if (!this.isUpdatingPosition) {
-                this.isUpdatingPosition = true;
-
-                // 更新位置 - 第一次移动时才真正改变right为auto
-                this.buttonPosition = {
-                    left: newLeft,
-                    right: "auto",
-                    bottom: newBottom,
-                };
-
-                // 使用setTimeout代替requestAnimationFrame,在微信小程序中更兼容
-                setTimeout(() => {
-                    this.isUpdatingPosition = false;
-                }, 16); // 约等于60fps的刷新率
-            }
+        
+        // 更新活动按钮位置
+        onActivityButtonPositionChange(position) {
+            this.activityPosition = position;
         },
-
-        // 触摸结束,实现吸附效果
-        touchEnd() {
-            // 确保不再有待处理的更新
-            this.isUpdatingPosition = false;
-
-            const buttonCenter = this.buttonPosition.left + 60; // 按钮中心位置
-            const halfScreen = this.screenWidth / 2;
-
-            // 判断是吸附到左边还是右边
-            if (buttonCenter < halfScreen) {
-                // 吸附到左边
-                this.buttonPosition = {
-                    left: 0,
-                    right: "auto",
-                    bottom: this.buttonPosition.bottom,
-                };
-            } else {
-                // 吸附到右边
-                this.buttonPosition = {
-                    left: "auto",
-                    right: 0,
-                    bottom: this.buttonPosition.bottom,
-                };
-            }
+        
+        // 联系客服
+        contactCustomerService() {
+            // 示例:打开客服会话
+            // 实际情况下可能需要调用微信的客服接口
+            uni.showToast({
+                title: '正在连接客服...',
+                icon: 'none'
+            });
+            
+            // 这只是一个示例,实际应用中可能需要调用微信的客服功能
+            // 比如可以跳转到联系客服的页面
+            setTimeout(() => {
+                let serviceItem = this.tools.find(item => item.name === "联系客服");
+                if (serviceItem && serviceItem.path) {
+                    this.navigateToTool(serviceItem.path);
+                }
+            }, 500);
         },
+        
         // 关闭提现进度弹窗
         closeWithdrawalModal() {
             this.$refs.withdrawalRef.handleClose();
@@ -726,37 +655,5 @@ export default {
             }
         }
     }
-
-    // 悬浮提现确认按钮样式
-    .withdrawal-confirm {
-        position: fixed;
-        bottom: 10%;
-        z-index: 999;
-        width: 120rpx;
-        height: 120rpx;
-        transition: all 0.3s ease;
-
-        .confirm-btn {
-            width: 100%;
-            height: 100%;
-            background-color: #4cd964;
-            border-radius: 50%;
-            display: flex;
-            flex-direction: column;
-            align-items: center;
-            justify-content: center;
-            border: 4rpx solid #fff;
-            box-shadow: 0 0 10rpx rgba(0, 0, 0, 0.2);
-
-            text {
-                color: #ffffff;
-                font-size: 34rpx;
-                text-align: center;
-                font-weight: 500;
-                padding: 0 10rpx;
-                line-height: 1.2;
-            }
-        }
-    }
 }
 </style>

BIN
static/img/activity.png