This commit is contained in:
2026-08-10 16:24:54 +08:00
parent e119b48662
commit 820432a381
52 changed files with 1015 additions and 705 deletions
@@ -190,9 +190,10 @@ describe('LocationsView', () => {
const tableToolbar = wrapper.find('.table-panel .data-table__toolbar')
expect(tableToolbar.exists()).toBe(true)
expect(tableToolbar.text()).toContain('库位列表')
expect(tableToolbar.text()).toContain('共 2 个库位')
expect(tableToolbar.text()).toContain('新增库位')
expect(tableToolbar.text()).not.toContain('库位列表')
expect(tableToolbar.text()).not.toContain('共 2 个库位')
expect(wrapper.find('.data-table__pagination-left').text()).toContain('共 2 个库位')
expect(wrapper.findComponent(QueryToolbar).text()).not.toContain('新增库位')
expect(listAllPlatformWarehouses).toHaveBeenCalledWith({ warehouseType: 'DOMESTIC' })
expect(listPlatformLocations).toHaveBeenLastCalledWith({
@@ -1,12 +1,17 @@
<script setup lang="ts">
import { computed, onMounted, reactive, ref, watch } from 'vue'
import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus'
import { Eye, MapPin, MoreHorizontal, Pencil, Plus, Power, Trash2 } from '@lucide/vue'
import { Eye, MapPin, MoreHorizontal, Pencil, Plus, Power, Save, Trash2 } from '@lucide/vue'
import { useAccessPreviewStore } from '@/core/access/accessPreview'
import { useUnsavedWorkRegistration } from '@/core/unsaved-work/useUnsavedWorkRegistration'
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
import AdminPage from '@/shared/components/AdminPage.vue'
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
import AppDrawer from '@/shared/components/AppDrawer.vue'
import DataTable, { type DataTableColumn } from '@/shared/components/DataTable.vue'
import FormSection from '@/shared/components/FormSection.vue'
import ListPageLayout from '@/shared/components/ListPageLayout.vue'
import LoadErrorState from '@/shared/components/LoadErrorState.vue'
import QueryToolbar, { type QueryField, type QueryModel } from '@/shared/components/QueryToolbar.vue'
import {
changePlatformLocationStatus,
@@ -499,32 +504,38 @@ onMounted(() => {
</script>
<template>
<div class="business-page location-page">
<QueryToolbar
v-model="filters"
ariaLabel="库位筛选"
:fields="queryFields"
preference-key="locations-query"
@search="applyFilters"
/>
<AdminPage as="div" class="location-page">
<ListPageLayout aria-label="库位列表页面">
<template #toolbar>
<QueryToolbar
v-model="filters"
ariaLabel="库位筛选"
:fields="queryFields"
preference-key="locations-query"
@search="applyFilters"
/>
</template>
<template #state>
<LoadErrorState
v-if="warehouseError"
title="仓库列表加载失败"
:message="warehouseError.message"
:loading="warehouseLoading"
@retry="loadWarehouses"
/>
<el-alert v-else-if="!warehouseLoading && domesticWarehouses.length === 0" type="info" :closable="false" show-icon title="暂无可选国内仓">
请先在仓库管理中建立国内仓,再维护库位。
</el-alert>
<LoadErrorState
v-else-if="loadError"
title="库位列表加载失败"
:message="loadError.message"
:loading="loading"
@retry="loadLocations"
/>
</template>
<el-alert v-if="warehouseError" type="error" :closable="false" show-icon class="state-alert">
<template #title>仓库列表加载失败</template>
<p>{{ warehouseError.message }}</p>
<ApiErrorMeta :details="warehouseError" />
<el-button size="small" @click="loadWarehouses()">重新加载仓库</el-button>
</el-alert>
<el-alert v-else-if="!warehouseLoading && domesticWarehouses.length === 0" type="info" :closable="false" show-icon title="暂无可选国内仓">
请先在仓库管理中建立国内仓,再维护库位。
</el-alert>
<el-alert v-if="loadError" type="error" :closable="false" show-icon class="state-alert">
<template #title>库位列表加载失败</template>
<p>{{ loadError.message }}</p>
<ApiErrorMeta :details="loadError" />
<el-button size="small" @click="loadLocations()">重新查询</el-button>
</el-alert>
<section class="surface-panel table-panel" aria-label="库位列表区域">
<section v-if="!loadError" class="surface-panel table-panel" aria-label="库位列表区域">
<DataTable
v-model:page="page"
v-model:page-size="pageSize"
@@ -538,7 +549,6 @@ onMounted(() => {
aria-label="库位列表"
:empty-text="emptyDescription"
>
<template #toolbar-start><div><h2>库位列表</h2><p>共 {{ total }} 个库位</p></div></template>
<template #toolbar-actions><el-button v-if="access.can('platform:location:create')" type="primary" @click="openCreate"><Plus :size="16" />新增库位</el-button></template>
<template #actions="{ row }">
<div class="table-actions location-actions">
@@ -558,7 +568,7 @@ onMounted(() => {
:disabled="loading || actionLoadingIds.has(String((row as PlatformLocationResource).id))"
@command="handleMoreAction(row as PlatformLocationResource, String($event))"
>
<el-button link type="primary" aria-label="更多库位操作"><MoreHorizontal :size="16" />更多</el-button>
<el-button link type="primary" aria-label="更多库位操作" title="更多库位操作"><MoreHorizontal :size="16" /></el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-if="canChangeStatus(row as PlatformLocationResource)" command="status">
@@ -610,18 +620,22 @@ onMounted(() => {
</template>
<template #pagination-left="{ total: tableTotal }"><span>共 {{ tableTotal }} 个库位</span></template>
</DataTable>
</section>
</section>
</ListPageLayout>
<el-drawer
<AppDrawer
v-model="drawerOpen"
:title="drawerTitle"
size="min(620px, 100vw)"
class="business-drawer location-drawer"
size="min(620px, 94vw)"
class="location-drawer"
:loading="drawerLoading"
:confirm-loading="saving"
:show-confirm="false"
:close-on-click-modal="false"
:close-on-press-escape="!saving"
:show-close="!saving"
>
<div v-loading="drawerLoading" class="drawer-content">
<div class="drawer-content">
<el-alert v-if="drawerError" type="error" :closable="false" show-icon title="库位详情加载失败">
<p>{{ drawerError.message }}</p>
<ApiErrorMeta :details="drawerError" />
@@ -689,9 +703,7 @@ onMounted(() => {
/>
</el-select>
</el-form-item>
<section class="form-section">
<h3>库位标识</h3>
<p class="section-description">库位编码在当前仓库内唯一;编辑后不能移动到其他仓库。</p>
<FormSection title="库位标识" description="库位编码在当前仓库内唯一;编辑后不能移动到其他仓库。">
<div class="form-grid two-cols">
<el-form-item label="库位编码" prop="locationCode" required>
<el-input v-model.trim="form.locationCode" maxlength="50" placeholder="例如 A1-01" />
@@ -706,10 +718,9 @@ onMounted(() => {
<el-input v-model.trim="form.zoneName" maxlength="100" placeholder="例如 常规区" />
</el-form-item>
</div>
</section>
</FormSection>
<section class="form-section">
<h3>资源属性</h3>
<FormSection title="资源属性">
<div class="form-grid two-cols">
<el-form-item label="库位类型" prop="locationType" required>
<el-select v-model="form.locationType" placeholder="请选择库位类型">
@@ -735,25 +746,24 @@ onMounted(() => {
<el-tag :type="statusMeta[form.status].type">{{ statusMeta[form.status].label }}</el-tag>
<small>状态只能通过列表中的启停操作变更。</small>
</div>
</section>
</FormSection>
<section class="form-section">
<h3>备注</h3>
<FormSection title="备注">
<el-form-item label="内部说明">
<el-input v-model.trim="form.remark" type="textarea" :rows="4" maxlength="500" show-word-limit placeholder="请输入库位位置、使用限制或其他说明" />
</el-form-item>
</section>
</FormSection>
</el-form>
</div>
<template #footer>
<div class="drawer-footer">
<el-button :disabled="saving" @click="drawerOpen = false">{{ drawerMode === 'view' ? '关闭' : '取消' }}</el-button>
<el-button v-if="drawerMode !== 'view'" type="primary" :loading="saving" @click="saveLocation">保存库位</el-button>
<el-button v-if="drawerMode !== 'view'" type="primary" :loading="saving" @click="saveLocation"><Save :size="16" />保存库位</el-button>
</div>
</template>
</el-drawer>
</div>
</AppDrawer>
</AdminPage>
</template>
<style scoped>