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

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