diff --git a/.kiro/steering/project-guidelines.md b/.kiro/steering/project-guidelines.md index c3321bf..7d33cdd 100644 --- a/.kiro/steering/project-guidelines.md +++ b/.kiro/steering/project-guidelines.md @@ -16,7 +16,7 @@ inclusion: always ## 编码规范 - 颜色用尽量用variables.scss里的统一颜色变量,特别是背景,按钮,文字的颜色 -- 除了Home.vue首页外,其他页面我上传了截图要写页面或组件的,需要参考图片里文字和布局结构,在保证美观前提上自由发挥 +- 除了Home.vue首页外,其他页面我上传了截图要写页面或组件的,需要参考图片里文字和布局结构,在保证美观前提上自由发挥(如果我粘贴了图片内容里的完整样式过来,那就要参考我给的样式结合图片里的结构和样式代码保证还原度) - 全局用1rem=100px的格式并注意对某些特殊元素组件的line-height行高影响,纵布局如非必要不用flex-direction: column布局 - 如果是建一个组件,这个组件看我说是用在views里哪个页面的,比如用在Profile.vue里的组件,组件名字最前面要加Profile,而且整个组件的命名不能过度简化,要容易看懂组件的用途;如果检测到某种名字开头的组件数量比如Profile开头的超过15个,就在components里新建个类似profile这样的页面名字的文件夹,把这类命名的组件都移到文件夹里并查找更新组件所有被引用地方的文件地址 diff --git a/src/App.vue b/src/App.vue index 0ab3184..090ef25 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,13 +1,11 @@ - diff --git a/src/assets/styles/pages/login.scss b/src/assets/styles/pages/login.scss index 9c1aca6..89720ad 100644 --- a/src/assets/styles/pages/login.scss +++ b/src/assets/styles/pages/login.scss @@ -1,87 +1,421 @@ -.login-dialog { - width: 6rem; - .el-dialog__header { - padding: 0.16rem 0.16rem 0; - margin-right: 0; - } - - .el-dialog__body { - padding: 0 0.4rem 0.4rem; - } +/* 登录页面 — 左右分栏全屏布局 */ +.login-view { + font-size: 0.14rem; + display: flex; + align-items: flex-start; + width: 100%; + height: var(--app-height, 100vh); + background: #F7FEFC; } -.login-page { +/* ==================== 左侧品牌面板 ==================== */ +.login-view__left { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 57%; + height: 100%; + background: linear-gradient(180deg, #0A9E97 0%, #12C7BE 50%, #06B6D4 100%); + position: relative; + overflow: hidden; +} + +/* 装饰圆圈 */ +.login-view__deco-circle { + border-radius: 50%; + position: absolute; +} + +.login-view__deco-circle--lg { + width: 4.8rem; + height: 4.8rem; + background: rgba(255, 255, 255, 0.06); + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.login-view__deco-circle--sm { + width: 3.2rem; + height: 3.2rem; + background: rgba(255, 255, 255, 0.08); + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +/* 左侧中心内容 */ +.login-view__left-content { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 0.32rem; + position: relative; + z-index: 1; +} + +/* Logo 行 */ +.login-view__logo-row { + display: flex; + align-items: center; + gap: 0.12rem; +} + +.login-view__logo-box { + display: flex; + justify-content: center; + align-items: center; + width: 0.56rem; + height: 0.56rem; + border-radius: 0.16rem; + font-weight: 700; + font-size: 0.22rem; + line-height: 1; +} + +.login-view__logo-box--light { + background: rgba(255, 255, 255, 0.2); + color: #FFFFFF; +} + +.login-view__logo-box--green { + background: rgba(9, 170, 119, 0.2); + color: #351616; +} + +.login-view__logo-text { + font-weight: 700; + font-size: 0.28rem; + line-height: 0.34rem; +} + +.login-view__logo-text--white { + color: #FFFFFF; +} + +.login-view__logo-text--dark { + color: #351616; +} + +/* 标语 */ +.login-view__slogan { + font-weight: 700; + font-size: 0.4rem; + line-height: 0.56rem; text-align: center; + color: #FFFFFF; +} - .login-title { - font-size: 0.28rem; - font-weight: 700; - color: #1a1a2e; - margin-bottom: 0.4rem; - } +.login-view__sub-slogan { + font-weight: 400; + font-size: 0.16rem; + line-height: 0.19rem; + text-align: center; + color: rgba(255, 255, 255, 0.85); +} - .login-form { - display: flex; - flex-direction: column; - gap: 0.16rem; - } +/* 特性药丸标签 */ +.login-view__pills { + display: flex; + flex-direction: column; + align-items: center; + padding-top: 0.24rem; + gap: 0.12rem; +} - .login-input { - height: 0.4rem; - .el-input__wrapper { - background-color: #f5f5f7; - border-radius: 0.08rem; - box-shadow: none; - padding: 0.00rem 0.16rem; - } - } +.login-view__pill { + display: flex; + align-items: center; + padding: 0.12rem 0.2rem; + gap: 0.08rem; + background: rgba(255, 255, 255, 0.15); + border-radius: 0.24rem; + font-weight: 500; + font-size: 0.14rem; + line-height: 0.17rem; + color: #FFFFFF; +} - .code-row { - display: flex; - align-items: center; - gap: 0.12rem; +/* ==================== 右侧表单面板 ==================== */ +.login-view__right { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 43%; + height: 100%; + background: #F6FDFB; +} - .login-input { - flex: 1; - } +.login-view__logo-row--right { + margin-bottom: 0.4rem; +} - .send-code-btn { - white-space: nowrap; - border-radius: 0.2rem; - padding: 0.08rem 0.2rem; - font-size: 0.14rem; - } - } +/* 表单包裹 */ +.login-view__form-wrap { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.28rem; + width: 4rem; +} - .login-btn { - margin-top: 0.16rem; - width: 100%; - height: 0.52rem; - border-radius: 0.26rem; - font-size: 0.18rem; - font-weight: 600; - background-color: #1a1a2e; - border-color: #1a1a2e; +/* 标题 */ +.login-view__heading { + width: 100%; + display: flex; + flex-direction: column; + gap: 0.1rem; +} - &:hover, - &:focus { - background-color: #2d2d44; - border-color: #2d2d44; - } - } +.login-view__title { + font-weight: 700; + font-size: 0.32rem; + line-height: 0.39rem; + color: #132034; +} - .register-link { - margin-top: 0.08rem; - font-size: 0.14rem; - color: #666; +.login-view__subtitle { + font-weight: 400; + font-size: 0.15rem; + line-height: 0.18rem; + color: #64748B; +} - a { - color: #409eff; - text-decoration: none; +/* ==================== 手机号输入行 ==================== */ +.login-view__phone-row { + display: flex; + align-items: center; + width: 100%; + height: 0.52rem; + border-radius: 0.12rem; + overflow: hidden; +} - &:hover { - text-decoration: underline; - } - } +.login-view__country-code { + display: flex; + align-items: center; + padding: 0 0.16rem; + gap: 0.06rem; + width: 0.96rem; + height: 100%; + background: #F3FFFD; + border: 1px solid #BFE8E2; + border-radius: 0.12rem 0 0 0.12rem; + cursor: pointer; +} + +.login-view__flag { + font-size: 0.16rem; + line-height: 1; +} + +.login-view__code-text { + font-weight: 500; + font-size: 0.15rem; + line-height: 0.18rem; + color: #132034; +} + +.login-view__code-arrow { + font-size: 0.11rem; + color: #94A3B8; +} + +.login-view__separator { + width: 1px; + height: 100%; + background: #BFE8E2; +} + +.login-view__phone-input { + flex: 1; + height: 100%; + padding: 0 0.16rem; + background: #FAFBFC; + border: 1px solid #E2E8F0; + border-left: none; + border-radius: 0 0.12rem 0.12rem 0; + font-size: 0.15rem; + color: #132034; + outline: none; + + &::placeholder { + color: #94A3B8; + } +} + +/* ==================== 发送验证码按钮(步骤一) ==================== */ +.login-view__send-btn { + width: 100%; + height: 0.52rem; + background: linear-gradient(90deg, #12C7BE 0%, #06B6D4 100%); + border-radius: 0.12rem; + border: none; + font-weight: 600; + font-size: 0.16rem; + color: #FFFFFF; + cursor: pointer; + transition: opacity 0.2s; + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + } + + &:not(:disabled):hover { + opacity: 0.9; + } +} + +/* ==================== OTP 验证码输入(步骤二) ==================== */ +.login-view__otp-wrap { + position: relative; + display: flex; + align-items: flex-start; + gap: 0.1rem; + width: 100%; + cursor: text; +} + +.login-view__otp-hidden-input { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + opacity: 0; + z-index: 2; + font-size: 0.16rem; + caret-color: transparent; +} + +.login-view__otp-box { + display: flex; + justify-content: center; + align-items: center; + width: 0.56rem; + height: 0.64rem; + background: #FAFBFC; + border: 1px solid #E2E8F0; + border-radius: 0.12rem; + transition: border-color 0.2s, background 0.2s; +} + +.login-view__otp-box--active { + background: #F3FFFD; + border: 2px solid #12C7BE; +} + +.login-view__otp-box--filled { + background: #F3FFFD; + border: 2px solid #12C7BE; +} + +.login-view__otp-digit { + font-weight: 700; + font-size: 0.24rem; + line-height: 0.29rem; + color: #132034; +} + +/* 闪烁光标 */ +.login-view__otp-cursor { + display: inline-block; + width: 2px; + height: 0.28rem; + background: #12C7BE; + border-radius: 1px; + animation: login-otp-blink 1s ease-in-out infinite; +} + +@keyframes login-otp-blink { + 0%, 100% { opacity: 1; } + 50% { opacity: 0; } +} + +/* ==================== 状态按钮(步骤二) ==================== */ +.login-view__status-btn { + width: 100%; + height: 0.52rem; + background: linear-gradient(90deg, #12C7BE 0%, #06B6D4 100%); + border-radius: 0.12rem; + border: none; + font-weight: 600; + font-size: 0.16rem; + color: #FFFFFF; + cursor: default; + display: flex; + align-items: center; + justify-content: center; + gap: 0.08rem; +} + +/* 登录中转圈动画 */ +.login-view__spinner { + display: inline-block; + width: 0.18rem; + height: 0.18rem; + border: 2px solid rgba(255, 255, 255, 0.3); + border-top-color: #FFFFFF; + border-radius: 50%; + animation: login-spin 0.7s linear infinite; +} + +@keyframes login-spin { + to { transform: rotate(360deg); } +} + +/* ==================== 协议勾选 ==================== */ +.login-view__agreement { + display: flex; + align-items: center; + gap: 0.06rem; +} + +.login-view__checkbox { + box-sizing: border-box; + width: 0.18rem; + height: 0.18rem; + background: #FFFFFF; + border: 1.5px solid #CBD5E1; + border-radius: 0.05rem; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s; +} + +.login-view__checkbox--checked { + background: #12C7BE; + border-color: #12C7BE; +} + +.login-view__check-icon { + font-size: 0.11rem; + color: #FFFFFF; + font-weight: 700; + line-height: 1; +} + +.login-view__agreement-text { + font-weight: 400; + font-size: 0.12rem; + line-height: 0.15rem; + color: #94A3B8; +} + +.login-view__agreement-link { + font-weight: 500; + font-size: 0.12rem; + line-height: 0.15rem; + color: #12C7BE; + cursor: pointer; + + &:hover { + text-decoration: underline; } } diff --git a/src/components/LoginDialog.vue b/src/components/LoginDialog.vue index 78c3c44..59ce36c 100644 --- a/src/components/LoginDialog.vue +++ b/src/components/LoginDialog.vue @@ -1,4 +1,8 @@ + import('@/views/Home.vue') }, - { path: '/jobs', name: 'Jobs', component: () => import('@/views/Jobs.vue') }, + { path: '/login', name: 'Login', component: () => import('@/views/Login.vue') }, + { + path: '/jobs', + name: 'Jobs', + component: () => import('@/views/Jobs.vue'), + meta: { requiresAuth: true }, + }, { path: '/resume/:id', name: 'ResumeDetail', @@ -59,6 +65,12 @@ router.beforeEach(async (to, _from, next) => { // 每次页面切换时静默刷新路由菜单数据(更新权限状态),不阻塞导航 store.dispatch('refreshDynamicMenus') + // 已登录用户访问登录页,直接跳转首页 + if (to.name === 'Login' && store.state.isAuthenticated) { + next({ name: 'Home' }) + return + } + // 需要鉴权的路由,每次都通过接口校验登录状态 if (to.meta?.requiresAuth) { try { @@ -69,16 +81,14 @@ router.beforeEach(async (to, _from, next) => { store.commit('SET_AUTHENTICATED', true) next() } else { - // 未登录或 Cookie 失效 + // 未登录或 Cookie 失效 — 跳转登录页 store.commit('SET_AUTHENTICATED', false) - store.dispatch('openLogin', to.fullPath) - next(false) + next({ name: 'Login', query: { redirect: to.fullPath } }) } } catch { // 请求异常(网络错误等),也视为未登录 store.commit('SET_AUTHENTICATED', false) - store.dispatch('openLogin', to.fullPath) - next(false) + next({ name: 'Login', query: { redirect: to.fullPath } }) } return } diff --git a/src/views/Home.vue b/src/views/Home.vue index d5e02e9..140abbf 100644 --- a/src/views/Home.vue +++ b/src/views/Home.vue @@ -661,9 +661,9 @@ onMounted(() => { store.dispatch('loadCommonData') }) -/** 点击登陆按钮 — 打开登录弹窗,登录成功后跳转到 jobs 页面 */ +/** 点击登陆按钮 — 跳转到登录页面,登录成功后跳转到 jobs 页面 */ function handleLoginClick() { - store.dispatch('openLogin', '/jobs') + router.push({ name: 'Login', query: { redirect: '/jobs' } }) } /** 隐私协议弹窗显示状态 */ diff --git a/src/views/Login.vue b/src/views/Login.vue new file mode 100644 index 0000000..bcc9c09 --- /dev/null +++ b/src/views/Login.vue @@ -0,0 +1,319 @@ + + + + + + + + + + + + O + Offer派 + + + 在Offer派找到你的理想工作 + + AI 驱动的求职平台,为你匹配最合适的岗位 + + + ✦ AI 智能岗位匹配 + ◱ 一键生成专属简历 + ○ 模拟面试精准备考 + + + + + + + + + + + O + Offer派 + + + + + + + + + 手机号登录/注册 + 未注册用户验证后将自动注册并登录 + + + + + + 🇨🇳 + +86 + ▾ + + + + + + + + + 发送验证码 + + + + + + + + 请输入验证码 + 短信验证码已发送至 +86{{ phone }} + + + + + + + + + {{ otpValue[idx] }} + + + + + + + + + + 登录中 + + + + 即将返回发送验证码 + + + + {{ countdown }} 秒后可继续发送 + + + + + + + + ✓ + + 登录即表示同意 + 《用户协议》 + 与 + 《隐私政策》 + + + + + + + + + +
AI 驱动的求职平台,为你匹配最合适的岗位
未注册用户验证后将自动注册并登录
短信验证码已发送至 +86{{ phone }}