爱简收旧衣按件回收前端代码仓库
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.

765 lines
22 KiB

3 weeks ago
2 weeks ago
3 weeks ago
3 weeks ago
5 days ago
3 weeks ago
5 days ago
3 weeks ago
5 days ago
3 weeks ago
2 weeks ago
3 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
3 weeks ago
5 days ago
3 weeks ago
5 days ago
3 weeks ago
5 days ago
3 weeks ago
5 days ago
3 weeks ago
2 weeks ago
5 days ago
5 days ago
3 weeks ago
5 days ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
5 days ago
3 weeks ago
5 days ago
3 weeks ago
5 days ago
3 weeks ago
5 days ago
5 days ago
3 weeks ago
2 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
  1. <template>
  2. <view class="pickup-container" :style="{paddingTop: navBarHeightRpx + 'rpx'}">
  3. <!-- 顶部导航栏 -->
  4. <view class="nav-bar" :style="{height: (statusBarHeight + 88) + 'rpx', paddingTop: statusBarHeight + 'px'}">
  5. <view class="back" @tap="goBack">
  6. <uni-icons type="left" size="20"></uni-icons>
  7. </view>
  8. <text class="title">免费上门取件预约</text>
  9. </view>
  10. <!-- 内容区域 -->
  11. <view class="content">
  12. <!-- 回收流程卡片 -->
  13. <view class="card process-card">
  14. <view class="card-title process-title">回收流程</view>
  15. <view class="process-steps">
  16. <view
  17. class="process-step-card"
  18. v-for="(step, i) in steps"
  19. :key="i"
  20. >
  21. <image :src="step.icon" class="step-icon" mode="aspectFit" />
  22. <view v-if="i === 0" class="step-bottom-bar">
  23. <view class="step-num-bar">
  24. <text class="text-main">{{ step.text }}</text>
  25. </view>
  26. </view>
  27. <view v-else class="step-label-gray">
  28. <text class="text-gray">{{ step.text }}</text>
  29. </view>
  30. </view>
  31. </view>
  32. <view class="divider"></view>
  33. <!-- 取件信息 -->
  34. <view class="pickup-info">
  35. <view class="info-item" @tap="selectAddress">
  36. <text class="label">取件地址</text>
  37. <view class="value">
  38. <text class="text" :class="{placeholder: !displayAddress}">{{ displayAddress || '请选择' }}</text>
  39. <text class="arrow">></text>
  40. </view>
  41. </view>
  42. <view class="info-item" @tap="openTimePicker">
  43. <text class="label">上门时间</text>
  44. <view class="value">
  45. <text class="text" :class="{placeholder: !selectedTime}">{{ selectedTime || '请选择' }}</text>
  46. <text class="arrow">></text>
  47. </view>
  48. </view>
  49. </view>
  50. </view>
  51. <!-- 订单详情卡片 -->
  52. <view class="card order-card">
  53. <view class="card-title process-title">订单详情</view>
  54. <view class="order-items">
  55. <view class="order-item" v-for="(item, index) in showAllItems ? selectedItems : selectedItems.slice(0, 3)" :key="index">
  56. <image :src="item.icon" mode="aspectFit"></image>
  57. <view class="item-info">
  58. <view class="name">{{ item.name }}</view>
  59. <view class="desc">{{ item.desc }}</view>
  60. <view class="price-row">
  61. <text class="price">{{ item.unitPrice }}/</text>
  62. <text class="count">x{{ item.quantity }}</text>
  63. <text class="amount">{{ item.unitPrice * item.quantity }}</text>
  64. </view>
  65. </view>
  66. </view>
  67. </view>
  68. <view v-if="selectedItems.length > 3" class="expand-btn" @tap="toggleExpandOrder">
  69. <text>{{ showAllItems ? '收起' : `展开(共${selectedItems.length}件)` }}</text>
  70. <text class="arrow">{{ showAllItems ? '▲' : '▼' }}</text>
  71. </view>
  72. </view>
  73. </view>
  74. <!-- 订单说明 -->
  75. <view class="order-desc">
  76. <view>1. 当前回收快递免费上门由于快递成本较高为避免不必要的成本及资源二次浪费不属于回收品类或不符合回收标准的物品请勿寄出</view>
  77. <view>2. 已通过的回收物品将正常结算不符合回收要求的物品可选择安排取回逾期未联系将默认捐赠无法再次取回</view>
  78. <view>3. 若用户寄出大量不可回收的物品平台有权限制下次回收权限或取消下次包邮服务</view>
  79. <view>4. 对于合格率高的回收订单平台将根据实际情况给予额外回收奖励</view>
  80. </view>
  81. <!-- 底部提交栏 -->
  82. <view class="agreement-bar">
  83. <view class="checkbox" :class="{active: agreed}" @tap="toggleAgreement">
  84. <text v-if="agreed"></text>
  85. </view>
  86. <text>我已阅读并同意</text>
  87. <text class="link" @tap="showServiceAgreement">回收服务协议</text>
  88. <text></text>
  89. <text class="link" @tap="showPrivacyPolicy">隐私政策</text>
  90. </view>
  91. <view class="bottom-bar">
  92. <view class="summary">
  93. <text>已选 {{ totalCount }} 预估回收可得</text>
  94. <text class="amount">{{ totalPriceRange }}</text>
  95. </view>
  96. <button class="main-btn" @tap="submitOrder">预约上门取件</button>
  97. </view>
  98. <!-- 时间选择弹窗 -->
  99. <view class="time-picker" v-if="showTimePicker">
  100. <view class="mask" @tap="closeTimePicker"></view>
  101. <view class="picker-content">
  102. <view class="picker-header">
  103. <text class="reset" @tap="resetPicker">重置</text>
  104. <text class="title">预约上门时间</text>
  105. </view>
  106. <view class="picker-section">
  107. <view class="section-title">选择日期</view>
  108. <view class="date-btns">
  109. <view
  110. v-for="(tab, index) in dateTabs"
  111. :key="index"
  112. :class="['date-btn', {active: currentDateTab === index}]"
  113. @tap="selectDateTab(index)"
  114. >
  115. {{ tab.label }}
  116. </view>
  117. </view>
  118. </view>
  119. <view class="picker-section">
  120. <view class="section-title">选择时间</view>
  121. <view class="time-btns">
  122. <view
  123. v-for="(slot, idx) in timeSlots"
  124. :key="idx"
  125. :class="['time-btn', {active: selectedTimeSlot === idx}]"
  126. @tap="selectTimeSlot(idx)"
  127. >
  128. {{ slot }}
  129. </view>
  130. </view>
  131. </view>
  132. <view class="confirm-btn" @tap="confirmTime">确认</view>
  133. </view>
  134. </view>
  135. </view>
  136. </template>
  137. <script>
  138. import pullRefreshMixin from '@/pages/mixins/pullRefreshMixin.js'
  139. export default {
  140. mixins: [pullRefreshMixin],
  141. data() {
  142. return {
  143. statusBarHeight: 0,
  144. navBarHeight: 0, // px
  145. navBarHeightRpx: 0, // rpx
  146. fromRecycle: false,
  147. address: '',
  148. selectedAddress: null,
  149. selectedTime: '',
  150. agreed: false,
  151. selectedItems: [],
  152. showTimePicker: false,
  153. currentDateTab: 0,
  154. dateTabs: [], // 动态生成
  155. timeSlots: ['11:00~13:00', '13:00~15:00', '15:00~17:00'],
  156. selectedTimeSlot: 0,
  157. steps: [], // 改为空数组,由接口获取
  158. showAllItems: false,
  159. addressId: ''
  160. }
  161. },
  162. onShow() {
  163. // 页面显示时触发,包括从地址选择页面返回时
  164. console.log('当前选中的地址:', this.selectedAddress)
  165. if (this.selectedAddress) {
  166. // 确保地址信息被正确更新
  167. this.address = this.selectedAddress.address
  168. this.addressId = this.selectedAddress.id
  169. // 强制更新视图
  170. this.$forceUpdate()
  171. }
  172. },
  173. onLoad(options) {
  174. // 判断是否从回收页面跳转而来
  175. this.fromRecycle = options.fromRecycle === 'true'
  176. // 如果是从回收页面跳转来的,解析传递的衣物信息
  177. if (this.fromRecycle && options.items) {
  178. try {
  179. this.selectedItems = JSON.parse(decodeURIComponent(options.items))
  180. } catch (e) {
  181. console.error('解析衣物信息失败:', e)
  182. }
  183. }
  184. // 监听地址选择事件
  185. uni.$on('addressSelected', (address) => {
  186. this.selectedAddress = address
  187. this.address = address.address
  188. this.addressId = address.id
  189. // 兼容 addressDetails
  190. if (address.addressDetails) this.selectedAddress.addressDetails = address.addressDetails
  191. this.$forceUpdate()
  192. })
  193. // 监听从订单详情页面返回的事件
  194. uni.$on('clearRecycleData', () => {
  195. // 清除回收页面的订单数据
  196. uni.$emit('clearRecycleOrderData')
  197. })
  198. const sysInfo = uni.getSystemInfoSync()
  199. this.statusBarHeight = sysInfo.statusBarHeight
  200. let navBarHeight = 44
  201. try {
  202. const menuButtonInfo = uni.getMenuButtonBoundingClientRect()
  203. navBarHeight = menuButtonInfo.bottom + menuButtonInfo.top - sysInfo.statusBarHeight
  204. } catch (e) {}
  205. this.navBarHeight = navBarHeight
  206. this.navBarHeightRpx = Math.round(navBarHeight * 750 / sysInfo.windowWidth)
  207. this.getAddressList();
  208. // 动态生成时间tab
  209. this.dateTabs = this.generateDateTabs();
  210. // 获取步骤条数据
  211. this.getAreaList();
  212. },
  213. onUnload() {
  214. // 页面卸载时移除事件监听
  215. uni.$off('addressSelected')
  216. uni.$off('clearRecycleData')
  217. },
  218. computed: {
  219. totalCount() {
  220. return this.selectedItems.reduce((sum, item) => sum + item.quantity, 0)
  221. },
  222. totalPriceRange() {
  223. if (this.selectedItems.length === 0) return '0-0'
  224. const total = this.selectedItems.reduce((sum, item) => sum + (item.unitPrice * item.quantity), 0)
  225. return `${(total * 0.92).toFixed(2)}~${(total * 1.1).toFixed(2)}`
  226. },
  227. canSubmit() {
  228. return this.agreed && this.selectedItems.length > 0 && this.selectedTime && this.displayAddress
  229. },
  230. displayAddress() {
  231. if (this.selectedAddress) {
  232. // 拼接 address 和 addressDetails
  233. return (this.selectedAddress.address || '') + (this.selectedAddress.addressDetails ? ' ' + this.selectedAddress.addressDetails : '')
  234. }
  235. return ''
  236. }
  237. },
  238. methods: {
  239. async onRefresh() {
  240. // 模拟刷新数据
  241. await new Promise(resolve => setTimeout(resolve, 1000))
  242. uni.stopPullRefresh()
  243. },
  244. goBack() {
  245. uni.navigateBack()
  246. },
  247. showMoreMenu() {
  248. uni.showModal({ title: '更多', content: '这里可以放更多操作' })
  249. },
  250. showScan() {
  251. uni.showModal({ title: '扫码', content: '这里可以实现扫码功能' })
  252. },
  253. selectAddress() {
  254. uni.navigateTo({ url: '/pages/subcomponent/select?mode=select' })
  255. },
  256. openTimePicker() {
  257. this.showTimePicker = true
  258. },
  259. closeTimePicker() {
  260. this.showTimePicker = false
  261. },
  262. selectDateTab(index) {
  263. this.currentDateTab = index
  264. },
  265. selectTimeSlot(index) {
  266. this.selectedTimeSlot = index
  267. },
  268. confirmTime() {
  269. const tab = this.dateTabs[this.currentDateTab]
  270. const dateObj = tab.date
  271. const timeStr = this.timeSlots[this.selectedTimeSlot] // 例如 '11:00~13:00'
  272. const startTime = timeStr.split('~')[0] // '11:00'
  273. const yyyy = dateObj.getFullYear()
  274. const mm = (dateObj.getMonth() + 1).toString().padStart(2, '0')
  275. const dd = dateObj.getDate().toString().padStart(2, '0')
  276. this.selectedTime = `${yyyy}-${mm}-${dd} ${startTime}:00`
  277. this.closeTimePicker()
  278. },
  279. resetPicker() {
  280. this.currentDateTab = 0
  281. this.selectedTimeSlot = 0
  282. },
  283. toggleAgreement() {
  284. this.agreed = !this.agreed
  285. },
  286. showServiceAgreement() {
  287. uni.showModal({ title: '回收服务协议', content: '这里展示回收服务协议内容' })
  288. },
  289. showPrivacyPolicy() {
  290. uni.showModal({ title: '隐私政策', content: '这里展示隐私政策内容' })
  291. },
  292. submitOrder() {
  293. if (!this.agreed) {
  294. uni.showToast({ title: '请先同意服务协议', icon: 'none' })
  295. return
  296. }
  297. if (!this.displayAddress || this.displayAddress === '请选择取件地址') {
  298. uni.showToast({ title: '请选择取件地址', icon: 'none' })
  299. return
  300. }
  301. if (!this.selectedTime) {
  302. uni.showToast({ title: '请选择上门时间', icon: 'none' })
  303. return
  304. }
  305. if (this.selectedItems.length === 0) {
  306. uni.showToast({ title: '请选择回收物品', icon: 'none' })
  307. return
  308. }
  309. const list = this.selectedItems.map(item => ({
  310. shopId: item.id,
  311. num: item.quantity
  312. }))
  313. console.log({
  314. addressId: this.addressId,
  315. strTime: this.selectedTime,
  316. list: list
  317. },'createOrder');
  318. // 校验通过,提交
  319. uni.showLoading({ title: '提交中...' })
  320. this.$api('createOrder', {
  321. addressId: this.addressId,
  322. strTime: this.selectedTime,
  323. list: JSON.stringify(list)
  324. }, (res) => {
  325. if (res && res.success) {
  326. console.log(res,'createOrder-res');
  327. uni.showToast({ title: '预约成功', icon: 'success' })
  328. uni.redirectTo({
  329. url: `/pages/subcomponent/detail?id=${res.result.id}`
  330. })
  331. }
  332. })
  333. // setTimeout(() => {
  334. // uni.hideLoading()
  335. // uni.showToast({ title: '预约成功', icon: 'success' })
  336. // setTimeout(() => {
  337. // uni.navigateBack()
  338. // }, 1500)
  339. // }, 1000)
  340. },
  341. toggleExpandOrder() {
  342. this.showAllItems = !this.showAllItems
  343. },
  344. async getAddressList() {
  345. const res = await this.$api('getAddressList', {});
  346. if (res && res.code === 200 && res.result && res.result.records) {
  347. const defaultAddr = res.result.records.find(item => item.defaultFlag === 'Y');
  348. if (defaultAddr) {
  349. this.selectedAddress = defaultAddr;
  350. this.address = defaultAddr.address;
  351. this.addressId = defaultAddr.id;
  352. // 兼容 addressDetails
  353. if (defaultAddr.addressDetails) this.selectedAddress.addressDetails = defaultAddr.addressDetails
  354. } else {
  355. this.selectedAddress = null;
  356. this.address = '';
  357. }
  358. }
  359. },
  360. generateDateTabs() {
  361. const weekMap = ['日', '一', '二', '三', '四', '五', '六']
  362. const result = []
  363. const today = new Date()
  364. for (let i = 0; i < 6; i++) {
  365. const d = new Date(today)
  366. d.setDate(today.getDate() + i)
  367. const mm = (d.getMonth() + 1).toString().padStart(2, '0')
  368. const dd = d.getDate().toString().padStart(2, '0')
  369. let label = ''
  370. if (i === 0) label = `今天 ${mm}-${dd}`
  371. else if (i === 1) label = `明天 ${mm}-${dd}`
  372. else if (i === 2) label = `后天 ${mm}-${dd}`
  373. else label = `${weekMap[d.getDay()]} ${mm}-${dd}`
  374. result.push({ label, date: new Date(d) })
  375. }
  376. return result
  377. },
  378. getAreaList() {
  379. this.$api('getAreaList', {}, (res) => {
  380. if (res.code == 200 && Array.isArray(res.result)) {
  381. // 按sort升序排序
  382. const sorted = res.result.slice().sort((a, b) => a.sort - b.sort)
  383. this.steps = sorted.map(item => ({
  384. icon: item.image,
  385. text: item.title
  386. }))
  387. }
  388. })
  389. }
  390. }
  391. }
  392. </script>
  393. <style lang="scss" scoped>
  394. .pickup-container {
  395. min-height: 100vh;
  396. background: #f8f8f8;
  397. }
  398. .nav-bar {
  399. position: fixed;
  400. top: 0;
  401. left: 0;
  402. right: 0;
  403. z-index: 999;
  404. display: flex;
  405. align-items: center;
  406. background: #fff;
  407. padding: 0 30rpx;
  408. .back {
  409. padding: 20rpx;
  410. margin-left: -20rpx;
  411. }
  412. .title {
  413. flex: 1;
  414. text-align: center;
  415. font-size: 34rpx;
  416. font-weight: 500;
  417. color: #222;
  418. }
  419. .right-btns {
  420. display: flex;
  421. gap: 30rpx;
  422. .more, .scan {
  423. font-size: 40rpx;
  424. color: #333;
  425. }
  426. }
  427. }
  428. .content {
  429. padding: 20rpx;
  430. }
  431. .card {
  432. background: linear-gradient(to bottom,#fff3db 0%,#fffefb 40%);
  433. border-radius: 20rpx;
  434. margin-bottom: 20rpx;
  435. overflow: hidden;
  436. box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.03);
  437. }
  438. .process-card {
  439. background: #fff;
  440. border-radius: 24rpx;
  441. box-shadow: 0 8rpx 32rpx rgba(255, 149, 0, 0.08);
  442. padding: 0 0 20rpx 0;
  443. }
  444. .process-steps {
  445. display: flex;
  446. justify-content: space-between;
  447. align-items: flex-start;
  448. padding: 0 30rpx 30rpx;
  449. .process-step-card {
  450. background: #FFF8ED;
  451. border-radius: 24rpx;
  452. min-width: 140rpx;
  453. min-height: 180rpx;
  454. display: flex;
  455. flex-direction: column;
  456. align-items: center;
  457. margin-right: 24rpx;
  458. position: relative;
  459. .step-icon {
  460. width: 64rpx;
  461. height: 64rpx;
  462. margin: 24rpx 0 18rpx 0;
  463. }
  464. .step-bottom-bar {
  465. position: absolute;
  466. left: 0;
  467. right: 0;
  468. bottom: 0;
  469. height: 56rpx;
  470. background: #FFB74D;
  471. border-radius: 0 0 24rpx 24rpx;
  472. display: flex;
  473. align-items: center;
  474. justify-content: center;
  475. .step-num-bar {
  476. display: flex;
  477. flex-direction: row;
  478. align-items: center;
  479. margin-top: 8rpx;
  480. .num-main {
  481. width: 32rpx;
  482. height: 32rpx;
  483. border-radius: 50%;
  484. background: #fff;
  485. color: #FFB74D;
  486. font-size: 22rpx;
  487. display: flex;
  488. align-items: center;
  489. justify-content: center;
  490. font-weight: 600;
  491. margin-right: 10rpx;
  492. }
  493. .text-main {
  494. color: #fff;
  495. font-size: 26rpx;
  496. font-weight: 500;
  497. }
  498. }
  499. }
  500. .step-label-gray {
  501. position: absolute;
  502. left: 0;
  503. right: 0;
  504. bottom: 0;
  505. height: 56rpx;
  506. background: #FFF8ED;
  507. border-radius: 0 0 24rpx 24rpx;
  508. display: flex;
  509. align-items: center;
  510. justify-content: center;
  511. .num-gray {
  512. width: 32rpx;
  513. height: 32rpx;
  514. border-radius: 50%;
  515. background: #eee;
  516. color: #bbb;
  517. font-size: 22rpx;
  518. display: flex;
  519. align-items: center;
  520. justify-content: center;
  521. font-weight: 600;
  522. margin-right: 10rpx;
  523. }
  524. .text-gray {
  525. color: #bbb;
  526. font-size: 26rpx;
  527. font-weight: 500;
  528. }
  529. }
  530. }
  531. .process-step-card:last-child {
  532. margin-right: 0;
  533. }
  534. }
  535. .divider {
  536. height: 1rpx;
  537. background: rgba(0, 0, 0, 0.05);
  538. margin: 0 30rpx;
  539. }
  540. .pickup-info {
  541. padding: 0 30rpx;
  542. .info-item {
  543. padding: 30rpx 0;
  544. border-bottom: 1rpx solid rgba(0, 0, 0, 0.05);
  545. &:last-child { border-bottom: none; }
  546. .label {
  547. font-size: 28rpx;
  548. color: #333;
  549. margin-bottom: 16rpx;
  550. display: block;
  551. }
  552. .value {
  553. display: flex;
  554. justify-content: space-between;
  555. align-items: center;
  556. .text {
  557. flex: 1;
  558. font-size: 28rpx;
  559. color: #333;
  560. overflow: hidden;
  561. text-overflow: ellipsis;
  562. white-space: nowrap;
  563. }
  564. .text.placeholder { color: #ccc; }
  565. .arrow {
  566. color: #999;
  567. font-size: 28rpx;
  568. margin-left: 10rpx;
  569. }
  570. }
  571. }
  572. }
  573. .order-card {
  574. background: #fff;
  575. border-radius: 24rpx;
  576. box-shadow: 0 8rpx 32rpx rgba(0,0,0,0.04);
  577. margin-bottom: 20rpx;
  578. .order-items {
  579. padding: 0 30rpx;
  580. .order-item {
  581. display: flex;
  582. align-items: center;
  583. padding: 30rpx 0;
  584. border-bottom: 1rpx solid #f5f5f5;
  585. &:last-child { border-bottom: none; }
  586. image { width: 80rpx; height: 80rpx; margin-right: 20rpx; }
  587. .item-info {
  588. flex: 1;
  589. .name { font-size: 30rpx; color: #333; font-weight: 500; }
  590. .desc { font-size: 24rpx; color: #999; margin: 4rpx 0 8rpx 0; }
  591. .price-row {
  592. display: flex;
  593. align-items: center;
  594. .price { color: #FF9500; font-size: 26rpx; margin-right: 10rpx; }
  595. .count { color: #999; font-size: 24rpx; margin-right: 10rpx; }
  596. .amount { color: #333; font-size: 28rpx; margin-left: auto; }
  597. }
  598. }
  599. }
  600. }
  601. .expand-btn {
  602. text-align: center;
  603. color: #999;
  604. font-size: 24rpx;
  605. padding: 10rpx 0;
  606. .arrow { font-size: 20rpx; }
  607. }
  608. }
  609. .agreement-bar {
  610. display: flex;
  611. align-items: center;
  612. background: #fffbe6;
  613. padding: 20rpx 30rpx;
  614. font-size: 24rpx;
  615. .checkbox {
  616. width: 32rpx; height: 32rpx; border-radius: 50%; border: 2rpx solid #FFB74D;
  617. margin-right: 10rpx; display: flex; align-items: center; justify-content: center;
  618. background: #fff;
  619. &.active { background: #FFB74D; color: #fff; }
  620. }
  621. .link { color: #FFB74D; }
  622. }
  623. .bottom-bar {
  624. display: flex;
  625. align-items: center;
  626. justify-content: space-between;
  627. background: #fff;
  628. padding: 20rpx 30rpx calc(40rpx + env(safe-area-inset-bottom));
  629. .summary { color: #666; font-size: 26rpx; }
  630. .amount { color: #FF9500; font-size: 32rpx; font-weight: bold; margin-left: 10rpx; }
  631. .main-btn {
  632. background: #FFB74D;
  633. color: #fff;
  634. font-size: 28rpx;
  635. border-radius: 40rpx;
  636. padding: 0 40rpx;
  637. width: 60%;
  638. height: 80rpx;
  639. display: flex;
  640. justify-content: center;
  641. &[disabled] { opacity: 0.5; }
  642. }
  643. }
  644. .order-desc {
  645. color: #999;
  646. font-size: 22rpx;
  647. padding: 0 30rpx 20rpx 30rpx;
  648. line-height: 1.7;
  649. }
  650. .time-picker {
  651. position: fixed;
  652. left: 0;
  653. right: 0;
  654. top: 0;
  655. bottom: 0;
  656. z-index: 1000;
  657. .mask {
  658. position: absolute;
  659. left: 0;
  660. right: 0;
  661. top: 0;
  662. bottom: 0;
  663. background: rgba(0, 0, 0, 0.5);
  664. }
  665. .picker-content {
  666. position: absolute;
  667. left: 0;
  668. right: 0;
  669. bottom: 0;
  670. background: #fff;
  671. border-radius: 20rpx 20rpx 0 0;
  672. padding-bottom: env(safe-area-inset-bottom);
  673. .picker-header {
  674. padding: 30rpx 0 0 0;
  675. display: flex;
  676. align-items: center;
  677. border-bottom: 1rpx solid #eee;
  678. .reset {
  679. color: #bbb;
  680. font-size: 28rpx;
  681. margin-left: 30rpx;
  682. }
  683. .title {
  684. flex: 1;
  685. text-align: center;
  686. font-size: 32rpx;
  687. font-weight: 500;
  688. color: #222;
  689. margin-right: 60rpx;
  690. }
  691. }
  692. .picker-section {
  693. padding: 30rpx 30rpx 0 30rpx;
  694. .section-title {
  695. font-size: 28rpx;
  696. color: #222;
  697. margin-bottom: 20rpx;
  698. }
  699. .date-btns, .time-btns {
  700. display: flex;
  701. flex-wrap: wrap;
  702. gap: 20rpx 20rpx;
  703. }
  704. .date-btn, .time-btn {
  705. width: 200rpx;
  706. height: 70rpx;
  707. background: #f5f5f5;
  708. color: #999;
  709. border-radius: 18rpx;
  710. display: flex;
  711. align-items: center;
  712. justify-content: center;
  713. font-size: 28rpx;
  714. border: 2rpx solid transparent;
  715. margin-bottom: 10rpx;
  716. }
  717. .date-btn.active, .time-btn.active {
  718. background: #fff;
  719. color: #FFB74D;
  720. border: 2rpx solid #FFB74D;
  721. font-weight: 500;
  722. }
  723. }
  724. .confirm-btn {
  725. margin: 40rpx 30rpx 30rpx 30rpx;
  726. height: 90rpx;
  727. background: linear-gradient(90deg, #FFB74D 0%, #FF9500 100%);
  728. color: #fff;
  729. font-size: 32rpx;
  730. border-radius: 45rpx;
  731. display: flex;
  732. align-items: center;
  733. justify-content: center;
  734. box-shadow: 0 4rpx 16rpx rgba(255, 149, 0, 0.08);
  735. }
  736. }
  737. }
  738. .process-title {
  739. font-size: 32rpx;
  740. font-weight: bold;
  741. background: linear-gradient(to bottom,#fff3db 0%,#fffefb 40%);
  742. color: #222;
  743. text-align: left;
  744. padding: 36rpx 0 24rpx 30rpx;
  745. letter-spacing: 1rpx;
  746. }
  747. </style>