586 lines
15 KiB
TypeScript
586 lines
15 KiB
TypeScript
// 管理员页面逻辑
|
||
import { UserInfo, Marker, Order, DeliveryPerson, WarehouseInfo } from '../../types';
|
||
import mapService from '../../services/mapService';
|
||
import warehouseService from '../../services/warehouseService';
|
||
import orderService from '../../services/orderService';
|
||
import deliveryService from '../../services/deliveryPersonService';
|
||
|
||
|
||
Page({
|
||
data: {
|
||
userInfo: null as UserInfo | null,
|
||
mapCenter: mapService.getKunmingCenter(),
|
||
markers: [] as Marker[],
|
||
polyline: {} as any,
|
||
|
||
// 订单相关
|
||
orders: [] as Order[],
|
||
filteredOrders: [] as Order[],
|
||
activeTab: 'all',
|
||
showAssignModal: false,
|
||
selectedOrderId: 0,
|
||
showAddModal: false,
|
||
|
||
// 新增订单表单数据
|
||
warehouseIndex: 0,
|
||
warehouses: [] as WarehouseInfo[],
|
||
endName: '',
|
||
goodsType: '',
|
||
goodsWeight: 0,
|
||
|
||
// 员工相关
|
||
deliveryPersons: [] as DeliveryPerson[],
|
||
availableStaff: [] as DeliveryPerson[],
|
||
|
||
// 底部页签
|
||
currentTab: 'main'
|
||
},
|
||
|
||
onLoad() {
|
||
// 模拟管理员登录状态
|
||
this.setData({
|
||
userInfo: {
|
||
id: 1,
|
||
name: '管理员',
|
||
role: 'ADMIN' as any,
|
||
phone: '13800138000'
|
||
}
|
||
});
|
||
|
||
// 初始化数据
|
||
this.initData();
|
||
},
|
||
|
||
onShow() {
|
||
// 页面显示时刷新数据
|
||
this.refreshData();
|
||
},
|
||
|
||
// 初始化数据
|
||
initData() {
|
||
// 获取仓库数据
|
||
warehouseService.getWarehouses().then(warehouses => {
|
||
this.setData({
|
||
warehouses
|
||
});
|
||
}).catch(error => {
|
||
console.error('获取仓库数据失败:', error);
|
||
});
|
||
// 刷新数据
|
||
this.refreshData();
|
||
},
|
||
|
||
// 刷新数据
|
||
refreshData() {
|
||
// 获取订单数据 - 使用正确的getPendingOrders方法
|
||
orderService.getPendingOrders().then(orders => {
|
||
// 获取员工数据 - 使用正确的getDeliveryPersons方法
|
||
deliveryService.getDeliveryPersons().then(deliveryPersons => {
|
||
// 更新地图标记点
|
||
const markers = this.generateMarkers(orders, deliveryPersons);
|
||
|
||
this.setData({
|
||
orders,
|
||
deliveryPersons,
|
||
markers
|
||
});
|
||
|
||
// 过滤订单
|
||
this.filterOrders();
|
||
}).catch(error => {
|
||
console.error('获取货运人员数据失败:', error);
|
||
});
|
||
}).catch(error => {
|
||
console.error('获取订单数据失败:', error);
|
||
});
|
||
},
|
||
|
||
// 生成地图标记点
|
||
generateMarkers(orders: Order[], deliveryPersons: DeliveryPerson[]): Marker[] {
|
||
const markers: Marker[] = [];
|
||
|
||
// 添加订单起点标记(仓库)
|
||
const warehouseMarkers = new Map<number, Marker>();
|
||
orders.forEach(order => {
|
||
if (!warehouseMarkers.has(order.startPoint.id)) {
|
||
warehouseMarkers.set(order.startPoint.id, {
|
||
id: order.startPoint.id,
|
||
longitude: order.startPoint.longitude,
|
||
latitude: order.startPoint.latitude,
|
||
title: order.startPoint.name,
|
||
iconPath: '/images/warehouse.png',
|
||
width: 32,
|
||
height: 32,
|
||
zIndex: 10,
|
||
callout: {
|
||
content: order.startPoint.name,
|
||
color: '#333',
|
||
fontSize: 24,
|
||
borderRadius: 10,
|
||
bgColor: '#fff',
|
||
padding: 10,
|
||
display: 'BYCLICK'
|
||
}
|
||
});
|
||
}
|
||
});
|
||
|
||
// 添加订单终点标记
|
||
orders.forEach((order) => {
|
||
markers.push({
|
||
id: 10000 + order.id, // 避免ID冲突
|
||
longitude: order.endPoint.longitude,
|
||
latitude: order.endPoint.latitude,
|
||
title: order.endPoint.name,
|
||
iconPath: this.getOrderStatusIcon(order.status),
|
||
width: 32,
|
||
height: 32,
|
||
zIndex: 20,
|
||
callout: {
|
||
content: `${order.endPoint.name}\n${order.goodsType} ${order.goodsWeight}kg`,
|
||
color: '#333',
|
||
fontSize: 24,
|
||
borderRadius: 10,
|
||
bgColor: '#fff',
|
||
padding: 10,
|
||
display: 'BYCLICK'
|
||
}
|
||
});
|
||
});
|
||
|
||
// 添加员工位置标记
|
||
deliveryPersons.forEach(person => {
|
||
// 使用默认坐标,实际位置由locationTrackingService统一管理
|
||
markers.push({
|
||
id: 20000 + person.id, // 避免ID冲突
|
||
longitude: 102.833722, // 默认经度
|
||
latitude: 24.880095, // 默认纬度
|
||
title: person.name,
|
||
iconPath: this.getStaffStatusIcon(person.status),
|
||
width: 32,
|
||
height: 32,
|
||
zIndex: 30,
|
||
callout: {
|
||
content: `${person.name}\n订单数: ${person.currentOrders.length}\n状态: ${this.getStatusText(person.status)}`,
|
||
color: '#333',
|
||
fontSize: 24,
|
||
borderRadius: 10,
|
||
bgColor: '#fff',
|
||
padding: 10,
|
||
display: 'BYCLICK'
|
||
}
|
||
});
|
||
});
|
||
|
||
// 添加仓库标记
|
||
markers.push(...Array.from(warehouseMarkers.values()));
|
||
|
||
return markers;
|
||
},
|
||
|
||
// 获取订单状态对应的图标
|
||
getOrderStatusIcon(status: string): string {
|
||
switch (status) {
|
||
case 'pending':
|
||
return '/images/order-pending.png';
|
||
case 'assigned':
|
||
return '/images/order-assigned.png';
|
||
case 'in_transit':
|
||
return '/images/order-transit.png';
|
||
case 'delivered':
|
||
return '/images/order-delivered.png';
|
||
default:
|
||
return '/images/order-pending.png';
|
||
}
|
||
},
|
||
|
||
// 获取员工状态对应的图标
|
||
getStaffStatusIcon(staff: any): string {
|
||
// 根据员工角色返回不同的图标
|
||
const userRole = staff?.role || 'employee';
|
||
return userRole === 'ADMIN' ? '/images/crown.png' : '/images/truck.png';
|
||
},
|
||
|
||
// 获取状态文本
|
||
getStatusText(status: string): string {
|
||
const statusMap: Record<string, string> = {
|
||
'pending': '未分配',
|
||
'assigned': '已分配',
|
||
'in_transit': '配送中',
|
||
'delivered': '已完成',
|
||
'idle': '空闲',
|
||
'busy': '忙碌',
|
||
'offline': '离线'
|
||
};
|
||
return statusMap[status] || status;
|
||
},
|
||
|
||
// 过滤订单
|
||
filterOrders() {
|
||
const { orders, activeTab } = this.data;
|
||
let filteredOrders = orders;
|
||
|
||
if (activeTab !== 'all') {
|
||
filteredOrders = orders.filter(order => order.status === activeTab);
|
||
}
|
||
|
||
this.setData({
|
||
filteredOrders
|
||
});
|
||
},
|
||
|
||
// 切换订单标签
|
||
switchTab(e: any) {
|
||
const tab = e.currentTarget.dataset.tab;
|
||
this.setData({
|
||
activeTab: tab
|
||
});
|
||
this.filterOrders();
|
||
},
|
||
|
||
// 处理订单点击
|
||
handleOrderTap(e: any) {
|
||
const orderId = e.currentTarget.dataset.id;
|
||
const order = this.data.orders.find(o => o.id === orderId);
|
||
|
||
if (!order) return;
|
||
|
||
if (order.status === 'pending') {
|
||
// 未分配的订单,显示分配弹窗
|
||
const availableStaff = this.data.deliveryPersons.filter(person => person.status === 'idle');
|
||
this.setData({
|
||
showAssignModal: true,
|
||
selectedOrderId: orderId,
|
||
availableStaff
|
||
});
|
||
} else if (order.status === 'assigned' || order.status === 'in_transit') {
|
||
// 已分配的订单,跳转到地图并聚焦到配送员
|
||
const assignedPerson = this.data.deliveryPersons.find(person =>
|
||
person.currentOrders.some(o => o.id === orderId)
|
||
);
|
||
|
||
if (assignedPerson) {
|
||
this.setData({
|
||
mapCenter: { longitude: 102.833722, latitude: 24.880095 } // 默认坐标
|
||
});
|
||
|
||
// 绘制从起点到终点的路线
|
||
this.drawRoute(order.startPoint.longitude, order.startPoint.latitude,
|
||
order.endPoint.longitude, order.endPoint.latitude);
|
||
}
|
||
}
|
||
},
|
||
|
||
// 处理员工点击
|
||
handleStaffTap(e: any) {
|
||
const staffId = e.currentTarget.dataset.id;
|
||
const staff = this.data.deliveryPersons.find(p => p.id === staffId);
|
||
|
||
if (staff) {
|
||
// 跳转到地图并聚焦到员工位置
|
||
this.setData({
|
||
mapCenter: staff.currentLocation
|
||
});
|
||
}
|
||
},
|
||
|
||
// 绘制路线
|
||
drawRoute(startLng: number, startLat: number, endLng: number, endLat: number) {
|
||
const origin = `${startLng},${startLat}`;
|
||
const destination = `${endLng},${endLat}`;
|
||
|
||
mapService.getDrivingRoute(origin, destination).then(result => {
|
||
if (result.polyline) {
|
||
// 解码polyline
|
||
const points = this.decodePolyline(result.polyline);
|
||
|
||
if (points.length > 0) {
|
||
this.setData({
|
||
polyline: {
|
||
points,
|
||
color: '#0091ff',
|
||
width: 8,
|
||
dottedLine: false
|
||
}
|
||
});
|
||
}
|
||
}
|
||
}).catch(err => {
|
||
console.error('绘制路线失败:', err);
|
||
});
|
||
},
|
||
|
||
// 解码polyline
|
||
decodePolyline(polyline: string): Array<{ longitude: number; latitude: number }> {
|
||
const points: Array<{ longitude: number; latitude: number }> = [];
|
||
let index = 0, len = polyline.length;
|
||
let lat = 0, lng = 0;
|
||
|
||
while (index < len) {
|
||
let b, shift = 0, result = 0;
|
||
do {
|
||
b = polyline.charCodeAt(index++) - 63;
|
||
result |= (b & 0x1f) << shift;
|
||
shift += 5;
|
||
} while (b >= 0x20);
|
||
const dlat = ((result & 1) !== 0 ? ~(result >> 1) : (result >> 1));
|
||
lat += dlat;
|
||
|
||
shift = 0;
|
||
result = 0;
|
||
do {
|
||
b = polyline.charCodeAt(index++) - 63;
|
||
result |= (b & 0x1f) << shift;
|
||
shift += 5;
|
||
} while (b >= 0x20);
|
||
const dlng = ((result & 1) !== 0 ? ~(result >> 1) : (result >> 1));
|
||
lng += dlng;
|
||
|
||
points.push({
|
||
longitude: lng / 100000,
|
||
latitude: lat / 100000
|
||
});
|
||
}
|
||
|
||
return points;
|
||
},
|
||
|
||
// 显示分配弹窗
|
||
showAssignModal() {
|
||
this.setData({
|
||
showAssignModal: true
|
||
});
|
||
},
|
||
|
||
// 隐藏分配弹窗
|
||
hideAssignModal() {
|
||
this.setData({
|
||
showAssignModal: false
|
||
});
|
||
},
|
||
|
||
// 分配订单
|
||
assignOrder(e: any) {
|
||
const staffId = e.currentTarget.dataset.id;
|
||
const { selectedOrderId } = this.data;
|
||
|
||
if (!selectedOrderId || !staffId) {
|
||
wx.showToast({
|
||
title: '请选择订单和货运人员',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
|
||
// 修复:使用正确的orderService.assignOrder方法
|
||
orderService.assignOrder(selectedOrderId, staffId).then(result => {
|
||
if (result.success) {
|
||
wx.showToast({
|
||
title: '指派成功',
|
||
icon: 'success'
|
||
});
|
||
|
||
// 刷新数据
|
||
this.refreshData();
|
||
} else {
|
||
wx.showToast({
|
||
title: result.message || '指派失败',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
}).catch(error => {
|
||
console.error('指派订单失败:', error);
|
||
wx.showToast({
|
||
title: '指派失败,请重试',
|
||
icon: 'none'
|
||
});
|
||
});
|
||
|
||
this.hideAssignModal();
|
||
},
|
||
|
||
// 显示新增订单弹窗
|
||
showAddOrderModal() {
|
||
this.setData({
|
||
showAddModal: true,
|
||
endName: '',
|
||
goodsType: '',
|
||
goodsWeight: 0,
|
||
warehouseIndex: 0
|
||
});
|
||
},
|
||
|
||
// 隐藏新增订单弹窗
|
||
hideAddModal() {
|
||
this.setData({
|
||
showAddModal: false
|
||
});
|
||
},
|
||
|
||
// 仓库选择变化
|
||
onWarehouseChange(e: any) {
|
||
this.setData({
|
||
warehouseIndex: e.detail.value
|
||
});
|
||
},
|
||
|
||
// 终点名称输入
|
||
onEndNameInput(e: any) {
|
||
this.setData({
|
||
endName: e.detail.value
|
||
});
|
||
},
|
||
|
||
// 货物类型输入
|
||
onGoodsTypeInput(e: any) {
|
||
this.setData({
|
||
goodsType: e.detail.value
|
||
});
|
||
},
|
||
|
||
// 货物重量输入
|
||
onGoodsWeightInput(e: any) {
|
||
this.setData({
|
||
goodsWeight: parseFloat(e.detail.value) || 0
|
||
});
|
||
},
|
||
|
||
// 创建订单
|
||
createOrder() {
|
||
const { warehouses, warehouseIndex, endName, goodsType, goodsWeight } = this.data;
|
||
|
||
// 表单验证
|
||
if (!warehouses[warehouseIndex]) {
|
||
wx.showToast({
|
||
title: '请选择仓库',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
|
||
if (!endName) {
|
||
wx.showToast({
|
||
title: '请输入终点名称',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
|
||
if (!goodsType) {
|
||
wx.showToast({
|
||
title: '请输入货物类型',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
|
||
if (goodsWeight <= 0) {
|
||
wx.showToast({
|
||
title: '请输入有效的货物重量',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
|
||
// 获取仓库信息
|
||
const warehouse = warehouses[warehouseIndex];
|
||
|
||
// 模拟终点坐标(实际应用中应该通过地图选择或搜索获取)
|
||
// 从已有仓库中获取一个坐标作为参考点
|
||
const referenceWarehouse = this.data.warehouses[0];
|
||
const endPoint = {
|
||
name: endName,
|
||
longitude: referenceWarehouse.address ? 102.715 + (Math.random() - 0.5) * 0.1 : 102.715,
|
||
latitude: referenceWarehouse.address ? 25.045 + (Math.random() - 0.5) * 0.1 : 25.045
|
||
};
|
||
|
||
// 创建订单数据
|
||
const newOrder: Omit<Order, 'id' | 'createTime'> = {
|
||
startPoint: {
|
||
id: warehouse.id,
|
||
name: warehouse.name,
|
||
longitude: 102.715, // 临时使用固定坐标
|
||
latitude: 25.045 // 临时使用固定坐标
|
||
},
|
||
endPoint,
|
||
status: 'pending',
|
||
goodsType,
|
||
goodsWeight
|
||
};
|
||
|
||
// 调用服务创建订单 - 使用正确的orderService.createOrder方法
|
||
orderService.createOrder(newOrder).then(() => {
|
||
wx.showToast({
|
||
title: '创建成功',
|
||
icon: 'success'
|
||
});
|
||
|
||
// 刷新数据
|
||
this.refreshData();
|
||
}).catch(error => {
|
||
console.error('创建订单失败:', error);
|
||
wx.showToast({
|
||
title: '创建失败',
|
||
icon: 'error'
|
||
});
|
||
});
|
||
|
||
this.hideAddModal();
|
||
},
|
||
|
||
// 底部页签切换
|
||
switchMainTab(e: any) {
|
||
const tab = e.currentTarget.dataset.tab;
|
||
this.setData({
|
||
currentTab: tab
|
||
});
|
||
},
|
||
|
||
// 跳转到员工管理页面
|
||
goToEmployeeManagement() {
|
||
wx.navigateTo({
|
||
url: '/pages/employee/employee'
|
||
});
|
||
},
|
||
|
||
// 地图点击事件
|
||
onMapTap() {
|
||
// 清除路线
|
||
this.setData({
|
||
polyline: {}
|
||
});
|
||
},
|
||
|
||
// 格式化时间
|
||
formatTime(time: number): string {
|
||
const date = new Date(time);
|
||
const now = new Date();
|
||
const diff = now.getTime() - date.getTime();
|
||
|
||
if (diff < 60000) {
|
||
return '刚刚';
|
||
} else if (diff < 3600000) {
|
||
return `${Math.floor(diff / 60000)}分钟前`;
|
||
} else if (diff < 86400000) {
|
||
return `${Math.floor(diff / 3600000)}小时前`;
|
||
} else {
|
||
const month = date.getMonth() + 1;
|
||
const day = date.getDate();
|
||
const hour = date.getHours();
|
||
const minute = date.getMinutes();
|
||
return `${month}月${day}日 ${hour}:${minute < 10 ? '0' + minute : minute}`;
|
||
}
|
||
},
|
||
|
||
// 返回上一页
|
||
goBack() {
|
||
// 尝试使用navigateBack,如果失败则使用redirectTo返回首页
|
||
wx.navigateBack({
|
||
fail: () => {
|
||
wx.redirectTo({
|
||
url: '/pages/index/index'
|
||
});
|
||
}
|
||
});
|
||
}
|
||
}); |