1
This commit is contained in:
99
ems-frontend/ems-monitoring-system/src/api/auth.ts
Normal file
99
ems-frontend/ems-monitoring-system/src/api/auth.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
// @/api/auth.ts
|
||||
import apiClient from './index';
|
||||
import type {
|
||||
LoginRequest,
|
||||
LoginResponse,
|
||||
SignUpRequest,
|
||||
ResetPasswordWithCodeRequest
|
||||
} from './types';
|
||||
|
||||
/**
|
||||
* 用户登录(使用 Fetch API)
|
||||
* @param credentials - 登录凭据 (邮箱和密码)
|
||||
* @returns Promise<LoginResponse>
|
||||
*/
|
||||
export const loginWithFetch = async (credentials: LoginRequest): Promise<LoginResponse> => {
|
||||
const response = await fetch('/api/auth/login', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(credentials),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
let errorMessage = `HTTP error ${response.status}: ${response.statusText}`;
|
||||
const responseText = await response.text();
|
||||
try {
|
||||
const errorBody = JSON.parse(responseText);
|
||||
if (errorBody && errorBody.message) {
|
||||
errorMessage = errorBody.message;
|
||||
} else if (responseText) {
|
||||
errorMessage = responseText;
|
||||
}
|
||||
} catch (e) {
|
||||
if (responseText) {
|
||||
errorMessage = responseText;
|
||||
}
|
||||
}
|
||||
const error: any = new Error(errorMessage);
|
||||
error.status = response.status;
|
||||
throw error;
|
||||
}
|
||||
|
||||
// If response is OK, we need to parse it as JSON.
|
||||
// The stream hasn't been read if response.ok is true.
|
||||
const data = await response.json();
|
||||
return data;
|
||||
};
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
* @param credentials - 登录凭据 (邮箱和密码)
|
||||
* @returns Promise<AxiosResponse<LoginResponse>>
|
||||
*/
|
||||
export const login = (credentials: LoginRequest) => {
|
||||
return apiClient.post<LoginResponse>('/auth/login', credentials);
|
||||
};
|
||||
|
||||
/**
|
||||
* 用户注册
|
||||
* @param data - 注册所需信息
|
||||
*/
|
||||
export const signup = (data: SignUpRequest) => {
|
||||
return apiClient.post('/auth/signup', data);
|
||||
};
|
||||
|
||||
/**
|
||||
* 发送【注册】验证码
|
||||
* @param email - 目标邮箱
|
||||
*/
|
||||
export const sendVerificationCode = (email: string) => {
|
||||
return apiClient.post(`/auth/send-verification-code?email=${email}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* 发送【密码重置】验证码
|
||||
* @param email - 目标邮箱
|
||||
*/
|
||||
export const sendPasswordResetCode = (email: string) => {
|
||||
return apiClient.post(`/auth/send-password-reset-code?email=${email}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* 使用验证码重置密码
|
||||
* @param data - 包含邮箱、验证码和新密码的对象
|
||||
*/
|
||||
export const resetPasswordWithCode = (data: ResetPasswordWithCodeRequest) => {
|
||||
return apiClient.post('/auth/reset-password-with-code', data);
|
||||
};
|
||||
|
||||
/**
|
||||
* 用户登出
|
||||
* 通知后端记录登出操作
|
||||
*/
|
||||
export const logout = () => {
|
||||
return apiClient.post('/auth/logout');
|
||||
};
|
||||
|
||||
// ... 其他认证相关的API调用
|
||||
147
ems-frontend/ems-monitoring-system/src/api/dashboard.ts
Normal file
147
ems-frontend/ems-monitoring-system/src/api/dashboard.ts
Normal file
@@ -0,0 +1,147 @@
|
||||
import apiClient from './index';
|
||||
import type {
|
||||
DashboardStatsDTO,
|
||||
AqiDistributionDTO,
|
||||
TaskStatsDTO,
|
||||
TrendDataPointDTO,
|
||||
GridCoverageDTO,
|
||||
HeatmapPointDTO,
|
||||
PollutionStatsDTO,
|
||||
AqiHeatmapPointDTO,
|
||||
PollutantThresholdDTO,
|
||||
Grid
|
||||
} from './types';
|
||||
|
||||
/**
|
||||
* Fetches the main dashboard statistics.
|
||||
* @returns A promise that resolves to the dashboard stats.
|
||||
*/
|
||||
export const getDashboardStats = (): Promise<DashboardStatsDTO> => {
|
||||
return apiClient.get('/dashboard/stats');
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetches the AQI level distribution.
|
||||
* @returns A promise that resolves to an array of AQI distribution data.
|
||||
*/
|
||||
export const getAqiDistribution = (): Promise<AqiDistributionDTO[]> => {
|
||||
return apiClient.get('/dashboard/reports/aqi-distribution');
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetches the monthly trend of exceedance events.
|
||||
* @returns A promise that resolves to an array of trend data points.
|
||||
*/
|
||||
export const getMonthlyExceedanceTrend = (): Promise<TrendDataPointDTO[]> => {
|
||||
return apiClient.get('/dashboard/reports/monthly-exceedance-trend')
|
||||
.then(data => {
|
||||
console.log('原始趋势数据:', data);
|
||||
// 确保数据格式正确
|
||||
if (Array.isArray(data)) {
|
||||
return data.map(item => ({
|
||||
yearMonth: item.yearMonth || '',
|
||||
count: item.count || 0
|
||||
}));
|
||||
}
|
||||
return [];
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetches the grid coverage statistics by city.
|
||||
* @returns A promise that resolves to an array of grid coverage data.
|
||||
*/
|
||||
export const getGridCoverageByCity = (): Promise<GridCoverageDTO[]> => {
|
||||
return apiClient.get('/dashboard/reports/grid-coverage');
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetches the heatmap data for pollution incidents.
|
||||
* @returns A promise that resolves to an array of heatmap points.
|
||||
*/
|
||||
export const getHeatmapData = (): Promise<HeatmapPointDTO[]> => {
|
||||
return apiClient.get('/dashboard/map/heatmap');
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetches the pollution statistics.
|
||||
* @returns A promise that resolves to an array of pollution stats.
|
||||
*/
|
||||
export const getPollutionStats = (): Promise<PollutionStatsDTO[]> => {
|
||||
return apiClient.get('/dashboard/reports/pollution-stats');
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetches the task completion statistics.
|
||||
* @returns A promise that resolves to the task stats.
|
||||
*/
|
||||
export const getTaskCompletionStats = (): Promise<TaskStatsDTO> => {
|
||||
return apiClient.get('/dashboard/reports/task-completion-stats');
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetches the AQI heatmap data.
|
||||
* @returns A promise that resolves to an array of AQI heatmap points.
|
||||
*/
|
||||
export const getAqiHeatmapData = (): Promise<AqiHeatmapPointDTO[]> => {
|
||||
return apiClient.get('/dashboard/map/aqi-heatmap');
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetches all pollutant thresholds.
|
||||
* @returns A promise that resolves to an array of pollutant thresholds.
|
||||
*/
|
||||
export const getPollutantThresholds = (): Promise<PollutantThresholdDTO[]> => {
|
||||
return apiClient.get('/dashboard/thresholds');
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetches a specific pollutant threshold.
|
||||
* @param pollutantName The name of the pollutant.
|
||||
* @returns A promise that resolves to the pollutant threshold.
|
||||
*/
|
||||
export const getPollutantThreshold = (pollutantName: string): Promise<PollutantThresholdDTO> => {
|
||||
return apiClient.get(`/dashboard/thresholds/${pollutantName}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* Saves a pollutant threshold.
|
||||
* @param threshold The pollutant threshold to save.
|
||||
* @returns A promise that resolves to the saved pollutant threshold.
|
||||
*/
|
||||
export const savePollutantThreshold = (threshold: PollutantThresholdDTO): Promise<PollutantThresholdDTO> => {
|
||||
return apiClient.post('/dashboard/thresholds', threshold);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取每种污染物的月度趋势数据
|
||||
* @returns 每种污染物的月度趋势数据
|
||||
*/
|
||||
export const getPollutantMonthlyTrends = (value: string): Promise<Record<string, TrendDataPointDTO[]>> => {
|
||||
return apiClient.get('/dashboard/reports/pollutant-monthly-trends')
|
||||
.then(data => {
|
||||
console.log('原始污染物趋势数据:', data);
|
||||
// 确保数据格式正确
|
||||
if (data && typeof data === 'object') {
|
||||
const result: Record<string, TrendDataPointDTO[]> = {};
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
if (Array.isArray(value)) {
|
||||
result[key] = value.map(item => ({
|
||||
yearMonth: item.yearMonth || '',
|
||||
count: item.count || 0
|
||||
}));
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
return {};
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetches all grid data from the server.
|
||||
* @returns A promise that resolves to an array of Grid objects.
|
||||
*/
|
||||
export const getGrids = (): Promise<Grid[]> => {
|
||||
return apiClient.get('/grids');
|
||||
};
|
||||
292
ems-frontend/ems-monitoring-system/src/api/feedback.ts
Normal file
292
ems-frontend/ems-monitoring-system/src/api/feedback.ts
Normal file
@@ -0,0 +1,292 @@
|
||||
import apiClient from './index';
|
||||
import type { Page, AssigneeInfoDTO, UserInfoDTO, TaskInfoDTO, AttachmentDTO } from './types';
|
||||
import { PollutionType } from './types';
|
||||
import type { AxiosResponse } from 'axios';
|
||||
|
||||
// 导出必要的类型
|
||||
export { PollutionType };
|
||||
|
||||
// API URL
|
||||
const API_URL = '/feedback';
|
||||
|
||||
// --- Start: Copied from TaskDetailView.vue for now ---
|
||||
// This is technical debt and should be refactored into a central types file.
|
||||
interface FeedbackDTO {
|
||||
feedbackId: number;
|
||||
eventId: string;
|
||||
title: string;
|
||||
}
|
||||
interface AssigneeDTO {
|
||||
id: number;
|
||||
name: string;
|
||||
phone: string;
|
||||
}
|
||||
interface TaskHistoryDTO {
|
||||
id: number;
|
||||
oldStatus: string | null;
|
||||
newStatus: string;
|
||||
comments: string;
|
||||
changedAt: string;
|
||||
changedBy: {
|
||||
id: number;
|
||||
name:string;
|
||||
};
|
||||
}
|
||||
interface SubmissionInfoDTO {
|
||||
comments: string;
|
||||
attachments: AttachmentDTO[];
|
||||
}
|
||||
export interface TaskDetail {
|
||||
id: number;
|
||||
feedback: FeedbackDTO;
|
||||
title: string;
|
||||
description: string;
|
||||
status: string;
|
||||
assignee: AssigneeDTO;
|
||||
assignedAt: string | null;
|
||||
completedAt: string | null;
|
||||
history: TaskHistoryDTO[];
|
||||
submissionInfo: SubmissionInfoDTO | null;
|
||||
}
|
||||
// --- End: Copied from TaskDetailView.vue ---
|
||||
|
||||
/**
|
||||
* 反馈状态枚举
|
||||
*/
|
||||
export enum FeedbackStatus {
|
||||
AI_REVIEWING = 'AI_REVIEWING', // AI审核中
|
||||
PENDING_REVIEW = 'PENDING_REVIEW', // 待人工审核
|
||||
REJECTED = 'REJECTED', // 已拒绝
|
||||
CONFIRMED = 'CONFIRMED', // 已确认
|
||||
ASSIGNED = 'ASSIGNED', // 已分配
|
||||
IN_PROGRESS = 'IN_PROGRESS', // 处理中
|
||||
RESOLVED = 'RESOLVED', // 已解决
|
||||
CLOSED = 'CLOSED', // 已关闭
|
||||
PENDING_ASSIGNMENT = 'PENDING_ASSIGNMENT', // 待分配
|
||||
PROCESSED = 'PROCESSED' // 已处理
|
||||
}
|
||||
|
||||
/**
|
||||
* 严重程度枚举
|
||||
*/
|
||||
export enum SeverityLevel {
|
||||
LOW = 'LOW', // 低
|
||||
MEDIUM = 'MEDIUM', // 中
|
||||
HIGH = 'HIGH' // 高
|
||||
}
|
||||
|
||||
/**
|
||||
* 反馈列表项接口 (现在匹配后端的 FeedbackResponseDTO)
|
||||
*/
|
||||
export interface FeedbackResponse {
|
||||
id: number;
|
||||
eventId: string;
|
||||
title: string;
|
||||
description: string;
|
||||
pollutionType: PollutionType;
|
||||
severityLevel: SeverityLevel;
|
||||
status: FeedbackStatus;
|
||||
textAddress: string;
|
||||
gridX: number;
|
||||
gridY: number;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
createdAt: string; // ISO date string
|
||||
updatedAt: string; // ISO date string
|
||||
submitterId: number;
|
||||
user: UserInfoDTO;
|
||||
task: TaskDetail | null; // Task can be null if not assigned
|
||||
attachments: AttachmentDTO[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 反馈详情接口 (为了简化,我们可以让它继承自 FeedbackResponse)
|
||||
*/
|
||||
export interface FeedbackDetail extends FeedbackResponse {
|
||||
aiAnalysis?: string;
|
||||
reviewNotes?: string;
|
||||
assignmentId?: number;
|
||||
assignmentMethod?: string;
|
||||
assignmentTime?: string;
|
||||
assignedWorkerId?: number;
|
||||
resolutionNotes?: string;
|
||||
resolutionTime?: string;
|
||||
// `images` is now `attachments`, which is already in FeedbackResponse
|
||||
}
|
||||
|
||||
/**
|
||||
* 反馈筛选参数接口
|
||||
*/
|
||||
export interface FeedbackFilters {
|
||||
status?: FeedbackStatus;
|
||||
pollutionType?: PollutionType;
|
||||
severityLevel?: SeverityLevel;
|
||||
cityName?: string;
|
||||
districtName?: string;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
keyword?: string;
|
||||
page?: number;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 反馈处理请求接口
|
||||
*/
|
||||
export interface ProcessFeedbackRequest {
|
||||
status: FeedbackStatus;
|
||||
notes?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 反馈分配请求接口
|
||||
*/
|
||||
export interface AssignFeedbackRequest {
|
||||
gridWorkerId: number;
|
||||
notes?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 反馈拒绝请求接口
|
||||
*/
|
||||
export interface RejectFeedbackRequest {
|
||||
notes: string;
|
||||
}
|
||||
|
||||
export interface LocationInfo {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
textAddress: string;
|
||||
gridX: number;
|
||||
gridY: number;
|
||||
}
|
||||
|
||||
export interface FeedbackSubmissionRequest {
|
||||
title: string;
|
||||
description: string;
|
||||
pollutionType: PollutionType;
|
||||
severityLevel: SeverityLevel;
|
||||
location: LocationInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取反馈列表
|
||||
* @param params 筛选参数
|
||||
* @returns 分页反馈列表
|
||||
*/
|
||||
export function getFeedbackList(params?: FeedbackFilters): Promise<Page<FeedbackResponse>> {
|
||||
return apiClient.get(API_URL, { params });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取反馈详情
|
||||
* @param id 反馈ID
|
||||
* @returns 反馈详情
|
||||
*/
|
||||
export function getFeedbackDetail(id: number): Promise<AxiosResponse<FeedbackDetail>> {
|
||||
return apiClient.get(`/feedback/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理反馈
|
||||
* @param id 反馈ID
|
||||
* @param data 处理数据
|
||||
* @returns 处理结果
|
||||
*/
|
||||
export function processFeedback(id: number, request: { status: string; notes?: string }): Promise<AxiosResponse<FeedbackDetail>> {
|
||||
return apiClient.post(`/feedback/${id}/process`, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分配反馈给网格员
|
||||
* @param id 反馈ID
|
||||
* @param data 分配数据
|
||||
* @returns 分配结果
|
||||
*/
|
||||
export function assignFeedback(id: number, data: AssignFeedbackRequest): Promise<any> {
|
||||
return apiClient.post(`${API_URL}/${id}/assign`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记反馈为已解决
|
||||
* @param id 反馈ID
|
||||
* @param notes 解决说明
|
||||
* @returns 操作结果
|
||||
*/
|
||||
export function resolveFeedback(id: number, notes?: string): Promise<any> {
|
||||
return apiClient.post(`${API_URL}/${id}/resolve`, { notes });
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭反馈
|
||||
* @param id 反馈ID
|
||||
* @param notes 关闭说明
|
||||
* @returns 操作结果
|
||||
*/
|
||||
export function closeFeedback(id: number, notes?: string): Promise<any> {
|
||||
return apiClient.post(`${API_URL}/${id}/close`, { notes });
|
||||
}
|
||||
|
||||
/**
|
||||
* 拒绝反馈
|
||||
* @param id 反馈ID
|
||||
* @param data 拒绝数据
|
||||
* @returns 操作结果
|
||||
*/
|
||||
export function rejectFeedback(id: number, data: RejectFeedbackRequest): Promise<any> {
|
||||
return apiClient.post(`/supervisor/reviews/${id}/reject`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认反馈
|
||||
* @param id 反馈ID
|
||||
* @returns 响应体
|
||||
*/
|
||||
export function confirmFeedback(id: number): Promise<any> {
|
||||
return apiClient.post(`${API_URL}/${id}/confirm`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交新的反馈
|
||||
* @param formData 包含反馈数据和文件的表单
|
||||
* @returns 提交结果
|
||||
*/
|
||||
export function submitFeedback(formData: FormData): Promise<any> {
|
||||
return apiClient.post(`${API_URL}/submit`, formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取反馈统计数据
|
||||
* @returns 反馈统计数据
|
||||
*/
|
||||
export const getFeedbackStats = async () => {
|
||||
try {
|
||||
const response = await apiClient.get(`${API_URL}/stats`);
|
||||
return response || {}; // 如果响应为空,返回一个空对象
|
||||
} catch (error) {
|
||||
console.error('获取反馈统计数据API错误:', error);
|
||||
// 即使API失败也返回一个默认的统计对象结构,防止UI崩溃
|
||||
return {
|
||||
total: 0,
|
||||
pending: 0,
|
||||
confirmed: 0,
|
||||
assigned: 0,
|
||||
inProgress: 0,
|
||||
resolved: 0,
|
||||
closed: 0,
|
||||
rejected: 0,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取待处理的反馈列表
|
||||
* @returns 待处理的反馈列表
|
||||
*/
|
||||
export function getPendingFeedback(): Promise<AxiosResponse<FeedbackResponse[]>> {
|
||||
return apiClient.get('/feedback/pending');
|
||||
}
|
||||
42
ems-frontend/ems-monitoring-system/src/api/grid.ts
Normal file
42
ems-frontend/ems-monitoring-system/src/api/grid.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import apiClient from './index';
|
||||
import type { Grid } from './types';
|
||||
|
||||
/**
|
||||
* DTO for updating a grid's core properties.
|
||||
* This should align with GridUpdateRequest in the backend.
|
||||
*/
|
||||
export interface GridUpdateRequest {
|
||||
isObstacle?: boolean;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新网格信息
|
||||
* @param gridId - 要更新的网格ID
|
||||
* @param data - 包含更新数据的对象
|
||||
* @returns 更新后的网格对象
|
||||
*/
|
||||
export const updateGrid = (gridId: number, data: GridUpdateRequest): Promise<Grid> => {
|
||||
return apiClient.patch(`/grids/${gridId}`, data);
|
||||
};
|
||||
|
||||
/**
|
||||
* 通过坐标将网格员分配到网格
|
||||
* @param gridX - 网格X坐标
|
||||
* @param gridY - 网格Y坐标
|
||||
* @param userId - 要分配的用户ID
|
||||
* @returns 成功则返回void
|
||||
*/
|
||||
export const assignWorkerByCoordinates = (gridX: number, gridY: number, userId: number): Promise<void> => {
|
||||
return apiClient.post(`/grids/coordinates/${gridX}/${gridY}/assign`, { userId });
|
||||
};
|
||||
|
||||
/**
|
||||
* 通过坐标从网格中移除网格员
|
||||
* @param gridX - 网格X坐标
|
||||
* @param gridY - 网格Y坐标
|
||||
* @returns 成功则返回void
|
||||
*/
|
||||
export const unassignWorkerByCoordinates = (gridX: number, gridY: number): Promise<void> => {
|
||||
return apiClient.post(`/grids/coordinates/${gridX}/${gridY}/unassign`);
|
||||
};
|
||||
50
ems-frontend/ems-monitoring-system/src/api/index.ts
Normal file
50
ems-frontend/ems-monitoring-system/src/api/index.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import axios from 'axios';
|
||||
import { useAuthStore } from '@/stores/auth';
|
||||
|
||||
// 创建 Axios 实例
|
||||
const apiClient = axios.create({
|
||||
baseURL: import.meta.env.VITE_API_BASE_URL || '/api', // 从环境变量或默认值设置基础URL
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
// 添加请求拦截器
|
||||
apiClient.interceptors.request.use(
|
||||
(config) => {
|
||||
// 这个拦截器会在每个请求发送前执行
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) {
|
||||
// 为请求头添加Authorization字段
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
// 对请求错误做些什么
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// 响应拦截器
|
||||
apiClient.interceptors.response.use(
|
||||
(response) => {
|
||||
// 对响应数据做点什么
|
||||
return response.data;
|
||||
},
|
||||
(error) => {
|
||||
// 对响应错误做点什么
|
||||
if (error.response && error.response.status === 401) {
|
||||
// 如果是401错误,说明token无效或过期
|
||||
// 我们需要调用auth store的logout方法
|
||||
// 为了避免循环依赖,我们在函数内部获取store实例
|
||||
const authStore = useAuthStore();
|
||||
authStore.logout();
|
||||
// 在这里重定向到登录页
|
||||
window.location.href = '/login';
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
export default apiClient;
|
||||
10
ems-frontend/ems-monitoring-system/src/api/log.ts
Normal file
10
ems-frontend/ems-monitoring-system/src/api/log.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import apiClient from '.';
|
||||
import type { OperationLog } from './types';
|
||||
|
||||
export function getOperationLogs(params: any): Promise<OperationLog[]> {
|
||||
return apiClient.get('/logs', { params });
|
||||
}
|
||||
|
||||
export function getMyOperationLogs(): Promise<OperationLog[]> {
|
||||
return apiClient.get('/logs/my-logs');
|
||||
}
|
||||
30
ems-frontend/ems-monitoring-system/src/api/pathfinding.ts
Normal file
30
ems-frontend/ems-monitoring-system/src/api/pathfinding.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import apiClient from './index';
|
||||
|
||||
/**
|
||||
* Represents a point with x and y coordinates.
|
||||
* Matches the backend Point DTO.
|
||||
*/
|
||||
export interface Point {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the request payload for finding a path.
|
||||
* Matches the backend PathfindingRequest DTO.
|
||||
*/
|
||||
export interface PathfindingRequest {
|
||||
startX: number;
|
||||
startY: number;
|
||||
endX: number;
|
||||
endY: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the backend API to find a path between two points.
|
||||
* @param request The pathfinding request containing start and end coordinates.
|
||||
* @returns A promise that resolves to a list of points representing the path.
|
||||
*/
|
||||
export const findPath = (request: PathfindingRequest): Promise<Point[]> => {
|
||||
return apiClient.post('/pathfinding/find', request);
|
||||
};
|
||||
40
ems-frontend/ems-monitoring-system/src/api/personnel.ts
Normal file
40
ems-frontend/ems-monitoring-system/src/api/personnel.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import apiClient from './index';
|
||||
import type { Page, Pageable, UserAccount, UserUpdateRequest } from './types';
|
||||
|
||||
/**
|
||||
* 获取用户列表(可分页、可筛选)
|
||||
* @param params - 包含分页和筛选条件的参数
|
||||
* @returns 用户数据分页对象
|
||||
*/
|
||||
export const getUsers = (params: { role?: string; name?: string; } & Pageable): Promise<Page<UserAccount>> => {
|
||||
return apiClient.get('/personnel/users', { params });
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取所有网格员信息
|
||||
* @returns 所有网格员的列表
|
||||
*/
|
||||
export const getAllGridWorkers = async (): Promise<UserAccount[]> => {
|
||||
// We fetch with a large size to get all workers, assuming the number is manageable.
|
||||
// A more robust solution might involve paginating through all results if the number of workers is very large.
|
||||
const response = await getUsers({ role: 'GRID_WORKER', page: 0, size: 1000 });
|
||||
return response.content;
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据ID更新用户信息
|
||||
* @param userId - 用户ID
|
||||
* @param data - 需要更新的用户信息
|
||||
* @returns 更新后的用户信息
|
||||
*/
|
||||
export const updateUser = (userId: number, data: UserUpdateRequest): Promise<UserAccount> => {
|
||||
return apiClient.patch(`/personnel/users/${userId}`, data);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据ID删除用户
|
||||
* @param userId - 用户ID
|
||||
*/
|
||||
export const deleteUser = (userId: number): Promise<void> => {
|
||||
return apiClient.delete(`/personnel/users/${userId}`);
|
||||
};
|
||||
15
ems-frontend/ems-monitoring-system/src/api/public.ts
Normal file
15
ems-frontend/ems-monitoring-system/src/api/public.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import apiClient from './index';
|
||||
|
||||
/**
|
||||
* Submits public feedback.
|
||||
* This endpoint is for unauthenticated users (guests).
|
||||
* @param formData The form data containing the feedback details and any files.
|
||||
* @returns A promise that resolves on successful submission.
|
||||
*/
|
||||
export const submitPublicFeedback = (formData: FormData): Promise<any> => {
|
||||
return apiClient.post('/public/feedback', formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
});
|
||||
};
|
||||
37
ems-frontend/ems-monitoring-system/src/api/tasks.ts
Normal file
37
ems-frontend/ems-monitoring-system/src/api/tasks.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import type { AxiosResponse, AxiosError } from 'axios';
|
||||
import apiClient from './index';
|
||||
import type { UserAccount } from './types';
|
||||
|
||||
export const getAvailableGridWorkers = (): Promise<UserAccount[]> => {
|
||||
// 根据TaskAssignmentController的路径应为/tasks/grid-workers
|
||||
return apiClient.get<UserAccount[]>('/tasks/grid-workers')
|
||||
.then((response: AxiosResponse<UserAccount[]>) => response.data)
|
||||
.catch((error: AxiosError) => {
|
||||
console.error('获取可用网格员API错误:', error);
|
||||
// 如果API调用失败,返回空数组而不是模拟数据
|
||||
return [];
|
||||
});
|
||||
};
|
||||
|
||||
export const assignTask = (feedbackId: number, assigneeId: number): Promise<any> => {
|
||||
return apiClient.post('/tasks/assign', { feedbackId, assigneeId });
|
||||
};
|
||||
|
||||
/**
|
||||
* Approves a submitted task.
|
||||
* @param taskId The ID of the task to approve.
|
||||
* @returns A promise that resolves with the updated task details.
|
||||
*/
|
||||
export const approveTask = (taskId: number): Promise<any> => {
|
||||
return apiClient.post(`/management/tasks/${taskId}/approve`);
|
||||
};
|
||||
|
||||
/**
|
||||
* Rejects a submitted task.
|
||||
* @param taskId The ID of the task to reject.
|
||||
* @param reason The reason for rejection.
|
||||
* @returns A promise that resolves with the updated task details.
|
||||
*/
|
||||
export const rejectTask = (taskId: number, reason: string): Promise<any> => {
|
||||
return apiClient.post(`/management/tasks/${taskId}/reject`, { reason });
|
||||
};
|
||||
307
ems-frontend/ems-monitoring-system/src/api/types.ts
Normal file
307
ems-frontend/ems-monitoring-system/src/api/types.ts
Normal file
@@ -0,0 +1,307 @@
|
||||
/**
|
||||
* 通用用户信息类型, 反映了JWT中包含的声明
|
||||
*/
|
||||
export interface User {
|
||||
id: number;
|
||||
name: string;
|
||||
email: string;
|
||||
role: string;
|
||||
phone?: string;
|
||||
status?: string;
|
||||
region?: string;
|
||||
skills?: string[];
|
||||
gridX?: number;
|
||||
gridY?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登录请求体
|
||||
*/
|
||||
export interface LoginRequest {
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录成功响应体, 与后端完全匹配
|
||||
*/
|
||||
export interface LoginResponse {
|
||||
accessToken: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户注册请求体
|
||||
*/
|
||||
export interface SignUpRequest {
|
||||
name: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
password: string;
|
||||
verificationCode: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用验证码重置密码请求体, 对应后端的 PasswordResetWithCodeDto
|
||||
*/
|
||||
export interface ResetPasswordWithCodeRequest {
|
||||
email: string;
|
||||
code: string;
|
||||
newPassword: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页请求参数
|
||||
*/
|
||||
export interface Pageable {
|
||||
page?: number;
|
||||
size?: number;
|
||||
sort?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页响应体
|
||||
*/
|
||||
export interface Page<T> {
|
||||
content: T[];
|
||||
totalPages: number;
|
||||
totalElements: number;
|
||||
size: number;
|
||||
number: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 后端 UserAccount 实体类的完整映射
|
||||
*/
|
||||
export interface UserAccount {
|
||||
id: number;
|
||||
name: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
role: string;
|
||||
status: string;
|
||||
gender?: string;
|
||||
region?: string;
|
||||
gridX?: number;
|
||||
gridY?: number;
|
||||
level?: string;
|
||||
skills?: string[];
|
||||
currentLatitude?: number;
|
||||
currentLongitude?: number;
|
||||
createdAt: string; // Assuming ISO date string
|
||||
updatedAt: string; // Assuming ISO date string
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户更新请求体, 对应后端的 UserUpdateRequest
|
||||
*/
|
||||
export interface UserUpdateRequest {
|
||||
name?: string;
|
||||
phone?: string;
|
||||
region?: string;
|
||||
role?: string;
|
||||
status?: string;
|
||||
gender?: 'MALE' | 'FEMALE' | 'OTHER';
|
||||
gridX?: number;
|
||||
gridY?: number;
|
||||
level?: string;
|
||||
skills?: string[];
|
||||
currentLatitude?: number;
|
||||
currentLongitude?: number;
|
||||
}
|
||||
|
||||
// --- Dashboard DTOs ---
|
||||
|
||||
/**
|
||||
* Key statistics for the main dashboard.
|
||||
* @param totalFeedbacks - Total number of feedbacks.
|
||||
* @param confirmedFeedbacks - Number of confirmed feedbacks.
|
||||
* @param totalAqiRecords - Total number of AQI records.
|
||||
* @param activeGridWorkers - Number of active grid workers.
|
||||
*/
|
||||
export type DashboardStatsDTO = {
|
||||
totalFeedbacks: number;
|
||||
confirmedFeedbacks: number;
|
||||
totalAqiRecords: number;
|
||||
activeGridWorkers: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Data for AQI level distribution chart.
|
||||
* @param level - The AQI descriptive level (e.g., "Good", "Moderate").
|
||||
* @param count - The number of monitoring points at this level.
|
||||
*/
|
||||
export type AqiDistributionDTO = {
|
||||
level: string;
|
||||
count: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* A single data point for a time-series trend chart.
|
||||
* @param yearMonth - The date for the data point in "YYYY-MM" format.
|
||||
* @param count - The value for that date (e.g., number of exceedances).
|
||||
*/
|
||||
export type TrendDataPointDTO = {
|
||||
yearMonth: string;
|
||||
count: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Grid coverage statistics for a specific city.
|
||||
* @param city - The name of the city.
|
||||
* @param totalGrids - The total number of grids in the city.
|
||||
* @param coveredGrids - The number of grids with assigned personnel.
|
||||
* @param coverageRate - The calculated coverage percentage.
|
||||
*/
|
||||
export type GridCoverageDTO = {
|
||||
city: string;
|
||||
totalGrids: number;
|
||||
coveredGrids: number;
|
||||
coverageRate: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* A single point for a heatmap visualization.
|
||||
* @param lat - Latitude of the data point.
|
||||
* @param lng - Longitude of the data point.
|
||||
* @param value - The intensity value for the heatmap.
|
||||
*/
|
||||
export type HeatmapPointDTO = {
|
||||
lat: number;
|
||||
lng: number;
|
||||
value: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Detailed statistics for a specific pollutant.
|
||||
* @param pollutantName - The name of the pollutant (e.g., "PM2.5").
|
||||
* @param averageValue - The average value over a period.
|
||||
* @param maxValue - The maximum recorded value.
|
||||
* @param unit - The measurement unit (e.g., "µg/m³").
|
||||
*/
|
||||
export type PollutionStatsDTO = {
|
||||
pollutantName: string;
|
||||
averageValue: number;
|
||||
maxValue: number;
|
||||
unit: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Statistics on task completion status.
|
||||
* @param totalTasks - Total number of tasks.
|
||||
* @param completedTasks - Number of completed tasks.
|
||||
* @param completionRate - Completion rate of tasks.
|
||||
*/
|
||||
export type TaskStatsDTO = {
|
||||
totalTasks: number;
|
||||
completedTasks: number;
|
||||
completionRate: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* A single point for the AQI heatmap, including pollutant info.
|
||||
* Extends HeatmapPointDTO with additional details.
|
||||
*/
|
||||
export type AqiHeatmapPointDTO = HeatmapPointDTO & {
|
||||
primaryPollutant: string;
|
||||
aqi: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* 污染物类型枚举
|
||||
*/
|
||||
export enum PollutionType {
|
||||
PM25 = 'PM25',
|
||||
O3 = 'O3',
|
||||
NO2 = 'NO2',
|
||||
SO2 = 'SO2',
|
||||
OTHER = 'OTHER'
|
||||
}
|
||||
|
||||
/**
|
||||
* 污染物阈值设置
|
||||
* @param pollutionType - 污染物类型
|
||||
* @param pollutantName - 污染物名称
|
||||
* @param threshold - 阈值
|
||||
* @param unit - 单位
|
||||
* @param description - 描述
|
||||
*/
|
||||
export type PollutantThresholdDTO = {
|
||||
pollutionType: PollutionType;
|
||||
pollutantName: string;
|
||||
threshold: number;
|
||||
unit: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a geographical grid cell.
|
||||
* Matches the backend Grid entity.
|
||||
*/
|
||||
export interface Grid {
|
||||
id: number;
|
||||
gridX: number;
|
||||
gridY: number;
|
||||
cityName: string;
|
||||
districtName?: string;
|
||||
description?: string;
|
||||
isObstacle: boolean;
|
||||
}
|
||||
|
||||
// ... 您可以根据API文档继续添加其他类型定义
|
||||
|
||||
// --- Task and Assignment DTOs ---
|
||||
|
||||
export interface AssigneeInfoDTO {
|
||||
id: number;
|
||||
name: string;
|
||||
phone: string;
|
||||
}
|
||||
|
||||
export interface UserInfoDTO {
|
||||
id: number;
|
||||
name: string;
|
||||
phone: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
export interface TaskInfoDTO {
|
||||
id: number;
|
||||
status: string; // Should match TaskStatus enum from backend
|
||||
assignee: AssigneeInfoDTO;
|
||||
submissionInfo: SubmissionInfoDTO | null;
|
||||
}
|
||||
|
||||
export interface AttachmentDTO {
|
||||
id: number;
|
||||
fileName: string;
|
||||
fileType: string;
|
||||
url: string;
|
||||
uploadedAt: string; // ISO date string
|
||||
}
|
||||
|
||||
export interface SubmissionInfoDTO {
|
||||
comments: string;
|
||||
attachments: AttachmentDTO[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作日志类型
|
||||
*/
|
||||
export interface OperationLog {
|
||||
id: number;
|
||||
userId: number;
|
||||
userName: string;
|
||||
operationType: string;
|
||||
operationTypeDesc: string;
|
||||
description: string;
|
||||
targetId: string;
|
||||
targetType: string;
|
||||
ipAddress: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Task Summary DTO for supervisor view.
|
||||
* This should match `TaskSummaryDTO` from the backend.
|
||||
*/
|
||||
74
ems-frontend/ems-monitoring-system/src/api/user.ts
Normal file
74
ems-frontend/ems-monitoring-system/src/api/user.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import apiClient from './index';
|
||||
import type { GridWorker } from './grid';
|
||||
|
||||
/**
|
||||
* 用户信息接口
|
||||
*/
|
||||
export interface UserInfo {
|
||||
id: number;
|
||||
name: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
role: string;
|
||||
avatar?: string;
|
||||
permissions?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户更新请求接口
|
||||
*/
|
||||
export interface UserUpdateRequest {
|
||||
name?: string;
|
||||
phone?: string;
|
||||
email?: string;
|
||||
region?: string;
|
||||
level?: string;
|
||||
gridX?: number | null;
|
||||
gridY?: number | null;
|
||||
currentLatitude?: number | null;
|
||||
currentLongitude?: number | null;
|
||||
skills?: string[];
|
||||
status?: string;
|
||||
gender?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户信息
|
||||
* @returns 用户信息
|
||||
*/
|
||||
export function getCurrentUser(): Promise<UserInfo> {
|
||||
return apiClient.get('/auth/profile');
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户资料
|
||||
* @param userId 用户ID
|
||||
* @param data 更新数据
|
||||
* @returns 更新后的用户信息
|
||||
*/
|
||||
export function updateUserProfile(userId: number, data: UserUpdateRequest): Promise<GridWorker> {
|
||||
console.log(`API调用: 更新用户资料, userId=${userId}`, data);
|
||||
return apiClient.patch(`/personnel/users/${userId}`, data)
|
||||
.then(response => {
|
||||
console.log('更新用户资料API调用成功:', response.data);
|
||||
return response.data;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('更新用户资料API调用失败:', error);
|
||||
console.error('错误详情:', error.response?.data || error.message);
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户角色
|
||||
* @param userId 用户ID
|
||||
* @param role 角色名称
|
||||
* @param gridX 网格X坐标(可选)
|
||||
* @param gridY 网格Y坐标(可选)
|
||||
* @returns 更新后的用户信息
|
||||
*/
|
||||
export function updateUserRole(userId: number, role: string, gridX?: number, gridY?: number): Promise<GridWorker> {
|
||||
const data = { role, gridX, gridY };
|
||||
return apiClient.put(`/personnel/users/${userId}/role`, data).then(response => response.data);
|
||||
}
|
||||
Reference in New Issue
Block a user