feat: 重构订单面板,使用 API 获取当前和历史订单,添加订单状态映射
This commit is contained in:
@@ -1,198 +1,110 @@
|
||||
<script setup lang="ts">
|
||||
interface Order {
|
||||
id: string;
|
||||
symbol: string;
|
||||
side: "buy" | "sell";
|
||||
type: "limit" | "market";
|
||||
price: string;
|
||||
amount: string;
|
||||
filled: string;
|
||||
total: string;
|
||||
status: "pending" | "partial" | "filled" | "cancelled";
|
||||
time: string;
|
||||
}
|
||||
import type { Treaty } from "@elysiajs/eden";
|
||||
import { alertController, toastController } from "@ionic/vue";
|
||||
import { client, safeClient } from "@/api";
|
||||
import { orderStatusMap } from "../config";
|
||||
|
||||
type Item = Treaty.Data<typeof client.api.spot_order.list.get>["orders"][number];
|
||||
type TabType = "current" | "history";
|
||||
|
||||
const activeTab = ref<TabType>("current");
|
||||
const [DefineTemplate, ReuseTemplate] = createReusableTemplate<{ item: Item }>();
|
||||
|
||||
// TODO: 后续从API获取当前委托订单
|
||||
// const { data: currentOrders } = await client.api.trading.orders.get({ query: { status: 'open' } })
|
||||
const currentOrders = ref<Order[]>([
|
||||
{
|
||||
id: "1001",
|
||||
symbol: "BTC_USDT",
|
||||
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",
|
||||
},
|
||||
]);
|
||||
const { data } = await safeClient(client.api.spot_order.list.get());
|
||||
const currentOrders = computed(() =>
|
||||
data.value?.orders.filter(order => order.status !== "filled" && order.status !== "cancelled") || [],
|
||||
);
|
||||
const historyOrders = computed(() =>
|
||||
data.value?.orders.filter(order => order.status === "filled" || order.status === "cancelled") || [],
|
||||
);
|
||||
|
||||
// TODO: 后续从API获取历史订单
|
||||
// const { data: historyOrders } = await client.api.trading.orders.get({ query: { status: 'closed' } })
|
||||
const historyOrders = ref<Order[]>([
|
||||
function getStatusConfig(status: Item["status"]) {
|
||||
return orderStatusMap[status];
|
||||
}
|
||||
async function cancelOrder(orderId: string) {
|
||||
const alert = await alertController.create({
|
||||
header: "取消订单",
|
||||
message: "确定要取消该订单吗?",
|
||||
buttons: [
|
||||
{
|
||||
id: "1003",
|
||||
symbol: "BTC_USDT",
|
||||
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",
|
||||
text: "取消",
|
||||
role: "cancel",
|
||||
},
|
||||
{
|
||||
id: "1004",
|
||||
symbol: "BTC_USDT",
|
||||
side: "sell",
|
||||
type: "market",
|
||||
price: "43100.00",
|
||||
amount: "0.1500",
|
||||
filled: "0.1500",
|
||||
total: "6465.00",
|
||||
status: "filled",
|
||||
time: "2024-12-26 16:20:15",
|
||||
text: "确定",
|
||||
role: "destructive",
|
||||
handler: async () => {
|
||||
await safeClient(client.api.spot_order.cancel.post({ orderId }));
|
||||
await alert.dismiss();
|
||||
const toast = await toastController
|
||||
.create({
|
||||
message: "订单已取消",
|
||||
duration: 2000,
|
||||
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"]) {
|
||||
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()
|
||||
await alert.present();
|
||||
console.log("取消订单", orderId);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col h-full bg-(--ion-background-color)">
|
||||
<!-- Tab切换 -->
|
||||
<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"
|
||||
>
|
||||
<DefineTemplate v-slot="{ item }">
|
||||
<div class="flex justify-between items-center mb-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<span
|
||||
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-danger/10 text-(--ion-color-danger)'"
|
||||
>
|
||||
{{ order.side === 'buy' ? '买入' : '卖出' }}
|
||||
{{ item.side === 'buy' ? '买入' : '卖出' }}
|
||||
</span>
|
||||
<span class="text-sm font-medium">{{ order.symbol }}</span>
|
||||
<ion-badge :color="getStatusColor(order.status)" class="text-[10px] px-1.5 py-0.5">
|
||||
{{ getStatusText(order.status) }}
|
||||
<span class="text-sm font-medium">{{ item.symbol }}</span>
|
||||
<ion-badge :color="getStatusConfig(item.status).color" class="text-[10px] px-1.5 py-0.5">
|
||||
{{ getStatusConfig(item.status).text }}
|
||||
</ion-badge>
|
||||
</div>
|
||||
<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"
|
||||
@click="cancelOrder(order.id)"
|
||||
@click="cancelOrder(item.id)"
|
||||
>
|
||||
撤单
|
||||
</button>
|
||||
</div>
|
||||
</DefineTemplate>
|
||||
|
||||
<div class="grid grid-cols-2 gap-2 mb-2">
|
||||
<div class="flex justify-between text-xs">
|
||||
<span class="text-(--ion-text-color-step-400)">{{ order.type === 'limit' ? '价格' : '市价' }}</span>
|
||||
<span class="text-(--ion-text-color) font-medium">{{ order.type === 'limit' ? order.price : '-' }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between text-xs">
|
||||
<span class="text-(--ion-text-color-step-400)">数量</span>
|
||||
<span class="text-(--ion-text-color) font-medium">{{ order.amount }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between text-xs">
|
||||
<span class="text-(--ion-text-color-step-400)">成交</span>
|
||||
<span class="text-(--ion-text-color) font-medium">{{ order.filled }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between text-xs">
|
||||
<span class="text-(--ion-text-color-step-400)">总额</span>
|
||||
<span class="text-(--ion-text-color) font-medium">{{ order.total }} USDT</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pt-2">
|
||||
<span class="text-[11px] text-(--ion-text-color-step-500)">{{ order.time }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ui-tabs v-model="activeTab" size="small">
|
||||
<ui-tab-pane
|
||||
title="当前委托"
|
||||
name="current"
|
||||
>
|
||||
<ui-empty v-if="currentOrders.length === 0" title="当前暂无委托" />
|
||||
<template v-else>
|
||||
<div v-for="order in currentOrders" :key="order.id" class="bg-faint rounded-xl p-3">
|
||||
<ReuseTemplate :item="order" />
|
||||
</div>
|
||||
</template>
|
||||
</ui-tab-pane>
|
||||
<ui-tab-pane
|
||||
title="历史记录"
|
||||
name="history"
|
||||
>
|
||||
<ui-empty v-if="historyOrders.length === 0" title="暂无历史委托" />
|
||||
<template v-else>
|
||||
<div v-for="order in historyOrders" :key="order.id" class="bg-faint rounded-xl p-3">
|
||||
<ReuseTemplate :item="order" />
|
||||
</div>
|
||||
</template>
|
||||
</ui-tab-pane>
|
||||
</ui-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -53,3 +53,39 @@ export const confirmOrderSubmitSchema = confirmOrderSchema.transform(data => ({
|
||||
quantity: data.quantity.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: "已拒绝",
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user