app1工单抢单 / 拒单 / 提交功能更新 #75

Merged
hnu202326010125 merged 3 commits from luoyuehang_branch into develop 1 month ago

@ -10,5 +10,57 @@ export const workOrderService = {
} catch (error) {
throw error.response?.data || error.message
}
},
// 获取可抢工单
async getAvailableOrders(areaId) {
try {
const response = await api.get(`/api/app/repairman/available-orders?areaId=${areaId}`)
return response.data
} catch (error) {
throw error.response?.data || error.message
}
},
// 抢单
async grabOrder(orderId, repairmanId) {
try {
const response = await api.post('/api/app/repairman/grab-order', {
orderId,
repairmanId
})
return response.data
} catch (error) {
throw error.response?.data || error.message
}
},
// 拒单
async rejectOrder(orderId, repairmanId, reason) {
try {
const response = await api.post('/api/app/repairman/reject-order', {
orderId,
repairmanId,
reason
})
return response.data
} catch (error) {
throw error.response?.data || error.message
}
},
// 提交维修结果
async submitRepairResult(orderId, repairmanId, dealNote, imgUrl = null) {
try {
const response = await api.post('/api/app/repairman/submit-result', {
orderId,
repairmanId,
dealNote,
imgUrl
})
return response.data
} catch (error) {
throw error.response?.data || error.message
}
}
}

