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

506 lines
12 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 === 'pending'}">
  10. <image src="/static/images/ydd/icon1.png" mode="aspectFit" class="status-icon"></image>
  11. {{ status === 'pending' ? '待上门' : '已完成' }}{{ orderCount }}
  12. </view>
  13. </view>
  14. <!-- 时间线主体 -->
  15. <view class="timeline-body" v-for="(item, index) in list">
  16. <view class="timeline-line" :class="{'timeline-line-completed': status === 'completed'}"></view>
  17. <view class="time-point">
  18. <view class="time-icon">
  19. <image src="/static/images/ydd/icon1.png" mode="aspectFit" class="time-image"></image>
  20. </view>
  21. <view class="time-text">{{ item.address }}</view>
  22. </view>
  23. <!-- 服务内容卡片 -->
  24. <view class="service-card">
  25. <!-- 服务日期 -->
  26. <view class="service-section">
  27. <view class="section-title">
  28. <view class="title-indicator"></view>
  29. <text>服务日期</text>
  30. </view>
  31. <view class="section-content date-content">
  32. {{ item.fullDate }}
  33. </view>
  34. </view>
  35. <!-- 陪伴对象 -->
  36. <view class="service-section">
  37. <view class="section-title">
  38. <view class="title-indicator"></view>
  39. <text>陪伴对象</text>
  40. <view class="collapse-icon" @click="togglePetList">
  41. 收起 <text class="arrow" :class="{'arrow-up': !petListCollapsed}"></text>
  42. </view>
  43. </view>
  44. <view class="section-content pet-list" v-if="!petListCollapsed">
  45. <view v-for="(pet, i) in item.petList" :key="i" class="pet-item">
  46. <view class="pet-avatar">
  47. <image :src="pet.avatar || '/static/images/ydd/dog.png'" mode="aspectFill" class="avatar-image"></image>
  48. </view>
  49. <view class="pet-info">
  50. <view class="pet-name">
  51. {{ pet.name }}
  52. <text class="pet-gender" :class="{'pet-gender-male': pet.gender === 'male', 'pet-gender-female': pet.gender === 'female'}">
  53. {{ pet.gender === 'male' ? '♂' : '♀' }}
  54. </text>
  55. </view>
  56. <view class="pet-description">
  57. {{ pet.breed }}{{ pet.bodyType }} | {{ pet.services.join(',') }}
  58. </view>
  59. </view>
  60. </view>
  61. </view>
  62. </view>
  63. <!-- 上门地址 -->
  64. <view class="service-section">
  65. <view class="section-title">
  66. <view class="title-indicator"></view>
  67. <text>上门地址</text>
  68. </view>
  69. <view class="section-content address-content">
  70. {{ item.addressDetail }}
  71. </view>
  72. </view>
  73. <!-- 操作按钮 -->
  74. <view class="action-buttons">
  75. <view class="btn btn-clock" @click="handleClock">打卡</view>
  76. <view class="btn btn-clock" @click="handlePetFile">宠物档案</view>
  77. <view class="btn btn-clock" @click="handleServiceFile">服务档案</view>
  78. </view>
  79. </view>
  80. </view>
  81. </view>
  82. </template>
  83. <script setup>
  84. import { ref, computed } from 'vue';
  85. import { getOrderServiceText, getProductNameText } from '@/utils/serviceTime.js';
  86. // 定义组件属性
  87. const props = defineProps({
  88. date: {
  89. type: String,
  90. default: '2024-12-08'
  91. },
  92. orderCount: {
  93. type: Number,
  94. default: 2
  95. },
  96. status : {
  97. type: String,
  98. default: 'pending'
  99. },
  100. list: {
  101. type: Array,
  102. default: () => [
  103. {
  104. id: 1,
  105. orderId: 1,
  106. address: '广东省深圳市南山区',
  107. addressDetail: '科技南二路111号',
  108. fullDate : '2024-12-08 10:00',
  109. petList : [
  110. {
  111. name: '小汪',
  112. gender: 'male',
  113. breed: '中华田园犬',
  114. bodyType: '(小型犬)',
  115. services: ['专业喂养', '提前熟悉', '陪玩'],
  116. avatar: '/static/images/ydd/dog.png'
  117. },
  118. {
  119. name: 'Billion',
  120. gender: 'female',
  121. breed: '豹猫',
  122. bodyType: '(小型猫)',
  123. services: ['上门喂养', '陪玩'],
  124. avatar: '/static/images/ydd/cat.png'
  125. }
  126. ]
  127. }
  128. ]
  129. }
  130. });
  131. // 宠物列表折叠状态
  132. const petListCollapsed = ref(false);
  133. // 切换宠物列表显示状态
  134. const togglePetList = () => {
  135. petListCollapsed.value = !petListCollapsed.value;
  136. };
  137. // 格式化日期
  138. const formatDate = (dateString) => {
  139. const date = new Date(dateString);
  140. return {
  141. day: date.getDate().toString().padStart(2, '0'),
  142. month: (date.getMonth() + 1).toString().padStart(2, '0')
  143. };
  144. };
  145. // 完整日期显示
  146. const fullDate = computed(() => {
  147. const date = new Date(props.date);
  148. const year = date.getFullYear();
  149. const month = (date.getMonth() + 1).toString().padStart(2, '0');
  150. const day = date.getDate().toString().padStart(2, '0');
  151. return `${year}${month}${day}`;
  152. });
  153. // 按钮事件处理函数
  154. const handleClock = (id) => {
  155. // 打卡功能实现
  156. const paths = [`/otherPages/orderTakingManage/detail/index?id=${id}`,'/otherPages/myOrdersManage/clock/index','/otherPages/myOrdersManage/clock/detail']
  157. uni.navigateTo({
  158. url: paths[props.current]
  159. })
  160. };
  161. const handlePetFile = () => {
  162. // 宠物档案功能实现
  163. uni.navigateTo({
  164. url: "/otherPages/orderTakingManage/pet/index"
  165. })
  166. };
  167. const handleServiceFile = () => {
  168. uni.navigateTo({
  169. url: "/otherPages/myOrdersManage/service/index"
  170. })
  171. };
  172. function getTopBgColor(){
  173. return '#FFAA48'
  174. }
  175. </script>
  176. <style lang="scss" scoped>
  177. .timeline-container {
  178. position: relative;
  179. padding: 20rpx;
  180. margin-bottom: 30rpx;
  181. .date-header {
  182. display: flex;
  183. align-items: center;
  184. margin-bottom: 20rpx;
  185. .date-box {
  186. width: 80rpx;
  187. background-color: #ffffff;
  188. border: 2px solid #333;
  189. border-radius: 0;
  190. display: flex;
  191. flex-direction: column;
  192. justify-content: center;
  193. align-items: center;
  194. margin-right: 20rpx;
  195. box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.05);
  196. border-radius: 14rpx;
  197. .date-box-color{
  198. height: 20rpx;
  199. width: 100%;
  200. border-top-left-radius: 14rpx;
  201. border-top-right-radius: 14rpx;
  202. position: relative;
  203. &::before{
  204. content: '';
  205. display: block;
  206. background-color: #ddd;
  207. width: 100%;
  208. height: 26rpx;
  209. top: 100%;
  210. left: 0;
  211. position: absolute;
  212. }
  213. }
  214. .date-month-day {
  215. position: relative;
  216. font-size: 26rpx;
  217. font-weight: bold;
  218. color: #333;
  219. height: 50rpx;
  220. display: flex;
  221. flex-direction: column;
  222. justify-content: center;
  223. }
  224. }
  225. .status-tag {
  226. background-color: #4CD96422;
  227. color: #4CD964;
  228. border: 4rpx solid #4CD964;
  229. padding: 16rpx 26rpx;
  230. border-radius: 14rpx;
  231. font-size: 26rpx;
  232. display: flex;
  233. align-items: center;
  234. position: relative;
  235. margin-left: 20rpx;
  236. .status-icon {
  237. width: 32rpx;
  238. height: 32rpx;
  239. margin-right: 8rpx;
  240. }
  241. &::after{
  242. content: '';
  243. display: block;
  244. position: absolute;
  245. width: 0;
  246. height: 0;
  247. top: 50%;
  248. transform: translateY(-50%);
  249. left: -16rpx;
  250. border-top: 16rpx solid transparent;
  251. border-bottom: 16rpx solid transparent;
  252. border-right: 16rpx solid #4CD964;
  253. }
  254. &::before{
  255. content: '';
  256. display: block;
  257. position: absolute;
  258. width: 0;
  259. height: 0;
  260. top: 50%;
  261. transform: translateY(-50%);
  262. left: -12rpx;
  263. border-top: 12rpx solid transparent;
  264. border-bottom: 12rpx solid transparent;
  265. border-right: 12rpx solid #4CD96422;
  266. z-index: 1;
  267. }
  268. }
  269. .status-tag-pending {
  270. background-color: #FFAA4822;
  271. color: #FFAA48;
  272. border-color: #FFAA48;
  273. &::after{
  274. border-right-color: #FFAA48;
  275. }
  276. &::before{
  277. border-right-color: #FFAA4822;
  278. }
  279. }
  280. }
  281. .timeline-body {
  282. position: relative;
  283. padding-left: 40rpx;
  284. .timeline-line {
  285. position: absolute;
  286. left: 40rpx;
  287. top: 0;
  288. height: 100%;
  289. width: 2rpx;
  290. background-color: #FFAA48;
  291. z-index: 0;
  292. }
  293. .timeline-line-completed {
  294. background-color: #4CD964;
  295. }
  296. .time-point {
  297. display: flex;
  298. align-items: center;
  299. margin-bottom: 20rpx;
  300. position: relative;
  301. z-index: 1;
  302. .time-icon {
  303. width: 60rpx;
  304. height: 60rpx;
  305. background-color: #fff;
  306. border-radius: 50%;
  307. display: flex;
  308. justify-content: center;
  309. align-items: center;
  310. margin-right: 20rpx;
  311. position: relative;
  312. left: -20rpx;
  313. .time-image {
  314. width: 40rpx;
  315. height: 40rpx;
  316. }
  317. }
  318. .time-text {
  319. font-size: 28rpx;
  320. color: #333;
  321. }
  322. }
  323. .service-card {
  324. background-color: #fff;
  325. border-radius: 12rpx;
  326. padding: 30rpx;
  327. box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
  328. margin-left: 20rpx;
  329. .service-section {
  330. margin-bottom: 30rpx;
  331. .section-title {
  332. display: flex;
  333. align-items: center;
  334. margin-bottom: 15rpx;
  335. .title-indicator {
  336. width: 6rpx;
  337. height: 30rpx;
  338. background-color: #FFAA48;
  339. margin-right: 15rpx;
  340. }
  341. text {
  342. font-size: 28rpx;
  343. color: #333;
  344. font-weight: bold;
  345. }
  346. .collapse-icon {
  347. margin-left: auto;
  348. font-size: 24rpx;
  349. color: #999;
  350. .arrow {
  351. transition: transform 0.3s;
  352. display: inline-block;
  353. }
  354. .arrow-up {
  355. transform: rotate(180deg);
  356. }
  357. }
  358. }
  359. .section-content {
  360. padding: 0 15rpx;
  361. background-color: #FFF9F0;
  362. }
  363. .date-content {
  364. background-color: #FFF9F0;
  365. padding: 20rpx;
  366. border-radius: 8rpx;
  367. font-size: 28rpx;
  368. color: #333;
  369. }
  370. .pet-list {
  371. padding: 15rpx;
  372. .pet-item {
  373. display: flex;
  374. margin-bottom: 20rpx;
  375. &:last-child {
  376. margin-bottom: 0;
  377. }
  378. .pet-avatar {
  379. width: 80rpx;
  380. height: 80rpx;
  381. border-radius: 50%;
  382. overflow: hidden;
  383. margin-right: 20rpx;
  384. .avatar-image {
  385. width: 100%;
  386. height: 100%;
  387. }
  388. }
  389. .pet-info {
  390. flex: 1;
  391. .pet-name {
  392. font-size: 28rpx;
  393. color: #333;
  394. margin-bottom: 8rpx;
  395. .pet-gender {
  396. display: inline-block;
  397. width: 32rpx;
  398. height: 32rpx;
  399. line-height: 32rpx;
  400. text-align: center;
  401. border-radius: 50%;
  402. color: #fff;
  403. font-size: 20rpx;
  404. margin-left: 10rpx;
  405. }
  406. .pet-gender-male {
  407. background-color: #4A90E2;
  408. }
  409. .pet-gender-female {
  410. background-color: #FF6B9A;
  411. }
  412. }
  413. .pet-description {
  414. font-size: 24rpx;
  415. color: #7D8196;
  416. }
  417. }
  418. }
  419. }
  420. .address-content {
  421. padding: 20rpx;
  422. border-radius: 8rpx;
  423. font-size: 28rpx;
  424. color: #7D8196;
  425. }
  426. }
  427. .action-buttons {
  428. display: flex;
  429. justify-content: space-between;
  430. .btn {
  431. width: 30%;
  432. height: 80rpx;
  433. line-height: 80rpx;
  434. text-align: center;
  435. border-radius: 40rpx;
  436. font-size: 28rpx;
  437. }
  438. .btn-clock {
  439. background-color: #FFAA48;
  440. color: #fff;
  441. }
  442. .btn-pet-file, .btn-service-file {
  443. background-color: #F6F7FB;
  444. color: #333;
  445. border: 1px solid #E5E6EB;
  446. }
  447. }
  448. }
  449. }
  450. }
  451. </style>