Files
fabric_trade_user/src/views/cart/components/DesktopCartPage.vue
T

609 lines
15 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { computed } from 'vue'
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, formatCurrency } from '@/utils/format'
import type { CartItem } from '@/types'
const props = defineProps<{
items: CartItem[]
companyName: string
totalMeterage: number
maxMeterage: number
remainingCapacity: number
isEmpty: boolean
}>()
const emit = defineEmits<{
remove: [id: string]
checkout: []
selectCompany: []
back: []
}>()
const router = useRouter()
const payableAmount = computed(() =>
props.items.reduce((sum, item) => sum + (item.unitPrice ?? 0) * item.meterage, 0),
)
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 || '—' }}</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>
</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.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 class="cart-summary-card__row">
<span class="cart-summary-card__row-label">
<IconOrders :size="16" />
مبلغ قابل پرداخت
</span>
<strong>{{
payableAmount > 0 ? formatCurrency(payableAmount) : '۰ تومان'
}}</strong>
</div>
</div>
<button
type="button"
class="cart-summary-card__cta"
:disabled="isEmpty"
@click="emit('checkout')"
>
<IconChevronLeft :size="16" />
ادامه و انتخاب تسویه
</button>
</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 {
max-width: 980px;
margin-inline: auto;
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) 300px;
gap: 20px;
align-items: start;
margin-bottom: 24px;
}
.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: #fff;
}
.cart-btn--primary:hover {
background: var(--color-primary-dark);
}
.cart-btn--outline {
border: 1px solid var(--color-border);
background: #fff;
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: #f8fafc;
border-bottom: 1px solid var(--color-border);
}
.cart-items-table td {
padding: 16px 18px;
border-bottom: 1px solid #f1f5f9;
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: #64748b;
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 #f1f5f9;
}
.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__cta {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
width: 100%;
min-height: 44px;
border: none;
border-radius: var(--radius-md);
background: var(--color-primary);
color: #fff;
font-family: inherit;
font-size: 0.875rem;
font-weight: 600;
cursor: pointer;
transition: background 0.15s;
}
.cart-summary-card__cta:hover:not(:disabled) {
background: var(--color-primary-dark);
}
.cart-summary-card__cta:disabled {
background: #e8ecf3;
color: #94a3b8;
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>