Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 22 additions & 22 deletions web/src/components/errorPreview/index.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<template>
<div
class="fixed inset-0 bg-black/40 flex items-center justify-center z-[999]"
class="fixed inset-0 bg-black/40 dark:bg-black/60 flex items-center justify-center z-[999]"
@click.self="closeModal"
>
<div class="bg-white rounded-xl shadow-dialog w-full max-w-md mx-4 transform transition-all duration-300 ease-in-out">
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-dialog dark:shadow-lg w-full max-w-md mx-4 transform transition-all duration-300 ease-in-out border border-transparent dark:border-gray-700">
<!-- 弹窗头部 -->
<div class="p-5 border-b border-gray-100 flex justify-between items-center">
<h3 class="text-lg font-semibold text-gray-800">{{ displayData.title }}</h3>
<div class="text-gray-400 hover:text-gray-600 transition-colors cursor-pointer" @click="closeModal">
<div class="p-5 border-b border-gray-100 dark:border-gray-700 flex justify-between items-center">
<h3 class="text-lg font-semibold text-gray-800 dark:text-gray-100">{{ displayData.title }}</h3>
<div class="text-gray-400 dark:text-gray-300 hover:text-gray-600 dark:hover:text-gray-200 transition-colors cursor-pointer" @click="closeModal">
<close class="h-6 w-6" />
</div>
</div>
Expand All @@ -16,36 +16,36 @@
<div class="p-6 pt-0">
<!-- 错误类型 -->
<div class="mb-4">
<div class="text-xs font-medium text-gray-500 uppercase mb-2">错误类型</div>
<div class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase mb-2">错误类型</div>
<div class="flex items-center gap-2">
<lock v-if="displayData.icon === 'lock'" class="text-red-500 w-5 h-5" />
<warn v-if="displayData.icon === 'warn'" class="text-red-500 w-5 h-5" />
<server v-if="displayData.icon === 'server'" class="text-red-500 w-5 h-5" />
<span class="font-medium text-gray-800">{{ displayData.type }}</span>
<lock v-if="displayData.icon === 'lock'" :class="['w-5 h-5', displayData.color]" />
<warn v-if="displayData.icon === 'warn'" :class="['w-5 h-5', displayData.color]" />
<server v-if="displayData.icon === 'server'" :class="['w-5 h-5', displayData.color]" />
<span class="font-medium text-gray-800 dark:text-gray-100">{{ displayData.type }}</span>
</div>
</div>

<!-- 具体错误 -->
<div class="mb-6">
<div class="text-xs font-medium text-gray-500 uppercase mb-2">具体错误</div>
<div class="bg-gray-100 rounded-lg p-3 text-sm text-gray-700 leading-relaxed">
<div class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase mb-2">具体错误</div>
<div class="bg-gray-100 dark:bg-gray-900/40 rounded-lg p-3 text-sm text-gray-700 dark:text-gray-200 leading-relaxed">
{{ displayData.message }}
</div>
</div>

<!-- 提示信息 -->
<div v-if="displayData.tips">
<div class="text-xs font-medium text-gray-500 uppercase mb-2">提示</div>
<div class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase mb-2">提示</div>
<div class="flex items-center gap-2">
<idea class="text-blue-500 w-5 h-5" />
<p class="text-sm text-gray-600">{{ displayData.tips }}</p>
<idea class="text-blue-500 dark:text-blue-400 w-5 h-5" />
<p class="text-sm text-gray-600 dark:text-gray-300">{{ displayData.tips }}</p>
</div>
</div>
</div>

