修改位置交互,修改代码逻辑

This commit is contained in:
2025-10-18 22:21:04 +08:00
parent c446df73b5
commit 39fa0b2d04
36 changed files with 2743 additions and 1995 deletions

View File

@@ -42,7 +42,7 @@ Page({
userInfo: {
id: 1,
name: '管理员',
role: 'ADMIN',
role: 'ADMIN' as any,
phone: '13800138000'
}
});
@@ -150,10 +150,11 @@ Page({
// 添加员工位置标记
deliveryPersons.forEach(person => {
// 使用默认坐标实际位置由locationTrackingService统一管理
markers.push({
id: 20000 + person.id, // 避免ID冲突
longitude: person.currentLocation.longitude,
latitude: person.currentLocation.latitude,
longitude: 102.833722, // 默认经度
latitude: 24.880095, // 默认纬度
title: person.name,
iconPath: this.getStaffStatusIcon(person.status),
width: 32,
@@ -259,7 +260,7 @@ Page({
if (assignedPerson) {
this.setData({
mapCenter: assignedPerson.currentLocation
mapCenter: { longitude: 102.833722, latitude: 24.880095 } // 默认坐标
});
// 绘制从起点到终点的路线
@@ -526,25 +527,19 @@ Page({
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;
}
},
// 跳转到员工管理页面
goToEmployeeManagement() {
wx.navigateTo({
url: '/pages/employee/employee'
});
},
// 地图点击事件

View File

@@ -52,6 +52,7 @@
<view class="staff-section">
<view class="section-header">
<text class="section-title">员工管理</text>
<button class="manage-btn" bindtap="goToEmployeeManagement">管理员工</button>
</view>
<scroll-view class="staff-list" scroll-y>

View File

@@ -112,6 +112,20 @@
min-width: 160rpx;
}
.manage-btn {
background-color: #1890ff;
color: white;
font-size: 28rpx;
padding: 10rpx 20rpx;
border-radius: 40rpx;
border: none;
min-width: 160rpx;
}
.manage-btn:active {
background-color: #096dd9;
}
/* 订单标签页 */
.order-tabs {
display: flex;

View File

@@ -8,7 +8,6 @@ Page({
data: {
applyForm: {
name: '',
idCard: '',
phone: ''
}
},
@@ -44,7 +43,7 @@ Page({
* 提交申请
*/
async onSubmit() {
const { name, idCard, phone } = this.data.applyForm;
const { name, phone } = this.data.applyForm;
// 表单验证
if (!this.validateForm()) {
@@ -58,7 +57,7 @@ Page({
});
// 调用API提交申请
const result = await this.submitApplication({ name, idCard, phone });
const result = await this.submitApplication({ name, phone });
if (result.success) {
wx.showToast({
@@ -115,9 +114,9 @@ Page({
* 表单验证
*/
validateForm(): boolean {
const { name, idCard, phone } = this.data.applyForm;
const { name, phone } = this.data.applyForm;
if (!name || !idCard || !phone) {
if (!name || !phone) {
wx.showToast({
title: '请填写完整信息',
icon: 'none',
@@ -126,16 +125,6 @@ Page({
return false;
}
// 身份证号验证
if (idCard.length !== 18) {
wx.showToast({
title: '请输入正确的身份证号',
icon: 'none',
duration: 2000
});
return false;
}
// 手机号验证
if (!/^1[3-9]\d{9}$/.test(phone)) {
wx.showToast({

View File

@@ -25,21 +25,7 @@
<view class="apply-form-underline"></view>
</view>
<!-- 身份证号输入 -->
<view class="apply-form-group">
<text class="apply-form-label">身份证号</text>
<input
class="apply-form-input"
type="idcard"
value="{{applyForm.idCard}}"
data-field="idCard"
bindinput="onApplyFormInput"
placeholder="请输入18位身份证号码"
placeholder-class="apply-form-placeholder"
maxlength="18"
/>
<view class="apply-form-underline"></view>
</view>
<!-- 手机号输入 -->
<view class="apply-form-group">

View File

@@ -0,0 +1,7 @@
{
"usingComponents": {},
"navigationBarTitleText": "员工管理",
"navigationStyle": "custom",
"enablePullDownRefresh": true,
"backgroundColor": "#f5f5f5"
}

View File

@@ -0,0 +1,339 @@
// 员工管理页面逻辑
import { EmployeeInfo } from '../../types';
import employeeService from '../../services/employeeService';
import { Role, getRoleOptions } from '../../utils/roleUtils';
Page({
data: {
// 员工列表
employees: [] as EmployeeInfo[],
filteredEmployees: [] as EmployeeInfo[],
// 页面状态
currentTab: 'list', // list: 列表页, add: 添加页
loading: false,
// 添加员工表单数据
addForm: {
name: '',
phone: '',
role: Role.DELIVERY_PERSON
},
// 错误信息
errorMessage: '',
successMessage: '',
// 搜索关键词
searchKeyword: '',
// 角色选项
roleOptions: getRoleOptions()
},
onLoad() {
// 页面加载时获取员工列表
this.loadEmployees();
},
onShow() {
// 页面显示时刷新数据
this.loadEmployees();
},
/**
* 加载员工列表
*/
async loadEmployees() {
this.setData({
loading: true,
errorMessage: '',
successMessage: ''
});
try {
const employees = await employeeService.getEmployees();
// 获取过滤后的员工列表
const filteredEmployees = this.getFilteredEmployees(employees);
this.setData({
employees,
filteredEmployees,
loading: false
});
} catch (error) {
console.error('加载员工列表失败:', error);
this.setData({
loading: false,
errorMessage: '加载员工列表失败,请稍后重试'
});
}
},
/**
* 切换页面标签
*/
switchTab(e: any) {
const tab = e.currentTarget.dataset.tab;
this.setData({
currentTab: tab,
errorMessage: '',
successMessage: ''
});
if (tab === 'list') {
this.loadEmployees();
}
},
/**
* 处理添加员工表单输入
*/
onFormInput(e: any) {
const { field } = e.currentTarget.dataset;
const value = e.detail.value;
this.setData({
[`addForm.${field}`]: value,
errorMessage: '',
successMessage: ''
});
},
/**
* 处理角色选择
*/
onRoleChange(e: any) {
const index = e.detail.value;
const selectedRole = this.data.roleOptions[index].value;
this.setData({
'addForm.role': selectedRole
});
},
/**
* 验证表单数据
*/
validateForm(): boolean {
const { name, phone, role } = this.data.addForm;
if (!name.trim()) {
this.setData({
errorMessage: '请输入员工姓名'
});
return false;
}
if (!phone.trim()) {
this.setData({
errorMessage: '请输入手机号'
});
return false;
}
// 简单的手机号格式验证
const phoneRegex = /^1[3-9]\d{9}$/;
if (!phoneRegex.test(phone)) {
this.setData({
errorMessage: '请输入正确的手机号格式'
});
return false;
}
if (!role) {
this.setData({
errorMessage: '请选择员工角色'
});
return false;
}
return true;
},
/**
* 提交添加员工表单
*/
async submitAddForm() {
if (!this.validateForm()) {
return;
}
this.setData({
loading: true,
errorMessage: '',
successMessage: ''
});
try {
await employeeService.addEmployee(this.data.addForm);
this.setData({
loading: false,
successMessage: '员工添加成功',
addForm: {
name: '',
phone: '',
role: Role.DELIVERY_PERSON
}
});
// 添加成功后自动切换到列表页
setTimeout(() => {
this.setData({
currentTab: 'list'
});
this.loadEmployees();
}, 1500);
} catch (error) {
console.error('添加员工失败:', error);
this.setData({
loading: false,
errorMessage: (error as Error).message || '添加员工失败,请稍后重试'
});
}
},
/**
* 删除员工
*/
async deleteEmployee(e: any) {
const employeeId = e.currentTarget.dataset.id;
const employee = this.data.employees.find(emp => emp.id === employeeId);
if (!employee) {
return;
}
wx.showModal({
title: '确认删除',
content: `确定要删除员工 ${employee.name} (${employee.phone}) 吗?此操作不可恢复。`,
confirmText: '删除',
confirmColor: '#ff4d4f',
cancelText: '取消',
success: async (res) => {
if (res.confirm) {
this.setData({
loading: true,
errorMessage: '',
successMessage: ''
});
try {
const result = await employeeService.deleteEmployee(employeeId);
if (result.success) {
this.setData({
loading: false,
successMessage: result.message || '员工删除成功'
});
// 重新加载员工列表
this.loadEmployees();
} else {
this.setData({
loading: false,
errorMessage: result.message || '删除员工失败'
});
}
} catch (error) {
console.error('删除员工失败:', error);
this.setData({
loading: false,
errorMessage: '删除员工失败,请稍后重试'
});
}
}
}
});
},
/**
* 搜索员工
*/
onSearchInput(e: any) {
const keyword = e.detail.value;
this.setData({
searchKeyword: keyword
});
// 更新过滤后的员工列表
this.updateFilteredEmployees();
},
/**
* 更新过滤后的员工列表
*/
updateFilteredEmployees() {
const { employees } = this.data;
const filteredEmployees = this.getFilteredEmployees(employees);
this.setData({
filteredEmployees
});
},
/**
* 获取过滤后的员工列表
*/
getFilteredEmployees(employees?: EmployeeInfo[]): EmployeeInfo[] {
const { searchKeyword } = this.data;
// 如果employees为空返回空数组
if (!employees || !Array.isArray(employees)) {
return [];
}
// 获取当前登录用户信息
const app = getApp();
const currentUser = app.globalData.userInfo;
// 过滤掉当前登录用户
let filteredEmployees = employees.filter(emp => {
// 如果没有当前用户信息,显示所有员工
if (!currentUser || !currentUser.id) {
return true;
}
// 过滤掉当前用户
return emp.id !== currentUser.id;
});
// 更严格的搜索关键词检查
if (!searchKeyword || typeof searchKeyword !== 'string' || !searchKeyword.trim()) {
return filteredEmployees;
}
const keyword = searchKeyword.toLowerCase();
return filteredEmployees.filter(emp =>
emp.name.toLowerCase().includes(keyword) ||
emp.phone.includes(keyword) ||
(emp.role || '').toLowerCase().includes(keyword)
);
},
/**
* 获取角色显示文本
*/
getRoleText(role: string): string {
const roleMap: Record<string, string> = {
'DELIVERY_PERSON': '配送员',
'ADMIN': '管理员'
};
return roleMap[role] || role;
},
/**
* 清空消息
*/
clearMessages() {
this.setData({
errorMessage: '',
successMessage: ''
});
},
/**
* 返回上一页
*/
goBack() {
wx.navigateBack();
}
});

View File

@@ -0,0 +1,166 @@
<!-- 员工管理页面 -->
<view class="employee-container">
<!-- 顶部导航栏 -->
<view class="top-nav">
<view class="nav-left">
<text class="back-btn" bindtap="goBack">←</text>
<text class="nav-title">员工管理</text>
</view>
<view class="nav-right">
<text class="nav-count">共{{filteredEmployees.length}}名员工</text>
</view>
</view>
<!-- 页面标签 -->
<view class="page-tabs">
<view
class="tab-item {{currentTab === 'list' ? 'active' : ''}}"
bindtap="switchTab"
data-tab="list"
>
<text class="tab-text">员工列表</text>
</view>
<view
class="tab-item {{currentTab === 'add' ? 'active' : ''}}"
bindtap="switchTab"
data-tab="add"
>
<text class="tab-text">添加员工</text>
</view>
</view>
<!-- 消息提示 -->
<view class="message-container">
<view wx:if="{{errorMessage}}" class="error-message">
<text class="message-text">{{errorMessage}}</text>
<text class="close-btn" bindtap="clearMessages">×</text>
</view>
<view wx:if="{{successMessage}}" class="success-message">
<text class="message-text">{{successMessage}}</text>
<text class="close-btn" bindtap="clearMessages">×</text>
</view>
</view>
<!-- 加载状态 -->
<view wx:if="{{loading}}" class="loading-container">
<view class="loading-spinner"></view>
<text class="loading-text">加载中...</text>
</view>
<!-- 员工列表页面 -->
<view wx:if="{{currentTab === 'list'}}" class="list-container">
<!-- 搜索框 -->
<view class="search-container">
<view class="search-input-wrapper">
<input
class="search-input"
placeholder="搜索员工姓名、手机号或角色"
bindinput="onSearchInput"
value="{{searchKeyword}}"
/>
<text class="search-icon">🔎</text>
</view>
</view>
<!-- 员工列表 -->
<scroll-view class="employee-list" scroll-y>
<view wx:for="{{filteredEmployees}}" wx:key="id" class="employee-item">
<view class="employee-info">
<view class="employee-avatar">
<text class="avatar-text">{{item.name.charAt(0)}}</text>
</view>
<view class="employee-details">
<view class="employee-name-row">
<text class="employee-name">{{item.name}}</text>
<text class="employee-role {{item.role === 'ADMIN' ? 'admin-role' : ''}}">
<text class="role-icon">{{item.role === 'ADMIN' ? '👑' : '🚚'}}</text>
{{getRoleText(item.role)}}
</text>
</view>
<text class="employee-phone">{{item.phone}}</text>
<text class="employee-id">ID: {{item.id}}</text>
</view>
</view>
<view class="employee-actions">
<button
class="delete-btn"
bindtap="deleteEmployee"
data-id="{{item.id}}"
size="mini"
>
删除
</button>
</view>
</view>
<!-- 空状态 -->
<view wx:if="{{filteredEmployees.length === 0}}" class="empty-state">
<text class="empty-icon">👥</text>
<text class="empty-text">
{{searchKeyword ? '没有找到匹配的员工' : '暂无员工数据'}}
</text>
<text wx:if="{{!searchKeyword}}" class="empty-hint">点击右上角"添加员工"开始管理</text>
</view>
</scroll-view>
</view>
<!-- 添加员工页面 -->
<view wx:if="{{currentTab === 'add'}}" class="add-container">
<view class="form-container">
<view class="form-item">
<text class="form-label">员工姓名</text>
<input
class="form-input"
placeholder="请输入员工姓名"
value="{{addForm.name}}"
bindinput="onFormInput"
data-field="name"
/>
</view>
<view class="form-item">
<text class="form-label">手机号码</text>
<input
class="form-input"
placeholder="请输入手机号码"
type="number"
value="{{addForm.phone}}"
bindinput="onFormInput"
data-field="phone"
/>
</view>
<view class="form-item">
<text class="form-label">员工角色</text>
<picker
class="form-picker"
bindchange="onRoleChange"
value="{{addForm.role}}"
range="{{roleOptions}}"
range-key="label"
>
<view class="picker-display">
<text class="picker-text">
{{addForm.role === 'ADMIN' ? '管理员' : addForm.role === 'DELIVERY_PERSON' ? '配送员' : '请选择角色'}}
</text>
<text class="picker-arrow">▼</text>
</view>
</picker>
</view>
<view class="form-hint">
<text class="hint-text">
提示:添加员工后,用户可以使用该员工的姓名和手机号进行注册
</text>
</view>
<button
class="submit-btn"
bindtap="submitAddForm"
disabled="{{loading}}"
>
{{loading ? '添加中...' : '添加员工'}}
</button>
</view>
</view>
</view>

View File

@@ -0,0 +1,485 @@
/* 员工管理页面样式 */
.employee-container {
width: 100%;
height: 100vh;
background-color: #f5f5f5;
display: flex;
flex-direction: column;
}
/* 顶部导航栏 */
.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-left {
display: flex;
align-items: center;
gap: 20rpx;
}
.back-btn {
font-size: 40rpx;
font-weight: bold;
cursor: pointer;
}
.nav-title {
font-size: 36rpx;
font-weight: bold;
}
.nav-right {
font-size: 28rpx;
opacity: 0.9;
}
/* 页面标签 */
.page-tabs {
height: 80rpx;
background-color: white;
display: flex;
border-bottom: 1rpx solid #e0e0e0;
}
.tab-item {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
font-size: 30rpx;
color: #666;
border-bottom: 4rpx solid transparent;
transition: all 0.3s;
}
.tab-item.active {
color: #1aad19;
border-bottom-color: #1aad19;
font-weight: bold;
}
.tab-text {
padding: 10rpx 0;
}
/* 消息提示 */
.message-container {
padding: 20rpx 30rpx;
}
.error-message, .success-message {
padding: 20rpx 30rpx;
border-radius: 10rpx;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 28rpx;
}
.error-message {
background-color: #fff2f0;
color: #ff4d4f;
border: 1rpx solid #ffccc7;
}
.success-message {
background-color: #f6ffed;
color: #52c41a;
border: 1rpx solid #b7eb8f;
}
.message-text {
flex: 1;
}
.close-btn {
font-size: 36rpx;
cursor: pointer;
padding-left: 20rpx;
}
/* 加载状态 */
.loading-container {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
}
.loading-spinner {
width: 60rpx;
height: 60rpx;
border: 4rpx solid #f3f3f3;
border-top: 4rpx solid #1aad19;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 20rpx;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading-text {
font-size: 28rpx;
color: #666;
}
/* 列表容器 */
.list-container {
flex: 1;
display: flex;
flex-direction: column;
padding-bottom: 60rpx; /* 添加底部边距,避免内容被底部遮挡 */
}
/* 搜索框 */
.search-container {
padding: 30rpx;
background: linear-gradient(135deg, #ffffff, #fafafa);
border-bottom: 1rpx solid rgba(0, 0, 0, 0.05);
box-shadow: 0 2rpx 15rpx rgba(0, 0, 0, 0.03);
}
.search-input-wrapper {
position: relative;
display: flex;
align-items: center;
}
.search-input {
flex: 1;
height: 80rpx;
background: linear-gradient(135deg, #f8f9fa, #f1f3f4);
border-radius: 40rpx;
padding: 0 80rpx 0 35rpx;
font-size: 30rpx;
border: 2rpx solid transparent;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
}
.search-input:focus {
border-color: #1aad19;
background: linear-gradient(135deg, #ffffff, #f8f9fa);
box-shadow: 0 6rpx 25rpx rgba(26, 173, 25, 0.15);
}
.search-icon {
position: absolute;
right: 35rpx;
font-size: 36rpx;
color: #1aad19;
filter: drop-shadow(0 2rpx 4rpx rgba(26, 173, 25, 0.3));
}
/* 员工列表 */
.employee-list {
flex: 1;
padding: 30rpx;
box-sizing: border-box;
}
.employee-item {
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
border-radius: 20rpx;
padding: 35rpx;
margin: 25rpx 0;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 8rpx 30rpx rgba(0, 0, 0, 0.08);
border: 1rpx solid rgba(255, 255, 255, 0.8);
transition: all 0.3s ease;
position: relative;
overflow: hidden;
box-sizing: border-box;
width: 100%;
}
.employee-item::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4rpx;
background: linear-gradient(90deg, #1aad19, #52c41a);
}
.employee-item:active {
transform: translateY(2rpx);
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.12);
}
.employee-info {
display: flex;
align-items: center;
flex: 1;
min-width: 0;
overflow: hidden;
}
.employee-avatar {
width: 90rpx;
height: 90rpx;
border-radius: 50%;
background: linear-gradient(135deg, #1aad19, #52c41a);
display: flex;
align-items: center;
justify-content: center;
margin-right: 25rpx;
box-shadow: 0 4rpx 15rpx rgba(26, 173, 25, 0.3);
border: 3rpx solid rgba(255, 255, 255, 0.9);
}
.avatar-text {
color: white;
font-size: 36rpx;
font-weight: bold;
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.2);
}
.employee-details {
flex: 1;
min-width: 0;
overflow: hidden;
}
.employee-name-row {
display: flex;
align-items: center;
margin-bottom: 12rpx;
gap: 15rpx;
}
.employee-name {
font-size: 34rpx;
font-weight: 700;
color: #1a1a1a;
letter-spacing: 0.5rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 200rpx;
}
.employee-role {
font-size: 24rpx;
padding: 6rpx 16rpx;
border-radius: 20rpx;
background: linear-gradient(135deg, #f0f2f5, #e8ecef);
color: #595959;
display: flex;
align-items: center;
gap: 8rpx;
border: 1rpx solid rgba(0, 0, 0, 0.05);
}
.role-icon {
font-size: 28rpx;
display: flex;
align-items: center;
justify-content: center;
}
.employee-role.admin-role {
background: linear-gradient(135deg, #fff7e6, #ffe7ba);
color: #d46b08;
border: 1rpx solid rgba(250, 140, 22, 0.2);
}
.employee-phone {
font-size: 28rpx;
color: #666;
margin-bottom: 8rpx;
display: flex;
align-items: center;
gap: 8rpx;
}
.employee-phone::before {
content: '📱';
font-size: 24rpx;
}
.employee-id {
font-size: 24rpx;
color: #999;
display: flex;
align-items: center;
gap: 8rpx;
}
.employee-id::before {
content: '🆔';
font-size: 20rpx;
}
.employee-actions {
margin-left: 25rpx;
}
.delete-btn {
background: linear-gradient(135deg, #ff4d4f, #ff7875);
color: white;
border: none;
border-radius: 25rpx;
padding: 12rpx 24rpx;
font-size: 26rpx;
font-weight: 600;
box-shadow: 0 4rpx 12rpx rgba(255, 77, 79, 0.3);
transition: all 0.3s ease;
border: 1rpx solid rgba(255, 255, 255, 0.2);
}
.delete-btn:active {
background: linear-gradient(135deg, #d9363e, #ff7875);
transform: translateY(1rpx);
box-shadow: 0 2rpx 8rpx rgba(255, 77, 79, 0.2);
}
/* 空状态 */
.empty-state {
text-align: center;
padding: 120rpx 30rpx;
color: #999;
background: linear-gradient(135deg, #fafafa, #f5f5f5);
border-radius: 25rpx;
margin: 30rpx;
box-shadow: 0 8rpx 30rpx rgba(0, 0, 0, 0.05);
border: 1rpx solid rgba(255, 255, 255, 0.8);
}
.empty-icon {
font-size: 100rpx;
display: block;
margin-bottom: 25rpx;
filter: drop-shadow(0 4rpx 8rpx rgba(0, 0, 0, 0.1));
}
.empty-text {
font-size: 36rpx;
display: block;
margin-bottom: 15rpx;
font-weight: 600;
color: #666;
}
.empty-hint {
font-size: 30rpx;
opacity: 0.8;
line-height: 1.6;
}
/* 添加容器 */
.add-container {
flex: 1;
padding: 30rpx;
}
.form-container {
background-color: white;
border-radius: 15rpx;
padding: 40rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
}
.form-item {
margin-bottom: 40rpx;
}
.form-label {
display: block;
font-size: 30rpx;
color: #333;
margin-bottom: 15rpx;
font-weight: bold;
}
.form-input {
width: 100%;
height: 80rpx;
border: 1rpx solid #e0e0e0;
border-radius: 10rpx;
padding: 0 20rpx;
font-size: 28rpx;
box-sizing: border-box;
}
.form-input:focus {
border-color: #1aad19;
}
.form-picker {
width: 100%;
height: 80rpx;
border: 1rpx solid #e0e0e0;
border-radius: 10rpx;
padding: 0 20rpx;
font-size: 28rpx;
box-sizing: border-box;
display: flex;
align-items: center;
}
.picker-display {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
}
.picker-text {
color: #333;
}
.picker-arrow {
color: #999;
font-size: 24rpx;
}
.form-hint {
background-color: #f6ffed;
border: 1rpx solid #b7eb8f;
border-radius: 10rpx;
padding: 20rpx;
margin-bottom: 40rpx;
}
.hint-text {
font-size: 26rpx;
color: #52c41a;
line-height: 1.5;
}
.submit-btn {
width: 100%;
height: 90rpx;
background-color: #1aad19;
color: white;
border: none;
border-radius: 45rpx;
font-size: 32rpx;
font-weight: bold;
}
.submit-btn:active {
background-color: #179b16;
}
.submit-btn:disabled {
background-color: #ccc;
color: #999;
}

View File

@@ -149,25 +149,68 @@ page {
/* 控制按钮样式统一 */
.control-btn.signin-btn,
.control-btn.register-btn {
width: 100rpx;
height: 100rpx;
.control-btn.register-btn,
.control-btn.sign-out-btn,
.control-btn.auth-btn,
.control-btn.location-btn,
.control-btn.reset-btn,
.control-btn.staff-management-btn {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.15);
box-shadow: 0 6rpx 30rpx rgba(0, 0, 0, 0.2);
font-size: 24rpx;
padding: 0;
margin: 0;
border: none;
color: #333333;
text-align: center;
line-height: 1;
background: rgba(255, 255, 255, 0.9);
border: 1rpx solid rgba(0, 0, 0, 0.1);
}
/* 按钮图标样式 */
.btn-icon {
font-size: 40rpx;
margin-bottom: 8rpx;
display: block;
line-height: 1;
}
/* 按钮文字样式 */
.btn-text {
font-size: 22rpx;
font-weight: 500;
display: block;
line-height: 1.2;
}
.control-btn.signin-btn:active,
.control-btn.register-btn:active {
.control-btn.register-btn:active,
.control-btn.sign-out-btn:active {
transform: scale(0.95);
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
}
/* 所有按钮点击状态 */
.control-btn.signin-btn:active,
.control-btn.register-btn:active,
.control-btn.auth-btn:active,
.control-btn.sign-out-btn:active,
.control-btn.location-btn:active,
.control-btn.reset-btn:active,
.control-btn.staff-management-btn:active {
background: rgba(240, 240, 240, 0.9);
transform: scale(0.95);
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
border: 1rpx solid rgba(0, 0, 0, 0.2);
}
.btn-signin {
background-color: #1aad19;
color: white;

View File

@@ -1,61 +1,64 @@
// index.ts
// 引入服务和工具函数
import { UserInfo, Marker } from '../../types';
import { showToast } from '../../utils/helpers';
import userService from '../../services/userService';
import locationTrackingService from '../../services/locationTrackingService';
// 引入模块
// 引入主页面模块
import { MainPageModule } from './modules/mainPageModule';
// 主页面组件
Component({
// 主页面组件接口定义
interface IndexPageComponent {
data: {
// 地图中心点坐标
longitude: 102.833722,
latitude: 24.880095,
scale: 13, // 地图缩放级别
markers: [] as Marker[], // 地图标记点数组
userInfo: null as UserInfo | null, // 用户信息
// 用户认证状态(分离外部登录状态和用户二级状态)
authStatus: {
hasWxCode: false, // 是否已获取微信code外部登录状态
userStatus: 'unknown' as 'unknown' | 'registered' | 'unregistered' | 'signed_in' | 'signed_out', // 用户二级状态
},
showUserPanel: false, // 是否显示用户信息面板
showOrderPanel: false, // 是否显示订单详情面板
currentOrder: null as any, // 当前选中的订单
currentDeliveryPerson: null as any, // 当前选中的货运人员
currentWarehouse: null as any, // 当前选中的仓库
currentPanelPosition: { x: 0, y: 0 }, // 当前信息面板位置
polyline: null as any, // 路线规划结果
pendingOrders: [] as any[] ,// 待分配订单
currentRoute: null as any, // 当前路线信息
showRoute: false, // 是否显示路线
routeDistance: 0, // 路线距离
routeDuration: 0, // 路线预计时间
// 底部弹窗相关状态
showWarehouseModal: false, // 仓库底部弹窗
showDeliveryPersonModal: false, // 货运人员底部弹窗
// 底部弹窗状态bottom或full
warehouseModalState: 'bottom',
deliveryPersonModalState: 'bottom',
// 操作按钮显示状态(基于用户状态动态计算)
mainPageModule: MainPageModule | null;
// 核心UI状态
showUserPanel: boolean;
showOrderPanel: boolean;
showDeliveryPersonModal: boolean;
showWarehouseModal: boolean;
};
}
showSignOutButton: false // 是否显示签退按钮
// 主页面组件
Component<IndexPageComponent>({
data: {
mainPageModule: null as MainPageModule | null,
// 核心UI状态
showUserPanel: false,
showOrderPanel: false,
showDeliveryPersonModal: false,
showWarehouseModal: false
},
lifetimes: {
async attached() {
// 组件挂载时初始化
await this.initPage();
attached() {
console.log('index page attached');
this.initPage();
},
detached() {
console.log('index page detached');
// 清理资源
if (this.data.mainPageModule) {
this.data.mainPageModule.cleanup();
}
},
detached() {
// 组件卸载时清理
(this as any).mainPageModule = null;
show() {
console.log('index page show');
// 页面显示时调用主页面模块的onShow方法
if (this.data.mainPageModule) {
this.data.mainPageModule.onShow();
}
},
hide() {
console.log('index page hide');
// 页面隐藏时调用主页面模块的onHide方法
if (this.data.mainPageModule) {
this.data.mainPageModule.onHide();
}
}
},
@@ -82,11 +85,14 @@ Component({
const app = getApp<any>();
// 初始化主页面模块
(this as any).mainPageModule = new MainPageModule(this);
const loginModule = (this as any).mainPageModule.getLoginModule();
this.setData({
mainPageModule: new MainPageModule(this)
});
// 设置globalData中的loginModule引用用于废弃方法的重定向
app.globalData.loginModule = loginModule;
if (this.data.mainPageModule) {
app.globalData.loginModule = this.data.mainPageModule.getLoginModule();
}
// 异步检查登录状态
await this.checkAndUpdateLoginStatus();
@@ -95,9 +101,14 @@ Component({
// 异步检查并更新登录状态
async checkAndUpdateLoginStatus() {
const app = getApp<any>();
const loginModule = (this as any).mainPageModule.getLoginModule();
try {
// 获取登录模块
if (!this.data.mainPageModule) {
console.error('mainPageModule未初始化');
return;
}
const loginModule = this.data.mainPageModule.getLoginModule();
// 显示加载状态
wx.showLoading({
title: '检查登录状态...',
@@ -108,21 +119,8 @@ Component({
const isLoggedIn = await this.asyncCheckLoginStatus(loginModule);
if (isLoggedIn) {
// 已登录状态
const userStatus = await loginModule.determineUserStatus(app.globalData.userInfo);
this.setData({
userInfo: app.globalData.userInfo,
'authStatus.hasWxCode': true,
'authStatus.userStatus': userStatus,
// 初始化按钮显示状态
showSignInButton: loginModule.shouldShowSignInButton(),
showRegisterButton: loginModule.shouldShowRegisterButton(),
showAuthButton: false
});
// === 全局登录流程完成,登录成功 ===
// 统一在此处执行一次完整的页面刷新
this.refreshPageAfterLogin();
// 已登录状态 - 按钮状态会在refreshPageAfterLogin中统一更新
console.log('✅ 登录检查成功,等待统一页面刷新');
} else {
// 未登录状态
this.setData({
@@ -173,6 +171,9 @@ Component({
console.log('✅ 使用本地登录状态,用户状态已更新:', userStatus);
// 登录成功后统一刷新页面
this.refreshPageAfterLogin();
return true;
}
@@ -213,19 +214,24 @@ Component({
// 更新按钮显示状态
updateButtonDisplayStatus() {
const loginModule = (this as any).mainPageModule.getLoginModule();
if (!this.data.mainPageModule) {
console.error('mainPageModule未初始化');
return;
}
const loginModule = this.data.mainPageModule.getLoginModule();
const showSignInButton = loginModule.shouldShowSignInButton();
const showRegisterButton = loginModule.shouldShowRegisterButton();
// 签退按钮显示逻辑:已签到用户显示签退按钮
const showSignOutButton = this.data.authStatus.userStatus === 'signed_in';
const showSignOutButton = this.data.authStatus && this.data.authStatus.userStatus === 'signed_in';
console.log('🔄 更新按钮显示状态:');
console.log(' - showSignInButton:', showSignInButton);
console.log(' - showRegisterButton:', showRegisterButton);
console.log(' - showSignOutButton:', showSignOutButton);
console.log(' - 当前用户状态:', this.data.authStatus.userStatus);
console.log(' - 当前hasWxCode:', this.data.authStatus.hasWxCode);
console.log(' - 当前用户状态:', this.data.authStatus ? this.data.authStatus.userStatus : 'undefined');
console.log(' - 当前hasWxCode:', this.data.authStatus ? this.data.authStatus.hasWxCode : 'undefined');
this.setData({
showSignInButton,
@@ -238,7 +244,12 @@ Component({
// 登录成功后统一刷新页面
async refreshPageAfterLogin() {
const loginModule = (this as any).mainPageModule.getLoginModule();
if (!this.data.mainPageModule) {
console.error('mainPageModule未初始化');
return;
}
const loginModule = this.data.mainPageModule.getLoginModule();
const app = getApp<any>();
console.log('🔄 === 全局登录流程完成,执行统一页面刷新 ===');
@@ -250,13 +261,14 @@ Component({
this.updateButtonDisplayStatus();
// 3. 初始化主页面模块
await (this as any).mainPageModule.onLoad();
await this.data.mainPageModule.onLoad();
// 4. 登录成功后主动加载业务数据(仓库、订单等)
// 4. 登录成功后只加载公开数据(仓库等),不加载业务数据
// 业务数据(员工位置等)只有在用户签到后才应该加载
if (app.globalData.isLoggedIn) {
console.log('🔍 登录成功,开始加载业务数据...');
await (this as any).mainPageModule.refreshAllData();
console.log('✅ 业务数据加载完成');
console.log('🔍 登录成功,开始加载公开数据...');
await this.data.mainPageModule.loadPublicData();
console.log('✅ 公开数据加载完成');
}
console.log('✅ 统一页面刷新完成');
@@ -278,7 +290,12 @@ Component({
// 处理签到 - 已迁移到LoginModule
async handleSignIn() {
const loginModule = (this as any).mainPageModule.getLoginModule();
if (!this.data.mainPageModule) {
console.error('mainPageModule未初始化');
return;
}
const loginModule = this.data.mainPageModule.getLoginModule();
const success = await loginModule.handleSignIn();
if (success) {
@@ -286,18 +303,22 @@ Component({
this.updateButtonDisplayStatus();
// 刷新页面数据
await (this as any).mainPageModule.refreshAllData();
await this.data.mainPageModule.refreshAllData();
}
},
// 处理授权登录 - 已迁移到LoginModule
async handleAuthLogin() {
const loginModule = (this as any).mainPageModule.getLoginModule();
if (!this.data.mainPageModule) {
console.error('mainPageModule未初始化');
return;
}
const loginModule = this.data.mainPageModule.getLoginModule();
const success = await loginModule.handleAuthLogin();
if (success) {
const app = getApp<any>();
const loginModule = (this as any).mainPageModule.getLoginModule();
const userStatus = loginModule.determineUserStatus(app.globalData.userInfo);
this.setData({
userInfo: app.globalData.userInfo,
@@ -323,7 +344,11 @@ Component({
});
// 调用loginModule的logout方法
const loginModule = (this as any).mainPageModule.getLoginModule();
if (!this.data.mainPageModule) {
console.error('mainPageModule未初始化');
return;
}
const loginModule = this.data.mainPageModule.getLoginModule();
await loginModule.logout();
// 更新页面状态
@@ -364,10 +389,18 @@ Component({
console.warn('调试信息 - 停止位置追踪失败:', trackingError);
}
// 清除登录信息,防止自动重新登录
console.log('调试信息 - 开始清除登录信息');
const app = getApp<any>();
app.doGlobalLogout();
// 停止位置模块的实时跟踪
try {
if (this.data.mainPageModule) {
const locationModule = this.data.mainPageModule.getLocationModule();
if (locationModule) {
await locationModule.stopRealTimeTracking();
console.log('调试信息 - 位置模块实时跟踪已停止');
}
}
} catch (trackingError) {
console.warn('调试信息 - 停止位置模块实时跟踪失败:', trackingError);
}
// 保存签退状态到本地存储,防止自动重新登录
console.log('调试信息 - 保存签退状态到本地存储');
@@ -377,13 +410,25 @@ Component({
const savedStatus = wx.getStorageSync('userStatus');
console.log('调试信息 - 验证保存的签退状态:', savedStatus);
// 更新用户状态为已签退
// 更新用户状态为已签退不调用doGlobalLogout保持登录状态
this.setData({
'authStatus.userStatus': 'signed_out',
showSignOutButton: false,
showSignInButton: true
});
// 清除所有员工图标
if (this.data.mainPageModule) {
const employeeModule = this.data.mainPageModule.getEmployeeModule();
if (employeeModule) {
// 清除员工标记点
const { markers } = this.data;
const filteredMarkers = markers.filter((marker: any) => marker.type !== 'employee');
this.setData({ markers: filteredMarkers });
console.log('调试信息 - 已清除所有员工图标');
}
}
wx.hideLoading();
showToast('签退成功');
console.log('调试信息 - 用户签退完成');
@@ -401,9 +446,25 @@ Component({
});
},
// 跳转到管理员页面
goToAdminPage() {
wx.navigateTo({
url: '/pages/admin/admin'
});
},
// 跳转到员工管理页面
goToEmployeeManagement() {
wx.navigateTo({
url: '/pages/employee/employee'
});
},
// 阻止事件冒泡
stopPropagation(e: any) {
e.stopPropagation();
if (e && typeof e.stopPropagation === 'function') {
e.stopPropagation();
}
},
@@ -422,8 +483,8 @@ Component({
// 用户登出
userLogout() {
if ((this as any).mainPageModule) {
const loginModule = (this as any).mainPageModule.getLoginModule();
if (this.data.mainPageModule) {
const loginModule = this.data.mainPageModule.getLoginModule();
loginModule.logout();
// 更新页面状态
@@ -449,91 +510,91 @@ Component({
// 隐藏所有面板
hideAllPanels() {
if ((this as any).mainPageModule) {
(this as any).mainPageModule.hideAllPanels();
if (this.data.mainPageModule) {
this.data.mainPageModule.hideAllPanels();
}
},
// 重置标记点状态
resetMarkers() {
if ((this as any).mainPageModule) {
(this as any).mainPageModule.resetMarkers();
if (this.data.mainPageModule) {
this.data.mainPageModule.resetMarkers();
}
},
// 地图点击事件
onMapTap(e: any) {
if ((this as any).mainPageModule) {
(this as any).mainPageModule.onMapTap(e);
if (this.data.mainPageModule) {
this.data.mainPageModule.onMapTap(e);
}
},
// 标记点点击事件
onMarkerTap(e: any) {
if ((this as any).mainPageModule) {
(this as any).mainPageModule.onMarkerTap(e);
if (this.data.mainPageModule) {
this.data.mainPageModule.onMarkerTap(e);
}
},
// 分配订单
async assignOrder(orderId: number, deliveryPersonId: number) {
if ((this as any).mainPageModule) {
const orderModule = (this as any).mainPageModule.getOrderModule();
if (this.data.mainPageModule) {
const orderModule = this.data.mainPageModule.getOrderModule();
await orderModule.assignOrder(orderId, deliveryPersonId);
}
},
// 更新订单状态
async updateOrderStatus(orderId: number, status: 'pending' | 'assigned' | 'in_transit' | 'delivered') {
if ((this as any).mainPageModule) {
const orderModule = (this as any).mainPageModule.getOrderModule();
if (this.data.mainPageModule) {
const orderModule = this.data.mainPageModule.getOrderModule();
await orderModule.updateOrderStatus(orderId, status);
}
},
// 展开仓库面板
expandWarehousePanel() {
if ((this as any).mainPageModule) {
const warehouseModule = (this as any).mainPageModule.getWarehouseModule();
if (this.data.mainPageModule) {
const warehouseModule = this.data.mainPageModule.getWarehouseModule();
warehouseModule.expandWarehousePanel();
}
},
// 收起仓库面板
collapseWarehousePanel() {
if ((this as any).mainPageModule) {
const warehouseModule = (this as any).mainPageModule.getWarehouseModule();
if (this.data.mainPageModule) {
const warehouseModule = this.data.mainPageModule.getWarehouseModule();
warehouseModule.collapseWarehousePanel();
}
},
// 展开货运人员面板
// 展开员面板
expandDeliveryPersonPanel() {
if ((this as any).mainPageModule) {
const deliveryPersonModule = (this as any).mainPageModule.getDeliveryPersonModule();
deliveryPersonModule.expandDeliveryPersonPanel();
if (this.data.mainPageModule) {
const employeeModule = this.data.mainPageModule.getEmployeeModule();
employeeModule.expandDeliveryPersonPanel();
}
},
// 收起货运人员面板
// 收起员面板
collapseDeliveryPersonPanel() {
if ((this as any).mainPageModule) {
const deliveryPersonModule = (this as any).mainPageModule.getDeliveryPersonModule();
deliveryPersonModule.collapseDeliveryPersonPanel();
if (this.data.mainPageModule) {
const employeeModule = this.data.mainPageModule.getEmployeeModule();
employeeModule.collapseDeliveryPersonPanel();
}
},
// 刷新所有数据
async refreshAllData() {
if ((this as any).mainPageModule) {
await (this as any).mainPageModule.refreshAllData();
if (this.data.mainPageModule) {
await this.data.mainPageModule.refreshAllData();
}
},
// 开始定位(处理地图控制按钮点击)
async startLocation() {
if ((this as any).mainPageModule) {
const mapModule = (this as any).mainPageModule.getMapModule();
if (this.data.mainPageModule) {
const mapModule = this.data.mainPageModule.getMapModule();
await mapModule.startLocation();
}
},

View File

@@ -26,7 +26,8 @@
type="primary"
size="mini"
>
签到
<text class="btn-icon">✅</text>
<text class="btn-text">签到</text>
</button>
<!-- 注册按钮 - 游客用户 -->
@@ -37,7 +38,8 @@
type="default"
size="mini"
>
注册
<text class="btn-icon">📝</text>
<text class="btn-text">注册</text>
</button>
<!-- 授权登录按钮 - 未授权用户 -->
@@ -48,7 +50,8 @@
type="warn"
size="mini"
>
登录
<text class="btn-icon">🔑</text>
<text class="btn-text">登录</text>
</button>
<!-- 签退按钮 - 已签到用户 -->
@@ -59,14 +62,16 @@
type="warn"
size="mini"
>
签退
<text class="btn-icon">🚪</text>
<text class="btn-text">签退</text>
</button>
</view>
<!-- 地图控制按钮 -->
<view class="map-controls">
<view class="control-btn location-btn" bindtap="startLocation">
<image src="/images/ma.png" mode="aspectFit"></image>
<text class="btn-icon">📍</text>
<text class="btn-text">定位</text>
</view>
</view>
@@ -74,11 +79,17 @@
<view wx:if="{{authStatus.hasWxCode}}"
class="control-btn reset-btn"
bindtap="showUserPanel">
<image
src="/images/trucks.png"
mode="aspectFit"
></image>
<view wx:if="{{userInfo.role === 'ADMIN'}}" class="admin-badge">管理</view>
<text class="btn-icon">👤</text>
<text class="btn-text">我的</text>
<view wx:if="{{userInfo.role === 'ADMIN'}}" class="admin-badge"/>
</view>
<!-- 员工管理按钮(仅管理员可见) -->
<view wx:if="{{authStatus.hasWxCode && userInfo.role === 'ADMIN'}}"
class="control-btn staff-management-btn"
bindtap="goToEmployeeManagement">
<text class="btn-icon">👥</text>
<text class="btn-text">员工</text>
</view>
</view>
@@ -92,7 +103,7 @@
<view class="user-panel-content" bindtap="stopPropagation">
<view class="user-avatar">
<image
src="/images/user-avatar.png"
src="/images/trucks.png"
mode="aspectFill"
class="avatar-image"
></image>
@@ -103,6 +114,7 @@
<text class="user-id">ID: {{userInfo.id || '未获取'}}</text>
<text class="user-phone">电话:{{userInfo.phone || '未设置'}}</text>
<text class="user-role">角色:{{userInfo.role === 'ADMIN' ? '管理员' : '货运员'}}</text>
<!-- 退出后台代码 -->
<button class="btn-logout" bindtap="handleLogout" size="mini">退出登录</button>
</view>

View File

@@ -88,6 +88,17 @@
gap: 20rpx;
}
/* 右侧控制按钮容器 */
.right-controls-container {
position: absolute;
top: 20rpx;
right: 30rpx;
z-index: 10;
display: flex;
flex-direction: column;
gap: 20rpx;
}
.control-btn {
width: 80rpx;
height: 80rpx;
@@ -1407,4 +1418,26 @@
100% {
opacity: 0.7;
}
}
/* 员工管理按钮样式 */
.staff-management-btn {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
background-color: white;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
}
.staff-management-btn:active {
background-color: #f5f5f5;
transform: scale(0.98);
}
.staff-management-btn image {
width: 40rpx;
height: 40rpx;
}

View File

@@ -14,11 +14,32 @@ export class DataModule {
/**
* 初始化页面数据
*/
public initializeData(): void {
public async initializeData(): Promise<void> {
// 检查是否已静默登录,如果是则尝试获取真实位置
let initialLongitude = 102.833722;
let initialLatitude = 24.880095;
const app = getApp<any>();
if (app.globalData.isLoggedIn) {
try {
// 导入地图服务
const mapService = require('../../../services/mapService').default;
const location = await mapService.getCurrentLocation();
if (location && !isNaN(location.latitude) && !isNaN(location.longitude)) {
initialLongitude = location.longitude;
initialLatitude = location.latitude;
console.log('[DATA MODULE] 静默登录后使用真实位置:', location);
}
} catch (error) {
console.warn('[DATA MODULE] 获取真实位置失败,使用默认位置:', error);
}
}
this.pageContext.setData({
// 地图相关数据
longitude: 102.833722,
latitude: 24.880095,
longitude: initialLongitude,
latitude: initialLatitude,
scale: 13,
markers: [] as Marker[],
polyline: null,
@@ -148,7 +169,7 @@ export class DataModule {
*/
public updateMarkers(markers: Marker[]): void {
// 验证每个标记点的坐标
const validatedMarkers = markers.map((marker, index) => {
const validatedMarkers = markers.map((marker) => {
// 检查经纬度是否为有效数字
if (isNaN(marker.longitude) || isNaN(marker.latitude)) {
// 为无效坐标设置默认值
@@ -283,4 +304,26 @@ export class DataModule {
public resetAllData(): void {
this.initializeData();
}
/**
* 获取位置模块(用于其他模块访问位置模块)
*/
public getLocationModule(): any {
// 通过页面上下文获取位置模块
if (this.pageContext.data.mainPageModule) {
return this.pageContext.data.mainPageModule.getLocationModule();
}
return null;
}
/**
* 获取地图模块(用于其他模块访问地图模块)
*/
public getMapModule(): any {
// 通过页面上下文获取地图模块
if (this.pageContext.data.mainPageModule) {
return this.pageContext.data.mainPageModule.getMapModule();
}
return null;
}
}

View File

@@ -1,10 +1,12 @@
// 货运人员模块 - 处理货运人员管理、位置跟踪、交互
import deliveryPersonService from '../../../services/deliveryPersonService';
// 员模块 - 处理所有员工(管理员和货运人员管理、位置跟踪、交互
import employeeService from '../../../services/employeeService';
import { Role } from '../../../utils/roleUtils';
// getApp是微信小程序全局函数无需导入
import { showToast } from '../../../utils/helpers';
import { DataModule } from './dataModule';
export class DeliveryPersonModule {
export class EmployeeModule {
private pageContext: any;
private dataModule: DataModule;
@@ -14,210 +16,44 @@ export class DeliveryPersonModule {
}
/**
* 加载货运人员数据
* 加载所有员工数据(包括管理员和货运人员
*/
async loadDeliveryPersons(): Promise<void> {
async loadAllEmployees(): Promise<void> {
try {
const deliveryPersons = await deliveryPersonService.getDeliveryPersons();
// 获取所有员工数据
const allEmployees = await employeeService.getEmployees();
// 更新地图标记点
this.updateDeliveryPersonMarkers(deliveryPersons);
console.log('货运人员数据加载完成,数量:', deliveryPersons.length);
console.log('所有员工数据加载完成,数量:', allEmployees.length);
} catch (error) {
console.error('加载货运人员数据失败:', error);
showToast('加载货运人员数据失败');
}
}
/**
* 更新货运人员位置
*/
async updateDeliveryPersonLocation(personId: number, location: { longitude: number, latitude: number }): Promise<void> {
try {
await deliveryPersonService.updateDeliveryPersonLocation(personId, location);
console.log(`货运人员 ${personId} 位置更新:`, location);
// 重新加载货运人员数据
await this.loadDeliveryPersons();
} catch (error) {
console.error('更新货运人员位置失败:', error);
showToast('更新位置失败');
}
}
/**
* 开始实时位置跟踪
*/
async startRealTimeTracking(): Promise<void> {
try {
// 获取当前用户信息
const userInfo = this.dataModule.getData().userInfo;
if (!userInfo || !userInfo.id) {
throw new Error('用户信息获取失败');
}
// 使用新的WebSocket接口订阅位置更新
await deliveryPersonService.subscribeToRealTimeLocations(userInfo.id);
// 设置位置更新回调
deliveryPersonService.subscribeToRealTimeLocations(this.handleRealTimeLocationUpdate.bind(this));
showToast('开始实时跟踪');
console.log('开始实时跟踪货运人员位置');
} catch (error) {
console.error('开始实时跟踪失败:', error);
showToast('开始实时跟踪失败');
console.error('加载员数据失败:', error);
showToast('加载员数据失败');
}
}
handleRealTimeLocationUpdate(location: any): void {
console.log('收到实时位置更新:', location);
// 这里可以添加更新地图标记点的逻辑
// 根据位置更新信息更新对应的货运人员标记点
if (location && location.deliveryPersonId) {
this.updateSingleDeliveryPersonMarker(location);
}
}
/**
* 更新单个货运人员标记点
* 获取员工角色对应的图标
*/
private updateSingleDeliveryPersonMarker(location: any): void {
const { markers } = this.pageContext.data;
private getEmployeeIcon(role: string): string {
console.log(`获取员工图标,角色: ${role}`);
// 查找并更新对应的货运人员标记点
const updatedMarkers = markers.map((marker: any) => {
if (marker.type === 'delivery_person' && marker.data && marker.data.id === location.deliveryPersonId) {
// 更新标记点位置
return {
...marker,
longitude: location.longitude,
latitude: location.latitude,
data: {
...marker.data,
currentLocation: {
longitude: location.longitude,
latitude: location.latitude
}
}
};
}
return marker;
});
// 更新数据模块中的标记点
this.dataModule.updateMarkers(updatedMarkers);
}
/**
* 停止实时跟踪货运人员位置
*/
async stopRealTimeTracking(): Promise<void> {
try {
// 获取当前用户信息
const userInfo = this.dataModule.getData().userInfo;
if (!userInfo || !userInfo.id) {
throw new Error('用户信息获取失败');
}
// 使用新的WebSocket接口取消订阅
await deliveryPersonService.unsubscribeFromRealTimeLocations();
showToast('已停止实时跟踪');
console.log('停止实时跟踪货运人员位置');
} catch (error) {
console.error('停止实时跟踪失败:', error);
showToast('停止实时跟踪失败');
}
}
/**
* 更新货运人员标记点
*/
private updateDeliveryPersonMarkers(deliveryPersons: any[]): void {
console.log(`[DELIVERY PERSON MODULE] 开始更新货运人员标记点,货运人员总数: ${deliveryPersons.length}`);
const { markers } = this.pageContext.data;
// 移除现有的货运人员标记点
const filteredMarkers = markers.filter((marker: any) => marker.type !== 'delivery_person');
console.log(`[DELIVERY PERSON MODULE] 移除现有货运人员标记点后,剩余标记点数量: ${filteredMarkers.length}`);
// 添加新的货运人员标记点
const deliveryPersonMarkers = deliveryPersons.map((person, index) => {
// 验证货运人员坐标是否有效
// 注意坐标信息嵌套在currentLocation对象中
let validLongitude = person.currentLocation?.longitude;
let validLatitude = person.currentLocation?.latitude;
if (isNaN(validLongitude) || isNaN(validLatitude)) {
console.error(`[DELIVERY PERSON MODULE] 货运人员${index} (ID: ${person.id}) 坐标无效: (${validLongitude}, ${validLatitude}),使用默认坐标`);
validLongitude = 102.833722; // 默认经度
validLatitude = 24.880095; // 默认纬度
} else {
console.log(`[DELIVERY PERSON MODULE] 货运人员${index} (ID: ${person.id}) 坐标有效: (${validLongitude}, ${validLatitude})`);
}
const iconPath = this.getDeliveryPersonIcon(person.status);
return {
id: 2000 + index, // 货运人员标记点ID从2000开始
longitude: validLongitude,
latitude: validLatitude,
iconPath: iconPath,
width: 26,
height: 26,
zIndex: 20,
type: 'delivery_person',
data: person
};
});
console.log(`[DELIVERY PERSON MODULE] 生成新货运人员标记点数量: ${deliveryPersonMarkers.length}`);
// 更新数据模块中的标记点
const allMarkers = [...filteredMarkers, ...deliveryPersonMarkers];
console.log(`[DELIVERY PERSON MODULE] 准备更新所有标记点,总数: ${allMarkers.length}`);
this.dataModule.updateMarkers(allMarkers);
}
/**
* 获取货运人员状态对应的图标
*/
private getDeliveryPersonIcon(status: string): string {
console.log(`获取货运人员图标,状态: ${status}`);
// 根据报错信息,直接使用已知存在的备用图片路径
// 实际项目中,应确保相关图片资源正确放置在指定路径
const fallbackIconPath = '/images/trucks.png';
// 根据不同状态使用不同的图片路径
// 根据角色使用不同的图标
let iconPath = '';
switch (status) {
case 'idle':
// 为避免图片加载失败,暂时使用备用图片
iconPath = fallbackIconPath;
console.log('使用备用图标idle状态');
switch (role) {
case Role.ADMIN:
// 管理员使用皇冠图标
iconPath = '/images/crown.png';
console.log('使用管理员图标(👑');
break;
case 'busy':
// 为避免图片加载失败,暂时使用备用图片
iconPath = fallbackIconPath;
console.log('使用备用图标busy状态');
break;
case 'offline':
// 为避免图片加载失败,暂时使用备用图片
iconPath = fallbackIconPath;
console.log('使用备用图标offline状态');
case Role.DELIVERY_PERSON:
// 货运人员使用货车图标
iconPath = '/images/truck.png';
console.log('使用货运人员图标(🚚');
break;
default:
iconPath = fallbackIconPath;
console.log('使用备用图标(默认状态)');
// 默认使用货车图标
iconPath = '/images/truck.png';
console.log('使用默认图标(🚚)');
break;
}

View File

@@ -0,0 +1,278 @@
// 位置模块 - 处理位置追踪、位置更新和地图标记点更新
import { DataModule } from './dataModule';
import { showToast } from '../../../utils/helpers';
import locationTrackingService from '../../../services/locationTrackingService';
import { OnlineUserInfo } from '../../../services/locationTrackingService';
// 位置模块接口定义
export interface LocationModule {
initialize(): Promise<void>;
cleanup(): void;
startRealTimeTracking(): Promise<void>;
stopRealTimeTracking(): Promise<void>;
updateEmployeeLocation(employeeId: number, location: { longitude: number, latitude: number }): Promise<void>;
}
export class LocationModule {
private pageContext: any;
private dataModule: DataModule;
private isTracking: boolean;
constructor(pageContext: any, dataModule: DataModule) {
this.pageContext = pageContext;
this.dataModule = dataModule;
this.isTracking = false;
// 绑定回调方法
this.handleLocationUpdates = this.handleLocationUpdates.bind(this);
}
/**
* 初始化位置模块
*/
async initialize(): Promise<void> {
console.log('位置模块初始化');
// 订阅位置更新
this.subscribeToLocationUpdates();
console.log('位置模块初始化完成');
}
/**
* 清理位置模块
*/
cleanup(): void {
console.log('清理位置模块');
// 取消位置更新订阅
this.unsubscribeFromLocationUpdates();
// 停止实时跟踪
if (this.isTracking) {
this.stopRealTimeTracking().catch(error => {
console.error('停止实时跟踪失败:', error);
});
}
}
/**
* 开始实时跟踪位置
*/
async startRealTimeTracking(): Promise<void> {
try {
// 获取当前用户信息
const userInfo = this.dataModule.getData().userInfo;
if (!userInfo || !userInfo.id) {
throw new Error('用户信息获取失败');
}
// 启动位置跟踪订阅
await locationTrackingService.startTrackingAfterSignIn();
this.isTracking = true;
showToast('已开始实时跟踪');
console.log('开始实时跟踪位置');
} catch (error) {
console.error('开始实时跟踪失败:', error);
showToast('开始实时跟踪失败');
throw error;
}
}
/**
* 停止实时跟踪位置
*/
async stopRealTimeTracking(): Promise<void> {
try {
// 获取当前用户信息
const userInfo = this.dataModule.getData().userInfo;
if (!userInfo || !userInfo.id) {
throw new Error('用户信息获取失败');
}
// 停止位置跟踪
await locationTrackingService.stopTracking();
this.isTracking = false;
showToast('已停止实时跟踪');
console.log('停止实时跟踪位置');
} catch (error) {
console.error('停止实时跟踪失败:', error);
showToast('停止实时跟踪失败');
throw error;
}
}
/**
* 更新员工位置
*/
async updateEmployeeLocation(employeeId: number, location: { longitude: number, latitude: number }): Promise<void> {
try {
console.log(`员工 ${employeeId} 位置更新:`, location);
// 通过locationTrackingService更新位置
await locationTrackingService.updateUserLocation(location);
console.log(`员工 ${employeeId} 位置更新完成`);
} catch (error) {
console.error('更新员工位置失败:', error);
showToast('更新位置失败');
throw error;
}
}
/**
* 订阅位置更新
*/
private subscribeToLocationUpdates(): void {
console.log('订阅位置更新');
locationTrackingService.subscribeToLocationUpdates(this.handleLocationUpdates);
}
/**
* 取消订阅位置更新
*/
private unsubscribeFromLocationUpdates(): void {
console.log('取消订阅位置更新');
locationTrackingService.unsubscribeFromLocationUpdates(this.handleLocationUpdates);
}
/**
* 处理位置更新回调
*/
private handleLocationUpdates(locationData: any): void {
console.log('收到位置更新回调:', locationData);
if (locationData.type === 'onlineUserList' && locationData.users) {
// 处理在线用户列表更新
console.log('处理在线用户列表,用户数量:', locationData.users.length);
this.updateEmployeeMarkers(locationData.users);
} else if (Array.isArray(locationData)) {
// 处理位置更新数组(旧格式)
console.log('处理位置更新数组,用户数量:', locationData.length);
this.updateEmployeeMarkers(locationData);
} else if (locationData.userId && locationData.latitude && locationData.longitude) {
// 处理单个用户位置更新
console.log('处理单个用户位置更新:', locationData.userId);
this.updateSingleEmployeeMarker(locationData);
} else {
console.warn('未知的位置数据格式:', locationData);
}
}
/**
* 更新员工标记点
*/
private updateEmployeeMarkers(onlineUsers: any[]): void {
console.log('开始更新员工标记点,在线用户数量:', onlineUsers.length);
// 获取地图模块来更新标记点
const mapModule = this.dataModule.getMapModule();
if (!mapModule) {
console.error('地图模块未找到,无法更新员工标记点');
return;
}
// 为每个在线用户创建标记点
const employeeMarkers = onlineUsers.map(user => {
// 获取用户角色信息
const userRole = user.role || this.getUserRole(user.userId);
// 解析位置信息(支持多种格式)
const longitude = user.longitude || (user.lastLocation && user.lastLocation.longitude) || 0;
const latitude = user.latitude || (user.lastLocation && user.lastLocation.latitude) || 0;
const lastUpdateTime = user.lastUpdateTime || Date.now();
const employeeMarker = {
id: 10000 + user.userId, // 避免ID冲突
type: 'employee',
title: user.userName || `员工${user.userId}`,
longitude: longitude,
latitude: latitude,
iconPath: this.getEmployeeIcon(user.userId, userRole),
width: 32,
height: 32,
zIndex: 30,
data: {
userId: user.userId,
role: userRole,
lastUpdateTime: lastUpdateTime
}
};
return employeeMarker;
});
// 调用地图模块更新员工标记点
mapModule.updateEmployeeMarkers(employeeMarkers);
console.log(`成功更新了 ${employeeMarkers.length} 个员工标记点`);
}
/**
* 更新单个员工标记点
*/
private updateSingleEmployeeMarker(locationUpdate: any): void {
console.log('更新单个员工标记点:', locationUpdate.userId);
// 获取地图模块
const mapModule = this.dataModule.getMapModule();
if (!mapModule) {
console.error('地图模块未找到,无法更新员工标记点');
return;
}
// 获取用户角色信息
const userRole = this.getUserRole(locationUpdate.userId);
// 创建单个员工标记点
const employeeMarker = {
id: 10000 + locationUpdate.userId,
type: 'employee',
title: `员工${locationUpdate.userId}`,
longitude: locationUpdate.longitude,
latitude: locationUpdate.latitude,
iconPath: this.getEmployeeIcon(locationUpdate.userId, userRole),
width: 32,
height: 32,
zIndex: 30,
data: {
userId: locationUpdate.userId,
role: userRole,
lastUpdateTime: locationUpdate.timestamp || Date.now()
}
};
// 调用地图模块更新单个标记点
mapModule.updateSingleEmployeeMarker(employeeMarker);
console.log('单个员工标记点更新完成');
}
/**
* 获取用户角色
*/
private getUserRole(userId: number): string {
// 从数据模块获取用户信息
const userInfo = this.dataModule.getData().userInfo;
if (userInfo && userInfo.id === userId) {
return userInfo.role || 'employee';
}
// 如果是其他用户,默认为货运人员
return 'employee';
}
/**
* 获取员工图标
*/
private getEmployeeIcon(userId: number, userRole: string): string {
// 根据用户角色返回不同的图标
if (userRole === 'ADMIN') {
return '/images/crown.png'; // 管理员图标
} else {
return '/images/trucks.png'; // 货运人员图标
}
}
}

View File

@@ -234,9 +234,17 @@ export class LoginModule {
// 启动位置追踪服务
try {
// 先启动位置追踪服务
await locationTrackingService.startTrackingAfterSignIn();
console.log('位置追踪服务已启动');
// 然后调用位置模块的实时跟踪功能
const locationModule = this.dataModule.getLocationModule();
if (locationModule) {
await locationModule.startRealTimeTracking();
console.log('位置模块实时跟踪已启动');
}
// 订阅位置更新回调,采用统一方式更新所有用户位置
locationTrackingService.subscribeToLocationUpdates((onlineUsers) => {
console.log('🚚 位置更新回调 - 在线用户列表已更新,用户数量:', onlineUsers.length);
@@ -433,10 +441,8 @@ export class LoginModule {
// 根据服务器返回的状态映射到前端状态
switch (response.status) {
case 'signed_in':
case 'online':
return 'signed_in';
case 'signed_out':
case 'offline':
return 'signed_out';
case 'registered':
return 'registered';

View File

@@ -3,17 +3,32 @@ import { LoginModule } from './loginModule';
import { MapModule } from './mapModule';
import { OrderModule } from './orderModule';
import { WarehouseModule } from './warehouseModule';
import { DeliveryPersonModule } from './deliveryPersonModule';
import { EmployeeModule } from './deliveryPersonModule';
import { LocationModule } from './locationModule';
import { DataModule } from './dataModule';
import { showToast } from '../../../utils/helpers';
// 主页面模块接口定义
export interface MainPageModule {
getLoginModule(): LoginModule;
onLoad(): Promise<void>;
onShow(): void;
onHide(): void;
onLogout(): void;
hideAllPanels(): void;
resetMarkers(): void;
cleanup(): void;
refreshAllData(): Promise<void>;
}
export class MainPageModule {
private pageContext: any;
private loginModule: LoginModule;
private mapModule: MapModule;
private orderModule: OrderModule;
private warehouseModule: WarehouseModule;
private deliveryPersonModule: DeliveryPersonModule;
private employeeModule: EmployeeModule;
private locationModule: LocationModule;
private dataModule: DataModule;
constructor(pageContext: any) {
@@ -25,7 +40,8 @@ export class MainPageModule {
this.mapModule = new MapModule(pageContext, this.dataModule);
this.orderModule = new OrderModule(pageContext, this.dataModule);
this.warehouseModule = new WarehouseModule(pageContext, this.dataModule);
this.deliveryPersonModule = new DeliveryPersonModule(pageContext, this.dataModule);
this.employeeModule = new EmployeeModule(pageContext, this.dataModule);
this.locationModule = new LocationModule(pageContext, this.dataModule);
}
/**
@@ -38,6 +54,9 @@ export class MainPageModule {
// 初始化应用
await this.initApp();
// 初始化位置模块
await this.locationModule.initialize();
// 加载地图数据
await this.loadAllData();
@@ -58,12 +77,22 @@ export class MainPageModule {
this.refreshDataIfNeeded();
}
/**
* 页面隐藏
*/
onHide(): void {
console.log('主页面隐藏');
// 清理位置模块
this.locationModule.cleanup();
}
/**
* 初始化应用
*/
private async initApp(): Promise<void> {
// 初始化数据
this.dataModule.initializeData();
await this.dataModule.initializeData();
console.log('应用初始化');
}
@@ -119,7 +148,7 @@ export class MainPageModule {
// 并行加载各种业务数据(需要登录)
await Promise.all([
this.orderModule.loadPendingOrders(),
this.deliveryPersonModule.loadDeliveryPersons()
this.employeeModule.loadAllEmployees()
]);
console.log('所有业务数据加载完成');
@@ -160,7 +189,7 @@ export class MainPageModule {
this.dataModule.updatePendingOrders([]);
const filteredMarkers = this.pageContext.data.markers.filter((marker: any) =>
marker.type !== 'warehouse' && marker.type !== 'delivery_person'
marker.type !== 'warehouse' && marker.type !== 'employee'
);
this.dataModule.updateMarkers(filteredMarkers);
}
@@ -230,7 +259,7 @@ export class MainPageModule {
this.warehouseModule.onWarehouseMarkerClick(marker.data, e);
break;
case 'delivery_person':
this.deliveryPersonModule.onDeliveryPersonMarkerClick(marker.data, e);
this.employeeModule.onDeliveryPersonMarkerClick(marker.data, e);
break;
default:
this.mapModule.onMarkerTap(e);
@@ -267,10 +296,17 @@ export class MainPageModule {
}
/**
* 获取货运人员模块
* 获取员模块
*/
getDeliveryPersonModule(): DeliveryPersonModule {
return this.deliveryPersonModule;
getEmployeeModule(): EmployeeModule {
return this.employeeModule;
}
/**
* 获取位置模块
*/
getLocationModule(): LocationModule {
return this.locationModule;
}
/**

View File

@@ -83,11 +83,16 @@ export class MapModule {
return;
}
// 更新数据模块中的位置信息和缩放级别
// 强制重置地图视角到用户位置
// 1. 更新用户位置
this.dataModule.updateUserLocation(location.latitude, location.longitude);
this.dataModule.setMapScale(15); // 定位成功后放大到更详细级别
// 2. 重置地图缩放级别到15详细级别
this.dataModule.setMapScale(15);
// 3. 添加地图动画效果,让视角平滑回到用户位置
this.animateMapToUserLocation(location.latitude, location.longitude);
console.log('[MAP MODULE] 定位成功,坐标已更新:', location);
console.log('[MAP MODULE] 定位成功,坐标已更新,视角已重置:', location);
showToast('已定位到您的位置');
} catch (error) {
console.error('[MAP MODULE] 定位失败:', error);
this.setDefaultLocation();
@@ -105,9 +110,44 @@ export class MapModule {
// 更新数据模块中的位置信息和缩放级别
this.dataModule.updateUserLocation(defaultLatitude, defaultLongitude);
this.dataModule.setMapScale(13);
// 动画效果回到默认位置
this.animateMapToUserLocation(defaultLatitude, defaultLongitude);
console.log('[MAP MODULE] 默认位置设置完成');
}
/**
* 动画效果将地图视角平滑移动到用户位置
*/
private animateMapToUserLocation(latitude: number, longitude: number): void {
// 使用微信小程序的MapContext.moveToLocation方法实现地图移动
const mapContext = wx.createMapContext('myMap', this.pageContext);
// 先设置地图中心点
this.pageContext.setData({
latitude: latitude,
longitude: longitude
});
// 使用moveToLocation方法平滑移动地图视角
mapContext.moveToLocation({
latitude: latitude,
longitude: longitude,
success: () => {
console.log('[MAP MODULE] 地图视角移动成功,位置:', { latitude, longitude });
},
fail: (err: any) => {
console.error('[MAP MODULE] 地图视角移动失败:', err);
// 如果moveToLocation失败直接设置中心点
this.pageContext.setData({
latitude: latitude,
longitude: longitude
});
}
});
console.log('[MAP MODULE] 地图视角动画已启动,移动到位置:', { latitude, longitude });
}
/**
* 加载地图数据(仓库和货运人员)
@@ -126,39 +166,30 @@ export class MapModule {
/**
* 生成初始标记点
*/
private generateInitialMarkers(): Marker[] {
// 获取当前坐标
const longitude = this.pageContext.data.longitude;
const latitude = this.pageContext.data.latitude;
public generateInitialMarkers(): Marker[] {
console.log('生成初始标记点');
// 检查坐标是否为有效数字
let validLongitude = longitude;
let validLatitude = latitude;
const markers: Marker[] = [];
if (isNaN(longitude) || isNaN(latitude)) {
console.warn(`无效的坐标值: longitude=${longitude}, latitude=${latitude}, 使用默认坐标`);
validLongitude = 102.833722; // 默认经度
validLatitude = 24.880095; // 默认纬度
// 更新页面数据中的坐标
this.dataModule.updateUserLocation(validLatitude, validLongitude);
}
// 生成初始标记点
return [
{
id: -1, // 用户位置标记
// 获取用户位置
const userLocation = this.pageContext.data.userLocation;
if (userLocation && userLocation.longitude && userLocation.latitude) {
// 添加用户位置标记点
markers.push({
id: -1,
title: '用户位置',
longitude: validLongitude,
latitude: validLatitude,
longitude: userLocation.longitude,
latitude: userLocation.latitude,
iconPath: '/images/trucks.png',
width: 40,
height: 40,
zIndex: 99
}
];
});
console.log('已添加用户位置标记点');
}
return markers;
}
/**
@@ -191,6 +222,57 @@ export class MapModule {
console.log('用户头像已更新');
}
/**
* 更新员工标记点
*/
updateEmployeeMarkers(employeeMarkers: Marker[]): void {
console.log('开始更新员工标记点,数量:', employeeMarkers.length);
const { markers } = this.pageContext.data;
// 过滤掉现有的员工标记点
const filteredMarkers = markers.filter((marker: any) =>
marker.type !== 'employee'
);
// 合并标记点
const updatedMarkers = [...filteredMarkers, ...employeeMarkers];
// 更新数据模块中的标记点
this.dataModule.updateMarkers(updatedMarkers);
console.log('员工标记点更新完成,总标记点数量:', updatedMarkers.length);
}
/**
* 更新单个员工标记点
*/
updateSingleEmployeeMarker(employeeMarker: Marker): void {
console.log('更新单个员工标记点:', employeeMarker.id);
const { markers } = this.pageContext.data;
// 查找现有的员工标记点
const markerIndex = markers.findIndex((marker: any) =>
marker.type === 'employee' && marker.id === employeeMarker.id
);
let updatedMarkers;
if (markerIndex !== -1) {
// 更新现有的标记点
updatedMarkers = [...markers];
updatedMarkers[markerIndex] = employeeMarker;
} else {
// 添加新的标记点
updatedMarkers = [...markers, employeeMarker];
}
// 更新数据模块中的标记点
this.dataModule.updateMarkers(updatedMarkers);
console.log('单个员工标记点更新完成');
}
/**
* 处理地图点击事件
*/