diff --git a/frontend/src/features/orders/views/MobileOrderDetailView.css b/frontend/src/features/orders/views/MobileOrderDetailView.css index 7b156bf..0b34651 100644 --- a/frontend/src/features/orders/views/MobileOrderDetailView.css +++ b/frontend/src/features/orders/views/MobileOrderDetailView.css @@ -241,11 +241,28 @@ } .popup-close { + display: inline-flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + margin: -8px -8px -8px 0; + padding: 0; border: none; background: transparent; - font-size: 16px; - color: #a0aec0; + color: #94a3b8; cursor: pointer; + border-radius: 50%; + transition: + background-color 0.15s ease, + color 0.15s ease; + touch-action: manipulation; + -webkit-tap-highlight-color: transparent; +} + +.popup-close:active { + background-color: #f1f5f9; + color: #475569; } .popup-body { @@ -277,10 +294,91 @@ margin: 0 0 12px 0; } +.counter-input-card { + display: flex; + flex-direction: column; + gap: 10px; + margin-bottom: 14px; +} + +.counter-input-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 2px 0; +} + +.counter-label { + font-size: 14px; + font-weight: 600; + color: #1e293b; + white-space: nowrap; + flex-shrink: 0; +} + +.counter-input-wrapper { + flex: 1; + max-width: 170px; + display: flex; + align-items: center; + border: 1.5px solid #cbd5e1; + border-radius: 8px; + background: #ffffff; + padding: 6px 12px; + box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04); + transition: + border-color 0.15s ease, + box-shadow 0.15s ease, + background-color 0.15s ease; +} + +.counter-input-wrapper:hover { + border-color: #94a3b8; +} + +.counter-input-wrapper:focus-within { + border-color: #ff6a00; + background: #ffffff; + box-shadow: 0 0 0 3px rgba(255, 106, 0, 0.15); +} + +.counter-input-control { + flex: 1; + min-width: 0; + border: none; + background: transparent; + outline: none; + font-size: 16px; + font-weight: 600; + color: #0f172a; + text-align: right; + padding: 0; +} + +.counter-unit { + margin-left: 6px; + font-size: 13px; + color: #64748b; + font-weight: 600; + user-select: none; +} + .popup-field { - background: #f7fafc; + background: #f8fafc; + border: 1.5px solid #e2e8f0; border-radius: 8px; margin-bottom: 12px; + transition: + border-color 0.15s ease, + box-shadow 0.15s ease, + background-color 0.15s ease; +} + +.popup-field:focus-within { + border-color: #ff6a00; + background: #ffffff; + box-shadow: 0 0 0 3px rgba(255, 106, 0, 0.15); } /* iOS Safari:font-size < 16px 时聚焦会自动放大整个页面 */ diff --git a/frontend/src/features/orders/views/MobileOrderDetailView.vue b/frontend/src/features/orders/views/MobileOrderDetailView.vue index 2d1c0dd..8eb10e2 100644 --- a/frontend/src/features/orders/views/MobileOrderDetailView.vue +++ b/frontend/src/features/orders/views/MobileOrderDetailView.vue @@ -114,9 +114,18 @@ const { ordersPath: '/m/orders', chatPathPrefix: '/m/chats/', startCashier: (payment, onPaid) => cashier.openMobilePaymentCashier(payment, onPaid), - onCounterCheckoutSuccess: () => (showCounterPopup.value = false), - onRejectCheckoutSuccess: () => (showRejectPopup.value = false), - onCreateDisputeSuccess: () => (showDisputePopup.value = false), + onCounterCheckoutSuccess: () => { + markPopupClosed() + showCounterPopup.value = false + }, + onRejectCheckoutSuccess: () => { + markPopupClosed() + showRejectPopup.value = false + }, + onCreateDisputeSuccess: () => { + markPopupClosed() + showDisputePopup.value = false + }, }) const rentalTerms = computed(() => orderRentalTerms(order.value)) @@ -125,6 +134,48 @@ const rentalTerms = computed(() => orderRentalTerms(order.value)) const showDisputePopup = ref(false) const showCounterPopup = ref(false) const showRejectPopup = ref(false) + +// 弹窗防穿透/防抖冷却:防止移动端/iOS Safari 点击关闭时 300ms 穿透触发底层按钮重新唤起 +const POPUP_COOLDOWN_MS = 450 +let lastPopupClosedAt = 0 + +function markPopupClosed() { + lastPopupClosedAt = Date.now() +} + +function canOpenPopup(): boolean { + return Date.now() - lastPopupClosedAt >= POPUP_COOLDOWN_MS +} + +function openCounterPopup() { + if (!canOpenPopup()) return + showCounterPopup.value = true +} + +function closeCounterPopup() { + markPopupClosed() + showCounterPopup.value = false +} + +function openRejectPopup() { + if (!canOpenPopup()) return + showRejectPopup.value = true +} + +function closeRejectPopup() { + markPopupClosed() + showRejectPopup.value = false +} + +function openDisputePopup() { + if (!canOpenPopup()) return + showDisputePopup.value = true +} + +function closeDisputePopup() { + markPopupClosed() + showDisputePopup.value = false +} const isOwnerCheckoutPending = computed(() => { if (!order.value || !isOwner.value) return false return ( @@ -443,7 +494,7 @@ async function copyListingCode() { block plain round - @click="showCounterPopup = true" + @click="openCounterPopup" > 修改结账方案 @@ -481,11 +532,11 @@ async function copyListingCode() { block plain round - @click="showCounterPopup = true" + @click="openCounterPopup" > 还价修改方案 - + 发起结账争议 @@ -493,7 +544,7 @@ async function copyListingCode() {
- + {{ isCheckoutDisputeStage ? '对结账发起申诉争议' : '发起订单申诉争议' }}
@@ -536,10 +587,23 @@ async function copyListingCode() { /> - + + + @@ -583,6 +659,7 @@ async function copyListingCode() { v-model="counterForm.reason" rows="2" autosize + required label="修改原因" type="textarea" placeholder="请说明调整原因(必填)" @@ -618,10 +695,23 @@ async function copyListingCode() { - +