feat(components): 新增悬浮窗组件并优化直播相关功能
- 新增悬浮窗组件,用于在直播页面显示"回到直播"按钮- 优化直播室侧边按钮,使用新的悬浮窗组件 - 修复商品详情页面的成交价显示问题 - 优化首页拍卖列表的成交价显示 -改进用户主页的拍卖信息展示 - 重构签名面板组件,使用 vue-signature-pad 替代原生实现 - 优化 nuxt 配置,启用 vscode devtools
This commit is contained in:
parent
92cddb5da5
commit
f1e4601f7c
34
app/components/floatingBubble/floating.js
Normal file
34
app/components/floatingBubble/floating.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { createApp } from 'vue'
|
||||||
|
import MinWindow from '@/components/floatingBubble/index.vue'
|
||||||
|
|
||||||
|
let minWindowInstance = null
|
||||||
|
let minWindowApp = null // 新增:保存应用实例
|
||||||
|
|
||||||
|
// 创建悬浮窗
|
||||||
|
export const showMinWindow1 = ( props = {}) => {
|
||||||
|
if (process.client){
|
||||||
|
const container = document.createElement('div')
|
||||||
|
container.className = 'floating-bubble-container' // 添加类名
|
||||||
|
document.body.appendChild(container)
|
||||||
|
const app = createApp(MinWindow, {
|
||||||
|
...props
|
||||||
|
})
|
||||||
|
|
||||||
|
minWindowApp = app // 保存应用实例
|
||||||
|
minWindowInstance = app.mount(container)
|
||||||
|
return minWindowInstance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const hideMinWindow1 = () => {
|
||||||
|
if (!minWindowApp) return
|
||||||
|
const cleanup = () => {
|
||||||
|
minWindowApp.unmount() // 使用应用实例的unmount方法
|
||||||
|
const container = document.querySelector('.floating-bubble-container') // 假设您的容器有这个类名
|
||||||
|
container && document.body.removeChild(container)
|
||||||
|
minWindowApp = null
|
||||||
|
minWindowInstance = null
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
}
|
26
app/components/floatingBubble/index.vue
Normal file
26
app/components/floatingBubble/index.vue
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<script setup>
|
||||||
|
const props=defineProps({
|
||||||
|
onClick:{
|
||||||
|
type:Function,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<van-floating-bubble
|
||||||
|
axis="xy"
|
||||||
|
magnetic="x"
|
||||||
|
:offset="{ x: 300, y: 50 }"
|
||||||
|
@click="onClick"
|
||||||
|
>
|
||||||
|
回到直播
|
||||||
|
</van-floating-bubble>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.van-floating-bubble{
|
||||||
|
width: 70px;
|
||||||
|
height: 70px;
|
||||||
|
border-radius: 5px!important;
|
||||||
|
}
|
||||||
|
</style>
|
@ -8,7 +8,7 @@ const props = defineProps({
|
|||||||
default: null
|
default: null
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
console.log(props.detailInfo)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -41,6 +41,10 @@ const props = defineProps({
|
|||||||
<div class="text-[#575757] text-[14px]">{{$t('detail.text6')}}:</div>
|
<div class="text-[#575757] text-[14px]">{{$t('detail.text6')}}:</div>
|
||||||
<div class="text-#575757 text-14px font-bold">{{detailInfo?.startPriceCurrency}} {{detailInfo?.startPrice}}</div>
|
<div class="text-#575757 text-14px font-bold">{{detailInfo?.startPriceCurrency}} {{detailInfo?.startPrice}}</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex px-[16px] bg-#fff h-[36px] items-center mb-6px" v-if="detailInfo?.soldPrice">
|
||||||
|
<div class="text-[#B58047] text-[14px]">成交价:</div>
|
||||||
|
<div class="text-#B58047 text-14px font-bold">{{detailInfo?.soldPriceCurrency}} {{detailInfo?.soldPrice}}</div>
|
||||||
|
</div>
|
||||||
<div class="px-[16px] bg-#fff pt-12px pb-18px">
|
<div class="px-[16px] bg-#fff pt-12px pb-18px">
|
||||||
<div class="text-[#575757] text-[14px] mb-4px">{{$t('detail.text7')}}:</div>
|
<div class="text-[#575757] text-[14px] mb-4px">{{$t('detail.text7')}}:</div>
|
||||||
<div v-if="detailInfo?.priceRuleType!=='diy'">
|
<div v-if="detailInfo?.priceRuleType!=='diy'">
|
||||||
|
@ -44,7 +44,7 @@ router.push('/collectCode/signature/result')
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="signature-container">
|
<div class="signature-container">
|
||||||
<div class="flex flex-col h-100vh px-20px py-20px bg-gray">
|
<div class="flex flex-col h-100vh px-20px py-20px bg-gray w-100vw">
|
||||||
<VueSignaturePad
|
<VueSignaturePad
|
||||||
width="100%"
|
width="100%"
|
||||||
class="signature bg-#fff rounded-10px mb-10px"
|
class="signature bg-#fff rounded-10px mb-10px"
|
||||||
@ -67,40 +67,3 @@ router.push('/collectCode/signature/result')
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.signature-container {
|
|
||||||
width: 100%;
|
|
||||||
height: 100vh;
|
|
||||||
/* 强制竖屏显示 */
|
|
||||||
view-transition: none;
|
|
||||||
transform: rotate(0deg) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.signature>canvas) {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 横屏适配 */
|
|
||||||
@media screen and (orientation: landscape) {
|
|
||||||
.signature-container {
|
|
||||||
/* 在横屏时保持竖屏宽度 */
|
|
||||||
max-width: 100vh;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex {
|
|
||||||
/* 确保在横屏时内容不会过宽 */
|
|
||||||
max-width: 100vh;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 确保签名板在各种屏幕尺寸下都能正常显示 */
|
|
||||||
.signature {
|
|
||||||
flex: 1;
|
|
||||||
min-height: 60vh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -87,7 +87,7 @@ const openShow = async (item) => {
|
|||||||
v-if="item.soldPrice"
|
v-if="item.soldPrice"
|
||||||
class="mt-[4px] text-[12px] text-[#b58047]"
|
class="mt-[4px] text-[12px] text-[#b58047]"
|
||||||
>
|
>
|
||||||
{{ $t('home.close_price') }}:{{ item?.startPrice??0 }}
|
{{ $t('home.close_price') }}:{{ item?.soldPrice??0 }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,6 +5,7 @@ import ItemList from './components/ItemList/index.vue'
|
|||||||
import Cescribe from './components/Cescribe/index.vue'
|
import Cescribe from './components/Cescribe/index.vue'
|
||||||
import {message} from '@/components/x-message/useMessage.js'
|
import {message} from '@/components/x-message/useMessage.js'
|
||||||
import {liveStore} from "~/stores/live/index.js";
|
import {liveStore} from "~/stores/live/index.js";
|
||||||
|
import { showMinWindow1} from "~/components/floatingBubble/floating.js";
|
||||||
|
|
||||||
const {getAuctionDetail, auctionDetail} = goodStore();
|
const {getAuctionDetail, auctionDetail} = goodStore();
|
||||||
const {fullLive} = liveStore()
|
const {fullLive} = liveStore()
|
||||||
@ -13,6 +14,7 @@ const changeLive = () => {
|
|||||||
fullLive.value = true;
|
fullLive.value = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await getAuctionDetail()
|
await getAuctionDetail()
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
|
@ -8,6 +8,7 @@ import tangPopup from './tangPopup.vue'
|
|||||||
import { goodStore } from "@/stores/goods/index.js"
|
import { goodStore } from "@/stores/goods/index.js"
|
||||||
import { authStore } from "~/stores/auth/index.js"
|
import { authStore } from "~/stores/auth/index.js"
|
||||||
import {showMinWindow} from "~/components/liveMinWindow/createMinWindow.js";
|
import {showMinWindow} from "~/components/liveMinWindow/createMinWindow.js";
|
||||||
|
import {hideMinWindow1, showMinWindow1} from "~/components/floatingBubble/floating.js";
|
||||||
|
|
||||||
const { quoteStatus, changeStatus, show, auctionData, getSocketData ,lastSnapshot,fullLive} = liveStore()
|
const { quoteStatus, changeStatus, show, auctionData, getSocketData ,lastSnapshot,fullLive} = liveStore()
|
||||||
const { pageRef } = goodStore()
|
const { pageRef } = goodStore()
|
||||||
@ -135,17 +136,25 @@ const captureVideoFrame = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleCapture = () => {
|
const handleCapture = () => {
|
||||||
const imageUrl = captureVideoFrame()
|
/* const imageUrl = captureVideoFrame()
|
||||||
if (imageUrl) {
|
if (imageUrl) {
|
||||||
lastSnapshot.value=imageUrl
|
lastSnapshot.value=imageUrl
|
||||||
showMinWindow(lastSnapshot.value,{
|
|
||||||
|
/!* showMinWindow(lastSnapshot.value,{
|
||||||
onClick:()=>{
|
onClick:()=>{
|
||||||
router.replace('/')
|
router.replace('/')
|
||||||
fullLive.value=true
|
fullLive.value=true
|
||||||
console.log('执行')
|
console.log('执行')
|
||||||
}
|
}
|
||||||
})
|
})*!/
|
||||||
}
|
}*/
|
||||||
|
showMinWindow1({
|
||||||
|
onClick:()=>{
|
||||||
|
router.replace('/')
|
||||||
|
fullLive.value=true
|
||||||
|
hideMinWindow1()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 组件挂载时添加事件监听
|
// 组件挂载时添加事件监听
|
||||||
@ -167,18 +176,14 @@ const openOne = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const paySide = computed(() => {
|
const paySide = computed(() => {
|
||||||
//当前是否已成交,以及成交人是当前登录用户
|
//当前是否已成交
|
||||||
if (auctionData.value.artwork?.isSoled && auctionData.value.artwork?.buyInfo?.userID === userInfo.value.ID) {
|
return auctionData.value.needPayBuys?.length>0
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const goPay = () => {
|
const goPay = () => {
|
||||||
payment.value.leftCurrency=auctionData.value?.nowAuctionPrice?.currency
|
payment.value.leftCurrency=auctionData.value.needPayBuys?.[0]?.leftCurrency
|
||||||
payment.value.leftPrice=auctionData.value?.nowAuctionPrice?.successPrice
|
payment.value.leftPrice=auctionData.value.needPayBuys?.[0]?.leftPrice
|
||||||
payment.value.buyUid=auctionData.value?.nowAuctionPrice?.successBuyUuid
|
payment.value.buyUid=auctionData.value.needPayBuys?.[0]?.uuid
|
||||||
handleCapture()
|
handleCapture()
|
||||||
router.push('/signature/protocol')
|
router.push('/signature/protocol')
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,7 @@ watch(()=>props.show,(newValue)=>{
|
|||||||
loading="lazy"
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
<div class="w-45px h-17px bg-#2B53AC text-12px line-height-none flex justify-center items-center absolute top-2px left-2px text-#fff">LOT{{item.index}}</div>
|
<div class="w-45px h-17px bg-#2B53AC text-12px line-height-none flex justify-center items-center absolute top-2px left-2px text-#fff">LOT{{item.index}}</div>
|
||||||
<div v-show="auctionData.artwork.index===item?.index" class="w-80px h-20px bg-#B58047 flex line-height-none justify-center items-center text-#fff text-12px bottom-0 absolute blink">{{ $t('live_room.cast') }}</div>
|
<div v-show="auctionData?.artwork?.index===item?.index" class="w-80px h-20px bg-#B58047 flex line-height-none justify-center items-center text-#fff text-12px bottom-0 absolute blink">{{ $t('live_room.cast') }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="ellipsis line-height-20px text-16px font-600 min-h-40px">
|
<div class="ellipsis line-height-20px text-16px font-600 min-h-40px">
|
||||||
|
@ -151,7 +151,7 @@ nextTick(()=>{
|
|||||||
const goBuy = async () => {
|
const goBuy = async () => {
|
||||||
const res = await artworkBuy({
|
const res = await artworkBuy({
|
||||||
auctionArtworkUuid: auctionData.value?.artwork?.uuid,
|
auctionArtworkUuid: auctionData.value?.artwork?.uuid,
|
||||||
buyMoney: String(auctionData.value?.nowAuctionPrice?.nextPrice ?? 0)
|
buyMoney: String(auctionData?.value.nowAuctionPrice?.nowPrice??0)
|
||||||
})
|
})
|
||||||
if (res.status === 0) {
|
if (res.status === 0) {
|
||||||
message.success(t('live_room.success_mess'))
|
message.success(t('live_room.success_mess'))
|
||||||
|
@ -122,7 +122,7 @@ fetchData()
|
|||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<div>
|
<div>
|
||||||
<div class="text-#575757 text-14px line-height-none mb-5px">
|
<div class="text-#575757 text-14px line-height-none mb-5px">
|
||||||
{{ $t('home.start_price') }}:{{item.auctionArtworkInfo?.soldPriceCurrency}} {{item.auctionArtworkInfo?.soldPrice}}
|
{{ $t('home.start_price') }}:{{item.auctionArtworkInfo?.startPriceCurrency}} {{item.auctionArtworkInfo?.startPrice}}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-#B58047 text-14px line-height-none">
|
<div class="text-#B58047 text-14px line-height-none">
|
||||||
{{ $t('home.close_price') }}:{{item.baseCurrency}} {{item.baseMoney}}
|
{{ $t('home.close_price') }}:{{item.baseCurrency}} {{item.baseMoney}}
|
||||||
|
@ -2,12 +2,12 @@
|
|||||||
import {showToast} from 'vant';
|
import {showToast} from 'vant';
|
||||||
import {onMounted, onUnmounted, ref} from 'vue';
|
import {onMounted, onUnmounted, ref} from 'vue';
|
||||||
import {signOffline, signOnline} from "~/api/goods/index.js";
|
import {signOffline, signOnline} from "~/api/goods/index.js";
|
||||||
|
import {VueSignaturePad} from "vue-signature-pad";
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
layout: ''
|
layout: ''
|
||||||
})
|
})
|
||||||
|
const signaturePad = ref(null);
|
||||||
const signaturePadRef = ref(null);
|
|
||||||
const isLandscapeMode = ref(false);
|
const isLandscapeMode = ref(false);
|
||||||
|
|
||||||
const checkScreenOrientation = () => {
|
const checkScreenOrientation = () => {
|
||||||
@ -37,16 +37,16 @@ onUnmounted(() => {
|
|||||||
const imgUrl = ref('')
|
const imgUrl = ref('')
|
||||||
const show = ref(false)
|
const show = ref(false)
|
||||||
const clearSignature = () => {
|
const clearSignature = () => {
|
||||||
signaturePadRef.value?.resize();
|
signaturePad.value?.clearSignature();
|
||||||
signaturePadRef.value?.clear();
|
|
||||||
};
|
};
|
||||||
const submitSignature = () => {
|
const submitSignature = () => {
|
||||||
signaturePadRef.value?.submit();
|
if (signaturePad.value?.isEmpty()) {
|
||||||
};
|
showToast('请先签名');
|
||||||
|
return;
|
||||||
const handleSignatureSubmit = async (data) => {
|
}
|
||||||
imgUrl.value = data.image
|
const { data } = signaturePad.value?.saveSignature(); // 返回 base64 格式的图片数据
|
||||||
show.value = true
|
imgUrl.value = data;
|
||||||
|
show.value = true;
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
const overlay = document.querySelector('.signature-container .van-overlay');
|
const overlay = document.querySelector('.signature-container .van-overlay');
|
||||||
if (overlay) {
|
if (overlay) {
|
||||||
@ -56,7 +56,6 @@ const handleSignatureSubmit = async (data) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirm = async () => {
|
const confirm = async () => {
|
||||||
const res = await signOnline({
|
const res = await signOnline({
|
||||||
signImgFileData: imgUrl.value
|
signImgFileData: imgUrl.value
|
||||||
@ -71,15 +70,15 @@ router.back()
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="signature-container">
|
<div class="signature-container bg-gray ">
|
||||||
<template v-if="isLandscapeMode">
|
<template v-if="isLandscapeMode">
|
||||||
<div class="signature-content">
|
<div class="signature-content px-10px py-10px">
|
||||||
<van-signature
|
<VueSignaturePad
|
||||||
class="signature-pad"
|
width="100%"
|
||||||
ref="signaturePadRef"
|
class="signature bg-#fff rounded-10px mb-10px"
|
||||||
@submit="handleSignatureSubmit"
|
ref="signaturePad"
|
||||||
/>
|
/>
|
||||||
<div class="control-buttons">
|
<div class="control-buttons justify-evenly">
|
||||||
<van-button
|
<van-button
|
||||||
class="control-button"
|
class="control-button"
|
||||||
size="mini"
|
size="mini"
|
||||||
@ -122,7 +121,6 @@ router.back()
|
|||||||
.signature-container {
|
.signature-container {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background-color: #fff;
|
|
||||||
padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) 0;
|
padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,18 +138,11 @@ router.back()
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.van-overlay) {
|
|
||||||
/* left: initial;*/
|
|
||||||
}
|
|
||||||
|
|
||||||
.signature-pad {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.control-buttons {
|
.control-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 20px 10px 0;
|
padding: 0 10px 0;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,7 +160,4 @@ router.back()
|
|||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.van-signature__footer) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
@ -118,7 +118,10 @@ export default defineNuxtConfig({
|
|||||||
},
|
},
|
||||||
|
|
||||||
devtools: {
|
devtools: {
|
||||||
enabled: true,
|
vscode: {
|
||||||
|
// 配置为 cursor 编辑器
|
||||||
|
editor: 'cursor'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
typescript: {
|
typescript: {
|
||||||
|
Loading…
Reference in New Issue
Block a user