feat(token): 添加验证码输入及重新发送功能
- 新增输入验证码模态框组件,支持验证码输入和校验 - 仅在验证码已发送状态显示输入验证码按钮 - 仅在验证码验证失败状态显示重新发送验证码按钮 - 实现验证码表单验证与提交逻辑,提交成功关闭模态框并通知父组件 - 重新发送验证码时显示加载状态,并支持重试与错误提示 - 调整Token列表操作列宽度以适配新按钮 - 优化界面样式,确保模态框滚动条样式主题兼容
This commit is contained in:
@@ -0,0 +1,160 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="modalVisible"
|
||||||
|
title="输入验证码"
|
||||||
|
width="500px"
|
||||||
|
:ok-loading="loading"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
@before-ok="handleBeforeOk"
|
||||||
|
>
|
||||||
|
<a-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="formModel"
|
||||||
|
:rules="rules"
|
||||||
|
:label-col-props="{ span: 6 }"
|
||||||
|
:wrapper-col-props="{ span: 18 }"
|
||||||
|
label-align="left"
|
||||||
|
>
|
||||||
|
<a-form-item field="code" label="验证码" required>
|
||||||
|
<a-input
|
||||||
|
v-model="formModel.code"
|
||||||
|
placeholder="请输入收到的验证码"
|
||||||
|
:max-length="10"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, reactive, computed, watch } from 'vue';
|
||||||
|
import { Notification, FormInstance } from '@arco-design/web-vue';
|
||||||
|
import { KamiApiCamelOilV1TokenInfo } from '@/api/generated/index.ts';
|
||||||
|
import { jdV2TokenClient } from '@/api/index.ts';
|
||||||
|
import useLoading from '@/hooks/loading';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
visible: boolean;
|
||||||
|
tokenData?: KamiApiCamelOilV1TokenInfo | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
(e: 'update:visible', value: boolean): void;
|
||||||
|
(e: 'success'): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>();
|
||||||
|
const emit = defineEmits<Emits>();
|
||||||
|
|
||||||
|
const modalVisible = computed({
|
||||||
|
get: () => props.visible,
|
||||||
|
set: value => emit('update:visible', value)
|
||||||
|
});
|
||||||
|
|
||||||
|
const formRef = ref<FormInstance>();
|
||||||
|
const { loading, setLoading } = useLoading(false);
|
||||||
|
|
||||||
|
const generateFormModel = () => {
|
||||||
|
return {
|
||||||
|
tokenId: 0,
|
||||||
|
code: ''
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const formModel = reactive(generateFormModel());
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
code: [
|
||||||
|
{ required: true, message: '请输入验证码' },
|
||||||
|
{ min: 4, max: 10, message: '验证码长度为4-10个字符' }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
// 监听弹窗显示状态和token数据,更新表单
|
||||||
|
watch(
|
||||||
|
[() => props.visible, () => props.tokenData],
|
||||||
|
([visible, tokenData]) => {
|
||||||
|
if (visible && tokenData) {
|
||||||
|
Object.assign(formModel, {
|
||||||
|
tokenId: tokenData.id || 0,
|
||||||
|
code: ''
|
||||||
|
});
|
||||||
|
} else if (!visible) {
|
||||||
|
Object.assign(formModel, generateFormModel());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
emit('update:visible', false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBeforeOk = (done: (closed: boolean) => void) => {
|
||||||
|
formRef.value?.validate().then(async (res: any) => {
|
||||||
|
if (res) {
|
||||||
|
console.log('Validation failed:', res);
|
||||||
|
return done(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
console.log('Validation passed, submitting verification code...');
|
||||||
|
|
||||||
|
await jdV2TokenClient.apiTokenInputVerificationCodePost({
|
||||||
|
kamiApiCamelOilV1InputVerificationCodeReq: {
|
||||||
|
tokenId: formModel.tokenId,
|
||||||
|
code: formModel.code
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Notification.success({
|
||||||
|
content: '验证码提交成功',
|
||||||
|
closable: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// 操作成功,触发成功事件并关闭模态框
|
||||||
|
console.log('Verification code submitted successfully');
|
||||||
|
emit('success');
|
||||||
|
emit('update:visible', false);
|
||||||
|
done(true);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('提交验证码失败:', err);
|
||||||
|
Notification.error({
|
||||||
|
content: '提交验证码失败,请检查验证码是否正确',
|
||||||
|
closable: true
|
||||||
|
});
|
||||||
|
// 操作失败,阻止模态框关闭
|
||||||
|
console.log('Verification code submission failed, keeping modal open');
|
||||||
|
done(false);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 使用 Arco Design 的原生样式,确保主题兼容性 */
|
||||||
|
:deep(.arco-modal-body) {
|
||||||
|
max-height: 60vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
/* 确保滚动条样式跟随主题 */
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: var(--color-border-3) transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.arco-modal-body::-webkit-scrollbar) {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.arco-modal-body::-webkit-scrollbar-thumb) {
|
||||||
|
background-color: var(--color-border-3);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.arco-modal-body::-webkit-scrollbar-track) {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -104,6 +104,31 @@
|
|||||||
<!-- 操作列模板 -->
|
<!-- 操作列模板 -->
|
||||||
<template #operations="{ record }">
|
<template #operations="{ record }">
|
||||||
<a-space size="small">
|
<a-space size="small">
|
||||||
|
<!-- 输入验证码按钮 - 仅在验证码已发送时显示 -->
|
||||||
|
<a-tooltip v-if="record.status === 5" content="输入验证码">
|
||||||
|
<a-button
|
||||||
|
size="small"
|
||||||
|
type="secondary"
|
||||||
|
@click="showInputVerificationModal(record)"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<icon-message />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<!-- 重新发送验证码按钮 - 仅在验证码验证失败时显示 -->
|
||||||
|
<a-tooltip v-if="record.status === 6" content="重新发送验证码">
|
||||||
|
<a-button
|
||||||
|
size="small"
|
||||||
|
type="secondary"
|
||||||
|
:loading="resendLoadingIds.has(record.id)"
|
||||||
|
@click="resendVerificationCode(record)"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<icon-refresh />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
<a-tooltip content="编辑Token">
|
<a-tooltip content="编辑Token">
|
||||||
<a-button
|
<a-button
|
||||||
size="small"
|
size="small"
|
||||||
@@ -161,6 +186,13 @@
|
|||||||
v-model:visible="state.tokenDrawerVisible"
|
v-model:visible="state.tokenDrawerVisible"
|
||||||
:token-data="state.currentTokenData"
|
:token-data="state.currentTokenData"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- 输入验证码模态框 -->
|
||||||
|
<input-verification-modal
|
||||||
|
v-model:visible="state.inputVerificationModalVisible"
|
||||||
|
:token-data="state.currentVerificationToken"
|
||||||
|
@success="handleInputVerificationSuccess"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -171,6 +203,7 @@ import { onMounted, reactive, ref } from 'vue';
|
|||||||
import { Notification, TableColumnData } from '@arco-design/web-vue';
|
import { Notification, TableColumnData } from '@arco-design/web-vue';
|
||||||
import AddEditModal from './components/add-edit-modal.vue';
|
import AddEditModal from './components/add-edit-modal.vue';
|
||||||
import TokenDrawer from './components/token-drawer.vue';
|
import TokenDrawer from './components/token-drawer.vue';
|
||||||
|
import InputVerificationModal from './components/input-verification-modal.vue';
|
||||||
import {
|
import {
|
||||||
ApiTokenListGetPageSizeEnum,
|
ApiTokenListGetPageSizeEnum,
|
||||||
KamiApiCamelOilV1TokenInfo
|
KamiApiCamelOilV1TokenInfo
|
||||||
@@ -243,7 +276,7 @@ const columns: TableColumnData[] = [
|
|||||||
dataIndex: 'operations',
|
dataIndex: 'operations',
|
||||||
slotName: 'operations',
|
slotName: 'operations',
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
width: 250
|
width: 380
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -258,12 +291,17 @@ const { loading, setLoading } = useLoading(true);
|
|||||||
const renderData = ref<KamiApiCamelOilV1TokenInfo[]>([]);
|
const renderData = ref<KamiApiCamelOilV1TokenInfo[]>([]);
|
||||||
const formModel = ref(generateFormModel());
|
const formModel = ref(generateFormModel());
|
||||||
|
|
||||||
|
// 重新发送验证码的加载状态管理
|
||||||
|
const resendLoadingIds = ref(new Set<number>());
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
addEditModalVisible: false,
|
addEditModalVisible: false,
|
||||||
editModalVisible: false,
|
editModalVisible: false,
|
||||||
tokenDrawerVisible: false,
|
tokenDrawerVisible: false,
|
||||||
|
inputVerificationModalVisible: false,
|
||||||
currentTokenData: null as KamiApiCamelOilV1TokenInfo | null,
|
currentTokenData: null as KamiApiCamelOilV1TokenInfo | null,
|
||||||
currentEditData: null as KamiApiCamelOilV1TokenInfo | null
|
currentEditData: null as KamiApiCamelOilV1TokenInfo | null,
|
||||||
|
currentVerificationToken: null as KamiApiCamelOilV1TokenInfo | null
|
||||||
});
|
});
|
||||||
|
|
||||||
const fetchData = async (params: any = { current: 1, pageSize: 50 }) => {
|
const fetchData = async (params: any = { current: 1, pageSize: 50 }) => {
|
||||||
@@ -335,6 +373,16 @@ const showTokenDrawer = (record: KamiApiCamelOilV1TokenInfo) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const showInputVerificationModal = (record: KamiApiCamelOilV1TokenInfo) => {
|
||||||
|
console.log('showInputVerificationModal called with record:', record);
|
||||||
|
state.currentVerificationToken = record;
|
||||||
|
state.inputVerificationModalVisible = true;
|
||||||
|
console.log(
|
||||||
|
'inputVerificationModalVisible set to true, currentVerificationToken:',
|
||||||
|
state.currentVerificationToken
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const deleteToken = async (tokenId: number) => {
|
const deleteToken = async (tokenId: number) => {
|
||||||
try {
|
try {
|
||||||
await jdV2TokenClient.apiTokenDeletePost({
|
await jdV2TokenClient.apiTokenDeletePost({
|
||||||
@@ -366,6 +414,10 @@ const handleEditModalSuccess = () => {
|
|||||||
fetchData({ ...pagination });
|
fetchData({ ...pagination });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleInputVerificationSuccess = () => {
|
||||||
|
fetchData({ ...pagination });
|
||||||
|
};
|
||||||
|
|
||||||
const statusMapper = (status: number): { text: string; color: string } => {
|
const statusMapper = (status: number): { text: string; color: string } => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 0:
|
case 0:
|
||||||
@@ -407,6 +459,42 @@ const download = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重新发送验证码
|
||||||
|
* @param record Token记录
|
||||||
|
*/
|
||||||
|
const resendVerificationCode = async (record: KamiApiCamelOilV1TokenInfo) => {
|
||||||
|
if (!record.id) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 添加到加载状态
|
||||||
|
resendLoadingIds.value.add(record.id);
|
||||||
|
|
||||||
|
await jdV2TokenClient.apiTokenResendVerificationCodePost({
|
||||||
|
kamiApiCamelOilV1ResendVerificationCodeReq: {
|
||||||
|
tokenId: record.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Notification.success({
|
||||||
|
content: '验证码已重新发送',
|
||||||
|
closable: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// 刷新列表数据
|
||||||
|
fetchData({ ...pagination });
|
||||||
|
} catch (err) {
|
||||||
|
console.error('重新发送验证码失败:', err);
|
||||||
|
Notification.error({
|
||||||
|
content: '重新发送验证码失败,请稍后重试',
|
||||||
|
closable: true
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
// 移除加载状态
|
||||||
|
resendLoadingIds.value.delete(record.id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user