feat: 完善平台总后台模块
This commit is contained in:
12
frontend/platform_admin/src/api/auth.ts
Normal file
12
frontend/platform_admin/src/api/auth.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { request } from './http';
|
||||
|
||||
/** 平台登录和当前账号资料的接口模型。 */
|
||||
export type LoginData = { username: string; password: string };
|
||||
export type LoginReply = { access_token: string; token_type: string; identity: string; display_name: string; role_code: string };
|
||||
export type Profile = { identity: string; username: string; display_name: string; avatar: string; role_code: string };
|
||||
|
||||
export const authApi = {
|
||||
login: (data: LoginData) => request<LoginReply>('/auth/login', { method: 'POST', body: JSON.stringify(data) }),
|
||||
profile: () => request<Profile>('/auth/profile'),
|
||||
changePassword: (currentPassword: string, newPassword: string) => request<{ changed: boolean }>('/auth/password', { method: 'PUT', body: JSON.stringify({ current_password: currentPassword, new_password: newPassword }) }),
|
||||
};
|
||||
5
frontend/platform_admin/src/api/delivery.ts
Normal file
5
frontend/platform_admin/src/api/delivery.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { resourceApi } from './resource';
|
||||
|
||||
/** 配送点管理接口。 */
|
||||
export type DeliveryBasic = { identity: string; delivery_code: string; name: string; principal: string; address: string; status: string };
|
||||
export const deliveryApi = { list: () => resourceApi.list<DeliveryBasic>('/delivery/delivery_basic'), create: (data: Record<string, unknown>) => resourceApi.create<DeliveryBasic>('/delivery/delivery_basic', data) };
|
||||
5
frontend/platform_admin/src/api/gas.ts
Normal file
5
frontend/platform_admin/src/api/gas.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { resourceApi } from './resource';
|
||||
|
||||
/** 可燃气体站管理接口。 */
|
||||
export type GasBasic = { identity: string; code: string; name: string; principal: string; address: string; status: string };
|
||||
export const gasApi = { list: () => resourceApi.list<GasBasic>('/gas/gas_basic'), create: (data: Record<string, unknown>) => resourceApi.create<GasBasic>('/gas/gas_basic', data) };
|
||||
21
frontend/platform_admin/src/api/http.ts
Normal file
21
frontend/platform_admin/src/api/http.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
/** 平台总后台的共享 HTTP 客户端,统一处理响应体和 JWT 请求头。 */
|
||||
const apiBaseURL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:12426/heqi/platform/v1';
|
||||
|
||||
export const tokenStorageKey = 'token';
|
||||
|
||||
export type PageResult<T> = { total: number; list: T[] };
|
||||
|
||||
export async function request<T>(path: string, init?: RequestInit): Promise<T> {
|
||||
const token = localStorage.getItem(tokenStorageKey);
|
||||
const response = await fetch(`${apiBaseURL}${path}`, {
|
||||
...init,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(token ? { Authorization: token } : {}),
|
||||
...(init?.headers ?? {}),
|
||||
},
|
||||
});
|
||||
const payload = (await response.json()) as { code?: number; message?: string; details?: T };
|
||||
if (!response.ok || payload.code !== 0) throw new Error(payload.message || '请求失败');
|
||||
return payload.details as T;
|
||||
}
|
||||
@@ -1,62 +1,14 @@
|
||||
// 平台总后台 API 客户端;JWT 使用 BSM 中间件要求的原始令牌值。
|
||||
const apiBaseURL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:12426/heqi/v1';
|
||||
const developmentJWT = import.meta.env.VITE_DEV_JWT?.trim();
|
||||
const tokenStorageKey = 'heqi.platform_admin.access_token';
|
||||
import { request } from './http';
|
||||
import { resourceApi } from './resource';
|
||||
|
||||
export type OrgGasStation = { identity: string; stationCode: string; name: string; principal: string; serviceArea: string; status: string; createdAt: string };
|
||||
export type OrgDeliveryPoint = { identity: string; deliveryCode: string; name: string; gasStationName: string; serviceArea: string; status: string };
|
||||
export type OrgServicePerson = { identity: string; name: string; phoneMasked: string; roles: string; workStatus: string; credentialStatus: string };
|
||||
export type IdnAccount = { identity: string; phoneMasked: string; accountType: string; status: string; serviceArea: string };
|
||||
export type SafEvent = { identity: string; eventCode: string; level: number; title: string; status: string; createdAt: string };
|
||||
export type DashboardOverview = Record<string, number>;
|
||||
export type LoginReply = { accessToken: string; tokenType: string; identity: string; displayName: string; roleCode: string; mustChangePassword: boolean };
|
||||
export type Profile = { identity: string; username: string; displayName: string; roleCode: string; mustChangePassword: boolean; mfaEnabled: boolean };
|
||||
/** 平台角色、菜单、账号和工作台接口。 */
|
||||
export type PlatformRole = { identity: string; role_code: string; name: string; data_scope: string; is_system: boolean; status: string };
|
||||
export type PlatformMenu = { id: number; identity: string; parent_id: number; menu_code: string; name: string; icon: string; path: string; sort_no: number };
|
||||
|
||||
type ListReply<T> = { total: number; list: T[] };
|
||||
type RawOrgDeliveryPoint = { identity: string; delivery_code: string; name: string; gas_station_name?: string; service_area: string; status: string };
|
||||
type RawOrgServicePerson = { identity: string; name: string; phone_masked?: string; roles: string; work_status: string; credential_status: string };
|
||||
type RawIdnAccount = { identity: string; phone_masked: string; account_type: string; status: string; service_area: string };
|
||||
type RawSafEvent = { identity: string; event_code: string; level: number; title: string; status: string; created_at: string };
|
||||
|
||||
function currentToken(): string | undefined {
|
||||
return localStorage.getItem(tokenStorageKey) || developmentJWT;
|
||||
}
|
||||
|
||||
async function request<T>(path: string, init?: RequestInit): Promise<T> {
|
||||
const accessToken = currentToken();
|
||||
const response = await fetch(`${apiBaseURL}${path}`, {
|
||||
...init,
|
||||
headers: { 'Content-Type': 'application/json', ...(accessToken ? { Authorization: accessToken } : {}), ...(init?.headers ?? {}) },
|
||||
});
|
||||
const payload = (await response.json()) as { code?: number; message?: string; details?: T };
|
||||
if (!response.ok || payload.code !== 0) throw new Error(payload.message || '请求失败');
|
||||
return payload.details as T;
|
||||
}
|
||||
|
||||
export const platformAPI = {
|
||||
hasSession: () => Boolean(currentToken()),
|
||||
clearSession: () => localStorage.removeItem(tokenStorageKey),
|
||||
login: async (username: string, password: string) => {
|
||||
const item = await request<{ access_token: string; token_type: string; identity: string; display_name: string; role_code: string; must_change_password: boolean }>('/auth/login', { method: 'POST', body: JSON.stringify({ username, password }) });
|
||||
localStorage.setItem(tokenStorageKey, item.access_token);
|
||||
return { accessToken: item.access_token, tokenType: item.token_type, identity: item.identity, displayName: item.display_name, roleCode: item.role_code, mustChangePassword: item.must_change_password } satisfies LoginReply;
|
||||
},
|
||||
getProfile: async () => {
|
||||
const item = await request<{ identity: string; username: string; display_name: string; role_code: string; must_change_password: boolean; mfa_enabled: boolean }>('/auth/profile');
|
||||
return { identity: item.identity, username: item.username, displayName: item.display_name, roleCode: item.role_code, mustChangePassword: item.must_change_password, mfaEnabled: item.mfa_enabled } satisfies Profile;
|
||||
},
|
||||
changePassword: (currentPassword: string, newPassword: string) => request<{ changed: boolean }>('/auth/password', { method: 'PUT', body: JSON.stringify({ current_password: currentPassword, new_password: newPassword }) }),
|
||||
getDashboard: async () => {
|
||||
const item = await request<{ gas_station_count: number; delivery_point_count: number; service_person_count: number; user_count: number; pending_safety_count: number }>('/dashboard/overview');
|
||||
return { gasStationCount: item.gas_station_count, deliveryPointCount: item.delivery_point_count, servicePersonCount: item.service_person_count, userCount: item.user_count, pendingSafetyCount: item.pending_safety_count };
|
||||
},
|
||||
listOrgGasStation: async () => {
|
||||
const reply = await request<ListReply<{ identity: string; station_code: string; name: string; principal: string; service_area: string; status: string; created_at: string }>>('/organization/org_gas_station');
|
||||
return reply.list.map((item) => ({ identity: item.identity, stationCode: item.station_code, name: item.name, principal: item.principal, serviceArea: item.service_area, status: item.status, createdAt: item.created_at }));
|
||||
},
|
||||
createOrgGasStation: (body: Pick<OrgGasStation, 'stationCode' | 'name' | 'principal' | 'serviceArea'>) => request<OrgGasStation>('/organization/org_gas_station', { method: 'POST', body: JSON.stringify({ station_code: body.stationCode, name: body.name, principal: body.principal, service_area: body.serviceArea }) }),
|
||||
listOrgDeliveryPoint: async () => (await request<ListReply<RawOrgDeliveryPoint>>('/organization/org_delivery_point')).list.map((item) => ({ identity: item.identity, deliveryCode: item.delivery_code, name: item.name, gasStationName: item.gas_station_name ?? '未关联', serviceArea: item.service_area, status: item.status })),
|
||||
listOrgServicePerson: async () => (await request<ListReply<RawOrgServicePerson>>('/organization/org_service_person')).list.map((item) => ({ identity: item.identity, name: item.name, phoneMasked: item.phone_masked ?? '***', roles: item.roles, workStatus: item.work_status, credentialStatus: item.credential_status })),
|
||||
listIdnAccount: async () => (await request<ListReply<RawIdnAccount>>('/identity/idn_account')).list.map((item) => ({ identity: item.identity, phoneMasked: item.phone_masked, accountType: item.account_type, status: item.status, serviceArea: item.service_area })),
|
||||
listSafEvent: async () => (await request<ListReply<RawSafEvent>>('/safety/saf_event')).list.map((item) => ({ identity: item.identity, eventCode: item.event_code, level: item.level, title: item.title, status: item.status, createdAt: item.created_at })),
|
||||
export const platformApi = {
|
||||
overview: () => request<Record<string, number>>('/dashboard/overview'),
|
||||
listAccount: () => request<{ total: number; list: Record<string, unknown>[] }>('/platform/platfrom_account'),
|
||||
listRole: () => resourceApi.list<PlatformRole>('/platform/platform_role'),
|
||||
createRole: (data: Record<string, unknown>) => resourceApi.create<PlatformRole>('/platform/platform_role', data),
|
||||
listMenu: () => request<{ total: number; list: PlatformMenu[] }>('/platform/platform_menu'),
|
||||
};
|
||||
|
||||
11
frontend/platform_admin/src/api/resource.ts
Normal file
11
frontend/platform_admin/src/api/resource.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { request, type PageResult } from './http';
|
||||
|
||||
/** 所有标准 CRUD 资源共享的调用方法。 */
|
||||
export const resourceApi = {
|
||||
list: <T>(resource: string, page = 1, size = 20) => request<PageResult<T>>(`${resource}?page=${page}&size=${size}`),
|
||||
detail: <T>(resource: string, identity: string) => request<T>(`${resource}/${identity}`),
|
||||
create: <T>(resource: string, data: Record<string, unknown>) => request<T>(resource, { method: 'POST', body: JSON.stringify(data) }),
|
||||
update: <T>(resource: string, identity: string, data: Record<string, unknown>) => request<T>(`${resource}/${identity}`, { method: 'PUT', body: JSON.stringify(data) }),
|
||||
updateStatus: (resource: string, identity: string, status: string) => request<{ updated: boolean }>(`${resource}/${identity}/status`, { method: 'PATCH', body: JSON.stringify({ status }) }),
|
||||
archive: (resource: string, identity: string) => request<{ updated: boolean }>(`${resource}/${identity}`, { method: 'DELETE' }),
|
||||
};
|
||||
5
frontend/platform_admin/src/api/staff.ts
Normal file
5
frontend/platform_admin/src/api/staff.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { resourceApi } from './resource';
|
||||
|
||||
/** 服务人员管理接口。 */
|
||||
export type Staff = { identity: string; name: string; phone: string; role_code: string; work_status: string; status: string };
|
||||
export const staffApi = { list: () => resourceApi.list<Staff>('/staff/staff'), create: (data: Record<string, unknown>) => resourceApi.create<Staff>('/staff/staff', data) };
|
||||
5
frontend/platform_admin/src/api/user.ts
Normal file
5
frontend/platform_admin/src/api/user.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { resourceApi } from './resource';
|
||||
|
||||
/** 业主客户管理接口。 */
|
||||
export type User = { identity: string; name: string; phone: string; real_name: string; status: string };
|
||||
export const userApi = { list: () => resourceApi.list<User>('/user/user'), create: (data: Record<string, unknown>) => resourceApi.create<User>('/user/user', data) };
|
||||
Reference in New Issue
Block a user