feat(web): 透析 API + 积分账户组件 + 工作台 store + 统计页修复
- dialysis.ts: 新增透析管理 API 模块 - PointsAccountTab.tsx: 积分账户标签页组件 - workbenchStore.ts: 工作台状态管理 - StatisticsDashboard.tsx: 统计页空列表修复 - auth.test.ts: 修复权限码拼写 health.alert → health.alerts - api.test.ts: API 契约测试
This commit is contained in:
@@ -85,7 +85,7 @@ function createFakeUser(overrides: Partial<UserInfo> = {}): UserInfo {
|
||||
|
||||
function createFakeLoginResponse(overrides: Partial<LoginResponse> = {}): LoginResponse {
|
||||
return {
|
||||
access_token: createFakeToken(['health.patient.list', 'health.alert.manage']),
|
||||
access_token: createFakeToken(['health.patient.list', 'health.alerts.manage']),
|
||||
refresh_token: 'refresh-token-xxx',
|
||||
expires_in: 3600,
|
||||
user: createFakeUser(),
|
||||
@@ -157,7 +157,7 @@ describe('useAuthStore', () => {
|
||||
expect(state.user).toEqual(fakeUser);
|
||||
expect(state.isAuthenticated).toBe(true);
|
||||
expect(state.loading).toBe(false);
|
||||
expect(state.permissions).toEqual(['health.patient.list', 'health.alert.manage']);
|
||||
expect(state.permissions).toEqual(['health.patient.list', 'health.alerts.manage']);
|
||||
|
||||
// API 被正确调用
|
||||
expect(mockApiLogin).toHaveBeenCalledWith({ username: 'testuser', password: 'password123' });
|
||||
@@ -283,7 +283,7 @@ describe('useAuthStore', () => {
|
||||
// =========================================================================
|
||||
describe('权限提取', () => {
|
||||
it('登录后 permissions 应从 JWT token 中正确解析', async () => {
|
||||
const permissions = ['health.patient.list', 'health.alert.manage', 'health.report.review'];
|
||||
const permissions = ['health.patient.list', 'health.alerts.manage', 'health.report.review'];
|
||||
const token = createFakeToken(permissions);
|
||||
const fakeResponse = createFakeLoginResponse({ access_token: token });
|
||||
mockApiLogin.mockResolvedValue(fakeResponse);
|
||||
|
||||
58
apps/web/src/stores/workbenchStore.ts
Normal file
58
apps/web/src/stores/workbenchStore.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { create } from 'zustand';
|
||||
import { actionInboxApi, type ActionItem, type WorkbenchStats } from '../api/health/actionInbox';
|
||||
|
||||
interface WorkbenchState {
|
||||
tasks: ActionItem[];
|
||||
selectedTaskId: string | null;
|
||||
tab: 'pending' | 'completed';
|
||||
loading: boolean;
|
||||
stats: WorkbenchStats | null;
|
||||
|
||||
selectTask: (id: string | null) => void;
|
||||
setTab: (tab: 'pending' | 'completed') => void;
|
||||
refreshTasks: () => Promise<void>;
|
||||
refreshStats: () => Promise<void>;
|
||||
completeTask: (id: string) => void;
|
||||
}
|
||||
|
||||
export const useWorkbenchStore = create<WorkbenchState>((set, get) => ({
|
||||
tasks: [],
|
||||
selectedTaskId: null,
|
||||
tab: 'pending',
|
||||
loading: false,
|
||||
stats: null,
|
||||
|
||||
selectTask: (id) => set({ selectedTaskId: id }),
|
||||
|
||||
setTab: (tab) => {
|
||||
set({ tab, selectedTaskId: null });
|
||||
get().refreshTasks();
|
||||
},
|
||||
|
||||
refreshTasks: async () => {
|
||||
set({ loading: true });
|
||||
try {
|
||||
const status = get().tab === 'pending' ? 'pending' : 'completed';
|
||||
const resp = await actionInboxApi.list({ status, page: 1, page_size: 50 });
|
||||
const tasks = Array.isArray(resp?.data) ? resp.data : [];
|
||||
set({ tasks, loading: false });
|
||||
} catch {
|
||||
set({ loading: false });
|
||||
}
|
||||
},
|
||||
|
||||
refreshStats: async () => {
|
||||
try {
|
||||
const stats = await actionInboxApi.stats();
|
||||
set({ stats: stats ?? null });
|
||||
} catch { /* ignore */ }
|
||||
},
|
||||
|
||||
completeTask: (id) => {
|
||||
const { tasks } = get();
|
||||
const remaining = tasks.filter(t => t.id !== id);
|
||||
const nextId = remaining.length > 0 ? remaining[0].id : null;
|
||||
set({ tasks: remaining, selectedTaskId: nextId });
|
||||
get().refreshStats();
|
||||
},
|
||||
}));
|
||||
Reference in New Issue
Block a user