c293da23
刘淇
新园林init
|
1
2
3
|
import { defineStore } from 'pinia';
import cache from '@/common/utils/cache';
import globalConfig from '@/common/config/global';
|
e6a04285
刘淇
单图情况 宽高70*70
|
4
|
// 新增:导入 refreshToken 接口
|
e2e5221c
刘淇
验证码登录
|
5
|
import { login,smsLogin, getUserInfo, logout, moduleList, getSimpleDictDataList, refreshToken } from '@/api/user';
|
e6a04285
刘淇
单图情况 宽高70*70
|
6
7
8
|
// 新增:定义刷新Token的定时器标识(避免重复创建定时器)
let refreshTokenTimer = null;
|
c293da23
刘淇
新园林init
|
9
10
|
export const useUserStore = defineStore('user', {
|
9b30ab8c
刘淇
新增快速工单,原版
|
11
|
// 修复1:删除重复的 state 定义,只保留根层级的 state
|
c293da23
刘淇
新园林init
|
12
|
state: () => ({
|
9b30ab8c
刘淇
新增快速工单,原版
|
13
|
token: cache.get(globalConfig.cache.tokenKey) || '', // 初始值从缓存取(兼容持久化)
|
e6a04285
刘淇
单图情况 宽高70*70
|
14
|
refreshToken: cache.get(globalConfig.cache.refreshTokenKey) || '', // 新增:Refresh Token 状态
|
9b30ab8c
刘淇
新增快速工单,原版
|
15
16
17
18
19
20
|
userInfo: cache.get(globalConfig.cache.userInfoKey) || {},
userId: cache.get(globalConfig.cache.userIdKey) || '',
moduleListInfo: cache.get(globalConfig.cache.moduleListKey) || '',
expireTime: cache.get(globalConfig.cache.expireTimeKey) || 0,
dictData: cache.get(globalConfig.cache.dictDataKey) || {},
logoutLoading: false
|
c293da23
刘淇
新园林init
|
21
22
|
}),
|
c293da23
刘淇
新园林init
|
23
24
25
26
27
28
29
30
|
getters: {
isLogin: (state) => {
if (!state.token) return false;
if (state.expireTime && state.expireTime < Date.now()) {
return false;
}
return true;
},
|
e6a04285
刘淇
单图情况 宽高70*70
|
31
32
33
34
35
36
37
38
|
permissions: (state) => state.userInfo.permissions || [],
// 新增:判断是否需要刷新Token(过期前30秒触发,避免已过期才刷新)
needRefreshToken: (state) => {
if (!state.token || !state.refreshToken) return false;
const remainingTime = state.expireTime - Date.now();
// 剩余时间大于0秒 且 小于30秒(即将过期)
return remainingTime > 0 && remainingTime < 30 * 1000;
}
|
c293da23
刘淇
新园林init
|
39
40
|
},
|
c293da23
刘淇
新园林init
|
41
42
43
|
actions: {
async login(params) {
try {
|
e2e5221c
刘淇
验证码登录
|
44
45
46
47
48
49
50
51
|
// smsLogin
let res
if(params.type=='sms'){
res = await smsLogin(params);
}else{
res = await login(params);
}
|
e6a04285
刘淇
单图情况 宽高70*70
|
52
53
|
// 新增:从登录接口返回值中获取 refreshToken(若接口返回字段名不一致,可调整,如 res.refresh_token)
const { accessToken, expiresTime, userId, refreshToken } = res;
|
c293da23
刘淇
新园林init
|
54
55
56
57
58
|
if (!accessToken) {
throw new Error('登录失败,未获取到令牌');
}
|
9b30ab8c
刘淇
新增快速工单,原版
|
59
|
// 更新 Pinia state
|
c293da23
刘淇
新园林init
|
60
|
this.token = accessToken;
|
e6a04285
刘淇
单图情况 宽高70*70
|
61
|
this.refreshToken = refreshToken || ''; // 新增:存储Refresh Token
|
c293da23
刘淇
新园林init
|
62
63
64
65
|
this.expireTime = expiresTime;
this.userId = userId;
this.userInfo = {};
|
9b30ab8c
刘淇
新增快速工单,原版
|
66
|
// 等待 Pinia 持久化同步
|
c293da23
刘淇
新园林init
|
67
68
69
70
71
|
await new Promise(resolve => setTimeout(resolve, 50));
// 获取用户信息
const userInfo = await this.getUserInfo();
this.userInfo = userInfo;
|
c293da23
刘淇
新园林init
|
72
73
74
75
76
77
|
// 获取模块列表
let moduleListInfo = null;
try {
moduleListInfo = await this.getModuleList();
this.moduleListInfo = moduleListInfo;
|
c293da23
刘淇
新园林init
|
78
79
80
81
82
|
} catch (moduleErr) {
console.warn('获取模块列表失败(不影响登录):', moduleErr);
uni.showToast({ title: '获取模块列表失败,可正常登录', icon: 'none' });
}
|
9b30ab8c
刘淇
新增快速工单,原版
|
83
84
85
86
87
88
89
90
91
92
93
|
// 修复2:调用字典接口时,不直接抛错(避免阻断登录)
try {
const dictRes = await this.getAndSaveDictData();
// 仅接口返回成功时才更新字典
this.dictData = dictRes || {};
console.log('字典数据获取成功:', this.dictData);
} catch (dictErr) {
console.warn('获取字典失败(不影响登录):', dictErr);
uni.showToast({ title: '获取字典失败,可正常使用', icon: 'none' });
}
|
e6a04285
刘淇
单图情况 宽高70*70
|
94
95
96
|
// 新增:登录成功后,启动 Token 自动刷新定时器
this.startRefreshTokenTimer();
|
c293da23
刘淇
新园林init
|
97
98
99
100
101
102
103
|
return { ...res, userInfo, moduleListInfo };
} catch (err) {
console.error('登录流程失败:', err);
throw err;
}
},
|
e6a04285
刘淇
单图情况 宽高70*70
|
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
|
// 新增:调用刷新Token接口,返回刷新是否成功
async refreshTokenApi() {
// 前置校验:无Refresh Token直接返回失败
if (!this.refreshToken) {
console.warn('无刷新令牌,无法刷新Token');
this.logout(); // 无刷新令牌,直接退出登录
return false;
}
try {
// 调用修正后的 refreshToken 接口,传入当前的 refreshToken
const res = await refreshToken(this.refreshToken);
// 校验接口返回结果(根据你的实际接口返回格式调整)
if (!res || !res.accessToken) {
throw new Error('刷新Token失败,未获取到新令牌');
}
// 更新 Token 相关状态
this.token = res.accessToken; // 新的访问令牌
this.refreshToken = res.refreshToken || this.refreshToken; // 若接口返回新的Refresh Token则更新
this.expireTime = res.expiresTime; // 新的过期时间
console.log('Token 刷新成功');
return true;
} catch (err) {
console.error('刷新Token失败:', err);
// 刷新失败,直接退出登录
this.logout();
return false;
}
},
// 新增:启动 Token 自动刷新定时器
startRefreshTokenTimer() {
// 先清除已有定时器,避免重复创建
if (refreshTokenTimer) {
clearTimeout(refreshTokenTimer);
refreshTokenTimer = null;
}
// 计算剩余时间(过期前30秒执行刷新)
const remainingTime = this.expireTime - Date.now() - 30 * 1000;
if (remainingTime <= 0) {
// 剩余时间不足,立即执行刷新
this.refreshTokenApi();
return;
}
// 设置定时器,到期后执行刷新
refreshTokenTimer = setTimeout(async () => {
const refreshSuccess = await this.refreshTokenApi();
// 刷新成功后,重新启动定时器(循环自动刷新)
if (refreshSuccess) {
this.startRefreshTokenTimer();
}
}, remainingTime);
console.log(`Token 自动刷新定时器已启动,将在 ${Math.ceil(remainingTime / 1000)} 秒后执行刷新`);
},
// 新增:停止 Token 自动刷新定时器(避免内存泄漏)
stopRefreshTokenTimer() {
if (refreshTokenTimer) {
clearTimeout(refreshTokenTimer);
refreshTokenTimer = null;
}
},
|
9b30ab8c
刘淇
新增快速工单,原版
|
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
|
// 修复3:重构字典获取方法(加 Token 校验 + 强制携带 Token + 宽松错误处理)
async getAndSaveDictData() {
// 前置校验:无登录态直接返回,不请求接口
if (!this.isLogin) {
console.warn('未登录,跳过字典获取');
return { code: -1, msg: '未登录' };
}
try {
// 强制携带 Token(和 getModuleList 保持一致,避免拦截器同步延迟)
const res = await getSimpleDictDataList(
{}, // 接口入参(按需传,比如 dictType: ['level'])
{ header: { 'Authorization': `Bearer ${this.token}` } }
);
// 校验接口返回码(核心:避免非 0 码数据存入)
if (res.code !== 0) {
console.warn('字典接口返回失败:', res.msg);
return res; // 返回错误信息,但不抛错
}
return res;
} catch (err) {
// 修复4:宽松错误处理,只打印日志,不抛错(避免阻断登录)
console.error('字典接口请求异常:', err);
return { code: -2, msg: '接口请求异常:' + (err.message || '网络错误') };
}
},
|
c293da23
刘淇
新园林init
|
199
200
|
async getModuleList() {
try {
|
c293da23
刘淇
新园林init
|
201
202
203
204
|
if (!this.token) {
throw new Error('未获取到登录令牌,无法获取模块列表');
}
|
c293da23
刘淇
新园林init
|
205
|
const res = await moduleList({}, {
|
9b30ab8c
刘淇
新增快速工单,原版
|
206
|
header: { 'Authorization': `Bearer ${this.token}` }
|
c293da23
刘淇
新园林init
|
207
208
209
210
|
});
return res;
} catch (err) {
console.error('获取用户菜单失败:', err);
|
c293da23
刘淇
新园林init
|
211
212
213
214
215
216
217
218
219
220
|
if (err?.data?.code === 401 || err?.message.includes('401')) {
throw new Error('登录态已过期,请重新登录');
} else {
throw new Error('获取用户菜单失败,请重新登录');
}
}
},
async getUserInfo() {
try {
|
c293da23
刘淇
新园林init
|
221
222
223
224
225
226
227
|
const res = await getUserInfo();
return res;
} catch (err) {
console.error('获取用户信息失败:', err);
throw new Error('获取用户信息失败,请重新登录');
}
},
|
9b30ab8c
刘淇
新增快速工单,原版
|
228
|
|
c293da23
刘淇
新园林init
|
229
|
logout() {
|
e6a04285
刘淇
单图情况 宽高70*70
|
230
231
232
|
// 新增:退出登录时,停止刷新定时器
this.stopRefreshTokenTimer();
|
c293da23
刘淇
新园林init
|
233
234
235
236
237
238
239
240
241
|
const pages = getCurrentPages();
if (pages.length === 0) return;
const currentPageRoute = pages[pages.length - 1].route;
const loginPath = globalConfig.router.loginPath
.replace(/^\//, '')
.split('?')[0];
if (currentPageRoute === loginPath) {
|
c293da23
刘淇
新园林init
|
242
|
this.token = '';
|
e6a04285
刘淇
单图情况 宽高70*70
|
243
|
this.refreshToken = ''; // 新增:清空Refresh Token
|
c293da23
刘淇
新园林init
|
244
245
246
247
|
this.userInfo = {};
this.userId = '';
this.moduleListInfo = '';
this.expireTime = 0;
|
9b30ab8c
刘淇
新增快速工单,原版
|
248
|
this.dictData = {};
|
c293da23
刘淇
新园林init
|
249
250
251
252
253
254
255
256
257
258
259
260
|
return;
}
const logoutWithLock = async () => {
if (this.logoutLoading) return;
this.logoutLoading = true;
try {
await logout();
} catch (err) {
console.error('退出登录接口调用失败:', err);
} finally {
|
c293da23
刘淇
新园林init
|
261
|
this.token = '';
|
e6a04285
刘淇
单图情况 宽高70*70
|
262
|
this.refreshToken = ''; // 新增:清空Refresh Token
|
c293da23
刘淇
新园林init
|
263
264
265
|
this.userInfo = {};
this.userId = '';
this.moduleListInfo = '';
|
9b30ab8c
刘淇
新增快速工单,原版
|
266
|
this.dictData = {};
|
c293da23
刘淇
新园林init
|
267
268
269
|
this.expireTime = 0;
this.logoutLoading = false;
|
c293da23
刘淇
新园林init
|
270
271
272
273
274
275
276
277
278
|
uni.redirectTo({
url: globalConfig.router.loginPath
});
}
};
logoutWithLock();
},
|
c293da23
刘淇
新园林init
|
279
280
|
checkLogin() {
if (!this.isLogin) {
|
c293da23
刘淇
新园林init
|
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
|
const pages = getCurrentPages();
if (pages.length === 0) return false;
const currentPageRoute = pages[pages.length - 1].route;
const loginPath = globalConfig.router.loginPath
.replace(/^\//, '')
.split('?')[0];
if (currentPageRoute !== loginPath) {
uni.redirectTo({
url: globalConfig.router.loginPath
});
}
return false;
}
|
e6a04285
刘淇
单图情况 宽高70*70
|
296
297
298
|
// 新增:已登录时,启动刷新定时器(防止页面刷新后定时器丢失)
this.startRefreshTokenTimer();
|
c293da23
刘淇
新园林init
|
299
300
301
302
|
return true;
}
},
|
c293da23
刘淇
新园林init
|
303
304
|
persist: {
enabled: true,
|
9b30ab8c
刘淇
新增快速工单,原版
|
305
|
key: 'user_store',
|
c293da23
刘淇
新园林init
|
306
307
308
309
310
|
storage: {
getItem: (key) => uni.getStorageSync(key),
setItem: (key, value) => uni.setStorageSync(key, value),
removeItem: (key) => uni.removeStorageSync(key)
},
|
c293da23
刘淇
新园林init
|
311
312
|
serializer: {
serialize: (state) => {
|
c293da23
刘淇
新园林init
|
313
|
uni.setStorageSync(globalConfig.cache.tokenKey, state.token);
|
e6a04285
刘淇
单图情况 宽高70*70
|
314
|
uni.setStorageSync(globalConfig.cache.refreshTokenKey, state.refreshToken); // 新增:持久化Refresh Token
|
c293da23
刘淇
新园林init
|
315
316
317
318
|
uni.setStorageSync(globalConfig.cache.userIdKey, state.userId);
uni.setStorageSync(globalConfig.cache.expireTimeKey, state.expireTime);
uni.setStorageSync(globalConfig.cache.userInfoKey, state.userInfo);
uni.setStorageSync(globalConfig.cache.moduleListKey, state.moduleListInfo);
|
9b30ab8c
刘淇
新增快速工单,原版
|
319
320
|
uni.setStorageSync(globalConfig.cache.dictDataKey, state.dictData);
return state;
|
c293da23
刘淇
新园林init
|
321
322
|
},
deserialize: (value) => {
|
c293da23
刘淇
新园林init
|
323
324
|
return {
token: uni.getStorageSync(globalConfig.cache.tokenKey) || '',
|
e6a04285
刘淇
单图情况 宽高70*70
|
325
|
refreshToken: uni.getStorageSync(globalConfig.cache.refreshTokenKey) || '', // 新增:读取Refresh Token
|
c293da23
刘淇
新园林init
|
326
327
328
329
|
userId: uni.getStorageSync(globalConfig.cache.userIdKey) || '',
expireTime: uni.getStorageSync(globalConfig.cache.expireTimeKey) || 0,
userInfo: uni.getStorageSync(globalConfig.cache.userInfoKey) || {},
moduleListInfo: uni.getStorageSync(globalConfig.cache.moduleListKey) || '',
|
9b30ab8c
刘淇
新增快速工单,原版
|
330
|
dictData: uni.getStorageSync(globalConfig.cache.dictDataKey) || {},
|
c293da23
刘淇
新园林init
|
331
332
333
334
|
logoutLoading: false
};
}
},
|
e6a04285
刘淇
单图情况 宽高70*70
|
335
|
// paths: []
|
c293da23
刘淇
新园林init
|
336
337
|
}
});
|