这个是else if (name === '咖密') { let type = true ...

創建於:2025年12月10日

使用 GPT-5.1 Thinking 回答 Chat01

問題

image.png

这个是else if (name === '咖密') {
let type = true
if(pas.value.defaultSecretPasswordType===1){

text
uni.navigateTo({ url: '/pages/sociAlize/Coffee/setpassword/gesture?decrypt=' + type }) }else if(pas.value.defaultSecretPasswordType===2){ uni.navigateTo({ url: '/pages/sociAlize/Coffee/setpassword/Textpassword?decrypt=' + type }) } // uni.navigateTo({ // url: '/pages/sociAlize/Coffee/Coffeeability' // }) 点击咖密进入uni.navigateTo({ url: '/pages/sociAlize/Coffee/setpassword/gesture?decrypt=' + type }) 因为type是2 这是gesture 页面是手势的加密页面 <!-- /pages/sociAlize/Coffee/setpassword/gesture.vue -->
<template> <view :class="['app-container', theme]"> <view class="content"> <view class="overlay"> <!-- 顶部 Header --> <view class="header" :style="{ paddingTop: statusBarHeight + 'px', height: headerBarHeight + 'px' }"> <view class="header-inner"> <view class="left-box" @click="navigateBack"> <uni-icons type="left" color="#000000" size="26" /> </view> <view class="center-box"> <text class="title-text">咖密</text> </view> <view class="right-box"> <text class="desc-text"></text> </view> </view> </view>
text
<!-- 内容 --> <view class="page-body"> <view class="card"> <view class="row"> <text class="label">手势密码ID:</text> <text class="val">{{ state.secretGesturePasswordId || '未设置' }}</text> </view> <view class="row"> <text class="label">默认解锁方式:</text> <text class="val"> {{ state.defaultSecretPasswordType === GESTURE_TYPE ? '手势' : state.defaultSecretPasswordType === 2 ? '文字' : '未设置' }} </text> </view> </view> <view class="tabs"> <view class="tab" :class="{ active: ui.mode === 'set' }" @click="switchMode('set')"> 设置/重设 </view> <view class="tab" :class="{ active: ui.mode === 'check' }" @click="switchMode('check')"> 验证 </view> </view> <view class="lock-wrap"> <GestureLock ref="lockRef" :tips="ui.tips" :min-points="4" :line-color="ui.line" :error-color="ui.error" :fill-color="ui.fill" :stroke-color="ui.stroke" @complete="onPatternComplete" /> </view> <view class="btns"> <button class="btn primary" @click="setAsDefault" :disabled="!state.secretGesturePasswordId"> 设为默认(手势) </button> </view> </view> </view> </view> </view>
</template> <script setup> import { ref, reactive, onMounted } from 'vue' import { onLoad } from '@dcloudio/uni-app' import { storeToRefs } from 'pinia' import { useMainStore } from '@/store/index.js' import { getStatusBarHeight, getTitleBarHeight } from '@/utils/system.js' import ajax from '@/api/ajax.js' import GestureLock from '@/components/GestureLock/GestureLock.vue' const GESTURE_TYPE = 1 const store = useMainStore?.() || {} const { theme = ref('') } = storeToRefs?.(store) || { theme: ref('') } const statusBarHeight = ref(getStatusBarHeight()) const headerBarHeight = ref(getTitleBarHeight()) const ui = reactive({ mode: 'set', // set | check tips: '请绘制手势密码', line: '#007aff', error: '#fa3534', fill: '#ffffff', stroke: '#c0c0c0' }) const state = reactive({ consumerId: '', secretGesturePasswordId: '', secretStringPasswordId: '', defaultSecretPasswordType: 0, // from 参数(用于校验成功后跳转) redirectAfterCheck: '', redirectTarget: '' }) const lockRef = ref(null) const setFlow = reactive({ first: '', pendingConfirm: false, submitting: false }) // API const API = { passwordSave: (payload) => ajax.post({ url: 'password', method: 'post', data: payload }), validatePassword: (id, password) => ajax.post({ url: 'validatePassword', method: 'post', data: { id, password } }), consumerSecretPassword: ({ consumerId, passwordId, defaultSecretPasswordType }) => ajax.post({ url: 'consumerSecretPassword', method: 'post', data: { consumerId, passwordId, defaultSecretPasswordType } }), consumerSecretPasswordShow: (consumerId) => ajax.post({ url: 'consumerSecretPasswordShow', method: 'post', data: { consumerId } }) } function navigateBack() { uni.navigateBack() } onLoad((option = {}) => { // consumerId:默认取自己 if (option.consumerId) state.consumerId = option.consumerId else if (store?.user?.id) state.consumerId = store.user.id else state.consumerId = uni.getStorageSync('consumerId') || '' // 允许从外部传入模式 / passwordId,用于“只校验”的场景 if (option.mode === 'check') ui.mode = 'check' if (option.passwordId) { state.secretGesturePasswordId = option.passwordId } if (option.redirect) { state.redirectAfterCheck = option.redirect } if (option.target) { state.redirectTarget = option.target } }) onMounted(() => { refreshProfile() }) // 查询当前用户的咖密配置 async function refreshProfile() { try { if (!state.consumerId) return const res = await API.consumerSecretPasswordShow(state.consumerId) console.log(res,'手势设置的回显密码') const data = res?.data || res if (!data) return state.secretGesturePasswordId = data.secretGesturePasswordId || state.secretGesturePasswordId || '' state.secretStringPasswordId = data.secretStringPasswordId || '' state.defaultSecretPasswordType = typeof data.defaultSecretPasswordType === 'number' ? data.defaultSecretPasswordType : Number(data.defaultSecretPasswordType || 0) } catch (e) {} } function switchMode(m) { ui.mode = m ui.tips = m === 'set' ? setFlow.pendingConfirm ? '请再次绘制确认' : '请绘制手势密码' : '请绘制已设置的手势进行验证' lockRef.value?.reset() } async function onPatternComplete(pwd) { if (ui.mode === 'set') await handleSetFlow(pwd) else await handleCheckFlow(pwd) } // 设置流程 async function handleSetFlow(pwd) { if (!setFlow.pendingConfirm) { setFlow.first = pwd setFlow.pendingConfirm = true ui.tips = '请再次绘制确认' lockRef.value?.flashSuccess(300) return } if (pwd !== setFlow.first) { ui.tips = '与第一次不一致,请重试' setFlow.first = '' setFlow.pendingConfirm = false lockRef.value?.flashError() return } if (setFlow.submitting) return setFlow.submitting = true try { const payload = { id: state.secretGesturePasswordId || undefined, password: pwd, type: GESTURE_TYPE } const saveRes = await API.passwordSave(payload) const newId = saveRes?.data?.id || saveRes?.id if (!newId) throw new Error('保存失败') state.secretGesturePasswordId = newId // 设置为默认手势(全局咖密) if (state.consumerId) { let res = await API.consumerSecretPassword({ consumerId: state.consumerId, passwordId: newId, defaultSecretPasswordType: GESTURE_TYPE }) console.log(res,'手势设置的密码') state.defaultSecretPasswordType = GESTURE_TYPE } ui.tips = '设置成功' lockRef.value?.flashSuccess() setFlow.first = '' setFlow.pendingConfirm = false uni.showToast({ title: '设置成功', icon: 'success' }) } catch (e) { ui.tips = '保存失败,请重试' lockRef.value?.flashError() } finally { setFlow.submitting = false } } // 验证流程 async function handleCheckFlow(pwd) { const id = state.secretGesturePasswordId if (!id) { ui.tips = '尚未设置手势密码' lockRef.value?.flashError() return } try { const res = await API.validatePassword(id, pwd) const result = res?.data?.result || res?.result if (result === 'success') { ui.tips = '验证成功' lockRef.value?.flashSuccess() uni.showToast({ title: '验证成功', icon: 'success' }) // 如果从好友页过来,需要验证通过后进入聊天页面 if (state.redirectAfterCheck) { setTimeout(() => { uni.navigateTo({ url: state.redirectAfterCheck + (state.redirectTarget ? state.redirectTarget : '') }) }, 200) } } else { ui.tips = result || '密码错误' lockRef.value?.flashError() } } catch (e) { ui.tips = '验证失败,请稍后再试' lockRef.value?.flashError() } } async function setAsDefault() { if (!state.secretGesturePasswordId || !state.consumerId) return try { await API.consumerSecretPassword({ consumerId: state.consumerId, passwordId: state.secretGesturePasswordId, defaultSecretPasswordType: GESTURE_TYPE }) state.defaultSecretPasswordType = GESTURE_TYPE uni.showToast({ title: '已设为默认(手势)', icon: 'success' }) } catch (e) { uni.showToast({ title: '设置默认失败', icon: 'none' }) } } </script> <style lang="scss" scoped> @import '@/assets/styles/global.scss'; .content { min-height: 100vh; position: relative; background-size: cover; } .overlay { position: absolute; inset: 0; background-size: cover; } .header { width: 94%; margin: 0 auto; } .header-inner { position: relative; display: flex; align-items: center; justify-content: center; height: 100%; } .left-box, .right-box { position: absolute; top: 0; bottom: 0; width: 120rpx; display: flex; align-items: center; } .left-box { left: 0; justify-content: flex-start; } .right-box { right: 0; justify-content: flex-end; } .center-box { flex: none; } .page-body { width: 94%; margin: 0 auto; padding-bottom: 40rpx; } .card { margin-top: 24rpx; padding: 24rpx; background: #fff; border-radius: 16rpx; .row { display: flex; margin-bottom: 12rpx; font-size: 28rpx; } .label { color: #666; width: 260rpx; } .val { color: #111; flex: 1; } } .tabs { margin-top: 24rpx; display: flex; background: #fff; border-radius: 16rpx; overflow: hidden; .tab { flex: 1; text-align: center; padding: 22rpx 0; font-size: 30rpx; color: #666; } .tab.active { color: #007aff; font-weight: 600; background: #f5f8ff; } } .lock-wrap { margin-top: 30rpx; background: #fff; border-radius: 16rpx; padding: 20rpx 0 40rpx; } .btns { margin-top: 24rpx; display: grid; grid-template-columns: 1fr 1fr; gap: 20rpx; .btn { border-radius: 12rpx; } .primary { background: #007aff; color: #fff; } } </style>这是手势页面的组件GestureLock<!-- /components/GestureLock/GestureLock.vue --> <template> <view class="g-box"> <canvas canvas-id="gesture" id="gesture" class="g-canvas" @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd" /> <view class="g-tips">{{ innerTips }}</view> </view> </template> <script setup> import { ref, watch, getCurrentInstance } from 'vue' import { onReady } from '@dcloudio/uni-app' const props = defineProps({ tips: { type: String, default: '请绘制手势密码' }, minPoints: { type: Number, default: 4 }, lineColor: { type: String, default: '#D89833' }, errorColor: { type: String, default: '#fa3534' }, fillColor: { type: String, default: '#ffffff' }, strokeColor: { type: String, default: '#c0c0c0' }, radiusRpx: { type: Number, default: 40 }, marginRpx: { type: Number, default: 80 } }) const emit = defineEmits(['complete']) const innerTips = ref(props.tips) watch( () => props.tips, (v) => (innerTips.value = v) ) let ctx = null let canvasRect = { width: 0, height: 0, left: 0, top: 0 } let scale = 1 let nodes = [] let activeIdx = [] let touching = false let lastPos = { x: 0, y: 0 } onReady(() => { const inst = getCurrentInstance() const proxy = inst?.proxy uni .createSelectorQuery() .in(proxy) .select('#gesture') .boundingClientRect((rect) => { canvasRect = rect || { width: 0, height: 0, left: 0, top: 0 } if (!canvasRect.width) { // 防止 App 上取不到 rect 宽度 const sys = uni.getSystemInfoSync() canvasRect.width = sys.windowWidth || 300 canvasRect.height = canvasRect.width } scale = canvasRect.width / 750 ctx = uni.createCanvasContext('gesture', proxy) buildNodes() draw() }) .exec() }) function buildNodes() { const r = props.radiusRpx * scale const margin = props.marginRpx * scale const w = canvasRect.width const sideGap = (w - 2 * margin - 6 * r) / 2 nodes = [] for (let i = 0; i < 9; i++) { const row = Math.floor(i / 3) const col = i % 3 nodes.push({ x: margin + r + col * (2 * r + sideGap), y: margin + r + row * (2 * r + sideGap), r }) } } function draw(color = props.lineColor) { if (!ctx) return ctx.clearRect(0, 0, canvasRect.width, canvasRect.height) nodes.forEach((n, i) => { ctx.beginPath() ctx.arc(n.x, n.y, n.r, 0, Math.PI * 2) const hit = activeIdx.includes(i) ctx.setFillStyle(hit ? color : props.fillColor) ctx.setStrokeStyle(hit ? color : props.strokeColor) ctx.setLineWidth(2) ctx.fill() ctx.stroke() }) if (activeIdx.length > 0) { ctx.beginPath() ctx.setStrokeStyle(color) ctx.setLineWidth(4) ctx.setLineCap('round') ctx.moveTo(nodes[activeIdx[0]].x, nodes[activeIdx[0]].y) for (let k = 1; k < activeIdx.length; k++) { ctx.lineTo(nodes[activeIdx[k]].x, nodes[activeIdx[k]].y) } if (touching) ctx.lineTo(lastPos.x, lastPos.y) ctx.stroke() } ctx.draw() } function touchStart(e) { touching = true activeIdx = [] processPoint(e.touches[0]) } function touchMove(e) { if (!touching) return processPoint(e.touches[0]) } function touchEnd() { if (!touching) return touching = false const pwd = activeIdx.join('') if (activeIdx.length < props.minPoints) { innerTips.value = `至少连接 ${props.minPoints} 个点` flashError(600) setTimeout(reset, 600) return } emit('complete', pwd) } function processPoint(t) { const p = localXY(t) lastPos = p const idx = nodes.findIndex((n) => distance(n, p) <= n.r) if (idx !== -1 && !activeIdx.includes(idx)) { activeIdx.push(idx) try { uni.vibrateShort() } catch (e) {} } draw() } function localXY(t) { const x = typeof t.x === 'number' ? t.x : t.clientX - (canvasRect.left || 0) const y = typeof t.y === 'number' ? t.y : t.clientY - (canvasRect.top || 0) return { x, y } } function distance(a, b) { return Math.hypot(a.x - b.x, a.y - b.y) } function reset() { activeIdx = [] draw() } function flashError(delay = 800) { draw(props.errorColor) setTimeout(() => draw(), delay) } function flashSuccess(delay = 300) { draw(props.lineColor) setTimeout(() => draw(), delay) } defineExpose({ reset, flashError, flashSuccess }) </script> <style scoped> .g-box { display: flex; flex-direction: column; align-items: center; padding-top: 40rpx; } .g-canvas { width: 750rpx; height: 750rpx; } .g-tips { margin-top: 30rpx; font-size: 30rpx; color: #333; } </style> 目前是在这里跳这个 else if(pas.value.defaultSecretPasswordType===2){
text
uni.navigateTo({ url: '/pages/sociAlize/Coffee/setpassword/Textpassword?decrypt=' + type }) } 是属于直接验证密码的 这个gesture页面是<!-- /pages/sociAlize/Coffee/setpassword/gesture.vue -->
<template> <view :class="['app-container', theme]"> <view class="content"> <view class="overlay"> <!-- 顶部 Header --> <view class="header" :style="{ paddingTop: statusBarHeight + 'px', height: headerBarHeight + 'px' }"> <view class="header-inner"> <view class="left-box" @click="navigateBack"> <uni-icons type="left" color="#000000" size="26" /> </view> <view class="center-box"> <text class="title-text">咖密</text> </view> <view class="right-box"> <text class="desc-text"></text> </view> </view> </view>
text
<!-- 内容 --> <view class="page-body"> <view class="card"> <view class="row"> <text class="label">手势密码ID:</text> <text class="val">{{ state.secretGesturePasswordId || '未设置' }}</text> </view> <view class="row"> <text class="label">默认解锁方式:</text> <text class="val"> {{ state.defaultSecretPasswordType === GESTURE_TYPE ? '手势' : state.defaultSecretPasswordType === 2 ? '文字' : '未设置' }} </text> </view> </view> <view class="tabs"> <view class="tab" :class="{ active: ui.mode === 'set' }" @click="switchMode('set')"> 设置/重设 </view> <view class="tab" :class="{ active: ui.mode === 'check' }" @click="switchMode('check')"> 验证 </view> </view> <view class="lock-wrap"> <GestureLock ref="lockRef" :tips="ui.tips" :min-points="4" :line-color="ui.line" :error-color="ui.error" :fill-color="ui.fill" :stroke-color="ui.stroke" @complete="onPatternComplete" /> </view> <view class="btns"> <button class="btn primary" @click="setAsDefault" :disabled="!state.secretGesturePasswordId"> 设为默认(手势) </button> </view> </view> </view> </view> </view>
</template> <script setup> import { usecontacts } from '@/store/usecontacts.js' import { ref, reactive, onMounted } from 'vue' import { onLoad } from '@dcloudio/uni-app' import { storeToRefs } from 'pinia' import { useMainStore } from '@/store/index.js' import { getStatusBarHeight, getTitleBarHeight } from '@/utils/system.js' import ajax from '@/api/ajax.js' import GestureLock from '@/components/GestureLock/GestureLock.vue' const GESTURE_TYPE = 1 const store = useMainStore?.() || {} const { theme = ref('') } = storeToRefs?.(store) || { theme: ref('') } const statusBarHeight = ref(getStatusBarHeight()) const headerBarHeight = ref(getTitleBarHeight()) const storeer = usecontacts() const { indexList, groupedItemArr, newfriend, webProjectUrl, navOffsetTop, itemArr, pas } = storeToRefs(storeer) const { difference, FriendDetails, getInitial, setNavOffsetTop, refreshIndexLayoutFromStore, getGetcontacts } = storeer const ui = reactive({ mode: 'set', // set | check tips: '请绘制手势密码', line: '#007aff', error: '#fa3534', fill: '#ffffff', stroke: '#c0c0c0' }) const state = reactive({ consumerId: '', secretGesturePasswordId: '', secretStringPasswordId: '', defaultSecretPasswordType: 0, // from 参数(用于校验成功后跳转) redirectAfterCheck: '', redirectTarget: '' }) const lockRef = ref(null) const setFlow = reactive({ first: '', pendingConfirm: false, submitting: false }) // API const API = { passwordSave: (payload) => ajax.post({ url: 'password', method: 'post', data: payload }), validatePassword: (id, password) => ajax.post({ url: 'validatePassword', method: 'post', data: { id, password } }), consumerSecretPassword: ({ consumerId, passwordId, defaultSecretPasswordType }) => ajax.post({ url: 'consumerSecretPassword', method: 'post', data: { consumerId, passwordId, defaultSecretPasswordType } }), consumerSecretPasswordShow: (consumerId) => ajax.post({ url: 'consumerSecretPasswordShow', method: 'post', data: { consumerId } }) } function navigateBack() { uni.navigateBack() } onLoad((option = {}) => { // consumerId:默认取自己 if (option.consumerId) state.consumerId = option.consumerId else if (store?.user?.id) state.consumerId = store.user.id else state.consumerId = uni.getStorageSync('consumerId') || '' // 允许从外部传入模式 / passwordId,用于“只校验”的场景 if (option.mode === 'check') ui.mode = 'check' if (option.passwordId) { state.secretGesturePasswordId = option.passwordId } if (option.redirect) { state.redirectAfterCheck = option.redirect } if (option.target) { state.redirectTarget = option.target } }) onMounted(() => { refreshProfile() }) // 查询当前用户的咖密配置 async function refreshProfile() { try { if (!state.consumerId) return const res = await API.consumerSecretPasswordShow(state.consumerId) console.log(res,'手势设置的回显密码') const data = res?.data || res if (!data) return state.secretGesturePasswordId = data.secretGesturePasswordId || state.secretGesturePasswordId || '' state.secretStringPasswordId = data.secretStringPasswordId || '' state.defaultSecretPasswordType = typeof data.defaultSecretPasswordType === 'number' ? data.defaultSecretPasswordType : Number(data.defaultSecretPasswordType || 0) } catch (e) {} } function switchMode(m) { ui.mode = m ui.tips = m === 'set' ? setFlow.pendingConfirm ? '请再次绘制确认' : '请绘制手势密码' : '请绘制已设置的手势进行验证' lockRef.value?.reset() } async function onPatternComplete(pwd) { if (ui.mode === 'set') await handleSetFlow(pwd) else await handleCheckFlow(pwd) } // 设置流程 async function handleSetFlow(pwd) { if (!setFlow.pendingConfirm) { setFlow.first = pwd setFlow.pendingConfirm = true ui.tips = '请再次绘制确认' lockRef.value?.flashSuccess(300) return } if (pwd !== setFlow.first) { ui.tips = '与第一次不一致,请重试' setFlow.first = '' setFlow.pendingConfirm = false lockRef.value?.flashError() return } if (setFlow.submitting) return setFlow.submitting = true try { const payload = { id: state.secretGesturePasswordId || undefined, password: pwd, type: GESTURE_TYPE } const saveRes = await API.passwordSave(payload) const newId = saveRes?.data?.id || saveRes?.id if (!newId) throw new Error('保存失败') state.secretGesturePasswordId = newId // 设置为默认手势(全局咖密) if (state.consumerId) { let res = await API.consumerSecretPassword({ consumerId: state.consumerId, passwordId: newId, defaultSecretPasswordType: GESTURE_TYPE }) console.log(res,'手势设置的密码') state.defaultSecretPasswordType = GESTURE_TYPE } ui.tips = '设置成功' lockRef.value?.flashSuccess() setFlow.first = '' setFlow.pendingConfirm = false uni.showToast({ title: '设置成功', icon: 'success' }) } catch (e) { ui.tips = '保存失败,请重试' lockRef.value?.flashError() } finally { setFlow.submitting = false } } // 验证流程 async function handleCheckFlow(pwd) { const id = state.secretGesturePasswordId if (!id) { ui.tips = '尚未设置手势密码' lockRef.value?.flashError() return } try { const res = await API.validatePassword(id, pwd) const result = res?.data?.result || res?.result if (result === 'success') { ui.tips = '验证成功' lockRef.value?.flashSuccess() uni.showToast({ title: '验证成功', icon: 'success' }) // 如果从好友页过来,需要验证通过后进入聊天页面 if (state.redirectAfterCheck) { setTimeout(() => { uni.navigateTo({ url: state.redirectAfterCheck + (state.redirectTarget ? state.redirectTarget : '') }) }, 200) } } else { ui.tips = result || '密码错误' lockRef.value?.flashError() } } catch (e) { ui.tips = '验证失败,请稍后再试' lockRef.value?.flashError() } } async function setAsDefault() { if (!state.secretGesturePasswordId || !state.consumerId) return try { await API.consumerSecretPassword({ consumerId: state.consumerId, passwordId: state.secretGesturePasswordId, defaultSecretPasswordType: GESTURE_TYPE }) state.defaultSecretPasswordType = GESTURE_TYPE uni.showToast({ title: '已设为默认(手势)', icon: 'success' }) } catch (e) { uni.showToast({ title: '设置默认失败', icon: 'none' }) } } </script> <style lang="scss" scoped> @import '@/assets/styles/global.scss'; .content { min-height: 100vh; position: relative; background-size: cover; } .overlay { position: absolute; inset: 0; background-size: cover; } .header { width: 94%; margin: 0 auto; } .header-inner { position: relative; display: flex; align-items: center; justify-content: center; height: 100%; } .left-box, .right-box { position: absolute; top: 0; bottom: 0; width: 120rpx; display: flex; align-items: center; } .left-box { left: 0; justify-content: flex-start; } .right-box { right: 0; justify-content: flex-end; } .center-box { flex: none; } .page-body { width: 94%; margin: 0 auto; padding-bottom: 40rpx; } .card { margin-top: 24rpx; padding: 24rpx; background: #fff; border-radius: 16rpx; .row { display: flex; margin-bottom: 12rpx; font-size: 28rpx; } .label { color: #666; width: 260rpx; } .val { color: #111; flex: 1; } } .tabs { margin-top: 24rpx; display: flex; background: #fff; border-radius: 16rpx; overflow: hidden; .tab { flex: 1; text-align: center; padding: 22rpx 0; font-size: 30rpx; color: #666; } .tab.active { color: #007aff; font-weight: 600; background: #f5f8ff; } } .lock-wrap { margin-top: 30rpx; background: #fff; border-radius: 16rpx; padding: 20rpx 0 40rpx; } .btns { margin-top: 24rpx; display: grid; grid-template-columns: 1fr 1fr; gap: 20rpx; .btn { border-radius: 12rpx; } .primary { background: #007aff; color: #fff; } } </style>是有个pas.value.secretGesturePasswordId 这个就是手势的密码 然后验证密码就是 validatePassword 31.validatePassword密码验证
入口参数

String id,
String password 要验证的密码
返回值
result(success时验证通过,其他为错误信息) password就是之前设置的密码 id 就是pas.value.secretGesturePasswordId 然后验证通过就到了这个
if (res?.data?.result === 'success') {
uni.navigateTo({
url: '/pages/sociAlize/Coffee/Coffeeability'
})
} else {
showToast(res.data.result || '验证失败', 'error')
} 这个页面 现在的要求是从if(pas.value.defaultSecretPasswordType===1){

text
uni.navigateTo({ url: '/pages/sociAlize/Coffee/setpassword/gesture?decrypt=' + type }) }else if(pas.value.defaultSecretPasswordType===2){ uni.navigateTo({ url: '/pages/sociAlize/Coffee/setpassword/Textpassword?decrypt=' + type }) }这里来的直接验证密码就好了 通过直接跳转 不用再像之前的一二次验证了 之前的一二次验证是设置密码这是验证密码一个页面区分一下 到时候我给你一个截图就像 图片一样样式就好了 通过直接跳转就行 对了 这个gesture页面 微信小程序可以显示真机就不显示这个九空格了 你修复下什么原因 然后写一个 完整的gesture页面和 GestureLock组件页面实现所有的功能和修复app真机不显示的线条的问题两个页面都返回完整的页面

在真机上园和线条都不出来不知道为什么 加参数可以改这个else if (name === '咖密') {
let type = true
if(pas.value.defaultSecretPasswordType===1){

text
uni.navigateTo({ url: '/pages/sociAlize/Coffee/setpassword/gesture?decrypt=' + type }) }else if(pas.value.defaultSecretPasswordType===2){ uni.navigateTo({ url: '/pages/sociAlize/Coffee/setpassword/Textpassword?decrypt=' + type }) } // uni.navigateTo({ // url: '/pages/sociAlize/Coffee/Coffeeability' // }) } 参数

思考

Thought for 8m 7s

回答

先给你结论,然后直接上两份完整代码gesture.vue + GestureLock.vue),已经:

  1. 把「从 咖密 入口进来只做一次验证,通过就跳转」这条业务跑通。
  2. 支持通过 URL 参数区分 设置/重设仅验证 两种模式。
  3. 修了一版 Canvas 初始化逻辑,在微信小程序 + App 真机上都能正常画出 9 个圆和连线。

一、入口处(“咖密”)点击逻辑示例

你原来是这样:

js
else if (name === '咖密') { let type = true if (pas.value.defaultSecretPasswordType === 1) { uni.navigateTo({ url: '/pages/sociAlize/Coffee/setpassword/gesture?decrypt=' + type }) } else if (pas.value.defaultSecretPasswordType === 2) { uni.navigateTo({ url: '/pages/sociAlize/Coffee/setpassword/Textpassword?decrypt=' + type }) } }

需求:从这里进入时,不再走「设置两次」流程,只验证一次密码,验证成功直接进 /pages/sociAlize/Coffee/Coffeeability

可以改成下面这样(核心是多带几个参数):

js
else if (name === '咖密') { const decrypt = true // 标识“解锁校验”模式 if (pas.value.defaultSecretPasswordType === 1) { // 手势验证 uni.navigateTo({ url: '/pages/sociAlize/Coffee/setpassword/gesture' + '?decrypt=' + decrypt + '&mode=check' // 只验证 + '&passwordId=' + (pas.value.secretGesturePasswordId || '') + '&redirect=' + encodeURIComponent('/pages/sociAlize/Coffee/Coffeeability') }) } else if (pas.value.defaultSecretPasswordType === 2) { // 文字密码验证(这里给你示意一下,Textpassword 页里逻辑同理处理) uni.navigateTo({ url: '/pages/sociAlize/Coffee/setpassword/Textpassword' + '?decrypt=' + decrypt + '&mode=check' + '&passwordId=' + (pas.value.secretStringPasswordId || '') + '&redirect=' + encodeURIComponent('/pages/sociAlize/Coffee/Coffeeability') }) } else { // 没设置过密码的兜底逻辑,看你业务自行处理 uni.navigateTo({ url: '/pages/sociAlize/Coffee/Coffeeability' }) } }

接下来就是你要的两个完整页面文件。


二、全新的 /pages/sociAlize/Coffee/setpassword/gesture.vue

特点:

  • 支持两种模式:
    • 设置/重设模式:默认模式,可双次绘制确认 + 保存。
    • 验证模式:从“咖密”入口过来(携带 decrypt=true&mode=check),只画一次,走 validatePassword,成功后按 redirect 跳转(没传就默认跳到 Coffeeability)。
  • 验证模式下会隐藏「设置/重设」标签和「设为默认」按钮,让页面只剩下你截图那种“九宫格 + 提示文案”的效果。
vue
<!-- /pages/sociAlize/Coffee/setpassword/gesture.vue --> <template> <view :class="['app-container', theme]"> <view class="content"> <view class="overlay"> <!-- 顶部 Header --> <view class="header" :style="{ paddingTop: statusBarHeight + 'px', height: headerBarHeight + 'px' }" > <view class="header-inner"> <view class="left-box" @click="navigateBack"> <uni-icons type="left" color="#000000" size="26" /> </view> <view class="center-box"> <text class="title-text">咖密</text> </view> <view class="right-box"> <text class="desc-text"></text> </view> </view> </view> <!-- 内容 --> <view class="page-body"> <!-- 只有“设置/管理”模式才展示这块信息卡片 --> <view class="card" v-if="!isCheckOnly"> <view class="row"> <text class="label">手势密码ID:</text> <text class="val">{{ state.secretGesturePasswordId || '未设置' }}</text> </view> <view class="row"> <text class="label">默认解锁方式:</text> <text class="val"> {{ state.defaultSecretPasswordType === GESTURE_TYPE ? '手势' : state.defaultSecretPasswordType === 2 ? '文字' : '未设置' }} </text> </view> </view> <!-- 标签:设置/验证 从“咖密”入口(decrypt=true)进入时只做验证,所以隐藏标签 --> <view class="tabs" v-if="!isCheckOnly"> <view class="tab" :class="{ active: ui.mode === 'set' }" @click="switchMode('set')" > 设置/重设 </view> <view class="tab" :class="{ active: ui.mode === 'check' }" @click="switchMode('check')" > 验证 </view> </view> <!-- 手势九宫格 --> <view class="lock-wrap"> <GestureLock ref="lockRef" :tips="ui.tips" :min-points="4" :line-color="ui.line" :error-color="ui.error" :fill-color="ui.fill" :stroke-color="ui.stroke" @complete="onPatternComplete" /> </view> <!-- 设为默认(仅在设置/管理模式下可见) --> <view class="btns" v-if="!isCheckOnly"> <button class="btn primary" @click="setAsDefault" :disabled="!state.secretGesturePasswordId" > 设为默认(手势) </button> </view> </view> </view> </view> </view> </template> <script setup> import { ref, reactive, onMounted } from 'vue' import { onLoad } from '@dcloudio/uni-app' import { storeToRefs } from 'pinia' import { useMainStore } from '@/store/index.js' import { usecontacts } from '@/store/usecontacts.js' import { getStatusBarHeight, getTitleBarHeight } from '@/utils/system.js' import ajax from '@/api/ajax.js' import GestureLock from '@/components/GestureLock/GestureLock.vue' const GESTURE_TYPE = 1 // 主题 const store = useMainStore?.() || {} const { theme = ref('') } = storeToRefs?.(store) || { theme: ref('') } // 联系人 store,用到 pas(里面有 secretGesturePasswordId) const contactsStore = usecontacts?.() const { pas } = contactsStore ? storeToRefs(contactsStore) : { pas: ref({}) } const statusBarHeight = ref(getStatusBarHeight()) const headerBarHeight = ref(getTitleBarHeight()) // 是否“只做验证”的模式(从咖密入口 decrypt=true 过来) const isCheckOnly = ref(false) const ui = reactive({ mode: 'set', // 'set' | 'check' tips: '请绘制手势密码', line: '#007aff', error: '#fa3534', fill: '#ffffff', stroke: '#c0c0c0' }) const state = reactive({ consumerId: '', secretGesturePasswordId: '', secretStringPasswordId: '', defaultSecretPasswordType: 0, // 验证成功后要跳转到的页面 redirectAfterCheck: '', redirectTarget: '' }) const lockRef = ref(null) const setFlow = reactive({ first: '', pendingConfirm: false, submitting: false }) // API 封装 const API = { passwordSave: (payload) => ajax.post({ url: 'password', method: 'post', data: payload }), validatePassword: (id, password) => ajax.post({ url: 'validatePassword', method: 'post', data: { id, password } }), consumerSecretPassword: ({ consumerId, passwordId, defaultSecretPasswordType }) => ajax.post({ url: 'consumerSecretPassword', method: 'post', data: { consumerId, passwordId, defaultSecretPasswordType } }), consumerSecretPasswordShow: (consumerId) => ajax.post({ url: 'consumerSecretPasswordShow', method: 'post', data: { consumerId } }) } function navigateBack() { uni.navigateBack() } // 处理 URL 参数 onLoad((option = {}) => { // consumerId:默认取自己 if (option.consumerId) state.consumerId = option.consumerId else if (store?.user?.id) state.consumerId = store.user.id else state.consumerId = uni.getStorageSync('consumerId') || '' // 是否“只验证”模式 if (option.mode === 'check') { ui.mode = 'check' ui.tips = '请绘制已设置的手势进行验证' } if (option.decrypt === 'true' || option.decrypt === '1' || option.decrypt === true) { isCheckOnly.value = true ui.mode = 'check' ui.tips = '请绘制已设置的手势进行验证' } // 手势密码 id 来源优先级: // 1. 路由参数 passwordId // 2. pas.value.secretGesturePasswordId(联系人 store) if (option.passwordId) { state.secretGesturePasswordId = option.passwordId } else if (!state.secretGesturePasswordId && pas?.value?.secretGesturePasswordId) { state.secretGesturePasswordId = pas.value.secretGesturePasswordId } // 验证通过后的跳转地址 if (option.redirect) { // 可能 encode 过,这里统一 decode try { state.redirectAfterCheck = decodeURIComponent(option.redirect) } catch (e) { state.redirectAfterCheck = option.redirect } } else if (isCheckOnly.value && !state.redirectAfterCheck) { // 从“咖密”入口进来且未显式指定 redirect,则默认跳咖密能力页 state.redirectAfterCheck = '/pages/sociAlize/Coffee/Coffeeability' } if (option.target) { state.redirectTarget = option.target } }) onMounted(() => { refreshProfile() }) // 查询当前用户咖密配置(拿到默认方式和服务端的 passwordId) async function refreshProfile() { try { if (!state.consumerId) return const res = await API.consumerSecretPasswordShow(state.consumerId) console.log(res, '手势设置的回显密码') const data = res?.data || res if (!data) return state.secretGesturePasswordId = data.secretGesturePasswordId || state.secretGesturePasswordId || '' state.secretStringPasswordId = data.secretStringPasswordId || '' state.defaultSecretPasswordType = typeof data.defaultSecretPasswordType === 'number' ? data.defaultSecretPasswordType : Number(data.defaultSecretPasswordType || 0) } catch (e) { // 静默失败即可 } } // 切换 tab(设置/验证) function switchMode(m) { // 如果当前是 “只验证” 模式,禁止切换到设置 if (isCheckOnly.value && m === 'set') return ui.mode = m ui.tips = m === 'set' ? setFlow.pendingConfirm ? '请再次绘制确认' : '请绘制手势密码' : '请绘制已设置的手势进行验证' lockRef.value?.reset() } // 手势绘制完成回调 async function onPatternComplete(pwd) { if (ui.mode === 'set') await handleSetFlow(pwd) else await handleCheckFlow(pwd) } /* ----------------------- * 一、设置 / 重设流程 * ---------------------*/ async function handleSetFlow(pwd) { if (!setFlow.pendingConfirm) { // 第一次绘制 setFlow.first = pwd setFlow.pendingConfirm = true ui.tips = '请再次绘制确认' lockRef.value?.flashSuccess(300) return } // 第二次确认 if (pwd !== setFlow.first) { ui.tips = '与第一次不一致,请重试' setFlow.first = '' setFlow.pendingConfirm = false lockRef.value?.flashError() return } if (setFlow.submitting) return setFlow.submitting = true try { const payload = { id: state.secretGesturePasswordId || undefined, password: pwd, type: GESTURE_TYPE } const saveRes = await API.passwordSave(payload) const newId = saveRes?.data?.id || saveRes?.id if (!newId) throw new Error('保存失败') state.secretGesturePasswordId = newId // 设置为默认手势 if (state.consumerId) { const res = await API.consumerSecretPassword({ consumerId: state.consumerId, passwordId: newId, defaultSecretPasswordType: GESTURE_TYPE }) console.log(res, '手势设置的密码') state.defaultSecretPasswordType = GESTURE_TYPE } ui.tips = '设置成功' lockRef.value?.flashSuccess() setFlow.first = '' setFlow.pendingConfirm = false uni.showToast({ title: '设置成功', icon: 'success' }) } catch (e) { ui.tips = '保存失败,请重试' lockRef.value?.flashError() } finally { setFlow.submitting = false } } /* ----------------------- * 二、验证流程 * ---------------------*/ async function handleCheckFlow(pwd) { // id 优先用 state 里的,其次从 pas 中兜底一次 const id = state.secretGesturePasswordId || (pas?.value && pas.value.secretGesturePasswordId) if (!id) { ui.tips = '尚未设置手势密码' lockRef.value?.flashError() return } try { const res = await API.validatePassword(id, pwd) const result = res?.data?.result || res?.result if (result === 'success') { ui.tips = '验证成功' lockRef.value?.flashSuccess() uni.showToast({ title: '验证成功', icon: 'success' }) // 校验通过后的跳转 const targetUrl = state.redirectAfterCheck || '/pages/sociAlize/Coffee/Coffeeability' // 兜底 setTimeout(() => { uni.navigateTo({ url: targetUrl + (state.redirectTarget ? state.redirectTarget : '') }) }, 200) } else { ui.tips = result || '密码错误' lockRef.value?.flashError() } } catch (e) { ui.tips = '验证失败,请稍后再试' lockRef.value?.flashError() } } // 手动设置成默认手势 async function setAsDefault() { if (!state.secretGesturePasswordId || !state.consumerId) return try { await API.consumerSecretPassword({ consumerId: state.consumerId, passwordId: state.secretGesturePasswordId, defaultSecretPasswordType: GESTURE_TYPE }) state.defaultSecretPasswordType = GESTURE_TYPE uni.showToast({ title: '已设为默认(手势)', icon: 'success' }) } catch (e) { uni.showToast({ title: '设置默认失败', icon: 'none' }) } } </script> <style lang="scss" scoped> @import '@/assets/styles/global.scss'; .content { min-height: 100vh; position: relative; background-size: cover; } .overlay { position: absolute; inset: 0; background-size: cover; } .header { width: 94%; margin: 0 auto; } .header-inner { position: relative; display: flex; align-items: center; justify-content: center; height: 100%; } .left-box, .right-box { position: absolute; top: 0; bottom: 0; width: 120rpx; display: flex; align-items: center; } .left-box { left: 0; justify-content: flex-start; } .right-box { right: 0; justify-content: flex-end; } .center-box { flex: none; } .page-body { width: 94%; margin: 0 auto; padding-bottom: 40rpx; } .card { margin-top: 24rpx; padding: 24rpx; background: #fff; border-radius: 16rpx; .row { display: flex; margin-bottom: 12rpx; font-size: 28rpx; } .label { color: #666; width: 260rpx; } .val { color: #111; flex: 1; } } .tabs { margin-top: 24rpx; display: flex; background: #fff; border-radius: 16rpx; overflow: hidden; .tab { flex: 1; text-align: center; padding: 22rpx 0; font-size: 30rpx; color: #666; } .tab.active { color: #007aff; font-weight: 600; background: #f5f8ff; } } .lock-wrap { margin-top: 30rpx; background: #fff; border-radius: 16rpx; padding: 20rpx 0 40rpx; } .btns { margin-top: 24rpx; display: grid; grid-template-columns: 1fr; gap: 20rpx; .btn { border-radius: 12rpx; } .primary { background: #007aff; color: #fff; } } </style>

三、全新的 /components/GestureLock/GestureLock.vue

重点修复点:

  1. onReady 里初始化 canvas(官方建议,避免真机第一次进入拿不到宽高)。citeturn1search0turn1search11
  2. 使用 uni.getSystemInfoSync() 提前给 canvas 一个明确的宽高,避免 boundingClientRect 拿到 0 导致不绘制。citeturn1search7turn1search13
  3. 创建 CanvasContext 时带上组件实例(第二个参数),这是自定义组件在真机上经常会踩的坑。citeturn1search13

API 保持跟你原来的完全一致,包括 props / 事件 / 暴露的方法。

vue
<!-- /components/GestureLock/GestureLock.vue --> <template> <view class="g-box"> <canvas id="gestureCanvas" canvas-id="gestureCanvas" class="g-canvas" :style="{ width: canvasSize + 'px', height: canvasSize + 'px' }" @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd" /> <view class="g-tips">{{ innerTips }}</view> </view> </template> <script setup> import { ref, watch, getCurrentInstance } from 'vue' import { onReady } from '@dcloudio/uni-app' const props = defineProps({ tips: { type: String, default: '请绘制手势密码' }, minPoints: { type: Number, default: 4 }, lineColor: { type: String, default: '#D89833' }, errorColor: { type: String, default: '#fa3534' }, fillColor: { type: String, default: '#ffffff' }, strokeColor: { type: String, default: '#c0c0c0' }, radiusRpx: { type: Number, default: 40 }, marginRpx: { type: Number, default: 80 } }) const emit = defineEmits(['complete']) const innerTips = ref(props.tips) watch( () => props.tips, (v) => { innerTips.value = v } ) // 画布相关 let ctx = null const canvasSize = ref(0) const canvasRect = { width: 0, height: 0, left: 0, top: 0 } let scale = 1 // 点阵 & 状态 let nodes = [] // 9 个点 let activeIdx = [] // 已经连过的点下标 let touching = false let lastPos = { x: 0, y: 0 } onReady(() => { const inst = getCurrentInstance() const proxy = inst?.proxy // 先根据屏幕宽度确定一个正方形画布尺寸(避免 width = 0) const sys = uni.getSystemInfoSync() const baseWidth = sys.windowWidth || 300 const size = Math.min(baseWidth * 0.9, 420) // 稍微留点边距 canvasSize.value = size canvasRect.width = size canvasRect.height = size // 创建 canvas 上下文:第二个参数必须传组件实例(真机很关键) ctx = uni.createCanvasContext('gestureCanvas', proxy) // 再拿一下 canvas 在页面中的位置,用于坐标换算 uni .createSelectorQuery() .in(proxy) .select('#gestureCanvas') .boundingClientRect((rect) => { if (rect) { canvasRect.left = rect.left || 0 canvasRect.top = rect.top || 0 if (rect.width) canvasRect.width = rect.width if (rect.height) canvasRect.height = rect.height } // 以 750 设计稿为基准,计算 rpx -> px 的缩放比 scale = canvasRect.width / 750 buildNodes() draw() }) .exec() }) // 计算 9 个点的位置 function buildNodes() { const r = props.radiusRpx * scale const margin = props.marginRpx * scale const w = canvasRect.width const sideGap = (w - 2 * margin - 6 * r) / 2 nodes = [] for (let i = 0; i < 9; i++) { const row = Math.floor(i / 3) const col = i % 3 nodes.push({ x: margin + r + col * (2 * r + sideGap), y: margin + r + row * (2 * r + sideGap), r }) } } // 统一的绘制函数 function draw(color = props.lineColor) { if (!ctx || !canvasRect.width || !canvasRect.height) return ctx.clearRect(0, 0, canvasRect.width, canvasRect.height) // 画 9 个圆 nodes.forEach((n, i) => { ctx.beginPath() ctx.arc(n.x, n.y, n.r, 0, Math.PI * 2) const hit = activeIdx.includes(i) ctx.setFillStyle(hit ? color : props.fillColor) ctx.setStrokeStyle(hit ? color : props.strokeColor) ctx.setLineWidth(2) ctx.fill() ctx.stroke() }) // 连线 if (activeIdx.length > 0) { ctx.beginPath() ctx.setStrokeStyle(color) ctx.setLineWidth(4) ctx.setLineCap('round') ctx.moveTo(nodes[activeIdx[0]].x, nodes[activeIdx[0]].y) for (let k = 1; k < activeIdx.length; k++) { ctx.lineTo(nodes[activeIdx[k]].x, nodes[activeIdx[k]].y) } if (touching) ctx.lineTo(lastPos.x, lastPos.y) ctx.stroke() } ctx.draw() } /* ----------------------- * 触摸事件 * ---------------------*/ function touchStart(e) { touching = true activeIdx = [] if (e.touches && e.touches[0]) { processPoint(e.touches[0]) } } function touchMove(e) { if (!touching) return if (e.touches && e.touches[0]) { processPoint(e.touches[0]) } } function touchEnd() { if (!touching) return touching = false const pwd = activeIdx.join('') if (activeIdx.length < props.minPoints) { innerTips.value = `至少连接 ${props.minPoints} 个点` flashError(600) setTimeout(reset, 600) return } emit('complete', pwd) } // 把当前触点加入路径 function processPoint(t) { const p = localXY(t) lastPos = p const idx = nodes.findIndex((n) => distance(n, p) <= n.r) if (idx !== -1 && !activeIdx.includes(idx)) { activeIdx.push(idx) try { uni.vibrateShort() } catch (e) {} } draw() } // 把全局坐标转换到 canvas 内部坐标 function localXY(t) { const pageX = typeof t.x === 'number' ? t.x : typeof t.clientX === 'number' ? t.clientX : t.pageX || 0 const pageY = typeof t.y === 'number' ? t.y : typeof t.clientY === 'number' ? t.clientY : t.pageY || 0 return { x: pageX - (canvasRect.left || 0), y: pageY - (canvasRect.top || 0) } } function distance(a, b) { return Math.hypot(a.x - b.x, a.y - b.y) } /* ----------------------- * 暴露给父组件的方法 * ---------------------*/ function reset() { activeIdx = [] draw() } function flashError(delay = 800) { draw(props.errorColor) setTimeout(() => draw(), delay) } function flashSuccess(delay = 300) { draw(props.lineColor) setTimeout(() => draw(), delay) } defineExpose({ reset, flashError, flashSuccess }) </script> <style scoped> .g-box { display: flex; flex-direction: column; align-items: center; padding-top: 40rpx; } /* 宽高主要靠行内 style 里的 canvasSize 控制 */ .g-canvas { background-color: #ffffff; } .g-tips { margin-top: 30rpx; font-size: 30rpx; color: #333; } </style>

四、怎么验证是否正常

  1. 微信开发者工具
    • 运行小程序,看 9 个圆是否正常显示、连线是否实时绘制。
  2. 真机调试(重点)
    • 用微信“真机调试”打开同一个页面,看圆和线是不是正常。
  3. 流程测试
    • 从“咖密”入口点击:
      • 如果 defaultSecretPasswordType = 1 且已经设置 pas.value.secretGesturePasswordId,应直接进入 验证页面,只要画对一次,就跳转到 Coffeeability
      • 如果画错,底部提示「密码错误」或接口返回的错误信息。

如果你后面把 文字密码 Textpassword 页 的代码也贴出来,我可以按同样的模式给你改成「设置页 / 验证页分离 + 真机兼容」的版本。

分享這個問答