You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

556 lines
11 KiB

  1. <template>
  2. <view class="order-modify-container">
  3. <view class="content">
  4. <!-- 加载状态 -->
  5. <view class="loading-container" v-if="loading">
  6. <view class="loading-circle"></view>
  7. <text class="loading-text">加载中...</text>
  8. </view>
  9. <view v-else>
  10. <view class="info-section modify-instruction-section">
  11. <view class="gradient-header">
  12. <view class="section-title">订单修改说明</view>
  13. </view>
  14. <view class="info-content">
  15. <view class="desc-item">
  16. <view class="desc-icon">🐾</view>
  17. <view class="desc-text">您可以对<text class="highlight">不涉及服务费用变动</text>的订单信息进行修改</view>
  18. </view>
  19. <view class="desc-item">
  20. <view class="desc-icon">🐾</view>
  21. <view class="desc-text">若需修改涉及金额变动的服务信息"增加或删除服务宠物/服务项目"可点击下方按钮<text class="highlight">修改服务</text>来修改或者您也可"取消订单""重新下单"</view>
  22. </view>
  23. </view>
  24. </view>
  25. <view class="info-section service-modify-section">
  26. <view class="section-header">
  27. <view class="accent-bar"></view>
  28. <view class="section-title">服务修改信息</view>
  29. </view>
  30. <view class="info-content">
  31. <view class="info-row editable">
  32. <text class="info-label">联系人</text>
  33. <input class="info-value-input" type="text" v-model="modifyInfo.contactName" placeholder="请输入联系人姓名" />
  34. </view>
  35. <view class="info-row editable">
  36. <text class="info-label">联系方式</text>
  37. <input class="info-value-input" type="text" v-model="modifyInfo.contactPhone" placeholder="请输入联系电话" />
  38. </view>
  39. <view class="info-row clickable">
  40. <text class="info-label">钥匙交接方式</text>
  41. <view class="info-value-container">
  42. <text class="info-value">{{modifyInfo.keyHandoverMethod || '存于快递柜'}}</text>
  43. <text class="arrow-right">></text>
  44. </view>
  45. </view>
  46. </view>
  47. </view>
  48. </view>
  49. </view>
  50. <!-- 底部按钮区域 -->
  51. <view class="order-modify-footer">
  52. <view class="footer-btn modify-service-btn" @click="modifyOrder">
  53. <view class="btn-icon">📝</view>
  54. <text class="btn-text">修改服务</text>
  55. </view>
  56. <view class="footer-btn confirm-modify-btn" @click="confirmModify">
  57. <text class="btn-text">确认修改</text>
  58. </view>
  59. </view>
  60. <!-- 客服组件 -->
  61. <Kefu></Kefu>
  62. </view>
  63. </template>
  64. <script>
  65. import { getOrderDetail, updateOrder } from '@/api/order/order.js';
  66. export default {
  67. components: {
  68. },
  69. data() {
  70. return {
  71. loading: false,
  72. orderId: null,
  73. modifyInfo: {
  74. contactName: '',
  75. contactPhone: '',
  76. paymentMethod: '',
  77. keyHandoverMethod: '',
  78. },
  79. showCancelOrderPopup: false,
  80. originalOrderData: null
  81. }
  82. },
  83. onLoad(options) {
  84. if (options.orderId) {
  85. this.orderId = options.orderId;
  86. this.loadOrderData();
  87. } else {
  88. uni.showToast({
  89. title: '订单ID不存在',
  90. icon: 'none'
  91. });
  92. setTimeout(() => {
  93. uni.navigateBack();
  94. }, 1500);
  95. }
  96. },
  97. methods: {
  98. // 加载订单数据
  99. async loadOrderData() {
  100. if (!this.orderId) return;
  101. this.loading = true;
  102. try {
  103. const res = await getOrderDetail({ orderId: this.orderId });
  104. if (res) {
  105. const orderData = res;
  106. this.originalOrderData = orderData;
  107. // 初始化修改信息
  108. this.modifyInfo.contactName = orderData.receiverName || '';
  109. this.modifyInfo.contactPhone = orderData.receiverPhone || '';
  110. this.modifyInfo.paymentMethod = orderData.paymentMethod || '';
  111. } else {
  112. uni.showToast({
  113. title: res.msg || '获取订单信息失败',
  114. icon: 'none'
  115. });
  116. }
  117. } catch (error) {
  118. console.error('获取订单数据失败', error);
  119. uni.showToast({
  120. title: '网络异常,请稍后重试',
  121. icon: 'none'
  122. });
  123. } finally {
  124. this.loading = false;
  125. }
  126. },
  127. // 确认修改
  128. async confirmModify() {
  129. // 表单验证
  130. if (!this.modifyInfo.contactName.trim()) {
  131. return uni.showToast({
  132. title: '请输入联系人姓名',
  133. icon: 'none'
  134. });
  135. }
  136. if (!this.modifyInfo.contactPhone.trim()) {
  137. return uni.showToast({
  138. title: '请输入联系方式',
  139. icon: 'none'
  140. });
  141. }
  142. // 验证手机号
  143. const phoneReg = /^1[3-9]\d{9}$/;
  144. if (!phoneReg.test(this.modifyInfo.contactPhone)) {
  145. return uni.showToast({
  146. title: '请输入正确的手机号码',
  147. icon: 'none'
  148. });
  149. }
  150. // 检查是否有修改(如果有原始数据的话)
  151. let hasChanged = false;
  152. if (this.originalOrderData) {
  153. hasChanged =
  154. this.modifyInfo.contactName !== this.originalOrderData.contactName ||
  155. this.modifyInfo.contactPhone !== this.originalOrderData.contactPhone ||
  156. this.modifyInfo.paymentMethod !== this.originalOrderData.paymentMethod;
  157. } else {
  158. // 如果没有原始数据,只要有输入内容就认为有修改
  159. hasChanged =
  160. this.modifyInfo.contactName.trim() !== '' ||
  161. this.modifyInfo.contactPhone.trim() !== '';
  162. }
  163. if (!hasChanged) {
  164. return uni.showToast({
  165. title: '未检测到任何修改',
  166. icon: 'none'
  167. });
  168. }
  169. this.loading = true;
  170. try {
  171. const updateData = {
  172. id: this.orderId,
  173. contactName: this.modifyInfo.contactName,
  174. contactPhone: this.modifyInfo.contactPhone,
  175. paymentMethod: this.modifyInfo.paymentMethod,
  176. };
  177. const res = await updateOrder(updateData);
  178. if (res && res.code === 200) {
  179. uni.showToast({
  180. title: '订单修改成功',
  181. icon: 'success'
  182. });
  183. setTimeout(() => {
  184. uni.navigateBack();
  185. }, 1500);
  186. } else {
  187. uni.showToast({
  188. title: res.msg || '订单修改失败',
  189. icon: 'none'
  190. });
  191. }
  192. } catch (error) {
  193. console.error('订单修改失败', error);
  194. uni.showToast({
  195. title: '网络异常,请稍后重试',
  196. icon: 'none'
  197. });
  198. } finally {
  199. this.loading = false;
  200. }
  201. },
  202. // 修改服务(跳转到下单流程)
  203. modifyOrder() {
  204. uni.navigateTo({
  205. url: `/pages/newOrder/serviceNew?orderId=${this.orderId}&isModify=true`
  206. });
  207. },
  208. }
  209. }
  210. </script>
  211. <style lang="scss" scoped>
  212. .order-modify-container {
  213. background-color: #f5f5f5;
  214. min-height: 100vh;
  215. display: flex;
  216. flex-direction: column;
  217. }
  218. .header {
  219. background-color: #FFAA48;
  220. padding: 20rpx 30rpx;
  221. color: #FFFFFF;
  222. .title {
  223. font-size: 36rpx;
  224. font-weight: bold;
  225. }
  226. }
  227. .content {
  228. flex: 1;
  229. padding: 20rpx;
  230. }
  231. .loading-container {
  232. display: flex;
  233. flex-direction: column;
  234. align-items: center;
  235. justify-content: center;
  236. height: 300rpx;
  237. margin-top: 100rpx;
  238. }
  239. .loading-circle {
  240. width: 80rpx;
  241. height: 80rpx;
  242. border: 4rpx solid #FFAA48;
  243. border-top-color: transparent;
  244. border-radius: 50%;
  245. animation: spin 1s linear infinite;
  246. margin-bottom: 20rpx;
  247. }
  248. @keyframes spin {
  249. 0% {
  250. transform: rotate(0deg);
  251. }
  252. 100% {
  253. transform: rotate(360deg);
  254. }
  255. }
  256. .loading-text {
  257. font-size: 28rpx;
  258. color: #666;
  259. }
  260. .info-section {
  261. background-color: #FFFFFF;
  262. border-radius: 20rpx;
  263. margin-bottom: 20rpx;
  264. overflow: hidden;
  265. }
  266. .modify-instruction-section {
  267. background: linear-gradient(135deg, #FFF5E6 0%, #FFFFFF 100%);
  268. border: 1px solid #FFE4B3;
  269. }
  270. .gradient-header {
  271. //background: linear-gradient(135deg, #FFAA48 0%, #FFB366 100%);
  272. padding: 20rpx;
  273. border-radius: 20rpx 20rpx 0 0;
  274. }
  275. .modify-instruction-section .section-title {
  276. //color: #FFFFFF;
  277. border-bottom: none;
  278. padding: 0;
  279. font-size: 32rpx;
  280. font-weight: bold;
  281. }
  282. .service-modify-section {
  283. background-color: #FFFFFF;
  284. }
  285. .section-header {
  286. display: flex;
  287. align-items: center;
  288. padding: 20rpx;
  289. border-bottom: 1px solid #EEEEEE;
  290. }
  291. .accent-bar {
  292. width: 6rpx;
  293. height: 32rpx;
  294. background-color: #FFAA48;
  295. border-radius: 3rpx;
  296. margin-right: 15rpx;
  297. }
  298. .service-modify-section .section-title {
  299. color: #333333;
  300. border-bottom: none;
  301. padding: 0;
  302. font-size: 32rpx;
  303. font-weight: bold;
  304. }
  305. .info-row {
  306. display: flex;
  307. align-items: center;
  308. padding: 20rpx;
  309. border-bottom: 1px solid #F5F5F5;
  310. &:last-child {
  311. border-bottom: none;
  312. }
  313. &.clickable {
  314. cursor: pointer;
  315. }
  316. }
  317. .info-row .info-label {
  318. width: 180rpx;
  319. font-size: 28rpx;
  320. color: #666666;
  321. flex-shrink: 0;
  322. }
  323. .info-row .info-value {
  324. flex: 1;
  325. font-size: 28rpx;
  326. color: #333333;
  327. text-align: right;
  328. }
  329. .info-row.editable {
  330. .info-value-input {
  331. flex: 1;
  332. font-size: 28rpx;
  333. color: #333333;
  334. text-align: right;
  335. border: none;
  336. background: transparent;
  337. padding: 0;
  338. &::placeholder {
  339. color: #CCCCCC;
  340. }
  341. &:focus {
  342. outline: none;
  343. }
  344. }
  345. }
  346. .info-value-container {
  347. display: flex;
  348. align-items: center;
  349. justify-content: flex-end;
  350. flex: 1;
  351. }
  352. .arrow-right {
  353. font-size: 24rpx;
  354. color: #CCCCCC;
  355. margin-left: 10rpx;
  356. }
  357. .section-title {
  358. font-size: 32rpx;
  359. font-weight: bold;
  360. padding: 20rpx;
  361. border-bottom: 1px solid #EEEEEE;
  362. }
  363. .info-content {
  364. padding: 20rpx;
  365. }
  366. .desc-item {
  367. display: flex;
  368. align-items: flex-start;
  369. margin-bottom: 20rpx;
  370. &:last-child {
  371. margin-bottom: 0;
  372. }
  373. }
  374. .desc-icon {
  375. font-size: 24rpx;
  376. margin-right: 15rpx;
  377. margin-top: 4rpx;
  378. color: #FFAA48;
  379. }
  380. .desc-text {
  381. font-size: 28rpx;
  382. color: #666;
  383. line-height: 1.6;
  384. flex: 1;
  385. }
  386. .highlight {
  387. color: #FFAA48;
  388. font-weight: 500;
  389. }
  390. .info-item {
  391. display: flex;
  392. align-items: center;
  393. margin-bottom: 20rpx;
  394. &:last-child {
  395. margin-bottom: 0;
  396. }
  397. .info-label {
  398. width: 180rpx;
  399. font-size: 28rpx;
  400. color: #666;
  401. }
  402. .info-value {
  403. flex: 1;
  404. font-size: 28rpx;
  405. color: #333;
  406. padding: 10rpx;
  407. border: 1px solid #EEEEEE;
  408. border-radius: 8rpx;
  409. }
  410. }
  411. .payment-method {
  412. .payment-value {
  413. display: flex;
  414. align-items: center;
  415. input {
  416. flex: 1;
  417. border: none;
  418. padding: 0;
  419. }
  420. .arrow-right {
  421. font-size: 24rpx;
  422. color: #999;
  423. margin-left: 10rpx;
  424. }
  425. }
  426. }
  427. .reason-input {
  428. width: 100%;
  429. height: 200rpx;
  430. font-size: 28rpx;
  431. padding: 20rpx;
  432. box-sizing: border-box;
  433. border: 1px solid #EEEEEE;
  434. border-radius: 8rpx;
  435. }
  436. .order-modify-footer {
  437. padding: 30rpx 20rpx;
  438. background-color: #FFFFFF;
  439. border-top: 1px solid #EEEEEE;
  440. display: flex;
  441. justify-content: space-between;
  442. align-items: center;
  443. gap: 20rpx;
  444. }
  445. .footer-btn {
  446. display: flex;
  447. flex-direction: column;
  448. align-items: center;
  449. justify-content: center;
  450. padding: 20rpx;
  451. border-radius: 12rpx;
  452. text-align: center;
  453. transition: all 0.3s ease;
  454. &:active {
  455. transform: scale(0.95);
  456. }
  457. }
  458. .modify-service-btn {
  459. background-color: #FFFFFF;
  460. border-radius: 12rpx;
  461. padding: 20rpx 30rpx;
  462. min-width: 120rpx;
  463. .btn-icon {
  464. font-size: 32rpx;
  465. margin-bottom: 8rpx;
  466. }
  467. .btn-text {
  468. font-size: 24rpx;
  469. color: #333333;
  470. font-weight: 500;
  471. }
  472. }
  473. .confirm-modify-btn {
  474. background-color: #FFAA48;
  475. border: none;
  476. border-radius: 50rpx;
  477. padding: 20rpx 60rpx;
  478. flex: 1;
  479. .btn-text {
  480. font-size: 32rpx;
  481. color: #FFFFFF;
  482. font-weight: bold;
  483. }
  484. }
  485. </style>