<template>
|
<div class="app-container app-page bg-white rounded-2xl shadow-lg border border-gray-200 overflow-hidden">
|
<!-- 顶部导航栏 -->
|
<assets-head :title="$t('暗池交易') + '(ETF)'" />
|
|
<!-- Tab切换区 -->
|
<div class="fixed-section bg-white py-2 px-6 flex rounded-lg mx-4 mt-4 mb-2 border border-gray-200">
|
<button @click="activeTab = 'products'" :class="['tab-button flex-1 py-2 font-medium rounded-lg transition-all duration-200',
|
activeTab === 'products' ? 'active text-red-600' : 'text-gray-700']">
|
{{ $t('产品列表') }}
|
</button>
|
<button @click="activeTab = 'records'" :class="['tab-button flex-1 py-2 font-medium rounded-lg transition-all duration-200',
|
activeTab === 'records' ? 'active text-red-600' : 'text-gray-700']">
|
{{ $t('my') }}
|
</button>
|
</div>
|
|
<!-- 内容区: 可滚动 -->
|
<div class="content-flex hide-scrollbar px-4 pb-6 overflow-y-auto">
|
<!-- 产品列表 -->
|
<div v-show="activeTab === 'products'" class="space-y-4">
|
<div v-for="product in products" :key="product.id"
|
class="bg-white rounded-xl p-4 border border-gray-200 shadow-sm hover:shadow-md transition-shadow">
|
<div class="flex justify-between items-start">
|
<div class="flex-1">
|
<!-- 产品标题区域 -->
|
<div class="flex items-center justify-between mb-3">
|
<div class="flex items-center">
|
<div>
|
<h3 class="text-gray-800 font-bold text-base">{{ product.stockName }}</h3>
|
<p class="text-gray-500 text-xs mt-1">{{ product.stockCode }}</p>
|
</div>
|
</div>
|
</div>
|
|
<!-- 价格信息区域 -->
|
<div class="bg-gray-50 rounded-lg p-3 mb-3">
|
<div class="flex justify-between items-center">
|
<div>
|
<p class="text-gray-500 text-xs">{{ $t('开仓价格') }}</p>
|
<p class="text-gray-800 font-bold text-lg">
|
{{ product.nowPrice }} $
|
</p>
|
</div>
|
<div class="text-right">
|
<div class="flex items-center">
|
<span class="iconify text-gray-400 mr-1"
|
data-icon="mdi:clock-outline"></span>
|
<span class="text-gray-500 text-xs">{{ $t('最小数量') }}</span>
|
</div>
|
<div class="px-3 py-1 rounded-full text-xs font-medium">
|
{{ product.stockNum }}
|
</div>
|
</div>
|
</div>
|
<div class="flex justify-between items-center">
|
<div>
|
<p class="text-gray-500 text-xs">{{ $t('委托价格') }}</p>
|
<p class="text-gray-800 font-bold text-lg">
|
{{ product.currentPrice }} $
|
</p>
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
<!-- 下单按钮 -->
|
<div class="mt-4 pt-3 border-t border-gray-100">
|
<button @click="openTradeModal(product)"
|
class="trade-btn w-full bg-green-600 hover:bg-green-700 text-white py-3 rounded-lg text-sm font-bold transition-colors shadow-sm flex items-center justify-center">
|
<span class="iconify mr-2"></span>
|
{{ $t('确认下单') }}
|
</button>
|
</div>
|
</div>
|
<van-empty v-if="products.length === 0" :description="$t('暂无数据')" />
|
</div>
|
|
<!-- 购买记录 -->
|
<div v-show="activeTab === 'records'">
|
<!-- 子Tab切换区 -->
|
<div class="bg-gray-100 py-1 px-4 flex rounded-lg mb-4">
|
<button @click="activeSubTab = 'position'" :class="['subtab-button flex-1 py-2 text-sm font-medium rounded-lg transition-all duration-200',
|
activeSubTab === 'position' ? 'active text-red-600' : 'text-gray-700']">
|
{{ $t('持仓') }}
|
</button>
|
<button @click="activeSubTab = 'history'" :class="['subtab-button flex-1 py-2 text-sm font-medium rounded-lg transition-all duration-200',
|
activeSubTab === 'history' ? 'active text-red-600' : 'text-gray-700']">
|
{{ $t('历史') }}
|
</button>
|
</div>
|
|
<!-- 持仓部分 -->
|
<div v-show="activeSubTab === 'position'" class="space-y-4">
|
<div v-for="position in positions" :key="position.id"
|
class="bg-white rounded-xl p-4 border border-gray-200 shadow-sm hover:shadow-md transition-shadow">
|
<div class="flex justify-between items-center">
|
<div>
|
<h3 class="text-gray-800 font-bold">{{ position.stockName }}</h3>
|
<p class="text-gray-500 text-sm mt-1">{{ position.symbol }}</p>
|
</div>
|
<div class="flex">
|
<button @click="closePosition(position)"
|
class="close-btn bg-red-600 hover:bg-red-700 text-white py-2 px-4 rounded-lg text-sm transition-colors font-medium">
|
{{ $t('平仓') }}
|
</button>
|
</div>
|
</div>
|
|
<div class="grid grid-cols-2 gap-4 mt-4">
|
<div>
|
<p class="text-gray-500 text-xs">{{ $t('买入价格') }}</p>
|
<p class="text-gray-800 font-medium">{{ position.price }} $</p>
|
</div>
|
<div>
|
<p class="text-gray-500 text-xs">{{ $t('买入金额') }}</p>
|
<p class="text-gray-800 font-medium">{{ formatNumberWithComma(position.volume) }} $</p>
|
</div>
|
<div>
|
<p class="text-gray-500 text-xs">{{ $t('交易时间') }}</p>
|
<p class="text-gray-800 font-medium">{{ position.createTime }}</p>
|
</div>
|
|
<!-- <div>
|
<p class="text-gray-500 text-xs">{{ $t('状态') }}</p>
|
<p class="text-green-500 font-medium">{{ position.status }}</p>
|
</div> -->
|
</div>
|
|
<div class="mt-4 pt-3 border-t border-gray-100">
|
<div class="flex justify-between items-center">
|
<p class="text-gray-500 text-xs">{{ $t('浮动盈亏') }}</p>
|
<p :class="['font-bold', position.profitLoss >= 0 ? 'text-green-500' : 'text-red-500']">
|
{{ position.profitLoss >= 0 ? '+' : '' }} {{ position.profitLoss }} $ | {{
|
position.profitLossPercentage }}%
|
</p>
|
</div>
|
</div>
|
</div>
|
<van-empty v-if="positions.length === 0" :description="$t('暂无数据')" />
|
</div>
|
|
<!-- 历史部分 -->
|
<div v-show="activeSubTab === 'history'" class="space-y-4">
|
<div v-for="history in histories" :key="history.id"
|
class="bg-white rounded-xl p-4 border border-gray-200 shadow-sm hover:shadow-md transition-shadow">
|
<div class="flex justify-between items-center">
|
<div>
|
<h3 class="text-gray-800 font-bold">{{ history.stockName }}</h3>
|
<p class="text-gray-500 text-sm mt-1">{{ history.symbol }}</p>
|
</div>
|
<span class="bg-gray-100 text-gray-800 text-xs px-2 py-1 rounded-full tj"
|
:class="[history.state == 'position' ? 'cg' : history.state == 'failed' ? 'sb' : history.state == 'closed' ? 'pc' : '']">
|
{{ startStr[history.state] }}
|
</span>
|
</div>
|
|
<div class="grid grid-cols-2 gap-4 mt-4">
|
<div>
|
<p class="text-gray-500 text-xs">{{ $t('买入价格') }}</p>
|
<p class="text-gray-800 font-medium">{{ history.price }} $</p>
|
</div>
|
<div>
|
<p class="text-gray-500 text-xs">{{ $t('买入金额') }}</p>
|
<p class="text-gray-800 font-medium">{{ formatNumberWithComma(history.volume) }} $</p>
|
</div>
|
<div>
|
<p class="text-gray-500 text-xs">{{ $t('交易时间') }}</p>
|
<p class="text-gray-800 font-medium">{{ history.createTime }}</p>
|
</div>
|
|
<div>
|
<p class="text-gray-500 text-xs">{{ $t('卖出价格') }}</p>
|
<p class="text-gray-800 font-medium">{{ history.closePrice || '--' }}</p>
|
</div>
|
<div>
|
<p class="text-gray-500 text-xs">{{ $t('卖出金额') }}</p>
|
<p class="text-gray-800 font-medium">
|
{{ history.closePrice ? formatNumberWithComma(history.closePrice *
|
history.symbolValue) : '--' }}</p>
|
</div>
|
|
<div>
|
<p class="text-gray-500 text-xs">{{ $t('状态') }}</p>
|
<p class="text-orange-500 font-medium tj"
|
:class="[history.state == 'position' ? 'cg' : history.state == 'failed' ? 'sb' : history.state == 'closed' ? 'pc' : '']">
|
{{ startStr[history.state] }}</p>
|
</div>
|
<div>
|
<p class="text-gray-500 text-xs">{{ $t("订单号") }}</p>
|
<p :class="['font-bold']">
|
{{ history.orderNo }}
|
</p>
|
</div>
|
</div>
|
|
<div class="mt-4 pt-3 border-t border-gray-100" v-if="history.state == 'closed'">
|
<div class="flex justify-between items-center">
|
<p class="text-gray-500 text-xs">{{ $t('盈亏') }}</p>
|
<p :class="['font-bold', history.profitLoss >= 0 ? 'text-green-500' : 'text-red-500']">
|
{{ history.profitLoss >= 0 ? '+' : '' }} {{ history.profitLoss }} $ | {{
|
history.profitLossPercentage }}%
|
</p>
|
</div>
|
</div>
|
</div>
|
<van-empty v-if="histories.length === 0" :description="$t('暂无数据')" />
|
</div>
|
</div>
|
</div>
|
|
<!-- 交易弹框 -->
|
<van-popup v-model:show="showTradeModal">
|
<!-- <div v-if="showTradeModal"
|
class="fixed inset-0 bg-gray-800 bg-opacity-70 flex items-center justify-center p-4 z-50"
|
@click="closeTradeModal"> -->
|
<div class="bg-white rounded-xl w-full p-6" style="width:40rem" @click.stop>
|
<div class="flex justify-between items-center mb-4">
|
<h3 class="text-gray-800 text-lg font-bold">{{ $t('确认下单') }}</h3>
|
<button @click="closeTradeModal" class="text-gray-500 hover:text-gray-800">
|
<span class="iconify text-xl" data-icon="mdi:close"></span>
|
</button>
|
</div>
|
|
<div class="bg-gray-100 rounded-lg p-4 mb-4">
|
<div class="flex justify-between">
|
<h4 class="text-gray-800 font-bold">{{ selectedProduct?.stockName }}</h4>
|
<span class="text-gray-500 text-sm">{{ selectedProduct?.stockCode }}</span>
|
</div>
|
<p class="text-gray-800 mt-2 font-bold text-xl">{{ selectedProduct?.nowPrice }}
|
</p>
|
</div>
|
|
<div class="space-y-4">
|
<div>
|
<label class="text-gray-500 text-sm mb-1 block">{{ $t('交易数量') }}</label>
|
<input v-model="tradeQuantity" type="number"
|
class="w-full bg-white border border-gray-300 rounded-lg px-4 py-3 text-gray-800 focus:outline-none focus:ring-2 focus:ring-green-500"
|
:placeholder="$t('请输入数量')" oninput="value = value.replace(/[^\d]/g, '').replace(/^0+/, '')"
|
min="1">
|
</div>
|
</div>
|
|
<div class="flex space-x-3 mt-6">
|
<button @click="closeTradeModal"
|
class="flex-1 bg-transparent border border-red-600 text-red-600 py-3 rounded-lg transition-colors hover:bg-red-50 font-medium">
|
{{ $t('取消') }}
|
</button>
|
<button @click="confirmTrade"
|
class="flex-1 bg-green-600 text-white py-3 rounded-lg transition-colors hover:bg-green-700 font-medium">
|
{{ $t('确认') }}
|
</button>
|
</div>
|
</div>
|
<!-- </div> -->
|
</van-popup>
|
|
</div>
|
</template>
|
|
<script setup>
|
import { ref, reactive, watch } from 'vue'
|
import { _getetfDpList, _buyetfStockDp, _getetfDpHistories, _getetfDpOrderList, _getetfDzCloseStockDp } from '@/service/quotes.api'
|
import { showConfirmDialog } from 'vant';
|
import { useI18n } from 'vue-i18n'
|
import { showToast } from 'vant'
|
import { formatNumberWithComma } from '@/utils/utis';
|
|
// 响应式数据
|
const { t } = useI18n()
|
const activeTab = ref('products')
|
const activeSubTab = ref('position')
|
const showTradeModal = ref(false)
|
const selectedProduct = ref(null)
|
const tradeQuantity = ref(1)
|
const tradeType = ref('buy') // 'buy' or 'close'
|
const startStr = {
|
submitted: t('submitted'),
|
position: t('持仓'),
|
failed: t('失败'),
|
closed: t('平仓')
|
}
|
|
// 产品数据
|
const products = ref([])
|
|
// 获取产品数据
|
const getProducts = () => {
|
_getetfDpList().then(res => {
|
products.value = res.records
|
})
|
}
|
|
// 持仓数据
|
const positions = ref([])
|
|
// 历史数据
|
const histories = ref([])
|
|
// 获取历史数据
|
const getHistories = () => {
|
_getetfDpHistories().then(res => {
|
histories.value = res.records
|
})
|
}
|
|
// 获取持仓数据
|
const getOrderList = () => {
|
_getetfDpOrderList().then(res => {
|
positions.value = res.records
|
})
|
}
|
|
// 监听activeTab切换并调用接口
|
watch([activeTab, activeSubTab], ([newTab, newSubTab]) => {
|
// 获取产品数据
|
if (newTab == 'products') {
|
getProducts()
|
}
|
// 获取历史数据
|
else if (newTab == 'records' && activeSubTab.value == 'history') {
|
getHistories()
|
}
|
// 获取持仓数据
|
else if (newTab == 'records' && activeSubTab.value == 'position') {
|
getOrderList()
|
}
|
}, { immediate: true })
|
|
// 方法
|
// 打开下单弹窗
|
const openTradeModal = (product, type = 'buy') => {
|
selectedProduct.value = product
|
tradeType.value = type
|
tradeQuantity.value = 1
|
showTradeModal.value = true
|
}
|
// 关闭下单弹窗
|
const closeTradeModal = () => {
|
showTradeModal.value = false
|
selectedProduct.value = null
|
tradeQuantity.value = 1
|
}
|
// 确认下单
|
const confirmTrade = async () => {
|
if (tradeType.value === 'buy') {
|
if (!tradeQuantity.value || tradeQuantity.value == 0) {
|
showToast(t('请输入数量'))
|
return
|
}
|
// 买入
|
const newPosition = {
|
dzId: selectedProduct.value.uuid,
|
num: tradeQuantity.value,
|
}
|
let res = await _buyetfStockDp(newPosition)
|
showToast(t('SuccessfulOperation'))
|
}
|
closeTradeModal()
|
}
|
// 平仓
|
const closePosition = (row) => {
|
showConfirmDialog({
|
title: t('平仓提示'),
|
message: t('是否平仓?'),
|
confirmButtonText: t('确认'),
|
cancelButtonText: t('取消'),
|
}).then(async () => {
|
let res = await _getetfDzCloseStockDp({ id: row.uuid })
|
showToast(t('SuccessfulOperation'))
|
getOrderList()
|
}).catch(() => { });
|
}
|
|
</script>
|
|
<style scoped>
|
button {
|
/* font-size: ; */
|
}
|
|
.border,
|
.border-t {
|
border-color: #ccc;
|
}
|
|
.cg {
|
color: greenyellow !important;
|
}
|
|
.sb {
|
color: red !important;
|
}
|
|
.pc {
|
color: #aaa;
|
}
|
|
.tj {
|
color: green;
|
}
|
|
.app-container {
|
width: 100%;
|
min-height: 812px;
|
display: flex;
|
flex-direction: column;
|
overflow: hidden;
|
margin: 0 auto;
|
font-size: 1.8rem;
|
}
|
|
.fixed-section {
|
flex-shrink: 0;
|
}
|
|
.content-flex {
|
flex: 1;
|
overflow-y: auto;
|
min-height: 0;
|
}
|
|
.hide-scrollbar::-webkit-scrollbar {
|
display: none;
|
}
|
|
.hide-scrollbar {
|
-ms-overflow-style: none;
|
scrollbar-width: none;
|
}
|
|
.app-page {
|
border: 1px solid #e2e8f0;
|
border-radius: 12px;
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
}
|
|
.tab-button.active {
|
background-color: rgba(239, 68, 68, 0.15);
|
color: #dc2626;
|
font-weight: 600;
|
}
|
|
.subtab-button.active {
|
background-color: rgba(239, 68, 68, 0.15);
|
color: #dc2626;
|
font-weight: 600;
|
}
|
|
/* 移动端适配 */
|
@media (max-width: 768px) {
|
.app-container {
|
width: 100%;
|
height: 100vh;
|
border-radius: 0;
|
}
|
|
.app-page {
|
border-radius: 0;
|
box-shadow: none;
|
}
|
}
|
</style>
|