other-detail.ts 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. import DictionariesBehavior from "../../../../core/behavior/dictionaries.behavior";
  2. import PageContainerBehavior from "../../../../core/behavior/page-container.behavior";
  3. import { handleWeChatPayment } from "../../../../utils/util";
  4. import {
  5. getTickleContext,
  6. } from "../../../../core/behavior/tickle.behavior";
  7. import { getOrderDetailMethod, orderPayMethod, applyAfterSaleMethod } from "../../request";
  8. // module/order/pages/select-goods/other-detail.ts
  9. Page({
  10. behaviors: [PageContainerBehavior, DictionariesBehavior],
  11. data: {
  12. title: "",
  13. id: "",
  14. totalGoodsCount: 0,
  15. goodsList: [] as Array<{
  16. category: string;
  17. goods: Array<{
  18. id: string;
  19. name: string;
  20. description?: string;
  21. image: string;
  22. price: number;
  23. quantity: number;
  24. status?: string;
  25. statusClass?: string;
  26. }>;
  27. }>,
  28. // 三个分类的商品列表
  29. sellTypeFirstItems: [] as Array<any>, // 实体商品(快递类型在上,线下取货类型在下)
  30. sellTypeSecondItems: [] as Array<any>, // 线下服务
  31. sellTypeThirdItems: [] as Array<any>, // 线上权益
  32. selectAll: true,
  33. selectedCount: 0,
  34. totalPrice: 0,
  35. orderDetail: {},
  36. remarkLength: 0,
  37. showDetail: false,
  38. isPaymentLoading: false,
  39. orderStatus: "", // 订单状态:pending, paid, closed, completed
  40. // 售后半屏弹窗(组件 after-sale-type-popup)
  41. afterSalePopupVisible: false,
  42. refundReasonPopupVisible: false,
  43. refundConfirmPopupVisible: false,
  44. refundProofPopupVisible: false,
  45. refundSubmitSuccessPopupVisible: false,
  46. selectedAfterSaleType: "",
  47. selectedRefundReason: "",
  48. refundStatus: "",
  49. refundMaxAmount: 0,
  50. refundAmount: 0,
  51. refundDesc: "",
  52. refundProofImages: [] as string[],
  53. afterSaleSiteOption: {
  54. name: "",
  55. price: 0,
  56. meta1: "",
  57. meta2: "",
  58. image: "",
  59. patientConditioningProgramId: "",
  60. patientConditioningRecordId: "",
  61. sellType: "",
  62. },
  63. },
  64. onLoad(options: any) {
  65. const remark = (this.data.orderDetail as any)?.remark || '';
  66. this.setData({
  67. remarkLength: remark.length
  68. });
  69. if (options.id) {
  70. this.setData({ id: options.id });
  71. }
  72. if (options.status) {
  73. this.statusType(options.status);
  74. }
  75. },
  76. onShow() {
  77. if (this.data.id) {
  78. this.load(this.data.id);
  79. }
  80. },
  81. observers: {},
  82. statusType(status: any) {
  83. const code = status == null ? "" : String(status);
  84. switch (code) {
  85. case "0":
  86. this.setData({ orderStatus: "pending", title: "待付款" });
  87. break;
  88. case "6":
  89. this.setData({ orderStatus: "received", title: "已付款" });
  90. break;
  91. case "2":
  92. this.setData({ orderStatus: "closed", title: "交易关闭" });
  93. break;
  94. case "345":
  95. this.setData({ orderStatus: "completed", title: "交易成功" });
  96. break;
  97. default:
  98. this.setData({ orderStatus: "", title: "" });
  99. break;
  100. }
  101. },
  102. // 查看物流详情
  103. goLogistics(e: any) {
  104. const { id } = e.currentTarget.dataset.goods;
  105. const { liaison, phone, provinceName, cityName, areaName, detailAddress } = this.data.orderDetail;
  106. let address = `${provinceName}${cityName ? `${cityName}` : ""
  107. }${areaName ? `${areaName}` : ""}${detailAddress ? `${detailAddress}` : ""
  108. }`;
  109. const goods = JSON.stringify({ ...e.currentTarget.dataset.goods, liaison, phone, address });
  110. if (id) {
  111. wx.navigateTo({
  112. url: `/module/order/pages/logistics-detail/logistics-detail?id=${id}&goods=${goods}`,
  113. });
  114. } else {
  115. wx.showToast({
  116. title: "无参数id",
  117. icon: "none",
  118. })
  119. }
  120. },
  121. // 订单详情
  122. async load(id: string) {
  123. wx.showLoading({ title: "加载中" });
  124. try {
  125. const res = await getOrderDetailMethod(id);
  126. if (res && res.data) {
  127. this.setData({ orderDetail: res.data });
  128. if (
  129. !res.data.liaison ||
  130. !res.data.phone ||
  131. !res.data.provinceName ||
  132. !res.data.cityName ||
  133. !res.data.areaName ||
  134. !res.data.detailAddress
  135. ) {
  136. this.setData({
  137. showDetail: true,
  138. });
  139. } else {
  140. this.setData({
  141. showDetail: false,
  142. });
  143. this.setData({
  144. name: res.data.liaison ? `${res.data.liaison}` : "",
  145. phone: res.data.phone ? `${res.data.phone}` : "",
  146. address: `${res.data.provinceName}${res.data.cityName ? `${res.data.cityName}` : ""
  147. }${res.data.areaName ? `${res.data.areaName}` : ""}${res.data.detailAddress ? `${res.data.detailAddress}` : ""
  148. }`,
  149. });
  150. }
  151. // 0:待付款
  152. // 2 交易关闭
  153. // 6 待收货
  154. // 345 交易成功
  155. this.statusType(res.data.orderStatus);
  156. // 处理三个分类的商品数据
  157. const processItems = (items: any[]) => {
  158. if (!items || !Array.isArray(items)) {
  159. return [];
  160. }
  161. return items.map((item: any, index: number) => {
  162. // 0是一口价 1按穴位/经络次数计费
  163. const pricingType = item?.conditioningProgramDetail?.pricingType;
  164. return {
  165. id: item.id || '', //患者调理方案id
  166. patientConditioningRecordId: item?.patientConditioningRecordId || '',//调理记录id
  167. name: item.conditioningProgramName || '',
  168. description: (() => {
  169. const convertDose = item?.conditioningProgramDetail?.cpFixedPricingRule?.convertDose
  170. const dose = convertDose ?? '';
  171. const unit = item?.conditioningProgramDetail?.cpFixedPricingRule?.convertUnit ?? '次';
  172. return pricingType === '0' ? `${dose} ${unit}` : `1次`;
  173. })(),
  174. sellType: item?.sellType || '', //商品类型 1-实体商品 2-线下服务 3-线上权益
  175. //start实体商品所用到的字段
  176. receiptStatus: item?.receiptStatus || '', //收货状态 0-待发货 1-已发货 2-已收货 这个字段用于实体商品的收获状态
  177. receiptTime: item?.receiptTime || '', //收货时间
  178. receiptType: item?.receiptType || '', //收货类型 0-快递 1-线下取货
  179. expressType: item?.expressType || '', //快递类型 0-邮政速递 1-顺丰速运 2-京东快递 3-中通快递 4-圆通速递 5-申通快递 6-韵达快递 7-极兔速递
  180. expressNo: item?.expressNo || '', //快递单号
  181. expressTypeName: item?.expressType, //快递公司名称
  182. // expressTypeIcon: this.getExpressTypeIcon(item?.expressType), //快递公司图标
  183. //end实体商品所用到的字段
  184. progress: item?.progress || '', //进度 0-进行中 1-已完成 2-未开始 3-已取消 这个字段用于线下服务商品以及线上权益商品的进度状态
  185. image: item.conditioningProgramPhoto || '',
  186. price: item?.unitPrice || 0,
  187. totalPrice: item?.totalPrice || 0,
  188. quantity: item?.totalMeasure || 0,
  189. statusClass: this.getStatusClass(item?.sellType, item?.progress, item?.receiptStatus, item.expressStatus),
  190. statusText: this.getGoodsStatusText(item?.sellType, item?.progress, item?.receiptStatus, item.expressStatus),
  191. isCanEvaluate: item?.isCanEvaluate, //是否可以评价 false-否 true-是
  192. evaluateTime: item?.evaluateTime, //有评价时间就是已评价,是空就是未评价
  193. isShowAftersaleButton: item?.isShowAftersaleButton || false,
  194. aftersaleProgressStr: item?.aftersaleProgressStr || '',
  195. aftersaleProgress: item?.aftersaleProgress,
  196. }
  197. });
  198. };
  199. // 分别处理三个数组
  200. let sellTypeFirstItems = processItems(res.data?.sellTypeFirstItems || []);
  201. const sellTypeSecondItems = processItems(res.data?.sellTypeSecondItems || []);
  202. const sellTypeThirdItems = processItems(res.data?.sellTypeThirdItems || []);
  203. // 对实体商品进行排序:快递类型(receiptType === '0')在上,线下取货类型(receiptType === '1')在下
  204. sellTypeFirstItems = sellTypeFirstItems.sort((a: any, b: any) => {
  205. const aType = a.receiptType || '';
  206. const bType = b.receiptType || '';
  207. if (aType === '0' && bType === '1') {
  208. return -1;
  209. }
  210. if (aType === '1' && bType === '0') {
  211. return 1;
  212. }
  213. return 0;
  214. });
  215. this.setData({
  216. sellTypeFirstItems,
  217. sellTypeSecondItems,
  218. sellTypeThirdItems,
  219. });
  220. }
  221. } catch (error: any) {
  222. wx.showToast({
  223. title: error.errMsg || "获取订单详情失败",
  224. icon: "none",
  225. });
  226. }
  227. wx.hideLoading();
  228. },
  229. // 去预约
  230. goAppointment(e: any) {
  231. const { id } = e.currentTarget.dataset;
  232. // 去非药物治疗页面
  233. wx.navigateTo({
  234. url: `/module/care/pages/offlineTreatment/offlineTreatment?id=${id}`,
  235. });
  236. },
  237. // 立即支付
  238. async onPayment() {
  239. if (this.data.isPaymentLoading) {
  240. return;
  241. }
  242. this.setData({ isPaymentLoading: true });
  243. try {
  244. // 调用支付接口
  245. const payResult = await orderPayMethod(this.data.id, (this.data.orderDetail as any)?.remark);
  246. if (payResult && payResult.data) {
  247. const paymentParams = payResult.data;
  248. handleWeChatPayment(paymentParams, (_res: any) => {
  249. // 支付成功,跳转到成功页面
  250. wx.redirectTo({
  251. url: "/module/article/pages/success-page/success-page?title=订单支付成功",
  252. });
  253. }, (error: any) => {
  254. this.setData({ isPaymentLoading: false });
  255. if (error?.errMsg === 'requestPayment:fail cancel') {
  256. // 支付取消跳到支付订单页面
  257. wx.navigateBack({ delta: 1 });
  258. }
  259. });
  260. } else {
  261. wx.showToast({
  262. title: payResult.errMsg,
  263. icon: "none",
  264. });
  265. }
  266. } catch (error: any) {
  267. wx.showToast({
  268. title: error.errMsg,
  269. icon: "none",
  270. });
  271. } finally {
  272. this.setData({ isPaymentLoading: false });
  273. }
  274. },
  275. // 备注输入处理
  276. onRemarkInput(e: any) {
  277. const value = e.detail.value;
  278. const length = value.length;
  279. // 限制最大长度为200
  280. if (length > 200) {
  281. return;
  282. }
  283. this.setData({
  284. 'orderDetail.remark': value,
  285. remarkLength: length
  286. });
  287. },
  288. preventTap() {
  289. // 仅用于 catchtap 阻止冒泡,避免触发父级 goAppointment
  290. },
  291. onApplyAfterSale(e: any) {
  292. const { goods } = e.currentTarget.dataset;
  293. this.setData({
  294. afterSaleSiteOption: {
  295. image: goods.image || '',
  296. name: goods.name,
  297. price: goods.price,
  298. totalPrice: goods.totalPrice,
  299. meta1: goods.description || '',
  300. meta2: `x${goods.quantity}`,
  301. patientConditioningProgramId: goods.id || '',
  302. patientConditioningRecordId: goods.patientConditioningRecordId || '',
  303. sellType: goods.sellType || '',
  304. },
  305. refundMaxAmount: goods.totalPrice || 0,
  306. refundAmount: goods.totalPrice || 0,
  307. selectedAfterSaleType: "",
  308. selectedRefundReason: "",
  309. refundDesc: "",
  310. refundProofImages: [],
  311. afterSalePopupVisible: true,
  312. });
  313. },
  314. // 点击退款状态,跳转到售后详情页
  315. onAftersaleProgress(e: any) {
  316. const { goods } = e.currentTarget.dataset;
  317. wx.navigateTo({
  318. url: `/module/order/pages/refund-processing/refund-processing?id=${goods.id}&recordId=${goods.patientConditioningRecordId || ''}`,
  319. });
  320. },
  321. onAfterSalePopupClose() {
  322. this.setData({ afterSalePopupVisible: false });
  323. },
  324. onAfterSaleNext(e: WechatMiniprogram.CustomEvent<{ selectedKey: string }>) {
  325. const key = e.detail?.selectedKey;
  326. if (!key) {
  327. wx.showToast({ title: "请选择售后类型", icon: "none" });
  328. return;
  329. }
  330. this.setData({
  331. selectedAfterSaleType: key,
  332. afterSalePopupVisible: false,
  333. refundReasonPopupVisible: true,
  334. });
  335. },
  336. onRefundReasonPopupClose() {
  337. this.setData({ refundReasonPopupVisible: false });
  338. },
  339. onRefundReasonNext(e: WechatMiniprogram.CustomEvent<{ reason: string }>) {
  340. const reason = e.detail?.reason;
  341. if (!reason) {
  342. wx.showToast({ title: "请选择退款原因", icon: "none" });
  343. return;
  344. }
  345. this.setData({
  346. selectedRefundReason: reason,
  347. refundReasonPopupVisible: false,
  348. refundConfirmPopupVisible: true,
  349. refundAmount: this.data.refundMaxAmount,
  350. });
  351. },
  352. onRefundConfirmClose() {
  353. this.setData({ refundConfirmPopupVisible: false });
  354. },
  355. onChangeRefundStatus(e: WechatMiniprogram.CustomEvent<{ status: string }>) {
  356. this.setData({ refundStatus: e.detail?.status || "" });
  357. },
  358. onOpenRefundReasonAgain() {
  359. this.setData({ refundReasonPopupVisible: true });
  360. },
  361. onRefundAmountConfirm(e: WechatMiniprogram.CustomEvent<{ amount: number }>) {
  362. const amount = Number(e.detail?.amount || 0);
  363. this.setData({ refundAmount: amount });
  364. },
  365. onOpenRefundProofEditor() {
  366. this.setData({ refundProofPopupVisible: true });
  367. },
  368. onRefundProofPopupClose() {
  369. this.setData({ refundProofPopupVisible: false });
  370. },
  371. onRefundProofConfirm(e: WechatMiniprogram.CustomEvent<{ desc: string; images: string[] }>) {
  372. this.setData({
  373. refundDesc: e.detail?.desc || "",
  374. refundProofImages: e.detail?.images || [],
  375. refundProofPopupVisible: false,
  376. });
  377. },
  378. async onSubmitRefundApply() {
  379. if (!this.data.selectedRefundReason) {
  380. wx.showToast({ title: "请选择退款原因", icon: "none" });
  381. return;
  382. }
  383. if (!this.data.refundProofImages.length && !this.data.refundDesc.trim()) {
  384. wx.showToast({ title: "请上传凭证或填写描述", icon: "none" });
  385. return;
  386. }
  387. wx.showLoading({ title: "提交中" });
  388. const { afterSaleSiteOption, selectedAfterSaleType, refundStatus, selectedRefundReason, refundAmount, refundProofImages, refundDesc } = this.data;
  389. const params = {
  390. patientConditioningRecordId: (afterSaleSiteOption as any).patientConditioningRecordId,
  391. patientConditioningProgramId: (afterSaleSiteOption as any).patientConditioningProgramId,
  392. type: selectedAfterSaleType,
  393. ...((afterSaleSiteOption as any).sellType === '1' ? { receiptStatus: refundStatus } : {}),
  394. reason: selectedRefundReason,
  395. applyAmount: refundAmount,
  396. voucherImgs: refundProofImages,
  397. remark: refundDesc,
  398. };
  399. console.log(params, "参数111")
  400. try {
  401. await applyAfterSaleMethod(params);
  402. this.setData({ refundConfirmPopupVisible: false });
  403. wx.navigateTo({
  404. url: "/module/order/pages/refund-success/refund-success",
  405. });
  406. } catch (error: any) {
  407. wx.showToast({ title: error.errMsg || "提交失败", icon: "none" });
  408. }
  409. wx.hideLoading();
  410. },
  411. onReview(e: WechatMiniprogram.TouchEvent) {
  412. console.log(e.currentTarget.dataset);
  413. const { evaluateTime } = e.currentTarget.dataset.goods;
  414. if (evaluateTime) {
  415. // 单商品评价详情
  416. wx.navigateTo({
  417. url: `/module/order/pages/goods-evaluateDetail/goods-evaluateDetail?goodsInfo=${encodeURIComponent(JSON.stringify(e.currentTarget.dataset.goods))}`,
  418. });
  419. } else {
  420. // 去单商品评价页面
  421. wx.navigateTo({
  422. url: `/module/order/pages/goods-evaluate/goods-evaluate?goodsInfo=${encodeURIComponent(JSON.stringify(e.currentTarget.dataset.goods))}`,
  423. });
  424. }
  425. },
  426. // 复制订单号
  427. copyOrderNo(e: any) {
  428. const orderNo = e.currentTarget.dataset.orderno;
  429. if (!orderNo) {
  430. getTickleContext.call(this).showWarnMessage("订单号为空");
  431. return;
  432. }
  433. wx.setClipboardData({
  434. data: orderNo,
  435. success: () => {
  436. wx.showToast({
  437. title: '已复制',
  438. icon: 'none'
  439. });
  440. }
  441. });
  442. },
  443. // 获取状态类名
  444. // sellType: 商品类型 1-实体商品 2-线下服务 3-线上权益
  445. // progress: 进度状态(用于线下服务和线上权益):0-进行中 1-已完成 2-未开始
  446. // receiptStatus: 收货状态(用于实体商品):0-待发货 1-已发货 2-已收货
  447. getStatusClass(sellType?: string | number, progress?: string | number, receiptStatus?: string | number, expressStatus?: string): string {
  448. const type = String(sellType || '');
  449. // 实体商品 (sellType === 1) 使用 receiptStatus(三种状态)实体商品增加运输中
  450. if (type === "1") {
  451. const status = String(receiptStatus || '');
  452. if (status === "0") {
  453. return "status-pending"; // 待发货
  454. } else if (status === "1") {
  455. return "status-received"; // 已发货
  456. } else if (status === "2") {
  457. return "status-completed"; // 已收货
  458. } else {
  459. return "status-received"
  460. }
  461. }
  462. // 线下服务 (sellType === 2) 和线上权益 (sellType === 3) 使用 progress(三种状态)
  463. else if (type === "2" || type === "3") {
  464. const status = String(progress || '');
  465. if (status === "0") {
  466. return "status-pending"; // 进行中
  467. } else if (status === "1") {
  468. return "status-completed"; // 已完成
  469. } else if (status === "2") {
  470. return "status-closed"; // 未开始
  471. }
  472. }
  473. return "";
  474. },
  475. // 获取商品状态文本
  476. getGoodsStatusText(sellType?: string | number, progress?: string | number, receiptStatus?: string | number, expressStatus?: string): string {
  477. const type = String(sellType || '');
  478. // 实体商品 (sellType === 1) 使用 receiptStatus(三种状态)新增expressStatus运输状态
  479. if (type === "1") {
  480. const status = String(receiptStatus || '');
  481. if (expressStatus) {
  482. return expressStatus
  483. } else {
  484. if (status === "0") {
  485. return "待发货";
  486. } else if (status === "1") {
  487. return "已发货";
  488. } else if (status === "2") {
  489. return "已收货";
  490. }
  491. }
  492. }
  493. // 线下服务 (sellType === 2) 和线上权益 (sellType === 3) 使用 progress(三种状态)
  494. else if (type === "2" || type === "3") {
  495. const status = String(progress || '');
  496. if (status === "0") {
  497. return "进行中";
  498. } else if (status === "1") {
  499. return "已完成";
  500. } else if (status === "2") {
  501. return "未开始";
  502. }
  503. }
  504. return "";
  505. },
  506. // 获取订单状态文本
  507. getStatusText(status: string | number): string {
  508. const statusTextMap: Record<string, string> = {
  509. "0": "待付款",
  510. "6": "已付款",
  511. "2": "交易关闭",
  512. "345": "交易成功",
  513. };
  514. return statusTextMap[String(status)] || "";
  515. },
  516. // 确认收货
  517. onConfirmReceipt(e: any) {
  518. const { patientconditioningrecordid } = e.currentTarget.dataset;
  519. if (patientconditioningrecordid) {
  520. const shippedGoods = this.data.sellTypeFirstItems.filter((item: any) => item.receiptStatus === '1');
  521. if (shippedGoods.length === 0) {
  522. wx.showToast({
  523. title: '没有可确认收货的商品',
  524. icon: 'none'
  525. });
  526. return;
  527. }
  528. // 去确认收货页面
  529. wx.navigateTo({
  530. url: `/module/article/pages/confirm-receiving/confirm-receiving?patientConditioningRecordId=${patientconditioningrecordid as string}&goodsList=${encodeURIComponent(JSON.stringify(shippedGoods))}`,
  531. fail: (err) => {
  532. wx.showToast({
  533. title: err.errMsg || '跳转失败',
  534. icon: 'none'
  535. });
  536. }
  537. });
  538. } else {
  539. wx.showToast({
  540. title: '调理记录id为空',
  541. icon: 'none'
  542. });
  543. }
  544. },
  545. });