From 09cd59e111da050db1e26621a231c7e2eb7a415b Mon Sep 17 00:00:00 2001
From: 李凌 <344137771@qq.com>
Date: Wed, 21 Jan 2026 21:55:11 +0800
Subject: [PATCH] 1
---
src/views/ICO/ico.vue | 516 ++++++++++++++++++++++++++++++++++++++++++++++----------
1 files changed, 419 insertions(+), 97 deletions(-)
diff --git a/src/views/ICO/ico.vue b/src/views/ICO/ico.vue
index b394c17..a210695 100644
--- a/src/views/ICO/ico.vue
+++ b/src/views/ICO/ico.vue
@@ -2,154 +2,476 @@
<div class="ico">
<fx-header :showLeft="false">
<template v-slot:title>
- <div>ICO</div>
+ <div>{{ $t('ICO申购') }}</div>
</template>
- <template v-slot:right>
+ <!-- <template v-slot:right>
<van-icon name="todo-list-o" @click="$router.push('/ICO/icoRecord')" />
- </template>
+ </template> -->
</fx-header>
- <div class="ico_list">
- <div class="ico_item mb-5" v-for="i in icoList" :key="i.id">
- <div class="item_1">
- {{ i.name }}
- </div>
- <div class="item_2 flex justify-between">
- <div class="mr-5">{{ $t('申购时间') }}</div>
- <div>{{ i.startDate }} ~ {{ i.endDate }}</div>
- </div>
- <div class="item_2 flex justify-between">
- <div>{{ $t('最低认购') }}</div>
- <div>{{ i.minContribution }}</div>
- </div>
- <div class="item_2 flex justify-between">
- <div>{{ $t('币种数量') }}</div>
- <div>{{ i.maxContribution }}</div>
- </div>
- <div class="item_2 flex justify-between">
- <div>{{ $t('IssuePrice') }}</div>
- <div>{{ i.unitAmount }}</div>
- </div>
+ <div class="ico-intro">
+ {{ $t('发掘最热、最新币种,及时捕捉市场机会') }}
+ </div>
- <div class="item_2 flex justify-between">
- <div>{{ $t('listingDate') }}</div>
- <div>{{ i.marketDate }}</div>
+ <!-- ICO申购卡片 -->
+ <div class="ico-card">
+ <div class="card-header">
+ <div class="token-info">
+ <div class="token-symbol">CLK</div>
</div>
- <div class="item_2 flex justify-between align-center">
- <div>{{ $t('进度') }}</div>
- <div style="width: 70%;padding-top: 1rem;">
- <van-progress :percentage="i.progressRate || 0" track-color="#ccc" />
+ </div>
+
+ <div class="card-content">
+ <div class="price-row">
+ <div class="price-item">
+ <div class="price-label">{{ $t('募集价格') }}</div>
+ <div class="price-value green">{{ closePrice || '0' }} USDT</div>
+ </div>
+ <div class="price-item">
+ <div class="price-label">{{ $t('实际支付') }}</div>
+ <div class="price-value">{{ actualPayment }} USDT</div>
</div>
</div>
- <div class="item_3 flex justify-center">
- <van-button type="default" round size="large" @click="openBuy(i)">{{$t('申购')}}</van-button>
+
+ <div class="input-row">
+ <div class="input-label">{{ $t('申购数量') }}</div>
+ <div class="input-wrapper">
+ <van-field v-model="sgNum" type="digit" :placeholder="$t('请输入申购数量')" class="subscribe-input" />
+ <van-button size="small" type="default" @click="setAll" class="all-btn">{{ $t('全部')
+ }}</van-button>
+ </div>
</div>
+
+ <div class="balance-row">
+ <div class="balance-label">{{ $t('可用余额') }}</div>
+ <div class="balance-value">{{ initOpen.volume || '0' }} USDT</div>
+ </div>
+
+ <van-button type="primary" block round class="buy-btn" @click="buy">
+ {{ $t('买入') }}
+ </van-button>
</div>
</div>
-
- <!-- 购买弹窗 -->
- <van-popup v-model:show="show" round>
- <div class="buy_popup">
- <div class="buy_title flex justify-center">{{ itemObj.symbol }}</div>
-
- <van-field v-model="sgNum" type="digit" :label="$t('申购数量')" />
-
- <div class="flex justify-center mt-5">
- <van-button type="default" size="large" round @click="buy">{{ $t('confirm') }}</van-button>
+ <!-- 持仓信息卡片 -->
+ <div class="holding-card" v-if="recordData">
+ <div class="card-title">{{ $t('持仓信息') }}</div>
+ <div class="card-header">
+ <div class="token-info">
+ <div class="token-symbol">CLK</div>
</div>
</div>
- </van-popup>
+
+ <div class="card-content">
+ <div class="info-row">
+ <div class="info-label">{{ $t('限售时间') }}</div>
+ <div class="info-value">{{ recordData.lockingTime }}</div>
+ </div>
+ <div class="info-row">
+ <div class="info-label">{{ $t('申购数量') }}</div>
+ <div class="info-value">{{ recordData.purchaseQuantity || '0' }}</div>
+ </div>
+ <div class="info-row">
+ <div class="info-label">{{ $t('现价') }}</div>
+ <div class="info-value">{{ recordData.currentPrice || '--' }}</div>
+ </div>
+ <div class="info-row">
+ <div class="info-label">{{ $t('购买价') }}</div>
+ <div class="info-value">{{ recordData.purchasePrice || '--' }}</div>
+ </div>
+ <div class="info-row">
+ <div class="info-label">{{ $t('持仓市值') }}</div>
+ <div class="info-value">{{ recordData.positionvalue || '0.00' }}</div>
+ </div>
+ <div class="info-row">
+ <div class="info-label">{{ $t('购买总额') }}</div>
+ <div class="info-value">{{ (recordData.purchaseQuantity * recordData.purchasePrice).toFixed(4) ||
+ '--' }}
+ </div>
+ </div>
+ <div class="info-row">
+ <div class="info-label">{{ $t('盈亏') }}</div>
+ <div class="info-value red">
+ {{ recordData.profitPercent ? (recordData.profitPercent + '%') : '--%' }}
+ ({{ recordData.profit || '0.00' }}USDT)
+ </div>
+ </div>
+ <div class="sell-btn-wrapper" v-if="canSell">
+ <van-button type="primary" block round class="sell-btn" @click="goToSell(recordData)">
+ {{ $t('卖出') }}
+ </van-button>
+ </div>
+ </div>
+ </div>
</div>
</template>
<script setup>
-import { ref } from "vue";
+import { ref, computed, onMounted, onBeforeUnmount } from "vue";
import { showToast } from 'vant'
-import { _icoList, _icoSubscribe } from "@/service/ico.api.js";
+import { _icoList, _ico_buy, _get_ico_position } from "@/service/ico.api.js";
+import { _getBalance } from "@/service/user.api.js";
import { useI18n } from "vue-i18n";
+import { useUserStore } from '@/store/user';
+import { useRouter } from "vue-router";
+import { setStorage } from "@/utils/index.js";
+import trading from '@/service/trading'
+import { WS_URL } from '@/config'
const { t } = useI18n()
+const userStore = useUserStore()
+const router = useRouter()
-// 获取列表
-const icoList = ref([])
-_icoList().then(res => {
- icoList.value = res.records
-}).catch(err => {
- // showToast(err.msg)
+// 记录数据
+const recordData = ref(null)
+// 申购数量
+const sgNum = ref('')
+// 开仓数据
+const initOpen = ref({})
+// ws
+const socket = ref(null)
+// 价格
+const closePrice = ref(0)
+
+// 计算实际支付
+const actualPayment = computed(() => {
+ if (!sgNum.value || !closePrice.value) return '0'
+ return (parseFloat(sgNum.value) * parseFloat(closePrice.value)).toFixed(4)
})
-// 打开申购弹窗
-const show = ref(false) // 控制弹窗显示
-const itemObj = ref({}) // ico列表项
-const sgNum = ref(0) // 申购数量
-const openBuy = (i) => {
- show.value = true
- itemObj.value = i
+// 判断是否可以卖出(限售时间是否已过)
+const canSell = computed(() => {
+ if (!recordData.value || !recordData.value.lockingTime) return false
+ try {
+ // 将 lockingTime 格式 "2026-02-09" 转换为 Date 对象(设置为当天的 00:00:00)
+ const lockingDate = new Date(recordData.value.lockingTime + ' 00:00:00')
+ const now = new Date()
+ // 如果限售时间早于当前时间,则可以卖出
+ return lockingDate < now
+ } catch (error) {
+ console.error('时间解析错误', error)
+ return false
+ }
+})
+
+// 获取持仓
+const getRecordList = () => {
+ _get_ico_position({
+ session_token: initOpen.value.session_token,
+ }).then(res => {
+ recordData.value = res
+ }).catch(err => {
+ console.error('获取持仓失败', err)
+ })
+}
+
+// 获取开仓数据
+const getInitOpen = () => {
+ if (userStore.userInfo && userStore.userInfo.token) {
+ trading.tradeBuyToken().then(res => {
+ initOpen.value = res || {}
+ getRecordList()
+ }).catch(err => {
+ console.error('获取开仓数据失败', err)
+ })
+ }
+}
+
+// 设置全部
+const setAll = () => {
+ if (!closePrice.value || !initOpen.value.volume) {
+ showToast(t('余额不足'))
+ return
+ }
+ // 计算最大可申购数量(基于可用余额)
+ const maxNum = Math.floor((parseFloat(initOpen.value.volume) / parseFloat(closePrice.value)) * 10000) / 10000
+ sgNum.value = maxNum.toString()
+}
+
+// 最新价格获取
+const handleQoutes = (data) => {
+ if (data && data.length) {
+ const cur = data[0]
+ closePrice.value = cur.close
+ // this.range = cur.change_ratio + ''
+ // this.quote = cur
+ }
+}
+
+// 行情socket
+const startQuoteSocket = () => {
+ socket.value = new WebSocket(`${WS_URL}/1/pendleusdt`)
+ socket.value.onmessage = (evt) => {
+ const { data } = evt
+ const { code, data: _data } = JSON.parse(data)
+ if (code / 1 === 0) {
+ handleQoutes(_data)
+ }
+ }
+}
+
+// 关闭清空ws
+const closeSocket = () => {
+ socket.value && socket.value.close()
+ socket.value = null
}
// 申购
const buy = () => {
- let opt = {
- icoProjectId: itemObj.value.id,
- subscribeNums: sgNum.value,
- subscriptionType: 1,
+ if (!sgNum.value || parseFloat(sgNum.value) <= 0) {
+ showToast(t('请输入申购数量'))
+ return
}
- _icoSubscribe(opt).then(res => {
+
+ let opt = {
+ volume: actualPayment.value,
+ session_token: initOpen.value.session_token,
+ symbol: "pendleusdt", // 币种
+ price: closePrice.value,
+ total: sgNum.value,
+ order_price_type: "opponent", // 市价or限价
+ }
+ _ico_buy(opt).then(res => {
showToast(t('submitSuccess'))
- show.value = false
+ sgNum.value = ''
+ getInitOpen()
}).catch(err => {
- showToast(err)
+ showToast(err.msg || err || t('申购失败'))
})
}
+// 卖出
+const goToSell = (item) => {
+ if (!item) {
+ showToast(t('数据加载中,请稍候'))
+ return
+ }
+ // 将卖出参数存储到 localStorage,供交易页面读取
+ setStorage('tradeSellParams', {
+ volume: item.purchaseQuantity || '0',
+ mode: 'close' // 卖出模式
+ })
+ router.push(`/cryptos/trade/pendleusdt`)
+}
+
+onMounted(() => {
+ getInitOpen()
+ startQuoteSocket()
+})
+
+onBeforeUnmount(() => {
+ closeSocket()
+
+})
</script>
<style lang="scss" scoped>
.ico {
padding: 0rem 1.2rem 5rem 1.2rem;
font-size: 1.5rem;
+ min-height: 100vh;
+ background: #1a1a1a;
- .buy_popup {
- width: 40rem;
- padding: 1rem;
-
- .buy_title {
- font-size: 2.5rem;
- font-weight: 700;
- border-bottom: #aaa solid 1px;
- padding: 1rem;
- }
+ .ico-intro {
+ padding: 1.5rem 0;
+ font-size: 1.4rem;
+ color: #999;
+ text-align: center;
}
- .ico_list {
- padding: 1rem 0rem;
+ .ico-card,
+ .holding-card {
+ background-color: #333;
+ border-radius: 1rem;
+ padding: 1.5rem;
+ margin-bottom: 2rem;
+ border: 1px solid #444;
- .ico_item {
- background-color: #333;
- padding: .5rem 1rem;
- border: #aaa solid 1px;
- border-radius: 1rem;
+ .card-title {
+ font-size: 1.8rem;
+ font-weight: 700;
+ margin-bottom: 1.5rem;
+ color: #fff;
+ }
- .item_1 {
- padding: 1rem .5rem;
- border-bottom: #ccc solid 1px;
- font-size: 2rem;
- font-weight: 700;
+ .card-header {
+ margin-bottom: 1.5rem;
+ padding-bottom: 1rem;
+ border-bottom: 1px solid #555;
+
+ .token-info {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+
+ .token-logo {
+ width: 4rem;
+ height: 4rem;
+ border-radius: 50%;
+ background: #444;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ overflow: hidden;
+
+ img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ }
+
+ .token-placeholder {
+ font-size: 2rem;
+ font-weight: 700;
+ color: #fff;
+ }
+ }
+
+ .token-symbol {
+ font-size: 2rem;
+ font-weight: 700;
+ color: #fff;
+ }
}
+ }
- .item_2 {
- padding: 1rem .5rem;
- border-bottom: #ccc solid 1px;
- font-size: 1.6rem;
- font-weight: 500;
+ .card-content {
+ .price-row {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 1.5rem;
- &>div:last-child {
- color: #999;
+ .price-item {
+ flex: 1;
+
+ .price-label {
+ font-size: 1.4rem;
+ color: #999;
+ margin-bottom: 0.5rem;
+ }
+
+ .price-value {
+ font-size: 1.8rem;
+ font-weight: 600;
+ color: #fff;
+
+ &.green {
+ color: #06CDA5;
+ }
+ }
}
}
- .item_3 {
- padding: .5rem;
+ .input-row {
+ margin-bottom: 1.5rem;
+
+ .input-label {
+ font-size: 1.4rem;
+ color: #999;
+ margin-bottom: 0.5rem;
+ }
+
+ .input-wrapper {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+
+ .subscribe-input {
+ flex: 1;
+ background: #222;
+ border-radius: 0.5rem;
+ padding: 0;
+
+ :deep(.van-field__control) {
+ color: #fff;
+ font-size: 1.6rem;
+ padding: 0 1rem;
+ }
+
+ :deep(.van-field__label) {
+ display: none;
+ }
+ }
+
+ .all-btn {
+ min-width: 6rem;
+ height: 3.5rem;
+ border-radius: 0.5rem;
+ background: #444;
+ color: #fff;
+ border: 1px solid #555;
+ }
+ }
+ }
+
+ .balance-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 1.5rem;
+ padding: 1rem;
+ background: #222;
+ border-radius: 0.5rem;
+
+ .balance-label {
+ font-size: 1.4rem;
+ color: #999;
+ }
+
+ .balance-value {
+ font-size: 1.6rem;
+ color: #fff;
+ }
+ }
+
+ .buy-btn {
+ height: 4.5rem;
+ font-size: 1.8rem;
+ font-weight: 600;
+ background: #555;
+ border: none;
+ color: #fff;
+ }
+
+ .info-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 1rem 0;
+ border-bottom: 1px solid #444;
+
+ &:last-child {
+ border-bottom: none;
+ }
+
+ .info-label {
+ font-size: 1.4rem;
+ color: #999;
+ }
+
+ .info-value {
+ font-size: 1.6rem;
+ color: #fff;
+ font-weight: 500;
+
+ &.red {
+ color: #f43368;
+ }
+ }
+ }
+
+ .sell-btn-wrapper {
+ margin-top: 1.5rem;
+ padding-top: 1.5rem;
+ border-top: 1px solid #444;
+ }
+
+ .sell-btn {
+ height: 4.5rem;
+ font-size: 1.8rem;
+ font-weight: 600;
+ background: #555;
+ border: none;
+ color: #fff;
}
}
}
--
Gitblit v1.9.3