qiangdanxitong/frontend/src/views/RewardRecords.vue

265 lines
6.0 KiB
Vue

<template>
<DefaultLayout>
<div class="reward-records-page">
<div class="container">
<!-- 页面标题 -->
<div class="page-header">
<a-button type="text" @click="router.back()" class="back-btn">
<Icon name="chevronLeft" size="md" color="#AFAFAF" />
</a-button>
<div class="header-info">
<h1 class="page-title">
<Icon name="clock" size="xl" color="#31f08b" style="margin-right: 12px;" />
奖励记录
</h1>
<p class="page-desc">查看你获得的所有奖励</p>
</div>
</div>
<!-- 统计卡片 -->
<div class="stats-row">
<div class="stat-card">
<div class="stat-value">{{ totalScore }}</div>
<div class="stat-label">累计获得积分</div>
</div>
<div class="stat-card">
<div class="stat-value">{{ totalCount }}</div>
<div class="stat-label">获奖次数</div>
</div>
</div>
<!-- 记录列表 -->
<div class="records-section">
<a-table
:columns="columns"
:data-source="records"
:loading="loading"
:pagination="{
current: currentPage,
pageSize: pageSize,
total: total,
onChange: handlePageChange,
showSizeChanger: false
}"
row-key="id"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'rewardType'">
<a-tag :color="getTypeColor(record.rewardType)">
{{ getTypeName(record.rewardType) }}
</a-tag>
</template>
<template v-if="column.key === 'rewardValue'">
<span class="reward-value">{{ parseRewardValue(record.rewardValue) }}</span>
</template>
<template v-if="column.key === 'createTime'">
{{ formatTime(record.createTime) }}
</template>
</template>
</a-table>
</div>
</div>
</div>
</DefaultLayout>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import DefaultLayout from '@/layouts/DefaultLayout.vue'
import Icon from '@/components/Icon.vue'
import dayjs from 'dayjs'
import {
getMyRewardRecords,
type RewardRecord
} from '@/api/rewardRule'
const router = useRouter()
const loading = ref(false)
const records = ref<RewardRecord[]>([])
const currentPage = ref(1)
const pageSize = ref(20)
const total = ref(0)
const columns = [
{ title: '奖励规则', dataIndex: 'ruleName', key: 'ruleName' },
{ title: '奖励类型', dataIndex: 'rewardType', key: 'rewardType', width: 120 },
{ title: '奖励内容', dataIndex: 'rewardValue', key: 'rewardValue', width: 150 },
{ title: '获得时间', dataIndex: 'createTime', key: 'createTime', width: 180 },
]
const totalScore = computed(() => {
return records.value.reduce((sum, r) => {
try {
const obj = JSON.parse(r.rewardValue)
return sum + (obj.score || 0)
} catch {
return sum
}
}, 0)
})
const totalCount = computed(() => total.value)
const getTypeColor = (type: string) => {
const colorMap: Record<string, string> = {
score: 'green',
badge: 'gold',
title: 'purple',
exp: 'blue'
}
return colorMap[type] || 'default'
}
const getTypeName = (type: string) => {
const nameMap: Record<string, string> = {
score: '积分',
badge: '徽章',
title: '称号',
exp: '经验值'
}
return nameMap[type] || type
}
const parseRewardValue = (value: string) => {
try {
const obj = JSON.parse(value)
if (obj.score) return `+${obj.score} 积分`
if (obj.badge) return `徽章: ${obj.badge}`
if (obj.title) return `称号: ${obj.title}`
return value
} catch {
return value
}
}
const formatTime = (time: string) => {
return dayjs(time).format('YYYY-MM-DD HH:mm')
}
const handlePageChange = (page: number) => {
currentPage.value = page
loadData()
}
const loadData = async () => {
loading.value = true
try {
const result = await getMyRewardRecords({
page: currentPage.value,
pageSize: pageSize.value
})
records.value = result.records || []
total.value = result.total || 0
} catch (error) {
console.error('加载奖励记录失败:', error)
} finally {
loading.value = false
}
}
onMounted(() => {
loadData()
})
</script>
<style scoped lang="scss">
@import "@/styles/variables.scss";
.reward-records-page {
min-height: 100vh;
padding: 24px;
background: $bg-primary;
}
.container {
max-width: 1000px;
margin: 0 auto;
}
.page-header {
display: flex;
align-items: flex-start;
gap: 16px;
margin-bottom: 24px;
}
.back-btn {
margin-top: 4px;
}
.page-title {
font-size: 28px;
font-weight: 600;
color: $text-primary;
display: flex;
align-items: center;
margin-bottom: 8px;
}
.page-desc {
color: $text-secondary;
font-size: 14px;
}
.stats-row {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
margin-bottom: 32px;
}
.stat-card {
background: $bg-card;
border: 1px solid $border-primary;
border-radius: 12px;
padding: 24px;
text-align: center;
.stat-value {
font-size: 36px;
font-weight: 700;
color: $brand-primary;
}
.stat-label {
font-size: 14px;
color: $text-secondary;
margin-top: 8px;
}
}
.records-section {
background: $bg-card;
border: 1px solid $border-primary;
border-radius: 12px;
padding: 20px;
}
.reward-value {
color: $brand-primary;
font-weight: 600;
}
:deep(.ant-table) {
background: transparent;
.ant-table-thead > tr > th {
background: $bg-secondary;
border-bottom: 1px solid $border-primary;
color: $text-secondary;
}
.ant-table-tbody > tr > td {
border-bottom: 1px solid $border-primary;
color: $text-primary;
}
.ant-table-tbody > tr:hover > td {
background: $bg-secondary;
}
}
</style>