| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372 |
- <script lang="ts" setup>
- import type { ILoginForm } from '@/api/login'
- import type { ICaptcha } from '@/api/types/login'
- import { computed, onMounted, ref } from 'vue'
- import { getCaptcha } from '@/api/login'
- import { useAuthStore } from '@/store/auth'
- definePage({
- style: {
- navigationBarTitleText: '登录',
- },
- })
- const authStore = useAuthStore()
- // 验证码响应实体类
- const captchaInfo = ref<ICaptcha>({
- captchaEnabled: false,
- img: '',
- uuid: '',
- })
- // 登录表单数据
- const loginForm = ref<ILoginForm>({
- username: '',
- password: '',
- code: '',
- uuid: '',
- })
- // 验证码相关数据
- const captchaData = ref({
- code: '',
- uuid: '',
- })
- // 重定向地址
- const redirectUrl = ref('')
- // 表单验证规则
- const formRules = computed(() => ({
- username: {
- required: true,
- message: '请输入用户名',
- trigger: 'blur',
- },
- password: {
- required: true,
- message: '请输入密码',
- trigger: 'blur',
- },
- code: {
- required: captchaInfo.value.captchaEnabled,
- message: '请输入验证码',
- trigger: 'blur',
- },
- }))
- // 获取验证码接口
- async function loadCaptcha() {
- try {
- const resp = await getCaptcha()
- console.log('获取验证码响应:', resp)
- if (resp.captchaEnabled && resp.img) {
- resp.img = `data:image/png;base64,${resp.img}`
- }
- captchaInfo.value = resp
- loginForm.value.uuid = resp.uuid || ''
- }
- catch (error) {
- console.error('获取验证码失败:', error)
- uni.showToast({
- title: '获取验证码失败',
- icon: 'error',
- })
- }
- }
- // uni-app页面生命周期钩子
- onLoad((options) => {
- // 获取重定向地址
- if (options?.redirect) {
- redirectUrl.value = decodeURIComponent(options.redirect)
- }
- })
- onMounted(async () => {
- await loadCaptcha()
- })
- // 处理登录
- async function handleLogin() {
- try {
- // 构建登录参数
- const requestParam: ILoginForm = {
- username: loginForm.value.username,
- password: loginForm.value.password,
- code: '',
- uuid: '',
- }
- // 如果需要验证码,添加验证码参数
- if (captchaInfo.value.captchaEnabled) {
- requestParam.code = loginForm.value.code
- requestParam.uuid = loginForm.value.uuid
- }
- // 调用登录接口
- await authStore.authLogin(requestParam)
- uni.showToast({
- title: '登录成功',
- icon: 'success',
- })
- // 登录成功后跳转
- setTimeout(() => {
- if (redirectUrl.value) {
- // 如果有重定向地址,跳转到指定页面
- if (redirectUrl.value.startsWith('/pages/')) {
- // 检查是否为tabbar页面
- if (redirectUrl.value === '/pages/index/index' || redirectUrl.value === '/pages/me/me') {
- uni.switchTab({
- url: redirectUrl.value,
- })
- }
- else {
- uni.redirectTo({
- url: redirectUrl.value,
- })
- }
- }
- else {
- uni.redirectTo({
- url: redirectUrl.value,
- })
- }
- }
- else {
- // 没有重定向地址,跳转到首页
- uni.switchTab({
- url: '/pages/index/index',
- })
- }
- }, 1500)
- }
- catch (error) {
- console.error('登录失败:', error)
- uni.showToast({
- title: '登录失败,请检查用户名密码',
- icon: 'error',
- })
- // 处理验证码错误,刷新验证码
- if (captchaInfo.value.captchaEnabled) {
- await loadCaptcha()
- loginForm.value.code = ''
- }
- }
- }
- // 表单提交
- function onSubmit() {
- handleLogin()
- }
- // 刷新验证码
- function refreshCaptcha() {
- loadCaptcha()
- }
- // 表单验证
- const formRef = ref()
- function validateForm() {
- formRef.value?.validate().then(() => {
- onSubmit()
- }).catch(() => {
- uni.showToast({
- title: '请检查表单输入',
- icon: 'error',
- })
- })
- }
- </script>
- <template>
- <view class="login-container">
- <!-- 登录头部 -->
- <view class="login-header">
- <view class="logo-container">
- <image src="/static/logo.svg" class="logo" mode="aspectFit" />
- </view>
- <text class="login-title">用户登录</text>
- </view>
- <!-- 登录表单 -->
- <view class="login-form">
- <wd-form ref="formRef" :model="loginForm" :rules="formRules">
- <!-- 用户名输入 -->
- <wd-form-item prop="username" label="用户名" required>
- <wd-input v-model="loginForm.username" placeholder="请输入用户名" clearable :show-clear="true" />
- </wd-form-item>
- <!-- 密码输入 -->
- <wd-form-item prop="password" label="密码" required>
- <wd-input v-model="loginForm.password" type="password" placeholder="请输入密码" clearable :show-clear="true" :show-password="true" />
- </wd-form-item>
- <!-- 验证码输入 -->
- <wd-form-item v-if="captchaInfo && captchaInfo.captchaEnabled" prop="code" label="验证码" required>
- <view class="captcha-container">
- <wd-input v-model="loginForm.code" placeholder="请输入验证码" clearable :show-clear="true" class="captcha-input" />
- <view class="captcha-image-wrapper" @click="refreshCaptcha">
- <image v-if="captchaInfo && captchaInfo.img" :src="captchaInfo.img" class="captcha-image" mode="aspectFit" />
- <text v-else class="captcha-placeholder">点击获取</text>
- </view>
- </view>
- </wd-form-item>
- </wd-form>
- <!-- 登录按钮 -->
- <view class="login-actions">
- <wd-button type="primary" round block :loading="authStore.loginLoading" @click="validateForm">
- {{ authStore.loginLoading ? '登录中...' : '登录' }}
- </wd-button>
- </view>
- </view>
- </view>
- </template>
- <style lang="scss" scoped>
- .login-container {
- min-height: 100vh;
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
- padding: 0 32rpx;
- display: flex;
- flex-direction: column;
- justify-content: center;
- }
- .login-header {
- text-align: center;
- margin-bottom: 80rpx;
- .logo-container {
- margin-bottom: 40rpx;
- .logo {
- width: 120rpx;
- height: 120rpx;
- }
- }
- .login-title {
- display: block;
- font-size: 48rpx;
- font-weight: bold;
- color: white;
- margin-bottom: 16rpx;
- }
- .login-subtitle {
- display: block;
- font-size: 28rpx;
- color: rgba(255, 255, 255, 0.8);
- }
- }
- .login-form {
- background: white;
- border-radius: 24rpx;
- padding: 48rpx 32rpx;
- box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.1);
- .test-section {
- margin-bottom: 32rpx;
- padding: 16rpx;
- background: #f0f8ff;
- border-radius: 8rpx;
- text {
- display: block;
- margin-bottom: 16rpx;
- font-size: 28rpx;
- color: #333;
- }
- }
- :deep(.wd-form-item) {
- margin-bottom: 32rpx;
- }
- :deep(.wd-form-item__label) {
- font-weight: 500;
- color: #333;
- }
- :deep(.wd-input) {
- height: 88rpx;
- border-radius: 12rpx;
- background: #f8f9fa;
- }
- .captcha-container {
- display: flex;
- align-items: center;
- gap: 16rpx;
- .captcha-input {
- flex: 1;
- }
- .captcha-image-wrapper {
- width: 160rpx;
- height: 88rpx;
- border-radius: 12rpx;
- background: #f8f9fa;
- display: flex;
- align-items: center;
- justify-content: center;
- border: 2rpx solid #e9ecef;
- cursor: pointer;
- transition: all 0.3s ease;
- &:active {
- transform: scale(0.95);
- background: #e9ecef;
- }
- .captcha-image {
- width: 100%;
- height: 100%;
- border-radius: 8rpx;
- }
- .captcha-placeholder {
- font-size: 24rpx;
- color: #999;
- }
- }
- }
- .login-actions {
- margin: 48rpx 0 32rpx;
- :deep(.wd-button) {
- height: 96rpx;
- font-size: 32rpx;
- font-weight: 600;
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
- border: none;
- }
- }
- .login-footer {
- text-align: center;
- .footer-text {
- font-size: 28rpx;
- color: #666;
- }
- .register-link {
- font-size: 28rpx;
- color: #667eea;
- font-weight: 500;
- margin-left: 8rpx;
- }
- }
- }
- </style>
|