292 lines
7.2 KiB
Vue
292 lines
7.2 KiB
Vue
<script setup lang="ts">
|
||
import BaseButton from '@/components/ui/BaseButton.vue'
|
||
import EmptyState from '@/components/ui/EmptyState.vue'
|
||
import IconTrash from '@/components/icons/IconTrash.vue'
|
||
import IconChevronBack from '@/components/icons/IconChevronBack.vue'
|
||
import { formatMeterage, formatNumber } from '@/utils/format'
|
||
import type { CartItem } from '@/types'
|
||
|
||
defineProps<{
|
||
items: CartItem[]
|
||
companyName: string
|
||
companySummaries?: {
|
||
companyId: string
|
||
companyName: string
|
||
meterage: number
|
||
maxMeterage: number
|
||
remaining: number
|
||
}[]
|
||
totalMeterage: number
|
||
maxMeterage: number
|
||
remainingCapacity: number
|
||
isEmpty: boolean
|
||
submitting?: boolean
|
||
}>()
|
||
|
||
const emit = defineEmits<{
|
||
back: []
|
||
remove: [id: string]
|
||
checkout: []
|
||
selectCompany: []
|
||
}>()
|
||
</script>
|
||
|
||
<template>
|
||
<div class="cart-mobile-page">
|
||
<header class="cart-mobile-page__nav">
|
||
<button type="button" class="nav-btn" aria-label="بازگشت" @click="emit('back')">
|
||
<IconChevronBack :size="20" />
|
||
</button>
|
||
<h1 class="cart-mobile-page__title">سبد خرید</h1>
|
||
<button type="button" class="nav-btn nav-btn--ghost" aria-hidden="true" tabindex="-1">
|
||
<IconChevronBack :size="18" class="nav-btn__flip" />
|
||
</button>
|
||
</header>
|
||
|
||
<div v-if="!isEmpty" class="cart-mobile-page__body">
|
||
<p class="cart-mobile-page__company">
|
||
{{ companyName ? `شرکتها: ${companyName}` : 'سبد خرید مشترک' }}
|
||
</p>
|
||
|
||
<div class="cart-mobile-page__scroll">
|
||
<div class="cart-mobile-table">
|
||
<div class="cart-mobile-table__head">
|
||
<span>محصول</span>
|
||
<span>متراژ (متر)</span>
|
||
<span>حذف</span>
|
||
</div>
|
||
<div v-for="item in items" :key="item.id" class="cart-mobile-table__row">
|
||
<div class="col-product">
|
||
<strong>{{ item.productName }}</strong>
|
||
<span>کد: {{ item.productCode }}</span>
|
||
<span v-if="item.companyName" class="col-product__company">{{ item.companyName }}</span>
|
||
</div>
|
||
<div class="col-meterage">{{ formatNumber(item.meterage) }}</div>
|
||
<div class="col-delete">
|
||
<button type="button" aria-label="حذف" @click="emit('remove', item.id)">
|
||
<IconTrash :size="20" />
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="cart-mobile-page__footer">
|
||
<div class="cart-mobile-summary">
|
||
<div class="cart-mobile-summary__row">
|
||
<span>جمع متراژ کل</span>
|
||
<strong>{{ formatMeterage(totalMeterage) }}</strong>
|
||
</div>
|
||
<template v-if="companySummaries?.length">
|
||
<div
|
||
v-for="row in companySummaries"
|
||
:key="row.companyId"
|
||
class="cart-mobile-summary__row cart-mobile-summary__row--sub"
|
||
>
|
||
<span>{{ row.companyName }}</span>
|
||
<strong>
|
||
{{ formatMeterage(row.meterage) }}
|
||
<span class="cart-mobile-summary__cap">/ {{ formatMeterage(row.maxMeterage) }}</span>
|
||
</strong>
|
||
</div>
|
||
</template>
|
||
<div v-else class="cart-mobile-summary__row">
|
||
<span>حداکثر مجاز</span>
|
||
<strong>{{ formatMeterage(maxMeterage) }}</strong>
|
||
</div>
|
||
<div class="cart-mobile-summary__row">
|
||
<span>باقی مانده</span>
|
||
<strong>{{ formatMeterage(remainingCapacity) }}</strong>
|
||
</div>
|
||
</div>
|
||
<p class="cart-mobile-page__hint">
|
||
پس از ثبت، برای هر شرکت یک پیشسفارش جداگانه ساخته میشود و مدیریت قیمتگذاری میکند.
|
||
</p>
|
||
<BaseButton block :loading="submitting" @click="emit('checkout')">ثبت پیشسفارش</BaseButton>
|
||
</div>
|
||
</div>
|
||
|
||
<EmptyState v-else variant="cart" message="برای شروع سفارش، یک شرکت انتخاب کنید">
|
||
<BaseButton @click="emit('selectCompany')">انتخاب شرکت</BaseButton>
|
||
</EmptyState>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.cart-mobile-page {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-height: 0;
|
||
width: 100%;
|
||
background: var(--color-bg);
|
||
color: var(--color-text);
|
||
padding: 12px 0 0;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.cart-mobile-page__nav {
|
||
display: grid;
|
||
grid-template-columns: 40px 1fr 40px;
|
||
align-items: center;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.cart-mobile-page__hint {
|
||
margin: 0 0 10px;
|
||
font-size: 0.75rem;
|
||
line-height: 1.6;
|
||
color: var(--color-text-muted);
|
||
}
|
||
|
||
.cart-mobile-page__title {
|
||
margin: 0;
|
||
font-size: 1.0625rem;
|
||
font-weight: 700;
|
||
text-align: center;
|
||
color: var(--color-text);
|
||
}
|
||
|
||
.nav-btn {
|
||
width: 40px;
|
||
height: 40px;
|
||
border: none;
|
||
background: none;
|
||
color: var(--color-text);
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.nav-btn--ghost {
|
||
visibility: hidden;
|
||
}
|
||
|
||
.nav-btn__flip {
|
||
transform: scaleX(-1);
|
||
}
|
||
|
||
.cart-mobile-page__body {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-height: 0;
|
||
}
|
||
|
||
.cart-mobile-page__company {
|
||
margin: 0 0 16px;
|
||
font-size: 0.875rem;
|
||
color: var(--color-text-muted);
|
||
}
|
||
|
||
.cart-mobile-page__scroll {
|
||
flex: 1;
|
||
min-height: 0;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.cart-mobile-table {
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-md);
|
||
overflow: hidden;
|
||
background: var(--color-elevated);
|
||
}
|
||
|
||
.cart-mobile-table__head,
|
||
.cart-mobile-table__row {
|
||
display: grid;
|
||
grid-template-columns: 1fr 88px 52px;
|
||
gap: 8px;
|
||
padding: 10px 12px;
|
||
align-items: center;
|
||
}
|
||
|
||
.cart-mobile-table__head {
|
||
background: var(--color-summary-bg);
|
||
font-size: 0.75rem;
|
||
font-weight: 600;
|
||
color: var(--color-text-muted);
|
||
}
|
||
|
||
.cart-mobile-table__row {
|
||
border-top: 1px solid var(--color-border);
|
||
color: var(--color-text);
|
||
}
|
||
|
||
.col-product strong {
|
||
display: block;
|
||
font-size: 0.875rem;
|
||
}
|
||
|
||
.col-product span {
|
||
display: block;
|
||
font-size: 0.75rem;
|
||
color: var(--color-text-muted);
|
||
}
|
||
|
||
.col-product__company {
|
||
margin-top: 2px;
|
||
color: var(--color-primary) !important;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.col-meterage {
|
||
text-align: center;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.col-delete {
|
||
display: flex;
|
||
justify-content: center;
|
||
}
|
||
|
||
.col-delete button {
|
||
border: none;
|
||
background: none;
|
||
cursor: pointer;
|
||
color: var(--color-text-muted);
|
||
}
|
||
|
||
.cart-mobile-page__footer {
|
||
flex-shrink: 0;
|
||
margin: 0;
|
||
padding: 12px 0 calc(12px + env(safe-area-inset-bottom, 0));
|
||
border-top: 1px solid var(--color-border);
|
||
background: var(--color-bg);
|
||
}
|
||
|
||
.cart-mobile-summary {
|
||
margin-bottom: 12px;
|
||
padding: 14px 16px;
|
||
background: var(--color-elevated);
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-md);
|
||
color: var(--color-text);
|
||
box-shadow: var(--shadow-sm);
|
||
}
|
||
|
||
.cart-mobile-summary__row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 8px 0;
|
||
font-size: 0.875rem;
|
||
}
|
||
|
||
.cart-mobile-summary__row--sub {
|
||
color: var(--color-text-muted);
|
||
font-size: 0.8125rem;
|
||
}
|
||
|
||
.cart-mobile-summary__cap {
|
||
font-weight: 500;
|
||
color: var(--color-text-muted);
|
||
}
|
||
|
||
.cart-mobile-summary__row:not(:last-child) {
|
||
border-bottom: 1px solid var(--color-border);
|
||
}
|
||
</style>
|