detail-logic.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. import { getOrderInfo, getOrderLogs, uploadFile, clockIn } from '@/api/fulfiller'
  2. import { getServiceList } from '@/api/service'
  3. export default {
  4. data() {
  5. return {
  6. orderId: null,
  7. orderType: 1,
  8. orderStatus: 2,
  9. stepsPickup: ['到达打卡', '确认出发', '送达打卡'],
  10. stepsWalkWash: ['到达打卡', '开始服务', '服务结束'],
  11. currentStep: 1,
  12. orderDetail: {
  13. type: 1,
  14. price: '0.00',
  15. timeLabel: '服务时间',
  16. time: '',
  17. petAvatar: '/static/dog.png',
  18. petName: '',
  19. petBreed: '',
  20. serviceTag: '',
  21. startLocation: '',
  22. startAddress: '',
  23. endLocation: '',
  24. endAddress: '',
  25. serviceContent: '',
  26. remark: '',
  27. orderNo: '',
  28. createTime: '',
  29. progressLogs: []
  30. },
  31. serviceList: [],
  32. showPetModal: false,
  33. currentPetInfo: {},
  34. showNavModal: false,
  35. navTargetPointType: '',
  36. showUploadModal: false,
  37. modalMediaList: [],
  38. modalRemark: '',
  39. showSumModal: false,
  40. sumContent: '',
  41. sumDate: '',
  42. sumSigner: '张*哥',
  43. showPetRemarkInput: false,
  44. petRemarkText: ''
  45. }
  46. },
  47. computed: {
  48. steps() {
  49. return this.orderType === 1 ? this.stepsPickup : this.stepsWalkWash;
  50. },
  51. displayStatusText() {
  52. if (this.currentStep === 4) return '待商家确认';
  53. if (this.currentStep >= this.steps.length) return '已完成';
  54. let status = this.steps[this.currentStep];
  55. if (status === '已完成' || status === '完成') return '已完成';
  56. if (status === '已拒绝') return '已拒绝';
  57. if (status === '接单') {
  58. return this.orderType === 1 ? '待接送' : '待服务';
  59. }
  60. // 对于其他活跃状态 (出发, 到达, 送达, 开始, 结束)
  61. return this.orderType === 1 ? '配送中' : '服务中';
  62. },
  63. currentStatusText() {
  64. if (this.currentStep === 4) return '待确认';
  65. return this.currentStep >= this.steps.length ? '已完成' : this.steps[this.currentStep];
  66. },
  67. currentTaskTitle() {
  68. if (this.currentStep === 4) return '待商家确认';
  69. if (this.currentStep >= this.steps.length) return '订单已完成';
  70. let action = this.steps[this.currentStep];
  71. if (action === '到达打卡') return '到达打卡';
  72. if (action === '开始服务') return '开始服务';
  73. if (action === '确认出发') return '确认出发';
  74. if (action === '送达打卡' || action === '服务结束') return '服务完成';
  75. return action;
  76. },
  77. currentTaskDesc() {
  78. if (this.currentStep === 4) return '服务已提交,请等待商家确认完成后即可结束订单';
  79. if (this.currentStep >= this.steps.length) return '感谢您的服务,请注意休息';
  80. let action = this.steps[this.currentStep];
  81. if (action === '到达打卡') {
  82. return '打卡穿着工装消毒站门口的照片或视频';
  83. }
  84. if (this.orderType === 1) {
  85. if (action === '确认出发') return '拍摄宠物上车/出发时的状态照片或视频';
  86. if (action === '送达打卡') return '打卡确认送达的照片或视频';
  87. } else {
  88. if (action === '开始服务') return '开始服务并拍摄照片 or 视频';
  89. if (action === '服务结束') return '服务完成拍摄照片或视频';
  90. }
  91. return '请按要求提交照片或视频及备注';
  92. }
  93. },
  94. async onLoad(options) {
  95. if (options.id) {
  96. this.orderId = options.id
  97. }
  98. await this.loadServiceList()
  99. await this.loadOrderDetail()
  100. },
  101. methods: {
  102. async loadServiceList() {
  103. try {
  104. const res = await getServiceList()
  105. this.serviceList = res.data || []
  106. } catch (err) {
  107. console.error('获取服务类型失败:', err)
  108. }
  109. },
  110. async loadOrderDetail() {
  111. if (!this.orderId) {
  112. console.log('订单ID缺失')
  113. uni.showToast({ title: '订单ID缺失', icon: 'none' })
  114. return
  115. }
  116. try {
  117. console.log('请求订单详情,ID:', this.orderId)
  118. const res = await getOrderInfo(this.orderId)
  119. console.log('订单详情响应:', res)
  120. const order = res.data
  121. if (!order) {
  122. console.log('订单数据为空')
  123. uni.showToast({ title: '订单不存在', icon: 'none' })
  124. return
  125. }
  126. console.log('订单数据:', order)
  127. this.transformOrderData(order)
  128. await this.loadOrderLogs()
  129. } catch (err) {
  130. console.error('获取订单详情失败:', err)
  131. uni.showToast({ title: '加载失败', icon: 'none' })
  132. }
  133. },
  134. async loadOrderLogs() {
  135. try {
  136. const res = await getOrderLogs(this.orderId)
  137. const logs = res.data || []
  138. console.log('订单日志:', logs)
  139. if (logs.length > 0) {
  140. console.log('第一条日志详情:', JSON.stringify(logs[0]))
  141. }
  142. const progressLogs = logs.filter(log => log.logType === 1)
  143. this.orderDetail.progressLogs = progressLogs.map(log => ({
  144. status: log.title || '',
  145. time: log.createTime || '',
  146. medias: log.photoUrls || [],
  147. remark: log.content || ''
  148. }))
  149. // 根据打卡日志的 actionType 确定下一步骤。规则:最新的打卡记录是什么,下一步就往后延。
  150. // 仅筛选打卡类型的日志(logType: 1),并按时间倒序排列,取最新的一条作为当前进度的基准
  151. const validLogs = logs.filter(log => log.logType === 1 && log.actionType !== undefined)
  152. .sort((a, b) => new Date(b.createTime).getTime() - new Date(a.createTime).getTime())
  153. if (validLogs.length > 0) {
  154. const latestLog = validLogs[0]
  155. const actionType = latestLog.actionType
  156. // 如果日志显示待商家确认,则为无需打卡
  157. if (latestLog.actionType >= 7) {
  158. this.currentStep = 4 // 无需打卡状态
  159. } else if (actionType === 7) {
  160. this.currentStep = 3 // 已完成
  161. } else if (actionType === 6) {
  162. this.currentStep = 2 // 当前应进行:送达打卡/服务结束
  163. } else if (actionType === 4) {
  164. this.currentStep = 1 // 当前应进行:确认出发/开始服务
  165. } else if (actionType === 2 || actionType === 3) {
  166. this.currentStep = 0 // 已接单,当前应进行:到达打卡
  167. } else {
  168. this.currentStep = 0
  169. }
  170. } else {
  171. this.currentStep = 0
  172. }
  173. console.log('根据最新日志推算的当前步骤:', this.currentStep)
  174. } catch (err) {
  175. console.error('获取订单日志失败:', err)
  176. }
  177. },
  178. transformOrderData(order) {
  179. const mode = order.mode || 0
  180. const isRoundTrip = mode === 1
  181. this.orderType = isRoundTrip ? 1 : 2
  182. this.orderStatus = order.status || 2
  183. this.orderDetail = {
  184. type: this.orderType,
  185. price: (order.price / 100).toFixed(2),
  186. timeLabel: isRoundTrip ? '取货时间' : '服务时间',
  187. time: order.serviceTime || '',
  188. petAvatar: '/static/dog.png',
  189. petName: order.petName || order.contact || '',
  190. petBreed: order.breed || '',
  191. serviceTag: order.groupPurchasePackageName || '',
  192. startLocation: order.fromAddress || '',
  193. startAddress: order.fromAddress || '',
  194. endLocation: (order.contact || '') + ' ' + (order.contactPhoneNumber || ''),
  195. endAddress: order.toAddress || '',
  196. serviceContent: '',
  197. remark: '',
  198. orderNo: order.code || 'T' + order.id,
  199. createTime: order.serviceTime || '',
  200. progressLogs: [
  201. { status: '您已接单', time: order.serviceTime || '' }
  202. ]
  203. }
  204. },
  205. updateStepByStatus() {
  206. if (this.orderStatus === 2) {
  207. this.currentStep = 0
  208. } else if (this.orderStatus === 3) {
  209. this.currentStep = 1
  210. } else if (this.orderStatus === 5) {
  211. this.currentStep = this.steps.length - 1
  212. } else {
  213. this.currentStep = 0
  214. }
  215. },
  216. showPetProfile() {
  217. // Use orderDetail basic info and mock the rest
  218. this.currentPetInfo = {
  219. ...this.orderDetail,
  220. petGender: 'M',
  221. petAge: '2岁',
  222. petWeight: '15kg',
  223. petPersonality: '活泼亲人,精力旺盛',
  224. petHobby: '喜欢追飞盘,爱吃肉干',
  225. petRemark: '肠胃较弱,不能乱喂零食;出门易爆冲,请拉紧牵引绳。',
  226. petTags: ['拉响警报', '不能吃鸡肉', '精力旺盛'],
  227. petLogs: [
  228. { date: '2026-02-09 14:00', content: '今天遛弯拉了两次粑粑,精神状态很好。', recorder: '王阿姨' },
  229. { date: '2026-02-08 10:30', content: '有些挑食,剩了小半碗狗粮。', recorder: '李师傅' },
  230. { date: '2026-02-05 09:00', content: '建档。', recorder: '系统记录' }
  231. ]
  232. };
  233. this.showPetModal = true;
  234. },
  235. closePetProfile() {
  236. this.showPetModal = false;
  237. },
  238. openPetRemarkInput() {
  239. this.petRemarkText = '';
  240. this.showPetRemarkInput = true;
  241. },
  242. closePetRemarkInput() {
  243. this.showPetRemarkInput = false;
  244. },
  245. submitPetRemark() {
  246. if (!this.petRemarkText.trim()) {
  247. uni.showToast({ title: '备注内容不能为空', icon: 'none' });
  248. return;
  249. }
  250. const now = new Date();
  251. const date = `${now.getFullYear()}/${String(now.getMonth() + 1).padStart(2, '0')}/${String(now.getDate()).padStart(2, '0')}`;
  252. if (!this.currentPetInfo.petLogs) {
  253. this.currentPetInfo.petLogs = [];
  254. }
  255. this.currentPetInfo.petLogs.unshift({
  256. date: date,
  257. content: this.petRemarkText,
  258. recorder: '张*哥'
  259. });
  260. this.closePetRemarkInput();
  261. uni.showToast({ title: '备注已添加', icon: 'success' });
  262. },
  263. goToAnomaly() {
  264. uni.navigateTo({
  265. url: '/pages/orders/anomaly?orderId=' + (this.orderDetail.orderNo || '')
  266. });
  267. },
  268. callPhone() {
  269. uni.makePhoneCall({ phoneNumber: '18900008451' });
  270. },
  271. openNavigation(type) {
  272. this.navTargetPointType = type;
  273. this.showNavModal = true;
  274. },
  275. closeNavModal() {
  276. this.showNavModal = false;
  277. },
  278. chooseMap(mapType) {
  279. let pointType = this.navTargetPointType;
  280. let name = pointType === 'start' ? this.orderDetail.startLocation : this.orderDetail.endLocation;
  281. let address = pointType === 'start' ? this.orderDetail.startAddress : this.orderDetail.endAddress;
  282. this.showNavModal = false;
  283. uni.openLocation({
  284. latitude: 30.52, // Mock lat
  285. longitude: 114.31, // Mock lng
  286. name: name || '目的地',
  287. address: address || '默认地址',
  288. success: function () {
  289. console.log('打开导航成功: ' + mapType);
  290. }
  291. });
  292. },
  293. openUploadModal() {
  294. this.modalMediaList = [];
  295. this.modalRemark = '';
  296. this.showUploadModal = true;
  297. },
  298. closeUploadModal() {
  299. this.showUploadModal = false;
  300. },
  301. handleConfirmUpload() {
  302. console.log('handleConfirmUpload被调用');
  303. this.confirmUploadModal();
  304. },
  305. async chooseModalMedia() {
  306. console.log('chooseModalMedia被调用');
  307. uni.chooseImage({
  308. count: 5 - this.modalMediaList.length,
  309. success: async (res) => {
  310. console.log('选择图片成功,文件路径:', res.tempFilePaths);
  311. uni.showLoading({ title: '上传中...' });
  312. try {
  313. for (const filePath of res.tempFilePaths) {
  314. console.log('上传文件:', filePath);
  315. const uploadRes = await uploadFile(filePath);
  316. console.log('上传响应:', uploadRes);
  317. if (uploadRes.code === 200) {
  318. this.modalMediaList.push({
  319. url: uploadRes.data.url,
  320. ossId: uploadRes.data.ossId,
  321. localPath: filePath
  322. });
  323. console.log('上传成功,url:', uploadRes.data.url);
  324. }
  325. }
  326. uni.hideLoading();
  327. console.log('当前modalMediaList:', this.modalMediaList);
  328. uni.showToast({ title: '上传成功', icon: 'success' });
  329. } catch (err) {
  330. uni.hideLoading();
  331. console.error('上传失败:', err);
  332. uni.showToast({ title: '上传失败', icon: 'none' });
  333. }
  334. },
  335. fail: (err) => {
  336. console.error('选择图片失败:', err);
  337. }
  338. });
  339. },
  340. removeModalMedia(index) {
  341. this.modalMediaList.splice(index, 1);
  342. },
  343. getCurrentTime() {
  344. const now = new Date();
  345. const y = now.getFullYear();
  346. const m = String(now.getMonth() + 1).padStart(2, '0');
  347. const d = String(now.getDate()).padStart(2, '0');
  348. const h = String(now.getHours()).padStart(2, '0');
  349. const min = String(now.getMinutes()).padStart(2, '0');
  350. return `${y}/${m}/${d} ${h}:${min}`;
  351. },
  352. async confirmUploadModal() {
  353. console.log('confirmUploadModal被调用,文件数量:', this.modalMediaList.length);
  354. if (this.modalMediaList.length === 0) {
  355. uni.showToast({ title: '请上传至少一张图片或视频', icon: 'none' });
  356. return;
  357. }
  358. try {
  359. uni.showLoading({ title: '提交中...' });
  360. const uploadedMedias = this.modalMediaList.map(item => item.url);
  361. const ossIds = this.modalMediaList.map(item => item.ossId);
  362. console.log('准备打卡,ossIds:', ossIds);
  363. const currentAction = this.steps[this.currentStep];
  364. let clockInType = 4;
  365. if (currentAction === '到达打卡') {
  366. clockInType = 4;
  367. } else if (currentAction === '确认出发' || currentAction === '开始服务') {
  368. clockInType = 6;
  369. } else if (currentAction === '送达打卡' || currentAction === '服务结束') {
  370. clockInType = 7;
  371. }
  372. const clockInData = {
  373. orderId: this.orderId,
  374. photos: ossIds,
  375. content: this.modalRemark || '',
  376. type: clockInType,
  377. title: this.currentTaskTitle
  378. };
  379. console.log('打卡数据:', clockInData);
  380. await clockIn(clockInData);
  381. uni.hideLoading();
  382. this.closeUploadModal();
  383. uni.showToast({ title: '打卡成功', icon: 'success' });
  384. await this.loadOrderDetail();
  385. } catch (err) {
  386. uni.hideLoading();
  387. console.error('打卡失败:', err);
  388. uni.showToast({ title: '打卡失败,请重试', icon: 'none' });
  389. }
  390. },
  391. copyOrderNo() {
  392. uni.setClipboardData({
  393. data: this.orderDetail.orderNo,
  394. success: () => {
  395. uni.showToast({ title: '复制成功', icon: 'none' });
  396. }
  397. });
  398. },
  399. openSumModal() {
  400. // 初始化日期
  401. const now = new Date();
  402. const y = now.getFullYear();
  403. const m = String(now.getMonth() + 1).padStart(2, '0');
  404. const d = String(now.getDate()).padStart(2, '0');
  405. this.sumDate = `${y}/${m}/${d}`;
  406. // 预设服务内容模板
  407. if (!this.sumContent) {
  408. this.sumContent =
  409. '1. 精神/身体状态:\n' +
  410. '2. 进食/饮水:\n' +
  411. '3. 排泤情况:\n' +
  412. '4. 卫生情况:\n' +
  413. '5. 互动情况:\n' +
  414. '6. 特殊情况/备注:';
  415. }
  416. this.showSumModal = true;
  417. },
  418. closeSumModal() {
  419. this.showSumModal = false;
  420. },
  421. submitSumModal() {
  422. if (!this.sumContent.trim()) {
  423. uni.showToast({ title: '请填写服务内容', icon: 'none' });
  424. return;
  425. }
  426. this.closeSumModal();
  427. uni.showToast({ title: '小结已提交', icon: 'success' });
  428. }
  429. }
  430. }