From 50ea83f0cfcaaf7d39b1228c11731afe064cfe4f Mon Sep 17 00:00:00 2001 From: ZHW <1941286652@qq.com> Date: Thu, 11 Dec 2025 13:01:17 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E5=BE=85=E6=8A=A2=E5=8D=95=E5=B7=A5?= =?UTF-8?q?=E5=8D=95=E5=88=97=E8=A1=A8=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/web/src/api/request.ts | 27 ++-- src/main/resources/web/src/stores/auth.ts | 5 +- .../web/src/views/workorder/Pending.vue | 118 +++++++++++------- 3 files changed, 95 insertions(+), 55 deletions(-) diff --git a/src/main/resources/web/src/api/request.ts b/src/main/resources/web/src/api/request.ts index 4f80b6f..8ef72c2 100644 --- a/src/main/resources/web/src/api/request.ts +++ b/src/main/resources/web/src/api/request.ts @@ -6,11 +6,19 @@ export async function request( url: string, options: RequestInit = {} ): Promise { - console.log(`🌐 发送请求: ${API_BASE_URL}${url}`, { - method: options.method || 'GET', + // 处理日志数据,GET/HEAD 方法不显示 body + const method = options.method?.toUpperCase() || 'GET'; + const logData: Record = { + method, headers: options.headers, - body: options.body ? JSON.parse(options.body as string) : undefined, - }) + }; + + // 只对非 GET/HEAD 方法显示 body + if (!['GET', 'HEAD'].includes(method)) { + logData.body = options.body ? JSON.parse(options.body as string) : undefined; + } + + console.log(`🌐 发送请求: ${API_BASE_URL}${url}`, logData) const defaultOptions: RequestInit = { headers: { @@ -39,10 +47,13 @@ export async function request( } try { - const response = await fetch(`${API_BASE_URL}${url}`, { - ...defaultOptions, - ...options, - }) + // 确保 GET/HEAD 请求不包含 body + const fetchOptions = { ...defaultOptions, ...options }; + if (['GET', 'HEAD'].includes(method)) { + delete fetchOptions.body; + } + + const response = await fetch(`${API_BASE_URL}${url}`, fetchOptions) console.log('📥 响应状态:', response.status, response.statusText) diff --git a/src/main/resources/web/src/stores/auth.ts b/src/main/resources/web/src/stores/auth.ts index cff9364..6042893 100644 --- a/src/main/resources/web/src/stores/auth.ts +++ b/src/main/resources/web/src/stores/auth.ts @@ -7,9 +7,10 @@ import type { LoginRequest, LoginVO, ResultVO } from '@/api/types/auth' interface UserInfo { id: number username: string - realName: string - role: string + realName?: string + role?: string avatar?: string + areaId?: string } export const useAuthStore = defineStore('auth', () => { diff --git a/src/main/resources/web/src/views/workorder/Pending.vue b/src/main/resources/web/src/views/workorder/Pending.vue index f468bbf..3099f5b 100644 --- a/src/main/resources/web/src/views/workorder/Pending.vue +++ b/src/main/resources/web/src/views/workorder/Pending.vue @@ -163,7 +163,14 @@ -- 2.34.1 From 533e40f3d30086b4b66b883c5f421e0fa9be41a0 Mon Sep 17 00:00:00 2001 From: ZHW <1941286652@qq.com> Date: Fri, 12 Dec 2025 15:08:16 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E7=BB=B4=E4=BF=AE=E4=BA=BA=E5=91=98?= =?UTF-8?q?=E4=B8=8E=E7=94=A8=E6=88=B7=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/web/src/api/types/repairman.ts | 2 + .../web/src/views/personnel/Maintenance.vue | 217 +++++++++-------- .../web/src/views/personnel/User.vue | 230 +++++++++--------- .../web/src/views/workorder/Processing.vue | 87 ++++--- 4 files changed, 289 insertions(+), 247 deletions(-) create mode 100644 src/main/resources/web/src/api/types/repairman.ts diff --git a/src/main/resources/web/src/api/types/repairman.ts b/src/main/resources/web/src/api/types/repairman.ts new file mode 100644 index 0000000..3f7636f --- /dev/null +++ b/src/main/resources/web/src/api/types/repairman.ts @@ -0,0 +1,2 @@ + // src/api/types/repairman.ts + export type RepairmanStatus = 'idle' | 'busy' | 'vacation' diff --git a/src/main/resources/web/src/views/personnel/Maintenance.vue b/src/main/resources/web/src/views/personnel/Maintenance.vue index c4d1de9..763f1db 100644 --- a/src/main/resources/web/src/views/personnel/Maintenance.vue +++ b/src/main/resources/web/src/views/personnel/Maintenance.vue @@ -10,11 +10,11 @@
- +
@@ -170,7 +170,8 @@ + \ No newline at end of file diff --git a/src/main/resources/web/src/views/personnel/Admin.vue b/src/main/resources/web/src/views/personnel/Admin.vue index 5f49b21..c2ef0d7 100644 --- a/src/main/resources/web/src/views/personnel/Admin.vue +++ b/src/main/resources/web/src/views/personnel/Admin.vue @@ -1,3 +1,4 @@ + @@ -371,6 +653,15 @@ const handleAddAdmin = () => { color: #1890ff; } +.btn-delete { + background-color: #ffebe6; + color: #cf1322; +} + +.btn-delete:hover { + background-color: #ffccc7; +} + .btn-enable { background-color: #e6f7ee; color: #00875a; @@ -410,6 +701,120 @@ const handleAddAdmin = () => { cursor: not-allowed; } +/* 弹窗样式 */ +.modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; +} + +.modal-container { + background-color: white; + border-radius: 8px; + width: 500px; + max-width: 90%; + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1); +} + +.modal-header { + padding: 16px 20px; + border-bottom: 1px solid #eee; + display: flex; + justify-content: space-between; + align-items: center; +} + +.modal-header h3 { + margin: 0; + font-size: 18px; + color: #333; +} + +.modal-close { + background: none; + border: none; + font-size: 20px; + cursor: pointer; + color: #999; +} + +.modal-body { + padding: 20px; +} + +.form-group { + margin-bottom: 16px; +} + +.form-label { + display: block; + margin-bottom: 8px; + font-weight: 500; + color: #333; +} + +.form-label.required::after { + content: '*'; + color: #cf1322; + margin-left: 4px; +} + +.form-group input, +.form-group select { + width: 100%; + padding: 8px 12px; + border: 1px solid #ddd; + border-radius: 4px; + font-size: 14px; +} + +.form-group input:disabled { + background-color: #f5f5f5; + cursor: not-allowed; +} + +.form-actions { + display: flex; + gap: 16px; + justify-content: flex-end; + margin-top: 24px; +} + +.btn-submit { + background: #42b983; + color: white; + border: none; + padding: 8px 16px; + border-radius: 4px; + cursor: pointer; + font-size: 14px; +} + +.btn-submit:hover { + background: #359e75; +} + +.btn-cancel { + background: #f0f0f0; + color: #333; + border: 1px solid #ddd; + padding: 8px 16px; + border-radius: 4px; + cursor: pointer; + font-size: 14px; +} + +.btn-cancel:hover { + background: #e0e0e0; +} + /* 响应式调整 */ @media (max-width: 768px) { .action-bar { diff --git a/src/main/resources/web/src/views/personnel/Maintenance.vue b/src/main/resources/web/src/views/personnel/Maintenance.vue index 763f1db..e96e345 100644 --- a/src/main/resources/web/src/views/personnel/Maintenance.vue +++ b/src/main/resources/web/src/views/personnel/Maintenance.vue @@ -9,16 +9,40 @@
- - - @@ -26,42 +50,48 @@
- - - - - - - + + + + + + + - - - - - + + + + - - - - - + @click="openEditForm(staff)" + > + 编辑 + + + + + + +
姓名联系电话维修片区状态操作
姓名联系电话维修片区状态操作
{{ staff.repairmanName }}{{ staff.phone }}{{ staff.areaId }} +
{{ staff.repairmanName }}{{ staff.phone }}{{ staff.areaId }} {{ getStatusText(staff.status) }} - - + - + -
暂无维修人员数据
暂无维修人员数据
@@ -69,23 +99,102 @@ + + + + + +
@@ -94,9 +203,12 @@ import { ref, computed, onMounted } from 'vue' import { useRouter } from 'vue-router' import { request } from '@/api/request' import { useAuthStore } from '@/stores/auth' -import type { RepairmanStatus } from '@/api/types/repairman' +import type { ResultVO } from '@/api/types/auth' + +// 维修人员状态类型 +type RepairmanStatus = 'idle' | 'busy' | 'vacation' -// 维修人员数据接口 +// 维修人员数据接口(与后端实体保持一致) interface MaintenanceStaff { repairmanId: string repairmanName: string @@ -105,16 +217,54 @@ interface MaintenanceStaff { status: RepairmanStatus } +// 搜索筛选条件接口 +interface SearchFilters { + name: string + areaId: string + status: string +} + +// 表单数据接口 +interface FormData { + repairmanId: string + repairmanName: string + phone: string + areaId: string + status: RepairmanStatus +} + const authStore = useAuthStore() const router = useRouter() +// ========== 已移除权限检查函数 ========== + // 响应式数据 const staffList = ref([]) -const searchKeyword = ref('') +const searchFilters = ref({ + name: '', + areaId: '', + status: '' +}) const currentPage = ref(1) const pageSize = 10 // 每页显示数量 const loading = ref(false) +// 弹窗相关数据 +const isModalOpen = ref(false) +const isEditing = ref(false) +const isDeleteConfirmOpen = ref(false) +const deleteId = ref('') +const deleteName = ref('') + +// 表单数据(初始化) +const form = ref({ + repairmanId: '', + repairmanName: '', + phone: '', + areaId: '', + status: 'idle' +}) + // 获取维修人员列表 const fetchMaintenanceStaff = async () => { loading.value = true @@ -128,24 +278,29 @@ const fetchMaintenanceStaff = async () => { // 构建查询参数 const params = new URLSearchParams() - if (searchKeyword.value.trim()) { - params.append('name', searchKeyword.value.trim()) + if (searchFilters.value.name.trim()) { + params.append('name', searchFilters.value.name.trim()) + } + if (searchFilters.value.areaId) { + params.append('areaId', searchFilters.value.areaId) + } + if (searchFilters.value.status) { + params.append('status', searchFilters.value.status) } // 使用封装的request工具发送请求 - const response = await request<{ - code: number - msg: string - data: MaintenanceStaff[] - }>(`/api/web/repairman/list?${params.toString()}`, { - method: 'GET' - }) + const response = await request>( + `/api/web/repairman/list?${params.toString()}`, + { + method: 'GET' + } + ) // 处理响应 if (response.code === 200) { staffList.value = response.data || [] } else { - const errorMsg = response.msg || `获取失败(错误码:${response.code})` + const errorMsg = response.message || `获取失败(错误码:${response.code})` console.error('获取维修人员列表失败:', errorMsg) alert(`获取维修人员列表失败:${errorMsg}`) } @@ -170,10 +325,18 @@ const fetchMaintenanceStaff = async () => { // 筛选后的维修人员列表 const filteredStaff = computed(() => { + // 前端额外筛选(作为后备保障) return staffList.value.filter(person => { - const keywordMatch = searchKeyword.value.trim() === '' || - person.repairmanName.toLowerCase().includes(searchKeyword.value.toLowerCase()) - return keywordMatch + const nameMatch = searchFilters.value.name.trim() === '' || + person.repairmanName.toLowerCase().includes(searchFilters.value.name.toLowerCase()) + + const areaMatch = searchFilters.value.areaId === '' || + person.areaId === searchFilters.value.areaId + + const statusMatch = searchFilters.value.status === '' || + person.status === searchFilters.value.status + + return nameMatch && areaMatch && statusMatch }) }) @@ -194,6 +357,17 @@ const handleSearch = () => { fetchMaintenanceStaff() } +// 重置筛选条件 +const resetFilters = () => { + searchFilters.value = { + name: '', + areaId: '', + status: '' + } + currentPage.value = 1 + fetchMaintenanceStaff() +} + // 获取状态文本 const getStatusText = (status: RepairmanStatus) => { const statusMap: Record = { @@ -204,21 +378,124 @@ const getStatusText = (status: RepairmanStatus) => { return statusMap[status] || status } -// 编辑处理 -const handleEdit = (id: string) => { - router.push(`/home/personnel/maintenance/edit/${id}`) +// ========== 编辑处理(已移除权限检查) ========== +const openEditForm = (staff: MaintenanceStaff) => { + // 深拷贝数据,避免直接修改原数据 + form.value = JSON.parse(JSON.stringify(staff)) + isEditing.value = true + isModalOpen.value = true +} + +// ========== 新增处理(已移除权限检查) ========== +const openAddForm = () => { + // 重置表单数据 + form.value = { + repairmanId: '', + repairmanName: '', + phone: '', + areaId: '', + status: 'idle' + } + isEditing.value = false + isModalOpen.value = true +} + +// 关闭弹窗 +const closeModal = () => { + isModalOpen.value = false + isEditing.value = false +} + +// 保存维修人员 +const saveRepairman = async () => { + try { + // 验证表单数据 + if (!form.value.repairmanName.trim()) { + alert('请输入姓名') + return + } + if (!form.value.phone.trim()) { + alert('请输入联系电话') + return + } + if (!form.value.areaId) { + alert('请选择维修片区') + return + } + + // 发送请求 + const response = await request>( + `/api/web/repairman/save`, + { + method: 'POST', + body: JSON.stringify(form.value) + } + ) + + if (response.code === 200) { + alert(isEditing.value ? '维修人员更新成功' : '维修人员新增成功') + closeModal() + fetchMaintenanceStaff() // 刷新列表 + } else { + // 如果后端返回权限错误,这里会显示 + alert(`保存失败:${response.message}`) + } + } catch (error: any) { + console.error('保存失败:', error) + if (error.message.includes('403')) { + alert('权限不足:您没有权限执行此操作,请联系管理员') + } else { + alert('保存失败,请稍后重试') + } + } +} + +// 删除确认 +const confirmDelete = (id: string, name: string) => { + deleteId.value = id + deleteName.value = name + isDeleteConfirmOpen.value = true +} + +// 关闭删除确认 +const closeDeleteConfirm = () => { + isDeleteConfirmOpen.value = false + deleteId.value = '' + deleteName.value = '' +} + +// 删除维修人员 +const deleteRepairman = async () => { + if (!deleteId.value) return + + try { + const response = await request( + `/api/web/repairman/${deleteId.value}`, + { + method: 'DELETE' + } + ) + + if (response.code === 200) { + alert('删除成功') + closeDeleteConfirm() + fetchMaintenanceStaff() // 刷新列表 + } else { + alert(`删除失败:${response.message}`) + } + } catch (error: any) { + console.error('删除失败:', error) + alert('删除失败,请稍后重试') + } } // 查看维修记录 const handleViewRecords = (id: string) => { + // 确保 id 参数正确传递 + console.log('跳转到维修记录:', id) router.push(`/home/personnel/maintenance/records/${id}`) } -// 新增维修人员 -const handleAddMaintenance = () => { - router.push('/home/personnel/maintenance/add') -} - // 页面加载时获取数据 onMounted(() => { fetchMaintenanceStaff() @@ -270,16 +547,27 @@ onMounted(() => { background: #359e75; } -.search-box { +.filters { display: flex; - gap: 8px; + gap: 12px; + align-items: center; + flex-wrap: wrap; } -.search-box input { +.filter-item input, +.filter-item select { padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; - width: 240px; + font-size: 14px; +} + +.filter-item input { + width: 180px; +} + +.filter-item select { + min-width: 120px; } .search-btn { @@ -291,6 +579,15 @@ onMounted(() => { cursor: pointer; } +.reset-btn { + background: #f0f0f0; + color: #333; + border: 1px solid #ddd; + padding: 8px 16px; + border-radius: 4px; + cursor: pointer; +} + .maintenance-table { width: 100%; border-collapse: collapse; @@ -365,6 +662,11 @@ onMounted(() => { color: #1890ff; } +.btn-delete { + background-color: #ffebe6; + color: #cf1322; +} + .no-data { text-align: center; padding: 40px 0; @@ -401,12 +703,137 @@ onMounted(() => { align-items: flex-start; } - .search-box { + .filters { width: 100%; } - .search-box input { + .filter-item { width: 100%; } + + .filter-item input, + .filter-item select { + width: 100%; + } +} + +/* 弹窗样式 */ +.modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; +} + +.modal-content { + background: white; + border-radius: 8px; + width: 500px; + max-width: 90%; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + overflow: hidden; +} + +.modal-header { + padding: 16px 20px; + border-bottom: 1px solid #eee; + display: flex; + justify-content: space-between; + align-items: center; +} + +.modal-header h3 { + margin: 0; + font-size: 18px; + color: #333; +} + +.close-btn { + background: none; + border: none; + font-size: 24px; + cursor: pointer; + color: #666; + padding: 4px; +} + +.close-btn:hover { + color: #333; +} + +.modal-body { + padding: 20px; +} + +.form-group { + margin-bottom: 16px; +} + +.form-label { + display: block; + margin-bottom: 8px; + font-weight: 500; + color: #333; +} + +.form-label.required::after { + content: '*'; + color: #cf1322; + margin-left: 4px; +} + +.form-group input, +.form-group select { + width: 100%; + padding: 8px 12px; + border: 1px solid #ddd; + border-radius: 4px; + font-size: 14px; +} + +.disabled-field { + background-color: #f5f5f5; + cursor: not-allowed; +} + +.form-actions { + display: flex; + justify-content: flex-end; + gap: 12px; + margin-top: 24px; +} + +.btn-submit { + background: #42b983; + color: white; + border: none; + padding: 8px 16px; + border-radius: 4px; + font-size: 14px; + cursor: pointer; +} + +.btn-cancel { + background: #f0f0f0; + color: #333; + border: 1px solid #ddd; + padding: 8px 16px; + border-radius: 4px; + cursor: pointer; + font-size: 14px; +} + +.btn-submit:hover { + background: #359e75; +} + +.btn-cancel:hover { + background: #e0e0e0; } - + \ No newline at end of file diff --git a/src/main/resources/web/src/views/personnel/MaintenanceRecord.vue b/src/main/resources/web/src/views/personnel/MaintenanceRecord.vue new file mode 100644 index 0000000..42bdb5e --- /dev/null +++ b/src/main/resources/web/src/views/personnel/MaintenanceRecord.vue @@ -0,0 +1,569 @@ + + + + + + diff --git a/src/main/resources/web/src/views/workorder/Completed.vue b/src/main/resources/web/src/views/workorder/Completed.vue index b675c52..c5fa014 100644 --- a/src/main/resources/web/src/views/workorder/Completed.vue +++ b/src/main/resources/web/src/views/workorder/Completed.vue @@ -1,4 +1,4 @@ - + \ No newline at end of file + diff --git a/src/main/resources/web/src/views/workorder/Processing.vue b/src/main/resources/web/src/views/workorder/Processing.vue index 1379828..c04ed5a 100644 --- a/src/main/resources/web/src/views/workorder/Processing.vue +++ b/src/main/resources/web/src/views/workorder/Processing.vue @@ -249,7 +249,9 @@ const loadProcessingOrders = async () => { } const queryString = params.toString() - const url = `/api/work-orders/my${queryString ? `?${queryString}` : ''}` + // 修改后 + const url = `/api/work-orders/by-status?status=processing${queryString ? `&${queryString}` : ''}` + // 调用后端接口获取处理中工单 const response = await request<{ diff --git a/src/main/resources/web/src/views/workorder/Review.vue b/src/main/resources/web/src/views/workorder/Review.vue index 7c428ce..9894b48 100644 --- a/src/main/resources/web/src/views/workorder/Review.vue +++ b/src/main/resources/web/src/views/workorder/Review.vue @@ -1,4 +1,4 @@ - + \ No newline at end of file + diff --git a/src/main/resources/web/vite.config.ts b/src/main/resources/web/vite.config.ts index 2c67947..c68830d 100644 --- a/src/main/resources/web/vite.config.ts +++ b/src/main/resources/web/vite.config.ts @@ -3,12 +3,33 @@ import vue from '@vitejs/plugin-vue' // https://vite.dev/config/ export default defineConfig({ - plugins: [ - vue(), - ], - resolve: { - alias: { - '@': '/src' + plugins: [ + vue(), + ], + resolve: { + alias: { + '@': '/src' + }, }, - }, -}) + server: { + proxy: { + // 代理所有以 /api 开头的请求到后端 + '/api': { + target: 'http://localhost:8080', // Spring Boot 后端地址 + changeOrigin: true, // 改变请求来源 + secure: false, // 如果是https,可能需要设置为false + // 如果需要重写路径,可以取消下面的注释 + // rewrite: (path) => path.replace(/^\/api/, '') + }, + // 如果需要代理其他路径,可以继续添加 + // '/ws': { + // target: 'ws://localhost:8080', + // ws: true + // } + }, + // 可选:设置端口 + port: 5173, // Vite默认端口 + // 可选:自动打开浏览器 + open: true + } +}) \ No newline at end of file -- 2.34.1