调整登录组织校验和图形验证码交互
This commit is contained in:
@@ -5,6 +5,7 @@ import type { TokenResponse } from '@/types/api'
|
|||||||
import { joinUrl } from '@/utils/url'
|
import { joinUrl } from '@/utils/url'
|
||||||
|
|
||||||
export interface OrganizationMemberInfo {
|
export interface OrganizationMemberInfo {
|
||||||
|
userId?: string
|
||||||
memberId?: string
|
memberId?: string
|
||||||
organizationId?: string
|
organizationId?: string
|
||||||
memberName?: string
|
memberName?: string
|
||||||
@@ -28,7 +29,7 @@ export const getImageCodeUrl = (imageRandomStr: string) => {
|
|||||||
randomStr: imageRandomStr,
|
randomStr: imageRandomStr,
|
||||||
t: String(Date.now())
|
t: String(Date.now())
|
||||||
})
|
})
|
||||||
return `${joinUrl(env.authBase, 'code/image')}?${params.toString()}`
|
return `${joinUrl(env.apiBase, env.authBase, 'code/image')}?${params.toString()}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export const sendMobileCode = async (payload: SendMobileCodePayload) => {
|
export const sendMobileCode = async (payload: SendMobileCodePayload) => {
|
||||||
@@ -72,3 +73,10 @@ export const bindUserInfoAndGetUserMemberInfoByPhone = async (phone: string) =>
|
|||||||
{ userPhone: phone }
|
{ userPhone: phone }
|
||||||
) as unknown as OrganizationMemberInfo
|
) as unknown as OrganizationMemberInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getBindStaffInfo = async () => {
|
||||||
|
if (env.useMock) return mockApi.getBindStaffInfo()
|
||||||
|
return await http.post<OrganizationMemberInfo>(
|
||||||
|
joinUrl(env.staffBase, 'user/getBindStaffInfo')
|
||||||
|
) as unknown as OrganizationMemberInfo
|
||||||
|
}
|
||||||
|
|||||||
@@ -171,6 +171,17 @@ export const mockApi = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async getBindStaffInfo() {
|
||||||
|
await wait(180)
|
||||||
|
return {
|
||||||
|
userId: 'USER10001',
|
||||||
|
memberId: 'MEMBER10001',
|
||||||
|
organizationId: 'ORG10001',
|
||||||
|
memberName: '当前员工',
|
||||||
|
memberRoleId: 'ROLE_STAFF'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
async orderList(query: CommodityOrderSearch) {
|
async orderList(query: CommodityOrderSearch) {
|
||||||
await wait()
|
await wait()
|
||||||
const keyword = query.orderId || query.contactPhone || ''
|
const keyword = query.orderId || query.contactPhone || ''
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import {
|
import {
|
||||||
bindUserInfoAndGetUserMemberInfoByPhone,
|
getBindStaffInfo,
|
||||||
loginByMobile,
|
loginByMobile,
|
||||||
type OrganizationMemberInfo
|
type OrganizationMemberInfo
|
||||||
} from '@/api/auth'
|
} from '@/api/auth'
|
||||||
@@ -66,7 +66,7 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
const payload = await loginByMobile(phone, code)
|
const payload = await loginByMobile(phone, code)
|
||||||
persistToken(payload, phone)
|
persistToken(payload, phone)
|
||||||
try {
|
try {
|
||||||
const member = await bindUserInfoAndGetUserMemberInfoByPhone(phone)
|
const member = await getBindStaffInfo()
|
||||||
if (!member?.organizationId || !member?.memberId) {
|
if (!member?.organizationId || !member?.memberId) {
|
||||||
throw new Error('未绑定组织,请联系管理员')
|
throw new Error('未绑定组织,请联系管理员')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, ref } from 'vue'
|
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { showToast } from 'vant'
|
import { showToast } from 'vant'
|
||||||
import { BadgeCheck, Phone, ShieldCheck } from 'lucide-vue-next'
|
import { BadgeCheck, Phone, ShieldCheck } from 'lucide-vue-next'
|
||||||
@@ -36,6 +36,7 @@ const refreshImageCode = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const canSend = computed(() => /^1\d{10}$/.test(phone.value) && seconds.value === 0)
|
const canSend = computed(() => /^1\d{10}$/.test(phone.value) && seconds.value === 0)
|
||||||
|
const captchaLocked = computed(() => seconds.value > 0 || sending.value)
|
||||||
|
|
||||||
const startCountdown = () => {
|
const startCountdown = () => {
|
||||||
seconds.value = 60
|
seconds.value = 60
|
||||||
@@ -43,7 +44,9 @@ const startCountdown = () => {
|
|||||||
timer = window.setInterval(() => {
|
timer = window.setInterval(() => {
|
||||||
seconds.value -= 1
|
seconds.value -= 1
|
||||||
if (seconds.value <= 0) {
|
if (seconds.value <= 0) {
|
||||||
|
seconds.value = 0
|
||||||
window.clearInterval(timer)
|
window.clearInterval(timer)
|
||||||
|
refreshImageCode()
|
||||||
}
|
}
|
||||||
}, 1000)
|
}, 1000)
|
||||||
}
|
}
|
||||||
@@ -74,7 +77,6 @@ const handleSend = async () => {
|
|||||||
showToast('验证码已发送')
|
showToast('验证码已发送')
|
||||||
}
|
}
|
||||||
startCountdown()
|
startCountdown()
|
||||||
refreshImageCode()
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showToast(error instanceof Error ? error.message : '验证码发送失败')
|
showToast(error instanceof Error ? error.message : '验证码发送失败')
|
||||||
refreshImageCode()
|
refreshImageCode()
|
||||||
@@ -105,6 +107,7 @@ const handleLogin = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(refreshImageCode)
|
onMounted(refreshImageCode)
|
||||||
|
onBeforeUnmount(() => window.clearInterval(timer))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -137,6 +140,7 @@ onMounted(refreshImageCode)
|
|||||||
maxlength="8"
|
maxlength="8"
|
||||||
placeholder="请输入图形验证码"
|
placeholder="请输入图形验证码"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
|
:disabled="captchaLocked"
|
||||||
clearable
|
clearable
|
||||||
>
|
>
|
||||||
<template #left-icon><ShieldCheck :size="18" /></template>
|
<template #left-icon><ShieldCheck :size="18" /></template>
|
||||||
@@ -145,6 +149,7 @@ onMounted(refreshImageCode)
|
|||||||
class="captcha-image-button"
|
class="captcha-image-button"
|
||||||
type="button"
|
type="button"
|
||||||
aria-label="刷新图形验证码"
|
aria-label="刷新图形验证码"
|
||||||
|
:disabled="captchaLocked"
|
||||||
@click="refreshImageCode"
|
@click="refreshImageCode"
|
||||||
>
|
>
|
||||||
<img v-if="imageCodeUrl" :src="imageCodeUrl" alt="图形验证码" />
|
<img v-if="imageCodeUrl" :src="imageCodeUrl" alt="图形验证码" />
|
||||||
@@ -200,6 +205,7 @@ onMounted(refreshImageCode)
|
|||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
background:
|
background:
|
||||||
|
radial-gradient(circle at 18% 4%, rgba(19, 144, 111, 0.18), transparent 220px),
|
||||||
linear-gradient(180deg, #dff4ec 0, rgba(246, 248, 247, 0) 44%),
|
linear-gradient(180deg, #dff4ec 0, rgba(246, 248, 247, 0) 44%),
|
||||||
var(--app-bg);
|
var(--app-bg);
|
||||||
}
|
}
|
||||||
@@ -210,10 +216,11 @@ onMounted(refreshImageCode)
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
padding: 22px 16px 16px;
|
padding: 22px 16px 16px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 1px solid var(--line);
|
border: 1px solid rgba(202, 214, 208, 0.86);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--panel-radius);
|
||||||
background: var(--surface);
|
background: rgba(255, 255, 255, 0.9);
|
||||||
box-shadow: var(--shadow);
|
box-shadow: var(--shadow);
|
||||||
|
backdrop-filter: blur(12px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-brand {
|
.login-brand {
|
||||||
@@ -233,7 +240,8 @@ onMounted(refreshImageCode)
|
|||||||
width: 54px;
|
width: 54px;
|
||||||
height: 54px;
|
height: 54px;
|
||||||
flex: 0 0 54px;
|
flex: 0 0 54px;
|
||||||
border-radius: var(--radius);
|
border: 1px solid rgba(19, 144, 111, 0.16);
|
||||||
|
border-radius: var(--panel-radius);
|
||||||
background: var(--primary-soft);
|
background: var(--primary-soft);
|
||||||
color: var(--primary-deep);
|
color: var(--primary-deep);
|
||||||
place-items: center;
|
place-items: center;
|
||||||
@@ -250,6 +258,7 @@ onMounted(refreshImageCode)
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
color: var(--text-strong);
|
color: var(--text-strong);
|
||||||
font-size: 25px;
|
font-size: 25px;
|
||||||
|
font-weight: 750;
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,10 +270,18 @@ onMounted(refreshImageCode)
|
|||||||
|
|
||||||
.login-form :deep(.van-cell) {
|
.login-form :deep(.van-cell) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
height: 50px;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
min-height: 50px;
|
padding: 0 12px;
|
||||||
|
align-items: center;
|
||||||
border: 1px solid var(--line);
|
border: 1px solid var(--line);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
|
background: rgba(255, 255, 255, 0.86);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form :deep(.van-cell:focus-within) {
|
||||||
|
border-color: var(--primary);
|
||||||
|
box-shadow: var(--focus-ring);
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-form :deep(.van-field__value),
|
.login-form :deep(.van-field__value),
|
||||||
@@ -273,8 +290,16 @@ onMounted(refreshImageCode)
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-form :deep(.van-field__value),
|
||||||
|
.login-form :deep(.van-field__body) {
|
||||||
|
height: 100%;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
.login-form :deep(.van-field__button) {
|
.login-form :deep(.van-field__button) {
|
||||||
|
display: inline-flex;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
|
align-items: center;
|
||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,10 +307,17 @@ onMounted(refreshImageCode)
|
|||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-form :deep(.van-field__button .van-button) {
|
||||||
|
min-width: 86px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.captcha-image-button {
|
.captcha-image-button {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
width: 100px;
|
width: 96px;
|
||||||
height: 40px;
|
height: 32px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -294,13 +326,23 @@ onMounted(refreshImageCode)
|
|||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
background: var(--surface-soft);
|
background: var(--surface-soft);
|
||||||
color: var(--primary-deep);
|
color: var(--primary-deep);
|
||||||
|
transition: transform var(--duration-fast) var(--ease-out);
|
||||||
|
}
|
||||||
|
|
||||||
|
.captcha-image-button:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
|
||||||
|
.captcha-image-button:disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.66;
|
||||||
}
|
}
|
||||||
|
|
||||||
.captcha-image-button img {
|
.captcha-image-button img {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100px;
|
width: 96px;
|
||||||
height: 40px;
|
height: 32px;
|
||||||
object-fit: cover;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-note {
|
.login-note {
|
||||||
|
|||||||
Reference in New Issue
Block a user