feat: 重构订单面板,使用 API 获取当前和历史订单,添加订单状态映射
This commit is contained in:
@@ -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[]>([
|
|
||||||
{
|
|
||||||
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",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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) {
|
async function cancelOrder(orderId: string) {
|
||||||
// TODO: 后续调用取消订单API
|
const alert = await alertController.create({
|
||||||
// await client.api.trading.order[orderId].delete()
|
header: "取消订单",
|
||||||
|
message: "确定要取消该订单吗?",
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: "取消",
|
||||||
|
role: "cancel",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await alert.present();
|
||||||
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">
|
|
||||||
<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>
|
||||||
|
</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>
|
</div>
|
||||||
|
</template>
|
||||||
|
</ui-tab-pane>
|
||||||
|
</ui-tabs>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -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: "已拒绝",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user