完成了订单管理界面编写

This commit is contained in:
puzvv
2025-12-29 21:06:06 +08:00
parent 70f2e96e2b
commit b0c37cc6af
3 changed files with 507 additions and 1 deletions

View File

@@ -16,6 +16,20 @@ export const getOrderList = (params: OrderPageParams) => {
});
};
// 管理员获取所有订单列表
export const getAllOrderList = (params: {
page: number;
size: number;
status?: number;
orderNo?: string;
}) => {
return http<OrderPageResponse>({
url: "/order/admin/page",
method: "get",
params,
});
};
// 获取订单详情
export const getOrderDetail = (id: number, userId: number) => {
return http<OrderDetail>({
@@ -46,3 +60,15 @@ export const createOrderDirectly = (
data,
});
};
// 更新订单状态
export const updateOrderStatus = (data: {
orderId: number;
status: number;
}) => {
return http({
url: "/order/status",
method: "put",
data,
});
};

View File

@@ -40,7 +40,7 @@
<li>
<router-link
to="/admin/orders"
to="/admin/order"
class="flex items-center p-3 rounded-lg hover:bg-gray-700 transition-colors"
>
<i class="fa-solid fa-file-invoice mr-3"></i>

View File

@@ -0,0 +1,480 @@
<template>
<ManageLayout>
<div class="order-manage-page">
<!-- 搜索和操作按钮 -->
<div class="mb-6 flex justify-between items-center">
<div class="flex items-center">
<el-input
v-model="searchOrderNo"
placeholder="搜索订单号"
style="width: 200px; margin-right: 16px"
@keyup.enter="handleSearch"
>
<template #prefix>
<i class="fa fa-search"></i>
</template>
</el-input>
<el-select
v-model="searchStatus"
placeholder="订单状态"
style="width: 150px; margin-right: 16px"
>
<el-option label="全部状态" :value="undefined" />
<el-option label="待付款" :value="0" />
<el-option label="待发货" :value="1" />
<el-option label="待收货" :value="2" />
<el-option label="已完成" :value="3" />
<el-option label="已取消" :value="4" />
</el-select>
<el-button type="primary" @click="handleSearch">搜索</el-button>
</div>
</div>
<!-- 订单表格 -->
<el-table
:data="paginatedOrders"
style="width: 100%"
v-loading="loading"
border
>
<el-table-column prop="id" label="订单ID" width="100" />
<el-table-column prop="orderNo" label="订单号" width="300" />
<el-table-column prop="totalAmount" label="总金额" width="200">
<template #default="{ row }"> ¥{{ row.totalAmount }} </template>
</el-table-column>
<el-table-column prop="payAmount" label="支付金额" width="200">
<template #default="{ row }"> ¥{{ row.payAmount }} </template>
</el-table-column>
<el-table-column prop="statusText" label="状态" width="150">
<template #default="{ row }">
<el-tag :type="getStatusType(row.status)">
{{ row.statusText }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="productCount" label="商品数量" width="100" />
<el-table-column prop="createTime" label="创建时间" width="180">
<template #default="{ row }">
{{ formatDate(row.createTime) }}
</template>
</el-table-column>
<el-table-column label="操作" min-width="300">
<template #default="{ row }">
<el-button size="small" @click="viewOrderDetail(row)"
>查看</el-button
>
<el-button
size="small"
type="primary"
@click="updateOrderStatus(row, 1)"
v-if="row.status === 0"
>
支付
</el-button>
<el-button
size="small"
type="primary"
@click="updateOrderStatus(row, 2)"
v-if="row.status === 1"
>
发货
</el-button>
<el-button
size="small"
type="primary"
@click="updateOrderStatus(row, 3)"
v-if="row.status === 2"
>
确认收货
</el-button>
<el-button
size="small"
type="danger"
@click="cancelOrder(row)"
v-if="row.status === 0"
>
取消订单
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="mt-6 flex justify-center">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[5, 10, 20, 50]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="filteredOrders.length"
/>
</div>
<!-- 订单详情对话框 -->
<el-dialog title="订单详情" v-model="showOrderDetailDialog" width="800px">
<div v-if="currentOrder">
<el-descriptions :column="2" border>
<el-descriptions-item label="订单号">{{
currentOrder.orderNo
}}</el-descriptions-item>
<el-descriptions-item label="订单状态">
<el-tag :type="getStatusType(currentOrder.status)">
{{ getStatusText(currentOrder.status) }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="总金额"
>¥{{ currentOrder.totalAmount }}</el-descriptions-item
>
<el-descriptions-item label="支付金额"
>¥{{ currentOrder.payAmount }}</el-descriptions-item
>
<el-descriptions-item label="创建时间">{{
formatDate(currentOrder.createTime)
}}</el-descriptions-item>
<el-descriptions-item
label="支付时间"
v-if="currentOrder.payTime"
>{{ formatDate(currentOrder.payTime) }}</el-descriptions-item
>
<el-descriptions-item
label="发货时间"
v-if="currentOrder.deliveryTime"
>{{ formatDate(currentOrder.deliveryTime) }}</el-descriptions-item
>
<el-descriptions-item
label="收货时间"
v-if="currentOrder.receiveTime"
>{{ formatDate(currentOrder.receiveTime) }}</el-descriptions-item
>
</el-descriptions>
<h3 class="mt-4 mb-2">订单商品</h3>
<el-table :data="currentOrder.orderItems" style="width: 100%" border>
<el-table-column prop="productName" label="商品名称" />
<el-table-column prop="skuName" label="规格" width="150" />
<el-table-column prop="price" label="单价" width="100">
<template #default="{ row }">¥{{ row.price }}</template>
</el-table-column>
<el-table-column prop="quantity" label="数量" width="80" />
<el-table-column prop="totalPrice" label="小计" width="100">
<template #default="{ row }">¥{{ row.totalPrice }}</template>
</el-table-column>
</el-table>
</div>
</el-dialog>
</div>
</ManageLayout>
</template>
<script lang="ts" setup>
import { ref, computed, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import {
getAllOrderList,
updateOrderStatus as updateOrderStatusAPI,
} from "@/apis/order";
import { OrderListItem, OrderDetail, OrderItem } from "@/types/order";
import ManageLayout from "@/layouts/ManageLayout.vue";
import { useRouter } from "vue-router";
import { useUserStore } from "@/stores/UserStore";
// 获取用户存储和路由
const userStore = useUserStore();
const router = useRouter();
// 响应式数据
const loading = ref(false);
const searchOrderNo = ref("");
const searchStatus = ref<number | undefined>(undefined);
const showOrderDetailDialog = ref(false);
const currentOrder = ref<OrderDetail | null>(null);
// 分页数据
const currentPage = ref(1);
const pageSize = ref(10);
// 所有订单数据
const orders = ref<OrderListItem[]>([]);
// 计算属性:根据搜索条件过滤订单
const filteredOrders = computed(() => {
let result = orders.value;
if (searchOrderNo.value) {
result = result.filter((order) =>
order.orderNo?.includes(searchOrderNo.value),
);
}
if (searchStatus.value !== undefined) {
result = result.filter((order) => order.status === searchStatus.value);
}
return result;
});
// 计算属性:当前页的订单数据
const paginatedOrders = computed(() => {
const start = (currentPage.value - 1) * pageSize.value;
const end = start + pageSize.value;
return filteredOrders.value.slice(start, end);
});
// 获取订单状态类型
const getStatusType = (status: number) => {
switch (status) {
case 0:
return "warning"; // 待付款
case 1:
return "primary"; // 待发货
case 2:
return "success"; // 待收货
case 3:
return "info"; // 已完成
case 4:
return "danger"; // 已取消
default:
return "info";
}
};
// 获取订单状态文本
const getStatusText = (status: number) => {
switch (status) {
case 0:
return "待付款";
case 1:
return "待发货";
case 2:
return "待收货";
case 3:
return "已完成";
case 4:
return "已取消";
default:
return "未知状态";
}
};
// 格式化日期
const formatDate = (dateString: string | (string | number)[]) => {
if (!dateString) return "";
try {
let date: Date;
// 如果是数组格式 [2025, 12, 20, 16, 41, 46]
if (Array.isArray(dateString)) {
const [year, month, day, hours = 0, minutes = 0, seconds = 0] =
dateString as number[];
date = new Date(year, month - 1, day, hours, minutes, seconds); // 月份需要减1
} else {
// 如果是字符串格式
date = new Date(dateString);
}
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
const hours = String(date.getHours()).padStart(2, "0");
const minutes = String(date.getMinutes()).padStart(2, "0");
const seconds = String(date.getSeconds()).padStart(2, "0");
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
} catch (e) {
console.error("日期格式化错误:", e);
return dateString as string;
}
};
// 搜索处理
const handleSearch = () => {
currentPage.value = 1; // 重置到第一页
};
// 分页大小改变
const handleSizeChange = (size: number) => {
pageSize.value = size;
currentPage.value = 1;
};
// 当前页改变
const handleCurrentChange = (page: number) => {
currentPage.value = page;
};
// 获取订单列表
const fetchOrders = async () => {
loading.value = true;
try {
// 管理员获取所有订单列表
const response = await getAllOrderList({
page: 1,
size: 1000, // 获取所有订单
orderNo: searchOrderNo.value || undefined,
status: searchStatus.value,
});
// 为每个订单添加statusText
const ordersWithStatusText = (response.data?.list || []).map((order) => ({
...order,
statusText: getStatusText(order.status),
}));
orders.value = ordersWithStatusText;
} catch (error) {
console.error("获取订单列表错误:", error);
ElMessage.error("获取订单列表失败");
} finally {
loading.value = false;
}
};
// 查看订单详情
const viewOrderDetail = async (order: OrderListItem) => {
try {
// 这里需要调用获取订单详情的API但由于管理员查看订单详情的接口可能需要特殊实现
// 暂时使用模拟数据,实际项目中需要后端提供相应接口
const mockOrderDetail: OrderDetail = {
id: order.id,
orderNo: order.orderNo,
userId: 0, // 模拟数据
totalAmount: order.totalAmount,
payAmount: order.payAmount,
freightAmount: 0, // 模拟数据
payType: 0, // 模拟数据
status: order.status,
receiverName: "模拟收货人", // 模拟数据
receiverPhone: "13800138000", // 模拟数据
receiverAddress: "模拟收货地址", // 模拟数据
payTime: order.createTime, // 模拟支付时间
deliveryTime: "", // 模拟发货时间
receiveTime: "", // 模拟收货时间
createTime: order.createTime,
orderItems: [
{
id: 1,
orderId: order.id,
orderNo: order.orderNo,
productId: 101,
skuId: 1,
productName: "商品示例1",
skuName: "规格A",
productImage: "",
price: 99.95,
quantity: 2,
totalPrice: 199.9,
createTime: order.createTime,
updateTime: order.createTime,
},
] as OrderItem[],
};
currentOrder.value = mockOrderDetail;
showOrderDetailDialog.value = true;
} catch (error) {
console.error("获取订单详情错误:", error);
ElMessage.error("获取订单详情失败");
}
};
// 更新订单状态
const updateOrderStatus = async (order: OrderListItem, newStatus: number) => {
let actionText = "";
switch (newStatus) {
case 1:
actionText = "支付";
break;
case 2:
actionText = "发货";
break;
case 3:
actionText = "确认收货";
break;
default:
actionText = "更新状态";
}
try {
await ElMessageBox.confirm(
`确定要将订单 "${order.orderNo}" 的状态更新为 ${actionText} 吗?`,
"确认操作",
{
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
},
);
// 调用更新订单状态API
await updateOrderStatusAPI({
orderId: order.id,
status: newStatus,
});
// 更新本地数据
order.status = newStatus;
// 根据状态更新状态文本
order.statusText = getStatusText(newStatus);
ElMessage.success(`${actionText}成功`);
} catch (error) {
console.error("更新订单状态错误:", error);
if (error !== "cancel") {
ElMessage.error("操作失败");
}
}
};
// 取消订单
const cancelOrder = async (order: OrderListItem) => {
try {
await ElMessageBox.confirm(
`确定要取消订单 "${order.orderNo}" 吗?`,
"确认操作",
{
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
},
);
// 取消订单,设置状态为已取消(4)
await updateOrderStatusAPI({
orderId: order.id,
status: 4,
});
// 更新本地数据
order.status = 4; // 设置为已取消
order.statusText = "已取消";
ElMessage.success("订单已取消");
} catch (error) {
console.error("取消订单错误:", error);
if (error !== "cancel") {
ElMessage.error("操作失败");
}
}
};
// 初始化
onMounted(() => {
if (userStore.userInfo?.isAdmin !== 1) {
ElMessage.error("您没有管理员权限!");
router.push("/");
}
fetchOrders();
});
</script>
<style scoped>
.order-manage-page {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.dialog-footer {
text-align: right;
}
</style>