feat(signature): 实现离线签名功能并优化相关页面
- 新增离线签名 API 接口 - 实现离线签名面板页面,包括签名、清空、确认等功能 - 添加屏幕旋转检测和处理逻辑 - 优化签名协议页面,增加同意并签字按钮 - 移除冗余组件和代码
This commit is contained in:
parent
0c2b973419
commit
4bb0f318e3
@ -53,4 +53,12 @@ export async function logSendlog(data) {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
export async function signOffline(data) {
|
||||||
|
|
||||||
|
return await request( {
|
||||||
|
url:'/api/v1/contract/sign-offline',
|
||||||
|
method: 'POST',
|
||||||
|
data
|
||||||
|
})
|
||||||
}
|
}
|
@ -1,181 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="signature-pad-container">
|
|
||||||
<canvas
|
|
||||||
ref="canvasRef"
|
|
||||||
class="signature-pad"
|
|
||||||
:style="{
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
backgroundColor: '#fff',
|
|
||||||
border: '1px solid #e5e5e5',
|
|
||||||
borderRadius: '4px'
|
|
||||||
}"
|
|
||||||
@touchstart="handleStart"
|
|
||||||
@touchmove="handleMove"
|
|
||||||
@touchend="handleEnd"
|
|
||||||
@mousedown="handleStart"
|
|
||||||
@mousemove="handleMove"
|
|
||||||
@mouseup="handleEnd"
|
|
||||||
@mouseleave="handleEnd"
|
|
||||||
></canvas>
|
|
||||||
<div class="signature-controls">
|
|
||||||
<van-button
|
|
||||||
type="default"
|
|
||||||
size="small"
|
|
||||||
@click="clearCanvas"
|
|
||||||
>清除</van-button>
|
|
||||||
<van-button
|
|
||||||
type="primary"
|
|
||||||
size="small"
|
|
||||||
@click="handleConfirm"
|
|
||||||
>确认</van-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
modelValue: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue', 'change'])
|
|
||||||
|
|
||||||
const canvasRef = ref(null)
|
|
||||||
const ctx = ref(null)
|
|
||||||
const isDrawing = ref(false)
|
|
||||||
const lastX = ref(0)
|
|
||||||
const lastY = ref(0)
|
|
||||||
const LINE_WIDTH = 2 // 固定画笔粗细
|
|
||||||
|
|
||||||
// 初始化画布
|
|
||||||
const initCanvas = () => {
|
|
||||||
const canvas = canvasRef.value
|
|
||||||
const dpr = window.devicePixelRatio || 1
|
|
||||||
const rect = canvas.getBoundingClientRect()
|
|
||||||
|
|
||||||
// 设置画布的实际大小
|
|
||||||
canvas.width = rect.width * dpr
|
|
||||||
canvas.height = rect.height * dpr
|
|
||||||
|
|
||||||
ctx.value = canvas.getContext('2d')
|
|
||||||
|
|
||||||
// 缩放画布以匹配设备像素比
|
|
||||||
ctx.value.scale(dpr, dpr)
|
|
||||||
ctx.value.lineCap = 'round'
|
|
||||||
ctx.value.lineJoin = 'round'
|
|
||||||
ctx.value.strokeStyle = '#000'
|
|
||||||
ctx.value.lineWidth = LINE_WIDTH
|
|
||||||
}
|
|
||||||
|
|
||||||
// 开始绘制
|
|
||||||
const handleStart = (e) => {
|
|
||||||
e.preventDefault() // 防止页面滚动
|
|
||||||
isDrawing.value = true
|
|
||||||
const point = getPoint(e)
|
|
||||||
lastX.value = point.x
|
|
||||||
lastY.value = point.y
|
|
||||||
}
|
|
||||||
|
|
||||||
// 绘制中
|
|
||||||
const handleMove = (e) => {
|
|
||||||
if (!isDrawing.value) return
|
|
||||||
e.preventDefault() // 防止页面滚动
|
|
||||||
|
|
||||||
const point = getPoint(e)
|
|
||||||
ctx.value.beginPath()
|
|
||||||
ctx.value.moveTo(lastX.value, lastY.value)
|
|
||||||
ctx.value.lineTo(point.x, point.y)
|
|
||||||
ctx.value.stroke()
|
|
||||||
|
|
||||||
lastX.value = point.x
|
|
||||||
lastY.value = point.y
|
|
||||||
}
|
|
||||||
|
|
||||||
// 结束绘制
|
|
||||||
const handleEnd = () => {
|
|
||||||
isDrawing.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取触点坐标
|
|
||||||
const getPoint = (e) => {
|
|
||||||
const canvas = canvasRef.value
|
|
||||||
const rect = canvas.getBoundingClientRect()
|
|
||||||
const dpr = window.devicePixelRatio || 1
|
|
||||||
const event = e.touches ? e.touches[0] : e
|
|
||||||
|
|
||||||
// 计算实际的触点位置
|
|
||||||
const x = (event.clientX - rect.left)
|
|
||||||
const y = (event.clientY - rect.top)
|
|
||||||
|
|
||||||
return {
|
|
||||||
x: x,
|
|
||||||
y: y
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清除画布
|
|
||||||
const clearCanvas = () => {
|
|
||||||
const canvas = canvasRef.value
|
|
||||||
ctx.value.clearRect(0, 0, canvas.width, canvas.height)
|
|
||||||
emit('update:modelValue', '')
|
|
||||||
emit('change', '')
|
|
||||||
}
|
|
||||||
|
|
||||||
// 确认并生成图片
|
|
||||||
const handleConfirm = () => {
|
|
||||||
const canvas = canvasRef.value
|
|
||||||
const imageData = canvas.toDataURL('image/png')
|
|
||||||
emit('update:modelValue', imageData)
|
|
||||||
emit('change', imageData)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听屏幕旋转
|
|
||||||
const handleResize = () => {
|
|
||||||
const canvas = canvasRef.value
|
|
||||||
const imageData = canvas.toDataURL('image/png')
|
|
||||||
initCanvas()
|
|
||||||
// 恢复之前的内容
|
|
||||||
const img = new Image()
|
|
||||||
img.onload = () => {
|
|
||||||
ctx.value.drawImage(img, 0, 0, canvas.width, canvas.height)
|
|
||||||
}
|
|
||||||
img.src = imageData
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
initCanvas()
|
|
||||||
window.addEventListener('resize', handleResize)
|
|
||||||
})
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
window.removeEventListener('resize', handleResize)
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.signature-pad-container {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.signature-pad {
|
|
||||||
flex: 1;
|
|
||||||
touch-action: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.signature-controls {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-end;
|
|
||||||
gap: 16px;
|
|
||||||
padding: 8px;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,119 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div
|
|
||||||
class="draggable-container"
|
|
||||||
:style="containerStyle"
|
|
||||||
@mousedown="startDrag"
|
|
||||||
@touchstart.prevent="startDrag"
|
|
||||||
>
|
|
||||||
<slot />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
const props = defineProps({
|
|
||||||
fixedSide: {
|
|
||||||
type: String,
|
|
||||||
default: 'none',
|
|
||||||
validator: (v) => ['left', 'right', 'top', 'bottom', 'none'].includes(v)
|
|
||||||
},
|
|
||||||
initialPosition: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({ x: 0, y: 0 })
|
|
||||||
},
|
|
||||||
width: Number,
|
|
||||||
height: Number
|
|
||||||
})
|
|
||||||
|
|
||||||
const position = ref({ ...props.initialPosition })
|
|
||||||
const isDragging = ref(false)
|
|
||||||
const startPosition = ref({ x: 0, y: 0 })
|
|
||||||
|
|
||||||
const containerStyle = computed(() => ({
|
|
||||||
position: 'fixed',
|
|
||||||
width: props.width ? `${props.width}px` : 'auto',
|
|
||||||
height: props.height ? `${props.height}px` : 'auto',
|
|
||||||
left: `${position.value.x}px`,
|
|
||||||
top: `${position.value.y}px`,
|
|
||||||
cursor: isDragging.value ? 'grabbing' : 'grab'
|
|
||||||
}))
|
|
||||||
|
|
||||||
const getClientPos = (e) => ({
|
|
||||||
x: e.touches ? e.touches[0].clientX : e.clientX,
|
|
||||||
y: e.touches ? e.touches[0].clientY : e.clientY
|
|
||||||
})
|
|
||||||
|
|
||||||
const startDrag = (e) => {
|
|
||||||
isDragging.value = true
|
|
||||||
const { x, y } = getClientPos(e)
|
|
||||||
startPosition.value = {
|
|
||||||
x: x - position.value.x,
|
|
||||||
y: y - position.value.y
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const onDrag = (e) => {
|
|
||||||
if (!isDragging.value) return
|
|
||||||
|
|
||||||
const { x: clientX, y: clientY } = getClientPos(e)
|
|
||||||
const maxX = window.innerWidth - (props.width || 0)
|
|
||||||
const maxY = window.innerHeight - (props.height || 0)
|
|
||||||
|
|
||||||
let newX = clientX - startPosition.value.x
|
|
||||||
let newY = clientY - startPosition.value.y
|
|
||||||
|
|
||||||
// 根据固定边限制移动方向
|
|
||||||
switch (props.fixedSide) {
|
|
||||||
case 'left':
|
|
||||||
newX = 0
|
|
||||||
newY = Math.max(0, Math.min(newY, maxY))
|
|
||||||
break
|
|
||||||
case 'right':
|
|
||||||
newX = maxX
|
|
||||||
newY = Math.max(0, Math.min(newY, maxY))
|
|
||||||
break
|
|
||||||
case 'top':
|
|
||||||
newX = Math.max(0, Math.min(newX, maxX))
|
|
||||||
newY = 0
|
|
||||||
break
|
|
||||||
case 'bottom':
|
|
||||||
newX = Math.max(0, Math.min(newX, maxX))
|
|
||||||
newY = maxY
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
newX = Math.max(0, Math.min(newX, maxX))
|
|
||||||
newY = Math.max(0, Math.min(newY, maxY))
|
|
||||||
}
|
|
||||||
|
|
||||||
position.value = {
|
|
||||||
x: ['top', 'bottom', 'none'].includes(props.fixedSide) ? newX : position.value.x,
|
|
||||||
y: ['left', 'right', 'none'].includes(props.fixedSide) ? newY : position.value.y
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const stopDrag = () => {
|
|
||||||
isDragging.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// 事件监听
|
|
||||||
onMounted(() => {
|
|
||||||
document.addEventListener('mousemove', onDrag)
|
|
||||||
document.addEventListener('mouseup', stopDrag)
|
|
||||||
document.addEventListener('touchmove', onDrag)
|
|
||||||
document.addEventListener('touchend', stopDrag)
|
|
||||||
})
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
document.removeEventListener('mousemove', onDrag)
|
|
||||||
document.removeEventListener('mouseup', stopDrag)
|
|
||||||
document.removeEventListener('touchmove', onDrag)
|
|
||||||
document.removeEventListener('touchend', stopDrag)
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.draggable-container {
|
|
||||||
touch-action: none;
|
|
||||||
user-select: none;
|
|
||||||
z-index: 9999;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -2,8 +2,7 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { goodStore } from "@/stores/goods"
|
import { goodStore } from "@/stores/goods"
|
||||||
import DetailPopup from '../DetailPopup/index.vue'
|
import DetailPopup from '../DetailPopup/index.vue'
|
||||||
import MasonryWall from '@yeger/vue-masonry-wall'
|
import WaterfallFlow from '@/components/waterfallFlow/index.vue'
|
||||||
import WaterfallFlow from '@/components/WaterfallFlow.vue'
|
|
||||||
const {
|
const {
|
||||||
itemList,
|
itemList,
|
||||||
pageRef,
|
pageRef,
|
||||||
|
@ -51,7 +51,7 @@ const onRefresh = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 导航函数
|
// 导航函数
|
||||||
const goPay = () => router.push('/signature/personal-Info')
|
const goPay = () => router.push('/signature/protocol')
|
||||||
const goDetail = (item) => router.push({ path: '/artDetail', query: { uuid: item.uuid } })
|
const goDetail = (item) => router.push({ path: '/artDetail', query: { uuid: item.uuid } })
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
|
@ -1,18 +1,173 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
const image = ref('');
|
import {showToast} from 'vant';
|
||||||
import { showToast } from 'vant';
|
import {onMounted, onUnmounted, ref} from 'vue';
|
||||||
|
import {signOffline} from "~/api/goods/index.js";
|
||||||
|
const router = useRouter();
|
||||||
|
definePageMeta({
|
||||||
|
layout: ''
|
||||||
|
})
|
||||||
|
|
||||||
const onSubmit = (data) => {
|
const signaturePadRef = ref(null);
|
||||||
image.value = data.image;
|
const isLandscapeMode = ref(false);
|
||||||
|
|
||||||
|
const checkScreenOrientation = () => {
|
||||||
|
|
||||||
|
const orientation = screen.orientation?.type || window.orientation;
|
||||||
|
if (orientation === 'landscape-primary' || orientation === 'landscape-secondary' ||
|
||||||
|
orientation === 90 || orientation === -90) {
|
||||||
|
isLandscapeMode.value = true;
|
||||||
|
} else {
|
||||||
|
isLandscapeMode.value = false;
|
||||||
|
showToast('请将手机横屏使用');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
const onClear = () => showToast('clear');
|
|
||||||
|
onMounted(() => {
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
checkScreenOrientation();
|
||||||
|
});
|
||||||
|
window.addEventListener('orientationchange', checkScreenOrientation);
|
||||||
|
screen.orientation?.addEventListener('change', checkScreenOrientation);
|
||||||
|
});
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('orientationchange', checkScreenOrientation);
|
||||||
|
screen.orientation?.removeEventListener('change', checkScreenOrientation);
|
||||||
|
});
|
||||||
|
const imgUrl = ref('')
|
||||||
|
const show = ref(false)
|
||||||
|
const clearSignature = () => {
|
||||||
|
signaturePadRef.value?.resize();
|
||||||
|
signaturePadRef.value?.clear();
|
||||||
|
};
|
||||||
|
const submitSignature = () => {
|
||||||
|
signaturePadRef.value?.submit();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSignatureSubmit = async (data) => {
|
||||||
|
imgUrl.value = data.image
|
||||||
|
show.value = true
|
||||||
|
nextTick(() => {
|
||||||
|
const overlay = document.querySelector('.signature-container .van-overlay');
|
||||||
|
if (overlay) {
|
||||||
|
overlay.style.width = '100vw';
|
||||||
|
overlay.style.left = '0';
|
||||||
|
overlay.style.right = '0';
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
const confirm = async () => {
|
||||||
|
const res = await signOffline({
|
||||||
|
signImgFileData: data.image
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('res', res)
|
||||||
|
}
|
||||||
|
const goBack = () => {
|
||||||
|
router.back()
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<van-signature @submit="onSubmit" @clear="onClear" />
|
<div class="signature-container">
|
||||||
<van-image v-if="image" :src="image" />
|
<template v-if="isLandscapeMode">
|
||||||
|
<div class="signature-content">
|
||||||
|
<van-signature
|
||||||
|
class="signature-pad"
|
||||||
|
ref="signaturePadRef"
|
||||||
|
@submit="handleSignatureSubmit"
|
||||||
|
/>
|
||||||
|
<div class="control-buttons">
|
||||||
|
<van-button
|
||||||
|
class="control-button"
|
||||||
|
size="mini"
|
||||||
|
type="primary"
|
||||||
|
@click="goBack"
|
||||||
|
>
|
||||||
|
返回
|
||||||
|
</van-button>
|
||||||
|
<van-button
|
||||||
|
class="control-button"
|
||||||
|
size="mini"
|
||||||
|
type="warning"
|
||||||
|
@click="clearSignature"
|
||||||
|
>
|
||||||
|
清空
|
||||||
|
</van-button>
|
||||||
|
<van-button
|
||||||
|
class="control-button"
|
||||||
|
size="mini"
|
||||||
|
type="primary"
|
||||||
|
@click="submitSignature"
|
||||||
|
>
|
||||||
|
确认
|
||||||
|
</van-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div class="orientation-hint">
|
||||||
|
<p>请将手机横屏使用</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<van-dialog v-model:show="show" class="signature-dialog" show-cancel-button @confirm="confirm">
|
||||||
|
<img class="h-100px" :src="imgUrl"/>
|
||||||
|
</van-dialog>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.signature-container {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.van-button--mini+.van-button--mini) {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.van-dialog__content) {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature-content {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.van-overlay) {
|
||||||
|
/* left: initial;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature-pad {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 20px 10px 0;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-button {
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.orientation-hint {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: #fff;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.van-signature__footer) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
@ -4,6 +4,12 @@ definePageMeta({
|
|||||||
layout: 'default',
|
layout: 'default',
|
||||||
title: '签署内容'
|
title: '签署内容'
|
||||||
})
|
})
|
||||||
|
const router = useRouter();
|
||||||
|
const goSignature=()=>{
|
||||||
|
router.push({
|
||||||
|
path:'/signature/panel'
|
||||||
|
})
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -30,7 +36,7 @@ definePageMeta({
|
|||||||
</van-collapse-item>
|
</van-collapse-item>
|
||||||
</van-collapse>
|
</van-collapse>
|
||||||
<div class="h-81px bg-#fff flex justify-center pt-7px border-t">
|
<div class="h-81px bg-#fff flex justify-center pt-7px border-t">
|
||||||
<van-button color="#2B53AC" class="w-213px van-btn-h-38px">同意并签字</van-button>
|
<van-button color="#2B53AC" class="w-213px van-btn-h-38px" @click="goSignature">同意并签字</van-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -109,6 +109,46 @@ export const liveStore = createGlobalState(() => {
|
|||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
// 定义常量
|
||||||
|
const WS_TYPES = {
|
||||||
|
TIP: 'tip',
|
||||||
|
STOP_ARTWORK: 'stopArtwor',
|
||||||
|
OVER: 'over'
|
||||||
|
}
|
||||||
|
|
||||||
|
const TIP_TYPES = {
|
||||||
|
FALLING: 'falling',
|
||||||
|
OTHERS_BID: 'othersBid',
|
||||||
|
SUCCESS_BID: 'successBid',
|
||||||
|
ARTWORK_OVER: 'artworkOver',
|
||||||
|
FAIL_BID: 'failBid'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 基础消息配置
|
||||||
|
const BASE_MESSAGE_STYLE = {
|
||||||
|
width: '151px',
|
||||||
|
bottom: '265px'
|
||||||
|
}
|
||||||
|
const createMessageConfig = (text, color, subText = '', extraStyle = {}) => ({
|
||||||
|
title: {
|
||||||
|
text,
|
||||||
|
color,
|
||||||
|
align: 'center'
|
||||||
|
},
|
||||||
|
icon: false,
|
||||||
|
...(subText && {
|
||||||
|
subTitle: {
|
||||||
|
text: subText,
|
||||||
|
color: '#939393',
|
||||||
|
align: 'center'
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
style: {
|
||||||
|
...BASE_MESSAGE_STYLE,
|
||||||
|
...extraStyle
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const getSocketData = async () => {
|
const getSocketData = async () => {
|
||||||
const wsClient = new WebSocketClient(
|
const wsClient = new WebSocketClient(
|
||||||
config.public.NUXT_PUBLIC_SOCKET_URL,
|
config.public.NUXT_PUBLIC_SOCKET_URL,
|
||||||
@ -118,119 +158,67 @@ export const liveStore = createGlobalState(() => {
|
|||||||
auctionUuid: auctionDetail.value.uuid,
|
auctionUuid: auctionDetail.value.uuid,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 处理消息提示
|
||||||
|
const handleTipMessage = (tipType) => {
|
||||||
|
const tipConfigs = {
|
||||||
|
[TIP_TYPES.FALLING]: () =>
|
||||||
|
message.warning(createMessageConfig(t('live_room.text1'), '#F09F1F')),
|
||||||
|
|
||||||
|
[TIP_TYPES.OTHERS_BID]: () =>
|
||||||
|
message.error(createMessageConfig(t('live_room.text2'), '#CF3050', t('live_room.text3'))),
|
||||||
|
|
||||||
|
[TIP_TYPES.SUCCESS_BID]: () =>
|
||||||
|
message.success(createMessageConfig(t('live_room.text4'), '#18A058', t('live_room.text5'))),
|
||||||
|
|
||||||
|
[TIP_TYPES.ARTWORK_OVER]: () =>
|
||||||
|
message.success(createMessageConfig(
|
||||||
|
t('live_room.text6'),
|
||||||
|
'#575757',
|
||||||
|
t('live_room.text7'),
|
||||||
|
{ backgroundColor: '#fff', borderColor: '#fff' }
|
||||||
|
)),
|
||||||
|
|
||||||
|
[TIP_TYPES.FAIL_BID]: () =>
|
||||||
|
message.error(createMessageConfig(
|
||||||
|
t('live_room.text8'),
|
||||||
|
'#CF3050',
|
||||||
|
t('live_room.text9'),
|
||||||
|
{ width: '186px' }
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
const handler = tipConfigs[tipType]
|
||||||
|
if (handler) handler()
|
||||||
|
}
|
||||||
|
|
||||||
|
// WebSocket 事件处理
|
||||||
ws.onOpen(() => {
|
ws.onOpen(() => {
|
||||||
console.log('WebSocket connected')
|
console.log('WebSocket connected')
|
||||||
})
|
})
|
||||||
|
|
||||||
ws.onMessage((data) => {
|
ws.onMessage((data) => {
|
||||||
auctionData.value = data.data
|
auctionData.value = data.data
|
||||||
if (data.data?.wsType === 'tip' ) {
|
const { wsType, tip } = data.data || {}
|
||||||
if (data.data?.tip?.tipType === 'falling'){
|
|
||||||
message.warning({
|
|
||||||
title: {
|
|
||||||
text: t('live_room.text1'),
|
|
||||||
color: '#F09F1F',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
style: {
|
|
||||||
width: '151px',
|
|
||||||
bottom: '230px',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}else if (data.data?.tip?.tipType === 'othersBid'){
|
|
||||||
message.error({
|
|
||||||
title: {
|
|
||||||
text: t('live_room.text2'),
|
|
||||||
color: '#CF3050',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
icon:false,
|
|
||||||
subTitle:{
|
|
||||||
text:t('live_room.text3'),
|
|
||||||
color: '#939393',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
style: {
|
|
||||||
width: '151px',
|
|
||||||
bottom: '230px'
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}else if (data.data?.tip?.tipType === 'successBid'){
|
|
||||||
message.success({
|
|
||||||
title: {
|
|
||||||
text: t('live_room.text4'),
|
|
||||||
color: '#18A058',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
icon:false,
|
|
||||||
subTitle:{
|
|
||||||
text:t('live_room.text5'),
|
|
||||||
color: '#939393',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
style: {
|
|
||||||
width: '151px',
|
|
||||||
bottom: '230px'
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}else if (data.data?.tip?.tipType === 'artworkOver'){
|
|
||||||
message.success({
|
|
||||||
title: {
|
|
||||||
text: t('live_room.text6'),
|
|
||||||
color: '#575757',
|
|
||||||
align: 'center',
|
|
||||||
|
|
||||||
},
|
switch (wsType) {
|
||||||
icon:false,
|
case WS_TYPES.TIP:
|
||||||
subTitle:{
|
handleTipMessage(tip?.tipType)
|
||||||
text:t('live_room.text7'),
|
break
|
||||||
color: '#939393',
|
case WS_TYPES.STOP_ARTWORK:
|
||||||
align: 'center',
|
quoteStatus.value = false
|
||||||
},
|
break
|
||||||
style: {
|
case WS_TYPES.OVER:
|
||||||
width: '151px',
|
message.success(createMessageConfig(
|
||||||
bottom: '230px',
|
t('live_room.text10'),
|
||||||
|
'#575757',
|
||||||
|
'',
|
||||||
|
{
|
||||||
|
width: '195px',
|
||||||
backgroundColor: '#fff',
|
backgroundColor: '#fff',
|
||||||
borderColor:'#fff'
|
borderColor: '#fff'
|
||||||
},
|
}
|
||||||
})
|
))
|
||||||
}else if (data.data?.tip?.tipType === 'failBid'){
|
break
|
||||||
message.error({
|
|
||||||
title: {
|
|
||||||
text: t('live_room.text8'),
|
|
||||||
color: '#CF3050',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
icon:false,
|
|
||||||
subTitle:{
|
|
||||||
text: t('live_room.text9'),
|
|
||||||
color: '#939393',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
style: {
|
|
||||||
width: '186px',
|
|
||||||
bottom: '230px'
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}else if (data.data?.wsType==='stopArtwor'){
|
|
||||||
quoteStatus.value=false
|
|
||||||
}else if (data.data?.wsType==='over'){
|
|
||||||
message.success({
|
|
||||||
title: {
|
|
||||||
text: t('live_room.text10'),
|
|
||||||
color: '#575757',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
icon:false,
|
|
||||||
style: {
|
|
||||||
width: '195px',
|
|
||||||
bottom: '230px',
|
|
||||||
backgroundColor: '#fff',
|
|
||||||
borderColor:'#fff'
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('onmessage', data)
|
console.log('onmessage', data)
|
||||||
|
@ -49,6 +49,7 @@ export default defineNuxtConfig({
|
|||||||
rootContainingBlockSelectorList: [
|
rootContainingBlockSelectorList: [
|
||||||
'van-tabbar',
|
'van-tabbar',
|
||||||
'van-popup',
|
'van-popup',
|
||||||
|
'van-overlay',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -22,7 +22,6 @@
|
|||||||
"@fingerprintjs/fingerprintjs": "^4.5.1",
|
"@fingerprintjs/fingerprintjs": "^4.5.1",
|
||||||
"@nuxtjs/i18n": "^9.1.1",
|
"@nuxtjs/i18n": "^9.1.1",
|
||||||
"@vueuse/core": "^12.4.0",
|
"@vueuse/core": "^12.4.0",
|
||||||
"@yeger/vue-masonry-wall": "^5.0.17",
|
|
||||||
"aliyun-aliplayer": "^2.28.5",
|
"aliyun-aliplayer": "^2.28.5",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
@ -32,10 +31,10 @@
|
|||||||
"pinyin": "4.0.0-alpha.2",
|
"pinyin": "4.0.0-alpha.2",
|
||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
"segmentit": "^2.0.3",
|
"segmentit": "^2.0.3",
|
||||||
|
"tslib": "^2.6.0",
|
||||||
"vconsole": "^3.15.1",
|
"vconsole": "^3.15.1",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
"vue-router": "^4.5.0",
|
"vue-router": "^4.5.0"
|
||||||
"tslib": "^2.6.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@iconify-json/carbon": "^1.2.5",
|
"@iconify-json/carbon": "^1.2.5",
|
||||||
|
Loading…
Reference in New Issue
Block a user