# ConvertRecordCash
A record detail component that supports both vertical and horizontal layout modes for displaying user's point earning, consumption, and exchange records.
# Usage
# Basic Usage (Vertical Layout)
<template>
<div>
<button @click="showDialog = true">Open Record Detail</button>
<PressConvertRecordCash
:show="showDialog"
:tab-index="currentTabIndex"
:tab-list="tabOptions"
:point-name="pointName"
:point-amount="userPointAmount"
:point-num="totalPointNum"
:expire-time="pointExpireTime"
:list="recordList"
:loading="isLoading"
:finished="isFinished"
@close="showDialog = false"
@go-back="showDialog = false"
@change-tab="handleTabChange"
@load="handleLoadMore"
@jump-to-feedback="handleJumpToFeedback"
@go-jump="handleGoToTask"
@goto-goods-detail="handleGoodsDetail"
@record-refresh="handleRecordRefresh"
>
<!-- Record Text Slot: Custom record item text display -->
<template #record-text="{ item, tabIndex, pointName }">
<template v-if="tabIndex === 1">
<!-- Earned records: +2 draw times, 9 tokens -->
<span class="demo-highlight">+{{ item.point }}</span> draw times, <span class="demo-highlight">{{ item.point }}</span> {{ pointName }}
</template>
<template v-else-if="tabIndex === 3">
<!-- Consumed records: display different text based on consumeType -->
<template v-if="item.consumeType === 'Draw Count'">
<span class="demo-highlight">-{{ item.point }}</span> draw times
</template>
<template v-else>
<span class="demo-highlight">-{{ item.point }}</span> {{ pointName }}
</template>
</template>
</template>
<!-- Record Tip Slot: Custom statistics text -->
<template #record-tip>
<template v-if="currentTabIndex === 1">
Total earned: <span class="highlight">{{ calculateTotal(recordList) }}</span> draw times, <span class="highlight">{{ calculateTotal(recordList) }}</span> {{ pointName }}
</template>
<template v-else-if="currentTabIndex === 3">
Total consumed: <span class="highlight">{{ calculateConsumeTotal(recordList, 'Draw Count') }}</span> draw times, <span class="highlight">{{ calculateConsumeTotal(recordList, 'Points') }}</span> {{ pointName }}
</template>
</template>
</PressConvertRecordCash>
</div>
</template>
<script>
import { getMockData } from 'src/packages/press-convert-record-cash/demo-data/index.ts';
export default {
data() {
return {
showDialog: false,
currentTabIndex: 1,
...getMockData(),
};
},
methods: {
// Calculate total
calculateTotal(list) {
if (!list || list.length === 0) return '0';
return list.reduce((sum, item) => sum + parseInt(item.point || 0, 10), 0);
},
// Calculate total by consume type (Draw Count/Points calculated separately)
calculateConsumeTotal(list, consumeType) {
if (!list || list.length === 0) return '0';
const filteredList = list.filter(item => item.consumeType === consumeType);
return filteredList.reduce((sum, item) => sum + parseInt(item.point || 0, 10), 0);
},
},
};
</script>
<style scoped>
.highlight {
color: #f16253;
}
.demo-highlight {
color: #ff6b6b;
font-weight: bold;
}
</style>
# Horizontal Layout (via styleName)
<template>
<PressConvertRecordCash
:show="showDialog"
style-name="tip-hori"
:tab-index="currentTabIndex"
:tab-list="tabOptions"
:point-name="pointName"
:point-amount="userPointAmount"
:point-num="totalPointNum"
:expire-time="pointExpireTime"
:list="recordList"
@close="showDialog = false"
/>
</template>
# Empty State
<template>
<PressConvertRecordCash
:show="showDialog"
:tab-index="currentTabIndex"
:tab-list="tabOptions"
:point-name="pointName"
:point-amount="0"
:point-num="0"
:list="[]"
@close="showDialog = false"
/>
</template>
# API
# Props
| Property | Description | Type | Default |
|---|---|---|---|
| show | Whether to show the component | boolean | false |
| z-index | Component z-index | number | 1000 |
| tab-index | Current selected tab index | number | 1 |
| tab-list | Tab list data | array | [] |
| style-name | Style class for theme adaptation | string | '' |
| money-type | Currency type | number | 0 |
| point-name | Point name | string | '' |
| point-num | Total earned/consumed points | string | '' |
| point-amount | Current user points | string | '' |
| expire-time | Point expiration time (ms) | number | 0 |
| list | Record list data | array | [] |
| loading | Whether loading more data | boolean | false |
| finished | Whether all data loaded | boolean | false |
| immediate-check | Whether to check immediately | boolean | false |
# Events
| Event | Description | Parameters |
|---|---|---|
| close | Close component | - |
| go-back | Go back | - |
| load | Load more data | - |
| change-tab | Tab change | tabId: number |
| jump-to-feedback | Jump to feedback page | - |
| go-jump | Jump to task page | - |
| record-refresh | Refresh after expiration | - |
| goto-goods-detail | View reward detail | item: object |
# Slots
| Name | Description | Parameters |
|---|---|---|
| expire-time | Custom expire time display | { pointName: string, expireTime: number, onFinish: function } |
| record-text | Custom record item text display | { item: object, tabIndex: number, pointName: string } |
| record-tip | Custom record statistics text | - |
# Slot Usage
expire-time Slot
Used to customize the display content and format of point expiration time. The component provides a default countdown display.
<template #expire-time="{ pointName, expireTime, onFinish }">
<div v-if="expireTime > 0">
{{ pointName }}
<span class="custom-highlight">
{{ formatCountdown(expireTime) }}
</span> will expire
</div>
</template>
Parameters:
pointName: Point name (e.g., "Lucky Coins")expireTime: Expiration timestamp (milliseconds)onFinish: Countdown finish callback function
record-text Slot
Used to customize the text display of record items. Only displayed when item.showViewButton is false or doesn't exist.
<template #record-text="{ item, tabIndex, pointName }">
<template v-if="tabIndex === 1">
<!-- Earned records: +2 draw times, 9 tokens -->
<span class="demo-highlight">+{{ item.point }}</span> draw times, <span class="demo-highlight">{{ item.point }}</span> {{ pointName }}
</template>
<template v-else-if="tabIndex === 3">
<!-- Consumed records: display different text based on consumeType -->
<template v-if="item.consumeType === 'Draw Count'">
<span class="demo-highlight">-{{ item.point }}</span> draw times
</template>
<template v-else>
<span class="demo-highlight">-{{ item.point }}</span> {{ pointName }}
</template>
</template>
</template>
Parameters:
item: Record item datatabIndex: Current tab index (1: Earned, 2: Rewards, 3: Consumed)pointName: Point name (e.g., "Lucky Coins")
record-tip Slot
Used to customize the statistics text at the top of the record list, supporting different content for different tabs.
<template #record-tip>
<template v-if="currentTabIndex === 1">
Total earned: <span class="highlight">{{ calculateTotal(list) }}</span> draw times, <span class="highlight">{{ calculateTotal(list) }}</span> {{ pointName }}
</template>
<template v-else-if="currentTabIndex === 3">
Total consumed: <span class="highlight">{{ calculateConsumeTotal(list, 'Draw Count') }}</span> draw times, <span class="highlight">{{ calculateConsumeTotal(list, 'Points') }}</span> {{ pointName }}
</template>
</template>
Calculation Methods Example:
methods: {
// Calculate total
calculateTotal(list) {
if (!list || list.length === 0) return '0';
return list.reduce((sum, item) => sum + parseInt(item.point || 0, 10), 0);
},
// Calculate total by consume type (Draw Count/Points calculated separately)
calculateConsumeTotal(list, consumeType) {
if (!list || list.length === 0) return '0';
const filteredList = list.filter(item => item.consumeType === consumeType);
return filteredList.reduce((sum, item) => sum + parseInt(item.point || 0, 10), 0);
},
}
# Data Structure
# tabList Format
[
{ id: 1, name: 'Earned Records' },
{ id: 2, name: 'Reward Records' },
{ id: 3, name: 'Consumed Records' },
]
# list Format
[
{
orderid: 'Order ID',
desc: 'Record description',
time: 'Time string',
opertype: 1, // 1:earned times 2:reward 3:consumed draw times 4:exchange deduction 5:expired
point: 'Point amount', // Point amount or draw count
showViewButton: true, // Whether to show "View Reward" button (optional)
rewardType: 'Draw Reward', // Reward type (optional, for business logic)
consumeType: 'Draw Count' | 'Points' // Consume type (optional, for consumed records)
}
]
# Features
# Slot Architecture Design
The component adopts the design philosophy of complete separation of UI and business logic:
expire-time Slot: Fully customizable expiration time display
- Component provides default countdown display
- User controls: display conditions, text format, time formatting logic
record-text Slot: Fully customizable record item text display
- Component only provides container styles
- User controls: text format for different tabs, highlight styles
- When
item.showViewButtonistrue, component automatically shows "View Reward" button instead of this Slot
record-tip Slot: Fully customizable statistics text
- Component only provides container styles
- User controls: statistics logic for different tabs, text format, highlight styles
Design Advantages:
- ✅ UI component has single responsibility, only handles styles and structure
- ✅ Business logic is flexible and controllable, can be customized for different projects
- ✅ Flexible button display control through
showViewButtonfield
# Other Features
- Multi-tab Support: Support earned, reward, and consumed record tabs
- Countdown Timer: Display point expiration countdown with custom format
- Infinite Scroll: Support loading more data with loading states
- Empty State: Automatic empty state display when no data
- Responsive Design: Automatic layout switching for different orientations
- Statistics Display: Show accumulated totals for different record types
# Style Customization
Component styles are written in SCSS, with all definitions in the css/index.scss file. To customize styles:
- Via
customClassprop: Pass custom class name to override default styles - Via
styleNameprop: Pass game theme style class name (e.g.,tip-horifor horizontal layout) - Direct modification: Modify style definitions in
css/index.scss
# Style Adaptation
The component supports automatic switching between vertical and horizontal layouts through the style-name prop:
// Vertical
styleName: 'tip-pvp tip-vert'
// Horizontal
styleName: 'tip-pvp tip-hori'
# Notes
- Slot Usage: The component adopts Slot architecture design,
record-textandrecord-tipSlots are provided by the user with business logic - View Reward Button: Controlled by the
showViewButtonfield in record data, shows button whentrue, showsrecord-textSlot content whenfalseor doesn't exist - Style Class Names: Demo files should use custom style classes (e.g.,
demo-highlight), not component internal BEM class names - Fixed Positioning: Component uses fixed positioning for full-screen overlay
- Horizontal Adaptation: In horizontal mode, return button and wish button are automatically hidden
- Countdown Callback: Automatically triggers
record-refreshevent when countdown expires - Empty State: Empty state automatically shows empty page when no data
- Record Types: Different record types are distinguished by
opertypefield (1:earned times 2:reward 3:consumed draw times 4:exchange deduction 5:expired)