diff --git a/src/modules/fba-pricing/carrier-rates/pages/CarrierRatesView.test.ts b/src/modules/fba-pricing/carrier-rates/pages/CarrierRatesView.test.ts index 4be83e8c..863950b1 100644 --- a/src/modules/fba-pricing/carrier-rates/pages/CarrierRatesView.test.ts +++ b/src/modules/fba-pricing/carrier-rates/pages/CarrierRatesView.test.ts @@ -64,6 +64,13 @@ describe('CarrierRatesView source contract', () => { expect(source).toContain('customerLabel(detail.tenantId)') }) + it('loads shared carrier master data under the platform public scope', () => { + expect(source).toContain('listPlatformPartners({ page: 1, size: 200, type: \'CARRIER\', status: \'ACTIVE\' })') + expect(source).toContain('平台仓库或船司资料加载失败') + expect(source).not.toContain('listPlatformPartners(customerId,') + expect(source).not.toContain('所属企业的仓库或船司资料加载失败') + }) + it('inherits QueryToolbar wrapping and full-width mobile controls', () => { expect(queryToolbarSource).toContain('flex-wrap: wrap') expect(queryToolbarSource).toContain('@media (max-width: 720px)') diff --git a/src/modules/fba-pricing/carrier-rates/pages/CarrierRatesView.vue b/src/modules/fba-pricing/carrier-rates/pages/CarrierRatesView.vue index ed2e4694..20b92b04 100644 --- a/src/modules/fba-pricing/carrier-rates/pages/CarrierRatesView.vue +++ b/src/modules/fba-pricing/carrier-rates/pages/CarrierRatesView.vue @@ -276,7 +276,7 @@ async function loadCustomerMasterData(customerId: string) { try { const [warehouseResult, partnerResult] = await Promise.all([ listPlatformWarehouses(1, 200), - listPlatformPartners(customerId, { page: 1, size: 200, type: 'CARRIER', status: 'ACTIVE' }), + listPlatformPartners({ page: 1, size: 200, type: 'CARRIER', status: 'ACTIVE' }), ]) if (sequence !== masterDataSequence) return false warehouses.value = warehouseResult.records.map((item) => ({ ...item, id: String(item.id), tenantId: String(item.tenantId) })) @@ -288,7 +288,7 @@ async function loadCustomerMasterData(customerId: string) { warehouses.value = [] carriers.value = [] loadedCustomerId.value = null - ElMessage.error(getApiErrorDetails(error, '所属企业的仓库或船司资料加载失败').message) + ElMessage.error(getApiErrorDetails(error, '平台仓库或船司资料加载失败').message) } return false } finally { diff --git a/src/modules/master-data/counterparties/api/platformPartners.test.ts b/src/modules/master-data/counterparties/api/platformPartners.test.ts index 4b857f4e..9ad029fd 100644 --- a/src/modules/master-data/counterparties/api/platformPartners.test.ts +++ b/src/modules/master-data/counterparties/api/platformPartners.test.ts @@ -37,13 +37,13 @@ const payload: SavePlatformPartnerRequest = { describe('platform partner API', () => { beforeEach(() => vi.clearAllMocks()) - it('queries the platform partner page with tenant and filters', async () => { + it('queries the platform partner page with filters only', async () => { vi.mocked(http.get).mockResolvedValue({ code: '200', message: 'success', data: { records: [], page: '1', size: '20', total: '1', pages: '1' } }) - const result = await listPlatformPartners(10001, { page: 1, size: 20, keyword: '测试', type: 'CUSTOMER', status: 'ACTIVE' }) + const result = await listPlatformPartners({ page: 1, size: 20, keyword: '测试', type: 'CUSTOMER', status: 'ACTIVE' }) expect(http.get).toHaveBeenCalledWith('/platform/v1/partners', { - params: { tenantId: 10001, page: 1, size: 20, keyword: '测试', type: 'CUSTOMER', status: 'ACTIVE' }, + params: { page: 1, size: 20, keyword: '测试', type: 'CUSTOMER', status: 'ACTIVE' }, }) expect(result).toMatchObject({ page: 1, size: 20, total: 1, pages: 1 }) }) @@ -53,13 +53,13 @@ describe('platform partner API', () => { vi.mocked(http.put).mockResolvedValue({ code: '200', message: 'success', data: null }) vi.mocked(http.patch).mockResolvedValue({ code: '200', message: 'success', data: null }) - await createPlatformPartner(10001, payload) - await updatePlatformPartner(10001, '19001', payload) - await changePlatformPartnerStatus(10001, '19001', 'INACTIVE') + await createPlatformPartner(payload) + await updatePlatformPartner('19001', payload) + await changePlatformPartnerStatus('19001', 'INACTIVE') - expect(http.post).toHaveBeenCalledWith('/platform/v1/partners', payload, { params: { tenantId: 10001 } }) - expect(http.put).toHaveBeenCalledWith('/platform/v1/partners/19001', payload, { params: { tenantId: 10001 } }) - expect(http.patch).toHaveBeenCalledWith('/platform/v1/partners/19001/status', { status: 'INACTIVE' }, { params: { tenantId: 10001 } }) + expect(http.post).toHaveBeenCalledWith('/platform/v1/partners', payload, { params: { tenantId: 0 } }) + expect(http.put).toHaveBeenCalledWith('/platform/v1/partners/19001', payload, { params: { tenantId: 0 } }) + expect(http.patch).toHaveBeenCalledWith('/platform/v1/partners/19001/status', { status: 'INACTIVE' }, { params: { tenantId: 0 } }) }) it('normalizes legacy detail types and uses import contracts', async () => { @@ -75,17 +75,17 @@ describe('platform partner API', () => { fileName: 'partners.xlsx', totalRows: 1, importedRows: 1, success: true, errors: [], } }) - const detail = await getPlatformPartner(10001, '19001') + const detail = await getPlatformPartner('19001') const template = await downloadPlatformPartnerImportTemplate() const file = new File(['xlsx'], 'partners.xlsx') - const result = await importPlatformPartners(10001, file) + const result = await importPlatformPartners(file) expect(detail.types).toEqual(['CUSTOMER', 'SUPPLIER']) expect(detail.dataScopeId).toBe(10001) expect(template).toBeInstanceOf(Blob) expect(result.success).toBe(true) - expect(http.get).toHaveBeenNthCalledWith(1, '/platform/v1/partners/19001', { params: { tenantId: 10001 } }) + expect(http.get).toHaveBeenNthCalledWith(1, '/platform/v1/partners/19001', { params: { tenantId: 0 } }) expect(http.get).toHaveBeenNthCalledWith(2, '/platform/v1/partners/import-template', { responseType: 'blob' }) - expect(http.post).toHaveBeenCalledWith('/platform/v1/partners/import', expect.any(FormData), { params: { tenantId: 10001 } }) + expect(http.post).toHaveBeenCalledWith('/platform/v1/partners/import', expect.any(FormData), { params: { tenantId: 0 } }) }) }) diff --git a/src/modules/master-data/counterparties/api/platformPartners.ts b/src/modules/master-data/counterparties/api/platformPartners.ts index 3960b457..17c4e203 100644 --- a/src/modules/master-data/counterparties/api/platformPartners.ts +++ b/src/modules/master-data/counterparties/api/platformPartners.ts @@ -83,6 +83,7 @@ export interface PlatformPartnerImportResult { } const basePath = '/platform/v1/partners' +const platformResourceScope = 0 function normalizePartner(resource: PlatformPartnerResource): PlatformPartnerResource { const legacyTypes: PlatformPartnerType[] = resource.partnerType === 'BOTH' @@ -96,9 +97,9 @@ function normalizePartner(resource: PlatformPartnerResource): PlatformPartnerRes } } -export async function listPlatformPartners(tenantId: number | string | undefined, query: PlatformPartnerQuery) { +export async function listPlatformPartners(query: PlatformPartnerQuery) { const response = await http.get, ApiEnvelope>(basePath, { - params: { tenantId, ...query }, + params: query, }) const page = unwrapApiResponse(response) return { @@ -111,10 +112,10 @@ export async function listPlatformPartners(tenantId: number | string | undefined } } -export async function getPlatformPartner(tenantId: number | string, partnerId: number | string) { +export async function getPlatformPartner(partnerId: number | string) { const response = await http.get, ApiEnvelope>( `${basePath}/${partnerId}`, - { params: { tenantId } }, + { params: { tenantId: platformResourceScope } }, ) return normalizePartner(unwrapApiResponse(response)) } @@ -123,42 +124,40 @@ export async function downloadPlatformPartnerImportTemplate() { return http.get(`${basePath}/import-template`, { responseType: 'blob' }) } -export async function importPlatformPartners(tenantId: number | string, file: File) { +export async function importPlatformPartners(file: File) { const form = new FormData() form.append('file', file) const response = await http.post, ApiEnvelope>( `${basePath}/import`, form, - { params: { tenantId } }, + { params: { tenantId: platformResourceScope } }, ) return unwrapApiResponse(response) } -export async function createPlatformPartner(tenantId: number | string, payload: SavePlatformPartnerRequest) { +export async function createPlatformPartner(payload: SavePlatformPartnerRequest) { const response = await http.post, ApiEnvelope>(basePath, payload, { - params: { tenantId }, + params: { tenantId: platformResourceScope }, }) return unwrapApiResponse(response) } export async function updatePlatformPartner( - tenantId: number | string, partnerId: number | string, payload: SavePlatformPartnerRequest, ) { const response = await http.put, ApiEnvelope>(`${basePath}/${partnerId}`, payload, { - params: { tenantId }, + params: { tenantId: platformResourceScope }, }) return unwrapApiResponse(response) } export async function changePlatformPartnerStatus( - tenantId: number | string, partnerId: number | string, status: PlatformPartnerStatus, ) { const response = await http.patch, ApiEnvelope>(`${basePath}/${partnerId}/status`, { status }, { - params: { tenantId }, + params: { tenantId: platformResourceScope }, }) return unwrapApiResponse(response) } diff --git a/src/modules/master-data/counterparties/pages/PartnersView.test.ts b/src/modules/master-data/counterparties/pages/PartnersView.test.ts index 3a1bd78b..9378b53f 100644 --- a/src/modules/master-data/counterparties/pages/PartnersView.test.ts +++ b/src/modules/master-data/counterparties/pages/PartnersView.test.ts @@ -3,13 +3,13 @@ import { createPinia, setActivePinia } from 'pinia' import { enableAutoUnmount, flushPromises, mount } from '@vue/test-utils' import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { + getPlatformPartner, listPlatformPartners, type PlatformPartnerResource, } from '@/modules/master-data/counterparties/api/platformPartners' import { useAccessPreviewStore } from '@/core/access/accessPreview' import { useAuthStore } from '@/core/auth/auth' import QueryToolbar from '@/shared/components/QueryToolbar.vue' -import { listPlatformTenants, type PlatformTenantResource } from '../../../iam/api/platformIam' import PartnersView from './PartnersView.vue' vi.mock('@/modules/master-data/counterparties/api/platformPartners', () => ({ @@ -22,8 +22,6 @@ vi.mock('@/modules/master-data/counterparties/api/platformPartners', () => ({ changePlatformPartnerStatus: vi.fn(), })) -vi.mock('../../../iam/api/platformIam', () => ({ listPlatformTenants: vi.fn() })) - class ResizeObserverStub implements ResizeObserver { observe() {} unobserve() {} @@ -44,8 +42,8 @@ function createMemoryStorage(): Storage { const partner: PlatformPartnerResource = { id: '19001', - tenantId: 10001, - dataScopeId: 10001, + tenantId: 0, + dataScopeId: 0, partnerCode: 'CUS-001', partnerName: '深圳示例贸易有限公司', partnerType: 'CUSTOMER', @@ -64,16 +62,6 @@ const partner: PlatformPartnerResource = { updatedAt: '2026-07-30T22:45:00', } -const tenant = (id: number | string, tenantName: string): PlatformTenantResource => ({ - id, tenantCode: String(id), tenantName, status: 'ACTIVE', legalName: tenantName, - legalRepresentative: null, unifiedSocialCreditCode: null, contactName: null, - contactPhone: null, contactEmail: null, servicePackage: null, contactWechat: null, - salesOwnerName: null, paymentTermDays: null, enterpriseType: null, establishedDate: null, - registeredCapital: null, registeredCapitalCurrency: null, registeredAddress: null, - internalRemark: null, createdAt: null, approvedAt: null, -}) -const snowflakeTenantId = '9223372036854775806' - async function mountView() { vi.stubGlobal('localStorage', createMemoryStorage()) vi.stubGlobal('sessionStorage', createMemoryStorage()) @@ -103,22 +91,20 @@ describe('PartnersView real API integration', () => { beforeEach(() => { vi.stubGlobal('ResizeObserver', ResizeObserverStub) document.body.innerHTML = '' - vi.mocked(listPlatformTenants).mockReset().mockResolvedValue({ - records: [tenant(10001, '默认企业'), tenant(snowflakeTenantId, '雪花企业')], page: 1, size: 200, total: 2, pages: 1, - }) vi.mocked(listPlatformPartners).mockReset().mockResolvedValue({ records: [partner], page: 1, size: 20, total: 1, pages: 1, }) + vi.mocked(getPlatformPartner).mockReset().mockResolvedValue(partner) }) - it('loads all enterprises by default and renders the real partner fields', async () => { + it('loads platform partner data and renders the real partner fields', async () => { const wrapper = await mountView() - expect(listPlatformPartners).toHaveBeenCalledWith(undefined, { + expect(listPlatformPartners).toHaveBeenCalledWith({ page: 1, size: 20, keyword: undefined, type: undefined, status: undefined, }) - expect(wrapper.find('input[aria-label="所属企业"]').exists()).toBe(true) - expect(wrapper.text()).toContain('全部企业 · 共 1 个往来单位') + expect(wrapper.find('input[aria-label="所属企业"]').exists()).toBe(false) + expect(wrapper.text()).toContain('平台公共数据 · 共 1 个往来单位') expect(wrapper.text()).toContain('深圳示例贸易有限公司') expect(wrapper.text()).toContain('CUS-001') expect(wrapper.text()).toContain('contact@example.com') @@ -130,18 +116,30 @@ describe('PartnersView real API integration', () => { expect(wrapper.find('.table-panel').exists()).toBe(true) }) - it('preserves a snowflake tenant id as a string when switching enterprises', async () => { + it('opens the create form without selecting an enterprise', async () => { const wrapper = await mountView() - - const toolbar = wrapper.findComponent(QueryToolbar) - const model = { tenantId: snowflakeTenantId, keyword: '', type: '', status: '' } - toolbar.vm.$emit('update:modelValue', model) - toolbar.vm.$emit('search', model) + const createButton = wrapper.findAll('button').find((button) => button.text().trim() === '新增往来单位') + await createButton?.trigger('click') await flushPromises() - expect(listPlatformPartners).toHaveBeenLastCalledWith(snowflakeTenantId, { - page: 1, size: 20, keyword: undefined, type: undefined, status: undefined, - }) + expect(document.body.textContent).toContain('新增往来单位') + expect(document.body.textContent).not.toContain('请先在筛选条件中选择所属企业') + }) + + it('renders a padded detail layout without an empty data-scope cell', async () => { + const wrapper = await mountView() + const detailButton = wrapper.findAll('button').find((button) => button.text().trim() === '详情') + await detailButton?.trigger('click') + await flushPromises() + + const detailContent = document.querySelector('.detail-drawer .partner-detail') + const sections = detailContent?.querySelectorAll('section') + const otherInfo = sections?.item(sections.length - 1) + expect(detailContent).not.toBeNull() + expect(otherInfo?.querySelectorAll('.detail-grid > .detail-span')).toHaveLength(2) + expect(otherInfo?.textContent).toContain('更新时间') + expect(otherInfo?.textContent).toContain('长期合作单位') + expect(document.body.textContent).not.toContain('数据范围') }) it('updates filters locally and only requests after search', async () => { @@ -158,7 +156,7 @@ describe('PartnersView real API integration', () => { await flushPromises() expect(listPlatformPartners).toHaveBeenCalledTimes(2) - expect(listPlatformPartners).toHaveBeenLastCalledWith(undefined, { + expect(listPlatformPartners).toHaveBeenLastCalledWith({ page: 1, size: 20, keyword: '深圳', type: undefined, status: undefined, }) }) @@ -166,7 +164,7 @@ describe('PartnersView real API integration', () => { it('shows useful placeholders and visible required markers in the create form', async () => { const wrapper = await mountView() const toolbar = wrapper.findComponent(QueryToolbar) - const model = { tenantId: '10001', keyword: '', type: '', status: '' } + const model = { keyword: '', type: '', status: '' } toolbar.vm.$emit('update:modelValue', model) toolbar.vm.$emit('search', model) await flushPromises() diff --git a/src/modules/master-data/counterparties/pages/PartnersView.vue b/src/modules/master-data/counterparties/pages/PartnersView.vue index ce456fc7..d8278849 100644 --- a/src/modules/master-data/counterparties/pages/PartnersView.vue +++ b/src/modules/master-data/counterparties/pages/PartnersView.vue @@ -23,7 +23,6 @@ import { type SavePlatformPartnerRequest, } from '@/modules/master-data/counterparties/api/platformPartners' import { useAccessPreviewStore } from '@/core/access/accessPreview' -import { listPlatformTenants, type PlatformTenantResource } from '@/modules/iam' const pageSize = ref(20) const access = useAccessPreviewStore() @@ -42,36 +41,23 @@ const formRef = ref() const rows = ref([]) const page = ref(1) const total = ref(0) -const tenants = ref([]) -const tenantLoadError = ref(null) -const editorTenantId = ref('') const loadError = ref(null) const statusLoadingIds = ref(new Set()) let loadSequence = 0 type PartnerFilters = QueryModel & { - tenantId: string keyword: string type: PlatformPartnerType | '' status: PlatformPartnerStatus | '' } const filters = ref({ - tenantId: '', keyword: '', type: '', status: '', }) const appliedFilters = ref({ ...filters.value }) const queryFields = computed(() => [ - { - key: 'tenantId', - type: 'select', - width: 180, - placeholder: tenantLoadError.value ? '企业列表加载失败' : '全部企业', - props: { 'aria-label': '所属企业', filterable: true, disabled: Boolean(tenantLoadError.value) }, - options: tenants.value.map((tenant) => ({ value: String(tenant.id), label: `${tenant.tenantName}(${tenant.tenantCode || tenant.id})` })), - }, { key: 'keyword', type: 'input', placeholder: '单位名称、编码、联系人或电话' }, { key: 'type', @@ -130,13 +116,6 @@ const { captureBaseline } = useUnsavedWorkRegistration({ requiredPermission: editorPermission, }) -const selectedEnterpriseId = computed(() => appliedFilters.value.tenantId) -const targetEnterpriseId = computed(() => filters.value.tenantId) -const selectedEnterpriseName = computed(() => { - if (!selectedEnterpriseId.value) return '全部企业' - return tenants.value.find((tenant) => String(tenant.id) === selectedEnterpriseId.value)?.tenantName || `企业 ${selectedEnterpriseId.value}` -}) - const rules: FormRules = { partnerCode: [{ required: true, whitespace: true, message: '请输入往来单位编码', trigger: 'blur' }], partnerName: [{ required: true, whitespace: true, message: '请输入往来单位名称', trigger: 'blur' }], @@ -168,7 +147,6 @@ function partnerTypeLabel(type: PlatformPartnerLegacyType) { const partnerColumns: DataTableColumn[] = [ { key: 'actions', label: '操作', width: 210, fixed: 'left', slotName: 'actions' }, { key: 'partner', label: '往来单位', minWidth: 210, slotName: 'partner' }, - { key: 'tenant', label: '所属企业', minWidth: 190, showOverflowTooltip: true, slotName: 'tenant' }, { key: 'type', label: '类型', minWidth: 115, slotName: 'type' }, { key: 'contact', label: '联系人', minWidth: 165, slotName: 'contact' }, { key: 'address', label: '地址', minWidth: 240, slotName: 'address' }, @@ -196,49 +174,12 @@ function queryParams() { } } -function apiTenantId(value: string | undefined) { - if (!value) return undefined - const numeric = Number(value) - return Number.isSafeInteger(numeric) ? numeric : value -} - -function requireSelectedEnterprise(action: string) { - if (!targetEnterpriseId.value) { - ElMessage.warning(`请先在筛选条件中选择所属企业,再${action}`) - return null - } - return apiTenantId(targetEnterpriseId.value)! -} - -function tenantName(tenantId: number | string) { - return tenants.value.find((tenant) => String(tenant.id) === String(tenantId))?.tenantName || `企业 ${tenantId}` -} - -async function loadTenants() { - tenantLoadError.value = null - try { - const records: PlatformTenantResource[] = [] - let currentPage = 1 - let pages = 1 - do { - const result = await listPlatformTenants({ page: currentPage, size: 200 }) - records.push(...result.records) - pages = Math.max(1, result.pages) - currentPage += 1 - } while (currentPage <= pages) - tenants.value = records - } catch (error) { - tenants.value = [] - tenantLoadError.value = getApiErrorDetails(error, '企业列表加载失败,暂时无法按所属企业筛选') - } -} - async function loadPartners() { const sequence = ++loadSequence loading.value = true loadError.value = null try { - const result = await listPlatformPartners(apiTenantId(appliedFilters.value.tenantId), queryParams()) + const result = await listPlatformPartners(queryParams()) if (sequence !== loadSequence) return rows.value = result.records total.value = result.total @@ -254,7 +195,6 @@ async function loadPartners() { function searchPartners(model: QueryModel) { appliedFilters.value = { - tenantId: String(model.tenantId || ''), keyword: String(model.keyword || ''), type: (model.type || '') as PlatformPartnerType | '', status: (model.status || '') as PlatformPartnerStatus | '', @@ -267,9 +207,6 @@ function searchPartners(model: QueryModel) { } function openCreate() { - const tenantId = requireSelectedEnterprise('新增往来单位') - if (!tenantId) return - editorTenantId.value = String(tenantId) Object.assign(form, emptyForm()) captureBaseline() editorOpen.value = true @@ -281,7 +218,6 @@ function nullable(value: string | null) { } function openEdit(row: PlatformPartnerResource) { - editorTenantId.value = String(row.tenantId) Object.assign(form, { id: String(row.id), partnerCode: row.partnerCode, @@ -327,12 +263,11 @@ function savePayload(): SavePlatformPartnerRequest { } async function openDetail(row: PlatformPartnerResource) { - const tenantId = apiTenantId(String(row.tenantId))! detailOpen.value = true detailLoading.value = true detail.value = null try { - detail.value = await getPlatformPartner(tenantId, row.id) + detail.value = await getPlatformPartner(row.id) } catch (error) { const meta = getApiErrorDetails(error, '往来单位详情加载失败') ElMessage.error(meta.requestId ? `${meta.message}(requestId: ${meta.requestId})` : meta.message) @@ -343,7 +278,6 @@ async function openDetail(row: PlatformPartnerResource) { } function openImport() { - if (!requireSelectedEnterprise('批量导入')) return importFile.value = null importResult.value = null importOpen.value = true @@ -376,8 +310,6 @@ async function downloadTemplate() { } async function submitImport() { - const tenantId = requireSelectedEnterprise('批量导入') - if (!tenantId) return if (!importFile.value) { ElMessage.warning('请先选择 XLSX 文件') return @@ -385,7 +317,7 @@ async function submitImport() { importing.value = true importResult.value = null try { - importResult.value = await importPlatformPartners(tenantId, importFile.value) + importResult.value = await importPlatformPartners(importFile.value) if (importResult.value.success) { ElMessage.success(`成功导入 ${importResult.value.importedRows} 条往来单位`) await loadPartners() @@ -415,18 +347,16 @@ function downloadImportErrors() { } async function savePartner() { - const tenantId = form.id ? apiTenantId(editorTenantId.value) : requireSelectedEnterprise('新增往来单位') - if (!tenantId) return const valid = await formRef.value?.validate().catch(() => false) if (!valid) return saving.value = true try { const payload = savePayload() if (form.id) { - await updatePlatformPartner(tenantId, form.id, payload) + await updatePlatformPartner(form.id, payload) ElMessage.success('往来单位已更新') } else { - await createPlatformPartner(tenantId, payload) + await createPlatformPartner(payload) ElMessage.success('往来单位已新增') } editorOpen.value = false @@ -440,7 +370,6 @@ async function savePartner() { } async function toggleStatus(row: PlatformPartnerResource) { - const tenantId = apiTenantId(String(row.tenantId))! const nextStatus: PlatformPartnerStatus = row.status === 'ACTIVE' ? 'INACTIVE' : 'ACTIVE' const action = nextStatus === 'ACTIVE' ? '启用' : '停用' try { @@ -455,7 +384,7 @@ async function toggleStatus(row: PlatformPartnerResource) { const id = String(row.id) statusLoadingIds.value = new Set(statusLoadingIds.value).add(id) try { - await changePlatformPartnerStatus(tenantId, row.id, nextStatus) + await changePlatformPartnerStatus(row.id, nextStatus) ElMessage.success(`往来单位已${action}`) await loadPartners() } catch (error) { @@ -474,7 +403,6 @@ function formatUpdatedAt(value?: string | null) { watch([page, pageSize], () => { void loadPartners() }) onMounted(() => { - void loadTenants() void loadPartners() }) @@ -483,12 +411,6 @@ onMounted(() => {
- - -

{{ tenantLoadError.message }};往来单位仍按全部企业展示。

- - 重新加载企业列表 -

其他信息

-
数据范围
{{ detail.dataScopeId }}
更新时间
{{ formatUpdatedAt(detail.updatedAt) }}
+
更新时间
{{ formatUpdatedAt(detail.updatedAt) }}
备注
{{ detail.remark || '-' }}
@@ -622,8 +543,8 @@ onMounted(() => { .partner-cell small, .partner-subline { display: block; color: var(--cc-muted); font-size: 11px; line-height: 1.45; overflow-wrap: anywhere; } .partner-address { display: flex; align-items: flex-start; gap: 7px; line-height: 1.55; overflow-wrap: anywhere; } .partner-address svg { flex: 0 0 15px; margin-top: 3px; color: var(--cc-muted); } -.partner-detail { min-height: 180px; } -.partner-detail section + section { margin-top: 24px; } +.partner-detail { min-height: 180px; padding: 20px 24px 28px; box-sizing: border-box; } +.partner-detail section + section { margin-top: 28px; } .partner-detail h3 { margin: 0 0 12px; color: var(--cc-text); font-size: 15px; } .detail-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1px; margin: 0; overflow: hidden; border: 1px solid var(--cc-border); border-radius: 8px; background: var(--cc-border); } .detail-grid > div { min-width: 0; padding: 12px 14px; background: #fff; } @@ -644,6 +565,7 @@ onMounted(() => { .partners-table-header { align-items: stretch; flex-direction: column; } .partners-table-header .el-button { width: 100%; min-height: 40px; } .partners-primary-actions { flex-direction: column; } + .partner-detail { padding: 16px; } .detail-grid { grid-template-columns: minmax(0, 1fr); } .detail-grid .detail-span { grid-column: auto; } .import-result-heading { align-items: stretch; flex-direction: column; } diff --git a/src/modules/master-data/express-companies/api/platformExpressCompanies.test.ts b/src/modules/master-data/express-companies/api/platformExpressCompanies.test.ts index 43defafa..5f26224d 100644 --- a/src/modules/master-data/express-companies/api/platformExpressCompanies.test.ts +++ b/src/modules/master-data/express-companies/api/platformExpressCompanies.test.ts @@ -40,19 +40,19 @@ describe('platform express company API', () => { .mockResolvedValueOnce({ code: '200', message: 'success', data: page }) .mockResolvedValueOnce({ code: '200', message: 'success', data: { id: '19001' } }) - await expect(listPlatformExpressCompanies(10001, 2, 20, ' 顺丰 ', 'ACTIVE')).resolves.toEqual({ + await expect(listPlatformExpressCompanies(2, 20, ' 顺丰 ', 'ACTIVE')).resolves.toEqual({ records: [], page: 1, size: 20, total: 0, pages: 0, }) - await expect(getPlatformExpressCompany(10001, '19001')).resolves.toEqual({ id: '19001' }) + await expect(getPlatformExpressCompany('19001')).resolves.toEqual({ id: '19001' }) expect(http.get).toHaveBeenNthCalledWith(1, '/platform/v1/express-companies', { - params: { tenantId: 10001, page: 2, size: 20, keyword: '顺丰', status: 'ACTIVE' }, + params: { page: 2, size: 20, keyword: '顺丰', status: 'ACTIVE' }, }) expect(http.get).toHaveBeenNthCalledWith(2, '/platform/v1/express-companies/19001', { - params: { tenantId: 10001 }, + params: { tenantId: 0 }, }) }) @@ -61,16 +61,16 @@ describe('platform express company API', () => { vi.mocked(http.put).mockResolvedValue({ code: '200', message: 'success', data: null }) vi.mocked(http.patch).mockResolvedValue({ code: '200', message: 'success', data: null }) - await expect(createPlatformExpressCompany(10001, payload)).resolves.toBe('19001') - await expect(updatePlatformExpressCompany(10001, '19001', payload)).resolves.toBeNull() - await expect(changePlatformExpressCompanyStatus(10001, '19001', 'INACTIVE')).resolves.toBeNull() + await expect(createPlatformExpressCompany(payload)).resolves.toBe('19001') + await expect(updatePlatformExpressCompany('19001', payload)).resolves.toBeNull() + await expect(changePlatformExpressCompanyStatus('19001', 'INACTIVE')).resolves.toBeNull() - expect(http.post).toHaveBeenCalledWith('/platform/v1/express-companies', payload, { params: { tenantId: 10001 } }) - expect(http.put).toHaveBeenCalledWith('/platform/v1/express-companies/19001', payload, { params: { tenantId: 10001 } }) + expect(http.post).toHaveBeenCalledWith('/platform/v1/express-companies', payload, { params: { tenantId: 0 } }) + expect(http.put).toHaveBeenCalledWith('/platform/v1/express-companies/19001', payload, { params: { tenantId: 0 } }) expect(http.patch).toHaveBeenCalledWith( '/platform/v1/express-companies/19001/status', { status: 'INACTIVE' }, - { params: { tenantId: 10001 } }, + { params: { tenantId: 0 } }, ) }) @@ -89,12 +89,12 @@ describe('platform express company API', () => { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', }) - await expect(importPlatformExpressCompanies(10001, file)).resolves.toEqual(result) + await expect(importPlatformExpressCompanies(file)).resolves.toEqual(result) expect(http.post).toHaveBeenCalledOnce() const [path, body, config] = vi.mocked(http.post).mock.calls[0]! expect(path).toBe('/platform/v1/express-companies/import') expect(body).toBeInstanceOf(FormData) expect((body as FormData).get('file')).toBe(file) - expect(config).toEqual({ params: { tenantId: 10001 } }) + expect(config).toEqual({ params: { tenantId: 0 } }) }) }) diff --git a/src/modules/master-data/express-companies/api/platformExpressCompanies.ts b/src/modules/master-data/express-companies/api/platformExpressCompanies.ts index a929fcfc..10281958 100644 --- a/src/modules/master-data/express-companies/api/platformExpressCompanies.ts +++ b/src/modules/master-data/express-companies/api/platformExpressCompanies.ts @@ -49,9 +49,9 @@ export interface PlatformExpressCompanyImportResult { } const basePath = '/platform/v1/express-companies' +const platformResourceScope = 0 export async function listPlatformExpressCompanies( - tenantId: number | string | undefined, page = 1, size = 20, keyword = '', @@ -59,7 +59,6 @@ export async function listPlatformExpressCompanies( ) { const response = await http.get, ApiEnvelope>(basePath, { params: { - tenantId, page, size, keyword: keyword.trim() || undefined, @@ -76,39 +75,37 @@ export async function listPlatformExpressCompanies( } } -export async function getPlatformExpressCompany(tenantId: number | string, id: number | string) { +export async function getPlatformExpressCompany(id: number | string) { const response = await http.get, ApiEnvelope>( `${basePath}/${id}`, - { params: { tenantId } }, + { params: { tenantId: platformResourceScope } }, ) return unwrapApiResponse(response) } -export async function createPlatformExpressCompany(tenantId: number | string, payload: SavePlatformExpressCompanyRequest) { +export async function createPlatformExpressCompany(payload: SavePlatformExpressCompanyRequest) { const response = await http.post, ApiEnvelope>(basePath, payload, { - params: { tenantId }, + params: { tenantId: platformResourceScope }, }) return unwrapApiResponse(response) } export async function updatePlatformExpressCompany( - tenantId: number | string, id: number | string, payload: SavePlatformExpressCompanyRequest, ) { const response = await http.put, ApiEnvelope>(`${basePath}/${id}`, payload, { - params: { tenantId }, + params: { tenantId: platformResourceScope }, }) return unwrapApiResponse(response) } export async function changePlatformExpressCompanyStatus( - tenantId: number | string, id: number | string, status: PlatformExpressCompanyStatus, ) { const response = await http.patch, ApiEnvelope>(`${basePath}/${id}/status`, { status }, { - params: { tenantId }, + params: { tenantId: platformResourceScope }, }) return unwrapApiResponse(response) } @@ -117,13 +114,13 @@ export async function downloadPlatformExpressCompanyImportTemplate() { return http.get(`${basePath}/import-template`, { responseType: 'blob' }) } -export async function importPlatformExpressCompanies(tenantId: number | string, file: File) { +export async function importPlatformExpressCompanies(file: File) { const formData = new FormData() formData.append('file', file) const response = await http.post, ApiEnvelope>( `${basePath}/import`, formData, - { params: { tenantId } }, + { params: { tenantId: platformResourceScope } }, ) return unwrapApiResponse(response) } diff --git a/src/modules/master-data/express-companies/pages/ExpressCompaniesView.test.ts b/src/modules/master-data/express-companies/pages/ExpressCompaniesView.test.ts index 2cebe8bb..b67dc1a6 100644 --- a/src/modules/master-data/express-companies/pages/ExpressCompaniesView.test.ts +++ b/src/modules/master-data/express-companies/pages/ExpressCompaniesView.test.ts @@ -5,8 +5,6 @@ import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from 'vites import { listPlatformExpressCompanies, type PlatformExpressCompanyResource } from '@/modules/master-data/express-companies/api/platformExpressCompanies' import { useAccessPreviewStore } from '@/core/access/accessPreview' import { useAuthStore } from '@/core/auth/auth' -import QueryToolbar from '@/shared/components/QueryToolbar.vue' -import { listPlatformTenants, type PlatformTenantResource } from '../../../iam/api/platformIam' import ExpressCompaniesView from './ExpressCompaniesView.vue' vi.mock('@/modules/master-data/express-companies/api/platformExpressCompanies', () => ({ @@ -16,8 +14,6 @@ vi.mock('@/modules/master-data/express-companies/api/platformExpressCompanies', changePlatformExpressCompanyStatus: vi.fn(), })) -vi.mock('../../../iam/api/platformIam', () => ({ listPlatformTenants: vi.fn() })) - class ResizeObserverStub implements ResizeObserver { observe() {} unobserve() {} @@ -38,7 +34,7 @@ function createMemoryStorage(): Storage { const company: PlatformExpressCompanyResource = { id: '19001', - tenantId: 10001, + tenantId: 0, companyCode: 'SF', companyName: '顺丰速运', servicePhone: '95338', @@ -49,16 +45,6 @@ const company: PlatformExpressCompanyResource = { updatedAt: '2026-07-31T10:20:00', } -const tenant = (id: number | string, tenantName: string): PlatformTenantResource => ({ - id, tenantCode: String(id), tenantName, status: 'ACTIVE', legalName: tenantName, - legalRepresentative: null, unifiedSocialCreditCode: null, contactName: null, - contactPhone: null, contactEmail: null, servicePackage: null, contactWechat: null, - salesOwnerName: null, paymentTermDays: null, enterpriseType: null, establishedDate: null, - registeredCapital: null, registeredCapitalCurrency: null, registeredAddress: null, - internalRemark: null, createdAt: null, approvedAt: null, -}) -const snowflakeTenantId = '9223372036854775806' - async function mountView() { vi.stubGlobal('localStorage', createMemoryStorage()) vi.stubGlobal('sessionStorage', createMemoryStorage()) @@ -83,9 +69,6 @@ describe('ExpressCompaniesView query and table migration', () => { beforeEach(() => { vi.stubGlobal('ResizeObserver', ResizeObserverStub) document.body.innerHTML = '' - vi.mocked(listPlatformTenants).mockReset().mockResolvedValue({ - records: [tenant(10001, '默认企业'), tenant(snowflakeTenantId, '雪花企业')], page: 1, size: 200, total: 2, pages: 1, - }) vi.mocked(listPlatformExpressCompanies).mockReset().mockResolvedValue({ records: [company], page: 1, size: 20, total: 1, pages: 1, }) @@ -95,12 +78,12 @@ describe('ExpressCompaniesView query and table migration', () => { const wrapper = await mountView() expect(wrapper.find('.query-toolbar').exists()).toBe(true) - expect(wrapper.find('input[aria-label="所属企业"]').exists()).toBe(true) + expect(wrapper.find('input[aria-label="所属企业"]').exists()).toBe(false) expect(wrapper.find('.data-table').exists()).toBe(true) expect(wrapper.text()).toContain('顺丰速运') - expect(wrapper.text()).toContain('全部企业 · 共 1 家快递公司') + expect(wrapper.text()).toContain('平台公共数据 · 共 1 家快递公司') expect(wrapper.find('.metric-strip').exists()).toBe(false) - expect(listPlatformExpressCompanies).toHaveBeenCalledWith(undefined, 1, 20, '', '') + expect(listPlatformExpressCompanies).toHaveBeenCalledWith(1, 20, '', '') }) it('does not request with edited filters until search is submitted', async () => { @@ -113,18 +96,16 @@ describe('ExpressCompaniesView query and table migration', () => { await searchButton?.trigger('click') await flushPromises() - expect(listPlatformExpressCompanies).toHaveBeenLastCalledWith(undefined, 1, 20, '顺丰', '') + expect(listPlatformExpressCompanies).toHaveBeenLastCalledWith(1, 20, '顺丰', '') }) - it('preserves a snowflake tenant id as a string when switching enterprises', async () => { + it('opens the create form without selecting an enterprise', async () => { const wrapper = await mountView() - - const toolbar = wrapper.findComponent(QueryToolbar) - const model = { tenantId: snowflakeTenantId, keyword: '', status: '' } - toolbar.vm.$emit('update:modelValue', model) - toolbar.vm.$emit('search', model) + const createButton = wrapper.findAll('button').find((button) => button.text().trim() === '新增快递公司') + await createButton?.trigger('click') await flushPromises() - expect(listPlatformExpressCompanies).toHaveBeenLastCalledWith(snowflakeTenantId, 1, 20, '', '') + expect(document.body.textContent).toContain('新增快递公司') + expect(document.body.textContent).not.toContain('请先在筛选条件中选择所属企业') }) }) diff --git a/src/modules/master-data/express-companies/pages/ExpressCompaniesView.vue b/src/modules/master-data/express-companies/pages/ExpressCompaniesView.vue index de4b53f5..9355865e 100644 --- a/src/modules/master-data/express-companies/pages/ExpressCompaniesView.vue +++ b/src/modules/master-data/express-companies/pages/ExpressCompaniesView.vue @@ -21,7 +21,6 @@ import { type SavePlatformExpressCompanyRequest, } from '@/modules/master-data/express-companies/api/platformExpressCompanies' import { useAccessPreviewStore } from '@/core/access/accessPreview' -import { listPlatformTenants, type PlatformTenantResource } from '@/modules/iam' const access = useAccessPreviewStore() const loading = ref(false) @@ -34,9 +33,6 @@ const rows = ref([]) const page = ref(1) const pageSize = ref(20) const total = ref(0) -const tenants = ref([]) -const tenantLoadError = ref(null) -const editorTenantId = ref('') const loadError = ref(null) const statusLoadingIds = ref(new Set()) const editLoadingIds = ref(new Set()) @@ -46,21 +42,12 @@ const importFile = ref(null) const importResult = ref(null) const importError = ref(null) type ExpressCompanyFilters = QueryModel & { - tenantId: string keyword: string status: PlatformExpressCompanyStatus | '' } -const filters = ref({ tenantId: '', keyword: '', status: '' }) +const filters = ref({ keyword: '', status: '' }) const appliedFilters = ref({ ...filters.value }) const queryFields = computed(() => [ - { - key: 'tenantId', - type: 'select', - width: 180, - placeholder: tenantLoadError.value ? '企业列表加载失败' : '全部企业', - props: { 'aria-label': '所属企业', filterable: true, disabled: Boolean(tenantLoadError.value) }, - options: tenants.value.map((tenant) => ({ value: String(tenant.id), label: `${tenant.tenantName}(${tenant.tenantCode || tenant.id})` })), - }, { key: 'keyword', type: 'input', placeholder: '快递公司名称或编码', props: { maxlength: 128, 'aria-label': '快递公司关键词' } }, { key: 'status', @@ -103,13 +90,6 @@ const { captureBaseline } = useUnsavedWorkRegistration({ requiredPermission: editorPermission, }) -const selectedEnterpriseId = computed(() => appliedFilters.value.tenantId) -const targetEnterpriseId = computed(() => filters.value.tenantId) -const selectedEnterpriseName = computed(() => { - if (!selectedEnterpriseId.value) return '全部企业' - return tenants.value.find((tenant) => String(tenant.id) === selectedEnterpriseId.value)?.tenantName || `企业 ${selectedEnterpriseId.value}` -}) - watch( () => access.can(editorPermission.value), (canWrite) => { @@ -142,7 +122,6 @@ function formatUpdatedAt(value?: string | null) { const expressCompanyColumns: DataTableColumn[] = [ { key: 'actions', label: '操作', width: 170, fixed: 'left', slotName: 'actions' }, { key: 'company', label: '快递公司', minWidth: 210, slotName: 'company' }, - { key: 'tenant', label: '所属企业', minWidth: 190, showOverflowTooltip: true, slotName: 'tenant' }, { prop: 'servicePhone', label: '客服电话', minWidth: 130, formatter: (row: PlatformExpressCompanyResource) => row.servicePhone || '—' }, { key: 'website', label: '官方网站', minWidth: 210, slotName: 'website' }, { key: 'trackingUrlTemplate', label: '轨迹查询模板', minWidth: 260, slotName: 'trackingUrlTemplate' }, @@ -151,50 +130,12 @@ const expressCompanyColumns: DataTableColumn[] = [ { key: 'status', label: '状态', width: 100, slotName: 'status' }, ] -function apiTenantId(value: string | undefined) { - if (!value) return undefined - const numeric = Number(value) - return Number.isSafeInteger(numeric) ? numeric : value -} - -function requireSelectedEnterprise(action: string) { - if (!targetEnterpriseId.value) { - ElMessage.warning(`请先在筛选条件中选择所属企业,再${action}`) - return null - } - return apiTenantId(targetEnterpriseId.value)! -} - -function tenantName(tenantId: number | string) { - return tenants.value.find((tenant) => String(tenant.id) === String(tenantId))?.tenantName || `企业 ${tenantId}` -} - -async function loadTenants() { - tenantLoadError.value = null - try { - const records: PlatformTenantResource[] = [] - let currentPage = 1 - let pages = 1 - do { - const result = await listPlatformTenants({ page: currentPage, size: 200 }) - records.push(...result.records) - pages = Math.max(1, result.pages) - currentPage += 1 - } while (currentPage <= pages) - tenants.value = records - } catch (error) { - tenants.value = [] - tenantLoadError.value = getApiErrorDetails(error, '企业列表加载失败,暂时无法按所属企业筛选') - } -} - async function loadExpressCompanies() { const sequence = ++loadSequence loading.value = true loadError.value = null try { const result = await listPlatformExpressCompanies( - apiTenantId(appliedFilters.value.tenantId), page.value, pageSize.value, appliedFilters.value.keyword, @@ -215,7 +156,6 @@ async function loadExpressCompanies() { function applyFilters(model: QueryModel) { appliedFilters.value = { - tenantId: String(model.tenantId || ''), keyword: String(model.keyword || ''), status: (model.status || '') as PlatformExpressCompanyStatus | '', } @@ -227,9 +167,6 @@ function applyFilters(model: QueryModel) { } function openCreate() { - const tenantId = requireSelectedEnterprise('新增快递公司') - if (!tenantId) return - editorTenantId.value = String(tenantId) Object.assign(form, emptyForm()) captureBaseline() editorOpen.value = true @@ -253,14 +190,12 @@ function assignEditor(row: PlatformExpressCompanyResource) { } async function openEdit(row: PlatformExpressCompanyResource) { - const tenantId = apiTenantId(String(row.tenantId))! - editorTenantId.value = String(row.tenantId) const id = String(row.id) const pending = new Set(editLoadingIds.value) pending.add(id) editLoadingIds.value = pending try { - assignEditor(await getPlatformExpressCompany(tenantId, row.id)) + assignEditor(await getPlatformExpressCompany(row.id)) } catch (error) { const details = getApiErrorDetails(error, '快递公司详情加载失败,请稍后重试') ElMessage.error(details.requestId ? `${details.message}(requestId: ${details.requestId})` : details.message) @@ -272,7 +207,6 @@ async function openEdit(row: PlatformExpressCompanyResource) { } function openImport() { - if (!requireSelectedEnterprise('批量导入')) return importFile.value = null importResult.value = null importError.value = null @@ -326,8 +260,6 @@ function selectImportFile(event: Event) { } async function submitImport() { - const tenantId = requireSelectedEnterprise('批量导入') - if (!tenantId) return if (!importFile.value) { ElMessage.warning('请先选择填写完成的 XLSX 文件') return @@ -336,7 +268,7 @@ async function submitImport() { importError.value = null importResult.value = null try { - const result = await importPlatformExpressCompanies(tenantId, importFile.value) + const result = await importPlatformExpressCompanies(importFile.value) importResult.value = result if (result.success) { ElMessage.success(`成功导入 ${result.importedRows} 家快递公司`) @@ -374,15 +306,13 @@ function buildPayload(): SavePlatformExpressCompanyRequest { } async function saveExpressCompany() { - const tenantId = form.id ? apiTenantId(editorTenantId.value) : requireSelectedEnterprise('新增快递公司') - if (!tenantId) return const valid = await formRef.value?.validate().catch(() => false) if (!valid) return saving.value = true try { const payload = buildPayload() - if (form.id) await updatePlatformExpressCompany(tenantId, form.id, payload) - else await createPlatformExpressCompany(tenantId, payload) + if (form.id) await updatePlatformExpressCompany(form.id, payload) + else await createPlatformExpressCompany(payload) editorOpen.value = false ElMessage.success(form.id ? '快递公司资料已更新' : '快递公司已创建') await loadExpressCompanies() @@ -395,7 +325,6 @@ async function saveExpressCompany() { } async function toggleStatus(row: PlatformExpressCompanyResource) { - const tenantId = apiTenantId(String(row.tenantId))! const nextStatus: PlatformExpressCompanyStatus = row.status === 'ACTIVE' ? 'INACTIVE' : 'ACTIVE' const action = nextStatus === 'ACTIVE' ? '启用' : '停用' try { @@ -412,7 +341,7 @@ async function toggleStatus(row: PlatformExpressCompanyResource) { pending.add(id) statusLoadingIds.value = pending try { - await changePlatformExpressCompanyStatus(tenantId, row.id, nextStatus) + await changePlatformExpressCompanyStatus(row.id, nextStatus) ElMessage.success(`已${action}${row.companyName}`) await loadExpressCompanies() } catch (error) { @@ -427,19 +356,12 @@ async function toggleStatus(row: PlatformExpressCompanyResource) { watch([page, pageSize], () => { void loadExpressCompanies() }) onMounted(() => { - void loadTenants() void loadExpressCompanies() }) -