Files
cashier_wx/components/devetools.vue

382 lines
8.8 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="floating-widget" v-if="show">
<!-- 悬浮按钮 -->
<view
class="floating-btn"
@click="togglePopup"
:style="{
backgroundColor: btnColor,
width: `${btnSize}px`,
height: `${btnSize}px`,
transform: `translateX(${currentOffsetX}px) translateY(${currentOffsetY}px)`,
}"
:class="{ active: isPopupVisible }"
@touchstart="touchstart"
@touchmove="touchmove"
@touchend="touchend"
>
<up-icon
name="plus"
size="24"
color="#ffffff"
:class="{ rotate: isPopupVisible }"
></up-icon>
</view>
<!-- 操作弹窗 -->
<view
class="popup"
v-if="isPopupVisible"
:class="{ show: isPopupVisible }"
:style="{
transform: `translateX(${currentOffsetX}px) translateY(${currentOffsetY}px)`,
}"
>
<view class="popup-arrow"></view>
<view class="popup-content">
<view
class="popup-item"
>
<text class="item-text">
<text> 当前环境:</text>
<text style="color: rgb(255, 0, 0);"> {{ uni.conf.debug ? "测试环境" : "生产环境" }}</text>
</text>
</view>
<view
class="popup-item"
v-for="(item, index) in operations"
:key="index"
@click="handleOperation(item.action)"
>
<text class="item-text">{{ item.name }}</text>
</view>
</view>
</view>
<!-- 点击外部关闭遮罩 -->
<view class="overlay" v-if="isPopupVisible" @click="closePopup"></view>
</view>
</template>
<script setup>
import { ref, defineProps, defineEmits, computed } from "vue";
import { changeEnv } from "@/common/config.js";
import { Storelogin } from "@/stores/user.js";
const show = computed(() => {
// trial
const sysInfo = uni.getAccountInfoSync();
if (
sysInfo &&
sysInfo.miniProgram &&
sysInfo.miniProgram.envVersion == "develop"
) {
return true;
}
return false;
});
// 核心修改1新增“历史累积偏移量”变量保存上一次拖拽后的最终位置
const lastOffsetX = ref(0); // 历史X偏移累积值
const lastOffsetY = ref(0); // 历史Y偏移累积值
const currentOffsetX = ref(0); // 当前拖拽的实时偏移(基于历史值)
const currentOffsetY = ref(0); // 当前拖拽的实时偏移(基于历史值)
const startX = ref(0);
const startY = ref(0);
// 核心修改2touchstart - 基于历史偏移量初始化当前拖拽起点
function touchstart(e) {
const touch = e.touches[0];
startX.value = touch.clientX;
startY.value = touch.clientY;
// 关键:当前拖拽的起点 = 上一次拖拽的终点(历史累积偏移量)
currentOffsetX.value = lastOffsetX.value;
currentOffsetY.value = lastOffsetY.value;
}
// 核心修改3touchmove - 实时计算“历史偏移量 + 当前拖拽距离”
function touchmove(e) {
const touch = e.touches[0];
// 当前拖拽的相对距离 = 现在触摸点 - 拖拽起点
const moveX = touch.clientX - startX.value;
const moveY = touch.clientY - startY.value;
// 实时偏移 = 历史累积偏移 + 当前相对移动距离(位置连续)
currentOffsetX.value = lastOffsetX.value + moveX;
currentOffsetY.value = lastOffsetY.value + moveY;
}
// 核心修改4touchend - 保存当前拖拽终点为历史偏移量(供下次使用)
function touchend() {
// 关键:将本次拖拽的最终位置保存为历史值
lastOffsetX.value = currentOffsetX.value;
lastOffsetY.value = currentOffsetY.value;
}
// 定义组件属性
const props = defineProps({
// 操作选项列表
operations: {
type: Array,
default: () => [
{
name: "切换为测试环境",
icon: "arrowup",
action: "test",
color: "#007aff",
},
{
name: "切换为正式环境",
icon: "arrowup",
action: "prod",
color: "#007aff",
},
{
name: "复制token",
icon: "arrowup",
action: "token",
color: "#007aff",
},
{
name: "复制用户信息",
icon: "userInfo",
action: "userInfo",
color: "#52c41a",
},
{
name: "获取登录code",
icon: "userInfo",
action: "getLoginCode",
color: "#52c41a",
},
{
name: "复制当前门店信息",
icon: "userInfo",
action: "copyStoreInfo",
color: "#52c41a",
},
{
name: "复制当前门店用户信息",
icon: "userInfo",
action: "copyStoreUserInfo",
color: "#52c41a",
},
],
},
// 悬浮按钮颜色
btnColor: {
type: String,
default: "#007aff",
},
// 悬浮按钮大小(px)
btnSize: {
type: Number,
default: 60,
},
// 弹窗距离底部的距离
bottomDistance: {
type: Number,
default: 20,
},
// 弹窗距离右侧的距离
rightDistance: {
type: Number,
default: 20,
},
});
// 定义组件事件
const emit = defineEmits(["onOperation", "onOpen", "onClose"]);
// 弹窗显示状态
const isPopupVisible = ref(false);
// 切换弹窗显示/隐藏
const togglePopup = () => {
isPopupVisible.value = !isPopupVisible.value;
if (isPopupVisible.value) {
emit("onOpen");
} else {
emit("onClose");
}
};
// 关闭弹窗
const closePopup = () => {
if (isPopupVisible.value) {
isPopupVisible.value = false;
emit("onClose");
}
};
async function getWxloginCode() {
return new Promise((resolve, reject) => {
uni.login({
success: (res) => {
if (res.code) {
resolve(res.code);
} else {
console.log("获取登录凭证code失败" + res.errMsg);
reject(res.errMsg);
}
},
});
});
}
const storelogin = Storelogin();
// 处理操作选择
const handleOperation = async (action) => {
let data = "";
if (action == "prod" || action == "test") {
changeEnv(action);
emit("onOperation", action);
uni.showToast({
title: "切换成功",
icon: "success",
});
uni.clearStorageSync();
await storelogin.actionslogin();
setTimeout(() => {
uni.reLaunch({
url: "/pages/index/index",
});
}, 1500);
closePopup();
return;
}
if (action == "token") {
data = uni.cache.get("token");
}
if (action == "userInfo") {
data = JSON.stringify(uni.cache.get("userInfo"));
}
if (action == "getLoginCode") {
data = await getWxloginCode();
}
if (action == "copyStoreInfo") {
data = JSON.stringify(uni.cache.get("shopInfo"));
}
if (action == "copyStoreUserInfo") {
data = JSON.stringify(uni.cache.get("shopUserInfo"));
}
console.log("data", data);
if (data) {
uni.setClipboardData({
data: data,
success: function () {},
});
}
emit("onOperation", action);
closePopup();
};
</script>
<style scoped>
.floating-widget {
position: fixed;
right: v-bind(rightDistance + "px");
bottom: v-bind(bottomDistance + "px");
z-index: 9999;
display: flex;
flex-direction: column;
align-items: flex-end;
}
/* 悬浮按钮样式 */
.floating-btn {
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
background: linear-gradient(98deg, #fe6d1100 40.64%, #ffd1b4 105.2%),
linear-gradient(259deg, #fe6d11 50.14%, #ffd1b4 114.93%);
box-shadow: 0 0.4375rem 0.95rem 0 #fe8b435e;
cursor: pointer;
/*transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); */
z-index: 1001;
}
.floating-btn.active {
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
transform: scale(1.05);
}
.floating-btn .rotate {
transform: rotate(45deg);
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
/* 弹窗样式 */
.popup {
position: absolute;
bottom: calc(100% + 15px);
right: 0;
min-width: 180px;
background-color: #ffffff;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
opacity: 0;
transform: translateY(10px) scale(0.95);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
z-index: 1000;
pointer-events: none;
}
.popup.show {
opacity: 1;
transform: translateY(0) scale(1);
pointer-events: auto;
}
/* 弹窗箭头 */
.popup-arrow {
position: absolute;
right: 20px;
bottom: -8px;
width: 16px;
height: 16px;
background-color: #ffffff;
transform: rotate(45deg);
box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.05);
}
.popup-content {
padding: 8px 0;
position: relative;
z-index: 1;
}
/* 操作选项样式 */
.popup-item {
display: flex;
align-items: center;
padding: 12px 20px;
cursor: pointer;
transition: background-color 0.2s ease;
}
.popup-item:hover {
background-color: #f5f7fa;
}
.item-text {
font-size: 14px;
color: #333333;
line-height: 1;
}
/* 遮罩层样式 */
.overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0);
z-index: 999;
}
</style>