fix(frontend): adapt affiliate copy controls for mobile

This commit is contained in:
wucm667
2026-07-25 04:27:19 +08:00
parent cb24522dd5
commit 30d146e8b5
2 changed files with 122 additions and 6 deletions
+6 -6
View File
@@ -51,9 +51,9 @@
<div class="mt-5 grid gap-4 md:grid-cols-2">
<div class="space-y-2">
<p class="text-sm font-medium text-gray-700 dark:text-gray-300">{{ t('affiliate.yourCode') }}</p>
<div class="flex items-center gap-2 rounded-xl border border-gray-200 bg-gray-50 px-3 py-2 dark:border-dark-700 dark:bg-dark-900">
<code class="flex-1 truncate text-sm font-semibold text-gray-900 dark:text-white">{{ detail.aff_code }}</code>
<button class="btn btn-secondary btn-sm" @click="copyCode">
<div class="flex flex-col items-stretch gap-2 rounded-xl border border-gray-200 bg-gray-50 px-3 py-2 dark:border-dark-700 dark:bg-dark-900 sm:flex-row sm:items-center">
<code class="min-w-0 break-all text-sm font-semibold text-gray-900 dark:text-white sm:flex-1 sm:truncate">{{ detail.aff_code }}</code>
<button class="btn btn-secondary btn-sm w-full sm:w-auto sm:shrink-0" @click="copyCode">
<Icon name="copy" size="sm" />
<span>{{ t('affiliate.copyCode') }}</span>
</button>
@@ -62,9 +62,9 @@
<div class="space-y-2">
<p class="text-sm font-medium text-gray-700 dark:text-gray-300">{{ t('affiliate.inviteLink') }}</p>
<div class="flex items-center gap-2 rounded-xl border border-gray-200 bg-gray-50 px-3 py-2 dark:border-dark-700 dark:bg-dark-900">
<code class="flex-1 truncate text-sm text-gray-700 dark:text-gray-300">{{ inviteLink }}</code>
<button class="btn btn-secondary btn-sm" @click="copyInviteLink">
<div class="flex flex-col items-stretch gap-2 rounded-xl border border-gray-200 bg-gray-50 px-3 py-2 dark:border-dark-700 dark:bg-dark-900 sm:flex-row sm:items-center">
<code class="min-w-0 break-all text-sm text-gray-700 dark:text-gray-300 sm:flex-1 sm:truncate">{{ inviteLink }}</code>
<button class="btn btn-secondary btn-sm w-full sm:w-auto sm:shrink-0" @click="copyInviteLink">
<Icon name="copy" size="sm" />
<span>{{ t('affiliate.copyLink') }}</span>
</button>
@@ -0,0 +1,116 @@
import { flushPromises, mount } from '@vue/test-utils'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import AffiliateView from '../AffiliateView.vue'
const { copyToClipboard, getAffiliateDetail } = vi.hoisted(() => ({
copyToClipboard: vi.fn(),
getAffiliateDetail: vi.fn(),
}))
vi.mock('@/api/user', () => ({
default: {
getAffiliateDetail,
transferAffiliateQuota: vi.fn(),
},
}))
vi.mock('@/stores/app', () => ({
useAppStore: () => ({
showError: vi.fn(),
showSuccess: vi.fn(),
}),
}))
vi.mock('@/stores/auth', () => ({
useAuthStore: () => ({
refreshUser: vi.fn(),
}),
}))
vi.mock('@/composables/useClipboard', () => ({
useClipboard: () => ({ copyToClipboard }),
}))
vi.mock('vue-i18n', async (importOriginal) => {
const actual = await importOriginal<typeof import('vue-i18n')>()
return {
...actual,
useI18n: () => ({
t: (key: string) => key,
}),
}
})
describe('AffiliateView', () => {
const affiliateCode = 'affiliate-code-that-is-long-enough-to-overflow-a-mobile-viewport'
beforeEach(() => {
vi.clearAllMocks()
copyToClipboard.mockResolvedValue(true)
getAffiliateDetail.mockResolvedValue({
user_id: 1,
aff_code: affiliateCode,
inviter_id: null,
aff_count: 0,
aff_quota: 0,
aff_frozen_quota: 0,
aff_history_quota: 0,
effective_rebate_rate_percent: 10,
invitees: [],
})
})
it('stacks long values and copy controls on mobile while retaining desktop rows', async () => {
const wrapper = mount(AffiliateView, {
global: {
stubs: {
AppLayout: { template: '<main><slot /></main>' },
Icon: true,
},
},
})
await flushPromises()
const values = wrapper.findAll('code')
expect(values).toHaveLength(2)
for (const value of values) {
expect(value.classes()).toEqual(expect.arrayContaining([
'min-w-0',
'break-all',
'sm:flex-1',
'sm:truncate',
]))
expect(Array.from(value.element.parentElement?.classList ?? [])).toEqual(expect.arrayContaining([
'flex-col',
'items-stretch',
'sm:flex-row',
'sm:items-center',
]))
}
const copyButtons = wrapper.findAll('button').filter((button) =>
['affiliate.copyCode', 'affiliate.copyLink'].includes(button.text()),
)
expect(copyButtons).toHaveLength(2)
for (const button of copyButtons) {
expect(button.classes()).toEqual(expect.arrayContaining([
'w-full',
'sm:w-auto',
'sm:shrink-0',
]))
}
await copyButtons[0].trigger('click')
await copyButtons[1].trigger('click')
await flushPromises()
expect(copyToClipboard).toHaveBeenNthCalledWith(1, affiliateCode, 'affiliate.codeCopied')
expect(copyToClipboard).toHaveBeenNthCalledWith(
2,
`${window.location.origin}/register?aff=${encodeURIComponent(affiliateCode)}`,
'affiliate.linkCopied',
)
})
})