猫妈狗爸伴宠师小程序前端代码
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.

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