daShangDao_psiWebApp/backups/ShippingOrder.vue.bak
97694731 0543936df8
Some checks failed
CI / build (20.x) (push) Waiting to run
CI / lint (push) Waiting to run
CI / test (push) Waiting to run
CI / deploy-preview (push) Blocked by required conditions
CI / security (push) Waiting to run
CI / build (18.x) (push) Has been cancelled
change store
2026-06-04 11:18:46 +08:00

772 lines
29 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<el-card class="shipping-order-manager">
<template #header>
<div class="card-header">发货单管理</div>
</template>
<div class="filter-bar">
<el-input v-model="searchParams.keyword" placeholder="发货单号" clearable style="width: 200px"
@keyup.enter="handleSearch">
<template #prefix>
<el-icon>
<Search />
</el-icon>
</template>
</el-input>
<el-select v-model="searchParams.status" placeholder="状态" clearable style="width: 120px">
<el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<el-select v-model="searchParams.warehouse_id" placeholder="仓库" clearable style="width: 140px">
<el-option v-for="item in warehouseOptions" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
<el-select v-model="searchParams.customer_id" placeholder="客户" clearable filterable style="width: 160px">
<el-option v-for="item in customerOptions" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
<el-button type="primary" :icon="Search" @click="handleSearch">搜索</el-button>
<el-button :icon="Refresh" @click="resetSearch">重置</el-button>
</div>
<el-table :data="tableData" v-loading="loading" border stripe style="width: 100%"
@expand-change="handleExpandChange">
<!-- 发货单详情展开行 -->
<el-table-column type="expand">
<template #default="{ row }">
<div v-if="detailCache[row.id]" style="padding: 12px 20px">
<h4 style="margin: 0 0 10px; font-size: 14px; color: #303133">发货明细</h4>
<el-table :data="detailCache[row.id].items || []" border size="small">
<el-table-column prop="product_name" label="商品名称" min-width="120" show-overflow-tooltip align="center" />
<el-table-column prop="product_code" label="ISBN/条码" min-width="100" show-overflow-tooltip
align="center" />
<el-table-column label="库位" min-width="100" align="center">
<template #default="{ row: item }">
{{ locationMap[item.warehouse_code] || item.warehouse_code || '-' }}##{{
locationMap[item.location_name]
|| item.location_name || '-' }}
</template>
</el-table-column>
<el-table-column label="销售单号" min-width="140" align="center">
<template #default="{ row: item }">
<a v-if="item.sales_order_no" style="color: #409eff; cursor: pointer; text-decoration: underline;"
@click.stop="navigateToSalesOrder(item.sales_order_no)">
{{ item.sales_order_no }}
</a>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="出库单号" min-width="140" align="center">
<template #default="{ row: item }">
<a v-if="item.outbound_order_no" style="color: #409eff; cursor: pointer; text-decoration: underline;"
@click.stop="navigateToOutbound(item.outbound_order_no)">
{{ item.outbound_order_no }}
</a>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column prop="association_order_no" label="平台单号" min-width="90" align="center">
<template #default="{ row: item }">
<span style="display: inline-flex; align-items: center; gap: 4px;">
<span v-if="item.association_order_no" style="color: #409eff; text-decoration: underline;">{{ item.association_order_no }}</span>
<span v-else>-</span>
<el-button v-if="item.association_order_no" type="primary" size="small" link
@click="copyToClipboard(item.association_order_no, '平台单号已复制')">
<el-icon>
<CopyDocument />
</el-icon>
</el-button>
</span>
</template>
</el-table-column>
<el-table-column label="销售时间" min-width="100" align="center">
<template #default="{ row: item }">{{ formatDateForSale(item.sales_order_created_at) }}</template>
</el-table-column>
<el-table-column label="售价" min-width="40" align="center">
<template #default="{ row: item }">
<span style="color: #e6a23c; font-weight: 600">{{ formatAmount(item.unit_price) }}</span>
</template>
</el-table-column>
<el-table-column prop="quantity" label="出库数量" min-width="60" align="center">
<template #default="{ row: item }">
<span style="color: #409eff; font-weight: 600">{{ item.quantity }}</span>
</template>
</el-table-column>
<el-table-column label="快递公司" min-width="100" align="center">
<template #default="{ row: item }">{{ logisticsCompanyMap[item.logistics_company] ||
item.logistics_company || '-' }}</template>
</el-table-column>
<el-table-column prop="logistics_no" label="快递单号" min-width="100" align="center">
<template #default="{ row: item }">{{ item.logistics_no || '-' }}</template>
</el-table-column>
<el-table-column prop="receiver_address" label="收货地址" min-width="100" align="center">
<template #default="{ row: item }">{{ item.receiver_address || '-' }}</template>
</el-table-column>
</el-table>
</div>
<div v-else style="padding: 20px; text-align: center; color: #909399">
<el-icon class="is-loading" style="margin-right: 6px">
<Loading />
</el-icon>加载中...
</div>
</template>
</el-table-column>
<el-table-column prop="shipping_no" label="发货单号" min-width="160" show-overflow-tooltip align="center" />
<el-table-column label="客户" min-width="160" align="center">
<template #default="{ row }">
<template v-if="row.shop_list && row.shop_list.length > 0">
<div v-for="(shop, idx) in row.shop_list" :key="idx">
{{ shop.shop_name }}({{ shop.shop_type_text }})
</div>
</template>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column prop="status_text" label="状态" min-width="80" show-overflow-tooltip align="center" />
<el-table-column prop="operator" label="操作员" min-width="90" align="center">
<template #default="{ row }">{{ row.operator || '-' }}</template>
</el-table-column>
<el-table-column label="创建时间" min-width="150" align="center">
<template #default="{ row }">{{ formatTimestamp(row.created_at) }}</template>
</el-table-column>
<el-table-column prop="remark" label="备注" min-width="140" show-overflow-tooltip align="center">
<template #default="{ row }">{{ row.remark || '-' }}</template>
</el-table-column>
<el-table-column label="操作" align="center" width="100">
<template #default="{ row }">
<el-button type="primary" size="small" link @click="handleStartShipping(row)">开始发货</el-button>
</template>
</el-table-column>
<el-table-column label="" align="center" width="70">
<template #default="{ row }">
<el-button type="primary" size="small" link>打单</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-wrapper">
<el-pagination v-model:current-page="pagination.current" v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 50, 100]" :total="pagination.total" layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange" @current-change="handleCurrentChange" />
</div>
<!-- 扫码发货弹窗 -->
<el-dialog v-model="scanDialogVisible" title="扫码发货" width="1000px" :close-on-click-modal="false"
@opened="onScanDialogOpened" @closed="onScanDialogClosed">
<div style="padding: 10px">
<p style="margin-bottom: 12px; color: #606266; text-align: center">
共 <strong style="color:#409eff">{{ currentScanTotal }}</strong> 件商品,已扫描 <strong style="color:#67c23a">{{
currentScanIndex }}</strong> 件
</p>
<el-table :data="scanList" border size="small" style="width: 100%" max-height="340"
:row-class-name="scanRowClassName">
<el-table-column type="index" label="#" width="40" align="center" />
<el-table-column prop="product_name" label="商品名称" min-width="130" show-overflow-tooltip />
<el-table-column prop="product_code" label="ISBN" width="120" align="center" />
<el-table-column prop="quantity" label="数量" width="60" align="center" />
<el-table-column prop="association_order_no" label="平台单号" min-width="150" align="center"
show-overflow-tooltip>
<template #default="{ row }">
<span v-if="row.association_order_no" style="display: inline-flex; align-items: center; gap: 4px;">
<span style="color: #409eff; text-decoration: underline;">{{ row.association_order_no }}</span>
<el-button type="primary" size="small" link
@click="copyToClipboard(row.association_order_no, '平台单号已复制')">
<el-icon>
<CopyDocument />
</el-icon>
</el-button>
</span>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="快递单号" min-width="120" align="center">
<template #default="{ row, $index }">
<span v-if="$index < currentScanIndex && row.logistics_no" style="color: #67c23a;">{{ row.logistics_no
}}</span>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="售价" width="80" align="center">
<template #default="{ row }">
<span style="color: #e6a23c; font-weight: 600">{{ formatAmount(row.unit_price) }}</span>
</template>
</el-table-column>
<el-table-column label="状态" width="90" align="center">
<template #default="{ row, $index }">
<template v-if="$index < currentScanIndex || row.logistics_no">
<el-tag type="success" size="small">已扫</el-tag>
</template>
<template v-else-if="$index === processingIndex">
<el-tag type="warning" size="small" effect="dark">扫描中</el-tag>
</template>
<template v-else>
<el-tag type="info" size="small">待扫</el-tag>
</template>
</template>
</el-table-column>
</el-table>
<el-input ref="scanInputRef" v-model="scanCode" placeholder="请用扫码枪扫描 ISBN..." @keyup.enter="handleScanSubmit"
class="hidden-scan-input" />
<p style="margin-top: 8px; color: #c0c4cc; font-size: 12px; text-align: center">请使用扫码枪扫描商品条码</p>
</div>
</el-dialog>
</el-card>
</template>
<script lang="ts">
import { defineComponent, ref, reactive, onMounted, computed, nextTick } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { Search, Refresh, View, Loading, CopyDocument } from '@element-plus/icons-vue'
import dayjs from 'dayjs'
import axios from 'axios'
import { copyToClipboard } from '@/utils/clipboard'
import { fetchShippingOrderList, fetchShippingOrderDetail, updateShippingOrderLogistics } from '@/api/shipping-order'
import { fetchWarehouseList } from '@/api/warehouse'
import { createPrintTask } from '@/api/print'
/** 状态映射 */
const STATUS_MAP: Record<number, { label: string; type: string }> = {
1: { label: '已创建', type: 'info' },
2: { label: '拣货中', type: 'warning' },
3: { label: '已完成', type: 'success' },
4: { label: '已取消', type: 'danger' }
}
export default defineComponent({
name: 'ShippingOrder',
setup() {
const router = useRouter()
const loading = ref<boolean>(false)
const tableData = ref<any[]>([])
/** 导航到销售订单页面 */
const navigateToSalesOrder = (salesOrderNo: string) => {
router.push({ name: 'sales-order', query: { keyword: salesOrderNo } })
}
/** 导航到出库单页面 */
const navigateToOutbound = (outboundOrderNo: string) => {
router.push({ name: 'outbound', query: { keyword: outboundOrderNo } })
}
const statusOptions = Object.entries(STATUS_MAP).map(([value, { label }]) => ({
value: Number(value),
label
}))
const searchParams = reactive<{
keyword: string
status: number | null
warehouse_id: number | null
customer_id: number | null
}>({
keyword: '',
status: null,
warehouse_id: null,
customer_id: null
})
const pagination = reactive({
current: 1,
pageSize: 10,
total: 0
})
const detailVisible = ref<boolean>(false)
const detailData = ref<any>(null)
// 下拉选项
const warehouseOptions = ref<any[]>([])
const customerOptions = ref<any[]>([])
// ID→名称映射
const warehouseMap = ref<Record<string, string>>({})
const customerMap = ref<Record<string, string>>({})
const salesOrderMap = ref<Record<string, string>>({})
const waveTaskMap = ref<Record<string, string>>({})
const locationMap = ref<Record<string, string>>({})
// 详情缓存
const detailCache = ref<Record<number, any>>({})
// ========== 扫码发货相关 ==========
const scanDialogVisible = ref(false)
const scanCode = ref('')
const scanInputRef = ref<any>(null)
const scanList = ref<any[]>([]) // 当前发货单的明细列表
const currentScanIndex = ref(0) // 当前扫描到的索引
const currentScanTotal = ref(0) // 总数量
const scanFocusTimer = ref<ReturnType<typeof setInterval> | null>(null)
const currentScanItem = computed(() => scanList.value[currentScanIndex.value] || null)
const formatAmount = (amount: number): string => {
if (!amount && amount !== 0) return '¥0.00'
return '¥' + (Number(amount) / 100).toFixed(2)
}
// 扫描防并发锁
const isProcessing = ref(false)
const processingIndex = ref(-1)
/** 已扫描完成的行添加背景色 */
const scanRowClassName = ({ row, rowIndex }: { row: any; rowIndex: number }) => {
if (rowIndex < currentScanIndex.value || row.logistics_no) {
return 'scan-completed-row'
}
return ''
}
/** 点击 "开始发货" */
const handleStartShipping = async (row: any) => {
// 优先用缓存,否则重新请求
let detail = detailCache.value[row.id]
if (!detail || !detail.items) {
try {
detail = await fetchShippingOrderDetail(row.id)
detailCache.value[row.id] = detail
} catch (e) {
ElMessage.error('加载发货明细失败')
return
}
}
const items = detail?.items || []
if (items.length === 0) {
ElMessage.warning('该发货单没有明细')
return
}
scanList.value = items.map((item: any) => ({ ...item, shipping_order_id: row.id }))
// 根据实际数据设置初始状态:已有物流单号的视为已扫描
const firstUnscanned = scanList.value.findIndex((item: any) => !item.logistics_no)
currentScanIndex.value = firstUnscanned >= 0 ? firstUnscanned : scanList.value.length
currentScanTotal.value = items.length
scanCode.value = ''
scanDialogVisible.value = true
}
/** 弹窗打开后聚焦并锁定焦点到隐藏 input */
const onScanDialogOpened = () => {
const focusInput = () => {
scanInputRef.value?.focus?.()
scanInputRef.value?.$el?.querySelector?.('input')?.focus?.()
}
// 立刻聚焦一次
nextTick(() => { focusInput() })
// 每隔 200ms 强制保持焦点(直到弹窗关闭)
const intervalId = setInterval(() => {
if (!scanDialogVisible.value) {
clearInterval(intervalId)
return
}
// 检查当前焦点是否已不在该输入框上,是则重新聚焦
const active = document.activeElement
const inputEl = scanInputRef.value?.$el?.querySelector?.('input')
if (inputEl && active !== inputEl) {
inputEl.focus()
}
}, 200)
// dialog 关闭时清理 interval
const origClose = onScanDialogClosed
const cleanup = () => {
clearInterval(intervalId)
origClose()
}
}
/** 弹窗关闭时清理 */
const onScanDialogClosed = () => {
scanCode.value = ''
scanList.value = []
currentScanIndex.value = 0
currentScanTotal.value = 0
}
/** 扫码回车处理 */
const handleScanSubmit = async () => {
const code = scanCode.value.trim()
if (!code) return
// 如果正在处理中,忽略新的扫码
if (isProcessing.value) return
// 锁定
isProcessing.value = true
processingIndex.value = currentScanIndex.value
// 在整个发货单明细中查找 ISBN 匹配的商品
const matchIndex = scanList.value.findIndex(
(item: any) => item.product_code === code
)
if (matchIndex === -1) {
ElMessage.warning(`未找到 ISBN 为 ${code} 的商品`)
isProcessing.value = false
processingIndex.value = -1
scanCode.value = ''
return
}
// 匹配成功:交换当前扫描位与匹配位(把匹配商品提到当前位置)
if (matchIndex !== currentScanIndex.value) {
const temp = scanList.value[currentScanIndex.value]
scanList.value[currentScanIndex.value] = scanList.value[matchIndex]
scanList.value[matchIndex] = temp
}
const currentItem = scanList.value[currentScanIndex.value]
if (!currentItem) {
isProcessing.value = false
processingIndex.value = -1
return
}
const salesPersonId = currentItem.sales_person_id
if (!salesPersonId) {
ElMessage.warning('当前商品缺少 sales_person_id')
isProcessing.value = false
processingIndex.value = -1
scanCode.value = ''
return
}
try {
// 第一步:获取快递账号列表
const res = await axios.get('https://api.buzhiyushu.cn/zhishu/fastMail/listApi', {
params: { shopId: salesPersonId }
})
console.log('fastMail/listApi 响应:', res.data)
// 第二步:提取 fastMailType=1 的成员,调用创建面单接口
const data = res.data?.data
if (!Array.isArray(data) || data.length === 0) {
ElMessage.error('未获取到快递账号配置')
isProcessing.value = false
processingIndex.value = -1
scanCode.value = ''
return
}
const fastMail = data.find((item: any) => item.fastMailType === '1')
if (!fastMail || !currentItem.association_order_no) {
ElMessage.error('未找到默认快递配置或缺少平台单号')
isProcessing.value = false
processingIndex.value = -1
scanCode.value = ''
return
}
const formData = new FormData()
formData.append('type', fastMail.type)
formData.append('partnerId', fastMail.partnerId)
formData.append('secret', fastMail.secret)
formData.append('orderSn', currentItem.association_order_no)
formData.append('contact', currentItem.warehouse_contact_person)
formData.append('phoneNumber', currentItem.warehouse_contact_phone)
formData.append('province', currentItem.warehouse_province)
formData.append('city', currentItem.warehouse_city)
formData.append('area', currentItem.warehouse_district)
formData.append('town', currentItem.warehouse_address)
const createRes = await axios.post('https://print.buzhiyushu.cn/api/print/createOrderBatch', formData)
console.log('createOrderBatch 响应:', createRes.data)
// 第三步获取打印PDF信息
const createData = await axios.get('https://print.buzhiyushu.cn/api/print/createBmOrderDaYin', {
params: {
mailno: createRes.data.data[0].mail_no || createRes.data.data[0].mailno,
partnerId: fastMail.partnerId,
secret: fastMail.secret
}
})
console.log('createBmOrderDaYin 响应:', createData.data)
const printData = createRes.data.data[0]
printData.pdf_info = createData.data.pdfInfo
printData.itemList = createRes.data.erpGoodsOrderList[0].itemList;
console.log('createBmOrderDaYin 响应:', JSON.stringify(printData))
// 调用createPrintTask接口创建打印任务并打印失败会阻断后续回填操作
console.log('正在创建打印任务并打印...')
const LODOP = await createPrintTask('yunda', printData) as any
LODOP.SET_PRINTER_INDEX(localStorage.getItem('printer_express'));
const printResult = LODOP.PRINT()
console.log('打印结果:', printResult)
// 调用updateShippingOrderLogistics更新物流信息失败会阻断后续回填操作
if (currentItem.sales_order_item_id) {
await updateShippingOrderLogistics({
shipping_order_id: currentItem.shipping_order_id,
sales_order_item_id: currentItem.sales_order_item_id,
logistics_company: fastMail.type,
logistics_no: createRes.data.data[0].mail_no || createRes.data.data[0].mailno
})
}
console.log('正在回填快递单号并更新状态...')
const submitCompany = await axios.post('https://api.buzhiyushu.cn/zhishu/orderExternalGoods/submitCompanyOrder', {
code: fastMail.type,
orderNo: createRes.data.data[0].mail_no || createRes.data.data[0].mailno,
erpOrderId: createRes.data.erpGoodsOrderList[0].id.toString()
}, {
headers: { 'Content-Type': 'application/json' }
});
console.log('回填快递单号 响应:', submitCompany.data)
// 回填快递单号到当前行
scanList.value[currentScanIndex.value].logistics_no = createRes.data.data[0].mail_no || createRes.data.data[0].mailno
} catch (err) {
console.error('发货处理失败:', err)
ElMessage.error('发货处理失败,请重试')
isProcessing.value = false
processingIndex.value = -1
scanCode.value = ''
return
}
// 解锁,前进到下一个商品
isProcessing.value = false
processingIndex.value = -1
currentScanIndex.value++
scanCode.value = ''
if (currentScanIndex.value >= scanList.value.length) {
ElMessage.success('全部扫描完成!')
scanDialogVisible.value = false
}
}
const statusLabel = (status: number): string => STATUS_MAP[status]?.label || '未知'
const statusTagType = (status: number): string => STATUS_MAP[status]?.type || 'info'
const formatTimestamp = (timestamp?: number | string | null): string => {
if (!timestamp && timestamp !== 0) return '-'
return dayjs.unix(Number(timestamp)).format('YYYY-MM-DD HH:mm')
}
const formatDate = (date?: number | string | null): string => {
if (!date) return '-'
if (typeof date === 'number' && date < 10000000000) {
return dayjs.unix(date).format('YYYY-MM-DD')
}
return dayjs(date).format('YYYY-MM-DD')
}
const formatDateForSale = (date?: number | string | null): string => {
if (!date) return '-'
if (typeof date === 'number' && date < 10000000000) {
return dayjs.unix(date).format('YYYY-MM-DD HH:mm')
}
return dayjs(date).format('YYYY-MM-DD HH:mm')
}
/** 快递公司映射 */
const logisticsCompanyMap: Record<string, string> = {
YUNDA: '韵达快递'
}
/** 加载下拉选项和名称映射 */
const loadOptions = async (): Promise<void> => {
try {
// 加载仓库
const warehouseRes = await fetchWarehouseList({ keyword: '', page: 1, pageSize: 9999 })
warehouseOptions.value = warehouseRes.list || []
const wMap: Record<string, string> = {}
for (const w of warehouseRes.list) {
wMap[String(w.id)] = w.name || w.code || String(w.id)
}
warehouseMap.value = wMap
// TODO: 加载客户、销售订单、波次任务、库位选项需要对应API
customerOptions.value = []
customerMap.value = {}
} catch (error) {
// console.error('加载选项失败:', error)
}
}
/** 加载发货单列表 */
const loadList = async (): Promise<void> => {
loading.value = true
try {
const res = await fetchShippingOrderList({
check_no: searchParams.keyword,
status: searchParams.status || undefined,
warehouse_id: searchParams.warehouse_id || undefined,
customer_id: searchParams.customer_id || undefined,
page: pagination.current,
pageSize: pagination.pageSize
})
tableData.value = res.list || []
pagination.total = res.total || 0
} catch (error) {
// console.error('加载发货单列表失败:', error)
ElMessage.error('加载发货单列表失败')
} finally {
loading.value = false
}
}
/** 搜索 */
const handleSearch = (): void => {
pagination.current = 1
loadList()
}
/** 重置搜索 */
const resetSearch = (): void => {
searchParams.keyword = ''
searchParams.status = null
searchParams.warehouse_id = null
searchParams.customer_id = null
pagination.current = 1
loadList()
}
/** 分页大小变化 */
const handleSizeChange = (size: number): void => {
pagination.pageSize = size
loadList()
}
/** 页码变化 */
const handleCurrentChange = (page: number): void => {
pagination.current = page
loadList()
}
/** 展开行时加载详情 */
const handleExpandChange = async (row: any, expandedRows: any[]): Promise<void> => {
if (expandedRows.find((r: any) => r.id === row.id) && !detailCache.value[row.id]) {
try {
const detail = await fetchShippingOrderDetail(row.id)
detailCache.value[row.id] = detail
} catch (error) {
// console.error('加载发货单详情失败:', error)
detailCache.value[row.id] = { items: [] }
}
}
}
/** 查看发货单详情 */
const handleView = async (row: any): Promise<void> => {
try {
const detail = await fetchShippingOrderDetail(row.id)
detailData.value = detail
detailVisible.value = true
} catch (error) {
// console.error('加载发货单详情失败:', error)
ElMessage.error('加载详情失败')
}
}
onMounted(() => {
loadOptions()
loadList()
})
return {
loading,
tableData,
statusOptions,
searchParams,
pagination,
detailVisible,
detailData,
warehouseOptions,
customerOptions,
warehouseMap,
customerMap,
salesOrderMap,
waveTaskMap,
locationMap,
detailCache,
scanDialogVisible,
scanInputRef,
scanCode,
scanList,
currentScanIndex,
currentScanTotal,
currentScanItem,
isProcessing,
processingIndex,
formatAmount,
scanRowClassName,
handleStartShipping,
onScanDialogOpened,
onScanDialogClosed,
handleScanSubmit,
statusLabel,
statusTagType,
formatTimestamp,
formatDate,
formatDateForSale,
logisticsCompanyMap,
handleSearch,
resetSearch,
handleSizeChange,
handleCurrentChange,
handleExpandChange,
handleView,
navigateToSalesOrder,
navigateToOutbound,
copyToClipboard,
Search,
Refresh,
View,
Loading,
CopyDocument
}
}
})
</script>
<style scoped lang="scss">
.shipping-order-manager {
width: 100%;
}
.card-header {
font-size: 16px;
font-weight: 600;
color: #303133;
}
.filter-bar {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 16px;
}
.pagination-wrapper {
display: flex;
justify-content: flex-end;
margin-top: 16px;
}
:deep(.el-table__expanded-cell) {
padding: 0;
}
/* 隐藏扫码输入框仅保留焦点功能 */
.hidden-scan-input {
position: absolute;
left: -9999px;
opacity: 0;
width: 1px;
height: 1px;
}
/* 已完成扫描的行背景色 */
:deep(.el-table__row.scan-completed-row) {
background-color: #ecf5ff;
}
:deep(.el-table__row.scan-completed-row:hover > td) {
background-color: #ecf5ff;
}
</style>