From 3bf14a7c469a7157734b25b439eb9e906522fcf9 Mon Sep 17 00:00:00 2001
From: zjk <852185829@qq.com>
Date: 星期一, 12 十月 2026 14:45:12 +0800
Subject: [PATCH] feat(miniapp, ruoyi-auth, ruoyi-user): 实现C端会员小程序登录注册与推荐码系统
---
hugray-user-miniapp/pages/login/index.vue | 763 +++++++++++++++++++++++++++++++++++++++++++++++-----------
1 files changed, 616 insertions(+), 147 deletions(-)
diff --git a/hugray-user-miniapp/pages/login/index.vue b/hugray-user-miniapp/pages/login/index.vue
index 04a2e76..87f0e13 100644
--- a/hugray-user-miniapp/pages/login/index.vue
+++ b/hugray-user-miniapp/pages/login/index.vue
@@ -1,49 +1,123 @@
<template>
<view class="login-page">
- <!-- 顶部品牌区:深蓝底承载品牌 logo 与欢迎语 -->
- <view class="hero" :style="{ paddingTop: statusBarHeight + 'px' }">
- <view class="nav-inner">
- <image class="brand-logo" src="/static/logo.png" mode="heightFix" />
- </view>
- <view class="welcome-block">
- <text class="welcome-title">欢迎使用</text>
- <view class="brand-row">
- <text class="brand-name">代谢</text>
- <text class="brand-num">300</text>
+ <!-- 背景装饰层:3 处微透十字 + 3 处白色微点阵(置于内容之下,不拦截点击) -->
+ <view class="deco-cross deco-cross--tr"></view>
+ <view class="deco-cross deco-cross--ml"></view>
+ <view class="deco-cross deco-cross--bl"></view>
+ <view class="deco-dots deco-dots--lt"></view>
+ <view class="deco-dots deco-dots--rt"></view>
+ <view class="deco-dots deco-dots--rb"></view>
+
+ <!-- 核心内容区:欢迎语 + 官方 Logo + 英译标语 + 医学双背书 + 产业4.0标语卡 -->
+ <view class="content" :style="{ paddingTop: statusBarHeight + 40 + 'px' }">
+ <text class="welcome-title">欢迎来到</text>
+ <image class="brand-logo" src="/static/metabolism-300-logo.png" mode="widthFix" />
+ <text class="en-slogan">RESET METABOLISM & DELIVER HEALTH</text>
+
+ <!-- 医学价值双背书行:严谨医学循证 / 传递健康价值 -->
+ <view class="endorse-row">
+ <view class="endorse-item">
+ <image class="endorse-icon" src="/static/medical-plus-icon.png" mode="aspectFit" />
+ <text class="endorse-text">严谨医学循证</text>
+ </view>
+ <view class="endorse-item">
+ <image
+ class="endorse-icon endorse-icon--check"
+ src="/static/check-icon.png"
+ mode="aspectFit"
+ />
+ <text class="endorse-text">传递健康价值</text>
</view>
</view>
+
+ <!-- 产业4.0核心标语卡:整图呈现,图片自带透明留白(左右对称、上下需负边距补偿) -->
+ <image class="slogan-card" src="/static/industry-4-0-slogan-card.png" mode="widthFix" />
</view>
- <!-- 底部主体区:协议勾选 + 手机号一键登录 -->
- <view class="login-body">
- <!-- 协议勾选 -->
+ <!-- 底部操作区:双按钮 + 内嵌验证码面板 + 协议行 -->
+ <view class="bottom-area">
+ <!-- 双按钮组:微信一键登录 / 手机验证登录 -->
+ <view class="btn-group">
+ <!-- 微信一键登录:未勾选协议时 open-type 置空,由点击事件拦截并弹窗提示 -->
+ <button
+ class="btn btn--wechat"
+ :open-type="agreed ? 'getPhoneNumber' : ''"
+ @tap="_handleWechatTap"
+ @getphonenumber="_handleGetPhoneNumber"
+ >
+ <image class="btn-icon" src="/static/wechat-icon.png" mode="aspectFit" />
+ <text class="btn-text">微信一键登录</text>
+ </button>
+
+ <!-- 手机验证登录:点击展开内嵌手机号+验证码面板(展开后隐藏本按钮) -->
+ <view v-if="!showCodePanel" class="btn btn--phone" @tap="_toggleCodePanel">
+ <image class="btn-icon" src="/static/phone-icon.png" mode="aspectFit" />
+ <text class="btn-text">手机验证登录</text>
+ </view>
+ </view>
+
+ <!-- 内嵌手机验证登录面板(不新增页面,当前页展开) -->
+ <view v-if="showCodePanel" class="code-panel">
+ <!-- 收起入口:按钮已隐藏,由此恢复「手机验证登录」按钮 -->
+ <view class="code-panel-collapse" @tap="_toggleCodePanel">收起</view>
+ <view class="input-wrap">
+ <input
+ v-model="phone"
+ class="field-input"
+ type="number"
+ maxlength="11"
+ placeholder="请输入手机号"
+ placeholder-style="color:#94A3B8"
+ />
+ </view>
+ <view class="code-row">
+ <view class="input-wrap input-wrap--flex">
+ <input
+ v-model="code"
+ class="field-input"
+ type="number"
+ maxlength="6"
+ placeholder="请输入验证码"
+ placeholder-style="color:#94A3B8"
+ />
+ </view>
+ <!-- 获取验证码:倒计时期间禁用,显示剩余秒数 -->
+ <view
+ class="code-btn"
+ :class="{ 'code-btn--disabled': counting }"
+ @tap="_handleSendCode"
+ >
+ {{ counting ? countdown + 's' : '获取验证码' }}
+ </view>
+ </view>
+ <view class="submit-btn" @tap="_handleCodeLogin">登录</view>
+ </view>
+
+ <!-- 协议勾选行:复选框 + 协议文案(设计稿 16pt 复选框 / 11.5pt 文案) -->
<view class="agreement-row">
<view
class="checkbox"
:class="{ 'checkbox--checked': agreed }"
@tap="agreed = !agreed"
></view>
- <text class="agreement-text">我已阅读并同意</text>
- <text class="agreement-link" @tap.stop="_openAgreement('用户协议')">《用户协议》</text>
- <text class="agreement-text">、</text>
- <text class="agreement-link" @tap.stop="_openAgreement('隐私与权限协议')">《隐私与权限协议》</text>
+ <view class="agreement-words">
+ <text class="agreement-text">我已阅读并同意</text>
+ <text class="agreement-link" @tap.stop="_openAgreement('用户协议')">《用户协议》</text>
+ <text class="agreement-text">与</text>
+ <text class="agreement-link" @tap.stop="_openAgreement('隐私与权限协议')">
+ 《隐私与权限》
+ </text>
+ </view>
</view>
- <!-- 手机号一键登录:未勾选协议时 open-type 置空,由点击事件拦截并弹窗提示 -->
- <button
- class="login-btn"
- :open-type="agreed ? 'getPhoneNumber' : ''"
- @tap="_handleLoginTap"
- @getphonenumber="_handleGetPhoneNumber"
- >
- 手机号一键登录
- </button>
-
- <!-- 注册入口:跳转注册页 -->
+ <!-- 注册入口:设计稿无该入口,按业务要求保留在协议行下方(弱化样式) -->
<view class="register-entry">
<text class="register-tip">还没有账号?</text>
<text class="register-link" @tap="_goRegister">立即注册</text>
</view>
+
+ <!-- 底部 Home 条(设计稿 135×5pt,居中贴底) -->
+ <view class="home-indicator"></view>
</view>
<!-- 未勾选协议时的确认弹窗 -->
@@ -63,8 +137,9 @@
</template>
<script setup>
-import { ref } from 'vue'
-import { phoneLogin } from '@/api/index.js'
+import { ref, computed } from 'vue'
+import { onUnload } from '@dcloudio/uni-app'
+import { phoneLogin, phoneCodeLogin, sendSmsCode } from '@/api/index.js'
import { setToken } from '@/utils/request.js'
// ======================== 数据定义区域 ========================
@@ -72,22 +147,54 @@
// 是否已勾选同意用户协议
const agreed = ref(false)
-// 登录请求进行中标记,防止重复提交
+// 微信一键登录进行中标记,防止重复提交
const logging = ref(false)
+
+// 验证码登录进行中标记,防止重复提交
+const codeLogging = ref(false)
// 未勾选协议时确认弹窗的显示状态
const showAgreementModal = ref(false)
-// 状态栏高度(px),用于顶部品牌区留白
+// 是否展开内嵌手机验证登录面板
+const showCodePanel = ref(false)
+
+// 验证码登录表单
+const phone = ref('')
+const code = ref('')
+
+// 验证码倒计时剩余秒数(0 表示未在倒计时)
+const countdown = ref(0)
+
+// 是否处于倒计时状态(用于禁用「获取验证码」)
+const counting = computed(() => countdown.value > 0)
+
+// 倒计时定时器句柄
+let countdownTimer = null
+
+// 状态栏高度(px),用于顶部内容区留白
const statusBarHeight = uni.getSystemInfoSync().statusBarHeight || 0
// ======================== 方法定义区域 ========================
-/** 点击登录:未勾选协议时弹窗二次确认 */
-const _handleLoginTap = () => {
+/** 校验手机号格式 */
+const _isValidPhone = (phone) => /^1[3-9]\d{9}$/.test(phone)
+
+/**
+ * 协议校验:未勾选时弹出确认弹窗并返回 false
+ * @returns {Boolean} 是否已同意协议
+ */
+const _ensureAgreed = () => {
if (!agreed.value) {
showAgreementModal.value = true
+ return false
}
+ return true
+}
+
+/** 点击微信一键登录:未勾选协议时弹窗二次确认(授权由 button open-type 触发) */
+const _handleWechatTap = () => {
+ _ensureAgreed()
}
/** 关闭协议确认弹窗 */
@@ -96,13 +203,13 @@
}
/**
- * 同意协议:自动勾选(手机号授权必须由用户点击按钮触发,
+ * 同意协议:自动勾选(微信授权必须由用户手势触发,
* 故此处仅提示用户再次点击按钮完成登录)
*/
const _handleAgreementConfirm = () => {
showAgreementModal.value = false
agreed.value = true
- uni.showToast({ title: '请再次点击「手机号一键登录」', icon: 'none' })
+ uni.showToast({ title: '请再次点击登录按钮', icon: 'none' })
}
/**
@@ -122,16 +229,91 @@
logging.value = true
try {
const { data } = await phoneLogin({ code: detail.code })
- const token = (data && (data.token || data.accessToken)) || ''
- if (!token) throw new Error('登录失败:未获取到登录凭证')
-
- setToken(token)
- uni.reLaunch({ url: '/pages/index/index' })
+ _saveTokenAndEnter(data)
} catch (err) {
uni.showToast({ title: err.message || '登录失败', icon: 'none' })
} finally {
logging.value = false
}
+}
+
+/** 展开/收起内嵌手机验证登录面板 */
+const _toggleCodePanel = () => {
+ showCodePanel.value = !showCodePanel.value
+}
+
+/** 启动 30 秒倒计时,期间禁用「获取验证码」 */
+const _startCountdown = () => {
+ countdown.value = 30
+ countdownTimer = setInterval(() => {
+ countdown.value -= 1
+ if (countdown.value <= 0) {
+ clearInterval(countdownTimer)
+ countdownTimer = null
+ }
+ }, 1000)
+}
+
+/** 获取登录验证码:校验协议与手机号后下发登录场景验证码 */
+const _handleSendCode = async () => {
+ if (counting.value) return
+ if (!_ensureAgreed()) return
+
+ const inputPhone = phone.value.trim()
+ if (!inputPhone) {
+ uni.showToast({ title: '请输入手机号', icon: 'none' })
+ return
+ }
+ if (!_isValidPhone(inputPhone)) {
+ uni.showToast({ title: '请输入正确手机号', icon: 'none' })
+ return
+ }
+
+ try {
+ // 登录场景:后端校验手机号已注册,未注册返回「该手机号未注册,请先注册」
+ await sendSmsCode(inputPhone, 'login')
+ uni.showToast({ title: '验证码已发送', icon: 'none' })
+ _startCountdown()
+ } catch (err) {
+ uni.showToast({ title: err.message || '验证码发送失败', icon: 'none' })
+ }
+}
+
+/** 提交验证码登录:手机号 + 短信验证码换取登录态 */
+const _handleCodeLogin = async () => {
+ if (codeLogging.value) return
+ if (!_ensureAgreed()) return
+
+ const inputPhone = phone.value.trim()
+ const inputCode = code.value.trim()
+ if (!inputPhone || !_isValidPhone(inputPhone)) {
+ uni.showToast({ title: '请输入正确手机号', icon: 'none' })
+ return
+ }
+ if (!inputCode) {
+ uni.showToast({ title: '请输入验证码', icon: 'none' })
+ return
+ }
+
+ codeLogging.value = true
+ try {
+ const { data } = await phoneCodeLogin({ phone: inputPhone, code: inputCode })
+ _saveTokenAndEnter(data)
+ } catch (err) {
+ uni.showToast({ title: err.message || '登录失败', icon: 'none' })
+ } finally {
+ codeLogging.value = false
+ }
+}
+
+/** 保存令牌并进入首页(两种登录方式共用的收尾逻辑) */
+const _saveTokenAndEnter = (data) => {
+ // 后端 TokenService 返回字段为 access_token
+ const token = (data && (data.access_token || data.token)) || ''
+ if (!token) throw new Error('登录失败:未获取到登录凭证')
+
+ setToken(token)
+ uni.reLaunch({ url: '/pages/index/index' })
}
/** 跳转注册页 */
@@ -143,117 +325,414 @@
const _openAgreement = (name) => {
uni.showToast({ title: name + '页面开发中', icon: 'none' })
}
+
+// 页面卸载时清理倒计时,避免定时器泄漏
+onUnload(() => {
+ if (countdownTimer) clearInterval(countdownTimer)
+})
</script>
<style scoped lang="scss">
-/* 页面最外层:纵向弹性布局,撑满整屏 */
+/* ==================== 页面底板 ==================== */
+
+/* 页面最外层:浅蓝品牌渐变底(165°,0.8 透明),纵向弹性布局撑满整屏 */
.login-page {
+ position: relative;
display: flex;
flex-direction: column;
min-height: 100vh;
+ box-sizing: border-box;
+ overflow: hidden;
background-color: #ffffff;
+ background-image: linear-gradient(
+ 165deg,
+ rgba(211, 228, 248, 0.8) 0%,
+ rgba(188, 214, 244, 0.8) 50%,
+ rgba(171, 204, 236, 0.8) 100%
+ );
}
-/* 顶部品牌区:深蓝底 + 品牌 logo(底色与 logo 背景 #0b3f6f 一致,使 logo 无缝融入) */
-.hero {
- min-height: 38vh;
+/* ==================== 背景装饰层 ==================== */
+
+/* 微透十字:容器尺寸按设计稿,竖条(::before)/横条(::after)等宽居中交叉 */
+.deco-cross {
+ position: absolute;
+ z-index: 0;
+ pointer-events: none;
+
+ &::before,
+ &::after {
+ content: '';
+ position: absolute;
+ background-color: var(--cross-color);
+ border-radius: calc(var(--bar) / 2);
+ }
+
+ /* 竖条:水平居中 */
+ &::before {
+ left: var(--offset);
+ top: 0;
+ width: var(--bar);
+ height: 100%;
+ }
+
+ /* 横条:垂直居中 */
+ &::after {
+ left: 0;
+ top: var(--offset);
+ width: 100%;
+ height: var(--bar);
+ }
+}
+
+/* 右上十字:设计稿 x290/y10,80×80pt,条宽 22 / R11,#FFFFFF35 */
+.deco-cross--tr {
+ top: 20rpx;
+ right: 10rpx;
+ width: 160rpx;
+ height: 160rpx;
+ --bar: 44rpx;
+ --offset: 58rpx;
+ --cross-color: rgba(255, 255, 255, 0.208);
+}
+
+/* 中左十字:设计稿 x-24/y470,68×68pt,条宽 18 / R9,#FFFFFF30 */
+.deco-cross--ml {
+ top: 57.9%;
+ left: -48rpx;
+ width: 136rpx;
+ height: 136rpx;
+ --bar: 36rpx;
+ --offset: 50rpx;
+ --cross-color: rgba(255, 255, 255, 0.188);
+}
+
+/* 左下十字:设计稿 x-10/y710,100×100pt,条宽 28 / R14,#FFFFFF35 */
+.deco-cross--bl {
+ bottom: 4rpx;
+ left: -20rpx;
+ width: 200rpx;
+ height: 200rpx;
+ --bar: 56rpx;
+ --offset: 72rpx;
+ --cross-color: rgba(255, 255, 255, 0.208);
+}
+
+/*
+ * 白色微点阵:微点 3×3pt(6rpx),行内/行间 gap 10pt(20rpx) → 平铺步距 26rpx
+ * 说明:设计稿三处点阵均为不规则装饰形状,此处以平铺网格近似
+ */
+.deco-dots {
+ position: absolute;
+ z-index: 0;
+ pointer-events: none;
+ background-image: radial-gradient(circle, rgba(255, 255, 255, 0.333) 3rpx, transparent 3rpx);
+ background-size: 26rpx 26rpx;
+}
+
+/* 左上点阵:设计稿 x10/y200(5 列 × 4 行) */
+.deco-dots--lt {
+ top: 24.6%;
+ left: 20rpx;
+ width: 110rpx;
+ height: 84rpx;
+}
+
+/* 右上点阵:设计稿 x310/y130(3 列 × 6 行) */
+.deco-dots--rt {
+ top: 16%;
+ right: 72rpx;
+ width: 58rpx;
+ height: 136rpx;
+}
+
+/* 右下点阵:设计稿 x240/y752(3 列 × 3 行) */
+.deco-dots--rb {
+ bottom: 62rpx;
+ right: 212rpx;
+ width: 58rpx;
+ height: 58rpx;
+}
+
+/* ==================== 核心内容区 ==================== */
+
+/* 内容区:x28/y100,宽 319pt,元素间距 20pt(40rpx),整体水平居中 */
+.content {
+ position: relative;
+ z-index: 1;
display: flex;
flex-direction: column;
- background-color: #0b3f6f;
- /* 底部留出间距,避免欢迎语贴近色块下边缘 */
- padding-bottom: 90rpx;
- box-sizing: border-box;
-}
-
-/* 品牌行:固定高度,与小程序右上角胶囊高度对齐 */
-.nav-inner {
- height: 88rpx;
- display: flex;
align-items: center;
- padding-left: 28rpx;
- box-sizing: border-box;
+ gap: 40rpx;
+ padding-left: 56rpx;
+ padding-right: 56rpx;
}
-.brand-logo {
- height: 56rpx;
- display: block;
-}
-
-/* 欢迎语与品牌名:margin-top 自动撑开上方空间,使其贴近品牌区底部 */
-.welcome-block {
- margin-top: auto;
- padding: 0 40rpx;
-}
-
+/* 欢迎语:26pt 500 深板岩色,居中 */
.welcome-title {
- display: block;
- font-size: 44rpx;
- font-weight: 700;
- color: #ffffff;
- line-height: 62rpx;
+ flex-shrink: 0;
+ font-size: 52rpx;
+ font-weight: 500;
+ line-height: 74rpx;
+ text-align: center;
+ color: #0f172a;
}
-.brand-row {
- display: flex;
- align-items: flex-end;
- margin-top: 24rpx;
+/* 官方 Logo:设计稿 220×76pt(图 660×228,比例一致) */
+.brand-logo {
+ flex-shrink: 0;
+ width: 440rpx;
+ height: 152rpx;
}
-.brand-name {
- font-size: 44rpx;
- font-weight: 700;
- color: #ffffff;
- line-height: 62rpx;
+/* 英译标语:11pt 600 深海蓝,字间距 1.5pt,居中 */
+.en-slogan {
+ flex-shrink: 0;
+ font-size: 22rpx;
+ font-weight: 600;
+ letter-spacing: 3rpx;
+ text-align: center;
+ color: #003e7e;
}
-/* 品牌数字:绿色大号斜体,突出代谢 300 品牌标识 */
-.brand-num {
- margin-left: 16rpx;
- font-size: 96rpx;
- font-weight: 800;
- font-style: italic;
- color: #8bc34a;
- line-height: 96rpx;
-}
-
-/* 主体区:承载协议勾选与登录按钮 */
-.login-body {
- flex: 1;
- padding: 300rpx 32rpx 0;
- box-sizing: border-box;
-}
-
-/* 协议勾选行:整体居中排列 */
-.agreement-row {
+/* 医学双背书行:占满整行,两项 gap 24pt(48rpx),居中 */
+.endorse-row {
display: flex;
align-items: center;
justify-content: center;
- flex-wrap: wrap;
- margin-bottom: 44rpx;
+ gap: 48rpx;
+ width: 100%;
}
+/* 单项:图标 + 文案,gap 6pt(12rpx) */
+.endorse-item {
+ display: flex;
+ align-items: center;
+ gap: 12rpx;
+}
+
+/* 加号图标 15×15pt → 30rpx */
+.endorse-icon {
+ width: 30rpx;
+ height: 30rpx;
+ flex-shrink: 0;
+}
+
+/* 勾选图标 18.2×15pt → 36.4×30rpx */
+.endorse-icon--check {
+ width: 36.4rpx;
+ height: 30rpx;
+}
+
+/* 背书文案:12pt 600 正文强色 */
+.endorse-text {
+ font-size: 24rpx;
+ font-weight: 600;
+ color: #1e293b;
+}
+
+/*
+ * 产业4.0核心标语卡:整图呈现
+ * 图 1141×457,卡片描边 bbox [90,72]-[1049,347](视觉 959×275)
+ * 目标卡片视觉宽 = 319pt = 638rpx → 图片宽 759rpx、高 304rpx
+ * 透明留白:上 48rpx / 下 72rpx 用负边距抵消,左右对称无需补偿
+ */
+.slogan-card {
+ flex-shrink: 0;
+ width: 759rpx;
+ height: 304rpx;
+ margin-top: -48rpx;
+ margin-bottom: -72rpx;
+}
+
+/* ==================== 底部操作区 ==================== */
+
+/* 底部操作区:margin-top 自动撑开,整体贴近底部,左右与内容区同边距 */
+.bottom-area {
+ position: relative;
+ z-index: 1;
+ margin-top: auto;
+ padding: 0 56rpx;
+ padding-bottom: calc(32rpx + constant(safe-area-inset-bottom));
+ padding-bottom: calc(32rpx + env(safe-area-inset-bottom));
+}
+
+/* 双按钮组:纵向排列,间距 14pt(28rpx) */
+.btn-group {
+ display: flex;
+ flex-direction: column;
+ gap: 28rpx;
+}
+
+/* 按钮基础:H48 → 96rpx,PILL R24 → 48rpx,图标与文字 gap 8pt(16rpx) */
+.btn {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 16rpx;
+ width: 100%;
+ height: 96rpx;
+ /* 重置小程序 button 默认的 margin: 0 auto(否则 flex 列内宽度会收缩为内容宽) */
+ margin: 0;
+ padding: 0;
+ border: none;
+ border-radius: 48rpx;
+ box-sizing: border-box;
+ line-height: 1;
+}
+
+/* 按钮内图标 18×18pt → 36rpx */
+.btn-icon {
+ width: 36rpx;
+ height: 36rpx;
+ flex-shrink: 0;
+}
+
+/* 按钮文字:15pt 600 白色 */
+.btn-text {
+ font-size: 30rpx;
+ font-weight: 600;
+ color: #ffffff;
+}
+
+/* 微信一键登录:微信绿渐变(90°)+ 绿色光晕投影 #07C16040 */
+.btn--wechat {
+ background-image: linear-gradient(90deg, #07c160 0%, #05a352 100%);
+ box-shadow: 0 12rpx 32rpx rgba(7, 193, 96, 0.25);
+}
+
+/* 覆盖小程序 button 默认边框伪元素 */
+.btn--wechat::after {
+ border: none;
+}
+
+/* 手机验证登录:医疗蓝渐变(90°)+ 蓝色光晕投影 #1E5EBC30 */
+.btn--phone {
+ background-image: linear-gradient(90deg, #1e5ebc 0%, #144a94 100%);
+ box-shadow: 0 12rpx 32rpx rgba(30, 94, 188, 0.188);
+}
+
+/* 内嵌验证码面板:表单间距 20rpx */
+.code-panel {
+ margin-top: 32rpx;
+}
+
+/* 收起入口:右对齐弱化文字,点击恢复「手机验证登录」按钮 */
+.code-panel-collapse {
+ margin-bottom: 20rpx;
+ text-align: right;
+ font-size: 26rpx;
+ color: #1e5ebc;
+}
+
+/* 输入框:R8 → 16rpx,H44 → 88rpx,浅底 + 描边 */
+.input-wrap {
+ display: flex;
+ align-items: center;
+ height: 88rpx;
+ padding: 0 28rpx;
+ border: 2rpx solid #cbd5e1;
+ border-radius: 16rpx;
+ background-color: #f8fafc;
+ box-sizing: border-box;
+}
+
+.code-row {
+ display: flex;
+ align-items: center;
+ margin-top: 20rpx;
+}
+
+.input-wrap--flex {
+ flex: 1;
+ min-width: 0;
+ margin-right: 16rpx;
+}
+
+.field-input {
+ flex: 1;
+ height: 100%;
+ min-width: 0;
+ font-size: 28rpx;
+ color: #1e293b;
+}
+
+/* 获取验证码:白底 + 医疗蓝描边胶囊,倒计时期间置灰 */
+.code-btn {
+ width: 208rpx;
+ height: 88rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+ border: 2rpx solid #1e5ebc;
+ border-radius: 16rpx;
+ background-color: #ffffff;
+ color: #1e5ebc;
+ font-size: 26rpx;
+ font-weight: 600;
+}
+
+.code-btn--disabled {
+ border-color: #cbd5e1;
+ color: #94a3b8;
+}
+
+/* 登录提交按钮:品牌主行动渐变,PILL R24 */
+.submit-btn {
+ height: 96rpx;
+ margin-top: 28rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 48rpx;
+ background-image: linear-gradient(90deg, #1e5ebc 0%, #003e7e 100%);
+ color: #ffffff;
+ font-size: 30rpx;
+ font-weight: 600;
+ box-shadow: 0 12rpx 28rpx rgba(0, 62, 126, 0.2);
+}
+
+/* 协议勾选行:与按钮组间距 19pt(38rpx),复选框与文案 gap 8pt(16rpx) */
+.agreement-row {
+ display: flex;
+ align-items: center;
+ margin-top: 38rpx;
+}
+
+/* 协议文案组合:内部字间距 1pt(2rpx) */
+.agreement-words {
+ display: flex;
+ align-items: center;
+ flex: 1;
+ min-width: 0;
+ gap: 2rpx;
+ margin-left: 16rpx;
+}
+
+/* 勾选框:16×16pt → 32rpx,R4 → 8rpx */
.checkbox {
position: relative;
width: 32rpx;
height: 32rpx;
- border: 2rpx solid #c0c4cc;
- border-radius: 4rpx;
- margin-right: 14rpx;
flex-shrink: 0;
+ border: 2rpx solid #cbd5e1;
+ border-radius: 8rpx;
+ background-color: #ffffff;
box-sizing: border-box;
}
.checkbox--checked {
- background-color: #8bc34a;
- border-color: #8bc34a;
+ background-color: #003e7e;
+ border-color: #003e7e;
}
/* 勾选后的白色对勾 */
.checkbox--checked::after {
content: '';
position: absolute;
- left: 9rpx;
- top: 3rpx;
+ left: 10rpx;
+ top: 2rpx;
width: 8rpx;
height: 16rpx;
border-right: 3rpx solid #ffffff;
@@ -261,98 +740,88 @@
transform: rotate(45deg);
}
+/* 协议文案:原稿 11.5pt,按反馈上调至 26rpx 提升可读性 */
.agreement-text {
- font-size: 24rpx;
- color: #6b7280;
+ font-size: 26rpx;
+ color: #64748b;
}
.agreement-link {
- font-size: 24rpx;
- color: #1a73e8;
-}
-
-/* 手机号一键登录按钮:绿色圆角块,两侧留边距 */
-.login-btn {
- margin: 0 32rpx;
- height: 96rpx;
- line-height: 96rpx;
- border: none;
- border-radius: 16rpx;
- background-color: #8bc34a;
- color: #ffffff;
- font-size: 32rpx;
+ font-size: 26rpx;
font-weight: 600;
+ color: #1e5ebc;
}
-/* 覆盖小程序 button 默认的边框伪元素 */
-.login-btn::after {
- border: none;
-}
-
-/* 注册入口:居中排列的提示文案 + 链接 */
+/* 注册入口:居中弱化文案 + 链接 */
.register-entry {
display: flex;
align-items: center;
justify-content: center;
- margin-top: 40rpx;
+ margin-top: 28rpx;
}
.register-tip {
font-size: 26rpx;
- color: #6b7280;
+ color: #64748b;
}
.register-link {
margin-left: 8rpx;
font-size: 26rpx;
- color: #1a73e8;
+ font-weight: 600;
+ color: #1e5ebc;
+}
+
+/* 底部 Home 条:设计稿 135×5pt → 270×10rpx,R2.5 → 5rpx,#0F172A25 */
+.home-indicator {
+ width: 270rpx;
+ height: 10rpx;
+ margin: 24rpx auto 0;
+ border-radius: 5rpx;
+ background-color: rgba(15, 23, 42, 0.145);
}
/* ======================== 未勾选协议确认弹窗 ======================== */
-/* 遮罩层:全屏半透明,居中承载弹窗 */
.modal-mask {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
- background-color: rgba(0, 0, 0, 0.45);
display: flex;
align-items: center;
justify-content: center;
z-index: 999;
+ background-color: rgba(15, 23, 42, 0.45);
}
.modal-box {
width: 560rpx;
- background-color: #ffffff;
- border-radius: 16rpx;
overflow: hidden;
+ border-radius: 24rpx;
+ background-color: #ffffff;
}
-/* 标题:绿色居中,下方带分隔线 */
.modal-title {
display: block;
padding: 40rpx 32rpx 30rpx;
font-size: 34rpx;
font-weight: 600;
- color: #8bc34a;
text-align: center;
- border-bottom: 1rpx solid #ececec;
+ color: #003e7e;
+ border-bottom: 2rpx solid #e2e8f0;
}
-/* 正文:灰色居中的提示文案 */
.modal-content {
display: block;
padding: 44rpx 48rpx;
font-size: 28rpx;
line-height: 42rpx;
- color: #9aa1ab;
text-align: center;
+ color: #64748b;
}
-/* 按钮行:取消 / 我已阅读并同意 各占一半 */
.modal-actions {
display: flex;
align-items: center;
@@ -367,10 +836,10 @@
}
.modal-btn--cancel {
- color: #1f2329;
+ color: #334155;
}
.modal-btn--confirm {
- color: #8bc34a;
+ color: #1e5ebc;
}
</style>
--
Gitblit v1.7.1