feat: add verification comp【31315a7f】
							parent
							
								
									1112409a3b
								
							
						
					
					
						commit
						eeb19808c9
					
				|  | @ -37,9 +37,11 @@ | |||
|     "vue": "catalog:", | ||||
|     "vue-json-viewer": "catalog:", | ||||
|     "vue-router": "catalog:", | ||||
|     "vue-tippy": "catalog:" | ||||
|     "vue-tippy": "catalog:", | ||||
|     "crypto-js": "catalog:" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@types/qrcode": "catalog:" | ||||
|     "@types/qrcode": "catalog:", | ||||
|     "@types/crypto-js": "catalog:" | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -4,3 +4,5 @@ export { default as PointSelectionCaptchaCard } from './point-selection-captcha/ | |||
| export { default as SliderCaptcha } from './slider-captcha/index.vue'; | ||||
| export { default as SliderRotateCaptcha } from './slider-rotate-captcha/index.vue'; | ||||
| export type * from './types'; | ||||
| 
 | ||||
| export { default as Verification } from './verification/index.vue'; | ||||
|  | @ -0,0 +1,304 @@ | |||
| <script lang="ts" setup> | ||||
| import type { VerificationProps } from '../types'; | ||||
| 
 | ||||
| import { | ||||
|   type ComponentInternalInstance, | ||||
|   getCurrentInstance, | ||||
|   nextTick, | ||||
|   onMounted, | ||||
|   reactive, | ||||
|   ref, | ||||
|   toRefs, | ||||
| } from 'vue'; | ||||
| 
 | ||||
| import { $t } from '@vben/locales'; | ||||
| 
 | ||||
| import { aesEncrypt } from '../utils/ase'; | ||||
| import { resetSize } from '../utils/util'; | ||||
| 
 | ||||
| /** | ||||
|  * VerifyPoints | ||||
|  * @description 点选 | ||||
|  */ | ||||
| 
 | ||||
| // const props = defineProps({ | ||||
| //   barSize: { | ||||
| //     default() { | ||||
| //       return { | ||||
| //         height: '40px', | ||||
| //         width: '310px', | ||||
| //       }; | ||||
| //     }, | ||||
| //     type: Object, | ||||
| //   }, | ||||
| //   captchaType: { | ||||
| //     default() { | ||||
| //       return 'VerifyPoints'; | ||||
| //     }, | ||||
| //     type: String, | ||||
| //   }, | ||||
| //   imgSize: { | ||||
| //     default() { | ||||
| //       return { | ||||
| //         height: '155px', | ||||
| //         width: '310px', | ||||
| //       }; | ||||
| //     }, | ||||
| //     type: Object, | ||||
| //   }, | ||||
| //   // 弹出式pop,固定fixed | ||||
| //   mode: { | ||||
| //     default: 'fixed', | ||||
| //     type: String, | ||||
| //   }, | ||||
| //   // 间隔 | ||||
| //   vSpace: { | ||||
| //     default: 5, | ||||
| //     type: Number, | ||||
| //   }, | ||||
| // }); | ||||
| 
 | ||||
| defineOptions({ | ||||
|   name: 'VerifyPoints', | ||||
| }); | ||||
| 
 | ||||
| const props = withDefaults(defineProps<VerificationProps>(), { | ||||
|   barSize: () => ({ | ||||
|     height: '40px', | ||||
|     width: '310px', | ||||
|   }), | ||||
|   captchaType: 'clickWord', | ||||
|   imgSize: () => ({ | ||||
|     height: '155px', | ||||
|     width: '310px', | ||||
|   }), | ||||
|   mode: 'fixed', | ||||
|   space: 5, | ||||
| }); | ||||
| 
 | ||||
| const emit = defineEmits(['onSuccess', 'onError', 'onClose', 'onReady']); | ||||
| 
 | ||||
| const { captchaType, mode, checkCaptchaApi, getCaptchaApi } = toRefs(props); | ||||
| const { proxy } = getCurrentInstance() as ComponentInternalInstance; | ||||
| const secretKey = ref(); // 后端返回的ase加密秘钥 | ||||
| const checkNum = ref(3); // 默认需要点击的字数 | ||||
| const fontPos = reactive<any[]>([]); // 选中的坐标信息 | ||||
| const checkPosArr = reactive<any[]>([]); // 用户点击的坐标 | ||||
| const num = ref(1); // 点击的记数 | ||||
| const pointBackImgBase = ref(); // 后端获取到的背景图片 | ||||
| const poinTextList = ref<any[]>([]); // 后端返回的点击字体顺序 | ||||
| const backToken = ref(); // 后端返回的token值 | ||||
| const setSize = reactive({ | ||||
|   barHeight: 0, | ||||
|   barWidth: 0, | ||||
|   imgHeight: 0, | ||||
|   imgWidth: 0, | ||||
| }); | ||||
| const tempPoints = reactive<any[]>([]); | ||||
| const text = ref(); | ||||
| const barAreaColor = ref(); | ||||
| const barAreaBorderColor = ref(); | ||||
| const showRefresh = ref(true); | ||||
| const bindingClick = ref(true); | ||||
| 
 | ||||
