seaislee1209 add3af7904 feat: v0.7.0 — 确认弹窗 + 秒数显示统一 + 弹窗拖拽修复 + 团队模型完善
- 新增 ConfirmModal 组件,为6处危险操作添加二次确认弹窗
  (禁用团队/用户/成员、删除视频×3处)
- 所有秒数显示统一为千位分隔符+s后缀(如 36,000s)
- 修复 modal/drawer 在 input 中拖拽导致误关闭的 bug
  (onClick → onMouseDown + e.target === e.currentTarget)
- 团队模型完善:三种角色(超管/团管/成员)、四层额度检查、
  团管成员管理页、超管团队管理页
- 关闭公开注册,所有账号由管理员创建

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 20:16:21 +08:00

97 lines
2.5 KiB
TypeScript

import { create } from 'zustand';
import type { User, Quota, TeamInfo } from '../types';
import { authApi } from '../lib/api';
interface AuthState {
user: User | null;
accessToken: string | null;
refreshToken: string | null;
isAuthenticated: boolean;
isLoading: boolean;
quota: Quota | null;
team: TeamInfo | null;
teamDisabled: boolean;
login: (username: string, password: string) => Promise<void>;
logout: () => void;
refreshAccessToken: () => Promise<void>;
fetchUserInfo: () => Promise<void>;
initialize: () => Promise<void>;
}
export const useAuthStore = create<AuthState>((set, get) => ({
user: null,
accessToken: localStorage.getItem('access_token'),
refreshToken: localStorage.getItem('refresh_token'),
isAuthenticated: !!localStorage.getItem('access_token'),
isLoading: true,
quota: null,
team: null,
teamDisabled: false,
login: async (username, password) => {
const { data } = await authApi.login(username, password);
localStorage.setItem('access_token', data.tokens.access);
localStorage.setItem('refresh_token', data.tokens.refresh);
set({
user: data.user,
accessToken: data.tokens.access,
refreshToken: data.tokens.refresh,
isAuthenticated: true,
});
// Fetch quota after login
await get().fetchUserInfo();
},
logout: () => {
localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');
set({
user: null,
accessToken: null,
refreshToken: null,
isAuthenticated: false,
quota: null,
team: null,
teamDisabled: false,
});
},
refreshAccessToken: async () => {
const refresh = get().refreshToken;
if (!refresh) throw new Error('No refresh token');
const { data } = await authApi.refreshToken(refresh);
localStorage.setItem('access_token', data.access);
set({ accessToken: data.access });
},
fetchUserInfo: async () => {
try {
const { data } = await authApi.getMe();
const { quota, team, team_disabled, ...user } = data;
set({
user,
quota,
team: team || null,
teamDisabled: team_disabled || false,
isAuthenticated: true,
});
} catch {
// Token invalid
get().logout();
}
},
initialize: async () => {
const token = localStorage.getItem('access_token');
if (token) {
try {
await get().fetchUserInfo();
} catch {
get().logout();
}
}
set({ isLoading: false });
},
}));