feat(auth): 添加滑动验证码功能
- 在 auth API 中新增 userCaptcha 和 userCaptchaValidate 方法 - 在登录页面集成滑动验证码组件 - 实现验证码获取
This commit is contained in:
parent
4041b45cca
commit
85523e8321
@ -23,3 +23,17 @@ 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
|
||||||
|
})
|
||||||
|
}
|
307
app/components/YourPuzzleComponent.vue
Normal file
307
app/components/YourPuzzleComponent.vue
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
<template>
|
||||||
|
<div class="puzzle-container">
|
||||||
|
<div class="puzzle-box" :style="{ height: boxHeight + 'px' }">
|
||||||
|
<!-- 背景图 -->
|
||||||
|
<img :src="bgImageUrl" class="bg-image" ref="bgImage" @load="onImageLoad">
|
||||||
|
<!-- 滑块 -->
|
||||||
|
<div
|
||||||
|
class="slider-block"
|
||||||
|
:style="{
|
||||||
|
backgroundImage: `url(${sliderImageUrl})`,
|
||||||
|
top: `${blockY}px`,
|
||||||
|
left: `${moveX}px`,
|
||||||
|
visibility: loaded ? 'visible' : 'hidden'
|
||||||
|
}"
|
||||||
|
></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 v-if="verifySuccess || verifyError" class="verify-result-bar" :class="{ 'success': verifySuccess, 'error': verifyError }">
|
||||||
|
{{ verifyTip }}
|
||||||
|
</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('')
|
||||||
|
|
||||||
|
// DOM引用
|
||||||
|
const bgImage = ref(null)
|
||||||
|
|
||||||
|
// 方法
|
||||||
|
const onImageLoad = () => {
|
||||||
|
if (bgImage.value) {
|
||||||
|
const img = bgImage.value
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = '验证失败'
|
||||||
|
moveX.value = 0 // 验证失败,滑块返回原位
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
verifySuccess.value = false
|
||||||
|
verifyError.value = true
|
||||||
|
verifyTip.value = '验证失败'
|
||||||
|
moveX.value = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleTouchEnd = () => {
|
||||||
|
handleMouseUp()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生命周期钩子
|
||||||
|
onMounted(() => {
|
||||||
|
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 {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 320px;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-block {
|
||||||
|
position: absolute;
|
||||||
|
width: var(--block-size);
|
||||||
|
height: var(--block-size);
|
||||||
|
background-size: 100% 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
touch-action: none;
|
||||||
|
will-change: transform;
|
||||||
|
transform: translateZ(0);
|
||||||
|
backface-visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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>
|
@ -1,15 +1,21 @@
|
|||||||
<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} from "@/api/auth/index.js";
|
import {senCode, userLogin,userCaptcha,userCaptchaValidate} 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])
|
||||||
|
console.log('zu6020');
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
name: 'login',
|
name: 'login',
|
||||||
i18n: 'login.title'
|
i18n: 'login.title'
|
||||||
@ -79,21 +85,44 @@ 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
|
|
||||||
const res=await senCode({
|
const res=await userCaptcha({
|
||||||
telNum:phoneNum.value,
|
"canvasWidth": 320, //画布的宽度(canvasWidth大于41并且(canvasWidth-10)/2 - 1> blockWidth)
|
||||||
zone:selectedZone.value
|
"canvasHeight": 191, //画布的高度(canvasHeight大于26并且 canvasHeight - blockHeight > 11
|
||||||
})
|
"blockWidth": 50, //块图像的宽度(blockWidth大于14)
|
||||||
loadingRef.value.loading1=false
|
"blockHeight": 50, //块图像的高度(blockHeight大于14)
|
||||||
|
// "blockRadius": 25, //块图像的圆角半径
|
||||||
|
"place": 0 //图像来源标识,0表示URL下载,1表示本地文件 一般用0
|
||||||
|
})
|
||||||
if (res.status===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=res.data.nonceStr
|
||||||
|
isShow.value=true
|
||||||
}
|
}
|
||||||
pane.value = 1
|
// loadingRef.value.loading1=true
|
||||||
vanSwipeRef.value?.swipeTo(pane.value)
|
// const res=await senCode({
|
||||||
|
// 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 = ''
|
||||||
@ -146,6 +175,27 @@ 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) => {
|
||||||
|
console.log('moveX',moveX);
|
||||||
|
const res=await userCaptchaValidate({
|
||||||
|
blockX:moveX,
|
||||||
|
nonceStr:captcha.value.nonceStr
|
||||||
|
})
|
||||||
|
if (res.status===0){
|
||||||
|
callback(true)
|
||||||
|
}else {
|
||||||
|
callback(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -221,6 +271,16 @@ onUnmounted(() => {
|
|||||||
<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
|
||||||
|
:show="true"
|
||||||
|
:blockY="blockY"
|
||||||
|
:bgImageUrl="captchaUrl"
|
||||||
|
:sliderImageUrl="captchaVerifyUrl"
|
||||||
|
@leave="onLeave"
|
||||||
|
/>
|
||||||
|
</van-popup>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -238,4 +298,15 @@ onUnmounted(() => {
|
|||||||
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>
|
||||||
|
BIN
app/static/images/reset.png
Normal file
BIN
app/static/images/reset.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
@ -38,7 +38,8 @@
|
|||||||
"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,6 +77,9 @@ 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
|
||||||
@ -809,8 +812,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.1':
|
'@intlify/shared@11.1.2':
|
||||||
resolution: {integrity: sha512-2kGiWoXaeV8HZlhU/Nml12oTbhv7j2ufsJ5vQaa0VTjzUmZVdd/nmKFRAOJ/FtjO90Qba5AnZDwsrY7ZND5udA==}
|
resolution: {integrity: sha512-dF2iMMy8P9uKVHV/20LA1ulFLL+MKSbfMiixSmn6fpwqzvix38OIc7ebgnFbBqElvghZCW9ACtzKTGKsTGTWGA==}
|
||||||
engines: {node: '>= 16'}
|
engines: {node: '>= 16'}
|
||||||
|
|
||||||
'@intlify/unplugin-vue-i18n@6.0.3':
|
'@intlify/unplugin-vue-i18n@6.0.3':
|
||||||
@ -4618,6 +4621,9 @@ 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:
|
||||||
@ -5342,14 +5348,14 @@ snapshots:
|
|||||||
|
|
||||||
'@intlify/shared@11.0.0-rc.1': {}
|
'@intlify/shared@11.0.0-rc.1': {}
|
||||||
|
|
||||||
'@intlify/shared@11.1.1': {}
|
'@intlify/shared@11.1.2': {}
|
||||||
|
|
||||||
'@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.1
|
'@intlify/shared': 11.1.2
|
||||||
'@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))
|
'@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))
|
||||||
'@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)
|
||||||
@ -5373,11 +5379,11 @@ snapshots:
|
|||||||
|
|
||||||
'@intlify/utils@0.13.0': {}
|
'@intlify/utils@0.13.0': {}
|
||||||
|
|
||||||
'@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))':
|
'@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))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/parser': 7.26.8
|
'@babel/parser': 7.26.8
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@intlify/shared': 11.1.1
|
'@intlify/shared': 11.1.2
|
||||||
'@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))
|
||||||
@ -9896,6 +9902,8 @@ 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
|
||||||
|
Loading…
Reference in New Issue
Block a user