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 }">
|
||||
<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-button
|
||||
size="small"
|
||||
@@ -161,6 +186,13 @@
|
||||
v-model:visible="state.tokenDrawerVisible"
|
||||
:token-data="state.currentTokenData"
|
||||
/>
|
||||
|
||||
<!-- 输入验证码模态框 -->
|
||||
<input-verification-modal
|
||||
v-model:visible="state.inputVerificationModalVisible"
|
||||
:token-data="state.currentVerificationToken"
|
||||
@success="handleInputVerificationSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -171,6 +203,7 @@ import { onMounted, reactive, ref } from 'vue';
|
||||
import { Notification, TableColumnData } from '@arco-design/web-vue';
|
||||
import AddEditModal from './components/add-edit-modal.vue';
|
||||
import TokenDrawer from './components/token-drawer.vue';
|
||||
import InputVerificationModal from './components/input-verification-modal.vue';
|
||||
import {
|
||||
ApiTokenListGetPageSizeEnum,
|
||||
KamiApiCamelOilV1TokenInfo
|
||||
@@ -243,7 +276,7 @@ const columns: TableColumnData[] = [
|
||||
dataIndex: 'operations',
|
||||
slotName: 'operations',
|
||||
fixed: 'right',
|
||||
width: 250
|
||||
width: 380
|
||||
}
|
||||
];
|
||||
|
||||
@@ -258,12 +291,17 @@ const { loading, setLoading } = useLoading(true);
|
||||
const renderData = ref<KamiApiCamelOilV1TokenInfo[]>([]);
|
||||
const formModel = ref(generateFormModel());
|
||||
|
||||
// 重新发送验证码的加载状态管理
|
||||
const resendLoadingIds = ref(new Set<number>());
|
||||
|
||||
const state = reactive({
|
||||
addEditModalVisible: false,
|
||||
editModalVisible: false,
|
||||
tokenDrawerVisible: false,
|
||||
inputVerificationModalVisible: false,
|
||||
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 }) => {
|
||||
@@ -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) => {
|
||||
try {
|
||||
await jdV2TokenClient.apiTokenDeletePost({
|
||||
@@ -366,6 +414,10 @@ const handleEditModalSuccess = () => {
|
||||
fetchData({ ...pagination });
|
||||
};
|
||||
|
||||
const handleInputVerificationSuccess = () => {
|
||||
fetchData({ ...pagination });
|
||||
};
|
||||
|
||||
const statusMapper = (status: number): { text: string; color: string } => {
|
||||
switch (status) {
|
||||
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(() => {
|
||||
fetchData();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user