| New file |
| | |
| | | <template> |
| | | <view class="task-container"> |
| | | <!-- 任务列表头部 --> |
| | | <view class="task-list-section"> |
| | | <view class="task-header"> |
| | | <view class="header-title">任务列表</view> |
| | | <view class="header-actions"> |
| | | <button class="search-toggle-btn" @click="toggleSearch"> |
| | | <uni-icons :type="showSearch ? 'close' : 'search'" size="20"></uni-icons> |
| | | </button> |
| | | <button class="refresh-btn" @click="refreshList"> |
| | | <uni-icons type="refresh" size="20"></uni-icons> |
| | | </button> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 查询条件区域 --> |
| | | <view class="search-section" v-show="showSearch"> |
| | | <view class="search-form"> |
| | | <view class="form-item"> |
| | | <view class="form-label">任务状态</view> |
| | | <picker mode="selector" :range="statusOptions" @change="onStatusChange"> |
| | | <view class="form-input picker-input"> |
| | | {{ selectedStatusText || '全部状态' }} |
| | | <uni-icons type="arrowright" size="16" color="#999"></uni-icons> |
| | | </view> |
| | | </picker> |
| | | </view> |
| | | |
| | | <view class="form-item"> |
| | | <view class="form-label">任务时间</view> |
| | | <view class="date-range"> |
| | | <!-- 使用uni-datetime-picker组件 --> |
| | | <uni-datetime-picker |
| | | v-model="startDate" |
| | | type="date" |
| | | :placeholder="'开始时间'" |
| | | class="date-input" |
| | | /> |
| | | <text class="divider">至</text> |
| | | <!-- 使用uni-datetime-picker组件 --> |
| | | <uni-datetime-picker |
| | | v-model="endDate" |
| | | type="date" |
| | | :placeholder="'结束时间'" |
| | | class="date-input" |
| | | /> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="form-item"> |
| | | <view class="form-label">车牌号</view> |
| | | <input |
| | | class="form-input" |
| | | placeholder="请输入车牌号" |
| | | v-model="searchForm.vehicle" |
| | | /> |
| | | </view> |
| | | |
| | | <view class="form-item"> |
| | | <view class="form-label">任务编号</view> |
| | | <input |
| | | class="form-input" |
| | | placeholder="请输入任务编号" |
| | | v-model="searchForm.taskNo" |
| | | /> |
| | | </view> |
| | | |
| | | <view class="form-actions"> |
| | | <button class="search-btn" @click="handleSearch">查询</button> |
| | | <button class="reset-btn" @click="resetSearch">重置</button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="task-filter"> |
| | | <scroll-view class="filter-tabs" scroll-x="true"> |
| | | <view |
| | | class="filter-item" |
| | | :class="{ active: currentFilter === 'all' }" |
| | | @click="changeFilter('all')" |
| | | > |
| | | 全部 |
| | | </view> |
| | | <view |
| | | class="filter-item" |
| | | :class="{ active: currentFilter === 'pending' }" |
| | | @click="changeFilter('pending')" |
| | | > |
| | | 待处理 |
| | | </view> |
| | | <view |
| | | class="filter-item" |
| | | :class="{ active: currentFilter === 'processing' }" |
| | | @click="changeFilter('processing')" |
| | | > |
| | | 处理中 |
| | | </view> |
| | | <view |
| | | class="filter-item" |
| | | :class="{ active: currentFilter === 'completed' }" |
| | | @click="changeFilter('completed')" |
| | | > |
| | | 已完成 |
| | | </view> |
| | | </scroll-view> |
| | | </view> |
| | | |
| | | <scroll-view class="task-list-scroll" scroll-y="true"> |
| | | <view class="task-list"> |
| | | <view class="task-item" v-for="task in filteredTaskList" :key="task.id"> |
| | | <view class="task-main" @click="viewTaskDetail(task)"> |
| | | <!-- 任务头部:标题和状态标签 --> |
| | | <view class="task-header"> |
| | | <view class="task-title">{{ getTaskTypeText(task.taskType) }} - {{ task.vehicle }}</view> |
| | | <view class="task-status" :class="task.taskStatus === 'PENDING' ? 'status-pending' : task.taskStatus === 'DEPARTING' ? 'status-departing' : task.taskStatus === 'ARRIVED' ? 'status-arrived' : task.taskStatus === 'RETURNING' ? 'status-returning' : task.taskStatus === 'COMPLETED' ? 'status-completed' : task.taskStatus === 'CANCELLED' ? 'status-cancelled' : task.taskStatus === 'IN_PROGRESS' ? 'status-in-progress' : 'status-default'"> |
| | | {{ getStatusText(task.taskStatus) }} |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 任务编号单独一行 --> |
| | | <view class="task-code-row"> |
| | | <text class="task-code">{{ task.taskCode }}</text> |
| | | </view> |
| | | |
| | | <!-- 任务详细信息 --> |
| | | <view class="task-info"> |
| | | <view class="info-row"> |
| | | <view class="info-item"> |
| | | <view class="label">出发地:</view> |
| | | <view class="value">{{ task.startLocation }}</view> |
| | | </view> |
| | | <view class="info-item"> |
| | | <view class="label">目的地:</view> |
| | | <view class="value">{{ task.endLocation }}</view> |
| | | </view> |
| | | </view> |
| | | <view class="info-row"> |
| | | <view class="info-item"> |
| | | <view class="label">出发时间:</view> |
| | | <view class="value">{{ task.startTime }}</view> |
| | | </view> |
| | | <view class="info-item"> |
| | | <view class="label">执行人员:</view> |
| | | <view class="value">{{ task.assignee }}</view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 操作按钮 --> |
| | | <view class="task-actions"> |
| | | <!-- 待处理状态: 显示出发、取消 --> |
| | | <template v-if="task.taskStatus === 'PENDING'"> |
| | | <button |
| | | class="action-btn primary" |
| | | @click="handleTaskAction(task, 'depart')" |
| | | > |
| | | 出发 |
| | | </button> |
| | | <button |
| | | class="action-btn cancel" |
| | | @click="handleTaskAction(task, 'cancel')" |
| | | > |
| | | 取消 |
| | | </button> |
| | | </template> |
| | | |
| | | <!-- 出发中状态: 显示已到达、强制结束 --> |
| | | <template v-else-if="task.taskStatus === 'DEPARTING'"> |
| | | <button |
| | | class="action-btn primary" |
| | | @click="handleTaskAction(task, 'arrive')" |
| | | > |
| | | 已到达 |
| | | </button> |
| | | <button |
| | | class="action-btn cancel" |
| | | @click="handleTaskAction(task, 'forceCancel')" |
| | | > |
| | | 强制结束 |
| | | </button> |
| | | </template> |
| | | |
| | | <!-- 已到达状态: 显示已返程 --> |
| | | <template v-else-if="task.taskStatus === 'ARRIVED'"> |
| | | <button |
| | | class="action-btn primary" |
| | | @click="handleTaskAction(task, 'return')" |
| | | > |
| | | 已返程 |
| | | </button> |
| | | </template> |
| | | |
| | | <!-- 返程中状态: 显示已完成 --> |
| | | <template v-else-if="task.taskStatus === 'RETURNING'"> |
| | | <button |
| | | class="action-btn primary" |
| | | @click="handleTaskAction(task, 'complete')" |
| | | > |
| | | 已完成 |
| | | </button> |
| | | </template> |
| | | |
| | | <!-- 已完成/已取消: 不显示按钮 --> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="no-data" v-if="filteredTaskList.length === 0"> |
| | | <uni-icons type="info" size="40" color="#ccc"></uni-icons> |
| | | <text>暂无任务数据</text> |
| | | </view> |
| | | </view> |
| | | </scroll-view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import uniDatetimePicker from '@/uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue' |
| | | import { listTask, changeTaskStatus } from '@/api/task' |
| | | import { mapState } from 'vuex' |
| | | import { formatDateTime } from '@/utils/common' |
| | | |
| | | export default { |
| | | components: { |
| | | uniDatetimePicker |
| | | }, |
| | | data() { |
| | | return { |
| | | // 控制查询界面显示/隐藏 |
| | | showSearch: false, |
| | | |
| | | // 查询条件 |
| | | searchForm: { |
| | | vehicle: '', |
| | | taskNo: '' |
| | | }, |
| | | statusOptions: ['全部状态', '待处理', '处理中', '已完成'], |
| | | statusValues: ['', 'pending', 'processing', 'completed'], |
| | | selectedStatus: '', |
| | | selectedStatusText: '', |
| | | startDate: '', |
| | | endDate: '', |
| | | currentFilter: 'all', |
| | | |
| | | // 任务列表 |
| | | taskList: [], |
| | | loading: false, |
| | | refreshing: false |
| | | } |
| | | }, |
| | | computed: { |
| | | ...mapState({ |
| | | currentUser: state => state.user |
| | | }), |
| | | filteredTaskList() { |
| | | let filtered = this.taskList; |
| | | |
| | | // 应用筛选器 |
| | | if (this.currentFilter !== 'all') { |
| | | filtered = filtered.filter(task => { |
| | | if (this.currentFilter === 'pending') return task.taskStatus === 'PENDING'; |
| | | if (this.currentFilter === 'processing') return ['DEPARTING', 'ARRIVED', 'RETURNING', 'IN_PROGRESS'].includes(task.taskStatus); |
| | | if (this.currentFilter === 'completed') return task.taskStatus === 'COMPLETED'; |
| | | return true; |
| | | }); |
| | | } |
| | | |
| | | // 应用状态筛选 |
| | | if (this.selectedStatus) { |
| | | const statusMap = { |
| | | 'pending': ['PENDING'], |
| | | 'processing': ['DEPARTING', 'ARRIVED', 'RETURNING', 'IN_PROGRESS'], |
| | | 'completed': ['COMPLETED'] |
| | | } |
| | | const validStatuses = statusMap[this.selectedStatus]; |
| | | if (validStatuses) { |
| | | filtered = filtered.filter(task => validStatuses.includes(task.taskStatus)); |
| | | } |
| | | } |
| | | |
| | | // 应用车牌号筛选 - 搜索assignedVehicles数组中的车辆 |
| | | if (this.searchForm.vehicle) { |
| | | filtered = filtered.filter(task => { |
| | | // 在车辆列表中查找匹配的车牌号 |
| | | if (task.vehicleList && task.vehicleList.length > 0) { |
| | | return task.vehicleList.some(vehicle => |
| | | vehicle.vehicleNo && vehicle.vehicleNo.includes(this.searchForm.vehicle) |
| | | ) |
| | | } |
| | | return false |
| | | }); |
| | | } |
| | | |
| | | // 应用任务编号筛选 - 使用taskCode而不是taskNo |
| | | if (this.searchForm.taskNo) { |
| | | filtered = filtered.filter(task => |
| | | task.taskCode && task.taskCode.includes(this.searchForm.taskNo) |
| | | ); |
| | | } |
| | | |
| | | // 应用时间范围筛选 |
| | | if (this.startDate) { |
| | | filtered = filtered.filter(task => |
| | | task.plannedStartTime && task.plannedStartTime >= this.startDate |
| | | ); |
| | | } |
| | | |
| | | if (this.endDate) { |
| | | // 结束日期加一天,以便包含当天的数据 |
| | | const end = new Date(this.endDate); |
| | | end.setDate(end.getDate() + 1); |
| | | const endDateStr = end.toISOString().split('T')[0]; |
| | | |
| | | filtered = filtered.filter(task => |
| | | task.plannedStartTime && task.plannedStartTime < endDateStr |
| | | ); |
| | | } |
| | | |
| | | return filtered; |
| | | } |
| | | }, |
| | | onLoad() { |
| | | this.loadTaskList() |
| | | }, |
| | | onPullDownRefresh() { |
| | | this.refreshList() |
| | | }, |
| | | methods: { |
| | | // 加载任务列表 |
| | | loadTaskList() { |
| | | this.loading = true |
| | | // 后端会自动获取当前用户信息,实现综合查询 |
| | | // 综合查询:当前用户所在机构任务 + 当前用户创建的任务 + 分配给当前用户的任务 |
| | | const queryParams = { |
| | | pageNum: 1, |
| | | pageSize: 100 |
| | | } |
| | | |
| | | listTask(queryParams).then(response => { |
| | | this.loading = false |
| | | const data = response.data || response.rows || [] |
| | | this.taskList = data.map(task => { |
| | | // 从assignedVehicles数组中获取车辆信息 |
| | | let vehicleInfo = '未分配车辆' |
| | | if (task.assignedVehicles && task.assignedVehicles.length > 0) { |
| | | // 如果有多个车辆,显示第一个,并标注数量 |
| | | const firstVehicle = task.assignedVehicles[0] |
| | | vehicleInfo = firstVehicle.vehicleNo || '未知车牌' |
| | | if (task.assignedVehicles.length > 1) { |
| | | vehicleInfo += ` 等${task.assignedVehicles.length}辆` |
| | | } |
| | | } |
| | | |
| | | return { |
| | | ...task, |
| | | // 格式化显示字段 - 使用后端返回的assignedVehicles数据 |
| | | vehicle: vehicleInfo, |
| | | vehicleList: task.assignedVehicles || [], |
| | | startLocation: this.formatAddress(task.departureAddress || task.startLocation || '未设置'), |
| | | endLocation: this.formatAddress(task.destinationAddress || task.endLocation || '未设置'), |
| | | startTime: task.plannedStartTime ? formatDateTime(task.plannedStartTime, 'YYYY-MM-DD HH:mm') : '未设置', |
| | | assignee: task.assigneeName || '未分配' |
| | | } |
| | | }) |
| | | }).catch(error => { |
| | | this.loading = false |
| | | console.error('加载任务列表失败:', error) |
| | | this.$modal.showToast('加载任务列表失败') |
| | | }) |
| | | }, |
| | | |
| | | // 格式化地址 - 只显示-前面的部分 |
| | | formatAddress(address) { |
| | | if (!address) return '未设置' |
| | | // 如果地址包含-,只返回-前面的部分 |
| | | const dashIndex = address.indexOf('-') |
| | | if (dashIndex > 0) { |
| | | return address.substring(0, dashIndex) |
| | | } |
| | | return address |
| | | }, |
| | | |
| | | // 切换查询界面显示/隐藏 |
| | | toggleSearch() { |
| | | this.showSearch = !this.showSearch; |
| | | }, |
| | | |
| | | // 状态选择 |
| | | onStatusChange(e) { |
| | | this.selectedStatus = this.statusValues[e.detail.value]; |
| | | this.selectedStatusText = this.statusOptions[e.detail.value]; |
| | | }, |
| | | |
| | | // 查询 |
| | | handleSearch() { |
| | | this.loadTaskList() |
| | | this.$modal.showToast('查询成功'); |
| | | // 查询完成后隐藏查询界面 |
| | | this.showSearch = false; |
| | | }, |
| | | |
| | | // 重置查询条件 |
| | | resetSearch() { |
| | | this.selectedStatus = ''; |
| | | this.selectedStatusText = ''; |
| | | this.startDate = ''; |
| | | this.endDate = ''; |
| | | this.searchForm.vehicle = ''; |
| | | this.searchForm.taskNo = ''; |
| | | }, |
| | | |
| | | // 刷新列表 |
| | | refreshList() { |
| | | this.refreshing = true |
| | | this.loadTaskList() |
| | | setTimeout(() => { |
| | | this.refreshing = false |
| | | this.$modal.showToast('列表已刷新'); |
| | | // 停止下拉刷新 |
| | | uni.stopPullDownRefresh() |
| | | }, 1000) |
| | | }, |
| | | |
| | | // 筛选 |
| | | changeFilter(filter) { |
| | | this.currentFilter = filter; |
| | | }, |
| | | |
| | | // 查看任务详情 |
| | | viewTaskDetail(task) { |
| | | // 跳转到任务详情页面 - 修复:使用taskId而不是id |
| | | this.$tab.navigateTo(`/pages/task/detail?id=${task.taskId}`); |
| | | }, |
| | | |
| | | // 处理任务操作 |
| | | handleTaskAction(task, action) { |
| | | switch (action) { |
| | | case 'depart': |
| | | // 出发 -> 状态变为出发中 |
| | | this.$modal.confirm('确定要出发吗?').then(() => { |
| | | this.updateTaskStatus(task.taskId, 'DEPARTING', '任务已出发') |
| | | }).catch(() => {}); |
| | | break; |
| | | |
| | | case 'cancel': |
| | | // 取消 -> 二次确认后状态变为已取消 |
| | | this.$modal.confirm('确定要取消此任务吗?').then(() => { |
| | | this.updateTaskStatus(task.taskId, 'CANCELLED', '任务已取消') |
| | | }).catch(() => {}); |
| | | break; |
| | | |
| | | case 'arrive': |
| | | // 已到达 -> 状态变为已到达 |
| | | this.$modal.confirm('确认已到达目的地?').then(() => { |
| | | this.updateTaskStatus(task.taskId, 'ARRIVED', '已到达目的地') |
| | | }).catch(() => {}); |
| | | break; |
| | | |
| | | case 'forceCancel': |
| | | // 强制结束 -> 状态变为已取消 |
| | | this.$modal.confirm('确定要强制结束此任务吗?').then(() => { |
| | | this.updateTaskStatus(task.taskId, 'CANCELLED', '任务已强制结束') |
| | | }).catch(() => {}); |
| | | break; |
| | | |
| | | case 'return': |
| | | // 已返程 -> 状态变为返程中 |
| | | this.$modal.confirm('确认开始返程?').then(() => { |
| | | this.updateTaskStatus(task.taskId, 'RETURNING', '已开始返程') |
| | | }).catch(() => {}); |
| | | break; |
| | | |
| | | case 'complete': |
| | | // 已完成 -> 状态变为已完成 |
| | | this.$modal.confirm('确认任务已完成?').then(() => { |
| | | this.updateTaskStatus(task.taskId, 'COMPLETED', '任务已完成') |
| | | }).catch(() => {}); |
| | | break; |
| | | } |
| | | }, |
| | | |
| | | // 更新任务状态 |
| | | updateTaskStatus(taskId, status, remark) { |
| | | // 获取GPS位置信息 |
| | | this.getLocationAndUpdateStatus(taskId, status, remark) |
| | | }, |
| | | |
| | | // 获取位置信息并更新状态 |
| | | getLocationAndUpdateStatus(taskId, status, remark) { |
| | | const that = this |
| | | |
| | | // 使用uni.getLocation获取GPS位置 |
| | | uni.getLocation({ |
| | | type: 'gcj02', |
| | | geocode: true, |
| | | altitude: true, |
| | | success: function(res) { |
| | | console.log('GPS定位成功:', res) |
| | | |
| | | const statusData = { |
| | | taskStatus: status, |
| | | remark: remark, |
| | | latitude: res.latitude, |
| | | longitude: res.longitude, |
| | | locationAddress: res.address ? res.address.street || res.address.poiName || '' : '', |
| | | locationProvince: res.address ? res.address.province || '' : '', |
| | | locationCity: res.address ? res.address.city || '' : '', |
| | | locationDistrict: res.address ? res.address.district || '' : '', |
| | | gpsAccuracy: res.accuracy, |
| | | altitude: res.altitude, |
| | | speed: res.speed, |
| | | heading: res.direction || res.heading |
| | | } |
| | | |
| | | changeTaskStatus(taskId, statusData).then(response => { |
| | | that.$modal.showToast('状态更新成功') |
| | | that.loadTaskList() |
| | | }).catch(error => { |
| | | console.error('更新任务状态失败:', error) |
| | | that.$modal.showToast('状态更新失败,请重试') |
| | | }) |
| | | }, |
| | | fail: function(err) { |
| | | console.error('GPS定位失败:', err) |
| | | |
| | | that.$modal.confirm('GPS定位失败,是否继续更新状态?').then(() => { |
| | | const statusData = { |
| | | taskStatus: status, |
| | | remark: remark |
| | | } |
| | | |
| | | changeTaskStatus(taskId, statusData).then(response => { |
| | | that.$modal.showToast('状态更新成功') |
| | | that.loadTaskList() |
| | | }).catch(error => { |
| | | console.error('更新任务状态失败:', error) |
| | | that.$modal.showToast('状态更新失败,请重试') |
| | | }) |
| | | }).catch(() => {}) |
| | | } |
| | | }) |
| | | }, |
| | | |
| | | getStatusText(status) { |
| | | const statusMap = { |
| | | 'PENDING': '待处理', |
| | | 'DEPARTING': '出发中', |
| | | 'ARRIVED': '已到达', |
| | | 'RETURNING': '返程中', |
| | | 'COMPLETED': '已完成', |
| | | 'CANCELLED': '已取消', |
| | | 'IN_PROGRESS': '处理中' // 兼容旧数据 |
| | | } |
| | | return statusMap[status] || '未知' |
| | | }, |
| | | |
| | | // 获取状态样式类 |
| | | getStatusClass(status) { |
| | | const statusClassMap = { |
| | | 'PENDING': 'status-pending', |
| | | 'DEPARTING': 'status-departing', |
| | | 'ARRIVED': 'status-arrived', |
| | | 'RETURNING': 'status-returning', |
| | | 'COMPLETED': 'status-completed', |
| | | 'CANCELLED': 'status-cancelled', |
| | | 'IN_PROGRESS': 'status-in-progress' |
| | | } |
| | | return statusClassMap[status] || 'status-default' |
| | | }, |
| | | |
| | | getTaskTypeText(type) { |
| | | const typeMap = { |
| | | 'MAINTENANCE': '维修保养', |
| | | 'FUEL': '加油', |
| | | 'OTHER': '其他', |
| | | 'EMERGENCY_TRANSFER': '转运任务', |
| | | 'WELFARE': '福祉车' |
| | | } |
| | | return typeMap[type] || '未知类型' |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | .task-container { |
| | | padding: 20rpx; |
| | | background-color: #f5f5f5; |
| | | height: 100vh; |
| | | display: flex; |
| | | flex-direction: column; |
| | | // 隐藏滚动条但保持滚动功能 |
| | | ::-webkit-scrollbar { |
| | | display: none; |
| | | width: 0 !important; |
| | | height: 0 !important; |
| | | background: transparent; |
| | | } |
| | | |
| | | // Firefox滚动条隐藏 |
| | | * { |
| | | scrollbar-width: none; /* Firefox */ |
| | | } |
| | | |
| | | // IE/Edge滚动条隐藏 |
| | | * { |
| | | -ms-overflow-style: none; /* IE 10+ */ |
| | | } |
| | | } |
| | | |
| | | // 任务列表区域 |
| | | .task-list-section { |
| | | flex: 1; |
| | | background-color: white; |
| | | border-radius: 15rpx; |
| | | padding: 30rpx; |
| | | box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .task-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 20rpx 0; |
| | | flex-shrink: 0; // 防止收缩 |
| | | |
| | | .header-title { |
| | | font-size: 36rpx; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .header-actions { |
| | | display: flex; |
| | | |
| | | .search-toggle-btn, .refresh-btn { |
| | | width: 60rpx; |
| | | height: 60rpx; |
| | | border-radius: 50%; |
| | | background-color: #f0f0f0; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | margin-left: 20rpx; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 查询条件区域 |
| | | .search-section { |
| | | background-color: #f9f9f9; |
| | | border-radius: 15rpx; |
| | | padding: 30rpx; |
| | | margin: 20rpx 0; |
| | | box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); |
| | | flex-shrink: 0; // 防止收缩 |
| | | |
| | | .form-item { |
| | | margin-bottom: 30rpx; |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .form-label { |
| | | font-size: 28rpx; |
| | | margin-bottom: 15rpx; |
| | | color: #333; |
| | | } |
| | | |
| | | .form-input { |
| | | height: 70rpx; |
| | | padding: 0 20rpx; |
| | | border: 1rpx solid #eee; |
| | | border-radius: 10rpx; |
| | | font-size: 28rpx; |
| | | |
| | | &.picker-input { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | } |
| | | } |
| | | |
| | | .date-range { |
| | | display: flex; |
| | | align-items: center; |
| | | |
| | | .date-input { |
| | | flex: 1; |
| | | height: 70rpx; |
| | | padding: 0 20rpx; |
| | | border: 1rpx solid #eee; |
| | | border-radius: 10rpx; |
| | | font-size: 28rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .divider { |
| | | margin: 0 20rpx; |
| | | color: #999; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .form-actions { |
| | | display: flex; |
| | | margin-top: 20rpx; |
| | | |
| | | .search-btn, .reset-btn { |
| | | flex: 1; |
| | | height: 70rpx; |
| | | border-radius: 10rpx; |
| | | font-size: 28rpx; |
| | | margin: 0 10rpx; |
| | | } |
| | | |
| | | .search-btn { |
| | | background-color: #007AFF; |
| | | color: white; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .task-filter { |
| | | margin-bottom: 30rpx; |
| | | flex-shrink: 0; // 防止收缩 |
| | | |
| | | .filter-tabs { |
| | | white-space: nowrap; |
| | | padding: 10rpx 0; |
| | | // 隐藏滚动条但保持滚动功能 |
| | | ::-webkit-scrollbar { |
| | | display: none; |
| | | width: 0 !important; |
| | | height: 0 !important; |
| | | background: transparent; |
| | | } |
| | | |
| | | // Firefox滚动条隐藏 |
| | | * { |
| | | scrollbar-width: none; /* Firefox */ |
| | | } |
| | | |
| | | // IE/Edge滚动条隐藏 |
| | | * { |
| | | -ms-overflow-style: none; /* IE 10+ */ |
| | | } |
| | | |
| | | .filter-item { |
| | | display: inline-block; |
| | | padding: 15rpx 30rpx; |
| | | margin-right: 20rpx; |
| | | background-color: #f5f5f5; |
| | | border-radius: 30rpx; |
| | | font-size: 28rpx; |
| | | |
| | | &.active { |
| | | background-color: #007AFF; |
| | | color: white; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .task-list-scroll { |
| | | flex: 1; |
| | | // 隐藏滚动条但保持滚动功能 |
| | | ::-webkit-scrollbar { |
| | | display: none; |
| | | width: 0 !important; |
| | | height: 0 !important; |
| | | background: transparent; |
| | | } |
| | | |
| | | // Firefox滚动条隐藏 |
| | | * { |
| | | scrollbar-width: none; /* Firefox */ |
| | | } |
| | | |
| | | // IE/Edge滚动条隐藏 |
| | | * { |
| | | -ms-overflow-style: none; /* IE 10+ */ |
| | | } |
| | | } |
| | | |
| | | .task-list { |
| | | .task-item { |
| | | background-color: #fafafa; |
| | | border-radius: 15rpx; |
| | | margin-bottom: 30rpx; |
| | | overflow: hidden; |
| | | |
| | | .task-main { |
| | | padding: 30rpx; |
| | | border-bottom: 1rpx solid #f0f0f0; |
| | | |
| | | // 任务头部:标题和状态 |
| | | .task-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: flex-start; |
| | | margin-bottom: 15rpx; |
| | | |
| | | .task-title { |
| | | flex: 1; |
| | | font-size: 32rpx; |
| | | font-weight: bold; |
| | | padding-right: 20rpx; |
| | | line-height: 1.4; |
| | | } |
| | | |
| | | .task-status { |
| | | padding: 8rpx 20rpx; |
| | | border-radius: 30rpx; |
| | | font-size: 24rpx; |
| | | white-space: nowrap; |
| | | flex-shrink: 0; |
| | | |
| | | // 待处理 - 橙色 |
| | | &.status-pending { |
| | | background-color: #fff3e0; |
| | | color: #ff9500; |
| | | } |
| | | |
| | | // 出发中 - 蓝色 |
| | | &.status-departing { |
| | | background-color: #e3f2fd; |
| | | color: #007AFF; |
| | | } |
| | | |
| | | // 已到达 - 紫色 |
| | | &.status-arrived { |
| | | background-color: #f3e5f5; |
| | | color: #9c27b0; |
| | | } |
| | | |
| | | // 返程中 - 青色 |
| | | &.status-returning { |
| | | background-color: #e0f2f1; |
| | | color: #009688; |
| | | } |
| | | |
| | | // 已完成 - 绿色 |
| | | &.status-completed { |
| | | background-color: #e8f5e9; |
| | | color: #34C759; |
| | | } |
| | | |
| | | // 已取消 - 灰色 |
| | | &.status-cancelled { |
| | | background-color: #f5f5f5; |
| | | color: #999; |
| | | } |
| | | |
| | | // 处理中 (兼容旧数据) - 蓝色 |
| | | &.status-in-progress { |
| | | background-color: #e3f2fd; |
| | | color: #007AFF; |
| | | } |
| | | |
| | | // 默认样式 |
| | | &.status-default { |
| | | background-color: #f5f5f5; |
| | | color: #666; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 任务编号单独一行 |
| | | .task-code-row { |
| | | margin-bottom: 15rpx; |
| | | padding: 10rpx 0; |
| | | border-bottom: 1rpx dashed #e0e0e0; |
| | | |
| | | .task-code { |
| | | font-size: 28rpx; |
| | | color: #333; |
| | | font-weight: 500; |
| | | font-family: monospace; |
| | | } |
| | | } |
| | | |
| | | .task-info { |
| | | .info-row { |
| | | display: flex; |
| | | margin-bottom: 15rpx; |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .info-item { |
| | | flex: 1; |
| | | display: flex; |
| | | |
| | | .label { |
| | | font-size: 26rpx; |
| | | color: #666; |
| | | margin-right: 10rpx; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .value { |
| | | font-size: 26rpx; |
| | | flex: 1; |
| | | word-break: break-all; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .task-actions { |
| | | display: flex; |
| | | padding: 20rpx; |
| | | |
| | | .action-btn { |
| | | flex: 1; |
| | | height: 70rpx; |
| | | border-radius: 10rpx; |
| | | font-size: 26rpx; |
| | | margin: 0 5rpx; |
| | | background-color: #f0f0f0; |
| | | color: #333; |
| | | |
| | | &.primary { |
| | | background-color: #007AFF; |
| | | color: white; |
| | | } |
| | | |
| | | &.cancel { |
| | | background-color: #ff3b30; |
| | | color: white; |
| | | } |
| | | |
| | | &.disabled { |
| | | opacity: 0.5; |
| | | } |
| | | |
| | | &:first-child { |
| | | margin-left: 0; |
| | | } |
| | | |
| | | &:last-child { |
| | | margin-right: 0; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .no-data { |
| | | text-align: center; |
| | | padding: 100rpx 0; |
| | | color: #999; |
| | | |
| | | text { |
| | | display: block; |
| | | margin-top: 20rpx; |
| | | } |
| | | } |
| | | } |
| | | </style> |