285 lines
6.0 KiB
Vue
285 lines
6.0 KiB
Vue
<template>
|
||
<view class="floating-widget" v-if="show">
|
||
<!-- 悬浮按钮 -->
|
||
<view
|
||
class="floating-btn"
|
||
@click="togglePopup"
|
||
:style="{
|
||
backgroundColor: btnColor,
|
||
width: `${btnSize}px`,
|
||
height: `${btnSize}px`,
|
||
}"
|
||
:class="{ active: isPopupVisible }"
|
||
>
|
||
<up-icon
|
||
name="plus"
|
||
size="24"
|
||
color="#ffffff"
|
||
:class="{ rotate: isPopupVisible }"
|
||
></up-icon>
|
||
</view>
|
||
|
||
<!-- 操作弹窗 -->
|
||
<view class="popup" v-if="isPopupVisible" :class="{ show: isPopupVisible }">
|
||
<view class="popup-arrow"></view>
|
||
<view class="popup-content">
|
||
<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";
|
||
const show=computed(()=>{
|
||
const sysInfo=uni.getAccountInfoSync();
|
||
if(sysInfo&&sysInfo.miniProgram && (sysInfo.miniProgram.envVersion == 'release'||sysInfo.miniProgram.envVersion == 'develop')) {
|
||
return true;
|
||
}
|
||
return false
|
||
})
|
||
|
||
// 定义组件属性
|
||
const props = defineProps({
|
||
// 操作选项列表
|
||
operations: {
|
||
type: Array,
|
||
default: () => [
|
||
{
|
||
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 handleOperation = async (action) => {
|
||
let data='';
|
||
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)
|
||
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: 160px;
|
||
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 {
|
||
margin-left: 12px;
|
||
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> |