<template>
|
<div class="wait-pakin-page art-full-height">
|
<ArtSearchBar
|
v-model="searchForm"
|
:items="searchItems"
|
:showExpand="true"
|
@search="handleSearch"
|
@reset="handleReset"
|
/>
|
|
<ElCard class="art-table-card">
|
<ArtTableHeader v-model:columns="columnChecks" :loading="loading" @refresh="refreshData">
|
<template #left>
|
<ElSpace wrap>
|
<ElButton
|
type="primary"
|
:disabled="generatableSelectedRows.length === 0"
|
@click="openGenerateTaskDialog(generatableSelectedRows)"
|
v-ripple
|
>
|
生成任务
|
</ElButton>
|
<ListExportPrint
|
class="inline-flex"
|
:preview-visible="previewVisible"
|
@update:previewVisible="handlePreviewVisibleChange"
|
:report-title="reportTitle"
|
:selected-rows="selectedRows"
|
:query-params="reportQueryParams"
|
:columns="columns"
|
:preview-rows="previewRows"
|
:preview-meta="resolvedPreviewMeta"
|
:total="pagination.total"
|
:disabled="loading"
|
@export="handleExport"
|
@print="handlePrint"
|
/>
|
</ElSpace>
|
</template>
|
</ArtTableHeader>
|
|
<ArtTable
|
:loading="loading"
|
:data="data"
|
:columns="columns"
|
:pagination="pagination"
|
@selection-change="handleSelectionChange"
|
@pagination:size-change="handleSizeChange"
|
@pagination:current-change="handleCurrentChange"
|
/>
|
|
<WaitPakinDetailDrawer
|
v-model:visible="detailDrawerVisible"
|
:loading="detailLoading"
|
:items-loading="detailItemsLoading"
|
:detail="detailData"
|
:item-rows="detailItemRows"
|
:item-columns="waitPakinItemColumns"
|
/>
|
|
<WaitPakinSiteDialog
|
v-model:visible="siteDialogVisible"
|
:selected-rows="taskSourceRows"
|
@select="handleSelectSite"
|
/>
|
</ElCard>
|
</div>
|
</template>
|
|
<script setup>
|
import { computed, ref } from 'vue'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { useUserStore } from '@/store/modules/user'
|
import { useTable } from '@/hooks/core/useTable'
|
import { usePrintExportPage } from '@/views/system/common/usePrintExportPage'
|
import ListExportPrint from '@/components/biz/list-export-print/index.vue'
|
import { defaultResponseAdapter } from '@/utils/table/tableUtils'
|
import { guardRequestWithMessage } from '@/utils/sys/requestGuard'
|
import {
|
fetchDeleteWaitPakin,
|
fetchExportWaitPakinReport,
|
fetchMergeWaitPakinTasks,
|
fetchWaitPakinDetail,
|
fetchWaitPakinItemPage,
|
fetchWaitPakinMany,
|
fetchWaitPakinPage
|
} from '@/api/wait-pakin'
|
import WaitPakinDetailDrawer from './modules/wait-pakin-detail-drawer.vue'
|
import WaitPakinSiteDialog from './modules/wait-pakin-site-dialog.vue'
|
import { createWaitPakinTableColumns } from './waitPakinTable.columns'
|
import {
|
WAIT_PAKIN_REPORT_STYLE,
|
WAIT_PAKIN_REPORT_TITLE,
|
buildWaitPakinMergePayload,
|
buildWaitPakinPageQueryParams,
|
buildWaitPakinPrintRows,
|
buildWaitPakinReportMeta,
|
buildWaitPakinSearchParams,
|
createWaitPakinItemColumns,
|
createWaitPakinSearchState,
|
getWaitPakinIoStatusOptions,
|
getWaitPakinPaginationKey,
|
getWaitPakinStatusOptions,
|
normalizeWaitPakinDetailRecord,
|
normalizeWaitPakinItemRow,
|
normalizeWaitPakinListRow
|
} from './waitPakinPage.helpers'
|
|
defineOptions({ name: 'WaitPakin' })
|
|
const userStore = useUserStore()
|
|
const reportTitle = WAIT_PAKIN_REPORT_TITLE
|
const searchForm = ref(createWaitPakinSearchState())
|
const selectedRows = ref([])
|
const detailDrawerVisible = ref(false)
|
const detailLoading = ref(false)
|
const detailItemsLoading = ref(false)
|
const detailData = ref({})
|
const detailItemRows = ref([])
|
const waitPakinItemColumns = createWaitPakinItemColumns()
|
const siteDialogVisible = ref(false)
|
const taskSourceRows = ref([])
|
|
const reportQueryParams = computed(() => buildWaitPakinSearchParams(searchForm.value))
|
|
const searchItems = computed(() => [
|
{
|
label: '关键字',
|
key: 'condition',
|
type: 'input',
|
props: {
|
clearable: true,
|
placeholder: '请输入组托单号/容器码'
|
}
|
},
|
{
|
label: '组托单号',
|
key: 'code',
|
type: 'input',
|
props: {
|
clearable: true,
|
placeholder: '请输入组托单号'
|
}
|
},
|
{
|
label: '容器码',
|
key: 'barcode',
|
type: 'input',
|
props: {
|
clearable: true,
|
placeholder: '请输入容器码'
|
}
|
},
|
{
|
label: '组托状态',
|
key: 'ioStatus',
|
type: 'select',
|
props: {
|
clearable: true,
|
options: getWaitPakinIoStatusOptions()
|
}
|
},
|
{
|
label: '状态',
|
key: 'status',
|
type: 'select',
|
props: {
|
clearable: true,
|
options: getWaitPakinStatusOptions()
|
}
|
},
|
{
|
label: '备注',
|
key: 'memo',
|
type: 'input',
|
props: {
|
clearable: true,
|
placeholder: '请输入备注'
|
}
|
}
|
])
|
|
const canGenerateTask = (row) => Number(row?.ioStatus) === 1
|
|
const {
|
columns,
|
columnChecks,
|
data,
|
loading,
|
pagination,
|
getData,
|
replaceSearchParams,
|
resetSearchParams,
|
handleSizeChange,
|
handleCurrentChange,
|
refreshData,
|
refreshRemove,
|
refreshUpdate
|
} = useTable({
|
core: {
|
apiFn: fetchWaitPakinPage,
|
apiParams: buildWaitPakinPageQueryParams(searchForm.value),
|
paginationKey: getWaitPakinPaginationKey(),
|
columnsFactory: () =>
|
createWaitPakinTableColumns({
|
handleView: openDetail,
|
handleDelete,
|
handleGenerateTask: openGenerateTaskDialog,
|
canDelete: true,
|
canGenerateTask
|
})
|
},
|
transform: {
|
dataTransformer: (records) => {
|
if (!Array.isArray(records)) {
|
return []
|
}
|
return records.map((item) => normalizeWaitPakinListRow(item))
|
}
|
}
|
})
|
|
const generatableSelectedRows = computed(() =>
|
selectedRows.value.filter((row) => canGenerateTask(row))
|
)
|
|
function handleSelectionChange(rows) {
|
selectedRows.value = Array.isArray(rows) ? rows : []
|
}
|
|
async function loadDetailItems(pakinId) {
|
detailItemsLoading.value = true
|
try {
|
const response = await guardRequestWithMessage(
|
fetchWaitPakinItemPage({ pakinId, current: 1, pageSize: 200 }),
|
{ records: [] },
|
{ timeoutMessage: '组托明细加载超时,已停止等待' }
|
)
|
detailItemRows.value = defaultResponseAdapter(response).records.map((item) =>
|
normalizeWaitPakinItemRow(item)
|
)
|
} finally {
|
detailItemsLoading.value = false
|
}
|
}
|
|
async function openDetail(row) {
|
detailDrawerVisible.value = true
|
detailLoading.value = true
|
detailItemRows.value = []
|
try {
|
const detail = await guardRequestWithMessage(
|
fetchWaitPakinDetail(row.id),
|
{},
|
{ timeoutMessage: '组托单详情加载超时,已停止等待' }
|
)
|
detailData.value = normalizeWaitPakinDetailRecord(detail)
|
await loadDetailItems(row.id)
|
} catch (error) {
|
detailDrawerVisible.value = false
|
detailData.value = {}
|
detailItemRows.value = []
|
ElMessage.error(error?.message || '获取组托单详情失败')
|
} finally {
|
detailLoading.value = false
|
}
|
}
|
|
async function handleDelete(row) {
|
try {
|
await ElMessageBox.confirm(
|
`确定要删除组托单「${row.code || row.barcode || row.id}」吗?`,
|
'删除确认',
|
{
|
confirmButtonText: '确定',
|
cancelButtonText: '取消',
|
type: 'warning'
|
}
|
)
|
await fetchDeleteWaitPakin(row.id)
|
ElMessage.success('删除成功')
|
await refreshRemove()
|
} catch (error) {
|
if (error !== 'cancel') {
|
ElMessage.error(error?.message || '删除失败')
|
}
|
}
|
}
|
|
function openGenerateTaskDialog(rows) {
|
if (!Array.isArray(rows) || rows.length === 0) {
|
ElMessage.warning('请先选择可生成任务的组托单')
|
return
|
}
|
const validRows = rows.filter((row) => canGenerateTask(row))
|
if (validRows.length === 0) {
|
ElMessage.warning('仅“入库中”的组托单支持生成任务')
|
return
|
}
|
taskSourceRows.value = validRows
|
siteDialogVisible.value = true
|
}
|
|
async function handleSelectSite(siteRow) {
|
try {
|
await fetchMergeWaitPakinTasks(buildWaitPakinMergePayload(taskSourceRows.value, siteRow.id))
|
ElMessage.success('生成任务成功')
|
siteDialogVisible.value = false
|
taskSourceRows.value = []
|
selectedRows.value = []
|
await refreshUpdate()
|
} catch (error) {
|
ElMessage.error(error?.message || '生成任务失败')
|
}
|
}
|
|
const buildPreviewDialogMeta = (rows) => {
|
const now = new Date()
|
return {
|
reportDate: now.toLocaleDateString('zh-CN'),
|
printedAt: now.toLocaleString('zh-CN', { hour12: false }),
|
operator: userStore.getUserInfo?.name || userStore.getUserInfo?.username || '',
|
count: rows.length
|
}
|
}
|
|
const resolvePrintRecords = async (payload) => {
|
const response =
|
Array.isArray(payload?.ids) && payload.ids.length > 0
|
? await fetchWaitPakinMany(payload.ids)
|
: await fetchWaitPakinPage({
|
...reportQueryParams.value,
|
current: 1,
|
pageSize:
|
Number(pagination.total) > 0
|
? Number(pagination.total)
|
: Number(payload?.pageSize) || 20
|
})
|
return defaultResponseAdapter(response).records
|
}
|
|
const {
|
previewVisible,
|
previewRows,
|
previewMeta,
|
handlePreviewVisibleChange,
|
handleExport,
|
handlePrint
|
} = usePrintExportPage({
|
downloadFileName: 'wait-pakin.xlsx',
|
requestExport: (payload) =>
|
fetchExportWaitPakinReport(payload, {
|
headers: {
|
Authorization: userStore.accessToken || ''
|
}
|
}),
|
resolvePrintRecords,
|
buildPreviewRows: (records) => buildWaitPakinPrintRows(records),
|
buildPreviewMeta: buildPreviewDialogMeta
|
})
|
|
const resolvedPreviewMeta = computed(() =>
|
buildWaitPakinReportMeta({
|
previewMeta: previewMeta.value,
|
count: previewRows.value.length,
|
orientation:
|
previewMeta.value?.reportStyle?.orientation || WAIT_PAKIN_REPORT_STYLE.orientation
|
})
|
)
|
|
function handleSearch(params) {
|
replaceSearchParams(buildWaitPakinSearchParams(params))
|
getData()
|
}
|
|
function handleReset() {
|
Object.assign(searchForm.value, createWaitPakinSearchState())
|
resetSearchParams()
|
}
|
</script>
|