logic.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. export default {
  2. data() {
  3. return {
  4. currentTab: 0,
  5. tabs: ['待接送/服务', '配送/服务中', '已完成', '已拒绝'],
  6. // Filter dropdown states
  7. typeFilterOptions: ['全部类型', '接送', '喂遛', '洗护'],
  8. currentTypeFilterIdx: 0,
  9. activeDropdown: 0, // 0 = none, 1 = type filter, 2 = time filter
  10. hasTimeFilter: false,
  11. // Custom Calendar State (Mock Feb 2026)
  12. currentMonth: '2026年2月',
  13. weekDays: ['日', '一', '二', '三', '四', '五', '六'],
  14. calendarDays: [], // Generated in onLoad or created
  15. selectedDateRange: [], // stores [start, end]
  16. allOrderList: [
  17. // === Tab 1: 待接送/服务 (Status: 接单) ===
  18. { type: 1, typeText: '接送', typeIcon: '/static/icons/car.svg', statusText: '接单', price: '20.00', timeLabel: '取货时间', time: '2026/02/10 10:00', petAvatar: '/static/dog.png', petName: '哈士奇宝宝', petBreed: '哈士奇', startLocation: '武汉大学宠物店', startAddress: '武汉市洪山区珞喻路458号', startDistance: '0.5km', endLocation: '张** 189****8451', endAddress: '武汉市武昌区新区大道凤凰广场25号', endDistance: '2.5km', remark: '尽快接单。' },
  19. { type: 2, typeText: '喂遛', typeIcon: '/static/icons/walk.svg', statusText: '接单', price: '30.00', timeLabel: '服务时间', time: '2026/02/10 14:00', petAvatar: '/static/dog.png', petName: '边牧', petBreed: '边境牧羊犬', endLocation: '李** 138****0001', endAddress: '武汉市洪山区保利花园', endDistance: '1.5km', serviceContent: '需自带牵引绳。', remark: '狗狗很活泼。' },
  20. { type: 3, typeText: '洗护', typeIcon: '/static/icons/wash.svg', statusText: '接单', price: '60.00', timeLabel: '服务时间', time: '2026/02/10 16:00', petAvatar: '/static/dog.png', petName: '布偶', petBreed: '布偶猫', endLocation: '王** 139****0002', endAddress: '武汉市汉阳区钟家村', endDistance: '3.5km', serviceContent: '上门洗护、全身检查', remark: '猫咪怕水。' },
  21. // === Tab 2: 配送/服务中 (Status: 出发/开始/到达等) ===
  22. { type: 1, typeText: '接送', typeIcon: '/static/icons/car.svg', statusText: '出发', price: '25.00', timeLabel: '取货时间', time: '2026/02/11 09:00', petAvatar: '/static/dog.png', petName: '柯基', petBreed: '柯基犬', startLocation: '南湖宠物店', startAddress: '武汉市洪山区南湖大道', startDistance: '1.0km', endLocation: '陈** 158****0003', endAddress: '武汉市江夏区藏龙岛', endDistance: '6.5km', remark: '已接到狗狗,送往目的地。' },
  23. { type: 2, typeText: '喂遛', typeIcon: '/static/icons/walk.svg', statusText: '开始', price: '35.00', timeLabel: '服务时间', time: '2026/02/11 15:00', petAvatar: '/static/dog.png', petName: '金毛', petBreed: '金毛寻回犬', endLocation: '赵** 159****0004', endAddress: '武汉市江汉区泛海国际', endDistance: '4.5km', serviceContent: '遛弯一小时', remark: '注意避开大型犬。' },
  24. { type: 3, typeText: '洗护', typeIcon: '/static/icons/wash.svg', statusText: '开始', price: '80.00', timeLabel: '服务时间', time: '2026/02/11 18:00', petAvatar: '/static/dog.png', petName: '英短', petBreed: '英国短毛猫', endLocation: '钱** 177****0005', endAddress: '武汉市武昌区楚河汉街', endDistance: '2.0km', serviceContent: '深度去污、洗护', remark: '客户要求修剪指甲。' },
  25. // === Tab 3: 已完成 (Status: 已完成) ===
  26. { type: 1, typeText: '接送', typeIcon: '/static/icons/car.svg', statusText: '已完成', price: '40.00', timeLabel: '取货时间', time: '2026/02/12 10:00', petAvatar: '/static/dog.png', petName: '柴犬', petBreed: '柴犬', startLocation: '汉口宠物医院', startAddress: '武汉市江岸区解放大道', startDistance: '2.0km', endLocation: '孙** 186****0006', endAddress: '武汉市东西湖区金银湖', endDistance: '10.5km', remark: '安全抵达。' },
  27. { type: 2, typeText: '喂遛', typeIcon: '/static/icons/walk.svg', statusText: '已完成', price: '28.00', timeLabel: '服务时间', time: '2026/02/12 16:00', petAvatar: '/static/dog.png', petName: '萨摩耶', petBreed: '萨摩耶犬', endLocation: '周** 188****0007', endAddress: '武汉市洪山区软件园', endDistance: '0.8km', serviceContent: '遛狗', remark: '遛得很开心。' },
  28. { type: 3, typeText: '洗护', typeIcon: '/static/icons/wash.svg', statusText: '已完成', price: '120.00', timeLabel: '服务时间', time: '2026/02/12 19:00', petAvatar: '/static/dog.png', petName: '加菲', petBreed: '加菲猫', endLocation: '吴** 189****0008', endAddress: '武汉市青山区别区', endDistance: '5.0km', serviceContent: '全套美容洗护', remark: '客户非常满意。' },
  29. // === Tab 4: 已拒绝 (Status: 已拒绝) ===
  30. { type: 1, typeText: '接送', typeIcon: '/static/icons/car.svg', statusText: '已拒绝', price: '15.00', timeLabel: '取货时间', time: '2026/02/13 09:00', petAvatar: '/static/dog.png', petName: '吉娃娃', petBreed: '吉娃娃', startLocation: '宠物乐园', startAddress: '武汉市黄陂区盘龙城', startDistance: '3.0km', endLocation: '郑** 133****0009', endAddress: '武汉市汉南区', endDistance: '20.5km', remark: '路程太远。' },
  31. { type: 2, typeText: '喂遛', typeIcon: '/static/icons/walk.svg', statusText: '已拒绝', price: '22.00', timeLabel: '服务时间', time: '2026/02/13 14:00', petAvatar: '/static/dog.png', petName: '博美', petBreed: '博美犬', endLocation: '刘** 134****0010', endAddress: '武汉市蔡甸区', endDistance: '15.0km', serviceContent: '公园遛弯', remark: '天气原因取消。' },
  32. { type: 3, typeText: '洗护', typeIcon: '/static/icons/wash.svg', statusText: '已拒绝', price: '45.00', timeLabel: '服务时间', time: '2026/02/13 17:00', petAvatar: '/static/dog.png', petName: '暹罗', petBreed: '暹罗猫', endLocation: '张** 135****0011', endAddress: '武汉市新洲区', endDistance: '18.0km', serviceContent: '洗澡', remark: '时间冲突。' }
  33. ],
  34. showPetModal: false,
  35. currentPetInfo: {},
  36. showNavModal: false,
  37. navTargetItem: null,
  38. navTargetPointType: '',
  39. // Popover control
  40. activeCallItem: null,
  41. // 备注输入弹窗
  42. showRemarkInput: false,
  43. remarkText: ''
  44. }
  45. },
  46. created() {
  47. this.initCalendar();
  48. },
  49. computed: {
  50. orderList() {
  51. return this.allOrderList.filter(item => {
  52. // 1. Tab Status Filter
  53. // 待接送/服务: [接送]的接单, [喂遛/洗护]的接单
  54. // 配送/服务中: [接送]的到达、出发、送达; [喂遛/洗护]的到达、开始、结束
  55. // 已完成: 完成
  56. let matchTab = false;
  57. if (this.currentTab === 0) {
  58. matchTab = item.statusText === '接单';
  59. } else if (this.currentTab === 1) {
  60. let pickingTypes = ['到达', '出发', '送达'];
  61. let serviceTypes = ['到达', '开始', '结束'];
  62. if (item.type === 1) {
  63. matchTab = pickingTypes.includes(item.statusText);
  64. } else {
  65. matchTab = serviceTypes.includes(item.statusText);
  66. }
  67. } else if (this.currentTab === 2) {
  68. matchTab = item.statusText === '已完成';
  69. } else if (this.currentTab === 3) {
  70. matchTab = item.statusText === '已拒绝';
  71. }
  72. // 2. Type Filter
  73. let matchType = true;
  74. if (this.currentTypeFilterIdx > 0) {
  75. matchType = item.type === parseInt(this.currentTypeFilterIdx);
  76. }
  77. // 3. Time Filter (Mocked)
  78. let matchTime = true; // For now treat all equally unless parsed
  79. return matchTab && matchType && matchTime;
  80. });
  81. }
  82. },
  83. methods: {
  84. getDisplayStatus(item) {
  85. if (item.statusText === '已完成') return '已完成';
  86. if (item.statusText === '已拒绝') return '已拒绝';
  87. if (item.statusText === '接单') {
  88. return item.type === 1 ? '待接送' : '待服务';
  89. }
  90. // For other active states (出发, 到达, 送达, 开始, 结束)
  91. return item.type === 1 ? '配送中' : '服务中';
  92. },
  93. getStatusClass(item) {
  94. let display = this.getDisplayStatus(item);
  95. if (display === '已完成') return 'finish';
  96. if (display === '已拒绝') return 'reject';
  97. if (display === '配送中' || display === '服务中') return 'processing';
  98. return 'highlight';
  99. },
  100. goToDetail(item) {
  101. // 将在 detail 页面实现
  102. uni.navigateTo({
  103. url: `/pages/orders/detail?type=${item.type}`
  104. });
  105. },
  106. showPetProfile(item) {
  107. // 补全 mock 数据
  108. this.currentPetInfo = {
  109. ...item,
  110. petGender: 'M',
  111. petAge: '2岁',
  112. petWeight: '15kg',
  113. petPersonality: '活泼亲人,精力旺盛',
  114. petHobby: '喜欢追飞盘,爱吃肉干',
  115. petRemark: '肠胃较弱,不能乱喂零食;出门易爆冲,请拉紧牵引绳。',
  116. petTags: ['拉响警报', '不能吃鸡肉', '精力旺盛'],
  117. petLogs: [
  118. { date: '2026-02-09 14:00', content: '今天遛弯拉了两次粑粑,精神状态很好。', recorder: '王阿姨' },
  119. { date: '2026-02-08 10:30', content: '有些挑食,剩了小半碗狗粮。', recorder: '李师傅' },
  120. { date: '2026-02-05 09:00', content: '建档。', recorder: '系统记录' }
  121. ]
  122. };
  123. this.showPetModal = true;
  124. },
  125. closePetProfile() {
  126. this.showPetModal = false;
  127. },
  128. openNavigation(item, pointType) {
  129. this.navTargetItem = item;
  130. this.navTargetPointType = pointType;
  131. this.showNavModal = true;
  132. },
  133. closeNavModal() {
  134. this.showNavModal = false;
  135. },
  136. chooseMap(mapType) {
  137. let item = this.navTargetItem;
  138. let pointType = this.navTargetPointType;
  139. let name = pointType === 'start' ? item.startLocation : item.endLocation;
  140. let address = pointType === 'start' ? item.startAddress : item.endAddress;
  141. this.showNavModal = false;
  142. uni.openLocation({
  143. latitude: 30.52, // Mock lat
  144. longitude: 114.31, // Mock lng
  145. name: name || '目的地',
  146. address: address || '默认地址',
  147. success: function () {
  148. console.log('打开导航成功: ' + mapType);
  149. }
  150. });
  151. },
  152. toggleCallMenu(item) {
  153. if (this.activeCallItem === item) {
  154. this.activeCallItem = null;
  155. } else {
  156. this.activeCallItem = item;
  157. }
  158. },
  159. closeCallMenu() {
  160. this.activeCallItem = null;
  161. },
  162. doCall(type) {
  163. let phoneNum = ''
  164. if (type === 'merchant') {
  165. phoneNum = '18900008451' // Mock merchant
  166. } else if (type === 'customer') {
  167. phoneNum = '13800000001' // Mock customer
  168. }
  169. if (phoneNum) {
  170. uni.makePhoneCall({ phoneNumber: phoneNum })
  171. }
  172. this.activeCallItem = null;
  173. },
  174. reportAbnormal(item) {
  175. uni.showToast({ title: '上报功能待开发', icon: 'none' });
  176. },
  177. toggleDropdown(idx) {
  178. // If clicking the currently open one, close it
  179. if (this.activeDropdown === idx) {
  180. this.activeDropdown = 0;
  181. } else {
  182. this.activeDropdown = idx;
  183. }
  184. },
  185. closeDropdown() {
  186. this.activeDropdown = 0;
  187. },
  188. selectType(index) {
  189. this.currentTypeFilterIdx = index;
  190. this.closeDropdown();
  191. },
  192. initCalendar() {
  193. // Mock Feb 2026 (1 is Sunday, 28 is Saturday)
  194. let days = [];
  195. for (let i = 1; i <= 28; i++) {
  196. days.push(i);
  197. }
  198. this.calendarDays = days;
  199. // Pre-select 2, 3, 4 for the demo in Figure 2
  200. this.selectedDateRange = [2, 4];
  201. },
  202. prevMonth() { uni.showToast({ title: '上个月', icon: 'none' }); },
  203. nextMonth() { uni.showToast({ title: '下个月', icon: 'none' }); },
  204. selectDateItem(day) {
  205. // Simple range toggle logic
  206. if (this.selectedDateRange.length === 2) {
  207. this.selectedDateRange = [day]; // Reset to new start
  208. } else if (this.selectedDateRange.length === 1) {
  209. let start = this.selectedDateRange[0];
  210. if (day > start) {
  211. this.selectedDateRange = [start, day];
  212. } else if (day < start) {
  213. this.selectedDateRange = [day, start];
  214. } else {
  215. this.selectedDateRange = []; // clear if same clicked
  216. }
  217. } else {
  218. this.selectedDateRange = [day];
  219. }
  220. },
  221. getDateClass(day) {
  222. if (this.selectedDateRange.length === 0) return '';
  223. if (this.selectedDateRange.length === 1) {
  224. return day === this.selectedDateRange[0] ? 'is-start' : '';
  225. }
  226. let start = this.selectedDateRange[0];
  227. let end = this.selectedDateRange[1];
  228. if (day === start) return 'is-start';
  229. if (day === end) return 'is-end';
  230. if (day > start && day < end) return 'is-between';
  231. return '';
  232. },
  233. resetTimeFilter() {
  234. this.hasTimeFilter = false;
  235. this.selectedDateRange = [];
  236. uni.showToast({ title: '已重置服务时间', icon: 'none' });
  237. this.closeDropdown();
  238. },
  239. confirmTimeFilter() {
  240. if (this.selectedDateRange.length === 0) {
  241. uni.showToast({ title: '请先选择日期', icon: 'none' });
  242. return;
  243. }
  244. let text = this.selectedDateRange.length === 2 ?
  245. `${this.selectedDateRange[0]}日-${this.selectedDateRange[1]}日` :
  246. `${this.selectedDateRange[0]}日`;
  247. uni.showToast({ title: '已应用: ' + text, icon: 'none' });
  248. this.hasTimeFilter = true;
  249. this.closeDropdown();
  250. },
  251. getMainActionText(item) {
  252. if (item.statusText === '接单') {
  253. return '到达打卡'; // Next step
  254. } else if (item.statusText === '到达' && item.type === 1) {
  255. return '确认出发';
  256. } else if (item.statusText === '到达' && item.type !== 1) {
  257. return '开始服务';
  258. } else if (item.statusText === '出发') {
  259. return '送达打卡';
  260. } else if (item.statusText === '开始') {
  261. return '结束服务';
  262. } else if (item.statusText === '送达' || item.statusText === '结束') {
  263. return '服务完成';
  264. }
  265. return '查看详情';
  266. },
  267. mainAction(item) {
  268. // Simply navigate to detail page, where the real action button lives
  269. uni.navigateTo({
  270. url: `/pages/orders/detail?type=${item.type}`
  271. });
  272. },
  273. reportAbnormal(item) {
  274. uni.navigateTo({
  275. url: '/pages/orders/anomaly?orderId=' + (item.orderNo || '')
  276. });
  277. },
  278. // 打开备注输入弹窗
  279. openRemarkInput() {
  280. this.remarkText = '';
  281. this.showRemarkInput = true;
  282. },
  283. // 关闭备注输入弹窗
  284. closeRemarkInput() {
  285. this.showRemarkInput = false;
  286. this.remarkText = '';
  287. },
  288. // 提交备注,追加到宠物档案日志列表
  289. submitRemark() {
  290. const text = this.remarkText.trim();
  291. if (!text) {
  292. uni.showToast({ title: '备注内容不能为空', icon: 'none' });
  293. return;
  294. }
  295. // 获取当前时间
  296. const now = new Date();
  297. const dateStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;
  298. // 追加到当前宠物的日志列表头部
  299. if (!this.currentPetInfo.petLogs) {
  300. this.$set(this.currentPetInfo, 'petLogs', []);
  301. }
  302. this.currentPetInfo.petLogs.unshift({
  303. date: dateStr,
  304. content: text,
  305. recorder: '我'
  306. });
  307. uni.showToast({ title: '备注已添加', icon: 'success' });
  308. this.closeRemarkInput();
  309. }
  310. }
  311. }