14 changed files with 1182 additions and 102 deletions
@ -0,0 +1,242 @@ |
|||||
|
<template> |
||||
|
<div class="gift-records"> |
||||
|
<div class="flex justify-between items-center mb-4"> |
||||
|
<h3 class="text-lg font-bold">赠品记录</h3> |
||||
|
<el-button type="primary" @click="refreshRecords">刷新</el-button> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 加载状态 --> |
||||
|
<div v-if="loading" class="text-center py-8"> |
||||
|
<el-icon class="is-loading mr-2"><Loading /></el-icon> |
||||
|
加载中... |
||||
|
</div> |
||||
|
|
||||
|
<!-- 空状态 --> |
||||
|
<div v-else-if="!giftRecords || giftRecords.length === 0" class="text-center py-8 text-gray-500"> |
||||
|
<el-icon class="text-4xl mb-2"><Box /></el-icon> |
||||
|
<p>暂无赠品记录</p> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 赠品记录列表 --> |
||||
|
<div v-else class="gift-records-list"> |
||||
|
<el-card |
||||
|
v-for="record in giftRecords" |
||||
|
:key="record.id" |
||||
|
class="mb-4 gift-record-card" |
||||
|
shadow="hover" |
||||
|
> |
||||
|
<div class="flex justify-between items-start"> |
||||
|
<div class="flex-1"> |
||||
|
<div class="flex items-center mb-2"> |
||||
|
<h4 class="font-bold text-lg mr-3">{{ record.gift_name }}</h4> |
||||
|
<el-tag |
||||
|
:type="getStatusTagType(record.gift_status)" |
||||
|
size="small" |
||||
|
> |
||||
|
{{ record.gift_status_text }} |
||||
|
</el-tag> |
||||
|
</div> |
||||
|
|
||||
|
<div class="grid grid-cols-2 gap-4 text-sm text-gray-600"> |
||||
|
<div> |
||||
|
<span class="font-medium">赠品类型:</span> |
||||
|
<el-tag size="small" type="info">{{ record.gift_type_text }}</el-tag> |
||||
|
</div> |
||||
|
<div> |
||||
|
<span class="font-medium">赠送人:</span> |
||||
|
{{ record.giver_name }} |
||||
|
<span v-if="record.giver_phone" class="text-gray-500">({{ record.giver_phone }})</span> |
||||
|
</div> |
||||
|
<div> |
||||
|
<span class="font-medium">发放时间:</span> |
||||
|
{{ record.gift_time_formatted }} |
||||
|
</div> |
||||
|
<div v-if="record.use_time_formatted"> |
||||
|
<span class="font-medium">使用时间:</span> |
||||
|
{{ record.use_time_formatted }} |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="text-right text-sm text-gray-500"> |
||||
|
创建时间:{{ record.create_time }} |
||||
|
</div> |
||||
|
</div> |
||||
|
</el-card> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { ref, onMounted, defineProps } from 'vue' |
||||
|
import { Loading, Box } from '@element-plus/icons-vue' |
||||
|
import { getGiftRecordList } from '@/app/api/customer_resources' |
||||
|
|
||||
|
const props = defineProps({ |
||||
|
customer_resource_id: { |
||||
|
type: [String, Number], |
||||
|
required: true |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
const loading = ref(false) |
||||
|
const giftRecords = ref([]) |
||||
|
|
||||
|
// 获取赠品记录列表 |
||||
|
const fetchGiftRecords = async () => { |
||||
|
if (!props.customer_resource_id) return |
||||
|
|
||||
|
loading.value = true |
||||
|
try { |
||||
|
const response = await getGiftRecordList({ |
||||
|
resource_id: props.customer_resource_id |
||||
|
}) |
||||
|
|
||||
|
if (response.code === 1) { |
||||
|
giftRecords.value = response.data || [] |
||||
|
} else { |
||||
|
console.error('获取赠品记录失败:', response.msg) |
||||
|
giftRecords.value = [] |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error('获取赠品记录异常:', error) |
||||
|
giftRecords.value = [] |
||||
|
} finally { |
||||
|
loading.value = false |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 刷新记录 |
||||
|
const refreshRecords = () => { |
||||
|
fetchGiftRecords() |
||||
|
} |
||||
|
|
||||
|
// 获取状态标签类型 |
||||
|
const getStatusTagType = (status) => { |
||||
|
switch (status) { |
||||
|
case 0: return 'info' // 已失效 |
||||
|
case 1: return 'success' // 可使用 |
||||
|
case 2: return 'warning' // 已使用 |
||||
|
default: return 'info' |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 组件挂载时获取数据 |
||||
|
onMounted(() => { |
||||
|
fetchGiftRecords() |
||||
|
}) |
||||
|
|
||||
|
// 监听customer_resource_id变化 |
||||
|
import { watch } from 'vue' |
||||
|
watch(() => props.customer_resource_id, (newId) => { |
||||
|
if (newId) { |
||||
|
fetchGiftRecords() |
||||
|
} |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.gift-records { |
||||
|
padding: 16px; |
||||
|
} |
||||
|
|
||||
|
.gift-record-card { |
||||
|
transition: all 0.3s ease; |
||||
|
} |
||||
|
|
||||
|
.gift-record-card:hover { |
||||
|
transform: translateY(-2px); |
||||
|
} |
||||
|
|
||||
|
.grid { |
||||
|
display: grid; |
||||
|
} |
||||
|
|
||||
|
.grid-cols-2 { |
||||
|
grid-template-columns: repeat(2, minmax(0, 1fr)); |
||||
|
} |
||||
|
|
||||
|
.gap-4 { |
||||
|
gap: 1rem; |
||||
|
} |
||||
|
|
||||
|
.text-gray-500 { |
||||
|
color: #6b7280; |
||||
|
} |
||||
|
|
||||
|
.text-gray-600 { |
||||
|
color: #4b5563; |
||||
|
} |
||||
|
|
||||
|
.font-medium { |
||||
|
font-weight: 500; |
||||
|
} |
||||
|
|
||||
|
.font-bold { |
||||
|
font-weight: 700; |
||||
|
} |
||||
|
|
||||
|
.text-lg { |
||||
|
font-size: 1.125rem; |
||||
|
line-height: 1.75rem; |
||||
|
} |
||||
|
|
||||
|
.text-sm { |
||||
|
font-size: 0.875rem; |
||||
|
line-height: 1.25rem; |
||||
|
} |
||||
|
|
||||
|
.text-4xl { |
||||
|
font-size: 2.25rem; |
||||
|
line-height: 2.5rem; |
||||
|
} |
||||
|
|
||||
|
.mb-2 { |
||||
|
margin-bottom: 0.5rem; |
||||
|
} |
||||
|
|
||||
|
.mb-4 { |
||||
|
margin-bottom: 1rem; |
||||
|
} |
||||
|
|
||||
|
.mr-2 { |
||||
|
margin-right: 0.5rem; |
||||
|
} |
||||
|
|
||||
|
.mr-3 { |
||||
|
margin-right: 0.75rem; |
||||
|
} |
||||
|
|
||||
|
.py-8 { |
||||
|
padding-top: 2rem; |
||||
|
padding-bottom: 2rem; |
||||
|
} |
||||
|
|
||||
|
.text-center { |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
.text-right { |
||||
|
text-align: right; |
||||
|
} |
||||
|
|
||||
|
.flex { |
||||
|
display: flex; |
||||
|
} |
||||
|
|
||||
|
.flex-1 { |
||||
|
flex: 1 1 0%; |
||||
|
} |
||||
|
|
||||
|
.items-center { |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.items-start { |
||||
|
align-items: flex-start; |
||||
|
} |
||||
|
|
||||
|
.justify-between { |
||||
|
justify-content: space-between; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,155 @@ |
|||||
|
<!--赠品记录卡片组件--> |
||||
|
<template> |
||||
|
<view class="gift-record-card"> |
||||
|
<view class="gift-header"> |
||||
|
<view class="gift-name">{{ record.gift_name }}</view> |
||||
|
<view class="gift-status" :class="getStatusClass(record.gift_status)"> |
||||
|
{{ record.gift_status_text }} |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="gift-info"> |
||||
|
<view class="info-row"> |
||||
|
<text class="info-label">赠品类型:</text> |
||||
|
<text class="info-value">{{ record.gift_type_text }}</text> |
||||
|
</view> |
||||
|
<view class="info-row"> |
||||
|
<text class="info-label">获得时间:</text> |
||||
|
<text class="info-value">{{ formatTime(record.gift_time_formatted) }}</text> |
||||
|
</view> |
||||
|
<view class="info-row" v-if="record.giver_name"> |
||||
|
<text class="info-label">赠送人:</text> |
||||
|
<text class="info-value">{{ record.giver_name }}</text> |
||||
|
</view> |
||||
|
<view class="info-row" v-if="record.use_time_formatted"> |
||||
|
<text class="info-label">使用时间:</text> |
||||
|
<text class="info-value">{{ formatTime(record.use_time_formatted) }}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: 'GiftRecordCard', |
||||
|
props: { |
||||
|
record: { |
||||
|
type: Object, |
||||
|
required: true |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
formatTime(timeStr) { |
||||
|
if (!timeStr) return '未知时间' |
||||
|
|
||||
|
try { |
||||
|
// 如果有工具方法则使用,否则使用内置格式化 |
||||
|
if (this.$util && this.$util.formatToDateTime) { |
||||
|
return this.$util.formatToDateTime(timeStr, 'Y-m-d H:i') |
||||
|
} |
||||
|
|
||||
|
// 内置格式化方法 |
||||
|
if (timeStr.includes('-')) { |
||||
|
const date = new Date(timeStr) |
||||
|
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 timeStr |
||||
|
} catch (error) { |
||||
|
console.error('时间格式化失败:', error) |
||||
|
return timeStr |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
getStatusClass(status) { |
||||
|
const statusMap = { |
||||
|
0: 'status-expired', // 已失效 |
||||
|
1: 'status-available', // 可使用 |
||||
|
2: 'status-used' // 已使用 |
||||
|
} |
||||
|
return statusMap[status] || 'status-unknown' |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="less" scoped> |
||||
|
.gift-record-card { |
||||
|
background: #fff; |
||||
|
border-radius: 16rpx; |
||||
|
padding: 32rpx; |
||||
|
margin-bottom: 24rpx; |
||||
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06); |
||||
|
} |
||||
|
|
||||
|
.gift-header { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
margin-bottom: 24rpx; |
||||
|
|
||||
|
.gift-name { |
||||
|
font-size: 32rpx; |
||||
|
font-weight: 600; |
||||
|
color: #333; |
||||
|
flex: 1; |
||||
|
} |
||||
|
|
||||
|
.gift-status { |
||||
|
padding: 8rpx 16rpx; |
||||
|
border-radius: 8rpx; |
||||
|
font-size: 24rpx; |
||||
|
font-weight: 500; |
||||
|
|
||||
|
&.status-available { |
||||
|
background: #E8F5E8; |
||||
|
color: #52C41A; |
||||
|
} |
||||
|
|
||||
|
&.status-used { |
||||
|
background: #F0F0F0; |
||||
|
color: #8C8C8C; |
||||
|
} |
||||
|
|
||||
|
&.status-expired { |
||||
|
background: #FFF2F0; |
||||
|
color: #FF4D4F; |
||||
|
} |
||||
|
|
||||
|
&.status-unknown { |
||||
|
background: #F6F6F6; |
||||
|
color: #999; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.gift-info { |
||||
|
.info-row { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
margin-bottom: 16rpx; |
||||
|
|
||||
|
&:last-child { |
||||
|
margin-bottom: 0; |
||||
|
} |
||||
|
|
||||
|
.info-label { |
||||
|
font-size: 28rpx; |
||||
|
color: #999; |
||||
|
width: 160rpx; |
||||
|
flex-shrink: 0; |
||||
|
} |
||||
|
|
||||
|
.info-value { |
||||
|
font-size: 28rpx; |
||||
|
color: #333; |
||||
|
flex: 1; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
Loading…
Reference in new issue