Release/test #11
@@ -43,6 +43,12 @@ export async function listOverseasLocations(warehouseId: string, query: Overseas
|
||||
return normalizeOverseasPage(unwrapApiResponse(response))
|
||||
}
|
||||
|
||||
export async function listAllOverseasLocations(warehouseIds: string[], query: OverseasLocationQuery = {}) {
|
||||
const results = await Promise.all(warehouseIds.map((warehouseId) => listOverseasLocations(warehouseId, { ...query, page: 1, size: 200 })))
|
||||
const records = results.flatMap((result) => result.records)
|
||||
return { records, total: records.length }
|
||||
}
|
||||
|
||||
export async function createOverseasLocation(warehouseId: string, payload: SaveOverseasLocationPayload) {
|
||||
const response = await overseasHttp.post<ApiEnvelope<string>, ApiEnvelope<string>>(base(warehouseId), payload)
|
||||
return unwrapApiResponse(response)
|
||||
|
||||
@@ -12,7 +12,7 @@ import QueryToolbar, { type QueryField, type QueryModel } from '@/shared/compone
|
||||
import { getApiErrorDetails } from '@/shared/api/http'
|
||||
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
||||
import { listReceivingOverseasWarehouses, type ReceivingOverseasWarehouseDto } from '../api/pallets'
|
||||
import { changeOverseasLocationStatus, createOverseasLocation, deleteOverseasLocation, listOverseasLocations, updateOverseasLocation, type OverseasLocationDto, type OverseasLocationStatus, type OverseasLocationType } from '../api/locations'
|
||||
import { changeOverseasLocationStatus, createOverseasLocation, deleteOverseasLocation, listAllOverseasLocations, listOverseasLocations, updateOverseasLocation, type OverseasLocationDto, type OverseasLocationStatus, type OverseasLocationType } from '../api/locations'
|
||||
import { overseasWarehousePermissions as permission } from '../permissions'
|
||||
|
||||
const access = useAccessPreviewStore(); const warehouses = ref<ReceivingOverseasWarehouseDto[]>([]); const rows = ref<OverseasLocationDto[]>([])
|
||||
@@ -28,13 +28,14 @@ const queryFields = computed<QueryField[]>(() => [
|
||||
const columns: DataTableColumn[] = [{ key: 'actions', label: '操作', width: 220, fixed: 'left', slotName: 'actions' }, { key: 'locationCode', prop: 'locationCode', label: '库位编码', minWidth: 180 }, { key: 'warehouse', label: '海外仓', minWidth: 190, slotName: 'warehouse' }, { key: 'zone', label: '库区', minWidth: 150, slotName: 'zone' }, { key: 'locationType', label: '类型', width: 130, slotName: 'locationType' }, { key: 'status', label: '状态', width: 110, slotName: 'status' }, { key: 'occupiedPalletCount', prop: 'occupiedPalletCount', label: '托盘数', width: 100, align: 'right' }, { key: 'remark', prop: 'remark', label: '备注', minWidth: 180 }]
|
||||
const typeLabels: Record<OverseasLocationType, string> = { VIRTUAL: '虚拟收货位', NORMAL: '普通库位', SHARED_OVERFLOW: 'A0共享溢出位' }; const statusLabels: Record<OverseasLocationStatus, string> = { ACTIVE: '可用', IDLE: '空闲', OCCUPIED: '已占用', INACTIVE: '已停用', DISABLED: '已停用' }
|
||||
const selectedWarehouse = computed(() => warehouses.value.find(item => String(item.id) === String(form.warehouseId)))
|
||||
const warehouseNameById = computed(() => new Map(warehouses.value.map(item => [String(item.id), item.warehouseName])))
|
||||
function isA0(row: OverseasLocationDto) { return row.sharedOverflow || row.locationType === 'SHARED_OVERFLOW' || row.locationCode.toUpperCase() === 'A0' }
|
||||
function canEdit(row: OverseasLocationDto) { return access.can(permission.locationEdit) && !isA0(row) && row.locationType === 'NORMAL' && (row.status === 'ACTIVE' || row.status === 'INACTIVE' || row.status === 'DISABLED') }
|
||||
function canStatus(row: OverseasLocationDto) { return access.can(permission.locationEdit) && !isA0(row) && row.locationType === 'NORMAL' && (row.status === 'ACTIVE' || row.status === 'INACTIVE' || row.status === 'DISABLED') }
|
||||
function canDelete(row: OverseasLocationDto) { return access.can(permission.locationDelete) && !isA0(row) && row.status === 'DISABLED' && Number(row.occupiedPalletCount || 0) === 0 }
|
||||
function actionError(error: unknown, fallback: string) { ElMessage.error(getApiErrorDetails(error, fallback).message) }
|
||||
async function loadWarehouses() { contextLoading.value = true; try { warehouses.value = (await listReceivingOverseasWarehouses()).records } catch (error) { actionError(error, '海外仓列表加载失败') } finally { contextLoading.value = false } }
|
||||
async function load(model: QueryModel = filters.value) { Object.assign(filters.value, model); const warehouseId = String(filters.value.warehouseId || ''); if (!warehouseId) { rows.value = []; total.value = 0; return }; loading.value = true; loadError.value = null; try { const result = await listOverseasLocations(warehouseId, { page: page.value, size: pageSize.value, zoneCode: String(filters.value.zoneCode || '').trim() || undefined, keyword: String(filters.value.locationCode || '').trim() || undefined, locationType: String(filters.value.locationType || '') as OverseasLocationType | '', status: (String(filters.value.status || '') === 'DISABLED' ? 'INACTIVE' : String(filters.value.status || '')) as OverseasLocationStatus | '' }); rows.value = result.records; total.value = result.total } catch (error) { rows.value = []; total.value = 0; loadError.value = getApiErrorDetails(error, '海外库位加载失败') } finally { loading.value = false } }
|
||||
async function loadWarehouses() { contextLoading.value = true; try { warehouses.value = (await listReceivingOverseasWarehouses()).records; await load() } catch (error) { actionError(error, '海外仓列表加载失败') } finally { contextLoading.value = false } }
|
||||
async function load(model: QueryModel = filters.value) { Object.assign(filters.value, model); if (!warehouses.value.length) { rows.value = []; total.value = 0; return }; loading.value = true; loadError.value = null; try { const selectedWarehouseId = String(filters.value.warehouseId || ''); const result = await (selectedWarehouseId ? listOverseasLocations(selectedWarehouseId, { page: 1, size: 200, zoneCode: String(filters.value.zoneCode || '').trim() || undefined, keyword: String(filters.value.locationCode || '').trim() || undefined, locationType: String(filters.value.locationType || '') as OverseasLocationType | '', status: (String(filters.value.status || '') === 'DISABLED' ? 'INACTIVE' : String(filters.value.status || '')) as OverseasLocationStatus | '' }) : listAllOverseasLocations(warehouses.value.map(item => String(item.id)), { zoneCode: String(filters.value.zoneCode || '').trim() || undefined, keyword: String(filters.value.locationCode || '').trim() || undefined, locationType: String(filters.value.locationType || '') as OverseasLocationType | '', status: (String(filters.value.status || '') === 'DISABLED' ? 'INACTIVE' : String(filters.value.status || '')) as OverseasLocationStatus | '' })); const records = result.records.map(row => ({ ...row, warehouseName: row.warehouseName || warehouseNameById.value.get(String(row.warehouseId)) || null })); total.value = records.length; const start = (page.value - 1) * pageSize.value; rows.value = records.slice(start, start + pageSize.value) } catch (error) { rows.value = []; total.value = 0; loadError.value = getApiErrorDetails(error, '海外库位加载失败') } finally { loading.value = false } }
|
||||
function search(model: QueryModel) { page.value = 1; void load(model) }
|
||||
function resetForm() { Object.assign(form, { warehouseId: String(filters.value.warehouseId || ''), zoneCode: '', zoneName: '', locationCode: '', locationName: '', locationType: 'NORMAL', maxWeightKg: null, status: 'ACTIVE', remark: '' }); editing.value = null }
|
||||
function openCreate() { resetForm(); editorOpen.value = true }
|
||||
@@ -45,6 +46,6 @@ async function remove(row: OverseasLocationDto) { if (!canDelete(row)) return; t
|
||||
onMounted(() => { void loadWarehouses() })
|
||||
</script>
|
||||
|
||||
<template><AdminPage class="admin-density"><ListPageLayout aria-label="海外仓库位管理"><template #toolbar><QueryToolbar v-model="filters" :fields="queryFields" ariaLabel="海外库位筛选" preference-key="overseas-locations-query" @search="search" @reset="page = 1; load()" /></template><template #state><LoadErrorState v-if="loadError" title="海外库位加载失败" :message="loadError.message" :loading="loading" @retry="load" /><el-alert v-else-if="!filters.warehouseId" title="请选择海外仓后查询库位" type="info" :closable="false" /></template><section v-if="!loadError && filters.warehouseId" class="surface-panel table-panel"><DataTable v-model:page="page" v-model:page-size="pageSize" :data="rows" :columns="columns" :loading="loading" :total="total" row-key="id" empty-text="暂无库位" @update:page="load()" @update:page-size="page = 1; load()"><template #toolbar-actions><el-button v-if="access.can(permission.locationCreate)" type="primary" @click="openCreate"><Plus :size="16" />新增库位</el-button></template><template #actions="{ row }"><div class="density-actions"><el-button v-if="canEdit(row as OverseasLocationDto)" size="small" class="density-action" @click="openEdit(row as OverseasLocationDto)">编辑</el-button><el-button v-if="canStatus(row as OverseasLocationDto)" size="small" class="density-action" :data-tone="(row as OverseasLocationDto).status === 'DISABLED' ? 'success' : 'danger'" @click="toggleStatus(row as OverseasLocationDto)">{{ (row as OverseasLocationDto).status === 'DISABLED' ? '启用' : '停用' }}</el-button><el-button v-if="canDelete(row as OverseasLocationDto)" size="small" class="density-action" data-tone="danger" @click="remove(row as OverseasLocationDto)">删除</el-button></div></template><template #warehouse="{ row }">{{ (row as OverseasLocationDto).warehouseName || selectedWarehouse?.warehouseName || '—' }}</template><template #zone="{ row }">{{ (row as OverseasLocationDto).zoneName || '—' }}({{ (row as OverseasLocationDto).zoneCode }})</template><template #locationType="{ row }"><el-tag :type="isA0(row as OverseasLocationDto) ? 'warning' : 'info'">{{ typeLabels[(row as OverseasLocationDto).locationType] }}</el-tag></template><template #status="{ row }"><el-tag>{{ statusLabels[(row as OverseasLocationDto).status] }}</el-tag></template><template #pagination-left>共 {{ total }} 个库位</template></DataTable></section></ListPageLayout><AppDialog v-model="editorOpen" :title="editing ? '编辑海外库位' : '新增海外库位'" width="min(560px, calc(100vw - 28px))" :confirm-loading="saving" :show-confirm="false"><el-form label-position="top" :disabled="saving"><div class="editor-grid"><el-form-item label="海外仓"><el-select v-model="form.warehouseId" disabled><el-option v-if="selectedWarehouse" :value="selectedWarehouse.id" :label="`${selectedWarehouse.warehouseName}(${selectedWarehouse.warehouseCode})`" /></el-select></el-form-item><el-form-item label="库位编码"><el-input v-model="form.locationCode" maxlength="50" /></el-form-item><el-form-item label="库区编码"><el-input v-model="form.zoneCode" maxlength="50" /></el-form-item><el-form-item label="库区名称"><el-input v-model="form.zoneName" maxlength="100" /></el-form-item><el-form-item label="库位名称"><el-input v-model="form.locationName" maxlength="100" /></el-form-item><el-form-item label="最大承重(kg)"><el-input-number v-model="form.maxWeightKg" :min="0" :precision="3" controls-position="right" /></el-form-item></div><el-form-item label="备注"><el-input v-model="form.remark" maxlength="500" /></el-form-item></el-form><template #footer><el-button :disabled="saving" @click="editorOpen = false">取消</el-button><el-button type="primary" :loading="saving" @click="save"><Save :size="16" />保存库位</el-button></template></AppDialog></AdminPage></template>
|
||||
<template><AdminPage class="admin-density"><ListPageLayout aria-label="海外仓库位管理"><template #toolbar><QueryToolbar v-model="filters" :fields="queryFields" ariaLabel="海外库位筛选" preference-key="overseas-locations-query" @search="search" @reset="page = 1; load()" /></template><template #state><LoadErrorState v-if="loadError" title="海外库位加载失败" :message="loadError.message" :loading="loading" @retry="load" /></template><section v-if="!loadError" class="surface-panel table-panel"><DataTable v-model:page="page" v-model:page-size="pageSize" :data="rows" :columns="columns" :loading="loading" :total="total" row-key="id" empty-text="暂无库位" @update:page="load()" @update:page-size="page = 1; load()"><template #toolbar-actions><el-button v-if="access.can(permission.locationCreate)" type="primary" @click="openCreate"><Plus :size="16" />新增库位</el-button></template><template #actions="{ row }"><div class="density-actions"><el-button v-if="canEdit(row as OverseasLocationDto)" size="small" class="density-action" @click="openEdit(row as OverseasLocationDto)">编辑</el-button><el-button v-if="canStatus(row as OverseasLocationDto)" size="small" class="density-action" :data-tone="(row as OverseasLocationDto).status === 'DISABLED' || (row as OverseasLocationDto).status === 'INACTIVE' ? 'success' : 'danger'" @click="toggleStatus(row as OverseasLocationDto)">{{ (row as OverseasLocationDto).status === 'DISABLED' || (row as OverseasLocationDto).status === 'INACTIVE' ? '启用' : '停用' }}</el-button><el-button v-if="canDelete(row as OverseasLocationDto)" size="small" class="density-action" data-tone="danger" @click="remove(row as OverseasLocationDto)">删除</el-button></div></template><template #warehouse="{ row }">{{ (row as OverseasLocationDto).warehouseName || warehouseNameById.get(String((row as OverseasLocationDto).warehouseId)) || '—' }}</template><template #zone="{ row }">{{ (row as OverseasLocationDto).zoneName || '—' }}({{ (row as OverseasLocationDto).zoneCode }})</template><template #locationType="{ row }"><el-tag :type="isA0(row as OverseasLocationDto) ? 'warning' : 'info'">{{ typeLabels[(row as OverseasLocationDto).locationType] || '普通库位' }}</el-tag></template><template #status="{ row }"><el-tag>{{ statusLabels[(row as OverseasLocationDto).status] || '—' }}</el-tag></template><template #pagination-left>共 {{ total }} 个库位</template></DataTable></section></ListPageLayout><AppDialog v-model="editorOpen" :title="editing ? '编辑海外库位' : '新增海外库位'" width="min(560px, calc(100vw - 28px))" :confirm-loading="saving" :show-confirm="false"><el-form label-position="top" :disabled="saving"><div class="editor-grid"><el-form-item label="海外仓"><el-select v-model="form.warehouseId" :disabled="Boolean(editing)"><el-option v-for="item in warehouses" :key="item.id" :value="item.id" :label="`${item.warehouseName}(${item.warehouseCode})`" /></el-select></el-form-item><el-form-item label="库位编码"><el-input v-model="form.locationCode" maxlength="50" /></el-form-item><el-form-item label="库区编码"><el-input v-model="form.zoneCode" maxlength="50" /></el-form-item><el-form-item label="库区名称"><el-input v-model="form.zoneName" maxlength="100" /></el-form-item><el-form-item label="库位名称"><el-input v-model="form.locationName" maxlength="100" /></el-form-item><el-form-item label="最大承重(kg)"><el-input-number v-model="form.maxWeightKg" :min="0" :precision="3" controls-position="right" /></el-form-item></div><el-form-item label="备注"><el-input v-model="form.remark" maxlength="500" /></el-form-item></el-form><template #footer><el-button :disabled="saving" @click="editorOpen = false">取消</el-button><el-button type="primary" :loading="saving" @click="save"><Save :size="16" />保存库位</el-button></template></AppDialog></AdminPage></template>
|
||||
|
||||
<style scoped>.editor-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0 14px}.editor-grid :deep(.el-select),.editor-grid :deep(.el-input-number){width:100%}@media(max-width:720px){.editor-grid{grid-template-columns:1fr}}</style>
|
||||
|
||||
Reference in New Issue
Block a user