- <template>
- <view class="container">
- <!-- 顶部导航栏 -->
- <view class="nav-bar" :style="{paddingTop: statusBarHeight + 'px'}">
- <view class="back" @tap="goBack">
- <uni-icons type="left" size="20" color="#fff"></uni-icons>
- </view>
- <text class="nav-title">钱包流水</text>
- <view class="nav-icons">
- <!-- 占位元素,保持布局对称 -->
- </view>
- </view>
- <!-- banner -->
- <view class="banner"
- :style="{ marginTop: 'calc(150rpx)', height: (bannerBaseHeight + statusBarHeight + 88) + 'rpx' }">
- <swiper :indicator-dots="false" :autoplay="true" :interval="3000" :duration="500" circular
- style="width: 100%; height: 100%;">
- <swiper-item v-for="(item, index) in bannerList" :key="item.id || index">
- <view v-if="item.type == 1" class="video-container">
- <!-- 预览状态:显示封面图 -->
- <image
- v-if="!videoPlayingStates[index]"
- :src="item.image || ''"
- mode="aspectFill"
- style="width: 100%; height: 100%;"
- class="video-poster"
- @click="playVideoFullscreen(item, index)"
- />
- <!-- 播放状态:显示视频 -->
- <video
- v-else
- :id="`wallet-video-${index}`"
- :src="item.voUrl"
- :autoplay="true"
- :muted="false"
- :loop="false"
- :controls="true"
- :show-play-btn="true"
- :show-center-play-btn="false"
- :show-fullscreen-btn="true"
- :show-progress="true"
- :show-mute-btn="true"
- :enable-progress-gesture="true"
- :enable-play-gesture="true"
- object-fit="cover"
- style="width: 100%; height: 100%;"
- @fullscreenchange="onFullscreenChange"
- @play="onVideoPlay(index)"
- @pause="onVideoPause(index)"
- @ended="onVideoEnded(index)"
- ></video>
- <!-- 播放按钮覆盖层 -->
- <view v-if="!videoPlayingStates[index]" class="video-overlay" @click="playVideoFullscreen(item, index)">
- <view class="play-button-large">
- <view class="play-triangle"></view>
- </view>
- </view>
- </view>
- <image v-else :src="item.image" mode="aspectFill" style="width: 100%; height: 100%;" />
- </swiper-item>
- </swiper>
- </view>
-
- <!-- 账户余额 -->
- <view class="balance-card">
- <view class="balance-info">
- <text class="label">账户</text>
- <view class="amount">
- <text class="symbol">¥</text>
- <text class="value">{{ userInfo.money || '0.00' }}</text>
- </view>
- </view>
- <button class="withdraw-btn" @tap="withdraw">提现</button>
- </view>
-
- <!-- 记录切换标签 -->
- <view class="record-tabs">
- <view class="tab-item" :class="{ active: currentTab === 'settlement' }" @tap="switchTab('settlement')">
- 结算日志
- </view>
- <view class="tab-item" :class="{ active: currentTab === 'withdrawal' }" @tap="switchTab('withdrawal')">
- 提现记录
- </view>
- </view>
-
- <!-- 记录列表 -->
- <view class="record-item" v-for="(item, index) in recordList" :key="index">
- <view class="record-info">
- <view class="record-left">
- <text class="type">{{ item.title || (currentTab === 'settlement' ? '结算记录' : '提现记录') }}</text>
- <!-- 结算记录显示状态 -->
- <view class="status" v-if="currentTab === 'settlement' && item.state === 0">
- <text class="status-text">48小时后结算</text>
- </view>
- <!-- 提现记录显示时间 -->
- <text v-if="currentTab === 'withdrawal'" class="date">{{ formatDate(item.createTime) }}</text>
- </view>
- <view class="record-right">
- <text class="amount">¥{{ item.money || '0.00' }}</text>
- <!-- 结算记录显示时间 -->
- <text v-if="currentTab === 'settlement'" class="date">{{ formatDate(item.createTime) }}</text>
- <!-- 提现记录显示操作按钮 -->
- <template v-if="currentTab === 'withdrawal'">
- <button v-if="item.state === 0" class="withdraw-btn" @tap="requestWechatTransfer(item, index)">
- 提现
- </button>
- <text v-else-if="item.state === 1" class="received-text">已到账</text>
- <text v-else class="received-text">已取消</text>
- </template>
- </view>
- </view>
- </view>
-
- <!-- 初始加载状态 -->
- <view v-if="loading && !isInitialized" class="loading-more">
- <text>数据加载中...</text>
- </view>
-
- <!-- 空状态 -->
- <view v-if="recordList.length === 0 && !loading && isInitialized" class="empty-state">
- <text>{{ currentTab === 'settlement' ? '暂无结算记录' : '暂无提现记录' }}</text>
- </view>
-
- <!-- 调试信息(临时启用查看状态) -->
- <!-- <view class="debug-info" style="padding: 20rpx; background: #f0f0f0; margin: 20rpx; font-size: 24rpx; border-radius: 10rpx;">
- <text>recordList.length: {{ recordList.length }}</text><br/>
- <text>loading: {{ loading }}</text><br/>
- <text>isInitialized: {{ isInitialized }}</text><br/>
- <text>currentTab: {{ currentTab }}</text><br/>
- <text>loadingMore: {{ loadingMore }}</text><br/>
- <text>noMore: {{ noMore }}</text>
- </view> -->
-
- <!-- 加载更多状态 -->
- <view v-if="loadingMore" class="loading-more">
- <text>加载更多中...</text>
- </view>
-
- <!-- 没有更多 -->
- <view v-if="noMore && recordList.length > 0" class="no-more">
- <text>没有更多了</text>
- </view>
- </view>
- </template>
-
- <script>
- import pullRefreshMixin from '@/pages/mixins/pullRefreshMixin.js'
- import api from '@/api/api.js'
- import config from '@/config.js'
-
- export default {
- mixins: [pullRefreshMixin],
- data() {
- return {
- statusBarHeight: 0,
- bannerBaseHeight: 300,
- currentTab: 'settlement',
- userInfo: {
- money: '0.00'
- },
- recordList: [],
- pageNum: 1,
- pageSize: 20,
- loading: false,
- loadingMore: false,
- noMore: false,
- refreshing: false,
- isInitialized: false,
- bannerList: [],
- videoPlayingStates: {} // 记录每个视频的播放状态
- }
- },
- computed: {
- },
- onLoad() {
- this.statusBarHeight = uni.getSystemInfoSync().statusBarHeight
- },
- onShow() {
- this.initData()
- },
- onPullDownRefresh() {
- this.onRefresh()
- },
- onReachBottom(){
- this.loadMore()
- },
- onUnload() {
- uni.$off('bannerListUpdated')
- },
- methods: {
- async initData() {
- this.getBannerList()
- await this.getUserInfo()
- await this.loadRecords()
- },
-
- getBannerList() {
- // 从全局数据获取轮播图
- this.bannerList = getApp().globalData.bannerList || []
-
- // 监听轮播图数据更新
- uni.$on('bannerListUpdated', () => {
- this.bannerList = getApp().globalData.bannerList || []
- })
- },
-
- async getUserInfo() {
- try {
- const res = await api('getUserByToken')
- if (res.code === 200 && res.result) {
- this.userInfo = res.result
- }
- } catch (error) {
- console.error('获取用户信息失败:', error)
- }
- },
-
- async loadRecords(isRefresh = false) {
- if (this.loading) return
- this.loading = true
-
- // 如果是刷新,重置分页状态
- if (isRefresh) {
- this.pageNum = 1
- this.noMore = false
- this.recordList = []
- }
-
- try {
- // 获取记录
- const res = await api('getMyMoneyLogPage', {
- status: this.currentTab === 'settlement' ? 0 : 1,
- pageNum: this.pageNum,
- pageSize: this.pageSize
- })
-
- if (res.code === 200 && res.result) {
- const records = res.result.records || []
-
- if (isRefresh) {
- this.recordList = records
- } else {
- this.recordList = [...this.recordList, ...records]
- }
-
- if (records.length < this.pageSize) {
- this.noMore = true
- }
- } else {
- this.recordList = []
- }
- } catch (error) {
- console.error('获取流水记录失败:', error)
- uni.showToast({
- title: '获取数据失败',
- icon: 'none'
- })
- } finally {
- this.loading = false
- this.isInitialized = true
- }
- },
-
- formatDate(timestamp) {
- if (!timestamp) return ''
- const date = new Date(timestamp)
- const month = String(date.getMonth() + 1).padStart(2, '0')
- const day = String(date.getDate()).padStart(2, '0')
- return `${month}-${day}`
- },
-
- async onRefresh() {
- this.refreshing = true
- try {
- await this.getUserInfo()
- await this.loadRecords(true)
- } finally {
- this.refreshing = false
- uni.stopPullRefresh()
- }
- },
-
- goBack() {
- uni.navigateBack()
- },
-
- async switchTab(tab) {
- this.currentTab = tab
- // 切换标签时重新加载数据
- this.pageNum = 1
- this.noMore = false
- this.recordList = []
- this.isInitialized = false
- await this.loadRecords()
- },
-
- withdraw() {
- uni.navigateTo({
- url: '/pages/subcomponent/withdraw'
- })
- },
-
- // 微信商户转账
- async requestWechatTransfer(payData) {
- try {
- // 调用微信商户转账API
- const transferResult = await new Promise((resolve, reject) => {
- // 拉起微信收款确认页面
- if (!wx.canIUse('requestMerchantTransfer')) {
- wx.showModal({
- content: '你的微信版本过低,请更新至最新版本。',
- showCancel: false,
- });
- return
- }
-
- // 在真机环境中,调用API
- wx.requestMerchantTransfer({
- mchId: config.mchId,
- appId: wx.getAccountInfoSync().miniProgram.appId,
- package: payData.packageInfo,
- success: (res) => {
- resolve(res)
- },
- fail: (res) => {
- reject(res)
- },
- complete: (res) => {
- }
- });
- })
-
- // 转账成功后调用成功接口
- await api('withdrawSUccess', { id: payData.id }, '确认提现中...')
-
-
- this.pageNum = 1
- this.noMore = false
- this.recordList = []
- this.isInitialized = false
- await this.loadRecords()
-
- } catch (error) {
- throw error
- }
- },
-
- async loadMore() {
- if (this.noMore || this.loadingMore) return
- this.loadingMore = true
- this.pageNum++
- try {
- const res = await api('getMyMoneyLogPage', {
- status: this.currentTab === 'settlement' ? 0 : 1,
- pageNum: this.pageNum,
- pageSize: this.pageSize
- })
-
- if (res.code === 200 && res.data) {
- const records = res.data.records || []
-
- this.recordList = [...this.recordList, ...records]
- if (records.length < this.pageSize) {
- this.noMore = true
- }
- }
- } catch (error) {
- console.error('加载更多记录失败:', error)
- this.pageNum-- // 失败时回退页码
- uni.showToast({
- title: '加载更多数据失败',
- icon: 'none'
- })
- } finally {
- this.loadingMore = false
- }
- },
-
- // 视频相关方法
- playVideoFullscreen(item, index) {
- if (!this.videoPlayingStates[index]) {
- // 第一次点击:显示视频并开始播放
- this.$set(this.videoPlayingStates, index, true);
-
- // 等待视频元素渲染后再进行操作
- this.$nextTick(() => {
- setTimeout(() => {
- const videoContext = uni.createVideoContext(`wallet-video-${index}`, this);
- if (videoContext) {
- // 直接进入全屏播放
- videoContext.requestFullScreen({
- direction: -1 // 自动选择方向
- });
- }
- }, 200);
- });
- }
- },
-
- onVideoPlay(index) {
- this.$set(this.videoPlayingStates, index, true);
- },
-
- onVideoPause(index) {
- this.$set(this.videoPlayingStates, index, false);
- },
-
- onVideoEnded(index) {
- // 视频播放结束,回到预览状态
- this.$set(this.videoPlayingStates, index, false);
- },
-
- onFullscreenChange(e) {
- console.log('全屏状态改变:', e.detail);
- const videoIndex = e.target.id.replace('wallet-video-', '');
-
- if (e.detail.fullScreen) {
- // 进入全屏时,不做任何操作
- console.log('进入全屏模式,方向:', e.detail.direction);
- } else {
- // 退出全屏时,回到预览状态
- console.log('退出全屏模式,回到预览状态');
- this.$set(this.videoPlayingStates, videoIndex, false);
- }
- }
- }
- }
- </script>
-
- <style lang="scss" scoped>
- .container {
- min-height: 100vh;
- background: #fff;
- }
-
- .nav-bar {
- display: flex;
- align-items: center;
- height: calc(150rpx + var(--status-bar-height));
- padding: 0 32rpx;
- background: linear-gradient(to right, #f68240 0%, #fc8940 10%);
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- z-index: 999;
- box-sizing: border-box;
- box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.03);
- }
-
- .back {
- padding: 20rpx;
- margin-left: -20rpx;
- }
-
- .nav-title {
- flex: 1;
- text-align: center;
- font-size: 32rpx;
- font-weight: 500;
- color: #fff;
- }
-
- .nav-icons {
- display: flex;
- align-items: center;
- gap: 12px;
- }
-
- .banner {
- position: relative;
- background: linear-gradient(to right, #f78b49, #fc8940);
- overflow: hidden;
- border-radius: 0 0 30rpx 30rpx;
- margin-top: 0;
-
- .video-container {
- position: relative;
- width: 100%;
- height: 100%;
-
- .video-poster {
- cursor: pointer;
- transition: transform 0.2s ease;
-
- &:active {
- transform: scale(0.98);
- }
- }
-
- .video-overlay {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: rgba(0, 0, 0, 0.4);
- z-index: 10;
- cursor: pointer;
- display: flex;
- justify-content: center;
- align-items: center;
-
- .play-button-large {
- width: 120rpx;
- height: 120rpx;
- background: transparent;
- border-radius: 50%;
- display: flex;
- justify-content: center;
- align-items: center;
- backdrop-filter: blur(10rpx);
- transition: all 0.3s ease;
- box-shadow: 0 8rpx 30rpx rgba(0, 0, 0, 0.3);
-
- &:active {
- transform: scale(0.9);
- background: rgba(255, 255, 255, 0.1);
- }
-
- .play-triangle {
- width: 0;
- height: 0;
- border-left: 24rpx solid #fff;
- border-top: 18rpx solid transparent;
- border-bottom: 18rpx solid transparent;
- margin-left: 8rpx;
- transition: all 0.3s ease;
- filter: drop-shadow(0 2rpx 4rpx rgba(0, 0, 0, 0.3));
- }
- }
-
- &:hover .play-button-large {
- transform: scale(1.1);
- box-shadow: 0 12rpx 40rpx rgba(0, 0, 0, 0.4);
-
- .play-triangle {
- border-left-color: #ff8917;
- }
- }
- }
- }
- }
-
-
-
- /* .banner-content 样式已移除,现在使用轮播图 */
-
- .balance-card {
- margin: -60rpx 30rpx 0;
- padding: 30rpx;
- background: #fff;
- border-radius: 20rpx;
- box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
- display: flex;
- justify-content: space-between;
- align-items: center;
- position: relative;
- z-index: 2;
-
- .balance-info {
- .label {
- font-size: 28rpx;
- color: #666;
- }
-
- .amount {
- margin-top: 8rpx;
- display: flex;
- align-items: baseline;
-
- .symbol {
- font-size: 32rpx;
- color: #333;
- }
-
- .value {
- font-size: 48rpx;
- font-weight: bold;
- color: #333;
- margin-left: 4rpx;
- }
- }
- }
-
- .withdraw-btn {
- width: 140rpx;
- height: 70rpx;
- background: #FFB74D;
- color: #fff;
- font-size: 28rpx;
- border-radius: 35rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- border: none;
- padding: 0;
- margin: 0;
- line-height: 1;
- box-sizing: border-box;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-
- &::after {
- border: none;
- }
- }
- }
-
- .record-tabs {
- display: flex;
- padding: 20rpx 0;
- border-bottom: 1rpx solid #f5f5f5;
-
- .tab-item {
- position: relative;
- padding: 16rpx 0;
- font-size: 28rpx;
- color: #666;
- flex: 1;
- text-align: center;
-
- &.active {
- color: #333;
- font-weight: 500;
-
- &::after {
- content: '';
- position: absolute;
- left: 50%;
- transform: translateX(-50%);
- bottom: -21rpx;
- width: 48rpx;
- height: 2rpx;
- background: #FFB74D;
- }
- }
- }
- }
-
-
- .record-item {
- padding: 30rpx;
- border-bottom: 1rpx solid rgba(0, 0, 0, 0.05);
-
- .record-info {
- display: flex;
- justify-content: space-between;
- align-items: flex-start;
-
- .record-left {
- flex: 1;
- display: flex;
- align-items: center;
-
- .type {
- font-size: 28rpx;
- color: #333;
- margin-bottom: 8rpx;
- }
-
- .status {
- padding: 4rpx 8rpx;
- background: #FFB74D;
- border-radius: 8rpx;
- display: flex;
- align-items: center;
- justify-content: center;
-
- .status-text {
- font-size: 22rpx;
- color: #fff;
- }
- }
-
- .date {
- font-size: 24rpx;
- color: #999;
- margin-top: 8rpx;
- }
- }
-
- .record-right {
- display: flex;
- flex-direction: column;
- align-items: flex-end;
-
- .amount {
- font-size: 28rpx;
- color: #333;
- font-weight: 500;
- margin-bottom: 8rpx;
- }
-
- .date {
- font-size: 24rpx;
- color: #999;
- }
-
- .withdraw-btn {
- width: 120rpx;
- height: 50rpx;
- background: #FFB74D;
- color: #fff;
- font-size: 24rpx;
- border-radius: 25rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- border: none;
- margin-top: 8rpx;
- padding: 0;
- line-height: 1;
- box-sizing: border-box;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-
- &::after {
- border: none;
- }
- }
-
- .received-text {
- font-size: 22rpx;
- color: #52C41A;
- margin-top: 8rpx;
- }
- }
- }
- }
-
- .empty-state {
- width: 100%;
- text-align: center;
- padding: 100rpx 0;
- color: #999;
- font-size: 28rpx;
- }
-
- .loading-more {
- text-align: center;
- padding: 20rpx 0;
- color: #999;
- font-size: 28rpx;
- }
-
- .no-more {
- text-align: center;
- padding: 20rpx 0;
- color: #999;
- font-size: 28rpx;
- }
- </style>
|