2026-03-12 18:57:28 +08:00

504 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="flower-page">
<!-- 顶部统计栏与截图一致两项 -->
<view class="navbox">
<view class="status_bar"></view>
<uni-nav-bar
left-icon="left"
title="我的鲜花"
@clickLeft="goBack"
color="#8B2316"
:border="false"
backgroundColor="#eeeeee"
/>
</view>
<view class="fixed-top-block">
<view class="stats-bar">
<view class="stat">
<up-image :src="flowerImg" width="36rpx" height="36rpx" ></up-image>
<text class="num">{{ stat.totalCount }}</text>
</view>
<view class="stat">
<up-image :src="moneyImg" width="36rpx" height="36rpx" ></up-image>
<text class="num">{{ stat.totalAmount.toFixed(2) }}</text>
</view>
</view>
<!-- 表头条红底白字 -->
<view class="table-header">
<text class="col name">姓名</text>
<text class="col time">时间</text>
<text class="col qty">数量</text>
</view>
</view>
<!-- 表体/列表或空状态 -->
<scroll-view
class="table-body"
scroll-y
:show-scrollbar="false"
refresher-enabled
:refresher-triggered="refreshing"
@refresherrefresh="onRefresh"
@scrolltolower="onLoadMore"
lower-threshold="100"
:scroll-top="scrollTop"
>
<view class="list-content">
<view v-if="records.length === 0" class="empty-wrap">
<up-image :src="emptyImg" width="176rpx" height="204rpx" ></up-image>
<text class="empty-text">您暂未收到鲜花</text>
</view>
<view v-else class="row" v-for="(item, idx) in records" :key="idx">
<text class="cell name">{{ item.name }}</text>
<text class="cell time">{{ item.time }}</text>
<text class="cell qty" :class="{ plus: item.amount > 0, minus: item.amount < 0 }">{{ item.amount }}</text>
</view>
<view v-if="loading" class="loading">
<text>加载中...</text>
</view>
<view v-if="noMore && records.length > 0" class="no-more">
<text>没有更多数据了</text>
</view>
<!-- <view class="debug-actions">
<button @click="testLoadMore" size="mini" type="primary">测试加载更多</button>
<text class="debug-info">当前页: {{ page }}, 加载中: {{ loading }}, 无更多: {{ noMore }}</text>
</view> -->
</view>
</scroll-view>
</view>
</template>
<script setup>
import { ref, onMounted, nextTick } from 'vue';
import flowerImg from "@/static/flowers.png"
import moneyImg from "@/static/mind_totle_money.png"
import emptyImg from "@/static/icon_empty.png"
import api from '@/api/api.js';
import { onBackPress,onLoad } from '@dcloudio/uni-app';
const from=ref('');
const user_uuid=ref('');
onLoad((options) => {
if(options.from){
from.value = options.from;
}
let userInfo= uni.getStorageSync('userInfo')
user_uuid.value = userInfo.uuid;
loadFirstTwoPages();
});
onBackPress(() => {
if(!from.value){
plus.runtime.quit();
return true;
}
});
// 与截图一致的顶部两项统计:总鲜花数与累计金额
const stat = ref({ totalCount: 0, totalAmount: 0.0 });
// 与表格结构匹配:姓名/时间/数量
const records = ref([]);
// 列表状态
const refreshing = ref(false);
const loading = ref(false);
const noMore = ref(false);
const page = ref(1);
const pageSize = ref(10);
const scrollTop = ref(0);
const autoLoading = ref(false);
const formatDateToYDM = (value) => {
if (!value) return '-';
const raw = String(value).trim();
const directMatch = raw.match(/^(\d{4})[-/.](\d{1,2})[-/.](\d{1,2})/);
if (directMatch) {
const [, year, month, day] = directMatch;
return `${year}-${day.padStart(2, '0')}-${month.padStart(2, '0')}`;
}
const parsedDate = new Date(raw);
if (Number.isNaN(parsedDate.getTime())) return '-';
const year = parsedDate.getFullYear();
const month = String(parsedDate.getMonth() + 1).padStart(2, '0');
const day = String(parsedDate.getDate()).padStart(2, '0');
return `${year}-${day}-${month}`;
};
const goBack = () => {
if(!from.value){
plus.runtime.quit();
}else{
uni.navigateBack();
}
};
const getFlowerList = () => {
return new Promise((resolve, reject) => {
console.log('[myFlower] getFlowerList:start', {
page: page.value,
loading: loading.value,
refreshing: refreshing.value,
noMore: noMore.value
});
// 只在第一页时设置loading状态避免与onLoadMore冲突
if (page.value === 1) {
loading.value = true;
}
console.log('正在获取第', page.value, '页数据...');
api.getFlowerList({
page: page.value,
user_uuid: user_uuid.value,
}).then(res => {
console.log('接口返回数据:', res);
if (res.code === 200 && res.data) {
const { flower_data, total_amount, total_num } = res.data;
console.log('[myFlower] getFlowerList:response', {
reqPage: page.value,
pageNum: flower_data?.pageNum,
pages: flower_data?.pages,
isLastPage: flower_data?.isLastPage,
listLength: flower_data?.list?.length || 0
});
// 更新统计数据
stat.value.totalCount = total_num || 0;
stat.value.totalAmount = total_amount || 0;
// 处理列表数据
if (flower_data && Array.isArray(flower_data.list)) {
const newRecords = flower_data.list.map(item => ({
name: item.patient_name || '账号已注销',
time: formatDateToYDM(item.create_date),
amount: item.num || 0,
// 保留原始数据以备后用
original: item
}));
// 如果是第一页,替换数据;否则追加数据
if (page.value === 1) {
records.value = newRecords;
console.log('第一页数据,替换列表,共', newRecords.length, '条');
} else {
records.value.push(...newRecords);
console.log('第', page.value, '页数据,追加到列表,新增', newRecords.length, '条');
}
// 判断是否还有更多数据,兼容不同接口字段
if (typeof flower_data.isLastPage === 'boolean') {
noMore.value = flower_data.isLastPage;
} else if (Number(flower_data.pages) > 0) {
noMore.value = page.value >= Number(flower_data.pages);
} else {
// 未返回分页字段时,仅在本页无数据时判定到底
noMore.value = newRecords.length === 0;
}
console.log('是否最后一页:', noMore.value, ',总页数:', flower_data.pages);
}
console.log('[myFlower] getFlowerList:end', {
page: page.value,
records: records.value.length,
noMore: noMore.value
});
resolve(res);
} else {
console.log('[myFlower] getFlowerList:invalid', {
code: res?.code,
msg: res?.msg
});
reject(new Error('接口返回错误'));
}
}).catch(err => {
console.error('获取鲜花列表失败:', err);
uni.showToast({ title: '获取数据失败', icon: 'error' });
reject(err);
}).finally(() => {
// 只在第一页时重置loading状态
if (page.value === 1) {
loading.value = false;
}
refreshing.value = false;
});
});
};
const onRefresh = async () => {
if (refreshing.value || loading.value) return;
refreshing.value = true;
try {
await loadFirstTwoPages();
uni.showToast({ title: '刷新成功', icon: 'none' });
} catch (error) {
console.error('刷新失败:', error);
uni.showToast({ title: '刷新失败', icon: 'error' });
}
};
const onLoadMore = async () => {
console.log('触发上拉加载更多事件');
console.log('[myFlower] onLoadMore:enter', {
page: page.value,
loading: loading.value,
refreshing: refreshing.value,
noMore: noMore.value
});
// 检查各种状态
if (loading.value || refreshing.value) {
console.log('正在加载中,忽略重复请求');
console.log('[myFlower] onLoadMore:blockByState');
return;
}
if (noMore.value) {
console.log('已经没有更多数据了');
console.log('[myFlower] onLoadMore:blockByNoMore');
return;
}
console.log('开始加载下一页,当前页码:', page.value);
loading.value = true;
const currentPage = page.value;
try {
page.value = currentPage + 1;
console.log('[myFlower] onLoadMore:request', {
fromPage: currentPage,
toPage: page.value
});
await getFlowerList(); // 等待数据加载完成
console.log('加载完成,当前页码:', page.value);
console.log('[myFlower] onLoadMore:done', {
page: page.value,
records: records.value.length,
noMore: noMore.value
});
} catch (error) {
console.error('加载更多失败:', error);
page.value = currentPage; // 恢复页码
console.log('[myFlower] onLoadMore:rollback', { page: page.value });
uni.showToast({ title: '加载失败', icon: 'error' });
} finally {
loading.value = false;
}
};
const getListLayoutInfo = () => {
return new Promise((resolve) => {
const query = uni.createSelectorQuery();
query.select('.table-body').boundingClientRect();
query.select('.list-content').boundingClientRect();
query.exec((res = []) => {
const bodyHeight = Number(res?.[0]?.height || 0);
const contentHeight = Number(res?.[1]?.height || 0);
resolve({ bodyHeight, contentHeight });
});
});
};
const ensureListCanScroll = async () => {
if (autoLoading.value || loading.value || refreshing.value || noMore.value) return;
autoLoading.value = true;
try {
while (!noMore.value) {
await nextTick();
await new Promise((resolve) => setTimeout(resolve, 50));
const { bodyHeight, contentHeight } = await getListLayoutInfo();
if (!bodyHeight || !contentHeight) break;
if (contentHeight > bodyHeight) break;
await onLoadMore();
if (loading.value) break;
}
} finally {
autoLoading.value = false;
}
};
const loadFirstTwoPages = async () => {
console.log('[myFlower] loadFirstTwoPages:start');
page.value = 1;
noMore.value = false;
records.value = [];
console.log('[myFlower] loadFirstTwoPages:reset', {
page: page.value,
records: records.value.length,
noMore: noMore.value
});
await getFlowerList();
// 第1页 Promise resolve 早于 finally 复位 loading这里手动兜底复位
loading.value = false;
console.log('[myFlower] loadFirstTwoPages:afterPage1', {
page: page.value,
records: records.value.length,
noMore: noMore.value
});
if (!noMore.value) {
console.log('[myFlower] loadFirstTwoPages:triggerPage2');
const firstPage = page.value;
try {
page.value = firstPage + 1;
console.log('[myFlower] loadFirstTwoPages:requestPage2', {
fromPage: firstPage,
toPage: page.value,
refreshing: refreshing.value
});
await getFlowerList();
loading.value = false;
} catch (error) {
page.value = firstPage;
console.log('[myFlower] loadFirstTwoPages:page2FailedRollback', { page: page.value });
throw error;
}
} else {
console.log('[myFlower] loadFirstTwoPages:skipPage2(noMore=true)');
}
console.log('[myFlower] loadFirstTwoPages:end', {
page: page.value,
records: records.value.length,
noMore: noMore.value
});
};
// 测试加载更多功能
const testLoadMore = () => {
console.log('手动测试加载更多');
onLoadMore();
};
</script>
<style lang="scss" scoped>
$theme: #8B2316;
$bg: #ffffff;
$text: #333;
$muted: #999;
$card: #ffffff;
.flower-page {
background: $bg;
--nav-offset: calc(var(--status-bar-height) + 44px);
--stats-height: 86rpx;
--header-height: 74rpx;
/* 导航栏下方固定区域总高度:统计栏 + 表头 */
--fixed-block-height: calc(var(--stats-height) + var(--header-height));
}
.fixed-top-block {
position: fixed;
top: var(--nav-offset);
left: 0;
right: 0;
z-index: 20;
}
.stats-bar {
display: flex;
align-items: center;
height: var(--stats-height);
padding: 0 30rpx;
box-sizing: border-box;
background: #fff;
border-bottom: 2rpx solid #eee;
.stat { display: flex; align-items: center; gap: 16rpx; }
.stat:last-child{
margin-left: 120px;
}
.icon { font-size: 34rpx; }
.num { font-size: 30rpx; color: #333; }
}
.table-header {
background: #9e3a2e;
color: #fff;
display: flex;
justify-content: space-between;
align-items: center;
height: var(--header-height);
padding: 0 30rpx;
box-sizing: border-box;
font-size: 30rpx;
.col { width: 33%; }
.name { text-align: left; }
.time { text-align: center; }
.qty { text-align: center; }
}
.table-body {
position: fixed;
top: calc(var(--nav-offset) + var(--fixed-block-height));
left: 0;
right: 0;
bottom: 0;
box-sizing: border-box;
/* 充满剩余高度,避免滚动穿透 */
}
.row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 26rpx 30rpx;
background: #fff;
border-bottom: 2rpx solid #ccc;
font-size: 28rpx;
color: #333;
.cell { width: 33%; }
.name { text-align: left; }
.time { text-align: center; color: #666; }
.qty { text-align: center; }
.qty.plus { color: #333; }
.qty.minus { color: #333; }
}
.empty-wrap { padding-top: 200rpx; display: flex; flex-direction: column; align-items: center; color: #bdbdbd; }
.empty-icon { width: 220rpx; height: 220rpx; opacity: .4; }
.empty-text { margin-top: 20rpx; font-size: 30rpx; }
.loading, .no-more {
text-align: center;
color: #9aa0a6;
padding: 30rpx 0;
font-size: 26rpx;
text {
display: inline-block;
padding: 10rpx 20rpx;
background: #f5f5f5;
border-radius: 20rpx;
}
}
.debug-actions {
height: 50rpx;
text-align: center;
padding: 20rpx;
border-top: 1rpx solid #eee;
button {
margin-bottom: 10rpx;
}
.debug-info {
display: block;
font-size: 24rpx;
color: #999;
background: #f8f8f8;
padding: 10rpx;
border-radius: 8rpx;
}
}
</style>