Parcourir la source

新增登录手机号校验,失败提示内容

yuanmingze il y a 3 mois
Parent
commit
e938f81a5b
4 fichiers modifiés avec 142 ajouts et 56 suppressions
  1. 100 31
      src/components/ModernDialog.vue
  2. 4 0
      src/main.ts
  3. 20 20
      src/services/request/index.ts
  4. 18 5
      src/views/login/index.vue

+ 100 - 31
src/components/ModernDialog.vue

@@ -7,38 +7,83 @@
         <div class="dialog-title">{{ title }}</div>
         <div class="dialog-message">{{ message }}</div>
 
-        <van-button
-          type="primary"
-          block
-          round
-          class="dialog-btn"
-          color="linear-gradient(90deg, #ff7a00, #ffa94d)"
-          @click="handleConfirm"
+        <!-- 按钮区:支持 1 或 2 个按钮 -->
+        <div
+          class="dialog-actions"
+          :class="{
+            two: hasConfirm && hasCancel,
+            one: hasConfirm !== hasCancel,
+          }"
         >
-          {{ confirmText }}
-        </van-button>
+          <!-- 失败/取消:无背景,边框+文字色由 color 控制 -->
+          <van-button
+            v-if="hasCancel"
+            plain
+            class="btn btn--fail"
+            :color="cancelColor"
+            round
+            @click="handleCancel"
+          >
+            {{ cancelText }}
+          </van-button>
+
+          <!-- 成功/确定:填充背景,支持渐变 -->
+          <van-button
+            v-if="hasConfirm"
+            type="primary"
+            class="btn btn--success"
+            :color="confirmColor"
+            round
+            @click="handleConfirm"
+          >
+            {{ confirmText }}
+          </van-button>
+        </div>
       </div>
     </div>
   </transition>
 </template>
 
 <script setup lang="ts">
-import { defineProps, defineEmits } from 'vue'
+import { computed } from 'vue'
 
 const props = defineProps({
   show: { type: Boolean, required: true },
   title: { type: String, default: '提示' },
   message: { type: String, default: '' },
-  confirmText: { type: String, default: '确定' },
+
+  /** 成功按钮(右侧) */
+  confirmText: { type: String, default: '' },
+  /** 失败按钮(左侧),无背景 */
+  cancelText: { type: String, default: '' },
+
+  /** 成功按钮背景色(可用渐变) */
+  confirmColor: {
+    type: String,
+    default: 'linear-gradient(90deg, #ff7a00, #ffa94d)',
+  },
+  /** 失败按钮文字/边框颜色(plain 模式下作为描边+文字色) */
+  cancelColor: {
+    type: String,
+    default: '#fe783d',
+  },
+
+  /** 点击遮罩是否关闭 */
   closeOnClickOverlay: { type: Boolean, default: true },
 })
 
-const emit = defineEmits(['update:show', 'confirm', 'close'])
+const emit = defineEmits<{
+  (e: 'update:show', v: boolean): void
+  (e: 'confirm'): void
+  (e: 'cancel'): void
+  (e: 'close'): void
+}>()
 
-const handleConfirm = () => {
-  emit('confirm') // 让父组件自己处理逻辑
-}
+const hasConfirm = computed(() => !!props.confirmText)
+const hasCancel = computed(() => !!props.cancelText)
 
