first commit
This commit is contained in:
8
miniprogram/pages/admin/admin.json
Normal file
8
miniprogram/pages/admin/admin.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"navigationBarTitleText": "配送管理系统",
|
||||
"navigationBarBackgroundColor": "#1aad19",
|
||||
"navigationBarTextStyle": "white",
|
||||
"navigationStyle": "custom",
|
||||
"enablePullDownRefresh": false,
|
||||
"disableScroll": true
|
||||
}
|
||||
578
miniprogram/pages/admin/admin.ts
Normal file
578
miniprogram/pages/admin/admin.ts
Normal file
@@ -0,0 +1,578 @@
|
||||
// 管理员页面逻辑
|
||||
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',
|
||||
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 => {
|
||||
markers.push({
|
||||
id: 20000 + person.id, // 避免ID冲突
|
||||
longitude: person.currentLocation.longitude,
|
||||
latitude: person.currentLocation.latitude,
|
||||
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(_status: string): string {
|
||||
// 统一使用trucks.png作为货运人员图标
|
||||
return '/images/trucks.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: assignedPerson.currentLocation
|
||||
});
|
||||
|
||||
// 绘制从起点到终点的路线
|
||||
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
|
||||
});
|
||||
|
||||
// 根据不同的页签执行不同的逻辑
|
||||
switch (tab) {
|
||||
case 'main':
|
||||
// 管理中心,显示所有内容
|
||||
break;
|
||||
case 'orders':
|
||||
// 订单详情,可能需要调整布局
|
||||
break;
|
||||
case 'staff':
|
||||
// 员工管理,可能需要调整布局
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
// 地图点击事件
|
||||
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}`;
|
||||
}
|
||||
}
|
||||
});
|
||||
137
miniprogram/pages/admin/admin.wxml
Normal file
137
miniprogram/pages/admin/admin.wxml
Normal file
@@ -0,0 +1,137 @@
|
||||
<!-- 管理员主界面 -->
|
||||
<view class="admin-container">
|
||||
<!-- 顶部导航栏 -->
|
||||
<view class="top-nav">
|
||||
<text class="nav-title">配送管理系统</text>
|
||||
<view class="user-info" wx:if="{{userInfo}}">
|
||||
<image class="user-avatar" src="/images/user-avatar.png" mode="aspectFill"></image>
|
||||
<text class="user-name">{{userInfo.name || '管理员'}}</text>
|
||||
<text class="admin-badge" wx:if="{{userInfo.role === 'ADMIN'}}">管理员</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 主内容区域 -->
|
||||
<view class="main-content">
|
||||
<!-- 左侧地图区域 -->
|
||||
<view class="map-section">
|
||||
<map class="map" id="map" longitude="{{mapCenter.longitude}}" latitude="{{mapCenter.latitude}}" scale="14"
|
||||
markers="{{markers}}" polyline="{{polyline}}" show-location="true" bindtap="onMapTap">
|
||||
</map>
|
||||
</view>
|
||||
|
||||
<!-- 中间订单管理区域 -->
|
||||
<view class="order-section">
|
||||
<view class="section-header">
|
||||
<text class="section-title">订单管理</text>
|
||||
<button class="add-btn" bindtap="showAddOrderModal">新增订单</button>
|
||||
</view>
|
||||
|
||||
<view class="order-tabs">
|
||||
<text class="tab {{activeTab === 'all' ? 'active' : ''}}" bindtap="switchTab" data-tab="all">全部</text>
|
||||
<text class="tab {{activeTab === 'pending' ? 'active' : ''}}" bindtap="switchTab" data-tab="pending">未分配</text>
|
||||
<text class="tab {{activeTab === 'assigned' ? 'active' : ''}}" bindtap="switchTab" data-tab="assigned">已分配</text>
|
||||
<text class="tab {{activeTab === 'delivered' ? 'active' : ''}}" bindtap="switchTab" data-tab="delivered">已完成</text>
|
||||
</view>
|
||||
|
||||
<scroll-view class="order-list" scroll-y>
|
||||
<view wx:for="{{filteredOrders}}" wx:key="id" class="order-item" bindtap="handleOrderTap" data-id="{{item.id}}">
|
||||
<view class="order-header">
|
||||
<text class="order-id">订单#{{item.id}}</text>
|
||||
<text class="order-status">{{getStatusText(item.status)}}</text>
|
||||
</view>
|
||||
<view class="order-info">
|
||||
<text class="order-location">{{item.startPoint.name}} → {{item.endPoint.name}}</text>
|
||||
<text class="order-goods">{{item.goodsType}} {{item.goodsWeight}}kg</text>
|
||||
<text class="order-time">{{formatTime(item.createTime)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 右侧员工管理区域 -->
|
||||
<view class="staff-section">
|
||||
<view class="section-header">
|
||||
<text class="section-title">员工管理</text>
|
||||
</view>
|
||||
|
||||
<scroll-view class="staff-list" scroll-y>
|
||||
<view wx:for="{{deliveryPersons}}" wx:key="id" class="staff-item" bindtap="handleStaffTap" data-id="{{item.id}}">
|
||||
<image class="staff-avatar" src="{{item.avatarUrl}}" mode="aspectFill"></image>
|
||||
<view class="staff-info">
|
||||
<text class="staff-name">{{item.name}}</text>
|
||||
<text class="staff-phone">{{item.phone}}</text>
|
||||
<text class="staff-status">{{getStatusText(item.status)}}</text>
|
||||
<text class="order-count">当前订单: {{item.currentOrders.length}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部页签 -->
|
||||
<view class="bottom-tabs">
|
||||
<view class="tab-item {{currentTab === 'main' ? 'active' : ''}}" bindtap="switchMainTab" data-tab="main">
|
||||
<text class="tab-icon">📍</text>
|
||||
<text class="tab-text">管理中心</text>
|
||||
</view>
|
||||
<view class="tab-item {{currentTab === 'orders' ? 'active' : ''}}" bindtap="switchMainTab" data-tab="orders">
|
||||
<text class="tab-icon">📋</text>
|
||||
<text class="tab-text">订单详情</text>
|
||||
</view>
|
||||
<view class="tab-item {{currentTab === 'staff' ? 'active' : ''}}" bindtap="switchMainTab" data-tab="staff">
|
||||
<text class="tab-icon">👥</text>
|
||||
<text class="tab-text">员工管理</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 分配订单弹窗 -->
|
||||
<view class="modal-mask" wx:if="{{showAssignModal}}" bindtap="hideAssignModal"></view>
|
||||
<view class="modal-content" wx:if="{{showAssignModal}}">
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">分配订单</text>
|
||||
<text class="modal-close" bindtap="hideAssignModal">✕</text>
|
||||
</view>
|
||||
<view class="modal-body">
|
||||
<text class="modal-text">请选择配送人员:</text>
|
||||
<view class="staff-select-list">
|
||||
<view wx:for="{{availableStaff}}" wx:key="id" class="staff-select-item" bindtap="assignOrder" data-id="{{item.id}}">
|
||||
<image class="staff-select-avatar" src="{{item.avatarUrl}}" mode="aspectFill"></image>
|
||||
<text class="staff-select-name">{{item.name}}</text>
|
||||
<text class="staff-select-status">{{getStatusText(item.status)}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 新增订单弹窗 -->
|
||||
<view class="modal-mask" wx:if="{{showAddModal}}" bindtap="hideAddModal"></view>
|
||||
<view class="modal-content large" wx:if="{{showAddModal}}">
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">新增订单</text>
|
||||
<text class="modal-close" bindtap="hideAddModal">✕</text>
|
||||
</view>
|
||||
<view class="modal-body">
|
||||
<view class="form-item">
|
||||
<text class="form-label">选择仓库:</text>
|
||||
<picker bindchange="onWarehouseChange" value="{{warehouseIndex}}" range="{{warehouses}}" range-key="name">
|
||||
<view class="form-picker">
|
||||
{{warehouses[warehouseIndex] ? warehouses[warehouseIndex].name : '请选择仓库'}}
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">终点名称:</text>
|
||||
<input class="form-input" bindinput="onEndNameInput" placeholder="请输入终点名称"/>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">货物类型:</text>
|
||||
<input class="form-input" bindinput="onGoodsTypeInput" placeholder="请输入货物类型"/>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">货物重量(kg):</text>
|
||||
<input class="form-input" bindinput="onGoodsWeightInput" type="number" placeholder="请输入货物重量"/>
|
||||
</view>
|
||||
<button class="submit-btn" bindtap="createOrder">创建订单</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
446
miniprogram/pages/admin/admin.wxss
Normal file
446
miniprogram/pages/admin/admin.wxss
Normal file
@@ -0,0 +1,446 @@
|
||||
/* 管理员主页面样式 */
|
||||
.admin-container {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #f5f5f5;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 顶部导航栏 */
|
||||
.top-nav {
|
||||
height: 90rpx;
|
||||
background-color: #1aad19;
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 30rpx;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15rpx;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.admin-badge {
|
||||
background-color: #ff4d4f;
|
||||
color: white;
|
||||
font-size: 20rpx;
|
||||
padding: 5rpx 10rpx;
|
||||
border-radius: 15rpx;
|
||||
}
|
||||
|
||||
/* 主内容区域 */
|
||||
.main-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 左侧地图区域 */
|
||||
.map-section {
|
||||
width: 33.33%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.map {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 中间订单管理区域 */
|
||||
.order-section {
|
||||
width: 33.33%;
|
||||
height: 100%;
|
||||
background-color: white;
|
||||
border-left: 1rpx solid #e0e0e0;
|
||||
border-right: 1rpx solid #e0e0e0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 右侧员工管理区域 */
|
||||
.staff-section {
|
||||
width: 33.33%;
|
||||
height: 100%;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
/* 区域标题 */
|
||||
.section-header {
|
||||
height: 80rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 20rpx;
|
||||
border-bottom: 1rpx solid #e0e0e0;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.add-btn {
|
||||
background-color: #1aad19;
|
||||
color: white;
|
||||
font-size: 28rpx;
|
||||
padding: 10rpx 20rpx;
|
||||
border-radius: 40rpx;
|
||||
border: none;
|
||||
min-width: 160rpx;
|
||||
}
|
||||
|
||||
/* 订单标签页 */
|
||||
.order-tabs {
|
||||
display: flex;
|
||||
border-bottom: 1rpx solid #e0e0e0;
|
||||
}
|
||||
|
||||
.tab {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 20rpx 0;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.tab.active {
|
||||
color: #1aad19;
|
||||
font-weight: bold;
|
||||
border-bottom: 4rpx solid #1aad19;
|
||||
}
|
||||
|
||||
/* 订单列表 */
|
||||
.order-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.order-item {
|
||||
padding: 20rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.order-item:active {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.order-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.order-id {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.order-status {
|
||||
font-size: 24rpx;
|
||||
padding: 5rpx 15rpx;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
/* 订单状态颜色 */
|
||||
.order-status.pending {
|
||||
background-color: #fff7e6;
|
||||
color: #fa8c16;
|
||||
}
|
||||
|
||||
.order-status.assigned {
|
||||
background-color: #e6f7ff;
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.order-status.in_transit {
|
||||
background-color: #f6ffed;
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.order-status.delivered {
|
||||
background-color: #f0f0f0;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.order-info {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* 员工列表 */
|
||||
.staff-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.staff-item {
|
||||
display: flex;
|
||||
padding: 20rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.staff-item:active {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.staff-avatar {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.staff-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.staff-name {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
display: block;
|
||||
margin-bottom: 5rpx;
|
||||
}
|
||||
|
||||
.staff-phone {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
display: block;
|
||||
margin-bottom: 5rpx;
|
||||
}
|
||||
|
||||
.staff-status {
|
||||
font-size: 24rpx;
|
||||
padding: 5rpx 15rpx;
|
||||
border-radius: 20rpx;
|
||||
display: inline-block;
|
||||
margin-bottom: 5rpx;
|
||||
}
|
||||
|
||||
.staff-status.idle {
|
||||
background-color: #f6ffed;
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.staff-status.busy {
|
||||
background-color: #fff1f0;
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
.order-count {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* 底部页签 */
|
||||
.bottom-tabs {
|
||||
height: 100rpx;
|
||||
background-color: white;
|
||||
border-top: 1rpx solid #e0e0e0;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.tab-item.active {
|
||||
color: #1aad19;
|
||||
}
|
||||
|
||||
.tab-icon {
|
||||
font-size: 40rpx;
|
||||
margin-bottom: 5rpx;
|
||||
}
|
||||
|
||||
.tab-text {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
/* 弹窗样式 */
|
||||
.modal-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 80%;
|
||||
max-width: 600rpx;
|
||||
background-color: white;
|
||||
border-radius: 20rpx;
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
.modal-content.large {
|
||||
width: 90%;
|
||||
max-width: 800rpx;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 30rpx;
|
||||
border-bottom: 1rpx solid #e0e0e0;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
font-size: 40rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 30rpx;
|
||||
}
|
||||
|
||||
.modal-text {
|
||||
font-size: 30rpx;
|
||||
color: #333;
|
||||
margin-bottom: 20rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 员工选择列表 */
|
||||
.staff-select-list {
|
||||
max-height: 400rpx;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.staff-select-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.staff-select-avatar {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.staff-select-name {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.staff-select-status {
|
||||
font-size: 24rpx;
|
||||
padding: 5rpx 15rpx;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
/* 表单样式 */
|
||||
.form-item {
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.form-picker {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 10rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 20rpx;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 10rpx;
|
||||
padding: 0 20rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
background-color: #1aad19;
|
||||
color: white;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
border-radius: 10rpx;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768rpx) {
|
||||
.main-content {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.map-section,
|
||||
.order-section,
|
||||
.staff-section {
|
||||
width: 100%;
|
||||
height: 33.33%;
|
||||
}
|
||||
|
||||
.order-section {
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
border-top: 1rpx solid #e0e0e0;
|
||||
border-bottom: 1rpx solid #e0e0e0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user