fix(transfer): improve manual review dialog on mobile

This commit is contained in:
jxxghp
2026-09-05 09:14:19 +08:00
parent 653a912bde
commit 6e6d03b518
2 changed files with 67 additions and 7 deletions
@@ -1,5 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import { useDisplay } from 'vuetify'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useToast } from 'vue-toastification' import { useToast } from 'vue-toastification'
import api from '@/api' import api from '@/api'
@@ -16,6 +17,8 @@ const emit = defineEmits<{
const { t } = useI18n() const { t } = useI18n()
const toast = useToast() const toast = useToast()
// 小屏详情弹窗占满可用屏幕,避免长提示和操作区被裁切。
const display = useDisplay()
const reason = ref('') const reason = ref('')
const submittingDecision = ref<TransferManualReviewDecision | null>(null) const submittingDecision = ref<TransferManualReviewDecision | null>(null)
@@ -90,15 +93,19 @@ async function resolveManualReview(decision: TransferManualReviewDecision) {
</script> </script>
<template> <template>
<VDialog scrollable max-width="52rem"> <VDialog scrollable max-width="52rem" :fullscreen="!display.mdAndUp.value">
<VCard class="manual-review-dialog" width="100%"> <VCard class="manual-review-dialog" width="100%">
<VCardItem> <VCardItem class="manual-review-dialog__header">
<VDialogCloseBtn @click="emit('close')" /> <VDialogCloseBtn @click="emit('close')" />
<template #prepend> <template #prepend>
<VIcon icon="mdi-help-circle-outline" color="warning" size="28" class="me-2" /> <VIcon icon="mdi-help-circle-outline" color="warning" size="28" class="me-2" />
</template> </template>
<VCardTitle>{{ t('dialog.transferQueue.manualReviewTitle') }}</VCardTitle> <VCardTitle class="manual-review-dialog__title">
<VCardSubtitle>{{ t('dialog.transferQueue.manualReviewHint') }}</VCardSubtitle> {{ t('dialog.transferQueue.manualReviewTitle') }}
</VCardTitle>
<VCardSubtitle class="manual-review-dialog__subtitle">
{{ t('dialog.transferQueue.manualReviewHint') }}
</VCardSubtitle>
</VCardItem> </VCardItem>
<VDivider /> <VDivider />
@@ -203,10 +210,35 @@ async function resolveManualReview(decision: TransferManualReviewDecision) {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 1rem; gap: 1rem;
max-block-size: min(70vh, 42rem); min-block-size: 0;
min-inline-size: 0;
overflow-y: auto;
padding: 1.5rem !important; padding: 1.5rem !important;
} }
.manual-review-dialog__header :deep(.v-card-item__content) {
min-inline-size: 0;
overflow: visible;
padding-inline-end: 2.5rem;
}
.manual-review-dialog :deep(.manual-review-dialog__title),
.manual-review-dialog :deep(.manual-review-dialog__subtitle) {
overflow: visible;
overflow-wrap: anywhere;
text-overflow: clip;
white-space: normal;
word-break: break-word;
}
.manual-review-dialog :deep(.manual-review-dialog__title) {
line-height: 1.35;
}
.manual-review-dialog :deep(.manual-review-dialog__subtitle) {
line-height: 1.5;
}
.manual-review-dialog__summary { .manual-review-dialog__summary {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -250,15 +282,28 @@ async function resolveManualReview(decision: TransferManualReviewDecision) {
font-size: 0.84rem; font-size: 0.84rem;
gap: 0.5rem; gap: 0.5rem;
line-height: 1.5; line-height: 1.5;
min-inline-size: 0;
padding-block-start: 0.75rem; padding-block-start: 0.75rem;
} }
.manual-review-dialog__error > span {
min-inline-size: 0;
overflow-wrap: anywhere;
white-space: normal;
word-break: break-word;
}
.manual-review-dialog__notice { .manual-review-dialog__notice {
inline-size: 100%;
margin: 0; margin: 0;
} }
.manual-review-dialog__evidence { .manual-review-dialog__notice :deep(.v-alert__content) {
border: 1px solid rgba(var(--v-border-color), var(--v-border-opacity)); min-inline-size: 0;
overflow: visible;
overflow-wrap: anywhere;
white-space: normal;
word-break: break-word;
} }
.manual-review-dialog__evidence pre { .manual-review-dialog__evidence pre {
@@ -5,6 +5,14 @@ import { renderWithProviders } from '@tests/support/render'
import { flushPromises } from '@vue/test-utils' import { flushPromises } from '@vue/test-utils'
import userEvent from '@testing-library/user-event' import userEvent from '@testing-library/user-event'
import { beforeEach, describe, expect, it, vi } from 'vitest' import { beforeEach, describe, expect, it, vi } from 'vitest'
import { readFileSync } from 'node:fs'
import { resolve } from 'node:path'
import { cwd } from 'node:process'
const transferManualReviewSource = readFileSync(
resolve(cwd(), 'src/components/dialog/TransferManualReviewDialog.vue'),
'utf8',
)
const mocks = vi.hoisted(() => ({ const mocks = vi.hoisted(() => ({
apiPost: vi.fn(), apiPost: vi.fn(),
@@ -64,6 +72,13 @@ describe('TransferManualReviewDialog', () => {
mocks.apiPost.mockResolvedValue({}) mocks.apiPost.mockResolvedValue({})
}) })
it('uses mobile fullscreen and keeps long review content readable', () => {
expect(transferManualReviewSource).toContain(':fullscreen="!display.mdAndUp.value"')
expect(transferManualReviewSource).toContain('.manual-review-dialog__error > span')
expect(transferManualReviewSource).toContain('.manual-review-dialog__notice :deep(.v-alert__content)')
expect(transferManualReviewSource).not.toMatch(/\.manual-review-dialog__evidence\s*\{[^}]*border:/s)
})
it('requires review notes before submitting a decision', async () => { it('requires review notes before submitting a decision', async () => {
const user = userEvent.setup() const user = userEvent.setup()