<template>
|
<div class="ico">
|
<fx-header :showLeft="false">
|
<template v-slot:title>
|
<div>{{ $t('ICO申购') }}</div>
|
</template>
|
<!-- <template v-slot:right>
|
<van-icon name="todo-list-o" @click="$router.push('/ICO/icoRecord')" />
|
</template> -->
|
</fx-header>
|
|
<div class="ico-intro">
|
{{ $t('发掘最热、最新币种,及时捕捉市场机会') }}
|
</div>
|
|
<!-- ICO申购卡片 -->
|
<div class="ico-card">
|
<div class="card-header">
|
<div class="token-info">
|
<div class="token-symbol">CLK</div>
|
</div>
|
</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="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>
|
|
<!-- 持仓信息卡片 -->
|
<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>
|
|
<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, computed, onMounted, onBeforeUnmount } from "vue";
|
import { showToast } from 'vant'
|
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 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 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 = () => {
|
if (!sgNum.value || parseFloat(sgNum.value) <= 0) {
|
showToast(t('请输入申购数量'))
|
return
|
}
|
|
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'))
|
sgNum.value = ''
|
getInitOpen()
|
}).catch(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;
|
|
.ico-intro {
|
padding: 1.5rem 0;
|
font-size: 1.4rem;
|
color: #999;
|
text-align: center;
|
}
|
|
.ico-card,
|
.holding-card {
|
background-color: #333;
|
border-radius: 1rem;
|
padding: 1.5rem;
|
margin-bottom: 2rem;
|
border: 1px solid #444;
|
|
.card-title {
|
font-size: 1.8rem;
|
font-weight: 700;
|
margin-bottom: 1.5rem;
|
color: #fff;
|
}
|
|
.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;
|
}
|
}
|
}
|
|
.card-content {
|
.price-row {
|
display: flex;
|
justify-content: space-between;
|
margin-bottom: 1.5rem;
|
|
.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;
|
}
|
}
|
}
|
}
|
|
.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;
|
}
|
}
|
}
|
}
|
</style>
|