peixue-dev/peidu/uniapp/manager-package/pages/manager/work-orders.vue

384 lines
8.7 KiB
Vue

<template>
<view class="container">
<!-- 统计卡片 -->
<view class="statistics-card">
<view class="stat-item" @click="filterStatus(null)">
<text class="stat-value">{{ statistics.totalOrders || 0 }}</text>
<text class="stat-label">全部工单</text>
</view>
<view class="stat-item" @click="filterStatus(0)">
<text class="stat-value">{{ statistics.pendingOrders || 0 }}</text>
<text class="stat-label">待服务</text>
</view>
<view class="stat-item" @click="filterStatus(1)">
<text class="stat-value">{{ statistics.inProgressOrders || 0 }}</text>
<text class="stat-label">进行中</text>
</view>
<view class="stat-item" @click="filterStatus(2)">
<text class="stat-value">{{ statistics.completedOrders || 0 }}</text>
<text class="stat-label">已完成</text>
</view>
</view>
<!-- 工单列表 -->
<scroll-view
class="work-order-list"
scroll-y
@scrolltolower="loadMore"
>
<view
v-for="item in list"
:key="item.id"
class="work-order-item"
@click="goDetail(item.id)"
>
<view class="order-header">
<view class="order-no">工单号:{{ item.id }}</view>
<view class="status" :class="'status-' + item.status">
{{ getStatusText(item.status) }}
</view>
</view>
<view class="order-info">
<view class="info-row">
<text class="label">服务名称:</text>
<text class="value">{{ item.serviceName }}</text>
</view>
<view class="info-row">
<text class="label">陪伴员:</text>
<text class="value">{{ item.teacherName }}</text>
</view>
<view class="info-row">
<text class="label">服务时间:</text>
<text class="value">{{ item.serviceDate }} {{ item.timeSlot }}</text>
</view>
<view class="info-row">
<text class="label">服务地址:</text>
<text class="value">{{ item.serviceAddress }}</text>
</view>
</view>
<view class="order-footer">
<text class="create-time">创建时间:{{ formatTime(item.createTime) }}</text>
<view class="actions">
<button
v-if="item.status === 0"
class="btn-action"
@click.stop="reassign(item)"
>
重新派单
</button>
<button
v-if="item.status === 1"
class="btn-action primary"
@click.stop="complete(item)"
>
完成工单
</button>
</view>
</view>
</view>
<view v-if="loading" class="loading">加载中...</view>
<view v-if="!hasMore && list.length > 0" class="no-more">没有更多了</view>
<view v-if="!loading && list.length === 0" class="empty">
<text class="empty-icon">📭</text>
<text>暂无工单</text>
</view>
</scroll-view>
</view>
</template>
<script>
import { managerApi } from '@/api/index.js'
export default {
data() {
return {
managerId: null,
currentStatus: null,
list: [],
statistics: {},
loading: false,
hasMore: true,
page: 1
}
},
onLoad(options) {
this.managerId = options.managerId
this.loadStatistics()
this.loadList()
},
methods: {
async loadStatistics() {
try {
const res = await managerApi.getStatistics(this.managerId)
this.statistics = res
} catch (error) {
console.error('加载统计数据失败', error)
}
},
async loadList() {
if (this.loading) return
this.loading = true
try {
const res = await managerApi.getWorkOrders({
managerId: this.managerId,
status: this.currentStatus,
page: this.page,
size: 10
})
if (this.page === 1) {
this.list = res.records
} else {
this.list.push(...res.records)
}
this.hasMore = res.records.length >= 10
} catch (error) {
uni.showToast({ title: '加载失败', icon: 'none' })
} finally {
this.loading = false
}
},
loadMore() {
if (!this.hasMore || this.loading) return
this.page++
this.loadList()
},
filterStatus(status) {
this.currentStatus = status
this.page = 1
this.list = []
this.loadList()
},
goDetail(id) {
uni.navigateTo({
url: `/pages/manager/work-order-detail?id=${id}`
})
},
reassign(item) {
uni.navigateTo({
url: `/pages/manager/assign?orderId=${item.orderId}&managerId=${this.managerId}`
})
},
complete(item) {
const self = this
uni.showModal({
title: '确认完成',
content: '确认完成该工单吗?',
success: (res) => {
if (res.confirm) {
managerApi.updateWorkOrderStatus({
workOrderId: item.id,
status: 2,
remark: '工单已完成'
}).then(() => {
uni.showToast({ title: '操作成功', icon: 'success' })
self.loadStatistics()
self.loadList()
}).catch(error => {
uni.showToast({ title: '操作失败', icon: 'none' })
})
}
}
})
},
formatTime(time) {
if (!time) return ''
return time.replace('T', ' ').substring(0, 16)
},
getStatusText(status) {
const statusMap = {
0: '待派单',
1: '已派单',
2: '待服务',
3: '服务中',
4: '已完成',
'-1': '已取消',
'-2': '已退款'
}
return statusMap[status] || '未知'
}
}
}
</script>
<style lang="scss" scoped>
.container {
height: 100vh;
display: flex;
flex-direction: column;
background: #f8f8f8;
}
.statistics-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 40rpx 30rpx;
display: flex;
justify-content: space-around;
.stat-item {
display: flex;
flex-direction: column;
align-items: center;
.stat-value {
font-size: 48rpx;
font-weight: bold;
color: #fff;
margin-bottom: 12rpx;
}
.stat-label {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.8);
}
}
}
.work-order-list {
flex: 1;
padding: 20rpx 30rpx;
}
.work-order-item {
background: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 20rpx;
.order-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
padding-bottom: 20rpx;
border-bottom: 1rpx solid #f0f0f0;
.order-no {
font-size: 28rpx;
color: #666;
}
.status {
padding: 8rpx 20rpx;
border-radius: 20rpx;
font-size: 24rpx;
&.status-0 {
background: #fff7e6;
color: #fa8c16;
}
&.status-1 {
background: #e6f7ff;
color: #1890ff;
}
&.status-2 {
background: #f6ffed;
color: #52c41a;
}
&.status-3 {
background: #f5f5f5;
color: #999;
}
}
}
.order-info {
margin-bottom: 20rpx;
.info-row {
display: flex;
margin-bottom: 16rpx;
font-size: 28rpx;
&:last-child {
margin-bottom: 0;
}
.label {
color: #999;
min-width: 160rpx;
}
.value {
flex: 1;
color: #333;
}
}
}
.order-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 20rpx;
border-top: 1rpx solid #f0f0f0;
.create-time {
font-size: 24rpx;
color: #999;
}
.actions {
display: flex;
gap: 16rpx;
.btn-action {
padding: 12rpx 32rpx;
background: #f0f0f0;
color: #666;
border: none;
border-radius: 40rpx;
font-size: 26rpx;
&.primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #fff;
}
}
}
}
}
.loading, .no-more {
text-align: center;
padding: 40rpx;
color: #999;
font-size: 26rpx;
}
.empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 120rpx 0;
image {
width: 300rpx;
height: 300rpx;
margin-bottom: 30rpx;
}
text {
font-size: 28rpx;
color: #999;
}
}
</style>