feat(components): 添加浮动气泡组件并优化直播相关页面

- 在首页引入 FloatingBubble 组件
- 移除 liveRoom 页面的 SideButton 组件中的拖动功能
- 在 liveRoom 页面中使用 floating2 组件包裹 SideButton
- 优化 signature 页面布局
- 在 nuxt.config.js 中启用 vscode 配置
This commit is contained in:
xingyy 2025-03-05 19:59:48 +08:00
parent da4a2c509c
commit 4d358d130b
6 changed files with 114 additions and 143 deletions

View File

@ -0,0 +1,93 @@
<template>
<div
ref="bubbleRef"
class="floating-bubble"
:style="bubbleStyle"
@touchstart="onTouchStart"
@touchmove="onTouchMove"
@touchend="onTouchEnd"
>
<slot></slot>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import { useThrottleFn } from '@vueuse/core'
const props = defineProps({
offset: {
type: Number,
default: 0
}
})
const bubbleRef = ref(null)
const position = ref({
right: props.offset,
bottom: 100
})
let startTouch = { x: 0, y: 0 }
let startPos = { right: 0, bottom: 0 }
let isDragging = false
const bubbleStyle = computed(() => ({
position: 'fixed',
right: `${position.value.right}px`,
bottom: `${position.value.bottom}px`,
zIndex: 99999,
transition: isDragging ? 'none' : 'all 0.3s'
}))
const onTouchStart = (event) => {
isDragging = true
const touch = event.touches[0]
startTouch = { x: touch.clientX, y: touch.clientY }
startPos = { ...position.value }
}
const onTouchMove = useThrottleFn((event) => {
if (!isDragging) return
event.preventDefault()
const touch = event.touches[0]
const { clientWidth, clientHeight } = document.documentElement
const rect = bubbleRef.value.getBoundingClientRect()
//
const deltaX = startTouch.x - touch.clientX
const deltaY = startTouch.y - touch.clientY
const newRight = startPos.right + deltaX
const newBottom = startPos.bottom + deltaY
//
position.value = {
right: Math.min(Math.max(newRight, props.offset),
clientWidth - rect.width - props.offset),
bottom: Math.min(Math.max(newBottom, props.offset),
clientHeight - rect.height - props.offset)
}
}, 16)
const onTouchEnd = () => {
if (!isDragging) return
isDragging = false
const { clientWidth } = document.documentElement
const rect = bubbleRef.value.getBoundingClientRect()
const left = clientWidth - position.value.right - rect.width
//
position.value.right = left < clientWidth / 2
? clientWidth - rect.width - props.offset
: props.offset
}
</script>
<style scoped>
.floating-bubble {
touch-action: none;
user-select: none;
}
</style>

View File

@ -3,6 +3,7 @@ import liveRoom from '@/pages/liveRoom/index.client.vue';
import {goodStore} from "@/stores/goods/index.js";
import ItemList from './components/ItemList/index.vue'
import Cescribe from './components/Cescribe/index.vue'
import FloatingBubble from '~/components/floating2/index.vue'
import {liveStore} from "~/stores/live/index.js";
const {auctionDetail,getArtworkList} = goodStore();

View File

