优化
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import { apiRequest } from '@/api/http'
|
||||
interface Statistics { orderCount: number; skuCount: number; requestedPackages: number; actualPackages: number | null; requestedPieces: number | null; actualPieces: number | null; palletCount: number | null }
|
||||
const props = defineProps<{ query: Record<string, unknown>; revision: number }>()
|
||||
const statistics = ref<Statistics | null>(null)
|
||||
const error = ref(false)
|
||||
let generation = 0
|
||||
/** 仅消费与列表已提交查询一致的全筛选统计,旧响应不得覆盖新查询。 */
|
||||
watch(() => [props.query, props.revision], async () => {
|
||||
const request = ++generation
|
||||
statistics.value = null; error.value = false
|
||||
try {
|
||||
const result = await apiRequest<Statistics>({ method: 'GET', url: '/merchant/v1/last-leg/cargo-discards/statistics', params: props.query })
|
||||
if (request === generation) statistics.value = result
|
||||
} catch { if (request === generation) error.value = true }
|
||||
}, { immediate: true })
|
||||
/** 缺少商品或托盘事实时保留空值含义。 */
|
||||
function quantity(value: number | null, unit: string) { return value == null ? '未记录 / 不适用' : `${value} ${unit}` }
|
||||
</script>
|
||||
<template>
|
||||
<p aria-live="polite" class="discard-statistics" v-if="statistics">全筛选:{{ statistics.orderCount }} 票 · {{ statistics.skuCount }} 种 SKU · 申请 {{ statistics.requestedPackages }} 箱 · 已确认实际 {{ quantity(statistics.actualPackages, '箱') }} · 商品 {{ quantity(statistics.requestedPieces, '个') }} · 托盘 {{ quantity(statistics.palletCount, '托盘') }}</p>
|
||||
<p v-else class="discard-statistics">{{ error ? '数量汇总加载失败,请重新查询' : '数量汇总加载中…' }}</p>
|
||||
</template>
|
||||
<style scoped>.discard-statistics{margin:10px 0;color:var(--muted);font-size:13px;line-height:1.6}</style>
|
||||
@@ -0,0 +1,37 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import { getRelabelQuantities, type RelabelQuantityTotals } from '../api/relabelQuantities'
|
||||
const props = defineProps<{ query?: Record<string, unknown>; relabelNo?: string }>()
|
||||
const data = ref<RelabelQuantityTotals | null>(null)
|
||||
const error = ref('')
|
||||
let generation = 0
|
||||
function dimension(known: number, unknown: number, unit: string) {
|
||||
if (unknown > 0 && known === 0) return `未提供(${unknown} 票)`
|
||||
return `已知 ${known} ${unit}${unknown > 0 ? `(${unknown} 票未提供)` : ''}`
|
||||
}
|
||||
watch(() => [props.query, props.relabelNo], async () => {
|
||||
const current = ++generation
|
||||
data.value = null; error.value = ''
|
||||
try {
|
||||
const result = await getRelabelQuantities({ ...props.query, ...(props.relabelNo ? { relabelNo: props.relabelNo } : {}) })
|
||||
if (current === generation) data.value = result
|
||||
} catch { if (current === generation) error.value = '数量汇总加载失败,请刷新重试' }
|
||||
}, { immediate: true, deep: true })
|
||||
</script>
|
||||
<template>
|
||||
<section class="relabel-quantity-panel" aria-label="贴标数量维度">
|
||||
<p v-if="error" role="alert">{{ error }}</p>
|
||||
<template v-else-if="data">
|
||||
<strong>{{ relabelNo ? '本单数量' : '全部筛选结果' }}</strong>
|
||||
<span>{{ data.orderCount }} 票</span><span>{{ data.skuCount }} 种 SKU</span>
|
||||
<span>贴标件数:{{ dimension(Number(data.knownLabelPieceCount), Number(data.unknownPieceOrders), '件') }}</span>
|
||||
<span>箱数:{{ dimension(Number(data.knownCartonCount), Number(data.unknownCartonOrders), '箱') }}</span>
|
||||
<p v-if="relabelNo && data.orders[relabelNo]?.duplicateCarton" role="status">存在重复箱唛关联,箱数已去重,贴标件数待核查。</p>
|
||||
</template>
|
||||
<span v-else>数量加载中…</span>
|
||||
</section>
|
||||
</template>
|
||||
<style scoped>
|
||||
.relabel-quantity-panel { display: flex; gap: 12px; flex-wrap: wrap; padding: 12px 16px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface, white); margin-bottom: 12px; }
|
||||
.relabel-quantity-panel p { margin: 0; }
|
||||
</style>
|
||||
@@ -0,0 +1,52 @@
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{ modelValue: number }>()
|
||||
const emit = defineEmits<{ 'update:modelValue': [value: number] }>()
|
||||
let drag: { pointerId: number; startX: number; startValue: number; width: number } | null = null
|
||||
|
||||
function resize(value: number) {
|
||||
emit('update:modelValue', Math.min(65, Math.max(35, value)))
|
||||
}
|
||||
function startDrag(event: PointerEvent) {
|
||||
if (event.button !== 0 || !event.isPrimary) return
|
||||
const handle = event.currentTarget as HTMLElement
|
||||
const width = (handle.parentElement?.getBoundingClientRect().width ?? 0) - 16
|
||||
if (width <= 0) return
|
||||
event.preventDefault()
|
||||
handle.focus()
|
||||
drag = { pointerId: event.pointerId, startX: event.clientX, startValue: props.modelValue, width }
|
||||
handle.setPointerCapture(event.pointerId)
|
||||
}
|
||||
function moveDrag(event: PointerEvent) {
|
||||
if (!drag || drag.pointerId !== event.pointerId) return
|
||||
resize(drag.startValue + (event.clientX - drag.startX) / drag.width * 100)
|
||||
}
|
||||
function endDrag(event: PointerEvent) {
|
||||
if (drag?.pointerId !== event.pointerId) return
|
||||
drag = null
|
||||
const handle = event.currentTarget as HTMLElement
|
||||
if (handle.hasPointerCapture(event.pointerId)) handle.releasePointerCapture(event.pointerId)
|
||||
}
|
||||
function keyResize(event: KeyboardEvent) {
|
||||
const value = { ArrowLeft: props.modelValue - 2, ArrowRight: props.modelValue + 2, Home: 35, End: 65, Enter: 43 }[event.key]
|
||||
if (value === undefined) return
|
||||
event.preventDefault()
|
||||
resize(value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="transfer-pane-divider" role="separator" tabindex="0" aria-label="调整转运信息和换标申请宽度"
|
||||
aria-orientation="vertical" :aria-valuenow="Math.round(modelValue)" :aria-valuemin="35" :aria-valuemax="65"
|
||||
aria-controls="transfer-main-pane transfer-relabel-pane" title="左右拖动调整宽度,双击恢复"
|
||||
@pointerdown="startDrag" @pointermove="moveDrag" @pointerup="endDrag" @pointercancel="endDrag"
|
||||
@lostpointercapture="drag = null" @keydown="keyResize" @dblclick="resize(43)">
|
||||
<span aria-hidden="true" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.transfer-pane-divider { display:flex; width:16px; align-items:center; justify-content:center; align-self:stretch; cursor:col-resize; touch-action:none; user-select:none; outline:none; }
|
||||
.transfer-pane-divider span { width:4px; height:64px; border-radius:4px; background:#c5d1e1; transition:background .15s; }
|
||||
.transfer-pane-divider:hover, .transfer-pane-divider:focus-visible { background:#e9f1ff; border-radius:6px; }
|
||||
.transfer-pane-divider:hover span, .transfer-pane-divider:focus-visible span { background:#3478c8; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user