7/7/1405
This commit is contained in:
@@ -4,6 +4,7 @@ import { useRoute } from 'vue-router'
|
||||
import AdminIcon from '@/components/icons/AdminIcon.vue'
|
||||
import AdminPageSkeleton from '@/components/ui/AdminPageSkeleton.vue'
|
||||
import AdminSelect from '@/components/ui/AdminSelect.vue'
|
||||
import AdminImageLightbox from '@/components/ui/AdminImageLightbox.vue'
|
||||
import { cashPaymentsApi, ordersApi } from '@/services/api'
|
||||
import { ApiError } from '@/services/http'
|
||||
import {
|
||||
@@ -21,6 +22,13 @@ const statusFilter = ref<'all' | CashPayment['status']>('all')
|
||||
const loading = ref(true)
|
||||
const toast = useToast()
|
||||
const busyId = ref<string | null>(null)
|
||||
const receiptPreviewOpen = ref(false)
|
||||
const receiptPreviewSrc = ref('')
|
||||
|
||||
function openReceiptPreview(src: string) {
|
||||
receiptPreviewSrc.value = src
|
||||
receiptPreviewOpen.value = true
|
||||
}
|
||||
|
||||
const statusFilterOptions = [
|
||||
{ value: 'all', label: 'همه وضعیتها' },
|
||||
@@ -144,6 +152,7 @@ async function rejectPayment(payment: CashPayment) {
|
||||
<th>شماره پیگیری</th>
|
||||
<th>پایان اعتبار</th>
|
||||
<th>وضعیت</th>
|
||||
<th>رسید</th>
|
||||
<th>عملیات</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -161,6 +170,17 @@ async function rejectPayment(payment: CashPayment) {
|
||||
{{ CASH_PAYMENT_STATUS_LABELS[payment.status] }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
v-if="payment.receiptImage"
|
||||
type="button"
|
||||
class="admin-btn admin-btn--outline admin-btn--xs"
|
||||
@click="openReceiptPreview(payment.receiptImage!)"
|
||||
>
|
||||
مشاهده
|
||||
</button>
|
||||
<span v-else>—</span>
|
||||
</td>
|
||||
<td>
|
||||
<div v-if="payment.status === 'pending_review'" class="admin-action-group">
|
||||
<button
|
||||
@@ -212,6 +232,14 @@ async function rejectPayment(payment: CashPayment) {
|
||||
<span v-if="payment.paidAmount != null">پرداختی: {{ formatCurrency(payment.paidAmount) }}</span>
|
||||
<span dir="ltr">{{ payment.trackingCode }}</span>
|
||||
</div>
|
||||
<button
|
||||
v-if="payment.receiptImage"
|
||||
type="button"
|
||||
class="admin-btn admin-btn--outline admin-btn--xs cash-payments-page__receipt-btn"
|
||||
@click="openReceiptPreview(payment.receiptImage!)"
|
||||
>
|
||||
مشاهده تصویر رسید
|
||||
</button>
|
||||
<div
|
||||
v-if="payment.status === 'pending_review' || payment.status === 'approved'"
|
||||
class="admin-card-item__actions"
|
||||
@@ -247,6 +275,13 @@ async function rejectPayment(payment: CashPayment) {
|
||||
</article>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<AdminImageLightbox
|
||||
v-if="receiptPreviewSrc"
|
||||
v-model="receiptPreviewOpen"
|
||||
:src="receiptPreviewSrc"
|
||||
alt="تصویر رسید پرداخت"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -3,10 +3,17 @@ import { computed, reactive, ref, watch } from 'vue'
|
||||
import BaseModal from '@/components/ui/BaseModal.vue'
|
||||
import AdminSelect from '@/components/ui/AdminSelect.vue'
|
||||
import { useOrdersPageContext } from '@/composables/useOrdersPage'
|
||||
import { productsApi } from '@/services/api'
|
||||
import { productsApi, settingsApi } from '@/services/api'
|
||||
import { ApiError } from '@/services/http'
|
||||
import { useToast } from '@/composables/useToast'
|
||||
import { formatNumber } from '@/utils/format'
|
||||
import {
|
||||
meterageFromQuantity,
|
||||
rollMetersForProduct,
|
||||
rollsFromMeterage,
|
||||
validateOrderMeterage,
|
||||
type QuantityMode,
|
||||
} from '@/utils/orderItemValidation'
|
||||
import type { Product } from '@/types'
|
||||
|
||||
const {
|
||||
@@ -24,9 +31,11 @@ const {
|
||||
|
||||
const toast = useToast()
|
||||
const products = ref<Product[]>([])
|
||||
const minOrderRolls = ref(1)
|
||||
const draft = reactive({
|
||||
productId: '',
|
||||
meterage: 0,
|
||||
quantityMode: 'roll' as QuantityMode,
|
||||
quantity: 0,
|
||||
})
|
||||
|
||||
const customerOptions = computed(() =>
|
||||
@@ -53,6 +62,10 @@ const selectedProduct = computed(() =>
|
||||
products.value.find((product) => product.id === draft.productId) ?? null,
|
||||
)
|
||||
|
||||
const quantityLabel = computed(() =>
|
||||
draft.quantityMode === 'roll' ? 'تعداد طاقه' : 'متراژ (متر)',
|
||||
)
|
||||
|
||||
watch(
|
||||
() => form.customerId,
|
||||
(id) => {
|
||||
@@ -65,44 +78,85 @@ watch(
|
||||
},
|
||||
)
|
||||
|
||||
function defaultQuantity(product: Product, mode: QuantityMode) {
|
||||
const minRolls = minOrderRolls.value > 0 ? minOrderRolls.value : 1
|
||||
if (mode === 'roll') return minRolls
|
||||
const rollMeters = rollMetersForProduct(product)
|
||||
return Math.max(product.minMeterage || 0, minRolls * rollMeters)
|
||||
}
|
||||
|
||||
watch(showAddModal, async (open) => {
|
||||
if (!open) return
|
||||
draft.productId = ''
|
||||
draft.meterage = 0
|
||||
draft.quantityMode = 'roll'
|
||||
draft.quantity = 0
|
||||
formError.value = ''
|
||||
try {
|
||||
products.value = await productsApi.list({ activeOnly: true })
|
||||
const [list, settings] = await Promise.all([
|
||||
productsApi.list({ activeOnly: true }),
|
||||
settingsApi.get().catch(() => null),
|
||||
])
|
||||
products.value = list
|
||||
minOrderRolls.value = settings?.minOrderRollsPerProduct ?? 1
|
||||
} catch (err) {
|
||||
toast.error(err instanceof ApiError ? err.message : 'بارگذاری محصولات انجام نشد.')
|
||||
}
|
||||
})
|
||||
|
||||
watch(
|
||||
() => draft.productId,
|
||||
(id) => {
|
||||
() => [draft.productId, draft.quantityMode] as const,
|
||||
([id, mode]) => {
|
||||
const product = products.value.find((item) => item.id === id)
|
||||
if (!product) return
|
||||
draft.meterage = product.rollMeters || product.minMeterage || product.orderStep || 50
|
||||
draft.quantity = defaultQuantity(product, mode)
|
||||
},
|
||||
)
|
||||
|
||||
function addDraftProduct() {
|
||||
formError.value = ''
|
||||
const product = selectedProduct.value
|
||||
if (!product) {
|
||||
formError.value = 'محصول را انتخاب کنید.'
|
||||
toast.error(formError.value)
|
||||
return
|
||||
}
|
||||
if (!draft.meterage || draft.meterage <= 0) {
|
||||
formError.value = 'متراژ باید بزرگتر از صفر باشد.'
|
||||
if (!draft.quantity || draft.quantity <= 0) {
|
||||
formError.value = 'مقدار باید بزرگتر از صفر باشد.'
|
||||
toast.error(formError.value)
|
||||
return
|
||||
}
|
||||
addFormItem({
|
||||
|
||||
const meterage = meterageFromQuantity(product, draft.quantityMode, draft.quantity)
|
||||
const validationError = validateOrderMeterage(product, meterage, minOrderRolls.value)
|
||||
if (validationError) {
|
||||
formError.value = validationError
|
||||
toast.error(validationError)
|
||||
return
|
||||
}
|
||||
|
||||
const added = addFormItem({
|
||||
productId: product.id,
|
||||
productName: product.name,
|
||||
catalogCode: product.catalogCode,
|
||||
meterage: draft.meterage,
|
||||
meterage,
|
||||
})
|
||||
if (!added) {
|
||||
toast.error(formError.value || 'افزودن محصول انجام نشد.')
|
||||
return
|
||||
}
|
||||
|
||||
draft.productId = ''
|
||||
draft.meterage = 0
|
||||
draft.quantityMode = 'roll'
|
||||
draft.quantity = 0
|
||||
}
|
||||
|
||||
function itemRollsLabel(item: { productId: string; meterage: number }) {
|
||||
const product = products.value.find((p) => p.id === item.productId)
|
||||
if (!product) return null
|
||||
const rolls = rollsFromMeterage(product, item.meterage)
|
||||
if (!Number.isFinite(rolls)) return null
|
||||
const rounded = Math.round(rolls * 100) / 100
|
||||
return rounded % 1 === 0 ? formatNumber(rounded) : formatNumber(rounded)
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -133,26 +187,49 @@ function addDraftProduct() {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="admin-form__row">
|
||||
<div class="admin-form__field">
|
||||
<label>محصول</label>
|
||||
<AdminSelect
|
||||
v-model="draft.productId"
|
||||
block
|
||||
searchable
|
||||
aria-label="انتخاب محصول"
|
||||
placeholder="جستجو و انتخاب محصول"
|
||||
search-placeholder="نام، کد یا کالیته…"
|
||||
:options="productOptions"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="admin-form__row order-add__qty-row">
|
||||
<div class="admin-form__field">
|
||||
<label>محصول</label>
|
||||
<AdminSelect
|
||||
v-model="draft.productId"
|
||||
block
|
||||
searchable
|
||||
aria-label="انتخاب محصول"
|
||||
placeholder="جستجو و انتخاب محصول"
|
||||
search-placeholder="نام، کد یا کالیته…"
|
||||
:options="productOptions"
|
||||
/>
|
||||
<span class="admin-form__label">واحد سفارش</span>
|
||||
<div class="order-add__mode" role="group" aria-label="واحد سفارش">
|
||||
<button
|
||||
type="button"
|
||||
class="order-add__mode-btn"
|
||||
:class="{ 'order-add__mode-btn--active': draft.quantityMode === 'roll' }"
|
||||
@click="draft.quantityMode = 'roll'"
|
||||
>
|
||||
طاقه
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="order-add__mode-btn"
|
||||
:class="{ 'order-add__mode-btn--active': draft.quantityMode === 'meter' }"
|
||||
@click="draft.quantityMode = 'meter'"
|
||||
>
|
||||
متراژ
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="admin-form__field">
|
||||
<label for="order-item-meterage">متراژ (متر)</label>
|
||||
<label for="order-item-quantity">{{ quantityLabel }}</label>
|
||||
<input
|
||||
id="order-item-meterage"
|
||||
v-model.number="draft.meterage"
|
||||
id="order-item-quantity"
|
||||
v-model.number="draft.quantity"
|
||||
type="number"
|
||||
min="1"
|
||||
min="0"
|
||||
step="any"
|
||||
class="admin-input"
|
||||
/>
|
||||
</div>
|
||||
@@ -166,9 +243,19 @@ function addDraftProduct() {
|
||||
<div v-for="item in form.items" :key="item.productId" class="order-add__item">
|
||||
<div>
|
||||
<strong>{{ item.productName }}</strong>
|
||||
<span>کالیته {{ item.catalogCode }} — {{ formatNumber(item.meterage) }} متر</span>
|
||||
<span>
|
||||
کالیته {{ item.catalogCode }} —
|
||||
{{ formatNumber(item.meterage) }} متر
|
||||
<template v-if="itemRollsLabel(item)">
|
||||
({{ itemRollsLabel(item) }} طاقه)
|
||||
</template>
|
||||
</span>
|
||||
</div>
|
||||
<button type="button" class="admin-btn admin-btn--outline-danger admin-btn--xs" @click="removeFormItem(item.productId)">
|
||||
<button
|
||||
type="button"
|
||||
class="admin-btn admin-btn--outline-danger admin-btn--xs"
|
||||
@click="removeFormItem(item.productId)"
|
||||
>
|
||||
حذف
|
||||
</button>
|
||||
</div>
|
||||
@@ -199,6 +286,45 @@ function addDraftProduct() {
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.admin-form__label {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.order-add__qty-row {
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
.order-add__mode {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.order-add__mode-btn {
|
||||
height: var(--control-height, 40px);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--color-surface);
|
||||
color: var(--color-text-muted);
|
||||
font: inherit;
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
border-color 0.15s,
|
||||
background 0.15s,
|
||||
color 0.15s;
|
||||
}
|
||||
|
||||
.order-add__mode-btn--active {
|
||||
border-color: var(--color-primary);
|
||||
background: var(--color-primary-soft, rgba(var(--color-primary-rgb), 0.08));
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.order-add__items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -3,9 +3,10 @@ import { computed, ref, watch } from 'vue'
|
||||
import AdminIcon from '@/components/icons/AdminIcon.vue'
|
||||
import AdminSelect from '@/components/ui/AdminSelect.vue'
|
||||
import StatusBadge from '@/components/ui/StatusBadge.vue'
|
||||
import { formatCurrency, formatMeterage, formatNumber } from '@/utils/format'
|
||||
import { CUSTOMER_TYPE_LABELS } from '@/utils/adminLabels'
|
||||
import { ORDER_STATUS_META, RAW_ORDER_STATUS_LABELS, canFinalApproveOrder, canPriceOrder, rawStatusLabel } from '@/utils/orderStatus'
|
||||
import { formatCurrency, formatDateTime, formatMeterage, formatNumber } from '@/utils/format'
|
||||
import { CASH_PAYMENT_STATUS_LABELS, CUSTOMER_TYPE_LABELS } from '@/utils/adminLabels'
|
||||
import AdminImageLightbox from '@/components/ui/AdminImageLightbox.vue'
|
||||
import { ORDER_STATUS_META, canFinalApproveOrder, canPriceOrder, rawStatusLabel } from '@/utils/orderStatus'
|
||||
import type { CustomerType, Order, RawOrderStatus } from '@/types'
|
||||
|
||||
const props = withDefaults(
|
||||
@@ -28,25 +29,19 @@ const emit = defineEmits<{
|
||||
orderId: string
|
||||
status: RawOrderStatus
|
||||
note?: string
|
||||
reason?: string
|
||||
force?: boolean
|
||||
},
|
||||
]
|
||||
}>()
|
||||
|
||||
const manualMode = ref(false)
|
||||
const nextStatus = ref<RawOrderStatus | ''>('')
|
||||
const statusNote = ref('')
|
||||
const receiptPreviewOpen = ref(false)
|
||||
|
||||
const allStatuses = Object.keys(RAW_ORDER_STATUS_LABELS) as RawOrderStatus[]
|
||||
const cashReceipt = computed(() => props.order.cashReceipt ?? null)
|
||||
|
||||
const transitions = computed(() => props.order.allowedTransitions ?? [])
|
||||
|
||||
const statusOptions = computed<RawOrderStatus[]>(() =>
|
||||
manualMode.value
|
||||
? allStatuses.filter((s) => s !== props.order.rawStatus)
|
||||
: [...transitions.value],
|
||||
)
|
||||
const statusOptions = computed<RawOrderStatus[]>(() => [...transitions.value])
|
||||
|
||||
const statusSelectOptions = computed(() => [
|
||||
{ value: '', label: 'انتخاب کنید…' },
|
||||
@@ -56,10 +51,7 @@ const statusSelectOptions = computed(() => [
|
||||
})),
|
||||
])
|
||||
|
||||
const noteRequired = computed(() => manualMode.value)
|
||||
const canSubmitStatus = computed(
|
||||
() => !!nextStatus.value && (!noteRequired.value || statusNote.value.trim().length > 0),
|
||||
)
|
||||
const canSubmitStatus = computed(() => !!nextStatus.value)
|
||||
|
||||
const useTable = computed(() => props.layout === 'desktop')
|
||||
|
||||
@@ -100,10 +92,13 @@ const primaryAction = computed<'pricing' | 'final' | 'checks' | 'cash' | null>((
|
||||
return null
|
||||
})
|
||||
|
||||
watch([() => props.order.id, manualMode], () => {
|
||||
nextStatus.value = ''
|
||||
statusNote.value = ''
|
||||
})
|
||||
watch(
|
||||
() => props.order.id,
|
||||
() => {
|
||||
nextStatus.value = ''
|
||||
statusNote.value = ''
|
||||
},
|
||||
)
|
||||
|
||||
function submitStatus() {
|
||||
if (!nextStatus.value || !canSubmitStatus.value) return
|
||||
@@ -112,12 +107,18 @@ function submitStatus() {
|
||||
orderId: props.order.id,
|
||||
status: nextStatus.value,
|
||||
note,
|
||||
reason: manualMode.value ? note : undefined,
|
||||
force: manualMode.value || undefined,
|
||||
})
|
||||
nextStatus.value = ''
|
||||
statusNote.value = ''
|
||||
}
|
||||
|
||||
function onCashReceiptAction() {
|
||||
if (cashReceipt.value?.receiptImage) {
|
||||
receiptPreviewOpen.value = true
|
||||
return
|
||||
}
|
||||
emit('cashReceipt')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -157,9 +158,9 @@ function submitStatus() {
|
||||
type="button"
|
||||
class="admin-btn admin-btn--sm"
|
||||
:class="primaryAction === 'cash' ? 'admin-btn--primary' : 'admin-btn--outline'"
|
||||
@click="emit('cashReceipt')"
|
||||
@click="onCashReceiptAction"
|
||||
>
|
||||
رسید پرداخت
|
||||
{{ cashReceipt?.receiptImage ? 'مشاهده رسید' : 'رسید پرداخت' }}
|
||||
</button>
|
||||
<button
|
||||
v-if="showFinalApprove"
|
||||
@@ -275,20 +276,75 @@ function submitStatus() {
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="order-detail__section order-detail__status no-print">
|
||||
<div class="order-detail__status-head">
|
||||
<h3 class="order-detail__section-title">تغییر وضعیت</h3>
|
||||
<label class="order-detail__manual">
|
||||
<input v-model="manualMode" type="checkbox" />
|
||||
<span>تغییر دستی</span>
|
||||
</label>
|
||||
<section v-if="cashReceipt" class="order-detail__section">
|
||||
<h3 class="order-detail__section-title">رسید پرداخت نقدی</h3>
|
||||
<div class="order-detail__info">
|
||||
<div class="order-detail__info-row">
|
||||
<span class="order-detail__info-label">وضعیت</span>
|
||||
<span class="order-detail__info-value">
|
||||
{{ CASH_PAYMENT_STATUS_LABELS[cashReceipt.status] }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="order-detail__info-row">
|
||||
<span class="order-detail__info-label">مبلغ واریزی</span>
|
||||
<span class="order-detail__info-value">
|
||||
{{
|
||||
cashReceipt.paidAmount != null
|
||||
? formatCurrency(cashReceipt.paidAmount)
|
||||
: formatCurrency(cashReceipt.amount)
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="cashReceipt.transferDate" class="order-detail__info-row">
|
||||
<span class="order-detail__info-label">تاریخ واریز</span>
|
||||
<span class="order-detail__info-value">
|
||||
{{ formatDateTime(cashReceipt.transferDate) }}
|
||||
<template v-if="cashReceipt.transferTime"> · {{ cashReceipt.transferTime }}</template>
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="cashReceipt.trackingNumber || cashReceipt.trackingCode" class="order-detail__info-row">
|
||||
<span class="order-detail__info-label">پیگیری</span>
|
||||
<span class="order-detail__info-value" dir="ltr">
|
||||
{{ cashReceipt.trackingNumber || cashReceipt.trackingCode }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="cashReceipt.notes" class="order-detail__info-row">
|
||||
<span class="order-detail__info-label">یادداشت</span>
|
||||
<span class="order-detail__info-value">{{ cashReceipt.notes }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p v-if="manualMode" class="order-detail__status-hint">
|
||||
این گذر خارج از روال استاندارد است؛ ثبت یادداشت اجباری است.
|
||||
</p>
|
||||
<p v-else-if="!statusOptions.length" class="order-detail__status-hint">
|
||||
در این وضعیت گذر خودکاری وجود ندارد. برای تغییر، حالت دستی را فعال کنید.
|
||||
<div v-if="cashReceipt.receiptImage" class="order-detail__receipt">
|
||||
<button
|
||||
type="button"
|
||||
class="order-detail__receipt-thumb"
|
||||
@click="receiptPreviewOpen = true"
|
||||
>
|
||||
<img :src="cashReceipt.receiptImage" alt="تصویر رسید پرداخت" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="admin-btn admin-btn--outline admin-btn--sm"
|
||||
@click="receiptPreviewOpen = true"
|
||||
>
|
||||
مشاهده تصویر رسید
|
||||
</button>
|
||||
</div>
|
||||
<p v-else class="order-detail__status-hint">تصویر رسید هنوز آپلود نشده است.</p>
|
||||
</section>
|
||||
|
||||
<AdminImageLightbox
|
||||
v-if="cashReceipt?.receiptImage"
|
||||
v-model="receiptPreviewOpen"
|
||||
:src="cashReceipt.receiptImage"
|
||||
alt="تصویر رسید پرداخت"
|
||||
/>
|
||||
|
||||
<section class="order-detail__section order-detail__status no-print">
|
||||
<h3 class="order-detail__section-title">تغییر وضعیت</h3>
|
||||
|
||||
<p v-if="!statusOptions.length" class="order-detail__status-hint">
|
||||
در این مرحله گذر مجازی از این بخش نیست؛ از اقدامهای مرحلهای بالا (قیمتگذاری، چک، رسید نقدی و …) استفاده کنید.
|
||||
</p>
|
||||
|
||||
<div v-if="statusOptions.length" class="order-detail__status-form">
|
||||
@@ -304,9 +360,7 @@ function submitStatus() {
|
||||
</div>
|
||||
|
||||
<div class="admin-form__field">
|
||||
<label for="status-note">
|
||||
یادداشت{{ noteRequired ? ' (اجباری)' : ' (اختیاری)' }}
|
||||
</label>
|
||||
<label for="status-note">یادداشت (اختیاری)</label>
|
||||
<input
|
||||
id="status-note"
|
||||
v-model="statusNote"
|
||||
@@ -477,31 +531,33 @@ function submitStatus() {
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.order-detail__status-head {
|
||||
.order-detail__receipt {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 8px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.order-detail__status-head .order-detail__section-title {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.order-detail__manual {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 0.8125rem;
|
||||
color: var(--color-text-muted);
|
||||
.order-detail__receipt-thumb {
|
||||
padding: 0;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-bg);
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
.order-detail__manual input {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
accent-color: var(--color-primary);
|
||||
.order-detail__receipt-thumb img {
|
||||
display: block;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.order-detail__status .order-detail__section-title {
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
|
||||
.order-detail__status-hint {
|
||||
|
||||
Reference in New Issue
Block a user