feat: 重构订单面板,使用 API 获取当前和历史订单,添加订单状态映射

This commit is contained in:
2026-01-11 16:28:43 +07:00
parent 309606565b
commit 50400c42ae
2 changed files with 121 additions and 173 deletions

View File

@@ -1,198 +1,110 @@
<script setup lang="ts"> <script setup lang="ts">
interface Order { import type { Treaty } from "@elysiajs/eden";
id: string; import { alertController, toastController } from "@ionic/vue";
symbol: string; import { client, safeClient } from "@/api";
side: "buy" | "sell"; import { orderStatusMap } from "../config";
type: "limit" | "market";
price: string;
amount: string;
filled: string;
total: string;
status: "pending" | "partial" | "filled" | "cancelled";
time: string;
}
type Item = Treaty.Data<typeof client.api.spot_order.list.get>["orders"][number];
type TabType = "current" | "history"; type TabType = "current" | "history";
const activeTab = ref<TabType>("current"); const activeTab = ref<TabType>("current");
const [DefineTemplate, ReuseTemplate] = createReusableTemplate<{ item: Item }>();
// TODO: 后续从API获取当前委托订单 const { data } = await safeClient(client.api.spot_order.list.get());
// const { data: currentOrders } = await client.api.trading.orders.get({ query: { status: 'open' } }) const currentOrders = computed(() =>
const currentOrders = ref<Order[]>([ data.value?.orders.filter(order => order.status !== "filled" && order.status !== "cancelled") || [],
{ );
id: "1001", const historyOrders = computed(() =>
symbol: "BTC_USDT", data.value?.orders.filter(order => order.status === "filled" || order.status === "cancelled") || [],
side: "buy", );
type: "limit",
price: "43200.00",
amount: "0.5000",
filled: "0.2500",
total: "21600.00",
status: "partial",
time: "2024-12-27 10:30:25",
},
{
id: "1002",
symbol: "BTC_USDT",
side: "sell",
type: "limit",
price: "43500.00",
amount: "0.3000",
filled: "0.0000",
total: "13050.00",
status: "pending",
time: "2024-12-27 09:15:10",
},
]);
// TODO: 后续从API获取历史订单 function getStatusConfig(status: Item["status"]) {
// const { data: historyOrders } = await client.api.trading.orders.get({ query: { status: 'closed' } }) return orderStatusMap[status];
const historyOrders = ref<Order[]>([ }
async function cancelOrder(orderId: string) {
const alert = await alertController.create({
header: "取消订单",
message: "确定要取消该订单吗?",
buttons: [
{ {
id: "1003", text: "取消",
symbol: "BTC_USDT", role: "cancel",
side: "buy",
type: "limit",
price: "42800.00",
amount: "0.2000",
filled: "0.2000",
total: "8560.00",
status: "filled",
time: "2024-12-26 18:45:30",
}, },
{ {
id: "1004", text: "确定",
symbol: "BTC_USDT", role: "destructive",
side: "sell", handler: async () => {
type: "market", await safeClient(client.api.spot_order.cancel.post({ orderId }));
price: "43100.00", await alert.dismiss();
amount: "0.1500", const toast = await toastController
filled: "0.1500", .create({
total: "6465.00", message: "订单已取消",
status: "filled", duration: 2000,
time: "2024-12-26 16:20:15", position: "bottom",
color: "success",
});
await toast.present();
}, },
{
id: "1005",
symbol: "BTC_USDT",
side: "buy",
type: "limit",
price: "42500.00",
amount: "0.4000",
filled: "0.0000",
total: "17000.00",
status: "cancelled",
time: "2024-12-26 14:10:05",
}, },
]); ],
const displayOrders = computed(() => {
return activeTab.value === "current" ? currentOrders.value : historyOrders.value;
}); });
function getStatusText(status: Order["status"]) { await alert.present();
const statusMap = {
pending: "未成交",
partial: "部分成交",
filled: "已成交",
cancelled: "已取消",
};
return statusMap[status];
}
function getStatusColor(status: Order["status"]) {
const colorMap = {
pending: "warning",
partial: "primary",
filled: "success",
cancelled: "medium",
};
return colorMap[status];
}
async function cancelOrder(orderId: string) {
// TODO: 后续调用取消订单API
// await client.api.trading.order[orderId].delete()
console.log("取消订单", orderId); console.log("取消订单", orderId);
} }
</script> </script>
<template> <template>
<div class="flex flex-col h-full bg-(--ion-background-color)"> <div class="flex flex-col h-full bg-(--ion-background-color)">
<!-- Tab切换 --> <DefineTemplate v-slot="{ item }">
<ui-tabs v-model="activeTab" size="small">
<ui-tab-pane
title="当前委托"
name="current"
/>
<ui-tab-pane
title="历史记录"
name="history"
/>
</ui-tabs>
<!-- 订单列表 -->
<div class="flex-1 overflow-y-auto">
<div v-if="displayOrders.length === 0" class="flex items-center justify-center py-10 px-5">
<div class="text-(--ion-text-color-step-500) text-sm">
{{ activeTab === 'current' ? '暂无当前委托' : '暂无历史记录' }}
</div>
</div>
<div v-else class="space-y-3">
<div
v-for="order in displayOrders"
:key="order.id"
class="bg-faint rounded-xl p-3"
>
<div class="flex justify-between items-center mb-3"> <div class="flex justify-between items-center mb-3">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span <span
class="px-2 py-0.5 rounded text-[11px] font-semibold" class="px-2 py-0.5 rounded text-[11px] font-semibold"
:class="order.side === 'buy' :class="item.side === 'buy'
? 'bg-success/10 text-(--ion-color-success)' ? 'bg-success/10 text-(--ion-color-success)'
: 'bg-danger/10 text-(--ion-color-danger)'" : 'bg-danger/10 text-(--ion-color-danger)'"
> >
{{ order.side === 'buy' ? '买入' : '卖出' }} {{ item.side === 'buy' ? '买入' : '卖出' }}
</span> </span>
<span class="text-sm font-medium">{{ order.symbol }}</span> <span class="text-sm font-medium">{{ item.symbol }}</span>
<ion-badge :color="getStatusColor(order.status)" class="text-[10px] px-1.5 py-0.5"> <ion-badge :color="getStatusConfig(item.status).color" class="text-[10px] px-1.5 py-0.5">
{{ getStatusText(order.status) }} {{ getStatusConfig(item.status).text }}
</ion-badge> </ion-badge>
</div> </div>
<button <button
v-if="activeTab === 'current' && order.status !== 'filled'" v-if="activeTab === 'current' && item.status !== 'filled'"
class="px-3 py-1 bg-transparent border border-(--ion-color-danger) text-(--ion-color-danger) rounded-md text-xs transition-all active:bg-(--ion-color-danger) active:text-white" class="px-3 py-1 bg-transparent border border-(--ion-color-danger) text-(--ion-color-danger) rounded-md text-xs transition-all active:bg-(--ion-color-danger) active:text-white"
@click="cancelOrder(order.id)" @click="cancelOrder(item.id)"
> >
撤单 撤单
</button> </button>
</div> </div>
</DefineTemplate>
<div class="grid grid-cols-2 gap-2 mb-2"> <ui-tabs v-model="activeTab" size="small">
<div class="flex justify-between text-xs"> <ui-tab-pane
<span class="text-(--ion-text-color-step-400)">{{ order.type === 'limit' ? '价格' : '市价' }}</span> title="当前委托"
<span class="text-(--ion-text-color) font-medium">{{ order.type === 'limit' ? order.price : '-' }}</span> name="current"
</div> >
<div class="flex justify-between text-xs"> <ui-empty v-if="currentOrders.length === 0" title="当前暂无委托" />
<span class="text-(--ion-text-color-step-400)">数量</span> <template v-else>
<span class="text-(--ion-text-color) font-medium">{{ order.amount }}</span> <div v-for="order in currentOrders" :key="order.id" class="bg-faint rounded-xl p-3">
</div> <ReuseTemplate :item="order" />
<div class="flex justify-between text-xs"> </div>
<span class="text-(--ion-text-color-step-400)">成交</span> </template>
<span class="text-(--ion-text-color) font-medium">{{ order.filled }}</span> </ui-tab-pane>
</div> <ui-tab-pane
<div class="flex justify-between text-xs"> title="历史记录"
<span class="text-(--ion-text-color-step-400)">总额</span> name="history"
<span class="text-(--ion-text-color) font-medium">{{ order.total }} USDT</span> >
</div> <ui-empty v-if="historyOrders.length === 0" title="暂无历史委托" />
</div> <template v-else>
<div v-for="order in historyOrders" :key="order.id" class="bg-faint rounded-xl p-3">
<div class="pt-2"> <ReuseTemplate :item="order" />
<span class="text-[11px] text-(--ion-text-color-step-500)">{{ order.time }}</span> </div>
</div> </template>
</div> </ui-tab-pane>
</div> </ui-tabs>
</div>
</div> </div>
</template> </template>

View File

@@ -53,3 +53,39 @@ export const confirmOrderSubmitSchema = confirmOrderSchema.transform(data => ({
quantity: data.quantity.toString(), quantity: data.quantity.toString(),
price: data.price?.toString() ?? "", price: data.price?.toString() ?? "",
})); }));
export enum OrderStatusEnum {
PENDING = "pending",
OPEN = "open",
PARTIALLY_FILLED = "partially_filled",
FILLED = "filled",
CANCELED = "canceled",
REJECTED = "rejected",
}
export const orderStatusMap = {
[OrderStatusEnum.PENDING]: {
color: "warning",
text: "待处理",
},
[OrderStatusEnum.OPEN]: {
color: "primary",
text: "已挂单",
},
[OrderStatusEnum.PARTIALLY_FILLED]: {
color: "tertiary",
text: "部分成交",
},
[OrderStatusEnum.FILLED]: {
color: "success",
text: "已完成",
},
[OrderStatusEnum.CANCELED]: {
color: "medium",
text: "已取消",
},
[OrderStatusEnum.REJECTED]: {
color: "danger",
text: "已拒绝",
},
};