Commit b4717592 by liangjianmin

feat(approve): add skeleton loading and pagination to approval list

- Add skeleton card component with loading animation for initial page load
- Implement independent pagination for pending and done tabs with page tracking
- Add loadMore functionality with infinite scroll on page bottom reach
- Integrate u-loadmore component to display pagination status
- Refactor list rendering with conditional skeleton display and template wrapper
- Add skeleton-card styling with consistent card appearance and shadow
- Update getData method to support paginated API requests with page and limit parameters
- Add resetAndLoad method to reset pagination when switching tabs
- Improve UX by showing loading state during initial data fetch
parent 69e83bda
...@@ -33,6 +33,14 @@ ...@@ -33,6 +33,14 @@
.list-wrap { .list-wrap {
padding: 0 28rpx; padding: 0 28rpx;
.skeleton-card {
background: #ffffff;
border-radius: 16rpx;
margin-bottom: 24rpx;
padding: 28rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.05);
}
} }
.approve-card { .approve-card {
......
...@@ -9,40 +9,52 @@ ...@@ -9,40 +9,52 @@
</view> </view>
<view class="list-wrap"> <view class="list-wrap">
<view class="approve-card" v-for="item in currentList" :key="item.id"> <!-- 骨架屏:首次加载时展示 -->
<view class="card-title">费用减免申请</view> <view v-if="skeletonLoading">
<view class="card-body"> <view class="skeleton-card" v-for="i in 3" :key="i">
<view class="card-row row"> <u-skeleton rows="3" title :animate="true" :loading="true" :rowsWidth="['100%', '60%', '40%']" rowsHeight="30"></u-skeleton>
<text class="card-label">申请人:</text> </view>
<text class="card-value">{{ item.applicant_name }}</text> </view>
</view>
<view class="card-row row"> <!-- 列表内容 -->
<text class="card-label">客户名称:</text> <template v-else>
<text class="card-value">{{ item.customer_name }}</text> <view class="approve-card" v-for="item in currentList" :key="item.id">
<view class="card-title">费用减免申请</view>
<view class="card-body">
<view class="card-row row">
<text class="card-label">申请人:</text>
<text class="card-value">{{ item.applicant_name }}</text>
</view>
<view class="card-row row">
<text class="card-label">客户名称:</text>
<text class="card-value">{{ item.customer_name }}</text>
</view>
<view class="card-row row">
<text class="card-label">订单号:</text>
<text class="card-value">{{ item.order_sn }}</text>
</view>
<view class="card-row row verCenter bothSide">
<view class="row verCenter">
<u-icon name="clock-fill" color="#c0c4cc"></u-icon>
<text class="card-time">{{ item.create_time_cn }}</text>
</view>
<text class="card-tag">{{ item.relief_detail }}</text>
</view>
</view> </view>
<view class="card-row row"> <view class="card-footer row rowCenter" v-if="currentTab === 0">
<text class="card-label">订单号:</text> <view class="action-btn approve" @click="handleApprove(item)">审批</view>
<text class="card-value">{{ item.order_sn }}</text>
</view> </view>
<view class="card-row row verCenter bothSide"> <view class="card-status-bar row rowCenter" v-else>
<view class="row verCenter"> <text :class="['status-text', item.status === 1 ? 'approved' : 'rejected']">
<u-icon name="clock-fill" color="#c0c4cc"></u-icon> {{ item.status_cn }}
<text class="card-time">{{ item.create_time_cn }}</text> </text>
</view>
<text class="card-tag">{{ item.relief_detail }}</text>
</view> </view>
</view> </view>
<view class="card-footer row rowCenter" v-if="currentTab === 0">
<view class="action-btn approve" @click="handleApprove(item)">审批</view>
</view>
<view class="card-status-bar row rowCenter" v-else>
<text :class="['status-text', item.status === 1 ? 'approved' : 'rejected']">
{{ item.status_cn }}
</text>
</view>
</view>
<u-empty v-if="!currentList.length" mode="data" :text="currentTab === 0 ? '暂无待处理审批' : '暂无已处理审批'" iconSize="140" textSize="24"></u-empty> <u-empty v-if="!currentList.length" mode="data" :text="currentTab === 0 ? '暂无待处理审批' : '暂无已处理审批'" iconSize="140" textSize="24"></u-empty>
<u-loadmore v-if="currentList.length" :status="loadStatus" @loadmore="loadMore" marginTop="10" marginBottom="30" fontSize="28" iconSize="25" :line="true" lineColor="#e4e7ed"></u-loadmore>
</template>
</view> </view>
</view> </view>
</template> </template>
...@@ -60,58 +72,133 @@ ...@@ -60,58 +72,133 @@
{ name: '已处理' } { name: '已处理' }
], ],
pendingList: [], pendingList: [],
doneList: [] doneList: [],
// 分页参数(两个 tab 各自独立)
pendingPage: 1,
donePage: 1,
limit: 10,
pendingTotal: 0,
doneTotal: 0,
// 骨架屏:首次进入 tab 且无缓存数据时显示
skeletonLoading: false,
// loadMore 状态
loadStatus: 'loadmore'
}; };
}, },
computed: { computed: {
currentList() { currentList() {
return this.currentTab === 0 ? this.pendingList : this.doneList; return this.currentTab === 0 ? this.pendingList : this.doneList;
},
currentPage() {
return this.currentTab === 0 ? this.pendingPage : this.donePage;
},
currentTotal() {
return this.currentTab === 0 ? this.pendingTotal : this.doneTotal;
} }
}, },
onShow() { onShow() {
this.getData(); this.resetAndLoad();
},
onReachBottom() {
this.loadMore();
}, },
methods: { methods: {
/** /**
* 获取审批列表,根据当前 Tab 传不同 status * 重置当前 tab 分页并重新加载
* @return {void}
*/
resetAndLoad() {
if (this.currentTab === 0) {
this.pendingPage = 1;
this.pendingList = [];
} else {
this.donePage = 1;
this.doneList = [];
}
this.skeletonLoading = true;
this.loadStatus = 'loadmore';
this.getData();
},
/**
* 获取审批列表,支持分页追加
* @return {void} * @return {void}
*/ */
getData() { getData() {
var page = this.currentTab === 0 ? this.pendingPage : this.donePage;
var statusVal = this.currentTab === 0 ? '0' : '1'; var statusVal = this.currentTab === 0 ? '0' : '1';
var params = { status: statusVal }; var params = {
status: statusVal,
page,
limit: this.limit
};
if (this.order_sn) { if (this.order_sn) {
params.order_sn = this.order_sn; params.order_sn = this.order_sn;
} }
this.request(API.feeApproveList, 'GET', params, true).then(res => { this.request(API.feeApproveList, 'GET', params, !this.skeletonLoading).then(res => {
this.skeletonLoading = false;
if (res.code === 0) { if (res.code === 0) {
var { list = [], total = 0 } = res.data || {}; var { list = [], total = 0 } = res.data || {};
if (this.currentTab === 0) { if (this.currentTab === 0) {
this.pendingList = list; this.pendingList = page === 1 ? list : [...this.pendingList, ...list];
this.pendingTotal = total;
this.$set(this.tabList, 0, { name: total ? `待处理(${total})` : '待处理' }); this.$set(this.tabList, 0, { name: total ? `待处理(${total})` : '待处理' });
} else { } else {
this.doneList = list; this.doneList = page === 1 ? list : [...this.doneList, ...list];
this.doneTotal = total;
} }
this.updateLoadStatus();
} }
}).catch(() => {
this.skeletonLoading = false;
this.loadStatus = 'loadmore';
}); });
}, },
/** /**
* 搜索 * 根据当前数据量与总数更新底部加载状态
* @return {void} * @return {void}
*/ */
onSearch() { updateLoadStatus() {
var listLen = this.currentTab === 0 ? this.pendingList.length : this.doneList.length;
var total = this.currentTab === 0 ? this.pendingTotal : this.doneTotal;
this.loadStatus = listLen >= total ? 'nomore' : 'loadmore';
},
/**
* 加载下一页
* @return {void}
*/
loadMore() {
if (this.loadStatus !== 'loadmore') return;
this.loadStatus = 'loading';
if (this.currentTab === 0) {
this.pendingPage++;
} else {
this.donePage++;
}
this.getData(); this.getData();
}, },
/** /**
* Tab 切换 * 搜索时重置分页
* @return {void}
*/
onSearch() {
this.resetAndLoad();
},
/**
* Tab 切换,如果目标 tab 无缓存则显示骨架屏
* @param {Object} item - 选中的 tab 项 * @param {Object} item - 选中的 tab 项
* @return {void} * @return {void}
*/ */
onTabChange(item) { onTabChange(item) {
this.currentTab = item.index; this.currentTab = item.index;
this.getData(); var targetList = this.currentTab === 0 ? this.pendingList : this.doneList;
if (!targetList.length) {
this.resetAndLoad();
} else {
this.updateLoadStatus();
}
}, },
/** /**
* 审批 * 跳转审批详情
* @param {Object} item - 审批项 * @param {Object} item - 审批项
* @return {void} * @return {void}
*/ */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment