659 lines
16 KiB
Vue
659 lines
16 KiB
Vue
<script setup lang="ts">
|
||
import { useRouter } from 'vue-router'
|
||
import CheckoutStepper from '@/components/layout/CheckoutStepper.vue'
|
||
import IconCart from '@/components/icons/IconCart.vue'
|
||
import IconTrash from '@/components/icons/IconTrash.vue'
|
||
import IconBuilding from '@/components/icons/IconBuilding.vue'
|
||
import IconChevronLeft from '@/components/icons/IconChevronLeft.vue'
|
||
import IconOrders from '@/components/icons/IconOrders.vue'
|
||
import IconClipboardEmpty from '@/components/icons/IconClipboardEmpty.vue'
|
||
import IconLightbulb from '@/components/icons/IconLightbulb.vue'
|
||
import IconLayoutGrid from '@/components/icons/IconLayoutGrid.vue'
|
||
import IconDiamond from '@/components/icons/IconDiamond.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<{
|
||
remove: [id: string]
|
||
checkout: []
|
||
selectCompany: []
|
||
back: []
|
||
}>()
|
||
|
||
const router = useRouter()
|
||
|
||
const quickLinks = [
|
||
{
|
||
title: 'انتخاب شرکت',
|
||
desc: 'شروع سفارش از انتخاب شرکت',
|
||
icon: IconBuilding,
|
||
tone: 'blue',
|
||
action: () => emit('selectCompany'),
|
||
},
|
||
{
|
||
title: 'سفارشگذاری',
|
||
desc: 'مشاهده کاتالوگ و افزودن کالا',
|
||
icon: IconCart,
|
||
tone: 'green',
|
||
action: () => emit('back'),
|
||
},
|
||
{
|
||
title: 'سفارشهای من',
|
||
desc: 'پیگیری وضعیت سفارشها',
|
||
icon: IconOrders,
|
||
tone: 'blue',
|
||
action: () => router.push('/orders'),
|
||
},
|
||
]
|
||
</script>
|
||
|
||
<template>
|
||
<div class="desktop-page cart-desktop-page">
|
||
<CheckoutStepper :current-step="1" />
|
||
|
||
<header class="cart-desktop-page__head">
|
||
<h1 class="cart-desktop-page__title">سبد خرید</h1>
|
||
<p class="cart-desktop-page__subtitle">
|
||
{{ companyName ? `شرکتها: ${companyName}` : 'سبد خرید مشترک' }}
|
||
</p>
|
||
</header>
|
||
|
||
<div class="cart-desktop-page__layout">
|
||
<section class="cart-desktop-page__main">
|
||
<div v-if="isEmpty" class="cart-empty-card">
|
||
<div class="cart-empty-card__visual" aria-hidden="true">
|
||
<span class="cart-empty-card__spark cart-empty-card__spark--1">+</span>
|
||
<span class="cart-empty-card__spark cart-empty-card__spark--2">·</span>
|
||
<span class="cart-empty-card__circle">
|
||
<IconCart :size="40" />
|
||
</span>
|
||
<span class="cart-empty-card__spark cart-empty-card__spark--3">·</span>
|
||
</div>
|
||
<h2 class="cart-empty-card__heading">سبد خرید شما خالی است</h2>
|
||
<p class="cart-empty-card__text">
|
||
برای ثبت سفارش جدید، ابتدا شرکت موردنظر را انتخاب کنید و سپس کالیته و محصولات
|
||
را به سبد اضافه کنید.
|
||
</p>
|
||
<div class="cart-empty-card__actions">
|
||
<button type="button" class="cart-btn cart-btn--primary" @click="emit('selectCompany')">
|
||
<IconBuilding :size="18" />
|
||
انتخاب شرکت
|
||
</button>
|
||
<button type="button" class="cart-btn cart-btn--outline" @click="emit('back')">
|
||
<IconChevronLeft :size="16" />
|
||
بازگشت به سفارشگذاری
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div v-else class="cart-items-card">
|
||
<div class="cart-items-card__scroll">
|
||
<table class="cart-items-table">
|
||
<thead>
|
||
<tr>
|
||
<th scope="col">محصول</th>
|
||
<th scope="col">شرکت</th>
|
||
<th scope="col">کد</th>
|
||
<th scope="col">متراژ (متر)</th>
|
||
<th scope="col">حذف</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr v-for="item in items" :key="item.id">
|
||
<td>
|
||
<strong class="cart-items-table__name">{{ item.productName }}</strong>
|
||
</td>
|
||
<td>{{ item.companyName || '—' }}</td>
|
||
<td>{{ item.productCode }}</td>
|
||
<td>{{ formatNumber(item.meterage) }}</td>
|
||
<td>
|
||
<button
|
||
type="button"
|
||
class="cart-items-table__delete"
|
||
aria-label="حذف"
|
||
@click="emit('remove', item.id)"
|
||
>
|
||
<IconTrash :size="18" />
|
||
</button>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<aside class="cart-desktop-page__sidebar">
|
||
<div class="cart-summary-card">
|
||
<h2 class="cart-summary-card__title">
|
||
<span class="cart-summary-card__title-icon" aria-hidden="true">
|
||
<IconClipboardEmpty :size="22" />
|
||
</span>
|
||
خلاصه سفارش
|
||
</h2>
|
||
<div class="cart-summary-card__rows">
|
||
<div class="cart-summary-card__row">
|
||
<span class="cart-summary-card__row-label">
|
||
<IconLayoutGrid :size="16" />
|
||
تعداد آیتمها
|
||
</span>
|
||
<strong>{{ formatNumber(items.length) }}</strong>
|
||
</div>
|
||
<div class="cart-summary-card__row">
|
||
<span class="cart-summary-card__row-label">
|
||
<IconDiamond :size="16" />
|
||
متراژ کل
|
||
</span>
|
||
<strong>{{ formatMeterage(totalMeterage) }}</strong>
|
||
</div>
|
||
<div
|
||
v-for="row in companySummaries ?? []"
|
||
:key="row.companyId"
|
||
class="cart-summary-card__row cart-summary-card__row--sub"
|
||
>
|
||
<span class="cart-summary-card__row-label">{{ row.companyName }}</span>
|
||
<strong>
|
||
{{ formatMeterage(row.meterage) }}
|
||
<span class="cart-summary-card__cap">/ {{ formatMeterage(row.maxMeterage) }}</span>
|
||
</strong>
|
||
</div>
|
||
<div class="cart-summary-card__row cart-summary-card__row--hint">
|
||
<span class="cart-summary-card__row-label">
|
||
<IconOrders :size="16" />
|
||
مبلغ فاکتور
|
||
</span>
|
||
<span class="cart-summary-card__hint-value">
|
||
پس از قیمتگذاری مدیریت
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<button
|
||
type="button"
|
||
class="cart-summary-card__cta"
|
||
:disabled="isEmpty || submitting"
|
||
@click="emit('checkout')"
|
||
>
|
||
<IconChevronLeft :size="16" />
|
||
{{ submitting ? 'در حال ثبت…' : 'ثبت پیشسفارش' }}
|
||
</button>
|
||
<p class="cart-summary-card__note">
|
||
اقلام همه شرکتها در یک سبد و یک فاکتور ثبت میشوند؛ پس از قیمتگذاری، یکبار تسویه میکنید.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="cart-guide-card">
|
||
<h3 class="cart-guide-card__title">
|
||
<span class="cart-guide-card__title-icon" aria-hidden="true">
|
||
<IconLightbulb :size="18" />
|
||
</span>
|
||
راهنمای گام بعدی
|
||
</h3>
|
||
<p class="cart-guide-card__text">
|
||
پس از انتخاب شرکت و افزودن محصولات، متراژ کل و سقف مجاز فاکتور را بررسی کنید.
|
||
<template v-if="!isEmpty">
|
||
سقف مجاز: {{ formatMeterage(maxMeterage) }} — باقیمانده:
|
||
{{ formatMeterage(remainingCapacity) }}.
|
||
</template>
|
||
سپس برای تکمیل سفارش، گزینه «ادامه و انتخاب تسویه» را بزنید.
|
||
</p>
|
||
</div>
|
||
</aside>
|
||
</div>
|
||
|
||
<nav class="cart-quick-nav" aria-label="دسترسی سریع">
|
||
<button
|
||
v-for="link in quickLinks"
|
||
:key="link.title"
|
||
type="button"
|
||
class="cart-quick-nav__item"
|
||
@click="link.action()"
|
||
>
|
||
<span class="cart-quick-nav__icon" :class="`cart-quick-nav__icon--${link.tone}`">
|
||
<component :is="link.icon" :size="20" />
|
||
</span>
|
||
<span class="cart-quick-nav__body">
|
||
<strong class="cart-quick-nav__title">{{ link.title }}</strong>
|
||
<span class="cart-quick-nav__desc">{{ link.desc }}</span>
|
||
</span>
|
||
<IconChevronLeft :size="16" class="cart-quick-nav__chevron" aria-hidden="true" />
|
||
</button>
|
||
</nav>
|
||
|
||
<p class="cart-desktop-page__footer">پارس بافت — سامانه سفارشگذاری پارچه</p>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.cart-desktop-page {
|
||
width: 100%;
|
||
padding-bottom: 24px;
|
||
}
|
||
|
||
.cart-desktop-page__head {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.cart-desktop-page__title {
|
||
margin: 0 0 6px;
|
||
font-size: 1.375rem;
|
||
font-weight: 700;
|
||
color: var(--color-text);
|
||
}
|
||
|
||
.cart-desktop-page__subtitle {
|
||
margin: 0;
|
||
font-size: 0.875rem;
|
||
color: var(--color-text-muted);
|
||
}
|
||
|
||
.cart-desktop-page__layout {
|
||
direction: ltr;
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 1fr);
|
||
gap: 20px;
|
||
align-items: start;
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
@media (min-width: 1100px) {
|
||
.cart-desktop-page__layout {
|
||
grid-template-columns: minmax(0, 1fr) 300px;
|
||
}
|
||
}
|
||
|
||
.cart-desktop-page__main,
|
||
.cart-desktop-page__sidebar {
|
||
direction: rtl;
|
||
}
|
||
|
||
.cart-desktop-page__sidebar {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 16px;
|
||
}
|
||
|
||
.cart-empty-card,
|
||
.cart-items-card {
|
||
background: var(--color-surface);
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-lg);
|
||
box-shadow: var(--shadow-card);
|
||
}
|
||
|
||
.cart-empty-card {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
text-align: center;
|
||
padding: 48px 32px 40px;
|
||
min-height: 360px;
|
||
}
|
||
|
||
.cart-empty-card__visual {
|
||
position: relative;
|
||
width: 140px;
|
||
height: 100px;
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.cart-empty-card__circle {
|
||
position: absolute;
|
||
inset: 12px 24px;
|
||
border-radius: 50%;
|
||
background: var(--color-primary-soft);
|
||
color: var(--color-primary);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.cart-empty-card__spark {
|
||
position: absolute;
|
||
color: #93c5fd;
|
||
font-size: 1.125rem;
|
||
font-weight: 700;
|
||
line-height: 1;
|
||
}
|
||
|
||
.cart-empty-card__spark--1 {
|
||
top: 8px;
|
||
right: 18px;
|
||
}
|
||
|
||
.cart-empty-card__spark--2 {
|
||
top: 28px;
|
||
left: 8px;
|
||
font-size: 1.5rem;
|
||
}
|
||
|
||
.cart-empty-card__spark--3 {
|
||
bottom: 16px;
|
||
right: 10px;
|
||
font-size: 1.25rem;
|
||
}
|
||
|
||
.cart-empty-card__heading {
|
||
margin: 0 0 10px;
|
||
font-size: 1.0625rem;
|
||
font-weight: 700;
|
||
color: var(--color-text);
|
||
}
|
||
|
||
.cart-empty-card__text {
|
||
margin: 0 0 28px;
|
||
max-width: 380px;
|
||
font-size: 0.8125rem;
|
||
line-height: 1.7;
|
||
color: var(--color-text-muted);
|
||
}
|
||
|
||
.cart-empty-card__actions {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 12px;
|
||
}
|
||
|
||
.cart-btn {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 8px;
|
||
min-height: 44px;
|
||
padding: 0 22px;
|
||
border-radius: var(--radius-md);
|
||
font-family: inherit;
|
||
font-size: 0.875rem;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
transition: opacity 0.15s, background 0.15s;
|
||
}
|
||
|
||
.cart-btn--primary {
|
||
border: none;
|
||
background: var(--color-primary);
|
||
color: var(--color-surface);
|
||
}
|
||
|
||
.cart-btn--primary:hover {
|
||
background: var(--color-primary-dark);
|
||
}
|
||
|
||
.cart-btn--outline {
|
||
border: 1px solid var(--color-border);
|
||
background: var(--color-surface);
|
||
color: var(--color-primary);
|
||
}
|
||
|
||
.cart-btn--outline:hover {
|
||
background: var(--color-primary-soft);
|
||
}
|
||
|
||
.cart-items-card__scroll {
|
||
overflow-x: auto;
|
||
}
|
||
|
||
.cart-items-table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
font-size: 0.875rem;
|
||
min-width: 520px;
|
||
}
|
||
|
||
.cart-items-table th {
|
||
padding: 14px 18px;
|
||
text-align: start;
|
||
font-size: 0.8125rem;
|
||
font-weight: 600;
|
||
color: var(--color-text-muted);
|
||
background: var(--color-bg);
|
||
border-bottom: 1px solid var(--color-border);
|
||
}
|
||
|
||
.cart-items-table td {
|
||
padding: 16px 18px;
|
||
border-bottom: 1px solid var(--color-border);
|
||
color: var(--color-text);
|
||
vertical-align: middle;
|
||
}
|
||
|
||
.cart-items-table tr:last-child td {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.cart-items-table__name {
|
||
font-weight: 600;
|
||
}
|
||
|
||
.cart-items-table__delete {
|
||
border: none;
|
||
background: none;
|
||
cursor: pointer;
|
||
color: var(--color-text-muted);
|
||
padding: 4px;
|
||
display: inline-flex;
|
||
}
|
||
|
||
.cart-items-table__delete:hover {
|
||
color: var(--color-danger);
|
||
}
|
||
|
||
.cart-summary-card,
|
||
.cart-guide-card {
|
||
padding: 18px;
|
||
background: var(--color-surface);
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-lg);
|
||
box-shadow: var(--shadow-card);
|
||
}
|
||
|
||
.cart-summary-card__title,
|
||
.cart-guide-card__title {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
margin: 0 0 14px;
|
||
font-size: 0.9375rem;
|
||
font-weight: 700;
|
||
color: var(--color-text);
|
||
}
|
||
|
||
.cart-summary-card__title-icon,
|
||
.cart-guide-card__title-icon {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: var(--color-primary);
|
||
}
|
||
|
||
.cart-summary-card__rows {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.cart-summary-card__row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 12px;
|
||
padding: 10px 0;
|
||
font-size: 0.8125rem;
|
||
border-bottom: 1px solid var(--color-border);
|
||
}
|
||
|
||
.cart-summary-card__row--sub {
|
||
padding-block: 6px;
|
||
font-size: 0.8125rem;
|
||
color: var(--color-text-muted);
|
||
}
|
||
|
||
.cart-summary-card__cap {
|
||
font-weight: 500;
|
||
color: var(--color-text-muted);
|
||
}
|
||
|
||
.cart-summary-card__row:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.cart-summary-card__row-label {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
color: var(--color-text-muted);
|
||
}
|
||
|
||
.cart-summary-card__row strong {
|
||
font-variant-numeric: tabular-nums;
|
||
color: var(--color-text);
|
||
}
|
||
|
||
.cart-summary-card__hint-value {
|
||
color: var(--color-text-muted);
|
||
font-size: 0.75rem;
|
||
}
|
||
|
||
.cart-summary-card__note {
|
||
margin: 10px 0 0;
|
||
font-size: 0.75rem;
|
||
line-height: 1.6;
|
||
color: var(--color-text-muted);
|
||
}
|
||
|
||
.cart-summary-card__cta {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 8px;
|
||
width: 100%;
|
||
min-height: 44px;
|
||
border: 1px solid transparent;
|
||
border-radius: var(--radius-md);
|
||
background: var(--color-primary);
|
||
color: var(--color-surface);
|
||
font-family: inherit;
|
||
font-size: 0.875rem;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
transition: background 0.15s, color 0.15s, border-color 0.15s;
|
||
}
|
||
|
||
.cart-summary-card__cta:hover:not(:disabled) {
|
||
background: var(--color-primary-dark);
|
||
}
|
||
|
||
.cart-summary-card__cta:disabled {
|
||
background: var(--color-elevated);
|
||
border-color: var(--color-border);
|
||
color: var(--color-text-muted);
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.cart-guide-card__text {
|
||
margin: 0;
|
||
font-size: 0.75rem;
|
||
line-height: 1.75;
|
||
color: var(--color-text-muted);
|
||
}
|
||
|
||
.cart-quick-nav {
|
||
direction: ltr;
|
||
display: grid;
|
||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||
gap: 16px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.cart-quick-nav__item {
|
||
direction: rtl;
|
||
display: grid;
|
||
grid-template-columns: auto 1fr auto;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 16px 18px;
|
||
border: 1px solid var(--color-border);
|
||
border-radius: var(--radius-lg);
|
||
background: var(--color-surface);
|
||
box-shadow: var(--shadow-card);
|
||
cursor: pointer;
|
||
text-align: right;
|
||
font-family: inherit;
|
||
transition: border-color 0.15s, box-shadow 0.15s;
|
||
}
|
||
|
||
.cart-quick-nav__item:hover {
|
||
border-color: var(--color-primary);
|
||
box-shadow: var(--shadow-md);
|
||
}
|
||
|
||
.cart-quick-nav__icon {
|
||
width: 42px;
|
||
height: 42px;
|
||
border-radius: 12px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.cart-quick-nav__icon--blue {
|
||
background: var(--color-primary-soft);
|
||
color: var(--color-primary);
|
||
}
|
||
|
||
.cart-quick-nav__icon--green {
|
||
background: var(--color-success-soft);
|
||
color: var(--color-success);
|
||
}
|
||
|
||
.cart-quick-nav__body {
|
||
min-width: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
}
|
||
|
||
.cart-quick-nav__title {
|
||
font-size: 0.875rem;
|
||
font-weight: 700;
|
||
color: var(--color-text);
|
||
}
|
||
|
||
.cart-quick-nav__desc {
|
||
font-size: 0.6875rem;
|
||
color: var(--color-text-muted);
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.cart-quick-nav__chevron {
|
||
color: var(--color-text-muted);
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.cart-desktop-page__footer {
|
||
margin: 0;
|
||
text-align: center;
|
||
font-size: 0.75rem;
|
||
color: var(--color-text-muted);
|
||
}
|
||
</style>
|