|
|
@@ -1,73 +1,57 @@
|
|
|
<template>
|
|
|
- <view class="page-transaction">
|
|
|
- <!-- 顶部标题卡片 -->
|
|
|
- <view class="page-title-card">
|
|
|
- <text class="page-title-text">交易记录</text>
|
|
|
+ <view class="page-container">
|
|
|
+ <!-- 顶部导航栏 -->
|
|
|
+ <view class="custom-navbar">
|
|
|
+ <view class="navbar-back" @click="handleBack">
|
|
|
+ <text class="back-icon">←</text>
|
|
|
+ </view>
|
|
|
+ <view class="navbar-title">
|
|
|
+ <text class="title-text">交易记录</text>
|
|
|
+ </view>
|
|
|
+ <view class="navbar-placeholder"></view>
|
|
|
</view>
|
|
|
|
|
|
+ <!-- 交易记录列表 -->
|
|
|
<scroll-view class="scroll-view" scroll-y>
|
|
|
- <view class="content-wrapper">
|
|
|
- <!-- 统计卡片 -->
|
|
|
- <view class="stats-card">
|
|
|
- <view class="stat-item">
|
|
|
- <text class="stat-label">总交易次数</text>
|
|
|
- <text class="stat-value">{{ totalTransactions }}</text>
|
|
|
- </view>
|
|
|
- <view class="stat-divider"></view>
|
|
|
- <view class="stat-item">
|
|
|
- <text class="stat-label">总盈亏</text>
|
|
|
- <text :class="['stat-value', totalProfit >= 0 ? 'profit' : 'loss']">
|
|
|
- {{ totalProfit >= 0 ? '+' : '' }}¥{{ Math.abs(totalProfit).toFixed(2) }}
|
|
|
- </text>
|
|
|
+ <view class="transaction-list">
|
|
|
+ <!-- 按日期分组显示 -->
|
|
|
+ <view v-for="(group, dateKey) in groupedTransactions" :key="dateKey" class="date-group">
|
|
|
+ <view class="date-header">
|
|
|
+ <text class="date-text">{{ dateKey }}</text>
|
|
|
</view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <!-- 交易列表 -->
|
|
|
- <view class="transaction-list">
|
|
|
- <view v-if="transactions.length === 0" class="empty-state">
|
|
|
- <text class="empty-icon">📋</text>
|
|
|
- <text class="empty-text">暂无交易记录</text>
|
|
|
- <text class="empty-desc">开始您的模拟交易之旅吧</text>
|
|
|
- </view>
|
|
|
-
|
|
|
+
|
|
|
<view
|
|
|
- v-else
|
|
|
- v-for="(item, index) in transactions"
|
|
|
+ v-for="(item, index) in group"
|
|
|
:key="index"
|
|
|
class="transaction-item"
|
|
|
>
|
|
|
- <view class="transaction-header">
|
|
|
+ <view class="item-left">
|
|
|
<view class="stock-info">
|
|
|
<text class="stock-name">{{ item.stockName }}</text>
|
|
|
<text class="stock-code">{{ item.stockCode }}</text>
|
|
|
</view>
|
|
|
- <view :class="['transaction-type', item.type]">
|
|
|
- <text class="type-text">{{ item.type === 'buy' ? '买入' : '卖出' }}</text>
|
|
|
- </view>
|
|
|
+ <text class="transaction-time">{{ formatTime(item.timestamp) }}</text>
|
|
|
</view>
|
|
|
-
|
|
|
- <view class="transaction-details">
|
|
|
- <view class="detail-row">
|
|
|
- <text class="detail-label">价格:</text>
|
|
|
- <text class="detail-value">¥{{ item.price.toFixed(2) }}</text>
|
|
|
- </view>
|
|
|
- <view class="detail-row">
|
|
|
- <text class="detail-label">数量:</text>
|
|
|
- <text class="detail-value">{{ item.quantity }}股</text>
|
|
|
+
|
|
|
+ <view class="item-right">
|
|
|
+ <text :class="['amount-text', item.type === 'buy' ? 'amount-buy' : 'amount-sell']">
|
|
|
+ {{ item.type === 'buy' ? '-' : '+' }}¥{{ formatAmount(item.totalAmount) }}
|
|
|
+ </text>
|
|
|
+ <view :class="['status-badge', item.type === 'buy' ? 'status-buy' : 'status-sell']">
|
|
|
+ <text class="status-text">{{ item.type === 'buy' ? '买入' : '卖出' }}</text>
|
|
|
</view>
|
|
|
- <view class="detail-row">
|
|
|
- <text class="detail-label">总额:</text>
|
|
|
- <text class="detail-value amount">¥{{ item.totalAmount.toFixed(2) }}</text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <view class="transaction-footer">
|
|
|
- <text class="transaction-time">{{ formatTime(item.timestamp) }}</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
- <!-- 预留底部空间 -->
|
|
|
+ <!-- 空状态 -->
|
|
|
+ <view v-if="transactions.length === 0" class="empty-state">
|
|
|
+ <text class="empty-icon">📋</text>
|
|
|
+ <text class="empty-text">暂无交易记录</text>
|
|
|
+ <text class="empty-desc">开始您的模拟交易之旅吧</text>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 底部安全区域 -->
|
|
|
<view class="bottom-safe-area"></view>
|
|
|
</view>
|
|
|
</scroll-view>
|
|
|
@@ -79,31 +63,81 @@ import { ref, computed, onMounted } from 'vue'
|
|
|
|
|
|
const transactions = ref([])
|
|
|
|
|
|
-// 计算总交易次数
|
|
|
-const totalTransactions = computed(() => {
|
|
|
- return transactions.value.length
|
|
|
-})
|
|
|
+// 返回上一页
|
|
|
+const handleBack = () => {
|
|
|
+ const pages = getCurrentPages()
|
|
|
+ if (pages.length > 1) {
|
|
|
+ // 有上一页,返回
|
|
|
+ uni.navigateBack()
|
|
|
+ } else {
|
|
|
+ // 没有上一页,跳转到个人中心
|
|
|
+ uni.switchTab({
|
|
|
+ url: '/pages/mine/mine'
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
-// 计算总盈亏(简化计算:买入为负,卖出为正)
|
|
|
-const totalProfit = computed(() => {
|
|
|
- return transactions.value.reduce((sum, item) => {
|
|
|
- if (item.type === 'buy') {
|
|
|
- return sum - item.totalAmount
|
|
|
- } else {
|
|
|
- return sum + item.totalAmount
|
|
|
+// 按日期分组的交易记录
|
|
|
+const groupedTransactions = computed(() => {
|
|
|
+ const groups = {}
|
|
|
+
|
|
|
+ transactions.value.forEach(item => {
|
|
|
+ const dateKey = formatDate(item.timestamp)
|
|
|
+ if (!groups[dateKey]) {
|
|
|
+ groups[dateKey] = []
|
|
|
}
|
|
|
- }, 0)
|
|
|
+ groups[dateKey].push(item)
|
|
|
+ })
|
|
|
+
|
|
|
+ return groups
|
|
|
})
|
|
|
|
|
|
-// 格式化时间
|
|
|
+// 格式化日期(用于分组)
|
|
|
+const formatDate = (timestamp) => {
|
|
|
+ const date = new Date(timestamp)
|
|
|
+ const today = new Date()
|
|
|
+ const yesterday = new Date(today)
|
|
|
+ yesterday.setDate(yesterday.getDate() - 1)
|
|
|
+
|
|
|
+ const dateStr = date.toLocaleDateString('zh-CN', {
|
|
|
+ year: 'numeric',
|
|
|
+ month: '2-digit',
|
|
|
+ day: '2-digit'
|
|
|
+ })
|
|
|
+
|
|
|
+ const todayStr = today.toLocaleDateString('zh-CN', {
|
|
|
+ year: 'numeric',
|
|
|
+ month: '2-digit',
|
|
|
+ day: '2-digit'
|
|
|
+ })
|
|
|
+
|
|
|
+ const yesterdayStr = yesterday.toLocaleDateString('zh-CN', {
|
|
|
+ year: 'numeric',
|
|
|
+ month: '2-digit',
|
|
|
+ day: '2-digit'
|
|
|
+ })
|
|
|
+
|
|
|
+ if (dateStr === todayStr) {
|
|
|
+ return '今天'
|
|
|
+ } else if (dateStr === yesterdayStr) {
|
|
|
+ return '昨天'
|
|
|
+ } else {
|
|
|
+ return dateStr
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 格式化金额
|
|
|
+const formatAmount = (amount) => {
|
|
|
+ return amount.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
|
|
|
+}
|
|
|
+
|
|
|
+// 格式化时间(仅显示时分秒)
|
|
|
const formatTime = (timestamp) => {
|
|
|
const date = new Date(timestamp)
|
|
|
- const year = date.getFullYear()
|
|
|
- const month = String(date.getMonth() + 1).padStart(2, '0')
|
|
|
- const day = String(date.getDate()).padStart(2, '0')
|
|
|
const hours = String(date.getHours()).padStart(2, '0')
|
|
|
const minutes = String(date.getMinutes()).padStart(2, '0')
|
|
|
- return `${year}-${month}-${day} ${hours}:${minutes}`
|
|
|
+
|
|
|
+ return `${hours}:${minutes}`
|
|
|
}
|
|
|
|
|
|
// 加载交易记录
|
|
|
@@ -123,143 +157,117 @@ onMounted(() => {
|
|
|
})
|
|
|
</script>
|
|
|
|
|
|
-<style>
|
|
|
-.page-transaction {
|
|
|
- height: 100vh;
|
|
|
+<style scoped>
|
|
|
+.page-container {
|
|
|
+ min-height: 100vh;
|
|
|
+ background: #f5f6fb;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
- background: #f5f6fb;
|
|
|
}
|
|
|
|
|
|
-.page-title-card {
|
|
|
+/* 自定义导航栏 */
|
|
|
+.custom-navbar {
|
|
|
background: #ffffff;
|
|
|
- padding: 30rpx 0;
|
|
|
- text-align: center;
|
|
|
- box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
|
|
|
- border-radius: 0;
|
|
|
-}
|
|
|
-
|
|
|
-.page-title-text {
|
|
|
- font-size: 36rpx;
|
|
|
- font-weight: 800;
|
|
|
- color: #3F51F7;
|
|
|
- letter-spacing: 2rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.scroll-view {
|
|
|
- flex: 1;
|
|
|
- height: 0;
|
|
|
-}
|
|
|
-
|
|
|
-.content-wrapper {
|
|
|
- padding: 32rpx;
|
|
|
- background: #f5f6fb;
|
|
|
- min-height: 100%;
|
|
|
-}
|
|
|
-
|
|
|
-/* 统计卡片 */
|
|
|
-.stats-card {
|
|
|
- background: linear-gradient(135deg, #5d55e8, #7568ff);
|
|
|
- border-radius: 24rpx;
|
|
|
- padding: 40rpx 32rpx;
|
|
|
- margin-bottom: 32rpx;
|
|
|
- box-shadow: 0 16rpx 40rpx rgba(93, 85, 232, 0.3);
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ padding: 80rpx 32rpx 30rpx;
|
|
|
+ box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
|
|
|
+ position: relative;
|
|
|
}
|
|
|
|
|
|
-.stat-item {
|
|
|
- flex: 1;
|
|
|
+.navbar-back {
|
|
|
+ width: 80rpx;
|
|
|
+ height: 60rpx;
|
|
|
display: flex;
|
|
|
- flex-direction: column;
|
|
|
align-items: center;
|
|
|
+ justify-content: flex-start;
|
|
|
}
|
|
|
|
|
|
-.stat-divider {
|
|
|
- width: 2rpx;
|
|
|
- height: 60rpx;
|
|
|
- background: rgba(255, 255, 255, 0.3);
|
|
|
+.back-icon {
|
|
|
+ font-size: 40rpx;
|
|
|
+ color: #222222;
|
|
|
+ font-weight: bold;
|
|
|
}
|
|
|
|
|
|
-.stat-label {
|
|
|
- font-size: 24rpx;
|
|
|
- color: rgba(255, 255, 255, 0.8);
|
|
|
- margin-bottom: 12rpx;
|
|
|
+.navbar-title {
|
|
|
+ position: absolute;
|
|
|
+ left: 50%;
|
|
|
+ transform: translateX(-50%);
|
|
|
}
|
|
|
|
|
|
-.stat-value {
|
|
|
- font-size: 40rpx;
|
|
|
- font-weight: 700;
|
|
|
- color: #ffffff;
|
|
|
+.title-text {
|
|
|
+ font-size: 36rpx;
|
|
|
+ font-weight: 800;
|
|
|
+ color: #3F51F7;
|
|
|
+ letter-spacing: 2rpx;
|
|
|
}
|
|
|
|
|
|
-.stat-value.profit {
|
|
|
- color: #4fffb0;
|
|
|
+.navbar-placeholder {
|
|
|
+ width: 80rpx;
|
|
|
}
|
|
|
|
|
|
-.stat-value.loss {
|
|
|
- color: #ff6b9d;
|
|
|
+/* 交易记录列表 */
|
|
|
+.scroll-view {
|
|
|
+ flex: 1;
|
|
|
+ height: 0;
|
|
|
}
|
|
|
|
|
|
-/* 交易列表 */
|
|
|
.transaction-list {
|
|
|
- background: #ffffff;
|
|
|
- border-radius: 24rpx;
|
|
|
- overflow: hidden;
|
|
|
- box-shadow: 0 16rpx 40rpx rgba(37, 52, 94, 0.08);
|
|
|
+ padding: 0 0 32rpx;
|
|
|
}
|
|
|
|
|
|
-/* 空状态 */
|
|
|
-.empty-state {
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- align-items: center;
|
|
|
- padding: 100rpx 40rpx;
|
|
|
-}
|
|
|
-
|
|
|
-.empty-icon {
|
|
|
- font-size: 80rpx;
|
|
|
- margin-bottom: 24rpx;
|
|
|
+/* 日期分组 */
|
|
|
+.date-group {
|
|
|
+ margin-bottom: 32rpx;
|
|
|
}
|
|
|
|
|
|
-.empty-text {
|
|
|
- font-size: 28rpx;
|
|
|
- color: #666a7f;
|
|
|
- margin-bottom: 12rpx;
|
|
|
+.date-header {
|
|
|
+ padding: 24rpx 32rpx 16rpx;
|
|
|
}
|
|
|
|
|
|
-.empty-desc {
|
|
|
- font-size: 24rpx;
|
|
|
+.date-text {
|
|
|
+ font-size: 26rpx;
|
|
|
color: #9ca2b5;
|
|
|
+ font-weight: 500;
|
|
|
}
|
|
|
|
|
|
/* 交易项 */
|
|
|
.transaction-item {
|
|
|
+ background: #ffffff;
|
|
|
padding: 32rpx;
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
border-bottom: 1rpx solid #f5f6fb;
|
|
|
}
|
|
|
|
|
|
+.transaction-item:first-child {
|
|
|
+ border-top-left-radius: 0;
|
|
|
+ border-top-right-radius: 0;
|
|
|
+}
|
|
|
+
|
|
|
.transaction-item:last-child {
|
|
|
border-bottom: none;
|
|
|
}
|
|
|
|
|
|
-.transaction-header {
|
|
|
+.item-left {
|
|
|
+ flex: 1;
|
|
|
display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- align-items: center;
|
|
|
- margin-bottom: 20rpx;
|
|
|
+ flex-direction: column;
|
|
|
}
|
|
|
|
|
|
.stock-info {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
- gap: 16rpx;
|
|
|
+ margin-bottom: 8rpx;
|
|
|
}
|
|
|
|
|
|
.stock-name {
|
|
|
font-size: 30rpx;
|
|
|
font-weight: 600;
|
|
|
color: #222222;
|
|
|
+ margin-right: 12rpx;
|
|
|
}
|
|
|
|
|
|
.stock-code {
|
|
|
@@ -267,78 +275,87 @@ onMounted(() => {
|
|
|
color: #9ca2b5;
|
|
|
}
|
|
|
|
|
|
-.transaction-type {
|
|
|
- padding: 8rpx 20rpx;
|
|
|
- border-radius: 20rpx;
|
|
|
+.transaction-time {
|
|
|
font-size: 24rpx;
|
|
|
+ color: #9ca2b5;
|
|
|
}
|
|
|
|
|
|
-.transaction-type.buy {
|
|
|
- background: #e7f7ef;
|
|
|
-}
|
|
|
-
|
|
|
-.transaction-type.sell {
|
|
|
- background: #ffe7e7;
|
|
|
+.item-right {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: flex-end;
|
|
|
}
|
|
|
|
|
|
-.type-text {
|
|
|
- font-weight: 600;
|
|
|
+.amount-text {
|
|
|
+ font-size: 32rpx;
|
|
|
+ font-weight: 700;
|
|
|
+ margin-bottom: 8rpx;
|
|
|
}
|
|
|
|
|
|
-.transaction-type.buy .type-text {
|
|
|
+.amount-buy {
|
|
|
color: #3abf81;
|
|
|
}
|
|
|
|
|
|
-.transaction-type.sell .type-text {
|
|
|
+.amount-sell {
|
|
|
color: #f16565;
|
|
|
}
|
|
|
|
|
|
-.transaction-details {
|
|
|
- background: #f7f8fc;
|
|
|
+.status-badge {
|
|
|
+ padding: 4rpx 16rpx;
|
|
|
border-radius: 12rpx;
|
|
|
- padding: 20rpx 24rpx;
|
|
|
- margin-bottom: 16rpx;
|
|
|
}
|
|
|
|
|
|
-.detail-row {
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- align-items: center;
|
|
|
- margin-bottom: 12rpx;
|
|
|
+.status-buy {
|
|
|
+ background: #e7f7ef;
|
|
|
}
|
|
|
|
|
|
-.detail-row:last-child {
|
|
|
- margin-bottom: 0;
|
|
|
+.status-sell {
|
|
|
+ background: #ffe7ee;
|
|
|
}
|
|
|
|
|
|
-.detail-label {
|
|
|
- font-size: 24rpx;
|
|
|
- color: #666a7f;
|
|
|
+.status-text {
|
|
|
+ font-size: 22rpx;
|
|
|
+ font-weight: 500;
|
|
|
}
|
|
|
|
|
|
-.detail-value {
|
|
|
- font-size: 26rpx;
|
|
|
- color: #222222;
|
|
|
- font-weight: 500;
|
|
|
+.status-buy .status-text {
|
|
|
+ color: #3abf81;
|
|
|
}
|
|
|
|
|
|
-.detail-value.amount {
|
|
|
- font-size: 28rpx;
|
|
|
- font-weight: 700;
|
|
|
- color: #5d55e8;
|
|
|
+.status-sell .status-text {
|
|
|
+ color: #f16565;
|
|
|
}
|
|
|
|
|
|
-.transaction-footer {
|
|
|
+/* 空状态 */
|
|
|
+.empty-state {
|
|
|
display: flex;
|
|
|
- justify-content: flex-end;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ padding: 120rpx 0;
|
|
|
+ background: #ffffff;
|
|
|
+ margin: 0 32rpx;
|
|
|
+ border-radius: 24rpx;
|
|
|
}
|
|
|
|
|
|
-.transaction-time {
|
|
|
- font-size: 22rpx;
|
|
|
+.empty-icon {
|
|
|
+ font-size: 120rpx;
|
|
|
+ margin-bottom: 32rpx;
|
|
|
+ opacity: 0.5;
|
|
|
+}
|
|
|
+
|
|
|
+.empty-text {
|
|
|
+ font-size: 32rpx;
|
|
|
+ color: #666666;
|
|
|
+ margin-bottom: 16rpx;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.empty-desc {
|
|
|
+ font-size: 26rpx;
|
|
|
color: #9ca2b5;
|
|
|
}
|
|
|
|
|
|
.bottom-safe-area {
|
|
|
- height: 80rpx;
|
|
|
+ height: 40rpx;
|
|
|
}
|
|
|
</style>
|