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

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