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

595 lines
17 KiB

2 months ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
1 month ago
2 months ago
1 month ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
1 month ago
2 months ago
1 month ago
1 month ago
2 months ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
  1. <template>
  2. <view class="container">
  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="25" color="#fff"></uni-icons>
  7. </view>
  8. <text class="title">钱包流水</text>
  9. </view>
  10. <!-- banner -->
  11. <view class="banner"
  12. :style="{ marginTop: (statusBarHeight + 88) + 'rpx', height: (bannerBaseHeight + statusBarHeight + 88) + 'rpx' }">
  13. <image class="banner-bg" :src="image" mode="aspectFill"></image>
  14. <!-- <view class="banner-content">
  15. <image class="wallet-icon" src="/static/wallet/wallet-3d.png" mode="aspectFit"></image>
  16. </view> -->
  17. </view>
  18. <!-- 账户余额 -->
  19. <view class="balance-card">
  20. <view class="balance-info">
  21. <text class="label">账户</text>
  22. <view class="amount">
  23. <text class="symbol">¥</text>
  24. <text class="value">{{ userInfo.money || '0.00' }}</text>
  25. </view>
  26. </view>
  27. <button class="withdraw-btn" @tap="withdraw">提现</button>
  28. </view>
  29. <!-- 记录切换标签 -->
  30. <view class="record-tabs">
  31. <view class="tab-item" :class="{ active: currentTab === 'settlement' }" @tap="switchTab('settlement')">
  32. 结算日志
  33. </view>
  34. <view class="tab-item" :class="{ active: currentTab === 'withdrawal' }" @tap="switchTab('withdrawal')">
  35. 提现记录
  36. </view>
  37. </view>
  38. <!-- 记录列表 -->
  39. <view class="record-item" v-for="(item, index) in recordList" :key="index">
  40. <view class="record-info">
  41. <view class="record-left">
  42. <text class="type">{{ item.title || (currentTab === 'settlement' ? '结算记录' : '提现记录') }}</text>
  43. <!-- 结算记录显示状态 -->
  44. <view class="status" v-if="currentTab === 'settlement' && item.state === 0">
  45. <text class="status-text">24小时后结算</text>
  46. </view>
  47. <!-- 提现记录显示时间 -->
  48. <text v-if="currentTab === 'withdrawal'" class="date">{{ formatDate(item.createTime) }}</text>
  49. </view>
  50. <view class="record-right">
  51. <text class="amount">¥{{ item.money || '0.00' }}</text>
  52. <!-- 结算记录显示时间 -->
  53. <text v-if="currentTab === 'settlement'" class="date">{{ formatDate(item.createTime) }}</text>
  54. <!-- 提现记录显示操作按钮 -->
  55. <template v-if="currentTab === 'withdrawal'">
  56. <button v-if="item.state === 0" class="withdraw-btn" @tap="receiveWithdrawal(item, index)">
  57. 提现
  58. </button>
  59. <text v-else class="received-text">已到账</text>
  60. </template>
  61. </view>
  62. </view>
  63. </view>
  64. <!-- 初始加载状态 -->
  65. <view v-if="loading && !isInitialized" class="loading-more">
  66. <text>数据加载中...</text>
  67. </view>
  68. <!-- 空状态 -->
  69. <view v-if="recordList.length === 0 && !loading && isInitialized" class="empty-state">
  70. <text>{{ currentTab === 'settlement' ? '暂无结算记录' : '暂无提现记录' }}</text>
  71. </view>
  72. <!-- 调试信息临时启用查看状态 -->
  73. <!-- <view class="debug-info" style="padding: 20rpx; background: #f0f0f0; margin: 20rpx; font-size: 24rpx; border-radius: 10rpx;">
  74. <text>recordList.length: {{ recordList.length }}</text><br/>
  75. <text>loading: {{ loading }}</text><br/>
  76. <text>isInitialized: {{ isInitialized }}</text><br/>
  77. <text>currentTab: {{ currentTab }}</text><br/>
  78. <text>loadingMore: {{ loadingMore }}</text><br/>
  79. <text>noMore: {{ noMore }}</text>
  80. </view> -->
  81. <!-- 加载更多状态 -->
  82. <view v-if="loadingMore" class="loading-more">
  83. <text>加载更多中...</text>
  84. </view>
  85. <!-- 没有更多 -->
  86. <view v-if="noMore && recordList.length > 0" class="no-more">
  87. <text>没有更多了</text>
  88. </view>
  89. </view>
  90. </template>
  91. <script>
  92. import pullRefreshMixin from '@/pages/mixins/pullRefreshMixin.js'
  93. import api from '@/api/api.js'
  94. export default {
  95. mixins: [pullRefreshMixin],
  96. data() {
  97. return {
  98. statusBarHeight: 0,
  99. bannerBaseHeight: 300,
  100. currentTab: 'settlement',
  101. userInfo: {
  102. money: '0.00'
  103. },
  104. recordList: [],
  105. pageNum: 1,
  106. pageSize: 20,
  107. loading: false,
  108. loadingMore: false,
  109. noMore: false,
  110. refreshing: false,
  111. isInitialized: false
  112. }
  113. },
  114. computed: {
  115. image() {
  116. // console.log(getApp().globalData.bannerList,'getApp().globalData.bannerList')
  117. const item = getApp().globalData.bannerList.find(i => i.title === '我的-轮播图')
  118. return item ? item.image : ''
  119. },
  120. },
  121. onLoad() {
  122. this.statusBarHeight = uni.getSystemInfoSync().statusBarHeight
  123. this.initData()
  124. },
  125. onPullDownRefresh() {
  126. this.onRefresh()
  127. },
  128. onReachBottom(){
  129. this.loadMore()
  130. },
  131. methods: {
  132. async initData() {
  133. await this.getUserInfo()
  134. await this.loadRecords()
  135. },
  136. async getUserInfo() {
  137. try {
  138. const res = await api('getUserByToken')
  139. if (res.code === 200 && res.data) {
  140. this.userInfo = res.data
  141. }
  142. } catch (error) {
  143. console.error('获取用户信息失败:', error)
  144. }
  145. },
  146. async loadRecords(isRefresh = false) {
  147. if (this.loading) return
  148. this.loading = true
  149. // 如果是刷新,重置分页状态
  150. if (isRefresh) {
  151. this.pageNum = 1
  152. this.noMore = false
  153. this.recordList = []
  154. }
  155. try {
  156. // 获取记录
  157. const res = await api('getMyMoneyLogPage', {
  158. status: this.currentTab === 'settlement' ? 0 : 1,
  159. pageNum: this.pageNum,
  160. pageSize: this.pageSize
  161. })
  162. if (res.code === 200 && res.data) {
  163. const records = res.data.records || []
  164. if (isRefresh) {
  165. this.recordList = records
  166. } else {
  167. this.recordList = [...this.recordList, ...records]
  168. }
  169. if (records.length < this.pageSize) {
  170. this.noMore = true
  171. }
  172. } else {
  173. this.recordList = []
  174. }
  175. } catch (error) {
  176. console.error('获取流水记录失败:', error)
  177. uni.showToast({
  178. title: '获取数据失败',
  179. icon: 'none'
  180. })
  181. } finally {
  182. this.loading = false
  183. this.isInitialized = true
  184. }
  185. },
  186. formatDate(timestamp) {
  187. if (!timestamp) return ''
  188. const date = new Date(timestamp)
  189. const month = String(date.getMonth() + 1).padStart(2, '0')
  190. const day = String(date.getDate()).padStart(2, '0')
  191. return `${month}-${day}`
  192. },
  193. async onRefresh() {
  194. this.refreshing = true
  195. try {
  196. await this.getUserInfo()
  197. await this.loadRecords(true)
  198. } finally {
  199. this.refreshing = false
  200. uni.stopPullRefresh()
  201. }
  202. },
  203. goBack() {
  204. uni.navigateBack()
  205. },
  206. async switchTab(tab) {
  207. this.currentTab = tab
  208. // 切换标签时重新加载数据
  209. this.pageNum = 1
  210. this.noMore = false
  211. this.recordList = []
  212. this.isInitialized = false
  213. await this.loadRecords()
  214. },
  215. withdraw() {
  216. uni.navigateTo({
  217. url: '/pages/subcomponent/withdraw'
  218. })
  219. },
  220. async receiveWithdrawal(item, index) {
  221. try {
  222. // 验证提现金额
  223. if (!item.money || parseFloat(item.money) <= 0) {
  224. uni.showToast({
  225. title: '提现金额无效',
  226. icon: 'none'
  227. })
  228. return
  229. }
  230. // 获取用户名
  231. const userName = this.userInfo.name || this.userInfo.nickName || this.userInfo.userName
  232. if (!userName) {
  233. uni.showToast({
  234. title: '请先完善个人信息',
  235. icon: 'none'
  236. })
  237. return
  238. }
  239. // 显示确认弹窗
  240. const [error, res] = await uni.showModal({
  241. title: '确认提现',
  242. content: `确认提现金额 ¥${item.money} 到您的账户?`,
  243. confirmText: '确认提现',
  244. cancelText: '取消'
  245. })
  246. if (error || !res.confirm) {
  247. return
  248. }
  249. // 显示加载状态
  250. uni.showLoading({
  251. title: '正在提现...'
  252. })
  253. // 调用提现接口
  254. const result = await api('withdraw', {
  255. userName: userName,
  256. money: parseFloat(item.money)
  257. })
  258. if (result.code === 200) {
  259. // 更新本地状态
  260. this.recordList[index].state = 1
  261. uni.hideLoading()
  262. uni.showToast({
  263. title: '提现成功',
  264. icon: 'success'
  265. })
  266. // 重新获取用户信息更新余额
  267. await this.getUserInfo()
  268. } else {
  269. throw new Error(result.message || result.msg || '提现失败')
  270. }
  271. } catch (error) {
  272. uni.hideLoading()
  273. console.error('提现失败:', error)
  274. uni.showToast({
  275. title: error.message || '提现失败,请重试',
  276. icon: 'none'
  277. })
  278. }
  279. },
  280. async loadMore() {
  281. if (this.noMore || this.loadingMore) return
  282. this.loadingMore = true
  283. this.pageNum++
  284. try {
  285. const res = await api('getMyMoneyLogPage', {
  286. status: this.currentTab === 'settlement' ? 0 : 1,
  287. pageNum: this.pageNum,
  288. pageSize: this.pageSize
  289. })
  290. if (res.code === 200 && res.data) {
  291. const records = res.data.records || []
  292. this.recordList = [...this.recordList, ...records]
  293. if (records.length < this.pageSize) {
  294. this.noMore = true
  295. }
  296. }
  297. } catch (error) {
  298. console.error('加载更多记录失败:', error)
  299. this.pageNum-- // 失败时回退页码
  300. uni.showToast({
  301. title: '加载更多数据失败',
  302. icon: 'none'
  303. })
  304. } finally {
  305. this.loadingMore = false
  306. }
  307. }
  308. }
  309. }
  310. </script>
  311. <style lang="scss" scoped>
  312. .container {
  313. min-height: 100vh;
  314. background: #fff;
  315. }
  316. .nav-bar {
  317. display: flex;
  318. align-items: center;
  319. background: linear-gradient(to right, #f68240 0%, #fc8940 10%);
  320. position: fixed;
  321. top: 0;
  322. left: 0;
  323. right: 0;
  324. z-index: 999;
  325. }
  326. .back {
  327. padding: 20rpx;
  328. margin-left: -20rpx;
  329. }
  330. .title {
  331. flex: 1;
  332. text-align: center;
  333. font-size: 34rpx;
  334. font-weight: 500;
  335. color: #fff;
  336. }
  337. .banner {
  338. position: relative;
  339. background: linear-gradient(to right, #f78b49, #fc8940);
  340. overflow: hidden;
  341. border-radius: 0 0 30rpx 30rpx;
  342. margin-top: 0;
  343. }
  344. .banner-bg {
  345. position: absolute;
  346. width: 100%;
  347. height: 100%;
  348. // opacity: 0.8;
  349. }
  350. .banner-content {
  351. position: relative;
  352. z-index: 1;
  353. height: 100%;
  354. display: flex;
  355. justify-content: center;
  356. align-items: center;
  357. .wallet-icon {
  358. width: 240rpx;
  359. height: 240rpx;
  360. }
  361. }
  362. .balance-card {
  363. margin: -60rpx 30rpx 0;
  364. padding: 30rpx;
  365. background: #fff;
  366. border-radius: 20rpx;
  367. box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
  368. display: flex;
  369. justify-content: space-between;
  370. align-items: center;
  371. position: relative;
  372. z-index: 2;
  373. .balance-info {
  374. .label {
  375. font-size: 28rpx;
  376. color: #666;
  377. }
  378. .amount {
  379. margin-top: 8rpx;
  380. display: flex;
  381. align-items: baseline;
  382. .symbol {
  383. font-size: 32rpx;
  384. color: #333;
  385. }
  386. .value {
  387. font-size: 48rpx;
  388. font-weight: bold;
  389. color: #333;
  390. margin-left: 4rpx;
  391. }
  392. }
  393. }
  394. .withdraw-btn {
  395. width: 160rpx;
  396. height: 70rpx;
  397. background: #FFB74D;
  398. color: #fff;
  399. font-size: 28rpx;
  400. border-radius: 35rpx;
  401. display: flex;
  402. align-items: center;
  403. justify-content: center;
  404. border: none;
  405. &::after {
  406. border: none;
  407. }
  408. }
  409. }
  410. .record-tabs {
  411. display: flex;
  412. padding: 20rpx 0;
  413. border-bottom: 1rpx solid #f5f5f5;
  414. .tab-item {
  415. position: relative;
  416. padding: 16rpx 0;
  417. font-size: 28rpx;
  418. color: #666;
  419. flex: 1;
  420. text-align: center;
  421. &.active {
  422. color: #333;
  423. font-weight: 500;
  424. &::after {
  425. content: '';
  426. position: absolute;
  427. left: 50%;
  428. transform: translateX(-50%);
  429. bottom: -21rpx;
  430. width: 48rpx;
  431. height: 2rpx;
  432. background: #FFB74D;
  433. }
  434. }
  435. }
  436. }
  437. .record-item {
  438. padding: 30rpx;
  439. border-bottom: 1rpx solid rgba(0, 0, 0, 0.05);
  440. .record-info {
  441. display: flex;
  442. justify-content: space-between;
  443. align-items: flex-start;
  444. .record-left {
  445. flex: 1;
  446. .type {
  447. font-size: 28rpx;
  448. color: #333;
  449. margin-bottom: 8rpx;
  450. }
  451. .status {
  452. display: inline-block;
  453. padding: 4rpx 8rpx;
  454. background: #FFB74D;
  455. border-radius: 8rpx;
  456. margin-top: 8rpx;
  457. .status-text {
  458. font-size: 22rpx;
  459. color: #fff;
  460. }
  461. }
  462. .date {
  463. font-size: 24rpx;
  464. color: #999;
  465. margin-top: 8rpx;
  466. }
  467. }
  468. .record-right {
  469. display: flex;
  470. flex-direction: column;
  471. align-items: flex-end;
  472. .amount {
  473. font-size: 28rpx;
  474. color: #333;
  475. font-weight: 500;
  476. margin-bottom: 8rpx;
  477. }
  478. .date {
  479. font-size: 24rpx;
  480. color: #999;
  481. }
  482. .withdraw-btn {
  483. width: 100rpx;
  484. height: 50rpx;
  485. background: #FFB74D;
  486. color: #fff;
  487. font-size: 24rpx;
  488. border-radius: 25rpx;
  489. display: flex;
  490. align-items: center;
  491. justify-content: center;
  492. border: none;
  493. margin-top: 8rpx;
  494. &::after {
  495. border: none;
  496. }
  497. }
  498. .received-text {
  499. font-size: 22rpx;
  500. color: #52C41A;
  501. margin-top: 8rpx;
  502. }
  503. }
  504. }
  505. }
  506. .empty-state {
  507. width: 100%;
  508. text-align: center;
  509. padding: 100rpx 0;
  510. color: #999;
  511. font-size: 28rpx;
  512. }
  513. .loading-more {
  514. text-align: center;
  515. padding: 20rpx 0;
  516. color: #999;
  517. font-size: 28rpx;
  518. }
  519. .no-more {
  520. text-align: center;
  521. padding: 20rpx 0;
  522. color: #999;
  523. font-size: 28rpx;
  524. }
  525. </style>