Преглед изворни кода

bug修改,新增注册与bug弹窗

yuanmingze пре 3 месеци
родитељ
комит
9c44b73985

+ 2 - 0
src/constants/storage.ts

@@ -0,0 +1,2 @@
+// src/constants/storage.ts
+export const REGISTER_URL_KEY = 'H5_REGISTER_URL'

+ 6 - 0
src/router/routes.ts

@@ -12,6 +12,12 @@ export const routes: RouteRecordRaw[] = [
     component: () => import('@/views/login/index.vue'),
     meta: { title: '登录', requiresAuth: false },
   },
+  {
+    path: '/register',
+    name: 'Register',
+    component: () => import('@/views/register/index.vue'),
+    meta: { title: '登录', requiresAuth: false },
+  },
   {
     path: '/agreement',
     name: 'Agreement',

+ 9 - 1
src/services/modules/login/index.ts

@@ -1,5 +1,5 @@
 import http from '../../index'
-import type { LoginEtssmsRequest, SendTtsSmsRequest } from './type.d'
+import type { LoginEtssmsRequest, SendTtsSmsRequest, RegisterUrlRequest } from './type.d'
 import type { PushRecordIdRequest } from '../invoiceInformation/type.d'
 
 export const getPubNameApi = (data: PushRecordIdRequest) => {
@@ -22,3 +22,11 @@ export const loginEtssmsApi = (data: LoginEtssmsRequest) => {
     params: data,
   })
 }
+
+export const registerUrlApi = (params: RegisterUrlRequest, data: SendTtsSmsRequest) => {
+  return http.get({
+    url: '/admin/invoice/register-url',
+    params: params,
+    data,
+  })
+}

+ 5 - 0
src/services/modules/login/type.d.ts

@@ -9,3 +9,8 @@ export interface SendTtsSmsRequest {
   mobile: string
   pushRecordId: string
 }
+
+export interface RegisterUrlRequest {
+  areaId: string
+  returnUrl: string
+}

+ 78 - 19
src/views/login/index.vue

@@ -49,11 +49,11 @@
         </div>
 
         <!-- 登录按钮 -->
-        <div class="login-btn btn" @click="login">登录</div>
+        <div class="login-btn btn" @click="loginBtn">登录</div>
       </div>
 
       <div class="bold-text">未注册电子税务局</div>
-      <div class="register-btn btn">立即注册</div>
+      <div class="register-btn btn" @click="registerUrl">立即注册</div>
 
       <div class="footer">技术支持:要易云(北京)科技有限公司</div>
     </div>
@@ -63,10 +63,21 @@
 <script setup lang="ts">
 import { ref, reactive, onBeforeMount } from 'vue'
 import { useRouter, useRoute } from 'vue-router'
-import { showFailToast, showSuccessToast, showToast } from 'vant'
-import { getPubNameApi, sendEtsSmsApi, loginEtssmsApi } from '@/services/modules/login'
-import type { SendTtsSmsRequest, LoginEtssmsRequest } from '@/services/modules/login/type.d'
+import { showFailToast, showSuccessToast, showToast, showLoadingToast } from 'vant'
+import {
+  getPubNameApi,
+  sendEtsSmsApi,
+  loginEtssmsApi,
+  registerUrlApi,
+} from '@/services/modules/login'
+import type {
+  SendTtsSmsRequest,
+  LoginEtssmsRequest,
+  RegisterUrlRequest,
+} from '@/services/modules/login/type.d'
 import { useUserStore } from '@/stores/modules/user'
+import { useDebounceFn } from '@/utils/util'
+import { REGISTER_URL_KEY } from '@/constants/storage'
 
 const userStore = useUserStore()
 const router = useRouter()
@@ -101,19 +112,30 @@ const isCounting = ref(false)
 const countDown = ref(60)
 let timer: number | undefined
 
