标签栏最多展示15个并支持更多折叠/一键关闭;同步官网顾问、工作台关怀、下单与操作扫描等页面改动。 Co-authored-by: Cursor <cursoragent@cursor.com>
1039 lines
34 KiB
Vue
1039 lines
34 KiB
Vue
<script setup lang="ts">
|
|
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
|
import { useI18n } from 'vue-i18n'
|
|
import { Modal } from '@arco-design/web-vue'
|
|
import HandsontablePro from '@/components/HandsontablePro/index.vue'
|
|
import PaginationPro from '@/components/PaginationPro/index.vue'
|
|
import ModalPro from '@/components/ModalPro/index.vue'
|
|
import useModal from '@/components/ModalPro/useModal'
|
|
import { useTableContainerHeight } from '@/composables/useTableContainerHeight'
|
|
import { useWarehouseAdminCustomer } from '@/composables/useWarehouseAdminCustomer'
|
|
import {
|
|
declareCustoms9610,
|
|
declareCustoms9610Batch,
|
|
fetchCustoms9610DeclarationFormBatch,
|
|
fetchCustoms9610ManifestBatch,
|
|
getCustoms9610Detail,
|
|
getCustoms9610List,
|
|
getCustoms9610StatusCounts,
|
|
updateCustoms9610,
|
|
} from '@/api/order/customs9610'
|
|
import { $baseMessage } from '@/utils'
|
|
import { hasPermission } from '@/utils/rbac-permission'
|
|
import { formatDateTime, normalizeListRow, pickField } from '@/utils/warehouseListMapper'
|
|
import { renderWarehouseActionBtn, wrapWarehouseActions } from '@/utils/warehouseActionRenderer'
|
|
|
|
defineOptions({ name: 'OrderCustoms9610' })
|
|
|
|
const { t } = useI18n()
|
|
const { tableContainerRef, tableHeight } = useTableContainerHeight()
|
|
const {
|
|
isAdmin,
|
|
customerId: adminCustomerId,
|
|
customerOptions,
|
|
withCustomerId,
|
|
} = useWarehouseAdminCustomer()
|
|
|
|
const STATUS_ALL = 'all'
|
|
const statusTab = ref(STATUS_ALL)
|
|
const keyword = ref('')
|
|
const listLoading = ref(false)
|
|
const tableRows = ref<any[]>([])
|
|
const currentPage = ref(1)
|
|
const pageSize = ref(30)
|
|
const totalCount = ref(0)
|
|
const selectedIds = ref<number[]>([])
|
|
const statusCounts = ref({ total: 0, pending: 0, declared: 0 })
|
|
const declaring = ref(false)
|
|
const fetchingManifest = ref(false)
|
|
const fetchingDeclarationForm = ref(false)
|
|
const saving = ref(false)
|
|
const detailLoading = ref(false)
|
|
|
|
const [registerEditModal, { openModal: openEditModal, closeModal: closeEditModal }] = useModal() as any
|
|
|
|
const editForm = reactive({
|
|
id: 0,
|
|
status: '',
|
|
platformCode: '',
|
|
ecomCorpCode: '',
|
|
ecomCorpName: '',
|
|
producerCode: '',
|
|
producerName: '',
|
|
clientNo: '',
|
|
serviceNo: '',
|
|
businessType: 'B',
|
|
region: '',
|
|
agentDeclare: '',
|
|
actualSellerCode: '',
|
|
preRecordFlag: '1',
|
|
isSummarySimple: false,
|
|
grossWeight: 0 as number,
|
|
currency: 'CNY',
|
|
destCountry: '',
|
|
freight: undefined as number | undefined,
|
|
shipmentNo: '',
|
|
customsOrderNo: '',
|
|
failReason: '',
|
|
items: [] as Array<{
|
|
id?: number
|
|
orderDeclarationId?: number
|
|
sku: string
|
|
cnName: string
|
|
exHsCode?: string
|
|
declareElement?: string
|
|
unitPrice: number
|
|
quantity: number
|
|
freight?: number
|
|
sortNo: number
|
|
}>,
|
|
})
|
|
|
|
const isPending = computed(() => editForm.status === 'pending')
|
|
|
|
const statusTabs = computed(() => [
|
|
{ key: STATUS_ALL, label: t('common.all'), count: statusCounts.value.total, showCount: true },
|
|
{
|
|
key: 'pending',
|
|
label: t('order.customs9610.status.pending'),
|
|
count: statusCounts.value.pending,
|
|
showCount: true,
|
|
},
|
|
{
|
|
key: 'declared',
|
|
label: t('order.customs9610.status.declared'),
|
|
count: statusCounts.value.declared,
|
|
showCount: true,
|
|
},
|
|
])
|
|
|
|
const slidingTabItems = computed(() =>
|
|
statusTabs.value.map((tab) => ({
|
|
key: tab.key,
|
|
label: tab.label,
|
|
badge: tab.showCount && tab.count != null ? tab.count : undefined,
|
|
})),
|
|
)
|
|
|
|
const selectedRowCount = computed(() => selectedIds.value.length)
|
|
|
|
function handleTabChange(key: string) {
|
|
if (statusTab.value === key) return
|
|
statusTab.value = key
|
|
currentPage.value = 1
|
|
loadList()
|
|
}
|
|
|
|
function handleResetSearch() {
|
|
keyword.value = ''
|
|
if (isAdmin.value) adminCustomerId.value = undefined
|
|
currentPage.value = 1
|
|
loadList()
|
|
loadCounts()
|
|
}
|
|
|
|
const hotColumns = computed(() => [
|
|
{ key: 'serviceNo', label: t('order.customs9610.table.serviceNo'), width: 150 },
|
|
{ key: 'clientNo', label: t('order.customs9610.table.clientNo'), width: 140 },
|
|
{ key: 'customerName', label: t('warehouse.common.customerName'), width: 120 },
|
|
{ key: 'destCountry', label: t('order.customs9610.table.destCountry'), width: 80 },
|
|
{ key: 'grossWeight', label: t('order.customs9610.table.grossWeight'), width: 90 },
|
|
{ key: 'currency', label: t('order.customs9610.table.currency'), width: 70 },
|
|
{ key: 'businessType', label: t('order.customs9610.table.businessType'), width: 70 },
|
|
{ key: 'statusLabel', label: t('order.customs9610.table.status'), width: 90 },
|
|
{ key: 'customsOrderNo', label: t('order.customs9610.table.customsOrderNo'), width: 140 },
|
|
{ key: 'declareTime', label: t('order.customs9610.table.declareTime'), width: 160 },
|
|
{ key: 'createTime', label: t('order.customs9610.table.createTime'), width: 160 },
|
|
])
|
|
|
|
function statusLabel(code: string) {
|
|
if (code === 'declared') return t('order.customs9610.status.declared')
|
|
if (code === 'pending') return t('order.customs9610.status.pending')
|
|
return code || '-'
|
|
}
|
|
|
|
function actionRenderer(instance: any, td: HTMLElement, row: number) {
|
|
const record = tableRows.value[row]
|
|
if (!record) {
|
|
td.innerHTML = ''
|
|
return td
|
|
}
|
|
const status = String(record.status || '')
|
|
const buttons = [
|
|
renderWarehouseActionBtn('detail', t('warehouse.common.detail'), 'detail'),
|
|
]
|
|
if (status === 'pending') {
|
|
if (hasPermission('Order.Customs9610.Update')) {
|
|
buttons.push(renderWarehouseActionBtn('edit', t('warehouse.common.edit'), 'edit'))
|
|
}
|
|
if (hasPermission('Order.Customs9610.Declare')) {
|
|
buttons.push(renderWarehouseActionBtn('declare', t('order.customs9610.actions.declare'), 'primary'))
|
|
}
|
|
}
|
|
return wrapWarehouseActions(td, buttons)
|
|
}
|
|
|
|
function handleSelectionChange(rows: any[]) {
|
|
selectedIds.value = (rows || [])
|
|
.map((x) => Number(x?.id ?? x?.Id ?? 0))
|
|
.filter((x) => Number.isFinite(x) && x > 0)
|
|
}
|
|
|
|
async function loadCounts() {
|
|
try {
|
|
const data = await getCustoms9610StatusCounts(withCustomerId({}))
|
|
statusCounts.value = {
|
|
total: Number(pickField(data, 'total', 'Total') ?? 0),
|
|
pending: Number(pickField(data, 'pending', 'Pending') ?? 0),
|
|
declared: Number(pickField(data, 'declared', 'Declared') ?? 0),
|
|
}
|
|
} catch {
|
|
// ignore
|
|
}
|
|
}
|
|
|
|
async function loadList() {
|
|
listLoading.value = true
|
|
try {
|
|
const body = withCustomerId({
|
|
page: currentPage.value,
|
|
pageSize: pageSize.value,
|
|
keyword: keyword.value.trim() || undefined,
|
|
status: statusTab.value === STATUS_ALL ? undefined : statusTab.value,
|
|
})
|
|
const { items, total } = await getCustoms9610List(body)
|
|
tableRows.value = (items || []).map((raw: any) => {
|
|
const row = normalizeListRow(raw)
|
|
const status = String(pickField(raw, 'status', 'Status') ?? '')
|
|
return {
|
|
...row,
|
|
id: Number(pickField(raw, 'id', 'Id') ?? 0),
|
|
status,
|
|
statusLabel: statusLabel(status),
|
|
serviceNo: pickField(raw, 'serviceNo', 'ServiceNo') || '-',
|
|
clientNo: pickField(raw, 'clientNo', 'ClientNo') || '-',
|
|
customerName: pickField(raw, 'customerName', 'CustomerName') || '-',
|
|
destCountry: pickField(raw, 'destCountry', 'Country') || '-',
|
|
grossWeight: pickField(raw, 'grossWeight', 'GrossWeight') ?? '-',
|
|
currency: pickField(raw, 'currency', 'Currency') || '-',
|
|
businessType: pickField(raw, 'businessType', 'FormType') || '-',
|
|
customsOrderNo: pickField(raw, 'customsOrderNo', 'OrderNo') || '-',
|
|
declareTime: formatDateTime(pickField(raw, 'declareTime', 'DeclareTime')),
|
|
createTime: formatDateTime(pickField(raw, 'createTime', 'CreateTime')),
|
|
_raw: raw,
|
|
}
|
|
})
|
|
totalCount.value = total || 0
|
|
selectedIds.value = []
|
|
} catch (e: any) {
|
|
$baseMessage(e?.message || t('order.customs9610.message.loadFailed'), 'error')
|
|
} finally {
|
|
listLoading.value = false
|
|
}
|
|
}
|
|
|
|
function handleSearch() {
|
|
currentPage.value = 1
|
|
loadList()
|
|
loadCounts()
|
|
}
|
|
|
|
function handlePageChange(page: number) {
|
|
currentPage.value = page
|
|
loadList()
|
|
}
|
|
|
|
function handlePageSizeChange(size: number) {
|
|
pageSize.value = size
|
|
currentPage.value = 1
|
|
loadList()
|
|
}
|
|
|
|
function fillEditForm(data: any) {
|
|
const items = (pickField(data, 'items', 'Items') as any[]) || []
|
|
Object.assign(editForm, {
|
|
id: Number(pickField(data, 'id', 'Id') ?? 0),
|
|
status: String(pickField(data, 'status', 'Status') ?? ''),
|
|
platformCode: String(pickField(data, 'platformCode', 'Ebp') ?? ''),
|
|
ecomCorpCode: String(pickField(data, 'ecomCorpCode', 'EbcCode') ?? ''),
|
|
ecomCorpName: String(pickField(data, 'ecomCorpName', 'EbcName') ?? ''),
|
|
producerCode: String(pickField(data, 'producerCode', 'OwnerCode') ?? ''),
|
|
producerName: String(pickField(data, 'producerName', 'OwnerName') ?? ''),
|
|
clientNo: String(pickField(data, 'clientNo', 'ClientNo') ?? ''),
|
|
serviceNo: String(pickField(data, 'serviceNo', 'ServiceNo') ?? ''),
|
|
businessType: String(pickField(data, 'businessType', 'FormType') ?? 'B'),
|
|
region: String(pickField(data, 'region', 'Region') ?? ''),
|
|
agentDeclare: String(pickField(data, 'agentDeclare', 'IsAgent') ?? ''),
|
|
actualSellerCode: String(pickField(data, 'actualSellerCode', 'ActBuyer') ?? ''),
|
|
preRecordFlag: String(pickField(data, 'preRecordFlag', 'SoLabelFlag') ?? '1'),
|
|
isSummarySimple: Boolean(pickField(data, 'isSummarySimple', 'IsSimple')),
|
|
grossWeight: Number(pickField(data, 'grossWeight', 'GrossWeight') ?? 0),
|
|
currency: String(pickField(data, 'currency', 'Currency') ?? 'CNY'),
|
|
destCountry: String(pickField(data, 'destCountry', 'Country') ?? ''),
|
|
freight: pickField(data, 'freight', 'Freight') as number | undefined,
|
|
shipmentNo: String(pickField(data, 'shipmentNo', 'BlNo') ?? ''),
|
|
customsOrderNo: String(pickField(data, 'customsOrderNo', 'OrderNo') ?? ''),
|
|
failReason: String(pickField(data, 'failReason', 'FailReason') ?? ''),
|
|
items: items.map((it: any, idx: number) => ({
|
|
id: Number(pickField(it, 'id', 'Id') ?? 0) || undefined,
|
|
orderDeclarationId: pickField(it, 'orderDeclarationId', 'OrderDeclarationId') as number | undefined,
|
|
sku: String(pickField(it, 'sku', 'Sku', 'itemNo', 'ItemNo') ?? ''),
|
|
cnName: String(pickField(it, 'cnName', 'CnName', 'itemName', 'ItemName') ?? ''),
|
|
exHsCode: String(pickField(it, 'exHsCode', 'ExHsCode', 'gcode', 'Gcode') ?? ''),
|
|
declareElement: String(pickField(it, 'declareElement', 'Gmodel') ?? ''),
|
|
unitPrice: Number(pickField(it, 'unitPrice', 'UnitPrice', 'price', 'Price') ?? 0),
|
|
quantity: Number(pickField(it, 'quantity', 'Quantity', 'qty', 'Qty') ?? 0),
|
|
freight: pickField(it, 'freight', 'Freight') as number | undefined,
|
|
sortNo: Number(pickField(it, 'sortNo', 'SortNo') ?? idx),
|
|
})),
|
|
})
|
|
}
|
|
|
|
async function openDetail(id: number, editable: boolean) {
|
|
detailLoading.value = true
|
|
openEditModal(true)
|
|
try {
|
|
const data = await getCustoms9610Detail(id)
|
|
fillEditForm(data)
|
|
if (!editable) {
|
|
editForm.status = String(pickField(data, 'status', 'Status') ?? editForm.status)
|
|
}
|
|
} catch (e: any) {
|
|
$baseMessage(e?.message || t('order.customs9610.message.loadFailed'), 'error')
|
|
closeEditModal()
|
|
} finally {
|
|
detailLoading.value = false
|
|
}
|
|
}
|
|
|
|
function handleRowAction(payload: { action: string; row: number }) {
|
|
const record = tableRows.value[payload.row]
|
|
if (!record?.id) return
|
|
if (payload.action === 'detail') {
|
|
openDetail(record.id, false)
|
|
} else if (payload.action === 'edit') {
|
|
openDetail(record.id, true)
|
|
} else if (payload.action === 'declare') {
|
|
handleDeclare(record.id)
|
|
}
|
|
}
|
|
|
|
function addItemRow() {
|
|
editForm.items.push({
|
|
sku: '',
|
|
cnName: '',
|
|
exHsCode: '',
|
|
declareElement: '',
|
|
unitPrice: 0,
|
|
quantity: 1,
|
|
sortNo: editForm.items.length,
|
|
})
|
|
}
|
|
|
|
function removeItemRow(index: number) {
|
|
editForm.items.splice(index, 1)
|
|
}
|
|
|
|
function validateEdit() {
|
|
if (!editForm.platformCode.trim()) {
|
|
$baseMessage(t('order.customs9610.message.platformCodeRequired'), 'warning')
|
|
return false
|
|
}
|
|
if (!editForm.clientNo.trim() || !editForm.serviceNo.trim()) {
|
|
$baseMessage(t('order.customs9610.message.clientServiceRequired'), 'warning')
|
|
return false
|
|
}
|
|
if (!(editForm.grossWeight > 0)) {
|
|
$baseMessage(t('order.customs9610.message.weightRequired'), 'warning')
|
|
return false
|
|
}
|
|
if (!editForm.currency.trim() || !editForm.destCountry.trim()) {
|
|
$baseMessage(t('order.customs9610.message.currencyDestCountryRequired'), 'warning')
|
|
return false
|
|
}
|
|
if (editForm.businessType === 'B' && !editForm.preRecordFlag) {
|
|
$baseMessage(t('order.customs9610.message.preRecordRequired'), 'warning')
|
|
return false
|
|
}
|
|
if (editForm.businessType === 'A' && (!editForm.region || !editForm.agentDeclare)) {
|
|
$baseMessage(t('order.customs9610.message.aFieldsRequired'), 'warning')
|
|
return false
|
|
}
|
|
if (!editForm.items.length || editForm.items.some((x) => !x.sku.trim() || !x.cnName.trim())) {
|
|
$baseMessage(t('order.customs9610.message.itemsRequired'), 'warning')
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
async function handleSave() {
|
|
if (!isPending.value) {
|
|
closeEditModal()
|
|
return
|
|
}
|
|
if (!validateEdit()) return
|
|
saving.value = true
|
|
try {
|
|
await updateCustoms9610({
|
|
id: editForm.id,
|
|
platformCode: editForm.platformCode,
|
|
ecomCorpCode: editForm.ecomCorpCode || undefined,
|
|
ecomCorpName: editForm.ecomCorpName || undefined,
|
|
producerCode: editForm.producerCode || undefined,
|
|
producerName: editForm.producerName || undefined,
|
|
clientNo: editForm.clientNo,
|
|
serviceNo: editForm.serviceNo,
|
|
businessType: editForm.businessType,
|
|
region: editForm.region || undefined,
|
|
agentDeclare: editForm.agentDeclare || undefined,
|
|
actualSellerCode: editForm.actualSellerCode || undefined,
|
|
preRecordFlag: editForm.preRecordFlag || undefined,
|
|
isSummarySimple: editForm.isSummarySimple,
|
|
grossWeight: editForm.grossWeight,
|
|
currency: editForm.currency,
|
|
destCountry: editForm.destCountry,
|
|
freight: editForm.freight,
|
|
shipmentNo: editForm.shipmentNo || undefined,
|
|
items: editForm.items,
|
|
})
|
|
$baseMessage(t('order.customs9610.message.saveSuccess'), 'success')
|
|
closeEditModal()
|
|
await loadList()
|
|
await loadCounts()
|
|
} catch (e: any) {
|
|
$baseMessage(e?.message || t('order.customs9610.message.saveFailed'), 'error')
|
|
} finally {
|
|
saving.value = false
|
|
}
|
|
}
|
|
|
|
async function handleDeclare(id: number) {
|
|
Modal.confirm({
|
|
title: t('order.customs9610.confirm.declareTitle'),
|
|
content: t('order.customs9610.confirm.declareContent'),
|
|
onOk: async () => {
|
|
declaring.value = true
|
|
try {
|
|
const res = await declareCustoms9610(id)
|
|
const ok = Boolean(pickField(res, 'success', 'Success'))
|
|
$baseMessage(
|
|
String(pickField(res, 'message', 'Message') || (ok ? t('order.customs9610.message.declareSuccess') : t('order.customs9610.message.declareFailed'))),
|
|
ok ? 'success' : 'error',
|
|
)
|
|
await loadList()
|
|
await loadCounts()
|
|
} catch (e: any) {
|
|
$baseMessage(e?.message || t('order.customs9610.message.declareFailed'), 'error')
|
|
} finally {
|
|
declaring.value = false
|
|
}
|
|
},
|
|
})
|
|
}
|
|
|
|
async function handleBatchDeclare() {
|
|
const ids = selectedIds.value
|
|
if (!ids.length) {
|
|
$baseMessage(t('order.customs9610.message.selectFirst'), 'warning')
|
|
return
|
|
}
|
|
Modal.confirm({
|
|
title: t('order.customs9610.confirm.batchDeclareTitle'),
|
|
content: t('order.customs9610.confirm.batchDeclareContent', { count: ids.length }),
|
|
onOk: async () => {
|
|
declaring.value = true
|
|
try {
|
|
const res = await declareCustoms9610Batch(ids)
|
|
const successCount = Number(pickField(res, 'successCount', 'SuccessCount') ?? 0)
|
|
const failCount = Number(pickField(res, 'failCount', 'FailCount') ?? 0)
|
|
$baseMessage(
|
|
t('order.customs9610.message.batchResult', { success: successCount, fail: failCount }),
|
|
failCount > 0 ? 'warning' : 'success',
|
|
)
|
|
await loadList()
|
|
await loadCounts()
|
|
} catch (e: any) {
|
|
$baseMessage(e?.message || t('order.customs9610.message.declareFailed'), 'error')
|
|
} finally {
|
|
declaring.value = false
|
|
}
|
|
},
|
|
})
|
|
}
|
|
|
|
async function runBatchFetch(
|
|
kind: 'manifest' | 'declarationForm',
|
|
api: (ids: Array<number | string>) => Promise<any>,
|
|
) {
|
|
const ids = selectedIds.value
|
|
if (!ids.length) {
|
|
$baseMessage(t('order.customs9610.message.selectFirst'), 'warning')
|
|
return
|
|
}
|
|
const loadingRef = kind === 'manifest' ? fetchingManifest : fetchingDeclarationForm
|
|
const title =
|
|
kind === 'manifest'
|
|
? t('order.customs9610.confirm.batchManifestTitle')
|
|
: t('order.customs9610.confirm.batchDeclarationFormTitle')
|
|
const content =
|
|
kind === 'manifest'
|
|
? t('order.customs9610.confirm.batchManifestContent', { count: ids.length })
|
|
: t('order.customs9610.confirm.batchDeclarationFormContent', { count: ids.length })
|
|
const failFallback =
|
|
kind === 'manifest'
|
|
? t('order.customs9610.message.fetchManifestFailed')
|
|
: t('order.customs9610.message.fetchDeclarationFormFailed')
|
|
|
|
Modal.confirm({
|
|
title,
|
|
content,
|
|
onOk: async () => {
|
|
loadingRef.value = true
|
|
try {
|
|
const res = await api(ids)
|
|
const successCount = Number(pickField(res, 'successCount', 'SuccessCount') ?? 0)
|
|
const failCount = Number(pickField(res, 'failCount', 'FailCount') ?? 0)
|
|
$baseMessage(
|
|
t('order.customs9610.message.batchResult', { success: successCount, fail: failCount }),
|
|
failCount > 0 ? 'warning' : 'success',
|
|
)
|
|
} catch (e: any) {
|
|
$baseMessage(e?.message || failFallback, 'error')
|
|
} finally {
|
|
loadingRef.value = false
|
|
}
|
|
},
|
|
})
|
|
}
|
|
|
|
function handleBatchFetchManifest() {
|
|
return runBatchFetch('manifest', fetchCustoms9610ManifestBatch)
|
|
}
|
|
|
|
function handleBatchFetchDeclarationForm() {
|
|
return runBatchFetch('declarationForm', fetchCustoms9610DeclarationFormBatch)
|
|
}
|
|
|
|
watch(adminCustomerId, () => {
|
|
handleSearch()
|
|
})
|
|
|
|
onMounted(() => {
|
|
loadList()
|
|
loadCounts()
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div class="customs9610-container">
|
|
<div class="table-section">
|
|
<div class="tabs-actions-section">
|
|
<div class="tabs-slot">
|
|
<SlidingTabs
|
|
:model-value="statusTab"
|
|
:items="slidingTabItems"
|
|
@change="handleTabChange"
|
|
/>
|
|
</div>
|
|
|
|
<div class="toolbar-left">
|
|
<div class="list-filters">
|
|
<a-select
|
|
v-if="isAdmin"
|
|
v-model="adminCustomerId"
|
|
allow-clear
|
|
allow-search
|
|
:placeholder="t('warehouse.common.selectCustomer')"
|
|
:options="customerOptions"
|
|
style="width: 200px"
|
|
@change="handleSearch"
|
|
/>
|
|
<a-input
|
|
v-model="keyword"
|
|
allow-clear
|
|
:placeholder="t('order.customs9610.search.keyword')"
|
|
style="width: 240px"
|
|
@press-enter="handleSearch"
|
|
/>
|
|
<a-button class="app-btn-search" @click="handleSearch">
|
|
{{ t('order.myOrders.search.actions.search') }}
|
|
</a-button>
|
|
<a-button class="app-btn-reset" @click="handleResetSearch">
|
|
{{ t('order.myOrders.search.actions.reset') }}
|
|
</a-button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="table-toolbar">
|
|
<div class="actions-right">
|
|
<a-button
|
|
v-if="hasPermission('Order.Customs9610.DeclareBatch')"
|
|
class="app-btn-toolbar"
|
|
:loading="declaring"
|
|
:disabled="!selectedIds.length || declaring"
|
|
@click="handleBatchDeclare"
|
|
>
|
|
{{ t('order.customs9610.actions.batchDeclare') }}
|
|
</a-button>
|
|
<a-button
|
|
v-if="hasPermission('Order.Customs9610.FetchManifestBatch')"
|
|
class="app-btn-toolbar"
|
|
:loading="fetchingManifest"
|
|
:disabled="!selectedIds.length || fetchingManifest"
|
|
@click="handleBatchFetchManifest"
|
|
>
|
|
{{ t('order.customs9610.actions.batchFetchManifest') }}
|
|
</a-button>
|
|
<a-button
|
|
v-if="hasPermission('Order.Customs9610.FetchDeclarationFormBatch')"
|
|
class="app-btn-toolbar"
|
|
:loading="fetchingDeclarationForm"
|
|
:disabled="!selectedIds.length || fetchingDeclarationForm"
|
|
@click="handleBatchFetchDeclarationForm"
|
|
>
|
|
{{ t('order.customs9610.actions.batchFetchDeclarationForm') }}
|
|
</a-button>
|
|
</div>
|
|
<div class="toolbar-right">
|
|
<span class="selected-count">
|
|
{{ t('order.myOrders.selectedCount', { count: selectedRowCount }) }}
|
|
</span>
|
|
<div class="pagination-controls">
|
|
<PaginationPro
|
|
:total-count="totalCount"
|
|
v-model:current-page="currentPage"
|
|
v-model:page-size="pageSize"
|
|
:page-sizes="[10, 30, 50, 100]"
|
|
@page-change="handlePageChange"
|
|
@page-size-change="handlePageSizeChange"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div ref="tableContainerRef" class="list-wrap">
|
|
<div v-if="!listLoading && totalCount === 0" class="list-empty-hint">
|
|
<span>{{ t('order.customs9610.empty.hint') }}</span>
|
|
</div>
|
|
<a-spin :loading="listLoading" class="table-spin">
|
|
<HandsontablePro
|
|
:data="tableRows"
|
|
:columns="hotColumns"
|
|
:height="tableHeight"
|
|
stretch-columns
|
|
show-action-column
|
|
show-checkbox-column
|
|
:action-renderer="actionRenderer"
|
|
@action-click="handleRowAction"
|
|
@selection-change="handleSelectionChange"
|
|
/>
|
|
</a-spin>
|
|
</div>
|
|
</div>
|
|
|
|
<ModalPro
|
|
@register="registerEditModal"
|
|
:title="isPending ? t('order.customs9610.modal.edit') : t('order.customs9610.modal.detail')"
|
|
width="920px"
|
|
:mask-closable="false"
|
|
:confirm-loading="saving"
|
|
:ok-text="isPending ? t('warehouse.common.save') : t('warehouse.common.confirm')"
|
|
@ok="handleSave"
|
|
@cancel="closeEditModal"
|
|
>
|
|
<a-spin :loading="detailLoading" style="width: 100%">
|
|
<a-alert
|
|
v-if="editForm.failReason"
|
|
type="error"
|
|
:title="editForm.failReason"
|
|
style="margin-bottom: 12px"
|
|
/>
|
|
<a-form :model="editForm" layout="horizontal" auto-label-width>
|
|
<a-row :gutter="16">
|
|
<a-col :span="8">
|
|
<a-form-item :label="t('order.customs9610.field.platformCode')" required>
|
|
<a-input v-model="editForm.platformCode" :disabled="!isPending" :max-length="16" />
|
|
</a-form-item>
|
|
</a-col>
|
|
<a-col :span="8">
|
|
<a-form-item :label="t('order.customs9610.field.businessType')" required>
|
|
<a-select v-model="editForm.businessType" :disabled="!isPending">
|
|
<a-option value="A">A</a-option>
|
|
<a-option value="B">B</a-option>
|
|
</a-select>
|
|
</a-form-item>
|
|
</a-col>
|
|
<a-col :span="8">
|
|
<a-form-item :label="t('order.customs9610.field.preRecordFlag')">
|
|
<a-select v-model="editForm.preRecordFlag" :disabled="!isPending" allow-clear>
|
|
<a-option value="0">0</a-option>
|
|
<a-option value="1">1</a-option>
|
|
</a-select>
|
|
</a-form-item>
|
|
</a-col>
|
|
<a-col :span="8">
|
|
<a-form-item :label="t('order.customs9610.field.clientNo')" required>
|
|
<a-input v-model="editForm.clientNo" :disabled="!isPending" :max-length="48" />
|
|
</a-form-item>
|
|
</a-col>
|
|
<a-col :span="8">
|
|
<a-form-item :label="t('order.customs9610.field.serviceNo')" required>
|
|
<a-input v-model="editForm.serviceNo" :disabled="!isPending" :max-length="48" />
|
|
</a-form-item>
|
|
</a-col>
|
|
<a-col :span="8">
|
|
<a-form-item :label="t('order.customs9610.field.destCountry')" required>
|
|
<a-input v-model="editForm.destCountry" :disabled="!isPending" :max-length="4" />
|
|
</a-form-item>
|
|
</a-col>
|
|
<a-col :span="8">
|
|
<a-form-item :label="t('order.customs9610.field.currency')" required>
|
|
<a-input v-model="editForm.currency" :disabled="!isPending" :max-length="4" />
|
|
</a-form-item>
|
|
</a-col>
|
|
<a-col :span="8">
|
|
<a-form-item :label="t('order.customs9610.field.grossWeight')" required>
|
|
<a-input-number
|
|
v-model="editForm.grossWeight"
|
|
:disabled="!isPending"
|
|
:min="0"
|
|
:precision="5"
|
|
style="width: 100%"
|
|
/>
|
|
</a-form-item>
|
|
</a-col>
|
|
<a-col :span="8">
|
|
<a-form-item :label="t('order.customs9610.field.freight')">
|
|
<a-input-number
|
|
v-model="editForm.freight"
|
|
:disabled="!isPending"
|
|
:min="0"
|
|
:precision="2"
|
|
style="width: 100%"
|
|
/>
|
|
</a-form-item>
|
|
</a-col>
|
|
<a-col :span="8">
|
|
<a-form-item :label="t('order.customs9610.field.ecomCorpCode')">
|
|
<a-input v-model="editForm.ecomCorpCode" :disabled="!isPending" :max-length="18" />
|
|
</a-form-item>
|
|
</a-col>
|
|
<a-col :span="8">
|
|
<a-form-item :label="t('order.customs9610.field.ecomCorpName')">
|
|
<a-input v-model="editForm.ecomCorpName" :disabled="!isPending" :max-length="100" />
|
|
</a-form-item>
|
|
</a-col>
|
|
<a-col :span="8">
|
|
<a-form-item :label="t('order.customs9610.field.producerCode')">
|
|
<a-input v-model="editForm.producerCode" :disabled="!isPending" :max-length="18" />
|
|
</a-form-item>
|
|
</a-col>
|
|
<a-col :span="8">
|
|
<a-form-item :label="t('order.customs9610.field.producerName')">
|
|
<a-input v-model="editForm.producerName" :disabled="!isPending" :max-length="100" />
|
|
</a-form-item>
|
|
</a-col>
|
|
<a-col :span="8">
|
|
<a-form-item :label="t('order.customs9610.field.region')">
|
|
<a-input
|
|
v-model="editForm.region"
|
|
:disabled="!isPending"
|
|
:max-length="10"
|
|
placeholder="SZ / other"
|
|
/>
|
|
</a-form-item>
|
|
</a-col>
|
|
<a-col :span="8">
|
|
<a-form-item :label="t('order.customs9610.field.agentDeclare')">
|
|
<a-input v-model="editForm.agentDeclare" :disabled="!isPending" :max-length="2" />
|
|
</a-form-item>
|
|
</a-col>
|
|
<a-col :span="8">
|
|
<a-form-item :label="t('order.customs9610.field.actualSellerCode')">
|
|
<a-input v-model="editForm.actualSellerCode" :disabled="!isPending" :max-length="18" />
|
|
</a-form-item>
|
|
</a-col>
|
|
<a-col :span="8">
|
|
<a-form-item :label="t('order.customs9610.field.shipmentNo')">
|
|
<a-input v-model="editForm.shipmentNo" :disabled="!isPending" :max-length="32" />
|
|
</a-form-item>
|
|
</a-col>
|
|
<a-col :span="8">
|
|
<a-form-item :label="t('order.customs9610.field.customsOrderNo')">
|
|
<a-input :model-value="editForm.customsOrderNo" disabled />
|
|
</a-form-item>
|
|
</a-col>
|
|
<a-col :span="8">
|
|
<a-form-item :label="t('order.customs9610.field.isSummarySimple')">
|
|
<a-switch v-model="editForm.isSummarySimple" :disabled="!isPending" />
|
|
</a-form-item>
|
|
</a-col>
|
|
</a-row>
|
|
</a-form>
|
|
|
|
<div class="items-toolbar">
|
|
<span>{{ t('order.customs9610.items.title') }}</span>
|
|
<a-button v-if="isPending" size="mini" type="outline" @click="addItemRow">
|
|
{{ t('order.customs9610.items.add') }}
|
|
</a-button>
|
|
</div>
|
|
<a-table :data="editForm.items" :pagination="false" size="small" row-key="sortNo">
|
|
<template #columns>
|
|
<a-table-column :title="t('order.customs9610.items.sku')" :width="120">
|
|
<template #cell="{ record }">
|
|
<a-input v-model="record.sku" :disabled="!isPending" size="mini" />
|
|
</template>
|
|
</a-table-column>
|
|
<a-table-column :title="t('order.customs9610.items.cnName')" :width="160">
|
|
<template #cell="{ record }">
|
|
<a-input v-model="record.cnName" :disabled="!isPending" size="mini" />
|
|
</template>
|
|
</a-table-column>
|
|
<a-table-column :title="t('order.customs9610.items.exHsCode')" :width="100">
|
|
<template #cell="{ record }">
|
|
<a-input v-model="record.exHsCode" :disabled="!isPending" size="mini" />
|
|
</template>
|
|
</a-table-column>
|
|
<a-table-column :title="t('order.customs9610.items.declareElement')" :width="140">
|
|
<template #cell="{ record }">
|
|
<a-input v-model="record.declareElement" :disabled="!isPending" size="mini" />
|
|
</template>
|
|
</a-table-column>
|
|
<a-table-column :title="t('order.customs9610.items.unitPrice')" :width="100">
|
|
<template #cell="{ record }">
|
|
<a-input-number
|
|
v-model="record.unitPrice"
|
|
:disabled="!isPending"
|
|
size="mini"
|
|
:min="0"
|
|
:precision="5"
|
|
/>
|
|
</template>
|
|
</a-table-column>
|
|
<a-table-column :title="t('order.customs9610.items.quantity')" :width="90">
|
|
<template #cell="{ record }">
|
|
<a-input-number
|
|
v-model="record.quantity"
|
|
:disabled="!isPending"
|
|
size="mini"
|
|
:min="0"
|
|
:precision="5"
|
|
/>
|
|
</template>
|
|
</a-table-column>
|
|
<a-table-column v-if="isPending" :title="t('warehouse.common.delete')" :width="70">
|
|
<template #cell="{ rowIndex }">
|
|
<a-button size="mini" status="danger" type="text" @click="removeItemRow(rowIndex)">
|
|
{{ t('warehouse.common.delete') }}
|
|
</a-button>
|
|
</template>
|
|
</a-table-column>
|
|
</template>
|
|
</a-table>
|
|
</a-spin>
|
|
</ModalPro>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped lang="less">
|
|
.customs9610-container {
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
padding: 16px 16px 24px;
|
|
background-color: var(--app-page-bg);
|
|
flex: 1;
|
|
min-height: 0;
|
|
overflow-x: hidden;
|
|
overflow-y: hidden;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.tabs-actions-section {
|
|
background: transparent;
|
|
border-radius: 0;
|
|
padding: 10px 16px;
|
|
margin-bottom: 0;
|
|
border-bottom: 1px solid var(--color-border-2);
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
flex-wrap: nowrap;
|
|
gap: 12px;
|
|
flex-shrink: 0;
|
|
min-height: 52px;
|
|
box-sizing: border-box;
|
|
|
|
.tabs-slot {
|
|
flex: 1 1 auto;
|
|
min-width: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
overflow: hidden;
|
|
}
|
|
|
|
:deep(.t-tabs) {
|
|
flex: 0 1 auto;
|
|
min-width: 0;
|
|
max-width: 100%;
|
|
width: max-content;
|
|
}
|
|
}
|
|
|
|
.toolbar-left {
|
|
flex: 0 0 auto;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: flex-end;
|
|
padding: 0;
|
|
max-width: 100%;
|
|
height: 32px;
|
|
|
|
.list-filters {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: flex-end;
|
|
gap: 8px;
|
|
height: 32px;
|
|
|
|
:deep(.arco-select-view),
|
|
:deep(.arco-input-wrapper),
|
|
:deep(.arco-btn) {
|
|
height: 32px;
|
|
}
|
|
|
|
:deep(.arco-select-view-single),
|
|
:deep(.arco-input-wrapper) {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
}
|
|
|
|
:deep(.arco-input-wrapper) {
|
|
padding-top: 0;
|
|
padding-bottom: 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
.table-section {
|
|
background: var(--app-card-bg);
|
|
border-radius: 8px;
|
|
border: 1px solid var(--app-card-border);
|
|
padding: 0;
|
|
flex: 1;
|
|
min-height: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.table-toolbar {
|
|
padding: 12px 16px 0;
|
|
margin-bottom: 12px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
gap: 12px;
|
|
|
|
.toolbar-right {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
flex-wrap: wrap;
|
|
justify-content: flex-end;
|
|
|
|
.selected-count {
|
|
font-size: 13px;
|
|
color: var(--color-text-3);
|
|
white-space: nowrap;
|
|
line-height: 32px;
|
|
}
|
|
|
|
.pagination-controls {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
}
|
|
}
|
|
|
|
.actions-right {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: var(--app-button-gap, 8px);
|
|
align-items: center;
|
|
flex: 1;
|
|
min-width: 0;
|
|
|
|
:deep(.arco-btn) {
|
|
height: 32px;
|
|
padding: 0 12px;
|
|
border-radius: 4px;
|
|
font-size: 14px;
|
|
font-weight: 400;
|
|
line-height: 30px;
|
|
}
|
|
}
|
|
|
|
.list-empty-hint {
|
|
position: absolute;
|
|
inset: 0;
|
|
z-index: 2;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 24px;
|
|
pointer-events: none;
|
|
font-size: 14px;
|
|
color: var(--color-text-3);
|
|
text-align: center;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
.table-spin {
|
|
width: 100%;
|
|
flex: 1;
|
|
min-height: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
|
|
:deep(.arco-spin-children) {
|
|
flex: 1;
|
|
min-height: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
}
|
|
|
|
.list-wrap {
|
|
position: relative;
|
|
width: 100%;
|
|
overflow: hidden;
|
|
flex: 1;
|
|
min-height: 0;
|
|
padding: 0 16px 16px;
|
|
box-sizing: border-box;
|
|
display: flex;
|
|
flex-direction: column;
|
|
overscroll-behavior: contain;
|
|
touch-action: pan-x pan-y;
|
|
|
|
:deep(.handsontable-pro-container) {
|
|
width: 100%;
|
|
flex: 1;
|
|
min-height: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
}
|
|
|
|
.items-toolbar {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin: 8px 0;
|
|
font-weight: 600;
|
|
}
|
|
</style>
|