初始版本
This commit is contained in:
274
app/pages/console/points.vue
Normal file
274
app/pages/console/points.vue
Normal file
@@ -0,0 +1,274 @@
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<a-page-header title="积分明细" sub-title="查看积分收支记录" />
|
||||
|
||||
<!-- 积分概览 -->
|
||||
<a-card :bordered="false" class="card points-banner">
|
||||
<div class="points-row">
|
||||
<div>
|
||||
<div class="points-label">当前积分</div>
|
||||
<div class="points-value">{{ currentPoints }}</div>
|
||||
</div>
|
||||
<StarOutlined class="points-icon" />
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<!-- 筛选 -->
|
||||
<a-card :bordered="false" class="card">
|
||||
<div class="filter-row">
|
||||
<a-select
|
||||
v-model:value="filterScene"
|
||||
placeholder="全部类型"
|
||||
allow-clear
|
||||
style="width: 140px"
|
||||
@change="onFilterChange"
|
||||
>
|
||||
<a-select-option :value="1">充值赠送</a-select-option>
|
||||
<a-select-option :value="2">消费扣除</a-select-option>
|
||||
<a-select-option :value="3">签到奖励</a-select-option>
|
||||
<a-select-option :value="4">活动奖励</a-select-option>
|
||||
<a-select-option :value="5">积分兑换</a-select-option>
|
||||
<a-select-option :value="9">系统调整</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<a-range-picker
|
||||
v-model:value="filterDateRange"
|
||||
value-format="YYYY-MM-DD"
|
||||
:placeholder="['开始日期', '结束日期']"
|
||||
style="width: 240px"
|
||||
@change="onFilterChange"
|
||||
/>
|
||||
|
||||
<a-button @click="resetFilter">重置</a-button>
|
||||
</div>
|
||||
|
||||
<!-- 列表 -->
|
||||
<a-spin :spinning="loading">
|
||||
<div v-if="!list.length && !loading" class="empty-wrap">
|
||||
<a-empty description="暂无积分记录" :image="Empty.PRESENTED_IMAGE_SIMPLE" />
|
||||
</div>
|
||||
|
||||
<div v-else class="log-list">
|
||||
<div v-for="item in list" :key="item.logId" class="log-item">
|
||||
<div class="log-icon" :class="getSceneClass(item.scene)">
|
||||
<component :is="getSceneIcon(item.scene)" />
|
||||
</div>
|
||||
<div class="log-body">
|
||||
<div class="log-desc">{{ item.describe || getSceneLabel(item.scene) }}</div>
|
||||
<div class="log-time">{{ item.createTime }}</div>
|
||||
</div>
|
||||
<div class="log-points" :class="(item.points ?? 0) > 0 ? 'add' : 'sub'">
|
||||
{{ (item.points ?? 0) > 0 ? '+' : '' }}{{ item.points }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a-pagination
|
||||
v-if="total > pageSize"
|
||||
v-model:current="page"
|
||||
:total="total"
|
||||
:page-size="pageSize"
|
||||
style="margin-top: 16px; text-align: right"
|
||||
@change="loadList"
|
||||
/>
|
||||
</a-spin>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, type Component } from 'vue'
|
||||
import { Empty, message } from 'ant-design-vue'
|
||||
|
||||
import {
|
||||
StarOutlined,
|
||||
GiftOutlined,
|
||||
ShoppingOutlined,
|
||||
CheckCircleOutlined,
|
||||
TrophyOutlined,
|
||||
SwapOutlined,
|
||||
SettingOutlined,
|
||||
} from '@ant-design/icons-vue'
|
||||
import { getUserInfo } from '@/api/layout'
|
||||
import { pageUserPointLog } from '@/api/user/point-log'
|
||||
import type { UserPointLog } from '@/api/user/point-log/model'
|
||||
|
||||
definePageMeta({ layout: 'console' })
|
||||
useHead({ title: '积分明细 - 控制台' })
|
||||
|
||||
const currentPoints = ref(0)
|
||||
const loading = ref(false)
|
||||
const list = ref<UserPointLog[]>([])
|
||||
const page = ref(1)
|
||||
const total = ref(0)
|
||||
const pageSize = 15
|
||||
|
||||
const filterScene = ref<number | undefined>(undefined)
|
||||
const filterDateRange = ref<[string, string] | null>(null)
|
||||
|
||||
const SCENE_MAP: Record<number, { label: string; icon: Component; cls: string }> = {
|
||||
1: { label: '充值赠送', icon: GiftOutlined, cls: 'gift' },
|
||||
2: { label: '消费扣除', icon: ShoppingOutlined, cls: 'spend' },
|
||||
3: { label: '签到奖励', icon: CheckCircleOutlined, cls: 'checkin' },
|
||||
4: { label: '活动奖励', icon: TrophyOutlined, cls: 'activity' },
|
||||
5: { label: '积分兑换', icon: SwapOutlined, cls: 'exchange' },
|
||||
9: { label: '系统调整', icon: SettingOutlined, cls: 'system' },
|
||||
}
|
||||
|
||||
function getSceneLabel(scene?: number) {
|
||||
return scene !== undefined ? (SCENE_MAP[scene]?.label ?? '积分变动') : '积分变动'
|
||||
}
|
||||
function getSceneIcon(scene?: number): Component {
|
||||
return scene !== undefined ? (SCENE_MAP[scene]?.icon ?? StarOutlined) : StarOutlined
|
||||
}
|
||||
function getSceneClass(scene?: number) {
|
||||
return scene !== undefined ? (SCENE_MAP[scene]?.cls ?? 'default') : 'default'
|
||||
}
|
||||
|
||||
async function loadBalance() {
|
||||
try {
|
||||
const u = await getUserInfo()
|
||||
currentPoints.value = u?.points ?? 0
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
|
||||
async function loadList() {
|
||||
loading.value = true
|
||||
try {
|
||||
const params: Record<string, unknown> = {
|
||||
page: page.value,
|
||||
limit: pageSize,
|
||||
}
|
||||
if (filterScene.value !== undefined) params.scene = filterScene.value
|
||||
if (filterDateRange.value?.[0]) params.createTimeStart = filterDateRange.value[0]
|
||||
if (filterDateRange.value?.[1]) params.createTimeEnd = filterDateRange.value[1]
|
||||
|
||||
const res = await pageUserPointLog(params)
|
||||
list.value = res?.list ?? []
|
||||
total.value = res?.total ?? 0
|
||||
} catch (e) {
|
||||
message.error(e instanceof Error ? e.message : '加载失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function onFilterChange() {
|
||||
page.value = 1
|
||||
loadList()
|
||||
}
|
||||
|
||||
function resetFilter() {
|
||||
filterScene.value = undefined
|
||||
filterDateRange.value = null
|
||||
page.value = 1
|
||||
loadList()
|
||||
}
|
||||
|
||||
loadBalance()
|
||||
loadList()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.card {
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
/* 积分概览 */
|
||||
.points-banner {
|
||||
background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%);
|
||||
}
|
||||
.points-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
color: white;
|
||||
}
|
||||
.points-label {
|
||||
font-size: 14px;
|
||||
opacity: 0.85;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.points-value {
|
||||
font-size: 36px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.points-icon {
|
||||
font-size: 44px;
|
||||
opacity: 0.25;
|
||||
}
|
||||
:deep(.points-banner .ant-card-body) {
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* 筛选 */
|
||||
.filter-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
/* 记录列表 */
|
||||
.empty-wrap {
|
||||
padding: 32px 0;
|
||||
}
|
||||
.log-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.log-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
padding: 14px 0;
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
}
|
||||
.log-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.log-icon {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 18px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.log-icon.gift { background: #fff7e6; color: #fa8c16; }
|
||||
.log-icon.spend { background: #fff1f0; color: #f5222d; }
|
||||
.log-icon.checkin { background: #f6ffed; color: #52c41a; }
|
||||
.log-icon.activity { background: #fffbe6; color: #faad14; }
|
||||
.log-icon.exchange { background: #f0f5ff; color: #597ef7; }
|
||||
.log-icon.system { background: #f5f5f5; color: #8c8c8c; }
|
||||
.log-icon.default { background: #e6f7ff; color: #1890ff; }
|
||||
|
||||
.log-body {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.log-desc {
|
||||
font-size: 14px;
|
||||
color: rgba(0,0,0,0.85);
|
||||
}
|
||||
.log-time {
|
||||
font-size: 12px;
|
||||
color: rgba(0,0,0,0.35);
|
||||
margin-top: 2px;
|
||||
}
|
||||
.log-points {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.log-points.add {
|
||||
color: #52c41a;
|
||||
}
|
||||
.log-points.sub {
|
||||
color: #f5222d;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user