177 lines
6.2 KiB
Vue
177 lines
6.2 KiB
Vue
<template>
|
||
<view class="orders-page">
|
||
<!-- 导航栏 -->
|
||
<view class="nav-bar">
|
||
<text class="nav-back" @click="goBack">‹</text>
|
||
<text class="nav-title">我的订单</text>
|
||
<view style="width:40px;"></view>
|
||
</view>
|
||
|
||
<!-- Tab切换 -->
|
||
<view class="tab-bar">
|
||
<view
|
||
v-for="tab in tabs"
|
||
:key="tab.key"
|
||
:class="['tab-item', { active: currentStatus === tab.key }]"
|
||
@click="currentStatus = tab.key"
|
||
>{{ tab.label }}</view>
|
||
</view>
|
||
|
||
<!-- 列表 -->
|
||
<view class="order-list">
|
||
<view v-for="item in filteredOrders" :key="item.id" class="order-item">
|
||
<view class="order-title">{{ item.title }}</view>
|
||
<view class="order-desc">{{ item.desc }}</view>
|
||
<view class="order-footer">
|
||
<text class="order-time">{{ item.time }}</text>
|
||
<view class="status-tag" :class="'status-' + item.status">{{ item.statusText }}</view>
|
||
</view>
|
||
<view v-if="item.status === 'new'" class="action-btns">
|
||
<view class="btn-sm btn-primary-sm" @click="startService(item)">开始服务</view>
|
||
<view class="btn-sm btn-default-sm" @click="cancelService(item)">取消</view>
|
||
</view>
|
||
<view v-else-if="item.status === 'doing'">
|
||
<view class="btn-sm btn-outline-sm" @click="goReport(item)">填写报告</view>
|
||
</view>
|
||
</view>
|
||
<view v-if="filteredOrders.length === 0" class="empty-wrap">
|
||
<text class="empty-icon">📦</text>
|
||
<text class="empty-text">暂无数据</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed, onMounted } from 'vue'
|
||
import { getAppointmentList, startAppointment, cancelAppointment } from '../../../utils/api.js'
|
||
|
||
const userInfo = JSON.parse(uni.getStorageSync('petstore_user') || '{}')
|
||
const currentUserId = userInfo.id
|
||
|
||
const currentStatus = ref('new')
|
||
const orders = ref([])
|
||
|
||
const tabs = [
|
||
{ key: 'new', label: '待确认' },
|
||
{ key: 'doing', label: '进行中' },
|
||
{ key: 'done', label: '已完成' }
|
||
]
|
||
|
||
const filteredOrders = computed(() => orders.value.filter(o => {
|
||
if (currentStatus.value === 'new') return o.status === 'new'
|
||
if (currentStatus.value === 'doing') return o.status === 'doing'
|
||
if (currentStatus.value === 'done') return o.status === 'done' || o.status === 'cancel'
|
||
return true
|
||
}))
|
||
|
||
const goBack = () => uni.navigateBack()
|
||
|
||
const fetchOrders = async () => {
|
||
if (!currentUserId) return
|
||
const res = await getAppointmentList(currentUserId)
|
||
if (res.code === 200) {
|
||
orders.value = res.data.map(appt => ({
|
||
id: appt.id,
|
||
title: appt.serviceType || '洗澡美容预约',
|
||
desc: `${appt.petType || ''} - ${appt.petName || ''}`,
|
||
time: appt.appointmentTime ? new Date(appt.appointmentTime).toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }) : '',
|
||
status: appt.status || 'new',
|
||
statusText: { new: '待确认', doing: '服务中', done: '已完成', cancel: '已取消' }[appt.status] || '待确认',
|
||
petName: appt.petName,
|
||
petType: appt.petType,
|
||
serviceType: appt.serviceType,
|
||
appointmentTime: appt.appointmentTime
|
||
}))
|
||
}
|
||
}
|
||
|
||
const startService = async (item) => {
|
||
const res = await startAppointment(item.id, userInfo.id)
|
||
if (res.code === 200) { uni.showToast({ title: '已开始服务', icon: 'success' }); fetchOrders() }
|
||
else { uni.showToast({ title: res.message || '操作失败', icon: 'none' }) }
|
||
}
|
||
|
||
const cancelService = async (item) => {
|
||
uni.showModal({
|
||
title: '确认取消',
|
||
content: '确定取消该预约?',
|
||
success: async (res) => {
|
||
if (res.confirm) {
|
||
const r = await cancelAppointment(item.id)
|
||
if (r.code === 200) { uni.showToast({ title: '已取消', icon: 'success' }); fetchOrders() }
|
||
else { uni.showToast({ title: r.message || '操作失败', icon: 'none' }) }
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
const goReport = (item) => {
|
||
uni.setStorageSync('petstore_report_prefill', JSON.stringify({
|
||
appointmentId: item.id, petName: item.petName, serviceType: item.serviceType, appointmentTime: item.appointmentTime
|
||
}))
|
||
uni.switchTab({ url: '/pages/report/report' })
|
||
}
|
||
|
||
onMounted(() => fetchOrders())
|
||
</script>
|
||
|
||
<style scoped>
|
||
.orders-page { padding-bottom: 20px; background: #f5f5f5; min-height: 100vh; }
|
||
.nav-bar {
|
||
background: #fff;
|
||
padding: 40px 16px 12px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
border-bottom: 1px solid #f0f0f0;
|
||
}
|
||
.nav-back { font-size: 28px; color: #333; font-weight: 300; }
|
||
.nav-title { font-size: 16px; font-weight: 600; color: #333; }
|
||
|
||
.tab-bar {
|
||
display: flex;
|
||
background: #fff;
|
||
padding: 0 16px;
|
||
border-bottom: 1px solid #eee;
|
||
}
|
||
.tab-item {
|
||
flex: 1;
|
||
text-align: center;
|
||
padding: 12px 0;
|
||
font-size: 14px;
|
||
color: #999;
|
||
border-bottom: 2px solid transparent;
|
||
}
|
||
.tab-item.active { color: #07c160; border-bottom-color: #07c160; font-weight: 600; }
|
||
|
||
.order-list { padding: 12px 16px 0; }
|
||
.order-item {
|
||
background: #fff;
|
||
border-radius: 12px;
|
||
padding: 16px;
|
||
margin-bottom: 12px;
|
||
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
|
||
}
|
||
.order-title { font-size: 15px; font-weight: 500; color: #333; margin-bottom: 6px; }
|
||
.order-desc { font-size: 13px; color: #999; margin-bottom: 10px; }
|
||
.order-footer { display: flex; justify-content: space-between; align-items: center; }
|
||
.order-time { font-size: 12px; color: #999; }
|
||
.action-btns { display: flex; gap: 8px; margin-top: 10px; }
|
||
|
||
.btn-sm { font-size: 12px; padding: 5px 14px; border-radius: 6px; display: inline-flex; align-items: center; justify-content: center; }
|
||
.btn-primary-sm { background: #07c160; color: #fff; border: 1px solid #07c160; }
|
||
.btn-default-sm { background: #fff; color: #666; border: 1px solid #ddd; }
|
||
.btn-outline-sm { background: #fff; color: #07c160; border: 1px solid #07c160; }
|
||
|
||
.status-tag { font-size: 12px; padding: 2px 8px; border-radius: 10px; }
|
||
.status-new { background: #fff3e8; color: #ff9f00; }
|
||
.status-doing { background: #e8f7ef; color: #07c160; }
|
||
.status-done { background: #f0f0f0; color: #888; }
|
||
.status-cancel { background: #f5f5f5; color: #999; }
|
||
|
||
.empty-wrap { display: flex; flex-direction: column; align-items: center; padding: 60px 0; }
|
||
.empty-icon { font-size: 48px; }
|
||
.empty-text { font-size: 14px; color: #999; margin-top: 12px; }
|
||
</style>
|