395 lines
8.4 KiB
Vue
395 lines
8.4 KiB
Vue
<template>
|
||
<view class="page">
|
||
<!-- 顶部统计卡片 -->
|
||
<view class="stats-card card">
|
||
<view class="stat-item">
|
||
<text class="stat-value">{{ totalCount }}</text>
|
||
<text class="stat-label">借用总数</text>
|
||
</view>
|
||
<view class="stat-divider"></view>
|
||
<view class="stat-item">
|
||
<text class="stat-value highlight">{{ borrowingCount }}</text>
|
||
<text class="stat-label">借用中</text>
|
||
</view>
|
||
<view class="stat-divider"></view>
|
||
<view class="stat-item">
|
||
<text class="stat-value success">{{ returnedCount }}</text>
|
||
<text class="stat-label">已归还</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 筛选栏 -->
|
||
<view class="filter-bar card">
|
||
<view class="filter-tabs">
|
||
<view
|
||
class="filter-tab"
|
||
:class="{ active: filterStatus === null }"
|
||
@tap="changeFilter(null)"
|
||
>
|
||
全部
|
||
</view>
|
||
<view
|
||
class="filter-tab"
|
||
:class="{ active: filterStatus === 0 }"
|
||
@tap="changeFilter(0)"
|
||
>
|
||
借用中
|
||
</view>
|
||
<view
|
||
class="filter-tab"
|
||
:class="{ active: filterStatus === 1 }"
|
||
@tap="changeFilter(1)"
|
||
>
|
||
已归还
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 列表 -->
|
||
<scroll-view
|
||
class="list-container"
|
||
scroll-y
|
||
refresher-enabled
|
||
:refresher-triggered="refreshing"
|
||
@refresherrefresh="onRefresh"
|
||
>
|
||
<view v-if="loading && myBorrowList.length === 0" class="loading-wrap">
|
||
<text class="loading-text">加载中...</text>
|
||
</view>
|
||
|
||
<view v-else-if="myBorrowList.length === 0" class="empty-wrap">
|
||
<text class="empty-icon">📦</text>
|
||
<text class="empty-text">暂无借用记录</text>
|
||
</view>
|
||
|
||
<view v-else class="list">
|
||
<view
|
||
v-for="item in filteredList"
|
||
:key="item.borrowId"
|
||
class="list-item card"
|
||
>
|
||
<!-- 顶部:资产名称 + 状态标签 -->
|
||
<view class="item-header">
|
||
<view class="asset-name-wrap">
|
||
<text class="asset-name">{{ item.assetName }}</text>
|
||
</view>
|
||
<view class="status-badge" :class="item.isReturn === 1 ? 'returned' : 'borrowing'">
|
||
{{ item.isReturn === 1 ? '已归还' : '借用中' }}
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 信息行 -->
|
||
<view class="item-info">
|
||
<view class="info-row">
|
||
<text class="info-label">借用日期:</text>
|
||
<text class="info-value">{{ item.borrowDate || '-' }}</text>
|
||
</view>
|
||
<view class="info-row">
|
||
<text class="info-label">应还日期:</text>
|
||
<text class="info-value">{{ item.returnDate || '-' }}</text>
|
||
</view>
|
||
<view v-if="item.actualReturnDate" class="info-row">
|
||
<text class="info-label">实际归还:</text>
|
||
<text class="info-value success-text">{{ item.actualReturnDate }}</text>
|
||
</view>
|
||
<view class="info-row">
|
||
<text class="info-label">出借人:</text>
|
||
<text class="info-value">{{ item.lenderName || '-' }}</text>
|
||
</view>
|
||
<view v-if="item.lenderMobile" class="info-row">
|
||
<text class="info-label">联系方式:</text>
|
||
<text class="info-value">{{ item.lenderMobile }}</text>
|
||
</view>
|
||
<view v-if="item.note" class="info-row">
|
||
<text class="info-label">备注:</text>
|
||
<text class="info-value">{{ item.note }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, computed } from 'vue'
|
||
import { onLoad, onShow } from '@dcloudio/uni-app'
|
||
// @ts-ignore
|
||
import { fetchBorrowList } from '@/pages/api/fixedAssets.js'
|
||
|
||
interface BorrowRecord {
|
||
borrowId: number
|
||
assetId: number
|
||
assetName: string
|
||
borrowDate: string
|
||
returnDate: string
|
||
actualReturnDate: string | null
|
||
borrowerId: string
|
||
borrowerName: string | null
|
||
borrowerMobile: string | null
|
||
lenderId: string
|
||
lenderName: string | null
|
||
lenderMobile: string | null
|
||
registrantDate: string
|
||
isReturn: number // 0-借用中 1-已归还
|
||
note: string
|
||
}
|
||
|
||
const myBorrowList = ref<BorrowRecord[]>([])
|
||
const loading = ref(false)
|
||
const refreshing = ref(false)
|
||
const filterStatus = ref<number | null>(null)
|
||
const userMobile = uni.getStorageSync('userName') || ''
|
||
|
||
// 统计数据
|
||
const totalCount = computed(() => myBorrowList.value.length)
|
||
const borrowingCount = computed(() => myBorrowList.value.filter(item => item.isReturn === 0).length)
|
||
const returnedCount = computed(() => myBorrowList.value.filter(item => item.isReturn === 1).length)
|
||
|
||
// 筛选后的列表
|
||
const filteredList = computed(() => {
|
||
if (filterStatus.value === null) {
|
||
return myBorrowList.value
|
||
}
|
||
return myBorrowList.value.filter(item => item.isReturn === filterStatus.value)
|
||
})
|
||
|
||
onLoad(() => {
|
||
loadData()
|
||
})
|
||
|
||
onShow(() => {
|
||
loadData()
|
||
})
|
||
|
||
// 加载数据
|
||
async function loadData() {
|
||
loading.value = true
|
||
try {
|
||
const result = await fetchBorrowList()
|
||
if (result.code === 0 && Array.isArray(result.data)) {
|
||
// 在前端筛选出当前用户的借用记录(按手机号匹配)
|
||
myBorrowList.value = result.data.filter((item: BorrowRecord) =>
|
||
item.borrowerMobile === userMobile
|
||
)
|
||
} else {
|
||
uni.showToast({
|
||
title: result.msg || '加载失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
} catch (error) {
|
||
console.error('加载借用记录失败:', error)
|
||
uni.showToast({
|
||
title: '加载失败',
|
||
icon: 'none'
|
||
})
|
||
} finally {
|
||
loading.value = false
|
||
refreshing.value = false
|
||
}
|
||
}
|
||
|
||
// 下拉刷新
|
||
function onRefresh() {
|
||
refreshing.value = true
|
||
loadData()
|
||
}
|
||
|
||
// 切换筛选
|
||
function changeFilter(status: number | null) {
|
||
filterStatus.value = status
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.page {
|
||
min-height: 100vh;
|
||
background: #f5f7fb;
|
||
padding-bottom: 32rpx;
|
||
}
|
||
|
||
.card {
|
||
background: #fff;
|
||
border-radius: 20rpx;
|
||
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.06);
|
||
}
|
||
|
||
/* 统计卡片 */
|
||
.stats-card {
|
||
margin: 24rpx 24rpx 16rpx;
|
||
padding: 24rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-around;
|
||
}
|
||
|
||
.stat-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 4rpx;
|
||
}
|
||
|
||
.stat-value {
|
||
font-size: 36rpx;
|
||
font-weight: 600;
|
||
color: #333;
|
||
}
|
||
|
||
.stat-value.highlight {
|
||
color: #ff9800;
|
||
}
|
||
|
||
.stat-value.success {
|
||
color: #51cf66;
|
||
}
|
||
|
||
.stat-label {
|
||
font-size: 22rpx;
|
||
color: #999;
|
||
}
|
||
|
||
.stat-divider {
|
||
width: 2rpx;
|
||
height: 50rpx;
|
||
background: #e8e8e8;
|
||
}
|
||
|
||
/* 筛选栏 */
|
||
.filter-bar {
|
||
margin: 0 24rpx 16rpx;
|
||
padding: 12rpx;
|
||
}
|
||
|
||
.filter-tabs {
|
||
display: flex;
|
||
gap: 8rpx;
|
||
}
|
||
|
||
.filter-tab {
|
||
flex: 1;
|
||
text-align: center;
|
||
padding: 12rpx 8rpx;
|
||
border-radius: 8rpx;
|
||
font-size: 24rpx;
|
||
color: #666;
|
||
background: #f8f9fa;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.filter-tab.active {
|
||
background: #007aff;
|
||
color: #fff;
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* 列表容器 */
|
||
.list-container {
|
||
height: calc(100vh - 260rpx);
|
||
}
|
||
|
||
.loading-wrap,
|
||
.empty-wrap {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 120rpx 0;
|
||
}
|
||
|
||
.loading-text {
|
||
color: #999;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.empty-icon {
|
||
font-size: 120rpx;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.empty-text {
|
||
color: #999;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
/* 列表项 */
|
||
.list {
|
||
padding: 0 24rpx;
|
||
}
|
||
|
||
.list-item {
|
||
margin-bottom: 20rpx;
|
||
padding: 24rpx;
|
||
}
|
||
|
||
.item-header {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
justify-content: space-between;
|
||
margin-bottom: 16rpx;
|
||
padding-bottom: 16rpx;
|
||
border-bottom: 2rpx solid #f0f0f0;
|
||
}
|
||
|
||
.asset-name-wrap {
|
||
flex: 1;
|
||
margin-right: 16rpx;
|
||
}
|
||
|
||
.asset-name {
|
||
font-size: 30rpx;
|
||
font-weight: 600;
|
||
color: #222;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.status-badge {
|
||
padding: 6rpx 16rpx;
|
||
border-radius: 20rpx;
|
||
font-size: 22rpx;
|
||
font-weight: 500;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.status-badge.borrowing {
|
||
background: #fff3e0;
|
||
color: #ff9800;
|
||
}
|
||
|
||
.status-badge.returned {
|
||
background: #e8f5e9;
|
||
color: #4caf50;
|
||
}
|
||
|
||
/* 信息行 */
|
||
.item-info {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12rpx;
|
||
}
|
||
|
||
.info-row {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
font-size: 24rpx;
|
||
}
|
||
|
||
.info-label {
|
||
color: #999;
|
||
min-width: 140rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.info-value {
|
||
color: #333;
|
||
flex: 1;
|
||
word-break: break-all;
|
||
}
|
||
|
||
.success-text {
|
||
color: #51cf66;
|
||
font-weight: 500;
|
||
}
|
||
</style>
|