客服弹窗
This commit is contained in:
parent
cb9f0c680f
commit
3884974e68
97
app/components/ContactFloatingButton.vue
Normal file
97
app/components/ContactFloatingButton.vue
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
<!-- components/ContactFloatingButton.vue -->
|
||||
<template>
|
||||
<div class="fixed right-6 top-1/2 transform -translate-y-1/2 z-50 flex items-center">
|
||||
<!-- 二维码面板 -->
|
||||
<Transition name="fade-slide">
|
||||
<div v-if="showQR" class="w-64 overflow-hidden transition-all duration-300 ease-out;">
|
||||
<img
|
||||
:alt="qrAltText"
|
||||
:src="qrImage"
|
||||
class="w-full h-auto border-4 border-white rounded-lg shadow-xl"
|
||||
>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<!-- 客服按钮 -->
|
||||
<button
|
||||
:class="{ 'ml-2': showQR }"
|
||||
class="bg-green-500 hover:bg-green-600 text-white px-4 py-3 rounded-l-lg shadow-lg transition-all duration-300"
|
||||
@click="onButtonClick"
|
||||
@mouseenter="showQR = true"
|
||||
@mouseleave="onMouseLeave"
|
||||
>
|
||||
<span class="button-text">{{ buttonText }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref} from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
qrImage: {
|
||||
type: String,
|
||||
default: '/img/img.png'
|
||||
},
|
||||
qrAltText: {
|
||||
type: String,
|
||||
default: '客服二维码'
|
||||
},
|
||||
buttonText: {
|
||||
type: String,
|
||||
default: '联系客服'
|
||||
},
|
||||
hoverDelay: {
|
||||
type: Number,
|
||||
default: 300 // 延迟隐藏时间(ms)
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['click'])
|
||||
|
||||
const showQR = ref(false)
|
||||
let hideTimer = null
|
||||
|
||||
const onMouseLeave = () => {
|
||||
hideTimer = setTimeout(() => {
|
||||
showQR.value = false
|
||||
}, props.hoverDelay)
|
||||
}
|
||||
|
||||
const cancelHide = () => {
|
||||
if (hideTimer) {
|
||||
clearTimeout(hideTimer)
|
||||
hideTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
const onButtonClick = () => {
|
||||
emit('click')
|
||||
// 点击后保持二维码显示
|
||||
cancelHide()
|
||||
showQR.value = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 按钮样式 */
|
||||
|
||||
|
||||
.writing-mode-vertical {
|
||||
writing-mode: vertical-rl;
|
||||
text-orientation: mixed;
|
||||
}
|
||||
|
||||
|
||||
/* 动画效果 */
|
||||
.fade-slide-enter-active,
|
||||
.fade-slide-leave-active {
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.fade-slide-enter-from,
|
||||
.fade-slide-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(10px);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -22,7 +22,6 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {useThrottleFn} from '@vueuse/core'
|
||||
|
||||
interface Props {
|
||||
threshold?: number
|
||||
|
|
@ -78,9 +77,9 @@ const buttonClasses = computed(() => [
|
|||
}
|
||||
])
|
||||
|
||||
const handleScroll = useThrottleFn(() => {
|
||||
const handleScroll = () => {
|
||||
isVisible.value = window.scrollY > props.threshold
|
||||
}, 100)
|
||||
}
|
||||
|
||||
const handleClick = () => {
|
||||
if (props.smoothScroll) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
<AppHeader/>
|
||||
<slot/>
|
||||
<AppFooter/>
|
||||
<ContactFloatingButton/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UPage>
|
||||
<LandingHero/>
|
||||
|
|
@ -10,15 +6,8 @@
|
|||
<LandingOURSTRENGTHS/>
|
||||
<LandingPRODUCT_INTRODUCTION/>
|
||||
<LandingCaseFacts/>
|
||||
<div class="fixed right-6 top-1/2 transform -translate-y-1/2 z-50">
|
||||
<button class="bg-green-500 text-white px-4 py-3 rounded-l-lg shadow-lg writing-mode-vertical">
|
||||
<span class="font-medium">联系客服</span>
|
||||
</button>
|
||||
</div>
|
||||
<!-- <ScrollToTop/>-->
|
||||
|
||||
</UPage>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
|
@ -13,7 +13,6 @@
|
|||
"@nuxt/eslint": "1.9.0",
|
||||
"@nuxt/image": "1.11.0",
|
||||
"@nuxt/ui": "4.0.1",
|
||||
"@vueuse/core": "^14.0.0",
|
||||
"eslint": "^9.38.0",
|
||||
"nuxt": "^4.1.3",
|
||||
"typescript": "^5.9.3",
|
||||
|
|
|
|||
|
|
@ -17,9 +17,6 @@ importers:
|
|||
'@nuxt/ui':
|
||||
specifier: 4.0.1
|
||||
version: 4.0.1(@babel/parser@7.28.4)(change-case@5.4.4)(db0@0.3.4)(embla-carousel@8.6.0)(ioredis@5.8.1)(magicast@0.3.5)(typescript@5.9.3)(vite@7.1.11(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(yaml@2.8.1))(vue-router@4.6.3(vue@3.5.22(typescript@5.9.3)))(vue@3.5.22(typescript@5.9.3))(zod@4.1.12)
|
||||
'@vueuse/core':
|
||||
specifier: ^14.0.0
|
||||
version: 14.0.0(vue@3.5.22(typescript@5.9.3))
|
||||
eslint:
|
||||
specifier: ^9.38.0
|
||||
version: 9.38.0(jiti@2.6.1)
|
||||
|
|
@ -1699,11 +1696,6 @@ packages:
|
|||
peerDependencies:
|
||||
vue: ^3.5.0
|
||||
|
||||
'@vueuse/core@14.0.0':
|
||||
resolution: {integrity: sha512-d6tKRWkZE8IQElX2aHBxXOMD478fHIYV+Dzm2y9Ag122ICBpNKtGICiXKOhWU3L1kKdttDD9dCMS4bGP3jhCTQ==}
|
||||
peerDependencies:
|
||||
vue: ^3.5.0
|
||||
|
||||
'@vueuse/integrations@13.9.0':
|
||||
resolution: {integrity: sha512-SDobKBbPIOe0cVL7QxMzGkuUGHvWTdihi9zOrrWaWUgFKe15cwEcwfWmgrcNzjT6kHnNmWuTajPHoIzUjYNYYQ==}
|
||||
peerDependencies:
|
||||
|
|
@ -1755,9 +1747,6 @@ packages:
|
|||
'@vueuse/metadata@13.9.0':
|
||||
resolution: {integrity: sha512-1AFRvuiGphfF7yWixZa0KwjYH8ulyjDCC0aFgrGRz8+P4kvDFSdXLVfTk5xAN9wEuD1J6z4/myMoYbnHoX07zg==}
|
||||
|
||||
'@vueuse/metadata@14.0.0':
|
||||
resolution: {integrity: sha512-6yoGqbJcMldVCevkFiHDBTB1V5Hq+G/haPlGIuaFZHpXC0HADB0EN1ryQAAceiW+ryS3niUwvdFbGiqHqBrfVA==}
|
||||
|
||||
'@vueuse/shared@10.11.1':
|
||||
resolution: {integrity: sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==}
|
||||
|
||||
|
|
@ -1769,11 +1758,6 @@ packages:
|
|||
peerDependencies:
|
||||
vue: ^3.5.0
|
||||
|
||||
'@vueuse/shared@14.0.0':
|
||||
resolution: {integrity: sha512-mTCA0uczBgurRlwVaQHfG0Ja7UdGe4g9mwffiJmvLiTtp1G4AQyIjej6si/k8c8pUwTfVpNufck+23gXptPAkw==}
|
||||
peerDependencies:
|
||||
vue: ^3.5.0
|
||||
|
||||
abbrev@3.0.1:
|
||||
resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==}
|
||||
engines: {node: ^18.17.0 || >=20.5.0}
|
||||
|
|
@ -6702,13 +6686,6 @@ snapshots:
|
|||
'@vueuse/shared': 13.9.0(vue@3.5.22(typescript@5.9.3))
|
||||
vue: 3.5.22(typescript@5.9.3)
|
||||
|
||||
'@vueuse/core@14.0.0(vue@3.5.22(typescript@5.9.3))':
|
||||
dependencies:
|
||||
'@types/web-bluetooth': 0.0.21
|
||||
'@vueuse/metadata': 14.0.0
|
||||
'@vueuse/shared': 14.0.0(vue@3.5.22(typescript@5.9.3))
|
||||
vue: 3.5.22(typescript@5.9.3)
|
||||
|
||||
'@vueuse/integrations@13.9.0(change-case@5.4.4)(fuse.js@7.1.0)(vue@3.5.22(typescript@5.9.3))':
|
||||
dependencies:
|
||||
'@vueuse/core': 13.9.0(vue@3.5.22(typescript@5.9.3))
|
||||
|
|
@ -6724,8 +6701,6 @@ snapshots:
|
|||
|
||||
'@vueuse/metadata@13.9.0': {}
|
||||
|
||||
'@vueuse/metadata@14.0.0': {}
|
||||
|
||||
'@vueuse/shared@10.11.1(vue@3.5.22(typescript@5.9.3))':
|
||||
dependencies:
|
||||
vue-demi: 0.14.10(vue@3.5.22(typescript@5.9.3))
|
||||
|
|
@ -6743,10 +6718,6 @@ snapshots:
|
|||
dependencies:
|
||||
vue: 3.5.22(typescript@5.9.3)
|
||||
|
||||
'@vueuse/shared@14.0.0(vue@3.5.22(typescript@5.9.3))':
|
||||
dependencies:
|
||||
vue: 3.5.22(typescript@5.9.3)
|
||||
|
||||
abbrev@3.0.1: {}
|
||||
|
||||
abort-controller@3.0.0:
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user