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.

578 lines
13 KiB

  1. <template>
  2. <view class="timeline-container">
  3. <!-- 日期和状态标签 -->
  4. <view class="date-header">
  5. <view class="date-box">
  6. <view class="date-box-color" :style="{'background-color': getTopBgColor()}"></view>
  7. <view class="date-month-day">{{ formatDate(date).month }}-{{ formatDate(date).day }}</view>
  8. </view>
  9. <!-- <view class="status-tag" :class="{'status-tag-pending': status}">
  10. <image src="/static/images/ydd/icon1.png"
  11. mode="aspectFit"
  12. v-if="status"
  13. class="status-icon"></image>
  14. <image src="/static/images/order/success.png"
  15. mode="aspectFit"
  16. v-else
  17. class="status-icon"></image>
  18. {{ status ? '待上门' : '已完成' }}
  19. </view> -->
  20. </view>
  21. <!-- 空状态显示 -->
  22. <view v-if="!list || list.length === 0" class="empty-state">
  23. <text class="empty-text">暂无订单数据</text>
  24. </view>
  25. <!-- 时间线主体 -->
  26. <view v-else class="timeline-body" v-for="(item, index) in list" :key="index">
  27. <view class="timeline-line"></view>
  28. <!-- <view class="time-point">
  29. <view class="time-icon">
  30. <image src="/static/images/order/address.png" mode="aspectFit" class="time-image"></image>
  31. </view>
  32. <view class="time-text">{{ item.receiverCity }}</view>
  33. <view class="collapse-icon" @click="toggleServiceCard(index)">
  34. {{ serviceCardCollapsed[index] ? '展开' : '收起' }} <text class="arrow" :class="{'arrow-up': !serviceCardCollapsed[index]}"></text>
  35. </view>
  36. </view> -->
  37. <!-- 服务内容卡片 -->
  38. <view v-if="!serviceCardCollapsed[index]" class="service-card">
  39. <!-- 服务日期 -->
  40. <view class="service-section">
  41. <view class="section-title">
  42. <view class="title-indicator"></view>
  43. <text>服务日期</text>
  44. <text style="margin-left: auto;font-weight: 500;font-size: 24rpx;">订单编号{{ item.orderId }}</text>
  45. </view>
  46. <view class="section-content date-content" :class="{bgSuccessQ : item.status == '2'}">
  47. {{ dayjs(item.serviceDate).format('YYYY/MM/DD') }}
  48. </view>
  49. </view>
  50. <!-- 陪伴对象 -->
  51. <view class="service-section">
  52. <view class="section-title">
  53. <view class="title-indicator"></view>
  54. <text>陪伴对象</text>
  55. <view class="collapse-icon" @click="togglePetList(index)">
  56. {{ petListCollapsed[index] ? '展开' : '收起' }} <text class="arrow" :class="{'arrow-up': !petListCollapsed[index]}"></text>
  57. </view>
  58. </view>
  59. <view class="section-content pet-list" :class="{bgSuccessQ : item.status == '2'}" v-if="!petListCollapsed[index]">
  60. <view v-for="(pet, i) in item.pets" :key="i" class="pet-item">
  61. <view class="pet-avatar">
  62. <image :src="pet.photo" mode="aspectFill" class="avatar-image"></image>
  63. </view>
  64. <view class="pet-info">
  65. <view class="pet-name">
  66. {{ pet.name }}
  67. <text class="pet-gender" :class="{'pet-gender-male': pet.gender === 'male', 'pet-gender-female': pet.gender === 'female'}">
  68. {{ pet.gender === 'male' ? '♂' : '♀' }}
  69. </text>
  70. </view>
  71. <view class="pet-description">
  72. {{ pet.breed }}{{ pet.bodyType }} | {{ pet.orderItemList.map(n => n.productName).join(',') }}
  73. </view>
  74. </view>
  75. </view>
  76. </view>
  77. </view>
  78. <!-- 上门地址 -->
  79. <!-- <view class="service-section">
  80. <view class="section-title">
  81. <view class="title-indicator"></view>
  82. <text>上门地址</text>
  83. </view>
  84. <view class="section-content address-content" :class="{bgSuccessQ : item.status == '2'}">
  85. {{ item.receiverDetailAddress }}
  86. </view>
  87. </view> -->
  88. <!-- 操作按钮 -->
  89. <view class="action-buttons">
  90. <view class="btn btn-clock"
  91. v-if="item.status == '2'"
  92. :class="{bgSuccess : item.status == '2'}"
  93. @click="handleClock(item)">查看记录</view>
  94. <view class="btn btn-clock"
  95. v-else
  96. style="background-color: #aaa;color: #fff;"
  97. >暂无记录</view>
  98. </view>
  99. </view>
  100. </view>
  101. </view>
  102. </template>
  103. <script>
  104. import dayjs from '@/utils/lib/dayjs.min.js';
  105. export default {
  106. name: 'TimelineService',
  107. // 定义组件属性
  108. props: {
  109. date: {
  110. type: String,
  111. default: '2024-12-08'
  112. },
  113. orderCount: {
  114. type: Number,
  115. default: 2
  116. },
  117. status: {
  118. type: Boolean,
  119. default: true
  120. },
  121. current: {
  122. type: Number,
  123. default: 0
  124. },
  125. list: {
  126. type: Array,
  127. default: () => []
  128. },
  129. },
  130. data() {
  131. return {
  132. // 宠物列表折叠状态 - 使用数组来单独控制每个卡片中的宠物列表
  133. petListCollapsed: [],
  134. // 服务卡片折叠状态 - 使用数组来单独控制每个卡片
  135. serviceCardCollapsed: []
  136. };
  137. },
  138. //计算属性
  139. computed: {
  140. // 判断list中是否所有项的status都等于2
  141. orderCompleted() {
  142. if (!this.list || this.list.length === 0) {
  143. return false;
  144. }
  145. return !this.list.every(item => item.status == '2');
  146. }
  147. },
  148. methods: {
  149. dayjs,
  150. // 切换宠物列表显示状态
  151. togglePetList(index) {
  152. if (this.petListCollapsed[index] === undefined) {
  153. this.$set(this.petListCollapsed, index, true);
  154. } else {
  155. this.$set(this.petListCollapsed, index, !this.petListCollapsed[index]);
  156. }
  157. },
  158. // 切换服务卡片显示状态
  159. toggleServiceCard(index) {
  160. if (this.serviceCardCollapsed[index] === undefined) {
  161. this.$set(this.serviceCardCollapsed, index, true);
  162. } else {
  163. this.$set(this.serviceCardCollapsed, index, !this.serviceCardCollapsed[index]);
  164. }
  165. },
  166. // 格式化日期
  167. formatDate(dateString) {
  168. const date = new Date(dateString);
  169. return {
  170. day: date.getDate().toString().padStart(2, '0'),
  171. month: (date.getMonth() + 1).toString().padStart(2, '0')
  172. };
  173. },
  174. // 按钮事件处理函数
  175. handleClock(item) {
  176. uni.navigateTo({
  177. url: `/pages/personalCenter/orderDetailImage?id=${item.id}`,
  178. });
  179. },
  180. getTopBgColor() {
  181. return this.orderCompleted ? '#FFAA48' : '#4CD964';
  182. }
  183. }
  184. }
  185. </script>
  186. <style lang="scss" scoped>
  187. .bgSuccess{
  188. background-color: #4CD964 !important;
  189. }
  190. .bgSuccessQ{
  191. background-color: #4CD96422 !important;
  192. }
  193. .timeline-container {
  194. position: relative;
  195. padding: 20rpx;
  196. margin-bottom: 30rpx;
  197. .empty-state {
  198. display: flex;
  199. flex-direction: column;
  200. align-items: center;
  201. justify-content: center;
  202. padding: 80rpx 40rpx;
  203. .empty-image {
  204. width: 200rpx;
  205. height: 200rpx;
  206. margin-bottom: 20rpx;
  207. }
  208. .empty-text {
  209. color: #999;
  210. font-size: 28rpx;
  211. }
  212. }
  213. .date-header {
  214. display: flex;
  215. align-items: center;
  216. margin-bottom: 20rpx;
  217. .date-box {
  218. width: 80rpx;
  219. background-color: #ffffff;
  220. border: 2px solid #333;
  221. border-radius: 0;
  222. display: flex;
  223. flex-direction: column;
  224. justify-content: center;
  225. align-items: center;
  226. margin-right: 20rpx;
  227. box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.05);
  228. border-radius: 14rpx;
  229. .date-box-color{
  230. height: 20rpx;
  231. width: 100%;
  232. border-top-left-radius: 14rpx;
  233. border-top-right-radius: 14rpx;
  234. position: relative;
  235. &::before{
  236. content: '';
  237. display: block;
  238. background-color: #ddd;
  239. width: 100%;
  240. height: 26rpx;
  241. top: 100%;
  242. left: 0;
  243. position: absolute;
  244. }
  245. }
  246. .date-month-day {
  247. position: relative;
  248. font-size: 26rpx;
  249. font-weight: bold;
  250. color: #333;
  251. height: 50rpx;
  252. display: flex;
  253. flex-direction: column;
  254. justify-content: center;
  255. }
  256. }
  257. .status-tag {
  258. background-color: #4CD96422;
  259. color: #4CD964;
  260. border: 4rpx solid #4CD964;
  261. padding: 16rpx 26rpx;
  262. border-radius: 14rpx;
  263. font-size: 26rpx;
  264. display: flex;
  265. align-items: center;
  266. position: relative;
  267. margin-left: 20rpx;
  268. .status-icon {
  269. width: 32rpx;
  270. height: 32rpx;
  271. margin-right: 8rpx;
  272. }
  273. &::after{
  274. content: '';
  275. display: block;
  276. position: absolute;
  277. width: 0;
  278. height: 0;
  279. top: 50%;
  280. transform: translateY(-50%);
  281. left: -16rpx;
  282. border-top: 16rpx solid transparent;
  283. border-bottom: 16rpx solid transparent;
  284. border-right: 16rpx solid #4CD964;
  285. }
  286. &::before{
  287. content: '';
  288. display: block;
  289. position: absolute;
  290. width: 0;
  291. height: 0;
  292. top: 50%;
  293. transform: translateY(-50%);
  294. left: -12rpx;
  295. border-top: 12rpx solid transparent;
  296. border-bottom: 12rpx solid transparent;
  297. border-right: 12rpx solid #4CD96422;
  298. z-index: 1;
  299. }
  300. }
  301. .status-tag-pending {
  302. background-color: #FFAA4822;
  303. color: #FFAA48;
  304. border-color: #FFAA48;
  305. &::after{
  306. border-right-color: #FFAA48;
  307. }
  308. &::before{
  309. border-right-color: #FFAA4822;
  310. }
  311. }
  312. }
  313. .timeline-body {
  314. position: relative;
  315. padding-left: 40rpx;
  316. padding-bottom: 40rpx;
  317. .timeline-line {
  318. position: absolute;
  319. left: 40rpx;
  320. top: 0;
  321. height: 100%;
  322. width: 0;
  323. border-left: 2rpx dashed #707070;
  324. border-left-style: dashed;
  325. border-image: repeating-linear-gradient(to bottom, #707070 0, #707070 8rpx, transparent 8rpx, transparent 20rpx) 1;
  326. z-index: 0;
  327. &::after{
  328. content: '';
  329. display: block;
  330. position: absolute;
  331. width: 8rpx;
  332. height: 8rpx;
  333. background-color: #000;
  334. border: 2rpx solid #707070;
  335. border-radius: 50%;
  336. left: -7rpx;
  337. top: 30rpx;
  338. }
  339. }
  340. .time-point {
  341. display: flex;
  342. align-items: center;
  343. margin-bottom: 20rpx;
  344. position: relative;
  345. z-index: 1;
  346. .time-icon {
  347. width: 60rpx;
  348. height: 60rpx;
  349. background-color: #fff;
  350. border-radius: 50%;
  351. display: flex;
  352. justify-content: center;
  353. align-items: center;
  354. margin-right: 20rpx;
  355. position: relative;
  356. left: 20rpx;
  357. .time-image {
  358. width: 40rpx;
  359. height: 40rpx;
  360. }
  361. }
  362. .time-text {
  363. font-size: 28rpx;
  364. color: #333;
  365. margin-left: 20rpx;
  366. flex: 1;
  367. }
  368. .collapse-icon {
  369. font-size: 24rpx;
  370. color: #999;
  371. padding: 0 20rpx;
  372. .arrow {
  373. transition: transform 0.3s;
  374. display: inline-block;
  375. }
  376. .arrow-up {
  377. transform: rotate(180deg);
  378. }
  379. }
  380. }
  381. .service-card {
  382. background-color: #fff;
  383. border-radius: 12rpx;
  384. padding: 30rpx;
  385. box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
  386. margin-left: 20rpx;
  387. .service-section {
  388. margin-bottom: 30rpx;
  389. .section-title {
  390. display: flex;
  391. align-items: center;
  392. margin-bottom: 15rpx;
  393. .title-indicator {
  394. width: 6rpx;
  395. height: 30rpx;
  396. background-color: #FFAA48;
  397. margin-right: 15rpx;
  398. }
  399. text {
  400. font-size: 28rpx;
  401. color: #333;
  402. font-weight: bold;
  403. }
  404. .collapse-icon {
  405. margin-left: auto;
  406. font-size: 24rpx;
  407. color: #999;
  408. .arrow {
  409. transition: transform 0.3s;
  410. display: inline-block;
  411. }
  412. .arrow-up {
  413. transform: rotate(180deg);
  414. }
  415. }
  416. }
  417. .section-content {
  418. padding: 0 15rpx;
  419. background-color: #FFF9F0;
  420. }
  421. .date-content {
  422. background-color: #FFF9F0;
  423. padding: 20rpx;
  424. border-radius: 8rpx;
  425. font-size: 28rpx;
  426. color: #333;
  427. }
  428. .pet-list {
  429. padding: 15rpx;
  430. .pet-item {
  431. display: flex;
  432. margin-bottom: 20rpx;
  433. &:last-child {
  434. margin-bottom: 0;
  435. }
  436. .pet-avatar {
  437. width: 80rpx;
  438. height: 80rpx;
  439. border-radius: 50%;
  440. overflow: hidden;
  441. margin-right: 20rpx;
  442. .avatar-image {
  443. width: 100%;
  444. height: 100%;
  445. }
  446. }
  447. .pet-info {
  448. flex: 1;
  449. .pet-name {
  450. font-size: 28rpx;
  451. color: #333;
  452. margin-bottom: 8rpx;
  453. .pet-gender {
  454. display: inline-block;
  455. width: 32rpx;
  456. height: 32rpx;
  457. line-height: 32rpx;
  458. text-align: center;
  459. border-radius: 50%;
  460. color: #fff;
  461. font-size: 20rpx;
  462. margin-left: 10rpx;
  463. }
  464. .pet-gender-male {
  465. background-color: #4A90E2;
  466. }
  467. .pet-gender-female {
  468. background-color: #FF6B9A;
  469. }
  470. }
  471. .pet-description {
  472. font-size: 24rpx;
  473. color: #7D8196;
  474. }
  475. }
  476. }
  477. }
  478. .address-content {
  479. padding: 20rpx;
  480. border-radius: 8rpx;
  481. font-size: 28rpx;
  482. color: #7D8196;
  483. }
  484. }
  485. .action-buttons {
  486. display: flex;
  487. justify-content: space-between;
  488. gap: 20rpx;
  489. .btn {
  490. height: 80rpx;
  491. line-height: 80rpx;
  492. text-align: center;
  493. border-radius: 40rpx;
  494. font-size: 28rpx;
  495. flex: 1;
  496. }
  497. .btn-clock {
  498. background-color: #FFAA48;
  499. color: #fff;
  500. }
  501. .btn-pet-file, .btn-service-file {
  502. background-color: #F6F7FB;
  503. color: #333;
  504. border: 1px solid #E5E6EB;
  505. }
  506. }
  507. }
  508. }
  509. }
  510. </style>