debug
This commit is contained in:
26
docker-compose.hub.yml
Normal file
26
docker-compose.hub.yml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
services:
|
||||||
|
backend:
|
||||||
|
image: chuxunyu/ems-backend:latest
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
environment:
|
||||||
|
APP_BASE_URL: ${APP_BASE_URL:-http://localhost:5173}
|
||||||
|
TOKEN_SIGNING_KEY: ${TOKEN_SIGNING_KEY:-}
|
||||||
|
JWT_SECRET: ${JWT_SECRET:-}
|
||||||
|
VOLCANO_API_KEY: ${VOLCANO_API_KEY:-}
|
||||||
|
SPRING_MAIL_USERNAME: ${SPRING_MAIL_USERNAME:-}
|
||||||
|
SPRING_MAIL_PASSWORD: ${SPRING_MAIL_PASSWORD:-}
|
||||||
|
volumes:
|
||||||
|
- ems_json_db:/app/json-db
|
||||||
|
- ems_uploads:/app/uploads
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
image: chuxunyu/ems-frontend:latest
|
||||||
|
ports:
|
||||||
|
- "5173:80"
|
||||||
|
depends_on:
|
||||||
|
- backend
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
ems_json_db:
|
||||||
|
ems_uploads:
|
||||||
@@ -8,7 +8,7 @@ server {
|
|||||||
client_max_body_size 50m;
|
client_max_body_size 50m;
|
||||||
|
|
||||||
location /api/ {
|
location /api/ {
|
||||||
proxy_pass http://backend:8080/;
|
proxy_pass http://backend:8080;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
|||||||
36
ems-frontend/ems-monitoring-system/package-lock.json
generated
36
ems-frontend/ems-monitoring-system/package-lock.json
generated
@@ -12,7 +12,9 @@
|
|||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
"axios": "^1.10.0",
|
"axios": "^1.10.0",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
|
"echarts": "^6.0.0",
|
||||||
"element-plus": "^2.10.2",
|
"element-plus": "^2.10.2",
|
||||||
|
"jwt-decode": "^4.0.0",
|
||||||
"pinia": "^3.0.1",
|
"pinia": "^3.0.1",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
"vue-router": "^4.5.0"
|
"vue-router": "^4.5.0"
|
||||||
@@ -2157,6 +2159,16 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/echarts": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/echarts/-/echarts-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "2.3.0",
|
||||||
|
"zrender": "6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.5.170",
|
"version": "1.5.170",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.170.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.170.tgz",
|
||||||
@@ -2772,6 +2784,15 @@
|
|||||||
"graceful-fs": "^4.1.6"
|
"graceful-fs": "^4.1.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jwt-decode": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/kolorist": {
|
"node_modules/kolorist": {
|
||||||
"version": "1.8.0",
|
"version": "1.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz",
|
||||||
@@ -3408,6 +3429,12 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tslib": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==",
|
||||||
|
"license": "0BSD"
|
||||||
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.8.3",
|
"version": "5.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
||||||
@@ -3747,6 +3774,15 @@
|
|||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/zrender": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/zrender/-/zrender-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "2.3.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,9 @@
|
|||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
"axios": "^1.10.0",
|
"axios": "^1.10.0",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
|
"echarts": "^6.0.0",
|
||||||
"element-plus": "^2.10.2",
|
"element-plus": "^2.10.2",
|
||||||
|
"jwt-decode": "^4.0.0",
|
||||||
"pinia": "^3.0.1",
|
"pinia": "^3.0.1",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
"vue-router": "^4.5.0"
|
"vue-router": "^4.5.0"
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import apiClient from './index';
|
import apiClient from './index';
|
||||||
import type { Page, AssigneeInfoDTO, UserInfoDTO, TaskInfoDTO, AttachmentDTO } from './types';
|
import type { Page, AssigneeInfoDTO, UserInfoDTO, TaskInfoDTO, AttachmentDTO } from './types';
|
||||||
import { PollutionType } from './types';
|
import { PollutionType } from './types';
|
||||||
import type { AxiosResponse } from 'axios';
|
|
||||||
|
|
||||||
// 导出必要的类型
|
// 导出必要的类型
|
||||||
export { PollutionType };
|
export { PollutionType };
|
||||||
@@ -42,11 +41,17 @@ export interface TaskDetail {
|
|||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
status: string;
|
status: string;
|
||||||
assignee: AssigneeDTO;
|
assignee?: AssigneeDTO;
|
||||||
assignedAt: string | null;
|
assignedAt: string | null;
|
||||||
completedAt: string | null;
|
completedAt: string | null;
|
||||||
history: TaskHistoryDTO[];
|
history: TaskHistoryDTO[];
|
||||||
submissionInfo: SubmissionInfoDTO | null;
|
submissionInfo: SubmissionInfoDTO | null;
|
||||||
|
gridX?: number | null;
|
||||||
|
gridY?: number | null;
|
||||||
|
assignment?: {
|
||||||
|
assignmentTime?: string;
|
||||||
|
remarks?: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
// --- End: Copied from TaskDetailView.vue ---
|
// --- End: Copied from TaskDetailView.vue ---
|
||||||
|
|
||||||
@@ -97,6 +102,7 @@ export interface FeedbackResponse {
|
|||||||
user: UserInfoDTO;
|
user: UserInfoDTO;
|
||||||
task: TaskDetail | null; // Task can be null if not assigned
|
task: TaskDetail | null; // Task can be null if not assigned
|
||||||
attachments: AttachmentDTO[];
|
attachments: AttachmentDTO[];
|
||||||
|
reporterPhone?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -126,6 +132,7 @@ export interface FeedbackFilters {
|
|||||||
startDate?: string;
|
startDate?: string;
|
||||||
endDate?: string;
|
endDate?: string;
|
||||||
keyword?: string;
|
keyword?: string;
|
||||||
|
submitterId?: number;
|
||||||
page?: number;
|
page?: number;
|
||||||
size?: number;
|
size?: number;
|
||||||
}
|
}
|
||||||
@@ -183,7 +190,7 @@ export function getFeedbackList(params?: FeedbackFilters): Promise<Page<Feedback
|
|||||||
* @param id 反馈ID
|
* @param id 反馈ID
|
||||||
* @returns 反馈详情
|
* @returns 反馈详情
|
||||||
*/
|
*/
|
||||||
export function getFeedbackDetail(id: number): Promise<AxiosResponse<FeedbackDetail>> {
|
export function getFeedbackDetail(id: number): Promise<FeedbackDetail> {
|
||||||
return apiClient.get(`/feedback/${id}`);
|
return apiClient.get(`/feedback/${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,7 +200,7 @@ export function getFeedbackDetail(id: number): Promise<AxiosResponse<FeedbackDet
|
|||||||
* @param data 处理数据
|
* @param data 处理数据
|
||||||
* @returns 处理结果
|
* @returns 处理结果
|
||||||
*/
|
*/
|
||||||
export function processFeedback(id: number, request: { status: string; notes?: string }): Promise<AxiosResponse<FeedbackDetail>> {
|
export function processFeedback(id: number, request: { status: string; notes?: string }): Promise<FeedbackDetail> {
|
||||||
return apiClient.post(`/feedback/${id}/process`, request);
|
return apiClient.post(`/feedback/${id}/process`, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,6 +294,6 @@ export const getFeedbackStats = async () => {
|
|||||||
* 获取待处理的反馈列表
|
* 获取待处理的反馈列表
|
||||||
* @returns 待处理的反馈列表
|
* @returns 待处理的反馈列表
|
||||||
*/
|
*/
|
||||||
export function getPendingFeedback(): Promise<AxiosResponse<FeedbackResponse[]>> {
|
export function getPendingFeedback(): Promise<FeedbackResponse[]> {
|
||||||
return apiClient.get('/feedback/pending');
|
return apiClient.get('/feedback/pending');
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,13 @@
|
|||||||
import apiClient from './index';
|
import apiClient from './index';
|
||||||
import type { Grid } from './types';
|
import type { Grid, UserAccount } from './types';
|
||||||
|
|
||||||
|
export interface GridData extends Grid {}
|
||||||
|
|
||||||
|
export type GridWorker = UserAccount;
|
||||||
|
|
||||||
|
export interface GridWorkersQuery {
|
||||||
|
unassigned?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DTO for updating a grid's core properties.
|
* DTO for updating a grid's core properties.
|
||||||
@@ -16,8 +24,49 @@ export interface GridUpdateRequest {
|
|||||||
* @param data - 包含更新数据的对象
|
* @param data - 包含更新数据的对象
|
||||||
* @returns 更新后的网格对象
|
* @returns 更新后的网格对象
|
||||||
*/
|
*/
|
||||||
export const updateGrid = (gridId: number, data: GridUpdateRequest): Promise<Grid> => {
|
export function updateGrid(gridId: number, data: GridUpdateRequest): Promise<Grid>;
|
||||||
return apiClient.patch(`/grids/${gridId}`, data);
|
export function updateGrid(grid: GridData & GridUpdateRequest): Promise<Grid>;
|
||||||
|
export function updateGrid(
|
||||||
|
gridOrId: number | (GridData & GridUpdateRequest),
|
||||||
|
data?: GridUpdateRequest
|
||||||
|
): Promise<Grid> {
|
||||||
|
if (typeof gridOrId === 'number') {
|
||||||
|
return apiClient.patch(`/grids/${gridOrId}`, data ?? {});
|
||||||
|
}
|
||||||
|
const payload: GridUpdateRequest = {
|
||||||
|
isObstacle: gridOrId.isObstacle,
|
||||||
|
description: gridOrId.description,
|
||||||
|
};
|
||||||
|
return apiClient.patch(`/grids/${gridOrId.id}`, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有网格
|
||||||
|
*/
|
||||||
|
export const getGrids = (): Promise<GridData[]> => {
|
||||||
|
return apiClient.get('/grids');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取网格员列表(可选:仅未分配)
|
||||||
|
*/
|
||||||
|
export const getGridWorkers = async (params?: GridWorkersQuery): Promise<GridWorker[]> => {
|
||||||
|
const response = await apiClient.get('/personnel/users', {
|
||||||
|
params: { role: 'GRID_WORKER' },
|
||||||
|
});
|
||||||
|
let workers: GridWorker[] = Array.isArray(response) ? response : (response as any).content || [];
|
||||||
|
|
||||||
|
if (params?.unassigned) {
|
||||||
|
workers = workers.filter(worker =>
|
||||||
|
worker.gridX === null ||
|
||||||
|
worker.gridY === null ||
|
||||||
|
worker.gridX === undefined ||
|
||||||
|
worker.gridY === undefined ||
|
||||||
|
worker.gridX === -1 ||
|
||||||
|
worker.gridY === -1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return workers;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,3 +89,7 @@ export const assignWorkerByCoordinates = (gridX: number, gridY: number, userId:
|
|||||||
export const unassignWorkerByCoordinates = (gridX: number, gridY: number): Promise<void> => {
|
export const unassignWorkerByCoordinates = (gridX: number, gridY: number): Promise<void> => {
|
||||||
return apiClient.post(`/grids/coordinates/${gridX}/${gridY}/unassign`);
|
return apiClient.post(`/grids/coordinates/${gridX}/${gridY}/unassign`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Aliases for compatibility with existing usage
|
||||||
|
export const assignGridWorkerByCoordinates = assignWorkerByCoordinates;
|
||||||
|
export const removeGridWorkerByCoordinates = unassignWorkerByCoordinates;
|
||||||
|
|||||||
@@ -1,13 +1,24 @@
|
|||||||
import axios from 'axios';
|
import axios, { type AxiosInstance, type AxiosRequestConfig } from 'axios';
|
||||||
import { useAuthStore } from '@/stores/auth';
|
import { useAuthStore } from '@/stores/auth';
|
||||||
|
|
||||||
|
interface ApiClient extends AxiosInstance {
|
||||||
|
request<T = any, R = T>(config: AxiosRequestConfig): Promise<R>;
|
||||||
|
get<T = any, R = T>(url: string, config?: AxiosRequestConfig): Promise<R>;
|
||||||
|
delete<T = any, R = T>(url: string, config?: AxiosRequestConfig): Promise<R>;
|
||||||
|
head<T = any, R = T>(url: string, config?: AxiosRequestConfig): Promise<R>;
|
||||||
|
options<T = any, R = T>(url: string, config?: AxiosRequestConfig): Promise<R>;
|
||||||
|
post<T = any, R = T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;
|
||||||
|
put<T = any, R = T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;
|
||||||
|
patch<T = any, R = T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;
|
||||||
|
}
|
||||||
|
|
||||||
// 创建 Axios 实例
|
// 创建 Axios 实例
|
||||||
const apiClient = axios.create({
|
const apiClient = axios.create({
|
||||||
baseURL: import.meta.env.VITE_API_BASE_URL || '/api', // 从环境变量或默认值设置基础URL
|
baseURL: import.meta.env.VITE_API_BASE_URL || '/api', // 从环境变量或默认值设置基础URL
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
});
|
}) as ApiClient;
|
||||||
|
|
||||||
// 添加请求拦截器
|
// 添加请求拦截器
|
||||||
apiClient.interceptors.request.use(
|
apiClient.interceptors.request.use(
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import type { AxiosResponse, AxiosError } from 'axios';
|
|
||||||
import apiClient from './index';
|
import apiClient from './index';
|
||||||
import type { UserAccount } from './types';
|
import type { UserAccount } from './types';
|
||||||
|
|
||||||
export const getAvailableGridWorkers = (): Promise<UserAccount[]> => {
|
export const getAvailableGridWorkers = (): Promise<UserAccount[]> => {
|
||||||
// 根据TaskAssignmentController的路径应为/tasks/grid-workers
|
// 根据TaskAssignmentController的路径应为/tasks/grid-workers
|
||||||
return apiClient.get<UserAccount[]>('/tasks/grid-workers')
|
return apiClient.get<UserAccount[]>('/tasks/grid-workers')
|
||||||
.then((response: AxiosResponse<UserAccount[]>) => response.data)
|
.catch((error: any) => {
|
||||||
.catch((error: AxiosError) => {
|
|
||||||
console.error('获取可用网格员API错误:', error);
|
console.error('获取可用网格员API错误:', error);
|
||||||
// 如果API调用失败,返回空数组而不是模拟数据
|
// 如果API调用失败,返回空数组而不是模拟数据
|
||||||
return [];
|
return [];
|
||||||
|
|||||||
@@ -263,6 +263,7 @@ export interface UserInfoDTO {
|
|||||||
name: string;
|
name: string;
|
||||||
phone: string;
|
phone: string;
|
||||||
email: string;
|
email: string;
|
||||||
|
role?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TaskInfoDTO {
|
export interface TaskInfoDTO {
|
||||||
|
|||||||
@@ -50,8 +50,8 @@ export function updateUserProfile(userId: number, data: UserUpdateRequest): Prom
|
|||||||
console.log(`API调用: 更新用户资料, userId=${userId}`, data);
|
console.log(`API调用: 更新用户资料, userId=${userId}`, data);
|
||||||
return apiClient.patch(`/personnel/users/${userId}`, data)
|
return apiClient.patch(`/personnel/users/${userId}`, data)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
console.log('更新用户资料API调用成功:', response.data);
|
console.log('更新用户资料API调用成功:', response);
|
||||||
return response.data;
|
return response;
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('更新用户资料API调用失败:', error);
|
console.error('更新用户资料API调用失败:', error);
|
||||||
@@ -70,5 +70,5 @@ export function updateUserProfile(userId: number, data: UserUpdateRequest): Prom
|
|||||||
*/
|
*/
|
||||||
export function updateUserRole(userId: number, role: string, gridX?: number, gridY?: number): Promise<GridWorker> {
|
export function updateUserRole(userId: number, role: string, gridX?: number, gridY?: number): Promise<GridWorker> {
|
||||||
const data = { role, gridX, gridY };
|
const data = { role, gridX, gridY };
|
||||||
return apiClient.put(`/personnel/users/${userId}/role`, data).then(response => response.data);
|
return apiClient.put(`/personnel/users/${userId}/role`, data);
|
||||||
}
|
}
|
||||||
@@ -425,7 +425,7 @@ const formatDate = (dateStr: string) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatRole = (role: string) => {
|
const formatRole = (role?: string) => {
|
||||||
const map: Record<string, string> = {
|
const map: Record<string, string> = {
|
||||||
'ADMIN': '管理员',
|
'ADMIN': '管理员',
|
||||||
'DECISION_MAKER': '决策人',
|
'DECISION_MAKER': '决策人',
|
||||||
@@ -433,6 +433,7 @@ const formatRole = (role: string) => {
|
|||||||
'GRID_WORKER': '网格员',
|
'GRID_WORKER': '网格员',
|
||||||
'PUBLIC_SUPERVISOR': '公众监督员'
|
'PUBLIC_SUPERVISOR': '公众监督员'
|
||||||
};
|
};
|
||||||
|
if (!role) return '未知';
|
||||||
return map[role] || role;
|
return map[role] || role;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const app = createApp(App)
|
|||||||
|
|
||||||
app.use(createPinia())
|
app.use(createPinia())
|
||||||
app.use(router)
|
app.use(router)
|
||||||
app.use(ElementPlus, {
|
const elementPlusOptions = {
|
||||||
locale: zhCn,
|
locale: zhCn,
|
||||||
zIndex: 3000,
|
zIndex: 3000,
|
||||||
popperOptions: {
|
popperOptions: {
|
||||||
@@ -26,6 +26,8 @@ app.use(ElementPlus, {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
})
|
} as any;
|
||||||
|
|
||||||
|
app.use(ElementPlus as any, elementPlusOptions)
|
||||||
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|||||||
4
ems-frontend/ems-monitoring-system/src/shims-element-plus.d.ts
vendored
Normal file
4
ems-frontend/ems-monitoring-system/src/shims-element-plus.d.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
declare module 'element-plus/dist/locale/zh-cn.mjs' {
|
||||||
|
const locale: any;
|
||||||
|
export default locale;
|
||||||
|
}
|
||||||
@@ -15,8 +15,7 @@ import {
|
|||||||
import type {
|
import type {
|
||||||
FeedbackResponse,
|
FeedbackResponse,
|
||||||
FeedbackDetail,
|
FeedbackDetail,
|
||||||
FeedbackFilters,
|
FeedbackFilters
|
||||||
Page
|
|
||||||
} from '@/api/feedback';
|
} from '@/api/feedback';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import { useAuthStore } from '../stores/auth';
|
import { useAuthStore } from '../stores/auth';
|
||||||
|
|||||||
@@ -80,13 +80,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive } from 'vue';
|
import { ref, reactive } from 'vue';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import type { FormInstance, FormRules } from 'element-plus';
|
import type { FormInstance, FormRules, UploadFile } from 'element-plus';
|
||||||
import { PollutionType, SeverityLevel } from '@/api/feedback';
|
import { PollutionType, SeverityLevel } from '@/api/feedback';
|
||||||
import { submitPublicFeedback } from '@/api/public'; // Assuming the function will be in a new api/public.ts
|
import { submitPublicFeedback } from '@/api/public'; // Assuming the function will be in a new api/public.ts
|
||||||
|
|
||||||
const feedbackForm = ref<FormInstance>();
|
const feedbackForm = ref<FormInstance>();
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const fileList = ref([]);
|
const fileList = ref<UploadFile[]>([]);
|
||||||
const rating = ref(2);
|
const rating = ref(2);
|
||||||
|
|
||||||
interface PublicFeedbackForm {
|
interface PublicFeedbackForm {
|
||||||
@@ -131,11 +131,11 @@ const rules = reactive<FormRules>({
|
|||||||
gridY: [{ required: true, type: 'number', message: '网格Y必须为数字' }],
|
gridY: [{ required: true, type: 'number', message: '网格Y必须为数字' }],
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleRemove = (file, fileListUpdated) => {
|
const handleRemove = (file: UploadFile, fileListUpdated: UploadFile[]) => {
|
||||||
fileList.value = fileListUpdated;
|
fileList.value = fileListUpdated;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChange = (file, fileListUpdated) => {
|
const handleChange = (file: UploadFile, fileListUpdated: UploadFile[]) => {
|
||||||
fileList.value = fileListUpdated;
|
fileList.value = fileListUpdated;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,7 +156,9 @@ const submitForm = async () => {
|
|||||||
formData.append('feedback', new Blob([JSON.stringify(submissionData)], { type: 'application/json' }));
|
formData.append('feedback', new Blob([JSON.stringify(submissionData)], { type: 'application/json' }));
|
||||||
|
|
||||||
fileList.value.forEach(file => {
|
fileList.value.forEach(file => {
|
||||||
formData.append('files', file.raw);
|
if (file.raw) {
|
||||||
|
formData.append('files', file.raw);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await submitPublicFeedback(formData);
|
await submitPublicFeedback(formData);
|
||||||
|
|||||||
@@ -76,7 +76,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive } from 'vue';
|
import { ref, reactive } from 'vue';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import type { FormInstance, FormRules } from 'element-plus';
|
import type { FormInstance, FormRules, UploadFile } from 'element-plus';
|
||||||
import { useFeedbackStore } from '@/store/feedback';
|
import { useFeedbackStore } from '@/store/feedback';
|
||||||
import { PollutionType } from '@/api/types';
|
import { PollutionType } from '@/api/types';
|
||||||
import { SeverityLevel } from '@/api/feedback';
|
import { SeverityLevel } from '@/api/feedback';
|
||||||
@@ -84,7 +84,7 @@ import type { FeedbackSubmissionRequest, LocationInfo } from '@/api/feedback';
|
|||||||
|
|
||||||
const feedbackForm = ref<FormInstance>();
|
const feedbackForm = ref<FormInstance>();
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const fileList = ref([]);
|
const fileList = ref<UploadFile[]>([]);
|
||||||
const rating = ref(2); // For el-rate which uses a number. 1=LOW, 2=MEDIUM, 3=HIGH
|
const rating = ref(2); // For el-rate which uses a number. 1=LOW, 2=MEDIUM, 3=HIGH
|
||||||
|
|
||||||
const form = reactive<Omit<FeedbackSubmissionRequest, 'severityLevel'>>({
|
const form = reactive<Omit<FeedbackSubmissionRequest, 'severityLevel'>>({
|
||||||
@@ -121,15 +121,15 @@ const rules = reactive<FormRules>({
|
|||||||
|
|
||||||
const feedbackStore = useFeedbackStore();
|
const feedbackStore = useFeedbackStore();
|
||||||
|
|
||||||
const handleRemove = (file, fileListUpdated) => {
|
const handleRemove = (file: UploadFile, fileListUpdated: UploadFile[]) => {
|
||||||
fileList.value = fileListUpdated;
|
fileList.value = fileListUpdated;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePreview = (file) => {
|
const handlePreview = (file: UploadFile) => {
|
||||||
console.log(file);
|
console.log(file);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChange = (file, fileListUpdated) => {
|
const handleChange = (file: UploadFile, fileListUpdated: UploadFile[]) => {
|
||||||
fileList.value = fileListUpdated;
|
fileList.value = fileListUpdated;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +157,9 @@ const submitForm = async () => {
|
|||||||
formData.append('feedback', new Blob([JSON.stringify(submissionData)], { type: 'application/json' }));
|
formData.append('feedback', new Blob([JSON.stringify(submissionData)], { type: 'application/json' }));
|
||||||
|
|
||||||
fileList.value.forEach(file => {
|
fileList.value.forEach(file => {
|
||||||
formData.append('files', file.raw);
|
if (file.raw) {
|
||||||
|
formData.append('files', file.raw);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await feedbackStore.submitFeedback(formData);
|
await feedbackStore.submitFeedback(formData);
|
||||||
|
|||||||
@@ -33,18 +33,18 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
import type { Feedback } from '@/api/types';
|
import type { FeedbackResponse } from '@/api/feedback';
|
||||||
import { getPendingFeedback, processFeedback } from '@/api/feedback'; // Assuming processFeedback exists
|
import { getPendingFeedback, processFeedback } from '@/api/feedback'; // Assuming processFeedback exists
|
||||||
import { formatDateTime } from '@/utils/formatter';
|
import { formatDateTime } from '@/utils/formatter';
|
||||||
|
|
||||||
const pendingFeedback = ref<Feedback[]>([]);
|
const pendingFeedback = ref<FeedbackResponse[]>([]);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
|
||||||
const fetchPendingFeedback = async () => {
|
const fetchPendingFeedback = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
const response = await getPendingFeedback();
|
const response = await getPendingFeedback();
|
||||||
pendingFeedback.value = response.data;
|
pendingFeedback.value = response;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ElMessage.error('获取待审核反馈失败');
|
ElMessage.error('获取待审核反馈失败');
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@@ -53,13 +53,13 @@ const fetchPendingFeedback = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCreateTask = (feedback: Feedback) => {
|
const handleCreateTask = (feedback: FeedbackResponse) => {
|
||||||
// TODO: Implement task creation logic
|
// TODO: Implement task creation logic
|
||||||
console.log('Create task from feedback:', feedback.id);
|
console.log('Create task from feedback:', feedback.id);
|
||||||
ElMessage.info(`准备为反馈 #${feedback.id} 创建任务`);
|
ElMessage.info(`准备为反馈 #${feedback.id} 创建任务`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRejectFeedback = async (feedback: Feedback) => {
|
const handleRejectFeedback = async (feedback: FeedbackResponse) => {
|
||||||
try {
|
try {
|
||||||
await ElMessageBox.confirm(`确定要拒绝这条反馈吗? (ID: ${feedback.id})`, '确认操作', {
|
await ElMessageBox.confirm(`确定要拒绝这条反馈吗? (ID: ${feedback.id})`, '确认操作', {
|
||||||
confirmButtonText: '确定',
|
confirmButtonText: '确定',
|
||||||
|
|||||||
@@ -171,8 +171,8 @@ const isSubmitDialogVisible = ref(false);
|
|||||||
const fetchTaskDetails = async () => {
|
const fetchTaskDetails = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.get(`/worker/${taskId.value}`);
|
const response = await apiClient.get<TaskDetail>(`/worker/${taskId.value}`);
|
||||||
task.value = response as TaskDetail;
|
task.value = response;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`获取任务详情失败 (ID: ${taskId.value}):`, error);
|
console.error(`获取任务详情失败 (ID: ${taskId.value}):`, error);
|
||||||
ElMessage.error('获取任务详情失败');
|
ElMessage.error('获取任务详情失败');
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ const fetchAllGrids = async () => {
|
|||||||
gridData.value = Array.isArray(response) ? response : (response as any).content || [];
|
gridData.value = Array.isArray(response) ? response : (response as any).content || [];
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
error.value = e.message || '获取网格数据时发生未知错误';
|
error.value = e.message || '获取网格数据时发生未知错误';
|
||||||
ElMessage.error(error.value);
|
ElMessage.error(error.value || '获取网格数据时发生未知错误');
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user