@ -35,11 +35,15 @@ export const useAuthStore = defineStore('auth', () => {
return user.value?.userType || localStorage.getItem('userType')
})
// 获取维修人员ID
const getRepairmanId = computed(() => {
return user.value?.userId || localStorage.getItem('userId') // 使用userId而不是repairmanId
})
// 获取维修人员ID
const getRepairmanId = computed(() => {
return user.value?.userId || localStorage.getItem('userId') // 使用userId而不是repairmanId
})
// 获取区域ID
const getAreaId = computed(() => {
return user.value?.areaId || localStorage.getItem('areaId')
})
// 登出
const logout = () => {
@ -53,6 +57,7 @@ const getRepairmanId = computed(() => {
localStorage.removeItem('repairmanId')
localStorage.removeItem('userId')
localStorage.removeItem('username')
localStorage.removeItem('areaId') // 移除 areaId
// 跳转到登录页
router.push('/')
@ -75,6 +80,7 @@ const getRepairmanId = computed(() => {
isAuthenticated,
getUserType,
getRepairmanId,
getAreaId, // 导出 getAreaId
login,
logout,
getUserInfo,

@ -42,7 +42,8 @@ const handleLogin = async () => {
username: result.data.username,
userType: result.data.userType,
userId: result.data.userId,
repairmanId: result.data.userId
repairmanId: result.data.userId,
areaId: result.data.areaId
}, result.data.token);
//
@ -51,6 +52,7 @@ const handleLogin = async () => {
localStorage.setItem('username', result.data.username);
localStorage.setItem('userType', result.data.userType);
localStorage.setItem('repairmanId', result.data.userId);
localStorage.setItem('areaId', result.data.areaId);
alert('登录成功!');
router.push('/home');

@ -1,3 +1,4 @@
<!-- src/views/WorkOrderDetail.vue -->
<template>
<div class="work-order-detail">
<!-- 顶部标题栏 -->
@ -11,34 +12,34 @@
<!-- 主要内容区域 -->
<div class="main-content">
<div class="detail-container">
<div class="detail-container" v-if="currentOrder">
<!-- 工单信息 -->
<div class="detail-section">
<div class="section-title">工单信息</div>
<div class="detail-grid">
<div class="detail-item">
<div class="item-label">工单ID</div>
<div class="item-value">1001</div>
<div class="item-value">{{ currentOrder.orderId }}</div>
</div>
<div class="detail-item">
<div class="item-label">设备ID</div>
<div class="item-value">供水机#A105</div>
<div class="item-value">{{ currentOrder.deviceId }}</div>
</div>
<div class="detail-item">
<div class="item-label">告警项目</div>
<div class="item-value">水位异常</div>
<div class="item-value">{{ getOrderTypeName(currentOrder.orderType) }}</div>
</div>
<div class="detail-item">
<div class="item-label">工单类型</div>
<div class="item-value">系统报修</div>
<div class="item-value">{{ getOrderPriorityName(currentOrder.priority) }}</div>
</div>
<div class="detail-item">
<div class="item-label">上报时间</div>
<div class="item-value">2023-10-28 13:20</div>
<div class="item-value">{{ formatDate(currentOrder.createdTime) }}</div>
</div>
<div class="detail-item">
<div class="item-label">安装位置</div>
<div class="item-value">A区教学楼 2楼走廊</div>
<div class="item-value">{{ getOrderLocation(currentOrder.deviceId) }}</div>
</div>
</div>
</div>
@ -49,11 +50,11 @@
<div class="alert-content">
<div class="alert-item">
<div class="alert-label">告警内容</div>
<div class="alert-value">水位低于设定下限值连续24小时低水位</div>
<div class="alert-value">{{ currentOrder.description }}</div>
</div>
<div class="alert-item">
<div class="alert-label">处理建议</div>
<div class="alert-value">检查供水系统</div>
<div class="alert-value">请根据实际情况进行处理</div>
</div>
</div>
</div>
@ -64,7 +65,7 @@
<div class="repair-content">
<div class="repair-item">
<div class="repair-label">报修项目</div>
<div class="repair-value">更换传感器</div>
<div class="repair-value">{{ currentOrder.description }}</div>
</div>
<!-- 标准维护项目选择 -->
@ -133,20 +134,20 @@
</div>
<!-- 根据工单状态显示不同操作区域 -->
<div v-if="orderStatus === 'pending'" class="action-buttons">
<div v-if="currentOrder.status === 'pending' && !showRepairForm" class="action-buttons">
<button class="action-btn grab" @click="grabOrder">
抢单
</button>
<button class="action-btn reject" @click="rejectOrder">
<button class="action-btn reject" @click="showRejectModal = true">
拒单
</button>
</div>
<div v-if="orderStatus === 'processing' && !showRepairForm" class="action-buttons">
<div v-if="currentOrder.status === 'processing' && !showRepairForm" class="action-buttons">
<button class="action-btn process" @click="startRepair">
开始维修
</button>
<button class="action-btn complete" @click="completeWithoutRepair">
<button class="action-btn complete" @click="showCompleteConfirm = true">
直接完成
</button>
</div>
@ -161,6 +162,30 @@
</div>
</div>
<!-- 拒单弹窗 -->
<div v-if="showRejectModal" class="confirm-modal">
<div class="modal-content">
<div class="modal-header">
<div class="modal-title">拒单</div>
</div>
<div class="modal-body">
<div class="form-group">
<label class="form-label">拒单原因</label>
<textarea
v-model="rejectReason"
class="form-textarea"
placeholder="请输入拒单原因..."
rows="3"
></textarea>
</div>
</div>
<div class="modal-footer">
<button class="modal-btn cancel" @click="showRejectModal = false">取消</button>
<button class="modal-btn confirm" @click="rejectOrder"></button>
</div>
</div>
</div>
<!-- 维修完成确认弹窗 -->
<div v-if="showCompleteConfirm" class="confirm-modal">
<div class="modal-content">
@ -171,7 +196,7 @@
<div class="modal-message">确定直接完成此工单吗此操作将跳过维修记录填写</div>
</div>
<div class="modal-footer">
<button class="modal-btn cancel" @click="cancelComplete"></button>
<button class="modal-btn cancel" @click="showCompleteConfirm = false"></button>
<button class="modal-btn confirm" @click="confirmComplete"></button>
</div>
</div>
@ -188,6 +213,11 @@
</button>
</div>
</div>
<!-- 加载状态 -->
<div v-if="loading" class="loading">
加载中...
</div>
</div>
<!-- 底部导航栏 -->
@ -203,21 +233,29 @@
<script setup>
import { ref, onMounted, computed } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
import { useDeviceStore } from '@/stores/device'
import { workOrderService } from '@/services/workOrderService'
const router = useRouter()
const route = useRoute()
const authStore = useAuthStore()
const deviceStore = useDeviceStore()
//
const orderStatus = ref(route.query.status || 'pending')
//
const loading = ref(false)
const currentOrder = ref(null)
const showRepairForm = ref(false)
const submitting = ref(false)
const showSuccessModal = ref(false)
const showCompleteConfirm = ref(false)
const showRejectModal = ref(false)
//
const selectedStandardItem = ref('')
const selectedActualItem = ref('')
const repairNotes = ref('')
const rejectReason = ref('')
const uploadedPhotos = ref([])
const fileInput = ref(null)
@ -244,17 +282,43 @@ const goToProfile = () => {
}
//
const grabOrder = () => {
console.log('抢单')
showRepairForm.value = true
orderStatus.value = 'processing'
const grabOrder = async () => {
try {
const repairmanId = authStore.getRepairmanId
await workOrderService.grabOrder(currentOrder.value.orderId, repairmanId)
//
currentOrder.value.status = 'processing'
showRepairForm.value = true
alert('抢单成功')
} catch (error) {
console.error('抢单失败:', error)
alert('抢单失败: ' + (error.message || '未知错误'))
}
}
//
const rejectOrder = () => {
if (confirm('确定要拒单吗?')) {
console.log('拒单')
const rejectOrder = async () => {
if (!rejectReason.value.trim()) {
alert('请输入拒单原因')
return
}
try {
const repairmanId = authStore.getRepairmanId
await workOrderService.rejectOrder(
currentOrder.value.orderId,
repairmanId,
rejectReason.value
)
showRejectModal.value = false
alert('拒单成功')
router.push('/work-orders')
} catch (error) {
console.error('拒单失败:', error)
alert('拒单失败: ' + (error.message || '未知错误'))
}
}
@ -264,36 +328,24 @@ const startRepair = () => {
}
//
const completeWithoutRepair = () => {
showCompleteConfirm.value = true
}
//
const confirmComplete = async () => {
try {
console.log('直接完成工单')
// API
await new Promise(resolve => setTimeout(resolve, 1000))
const repairmanId = authStore.getRepairmanId
await workOrderService.submitRepairResult(
currentOrder.value.orderId,
repairmanId,
'直接完成工单'
)
alert('工单已完成')
showCompleteConfirm.value = false
//
router.push({
path: '/work-orders',
query: { tab: 'history' }
})
alert('工单已完成')
router.push('/work-orders')
} catch (error) {
console.error('完成失败:', error)
alert('操作失败,请重试')
alert('操作失败: ' + (error.message || '未知错误'))
}
}
//
const cancelComplete = () => {
showCompleteConfirm.value = false
}
//
const cancelRepair = () => {
if (confirm('确定要取消维修吗?未保存的内容将丢失')) {
@ -317,22 +369,20 @@ const submitRepair = async () => {
submitting.value = true
try {
// API
await new Promise(resolve => setTimeout(resolve, 1500))
console.log('提交维修结果:', {
standardItem: selectedStandardItem.value,
actualItem: selectedActualItem.value,
notes: repairNotes.value,
photos: uploadedPhotos.value.length
})
const repairmanId = authStore.getRepairmanId
// URL
await workOrderService.submitRepairResult(
currentOrder.value.orderId,
repairmanId,
repairNotes.value,
null // URL
)
//
showSuccessModal.value = true
} catch (error) {
console.error('提交失败:', error)
alert('提交失败,请重试')
alert('提交失败: ' + (error.message || '未知错误'))
} finally {
submitting.value = false
}
@ -341,11 +391,7 @@ const submitRepair = async () => {
//
const closeSuccessModal = () => {
showSuccessModal.value = false
//
router.push({
path: '/work-orders',
query: { tab: 'history' }
})
router.push('/work-orders')
}
//
@ -385,17 +431,91 @@ const resetForm = () => {
//
const pageTitle = computed(() => {
return orderStatus.value === 'processing' ? '处理工单' : '工单详情'
if (!currentOrder.value) return '工单详情'
return currentOrder.value.status === 'processing' ? '处理工单' : '工单详情'
})
//
const getOrderTypeName = (type) => {
const types = {
repair: '维修',
maintenance: '保养',
inspection: '巡检'
}
return types[type] || type
}
//
const getOrderPriorityName = (priority) => {
const priorities = {
low: '低',
medium: '中',
high: '高',
urgent: '紧急'
}
return priorities[priority] || priority
}
//
const getOrderLocation = (deviceId) => {
const device = deviceStore.getWaterSupplierById(deviceId)
return device ? device.location : '未知位置'
}
//
const formatDate = (dateStr) => {
if (!dateStr) return '未知时间'
return new Date(dateStr).toLocaleString('zh-CN')
}
//
const loadOrderDetail = async (orderId) => {
loading.value = true
try {
// API
//
const myOrdersRes = await workOrderService.getMyOrders(authStore.getRepairmanId)
const order = myOrdersRes.data.find(o => o.orderId === orderId)
if (order) {
currentOrder.value = order
} else {
//
// 使 areaId 'A'
const areaId = authStore.getAreaId
if (areaId) {
const availableRes = await workOrderService.getAvailableOrders(areaId)
const availableOrder = availableRes.data.find(o => o.orderId === orderId)
if (availableOrder) {
currentOrder.value = availableOrder
} else {
throw new Error('工单不存在')
}
} else {
throw new Error('未找到区域ID无法获取工单信息')
}
}
} catch (error) {
console.error('加载工单详情失败:', error)
alert('加载工单详情失败: ' + (error.message || '未知错误'))
router.push('/work-orders')
} finally {
loading.value = false
}
}
onMounted(() => {
const orderId = route.params.id
const status = route.query.status
console.log('加载工单详情工单ID:', orderId, '状态:', status)
if (orderId) {
loadOrderDetail(orderId)
} else {
router.push('/work-orders')
}
})
</script>
<style scoped>
.work-order-detail {
width: 100%;
@ -928,4 +1048,11 @@ onMounted(() => {
.nav-item:hover {
color: #1890ff;
}
/* 添加加载状态样式 */
.loading {
text-align: center;
padding: 20px;
color: #1890ff;
}
</style>

@ -40,82 +40,84 @@
<div class="main-content">
<!-- 待抢单工单列表 -->
<div v-if="activeTab === 'pending'" class="order-list">
<div class="order-item" @click="viewOrderDetail('1001')">
<div
class="order-item"
v-for="order in availableOrders"
:key="order.orderId"
@click="viewOrderDetail(order.orderId)"
>
<div class="order-content">
<div class="order-title">制水机#A105 - TDS超标</div>
<div class="order-title">{{ order.description }}</div>
<div class="order-info">
<span class="order-location">A区教学楼</span>
<span class="order-time">10分钟前</span>
<span class="order-location">{{ getOrderLocation(order.deviceId) }}</span>
<span class="order-time">{{ formatTime(order.createdTime) }}</span>
</div>
</div>
<button class="order-btn grab" @click.stop="grabOrder('1001')">
<button class="order-btn grab" @click.stop="grabOrder(order.orderId)">
抢单
</button>
</div>
<div class="order-item" @click="viewOrderDetail('1002')">
<div class="order-content">
<div class="order-title">供水机#B105 - 水位异常</div>
<div class="order-info">
<span class="order-location">B区图书馆</span>
<span class="order-time">25分钟前</span>
</div>
</div>
<button class="order-btn grab" @click.stop="grabOrder('1002')">
抢单
</button>
<div v-if="availableOrders.length === 0" class="empty-state">
暂无待抢单工单
</div>
</div>
<!-- 处理中工单列表 -->
<div v-if="activeTab === 'processing'" class="order-list">
<div class="order-item" @click="viewOrderDetail('1002')">
<div
class="order-item"
v-for="order in processingOrders"
:key="order.orderId"
@click="viewOrderDetail(order.orderId)"
>
<div class="order-content">
<div class="order-title">供水机#B105 - 水位异常</div>
<div class="order-title">{{ order.description }}</div>
<div class="order-info">
<span class="order-location">B区图书馆</span>
<span class="order-time">抢单时间: 25分钟前</span>
<span class="order-location">{{ getOrderLocation(order.deviceId) }}</span>
<span class="order-time">抢单时间: {{ formatTime(order.grabbedTime) }}</span>
</div>
<div class="order-status">
<span class="status-badge processing">处理中</span>
</div>
</div>
<button class="order-btn process" @click.stop="startProcessOrder('1002')">
<button class="order-btn process" @click.stop="startProcessOrder(order.orderId)">
继续处理
</button>
</div>
<div class="order-item" @click="viewOrderDetail('1003')">
<div class="order-content">
<div class="order-title">制水机#A102 - TDS超标</div>
<div class="order-info">
<span class="order-location">A区教学楼</span>
<span class="order-time">抢单时间: 15分钟前</span>
</div>
<div class="order-status">
<span class="status-badge pending">待处理</span>
</div>
</div>
<button class="order-btn process" @click.stop="startProcessOrder('1003')">
开始处理
</button>
<div v-if="processingOrders.length === 0" class="empty-state">
暂无处理中的工单
</div>
</div>
<!-- 历史工单列表 -->
<div v-if="activeTab === 'history'" class="order-list">
<div class="order-item">
<div
class="order-item"
v-for="order in completedOrders"
:key="order.orderId"
>
<div class="order-content">
<div class="order-title">供水机#B105 - 滤芯损坏</div>
<div class="order-title">{{ order.description }}</div>
<div class="order-info">
<span class="order-location">B区图书馆</span>
<span class="order-time">2023-10-04</span>
<span class="order-location">{{ getOrderLocation(order.deviceId) }}</span>
<span class="order-time">{{ formatDate(order.completedTime) }}</span>
</div>
</div>
<button class="order-btn completed">
结款
完成
</button>
</div>
<div v-if="completedOrders.length === 0" class="empty-state">
暂无历史工单
</div>
</div>
<!-- 加载状态 -->
<div v-if="loading" class="loading">
加载中...
</div>
<!-- 抢单成功弹窗 -->
@ -142,13 +144,25 @@
</div>
</template>
<script setup>
import { ref } from 'vue'
<script setup>import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
import { workOrderService } from '@/services/workOrderService'
import { useDeviceStore } from '@/stores/device'
const router = useRouter()
const authStore = useAuthStore()
const deviceStore = useDeviceStore()
const activeTab = ref('pending')
const showGrabSuccess = ref(false)
const loading = ref(false)
//
const allOrders = ref([])
const availableOrders = ref([])
const processingOrders = ref([])
const completedOrders = ref([])
//
const switchTab = (tab) => {
@ -172,7 +186,7 @@ const goToInspection = () => {
const goToWorkOrders = () => {
//
if (router.currentRoute.value.path === '/work-orders') {
activeTab.value = 'pending'
loadOrders()
} else {
router.push('/work-orders')
}
@ -188,22 +202,28 @@ const viewOrderDetail = (orderId) => {
}
//
const grabOrder = (orderId) => {
console.log(`抢单 ${orderId}`)
//
showGrabSuccess.value = true
// 3
setTimeout(() => {
showGrabSuccess.value = false
//
switchTab('processing')
}, 3000)
const grabOrder = async (orderId) => {
try {
const repairmanId = authStore.getRepairmanId
await workOrderService.grabOrder(orderId, repairmanId)
//
showGrabSuccess.value = true
//
setTimeout(async () => {
showGrabSuccess.value = false
await loadOrders()
switchTab('processing')
}, 2000)
} catch (error) {
console.error('抢单失败:', error)
alert('抢单失败: ' + (error.message || '未知错误'))
}
}
//
const startProcessOrder = (orderId) => {
console.log(`开始处理工单 ${orderId}`)
// ID
router.push({
path: `/work-orders/${orderId}`,
query: { status: 'processing' }
@ -214,6 +234,74 @@ const startProcessOrder = (orderId) => {
const closeGrabSuccess = () => {
showGrabSuccess.value = false
}
//
const getOrderLocation = (deviceId) => {
const device = deviceStore.getWaterSupplierById(deviceId)
return device ? device.location : '未知位置'
}
//
const formatTime = (timeStr) => {
if (!timeStr) return '未知时间'
const date = new Date(timeStr)
const now = new Date()
const diffMs = now - date
const diffMins = Math.floor(diffMs / 60000)
const diffHours = Math.floor(diffMins / 60)
const diffDays = Math.floor(diffHours / 24)
if (diffMins < 1) return '刚刚'
if (diffMins < 60) return `${diffMins}分钟前`
if (diffHours < 24) return `${diffHours}小时前`
return `${diffDays}天前`
}
//
const formatDate = (dateStr) => {
if (!dateStr) return '未知日期'
return new Date(dateStr).toLocaleDateString('zh-CN')
}
//
const loadOrders = async () => {
loading.value = true
try {
//
const myOrders = await workOrderService.getMyOrders(authStore.getRepairmanId)
//
allOrders.value = myOrders.data || []
processingOrders.value = allOrders.value.filter(order =>
order.status === 'processing'
)
completedOrders.value = allOrders.value.filter(order =>
order.status === 'completed'
)
//
const areaId = authStore.getAreaId // areaId
if (areaId) {
const available = await workOrderService.getAvailableOrders(areaId)
availableOrders.value = available.data || []
} else {
console.warn('未找到区域ID无法获取可抢工单')
availableOrders.value = []
}
} catch (error) {
console.error('加载工单失败:', error)
alert('加载工单失败: ' + (error.message || '未知错误'))
} finally {
loading.value = false
}
}
//
onMounted(() => {
loadOrders()
})
</script>
<style scoped>
@ -263,6 +351,21 @@ const closeGrabSuccess = () => {
color: #096dd9;
}
/* 添加空状态样式 */
.empty-state {
text-align: center;
padding: 40px 20px;
color: #999;
font-size: 14px;
}
/* 添加加载状态样式 */
.loading {
text-align: center;
padding: 20px;
color: #1890ff;
}
/* 标签切换 */
.tab-container {
background: white;

Loading…
Cancel
Save