| function init() { | ||||
|   // 加载页面 | ||||
|   fontPos.splice(0, fontPos.length); | ||||
|   checkPosArr.splice(0, checkPosArr.length); | ||||
|   num.value = 1; | ||||
|   getPictrue(); | ||||
|   nextTick(() => { | ||||
|     const { barHeight, barWidth, imgHeight, imgWidth } = resetSize(proxy); | ||||
|     setSize.imgHeight = imgHeight; | ||||
|     setSize.imgWidth = imgWidth; | ||||
|     setSize.barHeight = barHeight; | ||||
|     setSize.barWidth = barWidth; | ||||
|     emit('onReady', proxy); | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| onMounted(() => { | ||||
|   // 禁止拖拽 | ||||
|   init(); | ||||
|   proxy?.$el?.addEventListener('selectstart', () => { | ||||
|     return false; | ||||
|   }); | ||||
| }); | ||||
| const canvas = ref(null); | ||||
| 
 | ||||
| // 获取坐标 | ||||
| const getMousePos = function (obj: any, e: any) { | ||||
|   const x = e.offsetX; | ||||
|   const y = e.offsetY; | ||||
|   return { x, y }; | ||||
| }; | ||||
| // 创建坐标点 | ||||
| const createPoint = function (pos: any) { | ||||
|   tempPoints.push(Object.assign({}, pos)); | ||||
|   return num.value + 1; | ||||
| }; | ||||
| 
 | ||||
| // 坐标转换函数 | ||||
| const pointTransfrom = function (pointArr: any, imgSize: any) { | ||||
|   const newPointArr = pointArr.map((p: any) => { | ||||
|     const x = Math.round((310 * p.x) / Number.parseInt(imgSize.imgWidth)); | ||||
|     const y = Math.round((155 * p.y) / Number.parseInt(imgSize.imgHeight)); | ||||
|     return { x, y }; | ||||
|   }); | ||||
|   return newPointArr; | ||||
| }; | ||||
| 
 | ||||
| const refresh = async function () { | ||||
|   tempPoints.splice(0, tempPoints.length); | ||||
|   barAreaColor.value = '#000'; | ||||
|   barAreaBorderColor.value = '#ddd'; | ||||
|   bindingClick.value = true; | ||||
|   fontPos.splice(0, fontPos.length); | ||||
|   checkPosArr.splice(0, checkPosArr.length); | ||||
|   num.value = 1; | ||||
|   await getPictrue(); | ||||
|   showRefresh.value = true; | ||||
| }; | ||||
| 
 | ||||
| function canvasClick(e: any) { | ||||
|   checkPosArr.push(getMousePos(canvas, e)); | ||||
|   if (num.value === checkNum.value) { | ||||
|     num.value = createPoint(getMousePos(canvas, e)); | ||||
|     // 按比例转换坐标值 | ||||
|     const arr = pointTransfrom(checkPosArr, setSize); | ||||
|     checkPosArr.length = 0; | ||||
|     checkPosArr.push(...arr); | ||||
|     // 等创建坐标执行完 | ||||
|     setTimeout(() => { | ||||
|       // var flag = this.comparePos(this.fontPos, this.checkPosArr); | ||||
|       // 发送后端请求 | ||||
|       const captchaVerification = secretKey.value | ||||
|         ? aesEncrypt( | ||||
|             `${backToken.value}---${JSON.stringify(checkPosArr)}`, | ||||
|             secretKey.value, | ||||
|           ) | ||||
|         : `${backToken.value}---${JSON.stringify(checkPosArr)}`; | ||||
|       const data = { | ||||
|         captchaType: captchaType.value, | ||||
|         pointJson: secretKey.value | ||||
|           ? aesEncrypt(JSON.stringify(checkPosArr), secretKey.value) | ||||
|           : JSON.stringify(checkPosArr), | ||||
|         token: backToken.value, | ||||
|       }; | ||||
|       checkCaptchaApi?.value?.(data).then((response: any) => { | ||||
|         const res = response.data; | ||||
|         if (res.repCode === '0000') { | ||||
|           barAreaColor.value = '#4cae4c'; | ||||
|           barAreaBorderColor.value = '#5cb85c'; | ||||
|           text.value = $t('ui.captcha.success'); | ||||
|           bindingClick.value = false; | ||||
|           if (mode.value === 'pop') { | ||||
|             setTimeout(() => { | ||||
|               emit('onClose'); | ||||
|               refresh(); | ||||
|             }, 1500); | ||||
|           } | ||||
|           emit('onSuccess', { captchaVerification }); | ||||
|         } else { | ||||
|           emit('onError', proxy); | ||||
|           barAreaColor.value = '#d9534f'; | ||||
|           barAreaBorderColor.value = '#d9534f'; | ||||
|           text.value = $t('ui.captcha.sliderRotateFailTip'); | ||||
|           setTimeout(() => { | ||||
|             refresh(); | ||||
|           }, 700); | ||||
|         } | ||||
|       }); | ||||
|     }, 400); | ||||
|   } | ||||
|   if (num.value < checkNum.value) | ||||
|     num.value = createPoint(getMousePos(canvas, e)); | ||||
| } | ||||
| 
 | ||||
| // 请求背景图片和验证图片 | ||||
| async function getPictrue() { | ||||
|   const data = { | ||||
|     captchaType: captchaType.value, | ||||
|   }; | ||||
|   const res = await getCaptchaApi?.value?.(data); | ||||
| 
 | ||||
|   if (res?.data?.repCode === '0000') { | ||||
|     pointBackImgBase.value = `data:image/png;base64,${res?.data?.repData?.originalImageBase64}`; | ||||
|     backToken.value = res.data.repData.token; | ||||
|     secretKey.value = res.data.repData.secretKey; | ||||
|     poinTextList.value = res.data.repData.wordList; | ||||
|     text.value = `${$t('ui.captcha.point')}【${poinTextList.value.join(',')}】`; | ||||
|   } else { | ||||
|     text.value = res?.data?.repMsg; | ||||
|   } | ||||
| } | ||||
| defineExpose({ | ||||
|   init, | ||||
|   refresh, | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <div style="position: relative"> | ||||
|     <div class="verify-img-out"> | ||||
|       <div | ||||
|         :style="{ | ||||
|           width: setSize.imgWidth, | ||||
|           height: setSize.imgHeight, | ||||
|           'background-size': `${setSize.imgWidth} ${setSize.imgHeight}`, | ||||
|           'margin-bottom': `${space}px`, | ||||
|         }" | ||||
|         class="verify-img-panel" | ||||
|       > | ||||
|         <div | ||||
|           v-show="showRefresh" | ||||
|           class="verify-refresh" | ||||
|           style="z-index: 3" | ||||
|           @click="refresh" | ||||
|         > | ||||
|           <i class="iconfont icon-refresh"></i> | ||||
|         </div> | ||||
|         <img | ||||
|           ref="canvas" | ||||
|           :src="pointBackImgBase" | ||||
|           alt="" | ||||
|           style="display: block; width: 100%; height: 100%" | ||||
|           @click="bindingClick ? canvasClick($event) : undefined" | ||||
|         /> | ||||
| 
 | ||||
|         <div | ||||
|           v-for="(tempPoint, index) in tempPoints" | ||||
|           :key="index" | ||||
|           :style="{ | ||||
|             'background-color': '#1abd6c', | ||||
|             color: '#fff', | ||||
|             'z-index': 9999, | ||||
|             width: '20px', | ||||
|             height: '20px', | ||||
|             'text-align': 'center', | ||||
|             'line-height': '20px', | ||||
|             'border-radius': '50%', | ||||
|             position: 'absolute', | ||||
|             top: `${tempPoint.y - 10}px`, | ||||
|             left: `${tempPoint.x - 10}px`, | ||||
|           }" | ||||
|           class="point-area" | ||||
|         > | ||||
|           {{ index + 1 }} | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <!-- 'height': this.barSize.height, --> | ||||
|     <div | ||||
|       :style="{ | ||||
|         width: setSize.imgWidth, | ||||
|         color: barAreaColor, | ||||
|         'border-color': barAreaBorderColor, | ||||
|         'line-height': barSize.height, | ||||
|       }" | ||||
|       class="verify-bar-area" | ||||
|     > | ||||
|       <span class="verify-msg">{{ text }}</span> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|  | @ -0,0 +1,376 @@ | |||
| <script lang="ts" setup> | ||||
| import type { VerificationProps } from '../types'; | ||||
| 
 | ||||
| /** | ||||
|  * VerifySlide | ||||
|  * @description 滑块 | ||||
|  */ | ||||
| import { | ||||
|   computed, | ||||
|   getCurrentInstance, | ||||
|   nextTick, | ||||
|   onMounted, | ||||
|   reactive, | ||||
|   ref, | ||||
|   toRefs, | ||||
| } from 'vue'; | ||||
| 
 | ||||
| import { $t } from '@vben/locales'; | ||||
| 
 | ||||
| import { aesEncrypt } from './../utils/ase'; | ||||
| import { resetSize } from './../utils/util'; | ||||
| 
 | ||||
| const props = withDefaults(defineProps<VerificationProps>(), { | ||||
|   barSize: () => ({ | ||||
|     height: '40px', | ||||
|     width: '310px', | ||||
|   }), | ||||
|   blockSize: () => ({ | ||||
|     height: '50px', | ||||
|     width: '50px', | ||||
|   }), | ||||
|   captchaType: 'blockPuzzle', | ||||
|   explain: '', | ||||
|   imgSize: () => ({ | ||||
|     height: '155px', | ||||
|     width: '310px', | ||||
|   }), | ||||
|   mode: 'fixed', | ||||
|   type: '1', | ||||
|   space: 5, | ||||
| }); | ||||
| 
 | ||||
| const emit = defineEmits(['onSuccess', 'onError', 'onClose']); | ||||
| 
 | ||||
| const { | ||||
|   blockSize, | ||||
|   captchaType, | ||||
|   explain, | ||||
|   mode, | ||||
|   checkCaptchaApi, | ||||
|   getCaptchaApi, | ||||
| } = toRefs(props); | ||||
| 
 | ||||
| const { proxy } = getCurrentInstance()!; | ||||
| const secretKey = ref(); // 后端返回的ase加密秘钥 | ||||
| const passFlag = ref(); // 是否通过的标识 | ||||
| const backImgBase = ref(); // 验证码背景图片 | ||||
| const blockBackImgBase = ref(); // 验证滑块的背景图片 | ||||
| const backToken = ref(); // 后端返回的唯一token值 | ||||
| const startMoveTime = ref(); // 移动开始的时间 | ||||
| const endMovetime = ref(); // 移动结束的时间 | ||||
| const tipWords = ref(); | ||||
| const text = ref(); | ||||
| const finishText = ref(); | ||||
| const setSize = reactive({ | ||||
|   barHeight: '0px', | ||||
|   barWidth: '0px', | ||||
|   imgHeight: '0px', | ||||
|   imgWidth: '0px', | ||||
| }); | ||||
| const moveBlockLeft = ref(); | ||||
| const leftBarWidth = ref(); | ||||
| // 移动中样式 | ||||
| const moveBlockBackgroundColor = ref(); | ||||
| const leftBarBorderColor = ref('#ddd'); | ||||
| const iconColor = ref(); | ||||
| const iconClass = ref('icon-right'); | ||||
| const status = ref(false); // 鼠标状态 | ||||
| const isEnd = ref(false); // 是够验证完成 | ||||
| const showRefresh = ref(true); | ||||
| const transitionLeft = ref(); | ||||
| const transitionWidth = ref(); | ||||
| const startLeft = ref(0); | ||||
| 
 | ||||
| const barArea = computed(() => { | ||||
|   return proxy?.$el.querySelector('.verify-bar-area'); | ||||
| }); | ||||
| function init() { | ||||
|   text.value = | ||||
|     explain.value === '' ? $t('ui.captcha.sliderDefaultText') : explain.value; | ||||
| 
 | ||||
|   getPictrue(); | ||||
|   nextTick(() => { | ||||
|     const { barHeight, barWidth, imgHeight, imgWidth } = resetSize(proxy); | ||||
|     setSize.imgHeight = imgHeight; | ||||
|     setSize.imgWidth = imgWidth; | ||||
|     setSize.barHeight = barHeight; | ||||
|     setSize.barWidth = barWidth; | ||||
|     proxy?.$parent?.$emit('ready', proxy); | ||||
|   }); | ||||
| 
 | ||||
|   window.removeEventListener('touchmove', move); | ||||
|   window.removeEventListener('mousemove', move); | ||||
| 
 | ||||
|   // 鼠标松开 | ||||
|   window.removeEventListener('touchend', end); | ||||
|   window.removeEventListener('mouseup', end); | ||||
| 
 | ||||
|   window.addEventListener('touchmove', move); | ||||
|   window.addEventListener('mousemove', move); | ||||
| 
 | ||||
|   // 鼠标松开 | ||||
|   window.addEventListener('touchend', end); | ||||
|   window.addEventListener('mouseup', end); | ||||
| } | ||||
| 
 | ||||
| onMounted(() => { | ||||
|   // 禁止拖拽 | ||||
|   init(); | ||||
|   proxy?.$el.addEventListener('selectstart', () => { | ||||
|     return false; | ||||
|   }); | ||||
| }); | ||||
| 
 | ||||
| // 鼠标按下 | ||||
| function start(e: MouseEvent | TouchEvent) { | ||||
|   const x = | ||||
|     ((e as TouchEvent).touches | ||||
|       ? (e as TouchEvent).touches[0]?.pageX | ||||
|       : (e as MouseEvent).clientX) || 0; | ||||
|   startLeft.value = Math.floor(x - barArea.value.getBoundingClientRect().left); | ||||
|   startMoveTime.value = Date.now(); // 开始滑动的时间 | ||||
|   if (isEnd.value === false) { | ||||
|     text.value = ''; | ||||
|     moveBlockBackgroundColor.value = '#337ab7'; | ||||
|     leftBarBorderColor.value = '#337AB7'; | ||||
|     iconColor.value = '#fff'; | ||||
|     e.stopPropagation(); | ||||
|     status.value = true; | ||||
|   } | ||||
| } | ||||
| // 鼠标移动 | ||||
| function move(e: MouseEvent | TouchEvent) { | ||||
|   if (status.value && isEnd.value === false) { | ||||
|     const x = | ||||
|       ((e as TouchEvent).touches | ||||
|         ? (e as TouchEvent).touches[0]?.pageX | ||||
|         : (e as MouseEvent).clientX) || 0; | ||||
|     const bar_area_left = barArea.value.getBoundingClientRect().left; | ||||
|     let move_block_left = x - bar_area_left; // 小方块相对于父元素的left值 | ||||
|     if ( | ||||
|       move_block_left >= | ||||
|       barArea.value.offsetWidth - Number.parseInt(blockSize.value.width) / 2 - 2 | ||||
|     ) | ||||
|       move_block_left = | ||||
|         barArea.value.offsetWidth - | ||||
|         Number.parseInt(blockSize.value.width) / 2 - | ||||
|         2; | ||||
| 
 | ||||
|     if (move_block_left <= 0) | ||||
|       move_block_left = Number.parseInt(blockSize.value.width) / 2; | ||||
| 
 | ||||
|     // 拖动后小方块的left值 | ||||
|     moveBlockLeft.value = `${move_block_left - startLeft.value}px`; | ||||
|     leftBarWidth.value = `${move_block_left - startLeft.value}px`; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| // 鼠标松开 | ||||
| function end() { | ||||
|   endMovetime.value = Date.now(); | ||||
|   // 判断是否重合 | ||||
|   if (status.value && isEnd.value === false) { | ||||
|     let moveLeftDistance = Number.parseInt( | ||||
|       (moveBlockLeft.value || '').replace('px', ''), | ||||
|     ); | ||||
|     moveLeftDistance = | ||||
|       (moveLeftDistance * 310) / Number.parseInt(setSize.imgWidth); | ||||
|     const data = { | ||||
|       captchaType: captchaType.value, | ||||
|       pointJson: secretKey.value | ||||
|         ? aesEncrypt( | ||||
|             JSON.stringify({ x: moveLeftDistance, y: 5 }), | ||||
|             secretKey.value, | ||||
|           ) | ||||
|         : JSON.stringify({ x: moveLeftDistance, y: 5 }), | ||||
|       token: backToken.value, | ||||
|     }; | ||||
|     checkCaptchaApi?.value?.(data).then((response) => { | ||||
|       const res = response.data; | ||||
|       if (res.repCode === '0000') { | ||||
|         moveBlockBackgroundColor.value = '#5cb85c'; | ||||
|         leftBarBorderColor.value = '#5cb85c'; | ||||
|         iconColor.value = '#fff'; | ||||
|         iconClass.value = 'icon-check'; | ||||
|         showRefresh.value = false; | ||||
|         isEnd.value = true; | ||||
|         if (mode.value === 'pop') { | ||||
|           setTimeout(() => { | ||||
|             emit('onClose'); | ||||
|             refresh(); | ||||
|           }, 1500); | ||||
|         } | ||||
|         passFlag.value = true; | ||||
|         tipWords.value = `${((endMovetime.value - startMoveTime.value) / 1000).toFixed(2)}s | ||||
|             ${$t('ui.captcha.title')}`; | ||||
|         const captchaVerification = secretKey.value | ||||
|           ? aesEncrypt( | ||||
|               `${backToken.value}---${JSON.stringify({ x: moveLeftDistance, y: 5 })}`, | ||||
|               secretKey.value, | ||||
|             ) | ||||
|           : `${backToken.value}---${JSON.stringify({ x: moveLeftDistance, y: 5 })}`; | ||||
|         setTimeout(() => { | ||||
|           tipWords.value = ''; | ||||
|           emit('onSuccess', { captchaVerification }); | ||||
|           emit('onClose'); | ||||
|         }, 1000); | ||||
|       } else { | ||||
|         moveBlockBackgroundColor.value = '#d9534f'; | ||||
|         leftBarBorderColor.value = '#d9534f'; | ||||
|         iconColor.value = '#fff'; | ||||
|         iconClass.value = 'icon-close'; | ||||
|         passFlag.value = false; | ||||
|         setTimeout(() => { | ||||
|           refresh(); | ||||
|         }, 1000); | ||||
|         emit('onError', proxy); | ||||
|         tipWords.value = $t('ui.captcha.sliderRotateFailTip'); | ||||
|         setTimeout(() => { | ||||
|           tipWords.value = ''; | ||||
|         }, 1000); | ||||
|       } | ||||
|     }); | ||||
|     status.value = false; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| async function refresh() { | ||||
|   showRefresh.value = true; | ||||
|   finishText.value = ''; | ||||
| 
 | ||||
|   transitionLeft.value = 'left .3s'; | ||||
|   moveBlockLeft.value = 0; | ||||
| 
 | ||||
|   leftBarWidth.value = undefined; | ||||
|   transitionWidth.value = 'width .3s'; | ||||
| 
 | ||||
|   leftBarBorderColor.value = '#ddd'; | ||||
|   moveBlockBackgroundColor.value = '#fff'; | ||||
|   iconColor.value = '#000'; | ||||
|   iconClass.value = 'icon-right'; | ||||
|   isEnd.value = false; | ||||
| 
 | ||||
|   await getPictrue(); | ||||
|   setTimeout(() => { | ||||
|     transitionWidth.value = ''; | ||||
|     transitionLeft.value = ''; | ||||
|     text.value = explain.value; | ||||
|   }, 300); | ||||
| } | ||||
| 
 | ||||
| // 请求背景图片和验证图片 | ||||
| async function getPictrue() { | ||||
|   const data = { | ||||
|     captchaType: captchaType.value, | ||||
|   }; | ||||
|   const res = await getCaptchaApi?.value?.(data); | ||||
| 
 | ||||
|   if (res?.data?.repCode === '0000') { | ||||
|     backImgBase.value = `data:image/png;base64,${res?.data?.repData?.originalImageBase64}`; | ||||
|     blockBackImgBase.value = `data:image/png;base64,${res?.data?.repData?.jigsawImageBase64}`; | ||||
|     backToken.value = res.data.repData.token; | ||||
|     secretKey.value = res.data.repData.secretKey; | ||||
|   } else { | ||||
|     tipWords.value = res?.data?.repMsg; | ||||
|   } | ||||
| } | ||||
| defineExpose({ | ||||
|   init, | ||||
|   refresh, | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <div style="position: relative"> | ||||
|     <div | ||||
|       v-if="type === '2'" | ||||
|       :style="{ height: `${Number.parseInt(setSize.imgHeight) + space}px` }" | ||||
|       class="verify-img-out" | ||||
|     > | ||||
|       <div | ||||
|         :style="{ width: setSize.imgWidth, height: setSize.imgHeight }" | ||||
|         class="verify-img-panel" | ||||
|       > | ||||
|         <img | ||||
|           :src="backImgBase" | ||||
|           alt="" | ||||
|           style="display: block; width: 100%; height: 100%" | ||||
|         /> | ||||
|         <div v-show="showRefresh" class="verify-refresh" @click="refresh"> | ||||
|           <i class="iconfont icon-refresh"></i> | ||||
|         </div> | ||||
|         <transition name="tips"> | ||||
|           <span | ||||
|             v-if="tipWords" | ||||
|             :class="passFlag ? 'suc-bg' : 'err-bg'" | ||||
|             class="verify-tips" | ||||
|           > | ||||
|             {{ tipWords }} | ||||
|           </span> | ||||
|         </transition> | ||||
|       </div> | ||||
|     </div> | ||||
|     <!-- 公共部分 --> | ||||
|     <div | ||||
|       :style="{ | ||||
|         width: setSize.imgWidth, | ||||
|         height: barSize.height, | ||||
|         'line-height': barSize.height, | ||||
|       }" | ||||
|       class="verify-bar-area" | ||||
|     > | ||||
|       <span class="verify-msg" v-text="text"></span> | ||||
|       <div | ||||
|         :style="{ | ||||
|           width: leftBarWidth !== undefined ? leftBarWidth : barSize.height, | ||||
|           height: barSize.height, | ||||
|           'border-color': leftBarBorderColor, | ||||
|           transition: transitionWidth, | ||||
|         }" | ||||
|         class="verify-left-bar" | ||||
|       > | ||||
|         <span class="verify-msg" v-text="finishText"></span> | ||||
|         <div | ||||
|           :style="{ | ||||
|             width: barSize.height, | ||||
|             height: barSize.height, | ||||
|             'background-color': moveBlockBackgroundColor, | ||||
|             left: moveBlockLeft, | ||||
|             transition: transitionLeft, | ||||
|           }" | ||||
|           class="verify-move-block" | ||||
|           @mousedown="start" | ||||
|           @touchstart="start" | ||||
|         > | ||||
|           <i | ||||
|             :class="[iconClass]" | ||||
|             :style="{ color: iconColor }" | ||||
|             class="iconfont verify-icon" | ||||
|           ></i> | ||||
|           <div | ||||
|             v-if="type === '2'" | ||||
|             :style="{ | ||||
|               width: `${Math.floor((Number.parseInt(setSize.imgWidth) * 47) / 310)}px`, | ||||
|               height: setSize.imgHeight, | ||||
|               top: `-${Number.parseInt(setSize.imgHeight) + space}px`, | ||||
|               'background-size': `${setSize.imgWidth} ${setSize.imgHeight}`, | ||||
|             }" | ||||
|             class="verify-sub-block" | ||||
|           > | ||||
|             <img | ||||
|               :src="blockBackImgBase" | ||||
|               alt="" | ||||
|               style=" | ||||
|                 display: block; | ||||
|                 width: 100%; | ||||
|                 height: 100%; | ||||
|                 -webkit-user-drag: none; | ||||
|               " | ||||
|             /> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|  | @ -0,0 +1,3 @@ | |||
| export { default as VerifyPoints } from './VerifyPoints.vue'; | ||||
| 
 | ||||
| export { default as VerifySlide } from './VerifySlide.vue'; | ||||
|  | @ -0,0 +1,150 @@ | |||
| <script setup lang="ts"> | ||||
| /** | ||||
|  * Verify 验证码组件 | ||||
|  * @description 分发验证码使用 | ||||
|  */ | ||||
| import type { VerificationProps } from './types'; | ||||
| 
 | ||||
| import { defineAsyncComponent, markRaw, ref, toRefs, watchEffect } from 'vue'; | ||||
| 
 | ||||
| import './style/verify.css'; | ||||
| 
 | ||||
| defineOptions({ | ||||
|   name: 'Verification', | ||||
| }); | ||||
| 
 | ||||
| const props = withDefaults(defineProps<VerificationProps>(), { | ||||
|   arith: 0, | ||||
|   barSize: () => ({ | ||||
|     height: '40px', | ||||
|     width: '310px', | ||||
|   }), | ||||
|   blockSize: () => ({ | ||||
|     height: '50px', | ||||
|     width: '50px', | ||||
|   }), | ||||
|   captchaType: 'blockPuzzle', | ||||
|   explain: '', | ||||
|   figure: 0, | ||||
|   imgSize: () => ({ | ||||
|     height: '155px', | ||||
|     width: '310px', | ||||
|   }), | ||||
|   mode: 'fixed', | ||||
|   space: 5, | ||||
| }); | ||||
| 
 | ||||
| const emit = defineEmits(['onSuccess', 'onError', 'onClose', 'onReady']); | ||||
| 
 | ||||
| const VerifyPoints = defineAsyncComponent( | ||||
|   () => import('./Verify/VerifyPoints.vue'), | ||||
| ); | ||||
| const VerifySlide = defineAsyncComponent( | ||||
|   () => import('./Verify/VerifySlide.vue'), | ||||
| ); | ||||
| 
 | ||||
| const { captchaType, mode, checkCaptchaApi, getCaptchaApi } = toRefs(props); | ||||
| const verifyType = ref(); | ||||
| const componentType = ref(); | ||||
| 
 | ||||
| const instance = ref<InstanceType<typeof VerifyPoints | typeof VerifySlide>>(); | ||||
| 
 | ||||
| const showBox = ref(false); | ||||
| 
 | ||||
| /** | ||||
|  * refresh | ||||
|  * @description 刷新 | ||||
|  */ | ||||
| const refresh = () => { | ||||
|   if (instance.value && instance.value.refresh) instance.value.refresh(); | ||||
| }; | ||||
| 
 | ||||
| const show = () => { | ||||
|   if (mode.value === 'pop') showBox.value = true; | ||||
| }; | ||||
| 
 | ||||
| const onError = (proxy: any) => { | ||||
|   emit('onError', proxy); | ||||
|   refresh(); | ||||
| }; | ||||
| 
 | ||||
| const onReady = (proxy: any) => { | ||||
|   emit('onReady', proxy); | ||||
|   refresh(); | ||||
| }; | ||||
| 
 | ||||
| const onClose = () => { | ||||
|   emit('onClose'); | ||||
|   showBox.value = false; | ||||
| }; | ||||
| 
 | ||||
| const onSuccess = (data: any) => { | ||||
|   emit('onSuccess', data); | ||||
| }; | ||||
| 
 | ||||
| watchEffect(() => { | ||||
|   switch (captchaType.value) { | ||||
|     case 'blockPuzzle': { | ||||
|       verifyType.value = '2'; | ||||
|       componentType.value = markRaw(VerifySlide); | ||||
|       break; | ||||
|     } | ||||
|     case 'clickWord': { | ||||
|       verifyType.value = ''; | ||||
|       componentType.value = markRaw(VerifyPoints); | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
| }); | ||||
| 
 | ||||
| defineExpose({ | ||||
|   onClose, | ||||
|   onError, | ||||
|   onReady, | ||||
|   onSuccess, | ||||
|   show, | ||||
|   refresh, | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <div v-show="showBox"> | ||||
|     <div | ||||
|       :class="mode === 'pop' ? 'verifybox' : ''" | ||||
|       :style="{ 'max-width': `${parseInt(imgSize.width) + 20}px` }" | ||||
|     > | ||||
|       <div v-if="mode === 'pop'" class="verifybox-top"> | ||||
|         {{ $t('ui.captcha.title') }} | ||||
|         <span class="verifybox-close" @click="onClose"> | ||||
|           <i class="iconfont icon-close"></i> | ||||
|         </span> | ||||
|       </div> | ||||
|       <div | ||||
|         :style="{ padding: mode === 'pop' ? '10px' : '0' }" | ||||
|         class="verifybox-bottom" | ||||
|       > | ||||
|         <component | ||||
|           :is="componentType" | ||||
|           v-if="componentType" | ||||
|           ref="instance" | ||||
|           :arith="arith" | ||||
|           :bar-size="barSize" | ||||
|           :block-size="blockSize" | ||||
|           :captcha-type="captchaType" | ||||
|           :check-captcha-api="checkCaptchaApi" | ||||
|           :explain="explain" | ||||
|           :figure="figure" | ||||
|           :get-captcha-api="getCaptchaApi" | ||||
|           :img-size="imgSize" | ||||
|           :mode="mode" | ||||
|           :space="space" | ||||
|           :type="verifyType" | ||||
|           @on-close="onClose" | ||||
|           @on-error="onError" | ||||
|           @on-ready="onReady" | ||||
|           @on-success="onSuccess" | ||||
|         /> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										25
									
								
								packages/effects/common-ui/src/components/captcha/verification/types/index.d.ts
								
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										25
									
								
								packages/effects/common-ui/src/components/captcha/verification/types/index.d.ts
								
								
								
									vendored
								
								
									Normal file
								
							|  | @ -0,0 +1,25 @@ | |||
| interface VerificationProps { | ||||
|   arith?: number; | ||||
|   barSize?: { | ||||
|     height: string; | ||||
|     width: string; | ||||
|   }; | ||||
|   blockSize?: { | ||||
|     height: string; | ||||
|     width: string; | ||||
|   }; | ||||
|   captchaType?: 'blockPuzzle' | 'clickWord'; | ||||
|   explain?: string; | ||||
|   figure?: number; | ||||
|   imgSize?: { | ||||
|     height: string; | ||||
|     width: string; | ||||
|   }; | ||||
|   mode?: 'fixed' | 'pop'; | ||||
|   space?: number; | ||||
|   type?: '1' | '2'; | ||||
|   checkCaptchaApi?: (data: any) => Promise<any>; | ||||
|   getCaptchaApi?: (data: any) => Promise<any>; | ||||
| } | ||||
| 
 | ||||
| export type { VerificationProps }; | ||||
|  | @ -0,0 +1,15 @@ | |||
| import CryptoJS from 'crypto-js'; | ||||
| 
 | ||||
| /** | ||||
|  * @word 要加密的内容 | ||||
|  * @keyWord String  服务器随机返回的关键字 | ||||
|  */ | ||||
| export function aesEncrypt(word: string, keyWord = 'XwKsGlMcdPMEhR1B') { | ||||
|   const key = CryptoJS.enc.Utf8.parse(keyWord); | ||||
|   const src = CryptoJS.enc.Utf8.parse(word); | ||||
|   const encrypted = CryptoJS.AES.encrypt(src, key, { | ||||
|     mode: CryptoJS.mode.ECB, | ||||
|     padding: CryptoJS.pad.Pkcs7, | ||||
|   }); | ||||
|   return encrypted.toString(); | ||||
| } | ||||
|  | @ -0,0 +1,102 @@ | |||
| export function resetSize(vm: any) { | ||||
|   const EmployeeWindow = window as any; | ||||
|   const parentWidth = | ||||
|     vm.$el.parentNode.offsetWidth || EmployeeWindow.offsetWidth; | ||||
|   const parentHeight = | ||||
|     vm.$el.parentNode.offsetHeight || EmployeeWindow.offsetHeight; | ||||
|   const img_width = vm.imgSize.width.includes('%') | ||||
|     ? `${(Number.parseInt(vm.imgSize.width) / 100) * parentWidth}px` | ||||
|     : vm.imgSize.width; | ||||
| 
 | ||||
|   const img_height = vm.imgSize.height.includes('%') | ||||
|     ? `${(Number.parseInt(vm.imgSize.height) / 100) * parentHeight}px` | ||||
|     : vm.imgSize.height; | ||||
| 
 | ||||
|   const bar_width = vm.barSize.width.includes('%') | ||||
|     ? `${(Number.parseInt(vm.barSize.width) / 100) * parentWidth}px` | ||||
|     : vm.barSize.width; | ||||
| 
 | ||||
|   const bar_height = vm.barSize.height.includes('%') | ||||
|     ? `${(Number.parseInt(vm.barSize.height) / 100) * parentHeight}px` | ||||
|     : vm.barSize.height; | ||||
| 
 | ||||
|   return { | ||||
|     barHeight: bar_height, | ||||
|     barWidth: bar_width, | ||||
|     imgHeight: img_height, | ||||
|     imgWidth: img_width, | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export const _code_chars = [ | ||||
|   1, | ||||
|   2, | ||||
|   3, | ||||
|   4, | ||||
|   5, | ||||
|   6, | ||||
|   7, | ||||
|   8, | ||||
|   9, | ||||
|   'a', | ||||
|   'b', | ||||
|   'c', | ||||
|   'd', | ||||
|   'e', | ||||
|   'f', | ||||
|   'g', | ||||
|   'h', | ||||
|   'i', | ||||
|   'j', | ||||
|   'k', | ||||
|   'l', | ||||
|   'm', | ||||
|   'n', | ||||
|   'o', | ||||
|   'p', | ||||
|   'q', | ||||
|   'r', | ||||
|   's', | ||||
|   't', | ||||
|   'u', | ||||
|   'v', | ||||
|   'w', | ||||
|   'x', | ||||
|   'y', | ||||
|   'z', | ||||
|   'A', | ||||
|   'B', | ||||
|   'C', | ||||
|   'D', | ||||
|   'E', | ||||
|   'F', | ||||
|   'G', | ||||
|   'H', | ||||
|   'I', | ||||
|   'J', | ||||
|   'K', | ||||
|   'L', | ||||
|   'M', | ||||
|   'N', | ||||
|   'O', | ||||
|   'P', | ||||
|   'Q', | ||||
|   'R', | ||||
|   'S', | ||||
|   'T', | ||||
|   'U', | ||||
|   'V', | ||||
|   'W', | ||||
|   'X', | ||||
|   'Y', | ||||
|   'Z', | ||||
| ]; | ||||
| export const _code_color1 = ['#fffff0', '#f0ffff', '#f0fff0', '#fff0f0']; | ||||
| export const _code_color2 = [ | ||||
|   '#FF0033', | ||||
|   '#006699', | ||||
|   '#993366', | ||||
|   '#FF9900', | ||||
|   '#66CC66', | ||||
|   '#FF33CC', | ||||
| ]; | ||||
|  | @ -78,6 +78,9 @@ catalogs: | |||
|     '@types/archiver': | ||||
|       specifier: ^6.0.3 | ||||
|       version: 6.0.3 | ||||
|     '@types/crypto-js': | ||||
|       specifier: ^4.2.2 | ||||
|       version: 4.2.2 | ||||
|     '@types/eslint': | ||||
|       specifier: ^9.6.1 | ||||
|       version: 9.6.1 | ||||
|  | @ -183,6 +186,9 @@ catalogs: | |||
|     cross-env: | ||||
|       specifier: ^7.0.3 | ||||
|       version: 7.0.3 | ||||
|     crypto-js: | ||||
|       specifier: ^4.2.0 | ||||
|       version: 4.2.0 | ||||
|     cspell: | ||||
|       specifier: ^8.17.5 | ||||
|       version: 8.17.5 | ||||
|  | @ -1530,6 +1536,9 @@ importers: | |||
|       '@vueuse/integrations': | ||||
|         specifier: 'catalog:' | ||||
|         version: 12.8.2(async-validator@4.2.5)(axios@1.8.2)(change-case@5.4.4)(focus-trap@7.6.4)(nprogress@0.2.0)(qrcode@1.5.4)(sortablejs@1.15.6)(typescript@5.8.2) | ||||
|       crypto-js: | ||||
|         specifier: 'catalog:' | ||||
|         version: 4.2.0 | ||||
|       qrcode: | ||||
|         specifier: 'catalog:' | ||||
|         version: 1.5.4 | ||||
|  | @ -1549,6 +1558,9 @@ importers: | |||
|         specifier: 'catalog:' | ||||
|         version: 6.6.0(vue@3.5.13(typescript@5.8.2)) | ||||
|     devDependencies: | ||||
|       '@types/crypto-js': | ||||
|         specifier: 'catalog:' | ||||
|         version: 4.2.2 | ||||
|       '@types/qrcode': | ||||
|         specifier: 'catalog:' | ||||
|         version: 1.5.5 | ||||
|  | @ -3534,22 +3546,22 @@ packages: | |||
|     resolution: {integrity: sha512-nmG512G8QOABsserleechwHGZxzKSAlggGf9hQX0nltvSwyKNVuB/4o6iFeG2OnjXK253r8p8eSDOZf8PgFdWw==} | ||||
|     engines: {node: '>= 16'} | ||||
| 
 | ||||
|   '@intlify/message-compiler@11.0.0-rc.1': | ||||
|     resolution: {integrity: sha512-TGw2uBfuTFTegZf/BHtUQBEKxl7Q/dVGLoqRIdw8lFsp9g/53sYn5iD+0HxIzdYjbWL6BTJMXCPUHp9PxDTRPw==} | ||||
|     engines: {node: '>= 16'} | ||||
| 
 | ||||
|   '@intlify/message-compiler@11.1.2': | ||||
|     resolution: {integrity: sha512-T/xbNDzi+Yv0Qn2Dfz2CWCAJiwNgU5d95EhhAEf4YmOgjCKktpfpiUSmLcBvK1CtLpPQ85AMMQk/2NCcXnNj1g==} | ||||
|     engines: {node: '>= 16'} | ||||
| 
 | ||||
|   '@intlify/shared@11.0.0-rc.1': | ||||
|     resolution: {integrity: sha512-8tR1xe7ZEbkabTuE/tNhzpolygUn9OaYp9yuYAF4MgDNZg06C3Qny80bes2/e9/Wm3aVkPUlCw6WgU7mQd0yEg==} | ||||
|   '@intlify/message-compiler@12.0.0-alpha.2': | ||||
|     resolution: {integrity: sha512-PD9C+oQbb7BF52hec0+vLnScaFkvnfX+R7zSbODYuRo/E2niAtGmHd0wPvEMsDhf9Z9b8f/qyDsVeZnD/ya9Ug==} | ||||
|     engines: {node: '>= 16'} | ||||
| 
 | ||||
|   '@intlify/shared@11.1.2': | ||||
|     resolution: {integrity: sha512-dF2iMMy8P9uKVHV/20LA1ulFLL+MKSbfMiixSmn6fpwqzvix38OIc7ebgnFbBqElvghZCW9ACtzKTGKsTGTWGA==} | ||||
|     engines: {node: '>= 16'} | ||||
| 
 | ||||
|   '@intlify/shared@12.0.0-alpha.2': | ||||
|     resolution: {integrity: sha512-P2DULVX9nz3y8zKNqLw9Es1aAgQ1JGC+kgpx5q7yLmrnAKkPR5MybQWoEhxanefNJgUY5ehsgo+GKif59SrncA==} | ||||
|     engines: {node: '>= 16'} | ||||
| 
 | ||||
|   '@intlify/unplugin-vue-i18n@6.0.3': | ||||
|     resolution: {integrity: sha512-9ZDjBlhUHtgjRl23TVcgfJttgu8cNepwVhWvOv3mUMRDAhjW0pur1mWKEUKr1I8PNwE4Gvv2IQ1xcl4RL0nG0g==} | ||||
|     engines: {node: '>= 18'} | ||||
|  | @ -4232,6 +4244,9 @@ packages: | |||
|   '@types/conventional-commits-parser@5.0.1': | ||||
|     resolution: {integrity: sha512-7uz5EHdzz2TqoMfV7ee61Egf5y6NkcO4FB/1iCCQnbeiI1F3xzv3vK5dBCXUCLQgGYS+mUeigK1iKQzvED+QnQ==} | ||||
| 
 | ||||
|   '@types/crypto-js@4.2.2': | ||||
|     resolution: {integrity: sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==} | ||||
| 
 | ||||
|   '@types/doctrine@0.0.9': | ||||
|     resolution: {integrity: sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA==} | ||||
| 
 | ||||
|  | @ -5367,6 +5382,9 @@ packages: | |||
|   crossws@0.3.4: | ||||
|     resolution: {integrity: sha512-uj0O1ETYX1Bh6uSgktfPvwDiPYGQ3aI4qVsaC/LWpkIzGj1nUYm5FK3K+t11oOlpN01lGbprFCH4wBlKdJjVgw==} | ||||
| 
 | ||||
|   crypto-js@4.2.0: | ||||
|     resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} | ||||
| 
 | ||||
|   crypto-random-string@2.0.0: | ||||
|     resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} | ||||
|     engines: {node: '>=8'} | ||||
|  | @ -12279,8 +12297,8 @@ snapshots: | |||
| 
 | ||||
|   '@intlify/bundle-utils@10.0.0(vue-i18n@11.1.2(vue@3.5.13(typescript@5.8.2)))': | ||||
|     dependencies: | ||||
|       '@intlify/message-compiler': 11.0.0-rc.1 | ||||
|       '@intlify/shared': 11.0.0-rc.1 | ||||
|       '@intlify/message-compiler': 12.0.0-alpha.2 | ||||
|       '@intlify/shared': 12.0.0-alpha.2 | ||||
|       acorn: 8.14.1 | ||||
|       escodegen: 2.1.0 | ||||
|       estree-walker: 2.0.2 | ||||
|  | @ -12296,20 +12314,20 @@ snapshots: | |||
|       '@intlify/message-compiler': 11.1.2 | ||||
|       '@intlify/shared': 11.1.2 | ||||
| 
 | ||||
|   '@intlify/message-compiler@11.0.0-rc.1': | ||||
|     dependencies: | ||||
|       '@intlify/shared': 11.0.0-rc.1 | ||||
|       source-map-js: 1.2.1 | ||||
| 
 | ||||
|   '@intlify/message-compiler@11.1.2': | ||||
|     dependencies: | ||||
|       '@intlify/shared': 11.1.2 | ||||
|       source-map-js: 1.2.1 | ||||
| 
 | ||||
|   '@intlify/shared@11.0.0-rc.1': {} | ||||
|   '@intlify/message-compiler@12.0.0-alpha.2': | ||||
|     dependencies: | ||||
|       '@intlify/shared': 12.0.0-alpha.2 | ||||
|       source-map-js: 1.2.1 | ||||
| 
 | ||||
|   '@intlify/shared@11.1.2': {} | ||||
| 
 | ||||
|   '@intlify/shared@12.0.0-alpha.2': {} | ||||
| 
 | ||||
|   '@intlify/unplugin-vue-i18n@6.0.3(@vue/compiler-dom@3.5.13)(eslint@9.22.0(jiti@2.4.2))(rollup@4.35.0)(typescript@5.8.2)(vue-i18n@11.1.2(vue@3.5.13(typescript@5.8.2)))(vue@3.5.13(typescript@5.8.2))': | ||||
|     dependencies: | ||||
|       '@eslint-community/eslint-utils': 4.4.1(eslint@9.22.0(jiti@2.4.2)) | ||||
|  | @ -13055,6 +13073,8 @@ snapshots: | |||
|     dependencies: | ||||
|       '@types/node': 22.13.10 | ||||
| 
 | ||||
|   '@types/crypto-js@4.2.2': {} | ||||
| 
 | ||||
|   '@types/doctrine@0.0.9': {} | ||||
| 
 | ||||
|   '@types/eslint@9.6.1': | ||||
|  | @ -14410,6 +14430,8 @@ snapshots: | |||
|     dependencies: | ||||
|       uncrypto: 0.1.3 | ||||
| 
 | ||||
|   crypto-js@4.2.0: {} | ||||
| 
 | ||||
|   crypto-random-string@2.0.0: {} | ||||
| 
 | ||||
|   cspell-config-lib@8.17.5: | ||||
|  |  | |||
|  | @ -50,6 +50,7 @@ catalog: | |||
|   '@types/postcss-import': ^14.0.3 | ||||
|   '@types/qrcode': ^1.5.5 | ||||
|   '@types/sortablejs': ^1.15.8 | ||||
|   '@types/crypto-js': ^4.2.2 | ||||
|   '@typescript-eslint/eslint-plugin': ^8.26.0 | ||||
|   '@typescript-eslint/parser': ^8.26.0 | ||||
|   '@vee-validate/zod': ^4.15.0 | ||||
|  | @ -76,6 +77,7 @@ catalog: | |||
|   commitlint-plugin-function-rules: ^4.0.1 | ||||
|   consola: ^3.4.0 | ||||
|   cross-env: ^7.0.3 | ||||
|   crypto-js: ^4.2.0 | ||||
|   cspell: ^8.17.5 | ||||
|   cssnano: ^7.0.6 | ||||
|   cz-git: ^1.11.1 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 YunaiV
						YunaiV