diff --git a/frontend/src/pages/refund-items/__tests__/index.spec.ts b/frontend/src/pages/refund-items/__tests__/index.spec.ts index 89f5f9f..b19c5b3 100644 --- a/frontend/src/pages/refund-items/__tests__/index.spec.ts +++ b/frontend/src/pages/refund-items/__tests__/index.spec.ts @@ -20,6 +20,12 @@ Object.defineProperty(window, 'matchMedia', { }), }) +const mockRouterPush = vi.fn() +vi.mock('vue-router', () => ({ + useRoute: () => ({ query: {} }), + useRouter: () => ({ push: mockRouterPush }), +})) + vi.mock('@/utils/request', () => ({ api: { get: vi.fn(), @@ -91,7 +97,7 @@ const mockLookupCompanies = [ { id: 1, name: 'Company A', label: '公司A' }, { id: 2, name: 'Company B', label: '公司B' }, ] -const mockLookupPlatforms = [{ id: 1, developer_id: 1 }] +const mockLookupPlatforms = [{ id: 1, name: 'Tmall', label: '天猫', developer_id: 1 }] const mockLookupStores = [ { id: 1, company_id: 1, platform_id: 1, name: 'Store 1', label: '店铺1' }, { id: 2, company_id: 2, platform_id: 1, name: 'Store 2', label: '店铺2' }, @@ -260,10 +266,26 @@ describe('useRefundItemStore', () => { await store.loadLookups() expect(store.companyMap.get(1)).toBe('公司A') - expect(store.platformMap.get(1)).toBe('平台 #1') + expect(store.platformMap.get(1)).toBe('天猫') expect(store.storeMap.get(1)).toBe('店铺1') }) + it('filters null and "null" labels in lookup maps', async () => { + const { api } = await import('@/utils/request') + vi.mocked(api.get) + .mockResolvedValueOnce([{ id: 1, name: 'CompanyA', label: null }, { id: 2, name: 'CompanyB', label: 'null' }]) + .mockResolvedValueOnce([{ id: 1, name: 'Tmall', label: 'null', developer_id: 1 }]) + .mockResolvedValueOnce([{ id: 1, company_id: 1, platform_id: 1, name: 'Store1', label: null }]) + + const store = useRefundItemStore() + await store.loadLookups() + + expect(store.companyMap.get(1)).toBe('CompanyA') + expect(store.companyMap.get(2)).toBe('CompanyB') + expect(store.platformMap.get(1)).toBe('Tmall') + expect(store.storeMap.get(1)).toBe('Store1') + }) + it('handles loadLookups failure gracefully', async () => { const { api } = await import('@/utils/request') const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}) @@ -287,6 +309,7 @@ describe('RefundItemsPage', () => { beforeEach(() => { setActivePinia(createPinia()) vi.restoreAllMocks() + mockRouterPush.mockClear() document.body.innerHTML = '' }) @@ -533,4 +556,92 @@ describe('RefundItemsPage', () => { expect(errorSpy).toHaveBeenCalledWith('获取退款项详情失败') }) + + it('filter form has filter-form class', async () => { + await mountPage() + + const form = wrapper.find('form.filter-form') + expect(form.exists()).toBe(true) + }) + + it('renders platform name from lookup map (not hardcoded fallback)', async () => { + await mountPage() + + const html = wrapper.html() + expect(html).toContain('天猫') + expect(html).not.toContain('平台 #1') + }) + + it('renders copy buttons before text in platform_refund_id column', async () => { + await mountPage() + + const cells = wrapper.findAll('td') + const refundIdCell = cells.find((c) => c.text().includes('RF-20260101-001')) + expect(refundIdCell).toBeDefined() + + const cellHtml = refundIdCell!.html() + const copyPos = cellHtml.indexOf('copy-icon-stub') + const textPos = cellHtml.indexOf('RF-20260101-001') + expect(copyPos).toBeLessThan(textPos) + }) + + it('renders copy button for platform_sub_order_id when value exists', async () => { + await mountPage() + + const cells = wrapper.findAll('td') + const subOrderCell = cells.find((c) => c.text().includes('SUB-001')) + expect(subOrderCell).toBeDefined() + expect(subOrderCell!.find('.copy-icon-stub').exists()).toBe(true) + }) + + it('does not render copy button for platform_sub_order_id when value is null', async () => { + await mountPage() + + const cells = wrapper.findAll('td') + const dashCells = cells.filter((c) => c.text().trim() === '-') + const noCopyDash = dashCells.some((c) => !c.find('.copy-icon-stub').exists()) + expect(noCopyDash).toBe(true) + }) + + it('renders copy button for platform_product_id column', async () => { + await mountPage() + + const cells = wrapper.findAll('td') + const prodIdCell = cells.find((c) => c.text().includes('PROD-001')) + expect(prodIdCell).toBeDefined() + expect(prodIdCell!.find('.copy-icon-stub').exists()).toBe(true) + }) + + it('handleGoToOrder includes auto_submit in router push', async () => { + await mountPage() + + const links = wrapper.findAll('a') + const orderLink = links.find((l) => l.text().includes('ORD-20260101-001')) + expect(orderLink).toBeDefined() + + await orderLink!.trigger('click') + await flushPromises() + + expect(mockRouterPush).toHaveBeenCalledWith({ + path: '/orders', + query: { platform_order_id: 'ORD-20260101-001', auto_submit: '1' }, + }) + }) + + it('handleGoToRefund passes platform_refund_id and auto_submit', async () => { + await mountPage() + + // refund_id column renders as clickable link + const links = wrapper.findAll('a') + const refundLink = links.find((l) => l.text().trim() === '201') + expect(refundLink).toBeDefined() + + await refundLink!.trigger('click') + await flushPromises() + + expect(mockRouterPush).toHaveBeenCalledWith({ + path: '/refunds', + query: { platform_refund_id: 'RF-20260101-001', auto_submit: '1' }, + }) + }) }) diff --git a/frontend/src/pages/refund-items/index.vue b/frontend/src/pages/refund-items/index.vue index 0bce549..8657dc5 100644 --- a/frontend/src/pages/refund-items/index.vue +++ b/frontend/src/pages/refund-items/index.vue @@ -45,8 +45,8 @@ const columns = [ { title: '退款单ID', key: 'refund_id', width: 100 }, { title: '平台退款ID', key: 'platform_refund_id', width: 160, ellipsis: true }, { title: '关联订单ID', key: 'platform_order_id', width: 160, ellipsis: true }, - { title: '子订单ID', dataIndex: 'platform_sub_order_id', width: 140, ellipsis: true }, - { title: '平台商品ID', dataIndex: 'platform_product_id', width: 140, ellipsis: true }, + { title: '子订单ID', key: 'platform_sub_order_id', width: 140, ellipsis: true }, + { title: '平台商品ID', key: 'platform_product_id', width: 140, ellipsis: true }, { title: '退款状态', key: 'refund_status', width: 110 }, { title: '退款类型', key: 'refund_type', width: 130 }, { title: '数量', dataIndex: 'quantity', width: 80 }, @@ -103,7 +103,7 @@ function formatTime(time: string | null) { return time.replace('T', ' ').substring(0, 16) } -async function handleCopyId(text: string, label: string) { +async function handleCopy(text: string, label: string) { try { await navigator.clipboard.writeText(text) message.success(`已复制${label}`) @@ -113,11 +113,17 @@ async function handleCopyId(text: string, label: string) { } function handleGoToOrder(platformOrderId: string) { - router.push({ path: '/orders', query: { platform_order_id: platformOrderId } }) + router.push({ + path: '/orders', + query: { platform_order_id: platformOrderId, auto_submit: '1' }, + }) } -function handleGoToRefund(refundId: number) { - router.push({ path: '/refunds', query: { id: String(refundId) } }) +function handleGoToRefund(platformRefundId: string) { + router.push({ + path: '/refunds', + query: { platform_refund_id: platformRefundId, auto_submit: '1' }, + }) } async function handleViewDetail(record: { id: number }) { @@ -147,7 +153,7 @@ async function handleViewDetail(record: { id: number }) { - + @@ -260,29 +266,50 @@ async function handleViewDetail(record: { id: number }) { + +