feat: add warehouse areas page
| New file |
| | |
| | | import request from '@/utils/http' |
| | | |
| | | function normalizeText(value) { |
| | | return typeof value === 'string' ? value.trim() : value |
| | | } |
| | | |
| | | function normalizeIds(ids) { |
| | | if (Array.isArray(ids)) { |
| | | return ids |
| | | .map((id) => String(id).trim()) |
| | | .filter(Boolean) |
| | | .join(',') |
| | | } |
| | | if (ids === null || ids === undefined) { |
| | | return '' |
| | | } |
| | | return String(ids).trim() |
| | | } |
| | | |
| | | function filterParams(params = {}, ignoredKeys = []) { |
| | | return Object.fromEntries( |
| | | Object.entries(params) |
| | | .filter(([key, value]) => { |
| | | if (ignoredKeys.includes(key)) return false |
| | | if (value === undefined || value === null) return false |
| | | if (typeof value === 'string' && value.trim() === '') return false |
| | | return true |
| | | }) |
| | | .map(([key, value]) => [key, normalizeText(value)]) |
| | | ) |
| | | } |
| | | |
| | | export function buildWarehouseAreasPageParams(params = {}) { |
| | | return { |
| | | current: params.current || 1, |
| | | pageSize: params.pageSize || params.size || 20, |
| | | ...filterParams(params, ['current', 'pageSize', 'size']) |
| | | } |
| | | } |
| | | |
| | | export function buildWarehouseAreasSavePayload(formData = {}) { |
| | | return { |
| | | ...(formData.id !== undefined && formData.id !== null && formData.id !== '' |
| | | ? { id: Number(formData.id) } |
| | | : {}), |
| | | ...(formData.warehouseId !== undefined && formData.warehouseId !== null && formData.warehouseId !== '' |
| | | ? { warehouseId: Number(formData.warehouseId) } |
| | | : {}), |
| | | code: normalizeText(formData.code) || '', |
| | | name: normalizeText(formData.name) || '', |
| | | type: normalizeText(formData.type) || '', |
| | | ...(formData.shipperId !== undefined && formData.shipperId !== null && formData.shipperId !== '' |
| | | ? { shipperId: Number(formData.shipperId) } |
| | | : {}), |
| | | ...(formData.supplierId !== undefined && formData.supplierId !== null && formData.supplierId !== '' |
| | | ? { supplierId: Number(formData.supplierId) } |
| | | : {}), |
| | | ...(formData.flagMinus !== undefined && formData.flagMinus !== null && formData.flagMinus !== '' |
| | | ? { flagMinus: Number(formData.flagMinus) } |
| | | : {}), |
| | | ...(formData.flagLabelMange !== undefined && formData.flagLabelMange !== null && formData.flagLabelMange !== '' |
| | | ? { flagLabelMange: Number(formData.flagLabelMange) } |
| | | : {}), |
| | | ...(formData.flagMix !== undefined && formData.flagMix !== null && formData.flagMix !== '' |
| | | ? { flagMix: Number(formData.flagMix) } |
| | | : {}), |
| | | status: |
| | | formData.status !== undefined && formData.status !== null && formData.status !== '' |
| | | ? Number(formData.status) |
| | | : 1, |
| | | ...(formData.sort !== undefined && formData.sort !== null && formData.sort !== '' |
| | | ? { sort: Number(formData.sort) } |
| | | : {}), |
| | | memo: normalizeText(formData.memo) || '' |
| | | } |
| | | } |
| | | |
| | | export function buildWarehouseAreasSearchParams(params = {}) { |
| | | const searchParams = { |
| | | condition: normalizeText(params.condition), |
| | | warehouseId: |
| | | params.warehouseId !== undefined && params.warehouseId !== null && params.warehouseId !== '' |
| | | ? Number(params.warehouseId) |
| | | : void 0, |
| | | code: normalizeText(params.code), |
| | | name: normalizeText(params.name), |
| | | type: normalizeText(params.type), |
| | | shipperId: |
| | | params.shipperId !== undefined && params.shipperId !== null && params.shipperId !== '' |
| | | ? Number(params.shipperId) |
| | | : void 0, |
| | | supplierId: |
| | | params.supplierId !== undefined && params.supplierId !== null && params.supplierId !== '' |
| | | ? Number(params.supplierId) |
| | | : void 0, |
| | | status: |
| | | params.status !== undefined && params.status !== null && params.status !== '' |
| | | ? Number(params.status) |
| | | : void 0, |
| | | memo: normalizeText(params.memo) |
| | | } |
| | | |
| | | return Object.fromEntries( |
| | | Object.entries(searchParams).filter(([, value]) => value !== '' && value !== void 0 && value !== null) |
| | | ) |
| | | } |
| | | |
| | | export function fetchWarehouseAreasPage(params = {}) { |
| | | return request.post({ |
| | | url: '/warehouseAreas/page', |
| | | params: buildWarehouseAreasPageParams(params) |
| | | }) |
| | | } |
| | | |
| | | export function fetchWarehouseAreasList() { |
| | | return request.post({ |
| | | url: '/warehouseAreas/list', |
| | | data: {} |
| | | }) |
| | | } |
| | | |
| | | export function fetchWarehouseAreasDetail(id) { |
| | | return request.get({ |
| | | url: `/warehouseAreas/${id}` |
| | | }) |
| | | } |
| | | |
| | | export function fetchWarehouseAreasMany(ids) { |
| | | return request.post({ |
| | | url: `/warehouseAreas/many/${normalizeIds(ids)}` |
| | | }) |
| | | } |
| | | |
| | | export function fetchSaveWarehouseAreas(params = {}) { |
| | | return request.post({ |
| | | url: '/warehouseAreas/save', |
| | | params: buildWarehouseAreasSavePayload(params) |
| | | }) |
| | | } |
| | | |
| | | export function fetchUpdateWarehouseAreas(params = {}) { |
| | | return request.post({ |
| | | url: '/warehouseAreas/update', |
| | | params: buildWarehouseAreasSavePayload(params) |
| | | }) |
| | | } |
| | | |
| | | export function fetchDeleteWarehouseAreas(ids) { |
| | | return request.post({ |
| | | url: `/warehouseAreas/remove/${normalizeIds(ids)}` |
| | | }) |
| | | } |
| | | |
| | | export function fetchWarehouseAreasQuery(condition = '') { |
| | | return request.post({ |
| | | url: '/warehouseAreas/query', |
| | | params: { condition: normalizeText(condition) } |
| | | }) |
| | | } |
| | | |
| | | export function fetchWarehouseList() { |
| | | return request.post({ |
| | | url: '/warehouse/list', |
| | | data: {} |
| | | }) |
| | | } |
| | | |
| | | export function fetchCompanysList() { |
| | | return request.post({ |
| | | url: '/companys/list', |
| | | data: {} |
| | | }) |
| | | } |
| | | |
| | | export async function fetchExportWarehouseAreasReport(payload = {}, options = {}) { |
| | | return fetch(`${import.meta.env.VITE_API_URL}/warehouseAreas/export`, { |
| | | method: 'POST', |
| | | headers: { |
| | | 'Content-Type': 'application/json', |
| | | ...(options.headers || {}) |
| | | }, |
| | | body: JSON.stringify(payload) |
| | | }) |
| | | } |
| | |
| | | fieldsItem: '/system/fields-item', |
| | | whMat: '/basic-info/wh-mat', |
| | | matnr: '/basic-info/wh-mat', |
| | | matnrGroup: '/basic-info/matnr-group', |
| | | basContainer: '/basic-info/bas-container', |
| | | warehouse: '/basic-info/warehouse', |
| | | warehouseAreas: '/basic-info/warehouse-areas', |
| | | warehouseStock: '/stock/warehouse-stock', |
| | | warehouseAreasItem: '/stock/warehouse-areas-item', |
| | | qlyInspect: '/manager/qly-inspect', |
| | |
| | | icon: 'ri:bill-line', |
| | | keepAlive: false |
| | | } |
| | | }, |
| | | { |
| | | path: 'matnr-group', |
| | | name: 'MatnrGroup', |
| | | component: () => import('@views/basic-info/matnr-group/index.vue'), |
| | | meta: { |
| | | title: 'menu.matnrGroup', |
| | | icon: 'ri:git-branch-line', |
| | | keepAlive: false |
| | | } |
| | | }, |
| | | { |
| | | path: 'bas-container', |
| | | name: 'BasContainer', |
| | | component: () => import('@views/basic-info/bas-container/index.vue'), |
| | | meta: { |
| | | title: 'menu.basContainer', |
| | | icon: 'ri:archive-line', |
| | | keepAlive: false |
| | | } |
| | | }, |
| | | { |
| | | path: 'warehouse', |
| | | name: 'Warehouse', |
| | | component: () => import('@views/basic-info/warehouse/index.vue'), |
| | | meta: { |
| | | title: 'menu.warehouse', |
| | | icon: 'ri:store-2-line', |
| | | keepAlive: false |
| | | } |
| | | }, |
| | | { |
| | | path: 'warehouse-areas', |
| | | name: 'WarehouseAreas', |
| | | component: () => import('@views/basic-info/warehouse-areas/index.vue'), |
| | | meta: { |
| | | title: 'menu.warehouseAreas', |
| | | icon: 'ri:layout-grid-line', |
| | | keepAlive: false |
| | | } |
| | | } |
| | | ] |
| | | }, |
| New file |
| | |
| | | <template> |
| | | <div class="warehouse-areas-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 v-auth="'add'" @click="showDialog('add')" v-ripple>新增库区</ElButton> |
| | | <ElButton |
| | | v-auth="'delete'" |
| | | type="danger" |
| | | :disabled="selectedRows.length === 0" |
| | | @click="handleBatchDelete" |
| | | v-ripple |
| | | > |
| | | 批量删除 |
| | | </ElButton> |
| | | </ElSpace> |
| | | </template> |
| | | </ArtTableHeader> |
| | | |
| | | <ArtTable |
| | | :loading="loading" |
| | | :data="data" |
| | | :columns="columns" |
| | | :pagination="pagination" |
| | | @selection-change="handleSelectionChange" |
| | | @pagination:size-change="handleSizeChange" |
| | | @pagination:current-change="handleCurrentChange" |
| | | /> |
| | | |
| | | <WarehouseAreasDialog |
| | | v-model:visible="dialogVisible" |
| | | :dialog-type="dialogType" |
| | | :warehouse-areas-data="currentWarehouseAreasData" |
| | | :warehouse-options="warehouseOptions" |
| | | :shipper-options="shipperOptions" |
| | | :supplier-options="supplierOptions" |
| | | :type-options="typeOptions" |
| | | @submit="handleDialogSubmit" |
| | | /> |
| | | |
| | | <WarehouseAreasDetailDrawer |
| | | v-model:visible="detailDrawerVisible" |
| | | :loading="detailLoading" |
| | | :detail="detailData" |
| | | /> |
| | | </ElCard> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { computed, onMounted, ref } from 'vue' |
| | | import { ElMessage } from 'element-plus' |
| | | import { useAuth } from '@/hooks/core/useAuth' |
| | | import { useTable } from '@/hooks/core/useTable' |
| | | import { useTableColumns } from '@/hooks/core/useTableColumns' |
| | | import { useCrudPage } from '@/views/system/common/useCrudPage' |
| | | import { defaultResponseAdapter } from '@/utils/table/tableUtils' |
| | | import { guardRequestWithMessage } from '@/utils/sys/requestGuard' |
| | | import { fetchDictDataPage } from '@/api/system-manage' |
| | | import { |
| | | fetchCompanysList, |
| | | fetchDeleteWarehouseAreas, |
| | | fetchWarehouseAreasDetail, |
| | | fetchWarehouseAreasPage, |
| | | fetchSaveWarehouseAreas, |
| | | fetchUpdateWarehouseAreas, |
| | | fetchWarehouseList |
| | | } from '@/api/warehouse-areas' |
| | | import WarehouseAreasDialog from './modules/warehouse-areas-dialog.vue' |
| | | import WarehouseAreasDetailDrawer from './modules/warehouse-areas-detail-drawer.vue' |
| | | import { createWarehouseAreasTableColumns } from './warehouseAreasTable.columns' |
| | | import { |
| | | buildWarehouseAreasDialogModel, |
| | | buildWarehouseAreasPageQueryParams, |
| | | buildWarehouseAreasSavePayload, |
| | | buildWarehouseAreasSearchParams, |
| | | createWarehouseAreasSearchState, |
| | | getWarehouseAreasPaginationKey, |
| | | getWarehouseAreasStatusOptions, |
| | | normalizeWarehouseAreasDetailRecord, |
| | | normalizeWarehouseAreasListRow |
| | | } from './warehouseAreasPage.helpers' |
| | | |
| | | defineOptions({ name: 'WarehouseAreas' }) |
| | | |
| | | const { hasAuth } = useAuth() |
| | | |
| | | const searchForm = ref(createWarehouseAreasSearchState()) |
| | | const detailDrawerVisible = ref(false) |
| | | const detailLoading = ref(false) |
| | | const detailData = ref({}) |
| | | const warehouseOptions = ref([]) |
| | | const shipperOptions = ref([]) |
| | | const supplierOptions = ref([]) |
| | | const typeOptions = ref([]) |
| | | let handleDeleteAction = null |
| | | |
| | | const searchItems = computed(() => [ |
| | | { |
| | | label: '关键字', |
| | | key: 'condition', |
| | | type: 'input', |
| | | props: { |
| | | clearable: true, |
| | | placeholder: '请输入库区名称/编码/备注' |
| | | } |
| | | }, |
| | | { |
| | | label: '仓库', |
| | | key: 'warehouseId', |
| | | type: 'select', |
| | | props: { |
| | | clearable: true, |
| | | filterable: true, |
| | | options: warehouseOptions.value |
| | | } |
| | | }, |
| | | { |
| | | label: '库区编码', |
| | | key: 'code', |
| | | type: 'input', |
| | | props: { |
| | | clearable: true, |
| | | placeholder: '请输入库区编码' |
| | | } |
| | | }, |
| | | { |
| | | label: '库区名称', |
| | | key: 'name', |
| | | type: 'input', |
| | | props: { |
| | | clearable: true, |
| | | placeholder: '请输入库区名称' |
| | | } |
| | | }, |
| | | { |
| | | label: '业务类型', |
| | | key: 'type', |
| | | type: 'select', |
| | | props: { |
| | | clearable: true, |
| | | filterable: true, |
| | | options: typeOptions.value |
| | | } |
| | | }, |
| | | { |
| | | label: '状态', |
| | | key: 'status', |
| | | type: 'select', |
| | | props: { |
| | | clearable: true, |
| | | options: getWarehouseAreasStatusOptions() |
| | | } |
| | | } |
| | | ]) |
| | | |
| | | async function openDetail(row) { |
| | | detailDrawerVisible.value = true |
| | | detailLoading.value = true |
| | | try { |
| | | const detail = await guardRequestWithMessage(fetchWarehouseAreasDetail(row.id), {}, { |
| | | timeoutMessage: '库区详情加载超时,已停止等待' |
| | | }) |
| | | detailData.value = normalizeWarehouseAreasDetailRecord(detail) |
| | | } catch (error) { |
| | | detailDrawerVisible.value = false |
| | | detailData.value = {} |
| | | ElMessage.error(error?.message || '获取库区详情失败') |
| | | } finally { |
| | | detailLoading.value = false |
| | | } |
| | | } |
| | | |
| | | async function openEditDialog(row) { |
| | | try { |
| | | const detail = await guardRequestWithMessage(fetchWarehouseAreasDetail(row.id), {}, { |
| | | timeoutMessage: '库区详情加载超时,已停止等待' |
| | | }) |
| | | showDialog('edit', detail) |
| | | } catch (error) { |
| | | ElMessage.error(error?.message || '获取库区详情失败') |
| | | } |
| | | } |
| | | |
| | | const { columns, columnChecks, data, loading, pagination, getData, replaceSearchParams, resetSearchParams, handleSizeChange, handleCurrentChange, refreshData, refreshCreate, refreshUpdate, refreshRemove } = |
| | | useTable({ |
| | | core: { |
| | | apiFn: fetchWarehouseAreasPage, |
| | | apiParams: buildWarehouseAreasPageQueryParams(searchForm.value), |
| | | paginationKey: getWarehouseAreasPaginationKey(), |
| | | columnsFactory: () => |
| | | createWarehouseAreasTableColumns({ |
| | | handleView: openDetail, |
| | | handleEdit: hasAuth('update') ? openEditDialog : null, |
| | | handleDelete: hasAuth('delete') ? (row) => handleDeleteAction?.(row) : null, |
| | | canEdit: hasAuth('update'), |
| | | canDelete: hasAuth('delete') |
| | | }) |
| | | }, |
| | | transform: { |
| | | dataTransformer: (records) => { |
| | | if (!Array.isArray(records)) { |
| | | return [] |
| | | } |
| | | return records.map((item) => normalizeWarehouseAreasListRow(item)) |
| | | } |
| | | } |
| | | }) |
| | | |
| | | const { |
| | | dialogVisible, |
| | | dialogType, |
| | | currentRecord: currentWarehouseAreasData, |
| | | selectedRows, |
| | | handleSelectionChange, |
| | | showDialog, |
| | | handleDialogSubmit, |
| | | handleDelete, |
| | | handleBatchDelete |
| | | } = useCrudPage({ |
| | | createEmptyModel: () => buildWarehouseAreasDialogModel(), |
| | | buildEditModel: (record) => buildWarehouseAreasDialogModel(record), |
| | | buildSavePayload: (formData) => buildWarehouseAreasSavePayload(formData), |
| | | saveRequest: fetchSaveWarehouseAreas, |
| | | updateRequest: fetchUpdateWarehouseAreas, |
| | | deleteRequest: fetchDeleteWarehouseAreas, |
| | | entityName: '库区', |
| | | resolveRecordLabel: (record) => record?.name || record?.code || record?.id, |
| | | refreshCreate, |
| | | refreshUpdate, |
| | | refreshRemove |
| | | }) |
| | | handleDeleteAction = handleDelete |
| | | |
| | | function handleSearch(params) { |
| | | replaceSearchParams(buildWarehouseAreasSearchParams(params)) |
| | | getData() |
| | | } |
| | | |
| | | function handleReset() { |
| | | Object.assign(searchForm.value, createWarehouseAreasSearchState()) |
| | | resetSearchParams() |
| | | } |
| | | |
| | | async function loadWarehouseOptions() { |
| | | const records = await guardRequestWithMessage(fetchWarehouseList(), [], { |
| | | timeoutMessage: '仓库选项加载超时,已停止等待' |
| | | }) |
| | | warehouseOptions.value = defaultResponseAdapter(records).records.map((item) => ({ |
| | | label: item.name || item.code || `仓库 ${item.id}`, |
| | | value: item.id |
| | | })) |
| | | } |
| | | |
| | | async function loadCompanyOptions() { |
| | | const records = await guardRequestWithMessage(fetchCompanysList(), [], { |
| | | timeoutMessage: '往来企业选项加载超时,已停止等待' |
| | | }) |
| | | const normalizedRecords = defaultResponseAdapter(records).records |
| | | shipperOptions.value = normalizedRecords |
| | | .filter((item) => item.type === 'shipper') |
| | | .map((item) => ({ |
| | | label: item.name || item.code || `货主 ${item.id}`, |
| | | value: item.id |
| | | })) |
| | | supplierOptions.value = normalizedRecords |
| | | .filter((item) => item.type === 'supplier') |
| | | .map((item) => ({ |
| | | label: item.name || item.code || `供应商 ${item.id}`, |
| | | value: item.id |
| | | })) |
| | | } |
| | | |
| | | async function loadTypeOptions() { |
| | | const response = await guardRequestWithMessage( |
| | | fetchDictDataPage({ |
| | | current: 1, |
| | | pageSize: 200, |
| | | dictTypeCode: 'sys_ware_areas_type', |
| | | status: 1 |
| | | }), |
| | | { records: [] }, |
| | | { timeoutMessage: '业务类型加载超时,已停止等待' } |
| | | ) |
| | | typeOptions.value = defaultResponseAdapter(response).records.map((item) => ({ |
| | | label: item.label || item.name || item.value, |
| | | value: item.value |
| | | })) |
| | | } |
| | | |
| | | onMounted(async () => { |
| | | await Promise.all([loadWarehouseOptions(), loadCompanyOptions(), loadTypeOptions(), getData()]) |
| | | }) |
| | | </script> |
| New file |
| | |
| | | <template> |
| | | <ElDrawer |
| | | :model-value="visible" |
| | | title="库区详情" |
| | | size="960px" |
| | | destroy-on-close |
| | | @update:model-value="handleVisibleChange" |
| | | > |
| | | <ElScrollbar class="h-[calc(100vh-180px)] pr-1"> |
| | | <div v-if="loading" class="py-6"> |
| | | <ElSkeleton :rows="12" animated /> |
| | | </div> |
| | | <div v-else class="space-y-4"> |
| | | <ElDescriptions title="基础信息" :column="2" border> |
| | | <ElDescriptionsItem label="仓库">{{ detail.warehouseName || '--' }}</ElDescriptionsItem> |
| | | <ElDescriptionsItem label="库区编码">{{ detail.code || '--' }}</ElDescriptionsItem> |
| | | <ElDescriptionsItem label="库区名称">{{ detail.name || '--' }}</ElDescriptionsItem> |
| | | <ElDescriptionsItem label="业务类型">{{ detail.typeText || '--' }}</ElDescriptionsItem> |
| | | <ElDescriptionsItem label="货主">{{ detail.shipperName || '--' }}</ElDescriptionsItem> |
| | | <ElDescriptionsItem label="供应商">{{ detail.supplierName || '--' }}</ElDescriptionsItem> |
| | | <ElDescriptionsItem label="支持混放">{{ detail.flagMixText || '--' }}</ElDescriptionsItem> |
| | | <ElDescriptionsItem label="允许负库存">{{ detail.flagMinusText || '--' }}</ElDescriptionsItem> |
| | | <ElDescriptionsItem label="标签管理">{{ detail.flagLabelMangeText || '--' }}</ElDescriptionsItem> |
| | | <ElDescriptionsItem label="状态"> |
| | | <ElTag :type="detail.statusType || 'info'" effect="light"> |
| | | {{ detail.statusText || '--' }} |
| | | </ElTag> |
| | | </ElDescriptionsItem> |
| | | <ElDescriptionsItem label="排序">{{ detail.sort ?? '--' }}</ElDescriptionsItem> |
| | | <ElDescriptionsItem label="备注" :span="2">{{ detail.memo || '--' }}</ElDescriptionsItem> |
| | | </ElDescriptions> |
| | | |
| | | <ElDescriptions title="审计信息" :column="2" border> |
| | | <ElDescriptionsItem label="创建人">{{ detail.createByText || '--' }}</ElDescriptionsItem> |
| | | <ElDescriptionsItem label="创建时间">{{ detail.createTimeText || '--' }}</ElDescriptionsItem> |
| | | <ElDescriptionsItem label="更新人">{{ detail.updateByText || '--' }}</ElDescriptionsItem> |
| | | <ElDescriptionsItem label="更新时间">{{ detail.updateTimeText || '--' }}</ElDescriptionsItem> |
| | | </ElDescriptions> |
| | | </div> |
| | | </ElScrollbar> |
| | | </ElDrawer> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { computed } from 'vue' |
| | | |
| | | const props = defineProps({ |
| | | visible: { type: Boolean, default: false }, |
| | | loading: { type: Boolean, default: false }, |
| | | detail: { type: Object, default: () => ({}) } |
| | | }) |
| | | |
| | | const emit = defineEmits(['update:visible']) |
| | | |
| | | const visible = computed({ |
| | | get: () => props.visible, |
| | | set: (value) => emit('update:visible', value) |
| | | }) |
| | | |
| | | function handleVisibleChange(value) { |
| | | visible.value = value |
| | | } |
| | | </script> |
| New file |
| | |
| | | <template> |
| | | <ElDialog |
| | | :title="dialogTitle" |
| | | :model-value="visible" |
| | | width="920px" |
| | | align-center |
| | | destroy-on-close |
| | | @update:model-value="handleCancel" |
| | | @closed="handleClosed" |
| | | > |
| | | <ArtForm |
| | | ref="formRef" |
| | | v-model="form" |
| | | :items="formItems" |
| | | :rules="rules" |
| | | :span="12" |
| | | :gutter="20" |
| | | label-width="110px" |
| | | :show-reset="false" |
| | | :show-submit="false" |
| | | /> |
| | | |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <ElButton @click="handleCancel">取消</ElButton> |
| | | <ElButton type="primary" @click="handleSubmit">确定</ElButton> |
| | | </span> |
| | | </template> |
| | | </ElDialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { computed, nextTick, reactive, ref, watch } from 'vue' |
| | | import ArtForm from '@/components/core/forms/art-form/index.vue' |
| | | import { |
| | | buildWarehouseAreasDialogModel, |
| | | createWarehouseAreasFormState, |
| | | getWarehouseAreasFlagOptions, |
| | | getWarehouseAreasStatusOptions |
| | | } from '../warehouseAreasPage.helpers' |
| | | |
| | | const props = defineProps({ |
| | | visible: { type: Boolean, default: false }, |
| | | dialogType: { type: String, default: 'add' }, |
| | | warehouseAreasData: { type: Object, default: () => ({}) }, |
| | | warehouseOptions: { type: Array, default: () => [] }, |
| | | shipperOptions: { type: Array, default: () => [] }, |
| | | supplierOptions: { type: Array, default: () => [] }, |
| | | typeOptions: { type: Array, default: () => [] } |
| | | }) |
| | | |
| | | const emit = defineEmits(['update:visible', 'submit']) |
| | | const formRef = ref() |
| | | const form = reactive(createWarehouseAreasFormState()) |
| | | |
| | | const isEdit = computed(() => props.dialogType === 'edit') |
| | | const dialogTitle = computed(() => (isEdit.value ? '编辑库区' : '新增库区')) |
| | | |
| | | const rules = computed(() => ({ |
| | | warehouseId: [{ required: true, message: '请选择仓库', trigger: 'change' }], |
| | | code: [{ required: true, message: '请输入库区编码', trigger: 'blur' }], |
| | | name: [{ required: true, message: '请输入库区名称', trigger: 'blur' }], |
| | | type: [{ required: true, message: '请选择业务类型', trigger: 'change' }], |
| | | flagMinus: [{ required: true, message: '请选择允许负库存', trigger: 'change' }], |
| | | flagMix: [{ required: true, message: '请选择支持混放', trigger: 'change' }] |
| | | })) |
| | | |
| | | const formItems = computed(() => [ |
| | | { |
| | | label: '仓库', |
| | | key: 'warehouseId', |
| | | type: 'select', |
| | | props: { |
| | | placeholder: '请选择仓库', |
| | | clearable: true, |
| | | filterable: true, |
| | | options: props.warehouseOptions |
| | | } |
| | | }, |
| | | { |
| | | label: '库区编码', |
| | | key: 'code', |
| | | type: 'input', |
| | | props: { |
| | | placeholder: '请输入库区编码', |
| | | clearable: true |
| | | } |
| | | }, |
| | | { |
| | | label: '库区名称', |
| | | key: 'name', |
| | | type: 'input', |
| | | props: { |
| | | placeholder: '请输入库区名称', |
| | | clearable: true |
| | | } |
| | | }, |
| | | { |
| | | label: '业务类型', |
| | | key: 'type', |
| | | type: 'select', |
| | | props: { |
| | | placeholder: '请选择业务类型', |
| | | clearable: true, |
| | | filterable: true, |
| | | options: props.typeOptions |
| | | } |
| | | }, |
| | | { |
| | | label: '货主', |
| | | key: 'shipperId', |
| | | type: 'select', |
| | | props: { |
| | | placeholder: '请选择货主', |
| | | clearable: true, |
| | | filterable: true, |
| | | options: props.shipperOptions |
| | | } |
| | | }, |
| | | { |
| | | label: '供应商', |
| | | key: 'supplierId', |
| | | type: 'select', |
| | | props: { |
| | | placeholder: '请选择供应商', |
| | | clearable: true, |
| | | filterable: true, |
| | | options: props.supplierOptions |
| | | } |
| | | }, |
| | | { |
| | | label: '允许负库存', |
| | | key: 'flagMinus', |
| | | type: 'select', |
| | | props: { |
| | | placeholder: '请选择', |
| | | options: getWarehouseAreasFlagOptions() |
| | | } |
| | | }, |
| | | { |
| | | label: '标签管理', |
| | | key: 'flagLabelMange', |
| | | type: 'select', |
| | | props: { |
| | | placeholder: '请选择', |
| | | options: getWarehouseAreasFlagOptions() |
| | | } |
| | | }, |
| | | { |
| | | label: '支持混放', |
| | | key: 'flagMix', |
| | | type: 'select', |
| | | props: { |
| | | placeholder: '请选择', |
| | | options: getWarehouseAreasFlagOptions() |
| | | } |
| | | }, |
| | | { |
| | | label: '状态', |
| | | key: 'status', |
| | | type: 'select', |
| | | props: { |
| | | placeholder: '请选择状态', |
| | | options: getWarehouseAreasStatusOptions() |
| | | } |
| | | }, |
| | | { |
| | | label: '排序', |
| | | key: 'sort', |
| | | type: 'number', |
| | | props: { |
| | | min: 0, |
| | | controlsPosition: 'right', |
| | | style: { width: '100%' } |
| | | } |
| | | }, |
| | | { |
| | | label: '备注', |
| | | key: 'memo', |
| | | type: 'input', |
| | | span: 24, |
| | | props: { |
| | | type: 'textarea', |
| | | rows: 3, |
| | | placeholder: '请输入备注', |
| | | clearable: true |
| | | } |
| | | } |
| | | ]) |
| | | |
| | | const loadFormData = () => { |
| | | Object.assign(form, buildWarehouseAreasDialogModel(props.warehouseAreasData)) |
| | | } |
| | | |
| | | const resetForm = () => { |
| | | Object.assign(form, createWarehouseAreasFormState()) |
| | | formRef.value?.clearValidate?.() |
| | | } |
| | | |
| | | const handleSubmit = async () => { |
| | | if (!formRef.value) return |
| | | try { |
| | | await formRef.value.validate() |
| | | emit('submit', { ...form }) |
| | | } catch { |
| | | return |
| | | } |
| | | } |
| | | |
| | | const handleCancel = () => { |
| | | emit('update:visible', false) |
| | | } |
| | | |
| | | const handleClosed = () => { |
| | | resetForm() |
| | | } |
| | | |
| | | watch( |
| | | () => props.visible, |
| | | (visible) => { |
| | | if (visible) { |
| | | loadFormData() |
| | | nextTick(() => { |
| | | formRef.value?.clearValidate?.() |
| | | }) |
| | | } |
| | | }, |
| | | { immediate: true } |
| | | ) |
| | | |
| | | watch( |
| | | () => props.warehouseAreasData, |
| | | () => { |
| | | if (props.visible) { |
| | | loadFormData() |
| | | } |
| | | }, |
| | | { deep: true } |
| | | ) |
| | | </script> |
| New file |
| | |
| | | const STATUS_META = { |
| | | 1: { text: '正常', type: 'success', bool: true }, |
| | | 0: { text: '冻结', type: 'danger', bool: false } |
| | | } |
| | | |
| | | export const WAREHOUSE_AREAS_REPORT_TITLE = '库区报表' |
| | | export const WAREHOUSE_AREAS_REPORT_STYLE = { |
| | | titleAlign: 'center', |
| | | titleLevel: 'strong', |
| | | orientation: 'portrait', |
| | | density: 'compact', |
| | | showSequence: true |
| | | } |
| | | |
| | | function normalizeText(value) { |
| | | return String(value ?? '').trim() |
| | | } |
| | | |
| | | function normalizeFlagText(value) { |
| | | if (value === 1 || value === '1' || value === true || value === '是') { |
| | | return '是' |
| | | } |
| | | if (value === 0 || value === '0' || value === false || value === '否') { |
| | | return '否' |
| | | } |
| | | return normalizeText(value) || '-' |
| | | } |
| | | |
| | | export function createWarehouseAreasSearchState() { |
| | | return { |
| | | condition: '', |
| | | warehouseId: '', |
| | | code: '', |
| | | name: '', |
| | | type: '', |
| | | shipperId: '', |
| | | supplierId: '', |
| | | status: '', |
| | | memo: '' |
| | | } |
| | | } |
| | | |
| | | export function createWarehouseAreasFormState() { |
| | | return { |
| | | id: void 0, |
| | | warehouseId: void 0, |
| | | code: '', |
| | | name: '', |
| | | type: '', |
| | | shipperId: void 0, |
| | | supplierId: void 0, |
| | | flagMinus: 0, |
| | | flagLabelMange: 0, |
| | | flagMix: 0, |
| | | status: 1, |
| | | sort: void 0, |
| | | memo: '' |
| | | } |
| | | } |
| | | |
| | | export function getWarehouseAreasPaginationKey() { |
| | | return { |
| | | current: 'current', |
| | | size: 'pageSize' |
| | | } |
| | | } |
| | | |
| | | export function getWarehouseAreasStatusOptions() { |
| | | return [ |
| | | { label: '正常', value: 1 }, |
| | | { label: '冻结', value: 0 } |
| | | ] |
| | | } |
| | | |
| | | export function getWarehouseAreasFlagOptions() { |
| | | return [ |
| | | { label: '否', value: 0 }, |
| | | { label: '是', value: 1 } |
| | | ] |
| | | } |
| | | |
| | | export function buildWarehouseAreasSearchParams(params = {}) { |
| | | const searchParams = { |
| | | condition: normalizeText(params.condition), |
| | | warehouseId: |
| | | params.warehouseId !== undefined && params.warehouseId !== null && params.warehouseId !== '' |
| | | ? Number(params.warehouseId) |
| | | : void 0, |
| | | code: normalizeText(params.code), |
| | | name: normalizeText(params.name), |
| | | type: normalizeText(params.type), |
| | | shipperId: |
| | | params.shipperId !== undefined && params.shipperId !== null && params.shipperId !== '' |
| | | ? Number(params.shipperId) |
| | | : void 0, |
| | | supplierId: |
| | | params.supplierId !== undefined && params.supplierId !== null && params.supplierId !== '' |
| | | ? Number(params.supplierId) |
| | | : void 0, |
| | | status: |
| | | params.status !== undefined && params.status !== null && params.status !== '' |
| | | ? Number(params.status) |
| | | : void 0, |
| | | memo: normalizeText(params.memo) |
| | | } |
| | | |
| | | return Object.fromEntries( |
| | | Object.entries(searchParams).filter(([, value]) => value !== '' && value !== void 0 && value !== null) |
| | | ) |
| | | } |
| | | |
| | | export function buildWarehouseAreasPageQueryParams(params = {}) { |
| | | return { |
| | | current: params.current || 1, |
| | | pageSize: params.pageSize || params.size || 20, |
| | | ...buildWarehouseAreasSearchParams(params) |
| | | } |
| | | } |
| | | |
| | | export function buildWarehouseAreasSavePayload(formData = {}) { |
| | | return { |
| | | ...(formData.id !== void 0 && formData.id !== null && formData.id !== '' |
| | | ? { id: Number(formData.id) } |
| | | : {}), |
| | | ...(formData.warehouseId !== void 0 && formData.warehouseId !== null && formData.warehouseId !== '' |
| | | ? { warehouseId: Number(formData.warehouseId) } |
| | | : {}), |
| | | code: normalizeText(formData.code) || '', |
| | | name: normalizeText(formData.name) || '', |
| | | type: normalizeText(formData.type) || '', |
| | | ...(formData.shipperId !== void 0 && formData.shipperId !== null && formData.shipperId !== '' |
| | | ? { shipperId: Number(formData.shipperId) } |
| | | : {}), |
| | | ...(formData.supplierId !== void 0 && formData.supplierId !== null && formData.supplierId !== '' |
| | | ? { supplierId: Number(formData.supplierId) } |
| | | : {}), |
| | | ...(formData.flagMinus !== void 0 && formData.flagMinus !== null && formData.flagMinus !== '' |
| | | ? { flagMinus: Number(formData.flagMinus) } |
| | | : {}), |
| | | ...(formData.flagLabelMange !== void 0 && formData.flagLabelMange !== null && formData.flagLabelMange !== '' |
| | | ? { flagLabelMange: Number(formData.flagLabelMange) } |
| | | : {}), |
| | | ...(formData.flagMix !== void 0 && formData.flagMix !== null && formData.flagMix !== '' |
| | | ? { flagMix: Number(formData.flagMix) } |
| | | : {}), |
| | | status: |
| | | formData.status !== void 0 && formData.status !== null && formData.status !== '' |
| | | ? Number(formData.status) |
| | | : 1, |
| | | ...(formData.sort !== void 0 && formData.sort !== null && formData.sort !== '' |
| | | ? { sort: Number(formData.sort) } |
| | | : {}), |
| | | memo: normalizeText(formData.memo) || '' |
| | | } |
| | | } |
| | | |
| | | export function buildWarehouseAreasDialogModel(record = {}) { |
| | | return { |
| | | ...createWarehouseAreasFormState(), |
| | | ...(record.id !== void 0 && record.id !== null && record.id !== '' ? { id: Number(record.id) } : {}), |
| | | warehouseId: |
| | | record.warehouseId !== void 0 && record.warehouseId !== null && record.warehouseId !== '' |
| | | ? Number(record.warehouseId) |
| | | : void 0, |
| | | code: normalizeText(record.code || ''), |
| | | name: normalizeText(record.name || ''), |
| | | type: normalizeText(record.type || ''), |
| | | shipperId: |
| | | record.shipperId !== void 0 && record.shipperId !== null && record.shipperId !== '' |
| | | ? Number(record.shipperId) |
| | | : void 0, |
| | | supplierId: |
| | | record.supplierId !== void 0 && record.supplierId !== null && record.supplierId !== '' |
| | | ? Number(record.supplierId) |
| | | : void 0, |
| | | flagMinus: record.flagMinus !== void 0 && record.flagMinus !== null ? Number(record.flagMinus) : 0, |
| | | flagLabelMange: |
| | | record.flagLabelMange !== void 0 && record.flagLabelMange !== null ? Number(record.flagLabelMange) : 0, |
| | | flagMix: record.flagMix !== void 0 && record.flagMix !== null ? Number(record.flagMix) : 0, |
| | | status: record.status !== void 0 && record.status !== null ? Number(record.status) : 1, |
| | | sort: record.sort !== void 0 && record.sort !== null && record.sort !== '' ? Number(record.sort) : void 0, |
| | | memo: normalizeText(record.memo || '') |
| | | } |
| | | } |
| | | |
| | | export function getWarehouseAreasStatusMeta(status) { |
| | | if (status === true || Number(status) === 1) { |
| | | return STATUS_META[1] |
| | | } |
| | | if (status === false || Number(status) === 0) { |
| | | return STATUS_META[0] |
| | | } |
| | | return { text: '未知', type: 'info', bool: false } |
| | | } |
| | | |
| | | export function normalizeWarehouseAreasDetailRecord(record = {}) { |
| | | const statusMeta = getWarehouseAreasStatusMeta(record.statusBool ?? record.status) |
| | | return { |
| | | ...record, |
| | | warehouseName: normalizeText(record.warehouseId$ || record.warehouseName || ''), |
| | | typeText: normalizeText(record.type$ || record.type || ''), |
| | | shipperName: normalizeText(record.shipperId$ || record.shipperName || ''), |
| | | supplierName: normalizeText(record.supplierId$ || record.supplierName || ''), |
| | | code: normalizeText(record.code || ''), |
| | | name: normalizeText(record.name || ''), |
| | | memo: normalizeText(record.memo || ''), |
| | | flagMinusText: normalizeFlagText(record.flagMinus$ ?? record.flagMinus), |
| | | flagLabelMangeText: normalizeFlagText(record.flagLabelMange$ ?? record.flagLabelMange), |
| | | flagMixText: normalizeFlagText(record.flagMix$ ?? record.flagMix), |
| | | statusText: statusMeta.text, |
| | | statusType: statusMeta.type, |
| | | statusBool: record.statusBool !== void 0 ? Boolean(record.statusBool) : statusMeta.bool, |
| | | createByText: normalizeText(record.createBy$ || record.createByText || ''), |
| | | createTimeText: normalizeText(record.createTime$ || record.createTime || ''), |
| | | updateByText: normalizeText(record.updateBy$ || record.updateByText || ''), |
| | | updateTimeText: normalizeText(record.updateTime$ || record.updateTime || '') |
| | | } |
| | | } |
| | | |
| | | export function normalizeWarehouseAreasListRow(record = {}) { |
| | | return normalizeWarehouseAreasDetailRecord(record) |
| | | } |
| | | |
| | | export function buildWarehouseAreasPrintRows(records = []) { |
| | | if (!Array.isArray(records)) { |
| | | return [] |
| | | } |
| | | return records.map((record) => normalizeWarehouseAreasListRow(record)) |
| | | } |
| | | |
| New file |
| | |
| | | import { h } from 'vue' |
| | | import { ElTag } from 'element-plus' |
| | | import ArtButtonMore from '@/components/core/forms/art-button-more/index.vue' |
| | | import { getWarehouseAreasStatusMeta } from './warehouseAreasPage.helpers' |
| | | |
| | | export function createWarehouseAreasTableColumns({ |
| | | handleView, |
| | | handleEdit, |
| | | handleDelete, |
| | | canEdit = true, |
| | | canDelete = true |
| | | } = {}) { |
| | | const operations = [{ key: 'view', label: '详情', icon: 'ri:eye-line' }] |
| | | |
| | | if (canEdit && handleEdit) { |
| | | operations.push({ key: 'edit', label: '编辑', icon: 'ri:pencil-line' }) |
| | | } |
| | | |
| | | if (canDelete && handleDelete) { |
| | | operations.push({ key: 'delete', label: '删除', icon: 'ri:delete-bin-5-line', color: 'var(--art-error)' }) |
| | | } |
| | | |
| | | return [ |
| | | { |
| | | type: 'selection', |
| | | width: 48, |
| | | align: 'center' |
| | | }, |
| | | { |
| | | type: 'globalIndex', |
| | | label: '序号', |
| | | width: 72, |
| | | align: 'center' |
| | | }, |
| | | { |
| | | prop: 'warehouseName', |
| | | label: '仓库', |
| | | minWidth: 160, |
| | | showOverflowTooltip: true, |
| | | formatter: (row) => row.warehouseName || '--' |
| | | }, |
| | | { |
| | | prop: 'code', |
| | | label: '库区编码', |
| | | minWidth: 150, |
| | | showOverflowTooltip: true, |
| | | formatter: (row) => row.code || '--' |
| | | }, |
| | | { |
| | | prop: 'name', |
| | | label: '库区名称', |
| | | minWidth: 180, |
| | | showOverflowTooltip: true, |
| | | formatter: (row) => row.name || '--' |
| | | }, |
| | | { |
| | | prop: 'typeText', |
| | | label: '业务类型', |
| | | minWidth: 140, |
| | | showOverflowTooltip: true, |
| | | formatter: (row) => row.typeText || '--' |
| | | }, |
| | | { |
| | | prop: 'shipperName', |
| | | label: '货主', |
| | | minWidth: 140, |
| | | showOverflowTooltip: true, |
| | | formatter: (row) => row.shipperName || '--' |
| | | }, |
| | | { |
| | | prop: 'supplierName', |
| | | label: '供应商', |
| | | minWidth: 140, |
| | | showOverflowTooltip: true, |
| | | formatter: (row) => row.supplierName || '--' |
| | | }, |
| | | { |
| | | prop: 'flagMixText', |
| | | label: '支持混放', |
| | | width: 100, |
| | | align: 'center', |
| | | formatter: (row) => row.flagMixText || '--' |
| | | }, |
| | | { |
| | | prop: 'flagMinusText', |
| | | label: '允许负库存', |
| | | width: 100, |
| | | align: 'center', |
| | | formatter: (row) => row.flagMinusText || '--' |
| | | }, |
| | | { |
| | | prop: 'flagLabelMangeText', |
| | | label: '标签管理', |
| | | width: 100, |
| | | align: 'center', |
| | | formatter: (row) => row.flagLabelMangeText || '--' |
| | | }, |
| | | { |
| | | prop: 'status', |
| | | label: '状态', |
| | | width: 100, |
| | | align: 'center', |
| | | formatter: (row) => { |
| | | const statusMeta = getWarehouseAreasStatusMeta(row.statusBool ?? row.status) |
| | | return h(ElTag, { type: statusMeta.type, effect: 'light' }, () => statusMeta.text) |
| | | } |
| | | }, |
| | | { |
| | | prop: 'sort', |
| | | label: '排序', |
| | | width: 80, |
| | | align: 'center', |
| | | formatter: (row) => row.sort ?? '--' |
| | | }, |
| | | { |
| | | prop: 'memo', |
| | | label: '备注', |
| | | minWidth: 180, |
| | | showOverflowTooltip: true, |
| | | formatter: (row) => row.memo || '--' |
| | | }, |
| | | { |
| | | prop: 'updateTimeText', |
| | | label: '更新时间', |
| | | minWidth: 170, |
| | | showOverflowTooltip: true, |
| | | formatter: (row) => row.updateTimeText || '--' |
| | | }, |
| | | { |
| | | prop: 'operation', |
| | | label: '操作', |
| | | width: 160, |
| | | align: 'right', |
| | | formatter: (row) => |
| | | h(ArtButtonMore, { |
| | | list: operations, |
| | | onClick: (item) => { |
| | | if (item.key === 'view') handleView?.(row) |
| | | if (item.key === 'edit') handleEdit?.(row) |
| | | if (item.key === 'delete') handleDelete?.(row) |
| | | } |
| | | }) |
| | | } |
| | | ] |
| | | } |
| New file |
| | |
| | | import assert from 'node:assert/strict' |
| | | import { readFile } from 'node:fs/promises' |
| | | import { fileURLToPath } from 'node:url' |
| | | import test from 'node:test' |
| | | |
| | | const apiPath = new URL('../src/api/warehouse-areas.js', import.meta.url) |
| | | const helpersPath = new URL('../src/views/basic-info/warehouse-areas/warehouseAreasPage.helpers.js', import.meta.url) |
| | | const columnsPath = new URL('../src/views/basic-info/warehouse-areas/warehouseAreasTable.columns.js', import.meta.url) |
| | | const pagePath = new URL('../src/views/basic-info/warehouse-areas/index.vue', import.meta.url) |
| | | const backendMenuAdapterPath = new URL('../src/router/adapters/backendMenuAdapter.js', import.meta.url) |
| | | const staticRoutesPath = new URL('../src/router/routes/staticRoutes.js', import.meta.url) |
| | | |
| | | test('warehouse areas api uses real backend endpoints', async () => { |
| | | const apiSource = await readFile(fileURLToPath(apiPath), 'utf8') |
| | | |
| | | assert.match(apiSource, /fetchWarehouseAreasPage/) |
| | | assert.match(apiSource, /fetchWarehouseAreasDetail/) |
| | | assert.match(apiSource, /fetchWarehouseAreasMany/) |
| | | assert.match(apiSource, /fetchSaveWarehouseAreas/) |
| | | assert.match(apiSource, /fetchUpdateWarehouseAreas/) |
| | | assert.match(apiSource, /fetchDeleteWarehouseAreas/) |
| | | assert.match(apiSource, /fetchWarehouseAreasQuery/) |
| | | assert.match(apiSource, /fetchExportWarehouseAreasReport/) |
| | | assert.match(apiSource, /url: '\/warehouseAreas\/page'/) |
| | | assert.match(apiSource, /url: '\/warehouseAreas\/list'/) |
| | | assert.match(apiSource, /url: `\/warehouseAreas\/many\/\$\{normalizeIds\(ids\)\}`/) |
| | | assert.match(apiSource, /url: '\/warehouseAreas\/save'/) |
| | | assert.match(apiSource, /url: '\/warehouseAreas\/update'/) |
| | | assert.match(apiSource, /url: `\/warehouseAreas\/remove\/\$\{normalizeIds\(ids\)\}`/) |
| | | assert.match(apiSource, /url: '\/warehouseAreas\/query'/) |
| | | assert.match(apiSource, /warehouseAreas\/export/) |
| | | }) |
| | | |
| | | test('warehouse areas helpers keep page, save and detail contracts stable', async () => { |
| | | const helpers = await import('../src/views/basic-info/warehouse-areas/warehouseAreasPage.helpers.js') |
| | | |
| | | assert.deepEqual(helpers.createWarehouseAreasSearchState(), { |
| | | condition: '', |
| | | warehouseId: '', |
| | | code: '', |
| | | name: '', |
| | | type: '', |
| | | shipperId: '', |
| | | supplierId: '', |
| | | status: '', |
| | | memo: '' |
| | | }) |
| | | |
| | | assert.deepEqual(helpers.getWarehouseAreasPaginationKey(), { |
| | | current: 'current', |
| | | size: 'pageSize' |
| | | }) |
| | | |
| | | assert.deepEqual( |
| | | helpers.buildWarehouseAreasPageQueryParams({ |
| | | current: 2, |
| | | pageSize: 30, |
| | | condition: ' 库区A ', |
| | | warehouseId: 8, |
| | | code: ' A01 ', |
| | | name: ' 一楼库区 ', |
| | | type: ' normal ', |
| | | shipperId: 5, |
| | | supplierId: 7, |
| | | status: 1, |
| | | memo: ' memo ' |
| | | }), |
| | | { |
| | | current: 2, |
| | | pageSize: 30, |
| | | condition: '库区A', |
| | | warehouseId: 8, |
| | | code: 'A01', |
| | | name: '一楼库区', |
| | | type: 'normal', |
| | | shipperId: 5, |
| | | supplierId: 7, |
| | | status: 1, |
| | | memo: 'memo' |
| | | } |
| | | ) |
| | | |
| | | assert.deepEqual( |
| | | helpers.buildWarehouseAreasSavePayload({ |
| | | id: '9', |
| | | warehouseId: '3', |
| | | code: ' A01 ', |
| | | name: ' 一楼库区 ', |
| | | type: ' normal ', |
| | | shipperId: '11', |
| | | supplierId: '12', |
| | | flagMinus: 1, |
| | | flagLabelMange: 0, |
| | | flagMix: 1, |
| | | status: '', |
| | | sort: '2', |
| | | memo: ' memo ' |
| | | }), |
| | | { |
| | | id: 9, |
| | | warehouseId: 3, |
| | | code: 'A01', |
| | | name: '一楼库区', |
| | | type: 'normal', |
| | | shipperId: 11, |
| | | supplierId: 12, |
| | | flagMinus: 1, |
| | | flagLabelMange: 0, |
| | | flagMix: 1, |
| | | status: 1, |
| | | sort: 2, |
| | | memo: 'memo' |
| | | } |
| | | ) |
| | | |
| | | const detail = helpers.normalizeWarehouseAreasDetailRecord({ |
| | | id: 1, |
| | | warehouseId: 4, |
| | | warehouseId$: '主仓', |
| | | type: 'A', |
| | | type$: '常温', |
| | | name: ' 一楼库区 ', |
| | | code: ' A01 ', |
| | | shipperId$: '货主A', |
| | | supplierId$: '供应商B', |
| | | flagMinus: 1, |
| | | flagLabelMange: 0, |
| | | flagMix: 1, |
| | | status: 1, |
| | | sort: 3, |
| | | memo: ' memo ', |
| | | createBy$: 'root', |
| | | createTime$: '2026-03-30 10:00:00', |
| | | updateBy$: 'root', |
| | | updateTime$: '2026-03-30 10:10:00' |
| | | }) |
| | | |
| | | assert.equal(detail.statusText, '正常') |
| | | assert.equal(detail.flagMixText, '是') |
| | | assert.equal(detail.warehouseName, '主仓') |
| | | assert.equal(detail.typeText, '常温') |
| | | assert.equal(detail.shipperName, '货主A') |
| | | assert.equal(detail.supplierName, '供应商B') |
| | | assert.equal(detail.memo, 'memo') |
| | | }) |
| | | |
| | | test('warehouse areas columns expose detail action slot and status tag', async () => { |
| | | const columnsSource = await readFile(fileURLToPath(columnsPath), 'utf8') |
| | | |
| | | assert.match(columnsSource, /createWarehouseAreasTableColumns/) |
| | | assert.match(columnsSource, /ArtButtonMore|ArtButtonTable/) |
| | | assert.match(columnsSource, /label: '操作'/) |
| | | assert.match(columnsSource, /useSlot: true|formatter:/) |
| | | assert.match(columnsSource, /label: '状态'/) |
| | | }) |
| | | |
| | | test('warehouse areas page uses real query, tree-like references and detail drawer structure', async () => { |
| | | const pageSource = await readFile(fileURLToPath(pagePath), 'utf8') |
| | | |
| | | assert.match(pageSource, /fetchWarehouseAreasPage/) |
| | | assert.match(pageSource, /fetchWarehouseAreasDetail/) |
| | | assert.match(pageSource, /fetchSaveWarehouseAreas/) |
| | | assert.match(pageSource, /fetchUpdateWarehouseAreas/) |
| | | assert.match(pageSource, /fetchDeleteWarehouseAreas/) |
| | | assert.match(pageSource, /WarehouseAreasDetailDrawer/) |
| | | assert.match(pageSource, /ArtSearchBar/) |
| | | assert.match(pageSource, /ArtTable/) |
| | | }) |
| | | |
| | | test('backend menu adapter releases warehouse areas route and static route is registered', async () => { |
| | | const backendMenuAdapterSource = await readFile(fileURLToPath(backendMenuAdapterPath), 'utf8') |
| | | const staticRoutesSource = await readFile(fileURLToPath(staticRoutesPath), 'utf8') |
| | | |
| | | assert.match(backendMenuAdapterSource, /warehouseAreas:\s*'\/basic-info\/warehouse-areas'/) |
| | | assert.match(staticRoutesSource, /path: 'warehouse-areas'/) |
| | | assert.match(staticRoutesSource, /title:\s*'menu\.warehouseAreas'/) |
| | | }) |