<template> <view :class="['app-container', theme]...

作成日: 2025年12月10日

使用モデル GPT-5.1 Thinking by Chat01

質問

<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>
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>
</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密码维护
入口参数

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

思考

Diagnosing the formRef.validate issue

The user’s code seems stuck after console.log('0') since the callback for formRef.value?.validate never runs. One possibility: the validation might not be triggered due to a mismatch in form rules or props. I should double-check the API signature for uView and confirm that everything matches up, especially for Vue 3's script setup method. I'll explore more about the rules.

Investigating validation callback issue in Vue 3

It seems the callback should fire even if validation fails, unless validate is missing. The issue might be due to confusion about how formRef is bound in Vue 3’s script setup, as ref="formRef" in <script setup> should bind the template to the variable correctly. I’ll check that part next to ensure proper wiring.

Exploring possible validation issues in uView

The issue could be that formRef.value.validate returns a Promise instead of using a callback. The callback might be mismatched with the expected signature. Another factor could be how the rules are defined; if they’re computed, they may need .value when passed to the template. I also wonder if the callback method has changed between uView versions. I'll check that.

Diagnosing formRef and template binding issue

The problem likely comes from improper binding of formRef. If formRef.value is undefined, formRef.value?.validate won’t execute, causing only console.log('0') to print. In Vue 3’s <script setup>, ref="formRef" should work correctly, but I’ll need to fix this and restructure the code per best practices. Let’s ensure handleSubmit works and incorporate the other APIs.

Improving password unlock page with proper API integration

The user's page is focused on alphanumeric password unlocking and uses the password API. I’ll refactor the component for <script setup>, fixing bugs and API calls, including SMS code validation and password updates. I'll ensure u-form is used correctly, check dependencies like SMS input, and rewrite the entire form functionality. I'll also use the ajax.post for SMS validation where required. Finally, I'll correct any mismatched rules or bindings. I’ll aim for complete working functionality after the rewrite.

Debugging form reference and SMS input in <script setup>

It seems like the call to formRef.value.validate() might not fire properly because the form reference isn't hooked correctly in <script setup>. Usually, you need to use const formRef = ref() and ref="formRef" in the template. It could also be a timing issue with the onMounted lifecycle. Another potential issue could be the async callback used, as some libraries expect synchronous validation callbacks.

Fixing Validate with Promise and improving SMS code logic

It seems like the Promise-based validate() method in uView3 is causing the issue because the callback isn't being properly called. We should use await formRef.value.validate() instead, to handle the returned Promise. Also, if Systemcode.value is null (before SMS is received), we should prompt the user to first obtain a verification code.

Improving form validation and bug fixes

We should adjust a few things: for the SMS code input, set type="number" with maxlength="6" for better validation. In handleGetCode, the toast should happen after verifying. The phone field should also be cleared in resetForm. Additionally, ensure the countdown button updates correctly, and ensure the set password page works as expected, integrating validation and deletion API if necessary.

Fixing password form handling and addressing bugs

The password page allows setting and resetting passwords, using type 2 ("text"). For form submission, we should use form.id to differentiate update vs new processing. Ensuring the correct API endpoint ('passwordMaintenance'), we'll need to fix issues with validation and checking formRef references. Additionally, review other potential runtime issues with the current setup in uni-app + uView.

Improving password validation and final adjustments

I plan to simplify the rules into a static const object since nothing about it is dynamic. The passwordReg regex correctly ensures the password has letters, digits, and symbols, and is 8-20 characters long. I'll update the form with a clearer handleSubmit, add extra checks, and give a full .vue code explanation. This will address the remaining bugs while keeping things concise.

Final adjustment and reworking handleSubmit

I’ll finalize handleSubmit with some improvements, such as turning it into an async function using await formRef.value.validate(). I'll also adjust the u-form to handle smsCode properly and ensure the u-input for better validation integration. Small UI adjustments include disabling buttons, correcting CSS, and improving validation flow. I'll provide a full .vue snippet for the user with these updates while addressing the known issues.

Tweaking handleSubmit for async flow and reset password functionality

The handleSubmit function is adjusted to handle async operations properly by marking the function as async. I'm also adding validation with await, handling potential SMS code errors, and ensuring proper form submission. Additionally, I'll implement the "取消加密" functionality and a deletePassword API call to reset the form when requested, while keeping the reset flow flexible. This follows UX norms to disable the password effectively.

Thought for 1m 25s

回答

先把问题说清楚:
你现在的 handleSubmit 写法是 uView2 的老用法(validate(callback)),但在 uView Plus / uView3 里面,u-formvalidate() 已经是 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>

关键修复点总结

  1. validate 改成 Promise 写法
js
const valid = await formRef.value.validate()

而不是:

js
formRef.value?.validate(async valid => { ... })

这就是你看到只打印 '0' 的根本原因。

  1. 补上对 验证码未获取 / 不一致 的校验提示。

  2. resetForm 把本地状态都清理干净,并预留了调用 deletePassword 接口的位置(你按实际需求决定要不要调)。

  3. smsCode 改回 u-input,跟 u-form 的校验体系更统一。

你可以先直接拷贝这个页面跑一下,如果还有接口字段名、返回结构和你后端不一致的地方,把那几行改成你自己的即可。
如果后面还想加上「验证旧密码」(31.validatePassword)或「根据 consumerSecretPasswordShow 自动带出默认密码类型」,我也可以帮你把那一块一起串起来。

このQ&Aを共有