| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585 |
- <route lang="json5" type="page">
- {
- style: {
- navigationBarTitleText: '登录',
- navigationStyle: 'custom',
- },
- }
- </route>
- <template>
- <view class="login-container">
- <!-- 背景装饰元素 -->
- <view class="bg-decoration bg-circle-1"></view>
- <view class="bg-decoration bg-circle-2"></view>
- <view class="bg-decoration bg-circle-3"></view>
- <view class="login-header">
- <image class="login-logo" :src="appLogo" mode="aspectFit"></image>
- <view class="login-title">{{ appTitle }}</view>
- </view>
- <view class="login-form">
- <view class="welcome-text">欢迎登录</view>
- <view class="login-desc">请输入您的账号和密码</view>
- <view class="login-input-group">
- <view class="input-wrapper">
- <wd-input
- v-model="loginForm.username"
- prefix-icon="user"
- placeholder="请输入用户名"
- clearable
- class="login-input"
- :border="false"
- required
- ></wd-input>
- <view class="input-bottom-line"></view>
- </view>
- <view class="input-wrapper">
- <wd-input
- v-model="loginForm.password"
- prefix-icon="lock-on"
- placeholder="请输入密码"
- clearable
- show-password
- class="login-input"
- :border="false"
- required
- ></wd-input>
- <view class="input-bottom-line"></view>
- </view>
- <!-- 验证码区域 -->
- <view class="input-wrapper captcha-wrapper">
- <wd-input
- v-if="captcha.captchaEnabled"
- v-model="loginForm.code"
- prefix-icon="secured"
- placeholder="请输入验证码"
- clearable
- class="login-input captcha-input"
- :border="false"
- required
- >
- <template #suffix>
- <image
- class="captcha-image"
- :src="'data:image/gif;base64,' + captcha.image"
- mode="aspectFit"
- @click="refreshCaptcha"
- ></image>
- </template>
- </wd-input>
- <view class="input-bottom-line"></view>
- </view>
- </view>
- <!-- 登录按钮组 -->
- <view class="login-buttons">
- <!-- 账号密码登录按钮 -->
- <wd-button
- type="primary"
- size="large"
- block
- @click="handleAccountLogin"
- class="account-login-btn"
- >
- <wd-icon name="right" size="18px" class="login-icon"></wd-icon>
- 登录
- </wd-button>
- <!-- 微信小程序一键登录按钮 -->
- <!-- #ifdef MP-WEIXIN -->
- <view class="divider">
- <view class="divider-line"></view>
- <view class="divider-text">或</view>
- <view class="divider-line"></view>
- </view>
- <wd-button
- type="info"
- size="large"
- block
- plain
- @click="handleWechatLogin"
- class="wechat-login-btn"
- >
- 微信一键登录
- </wd-button>
- <!-- #endif -->
- </view>
- </view>
- <!-- 隐私协议勾选 -->
- <view class="privacy-agreement">
- <wd-checkbox
- v-model="agreePrivacy"
- shape="square"
- class="privacy-checkbox"
- active-color="var(--wot-color-theme, #1989fa)"
- >
- <view class="agreement-text">
- 我已阅读并同意
- <text class="agreement-link" @click.stop="handleAgreement('user')">《用户协议》</text>
- 和
- <text class="agreement-link" @click.stop="handleAgreement('privacy')">《隐私政策》</text>
- </view>
- </wd-checkbox>
- </view>
- <view class="login-footer"></view>
- </view>
- </template>
- <script setup lang="ts">
- import { ref } from 'vue'
- import { useUserStore } from '@/store/user'
- import { isMpWeixin } from '@/utils/platform'
- import { getCode, ILoginForm } from '@/api/login'
- import { toast } from '@/utils/toast'
- import { isTableBar } from '@/utils/index'
- import { ICaptcha } from '@/api/login.typings'
- const redirectRoute = ref('')
- // 获取环境变量
- const appTitle = ref(import.meta.env.VITE_APP_TITLE || 'Unibest Login')
- const appLogo = ref(import.meta.env.VITE_APP_LOGO || '/static/logo.svg')
- // 初始化store
- const userStore = useUserStore()
- // 路由位置
- // 验证码图片
- const captcha = ref<ICaptcha>({
- captchaEnabled: false,
- uuid: '',
- image: '',
- })
- // 登录表单数据
- const loginForm = ref<ILoginForm>({
- username: 'admin',
- password: '123456',
- code: '',
- uuid: '',
- })
- // 隐私协议勾选状态
- const agreePrivacy = ref(true)
- // 页面加载完毕时触发
- onLoad((option) => {
- // 一进来就刷新验证码
- captcha.value.captchaEnabled && refreshCaptcha()
- // 获取跳转路由
- if (option.redirect) {
- redirectRoute.value = option.redirect
- }
- })
- // 账号密码登录
- const handleAccountLogin = async () => {
- if (!agreePrivacy.value) {
- toast.error('请阅读同意协议')
- return
- }
- // 表单验证
- if (!loginForm.value.username) {
- toast.error('请输入用户名')
- return
- }
- if (!loginForm.value.password) {
- toast.error('请输入密码')
- return
- }
- if (captcha.value.captchaEnabled && !loginForm.value.code) {
- toast.error('请输入验证码')
- return
- }
- // 执行登录
- await userStore.login(loginForm.value)
- // 跳转到首页或重定向页面
- const targetUrl = redirectRoute.value || '/pages/index/index'
- if (isTableBar(targetUrl)) {
- uni.switchTab({ url: targetUrl })
- } else {
- uni.redirectTo({ url: targetUrl })
- }
- }
- // 微信登录
- const handleWechatLogin = async () => {
- if (!isMpWeixin) {
- toast.info('请在微信小程序中使用此功能')
- return
- }
- // 验证是否同意隐私协议
- if (!agreePrivacy.value) {
- toast.error('请先阅读并同意用户协议和隐私政策')
- return
- }
- // 微信登录
- await userStore.wxLogin()
- // 跳转到首页或重定向页面
- const targetUrl = redirectRoute.value || '/pages/index/index'
- if (isTableBar(targetUrl)) {
- uni.switchTab({ url: targetUrl })
- } else {
- uni.redirectTo({ url: targetUrl })
- }
- }
- // 刷新验证码
- const refreshCaptcha = () => {
- // 获取验证码
- getCode().then((res) => {
- const { data } = res
- loginForm.value.uuid = data.uuid
- captcha.value = data
- })
- }
- // 处理协议点击
- const handleAgreement = (type: 'user' | 'privacy') => {
- const title = type === 'user' ? '用户协议' : '隐私政策'
- // showToast(`查看${title}`)
- // 实际项目中可以跳转到对应的协议页面
- // uni.navigateTo({
- // url: `/pages/agreement/${type}`
- // })
- }
- </script>
- <style lang="scss" scoped>
- /* 验证码输入框样式 */
- .captcha-wrapper {
- .captcha-input {
- :deep(.wd-input__suffix) {
- margin-right: 0;
- padding-right: 0;
- }
- }
- .captcha-image {
- width: 100px;
- height: 36px;
- margin-left: 10px;
- border-radius: 8px;
- cursor: pointer;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
- transition: all 0.3s ease;
- position: relative;
- overflow: hidden;
- &::after {
- content: '';
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: linear-gradient(to bottom, rgba(255, 255, 255, 0.1), transparent);
- pointer-events: none;
- }
- &:active {
- opacity: 0.8;
- transform: scale(0.96);
- box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
- }
- }
- }
- .login-container {
- box-sizing: border-box;
- display: flex;
- flex-direction: column;
- min-height: 100vh;
- padding: 0 70rpx;
- background-color: #ffffff;
- background-image: linear-gradient(
- 135deg,
- rgba(25, 137, 250, 0.05) 0%,
- rgba(255, 255, 255, 0) 100%
- );
- position: relative;
- overflow: hidden;
- }
- /* 背景装饰元素 */
- .bg-decoration {
- position: absolute;
- border-radius: 50%;
- background: linear-gradient(135deg, rgba(25, 137, 250, 0.05), rgba(25, 137, 250, 0.1));
- z-index: 0;
- pointer-events: none;
- }
- .bg-circle-1 {
- width: 500rpx;
- height: 500rpx;
- top: -200rpx;
- right: -200rpx;
- opacity: 0.6;
- }
- .bg-circle-2 {
- width: 400rpx;
- height: 400rpx;
- bottom: 10%;
- left: -200rpx;
- opacity: 0.4;
- }
- .bg-circle-3 {
- width: 300rpx;
- height: 300rpx;
- bottom: -100rpx;
- right: 10%;
- opacity: 0.3;
- background: linear-gradient(135deg, rgba(7, 193, 96, 0.05), rgba(7, 193, 96, 0.1));
- }
- .login-header {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- margin-top: 120rpx;
- animation: fadeInDown 0.8s ease-out;
- .login-logo {
- width: 200rpx;
- height: 200rpx;
- border-radius: 36rpx;
- box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.12);
- transition: all 0.3s ease;
- &:active {
- transform: scale(0.95);
- box-shadow: 0 6rpx 15rpx rgba(0, 0, 0, 0.1);
- }
- }
- .login-title {
- margin-top: 30rpx;
- font-size: 46rpx;
- font-weight: bold;
- color: #333333;
- letter-spacing: 3rpx;
- text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.05);
- }
- }
- .login-form {
- flex: 1;
- margin-top: 70rpx;
- animation: fadeIn 0.8s ease-out 0.2s both;
- .welcome-text {
- margin-bottom: 16rpx;
- font-size: 48rpx;
- font-weight: bold;
- color: #333333;
- text-align: center;
- letter-spacing: 1rpx;
- }
- .login-desc {
- margin-bottom: 70rpx;
- font-size: 28rpx;
- color: #888888;
- text-align: center;
- }
- .login-input-group {
- margin-bottom: 60rpx;
- position: relative;
- z-index: 1;
- .input-wrapper {
- position: relative;
- margin-bottom: 50rpx;
- transition: all 0.3s ease;
- border-radius: 16rpx;
- overflow: hidden;
- &:last-child {
- margin-bottom: 0;
- }
- .login-input {
- padding: 12rpx 20rpx;
- background-color: rgba(245, 247, 250, 0.7);
- border-radius: 16rpx;
- transition: all 0.3s ease;
- :deep(.wd-input__inner) {
- font-size: 30rpx;
- color: #333333;
- }
- :deep(.wd-input__placeholder) {
- font-size: 28rpx;
- color: #aaaaaa;
- }
- &:focus-within {
- background-color: rgba(245, 247, 250, 0.95);
- box-shadow: 0 6rpx 16rpx rgba(0, 0, 0, 0.06);
- transform: translateY(-3rpx);
- }
- }
- .input-bottom-line {
- position: absolute;
- bottom: -2rpx;
- left: 5%;
- width: 90%;
- height: 2rpx;
- background: linear-gradient(
- to right,
- transparent,
- var(--wot-color-theme, #1989fa),
- transparent
- );
- transition: transform 0.4s ease;
- transform: scaleX(0);
- opacity: 0.8;
- }
- &:focus-within .input-bottom-line {
- transform: scaleX(1);
- }
- .input-icon {
- margin-right: 16rpx;
- color: #666666;
- transition: color 0.3s ease;
- }
- &:focus-within .input-icon {
- color: var(--wot-color-theme, #1989fa);
- }
- }
- }
- .login-buttons {
- display: flex;
- flex-direction: column;
- gap: 36rpx;
- .account-login-btn {
- height: 96rpx;
- margin-top: 20rpx;
- font-size: 32rpx;
- font-weight: 500;
- letter-spacing: 2rpx;
- border-radius: 48rpx;
- box-shadow: 0 10rpx 20rpx rgba(25, 137, 250, 0.25);
- transition: all 0.3s ease;
- display: flex;
- align-items: center;
- justify-content: center;
- .login-icon {
- margin-right: 8rpx;
- opacity: 0.8;
- transition: all 0.3s ease;
- }
- &:active {
- box-shadow: 0 5rpx 10rpx rgba(25, 137, 250, 0.2);
- transform: scale(0.98);
- .login-icon {
- transform: translateX(3rpx);
- }
- }
- }
- .divider {
- display: flex;
- align-items: center;
- margin: 24rpx 0;
- .divider-line {
- flex: 1;
- height: 1px;
- background-color: #eeeeee;
- }
- .divider-text {
- padding: 0 24rpx;
- font-size: 24rpx;
- color: #999999;
- }
- }
- .wechat-login-btn {
- height: 96rpx;
- font-size: 32rpx;
- color: #07c160;
- border-color: #07c160;
- border-radius: 48rpx;
- transition: all 0.3s ease;
- .wechat-icon {
- margin-right: 12rpx;
- }
- &:active {
- background-color: rgba(7, 193, 96, 0.08);
- transform: scale(0.98);
- }
- }
- }
- }
- .privacy-agreement {
- display: flex;
- justify-content: center;
- margin: 30rpx 0 40rpx;
- animation: fadeIn 0.8s ease-out 0.4s both;
- .privacy-checkbox {
- display: flex;
- align-items: center;
- }
- .agreement-text {
- font-size: 26rpx;
- line-height: 1.6;
- color: #666666;
- .agreement-link {
- padding: 0 4rpx;
- font-weight: 500;
- color: var(--wot-color-theme, #1989fa);
- transition: all 0.3s ease;
- &:active {
- opacity: 0.8;
- transform: scale(0.98);
- }
- }
- }
- }
- .login-footer {
- padding: 50rpx 0;
- margin-top: auto;
- }
- /* 添加动画效果 */
- @keyframes fadeIn {
- from {
- opacity: 0;
- }
- to {
- opacity: 1;
- }
- }
- @keyframes fadeInDown {
- from {
- opacity: 0;
- transform: translateY(-20px);
- }
- to {
- opacity: 1;
- transform: translateY(0);
- }
- }
- </style>
|