685 lines
17 KiB
Vue
685 lines
17 KiB
Vue
<template>
|
|
<view class="service-records-page">
|
|
<!-- 头部区域 -->
|
|
<NavHeader title="服务记录" />
|
|
|
|
<!-- Tab 切换 -->
|
|
<view class="tab-bar">
|
|
<view
|
|
class="tab-item"
|
|
:class="{ active: currentTab === 'pending_payment' }"
|
|
@click="switchTab('pending_payment')"
|
|
>
|
|
<text class="tab-text">待支付</text>
|
|
</view>
|
|
<view
|
|
class="tab-item"
|
|
:class="{ active: currentTab === 'pending_verification' }"
|
|
@click="switchTab('pending_verification')"
|
|
>
|
|
<text class="tab-text">待核销</text>
|
|
</view>
|
|
<view
|
|
class="tab-item"
|
|
:class="{ active: currentTab === 'completed' }"
|
|
@click="switchTab('completed')"
|
|
>
|
|
<text class="tab-text">已完成</text>
|
|
</view>
|
|
<view
|
|
class="tab-item"
|
|
:class="{ active: currentTab === 'cancelled' }"
|
|
@click="switchTab('cancelled')"
|
|
>
|
|
<text class="tab-text">已取消</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 列表内容 -->
|
|
<scroll-view
|
|
class="record-list"
|
|
scroll-y="true"
|
|
:refresher-enabled="true"
|
|
:refresher-triggered="refreshing"
|
|
@refresherrefresh="handleRefresh"
|
|
@scrolltolower="handleLoadMore"
|
|
:lower-threshold="100"
|
|
>
|
|
<!-- 空数据提示 -->
|
|
<view class="empty-state" v-if="!loading && currentList.length === 0">
|
|
<image
|
|
class="empty-icon"
|
|
src="/static/home/entry_icon.png"
|
|
mode="aspectFit"
|
|
></image>
|
|
<text class="empty-text">暂无{{ getTabLabel() }}记录</text>
|
|
</view>
|
|
|
|
<!-- 记录列表项 -->
|
|
<view
|
|
class="record-item"
|
|
v-for="(item, index) in currentList"
|
|
:key="index"
|
|
@click="handleRecordClick(item)"
|
|
>
|
|
<view class="record-header">
|
|
<view class="record-title-row">
|
|
<text class="record-title">{{ item.serviceName }}</text>
|
|
<view class="status-badge" :class="getStatusClass(item.status)">
|
|
<text class="status-text">{{ getStatusText(item.status) }}</text>
|
|
</view>
|
|
</view>
|
|
<text class="record-time">{{ item.createTime }}</text>
|
|
</view>
|
|
|
|
<view class="record-content">
|
|
<view class="record-info-row">
|
|
<text class="info-label">服务类型:</text>
|
|
<text class="info-value">{{ item.serviceType }}</text>
|
|
</view>
|
|
<view class="record-info-row">
|
|
<text class="info-label">服务门店:</text>
|
|
<text class="info-value">{{ item.storeName }}</text>
|
|
</view>
|
|
<view class="record-info-row">
|
|
<text class="info-label">订单号:</text>
|
|
<text class="info-value">{{ item.orderNo }}</text>
|
|
</view>
|
|
<view class="record-info-row">
|
|
<text class="info-label">订单金额:</text>
|
|
<text class="info-value price">¥{{ item.amount }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 操作按钮区域 -->
|
|
<view class="record-actions" v-if="item.status === 'pending_payment'">
|
|
<button
|
|
class="action-btn cancel-btn"
|
|
@click.stop="handleCancel(item)"
|
|
>
|
|
取消订单
|
|
</button>
|
|
<button class="action-btn pay-btn" @click.stop="handlePay(item)">
|
|
立即支付
|
|
</button>
|
|
</view>
|
|
<view
|
|
class="record-actions"
|
|
v-else-if="item.status === 'pending_verification'"
|
|
>
|
|
<button
|
|
class="action-btn detail-btn"
|
|
@click.stop="handleViewDetail(item)"
|
|
>
|
|
查看详情
|
|
</button>
|
|
</view>
|
|
<view class="record-actions" v-else-if="item.status === 'completed'">
|
|
<button
|
|
class="action-btn detail-btn"
|
|
@click.stop="handleViewDetail(item)"
|
|
>
|
|
查看详情
|
|
</button>
|
|
<button
|
|
class="action-btn review-btn"
|
|
@click.stop="handleReview(item)"
|
|
>
|
|
评价
|
|
</button>
|
|
</view>
|
|
<view class="record-actions" v-else-if="item.status === 'cancelled'">
|
|
<button
|
|
class="action-btn detail-btn"
|
|
@click.stop="handleViewDetail(item)"
|
|
>
|
|
查看详情
|
|
</button>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 加载更多提示 -->
|
|
<view class="load-more" v-if="currentList.length > 0">
|
|
<text v-if="loadingMore" class="load-more-text">加载中...</text>
|
|
<text v-else-if="!hasMore" class="load-more-text">没有更多数据了</text>
|
|
<text v-else class="load-more-text">上拉加载更多</text>
|
|
</view>
|
|
</scroll-view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import NavHeader from "@/components/NavHeader/NavHeader.vue";
|
|
|
|
export default {
|
|
components: {
|
|
NavHeader
|
|
},
|
|
data() {
|
|
return {
|
|
currentTab: "pending_payment", // 当前选中的 tab
|
|
refreshing: false,
|
|
loading: false,
|
|
loadingMore: false,
|
|
hasMore: true,
|
|
pageNo: 1,
|
|
pageSize: 10,
|
|
// 假数据
|
|
mockData: {
|
|
pending_payment: [
|
|
{
|
|
id: 1,
|
|
orderNo: "ORD20250101001",
|
|
serviceName: "汽车维修保养",
|
|
serviceType: "维修服务",
|
|
storeName: "XX汽车维修中心",
|
|
amount: "299.00",
|
|
createTime: "2025-01-15 10:30:00",
|
|
status: "pending_payment",
|
|
},
|
|
{
|
|
id: 2,
|
|
orderNo: "ORD20250101002",
|
|
serviceName: "家电清洗服务",
|
|
serviceType: "清洗服务",
|
|
storeName: "XX家电清洗店",
|
|
amount: "158.00",
|
|
createTime: "2025-01-14 15:20:00",
|
|
status: "pending_payment",
|
|
},
|
|
{
|
|
id: 3,
|
|
orderNo: "ORD20250101003",
|
|
serviceName: "手机维修",
|
|
serviceType: "维修服务",
|
|
storeName: "XX手机维修店",
|
|
amount: "199.00",
|
|
createTime: "2025-01-13 09:15:00",
|
|
status: "pending_payment",
|
|
},
|
|
],
|
|
pending_verification: [
|
|
{
|
|
id: 4,
|
|
orderNo: "ORD20250101004",
|
|
serviceName: "汽车维修保养",
|
|
serviceType: "维修服务",
|
|
storeName: "XX汽车维修中心",
|
|
amount: "299.00",
|
|
createTime: "2025-01-10 14:30:00",
|
|
status: "pending_verification",
|
|
},
|
|
{
|
|
id: 5,
|
|
orderNo: "ORD20250101005",
|
|
serviceName: "家电清洗服务",
|
|
serviceType: "清洗服务",
|
|
storeName: "XX家电清洗店",
|
|
amount: "158.00",
|
|
createTime: "2025-01-09 11:20:00",
|
|
status: "pending_verification",
|
|
},
|
|
],
|
|
completed: [
|
|
{
|
|
id: 6,
|
|
orderNo: "ORD20250101006",
|
|
serviceName: "汽车维修保养",
|
|
serviceType: "维修服务",
|
|
storeName: "XX汽车维修中心",
|
|
amount: "299.00",
|
|
createTime: "2025-01-05 16:30:00",
|
|
status: "completed",
|
|
},
|
|
{
|
|
id: 7,
|
|
orderNo: "ORD20250101007",
|
|
serviceName: "家电清洗服务",
|
|
serviceType: "清洗服务",
|
|
storeName: "XX家电清洗店",
|
|
amount: "158.00",
|
|
createTime: "2025-01-04 10:20:00",
|
|
status: "completed",
|
|
},
|
|
{
|
|
id: 8,
|
|
orderNo: "ORD20250101008",
|
|
serviceName: "手机维修",
|
|
serviceType: "维修服务",
|
|
storeName: "XX手机维修店",
|
|
amount: "199.00",
|
|
createTime: "2025-01-03 08:15:00",
|
|
status: "completed",
|
|
},
|
|
],
|
|
cancelled: [
|
|
{
|
|
id: 9,
|
|
orderNo: "ORD20250101009",
|
|
serviceName: "汽车维修保养",
|
|
serviceType: "维修服务",
|
|
storeName: "XX汽车维修中心",
|
|
amount: "299.00",
|
|
createTime: "2025-01-02 14:30:00",
|
|
status: "cancelled",
|
|
},
|
|
{
|
|
id: 10,
|
|
orderNo: "ORD20250101010",
|
|
serviceName: "家电清洗服务",
|
|
serviceType: "清洗服务",
|
|
storeName: "XX家电清洗店",
|
|
amount: "158.00",
|
|
createTime: "2025-01-01 11:20:00",
|
|
status: "cancelled",
|
|
},
|
|
],
|
|
},
|
|
};
|
|
},
|
|
computed: {
|
|
currentList() {
|
|
return this.mockData[this.currentTab] || [];
|
|
},
|
|
},
|
|
onLoad() {
|
|
this.loadData();
|
|
},
|
|
methods: {
|
|
// 切换 Tab
|
|
switchTab(tab) {
|
|
if (this.currentTab === tab) return;
|
|
this.currentTab = tab;
|
|
this.pageNo = 1;
|
|
this.hasMore = true;
|
|
this.loadData();
|
|
},
|
|
// 获取 Tab 标签文本
|
|
getTabLabel() {
|
|
const labels = {
|
|
pending_payment: "待支付",
|
|
pending_verification: "待核销",
|
|
completed: "已完成",
|
|
cancelled: "已取消",
|
|
};
|
|
return labels[this.currentTab] || "";
|
|
},
|
|
// 获取状态文本
|
|
getStatusText(status) {
|
|
const statusMap = {
|
|
pending_payment: "待支付",
|
|
pending_verification: "待核销",
|
|
completed: "已完成",
|
|
cancelled: "已取消",
|
|
};
|
|
return statusMap[status] || "";
|
|
},
|
|
// 获取状态样式类
|
|
getStatusClass(status) {
|
|
const classMap = {
|
|
pending_payment: "status-pending",
|
|
pending_verification: "status-verification",
|
|
completed: "status-completed",
|
|
cancelled: "status-cancelled",
|
|
};
|
|
return classMap[status] || "";
|
|
},
|
|
// 加载数据
|
|
loadData() {
|
|
this.loading = true;
|
|
// 模拟数据加载
|
|
setTimeout(() => {
|
|
this.loading = false;
|
|
this.refreshing = false;
|
|
this.loadingMore = false;
|
|
// 这里使用假数据,实际应该调用接口
|
|
}, 500);
|
|
},
|
|
// 下拉刷新
|
|
handleRefresh() {
|
|
this.refreshing = true;
|
|
this.pageNo = 1;
|
|
this.hasMore = true;
|
|
this.loadData();
|
|
},
|
|
// 上拉加载更多
|
|
handleLoadMore() {
|
|
if (this.hasMore && !this.loadingMore && !this.loading) {
|
|
this.loadingMore = true;
|
|
this.pageNo += 1;
|
|
// 模拟加载更多
|
|
setTimeout(() => {
|
|
this.loadingMore = false;
|
|
// 假数据没有更多了
|
|
this.hasMore = false;
|
|
}, 500);
|
|
}
|
|
},
|
|
// 点击记录项
|
|
handleRecordClick(item) {
|
|
// 可以跳转到详情页
|
|
console.log("点击记录:", item);
|
|
},
|
|
// 取消订单
|
|
handleCancel(item) {
|
|
uni.showModal({
|
|
title: "提示",
|
|
content: "确定要取消该订单吗?",
|
|
success: (res) => {
|
|
if (res.confirm) {
|
|
uni.showToast({
|
|
title: "订单已取消",
|
|
icon: "success",
|
|
});
|
|
// 这里应该调用接口取消订单,然后刷新列表
|
|
// 暂时从待支付列表中移除
|
|
const index = this.mockData.pending_payment.findIndex(
|
|
(i) => i.id === item.id
|
|
);
|
|
if (index > -1) {
|
|
this.mockData.pending_payment.splice(index, 1);
|
|
// 添加到已取消列表
|
|
this.mockData.cancelled.unshift({
|
|
...item,
|
|
status: "cancelled",
|
|
});
|
|
}
|
|
}
|
|
},
|
|
});
|
|
},
|
|
// 立即支付
|
|
handlePay(item) {
|
|
uni.showToast({
|
|
title: "跳转支付页面",
|
|
icon: "none",
|
|
});
|
|
// 这里应该跳转到支付页面
|
|
// uni.navigateTo({
|
|
// url: `/pages/payment/payment?orderNo=${item.orderNo}&amount=${item.amount}`
|
|
// });
|
|
},
|
|
// 查看详情
|
|
handleViewDetail(item) {
|
|
uni.showToast({
|
|
title: "查看详情",
|
|
icon: "none",
|
|
});
|
|
// 这里应该跳转到详情页面
|
|
// uni.navigateTo({
|
|
// url: `/pages/order/detail?orderNo=${item.orderNo}`
|
|
// });
|
|
},
|
|
// 评价
|
|
handleReview(item) {
|
|
uni.showToast({
|
|
title: "跳转评价页面",
|
|
icon: "none",
|
|
});
|
|
// 这里应该跳转到评价页面
|
|
// uni.navigateTo({
|
|
// url: `/pages/order/review?orderNo=${item.orderNo}`
|
|
// });
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.service-records-page {
|
|
min-height: 100vh;
|
|
background: #e2e8f1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
/* Tab 切换栏 */
|
|
.tab-bar {
|
|
display: flex;
|
|
padding: 0 20rpx;
|
|
|
|
.tab-item {
|
|
flex: 1;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 88rpx;
|
|
position: relative;
|
|
|
|
.tab-text {
|
|
font-family: PingFang-SC, PingFang-SC;
|
|
font-weight: 500;
|
|
font-size: 28rpx;
|
|
color: #999999;
|
|
}
|
|
|
|
&.active {
|
|
.tab-text {
|
|
color: #004294;
|
|
font-weight: 600;
|
|
}
|
|
|
|
&::after {
|
|
content: "";
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
width: 60rpx;
|
|
height: 4rpx;
|
|
background-color: #004294;
|
|
border-radius: 2rpx;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 列表区域 */
|
|
.record-list {
|
|
flex: 1;
|
|
padding: 20rpx;
|
|
height: 0; // 配合 flex: 1 使用
|
|
box-sizing: border-box;
|
|
|
|
/* 空数据提示 */
|
|
.empty-state {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 200rpx 0;
|
|
min-height: 500rpx;
|
|
|
|
.empty-icon {
|
|
width: 200rpx;
|
|
height: 200rpx;
|
|
margin-bottom: 40rpx;
|
|
opacity: 0.5;
|
|
}
|
|
|
|
.empty-text {
|
|
font-family: PingFang-SC, PingFang-SC;
|
|
font-weight: 500;
|
|
font-size: 28rpx;
|
|
color: #999999;
|
|
}
|
|
}
|
|
|
|
/* 加载更多提示 */
|
|
.load-more {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
padding: 40rpx 0;
|
|
min-height: 80rpx;
|
|
|
|
.load-more-text {
|
|
font-family: PingFang-SC, PingFang-SC;
|
|
font-weight: 400;
|
|
font-size: 24rpx;
|
|
color: #999999;
|
|
}
|
|
}
|
|
|
|
/* 记录项 */
|
|
.record-item {
|
|
background-color: #ffffff;
|
|
border-radius: 20rpx;
|
|
margin-bottom: 20rpx;
|
|
padding: 30rpx;
|
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
|
|
|
|
.record-header {
|
|
margin-bottom: 24rpx;
|
|
padding-bottom: 20rpx;
|
|
border-bottom: 1rpx solid #f0f0f0;
|
|
|
|
.record-title-row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-bottom: 12rpx;
|
|
|
|
.record-title {
|
|
flex: 1;
|
|
font-family: PingFang-SC, PingFang-SC;
|
|
font-weight: bold;
|
|
font-size: 30rpx;
|
|
color: #1a1819;
|
|
}
|
|
|
|
.status-badge {
|
|
padding: 6rpx 16rpx;
|
|
border-radius: 20rpx;
|
|
font-size: 22rpx;
|
|
|
|
.status-text {
|
|
font-family: PingFang-SC, PingFang-SC;
|
|
font-weight: 500;
|
|
}
|
|
|
|
&.status-pending {
|
|
background-color: rgba(255, 107, 0, 0.1);
|
|
.status-text {
|
|
color: #ff6b00;
|
|
}
|
|
}
|
|
|
|
&.status-verification {
|
|
background-color: rgba(0, 66, 148, 0.1);
|
|
.status-text {
|
|
color: #004294;
|
|
}
|
|
}
|
|
|
|
&.status-completed {
|
|
background-color: rgba(76, 175, 80, 0.1);
|
|
.status-text {
|
|
color: #4caf50;
|
|
}
|
|
}
|
|
|
|
&.status-cancelled {
|
|
background-color: rgba(158, 158, 158, 0.1);
|
|
.status-text {
|
|
color: #9e9e9e;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.record-time {
|
|
font-family: PingFang-SC, PingFang-SC;
|
|
font-weight: 400;
|
|
font-size: 22rpx;
|
|
color: #999999;
|
|
}
|
|
}
|
|
|
|
.record-content {
|
|
margin-bottom: 24rpx;
|
|
|
|
.record-info-row {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
margin-bottom: 16rpx;
|
|
line-height: 1.5;
|
|
|
|
&:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.info-label {
|
|
font-family: PingFang-SC, PingFang-SC;
|
|
font-weight: 500;
|
|
font-size: 24rpx;
|
|
color: #888888;
|
|
margin-right: 8rpx;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.info-value {
|
|
font-family: PingFang-SC, PingFang-SC;
|
|
font-weight: 500;
|
|
font-size: 24rpx;
|
|
color: #333333;
|
|
flex: 1;
|
|
|
|
&.price {
|
|
color: #d51c3c;
|
|
font-weight: 600;
|
|
font-size: 28rpx;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.record-actions {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: flex-end;
|
|
gap: 20rpx;
|
|
padding-top: 20rpx;
|
|
border-top: 1rpx solid #f0f0f0;
|
|
|
|
button{
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
.action-btn {
|
|
width: 131rpx;
|
|
height: 50rpx;
|
|
border-radius: 10rpx;
|
|
font-family: PingFang-SC, PingFang-SC;
|
|
font-weight: 500;
|
|
font-size: 26rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
|
|
&.cancel-btn {
|
|
background-color: #f5f5f5;
|
|
color: #666666;
|
|
}
|
|
|
|
&.pay-btn {
|
|
background-color: #004294;
|
|
color: #ffffff;
|
|
}
|
|
|
|
&.detail-btn {
|
|
background-color: #f5f5f5;
|
|
color: #666666;
|
|
}
|
|
|
|
&.review-btn {
|
|
background-color: #004294;
|
|
color: #ffffff;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style>
|