前端代码

This commit is contained in:
2026-08-13 22:35:13 +08:00
parent 9044bafdff
commit f1a0b17bb0
412 changed files with 36769 additions and 6617 deletions
+294
View File
@@ -0,0 +1,294 @@
<template>
<div
id="userLayout"
class="relative h-full w-full bg-white dark:bg-slate-900 md:h-screen"
>
<!-- 右侧蓝色斜切 banner(仅桌面端显示) -->
<div
class="banner-oblique absolute inset-y-0 right-0 hidden w-[56%] overflow-hidden bg-[#2264f2] md:block"
>
<img
class="absolute right-0 top-0 h-full w-auto max-w-none"
src="@/assets/cover-redesign.svg"
alt="banner"
/>
</div>
<!-- 左侧登录区 -->
<div
class="relative z-10 flex h-full w-full items-center justify-center md:w-1/2"
>
<div class="w-4/5 md:w-96">
<EntryBrand class="mb-9" />
<el-form
ref="loginForm"
:model="loginFormData"
:rules="rules"
:validate-on-rule-change="false"
@keyup.enter="submitForm"
>
<el-form-item prop="username" class="mb-6">
<el-input
v-model="loginFormData.username"
size="large"
placeholder="请输入用户名"
suffix-icon="user"
/>
</el-form-item>
<el-form-item prop="password" class="mb-6">
<el-input
v-model="loginFormData.password"
show-password
size="large"
type="password"
placeholder="请输入密码"
/>
</el-form-item>
<el-form-item
v-if="loginFormData.openCaptcha"
prop="captcha"
class="mb-6"
>
<div class="flex w-full justify-between">
<el-input
v-model="loginFormData.captcha"
placeholder="请输入验证码"
size="large"
class="flex-1 mr-5"
/>
<div class="w-1/3 h-11 bg-[#c3d4f2] rounded">
<img
v-if="picPath"
class="w-full h-full"
:src="picPath"
alt="请输入验证码"
@click="loginVerify()"
/>
</div>
</div>
</el-form-item>
<el-form-item class="mb-6">
<el-button
class="btn-primary shadow shadow-active h-11 w-full"
type="primary"
size="large"
@click="submitForm"
>登 录</el-button
>
</el-form-item>
<el-form-item v-if="isDev" class="mb-6">
<el-button
class="btn-hollow h-11 w-full"
type="primary"
plain
size="large"
@click="checkInit"
>前往初始化</el-button
>
</el-form-item>
</el-form>
<BottomInfo class="login-footer" style="padding-top: 14px" />
</div>
</div>
</div>
</template>
<script setup>
import { captcha } from '@/api/user'
import { checkDB } from '@/api/initdb'
import BottomInfo from '@/components/bottomInfo/bottomInfo.vue'
import { reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { useRouter } from 'vue-router'
import { useUserStore } from '@/pinia/modules/user'
import EntryBrand from '@/components/entryBrand/index.vue'
import { isDev } from '@/utils/env.js'
defineOptions({
name: 'Login'
})
const router = useRouter()
const captchaRequiredLength = ref(6)
// 验证函数
const checkUsername = (rule, value, callback) => {
if (value.length < 5) {
return callback(new Error('请输入正确的用户名'))
} else {
callback()
}
}
const checkPassword = (rule, value, callback) => {
if (value.length < 6) {
return callback(new Error('请输入正确的密码'))
} else {
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()
captchaRequiredLength.value = Number(ele.data?.captchaLength) || 0
picPath.value = ele.data?.picPath
loginFormData.captchaId = ele.data?.captchaId
loginFormData.openCaptcha = ele.data?.openCaptcha
}
loginVerify()
// 登录相关操作
const loginForm = ref(null)
const picPath = ref('')
const loginFormData = reactive({
username: 'admin',
password: '',
captcha: '',
captchaId: '',
openCaptcha: false
})
const rules = reactive({
username: [{ validator: checkUsername, trigger: 'blur' }],
password: [{ validator: checkPassword, trigger: 'blur' }],
captcha: [{ validator: checkCaptcha, trigger: 'blur' }]
})
const userStore = useUserStore()
const login = async () => {
return await userStore.LoginIn(loginFormData)
}
const submitForm = () => {
loginForm.value.validate(async (v) => {
if (!v) {
// 未通过前端静态验证
ElMessage({
type: 'error',
message: '请正确填写登录信息',
showClose: true
})
return false
}
// 通过验证,请求登陆
const flag = await login()
// 登陆失败,刷新验证码
if (!flag) {
await loginVerify()
return false
}
// 登陆成功
return true
})
}
// 跳转初始化
const checkInit = async () => {
const res = await checkDB()
if (res.code === 0) {
if (res.data?.needInit) {
userStore.NeedInit()
await router.push({ name: 'Init' })
} else {
ElMessage({
type: 'info',
message: '已配置数据库信息,无法初始化'
})
}
}
}
</script>
<style scoped>
/* 右侧蓝色面板的左边缘斜切:顶部靠右、底部靠左,蓝色区上窄下宽 */
.banner-oblique {
clip-path: polygon(22% 0, 100% 0, 100% 100%, 4% 100%);
}
/* 镂空按钮:常态透明底;hover 用浅色底 + 主色文字表现(不再变白字实心) */
.btn-hollow {
--el-button-bg-color: transparent;
--el-button-hover-bg-color: var(--el-color-primary-light-9);
--el-button-hover-text-color: var(--el-color-primary);
--el-button-hover-border-color: var(--el-color-primary);
--el-button-active-bg-color: var(--el-color-primary-light-8);
--el-button-active-text-color: var(--el-color-primary);
--el-button-active-border-color: var(--el-color-primary);
}
/* 登录主按钮:主题色 #2264f2,边框与背景一致;
hover / active 沿用 Element Plus 的深浅关系(更浅 / 更深) */
.btn-primary {
--el-button-bg-color: #2264f2;
--el-button-border-color: #2264f2;
--el-button-hover-bg-color: #6493f6;
--el-button-hover-border-color: #6493f6;
--el-button-active-bg-color: #1b50c2;
--el-button-active-border-color: #1b50c2;
}
/* 登录页页脚仅作用于入口页。 */
.login-footer {
flex-direction: column !important;
/* 两行文字统一字号:常规屏 11px(!important 覆盖组件自带的 text-sm),
2K 屏放大到 14px。图标尺寸单独设置(见 .footer-icon)。 */
font-size: 11px !important;
}
/* 2K 及以上大屏(≥2560px):两行文字放大到 14px */
@media (min-width: 2560px) {
.login-footer {
font-size: 14px !important;
}
}
/* 页脚信息统一置灰 */
.login-footer :deep(.text-center) {
color: #94a3b8;
}
/* 两行文字里的链接:默认继承置灰色、常规字重 */
.login-footer :deep(.text-center a) {
color: inherit;
font-weight: 400;
}
/* 第一行 Kra 链接:使用主题色 #2264f2 */
.login-footer :deep(.text-center:first-child a) {
color: #2264f2;
}
/* 第二行图标:常规屏 16px,2K 屏 22px(与文字字号相互独立) */
.footer-icon {
width: 16px;
height: 16px;
}
@media (min-width: 2560px) {
.footer-icon {
width: 22px;
height: 22px;
}
}
</style>