Compare commits
No commits in common. "415267e307142b9cd97b598abd44e23149293a04" and "ebe909c4d74d53a490522df994a84111213c5bf4" have entirely different histories.
415267e307
...
ebe909c4d7
@ -1,125 +0,0 @@
|
|||||||
import {useRuntimeConfig} from '#app'
|
|
||||||
import {ofetch} from 'ofetch'
|
|
||||||
import {message} from '@/components/x-message/useMessage.js'
|
|
||||||
import { getFingerprint } from '@/utils/fingerprint'
|
|
||||||
let httpStatusErrorHandler
|
|
||||||
let http
|
|
||||||
|
|
||||||
// HTTP 状态码映射 - 使用i18n国际化
|
|
||||||
export function setupHttp() {
|
|
||||||
if (http) return http
|
|
||||||
|
|
||||||
const config = useRuntimeConfig()
|
|
||||||
const baseURL = config.public.NUXT_PUBLIC_API_BASE
|
|
||||||
const router = useRouter()
|
|
||||||
const i18n = useNuxtApp().$i18n
|
|
||||||
|
|
||||||
// 国际化的HTTP状态码映射
|
|
||||||
const HTTP_STATUS_MAP = {
|
|
||||||
400: i18n.t('http.error.badRequest'),
|
|
||||||
401: i18n.t('http.error.unauthorized'),
|
|
||||||
403: i18n.t('http.error.forbidden'),
|
|
||||||
404: i18n.t('http.error.notFound'),
|
|
||||||
500: i18n.t('http.error.serverError'),
|
|
||||||
502: i18n.t('http.error.badGateway'),
|
|
||||||
503: i18n.t('http.error.serviceUnavailable'),
|
|
||||||
504: i18n.t('http.error.gatewayTimeout')
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultOptions = {
|
|
||||||
baseURL,
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
timeout: 15000, // 15秒超时
|
|
||||||
retry: 3,
|
|
||||||
retryDelay: 1000,
|
|
||||||
}
|
|
||||||
|
|
||||||
http = ofetch.create({
|
|
||||||
...defaultOptions,
|
|
||||||
|
|
||||||
// 请求拦截
|
|
||||||
async onRequest({ options, request }) {
|
|
||||||
const fingerprint = await getFingerprint()
|
|
||||||
console.log('fingerprint',fingerprint)
|
|
||||||
// 添加 token
|
|
||||||
options.headers = {
|
|
||||||
'Authorization': '12312',
|
|
||||||
...options.headers,
|
|
||||||
'fingerprint':fingerprint,
|
|
||||||
'accept-language': i18n.locale.value
|
|
||||||
}
|
|
||||||
|
|
||||||
// GET 请求添加时间戳防止缓存
|
|
||||||
if (request.toLowerCase().includes('get')) {
|
|
||||||
options.params = {
|
|
||||||
...options.params,
|
|
||||||
_t: Date.now()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 响应拦截
|
|
||||||
async onResponse({ response }) {
|
|
||||||
const data = response._data
|
|
||||||
|
|
||||||
// 处理业务错误
|
|
||||||
if (data.status === 1) {
|
|
||||||
message.error(data.msg || i18n.t('http.error.operationFailed'))
|
|
||||||
}
|
|
||||||
|
|
||||||
return response
|
|
||||||
},
|
|
||||||
|
|
||||||
// 响应错误处理
|
|
||||||
async onResponseError({ response, request }) {
|
|
||||||
// 网络错误
|
|
||||||
if (!response) {
|
|
||||||
message.error(i18n.t('http.error.networkError'))
|
|
||||||
return Promise.reject(new Error(i18n.t('http.error.networkError')))
|
|
||||||
}
|
|
||||||
const status = response.status
|
|
||||||
const data = response._data
|
|
||||||
|
|
||||||
// 处理 HTTP 状态错误
|
|
||||||
const errorMessage = data.msg || HTTP_STATUS_MAP[status] || i18n.t('http.error.requestFailed')
|
|
||||||
|
|
||||||
if (Array.isArray(data.msg)) {
|
|
||||||
data.msg.forEach(item => {
|
|
||||||
httpStatusErrorHandler?.(item, status)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
httpStatusErrorHandler?.(errorMessage, status)
|
|
||||||
}
|
|
||||||
|
|
||||||
message.error(errorMessage)
|
|
||||||
return Promise.reject(data)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return http
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createAbortController() {
|
|
||||||
return new AbortController()
|
|
||||||
}
|
|
||||||
|
|
||||||
export function injectHttpStatusErrorHandler(handler) {
|
|
||||||
httpStatusErrorHandler = handler
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getHttp() {
|
|
||||||
if (!http) {
|
|
||||||
throw new Error(useNuxtApp().$i18n.t('http.error.httpNotInitialized'))
|
|
||||||
}
|
|
||||||
return http
|
|
||||||
}
|
|
||||||
|
|
||||||
// 导出请求工具函数
|
|
||||||
export async function request({url,...options}) {
|
|
||||||
const http = getHttp()
|
|
||||||
try {
|
|
||||||
return await http(url, {...options,body:options.data})
|
|
||||||
} catch (error) {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
import { request } from "../http";
|
|
||||||
|
|
||||||
export async function defaultDetail(data) {
|
|
||||||
|
|
||||||
return await request( {
|
|
||||||
url:'/api/v1/m/auction/out/default/detail',
|
|
||||||
method: 'POST',
|
|
||||||
data
|
|
||||||
})
|
|
||||||
}
|
|
||||||
export async function getLink(data) {
|
|
||||||
|
|
||||||
return await request( {
|
|
||||||
url:'/api/v1/m/auction/out/log/sendlog/aljdfoqueoirhkjsadhfiu',
|
|
||||||
method: 'POST',
|
|
||||||
data
|
|
||||||
})
|
|
||||||
}
|
|
||||||
export async function outBuyList(data) {
|
|
||||||
return await request( {
|
|
||||||
url:'/api/v1/m/auction/out/buy/list',
|
|
||||||
method: 'POST',
|
|
||||||
data
|
|
||||||
})
|
|
||||||
}
|
|
@ -23,17 +23,3 @@ export async function userUpdate(data) {
|
|||||||
data
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
export async function userCaptcha(data) {
|
|
||||||
return await request( {
|
|
||||||
url:'/api/v1/m/user/captcha',
|
|
||||||
method: 'POST',
|
|
||||||
data
|
|
||||||
})
|
|
||||||
}
|
|
||||||
export async function userCaptchaValidate(data) {
|
|
||||||
return await request( {
|
|
||||||
url:'/mall/user/validate/captcha',
|
|
||||||
method: 'POST',
|
|
||||||
data
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
import { request } from "../http";
|
|
||||||
|
|
||||||
export async function defaultDetail(data) {
|
|
||||||
|
|
||||||
return await request( {
|
|
||||||
url:'/api/v1/m/auction/out/default/detail',
|
|
||||||
headers:{
|
|
||||||
'fingerprint':'12312'
|
|
||||||
},
|
|
||||||
method: 'POST',
|
|
||||||
data
|
|
||||||
})
|
|
||||||
}
|
|
||||||
export async function getLink(data) {
|
|
||||||
|
|
||||||
return await request( {
|
|
||||||
url:'/api/v1/m/auction/out/log/sendlog/aljdfoqueoirhkjsadhfiu',
|
|
||||||
method: 'POST',
|
|
||||||
data
|
|
||||||
})
|
|
||||||
}
|
|
||||||
export async function outBuyList(data) {
|
|
||||||
return await request( {
|
|
||||||
url:'/api/v1/m/auction/out/buy/list',
|
|
||||||
method: 'POST',
|
|
||||||
data
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,367 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="puzzle-container">
|
|
||||||
<div class="puzzle-box" :style="{ height: boxHeight + 'px' }">
|
|
||||||
<!-- 背景图 -->
|
|
||||||
<img :src="bgImageUrl" style="width: 320px;height: 191px;" ref="bgImage" @load="onImageLoad" @error="handleImageError">
|
|
||||||
<!-- 滑块 -->
|
|
||||||
<img
|
|
||||||
class="slider-block"
|
|
||||||
:src="sliderImageUrl"
|
|
||||||
:style="{
|
|
||||||
top: `${blockY}px`,
|
|
||||||
left: `${moveX}px`,
|
|
||||||
visibility: loaded ? 'visible' : 'hidden',
|
|
||||||
width: '50px',
|
|
||||||
height: '50px'
|
|
||||||
}"
|
|
||||||
></img>
|
|
||||||
<div v-if="verifySuccess || verifyError" :class="`text-#fff ${verifySuccess?'bg-#52C41A':'bg-#FF4D4F'} h-24px w-100% text-14px absolute left-0 bottom-0 text-center leading-24px`">{{ verifyTip }}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 滑动条 -->
|
|
||||||
<div class="slider-container">
|
|
||||||
<div class="slider-track">
|
|
||||||
<div
|
|
||||||
class="slider-bar"
|
|
||||||
:style="{ width: `${moveX}px` }"
|
|
||||||
></div>
|
|
||||||
<div
|
|
||||||
class="slider-button"
|
|
||||||
:style="{ left: `${moveX}px` }"
|
|
||||||
@mousedown="handleMouseDown"
|
|
||||||
@touchstart="handleTouchStart"
|
|
||||||
>
|
|
||||||
<div class="slider-icon"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
show: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
blockY: {
|
|
||||||
type: Number,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
bgImageUrl: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
sliderImageUrl: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const emit = defineEmits(['leave'])
|
|
||||||
|
|
||||||
// 响应式状态
|
|
||||||
const moveX = ref(0)
|
|
||||||
const startX = ref(0)
|
|
||||||
const oldMoveX = ref(0)
|
|
||||||
const maxMoveX = ref(0)
|
|
||||||
const boxHeight = ref(0)
|
|
||||||
const loaded = ref(false)
|
|
||||||
const isDragging = ref(false)
|
|
||||||
const verifySuccess = ref(false)
|
|
||||||
const verifyError = ref(false)
|
|
||||||
const verifyTip = ref('')
|
|
||||||
|
|
||||||
// 重置方法
|
|
||||||
const reset = () => {
|
|
||||||
moveX.value = 0
|
|
||||||
verifySuccess.value = false
|
|
||||||
verifyError.value = false
|
|
||||||
verifyTip.value = ''
|
|
||||||
isDragging.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// DOM引用
|
|
||||||
const bgImage = ref(null)
|
|
||||||
|
|
||||||
// 方法
|
|
||||||
const onImageLoad = () => {
|
|
||||||
if (bgImage.value) {
|
|
||||||
try {
|
|
||||||
const img = bgImage.value
|
|
||||||
// 确保图片已经完全加载
|
|
||||||
if (!img.complete) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const scale = img.width / img.naturalWidth // 计算图片缩放比例
|
|
||||||
boxHeight.value = img.height
|
|
||||||
|
|
||||||
// 根据图片实际显示大小调整滑块大小
|
|
||||||
const blockSize = Math.round(50 * scale)
|
|
||||||
document.documentElement.style.setProperty('--block-size', blockSize + 'px')
|
|
||||||
|
|
||||||
maxMoveX.value = img.width - blockSize // 使用实际显示的滑块大小计算最大移动距离
|
|
||||||
loaded.value = true
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Image load error:', error)
|
|
||||||
// 设置默认值以确保组件仍然可用
|
|
||||||
const defaultBlockSize = 50
|
|
||||||
document.documentElement.style.setProperty('--block-size', defaultBlockSize + 'px')
|
|
||||||
maxMoveX.value = 320 - defaultBlockSize // 使用默认容器宽度
|
|
||||||
loaded.value = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加图片错误处理
|
|
||||||
const handleImageError = () => {
|
|
||||||
console.error('Image failed to load')
|
|
||||||
// 设置默认值以确保组件仍然可用
|
|
||||||
const defaultBlockSize = 50
|
|
||||||
document.documentElement.style.setProperty('--block-size', defaultBlockSize + 'px')
|
|
||||||
maxMoveX.value = 320 - defaultBlockSize
|
|
||||||
loaded.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleMouseDown = (event) => {
|
|
||||||
event.preventDefault()
|
|
||||||
startX.value = event.clientX
|
|
||||||
oldMoveX.value = moveX.value
|
|
||||||
isDragging.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleTouchStart = (event) => {
|
|
||||||
event.preventDefault()
|
|
||||||
const touch = event.touches[0]
|
|
||||||
startX.value = touch.clientX
|
|
||||||
oldMoveX.value = moveX.value
|
|
||||||
isDragging.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
const move = (clientX) => {
|
|
||||||
let diff = clientX - startX.value
|
|
||||||
let newMoveX = oldMoveX.value + diff
|
|
||||||
|
|
||||||
// 限制移动范围
|
|
||||||
if (newMoveX < 0) newMoveX = 0
|
|
||||||
if (newMoveX > maxMoveX.value) newMoveX = maxMoveX.value
|
|
||||||
|
|
||||||
moveX.value = Math.round(newMoveX) // 取整数避免小数点导致的模糊
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleMouseMove = (event) => {
|
|
||||||
if (!isDragging.value) return
|
|
||||||
move(event.clientX)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleTouchMove = (event) => {
|
|
||||||
if (!isDragging.value) return
|
|
||||||
event.preventDefault() // 防止页面滚动
|
|
||||||
move(event.touches[0].clientX)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleMouseUp = async () => {
|
|
||||||
if (!isDragging.value) return
|
|
||||||
isDragging.value = false
|
|
||||||
|
|
||||||
try {
|
|
||||||
emit('leave', moveX.value, (success) => {
|
|
||||||
if (success) {
|
|
||||||
verifySuccess.value = true
|
|
||||||
verifyError.value = false
|
|
||||||
verifyTip.value = '验证成功'
|
|
||||||
} else {
|
|
||||||
verifySuccess.value = false
|
|
||||||
verifyError.value = true
|
|
||||||
verifyTip.value = '验证失败'
|
|
||||||
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
verifySuccess.value = false
|
|
||||||
verifyError.value = true
|
|
||||||
verifyTip.value = '验证失败'
|
|
||||||
}finally{
|
|
||||||
setTimeout(() => {
|
|
||||||
reset()
|
|
||||||
}, 2000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleTouchEnd = () => {
|
|
||||||
handleMouseUp()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 预加载图片
|
|
||||||
const preloadImages = () => {
|
|
||||||
const bgImg = new Image()
|
|
||||||
const sliderImg = new Image()
|
|
||||||
|
|
||||||
bgImg.onload = () => {
|
|
||||||
if (bgImage.value) {
|
|
||||||
onImageLoad()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bgImg.src = props.bgImageUrl
|
|
||||||
sliderImg.src = props.sliderImageUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
// 生命周期钩子
|
|
||||||
onMounted(() => {
|
|
||||||
preloadImages()
|
|
||||||
window.addEventListener('mousemove', handleMouseMove)
|
|
||||||
window.addEventListener('mouseup', handleMouseUp)
|
|
||||||
window.addEventListener('touchmove', handleTouchMove)
|
|
||||||
window.addEventListener('touchend', handleTouchEnd)
|
|
||||||
})
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
window.removeEventListener('mousemove', handleMouseMove)
|
|
||||||
window.removeEventListener('mouseup', handleMouseUp)
|
|
||||||
window.removeEventListener('touchmove', handleTouchMove)
|
|
||||||
window.removeEventListener('touchend', handleTouchEnd)
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
:root {
|
|
||||||
--block-size: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.puzzle-container {
|
|
||||||
position: relative;
|
|
||||||
margin: 0 auto;
|
|
||||||
background: #fff;
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 10px;
|
|
||||||
touch-action: none;
|
|
||||||
-webkit-touch-callout: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.puzzle-box {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
background: #f8f8f8;
|
|
||||||
border-radius: 8px;
|
|
||||||
touch-action: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-image {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
pointer-events: none;
|
|
||||||
object-fit: contain;
|
|
||||||
max-width: 100%;
|
|
||||||
-webkit-touch-callout: none;
|
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slider-block {
|
|
||||||
position: absolute;
|
|
||||||
width: var(--block-size);
|
|
||||||
height: var(--block-size);
|
|
||||||
background-size: 100% 100%;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: center;
|
|
||||||
cursor: pointer;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
touch-action: none;
|
|
||||||
will-change: transform;
|
|
||||||
transform: translateZ(0);
|
|
||||||
backface-visibility: hidden;
|
|
||||||
-webkit-touch-callout: none;
|
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slider-container {
|
|
||||||
position: relative;
|
|
||||||
margin-top: 15px;
|
|
||||||
height: 40px;
|
|
||||||
touch-action: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slider-track {
|
|
||||||
position: relative;
|
|
||||||
height: 40px;
|
|
||||||
background: #f5f5f5;
|
|
||||||
border-radius: 20px;
|
|
||||||
touch-action: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slider-bar {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: #91d5ff;
|
|
||||||
border-radius: 20px;
|
|
||||||
will-change: transform;
|
|
||||||
transform-origin: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slider-button {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
background: #fff;
|
|
||||||
border-radius: 50%;
|
|
||||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
|
|
||||||
cursor: pointer;
|
|
||||||
will-change: transform;
|
|
||||||
transform: translateZ(0);
|
|
||||||
backface-visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slider-icon {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
background: #1890ff;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.verify-result-bar {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 40px;
|
|
||||||
line-height: 40px;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 14px;
|
|
||||||
border-radius: 20px;
|
|
||||||
transition: all 0.3s;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.verify-result-bar.success {
|
|
||||||
background: #52c41a;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.verify-result-bar.error {
|
|
||||||
background: #ff4d4f;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 移除旧的提示样式 */
|
|
||||||
.verify-tip {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
Before Width: | Height: | Size: 600 KiB |
Before Width: | Height: | Size: 610 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 6.0 KiB |
Before Width: | Height: | Size: 681 KiB |
Before Width: | Height: | Size: 691 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 600 KiB |
Before Width: | Height: | Size: 611 KiB |
Before Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 681 KiB |
Before Width: | Height: | Size: 691 KiB |
Before Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 600 KiB |
Before Width: | Height: | Size: 611 KiB |
Before Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 4.9 KiB |
@ -1,418 +0,0 @@
|
|||||||
<template>
|
|
||||||
<view class="slider-verify-box" v-if="isShow">
|
|
||||||
<view :style="`background-image: url('../../static/image/slider-verify/${
|
|
||||||
$i18n.locale
|
|
||||||
}/${isError ? 'bg_error' : 'bg'}.png');`" class="verifyBox">
|
|
||||||
<!-- <view class="slider-title">{{ $t('authentication.title') }}</view> -->
|
|
||||||
<image class="slider-verify-box-close" src="/static/image/icon/close.png" @click.stop="hideSliderBox">
|
|
||||||
</image>
|
|
||||||
<view class="slide-content">
|
|
||||||
<view class="slider-pintu">
|
|
||||||
<!-- <u-icon
|
|
||||||
name="reload"
|
|
||||||
size="32"
|
|
||||||
color="#fff"
|
|
||||||
class="reload"
|
|
||||||
@tap="refreshVerify"
|
|
||||||
v-if="!isLoading"
|
|
||||||
></u-icon> -->
|
|
||||||
<image src="../../static/image/slider-verify/reload.png" mode="widthFix" style="width: 38rpx"
|
|
||||||
class="reload" @tap="refreshVerify" v-if="!isLoading"></image>
|
|
||||||
<view class="load" v-if="isLoading">
|
|
||||||
<van-loading type="spinner" />
|
|
||||||
<!-- <u-loading-icon text="" textSize="16" :vertical="true"></u-loading-icon> -->
|
|
||||||
</view>
|
|
||||||
<template v-else>
|
|
||||||
<image id="pintuImg" :src="canvasSrc" class="pintu"></image>
|
|
||||||
<view class="pintukuai" :style="{ top: '0px', left: oldx + 'px' }">
|
|
||||||
<image :src="blockSrc" :style="{
|
|
||||||
top: blockY + 'px',
|
|
||||||
left: oldx + 'px',
|
|
||||||
width: blockWidth + 'px',
|
|
||||||
height: blockHeight + 'px'
|
|
||||||
}"></image>
|
|
||||||
</view>
|
|
||||||
<view class="mark" v-if="isMess">
|
|
||||||
<image :src="`../../static/image/slider-verify/${$i18n.locale}/error.png`" mode="widthFix"
|
|
||||||
v-if="isError"></image>
|
|
||||||
<image :src="`../../static/image/slider-verify/${$i18n.locale}/success.png`" mode="widthFix"
|
|
||||||
v-else></image>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
</view>
|
|
||||||
<view class="slider-movearea" @touchend="endTouchMove">
|
|
||||||
<movable-area :animation="true">
|
|
||||||
<movable-view :class="
|
|
||||||
isLoading
|
|
||||||
? 'movable-view btn_info'
|
|
||||||
: isError
|
|
||||||
? 'movable-view btn_error'
|
|
||||||
: 'movable-view btn_success'
|
|
||||||
" :x="x" direction="horizontal" @change="startMove"></movable-view>
|
|
||||||
</movable-area>
|
|
||||||
<view class="huadao">{{
|
|
||||||
$t('authentication.content')
|
|
||||||
}}</view>
|
|
||||||
<view :class="
|
|
||||||
isError ? 'huadao_done error_bg' : 'huadao_done'
|
|
||||||
" :style="'width:' + doneWindth + 'px;'"></view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import {
|
|
||||||
postDataByParams
|
|
||||||
} from '../../utils/api.js'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'slider-verify',
|
|
||||||
props: {
|
|
||||||
isShow: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
x: 0, //初始距离
|
|
||||||
oldx: 0, //移动的距离
|
|
||||||
top: 0, //拼图的top距离
|
|
||||||
canvasSrc: '',
|
|
||||||
blockSrc: '',
|
|
||||||
blockY: '',
|
|
||||||
nonceStr: '',
|
|
||||||
blockWidth: 50, //块图像的宽度(blockWidth大于14)
|
|
||||||
blockHeight: 50,
|
|
||||||
isLoading: true,
|
|
||||||
doneWindth: 0,
|
|
||||||
isError: false,
|
|
||||||
isMess: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
// 每次打开重新刷新拼图
|
|
||||||
isShow(newValue, oldValue) {
|
|
||||||
if (newValue) {
|
|
||||||
this.refreshVerify() //刷新
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
var that = this
|
|
||||||
// that.refreshVerify();
|
|
||||||
that.getCaptcha()
|
|
||||||
// console.log(this.$i18n.locale, 'this.$i18n.locale')
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async getCaptcha() {
|
|
||||||
this.isLoading = true
|
|
||||||
var that = this
|
|
||||||
let url = 'generate/captcha'
|
|
||||||
let params = {
|
|
||||||
canvasWidth: 320, //画布的宽度(canvasWidth大于41并且(canvasWidth-10)/2 - 1> blockWidth)
|
|
||||||
canvasHeight: 190, //画布的高度(canvasHeight大于26并且 canvasHeight - blockHeight > 11
|
|
||||||
blockWidth: this.blockWidth, //块图像的宽度(blockWidth大于14)
|
|
||||||
blockHeight: this.blockHeight, //块图像的高度(blockHeight大于14)
|
|
||||||
// blockRadius: 9, //块图像的圆角半径
|
|
||||||
place: 0 //图像来源标识,0表示URL下载,1表示本地文件 一般用0
|
|
||||||
}
|
|
||||||
postDataByParams(url, params)
|
|
||||||
.then((res) => {
|
|
||||||
if (res.status === 0) {
|
|
||||||
that.canvasSrc = 'data:image/jpg;base64,' + res.data.canvasSrc
|
|
||||||
that.blockSrc = 'data:image/jpg;base64,' + res.data.blockSrc
|
|
||||||
that.blockY = res.data.blockY
|
|
||||||
that.nonceStr = res.data.nonceStr
|
|
||||||
that.isLoading = false
|
|
||||||
} else {
|
|
||||||
this.$emit('sliderError', encodeURIComponent(JSON.stringify(res)))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
this.$emit('sliderError', encodeURIComponent(JSON.stringify(err)), true)
|
|
||||||
});
|
|
||||||
},
|
|
||||||
//刷新验证
|
|
||||||
refreshVerify() {
|
|
||||||
this.x = 1
|
|
||||||
this.oldx = 1
|
|
||||||
setTimeout(() => {
|
|
||||||
this.x = 0
|
|
||||||
this.oldx = 0
|
|
||||||
this.doneWindth = 0
|
|
||||||
}, 300)
|
|
||||||
this.getCaptcha()
|
|
||||||
this.isError = false
|
|
||||||
this.isMess = false
|
|
||||||
},
|
|
||||||
/* 滑动中 */
|
|
||||||
startMove(e) {
|
|
||||||
// console.log(e.detail.x)
|
|
||||||
this.oldx = e.detail.x / 2 - 5
|
|
||||||
if (e.detail.x > 1) {
|
|
||||||
this.doneWindth = e.detail.x + 30
|
|
||||||
} else {
|
|
||||||
this.doneWindth = 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/* 滑动结束 */
|
|
||||||
async endTouchMove() {
|
|
||||||
var that = this
|
|
||||||
// console.log('滑块结束')
|
|
||||||
let url = 'validate/captcha'
|
|
||||||
let params = {
|
|
||||||
nonceStr: this.nonceStr,
|
|
||||||
blockX: this.oldx * 2 + 5
|
|
||||||
}
|
|
||||||
postDataByParams(url, params)
|
|
||||||
.then((res) => {
|
|
||||||
this.isMess = true
|
|
||||||
if (res.status == 0) {
|
|
||||||
setTimeout(() => {
|
|
||||||
that.$emit('touchSliderResult', res.data.nonceStr)
|
|
||||||
}, 1000)
|
|
||||||
} else {
|
|
||||||
this.isError = true
|
|
||||||
this.$emit('sliderError', encodeURIComponent(JSON.stringify(res)))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
this.$emit('sliderError', encodeURIComponent(JSON.stringify(err)), true)
|
|
||||||
});
|
|
||||||
},
|
|
||||||
/* 重置阴影位置 */
|
|
||||||
/* resetMove() {
|
|
||||||
this.x = 1;
|
|
||||||
this.oldx = 1;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.x = 0;
|
|
||||||
this.oldx = 0;
|
|
||||||
}, 300);
|
|
||||||
}, */
|
|
||||||
// 关闭
|
|
||||||
closeSlider() {
|
|
||||||
this.$emit('touchSliderResult', false)
|
|
||||||
},
|
|
||||||
//隐藏滑块验证弹窗
|
|
||||||
hideSliderBox() {
|
|
||||||
this.$emit('hideSliderBox')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.slider-verify-box {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
z-index: 999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error_box {
|
|
||||||
// background: url('../../static/image/slider-verify/bg_error.png') no-repeat !important;
|
|
||||||
background-size: 100% 100% !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.verifyBox {
|
|
||||||
// width: 588rpx;
|
|
||||||
// height: 662rpx;
|
|
||||||
padding: 218rpx 45rpx 30rpx 45rpx;
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
// width: 85%;
|
|
||||||
// background-color: #fff;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
// background: url('../../static/image/slider-verify/bg.png') no-repeat;
|
|
||||||
background-size: 100% 100%;
|
|
||||||
border-radius: 20upx;
|
|
||||||
// box-shadow: 0 0 5upx rgba(0, 0, 0, 1);
|
|
||||||
|
|
||||||
|
|
||||||
.slider-verify-box-close {
|
|
||||||
width: 40rpx;
|
|
||||||
height: 40rpx;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slider-title {
|
|
||||||
font-size: 36upx;
|
|
||||||
text-align: center;
|
|
||||||
padding: 12rpx 0;
|
|
||||||
color: #000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide-content {
|
|
||||||
// width: 560rpx;
|
|
||||||
// padding: 0 ;
|
|
||||||
// margin: 0 auto;
|
|
||||||
|
|
||||||
.slide-tips {
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: rgba(2, 20, 33, 0.45);
|
|
||||||
padding: 0.5em 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slider-pintu {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
border-radius: 10rpx;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.reload {
|
|
||||||
position: absolute;
|
|
||||||
right: 10rpx;
|
|
||||||
top: 10rpx;
|
|
||||||
z-index: 110;
|
|
||||||
}
|
|
||||||
|
|
||||||
.load {
|
|
||||||
width: 320px;
|
|
||||||
height: 190px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
margin: 0 auto;
|
|
||||||
background: #000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pintu {
|
|
||||||
width: 320px;
|
|
||||||
height: 190px;
|
|
||||||
display: block;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pintukuai {
|
|
||||||
position: absolute;
|
|
||||||
/* top: 0;
|
|
||||||
left: 0; */
|
|
||||||
z-index: 100;
|
|
||||||
box-shadow: 0 0 5upx rgba(0, 0, 0, 0.3);
|
|
||||||
|
|
||||||
image {
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
/* width: 560rpx;
|
|
||||||
height: 315rpx; */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mark {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background: #000000;
|
|
||||||
z-index: 101;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
image {
|
|
||||||
width: 60%;
|
|
||||||
height: 50rpx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.yinying {
|
|
||||||
position: absolute;
|
|
||||||
width: 120rpx;
|
|
||||||
height: 120rpx;
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.slider-movearea {
|
|
||||||
position: relative;
|
|
||||||
height: 80upx;
|
|
||||||
width: 320px;
|
|
||||||
margin-top: 25rpx;
|
|
||||||
|
|
||||||
movable-area {
|
|
||||||
width: 100%;
|
|
||||||
height: 64rpx;
|
|
||||||
|
|
||||||
.movable-view {
|
|
||||||
width: 100upx;
|
|
||||||
height: 74rpx;
|
|
||||||
border-radius: 40upx;
|
|
||||||
// background-color: #699A70;
|
|
||||||
background-image: url(../../static/image/slider-verify/icon-button-normal.png);
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: auto 30upx;
|
|
||||||
background-position: center;
|
|
||||||
position: relative;
|
|
||||||
z-index: 100;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn_info {
|
|
||||||
background-color: #878787 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn_error {
|
|
||||||
background-color: #fd343c !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn_success {
|
|
||||||
background-color: #699a70 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error_bg {
|
|
||||||
background-color: #ffb7ba !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.success_bg {
|
|
||||||
background-color: #aad0b0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.huadao {
|
|
||||||
width: 100%;
|
|
||||||
// width: 320px;
|
|
||||||
height: 66upx;
|
|
||||||
line-height: 66upx;
|
|
||||||
background: #ededed;
|
|
||||||
// box-shadow: inset 0 0 5upx #EDEDED;
|
|
||||||
border-radius: 40rpx;
|
|
||||||
color: #bcbcbc;
|
|
||||||
text-align: center;
|
|
||||||
box-sizing: border-box;
|
|
||||||
position: absolute;
|
|
||||||
top: 7rpx;
|
|
||||||
left: 0;
|
|
||||||
font-size: 28rpx;
|
|
||||||
z-index: 99;
|
|
||||||
}
|
|
||||||
|
|
||||||
.huadao_done {
|
|
||||||
height: 66upx;
|
|
||||||
line-height: 66upx;
|
|
||||||
background: #aad0b0;
|
|
||||||
border-radius: 40rpx;
|
|
||||||
text-align: center;
|
|
||||||
box-sizing: border-box;
|
|
||||||
position: absolute;
|
|
||||||
top: 7rpx;
|
|
||||||
left: 0;
|
|
||||||
font-size: 28rpx;
|
|
||||||
z-index: 99;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,20 +1,15 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { useRouter, useRoute } from 'vue-router';
|
import { useRouter, useRoute } from 'vue-router';
|
||||||
import Vcode from "vue3-puzzle-vcode";
|
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import countryCode from '../countryRegion/data/index.js'
|
import countryCode from '../countryRegion/data/index.js'
|
||||||
import {senCode, userLogin,userCaptcha,userCaptchaValidate,} from "@/api/auth/index.js";
|
import {senCode, userLogin} from "@/api/auth/index.js";
|
||||||
import {authStore} from "@/stores/auth/index.js";
|
import {authStore} from "@/stores/auth/index.js";
|
||||||
import {message} from '@/components/x-message/useMessage.js'
|
import {message} from '@/components/x-message/useMessage.js'
|
||||||
import {fddCheck} from "~/api/goods/index.js";
|
import {fddCheck} from "~/api/goods/index.js";
|
||||||
import zu6020 from '@/static/images/zu6020@2x.png'
|
|
||||||
import YourPuzzleComponent from '@/components/YourPuzzleComponent.vue'
|
|
||||||
const {userInfo,token,selectedZone}= authStore()
|
const {userInfo,token,selectedZone}= authStore()
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { locale } = useI18n()
|
const { locale } = useI18n()
|
||||||
const imgs=ref([zu6020])
|
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
name: 'login',
|
name: 'login',
|
||||||
i18n: 'login.title'
|
i18n: 'login.title'
|
||||||
@ -84,45 +79,21 @@ onMounted(()=>{
|
|||||||
selectedCountry.value=route.query.countryName || defaultCountry.name
|
selectedCountry.value=route.query.countryName || defaultCountry.name
|
||||||
})
|
})
|
||||||
const vanSwipeRef=ref(null)
|
const vanSwipeRef=ref(null)
|
||||||
const captcha=ref({
|
|
||||||
"nonceStr": "397b5e08168c31c4",
|
|
||||||
"blockX": 256
|
|
||||||
})
|
|
||||||
const captchaUrl=ref('')
|
|
||||||
const captchaVerifyUrl=ref('')
|
|
||||||
const blockY=ref(0)
|
|
||||||
const getCode =async () => {
|
const getCode =async () => {
|
||||||
loadingRef.value.loading1=true
|
loadingRef.value.loading1=true
|
||||||
const res=await userCaptcha({
|
const res=await senCode({
|
||||||
"canvasWidth": 320, //画布的宽度(canvasWidth大于41并且(canvasWidth-10)/2 - 1> blockWidth)
|
telNum:phoneNum.value,
|
||||||
"canvasHeight": 191, //画布的高度(canvasHeight大于26并且 canvasHeight - blockHeight > 11
|
zone:selectedZone.value
|
||||||
"blockWidth": 50, //块图像的宽度(blockWidth大于14)
|
|
||||||
"blockHeight": 50, //块图像的高度(blockHeight大于14)
|
|
||||||
// "blockRadius": 25, //块图像的圆角半径
|
|
||||||
"place": 0 //图像来源标识,0表示URL下载,1表示本地文件 一般用0
|
|
||||||
})
|
})
|
||||||
if (res.status===0){
|
|
||||||
captchaUrl.value=`data:image/png;base64,${res.data.canvasSrc}`
|
|
||||||
captchaVerifyUrl.value=`data:image/png;base64,${res.data.blockSrc}`
|
|
||||||
blockY.value=res.data.blockY
|
|
||||||
captcha.value.nonceStr=res.data.nonceStr
|
|
||||||
isShow.value=true
|
|
||||||
loadingRef.value.loading1=false
|
loadingRef.value.loading1=false
|
||||||
|
if (res.status===0){
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
// loadingRef.value.loading1=true
|
pane.value = 1
|
||||||
// const res=await senCode({
|
vanSwipeRef.value?.swipeTo(pane.value)
|
||||||
// telNum:phoneNum.value,
|
|
||||||
// zone:selectedZone.value
|
|
||||||
// })
|
|
||||||
// loadingRef.value.loading1=false
|
|
||||||
// if (res.status===0){
|
|
||||||
|
|
||||||
|
startCountdown();
|
||||||
// }
|
|
||||||
// pane.value = 1
|
|
||||||
// vanSwipeRef.value?.swipeTo(pane.value)
|
|
||||||
|
|
||||||
// startCountdown();
|
|
||||||
}
|
}
|
||||||
const goBack = () => {
|
const goBack = () => {
|
||||||
code.value = ''
|
code.value = ''
|
||||||
@ -175,41 +146,6 @@ onMounted(() => {
|
|||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
window.removeEventListener('resize', () => {})
|
window.removeEventListener('resize', () => {})
|
||||||
})
|
})
|
||||||
const isShow=ref(false)
|
|
||||||
const onSuccess=()=>{
|
|
||||||
// userCaptchaValidate()
|
|
||||||
isShow.value=false
|
|
||||||
}
|
|
||||||
const onClose=()=>{
|
|
||||||
isShow.value=false
|
|
||||||
}
|
|
||||||
const onLeave =async (moveX, callback) => {
|
|
||||||
loadingRef.value.loading1=true
|
|
||||||
const res=await senCode({
|
|
||||||
telNum:phoneNum.value,
|
|
||||||
zone:selectedZone.value,
|
|
||||||
verifyCaptcha:{
|
|
||||||
blockX:moveX,
|
|
||||||
nonceStr:captcha.value.nonceStr
|
|
||||||
}
|
|
||||||
})
|
|
||||||
loadingRef.value.loading1=false
|
|
||||||
if (res.status===408){
|
|
||||||
callback(false)
|
|
||||||
getCode()
|
|
||||||
}else{
|
|
||||||
callback(true)
|
|
||||||
setTimeout(() => {
|
|
||||||
pane.value = 1
|
|
||||||
vanSwipeRef.value?.swipeTo(pane.value)
|
|
||||||
startCountdown();
|
|
||||||
isShow.value=false
|
|
||||||
}, 1000)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -285,15 +221,6 @@ const onLeave =async (moveX, callback) => {
|
|||||||
<div v-if="!isKeyboardVisible" class="text-center text-14px absolute left-1/2 transform translate-x--1/2 bottom-20px">
|
<div v-if="!isKeyboardVisible" class="text-center text-14px absolute left-1/2 transform translate-x--1/2 bottom-20px">
|
||||||
{{ $t('login.agreement') }}<span class="text-#3454AF " @click="$router.push('/privacyPolicy')">{{ $t('login.privacyPolicy') }}</span>
|
{{ $t('login.agreement') }}<span class="text-#3454AF " @click="$router.push('/privacyPolicy')">{{ $t('login.privacyPolicy') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<van-popup v-model:show="isShow" round>
|
|
||||||
<YourPuzzleComponent
|
|
||||||
:blockY="blockY"
|
|
||||||
:bgImageUrl="captchaUrl"
|
|
||||||
:sliderImageUrl="captchaVerifyUrl"
|
|
||||||
@leave="onLeave"
|
|
||||||
/>
|
|
||||||
</van-popup>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -311,15 +238,4 @@ const onLeave =async (moveX, callback) => {
|
|||||||
width: 41px;
|
width: 41px;
|
||||||
height: 41px;
|
height: 41px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.verify-popup-content {
|
|
||||||
width: 90vw;
|
|
||||||
max-width: 350px;
|
|
||||||
padding: 20px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.van-popup) {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,76 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
import {publicStore} from "@/stores/public/index.js";
|
|
||||||
import {useI18n} from 'vue-i18n'
|
|
||||||
import {outBuyList} from "@/api-public/public/index.js";
|
|
||||||
|
|
||||||
const {auctionData} = publicStore()
|
|
||||||
function formatThousands(num) {
|
|
||||||
|
|
||||||
return Number(num).toLocaleString();
|
|
||||||
}
|
|
||||||
const headList=[
|
|
||||||
{
|
|
||||||
label:useI18n().t('live_room.head'),
|
|
||||||
color:'#D03050',
|
|
||||||
value:'head'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label:useI18n().t('live_room.out'),
|
|
||||||
color:'#939393',
|
|
||||||
value:'out'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label:useI18n().t('live_room.success'),
|
|
||||||
color:'#34B633',
|
|
||||||
value:'success'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
const buyList=ref([])
|
|
||||||
const headItem=(statusCode)=>{
|
|
||||||
return headList.find(x=>x.value===statusCode)
|
|
||||||
}
|
|
||||||
onMounted(async()=>{
|
|
||||||
const res=await outBuyList({uuid:auctionData.value.uuid})
|
|
||||||
buyList.value=res.data.buys
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div
|
|
||||||
id="list-container"
|
|
||||||
class="w-344px h-86px overflow-y-auto bg-#fff rounded-4px text-14px text-#939393 pt-7px pb-7px px-11px flex flex-col justify-between"
|
|
||||||
>
|
|
||||||
<transition-group name="list" tag="div">
|
|
||||||
|
|
||||||
<template v-if="buyList?.length>0">
|
|
||||||
<div v-for="(item, index) in buyList" :key="index" class="flex flex-shrink-0">
|
|
||||||
<!-- 将每列宽度改为相等(约86px),添加文本溢出处理 -->
|
|
||||||
<div class="text-start shrink-0 w-1/4 truncate" :style="`color: ${headItem(item.statusCode).color}`">
|
|
||||||
{{ headItem(item.statusCode).label }}
|
|
||||||
</div>
|
|
||||||
<div class="text-start shrink-0 w-1/4 truncate">
|
|
||||||
{{ item.auctionType==='local'? $t('live_room.spot'):$t('live_room.network') }}
|
|
||||||
</div>
|
|
||||||
<div class="text-start shrink-0 w-1/4 truncate">
|
|
||||||
{{ item.createdAt }}
|
|
||||||
</div>
|
|
||||||
<div class="text-start shrink-0 w-1/4 truncate">
|
|
||||||
{{item.baseCurrency}}{{ formatThousands(item.baseMoney) }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
|
|
||||||
</transition-group>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.list-enter-active, .list-leave-active {
|
|
||||||
transition: all 0.5s ease;
|
|
||||||
}
|
|
||||||
.list-enter-from, .list-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(20px);
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,123 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
import { defaultDetail,getLink } from '@/api-public/public/index.js'
|
|
||||||
import { liveStore } from '@/stores/live/index.js'
|
|
||||||
import AliyunPlayer from 'aliyun-aliplayer'
|
|
||||||
import { publicStore } from '@/stores/public/index.js'
|
|
||||||
import 'aliyun-aliplayer/build/skins/default/aliplayer-min.css'
|
|
||||||
import broadcast from './components/broadcast/index.vue'
|
|
||||||
const {decryptUtils} = liveStore()
|
|
||||||
const {auctionData} = publicStore()
|
|
||||||
definePageMeta({
|
|
||||||
layout: 'publicLiveRoom'
|
|
||||||
})
|
|
||||||
const {t}= useI18n()
|
|
||||||
const pullLink = ref('')
|
|
||||||
const player = ref(null)
|
|
||||||
const loading1=ref(false)
|
|
||||||
const handlePlayerError = (error) => {
|
|
||||||
showConfirmDialog({
|
|
||||||
message: t('live_room.error_mess'),
|
|
||||||
showCancelButton: true
|
|
||||||
}).then(() => {
|
|
||||||
initializePlayer()
|
|
||||||
}).catch(() => {
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const initializePlayer = async () => {
|
|
||||||
try {
|
|
||||||
if (player.value) {
|
|
||||||
player.value.dispose()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 判断是否是微信浏览器
|
|
||||||
const isWechat = /MicroMessenger/i.test(navigator.userAgent)
|
|
||||||
|
|
||||||
const playerConfig = {
|
|
||||||
id: 'J_prismPlayer1',
|
|
||||||
source: pullLink.value,
|
|
||||||
isLive: true,
|
|
||||||
preload: true,
|
|
||||||
autoplay: true, // 改为 true
|
|
||||||
muted: true, // 默认静音
|
|
||||||
diagnosisButtonVisible:false,
|
|
||||||
// vodRetry:10,
|
|
||||||
// liveRetry:10,
|
|
||||||
autoplayPolicy: {
|
|
||||||
fallbackToMute: true
|
|
||||||
},
|
|
||||||
width: '100%', //容器的大小
|
|
||||||
height: '100%', //容器的大小
|
|
||||||
skinLayout: false,
|
|
||||||
controlBarVisibility: 'never',
|
|
||||||
license: {
|
|
||||||
domain: "szjixun.cn",
|
|
||||||
key: "OProxmWaOZ2XVHXLtf4030126521c43429403194970aa8af9"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
player.value = new AliyunPlayer(playerConfig, (playerInstance) => {
|
|
||||||
// 在微信环境下,需要用户手动触发播放
|
|
||||||
if (isWechat) {
|
|
||||||
const startPlay = () => {
|
|
||||||
playerInstance?.play()
|
|
||||||
document.removeEventListener('WeixinJSBridgeReady', startPlay)
|
|
||||||
document.removeEventListener('touchstart', startPlay)
|
|
||||||
}
|
|
||||||
document.addEventListener('WeixinJSBridgeReady', startPlay)
|
|
||||||
document.addEventListener('touchstart', startPlay)
|
|
||||||
}
|
|
||||||
loading1.value = true
|
|
||||||
playerInstance?.play()
|
|
||||||
})
|
|
||||||
player.value.on('playing', () => {
|
|
||||||
loading1.value = false
|
|
||||||
|
|
||||||
})
|
|
||||||
player.value.on('loading', () => {
|
|
||||||
})
|
|
||||||
player.value.on('error', handlePlayerError)
|
|
||||||
} catch (error) {
|
|
||||||
console.log('error',error)
|
|
||||||
showConfirmDialog({
|
|
||||||
message: t('live_room.error_mess'),
|
|
||||||
showCancelButton: true
|
|
||||||
}).then(() => {
|
|
||||||
initializePlayer()
|
|
||||||
}).catch(() => {
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onMounted(async () => {
|
|
||||||
const res = await defaultDetail({})
|
|
||||||
if(res.status === 0){
|
|
||||||
auctionData.value = res.data
|
|
||||||
}
|
|
||||||
const linkRes = await getLink({uuid:auctionData.value.uuid})
|
|
||||||
pullLink.value =decryptUtils.decryptData(linkRes.data.code)
|
|
||||||
initializePlayer()
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="grow-1 relative">
|
|
||||||
<van-nav-bar :title="auctionData.title" />
|
|
||||||
<div id="J_prismPlayer1" class="w-100vw" style="height: calc(100vh - var(--van-nav-bar-height));"></div>
|
|
||||||
<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>
|
|
||||||
<div class="absolute left-1/2 transform -translate-x-1/2" style="bottom:calc(var(--safe-area-inset-bottom) + 46px)">
|
|
||||||
<broadcast></broadcast>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
|
|
||||||
</style>
|
|
@ -1,9 +1,6 @@
|
|||||||
import { setupHttp } from '@/api/http'
|
import { setupHttp } from '@/api/http'
|
||||||
import { setupHttp as setupHttp1} from '@/api-collect-code/http'
|
import { setupHttp as setupHttp1} from '@/api-collect-code/http'
|
||||||
import { setupHttp as setupHttp2} from '@/api-public/http'
|
|
||||||
export default defineNuxtPlugin(() => {
|
export default defineNuxtPlugin(() => {
|
||||||
setupHttp()
|
setupHttp()
|
||||||
setupHttp1()
|
setupHttp1()
|
||||||
setupHttp2()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 1.1 KiB |
@ -248,7 +248,6 @@ export const liveStore = createGlobalState(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return{
|
return{
|
||||||
decryptUtils,
|
|
||||||
wsClient,
|
wsClient,
|
||||||
fullLive,
|
fullLive,
|
||||||
isMinWindow,
|
isMinWindow,
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
import {createGlobalState, useLocalStorage} from '@vueuse/core'
|
|
||||||
export const publicStore = createGlobalState(() => {
|
|
||||||
const auctionData=useLocalStorage('auctionData',{})
|
|
||||||
return {
|
|
||||||
auctionData
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
|||||||
import FingerprintJS from '@fingerprintjs/fingerprintjs'
|
|
||||||
|
|
||||||
export async function getFingerprint() {
|
|
||||||
try {
|
|
||||||
// 初始化 FingerprintJS
|
|
||||||
const fp = await FingerprintJS.load()
|
|
||||||
|
|
||||||
// 获取访问者的指纹
|
|
||||||
const result = await fp.get()
|
|
||||||
|
|
||||||
// 返回指纹哈希值
|
|
||||||
return result.visitorId
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取浏览器指纹失败:', error)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
@ -38,8 +38,7 @@
|
|||||||
"vue-demi": "^0.14.10",
|
"vue-demi": "^0.14.10",
|
||||||
"vue-pdf-embed": "^2.1.2",
|
"vue-pdf-embed": "^2.1.2",
|
||||||
"vue-router": "^4.5.0",
|
"vue-router": "^4.5.0",
|
||||||
"vue-signature-pad": "^3.0.2",
|
"vue-signature-pad": "^3.0.2"
|
||||||
"vue3-puzzle-vcode": "1.1.6-nuxt"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@iconify-json/carbon": "^1.2.5",
|
"@iconify-json/carbon": "^1.2.5",
|
||||||
|
@ -77,9 +77,6 @@ importers:
|
|||||||
vue-signature-pad:
|
vue-signature-pad:
|
||||||
specifier: ^3.0.2
|
specifier: ^3.0.2
|
||||||
version: 3.0.2(vue@3.5.13(typescript@5.7.3))
|
version: 3.0.2(vue@3.5.13(typescript@5.7.3))
|
||||||
vue3-puzzle-vcode:
|
|
||||||
specifier: 1.1.6-nuxt
|
|
||||||
version: 1.1.6-nuxt
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@iconify-json/carbon':
|
'@iconify-json/carbon':
|
||||||
specifier: ^1.2.5
|
specifier: ^1.2.5
|
||||||
@ -812,8 +809,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-8tR1xe7ZEbkabTuE/tNhzpolygUn9OaYp9yuYAF4MgDNZg06C3Qny80bes2/e9/Wm3aVkPUlCw6WgU7mQd0yEg==}
|
resolution: {integrity: sha512-8tR1xe7ZEbkabTuE/tNhzpolygUn9OaYp9yuYAF4MgDNZg06C3Qny80bes2/e9/Wm3aVkPUlCw6WgU7mQd0yEg==}
|
||||||
engines: {node: '>= 16'}
|
engines: {node: '>= 16'}
|
||||||
|
|
||||||
'@intlify/shared@11.1.2':
|
'@intlify/shared@11.1.1':
|
||||||
resolution: {integrity: sha512-dF2iMMy8P9uKVHV/20LA1ulFLL+MKSbfMiixSmn6fpwqzvix38OIc7ebgnFbBqElvghZCW9ACtzKTGKsTGTWGA==}
|
resolution: {integrity: sha512-2kGiWoXaeV8HZlhU/Nml12oTbhv7j2ufsJ5vQaa0VTjzUmZVdd/nmKFRAOJ/FtjO90Qba5AnZDwsrY7ZND5udA==}
|
||||||
engines: {node: '>= 16'}
|
engines: {node: '>= 16'}
|
||||||
|
|
||||||
'@intlify/unplugin-vue-i18n@6.0.3':
|
'@intlify/unplugin-vue-i18n@6.0.3':
|
||||||
@ -4621,9 +4618,6 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
vue: ^3.2.0
|
vue: ^3.2.0
|
||||||
|
|
||||||
vue3-puzzle-vcode@1.1.6-nuxt:
|
|
||||||
resolution: {integrity: sha512-V3DrPIYznxko8jBAtZtmsNPw9QmkPnFicQ0p9B192vC3ncRv4IDazhLC7D/cY/OGq0OeqXmk2DiOcBR7dyt8GQ==}
|
|
||||||
|
|
||||||
vue@3.5.13:
|
vue@3.5.13:
|
||||||
resolution: {integrity: sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==}
|
resolution: {integrity: sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -5348,14 +5342,14 @@ snapshots:
|
|||||||
|
|
||||||
'@intlify/shared@11.0.0-rc.1': {}
|
'@intlify/shared@11.0.0-rc.1': {}
|
||||||
|
|
||||||
'@intlify/shared@11.1.2': {}
|
'@intlify/shared@11.1.1': {}
|
||||||
|
|
||||||
'@intlify/unplugin-vue-i18n@6.0.3(@vue/compiler-dom@3.5.13)(eslint@9.20.1(jiti@2.4.2))(rollup@4.34.6)(typescript@5.7.3)(vue-i18n@10.0.5(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))':
|
'@intlify/unplugin-vue-i18n@6.0.3(@vue/compiler-dom@3.5.13)(eslint@9.20.1(jiti@2.4.2))(rollup@4.34.6)(typescript@5.7.3)(vue-i18n@10.0.5(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint-community/eslint-utils': 4.4.1(eslint@9.20.1(jiti@2.4.2))
|
'@eslint-community/eslint-utils': 4.4.1(eslint@9.20.1(jiti@2.4.2))
|
||||||
'@intlify/bundle-utils': 10.0.0(vue-i18n@10.0.5(vue@3.5.13(typescript@5.7.3)))
|
'@intlify/bundle-utils': 10.0.0(vue-i18n@10.0.5(vue@3.5.13(typescript@5.7.3)))
|
||||||
'@intlify/shared': 11.1.2
|
'@intlify/shared': 11.1.1
|
||||||
'@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.1.2)(@vue/compiler-dom@3.5.13)(vue-i18n@10.0.5(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))
|
'@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.1.1)(@vue/compiler-dom@3.5.13)(vue-i18n@10.0.5(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))
|
||||||
'@rollup/pluginutils': 5.1.4(rollup@4.34.6)
|
'@rollup/pluginutils': 5.1.4(rollup@4.34.6)
|
||||||
'@typescript-eslint/scope-manager': 8.24.0
|
'@typescript-eslint/scope-manager': 8.24.0
|
||||||
'@typescript-eslint/typescript-estree': 8.24.0(typescript@5.7.3)
|
'@typescript-eslint/typescript-estree': 8.24.0(typescript@5.7.3)
|
||||||
@ -5379,11 +5373,11 @@ snapshots:
|
|||||||
|
|
||||||
'@intlify/utils@0.13.0': {}
|
'@intlify/utils@0.13.0': {}
|
||||||
|
|
||||||
'@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.1.2)(@vue/compiler-dom@3.5.13)(vue-i18n@10.0.5(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))':
|
'@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.1.1)(@vue/compiler-dom@3.5.13)(vue-i18n@10.0.5(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/parser': 7.26.8
|
'@babel/parser': 7.26.8
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@intlify/shared': 11.1.2
|
'@intlify/shared': 11.1.1
|
||||||
'@vue/compiler-dom': 3.5.13
|
'@vue/compiler-dom': 3.5.13
|
||||||
vue: 3.5.13(typescript@5.7.3)
|
vue: 3.5.13(typescript@5.7.3)
|
||||||
vue-i18n: 10.0.5(vue@3.5.13(typescript@5.7.3))
|
vue-i18n: 10.0.5(vue@3.5.13(typescript@5.7.3))
|
||||||
@ -9902,8 +9896,6 @@ snapshots:
|
|||||||
signature_pad: 3.0.0-beta.4
|
signature_pad: 3.0.0-beta.4
|
||||||
vue: 3.5.13(typescript@5.7.3)
|
vue: 3.5.13(typescript@5.7.3)
|
||||||
|
|
||||||
vue3-puzzle-vcode@1.1.6-nuxt: {}
|
|
||||||
|
|
||||||
vue@3.5.13(typescript@5.7.3):
|
vue@3.5.13(typescript@5.7.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vue/compiler-dom': 3.5.13
|
'@vue/compiler-dom': 3.5.13
|
||||||
|
@ -1,11 +1,4 @@
|
|||||||
{
|
{
|
||||||
// https://nuxt.com/docs/guide/concepts/typescript
|
// https://nuxt.com/docs/guide/concepts/typescript
|
||||||
"extends": "./.nuxt/tsconfig.json",
|
"extends": "./.nuxt/tsconfig.json"
|
||||||
"compilerOptions": {
|
|
||||||
"baseUrl": ".",
|
|
||||||
"paths": {
|
|
||||||
"@/*": ["app/*"] // 确保这里的路径别名配置和你的项目配置一致
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"include": ["app/**/*"]
|
|
||||||
}
|
}
|
||||||
|