优化
This commit is contained in:
@@ -86,4 +86,25 @@ describe('CarrierRatesView price-book source contract', () => {
|
||||
expect(queryToolbarSource).toContain('flex-wrap: wrap')
|
||||
expect(source).toContain('@media (max-width: 720px)')
|
||||
})
|
||||
|
||||
it('keeps upload actions in the table toolbar instead of the query toolbar', () => {
|
||||
const queryToolbarEnd = source.indexOf('/>', source.indexOf('<QueryToolbar'))
|
||||
const queryToolbarBlock = source.slice(source.indexOf('<QueryToolbar'), queryToolbarEnd + 2)
|
||||
expect(queryToolbarBlock).not.toContain('#actions')
|
||||
expect(source).toContain('<template #toolbar-actions><el-button v-if="canEdit" type="primary" @click="openUpload">')
|
||||
expect(source).not.toContain('#toolbar-start')
|
||||
expect(source).toContain('<template #pagination-left>共 {{ rows.length }} 个版本')
|
||||
})
|
||||
|
||||
it('keeps list spacing and error state mutually exclusive', () => {
|
||||
expect(source).toContain('<template v-if="loadError" #state>')
|
||||
expect(source).toContain('<section v-if="!loadError" class="surface-panel table-panel"')
|
||||
expect(source).not.toContain('<template #state>')
|
||||
})
|
||||
|
||||
it('keeps both row actions directly visible without a premature more menu', () => {
|
||||
expect(source).toContain('<Eye :size="15" />{{ detailActionLabel')
|
||||
expect(source).toContain('<Download :size="15" />原文件')
|
||||
expect(source).not.toContain('MoreHorizontal')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { AlertTriangle, Download, Eye, FileSpreadsheet, FileUp, Ship, Warehouse } from '@lucide/vue'
|
||||
import { AlertTriangle, Download, Eye, FileSpreadsheet, FileUp, RefreshCw, Ship, Warehouse } from '@lucide/vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
||||
import { listAllPlatformWarehouses, type PlatformWarehouseResource } from '@/modules/warehouses'
|
||||
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
|
||||
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
|
||||
import AdminPage from '@/shared/components/AdminPage.vue'
|
||||
import AppDialog from '@/shared/components/AppDialog.vue'
|
||||
import AppDrawer from '@/shared/components/AppDrawer.vue'
|
||||
import DataTable, { type DataTableColumn } from '@/shared/components/DataTable.vue'
|
||||
import DetailPageLayout from '@/shared/components/DetailPageLayout.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 {
|
||||
@@ -593,30 +596,31 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="business-page price-book-page">
|
||||
<QueryToolbar
|
||||
v-model="filters"
|
||||
ariaLabel="船期价格表筛选"
|
||||
:fields="queryFields"
|
||||
preference-key="price-book-query"
|
||||
@search="applyQuery"
|
||||
>
|
||||
<template #actions>
|
||||
<el-button v-if="canEdit" type="primary" @click="openUpload"><FileUp :size="16" />上传价格表</el-button>
|
||||
<AdminPage as="div" class="price-book-page" spacing="normal">
|
||||
<ListPageLayout aria-label="承运商报价版本管理">
|
||||
<template #toolbar>
|
||||
<QueryToolbar
|
||||
v-model="filters"
|
||||
class="surface-panel"
|
||||
ariaLabel="船期价格表筛选"
|
||||
:fields="queryFields"
|
||||
preference-key="price-book-query"
|
||||
@search="applyQuery"
|
||||
/>
|
||||
</template>
|
||||
</QueryToolbar>
|
||||
|
||||
<LoadErrorState
|
||||
v-if="loadError"
|
||||
title="船期价格表版本加载失败"
|
||||
:message="loadErrorMessage"
|
||||
:loading="loading"
|
||||
@retry="loadPriceBooks()"
|
||||
>
|
||||
<template #details><ApiErrorMeta :details="loadError" /></template>
|
||||
</LoadErrorState>
|
||||
<template v-if="loadError" #state>
|
||||
<LoadErrorState
|
||||
title="船期价格表版本加载失败"
|
||||
:message="loadErrorMessage"
|
||||
:loading="loading"
|
||||
@retry="loadPriceBooks()"
|
||||
>
|
||||
<template #details><ApiErrorMeta :details="loadError" /></template>
|
||||
</LoadErrorState>
|
||||
</template>
|
||||
|
||||
<section class="surface-panel table-panel" aria-live="polite">
|
||||
<section v-if="!loadError" class="surface-panel table-panel" aria-live="polite">
|
||||
<DataTable
|
||||
v-model:page="page"
|
||||
v-model:page-size="pageSize"
|
||||
@@ -630,7 +634,7 @@ onMounted(() => {
|
||||
aria-label="船期价格表版本列表"
|
||||
empty-text="暂无船期价格表版本"
|
||||
>
|
||||
<template #toolbar-start><div><h2>价格表版本</h2><p>共 {{ rows.length }} 个版本 · {{ activeCount }} 个生效中 · {{ pendingCount }} 个待确认</p></div></template>
|
||||
<template #toolbar-actions><el-button v-if="canEdit" type="primary" @click="openUpload"><FileUp :size="16" />上传价格表</el-button></template>
|
||||
<template #actions="{ row }">
|
||||
<div class="table-actions">
|
||||
<el-button link type="primary" @click="openDetail(row as PlatformPriceBookVersion)"><Eye :size="15" />{{ detailActionLabel(row as PlatformPriceBookVersion) }}</el-button>
|
||||
@@ -647,8 +651,10 @@ onMounted(() => {
|
||||
<template #contents="{ row }"><div class="metric-cell"><strong>{{ (row as PlatformPriceBookVersion).channelCount }}</strong><span>渠道</span><strong>{{ (row as PlatformPriceBookVersion).rateCount }}</strong><span>价格行</span></div></template>
|
||||
<template #issues="{ row }"><div class="issue-counts"><span :class="{ 'is-blocking': (row as PlatformPriceBookVersion).blockingErrorCount }">阻断 {{ (row as PlatformPriceBookVersion).blockingErrorCount }}</span><span>警告 {{ (row as PlatformPriceBookVersion).warningCount }}</span></div></template>
|
||||
<template #empty><el-empty description="暂无船期价格表版本" /></template>
|
||||
<template #pagination-left>共 {{ rows.length }} 个版本 · {{ activeCount }} 个生效中 · {{ pendingCount }} 个待确认</template>
|
||||
</DataTable>
|
||||
</section>
|
||||
</section>
|
||||
</ListPageLayout>
|
||||
|
||||
<AppDialog
|
||||
v-model="uploadOpen"
|
||||
@@ -690,16 +696,16 @@ onMounted(() => {
|
||||
<strong>价格表详情暂时无法加载</strong>
|
||||
<p>{{ detailError.message }}</p>
|
||||
<ApiErrorMeta :details="detailError" />
|
||||
<el-button type="primary" @click="selectedVersionId && openDetail({ id: selectedVersionId } as PlatformPriceBookVersion)">重新加载</el-button>
|
||||
<el-button type="primary" @click="selectedVersionId && openDetail({ id: selectedVersionId } as PlatformPriceBookVersion)"><RefreshCw :size="16" />重新加载</el-button>
|
||||
</section>
|
||||
|
||||
<div v-else-if="detail" class="detail-content">
|
||||
<header class="detail-summary">
|
||||
<DetailPageLayout v-else-if="detail" class="detail-content" aria-label="船期价格表版本详情">
|
||||
<template #header><header class="detail-summary">
|
||||
<div class="detail-summary__title"><FileSpreadsheet :size="22" /><div><h3>{{ detail.version.originalFileName }}</h3><p>版本 V{{ detail.version.versionNo }} · 上传于 {{ formatDateTime(detail.version.createdAt) }}</p></div></div>
|
||||
<div class="detail-summary__actions"><el-tag :type="priceBookStatusMeta(detail.version.status).type" effect="light">{{ priceBookStatusMeta(detail.version.status).label }}</el-tag><el-button :loading="downloadingIds.has(detail.version.id)" @click="downloadSource(detail.version)"><Download :size="16" />下载原文件</el-button></div>
|
||||
</header>
|
||||
</header></template>
|
||||
|
||||
<section class="detail-section version-facts">
|
||||
<template #summary><section class="detail-section version-facts">
|
||||
<dl class="detail-grid">
|
||||
<div><dt>解析渠道</dt><dd>{{ detail.version.channelCount }}</dd></div>
|
||||
<div><dt>解析价格行</dt><dd>{{ detail.version.rateCount }}</dd></div>
|
||||
@@ -707,7 +713,7 @@ onMounted(() => {
|
||||
<div><dt>警告</dt><dd>{{ detail.version.warningCount }}</dd></div>
|
||||
<div><dt>实际生效区间</dt><dd>{{ formatEffectivePeriod(detail.version) }}</dd></div>
|
||||
</dl>
|
||||
</section>
|
||||
</section></template>
|
||||
|
||||
<section v-if="isPendingDetail && canEdit" class="detail-section mapping-section">
|
||||
<header class="detail-section__heading"><div><h3><Warehouse :size="17" />仓库映射</h3><p>来源编码必须匹配启用的平台公共仓库编码。</p></div></header>
|
||||
@@ -722,7 +728,7 @@ onMounted(() => {
|
||||
|
||||
<div v-if="mappingWarehousesError" class="mapping-load-error" role="alert">
|
||||
<div><strong>平台仓库候选加载失败</strong><p>{{ mappingWarehousesError.message }}</p><ApiErrorMeta :details="mappingWarehousesError" /></div>
|
||||
<el-button :loading="mappingWarehousesLoading" @click="loadMappingWarehouses(true)">重新加载</el-button>
|
||||
<el-button :loading="mappingWarehousesLoading" @click="loadMappingWarehouses(true)"><RefreshCw :size="16" />重新加载</el-button>
|
||||
</div>
|
||||
<div v-else-if="mappingWarehousesLoaded" class="mapping-groups">
|
||||
<section class="mapping-group">
|
||||
@@ -789,9 +795,9 @@ onMounted(() => {
|
||||
</el-table>
|
||||
</template>
|
||||
</section>
|
||||
</div>
|
||||
</DetailPageLayout>
|
||||
</AppDrawer>
|
||||
</div>
|
||||
</AdminPage>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
Reference in New Issue
Block a user