修复app登录和接口问题
This commit is contained in:
parent
f9d7f980bb
commit
a8a7435721
12
.env
12
.env
@ -1,16 +1,16 @@
|
||||
# ===========================================
|
||||
# API配置 - 根据你的后端接口修改
|
||||
# ===========================================
|
||||
# 开发环境
|
||||
VUE_APP_API_BASE_URL=https://localhost:8000
|
||||
VUE_APP_API_TIMEOUT=10000
|
||||
# 开发环境(Vite 只能识别 VITE_ 前缀)
|
||||
VITE_APP_API_BASE_URL=http://localhost:8000
|
||||
VITE_APP_API_TIMEOUT=10000
|
||||
|
||||
# ===========================================
|
||||
# 应用配置
|
||||
# ===========================================
|
||||
VUE_APP_APP_NAME=babyhealth
|
||||
VUE_APP_APP_VERSION=1.0.0
|
||||
VUE_APP_DEBUG=true
|
||||
VITE_APP_APP_NAME=babyhealth
|
||||
VITE_APP_APP_VERSION=1.0.0
|
||||
VITE_APP_DEBUG=true
|
||||
|
||||
# ===========================================
|
||||
# 其他配置 (可选)
|
||||
|
||||
10
env.d.ts
vendored
Normal file
10
env.d.ts
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue'
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
||||
|
||||
// uni-app 的全局对象(让 TS 不再提示 uni 未定义)
|
||||
declare const uni: any
|
||||
21
main.js
21
main.js
@ -1,29 +1,12 @@
|
||||
import { createSSRApp } from 'vue'
|
||||
// 引入 createPinia 方法(命名导出)
|
||||
import { createPinia } from 'pinia'
|
||||
import App from './App.vue'
|
||||
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
||||
// 引入 lime-echart 组件
|
||||
import LEchart from './uni_modules/lime-echart/components/l-echart/l-echart.vue'
|
||||
// FontAwesome CSS 将通过 App.vue 中的全局样式引入
|
||||
|
||||
export function createApp() {
|
||||
const app = createSSRApp(App)
|
||||
// 创建 Pinia 实例
|
||||
const pinia = createPinia()
|
||||
// 注册 Pinia 持久化
|
||||
pinia.use(piniaPluginPersistedstate)
|
||||
// 注册 Pinia
|
||||
pinia.use(piniaPluginPersistedstate)
|
||||
app.use(pinia)
|
||||
|
||||
// 暂时注释掉 uView,因为与 Vue 3 有兼容性问题
|
||||
// 后续可以使用 uView Plus 或其他 Vue 3 兼容的 UI 库
|
||||
// app.use(uView)
|
||||
|
||||
// 全局注册 lime-echart 组件
|
||||
app.component('l-echart', LEchart)
|
||||
|
||||
return {
|
||||
app
|
||||
}
|
||||
return { app }
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
607
src/api/index.js
607
src/api/index.js
@ -1,607 +0,0 @@
|
||||
/**
|
||||
* API接口配置
|
||||
*/
|
||||
|
||||
import { apiBaseUrl, apiTimeout } from '../config/index.js'
|
||||
|
||||
// 基础配置 - 从配置文件获取
|
||||
const BASE_URL = apiBaseUrl
|
||||
const TIMEOUT = apiTimeout
|
||||
|
||||
|
||||
/**
|
||||
* 请求拦截器
|
||||
*/
|
||||
const requestInterceptor = (config) => {
|
||||
// 添加token
|
||||
const token = uni.getStorageSync('token')
|
||||
if (token) {
|
||||
config.header = {
|
||||
...config.header,
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
}
|
||||
|
||||
// 添加通用请求头
|
||||
config.header = {
|
||||
'Content-Type': 'application/json',
|
||||
...config.header
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
/**
|
||||
* 响应拦截器
|
||||
*/
|
||||
const responseInterceptor = (response) => {
|
||||
const { statusCode, data } = response
|
||||
|
||||
if (statusCode === 200) {
|
||||
if (data.code === 0) {
|
||||
return data.data
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: data.message || '请求失败',
|
||||
icon: 'none'
|
||||
})
|
||||
return Promise.reject(new Error(data.message || '请求失败'))
|
||||
}
|
||||
} else if (statusCode === 401) {
|
||||
// token过期,跳转登录
|
||||
uni.removeStorageSync('token')
|
||||
uni.reLaunch({
|
||||
url: '/pages/login/login'
|
||||
})
|
||||
return Promise.reject(new Error('登录已过期'))
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '网络错误',
|
||||
icon: 'none'
|
||||
})
|
||||
return Promise.reject(new Error('网络错误'))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用请求方法
|
||||
*/
|
||||
const request = (options) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 请求拦截
|
||||
const config = requestInterceptor({
|
||||
url: options.url.startsWith('/') ? BASE_URL + options.url : BASE_URL + '/' + options.url,
|
||||
method: options.method || 'GET',
|
||||
data: options.data,
|
||||
header: options.header || {},
|
||||
timeout: options.timeout || TIMEOUT
|
||||
})
|
||||
|
||||
uni.request({
|
||||
...config,
|
||||
success: (response) => {
|
||||
try {
|
||||
const result = responseInterceptor(response)
|
||||
resolve(result)
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
},
|
||||
fail: (error) => {
|
||||
uni.showToast({
|
||||
title: '网络连接失败',
|
||||
icon: 'none'
|
||||
})
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户相关API
|
||||
*/
|
||||
export const userApi = {
|
||||
// 登录
|
||||
login(data) {
|
||||
return request({
|
||||
url: '/api/login',
|
||||
method: 'POST',
|
||||
data
|
||||
})
|
||||
},
|
||||
|
||||
// 登出
|
||||
logout() {
|
||||
return request({
|
||||
url: '/api/logout',
|
||||
method: 'POST'
|
||||
})
|
||||
},
|
||||
|
||||
// 获取用户信息
|
||||
getUserInfo() {
|
||||
return request({
|
||||
url: '/api/user/info',
|
||||
method: 'GET'
|
||||
})
|
||||
},
|
||||
|
||||
// 更新用户信息
|
||||
updateUserInfo(data) {
|
||||
return request({
|
||||
url: '/api/user/info',
|
||||
method: 'PUT',
|
||||
data
|
||||
})
|
||||
},
|
||||
|
||||
// 修改密码
|
||||
changePassword(data) {
|
||||
return request({
|
||||
url: '/api/user/password',
|
||||
method: 'PUT',
|
||||
data
|
||||
})
|
||||
},
|
||||
|
||||
// 上传头像
|
||||
uploadAvatar(file) {
|
||||
return request({
|
||||
url: '/api/user/avatar',
|
||||
method: 'POST',
|
||||
data: file
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 考勤相关API
|
||||
*/
|
||||
export const attendanceApi = {
|
||||
// 打卡
|
||||
checkIn(data) {
|
||||
return request({
|
||||
url: '/api/attendance/checkin',
|
||||
method: 'POST',
|
||||
data
|
||||
})
|
||||
},
|
||||
|
||||
// 下班打卡
|
||||
checkOut(data) {
|
||||
return request({
|
||||
url: '/api/attendance/checkout',
|
||||
method: 'POST',
|
||||
data
|
||||
})
|
||||
},
|
||||
|
||||
// 获取考勤记录
|
||||
getAttendanceList(params) {
|
||||
return request({
|
||||
url: '/api/attendance/list',
|
||||
method: 'GET',
|
||||
data: params
|
||||
})
|
||||
},
|
||||
|
||||
// 获取考勤统计
|
||||
getAttendanceStats(params) {
|
||||
return request({
|
||||
url: '/api/attendance/stats',
|
||||
method: 'GET',
|
||||
data: params
|
||||
})
|
||||
},
|
||||
|
||||
// 获取考勤详情
|
||||
getAttendanceDetail(id) {
|
||||
return request({
|
||||
url: '/api/attendance/detail',
|
||||
method: 'GET',
|
||||
data: { id }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 请假相关API
|
||||
*/
|
||||
export const leaveApi = {
|
||||
// 申请请假
|
||||
applyLeave(data) {
|
||||
return request({
|
||||
url: '/api/leave/apply',
|
||||
method: 'POST',
|
||||
data
|
||||
})
|
||||
},
|
||||
|
||||
// 获取请假列表
|
||||
getLeaveList(params) {
|
||||
return request({
|
||||
url: '/api/leave/list',
|
||||
method: 'GET',
|
||||
data: params
|
||||
})
|
||||
},
|
||||
|
||||
// 获取请假详情
|
||||
getLeaveDetail(id) {
|
||||
return request({
|
||||
url: '/api/leave/detail',
|
||||
method: 'GET',
|
||||
data: { id }
|
||||
})
|
||||
},
|
||||
|
||||
// 取消请假
|
||||
cancelLeave(id) {
|
||||
return request({
|
||||
url: '/api/leave/cancel',
|
||||
method: 'PUT',
|
||||
data: { id }
|
||||
})
|
||||
},
|
||||
|
||||
// 审批请假
|
||||
approveLeave(id, data) {
|
||||
return request({
|
||||
url: '/api/leave/approve',
|
||||
method: 'PUT',
|
||||
data: { id, ...data }
|
||||
})
|
||||
},
|
||||
|
||||
// 拒绝请假
|
||||
rejectLeave(id, data) {
|
||||
return request({
|
||||
url: '/api/leave/reject',
|
||||
method: 'PUT',
|
||||
data: { id, ...data }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 报销相关API
|
||||
*/
|
||||
export const reimbursementApi = {
|
||||
// 提交报销
|
||||
submitReimbursement(data) {
|
||||
return request({
|
||||
url: '/api/reimbursement/submit',
|
||||
method: 'POST',
|
||||
data
|
||||
})
|
||||
},
|
||||
|
||||
// 获取报销列表
|
||||
getReimbursementList(params) {
|
||||
return request({
|
||||
url: '/api/reimbursement/list',
|
||||
method: 'GET',
|
||||
data: params
|
||||
})
|
||||
},
|
||||
|
||||
// 获取报销详情
|
||||
getReimbursementDetail(id) {
|
||||
return request({
|
||||
url: '/api/reimbursement/detail',
|
||||
method: 'GET',
|
||||
data: { id }
|
||||
})
|
||||
},
|
||||
|
||||
// 上传发票
|
||||
uploadInvoice(file) {
|
||||
return request({
|
||||
url: '/api/reimbursement/upload',
|
||||
method: 'POST',
|
||||
data: file
|
||||
})
|
||||
},
|
||||
|
||||
// 审批报销
|
||||
approveReimbursement(id, data) {
|
||||
return request({
|
||||
url: '/api/reimbursement/approve',
|
||||
method: 'PUT',
|
||||
data: { id, ...data }
|
||||
})
|
||||
},
|
||||
|
||||
// 拒绝报销
|
||||
rejectReimbursement(id, data) {
|
||||
return request({
|
||||
url: '/api/reimbursement/reject',
|
||||
method: 'PUT',
|
||||
data: { id, ...data }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务相关API
|
||||
*/
|
||||
export const taskApi = {
|
||||
// 获取任务列表
|
||||
getTaskList(params) {
|
||||
return request({
|
||||
url: '/api/task/list',
|
||||
method: 'GET',
|
||||
data: params
|
||||
})
|
||||
},
|
||||
|
||||
// 创建任务
|
||||
createTask(data) {
|
||||
return request({
|
||||
url: '/api/task/create',
|
||||
method: 'POST',
|
||||
data
|
||||
})
|
||||
},
|
||||
|
||||
// 获取任务详情
|
||||
getTaskDetail(id) {
|
||||
return request({
|
||||
url: '/api/task/detail',
|
||||
method: 'GET',
|
||||
data: { id }
|
||||
})
|
||||
},
|
||||
|
||||
// 更新任务
|
||||
updateTask(id, data) {
|
||||
return request({
|
||||
url: '/api/task/update',
|
||||
method: 'PUT',
|
||||
data: { id, ...data }
|
||||
})
|
||||
},
|
||||
|
||||
// 更新任务状态
|
||||
updateTaskStatus(id, status) {
|
||||
return request({
|
||||
url: '/api/task/status',
|
||||
method: 'PUT',
|
||||
data: { id, status }
|
||||
})
|
||||
},
|
||||
|
||||
// 分配任务
|
||||
assignTask(id, data) {
|
||||
return request({
|
||||
url: '/api/task/assign',
|
||||
method: 'PUT',
|
||||
data: { id, ...data }
|
||||
})
|
||||
},
|
||||
|
||||
// 完成任务
|
||||
completeTask(id, data) {
|
||||
return request({
|
||||
url: '/api/task/complete',
|
||||
method: 'PUT',
|
||||
data: { id, ...data }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息相关API
|
||||
*/
|
||||
export const messageApi = {
|
||||
// 获取消息列表
|
||||
getMessageList(params) {
|
||||
return request({
|
||||
url: '/api/message/list',
|
||||
method: 'GET',
|
||||
data: params
|
||||
})
|
||||
},
|
||||
|
||||
// 获取消息详情
|
||||
getMessageDetail(id) {
|
||||
return request({
|
||||
url: '/api/message/detail',
|
||||
method: 'GET',
|
||||
data: { id }
|
||||
})
|
||||
},
|
||||
|
||||
// 标记消息为已读
|
||||
markAsRead(id) {
|
||||
return request({
|
||||
url: '/api/message/read',
|
||||
method: 'PUT',
|
||||
data: { id }
|
||||
})
|
||||
},
|
||||
|
||||
// 获取未读消息数量
|
||||
getUnreadCount() {
|
||||
return request({
|
||||
url: '/api/message/unread-count',
|
||||
method: 'GET'
|
||||
})
|
||||
},
|
||||
|
||||
// 发送消息
|
||||
sendMessage(data) {
|
||||
return request({
|
||||
url: '/api/message/send',
|
||||
method: 'POST',
|
||||
data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件相关API
|
||||
*/
|
||||
export const fileApi = {
|
||||
// 上传文件
|
||||
uploadFile(file) {
|
||||
return request({
|
||||
url: '/api/file/upload',
|
||||
method: 'POST',
|
||||
data: file
|
||||
})
|
||||
},
|
||||
|
||||
// 获取文件列表
|
||||
getFileList(params) {
|
||||
return request({
|
||||
url: '/api/file/list',
|
||||
method: 'GET',
|
||||
data: params
|
||||
})
|
||||
},
|
||||
|
||||
// 下载文件
|
||||
downloadFile(id) {
|
||||
return request({
|
||||
url: '/api/file/download',
|
||||
method: 'GET',
|
||||
data: { id }
|
||||
})
|
||||
},
|
||||
|
||||
// 删除文件
|
||||
deleteFile(id) {
|
||||
return request({
|
||||
url: '/api/file/delete',
|
||||
method: 'DELETE',
|
||||
data: { id }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 客户相关API
|
||||
*/
|
||||
export const customerApi = {
|
||||
// 获取客户列表
|
||||
getCustomerList(params) {
|
||||
return request({
|
||||
url: '/api/customer/list',
|
||||
method: 'GET',
|
||||
data: params
|
||||
})
|
||||
},
|
||||
|
||||
// 获取客户详情
|
||||
getCustomerDetail(id) {
|
||||
return request({
|
||||
url: '/api/customer/detail',
|
||||
method: 'GET',
|
||||
data: { id }
|
||||
})
|
||||
},
|
||||
|
||||
// 添加客户
|
||||
addCustomer(data) {
|
||||
return request({
|
||||
url: '/api/customer/add',
|
||||
method: 'POST',
|
||||
data
|
||||
})
|
||||
},
|
||||
|
||||
// 更新客户信息
|
||||
updateCustomer(id, data) {
|
||||
return request({
|
||||
url: '/api/customer/update',
|
||||
method: 'PUT',
|
||||
data: { id, ...data }
|
||||
})
|
||||
},
|
||||
|
||||
// 删除客户
|
||||
deleteCustomer(id) {
|
||||
return request({
|
||||
url: '/api/customer/delete',
|
||||
method: 'DELETE',
|
||||
data: { id }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 部门相关API
|
||||
*/
|
||||
export const departmentApi = {
|
||||
// 获取部门列表
|
||||
getDepartmentList(params) {
|
||||
return request({
|
||||
url: '/api/department/list',
|
||||
method: 'GET',
|
||||
data: params
|
||||
})
|
||||
},
|
||||
|
||||
// 获取部门树
|
||||
getDepartmentTree() {
|
||||
return request({
|
||||
url: '/api/department/tree',
|
||||
method: 'GET'
|
||||
})
|
||||
},
|
||||
|
||||
// 获取部门详情
|
||||
getDepartmentDetail(id) {
|
||||
return request({
|
||||
url: '/api/department/detail',
|
||||
method: 'GET',
|
||||
data: { id }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知相关API
|
||||
*/
|
||||
export const notificationApi = {
|
||||
// 获取通知列表
|
||||
getNotificationList(params) {
|
||||
return request({
|
||||
url: '/api/notification/list',
|
||||
method: 'GET',
|
||||
data: params
|
||||
})
|
||||
},
|
||||
|
||||
// 标记通知为已读
|
||||
markAsRead(id) {
|
||||
return request({
|
||||
url: '/api/notification/read',
|
||||
method: 'PUT',
|
||||
data: { id }
|
||||
})
|
||||
},
|
||||
|
||||
// 获取未读通知数量
|
||||
getUnreadCount() {
|
||||
return request({
|
||||
url: '/api/notification/unread-count',
|
||||
method: 'GET'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
userApi,
|
||||
attendanceApi,
|
||||
leaveApi,
|
||||
reimbursementApi,
|
||||
taskApi,
|
||||
messageApi,
|
||||
fileApi,
|
||||
customerApi,
|
||||
departmentApi,
|
||||
notificationApi
|
||||
}
|
||||
17
src/api/login.js
Normal file
17
src/api/login.js
Normal file
@ -0,0 +1,17 @@
|
||||
/**
|
||||
* 登录相关 API
|
||||
*/
|
||||
import request from './request'
|
||||
|
||||
export const loginApi = {
|
||||
// 登录
|
||||
login(data) {
|
||||
return request({
|
||||
url: '/api/login',
|
||||
method: 'POST',
|
||||
data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default loginApi
|
||||
108
src/api/request.js
Normal file
108
src/api/request.js
Normal file
@ -0,0 +1,108 @@
|
||||
/**
|
||||
* 通用请求封装(拦截器等)
|
||||
*/
|
||||
import { apiBaseUrl, apiTimeout } from '../config/index.js'
|
||||
|
||||
// 基础配置 - 从配置文件获取
|
||||
const BASE_URL = apiBaseUrl
|
||||
const TIMEOUT = apiTimeout
|
||||
|
||||
/**
|
||||
* 请求拦截器
|
||||
*/
|
||||
const requestInterceptor = (config) => {
|
||||
// 添加token
|
||||
const token = uni.getStorageSync('token')
|
||||
if (token) {
|
||||
config.header = {
|
||||
...config.header,
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
}
|
||||
|
||||
// 添加通用请求头
|
||||
config.header = {
|
||||
'Content-Type': 'application/json',
|
||||
...config.header
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
/**
|
||||
* 响应拦截器
|
||||
*/
|
||||
const responseInterceptor = (response) => {
|
||||
const { statusCode, data } = response
|
||||
|
||||
if (statusCode === 200) {
|
||||
// 兼容两种后端返回格式:
|
||||
// 1) { code: 0, message, data }
|
||||
// 2) { code: 200, msg, data }
|
||||
const businessCode = data.code
|
||||
const success =
|
||||
businessCode === undefined || businessCode === 0 || businessCode === 200
|
||||
|
||||
if (success) {
|
||||
// 优先返回 data.data,其次整个 data
|
||||
return data.data !== undefined ? data.data : data
|
||||
}
|
||||
|
||||
const message = data.msg || data.message || '请求失败'
|
||||
uni.showToast({
|
||||
title: message,
|
||||
icon: 'none'
|
||||
})
|
||||
return Promise.reject(new Error(message))
|
||||
} else if (statusCode === 401) {
|
||||
// token过期,跳转登录
|
||||
uni.removeStorageSync('token')
|
||||
uni.reLaunch({
|
||||
url: 'pages/login/index'
|
||||
})
|
||||
return Promise.reject(new Error('登录已过期'))
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '网络错误',
|
||||
icon: 'none'
|
||||
})
|
||||
return Promise.reject(new Error('网络错误'))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用请求方法
|
||||
*/
|
||||
export const request = (options) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 请求拦截
|
||||
const config = requestInterceptor({
|
||||
url: options.url.startsWith('/') ? BASE_URL + options.url : BASE_URL + '/' + options.url,
|
||||
method: options.method || 'GET',
|
||||
data: options.data,
|
||||
header: options.header || {},
|
||||
timeout: options.timeout || TIMEOUT
|
||||
})
|
||||
|
||||
uni.request({
|
||||
...config,
|
||||
success: (response) => {
|
||||
try {
|
||||
const result = responseInterceptor(response)
|
||||
resolve(result)
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
},
|
||||
fail: (error) => {
|
||||
uni.showToast({
|
||||
title: '网络连接失败',
|
||||
icon: 'none'
|
||||
})
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export default request
|
||||
60
src/api/user.js
Normal file
60
src/api/user.js
Normal file
@ -0,0 +1,60 @@
|
||||
/**
|
||||
* 用户相关API
|
||||
*/
|
||||
import request from './request'
|
||||
|
||||
export const userApi = {
|
||||
// 登录
|
||||
login(data) {
|
||||
return request({
|
||||
url: '/api/login',
|
||||
method: 'POST',
|
||||
data
|
||||
})
|
||||
},
|
||||
|
||||
// 登出
|
||||
logout() {
|
||||
return request({
|
||||
url: '/api/logout',
|
||||
method: 'POST'
|
||||
})
|
||||
},
|
||||
|
||||
// 获取用户信息
|
||||
getUserInfo() {
|
||||
return request({
|
||||
url: '/api/user/info',
|
||||
method: 'GET'
|
||||
})
|
||||
},
|
||||
|
||||
// 更新用户信息
|
||||
updateUserInfo(data) {
|
||||
return request({
|
||||
url: '/api/user/info',
|
||||
method: 'PUT',
|
||||
data
|
||||
})
|
||||
},
|
||||
|
||||
// 修改密码
|
||||
changePassword(data) {
|
||||
return request({
|
||||
url: '/api/user/password',
|
||||
method: 'PUT',
|
||||
data
|
||||
})
|
||||
},
|
||||
|
||||
// 上传头像
|
||||
uploadAvatar(file) {
|
||||
return request({
|
||||
url: '/api/user/avatar',
|
||||
method: 'POST',
|
||||
data: file
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default userApi
|
||||
@ -1,19 +1,26 @@
|
||||
/**
|
||||
* 配置模块统一导出
|
||||
* 直接定义配置,简单明了
|
||||
* 优先使用 .env 中的环境变量,未配置时再使用默认值
|
||||
*
|
||||
* 说明:
|
||||
* - Vite / uni-app(vite) 中只能通过 import.meta.env 访问环境变量
|
||||
* - 且必须以 VITE_ 前缀开头才会被注入到客户端
|
||||
*/
|
||||
|
||||
// 常用配置 - 直接定义
|
||||
export const apiBaseUrl = 'https://apigo.yunzer.cn'
|
||||
export const apiTimeout = 10000
|
||||
export const appName = '企业办公移动应用'
|
||||
export const appVersion = '1.0.0'
|
||||
export const debug = true
|
||||
// 从环境变量读取
|
||||
const env = import.meta.env || {}
|
||||
|
||||
// 环境判断
|
||||
export const isDev = true
|
||||
export const isTest = false
|
||||
export const isProd = false
|
||||
// 常用配置(支持 VITE_APP_* 前缀)
|
||||
export const apiBaseUrl = env.VITE_APP_API_BASE_URL || 'https://localhost:8000/'
|
||||
export const apiTimeout = Number(env.VITE_APP_API_TIMEOUT || 10000)
|
||||
export const appName = env.VITE_APP_APP_NAME || 'babyhealth'
|
||||
export const appVersion = env.VITE_APP_APP_VERSION || '1.0.0'
|
||||
export const debug = String(env.VITE_APP_DEBUG || 'true').toLowerCase() === 'true'
|
||||
|
||||
// 环境判断(根据需要自己扩展)
|
||||
export const isDev = env.MODE === 'development'
|
||||
export const isProd = env.MODE === 'production'
|
||||
export const isTest = !isDev && !isProd
|
||||
|
||||
// 默认导出
|
||||
export default {
|
||||
@ -24,5 +31,5 @@ export default {
|
||||
debug,
|
||||
isDev,
|
||||
isTest,
|
||||
isProd
|
||||
isProd,
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
import { userApi } from '../api'
|
||||
import userApi from '../api/user'
|
||||
|
||||
export const useAuthStore = defineStore('auth', () => {
|
||||
// 状态
|
||||
@ -14,34 +14,43 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
})
|
||||
|
||||
// 登录
|
||||
const loginOld = (userData, authToken) => {
|
||||
userInfo.value = userData
|
||||
token.value = authToken
|
||||
// 兼容两种调用方式:
|
||||
// 1) login(userInfo, token) —— 推荐(你登录接口就返回 user + token)
|
||||
// 2) login(token) —— 仅保存 token,然后再拉取 userInfo
|
||||
const login = async (userDataOrToken, authToken) => {
|
||||
let userData = null
|
||||
let finalToken = null
|
||||
|
||||
if (typeof userDataOrToken === 'string' && authToken === undefined) {
|
||||
finalToken = userDataOrToken
|
||||
} else {
|
||||
userData = userDataOrToken
|
||||
finalToken = authToken
|
||||
}
|
||||
|
||||
token.value = finalToken
|
||||
isLoggedIn.value = true
|
||||
|
||||
|
||||
// 保存到本地存储
|
||||
uni.setStorageSync('userInfo', userData)
|
||||
uni.setStorageSync('token', authToken)
|
||||
uni.setStorageSync('token', finalToken)
|
||||
uni.setStorageSync('isLoggedIn', true)
|
||||
|
||||
console.log('用户登录成功:', userData)
|
||||
|
||||
// 如果有 userInfo 就直接保存;没有就尝试拉取
|
||||
if (userData) {
|
||||
userInfo.value = userData
|
||||
uni.setStorageSync('userInfo', userData)
|
||||
} else {
|
||||
try {
|
||||
await getUserInfo()
|
||||
} catch (e) {
|
||||
// 拉取用户信息失败不阻断登录态(路由守卫会用到 token + isLoggedIn)
|
||||
console.warn('获取用户信息失败:', e)
|
||||
}
|
||||
}
|
||||
|
||||
console.log('用户登录成功:', finalToken)
|
||||
}
|
||||
|
||||
|
||||
// 登录
|
||||
const login = (authToken) => {
|
||||
token.value = authToken
|
||||
isLoggedIn.value = true
|
||||
|
||||
// 保存到本地存储
|
||||
uni.setStorageSync('token', authToken)
|
||||
uni.setStorageSync('isLoggedIn', true)
|
||||
|
||||
//读取用户信息
|
||||
getUserInfo();
|
||||
console.log('用户登录成功:', authToken)
|
||||
}
|
||||
|
||||
//获取用户信息
|
||||
const getUserInfo = async ()=>{
|
||||
const res = await userApi.getUserInfo();
|
||||
|
||||
28
tsconfig.json
Normal file
28
tsconfig.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"allowJs": true,
|
||||
"checkJs": false,
|
||||
"strict": false,
|
||||
"skipLibCheck": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["*"],
|
||||
"@": ["."]
|
||||
},
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*",
|
||||
"pages/**/*",
|
||||
"components/**/*",
|
||||
"App.vue",
|
||||
"main.js",
|
||||
"env.d.ts",
|
||||
"**/*.vue"
|
||||
],
|
||||
"exclude": ["node_modules", "unpackage", "dist"]
|
||||
}
|
||||
@ -1,13 +1,13 @@
|
||||
{
|
||||
"hash": "308de400",
|
||||
"configHash": "11378e7b",
|
||||
"hash": "d040dac9",
|
||||
"configHash": "edf7a139",
|
||||
"lockfileHash": "eccfc8e7",
|
||||
"browserHash": "42a8a35f",
|
||||
"browserHash": "dcaa9f00",
|
||||
"optimized": {
|
||||
"pinia-plugin-persistedstate": {
|
||||
"src": "../../../../../node_modules/pinia-plugin-persistedstate/dist/index.js",
|
||||
"file": "pinia-plugin-persistedstate.js",
|
||||
"fileHash": "53c30cff",
|
||||
"fileHash": "79ff99f1",
|
||||
"needsInterop": false
|
||||
}
|
||||
},
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// E:/Demos/DemoOwns/PHP/official/mobile/node_modules/pinia-plugin-persistedstate/dist/index.js
|
||||
// E:/Demo/PHP/official_website/babyhealth/node_modules/pinia-plugin-persistedstate/dist/index.js
|
||||
function get(obj, path) {
|
||||
if (obj == null)
|
||||
return void 0;
|
||||
|
||||
3
unpackage/dist/cache/.vite/deps_temp_766ef8a9/package.json
vendored
Normal file
3
unpackage/dist/cache/.vite/deps_temp_766ef8a9/package.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "module"
|
||||
}
|
||||
143
unpackage/dist/cache/.vite/deps_temp_766ef8a9/pinia-plugin-persistedstate.js
vendored
Normal file
143
unpackage/dist/cache/.vite/deps_temp_766ef8a9/pinia-plugin-persistedstate.js
vendored
Normal file
@ -0,0 +1,143 @@
|
||||
// E:/Demo/PHP/official_website/babyhealth/node_modules/pinia-plugin-persistedstate/dist/index.js
|
||||
function get(obj, path) {
|
||||
if (obj == null)
|
||||
return void 0;
|
||||
let value = obj;
|
||||
for (let i = 0; i < path.length; i++) {
|
||||
if (value === void 0 || value[path[i]] === void 0)
|
||||
return void 0;
|
||||
if (value === null || value[path[i]] === null)
|
||||
return null;
|
||||
value = value[path[i]];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
function set(obj, value, path) {
|
||||
if (path.length === 0)
|
||||
return value;
|
||||
const idx = path[0];
|
||||
if (path.length > 1)
|
||||
value = set(typeof obj !== "object" || obj === null || !Object.prototype.hasOwnProperty.call(obj, idx) ? Number.isInteger(Number(path[1])) ? [] : {} : obj[idx], value, Array.prototype.slice.call(path, 1));
|
||||
if (Number.isInteger(Number(idx)) && Array.isArray(obj))
|
||||
return obj.slice()[idx];
|
||||
return Object.assign({}, obj, { [idx]: value });
|
||||
}
|
||||
function unset(obj, path) {
|
||||
if (obj == null || path.length === 0)
|
||||
return obj;
|
||||
if (path.length === 1) {
|
||||
if (obj == null)
|
||||
return obj;
|
||||
if (Number.isInteger(path[0]) && Array.isArray(obj))
|
||||
return Array.prototype.slice.call(obj, 0).splice(path[0], 1);
|
||||
const result = {};
|
||||
for (const p in obj)
|
||||
result[p] = obj[p];
|
||||
delete result[path[0]];
|
||||
return result;
|
||||
}
|
||||
if (obj[path[0]] == null) {
|
||||
if (Number.isInteger(path[0]) && Array.isArray(obj))
|
||||
return Array.prototype.concat.call([], obj);
|
||||
const result = {};
|
||||
for (const p in obj)
|
||||
result[p] = obj[p];
|
||||
return result;
|
||||
}
|
||||
return set(obj, unset(obj[path[0]], Array.prototype.slice.call(path, 1)), [path[0]]);
|
||||
}
|
||||
function deepPick(obj, paths) {
|
||||
return paths.map((p) => p.split(".")).map((p) => [p, get(obj, p)]).filter((t) => t[1] !== void 0).reduce((acc, cur) => set(acc, cur[1], cur[0]), {});
|
||||
}
|
||||
function deepOmit(obj, paths) {
|
||||
return paths.map((p) => p.split(".")).reduce((acc, cur) => unset(acc, cur), obj);
|
||||
}
|
||||
function hydrateStore(store, { storage, serializer, key, debug, pick, omit, beforeHydrate, afterHydrate }, context, runHooks = true) {
|
||||
try {
|
||||
if (runHooks)
|
||||
beforeHydrate == null ? void 0 : beforeHydrate(context);
|
||||
const fromStorage = storage.getItem(key);
|
||||
if (fromStorage) {
|
||||
const deserialized = serializer.deserialize(fromStorage);
|
||||
const picked = pick ? deepPick(deserialized, pick) : deserialized;
|
||||
const omitted = omit ? deepOmit(picked, omit) : picked;
|
||||
store.$patch(omitted);
|
||||
}
|
||||
if (runHooks)
|
||||
afterHydrate == null ? void 0 : afterHydrate(context);
|
||||
} catch (error) {
|
||||
if (debug)
|
||||
console.error("[pinia-plugin-persistedstate]", error);
|
||||
}
|
||||
}
|
||||
function persistState(state, { storage, serializer, key, debug, pick, omit }) {
|
||||
try {
|
||||
const picked = pick ? deepPick(state, pick) : state;
|
||||
const omitted = omit ? deepOmit(picked, omit) : picked;
|
||||
const toStorage = serializer.serialize(omitted);
|
||||
storage.setItem(key, toStorage);
|
||||
} catch (error) {
|
||||
if (debug)
|
||||
console.error("[pinia-plugin-persistedstate]", error);
|
||||
}
|
||||
}
|
||||
function parsePersistKey(key, storeId) {
|
||||
return typeof key === "function" ? key(storeId) : typeof key === "string" ? key : storeId;
|
||||
}
|
||||
function createPersistence(context, optionsParser, auto) {
|
||||
const { pinia, store, options: { persist = auto } } = context;
|
||||
if (!persist)
|
||||
return;
|
||||
if (!(store.$id in pinia.state.value)) {
|
||||
const originalStore = pinia._s.get(store.$id.replace("__hot:", ""));
|
||||
if (originalStore)
|
||||
Promise.resolve().then(() => originalStore.$persist());
|
||||
return;
|
||||
}
|
||||
const persistences = (Array.isArray(persist) ? persist : persist === true ? [{}] : [persist]).map(optionsParser);
|
||||
store.$hydrate = ({ runHooks = true } = {}) => {
|
||||
persistences.forEach((p) => {
|
||||
hydrateStore(store, p, context, runHooks);
|
||||
});
|
||||
};
|
||||
store.$persist = () => {
|
||||
persistences.forEach((p) => {
|
||||
persistState(store.$state, p);
|
||||
});
|
||||
};
|
||||
persistences.forEach((p) => {
|
||||
hydrateStore(store, p, context);
|
||||
store.$subscribe((_mutation, state) => persistState(state, p), { detached: true });
|
||||
});
|
||||
}
|
||||
function createPersistedState(options = {}) {
|
||||
return function(context) {
|
||||
createPersistence(context, (p) => {
|
||||
const persistKey = parsePersistKey(p.key, context.store.$id);
|
||||
return {
|
||||
key: (options.key ? options.key : (x) => x)(persistKey),
|
||||
debug: p.debug ?? options.debug ?? false,
|
||||
serializer: p.serializer ?? options.serializer ?? {
|
||||
serialize: (data) => JSON.stringify(data),
|
||||
deserialize: (data) => JSON.parse(data)
|
||||
},
|
||||
storage: p.storage ?? options.storage ?? window.localStorage,
|
||||
beforeHydrate: p.beforeHydrate ?? options.beforeHydrate,
|
||||
afterHydrate: p.afterHydrate ?? options.afterHydrate,
|
||||
pick: p.pick,
|
||||
omit: p.omit
|
||||
};
|
||||
}, options.auto ?? false);
|
||||
};
|
||||
}
|
||||
var src_default = createPersistedState();
|
||||
export {
|
||||
createPersistedState,
|
||||
src_default as default
|
||||
};
|
||||
/*! Bundled license information:
|
||||
|
||||
pinia-plugin-persistedstate/dist/index.js:
|
||||
(* v8 ignore if -- @preserve *)
|
||||
*/
|
||||
//# sourceMappingURL=pinia-plugin-persistedstate.js.map
|
||||
7
unpackage/dist/cache/.vite/deps_temp_766ef8a9/pinia-plugin-persistedstate.js.map
vendored
Normal file
7
unpackage/dist/cache/.vite/deps_temp_766ef8a9/pinia-plugin-persistedstate.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
14
vite.config.js
Normal file
14
vite.config.js
Normal file
@ -0,0 +1,14 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import uni from '@dcloudio/vite-plugin-uni'
|
||||
import path from 'path'
|
||||
|
||||
// uni-app 项目的 Vite 配置
|
||||
export default defineConfig({
|
||||
plugins: [uni()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, 'src')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Loading…
Reference in New Issue
Block a user