<template>
|
<view class="report-page">
|
<PageHeader title="库存报表" @back="goBack" />
|
|
<!-- 报表类型 -->
|
<view class="tabs-wrap">
|
<view
|
v-for="t in reportTypes"
|
:key="t.value"
|
class="tab-item"
|
:class="{ active: searchForm.reportType === t.value }"
|
@click="searchForm.reportType = t.value"
|
>
|
<text>{{ t.label }}</text>
|
</view>
|
</view>
|
|
<!-- 时间选择区域已去除,使用默认日期逻辑 -->
|
|
<!-- 列表 + 滚动分页 -->
|
<view class="list-section">
|
<view class="section-header">
|
<text class="table-title">{{ tableTitle }}</text>
|
</view>
|
<view v-if="tableData.length > 0">
|
<view v-for="(item, index) in tableData" :key="index" class="card-item">
|
<view class="card-header">
|
<view class="header-main">
|
<text class="product-name">{{ item.productName }}</text>
|
<text class="sub-title">{{ item.model }}</text>
|
</view>
|
</view>
|
<up-divider />
|
<view class="card-body">
|
<view class="row"><text class="l">单位</text><text class="r">{{ item.unit }}</text></view>
|
<view class="row" v-if="searchForm.reportType !== 'inout'"><text class="l">入库时间</text><text class="r">{{ item.createTime }}</text></view>
|
<view class="row" v-if="searchForm.reportType !== 'inout'"><text class="l">入库批次</text><text class="r">{{ item.inboundBatches }}</text></view>
|
<view class="row"><text class="l">入库数量</text><text class="r">{{ item.totalStockIn ?? item.stockInNum }}</text></view>
|
<view class="row" v-if="searchForm.reportType === 'inout'"><text class="l">出库数量</text><text class="r">{{ item.totalStockOut }}</text></view>
|
<view class="row"><text class="l">现在库存</text><text class="r highlight">{{ item.currentStock }}</text></view>
|
<view class="row" v-if="item.createBy"><text class="l">入库人</text><text class="r">{{ item.createBy }}</text></view>
|
</view>
|
</view>
|
<view class="load-more-wrap">
|
<u-loadmore :status="loadStatus" @loadmore="loadMore" />
|
</view>
|
</view>
|
<view v-else class="no-data">暂无数据</view>
|
</view>
|
|
<up-popup :show="showDatePicker" mode="bottom" @close="showDatePicker = false">
|
<up-datetime-picker
|
v-model="dateValue"
|
:mode="datePickerMode"
|
@confirm="onDateConfirm"
|
@cancel="showDatePicker = false"
|
/>
|
</up-popup>
|
</view>
|
</template>
|
|
<script setup>
|
import { ref, reactive, toRefs, computed, watch } from 'vue'
|
import dayjs from 'dayjs'
|
import PageHeader from '@/components/PageHeader.vue'
|
import { formatDateToYMD } from '@/utils/ruoyi'
|
import { onShow, onReachBottom } from '@dcloudio/uni-app'
|
import {
|
getStockInventoryReportList,
|
getStockInventoryInAndOutReportList
|
} from '@/api/inventoryManagement/stockInventory.js'
|
|
const reportTypes = [
|
{ label: '日报', value: 'daily' },
|
{ label: '月报', value: 'monthly' },
|
{ label: '进出存报表', value: 'inout' }
|
]
|
const tableData = ref([])
|
const showDatePicker = ref(false)
|
const dateValue = ref(Date.now())
|
const datePickerTarget = ref('') // single | startMonth | endMonth
|
const loadStatus = ref('loadmore') // loadmore | loading | nomore | error
|
const page = reactive({ current: 1, size: 20 })
|
const data = reactive({
|
searchForm: {
|
reportType: 'daily',
|
singleDate: '',
|
startMonth: '',
|
endMonth: '',
|
startDate: '',
|
endDate: ''
|
}
|
})
|
const { searchForm } = toRefs(data)
|
|
const datePickerMode = computed(() => {
|
if (datePickerTarget.value === 'startMonth' || datePickerTarget.value === 'endMonth') return 'month'
|
return 'date'
|
})
|
|
const tableTitle = computed(() => {
|
const m = { daily: '日报详细数据', monthly: '月报详细数据', inout: '进出存报表详细数据' }
|
return m[searchForm.value.reportType] || '报表数据'
|
})
|
|
const getQueryParams = () => {
|
const p = {
|
reportType: searchForm.value.reportType,
|
current: page.current,
|
size: page.size
|
}
|
if (searchForm.value.reportType === 'daily') {
|
p.reportDate = searchForm.value.singleDate
|
} else if (searchForm.value.reportType === 'monthly') {
|
p.startMonth = searchForm.value.startMonth
|
p.endMonth = searchForm.value.endMonth
|
} else if (searchForm.value.reportType === 'monthly') {
|
p.startMonth = searchForm.value.startMonth
|
p.endMonth = searchForm.value.endMonth
|
} else {
|
p.startDate = searchForm.value.startDate
|
p.endDate = searchForm.value.endDate
|
}
|
return p
|
}
|
|
const getList = () => {
|
const isFirstPage = page.current === 1
|
if (isFirstPage) {
|
uni.showLoading({ title: '查询中...', mask: true })
|
}
|
const params = getQueryParams()
|
const isInout = searchForm.value.reportType === 'inout'
|
const api = isInout ? getStockInventoryInAndOutReportList : getStockInventoryReportList
|
api(params)
|
.then(res => {
|
uni.hideLoading()
|
const records = res.data?.records || []
|
const total = res.data?.total || records.length
|
if (isFirstPage) {
|
tableData.value = records
|
} else {
|
tableData.value = [...tableData.value, ...records]
|
}
|
if (tableData.value.length >= total || total === 0) {
|
loadStatus.value = 'nomore'
|
} else {
|
loadStatus.value = 'loadmore'
|
}
|
})
|
.catch(() => {
|
uni.hideLoading()
|
loadStatus.value = 'error'
|
if (isFirstPage) {
|
uni.showToast({ title: '查询失败', icon: 'none' })
|
}
|
})
|
}
|
|
const handleQuery = () => {
|
page.current = 1
|
loadStatus.value = 'loadmore'
|
getList()
|
}
|
|
const loadMore = () => {
|
if (loadStatus.value === 'nomore' || loadStatus.value === 'loading') return
|
loadStatus.value = 'loading'
|
page.current++
|
getList()
|
}
|
|
const openDatePicker = (target) => {
|
datePickerTarget.value = target
|
let val = ''
|
if (target === 'single') val = searchForm.value.singleDate
|
else if (target === 'startMonth') val = searchForm.value.startMonth
|
else if (target === 'endMonth') val = searchForm.value.endMonth
|
dateValue.value = val ? new Date(val).getTime() : Date.now()
|
showDatePicker.value = true
|
}
|
|
const onDateConfirm = (e) => {
|
const isMonth = datePickerTarget.value === 'startMonth' || datePickerTarget.value === 'endMonth'
|
const str = isMonth ? dayjs(e.value).format('YYYY-MM') : formatDateToYMD(e.value)
|
if (datePickerTarget.value === 'single') searchForm.value.singleDate = str
|
else if (datePickerTarget.value === 'startMonth') searchForm.value.startMonth = str
|
else if (datePickerTarget.value === 'endMonth') searchForm.value.endMonth = str
|
showDatePicker.value = false
|
handleQuery()
|
}
|
|
// 初始化:日报默认今天,月报默认本月,进出存默认最近7天
|
const initDefaultDates = () => {
|
const today = dayjs()
|
if (!searchForm.value.singleDate) {
|
searchForm.value.singleDate = today.format('YYYY-MM-DD')
|
}
|
if (!searchForm.value.startMonth || !searchForm.value.endMonth) {
|
const startOfMonth = today.startOf('month').format('YYYY-MM-DD')
|
const endOfMonth = today.endOf('month').format('YYYY-MM-DD')
|
searchForm.value.startMonth = startOfMonth
|
searchForm.value.endMonth = endOfMonth
|
}
|
if (!searchForm.value.startDate || !searchForm.value.endDate) {
|
searchForm.value.endDate = today.format('YYYY-MM-DD')
|
searchForm.value.startDate = today.subtract(6, 'day').format('YYYY-MM-DD')
|
}
|
}
|
|
watch(
|
() => searchForm.value.reportType,
|
() => {
|
// 切换报表类型时保留已算好的默认时间,只重查
|
handleQuery()
|
}
|
)
|
|
onShow(() => {
|
initDefaultDates()
|
handleQuery()
|
})
|
|
const goBack = () => uni.navigateBack()
|
</script>
|
|
<style lang="scss" scoped>
|
.report-page { min-height: 100vh; background: #f5f5f5; padding-bottom: 40rpx; }
|
.tabs-wrap { display: flex; background: #fff; padding: 24rpx; gap: 24rpx; }
|
.tab-item { flex: 1; text-align: center; padding: 20rpx; border-radius: 12rpx; background: #f0f0f0; font-size: 28rpx; color: #666; }
|
.tab-item.active { background: #2979ff; color: #fff; }
|
.search-section { background: #fff; margin: 24rpx; padding: 24rpx; border-radius: 16rpx; }
|
.search-row { display: flex; align-items: center; margin-bottom: 0; flex-wrap: wrap; }
|
.search-row .label { width: 140rpx; font-size: 26rpx; color: #666; }
|
.search-row .label.end { margin-left: 24rpx; }
|
.date-picker { flex: 1; min-width: 200rpx; padding: 20rpx; background: #f5f5f5; border-radius: 12rpx; font-size: 28rpx; }
|
.btn-row { display: flex; gap: 24rpx; margin-top: 24rpx; }
|
.btn-query { flex: 1; text-align: center; padding: 24rpx; background: #2979ff; color: #fff; border-radius: 12rpx; }
|
.btn-reset { flex: 1; text-align: center; padding: 24rpx; background: #e0e0e0; border-radius: 12rpx; }
|
.list-section { margin: 24rpx; }
|
.section-header {
|
margin-bottom: 16rpx;
|
padding: 16rpx 20rpx;
|
}
|
.table-title { font-size: 30rpx; font-weight: 500; color: #333; }
|
.card-item {
|
background: #fff;
|
border-radius: 16rpx;
|
padding: 20rpx 24rpx;
|
margin-bottom: 20rpx;
|
box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
|
}
|
.card-header {
|
padding: 4rpx 0 12rpx;
|
}
|
.header-main {
|
display: flex;
|
flex-direction: column;
|
gap: 6rpx;
|
}
|
.product-name {
|
font-size: 30rpx;
|
font-weight: 500;
|
color: #333;
|
}
|
.sub-title {
|
font-size: 24rpx;
|
color: #999;
|
}
|
.card-body .row { display: flex; justify-content: space-between; padding: 6rpx 0; font-size: 26rpx; }
|
.card-body .l { color: #666; } .card-body .r { color: #333; } .card-body .r.highlight { color: #2979ff; font-weight: 500; }
|
.no-data { text-align: center; padding: 60rpx 0; color: #999; font-size: 28rpx; }
|
.load-more-wrap { padding: 24rpx 0 8rpx; }
|
</style>
|