修复app登录和接口问题
This commit is contained in:
+239
-272
@@ -11,7 +11,7 @@
|
||||
<view class="shape shape-3"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 主要内容 -->
|
||||
<view class="login-container">
|
||||
<!-- 头部区域 -->
|
||||
@@ -27,73 +27,90 @@
|
||||
<text class="welcome-subtitle">欢迎回来,开始您的工作之旅</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 登录卡片 -->
|
||||
<view class="login-card">
|
||||
<view class="card-header">
|
||||
<text class="card-title">登录账户</text>
|
||||
<view class="card-subtitle">请输入您的登录信息</view>
|
||||
</view>
|
||||
|
||||
|
||||
<view class="form-container">
|
||||
<!-- 用户名输入 -->
|
||||
<view class="input-field-group">
|
||||
<view class="input-container" :class="{ 'focused': usernameFocused, 'error': usernameError }">
|
||||
<view
|
||||
class="input-container"
|
||||
:class="{ focused: accountFocused, error: accountError }"
|
||||
>
|
||||
<view class="input-icon-wrapper">
|
||||
<i class="fas fa-user input-icon"></i>
|
||||
</view>
|
||||
<input
|
||||
v-model="form.username"
|
||||
placeholder="用户名"
|
||||
<input
|
||||
v-model="form.account"
|
||||
placeholder="用户名"
|
||||
class="input"
|
||||
type="text"
|
||||
@focus="handleUsernameFocus"
|
||||
@blur="handleUsernameBlur"
|
||||
@input="clearUsernameError">
|
||||
@focus="handleAccountFocus"
|
||||
@blur="handleAccountBlur"
|
||||
@input="clearAccountError"
|
||||
/>
|
||||
<view class="input-border"></view>
|
||||
</view>
|
||||
<text class="error-text" v-if="usernameError">{{ usernameError }}</text>
|
||||
<text class="error-text" v-if="accountError">{{
|
||||
accountError
|
||||
}}</text>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 密码输入 -->
|
||||
<view class="input-field-group">
|
||||
<view class="input-container" :class="{ 'focused': passwordFocused, 'error': passwordError }">
|
||||
<view
|
||||
class="input-container"
|
||||
:class="{ focused: passwordFocused, error: passwordError }"
|
||||
>
|
||||
<view class="input-icon-wrapper">
|
||||
<i class="fas fa-lock input-icon"></i>
|
||||
</view>
|
||||
<input
|
||||
v-model="form.password"
|
||||
placeholder="密码"
|
||||
<input
|
||||
v-model="form.password"
|
||||
placeholder="密码"
|
||||
class="input"
|
||||
:type="showPassword ? 'text' : 'password'"
|
||||
@focus="handlePasswordFocus"
|
||||
@blur="handlePasswordBlur"
|
||||
@input="clearPasswordError">
|
||||
@input="clearPasswordError"
|
||||
/>
|
||||
<view class="password-toggle" @click="togglePassword">
|
||||
<i :class="showPassword ? 'fas fa-eye-slash' : 'fas fa-eye'"></i>
|
||||
<i
|
||||
:class="showPassword ? 'fas fa-eye-slash' : 'fas fa-eye'"
|
||||
></i>
|
||||
</view>
|
||||
<view class="input-border"></view>
|
||||
</view>
|
||||
<text class="error-text" v-if="passwordError">{{ passwordError }}</text>
|
||||
<text class="error-text" v-if="passwordError">{{
|
||||
passwordError
|
||||
}}</text>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 选项区域 -->
|
||||
<view class="options-row">
|
||||
<view class="remember-section" @click="toggleRemember">
|
||||
<view class="custom-checkbox" :class="{ 'checked': rememberMe }">
|
||||
<view class="custom-checkbox" :class="{ checked: rememberMe }">
|
||||
<i class="fas fa-check" v-if="rememberMe"></i>
|
||||
</view>
|
||||
<text class="remember-text">记住我</text>
|
||||
</view>
|
||||
<text class="forgot-link" @click="handleForgotPassword">忘记密码?</text>
|
||||
<text class="forgot-link" @click="handleForgotPassword"
|
||||
>忘记密码?</text
|
||||
>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 登录按钮 -->
|
||||
<button
|
||||
:disabled="loading || !isFormValid"
|
||||
class="login-button"
|
||||
:class="{ 'loading': loading, 'disabled': !isFormValid }"
|
||||
@click="handleLogin">
|
||||
:class="{ loading: loading, disabled: !isFormValid }"
|
||||
@click="handleLogin"
|
||||
>
|
||||
<view class="button-content">
|
||||
<view class="button-icon" v-if="!loading">
|
||||
<i class="fas fa-arrow-right"></i>
|
||||
@@ -101,12 +118,14 @@
|
||||
<view class="loading-spinner" v-if="loading">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
</view>
|
||||
<text class="button-text">{{ loading ? '登录中...' : '立即登录' }}</text>
|
||||
<text class="button-text">{{
|
||||
loading ? "登录中..." : "立即登录"
|
||||
}}</text>
|
||||
</view>
|
||||
<view class="button-shine" v-if="!loading"></view>
|
||||
</button>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 测试提示 -->
|
||||
<view class="test-tips">
|
||||
<view class="tips-icon">
|
||||
@@ -119,228 +138,161 @@
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { userApi } from '../../src/api/index.js'
|
||||
import { useAuthStore } from '../../src/store/authStore.js'
|
||||
import { redirectAfterLogin } from '../../src/utils/routeGuard.js'
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, computed } from "vue";
|
||||
import loginApi from "@/src/api/login";
|
||||
import { useAuthStore } from "@/src/store/authStore.js";
|
||||
import { redirectAfterLogin } from "@/src/utils/routeGuard.js";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {
|
||||
username: '',
|
||||
password: ''
|
||||
},
|
||||
loading: false,
|
||||
usernameFocused: false,
|
||||
passwordFocused: false,
|
||||
showPassword: false,
|
||||
rememberMe: false,
|
||||
usernameError: '',
|
||||
passwordError: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isFormValid() {
|
||||
return this.form.username.trim() && this.form.password.trim()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async handleLogin() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await userApi.login(this.form);
|
||||
console.log('登录响应:', res);
|
||||
|
||||
// 根据你的后端返回结构调整
|
||||
const token = res.accessToken || res.token;
|
||||
|
||||
if (!token) {
|
||||
throw new Error('登录失败:未获取到访问令牌');
|
||||
}
|
||||
|
||||
const authStore = useAuthStore();
|
||||
// 从响应中获取用户信息
|
||||
const userInfo = res.user || {
|
||||
username: this.form.username,
|
||||
id: res.id
|
||||
};
|
||||
|
||||
authStore.login(userInfo, token);
|
||||
|
||||
uni.showToast({
|
||||
title: '登录成功',
|
||||
icon: 'success'
|
||||
});
|
||||
|
||||
// 延迟跳转,让用户看到成功提示
|
||||
setTimeout(() => {
|
||||
redirectAfterLogin();
|
||||
}, 500);
|
||||
} catch (e) {
|
||||
this.loading = false;
|
||||
console.error('登录错误:', e);
|
||||
|
||||
// 显示错误信息
|
||||
const errorMessage = e.message || e.msg || '登录失败,请检查网络连接';
|
||||
uni.showToast({
|
||||
title: errorMessage,
|
||||
icon: 'none',
|
||||
duration: 3000
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// async handleLogin2() {
|
||||
// if(!this.form.username || !this.form.password) {
|
||||
// uni.showToast({
|
||||
// title: '请输入用户名和密码',
|
||||
// icon: 'none'
|
||||
// });
|
||||
// return;
|
||||
// }
|
||||
|
||||
// this.loading = true;
|
||||
|
||||
// try {
|
||||
// // 模拟登录API请求
|
||||
// // 实际项目中应该调用真实的后端API
|
||||
// const mockLogin = () => {
|
||||
// return new Promise((resolve) => {
|
||||
// setTimeout(() => {
|
||||
// // 模拟登录成功
|
||||
// if (this.form.username === 'admin' && this.form.password === '123456') {
|
||||
// resolve({
|
||||
// code: 0,
|
||||
// message: '登录成功',
|
||||
// data: {
|
||||
// token: 'mock_token_' + Date.now(),
|
||||
// userInfo: {
|
||||
// id: 1,
|
||||
// username: this.form.username,
|
||||
// name: '管理员',
|
||||
// avatar: '/static/logo.png',
|
||||
// department: '技术部',
|
||||
// role: 'admin'
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// } else {
|
||||
// resolve({
|
||||
// code: 1,
|
||||
// message: '用户名或密码错误'
|
||||
// });
|
||||
// }
|
||||
// }, 1000);
|
||||
// });
|
||||
// };
|
||||
|
||||
// const result = await mockLogin();
|
||||
|
||||
// if (result.code === 0) {
|
||||
// // 使用 Pinia store 管理登录状态
|
||||
// const authStore = useAuthStore();
|
||||
// authStore.login(result.data.userInfo, result.data.token);
|
||||
|
||||
// uni.showToast({
|
||||
// title: '登录成功',
|
||||
// icon: 'success'
|
||||
// });
|
||||
|
||||
// // 延迟跳转,让用户看到成功提示
|
||||
// setTimeout(() => {
|
||||
// redirectAfterLogin();
|
||||
// }, 500);
|
||||
// } else {
|
||||
// uni.showToast({
|
||||
// title: result.message || '登录失败',
|
||||
// icon: 'none'
|
||||
// });
|
||||
// }
|
||||
// } catch (error) {
|
||||
// console.error('登录错误:', error);
|
||||
// uni.showToast({
|
||||
// title: '网络异常,请稍后重试',
|
||||
// icon: 'none'
|
||||
// });
|
||||
// }
|
||||
|
||||
// this.loading = false;
|
||||
// },
|
||||
|
||||
// 切换密码显示
|
||||
togglePassword() {
|
||||
this.showPassword = !this.showPassword
|
||||
},
|
||||
|
||||
// 切换记住我
|
||||
toggleRemember() {
|
||||
this.rememberMe = !this.rememberMe
|
||||
},
|
||||
|
||||
// 忘记密码
|
||||
handleForgotPassword() {
|
||||
uni.showToast({
|
||||
title: '请联系管理员重置密码',
|
||||
icon: 'none'
|
||||
})
|
||||
},
|
||||
|
||||
// 处理用户名输入框焦点
|
||||
handleUsernameFocus() {
|
||||
this.usernameFocused = true;
|
||||
this.clearUsernameError();
|
||||
},
|
||||
|
||||
handleUsernameBlur() {
|
||||
this.usernameFocused = false;
|
||||
this.validateUsername();
|
||||
},
|
||||
|
||||
// 处理密码输入框焦点
|
||||
handlePasswordFocus() {
|
||||
this.passwordFocused = true;
|
||||
this.clearPasswordError();
|
||||
},
|
||||
|
||||
handlePasswordBlur() {
|
||||
this.passwordFocused = false;
|
||||
this.validatePassword();
|
||||
},
|
||||
|
||||
// 清除用户名错误
|
||||
clearUsernameError() {
|
||||
this.usernameError = '';
|
||||
},
|
||||
|
||||
// 清除密码错误
|
||||
clearPasswordError() {
|
||||
this.passwordError = '';
|
||||
},
|
||||
|
||||
// 验证用户名
|
||||
validateUsername() {
|
||||
if (!this.form.username.trim()) {
|
||||
this.usernameError = '请输入用户名';
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
// 验证密码
|
||||
validatePassword() {
|
||||
if (!this.form.password.trim()) {
|
||||
this.passwordError = '请输入密码';
|
||||
return false;
|
||||
}
|
||||
if (this.form.password.length < 6) {
|
||||
this.passwordError = '密码至少6位';
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
interface LoginForm {
|
||||
account: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
interface LoginUser {
|
||||
id: number;
|
||||
account: string;
|
||||
name: string;
|
||||
group_id: number;
|
||||
}
|
||||
|
||||
interface LoginResponseData {
|
||||
token: string;
|
||||
user?: LoginUser;
|
||||
id?: number;
|
||||
}
|
||||
|
||||
const form = reactive<LoginForm>({
|
||||
account: "",
|
||||
password: "",
|
||||
});
|
||||
|
||||
const loading = ref(false);
|
||||
const accountFocused = ref(false);
|
||||
const passwordFocused = ref(false);
|
||||
const showPassword = ref(false);
|
||||
const rememberMe = ref(false);
|
||||
const accountError = ref("");
|
||||
const passwordError = ref("");
|
||||
|
||||
const authStore = useAuthStore();
|
||||
|
||||
const isFormValid = computed<boolean>(() => {
|
||||
return form.account.trim() !== "" && form.password.trim() !== "";
|
||||
});
|
||||
|
||||
async function handleLogin() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = (await loginApi.login(form)) as LoginResponseData;
|
||||
console.log("登录响应:", res);
|
||||
|
||||
const { token, user, id } = res || {};
|
||||
|
||||
if (!token) {
|
||||
throw new Error("登录失败:未获取到访问令牌");
|
||||
}
|
||||
|
||||
const userInfo: LoginUser | { account: string; id?: number } = user || {
|
||||
account: form.account,
|
||||
id: id,
|
||||
};
|
||||
|
||||
authStore.login(userInfo, token);
|
||||
|
||||
uni.showToast({
|
||||
title: "登录成功",
|
||||
icon: "success",
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
redirectAfterLogin();
|
||||
}, 500);
|
||||
} catch (e: any) {
|
||||
loading.value = false;
|
||||
console.error("登录错误:", e);
|
||||
|
||||
const errorMessage: string =
|
||||
e?.message || e?.msg || "登录失败,请检查网络连接";
|
||||
uni.showToast({
|
||||
title: errorMessage,
|
||||
icon: "none",
|
||||
duration: 3000,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 切换密码显示
|
||||
function togglePassword() {
|
||||
showPassword.value = !showPassword.value;
|
||||
}
|
||||
|
||||
// 切换记住我
|
||||
function toggleRemember() {
|
||||
rememberMe.value = !rememberMe.value;
|
||||
}
|
||||
|
||||
// 忘记密码
|
||||
function handleForgotPassword() {
|
||||
uni.showToast({
|
||||
title: "请联系管理员重置密码",
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
|
||||
// 处理用户名输入框焦点
|
||||
function handleAccountFocus() {
|
||||
accountFocused.value = true;
|
||||
clearAccountError();
|
||||
}
|
||||
|
||||
function handleAccountBlur() {
|
||||
accountFocused.value = false;
|
||||
validateAccount();
|
||||
}
|
||||
|
||||
// 处理密码输入框焦点
|
||||
function handlePasswordFocus() {
|
||||
passwordFocused.value = true;
|
||||
clearPasswordError();
|
||||
}
|
||||
|
||||
function handlePasswordBlur() {
|
||||
passwordFocused.value = false;
|
||||
validatePassword();
|
||||
}
|
||||
|
||||
// 清除用户名错误
|
||||
function clearAccountError() {
|
||||
accountError.value = "";
|
||||
}
|
||||
|
||||
// 清除密码错误
|
||||
function clearPasswordError() {
|
||||
passwordError.value = "";
|
||||
}
|
||||
|
||||
// 验证用户名
|
||||
function validateAccount(): boolean {
|
||||
if (!form.account.trim()) {
|
||||
accountError.value = "请输入用户名";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 验证密码
|
||||
function validatePassword(): boolean {
|
||||
if (!form.password.trim()) {
|
||||
passwordError.value = "请输入密码";
|
||||
return false;
|
||||
}
|
||||
if (form.password.length < 6) {
|
||||
passwordError.value = "密码至少6位";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -370,7 +322,11 @@ export default {
|
||||
.gradient-orb {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(45deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.05));
|
||||
background: linear-gradient(
|
||||
45deg,
|
||||
rgba(255, 255, 255, 0.1),
|
||||
rgba(255, 255, 255, 0.05)
|
||||
);
|
||||
animation: float 8s ease-in-out infinite;
|
||||
filter: blur(1rpx);
|
||||
}
|
||||
@@ -439,12 +395,13 @@ export default {
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% {
|
||||
transform: translateY(0px) rotate(0deg) scale(1);
|
||||
0%,
|
||||
100% {
|
||||
transform: translateY(0px) rotate(0deg) scale(1);
|
||||
opacity: 0.7;
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-30px) rotate(180deg) scale(1.1);
|
||||
50% {
|
||||
transform: translateY(-30px) rotate(180deg) scale(1.1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@@ -493,11 +450,12 @@ export default {
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
0%,
|
||||
100% {
|
||||
transform: scale(1);
|
||||
opacity: 0.7;
|
||||
}
|
||||
50% {
|
||||
50% {
|
||||
transform: scale(1.05);
|
||||
opacity: 1;
|
||||
}
|
||||
@@ -536,7 +494,7 @@ export default {
|
||||
}
|
||||
|
||||
.login-card::before {
|
||||
content: '';
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@@ -811,13 +769,22 @@ export default {
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
transparent,
|
||||
rgba(255, 255, 255, 0.2),
|
||||
transparent
|
||||
);
|
||||
animation: shine 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes shine {
|
||||
0% { left: -100%; }
|
||||
100% { left: 100%; }
|
||||
0% {
|
||||
left: -100%;
|
||||
}
|
||||
100% {
|
||||
left: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* 测试提示 */
|
||||
@@ -849,23 +816,23 @@ export default {
|
||||
.login-container {
|
||||
max-width: 95%;
|
||||
}
|
||||
|
||||
|
||||
.login-card {
|
||||
padding: 40rpx 30rpx;
|
||||
}
|
||||
|
||||
|
||||
.app-title {
|
||||
font-size: 42rpx;
|
||||
}
|
||||
|
||||
|
||||
.welcome-subtitle {
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
|
||||
.card-title {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
|
||||
.card-subtitle {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
@@ -876,50 +843,50 @@ export default {
|
||||
.login-page {
|
||||
background: var(--gradient-primary);
|
||||
}
|
||||
|
||||
|
||||
.login-card {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
|
||||
.card-title {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
|
||||
.card-subtitle {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
|
||||
.input-container {
|
||||
background: var(--gray-lighter);
|
||||
border-color: var(--border);
|
||||
}
|
||||
|
||||
|
||||
.input-container.focused {
|
||||
background: var(--white);
|
||||
}
|
||||
|
||||
|
||||
.input {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
|
||||
.input::placeholder {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
|
||||
.remember-text {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
|
||||
.forgot-link {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
|
||||
.test-tips {
|
||||
background: var(--info-light);
|
||||
border-color: var(--primary-light);
|
||||
}
|
||||
|
||||
|
||||
.tips-text {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user