@ -1,5 +1,5 @@
<script setup>
import { ref, computed, onMounted, onBeforeUnmount } from "vue"
import { ref, computed } from "vue"
import lockClosed from "@/static/images/lockdfd@2x.png"
import lockOpen from "@/static/images/lock4@2x.png"
import { liveStore } from "@/stores/live/index.js"
@ -15,104 +15,6 @@ const { pageRef } = goodStore()
const { userInfo ,payment} = authStore()
const showTang = ref(false)
const router = useRouter()
//
const isDragging = ref(false)
const startX = ref(0)
const startY = ref(0)
const currentTop = ref(196)
const currentLeft = ref(window.innerWidth - 60)
const sidebarRef = ref(null)
//
const minTop = 0
const maxTop = window.innerHeight - 180
const minLeft = 0
const maxLeft = window.innerWidth - 60
//
const snapThreshold = 30
const snapPosition = () => {
const centerX = currentLeft.value + 30
if (centerX < window.innerWidth / 2) {
currentLeft.value = 0
} else {
currentLeft.value = window.innerWidth - 60
}
}
//
const handleMouseDown = (e) => {
isDragging.value = true
startX.value = e.clientX - currentLeft.value
startY.value = e.clientY - currentTop.value
document.addEventListener('mousemove', handleMouseMove)
document.addEventListener('mouseup', handleMouseUp)
}
//
const handleMouseMove = (e) => {
if (!isDragging.value) return
let newTop = e.clientY - startY.value
let newLeft = e.clientX - startX.value
//
newTop = Math.max(minTop, Math.min(newTop, maxTop))
newLeft = Math.max(minLeft, Math.min(newLeft, maxLeft))
currentTop.value = newTop
currentLeft.value = newLeft
//
if (sidebarRef.value) {
sidebarRef.value.style.top = `${newTop}px`
sidebarRef.value.style.left = `${newLeft}px`
}
}
//
const handleMouseUp = () => {
isDragging.value = false
snapPosition()
if (sidebarRef.value) {
sidebarRef.value.style.left = `${currentLeft.value}px`
}
document.removeEventListener('mousemove', handleMouseMove)
document.removeEventListener('mouseup', handleMouseUp)
}
//
const handleTouchStart = (e) => {
isDragging.value = true
startX.value = e.touches[0].clientX - currentLeft.value
startY.value = e.touches[0].clientY - currentTop.value
}
const handleTouchMove = (e) => {
if (!isDragging.value) return
let newTop = e.touches[0].clientY - startY.value
let newLeft = e.touches[0].clientX - startX.value
newTop = Math.max(minTop, Math.min(newTop, maxTop))
newLeft = Math.max(minLeft, Math.min(newLeft, maxLeft))
currentTop.value = newTop
currentLeft.value = newLeft
if (sidebarRef.value) {
sidebarRef.value.style.top = `${newTop}px`
sidebarRef.value.style.left = `${newLeft}px`
}
}
const handleTouchEnd = () => {
isDragging.value = false
snapPosition()
if (sidebarRef.value) {
sidebarRef.value.style.left = `${currentLeft.value}px`
}
}
const captureVideoFrame = () => {
try {
@ -134,17 +36,6 @@ const captureVideoFrame = () => {
}
const handleCapture = () => {
/* const imageUrl = captureVideoFrame()
if (imageUrl) {
lastSnapshot.value=imageUrl
/!* showMinWindow(lastSnapshot.value,{
onClick:()=>{
router.replace('/')
fullLive.value=true
}
})*!/
}*/
showMinWindow1({
onClick:()=>{
router.replace('/')
@ -154,27 +45,13 @@ const handleCapture = () => {
})
}
//
onMounted(() => {
if (sidebarRef.value) {
sidebarRef.value.style.top = `${currentTop.value}px`
sidebarRef.value.style.left = `${currentLeft.value}px`
}
})
//
onBeforeUnmount(() => {
document.removeEventListener('mousemove', handleMouseMove)
document.removeEventListener('mouseup', handleMouseUp)
})
const openOne = () => {
showTang.value = true
}
const paySide = computed(() => {
//
return auctionData.value.needPayBuys?.length>0
return auctionData.value.needPayBuys?.length>0
})
const goPay = () => {
@ -188,14 +65,7 @@ const goPay = () => {
</script>
<template>
<div
ref="sidebarRef"
class="bg-white w-60px rounded-4px overflow-hidden absolute z-999 cursor-move"
@mousedown="handleMouseDown"
@touchstart="handleTouchStart"
@touchmove="handleTouchMove"
@touchend="handleTouchEnd"
>
<div class="bg-white w-60px rounded-4px overflow-hidden">
<!-- 拍品信息 -->
<van-button
class="w-60px !h-60px"
@ -250,10 +120,4 @@ const goPay = () => {
</template>
<style scoped>
.cursor-move {
cursor: move;
user-select: none;
touch-action: none;
transition: left 0.3s ease;
}
</style>

View File

@ -13,6 +13,7 @@ import {message} from "~/components/x-message/useMessage.js"
import {showConfirmDialog} from 'vant';
import {artworkBuy} from "@/api/goods/index.js"
import {useI18n} from 'vue-i18n'
import floating2 from '@/components/floating2/index.vue'
const { t } = useI18n()
const { auctionDetail,getAuctionDetail} = goodStore();
const player = ref(null)
@ -97,11 +98,16 @@ const initializePlayer = async () => {
onMounted(async () => {
await getAuctionDetail()
pullLink.value = await getLiveLink()
console.log('auctionDetail.value.isLiving',auctionDetail.value.isLiving)
if (auctionDetail.value.isLiving===1){
initializePlayer()
}
})
onActivated(()=>{
if (auctionDetail.value.isLiving===1){
initializePlayer()
}
})
onBeforeUnmount(() => {
player.value?.dispose()
player.value = null
@ -133,9 +139,13 @@ const tipOpen = () => {
<div v-if="loading1" class="absolute left-1/2 transform translate-x--1/2 top-1/2 translate-y--1/2">
<van-loading type="spinner" >直播加载中...</van-loading>
</div>
<transition name="fade">
<div v-if="fullLive">
<sideButton class="absolute top-196px right-0 z-999"></sideButton>
<floating2 top="200px">
<sideButton></sideButton>
</floating2>
<div class="absolute left-1/2 transform -translate-x-1/2 flex flex-col items-center"
style="bottom:calc(var(--safe-area-inset-bottom) + 26px)">
<div class="text-16px text-#FFB25F font-600 flex">

View File

@ -68,6 +68,7 @@ const goSignature = async () => {
</script>
<template>
<div class="bg-#EBEBEB h-screen-nav flex flex-col">
<div class="h-50px text-14px text-#191919 bg-#fff flex items-center px-21px mb-6px shrink-0">
{{ $t('signature.tips.prePayment') }}

View File

@ -133,7 +133,9 @@ export default defineNuxtConfig({
vscode: {
// 配置为 cursor 编辑器
editor: 'cursor'
}
},
enabled: true
},
typescript: {