Initial commit: Monisuo - 虚拟货币模拟交易平台
功能模块: - 用户注册/登录/KYC - 资金账户/交易账户 - 实时行情/币种管理 - 即时交易/充提审核 - 管理后台 技术栈: - 后端: SpringBoot 2.2.4 + MyBatis Plus - 前端: uni-app x (Vue3 + UTS) - 数据库: MySQL Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
448
app/pages/asset/asset.uvue
Normal file
448
app/pages/asset/asset.uvue
Normal file
@@ -0,0 +1,448 @@
|
||||
<template>
|
||||
<view class="asset-container">
|
||||
<!-- 总资产卡片 -->
|
||||
<view class="total-card">
|
||||
<text class="total-label">总资产估值(USDT)</text>
|
||||
<text class="total-value">${{ totalAssets }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 账户切换 -->
|
||||
<view class="account-tabs">
|
||||
<text :class="['tab', activeAccount === 'fund' ? 'active' : '']" @click="activeAccount = 'fund'">资金账户</text>
|
||||
<text :class="['tab', activeAccount === 'trade' ? 'active' : '']" @click="activeAccount = 'trade'">交易账户</text>
|
||||
</view>
|
||||
|
||||
<!-- 资金账户 -->
|
||||
<view v-if="activeAccount === 'fund'" class="account-section">
|
||||
<view class="balance-card">
|
||||
<text class="balance-label">USDT余额</text>
|
||||
<text class="balance-value">{{ fundBalance }}</text>
|
||||
</view>
|
||||
<view class="action-btns">
|
||||
<button class="action-btn deposit" @click="showDeposit = true">
|
||||
<text class="btn-text">充值</text>
|
||||
</button>
|
||||
<button class="action-btn withdraw" @click="showWithdraw = true">
|
||||
<text class="btn-text">提现</text>
|
||||
</button>
|
||||
<button class="action-btn transfer" @click="showTransfer = true">
|
||||
<text class="btn-text">划转</text>
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 交易账户 -->
|
||||
<view v-if="activeAccount === 'trade'" class="trade-section">
|
||||
<view class="position-list">
|
||||
<view class="position-item" v-for="(pos, index) in positions" :key="index">
|
||||
<view class="pos-left">
|
||||
<text class="pos-icon">{{ pos.icon }}</text>
|
||||
<view class="pos-info">
|
||||
<text class="pos-code">{{ pos.coinCode }}</text>
|
||||
<text class="pos-name">{{ pos.coinName }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="pos-right">
|
||||
<text class="pos-quantity">{{ pos.quantity }}</text>
|
||||
<text class="pos-value">≈ ${{ pos.value }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 充值弹窗 -->
|
||||
<view v-if="showDeposit" class="modal-mask" @click="showDeposit = false">
|
||||
<view class="modal-content" @click.stop="">
|
||||
<text class="modal-title">充值</text>
|
||||
<view class="modal-form">
|
||||
<text class="form-label">充值金额(USDT)</text>
|
||||
<input class="form-input" type="digit" v-model="depositAmount" placeholder="请输入金额" />
|
||||
</view>
|
||||
<view class="modal-btns">
|
||||
<button class="modal-btn cancel" @click="showDeposit = false"><text class="btn-text">取消</text></button>
|
||||
<button class="modal-btn confirm" @click="handleDeposit"><text class="btn-text">确认</text></button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 提现弹窗 -->
|
||||
<view v-if="showWithdraw" class="modal-mask" @click="showWithdraw = false">
|
||||
<view class="modal-content" @click.stop="">
|
||||
<text class="modal-title">提现</text>
|
||||
<view class="modal-form">
|
||||
<text class="form-label">提现金额(USDT)</text>
|
||||
<input class="form-input" type="digit" v-model="withdrawAmount" placeholder="请输入金额" />
|
||||
</view>
|
||||
<view class="modal-btns">
|
||||
<button class="modal-btn cancel" @click="showWithdraw = false"><text class="btn-text">取消</text></button>
|
||||
<button class="modal-btn confirm" @click="handleWithdraw"><text class="btn-text">确认</text></button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 划转弹窗 -->
|
||||
<view v-if="showTransfer" class="modal-mask" @click="showTransfer = false">
|
||||
<view class="modal-content" @click.stop="">
|
||||
<text class="modal-title">资金划转</text>
|
||||
<view class="transfer-direction">
|
||||
<text :class="['dir-item', transferDir === 1 ? 'active' : '']" @click="transferDir = 1">资金→交易</text>
|
||||
<text :class="['dir-item', transferDir === 2 ? 'active' : '']" @click="transferDir = 2">交易→资金</text>
|
||||
</view>
|
||||
<view class="modal-form">
|
||||
<text class="form-label">划转金额(USDT)</text>
|
||||
<input class="form-input" type="digit" v-model="transferAmount" placeholder="请输入金额" />
|
||||
</view>
|
||||
<view class="modal-btns">
|
||||
<button class="modal-btn cancel" @click="showTransfer = false"><text class="btn-text">取消</text></button>
|
||||
<button class="modal-btn confirm" @click="handleTransfer"><text class="btn-text">确认</text></button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="uts">
|
||||
import { getOverview, getTradeAccount, transfer } from '@/api/asset.uts'
|
||||
import { deposit, withdraw } from '@/api/fund.uts'
|
||||
|
||||
type PositionItem = {
|
||||
coinCode: string
|
||||
coinName: string
|
||||
quantity: string
|
||||
value: string
|
||||
icon: string
|
||||
}
|
||||
|
||||
const totalAssets = ref('0.00')
|
||||
const fundBalance = ref('0.00')
|
||||
const positions = ref<PositionItem[]>([])
|
||||
const activeAccount = ref('fund')
|
||||
|
||||
const showDeposit = ref(false)
|
||||
const showWithdraw = ref(false)
|
||||
const showTransfer = ref(false)
|
||||
const depositAmount = ref('')
|
||||
const withdrawAmount = ref('')
|
||||
const transferAmount = ref('')
|
||||
const transferDir = ref(1)
|
||||
|
||||
onShow(() => {
|
||||
loadData()
|
||||
})
|
||||
|
||||
func loadData () {
|
||||
getOverview()
|
||||
.then((res) => {
|
||||
const data = res.data as UTSJSONObject
|
||||
totalAssets.value = formatAmount(data['totalAssets'] as number)
|
||||
fundBalance.value = formatAmount(data['fundBalance'] as number)
|
||||
})
|
||||
.catch(() => {})
|
||||
|
||||
getTradeAccount()
|
||||
.then((res) => {
|
||||
const data = res.data as UTSJSONObject
|
||||
const list = data['positions'] as Array<UTSJSONObject>
|
||||
const items: PositionItem[] = []
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
const item = list[i]
|
||||
items.push({
|
||||
coinCode: item['coinCode'] as string,
|
||||
coinName: item['coinName'] as string,
|
||||
quantity: (item['quantity'] as number).toFixed(4),
|
||||
value: formatAmount(item['value'] as number),
|
||||
icon: getCoinIcon(item['coinCode'] as string)
|
||||
})
|
||||
}
|
||||
positions.value = items
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
func handleDeposit () {
|
||||
if (depositAmount.value === '' || parseFloat(depositAmount.value) <= 0) {
|
||||
uni.showToast({ title: '请输入有效金额', icon: 'none' })
|
||||
return
|
||||
}
|
||||
deposit(depositAmount.value, null)
|
||||
.then(() => {
|
||||
uni.showToast({ title: '申请成功,等待审批', icon: 'success' })
|
||||
showDeposit.value = false
|
||||
depositAmount.value = ''
|
||||
loadData()
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
func handleWithdraw () {
|
||||
if (withdrawAmount.value === '' || parseFloat(withdrawAmount.value) <= 0) {
|
||||
uni.showToast({ title: '请输入有效金额', icon: 'none' })
|
||||
return
|
||||
}
|
||||
withdraw(withdrawAmount.value, null)
|
||||
.then(() => {
|
||||
uni.showToast({ title: '申请成功,等待审批', icon: 'success' })
|
||||
showWithdraw.value = false
|
||||
withdrawAmount.value = ''
|
||||
loadData()
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
func handleTransfer () {
|
||||
if (transferAmount.value === '' || parseFloat(transferAmount.value) <= 0) {
|
||||
uni.showToast({ title: '请输入有效金额', icon: 'none' })
|
||||
return
|
||||
}
|
||||
transfer(transferDir.value, transferAmount.value)
|
||||
.then(() => {
|
||||
uni.showToast({ title: '划转成功', icon: 'success' })
|
||||
showTransfer.value = false
|
||||
transferAmount.value = ''
|
||||
loadData()
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
func formatAmount (value: number): string {
|
||||
return value.toFixed(2)
|
||||
}
|
||||
|
||||
func getCoinIcon (code: string): string {
|
||||
const icons: Map<string, string> = new Map()
|
||||
icons.set('BTC', '₿')
|
||||
icons.set('ETH', 'Ξ')
|
||||
icons.set('SOL', '◎')
|
||||
icons.set('USDT', '₮')
|
||||
return icons.get(code) || '●'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.asset-container {
|
||||
min-height: 100vh;
|
||||
background: #1A1A2E;
|
||||
padding: 24rpx;
|
||||
padding-bottom: 120rpx;
|
||||
}
|
||||
|
||||
.total-card {
|
||||
background: linear-gradient(135deg, #00D4AA 0%, #00B894 100%);
|
||||
border-radius: 24rpx;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.total-label {
|
||||
font-size: 26rpx;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.total-value {
|
||||
font-size: 48rpx;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.account-tabs {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
background: #16213E;
|
||||
border-radius: 16rpx;
|
||||
padding: 8rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.tab {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 16rpx 0;
|
||||
font-size: 28rpx;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.tab.active {
|
||||
background: rgba(0, 212, 170, 0.2);
|
||||
color: #00D4AA;
|
||||
}
|
||||
|
||||
.balance-card {
|
||||
background: #16213E;
|
||||
border-radius: 20rpx;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.balance-label {
|
||||
font-size: 26rpx;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.balance-value {
|
||||
font-size: 40rpx;
|
||||
font-weight: bold;
|
||||
color: #00D4AA;
|
||||
}
|
||||
|
||||
.action-btns {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
border: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.action-btn.deposit { background: #00C853; }
|
||||
.action-btn.withdraw { background: #FF5252; }
|
||||
.action-btn.transfer { background: #00D4AA; }
|
||||
|
||||
.btn-text {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.position-list {
|
||||
background: #16213E;
|
||||
border-radius: 20rpx;
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.position-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.position-item:last-child { border-bottom: none; }
|
||||
|
||||
.pos-left {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pos-icon {
|
||||
font-size: 32rpx;
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.pos-info { display: flex; flex-direction: column; }
|
||||
.pos-code { font-size: 28rpx; font-weight: bold; color: #fff; }
|
||||
.pos-name { font-size: 22rpx; color: rgba(255, 255, 255, 0.5); }
|
||||
|
||||
.pos-right { display: flex; flex-direction: column; align-items: flex-end; }
|
||||
.pos-quantity { font-size: 28rpx; color: #fff; }
|
||||
.pos-value { font-size: 22rpx; color: rgba(255, 255, 255, 0.5); }
|
||||
|
||||
.modal-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
width: 600rpx;
|
||||
background: #16213E;
|
||||
border-radius: 24rpx;
|
||||
padding: 32rpx;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.modal-form {
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-size: 26rpx;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 12rpx;
|
||||
padding: 0 24rpx;
|
||||
font-size: 32rpx;
|
||||
color: #fff;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.transfer-direction {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: 24rpx;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 12rpx;
|
||||
padding: 8rpx;
|
||||
}
|
||||
|
||||
.dir-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 16rpx 0;
|
||||
font-size: 26rpx;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.dir-item.active {
|
||||
background: #00D4AA;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.modal-btns {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
.modal-btn {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
border: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.modal-btn.cancel { background: rgba(255, 255, 255, 0.1); }
|
||||
.modal-btn.confirm { background: #00D4AA; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user