| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357 |
- <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.planNo || '未填计划号' }}</text>
- <db-tag :type="item.businessType === '入库' ? 'success' : 'warning'">
- {{ item.businessType }}
- </db-tag>
- </view>
- <view class="db-card__row">
- <text class="db-card__label">往来单位</text>
- <text>{{ item.customerName || '未填' }}</text>
- </view>
- <view class="db-card__row">
- <text class="db-card__label">产品</text>
- <text>{{ item.product || '未填' }}</text>
- </view>
- <view class="db-card__row">
- <text class="db-card__label">计划量</text>
- <text>{{ item.plannedQuantity ?? '--' }} 吨</text>
- </view>
- <view class="db-card__row">
- <text class="db-card__label">状态</text>
- <db-tag :type="statusType(execStatus(item))">
- {{ execStatus(item) }}
- </db-tag>
- </view>
- <view class="db-card__foot" @click.stop>
- <db-btn size="small" type="primary" plain @click="viewRelated(item)">查看</db-btn>
- <db-btn size="small" type="primary" plain @click="editItem(item)">编辑</db-btn>
- <db-btn size="small" type="primary" @click="genInventory(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; max-height: 75vh; overflow: auto">
- <view class="db-header__title" style="margin-bottom: 12px">关联数据</view>
- <view v-if="relatedLoading" class="db-empty">加载中...</view>
- <view v-else-if="!mergedRelated.length" class="db-empty">暂无关联数据</view>
- <view v-for="(row, idx) in mergedRelated" :key="idx" class="db-card">
- <db-tag :type="row.type === 'inventory' ? 'primary' : 'warning'">
- {{ row.type === 'inventory' ? '出入库单' : '地磅单' }}
- </db-tag>
- <view class="db-card__row" style="margin-top: 8px">
- <text class="db-card__label">业务</text>
- <text>{{ row.businessType || '--' }}</text>
- </view>
- <view class="db-card__row">
- <text class="db-card__label">产品</text>
- <text>{{ row.product || row.cargoName || '--' }}</text>
- </view>
- <view class="db-card__row">
- <text class="db-card__label">车牌</text>
- <text>{{ row.vehicleNo || '--' }}</text>
- </view>
- </view>
- </view>
- </db-popup>
- </view>
- </template>
- <script setup>
- import { ref, computed } from 'vue'
- import { onShow } from '@dcloudio/uni-app'
- import { PAGE_PATHS } from '@/config/api'
- import { getSafeAreaStyle } from '@/utils/safeArea'
- import { planApi } 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 relatedData = ref({ inventories: [], weighings: [] })
- let debounce = null
- const dateFilters = [
- { label: '全部', value: 'all' },
- { label: '今日', value: 'today' },
- { label: '明日', value: 'tomorrow' },
- { label: '昨日', value: 'yesterday' },
- { label: '本周', value: 'week' }
- ]
- const mergedRelated = computed(() => {
- const items = []
- ;(relatedData.value.inventories || []).forEach((inv) => {
- items.push({ ...inv, type: 'inventory', sortTime: inv.createdAt || inv.date || '' })
- })
- ;(relatedData.value.weighings || []).forEach((w) => {
- items.push({ ...w, type: 'weighing', sortTime: w.grossWeightTime || w.createdAt || '' })
- })
- return items.sort((a, b) => {
- const ta = a.sortTime ? new Date(a.sortTime).getTime() : 0
- const tb = b.sortTime ? new Date(b.sortTime).getTime() : 0
- if (!ta && !tb) return 0
- if (!ta) return 1
- if (!tb) return -1
- return tb - ta
- })
- })
- 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 execStatus(row) {
- if (!row.executionStatus) return row.businessType === '入库' ? '待入库' : '待出库'
- if (row.businessType === '入库') {
- if (['待出库', '出库中', '已出库'].includes(row.executionStatus)) {
- return (row.executedQuantity || 0) > 0 ? '入库中' : '待入库'
- }
- } else if (['待入库', '入库中', '已入库'].includes(row.executionStatus)) {
- return (row.executedQuantity || 0) > 0 ? '出库中' : '待出库'
- }
- return row.executionStatus
- }
- function statusType(status) {
- if (status === '已入库' || status === '已出库') return 'success'
- if (status === '入库中' || status === '出库中') return 'primary'
- if (status === '待入库' || status === '待出库') return 'warning'
- return 'default'
- }
- function buildParams() {
- const params = {
- page: currentPage.value,
- pageSize,
- q: keyword.value || undefined
- }
- const today = new Date()
- if (dateFilter.value === 'today') {
- const d = formatYmd(today)
- params.createdFrom = d
- params.createdTo = d
- } else if (dateFilter.value === 'tomorrow') {
- const t = formatYmd(addDays(today, 1))
- params.createdFrom = t
- params.createdTo = t
- } else if (dateFilter.value === 'yesterday') {
- const y = formatYmd(addDays(today, -1))
- params.createdFrom = y
- params.createdTo = y
- } else if (dateFilter.value === 'week') {
- const w = weekRange(today)
- params.createdFrom = w.from
- params.createdTo = w.to
- }
- return params
- }
- async function fetchPage() {
- if (loading.value && !refreshing.value) return
- loading.value = true
- try {
- const data = await planApi.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.dbPlansDetail}?id=${item.id}&mode=edit` })
- }
- function viewItem(item) {
- uni.navigateTo({ url: `${PAGE_PATHS.dbPlansDetail}?id=${item.id}&mode=view` })
- }
- async function viewRelated(item) {
- relatedOpen.value = true
- relatedLoading.value = true
- try {
- relatedData.value = (await planApi.getRelated(item.id)) || { inventories: [], weighings: [] }
- } catch (e) {
- relatedOpen.value = false
- } finally {
- relatedLoading.value = false
- }
- }
- function genInventory(item) {
- const q = [
- `planNo=${encodeURIComponent(item.planNo || '')}`,
- `product=${encodeURIComponent(item.product || '')}`,
- `productId=${encodeURIComponent(item.productId || '')}`,
- `businessType=${encodeURIComponent(item.businessType || '')}`,
- `customerName=${encodeURIComponent(item.customerName || '')}`,
- `purchasePlanId=${encodeURIComponent(item.id)}`,
- 'from=plan',
- 'mode=create'
- ].join('&')
- uni.navigateTo({ url: `${PAGE_PATHS.dbInventoryDetail}?${q}` })
- }
- function removeItem(item) {
- uni.showModal({
- title: '确认删除',
- content: '确定要删除这个计划吗?',
- success: async (res) => {
- if (!res.confirm) return
- try {
- await planApi.delete(item.id)
- uni.showToast({ title: '删除成功', icon: 'success' })
- onRefresh()
- } catch (e) {
- // toast by request
- }
- }
- })
- }
- 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>
|