<template> <view :class="['app-container', theme]...
創建於:2025年12月10日
使用 GPT-5.1 Thinking 回答 Chat01
創建於:2025年12月10日
使用 GPT-5.1 Thinking 回答 Chat01
</template> <script setup> import { ref, reactive, computed, onMounted, onBeforeUnmount } from 'vue' import { onLoad } from '@dcloudio/uni-app' import { useMainStore } from '@/store/index.js' import { storeToRefs } from 'pinia' import { getStatusBarHeight, getTitleBarHeight } from '@/utils/system.js' import ajax from '@/api/ajax.js' const store = useMainStore() const { theme } = storeToRefs(store) const statusBarHeight = ref(getStatusBarHeight()) const headerBarHeight = ref(getTitleBarHeight()) const navigateBack = () => { uni.navigateBack() } const formRef = ref(null) const toastRef = ref(null) let Systemcode = ref(null) const form = reactive({ id: '', password: '', confirmPassword: '', phone: '', smsCode: '', type: 2 }) const showPassword = reactive({ password: false, confirm: false }) const countdown = ref(59) const isCounting = ref(false) let timer = null const submitting = ref(false) const passwordReg = /^(?=.*[A-Za-z])(?=.*\d)(?=.*[~!@#$%^&*()_+\-={}[\]|:";'<>?,./]).{8,20}$/ const phoneReg = /^1[3-9]\d{9}$/ const rules = computed(() => ({ password: [{ required: true, message: '请输入密码', trigger: ['blur', 'change'] }, { validator: (rule, value) => passwordReg.test(value), message: '密码需 8-20 位,包含字母、数字及符号', trigger: ['blur', 'change'] } ], confirmPassword: [{ required: true, message: '请再次输入密码', trigger: ['blur', 'change'] }, { validator: (rule, value) => value === form.password, message: '两次输入的密码不一致', trigger: ['blur', 'change'] } ], phone: [{ required: true, message: '请输入手机号', trigger: ['blur', 'change'] }, { validator: (rule, value) => phoneReg.test(value), message: '请输入正确的手机号', trigger: ['blur', 'change'] } ], smsCode: [{ required: true, message: '请输入短信验证码', trigger: ['blur', 'change'] }, { validator: (rule, value) => /^\d{4,6}$/.test(value), message: '验证码格式不正确', trigger: ['blur', 'change'] } ] })) const togglePassword = type => { if (type === 'password') { showPassword.password = !showPassword.password } else if (type === 'confirm') { showPassword.confirm = !showPassword.confirm } } const startCountdown = () => { isCounting.value = true countdown.value = 59 timer && clearInterval(timer) timer = setInterval(() => { if (countdown.value <= 1) { clearInterval(timer) timer = null isCounting.value = false countdown.value = 59 return } countdown.value -= 1 }, 1000) } const showToast = (message, type = 'default') => { toastRef.value?.show({ message, type, position: 'bottom' }) } const handleGetCode = async () => { if (isCounting.value) return if (!phoneReg.test(form.phone)) { showToast('请先填写正确的手机号', 'warning') return } try { showToast('验证码发送中...', 'default') let res = await ajax.post({ url: 'getRandom.do', data: { mobilePhone: form.phone, } }) Systemcode.value = res.data.random console.log(Systemcode.value, '收到的验证码') showToast('验证码已发送', 'success') startCountdown() } catch (err) { console.error('sendSmsCode error:', err) showToast(err?.msg || '验证码发送失败', 'error') } } const handleResend = () => { if (!isCounting.value) handleGetCode() } const resetForm = () => { form.password = '' form.confirmPassword = '' form.smsCode = '' startCountdownReset() } const startCountdownReset = () => { if (timer) { clearInterval(timer) timer = null } isCounting.value = false countdown.value = 59 } const handleSubmit = () => { console.log('0') formRef.value?.validate(async valid => { if (!valid) return console.log('1') if (Systemcode.value !== form.smsCode) { return uni.showToast({ title: '验证码错误', icon: 'none' }) } console.log('2') submitting.value = true try { const res = await ajax.post({ url: 'password', data: { id: form.id, password: form.password, type: form.type, // phone: form.phone, // smsCode: form.smsCode } }) console.log(res, '设置密码结果') if (res?.data?.result === 'success') { showToast('密码设置成功', 'success') setTimeout(() => { navigateBack() }, 600) } else { showToast(res?.data?.msg || '密码设置失败', 'error') } } catch (error) { console.error('passwordMaintenance error:', error) showToast('网络异常,请稍后重试', 'error') } finally { submitting.value = false } }) } onLoad(options => { if (options) { form.id = options.id || '' if (options.password) form.password = options.password if (options.phone) form.phone = options.phone form.type = Number(options.type || 2) } }) onMounted(() => { statusBarHeight.value = getStatusBarHeight() headerBarHeight.value = getTitleBarHeight() }) onBeforeUnmount(() => { timer && clearInterval(timer) }) </script> <style lang="scss" scoped> @import '@/assets/styles/global.scss'; .app-container { background: #f2f2f2; min-height: 100vh; } .content { min-height: 100vh; position: relative; } .overlay { position: absolute; inset: 0; display: flex; flex-direction: column; } .header { width: 100%; } .header-inner { position: relative; display: flex; align-items: center; justify-content: center; height: 100%; padding: 0 32rpx; } .left-box { position: absolute; left: 32rpx; display: flex; align-items: center; height: 100%; } .center-box { display: flex; align-items: center; justify-content: center; } .title-text { font-size: 30rpx; font-weight: 600; color: #2c2c2c; } .scroll-container { flex: 1; } .form-wrapper { width: 94%; margin: 40rpx auto 80rpx; padding: 48rpx 36rpx 56rpx; background-color: #f5f5f5; border-radius: 24rpx; box-shadow: 0 12rpx 32rpx rgba(0, 0, 0, 0.04); } .tip-text { font-size: 24rpx; color: #828282; margin-bottom: 30rpx; } .sms-row { display: flex; align-items: center; gap: 20rpx; } .sms-input { flex: 1; width: 100rpx; } .sms-button { width: 160rpx; height: 84rpx; font-size: 26rpx; border-radius: 16rpx; } .btn-row { display: flex; justify-content: space-between; margin-top: 60rpx; gap: 24rpx; } .btn-cancel, .btn-confirm { flex: 1; height: 88rpx; font-size: 28rpx; border-radius: 16rpx; } .resend-row { margin-top: 36rpx; display: flex; justify-content: flex-end; } </style>下面是一些密码的api 参考29.password密码维护text<view class="center-box"> <text class="title-text">数字 + 字母密码解锁</text> </view> </view> </view> <!-- 可滚动内容区域 --> <scroll-view scroll-y class="scroll-container"> <view class="form-wrapper"> <view class="tip-text">密码(数字 + 字母 + 符号)</view> <u-form ref="formRef" :model="form" :rules="rules" labelPosition="top" labelWidth="210"> <u-form-item prop="password" label="密码"> <u-input v-model="form.password" :type="showPassword.password ? 'text' : 'password'" placeholder="请输入密码" maxlength="20" border="surround" clearable> <template #suffix> <u-icon :name="showPassword.password ? 'eye' : 'eye-off'" size="26" color="#7D7D7D" @click="togglePassword('password')" /> </template> </u-input> </u-form-item> <u-form-item prop="confirmPassword" label="再次输入密码"> <u-input v-model="form.confirmPassword" :type="showPassword.confirm ? 'text' : 'password'" placeholder="请再次输入密码" maxlength="20" border="surround" clearable> <template #suffix> <u-icon :name="showPassword.confirm ? 'eye' : 'eye-off'" size="26" color="#7D7D7D" @click="togglePassword('confirm')" /> </template> </u-input> </u-form-item> <u-form-item prop="phone" label="手机号"> <u-input v-model="form.phone" type="number" placeholder="请输入手机号" maxlength="11" border="surround" clearable /> </u-form-item> <u-form-item prop="smsCode" label="获取手机号短信验证码"> <view class="sms-row"> <!-- <u-input border="surround" class="sms-input" /> --> <input v-model="form.smsCode" style="width: 260rpx;height: 72rpx;border: 1rpx solid #cccccc;" placeholder="请输入验证码" maxlength="6" /> <u-button class="sms-button" :type="isCounting ? 'default' : 'primary'" :disabled="isCounting" @click="handleGetCode" shape="square" customStyle="background-color:#C7801F;border:none;color:#fff;"> {{ isCounting ? countdown : '获取验证码' }} </u-button> </view> </u-form-item> </u-form> <view class="btn-row"> <u-button class="btn-cancel" type="info" plain shape="square" customStyle="color:#5A5A5A;border-color:#D1D1D1;" @click="resetForm"> 取消加密 </u-button> <u-button class="btn-confirm" type="primary" shape="square" customStyle="background-color:#C7801F;border:none;" @click="handleSubmit" :loading="submitting"> 确定加密 </u-button> </view> <view class="resend-row"> <u-button type="info" plain shape="square" customStyle="width:180rpx;color:#C7801F;border-color:#C7801F;" @click="handleResend" :disabled="isCounting"> 再次发送 </u-button> </view> </view> </scroll-view> <u-toast ref="toastRef" /> </view> </view> </view>
入口参数
String id,
String password, 密码
Integer type 类型(1手势2文字)
返回值
id
30.deletePassword删除密码
入口参数
String id
31.validatePassword密码验证
入口参数
String id,
String password 要验证的密码
返回值
result(success时验证通过,其他为错误信息)
33.consumerSecretPasswordShow用户咖密显示
入口参数
String consumerId, 用户id
返回值
secretGesturePasswordId手势密码id
secretStringPasswordId 字符串密码id
defaultSecretPasswordType默认密码类型(1手势2文字)
修复一下 这个方法const handleSubmit = () => {
console.log('0')
formRef.value?.validate(async valid => {
if (!valid) return
console.log('1')
if (Systemcode.value !== form.smsCode) {
return uni.showToast({
title: '验证码错误',
icon: 'none'
})
text} console.log('2') submitting.value = true try { const res = await ajax.post({ url: 'password', data: { id: form.id, password: form.password, type: form.type, // phone: form.phone, // smsCode: form.smsCode } }) console.log(res, '设置密码结果') if (res?.data?.result === 'success') { showToast('密码设置成功', 'success') setTimeout(() => { navigateBack() }, 600) } else { showToast(res?.data?.msg || '密码设置失败', 'error') } } catch (error) { console.error('passwordMaintenance error:', error) showToast('网络异常,请稍后重试', 'error') } finally { submitting.value = false } }) }到 console.log('0') 就不会往下走了 写一个完整的页面功能 修复所有的bug
先把问题说清楚:
你现在的 handleSubmit 写法是 uView2 的老用法(validate(callback)),但在 uView Plus / uView3 里面,u-form 的 validate() 已经是 Promise 写法 了,你传的回调函数不会被执行,所以只看到 console.log('0'),后面完全不走。
下面我给你一个修好后的完整页面,把表单校验、短信验证码、密码设置、取消加密等逻辑都整理了一遍,你可以直接整体替换当前页面再按需要微调。
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"> <u-icon name="arrow-left" size="26" color="#2C2C2C" /> </view> <view class="center-box"> <text class="title-text">数字 + 字母密码解锁</text> </view> </view> </view> <!-- 可滚动内容区域 --> <scroll-view scroll-y class="scroll-container"> <view class="form-wrapper"> <view class="tip-text">密码(数字 + 字母 + 符号)</view> <u-form ref="formRef" :model="form" :rules="rules" labelPosition="top" labelWidth="210" > <u-form-item prop="password" label="密码"> <u-input v-model="form.password" :type="showPassword.password ? 'text' : 'password'" placeholder="请输入密码" maxlength="20" border="surround" clearable > <template #suffix> <u-icon :name="showPassword.password ? 'eye' : 'eye-off'" size="26" color="#7D7D7D" @click="togglePassword('password')" /> </template> </u-input> </u-form-item> <u-form-item prop="confirmPassword" label="再次输入密码"> <u-input v-model="form.confirmPassword" :type="showPassword.confirm ? 'text' : 'password'" placeholder="请再次输入密码" maxlength="20" border="surround" clearable > <template #suffix> <u-icon :name="showPassword.confirm ? 'eye' : 'eye-off'" size="26" color="#7D7D7D" @click="togglePassword('confirm')" /> </template> </u-input> </u-form-item> <u-form-item prop="phone" label="手机号"> <u-input v-model="form.phone" type="number" placeholder="请输入手机号" maxlength="11" border="surround" clearable /> </u-form-item> <u-form-item prop="smsCode" label="获取手机号短信验证码"> <view class="sms-row"> <u-input v-model="form.smsCode" class="sms-input" type="number" placeholder="请输入验证码" maxlength="6" border="surround" clearable /> <u-button class="sms-button" :type="isCounting ? 'default' : 'primary'" :disabled="isCounting" @click="handleGetCode" shape="square" customStyle="background-color:#C7801F;border:none;color:#fff;" > {{ isCounting ? countdown + 's' : '获取验证码' }} </u-button> </view> </u-form-item> </u-form> <view class="btn-row"> <u-button class="btn-cancel" type="info" plain shape="square" customStyle="color:#5A5A5A;border-color:#D1D1D1;" @click="resetForm" :disabled="submitting" > 取消加密 </u-button> <u-button class="btn-confirm" type="primary" shape="square" customStyle="background-color:#C7801F;border:none;" @click="handleSubmit" :loading="submitting" > 确定加密 </u-button> </view> <view class="resend-row"> <u-button type="info" plain shape="square" customStyle="width:180rpx;color:#C7801F;border-color:#C7801F;" @click="handleResend" :disabled="isCounting" > 再次发送 </u-button> </view> </view> </scroll-view> <u-toast ref="toastRef" /> </view> </view> </view> </template> <script setup> import { ref, reactive, computed, onMounted, onBeforeUnmount } from 'vue' import { onLoad } from '@dcloudio/uni-app' import { useMainStore } from '@/store/index.js' import { storeToRefs } from 'pinia' import { getStatusBarHeight, getTitleBarHeight } from '@/utils/system.js' import ajax from '@/api/ajax.js' // 主题 const store = useMainStore() const { theme } = storeToRefs(store) // 顶部高度 const statusBarHeight = ref(getStatusBarHeight()) const headerBarHeight = ref(getTitleBarHeight()) const navigateBack = () => { uni.navigateBack() } // 表单 & 组件 ref const formRef = ref(null) const toastRef = ref(null) // 短信验证码(系统返回的验证码) const Systemcode = ref(null) // 表单数据 const form = reactive({ id: '', password: '', confirmPassword: '', phone: '', smsCode: '', type: 2 // 1 手势 2 文字,本页是文字密码 }) // 显示密码开关 const showPassword = reactive({ password: false, confirm: false }) // 倒计时 const countdown = ref(59) const isCounting = ref(false) let timer = null // 提交 loading const submitting = ref(false) // 校验正则 const passwordReg = /^(?=.*[A-Za-z])(?=.*\d)(?=.*[~!@#$%^&*()_+\-={}[\]|:";'<>?,./]).{8,20}$/ const phoneReg = /^1[3-9]\d{9}$/ // 表单校验规则 const rules = computed(() => ({ password: [ { required: true, message: '请输入密码', trigger: ['blur', 'change'] }, { validator: (rule, value) => passwordReg.test(value), message: '密码需 8-20 位,包含字母、数字及符号', trigger: ['blur', 'change'] } ], confirmPassword: [ { required: true, message: '请再次输入密码', trigger: ['blur', 'change'] }, { validator: (rule, value) => value === form.password, message: '两次输入的密码不一致', trigger: ['blur', 'change'] } ], phone: [ { required: true, message: '请输入手机号', trigger: ['blur', 'change'] }, { validator: (rule, value) => phoneReg.test(value), message: '请输入正确的手机号', trigger: ['blur', 'change'] } ], smsCode: [ { required: true, message: '请输入短信验证码', trigger: ['blur', 'change'] }, { validator: (rule, value) => /^\d{4,6}$/.test(value), message: '验证码格式不正确', trigger: ['blur', 'change'] } ] })) // 切换密码显示 const togglePassword = (type) => { if (type === 'password') { showPassword.password = !showPassword.password } else if (type === 'confirm') { showPassword.confirm = !showPassword.confirm } } // 开始短信倒计时 const startCountdown = () => { isCounting.value = true countdown.value = 59 if (timer) { clearInterval(timer) timer = null } timer = setInterval(() => { if (countdown.value <= 1) { clearInterval(timer) timer = null isCounting.value = false countdown.value = 59 return } countdown.value -= 1 }, 1000) } // 停止倒计时并复位 const startCountdownReset = () => { if (timer) { clearInterval(timer) timer = null } isCounting.value = false countdown.value = 59 } // Toast 封装 const showToast = (message, type = 'default') => { toastRef.value?.show({ message, type, position: 'bottom' }) } // 获取短信验证码 const handleGetCode = async () => { if (isCounting.value) return if (!phoneReg.test(form.phone)) { showToast('请先填写正确的手机号', 'warning') return } try { showToast('验证码发送中...', 'default') const res = await ajax.post({ url: 'getRandom.do', data: { mobilePhone: form.phone } }) // 按你的接口约定取 random Systemcode.value = res?.data?.random || null console.log(Systemcode.value, '收到的验证码') showToast('验证码已发送', 'success') startCountdown() } catch (err) { console.error('sendSmsCode error:', err) showToast(err?.msg || '验证码发送失败', 'error') } } // 再次发送 const handleResend = () => { if (!isCounting.value) handleGetCode() } // 取消加密(如果已有 id,这里只做前端清空;如需要可在此调用 deletePassword 接口) const resetForm = async () => { // 如果需要同时删除服务端密码,可以取消注释以下代码: /* if (form.id) { try { const res = await ajax.post({ url: 'deletePassword', data: { id: form.id } }) if (res?.data?.result === 'success') { showToast('密码已取消', 'success') } else { showToast(res?.data?.msg || '取消密码失败', 'error') } } catch (e) { console.error('deletePassword error:', e) showToast('网络异常,取消失败', 'error') } } */ form.password = '' form.confirmPassword = '' form.smsCode = '' Systemcode.value = null startCountdownReset() } // === 核心修复点:提交方法,使用 Promise 写法的 validate === const handleSubmit = async () => { console.log('0') if (!formRef.value) { console.warn('formRef 未就绪') return } // 1. 先做表单校验(uView3 / uView Plus 是 Promise 写法) try { const valid = await formRef.value.validate() // 有些版本会直接 resolve,如果返回 false 就中断 if (valid === false) { console.log('表单校验未通过') return } } catch (errors) { // 有些版本校验失败会 reject,errors 里是错误信息 console.log('表单校验失败:', errors) return } console.log('1') // 2. 校验短信验证码 if (!Systemcode.value) { return uni.showToast({ title: '请先获取验证码', icon: 'none' }) } if (String(Systemcode.value).trim() !== String(form.smsCode).trim()) { return uni.showToast({ title: '验证码错误', icon: 'none' }) } console.log('2') if (submitting.value) return submitting.value = true try { // 29.password 密码维护接口 const res = await ajax.post({ url: 'password', data: { id: form.id, // 有 id 为修改,无 id 为新建,后端按你的约定处理 password: form.password, type: form.type // 1 手势 2 文字 // 如接口需要可加 phone、smsCode // phone: form.phone, // smsCode: form.smsCode } }) console.log(res, '设置密码结果') if (res?.data?.result === 'success') { // 如果接口会返回新的 id,则更新本地 id if (res?.data?.id) { form.id = res.data.id } showToast('密码设置成功', 'success') setTimeout(() => { navigateBack() }, 600) } else { showToast(res?.data?.msg || '密码设置失败', 'error') } } catch (error) { console.error('passwordMaintenance error:', error) showToast('网络异常,请稍后重试', 'error') } finally { submitting.value = false } } // 页面参数初始化 onLoad((options) => { if (options) { form.id = options.id || '' if (options.password) form.password = options.password if (options.phone) form.phone = options.phone form.type = Number(options.type || 2) } }) onMounted(() => { statusBarHeight.value = getStatusBarHeight() headerBarHeight.value = getTitleBarHeight() }) onBeforeUnmount(() => { if (timer) { clearInterval(timer) timer = null } }) </script> <style lang="scss" scoped> @import '@/assets/styles/global.scss'; .app-container { background: #f2f2f2; min-height: 100vh; } .content { min-height: 100vh; position: relative; } .overlay { position: absolute; inset: 0; display: flex; flex-direction: column; } .header { width: 100%; } .header-inner { position: relative; display: flex; align-items: center; justify-content: center; height: 100%; padding: 0 32rpx; } .left-box { position: absolute; left: 32rpx; display: flex; align-items: center; height: 100%; } .center-box { display: flex; align-items: center; justify-content: center; } .title-text { font-size: 30rpx; font-weight: 600; color: #2c2c2c; } .scroll-container { flex: 1; } .form-wrapper { width: 94%; margin: 40rpx auto 80rpx; padding: 48rpx 36rpx 56rpx; background-color: #f5f5f5; border-radius: 24rpx; box-shadow: 0 12rpx 32rpx rgba(0, 0, 0, 0.04); } .tip-text { font-size: 24rpx; color: #828282; margin-bottom: 30rpx; } .sms-row { display: flex; align-items: center; gap: 20rpx; } .sms-input { flex: 1; } .sms-button { width: 160rpx; height: 84rpx; font-size: 26rpx; border-radius: 16rpx; } .btn-row { display: flex; justify-content: space-between; margin-top: 60rpx; gap: 24rpx; } .btn-cancel, .btn-confirm { flex: 1; height: 88rpx; font-size: 28rpx; border-radius: 16rpx; } .resend-row { margin-top: 36rpx; display: flex; justify-content: flex-end; } </style>
validate 改成 Promise 写法jsconst valid = await formRef.value.validate()
而不是:
jsformRef.value?.validate(async valid => { ... })
这就是你看到只打印 '0' 的根本原因。
补上对 验证码未获取 / 不一致 的校验提示。
resetForm 把本地状态都清理干净,并预留了调用 deletePassword 接口的位置(你按实际需求决定要不要调)。
smsCode 改回 u-input,跟 u-form 的校验体系更统一。
你可以先直接拷贝这个页面跑一下,如果还有接口字段名、返回结构和你后端不一致的地方,把那几行改成你自己的即可。
如果后面还想加上「验证旧密码」(31.validatePassword)或「根据 consumerSecretPasswordShow 自动带出默认密码类型」,我也可以帮你把那一块一起串起来。