|
@@ -15,12 +15,39 @@
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div v-else class="order-cards">
|
|
<div v-else class="order-cards">
|
|
|
- <el-card v-for="order in orders" :key="order.id" class="order-card">
|
|
|
|
|
- <div class="order-info">
|
|
|
|
|
- <p>订单 ID: {{ order.id }}</p>
|
|
|
|
|
- <el-tag :type="statusTag(order.status)">{{ statusLabel(order.status) }}</el-tag>
|
|
|
|
|
|
|
+ <el-card v-for="order in orders" :key="order.id" class="order-card" shadow="hover">
|
|
|
|
|
+ <div class="order-header">
|
|
|
|
|
+ <span class="order-id">#{{ order.id }}</span>
|
|
|
|
|
+ <el-tag :type="statusTag(order.status)" size="small">{{ statusLabel(order.status) }}</el-tag>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="order-actions">
|
|
|
|
|
|
|
+ <div class="order-body">
|
|
|
|
|
+ <div class="order-field">
|
|
|
|
|
+ <span class="field-label">宠物</span>
|
|
|
|
|
+ <span>{{ order.petName || '-' }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="order-field">
|
|
|
|
|
+ <span class="field-label">服务类型</span>
|
|
|
|
|
+ <span>{{ typeLabel(order.type) }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="order-field">
|
|
|
|
|
+ <span class="field-label">服务套餐</span>
|
|
|
|
|
+ <span>{{ order.packageName || '-' }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="order-field">
|
|
|
|
|
+ <span class="field-label">服务日期</span>
|
|
|
|
|
+ <span>{{ formatDate(order.startDate) }} ~ {{ formatDate(order.endDate) }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="order-field">
|
|
|
|
|
+ <span class="field-label">总价</span>
|
|
|
|
|
+ <span class="price">¥{{ order.totalPrice }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="order-field">
|
|
|
|
|
+ <span class="field-label">定金</span>
|
|
|
|
|
+ <span>¥{{ order.deposit }} ({{ order.depositPaid ? '已付' : '未付' }})</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="order-footer">
|
|
|
|
|
+ <span class="order-time">创建时间: {{ formatDate(order.createdAt) }}</span>
|
|
|
<el-button size="small" @click="viewDetail(order.id)">查看详情</el-button>
|
|
<el-button size="small" @click="viewDetail(order.id)">查看详情</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
</el-card>
|
|
</el-card>
|
|
@@ -34,6 +61,8 @@ import { useRouter } from 'vue-router'
|
|
|
import { ElMessage } from 'element-plus'
|
|
import { ElMessage } from 'element-plus'
|
|
|
import type { Appointment } from '@/type'
|
|
import type { Appointment } from '@/type'
|
|
|
import { getAppointmentController } from '@/api/appointment-controller'
|
|
import { getAppointmentController } from '@/api/appointment-controller'
|
|
|
|
|
+import { getPetController } from '@/api/pet-controller'
|
|
|
|
|
+import { getPackageController } from '@/api/package-controller'
|
|
|
|
|
|
|
|
const router = useRouter()
|
|
const router = useRouter()
|
|
|
const activeStatus = ref('')
|
|
const activeStatus = ref('')
|
|
@@ -43,15 +72,28 @@ const loading = ref(false)
|
|
|
const loadOrders = async () => {
|
|
const loadOrders = async () => {
|
|
|
loading.value = true
|
|
loading.value = true
|
|
|
try {
|
|
try {
|
|
|
- const api = getAppointmentController()
|
|
|
|
|
- const res = await api.list1()
|
|
|
|
|
- if (res.code === 200 && res.data) {
|
|
|
|
|
- let list = res.data.map(a => ({
|
|
|
|
|
|
|
+ const [orderRes, petRes, pkgRes] = await Promise.all([
|
|
|
|
|
+ getAppointmentController().list1(),
|
|
|
|
|
+ getPetController().list(),
|
|
|
|
|
+ getPackageController().list4()
|
|
|
|
|
+ ])
|
|
|
|
|
+
|
|
|
|
|
+ const petMap = new Map<number, string>()
|
|
|
|
|
+ petRes.data?.forEach(p => { if (p.id != null && p.name) petMap.set(p.id, p.name) })
|
|
|
|
|
+
|
|
|
|
|
+ const pkgMap = new Map<number, string>()
|
|
|
|
|
+ pkgRes.data?.forEach(p => { if (p.id != null && p.name) pkgMap.set(p.id, p.name) })
|
|
|
|
|
+
|
|
|
|
|
+ if (orderRes.code === 200 && orderRes.data) {
|
|
|
|
|
+ let list = orderRes.data.map(a => ({
|
|
|
id: String(a.id ?? ''),
|
|
id: String(a.id ?? ''),
|
|
|
userId: String(a.userId ?? ''),
|
|
userId: String(a.userId ?? ''),
|
|
|
petId: String(a.petId ?? ''),
|
|
petId: String(a.petId ?? ''),
|
|
|
|
|
+ petName: a.petName || (a.petId != null ? petMap.get(a.petId) : undefined),
|
|
|
packageId: String(a.packageId ?? ''),
|
|
packageId: String(a.packageId ?? ''),
|
|
|
|
|
+ packageName: a.packageName || (a.packageId != null ? pkgMap.get(a.packageId) : undefined),
|
|
|
roomId: a.roomId ? String(a.roomId) : undefined,
|
|
roomId: a.roomId ? String(a.roomId) : undefined,
|
|
|
|
|
+ roomName: a.roomName,
|
|
|
type: (a.type as Appointment['type']) ?? 'GROOMING',
|
|
type: (a.type as Appointment['type']) ?? 'GROOMING',
|
|
|
startDate: a.startDate,
|
|
startDate: a.startDate,
|
|
|
endDate: a.endDate,
|
|
endDate: a.endDate,
|
|
@@ -90,6 +132,15 @@ const statusLabel = (status: string) => {
|
|
|
return map[status] || status
|
|
return map[status] || status
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+const typeLabel = (type: string) => {
|
|
|
|
|
+ return type === 'BOARDING' ? '寄养' : '美容'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const formatDate = (date?: string) => {
|
|
|
|
|
+ if (!date) return '-'
|
|
|
|
|
+ return date.replace('T', ' ').substring(0, 16)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
const statusTag = (status: string) => {
|
|
const statusTag = (status: string) => {
|
|
|
const map: Record<string, string> = {
|
|
const map: Record<string, string> = {
|
|
|
PENDING: 'warning',
|
|
PENDING: 'warning',
|
|
@@ -117,18 +168,62 @@ onMounted(loadOrders)
|
|
|
|
|
|
|
|
.order-card {
|
|
.order-card {
|
|
|
margin-bottom: 16px;
|
|
margin-bottom: 16px;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ transition: box-shadow 0.2s;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.order-info {
|
|
|
|
|
|
|
+.order-header {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
|
|
+ padding-bottom: 12px;
|
|
|
|
|
+ border-bottom: 1px solid #f0f0f0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.order-id {
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: var(--el-color-primary);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.order-body {
|
|
|
|
|
+ display: grid;
|
|
|
|
|
+ grid-template-columns: 1fr 1fr;
|
|
|
|
|
+ gap: 10px 24px;
|
|
|
|
|
+ padding: 12px 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.order-field {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 2px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.field-label {
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ color: #999;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.order-actions {
|
|
|
|
|
- margin-top: 12px;
|
|
|
|
|
|
|
+.price {
|
|
|
|
|
+ color: var(--el-color-danger);
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.order-footer {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
- gap: 8px;
|
|
|
|
|
- justify-content: flex-end;
|
|
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ padding-top: 12px;
|
|
|
|
|
+ border-top: 1px solid #f0f0f0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.order-time {
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ color: #999;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+@media (max-width: 480px) {
|
|
|
|
|
+ .order-body {
|
|
|
|
|
+ grid-template-columns: 1fr;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
</style>
|
|
</style>
|