10.10综合交易所原始源码_移动端
1
admin
2026-01-07 cc9e88924fd45b2893d2fb4213ca980e026611a4
src/views/cryptos/darkpoolTradingEtf/index.vue
New file
@@ -0,0 +1,463 @@
<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>