|
|
@@ -1,379 +1,361 @@
|
|
|
<template>
|
|
|
- <div class="client-login">
|
|
|
- <div class="client-login__panel">
|
|
|
- <h1 class="client-login__title">用户登录</h1>
|
|
|
- <p class="client-login__subtitle">探索并购买前沿AI工具,赋能您的效率与创意</p>
|
|
|
- <el-form ref="loginFormRef" :model="loginForm" :rules="loginRules" class="client-login__form" @submit.prevent>
|
|
|
- <el-form-item prop="username">
|
|
|
- <el-input
|
|
|
- v-model="loginForm.username"
|
|
|
- placeholder="请输入账号"
|
|
|
- size="large"
|
|
|
- clearable
|
|
|
- >
|
|
|
- <template #prefix>
|
|
|
- <Icon name="fa fa-user" size="16" color="var(--el-input-icon-color)" />
|
|
|
- </template>
|
|
|
- </el-input>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item prop="password">
|
|
|
- <el-input
|
|
|
- v-model="loginForm.password"
|
|
|
- type="password"
|
|
|
- placeholder="请输入密码"
|
|
|
- size="large"
|
|
|
- show-password
|
|
|
- @keyup.enter="handleLogin"
|
|
|
- >
|
|
|
- <template #prefix>
|
|
|
- <Icon name="fa fa-unlock-alt" size="16" color="var(--el-input-icon-color)" />
|
|
|
- </template>
|
|
|
- </el-input>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item>
|
|
|
- <el-button
|
|
|
- :loading="loading"
|
|
|
- type="primary"
|
|
|
- class="client-login__button"
|
|
|
- size="large"
|
|
|
- round
|
|
|
- @click="handleLogin"
|
|
|
- >
|
|
|
- 登录
|
|
|
- </el-button>
|
|
|
- </el-form-item>
|
|
|
- </el-form>
|
|
|
- <div class="client-login__links">
|
|
|
- <router-link to="/user/reset">
|
|
|
- 忘记密码?
|
|
|
- </router-link>
|
|
|
- </div>
|
|
|
- <div class="client-login__actions">
|
|
|
- <span>还没有账号?</span>
|
|
|
- <router-link to="/user/register">立即注册</router-link>
|
|
|
- </div>
|
|
|
+ <div class="client-login">
|
|
|
+ <div class="client-login__panel">
|
|
|
+ <h1 class="client-login__title">用户登录</h1>
|
|
|
+ <p class="client-login__subtitle">探索并购买前沿AI工具,赋能您的效率与创意</p>
|
|
|
+ <el-form ref="loginFormRef" :model="loginForm" :rules="loginRules" class="client-login__form" @submit.prevent>
|
|
|
+ <el-form-item prop="username">
|
|
|
+ <el-input v-model="loginForm.username" placeholder="请输入邮箱" size="large" clearable>
|
|
|
+ <template #prefix>
|
|
|
+ <Icon name="fa fa-user" size="16" color="var(--el-input-icon-color)" />
|
|
|
+ </template>
|
|
|
+ </el-input>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item prop="password">
|
|
|
+ <el-input v-model="loginForm.password" type="password" placeholder="请输入密码" size="large" show-password
|
|
|
+ @keyup.enter="handleLogin">
|
|
|
+ <template #prefix>
|
|
|
+ <Icon name="fa fa-unlock-alt" size="16" color="var(--el-input-icon-color)" />
|
|
|
+ </template>
|
|
|
+ </el-input>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button :loading="loading" type="primary" class="client-login__button" size="large" round
|
|
|
+ @click="handleLogin">
|
|
|
+ 登录
|
|
|
+ </el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <div class="client-login__links">
|
|
|
+ <router-link to="/user/reset">
|
|
|
+ 忘记密码?
|
|
|
+ </router-link>
|
|
|
+ </div>
|
|
|
+ <div class="client-login__actions">
|
|
|
+ <span>还没有账号?</span>
|
|
|
+ <router-link to="/user/register">立即注册</router-link>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </template>
|
|
|
-
|
|
|
- <script setup>
|
|
|
- import { ref, reactive, watch, onMounted } from 'vue'
|
|
|
- import { useRouter, useRoute } from 'vue-router'
|
|
|
- import { useUserInfo } from '/@/stores/userInfo'
|
|
|
- import { checkIn } from '/@/api/frontend/user/index'
|
|
|
- import { uuid } from '/@/utils/random'
|
|
|
- import clickCaptcha from '/@/components/clickCaptcha'
|
|
|
- import Icon from '/@/components/icon/index.vue'
|
|
|
-
|
|
|
- defineOptions({
|
|
|
- name: 'ClientLogin'
|
|
|
- })
|
|
|
-
|
|
|
- const router = useRouter()
|
|
|
- const route = useRoute()
|
|
|
- const userInfo = useUserInfo()
|
|
|
-
|
|
|
- const loginFormRef = ref(null)
|
|
|
- const loading = ref(false)
|
|
|
- const redirect = ref(undefined)
|
|
|
- const otherQuery = ref({})
|
|
|
-
|
|
|
- // 验证码开关
|
|
|
- const userLoginCaptchaSwitch = ref(true)
|
|
|
-
|
|
|
- // 登录成功后要跳转的URL
|
|
|
- const to = ref(Array.isArray(route.query.to) ? route.query.to[0] : (route.query.to || ''))
|
|
|
-
|
|
|
- const loginForm = reactive({
|
|
|
- username: '',
|
|
|
- password: '',
|
|
|
- keep: false,
|
|
|
- captchaId: uuid(),
|
|
|
- captchaInfo: ''
|
|
|
- })
|
|
|
-
|
|
|
- const validateUsername = (rule, value, callback) => {
|
|
|
- if (!value) {
|
|
|
- callback(new Error('请输入账号'))
|
|
|
- } else {
|
|
|
- callback()
|
|
|
- }
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { ref, reactive, watch, onMounted } from 'vue'
|
|
|
+import { useRouter, useRoute } from 'vue-router'
|
|
|
+import { useUserInfo } from '/@/stores/userInfo'
|
|
|
+import { checkIn } from '/@/api/frontend/user/index'
|
|
|
+import { uuid } from '/@/utils/random'
|
|
|
+import clickCaptcha from '/@/components/clickCaptcha'
|
|
|
+import Icon from '/@/components/icon/index.vue'
|
|
|
+
|
|
|
+defineOptions({
|
|
|
+ name: 'ClientLogin'
|
|
|
+})
|
|
|
+
|
|
|
+const router = useRouter()
|
|
|
+const route = useRoute()
|
|
|
+const userInfo = useUserInfo()
|
|
|
+
|
|
|
+const loginFormRef = ref(null)
|
|
|
+const loading = ref(false)
|
|
|
+const redirect = ref(undefined)
|
|
|
+const otherQuery = ref({})
|
|
|
+
|
|
|
+// 验证码开关
|
|
|
+const userLoginCaptchaSwitch = ref(true)
|
|
|
+
|
|
|
+// 登录成功后要跳转的URL
|
|
|
+const to = ref(Array.isArray(route.query.to) ? route.query.to[0] : (route.query.to || ''))
|
|
|
+
|
|
|
+const loginForm = reactive({
|
|
|
+ username: '',
|
|
|
+ password: '',
|
|
|
+ keep: false,
|
|
|
+ captchaId: uuid(),
|
|
|
+ captchaInfo: ''
|
|
|
+})
|
|
|
+
|
|
|
+const validateUsername = (rule, value, callback) => {
|
|
|
+ if (!value) {
|
|
|
+ callback(new Error('请输入邮箱'))
|
|
|
+ } else {
|
|
|
+ callback()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const validatePassword = (rule, value, callback) => {
|
|
|
+ if (!value) {
|
|
|
+ callback(new Error('请输入密码'))
|
|
|
+ } else if (value.length < 6) {
|
|
|
+ callback(new Error('密码长度不能小于6位'))
|
|
|
+ } else {
|
|
|
+ callback()
|
|
|
}
|
|
|
-
|
|
|
- const validatePassword = (rule, value, callback) => {
|
|
|
- if (!value) {
|
|
|
- callback(new Error('请输入密码'))
|
|
|
- } else if (value.length < 6) {
|
|
|
- callback(new Error('密码长度不能小于6位'))
|
|
|
+}
|
|
|
+
|
|
|
+const loginRules = reactive({
|
|
|
+ username: [{ required: true, trigger: 'blur', validator: validateUsername }],
|
|
|
+ password: [{ required: true, trigger: 'blur', validator: validatePassword }]
|
|
|
+})
|
|
|
+
|
|
|
+const getOtherQuery = (query) => {
|
|
|
+ return Object.keys(query).reduce((acc, cur) => {
|
|
|
+ if (cur !== 'redirect' && cur !== 'to') {
|
|
|
+ acc[cur] = query[cur]
|
|
|
+ }
|
|
|
+ return acc
|
|
|
+ }, {})
|
|
|
+}
|
|
|
+
|
|
|
+// 提交登录前的预处理(验证码检查)
|
|
|
+const handleLogin = () => {
|
|
|
+ if (!loginFormRef.value) return
|
|
|
+
|
|
|
+ loginFormRef.value.validate((valid) => {
|
|
|
+ if (!valid) return
|
|
|
+
|
|
|
+ // 如果启用了验证码,先进行验证码验证
|
|
|
+ if (userLoginCaptchaSwitch.value) {
|
|
|
+ clickCaptcha(loginForm.captchaId, (captchaInfo) => {
|
|
|
+ onSubmit(captchaInfo)
|
|
|
+ })
|
|
|
} else {
|
|
|
- callback()
|
|
|
+ onSubmit()
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- const loginRules = reactive({
|
|
|
- username: [{ required: true, trigger: 'blur', validator: validateUsername }],
|
|
|
- password: [{ required: true, trigger: 'blur', validator: validatePassword }]
|
|
|
})
|
|
|
-
|
|
|
- const getOtherQuery = (query) => {
|
|
|
- return Object.keys(query).reduce((acc, cur) => {
|
|
|
- if (cur !== 'redirect' && cur !== 'to') {
|
|
|
- acc[cur] = query[cur]
|
|
|
- }
|
|
|
- return acc
|
|
|
- }, {})
|
|
|
- }
|
|
|
-
|
|
|
- // 提交登录前的预处理(验证码检查)
|
|
|
- const handleLogin = () => {
|
|
|
- if (!loginFormRef.value) return
|
|
|
-
|
|
|
- loginFormRef.value.validate((valid) => {
|
|
|
- if (!valid) return
|
|
|
-
|
|
|
- // 如果启用了验证码,先进行验证码验证
|
|
|
- if (userLoginCaptchaSwitch.value) {
|
|
|
- clickCaptcha(loginForm.captchaId, (captchaInfo) => {
|
|
|
- onSubmit(captchaInfo)
|
|
|
- })
|
|
|
- } else {
|
|
|
- onSubmit()
|
|
|
- }
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- // 提交登录
|
|
|
- const onSubmit = (captchaInfo = '') => {
|
|
|
- loading.value = true
|
|
|
- loginForm.captchaInfo = captchaInfo
|
|
|
-
|
|
|
- checkIn('post', {
|
|
|
- tab: 'login',
|
|
|
- username: loginForm.username,
|
|
|
- password: loginForm.password,
|
|
|
- keep: loginForm.keep,
|
|
|
- captchaId: loginForm.captchaId,
|
|
|
- captchaInfo: loginForm.captchaInfo
|
|
|
- }).then((res) => {
|
|
|
- // 登录成功,更新用户信息
|
|
|
- if (res.data && res.data.userInfo) {
|
|
|
- userInfo.dataFill(res.data.userInfo, false)
|
|
|
- }
|
|
|
-
|
|
|
- // 如果有指定的跳转URL,则跳转
|
|
|
- if (to.value) {
|
|
|
- location.href = to.value
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- // 使用返回的routePath或redirect指定的页面
|
|
|
- router.push({
|
|
|
- path: res.data?.routePath || redirect.value || '/user/home',
|
|
|
- query: otherQuery.value
|
|
|
- })
|
|
|
- }).catch(() => {
|
|
|
- // 登录失败,错误信息由 axios 拦截器处理
|
|
|
- // 如果验证失败,重新生成验证码ID
|
|
|
- if (userLoginCaptchaSwitch.value) {
|
|
|
- loginForm.captchaId = uuid()
|
|
|
- }
|
|
|
- }).finally(() => {
|
|
|
- loading.value = false
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- // 监听路由变化
|
|
|
- watch(
|
|
|
- () => route.query,
|
|
|
- (query) => {
|
|
|
- if (query) {
|
|
|
- redirect.value = query.redirect
|
|
|
- to.value = Array.isArray(query.to) ? query.to[0] : (query.to || '')
|
|
|
- otherQuery.value = getOtherQuery(query)
|
|
|
- }
|
|
|
- },
|
|
|
- { immediate: true }
|
|
|
- )
|
|
|
-
|
|
|
- // 组件挂载时获取登录配置
|
|
|
- onMounted(() => {
|
|
|
- checkIn('get').then((res) => {
|
|
|
- if (res.data) {
|
|
|
- userLoginCaptchaSwitch.value = res.data.userLoginCaptchaSwitch !== undefined
|
|
|
- ? res.data.userLoginCaptchaSwitch
|
|
|
- : true
|
|
|
- }
|
|
|
+}
|
|
|
+
|
|
|
+// 提交登录
|
|
|
+const onSubmit = (captchaInfo = '') => {
|
|
|
+ loading.value = true
|
|
|
+ loginForm.captchaInfo = captchaInfo
|
|
|
+
|
|
|
+ checkIn('post', {
|
|
|
+ tab: 'login',
|
|
|
+ username: loginForm.username,
|
|
|
+ password: loginForm.password,
|
|
|
+ keep: loginForm.keep,
|
|
|
+ captchaId: loginForm.captchaId,
|
|
|
+ captchaInfo: loginForm.captchaInfo
|
|
|
+ }).then((res) => {
|
|
|
+ // 登录成功,更新用户信息
|
|
|
+ if (res.data && res.data.userInfo) {
|
|
|
+ userInfo.dataFill(res.data.userInfo, false)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果有指定的跳转URL,则跳转
|
|
|
+ if (to.value) {
|
|
|
+ location.href = to.value
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 使用返回的routePath或redirect指定的页面
|
|
|
+ router.push({
|
|
|
+ path: res.data?.routePath || redirect.value || '/',
|
|
|
+ query: otherQuery.value
|
|
|
})
|
|
|
+ }).catch(() => {
|
|
|
+ // 登录失败,错误信息由 axios 拦截器处理
|
|
|
+ // 如果验证失败,重新生成验证码ID
|
|
|
+ if (userLoginCaptchaSwitch.value) {
|
|
|
+ loginForm.captchaId = uuid()
|
|
|
+ }
|
|
|
+ }).finally(() => {
|
|
|
+ loading.value = false
|
|
|
})
|
|
|
- </script>
|
|
|
-
|
|
|
- <style scoped>
|
|
|
- .client-login {
|
|
|
- min-height: 100vh;
|
|
|
- display: flex;
|
|
|
- justify-content: center;
|
|
|
- align-items: center;
|
|
|
- background: url('/@/assets/images/login_bg.jpg') center center / cover no-repeat;
|
|
|
- position: relative;
|
|
|
- }
|
|
|
-
|
|
|
- .client-login::before {
|
|
|
- content: '';
|
|
|
- position: absolute;
|
|
|
- top: 0;
|
|
|
- left: 0;
|
|
|
- right: 0;
|
|
|
- bottom: 0;
|
|
|
- background: rgba(255, 255, 255, 0.1);
|
|
|
- backdrop-filter: blur(2px);
|
|
|
- }
|
|
|
-
|
|
|
+}
|
|
|
+
|
|
|
+// 监听路由变化
|
|
|
+watch(
|
|
|
+ () => route.query,
|
|
|
+ (query) => {
|
|
|
+ if (query) {
|
|
|
+ redirect.value = query.redirect
|
|
|
+ to.value = Array.isArray(query.to) ? query.to[0] : (query.to || '')
|
|
|
+ otherQuery.value = getOtherQuery(query)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ { immediate: true }
|
|
|
+)
|
|
|
+
|
|
|
+// 组件挂载时获取登录配置
|
|
|
+onMounted(() => {
|
|
|
+ checkIn('get').then((res) => {
|
|
|
+ if (res.data) {
|
|
|
+ userLoginCaptchaSwitch.value = res.data.userLoginCaptchaSwitch !== undefined
|
|
|
+ ? res.data.userLoginCaptchaSwitch
|
|
|
+ : true
|
|
|
+ }
|
|
|
+ })
|
|
|
+})
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.client-login {
|
|
|
+ min-height: 100vh;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ background: url('/@/assets/images/login_bg.jpg') center center / cover no-repeat;
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
+
|
|
|
+.client-login::before {
|
|
|
+ content: '';
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ bottom: 0;
|
|
|
+ background: rgba(255, 255, 255, 0.1);
|
|
|
+ backdrop-filter: blur(2px);
|
|
|
+}
|
|
|
+
|
|
|
+.client-login__panel {
|
|
|
+ width: 420px;
|
|
|
+ padding: 48px 40px 40px;
|
|
|
+ border-radius: 16px;
|
|
|
+ background: rgba(255, 255, 255, 0.98);
|
|
|
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
|
|
|
+ backdrop-filter: blur(10px);
|
|
|
+ position: relative;
|
|
|
+ z-index: 1;
|
|
|
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.client-login__panel:hover {
|
|
|
+ transform: translateY(-2px);
|
|
|
+ box-shadow: 0 16px 48px rgba(0, 0, 0, 0.2);
|
|
|
+}
|
|
|
+
|
|
|
+.client-login__title {
|
|
|
+ margin: 0 0 8px 0;
|
|
|
+ font-size: 28px;
|
|
|
+ font-weight: 700;
|
|
|
+ text-align: center;
|
|
|
+ background: linear-gradient(135deg, #409eff 0%, #638fee 100%);
|
|
|
+ -webkit-background-clip: text;
|
|
|
+ -webkit-text-fill-color: transparent;
|
|
|
+ background-clip: text;
|
|
|
+ letter-spacing: 0.5px;
|
|
|
+}
|
|
|
+
|
|
|
+.client-login__subtitle {
|
|
|
+ margin: 0 0 32px 0;
|
|
|
+ font-size: 14px;
|
|
|
+ text-align: center;
|
|
|
+ color: #909399;
|
|
|
+ line-height: 1.5;
|
|
|
+}
|
|
|
+
|
|
|
+.client-login__form {
|
|
|
+ margin-top: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.client-login__form :deep(.el-form-item) {
|
|
|
+ margin-bottom: 24px;
|
|
|
+}
|
|
|
+
|
|
|
+.client-login__form :deep(.el-input__wrapper) {
|
|
|
+ border-radius: 8px;
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
|
|
+ transition: all 0.3s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.client-login__form :deep(.el-input__wrapper:hover) {
|
|
|
+ box-shadow: 0 4px 12px rgba(64, 158, 255, 0.15);
|
|
|
+}
|
|
|
+
|
|
|
+.client-login__form :deep(.el-input.is-focus .el-input__wrapper) {
|
|
|
+ box-shadow: 0 4px 12px rgba(64, 158, 255, 0.25);
|
|
|
+}
|
|
|
+
|
|
|
+.client-login__form :deep(.el-input__prefix) {
|
|
|
+ padding-left: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.client-login__actions {
|
|
|
+ margin-top: 20px;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #606266;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+
|
|
|
+.client-login__actions a {
|
|
|
+ color: #409eff;
|
|
|
+ margin-left: 6px;
|
|
|
+ text-decoration: none;
|
|
|
+ transition: color 0.2s;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.client-login__actions a:hover {
|
|
|
+ color: #66b1ff;
|
|
|
+}
|
|
|
+
|
|
|
+.client-login__button {
|
|
|
+ width: 100%;
|
|
|
+ height: 44px;
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 600;
|
|
|
+ letter-spacing: 1px;
|
|
|
+ background: #409eff;
|
|
|
+ border: none;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
|
|
|
+}
|
|
|
+
|
|
|
+.client-login__button:hover {
|
|
|
+ transform: translateY(-2px);
|
|
|
+ box-shadow: 0 6px 16px rgba(64, 158, 255, 0.4);
|
|
|
+}
|
|
|
+
|
|
|
+.client-login__button:active {
|
|
|
+ transform: translateY(0);
|
|
|
+}
|
|
|
+
|
|
|
+.client-login__links {
|
|
|
+ margin-top: 8px;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ gap: 6px;
|
|
|
+ font-size: 13px;
|
|
|
+ color: #909399;
|
|
|
+}
|
|
|
+
|
|
|
+.client-login__links a {
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 4px;
|
|
|
+ color: #409eff;
|
|
|
+ text-decoration: none;
|
|
|
+ transition: all 0.2s;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.client-login__links a:hover {
|
|
|
+ color: #66b1ff;
|
|
|
+ transform: translateX(2px);
|
|
|
+}
|
|
|
+
|
|
|
+.client-login__tips {
|
|
|
+ margin-top: 12px;
|
|
|
+ font-size: 12px;
|
|
|
+ color: #909399;
|
|
|
+ line-height: 1.6;
|
|
|
+}
|
|
|
+
|
|
|
+/* 响应式设计 */
|
|
|
+@media (max-width: 768px) {
|
|
|
.client-login__panel {
|
|
|
- width: 420px;
|
|
|
- padding: 48px 40px 40px;
|
|
|
- border-radius: 16px;
|
|
|
- background: rgba(255, 255, 255, 0.98);
|
|
|
- box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
|
|
|
- backdrop-filter: blur(10px);
|
|
|
- position: relative;
|
|
|
- z-index: 1;
|
|
|
- transition: transform 0.3s ease, box-shadow 0.3s ease;
|
|
|
- }
|
|
|
-
|
|
|
- .client-login__panel:hover {
|
|
|
- transform: translateY(-2px);
|
|
|
- box-shadow: 0 16px 48px rgba(0, 0, 0, 0.2);
|
|
|
+ width: 90%;
|
|
|
+ max-width: 420px;
|
|
|
+ padding: 36px 28px 32px;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
.client-login__title {
|
|
|
- margin: 0 0 8px 0;
|
|
|
- font-size: 28px;
|
|
|
- font-weight: 700;
|
|
|
- text-align: center;
|
|
|
- background: linear-gradient(135deg, #409eff 0%, #638fee 100%);
|
|
|
- -webkit-background-clip: text;
|
|
|
- -webkit-text-fill-color: transparent;
|
|
|
- background-clip: text;
|
|
|
- letter-spacing: 0.5px;
|
|
|
- }
|
|
|
-
|
|
|
- .client-login__subtitle {
|
|
|
- margin: 0 0 32px 0;
|
|
|
- font-size: 14px;
|
|
|
- text-align: center;
|
|
|
- color: #909399;
|
|
|
- line-height: 1.5;
|
|
|
- }
|
|
|
-
|
|
|
- .client-login__form {
|
|
|
- margin-top: 8px;
|
|
|
- }
|
|
|
-
|
|
|
- .client-login__form :deep(.el-form-item) {
|
|
|
- margin-bottom: 24px;
|
|
|
- }
|
|
|
-
|
|
|
- .client-login__form :deep(.el-input__wrapper) {
|
|
|
- border-radius: 8px;
|
|
|
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
|
|
- transition: all 0.3s ease;
|
|
|
- }
|
|
|
-
|
|
|
- .client-login__form :deep(.el-input__wrapper:hover) {
|
|
|
- box-shadow: 0 4px 12px rgba(64, 158, 255, 0.15);
|
|
|
- }
|
|
|
-
|
|
|
- .client-login__form :deep(.el-input.is-focus .el-input__wrapper) {
|
|
|
- box-shadow: 0 4px 12px rgba(64, 158, 255, 0.25);
|
|
|
- }
|
|
|
-
|
|
|
- .client-login__form :deep(.el-input__prefix) {
|
|
|
- padding-left: 12px;
|
|
|
- }
|
|
|
-
|
|
|
- .client-login__actions {
|
|
|
- margin-top: 20px;
|
|
|
- font-size: 14px;
|
|
|
- color: #606266;
|
|
|
- text-align: center;
|
|
|
- }
|
|
|
-
|
|
|
- .client-login__actions a {
|
|
|
- color: #409eff;
|
|
|
- margin-left: 6px;
|
|
|
- text-decoration: none;
|
|
|
- transition: color 0.2s;
|
|
|
- font-weight: 500;
|
|
|
- }
|
|
|
-
|
|
|
- .client-login__actions a:hover {
|
|
|
- color: #66b1ff;
|
|
|
- }
|
|
|
-
|
|
|
- .client-login__button {
|
|
|
- width: 100%;
|
|
|
- height: 44px;
|
|
|
- font-size: 16px;
|
|
|
- font-weight: 600;
|
|
|
- letter-spacing: 1px;
|
|
|
- background: #409eff;
|
|
|
- border: none;
|
|
|
- transition: all 0.3s ease;
|
|
|
- box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
|
|
|
- }
|
|
|
-
|
|
|
- .client-login__button:hover {
|
|
|
- transform: translateY(-2px);
|
|
|
- box-shadow: 0 6px 16px rgba(64, 158, 255, 0.4);
|
|
|
- }
|
|
|
-
|
|
|
- .client-login__button:active {
|
|
|
- transform: translateY(0);
|
|
|
- }
|
|
|
-
|
|
|
- .client-login__links {
|
|
|
- margin-top: 8px;
|
|
|
- margin-bottom: 8px;
|
|
|
- display: flex;
|
|
|
- justify-content: flex-end;
|
|
|
- gap: 6px;
|
|
|
- font-size: 13px;
|
|
|
- color: #909399;
|
|
|
- }
|
|
|
-
|
|
|
- .client-login__links a {
|
|
|
- display: inline-flex;
|
|
|
- align-items: center;
|
|
|
- gap: 4px;
|
|
|
- color: #409eff;
|
|
|
- text-decoration: none;
|
|
|
- transition: all 0.2s;
|
|
|
- font-weight: 500;
|
|
|
- }
|
|
|
-
|
|
|
- .client-login__links a:hover {
|
|
|
- color: #66b1ff;
|
|
|
- transform: translateX(2px);
|
|
|
- }
|
|
|
-
|
|
|
- .client-login__tips {
|
|
|
- margin-top: 12px;
|
|
|
- font-size: 12px;
|
|
|
- color: #909399;
|
|
|
- line-height: 1.6;
|
|
|
- }
|
|
|
-
|
|
|
- /* 响应式设计 */
|
|
|
- @media (max-width: 768px) {
|
|
|
- .client-login__panel {
|
|
|
- width: 90%;
|
|
|
- max-width: 420px;
|
|
|
- padding: 36px 28px 32px;
|
|
|
- }
|
|
|
-
|
|
|
- .client-login__title {
|
|
|
- font-size: 24px;
|
|
|
- }
|
|
|
+ font-size: 24px;
|
|
|
}
|
|
|
- </style>
|
|
|
-
|
|
|
+}
|
|
|
+</style>
|