Files
shanxia2.0/miniprogram/pages/db/weighing/list.vue
T
2026-10-02 09:28:00 +08:00

335 lines
10 KiB
Vue

<template>
<view class="db-page db-page--scroll">
<view class="db-header" :style="safeAreaStyle">
<view class="db-header__nav">
<view class="db-header__left">
<text class="db-header__back" @click="goBack">←</text>
<text class="db-header__title">地磅单管理</text>
</view>
<view /><!-- 胶囊占位 -->
</view>
</view>
<view class="db-search-wrap">
<db-search
v-model="keyword"
placeholder="搜索车牌号"
@change="onKeyword"
@search="onRefresh"
@clear="onClear"
/>
</view>
<view class="db-filter-bar">
<view
v-for="item in dateFilters"
:key="item.value"
class="db-filter-chip"
:class="{ 'db-filter-chip--active': dateFilter === item.value }"
@click="setFilter(item.value)"
>
{{ item.label }}
</view>
</view>
<scroll-view
class="db-list db-list-scroll"
scroll-y
:style="{ height: listHeight }"
refresher-enabled
:refresher-triggered="refreshing"
@refresherrefresh="onPullRefresh"
@scrolltolower="onLoadMore"
lower-threshold="80"
>
<view v-if="!loading && !list.length" class="db-empty">暂无数据</view>
<view v-for="item in list" :key="item.id" class="db-card" @click="viewItem(item)">
<view class="db-card__head">
<text class="db-card__title">{{ item.vehicleNo || '未填车牌' }}</text>
<db-tag v-if="item.isVoided" type="danger">已作废</db-tag>
<db-tag v-else :type="item.businessType === '入库' ? 'success' : 'warning'">
{{ item.businessType }}
</db-tag>
</view>
<view class="db-card__row">
<text class="db-card__label">货物</text>
<text>{{ item.cargoName || '--' }}</text>
</view>
<view class="db-card__row">
<text class="db-card__label">往来单位</text>
<text>{{ partnerCompanyOf(item) }}</text>
</view>
<view class="db-card__row">
<text class="db-card__label">毛重/皮重</text>
<text>{{ item.grossWeight ?? '--' }} / {{ item.tareWeight ?? '--' }} kg</text>
</view>
<view class="db-card__row">
<text class="db-card__label">净重</text>
<text>{{ item.netWeight ?? '--' }} kg</text>
</view>
<view class="db-card__foot" @click.stop>
<db-btn size="small" type="primary" plain @click="viewRelated(item)">关联</db-btn>
<db-btn
v-if="!item.isVoided"
size="small"
type="primary"
@click="editItem(item)"
>
{{ needsSecond(item) ? '二次过磅' : '编辑' }}
</db-btn>
<db-btn
v-if="!item.isVoided"
size="small"
type="danger"
@click="voidItem(item)"
>
作废
</db-btn>
<db-btn size="small" type="danger" plain @click="removeItem(item)">删除</db-btn>
</view>
</view>
<view v-if="list.length" class="db-list-footer">
{{ finished ? '没有更多了' : loading ? '加载中...' : '上拉加载更多' }}
</view>
<view v-else-if="loading" class="db-empty">加载中...</view>
</scroll-view>
<db-popup :show="relatedOpen" height="75vh" @close="relatedOpen = false" @update:show="(v) => (relatedOpen = v)">
<view style="padding: 16px">
<view class="db-header__title" style="margin-bottom: 12px">关联数据</view>
<view v-if="relatedLoading" class="db-empty">加载中...</view>
<template v-else>
<view v-if="related.plan" class="db-card">
<db-tag type="primary">计划</db-tag>
<view class="db-card__row" style="margin-top: 8px">
<text class="db-card__label">计划号</text>
<text>{{ related.plan.planNo || '--' }}</text>
</view>
</view>
<view v-if="related.inventory" class="db-card">
<db-tag type="success">出入库</db-tag>
<view class="db-card__row" style="margin-top: 8px">
<text class="db-card__label">车牌</text>
<text>{{ related.inventory.vehicleNo || '--' }}</text>
</view>
<view class="db-card__row">
<text class="db-card__label">数量</text>
<text>{{ related.inventory.quantity ?? '--' }} 吨</text>
</view>
</view>
<view v-if="!related.plan && !related.inventory" class="db-empty">暂无关联数据</view>
</template>
</view>
</db-popup>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { PAGE_PATHS } from '@/config/api'
import { getSafeAreaStyle } from '@/utils/safeArea'
import { weighingApi } from '@/pages/db/api/db'
import { formatYmd, addDays, weekRange } from '@/pages/db/utils/dbDate'
import DbSearch from '@/components/db-ui/db-search.vue'
import DbTag from '@/components/db-ui/db-tag.vue'
import DbBtn from '@/components/db-ui/db-btn.vue'
import DbPopup from '@/components/db-ui/db-popup.vue'
const safeAreaStyle = getSafeAreaStyle()
const keyword = ref('')
const dateFilter = ref('all')
const loading = ref(false)
const finished = ref(false)
const refreshing = ref(false)
const list = ref([])
const currentPage = ref(1)
const pageSize = 20
const listHeight = ref('60vh')
const relatedOpen = ref(false)
const relatedLoading = ref(false)
const related = ref({ plan: null, inventory: null })
let debounce = null
const dateFilters = [
{ label: '全部', value: 'all' },
{ label: '今日', value: 'today' },
{ label: '明日', value: 'tomorrow' },
{ label: '昨日', value: 'yesterday' },
{ label: '本周', value: 'week' }
]
function computeListHeight() {
try {
const sys = uni.getSystemInfoSync()
const h = (sys.windowHeight || 600) - 220
listHeight.value = `${Math.max(h, 320)}px`
} catch (e) {
listHeight.value = '60vh'
}
}
function needsSecond(item) {
return Number(item.grossWeight) > 0 && (!item.tareWeight || Number(item.tareWeight) === 0)
}
/** 与出入库「往来单位」对齐:入库取供货单位,出库取收货单位 */
function partnerCompanyOf(item) {
const name =
item.businessType === '入库' ? item.supplier : item.businessType === '出库' ? item.consignee : item.supplier || item.consignee
return name || '--'
}
function buildParams() {
const params = {
page: currentPage.value,
pageSize
}
if (keyword.value) params.vehicleNo = keyword.value
const today = new Date()
if (dateFilter.value === 'today') {
const d = formatYmd(today)
params.dateFrom = d
params.dateTo = d
} else if (dateFilter.value === 'tomorrow') {
const t = formatYmd(addDays(today, 1))
params.dateFrom = t
params.dateTo = t
} else if (dateFilter.value === 'yesterday') {
const y = formatYmd(addDays(today, -1))
params.dateFrom = y
params.dateTo = y
} else if (dateFilter.value === 'week') {
const w = weekRange(today)
params.dateFrom = w.from
params.dateTo = w.to
}
return params
}
async function fetchPage() {
if (loading.value && !refreshing.value) return
loading.value = true
try {
const data = await weighingApi.list(buildParams())
const items = Array.isArray(data) ? data : data?.items || []
const total = Array.isArray(data) ? data.length : Number(data?.total || 0)
if (currentPage.value === 1) {
list.value = items
} else {
list.value = list.value.concat(items)
}
currentPage.value += 1
finished.value = items.length < pageSize || list.value.length >= total
} catch (e) {
if (currentPage.value === 1) list.value = []
finished.value = true
} finally {
loading.value = false
refreshing.value = false
}
}
function onRefresh() {
currentPage.value = 1
finished.value = false
loading.value = false
list.value = []
fetchPage()
}
function onPullRefresh() {
refreshing.value = true
currentPage.value = 1
finished.value = false
fetchPage()
}
function onLoadMore() {
if (finished.value || loading.value) return
fetchPage()
}
function onKeyword(v) {
keyword.value = typeof v === 'string' ? v : keyword.value
if (debounce) clearTimeout(debounce)
debounce = setTimeout(onRefresh, 300)
}
function onClear() {
keyword.value = ''
onRefresh()
}
function setFilter(v) {
if (dateFilter.value === v) return
dateFilter.value = v
onRefresh()
}
function goBack() {
uni.navigateBack({ fail: () => uni.reLaunch({ url: PAGE_PATHS.workbench }) })
}
function editItem(item) {
uni.navigateTo({ url: `${PAGE_PATHS.dbWeighingDetail}?id=${item.id}&mode=edit` })
}
function viewItem(item) {
uni.navigateTo({ url: `${PAGE_PATHS.dbWeighingDetail}?id=${item.id}&mode=view` })
}
async function viewRelated(item) {
relatedOpen.value = true
relatedLoading.value = true
try {
related.value = (await weighingApi.getRelated(item.id)) || { plan: null, inventory: null }
} catch (e) {
relatedOpen.value = false
} finally {
relatedLoading.value = false
}
}
function voidItem(item) {
import('@/utils/prompt-cancel-reason').then(async ({ promptOptionalCancelReason }) => {
const reason = await promptOptionalCancelReason({
title: '确认作废',
content: '确定作废该地磅单吗?作废原因选填。'
})
if (reason === null) return
await weighingApi.void(item.id, { voidReason: reason || undefined })
uni.showToast({ title: '已作废', icon: 'success' })
onRefresh()
})
}
function removeItem(item) {
uni.showModal({
title: '确认删除',
content: '确定删除该地磅单吗?',
success: async (res) => {
if (!res.confirm) return
await weighingApi.delete(item.id)
uni.showToast({ title: '删除成功', icon: 'success' })
onRefresh()
}
})
}
onShow(() => {
computeListHeight()
onRefresh()
})
</script>
<style lang="scss">
@import '@/styles/db-theme.scss';
.db-page--scroll {
padding-bottom: 0;
}
.db-list-scroll {
box-sizing: border-box;
}
.db-list-footer {
padding: 12px 0 24px;
text-align: center;
font-size: 12px;
color: #9ca3af;
}
</style>