+const handleConfirm = () => emit('confirm')
+const handleCancel = () => emit('cancel')
 const handleClose = () => {
   if (props.closeOnClickOverlay) emit('update:show', false)
   emit('close')
@@ -55,8 +100,7 @@ const handleClose = () => {
 
 .modern-dialog {
   position: fixed;
-  top: 50%;
-  left: 50%;
+  inset: 50% auto auto 50%;
   transform: translate(-50%, -50%);
   z-index: 9999;
   width: 80vw;
@@ -88,23 +132,48 @@ const handleClose = () => {
     word-break: break-word;
   }
 
-  .dialog-btn {
-    width: calc(100% - 10vw);
-    margin: 0 auto 2.5vw;
-    height: 12vw;
-    border-radius: 3vw;
-    font-size: 4vw;
-    font-weight: 600;
-    display: inline-flex;
-    align-items: center;
-    justify-content: center;
-    border: none;
-    box-shadow: 0 4px 12px rgba(255, 128, 54, 0.25);
-    letter-spacing: 0.2vw;
+  /* 按钮区域布局 */
+  .dialog-actions {
+    width: 100%;
+    padding: 0 5vw 2.5vw;
+    box-sizing: border-box;
+    display: grid;
+    gap: 3vw;
+
+    &.one {
+      grid-template-columns: 1fr;
+
+      .btn {
+        height: 12vw;
+        border-radius: 3vw;
+        font-size: 4vw;
+        font-weight: 600;
+        letter-spacing: 0.2vw;
+      }
+    }
+
+    &.two {
+      grid-template-columns: 1fr 1fr;
+
+      .btn {
+        height: 12vw;
+        border-radius: 3vw;
+        font-size: 4vw;
+        font-weight: 600;
+        letter-spacing: 0.2vw;
+      }
+
+      /* 失败按钮:无背景(plain),加淡边框 */
+      .btn--fail {
+        /* 让边框略淡一些,视觉层次更好 */
+        border-width: 1px;
+        background-color: #fe783d;
+      }
+    }
   }
 }
 
-/* 动画 */
+/* 过渡动画 */
 .dialog-pop-enter-active,
 .dialog-pop-leave-active {
   transition: all 0.25s ease;

+ 4 - 0
src/main.ts

@@ -12,6 +12,10 @@ import vueDompurifyHTMLPlugin from 'vue-dompurify-html'
 import VueViewer from 'v-viewer'
 import { setupStore } from '@/stores'
 import router from './router'
+import { setToastDefaultOptions } from 'vant'
+
+// 全局设置:仅对 showFailToast 生效
+setToastDefaultOptions('fail', { duration: 1200 })
 
 const app = createApp(App)
 

+ 20 - 20
src/services/request/index.ts

@@ -222,28 +222,28 @@ const onResponse = (response: AxiosResponse<ServiceResponse>) => {
   clearRequest(key)
 
   // ✅ 强制类型断言(解决 silent 类型丢失问题)
-  const customConfig = response.config as CustomAxiosRequestConfig
+  // const customConfig = response.config as CustomAxiosRequestConfig
 
   const res = response.data
-  const { code, msg } = res
-
-  if (SUCCESS_CODE_WHITE_LIST.includes(code)) {
-    return res
-  }
-
-  const url = response.config.url || ''
-  // 针对登录接口单独处理错误提示
-  if (url.includes('/auth/login/etssms')) {
-    return res
-  }
-
-  if (code === 1 || !SUCCESS_CODE_WHITE_LIST.includes(code)) {
-    const message = msg || '业务逻辑错误'
-    if (customConfig.silent !== true) {
-      showToast(msg)
-    }
-    throw new HttpError(message, { code, data: res })
-  }
+  // const { code, msg } = res
+
+  // if (SUCCESS_CODE_WHITE_LIST.includes(code)) {
+  //   return res
+  // }
+
+  // const url = response.config.url || ''
+  // // 针对登录接口单独处理错误提示
+  // if (url.includes('/auth/login/etssms')) {
+  //   return res
+  // }
+
+  // if (code === 1 || !SUCCESS_CODE_WHITE_LIST.includes(code)) {
+  //   const message = msg || '业务逻辑错误'
+  //   if (customConfig.silent !== true) {
+  //     showToast(msg)
+  //   }
+  //   throw new HttpError(message, { code, data: res })
+  // }
 
   return res
 }

+ 18 - 5
src/views/login/index.vue

@@ -56,6 +56,14 @@
       <div class="register-btn btn" @click="registerUrl">立即注册</div>
 
       <div class="footer">技术支持:要易云(北京)科技有限公司</div>
+      <!-- 失败弹窗 -->
+      <ModernDialog
+        v-model:show="showDialog"
+        title="提示"
+        :message="codeMessage"
+        cancelText="返回继续登录"
+        @cancel="onCancel"
+      />
     </div>
   </div>
 </template>
@@ -78,6 +86,7 @@ import type {
 import { useUserStore } from '@/stores/modules/user'
 import { useDebounceFn } from '@/utils/util'
 import { REGISTER_URL_KEY } from '@/constants/storage'
+import ModernDialog from '@/components/ModernDialog.vue'
 
 const userStore = useUserStore()
 const router = useRouter()
@@ -114,6 +123,11 @@ const countDown = ref(60)
 let timer: number | undefined
 
 const sending = ref(false)
+const showDialog = ref(false)
+const codeMessage = ref()
+const onCancel = () => {
+  showDialog.value = false
+}
 /* 发送验证码 */
 const sendCode = async () => {
   if (sending.value) return
@@ -130,7 +144,8 @@ const sendCode = async () => {
       showSuccessToast('发送成功')
       startCountDown()
     } else {
-      showFailToast(res.data.msg)
+      codeMessage.value = res.msg
+      showDialog.value = true
     }
   } catch (err: any) {
     showFailToast(err?.message || '获取验证码失败')
@@ -154,7 +169,6 @@ const startCountDown = () => {
 }
 
 /* 登录逻辑 */
-
 const loginBtn = useDebounceFn(async () => {
   if (!formData.mobile) {
     showFailToast('请输入手机号')
@@ -182,8 +196,6 @@ const loginBtn = useDebounceFn(async () => {
     }
   } catch (err: any) {
     const { code, message } = err || {}
-    console.log('message', message)
-
     if (code && message)
       showFailToast({
         message: message,
@@ -198,7 +210,7 @@ const loginBtn = useDebounceFn(async () => {
 // 获取注册链接
 const registerUrlParams = ref<RegisterUrlRequest>({
   areaId: '',
-  returnUrl: 'https://www.baidu.com',
+  returnUrl: '',
 })
 const registerUrl = useDebounceFn(async () => {
   if (!formData.mobile) {
@@ -243,6 +255,7 @@ const toAgreement = (type: string) => {
 }
 
 onBeforeMount(() => {
+  registerUrlParams.value.returnUrl = `${location.origin}${route.fullPath}`
   const pushRecordId = route?.query.pushRecordId || ''
   if (pushRecordId) {
     sendEtsSms.pushRecordId = pushRecordId as string