| | |
| | | <ElCard class="art-table-card"> |
| | | <ArtTableHeader v-model:columns="columnChecks" :loading="loading" @refresh="refreshData"> |
| | | <template #left> |
| | | <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 wrap> |
| | | <ElUpload |
| | | v-auth="'update'" |
| | | :auto-upload="false" |
| | | :show-file-list="false" |
| | | accept=".xlsx,.xls" |
| | | @change="handleImportFileChange" |
| | | > |
| | | <ElButton :loading="importing">{{ |
| | | t('pages.orders.delivery.buttons.import') |
| | | }}</ElButton> |
| | | </ElUpload> |
| | | <ElButton |
| | | v-auth="'update'" |
| | | :loading="templateDownloading" |
| | | @click="handleDownloadTemplate" |
| | | > |
| | | {{ t('pages.orders.delivery.buttons.downloadTemplate') }} |
| | | </ElButton> |
| | | <ElButton |
| | | v-auth="'delete'" |
| | | type="danger" |
| | | plain |
| | | :disabled="selectedRows.length === 0" |
| | | @click="handleBatchDelete" |
| | | > |
| | | {{ t('common.actions.batchDelete') }} |
| | | </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> |
| | | |
| | |
| | | @size-change="handleDetailSizeChange" |
| | | @current-change="handleDetailCurrentChange" |
| | | /> |
| | | |
| | | <DeliveryManageDialog |
| | | v-model:visible="manageDialogVisible" |
| | | :delivery="manageDeliveryData" |
| | | :can-add="hasAuth('update')" |
| | | :can-edit="hasAuth('update')" |
| | | :can-delete="hasAuth('delete')" |
| | | @changed="handleManageChanged" |
| | | /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { computed, reactive, ref } from 'vue' |
| | | import { useRouter } from 'vue-router' |
| | | import { useI18n } from 'vue-i18n' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { useUserStore } from '@/store/modules/user' |
| | | import { useAuth } from '@/hooks/core/useAuth' |
| | | import { useTable } from '@/hooks/core/useTable' |
| | | import { usePrintExportPage } from '@/views/system/common/usePrintExportPage' |
| | | import ListExportPrint from '@/components/biz/list-export-print/index.vue' |
| | |
| | | createDeliverySearchState, |
| | | getDeliveryReportTitle, |
| | | getDeliveryPaginationKey, |
| | | getDeliveryStatusOptions, |
| | | getDeliveryExceStatusOptions, |
| | | normalizeDeliveryItemRow, |
| | | normalizeDeliveryRow |
| | | } from './deliveryPage.helpers.js' |
| | | import { |
| | | fetchDeleteDelivery, |
| | | fetchDeleteDeliveryMany, |
| | | fetchDeliveryItemPage, |
| | | fetchDeliveryPage, |
| | | fetchDownloadDeliveryTemplate, |
| | | fetchExportDeliveryReport, |
| | | fetchGetDeliveryDetail, |
| | | fetchGetDeliveryMany |
| | | } from '@/api/delivery' |
| | | import { fetchImportDelivery } from '@/api/delivery' |
| | | import DeliveryDetailDrawer from './modules/delivery-detail-drawer.vue' |
| | | import DeliveryManageDialog from './modules/delivery-manage-dialog.vue' |
| | | import { createDeliveryTableColumns } from './deliveryTable.columns.js' |
| | | |
| | | defineOptions({ name: 'Delivery' }) |
| | | |
| | | const userStore = useUserStore() |
| | | const router = useRouter() |
| | | const { hasAuth } = useAuth() |
| | | const { t } = useI18n() |
| | | const reportTitle = computed(() => getDeliveryReportTitle(t)) |
| | | const searchForm = ref(createDeliverySearchState()) |
| | |
| | | const detailData = ref({}) |
| | | const detailItemRows = ref([]) |
| | | const activeDeliveryId = ref(null) |
| | | const importing = ref(false) |
| | | const templateDownloading = ref(false) |
| | | const manageDialogVisible = ref(false) |
| | | const manageDeliveryData = ref({}) |
| | | const detailItemPagination = reactive({ |
| | | current: 1, |
| | | size: 20, |
| | |
| | | props: { |
| | | clearable: true, |
| | | placeholder: t('pages.orders.delivery.placeholder.condition') |
| | | } |
| | | }, |
| | | { |
| | | label: t('pages.orders.delivery.search.timeStart'), |
| | | key: 'timeStart', |
| | | type: 'date', |
| | | props: { |
| | | clearable: true, |
| | | valueFormat: 'YYYY-MM-DD', |
| | | placeholder: t('pages.orders.delivery.placeholder.timeStart') |
| | | } |
| | | }, |
| | | { |
| | | label: t('pages.orders.delivery.search.timeEnd'), |
| | | key: 'timeEnd', |
| | | type: 'date', |
| | | props: { |
| | | clearable: true, |
| | | valueFormat: 'YYYY-MM-DD', |
| | | placeholder: t('pages.orders.delivery.placeholder.timeEnd') |
| | | } |
| | | }, |
| | | { |
| | |
| | | } |
| | | }, |
| | | { |
| | | label: t('pages.orders.delivery.search.exceStatus'), |
| | | key: 'exceStatus', |
| | | label: t('pages.orders.delivery.search.anfme'), |
| | | key: 'anfme', |
| | | type: 'number', |
| | | props: { |
| | | min: 0, |
| | | precision: 2, |
| | | controlsPosition: 'right', |
| | | placeholder: t('pages.orders.delivery.placeholder.anfme') |
| | | } |
| | | }, |
| | | { |
| | | label: t('pages.orders.delivery.search.qty'), |
| | | key: 'qty', |
| | | type: 'number', |
| | | props: { |
| | | min: 0, |
| | | precision: 2, |
| | | controlsPosition: 'right', |
| | | placeholder: t('pages.orders.delivery.placeholder.qty') |
| | | } |
| | | }, |
| | | { |
| | | label: t('pages.orders.delivery.search.workQty'), |
| | | key: 'workQty', |
| | | type: 'number', |
| | | props: { |
| | | min: 0, |
| | | precision: 2, |
| | | controlsPosition: 'right', |
| | | placeholder: t('pages.orders.delivery.placeholder.workQty') |
| | | } |
| | | }, |
| | | { |
| | | label: t('pages.orders.delivery.search.platCode'), |
| | | key: 'platCode', |
| | | type: 'input', |
| | | props: { |
| | | clearable: true, |
| | | placeholder: t('pages.orders.delivery.placeholder.platCode') |
| | | } |
| | | }, |
| | | { |
| | | label: t('pages.orders.delivery.search.startTime'), |
| | | key: 'startTime', |
| | | type: 'date', |
| | | props: { |
| | | clearable: true, |
| | | valueFormat: 'YYYY-MM-DD', |
| | | placeholder: t('pages.orders.delivery.placeholder.startTime') |
| | | } |
| | | }, |
| | | { |
| | | label: t('pages.orders.delivery.search.endTime'), |
| | | key: 'endTime', |
| | | type: 'date', |
| | | props: { |
| | | clearable: true, |
| | | valueFormat: 'YYYY-MM-DD', |
| | | placeholder: t('pages.orders.delivery.placeholder.endTime') |
| | | } |
| | | }, |
| | | { |
| | | label: t('table.status'), |
| | | key: 'status', |
| | | type: 'select', |
| | | props: { |
| | | clearable: true, |
| | | options: getDeliveryStatusOptions(t), |
| | | placeholder: t('pages.orders.delivery.placeholder.status') |
| | | } |
| | | }, |
| | | { |
| | | label: t('pages.orders.delivery.search.exceStatus'), |
| | | key: 'exceStatus', |
| | | type: 'select', |
| | | props: { |
| | | clearable: true, |
| | | options: getDeliveryExceStatusOptions(t), |
| | | placeholder: t('pages.orders.delivery.placeholder.exceStatus') |
| | | } |
| | | }, |
| | |
| | | { timeoutMessage: t('pages.orders.delivery.messages.itemsTimeout') } |
| | | ) |
| | | const normalizedResponse = defaultResponseAdapter(response) |
| | | detailItemRows.value = normalizedResponse.records.map((item) => normalizeDeliveryItemRow(item, t)) |
| | | detailItemRows.value = normalizedResponse.records.map((item) => |
| | | normalizeDeliveryItemRow(item, t) |
| | | ) |
| | | detailItemPagination.total = Number(normalizedResponse.total || 0) |
| | | detailItemPagination.current = Number(normalizedResponse.current || detailItemPagination.current || 1) |
| | | detailItemPagination.current = Number( |
| | | normalizedResponse.current || detailItemPagination.current || 1 |
| | | ) |
| | | detailItemPagination.size = Number(normalizedResponse.size || detailItemPagination.size || 20) |
| | | } finally { |
| | | detailItemsLoading.value = false |
| | |
| | | } |
| | | |
| | | async function handleDelete(row) { |
| | | try { |
| | | await ElMessageBox.confirm( |
| | | t('crud.confirm.deleteMessage', { |
| | | entity: t('pages.orders.delivery.entity'), |
| | | label: row.code || row.id |
| | | }), |
| | | t('crud.confirm.deleteTitle'), |
| | | { |
| | | confirmButtonText: t('common.confirm'), |
| | | cancelButtonText: t('common.cancel'), |
| | | type: 'warning' |
| | | } |
| | | ) |
| | | await fetchDeleteDelivery(row.id) |
| | | ElMessage.success(t('crud.messages.deleteSuccess')) |
| | | await refreshRemove() |
| | | } catch (error) { |
| | | if (error !== 'cancel') { |
| | | ElMessage.error(error?.message || t('crud.messages.deleteFailed')) |
| | | await ElMessageBox.confirm( |
| | | t('crud.confirm.deleteMessage', { |
| | | entity: t('pages.orders.delivery.entity'), |
| | | label: row.code || row.id |
| | | }), |
| | | t('crud.confirm.deleteTitle'), |
| | | { |
| | | confirmButtonText: t('common.confirm'), |
| | | cancelButtonText: t('common.cancel'), |
| | | type: 'warning' |
| | | } |
| | | } |
| | | ) |
| | | await fetchDeleteDeliveryMany([row.id]) |
| | | ElMessage.success(t('crud.messages.deleteSuccess')) |
| | | await refreshRemove() |
| | | } |
| | | |
| | | function handleTableActionClick(action, row) { |
| | | function openManageDialog(row) { |
| | | manageDeliveryData.value = normalizeDeliveryRow(row, t) |
| | | manageDialogVisible.value = true |
| | | } |
| | | |
| | | async function handleBatchDelete() { |
| | | if (!selectedRows.value.length) { |
| | | return |
| | | } |
| | | await ElMessageBox.confirm( |
| | | t('crud.confirm.batchDeleteMessage', { |
| | | count: selectedRows.value.length, |
| | | entity: t('pages.orders.delivery.entity') |
| | | }), |
| | | t('crud.confirm.batchDeleteTitle'), |
| | | { |
| | | confirmButtonText: t('common.confirm'), |
| | | cancelButtonText: t('common.cancel'), |
| | | type: 'warning' |
| | | } |
| | | ) |
| | | await fetchDeleteDeliveryMany(selectedRows.value.map((item) => item.id)) |
| | | ElMessage.success(t('crud.messages.batchDeleteSuccess')) |
| | | selectedRows.value = [] |
| | | await refreshData() |
| | | } |
| | | |
| | | async function handleTableActionClick(action, row) { |
| | | if (!action) { |
| | | return |
| | | } |
| | | if (action.key === 'view') { |
| | | openDetail(row) |
| | | await openDetail(row) |
| | | return |
| | | } |
| | | if (action.key === 'edit') { |
| | | openManageDialog(row) |
| | | return |
| | | } |
| | | if (action.key === 'items') { |
| | | if (!row?.id) { |
| | | return |
| | | } |
| | | router.push({ |
| | | path: '/orders/delivery-item', |
| | | query: { |
| | | deliveryId: String(row.id) |
| | | } |
| | | }) |
| | | openManageDialog(row) |
| | | return |
| | | } |
| | | if (action.key === 'delete') { |
| | | handleDelete(row) |
| | | try { |
| | | await handleDelete(row) |
| | | } catch (error) { |
| | | if (error === 'cancel' || error?.message === 'cancel') { |
| | | return |
| | | } |
| | | ElMessage.error(error?.message || t('crud.messages.deleteFailed')) |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | pageSize: 20 |
| | | }), |
| | | paginationKey: getDeliveryPaginationKey(), |
| | | columnsFactory: () => createDeliveryTableColumns({ handleActionClick: handleTableActionClick }) |
| | | columnsFactory: () => |
| | | createDeliveryTableColumns({ |
| | | handleActionClick: handleTableActionClick, |
| | | canEdit: hasAuth('update'), |
| | | canDelete: hasAuth('delete') |
| | | }) |
| | | }, |
| | | transform: { |
| | | dataTransformer: (records) => (Array.isArray(records) ? records.map((item) => normalizeDeliveryRow(item, t)) : []) |
| | | dataTransformer: (records) => |
| | | Array.isArray(records) ? records.map((item) => normalizeDeliveryRow(item, t)) : [] |
| | | } |
| | | }) |
| | | |
| | |
| | | resetSearchParams() |
| | | } |
| | | |
| | | async function handleManageChanged() { |
| | | await refreshData() |
| | | if (detailDrawerVisible.value && activeDeliveryId.value) { |
| | | await loadDetailItems(activeDeliveryId.value) |
| | | } |
| | | } |
| | | |
| | | async function handleImportFileChange(uploadFile) { |
| | | if (!uploadFile?.raw) { |
| | | return |
| | | } |
| | | importing.value = true |
| | | try { |
| | | await fetchImportDelivery(uploadFile.raw) |
| | | ElMessage.success(t('pages.orders.delivery.messages.importSuccess')) |
| | | await refreshData() |
| | | } catch (error) { |
| | | ElMessage.error(error?.message || t('pages.orders.delivery.messages.importFailed')) |
| | | } finally { |
| | | importing.value = false |
| | | } |
| | | } |
| | | |
| | | async function downloadFile(response, fallbackName) { |
| | | const blob = await response.blob() |
| | | if (!blob || !blob.size) { |
| | | throw new Error(t('pages.orders.delivery.messages.templateDownloadFailed')) |
| | | } |
| | | const disposition = response.headers.get('Content-Disposition') || '' |
| | | const matchedName = |
| | | disposition.match(/filename\*=UTF-8''([^;]+)/i)?.[1] || |
| | | disposition.match(/filename="?([^";]+)"?/i)?.[1] |
| | | const fileName = matchedName ? decodeURIComponent(matchedName) : fallbackName |
| | | const url = URL.createObjectURL(blob) |
| | | const anchor = document.createElement('a') |
| | | anchor.href = url |
| | | anchor.download = fileName |
| | | document.body.appendChild(anchor) |
| | | anchor.click() |
| | | anchor.remove() |
| | | URL.revokeObjectURL(url) |
| | | } |
| | | |
| | | async function handleDownloadTemplate() { |
| | | templateDownloading.value = true |
| | | try { |
| | | const response = await fetchDownloadDeliveryTemplate( |
| | | {}, |
| | | { |
| | | headers: { |
| | | Authorization: userStore.accessToken || '' |
| | | } |
| | | } |
| | | ) |
| | | await downloadFile(response, 'delivery-template.xlsx') |
| | | ElMessage.success(t('pages.orders.delivery.messages.templateDownloadSuccess')) |
| | | } catch (error) { |
| | | ElMessage.error(error?.message || t('pages.orders.delivery.messages.templateDownloadFailed')) |
| | | } finally { |
| | | templateDownloading.value = false |
| | | } |
| | | } |
| | | |
| | | const resolvePrintRecords = async (payload) => { |
| | | if (Array.isArray(payload?.ids) && payload.ids.length > 0) { |
| | | return defaultResponseAdapter(await fetchGetDeliveryMany(payload.ids)).records |
| | |
| | | await fetchDeliveryPage({ |
| | | ...reportQueryParams.value, |
| | | current: 1, |
| | | pageSize: Number(pagination.total) > 0 ? Number(pagination.total) : Number(payload?.pageSize) || 20 |
| | | pageSize: |
| | | Number(pagination.total) > 0 ? Number(pagination.total) : Number(payload?.pageSize) || 20 |
| | | }) |
| | | ).records |
| | | } |