+const sending = ref(false)
 /* 发送验证码 */
 const sendCode = async () => {
+  if (sending.value) return
   if (!/^1\d{10}$/.test(formData.mobile)) {
     showToast('请输入正确的手机号')
     return
   }
+  sending.value = true
+  const toast = showLoadingToast({ message: '发送中…', forbidClick: true, duration: 0 })
   sendEtsSms.mobile = formData.mobile
-  const res = await sendEtsSmsApi(sendEtsSms)
-  if (res.code === 0 && res.data) {
-    showSuccessToast('发送成功')
-    startCountDown()
-  } else {
-    showFailToast(res.data.msg)
+  try {
+    const res = await sendEtsSmsApi(sendEtsSms)
+    if (res.code === 0 && res.data) {
+      showSuccessToast('发送成功')
+      startCountDown()
+    } else {
+      showFailToast(res.data.msg)
+    }
+  } catch (err: any) {
+    showFailToast(err?.message || '获取验证码失败')
+  } finally {
+    toast.close()
+    sending.value = false
   }
 }
 
@@ -131,20 +153,25 @@ const startCountDown = () => {
 }
 
 /* 登录逻辑 */
-const login = async () => {
+
+const loginBtn = useDebounceFn(async () => {
   if (!formData.mobile) {
-    showFailToast('请输入手机号和验证码')
+    showFailToast('请输入手机号')
     return
   }
   if (!formData.code) {
     showFailToast('验证码不能为空')
     return
   }
-
   if (!agree.value) {
     showFailToast('请勾选阅读并同意')
     return
   }
+  const toast = showLoadingToast({
+    message: '登录中…',
+    forbidClick: true,
+    duration: 0,
+  })
 
   try {
     const res: any = await loginEtssmsApi(formData)
@@ -153,15 +180,46 @@ const login = async () => {
       router.replace({ path: '/invoice-information' })
     }
   } catch (err: any) {
-    const { code, message } = err
-    if (code && message) {
-      showFailToast(message)
-    }
+    const { code, message } = err || {}
+    console.log('message', message)
+
+    if (code && message)
+      showFailToast({
+        message: message,
+        duration: 1500, // 3.5 秒
+        forbidClick: true, // 可选:禁止点击
+      })
+  } finally {
+    toast.close()
   }
-}
+}, 800)
 
+// 获取注册链接
+const registerUrlParams = ref<RegisterUrlRequest>({
+  areaId: '',
+  returnUrl: 'https://www.baidu.com',
+})
+const registerUrl = useDebounceFn(async () => {
+  if (!formData.mobile) {
+    showFailToast('请输入手机号')
+    return
+  }
+  sendEtsSms.mobile = formData.mobile
+  try {
+    const res = await registerUrlApi(registerUrlParams.value, sendEtsSms)
+    if (res.code === 0 && res.data.url) {
+      // 保存一次性链接
+      sessionStorage.setItem(REGISTER_URL_KEY, res.data.url as string)
+      return router.push({ path: 'register' })
+    }
+    showFailToast('获取注册链接失败')
+  } catch (err: any) {
+    showFailToast(err?.message || '获取注册链接失败')
+  }
+}, 800)
 // 项目名称
 const pubName = ref('')
+
 const getPubName = async () => {
   try {
     const res = await getPubNameApi({
@@ -169,6 +227,7 @@ const getPubName = async () => {
     })
     if (res.code === 0) {
       pubName.value = res.data.projectName
+      registerUrlParams.value.areaId = res.data.cityCode
     }
   } catch (err) {
     console.log('err', err)

+ 122 - 0
src/views/register/index.vue

@@ -0,0 +1,122 @@
+<!-- src/pages/RegisterIframePage.vue -->
+<template>
+  <div class="register-page">
+    <van-nav-bar fixed :title="title" left-arrow @click-left="onBack" />
+    <div class="iframe-wrap">
+      <div v-if="loading" class="loading">
+        <van-loading size="24px" type="spinner" />
+        <span class="loading-text">加载中…</span>
+      </div>
+      <iframe
+        v-if="realSrc"
+        class="iframe"
+        :src="realSrc"
+        frameborder="0"
+        @load="onLoad"
+        scrolling="none"
+        allow="fullscreen; geolocation; microphone; camera; clipboard-read; clipboard-write"
+        referrerpolicy="strict-origin-when-cross-origin"
+      ></iframe>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, onMounted, onUnmounted } from 'vue'
+import { useRouter } from 'vue-router'
+import { showFailToast } from 'vant'
+import { REGISTER_URL_KEY } from '@/constants/storage'
+
+interface Props {
+  title?: string
+  src?: string
+}
+const props = defineProps<Props>()
+const title = props.title ?? '注册'
+
+const router = useRouter()
+const loading = ref(true)
+const realSrc = ref('')
+
+const onBack = () => {
+  if (history.length > 1) router.back()
+  else router.replace({ name: 'Login' })
+}
+const onLoad = () => {
+  loading.value = false
+}
+
+onMounted(() => {
+  // 禁止外层滚动,避免出现页面滚动条
+  document.body.style.overflow = 'hidden'
+
+  if (props.src) {
+    realSrc.value = props.src
+    return
+  }
+  const cached = sessionStorage.getItem(REGISTER_URL_KEY)
+  if (cached) {
+    realSrc.value = cached
+    sessionStorage.removeItem(REGISTER_URL_KEY) // 一次性
+  } else {
+    showFailToast('未检测到注册链接,已返回登录页')
+    router.back()
+  }
+})
+
+onUnmounted(() => {
+  document.body.style.overflow = ''
+})
+</script>
+
+<style lang="scss" scoped>
+/* 固定导航高度(若你的 NavBar 不是 46px,这里改数值即可) */
+.register-page {
+  --nav-height: 46px;
+  height: 100dvh; /* 占满视口高度 */
+  overflow: hidden; /* 外层不出现滚动条 */
+  background: #fff;
+}
+
+/* 从 NavBar 底部贴到屏幕底部,永远填满剩余空间 */
+.iframe-wrap {
+  position: fixed;
+  top: var(--nav-height);
+  left: 0;
+  right: 0;
+  bottom: 0;
+  overflow: hidden; /* 禁止外层滚动 */
+}
+
+/* iframe 充满容器;用 100%,避免 100vw 引入横向滚动条 */
+.iframe {
+  display: block;
+  width: 100%;
+  height: 100%;
+  border: 0;
+}
+
+/* 简易加载遮罩 */
+.loading {
+  position: absolute;
+  inset: 0;
+  z-index: 1;
+  display: grid;
+  place-items: center;
+  background: rgba(255, 255, 255, 0.85);
+
+  .loading-text {
+    margin-top: 8px;
+    font-size: 12px;
+    color: #999;
+  }
+}
+
+/* 站点级兜底:去掉默认间距并禁掉页面滚动(scoped 下需 :global) */
+:global(html, body) {
+  margin: 0;
+  padding: 0;
+  overflow: hidden;
+  overscroll-behavior: none;
+}
+</style>