完成50%
This commit is contained in:
@@ -2,52 +2,94 @@
|
||||
<template>
|
||||
<view class="phone-page" :class="{ 'phone-state--ready': phoneState === 'ready', 'phone-state--saving': phoneState === 'saving' }">
|
||||
<ModulePageBackground module="profile" />
|
||||
<view class="page-layer"><PageHeader title="修改手机号" /></view>
|
||||
<view class="page-layer"><PageHeader title="修改手机号" custom-back @back="requestBack" /></view>
|
||||
<view class="page-content page-layer">
|
||||
<view class="current-phone"><text>当前绑定手机号</text><text>{{ phoneForm.currentPhone }}</text><text>更换后,新手机号将用于登录与安全验证。</text></view>
|
||||
<view class="form-panel">
|
||||
<view class="form-row"><text>新手机号</text><input v-model.trim="phoneForm.newPhone" type="number" maxlength="11" aria-label="新手机号" placeholder="请输入新手机号" @input="errors.newPhone = ''" /></view>
|
||||
<text v-if="errors.newPhone" class="field-error">{{ errors.newPhone }}</text>
|
||||
<view class="form-row code-row"><text>验证码</text><input v-model.trim="phoneForm.code" type="number" maxlength="6" aria-label="短信验证码" placeholder="6 位验证码" @input="errors.code = ''" /><view class="code-action" role="button" :aria-label="codeCountdown ? `${codeCountdown}秒后可重新发送` : '发送验证码'" @click="sendCode">{{ codeCountdown ? `${codeCountdown}s` : "发送验证码" }}</view></view>
|
||||
<view class="form-row code-row"><text>验证码</text><input v-model.trim="phoneForm.code" type="number" maxlength="4" aria-label="短信验证码" placeholder="4 位验证码" @input="errors.code = ''" /><view class="code-action" role="button" aria-label="检查验证码发送条件" @click="sendCode">发送条件</view></view>
|
||||
<text v-if="errors.code" class="field-error">{{ errors.code }}</text>
|
||||
</view>
|
||||
<AppButton block :disabled="phoneState === 'saving'" :label="phoneState === 'saving' ? '正在更换' : '确认更换'" @click="savePhone" />
|
||||
<AppButton block :disabled="phoneState === 'saving'" :label="phoneState === 'saving' ? '正在校验' : '校验换绑信息(不提交)'" @click="savePhone" />
|
||||
</view>
|
||||
<AppToast :visible="toastVisible" :message="toastMessage" />
|
||||
<AppDialog
|
||||
:visible="discardVisible"
|
||||
:close-on-mask="false"
|
||||
eyebrow="放弃确认"
|
||||
title="放弃换绑填写?"
|
||||
message="新手机号和验证码尚未提交服务器。"
|
||||
confirm-text="确认放弃"
|
||||
cancel-text="继续填写"
|
||||
show-cancel
|
||||
@confirm="confirmDiscard"
|
||||
@cancel="cancelDiscard"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onUnmounted, reactive, ref } from "vue";
|
||||
import { computed, reactive, ref } from "vue";
|
||||
import { onBackPress, onUnload } from "@dcloudio/uni-app";
|
||||
import AppButton from "@/components/AppButton.vue";
|
||||
import AppDialog from "@/components/AppDialog.vue";
|
||||
import AppToast from "@/components/AppToast.vue";
|
||||
import ModulePageBackground from "@/components/ModulePageBackground.vue";
|
||||
import PageHeader from "@/components/PageHeader.vue";
|
||||
import { currentUser } from "@/data/mock.js";
|
||||
import { createDiscardConfirmation } from "@/utils/discard-confirmation.js";
|
||||
import { handleBackPress, runBackGuard } from "@/utils/navigation.js";
|
||||
|
||||
const phoneForm = reactive({ currentPhone: "139****6421", newPhone: "", code: "" });
|
||||
const phoneForm = reactive({ currentPhone: currentUser.phone, newPhone: "", code: "" });
|
||||
const errors = reactive({ newPhone: "", code: "" });
|
||||
const phoneState = ref("ready");
|
||||
const codeCountdown = ref(0);
|
||||
const discardVisible = ref(false);
|
||||
const toastVisible = ref(false); const toastMessage = ref("");
|
||||
let countdownTimer = null; let stateTimer = null; let toastTimer = null;
|
||||
let stateTimer = null; let toastTimer = null;
|
||||
const formSnapshot = computed(() => JSON.stringify({ newPhone: phoneForm.newPhone, code: phoneForm.code }));
|
||||
const baseline = ref(formSnapshot.value);
|
||||
const isDirty = computed(() => formSnapshot.value !== baseline.value);
|
||||
const discardConfirmation = createDiscardConfirmation((visible) => {
|
||||
discardVisible.value = visible;
|
||||
});
|
||||
const requestDiscardConfirmation = discardConfirmation.request;
|
||||
const confirmDiscard = discardConfirmation.confirm;
|
||||
const cancelDiscard = discardConfirmation.cancel;
|
||||
const showToast = (message) => { toastMessage.value = message; toastVisible.value = true; clearTimeout(toastTimer); toastTimer = setTimeout(() => (toastVisible.value = false), 1800); };
|
||||
const sendCode = () => {
|
||||
if (codeCountdown.value) return;
|
||||
if (!/^1\d{10}$/.test(phoneForm.newPhone)) { errors.newPhone = "请输入正确的新手机号"; return; }
|
||||
errors.newPhone = ""; codeCountdown.value = 60; showToast("演示验证码已发送,请输入任意 6 位数字");
|
||||
countdownTimer = setInterval(() => { codeCountdown.value -= 1; if (!codeCountdown.value) { clearInterval(countdownTimer); countdownTimer = null; } }, 1000);
|
||||
errors.newPhone = "";
|
||||
showToast("需先完成滑动行为验证;当前未发送验证码");
|
||||
};
|
||||
const validatePhone = () => {
|
||||
errors.newPhone = /^1\d{10}$/.test(phoneForm.newPhone) ? "" : "请输入正确的新手机号";
|
||||
errors.code = /^\d{6}$/.test(phoneForm.code) ? "" : "请输入 6 位验证码";
|
||||
errors.code = /^\d{4}$/.test(phoneForm.code) ? "" : "请输入 4 位验证码";
|
||||
return !errors.newPhone && !errors.code;
|
||||
};
|
||||
const savePhone = () => {
|
||||
if (!validatePhone() || phoneState.value === "saving") return;
|
||||
phoneState.value = "saving";
|
||||
stateTimer = setTimeout(() => { phoneState.value = "ready"; phoneForm.currentPhone = `${phoneForm.newPhone.slice(0,3)}****${phoneForm.newPhone.slice(-4)}`; phoneForm.newPhone = ""; phoneForm.code = ""; showToast("绑定手机号已更新"); }, 500);
|
||||
stateTimer = setTimeout(() => {
|
||||
phoneState.value = "ready";
|
||||
showToast("本地校验通过,尚未提交服务器");
|
||||
}, 500);
|
||||
};
|
||||
onUnmounted(() => { clearInterval(countdownTimer); clearTimeout(stateTimer); clearTimeout(toastTimer); });
|
||||
const requestBack = () =>
|
||||
runBackGuard({
|
||||
transientOpen: discardVisible.value,
|
||||
dirty: isDirty.value,
|
||||
submitting: phoneState.value === "saving",
|
||||
"close-transient": cancelDiscard,
|
||||
"block-submitting": () => true,
|
||||
"confirm-discard": requestDiscardConfirmation,
|
||||
});
|
||||
onBackPress((event) => handleBackPress(event, requestBack));
|
||||
onUnload(() => {
|
||||
clearTimeout(stateTimer);
|
||||
clearTimeout(toastTimer);
|
||||
discardConfirmation.dispose();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
Reference in New Issue
Block a user