/** * 司机端任务详情页面 * 职责:展示单个任务完整信息,支持接单、确认揽货(需上传运单号)、确认送达等操作。 */ function mapTaskStatus(status) { var statusMap = { pending: "待接单", accepted: "已接单", picked_up: "已揽货", delivered: "已送达", canceled: "已取消" }; return statusMap[status] || status || "-"; } function getStatusBadge(status) { var map = { pending: "badge-orange", accepted: "badge-blue", picked_up: "badge-blue", delivered: "badge-green", canceled: "badge-red" }; return map[status] || "badge-gray"; } Page({ data: { statusBarHeight: 20, loading: true, message: "", actionLoading: false, taskId: null, taskInfo: null, actionRemark: "", traceList: [], uploadedPhotos: [], waybillList: [], taskAttachments: [], recognizing: false, }, async onLoad(options) { var app = getApp(); this.setData({ statusBarHeight: app.globalData.statusBarHeight, taskId: Number(options.id || 0), }); await this.loadDetail(); }, async loadDetail() { var app = getApp(); this.setData({ loading: true, message: "", uploadedPhotos: [] }); try { var data = await app.request({ url: "/api/driver/tasks/" + this.data.taskId, method: "GET" }); this.setData({ taskInfo: Object.assign({}, data, { statusText: mapTaskStatus(data.status), badgeClass: getStatusBadge(data.status), pickupAddress: data.pickup_address || "-", deliveryAddress: data.delivery_address || "-", pickupContent: data.pickup_content || "-", factoryName: data.factory_name || "-", salesmanName: data.salesman_name || "-", }), }); if (data.order_id) { try { var traceData = await app.request({ url: "/api/logistics/" + data.order_id + "/trace", method: "GET" }); this.setData({ traceList: (traceData.trace_list || []).map(function (item) { item.node_time = item.node_time || item.created_at || "-"; return item; }), }); } catch (e) {} } // Load existing waybills from server if (data.status === "accepted" || data.status === "picked_up" || data.status === "delivered") { await this.loadWaybills(); } // Load existing attachments for completed states if (data.status === "picked_up" || data.status === "delivered") { await this.loadAttachments(); } } catch (error) { this.setData({ message: error.message || "加载任务详情失败" }); } finally { this.setData({ loading: false }); } }, loadWaybills: async function () { var app = getApp(); try { var waybills = await app.request({ url: "/api/driver/tasks/" + this.data.taskId + "/waybills", method: "GET" }); this.setData({ waybillList: waybills || [] }); } catch (e) { // Ignore - waybills may not exist yet } }, loadAttachments: async function () { var app = getApp(); try { var data = await app.request({ url: "/api/files/attachments?biz_type=logistics_task&biz_id=" + this.data.taskId, method: "GET", }); this.setData({ taskAttachments: data || [] }); } catch (e) { this.setData({ taskAttachments: [] }); } }, handleRemarkInput: function (event) { this.setData({ actionRemark: event.detail.value }); }, handleChoosePhoto: function () { var that = this; var remaining = 9 - this.data.uploadedPhotos.length; if (remaining <= 0) { wx.showToast({ title: "最多上传9张", icon: "none" }); return; } wx.chooseImage({ count: remaining, sizeType: ["compressed"], sourceType: ["camera", "album"], success: function (res) { res.tempFilePaths.forEach(function (path) { that.uploadPhotoToServer(path); }); }, }); }, uploadPhotoToServer: function (localPath) { var that = this; var app = getApp(); var token = app.globalData.authToken || ""; wx.showLoading({ title: "上传中..." }); wx.uploadFile({ url: app.globalData.apiBaseUrl + "/api/files/local-upload", filePath: localPath, name: "file", header: { Authorization: "Bearer " + token }, success: function (uploadRes) { wx.hideLoading(); try { var body = JSON.parse(uploadRes.data); if (body.code === 0 && body.data) { var newPhoto = { path: body.data.url, name: body.data.file_name || localPath.split("/").pop(), file_size: body.data.file_size || 0, }; that.setData({ uploadedPhotos: that.data.uploadedPhotos.concat([newPhoto]) }); } else { wx.showToast({ title: body.message || "上传失败", icon: "none" }); } } catch (e) { wx.showToast({ title: "上传结果解析失败", icon: "none" }); } }, fail: function () { wx.hideLoading(); wx.showToast({ title: "网络错误,上传失败", icon: "none" }); }, }); }, handleRemovePhoto: function (event) { var index = event.currentTarget.dataset.index; var photos = this.data.uploadedPhotos.slice(); photos.splice(index, 1); this.setData({ uploadedPhotos: photos }); }, // --- Waybill methods --- handleWaybillPhoto: function () { var that = this; if (this.data.recognizing) return; wx.chooseImage({ count: 1, sizeType: ["compressed"], sourceType: ["camera"], success: function (res) { var filePath = res.tempFilePaths[0]; that.setData({ recognizing: true }); wx.showLoading({ title: "识别中..." }); that.uploadAndRecognize(filePath); }, }); }, uploadAndRecognize: function (filePath) { var that = this; var app = getApp(); var token = app.globalData.authToken || ""; wx.uploadFile({ url: app.globalData.apiBaseUrl + "/api/files/local-upload", filePath: filePath, name: "file", header: { Authorization: "Bearer " + token }, success: function (uploadRes) { try { var body = JSON.parse(uploadRes.data); if (body.code !== 0 || !body.data) { wx.hideLoading(); that.setData({ recognizing: false }); wx.showToast({ title: body.message || "上传失败", icon: "none" }); return; } var imageUrl = body.data.url || body.data; that.callRecognizeWaybill(imageUrl, filePath); } catch (e) { wx.hideLoading(); that.setData({ recognizing: false }); wx.showToast({ title: "上传结果解析失败", icon: "none" }); } }, fail: function () { wx.hideLoading(); that.setData({ recognizing: false }); wx.showToast({ title: "文件上传失败", icon: "none" }); }, }); }, callRecognizeWaybill: function (imageUrl, localPath) { var that = this; var app = getApp(); app.request({ url: "/api/driver/recognize-waybill", method: "POST", data: { image_url: imageUrl }, }).then(function (result) { wx.hideLoading(); that.setData({ recognizing: false }); var waybill = { tracking_number: result.tracking_number || "", express_company: result.express_company || "", express_name: result.express_name || "", image_url: imageUrl, local_path: localPath, confidence: result.confidence || 0, }; var list = that.data.waybillList.slice(); list.push(waybill); that.setData({ waybillList: list }); wx.showToast({ title: "识别成功", icon: "success" }); }).catch(function (err) { wx.hideLoading(); that.setData({ recognizing: false }); wx.showToast({ title: err.message || "识别失败,请手动输入", icon: "none" }); }); }, handleManualWaybill: function () { var that = this; wx.showModal({ title: "手动输入运单号", editable: true, placeholderText: "请输入运单号", confirmText: "添加", confirmColor: "#2563eb", success: function (res) { if (res.confirm && res.content && res.content.trim()) { var trackingNumber = res.content.trim(); var list = that.data.waybillList.slice(); list.push({ tracking_number: trackingNumber, express_company: "", express_name: "", image_url: "", local_path: "", confidence: 0, }); that.setData({ waybillList: list }); } }, }); }, handleRemoveWaybill: function (event) { var index = event.currentTarget.dataset.index; var that = this; var item = this.data.waybillList[index]; // If it has an id, it's saved on server - delete from server too if (item && item.id) { var app = getApp(); app.request({ url: "/api/driver/tasks/" + this.data.taskId + "/waybills/" + item.id, method: "DELETE", }).catch(function () {}); } var list = this.data.waybillList.slice(); list.splice(index, 1); this.setData({ waybillList: list }); }, handleEditWaybill: function (event) { var index = event.currentTarget.dataset.index; var item = this.data.waybillList[index]; if (!item) return; var that = this; wx.showModal({ title: "修改运单号", editable: true, placeholderText: "请输入运单号", content: item.tracking_number || "", confirmText: "保存", confirmColor: "#2563eb", success: function (res) { if (res.confirm && res.content && res.content.trim()) { var list = that.data.waybillList.slice(); list[index] = Object.assign({}, list[index], { tracking_number: res.content.trim(), }); that.setData({ waybillList: list }); } }, }); }, getActionConfig: function () { var status = this.data.taskInfo && this.data.taskInfo.status; var taskId = this.data.taskId; if (status === "pending") { return { text: "接单", url: "/api/driver/tasks/" + taskId + "/accept", successMessage: "接单成功", confirmMsg: "确认接受此任务?", needAttachment: false }; } if (status === "accepted") { return { text: "确认揽货", url: "/api/driver/tasks/" + taskId + "/pickup", successMessage: "已确认揽货", confirmMsg: "确认已揽收货物?请先录入运单号。", needWaybill: true }; } if (status === "picked_up") { return { text: "确认送达", url: "/api/driver/tasks/" + taskId + "/deliver", successMessage: "已确认送达", confirmMsg: "确认货物已送达目的地?", needAttachment: false }; } return null; }, handleAction: function () { var that = this; var action = this.getActionConfig(); if (!action) return; if (action.needWaybill && this.data.waybillList.length === 0) { wx.showToast({ title: "请先录入运单号", icon: "none" }); return; } // Validate all waybills have tracking numbers if (action.needWaybill) { var hasEmpty = this.data.waybillList.some(function (w) { return !w.tracking_number; }); if (hasEmpty) { wx.showToast({ title: "请填写完整的运单号", icon: "none" }); return; } } wx.showModal({ title: "操作确认", content: action.confirmMsg, confirmText: "确认", confirmColor: "#2563eb", success: function (res) { if (res.confirm) { that.executeAction(action); } }, }); }, executeAction: async function (action) { var app = getApp(); this.setData({ actionLoading: true, message: "" }); try { // For accepted status, submit waybills first if (action.needWaybill && this.data.waybillList.length > 0) { var waybillPayload = this.data.waybillList.map(function (w) { return { tracking_number: w.tracking_number, express_company: w.express_company || "", express_name: w.express_name || "", image_url: w.image_url || "", }; }); await app.request({ url: "/api/driver/tasks/" + this.data.taskId + "/waybills", method: "POST", data: { waybills: waybillPayload }, }); } var photoFiles = this.data.uploadedPhotos.map(function (p) { return { file_url: p.path, file_name: p.name }; }); await app.request({ url: action.url, method: "POST", data: { photo_files: photoFiles, video_files: [], remark: this.data.actionRemark || action.successMessage }, }); wx.showToast({ title: action.successMessage, icon: "success" }); this.setData({ actionRemark: "", uploadedPhotos: [], waybillList: [] }); await this.loadDetail(); } catch (error) { wx.showToast({ title: error.message || "操作失败", icon: "none" }); } finally { this.setData({ actionLoading: false }); } }, handleViewTracking: function () { if (this.data.taskInfo && this.data.taskInfo.order_no) { wx.switchTab({ url: "/pages/driver/order-tracking/order-tracking" }); } }, handleBack: function () { wx.navigateBack({ delta: 1 }); }, });