Refactor code structure for improved readability and maintainability
This commit is contained in:
@@ -1,9 +1,130 @@
|
||||
<script lang='ts' setup>
|
||||
import type { SpotOrderBody } from "@/api/types";
|
||||
import { modalController, toastController } from "@ionic/vue";
|
||||
import { closeOutline } from "ionicons/icons";
|
||||
import { client, safeClient } from "@/api";
|
||||
import { tradeWayConfig } from "../config";
|
||||
|
||||
const props = defineProps<{
|
||||
form: SpotOrderBody & { amount: string };
|
||||
}>();
|
||||
const currentTradeWay = computed(() => {
|
||||
return tradeWayConfig.find(item => item.value === props.form.orderType);
|
||||
});
|
||||
|
||||
function onClose() {
|
||||
modalController.dismiss();
|
||||
}
|
||||
async function onConfirm() {
|
||||
await safeClient(client.api.spot_order.create.post({
|
||||
orderType: props.form.orderType,
|
||||
quantity: props.form.quantity,
|
||||
side: props.form.side,
|
||||
symbol: props.form.symbol,
|
||||
memo: props.form.memo,
|
||||
price: props.form.price,
|
||||
}));
|
||||
const toast = await toastController.create({
|
||||
message: "订单提交成功",
|
||||
duration: 2000,
|
||||
position: "top",
|
||||
color: "success",
|
||||
});
|
||||
await toast.present();
|
||||
modalController.dismiss();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
Hello world
|
||||
<div class="ion-padding h-80">
|
||||
<div class="flex justify-between items-center mb-5">
|
||||
<div class="font-semibold">
|
||||
下单确认
|
||||
</div>
|
||||
<ion-icon :icon="closeOutline" class="text-2xl" @click="onClose" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<div class="text-sm">
|
||||
{{ form.symbol }}
|
||||
</div>
|
||||
<ui-tag size="mini" :type="form.side === 'buy' ? 'success' : 'danger'">
|
||||
{{ form.side === 'buy' ? '买入' : '卖出' }}
|
||||
</ui-tag>
|
||||
</div>
|
||||
<template v-if="form.orderType === 'limit'">
|
||||
<div class="cell">
|
||||
<div class="label">
|
||||
委托价格
|
||||
</div>
|
||||
<div class="value">
|
||||
{{ form.price }} USDT
|
||||
</div>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<div class="label">
|
||||
数量
|
||||
</div>
|
||||
<div class="value">
|
||||
{{ form.quantity }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<div class="label">
|
||||
金额
|
||||
</div>
|
||||
<div class="value">
|
||||
{{ form.amount }} USDT
|
||||
</div>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<div class="label">
|
||||
类型
|
||||
</div>
|
||||
<div class="value">
|
||||
{{ currentTradeWay?.name }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else-if="form.orderType === 'market'">
|
||||
<div class="cell">
|
||||
<div class="label">
|
||||
委托价格
|
||||
</div>
|
||||
<div class="value">
|
||||
{{ form.price }} USDT
|
||||
</div>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<div class="label">
|
||||
数量
|
||||
</div>
|
||||
<div class="value">
|
||||
{{ form.quantity }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="mt-6">
|
||||
<ion-button expand="block" color="success" @click="onConfirm">
|
||||
确认下单
|
||||
</ion-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang='css' scoped></style>
|
||||
<style lang='css' scoped>
|
||||
@reference "tailwindcss";
|
||||
|
||||
.cell {
|
||||
@apply flex justify-between items-center py-1;
|
||||
}
|
||||
.label {
|
||||
@apply text-sm text-(--ion-text-color-step-400);
|
||||
}
|
||||
.value {
|
||||
@apply text-sm font-semibold;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -7,6 +7,9 @@ import { caretDownOutline } from "ionicons/icons";
|
||||
import { tradeWayConfig } from "../config";
|
||||
|
||||
const model = defineModel({ type: Object as PropType<SpotOrderBody>, required: true });
|
||||
const currentTradeWay = computed(() => {
|
||||
return tradeWayConfig.find(item => item.value === model.value.orderType);
|
||||
});
|
||||
|
||||
function onSelectTradeWay(item: TradeWayConfig) {
|
||||
model.value.orderType = item.value;
|
||||
@@ -17,7 +20,7 @@ function onSelectTradeWay(item: TradeWayConfig) {
|
||||
<template>
|
||||
<div id="open-modal" class="bg-faint flex items-center justify-between px-4 py-2 rounded-md">
|
||||
<div class="text-xs font-medium text-text-300">
|
||||
市场
|
||||
{{ currentTradeWay?.name }}
|
||||
</div>
|
||||
<ion-icon :icon="caretDownOutline" />
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as yup from "yup";
|
||||
import { z } from "zod";
|
||||
|
||||
export enum TradeWayValueEnum {
|
||||
LIMIT = "limit",
|
||||
@@ -21,28 +21,35 @@ export const tradeWayConfig: TradeWayConfig[] = [
|
||||
description: "以指定价格买入或卖出",
|
||||
icon: "hugeicons:trade-up",
|
||||
},
|
||||
{
|
||||
name: "市价委托",
|
||||
value: "market",
|
||||
description: "以市场价格买入或卖出",
|
||||
icon: "hugeicons:trade-down",
|
||||
},
|
||||
];
|
||||
|
||||
export const confirmOrderSchema = yup.object({
|
||||
price: yup.number().when("way", {
|
||||
is: TradeWayValueEnum.LIMIT !== undefined,
|
||||
then: yup
|
||||
.number()
|
||||
.typeError("请输入有效的价格")
|
||||
.required("价格为必填项")
|
||||
.moreThan(0, "价格必须大于0"),
|
||||
otherwise: yup.number().notRequired(),
|
||||
}),
|
||||
amount: yup
|
||||
.number()
|
||||
.typeError("请输入有效的数量")
|
||||
.required("数量为必填项")
|
||||
.moreThan(0, "数量必须大于0"),
|
||||
way: yup
|
||||
.mixed<TradeWayValue>()
|
||||
.oneOf(
|
||||
Object.values(TradeWayValueEnum),
|
||||
"请选择有效的交易方式",
|
||||
)
|
||||
.required("交易方式为必填项"),
|
||||
});
|
||||
export const confirmOrderSchema = z.object({
|
||||
quantity: z.coerce.number({ message: "请输入有效的数量" }).gt(0, "数量必须大于0"),
|
||||
price: z.coerce.number({ message: "请输入有效的价格" }).gt(0, "价格必须大于0").optional().or(z.coerce.number().optional()),
|
||||
orderType: z.enum([TradeWayValueEnum.LIMIT, TradeWayValueEnum.MARKET], {
|
||||
message: "请选择有效的交易方式",
|
||||
}) as z.ZodType<TradeWayValue>,
|
||||
}).refine(
|
||||
(data) => {
|
||||
if (data.orderType === TradeWayValueEnum.LIMIT) {
|
||||
return data.price !== undefined && data.price > 0;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
{
|
||||
message: "价格为必填项",
|
||||
path: ["price"],
|
||||
},
|
||||
);
|
||||
|
||||
export const confirmOrderSubmitSchema = confirmOrderSchema.transform(data => ({
|
||||
...data,
|
||||
quantity: data.quantity.toString(),
|
||||
price: data.price?.toString() ?? "",
|
||||
}));
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import type { ChartingLibraryWidgetOptions } from "#/charting_library";
|
||||
import type { SpotOrderBody } from "@/api/types";
|
||||
import type { TradingViewInst } from "@/tradingview/index";
|
||||
import type { ModalInstance } from "@/utils";
|
||||
import { modalController } from "@ionic/vue";
|
||||
import { useRouteQuery } from "@vueuse/router";
|
||||
import { caretDownOutline, ellipsisHorizontal } from "ionicons/icons";
|
||||
@@ -9,28 +10,32 @@ import MaterialSymbolsCandlestickChartOutline from "~icons/material-symbols/cand
|
||||
import { client, safeClient } from "@/api";
|
||||
import { TradeTypeEnum } from "@/api/enum";
|
||||
import { TradingViewChart } from "@/tradingview/index";
|
||||
import ConfirmOrder from "./components/confirm-order.vue";
|
||||
import OrdersPanel from "./components/orders-panel.vue";
|
||||
import TradePairsModal from "./components/trade-pairs-modal.vue";
|
||||
import TradeSwitch from "./components/trade-switch.vue";
|
||||
import TradeWay from "./components/trade-way.vue";
|
||||
import { confirmOrderSchema, TradeWayValueEnum } from "./config";
|
||||
import { confirmOrderSubmitSchema, TradeWayValueEnum } from "./config";
|
||||
|
||||
const { data } = await safeClient(client.api.trading_pairs.get({ query: { limit: 1 } }));
|
||||
const mode = useRouteQuery<TradeTypeEnum>("mode", TradeTypeEnum.BUY);
|
||||
const symbol = useRouteQuery<string>("symbol", "BTCUSD");
|
||||
|
||||
const symbol = useRouteQuery<string>("symbol", data.value?.data[0].symbol);
|
||||
const tradingviewOptions: Partial<ChartingLibraryWidgetOptions> = {
|
||||
disabled_features: [
|
||||
"create_volume_indicator_by_default",
|
||||
],
|
||||
};
|
||||
const tradingViewInst = useTemplateRef<TradingViewInst>("tradingViewInst");
|
||||
const [form] = useResetRef<SpotOrderBody>({
|
||||
const confirmModalInst = useTemplateRef<ModalInstance>("confirmModalInst");
|
||||
|
||||
const [form] = useResetRef<SpotOrderBody & { amount: string }>({
|
||||
orderType: TradeWayValueEnum.LIMIT,
|
||||
quantity: "",
|
||||
side: mode.value,
|
||||
symbol: symbol.value,
|
||||
memo: "",
|
||||
price: "",
|
||||
amount: "",
|
||||
});
|
||||
|
||||
async function openTradePairs() {
|
||||
@@ -40,22 +45,38 @@ async function openTradePairs() {
|
||||
initialBreakpoint: 0.95,
|
||||
handle: true,
|
||||
});
|
||||
|
||||
await modal.present();
|
||||
|
||||
const { data: result } = await modal.onWillDismiss<string>();
|
||||
|
||||
if (result) {
|
||||
symbol.value = result;
|
||||
result && (symbol.value = result);
|
||||
}
|
||||
function handleChangeQuantity(event) {
|
||||
const val = (event.target as HTMLInputElement).value;
|
||||
if (val && form.value.price) {
|
||||
const amount = Number(val) * Number(form.value.price);
|
||||
form.value.amount = amount.toString();
|
||||
}
|
||||
else {
|
||||
form.value.amount = "";
|
||||
}
|
||||
}
|
||||
|
||||
function handleSubmit() {
|
||||
confirmOrderSchema.validate(form.value).then(() => {
|
||||
console.log("submit successfully");
|
||||
}).catch((err) => {
|
||||
console.log("submit failed:", err);
|
||||
});
|
||||
function handleChangeAmount(event) {
|
||||
const val = (event.target as HTMLInputElement).value;
|
||||
if (val && form.value.price) {
|
||||
const quantity = Number(val) / Number(form.value.price);
|
||||
form.value.quantity = quantity.toString();
|
||||
}
|
||||
else {
|
||||
form.value.quantity = "";
|
||||
}
|
||||
}
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
await confirmOrderSubmitSchema.parseAsync(form.value);
|
||||
confirmModalInst.value?.$el.present();
|
||||
}
|
||||
catch (err) {
|
||||
console.error("订单验证失败:", err);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -90,21 +111,37 @@ function handleSubmit() {
|
||||
<TradeSwitch v-model:active="mode" @update:active="val => form.side = val" />
|
||||
<TradeWay v-model="form" />
|
||||
<template v-if="form.orderType === 'limit'">
|
||||
<ion-input v-model="form.price" label="价格" class="count" inputmode="decimal" type="number" placeholder="请输入价格(USDT)" />
|
||||
<ion-input v-model="form.price" label="价格" class="count" inputmode="decimal" type="number" placeholder="请输入价格">
|
||||
<span slot="end">USDT</span>
|
||||
</ion-input>
|
||||
<ion-input v-model="form.quantity" label="数量" class="count" inputmode="decimal" type="number" placeholder="请输入交易数量" @ion-input="handleChangeQuantity">
|
||||
<span slot="end">{{ symbol }}</span>
|
||||
</ion-input>
|
||||
<ion-input v-model="form.amount" label="金额" class="count" inputmode="decimal" type="number" placeholder="请输入交易金额" @ion-input="handleChangeAmount">
|
||||
<span slot="end">USDT</span>
|
||||
</ion-input>
|
||||
</template>
|
||||
<ion-input v-model="form.quantity" label="数量" class="count" inputmode="decimal" type="number" placeholder="请输入交易数量">
|
||||
<span slot="end">{{ symbol }}</span>
|
||||
</ion-input>
|
||||
<ion-input v-model="form.price" label="金额" class="count" inputmode="decimal" type="number" placeholder="请输入交易金额">
|
||||
<span slot="end">USDT</span>
|
||||
</ion-input>
|
||||
<template v-else-if="form.orderType === 'market'">
|
||||
<ion-input v-model="form.price" label="价格" class="count" inputmode="decimal" type="number" placeholder="请输入价格">
|
||||
<span slot="end">USDT</span>
|
||||
</ion-input>
|
||||
<ion-input v-model="form.quantity" label="数量" class="count" inputmode="decimal" type="number" placeholder="请输入交易数量" @ion-input="handleChangeQuantity">
|
||||
<span slot="end">{{ symbol }}</span>
|
||||
</ion-input>
|
||||
</template>
|
||||
|
||||
<!-- <ion-range class="range" aria-label="Range with ticks" :pin="true" :ticks="true" :snaps="true" :min="0" :max="5" /> -->
|
||||
<ion-button expand="block" size="small" shape="round" :color="mode === TradeTypeEnum.BUY ? 'success' : 'danger'" @click="handleSubmit">
|
||||
{{ mode === TradeTypeEnum.BUY ? '买入' : '卖出' }}
|
||||
</ion-button>
|
||||
|
||||
<ion-modal ref="confirmModalInst" class="confirm-modal" :breakpoints="[0, 1]" :initial-breakpoint="1" :handle="false">
|
||||
<ConfirmOrder :form="form" />
|
||||
</ion-modal>
|
||||
</div>
|
||||
<div class="col-span-2" />
|
||||
</div>
|
||||
|
||||
<div class="mt-6 px-4 pb-4">
|
||||
<OrdersPanel />
|
||||
</div>
|
||||
@@ -145,4 +182,7 @@ ion-range.range::part(tick-active) {
|
||||
top: 18px;
|
||||
border-radius: 100%;
|
||||
}
|
||||
.confirm-modal {
|
||||
--height: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user