<!-- 弹窗底部 -->
<div class="py-2 px-4 border-t border-gray-100 flex justify-end">
<div class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors font-medium text-sm shadow-sm cursor-pointer" @click="handleConfirm">
<div class="py-2 px-4 border-t border-gray-100 dark:border-gray-700 flex justify-end">
<div class="px-4 py-2 bg-blue-600 dark:bg-blue-500 text-white dark:text-gray-100 rounded-lg hover:bg-blue-700 dark:hover:bg-blue-600 transition-colors font-medium text-sm shadow-sm cursor-pointer" @click="handleConfirm">
确定
</div>
</div>
Expand All @@ -70,28 +70,28 @@ const presetErrors = {
title: '检测到接口错误',
type: '服务器发生内部错误',
icon: 'server',
color: 'text-red-500',
color: 'text-red-500 dark:text-red-400',
tips: '此类错误内容常见于后台panic,请先查看后台日志,如果影响您正常使用可强制登出清理缓存'
},
404: {
title: '资源未找到',
type: 'Not Found',
icon: 'warn',
color: 'text-orange-500',
color: 'text-orange-500 dark:text-orange-400',
tips: '此类错误多为接口未注册(或未重启)或者请求路径(方法)与api路径(方法)不符--如果为自动化代码请检查是否存在空格'
},
401: {
title: '身份认证失败',
type: '身份令牌无效',
icon: 'lock',
color: 'text-purple-500',
color: 'text-purple-500 dark:text-purple-400',
tips: '您的身份认证已过期或无效,请重新登录。'
},
'network': {
title: '网络错误',
type: 'Network Error',
icon: 'fa-wifi-slash',
color: 'text-gray-500',
color: 'text-gray-500 dark:text-gray-400',
tips: '无法连接到服务器,请检查您的网络连接。'
}
};
Expand All @@ -109,7 +109,7 @@ const displayData = computed(() => {
title: '未知错误',
type: '检测到请求错误',
icon: 'fa-question-circle',
color: 'text-gray-400',
color: 'text-gray-400 dark:text-gray-300',
message: props.errorData.message || '发生了一个未知错误。',
tips: '请检查控制台获取更多信息。'
};
Expand Down
3 changes: 2 additions & 1 deletion web/src/utils/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ service.interceptors.request.use(
)

function getErrorMessage(error) {
return error.response?.data?.msg || '请求失败'
// 优先级: 响应体中的 msg > statusText > 默认消息
return error.response?.data?.msg || error.response?.statusText || '请求失败'
}

// http response 拦截器
Expand Down
44 changes: 28 additions & 16 deletions web/src/view/login/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@
})

const router = useRouter()
const captchaRequiredLength = ref(6)
// 验证函数
const checkUsername = (rule, value, callback) => {
if (value.length < 5) {
Expand All @@ -153,19 +154,36 @@
callback()
}
}
const checkCaptcha = (rule, value, callback) => {
if (!loginFormData.openCaptcha) {
return callback()
}
const sanitizedValue = (value || '').replace(/\s+/g, '')
if (!sanitizedValue) {
return callback(new Error('请输入验证码'))
}
if (!/^\d+$/.test(sanitizedValue)) {
return callback(new Error('验证码须为数字'))
}
if (sanitizedValue.length < captchaRequiredLength.value) {
return callback(
new Error(`请输入至少${captchaRequiredLength.value}位数字验证码`)
)
}
if (sanitizedValue !== value) {
loginFormData.captcha = sanitizedValue
}
callback()
}

// 获取验证码
const loginVerify = async () => {
const ele = await captcha()
rules.captcha.push({
max: ele.data.captchaLength,
min: ele.data.captchaLength,
message: `请输入${ele.data.captchaLength}位验证码`,
trigger: 'blur'
})
picPath.value = ele.data.picPath
loginFormData.captchaId = ele.data.captchaId
loginFormData.openCaptcha = ele.data.openCaptcha
const lengthFromServer = Number(ele.data?.captchaLength) || 0
captchaRequiredLength.value = Math.max(6, lengthFromServer)
picPath.value = ele.data?.picPath
loginFormData.captchaId = ele.data?.captchaId
loginFormData.openCaptcha = ele.data?.openCaptcha
}
loginVerify()

Expand All @@ -182,12 +200,7 @@
const rules = reactive({
username: [{ validator: checkUsername, trigger: 'blur' }],
password: [{ validator: checkPassword, trigger: 'blur' }],
captcha: [
{
message: '验证码格式不正确',
trigger: 'blur'
}
]
captcha: [{ validator: checkCaptcha, trigger: 'blur' }]
})

const userStore = useUserStore()
Expand All @@ -203,7 +216,6 @@
message: '请正确填写登录信息',
showClose: true
})
await loginVerify()
return false
}

Expand Down