Add checks list page and richer order/check admin UX.
Replace the checks redirect with a real list, expand per-order check management, and add a row actions menu on the orders table. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { nextTick, onMounted, onUnmounted, ref } from 'vue'
|
||||
import AdminIcon from '@/components/icons/AdminIcon.vue'
|
||||
import StatusBadge from '@/components/ui/StatusBadge.vue'
|
||||
import BaseModal from '@/components/ui/BaseModal.vue'
|
||||
@@ -7,6 +8,7 @@ import OrderAddModal from '@/views/orders/components/OrderAddModal.vue'
|
||||
import { useOrdersPageContext } from '@/composables/useOrdersPage'
|
||||
import { ORDER_TABS } from '@/utils/orderStatus'
|
||||
import { formatCurrency, formatDateTime, formatMeterage } from '@/utils/format'
|
||||
import type { Order } from '@/types'
|
||||
|
||||
const {
|
||||
activeTab,
|
||||
@@ -26,6 +28,52 @@ const {
|
||||
openAddModal,
|
||||
} = useOrdersPageContext()
|
||||
|
||||
const menuOrder = ref<Order | null>(null)
|
||||
const menuStyle = ref<Record<string, string>>({})
|
||||
|
||||
function closeMenu() {
|
||||
menuOrder.value = null
|
||||
}
|
||||
|
||||
async function toggleMenu(order: Order, event: MouseEvent) {
|
||||
event.stopPropagation()
|
||||
if (menuOrder.value?.id === order.id) {
|
||||
closeMenu()
|
||||
return
|
||||
}
|
||||
const btn = event.currentTarget as HTMLElement
|
||||
const rect = btn.getBoundingClientRect()
|
||||
menuOrder.value = order
|
||||
await nextTick()
|
||||
const menuWidth = 168
|
||||
const left = Math.min(
|
||||
Math.max(8, rect.right - menuWidth),
|
||||
window.innerWidth - menuWidth - 8,
|
||||
)
|
||||
menuStyle.value = {
|
||||
position: 'fixed',
|
||||
top: `${rect.bottom + 4}px`,
|
||||
left: `${left}px`,
|
||||
width: `${menuWidth}px`,
|
||||
}
|
||||
}
|
||||
|
||||
function onDocumentClick() {
|
||||
closeMenu()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('click', onDocumentClick)
|
||||
window.addEventListener('scroll', closeMenu, true)
|
||||
window.addEventListener('resize', closeMenu)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('click', onDocumentClick)
|
||||
window.removeEventListener('scroll', closeMenu, true)
|
||||
window.removeEventListener('resize', closeMenu)
|
||||
})
|
||||
|
||||
function handlePricing() {
|
||||
if (!selectedOrder.value) return
|
||||
closeDetailModal()
|
||||
@@ -43,6 +91,25 @@ function handleCashReceipt() {
|
||||
closeDetailModal()
|
||||
goCashReceipt(selectedOrder.value)
|
||||
}
|
||||
|
||||
function runMenuAction(action: 'detail' | 'pricing' | 'checks' | 'cash') {
|
||||
const order = menuOrder.value
|
||||
if (!order) return
|
||||
closeMenu()
|
||||
if (action === 'detail') {
|
||||
openOrderDetail(order)
|
||||
return
|
||||
}
|
||||
if (action === 'pricing') {
|
||||
goPricing(order)
|
||||
return
|
||||
}
|
||||
if (action === 'checks') {
|
||||
goChecks(order)
|
||||
return
|
||||
}
|
||||
goCashReceipt(order)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -116,8 +183,15 @@ function handleCashReceipt() {
|
||||
<td>{{ formatMeterage(order.meterage) }}</td>
|
||||
<td><StatusBadge :status="order.status" /></td>
|
||||
<td>{{ formatDateTime(order.createdAt) }}</td>
|
||||
<td>
|
||||
<button type="button" class="orders-page__more-btn" @click.stop>
|
||||
<td class="orders-page__actions-cell">
|
||||
<button
|
||||
type="button"
|
||||
class="orders-page__more-btn"
|
||||
:aria-expanded="menuOrder?.id === order.id"
|
||||
aria-haspopup="menu"
|
||||
aria-label="عملیات سفارش"
|
||||
@click="toggleMenu(order, $event)"
|
||||
>
|
||||
<AdminIcon name="more" :size="18" />
|
||||
</button>
|
||||
</td>
|
||||
@@ -138,6 +212,51 @@ function handleCashReceipt() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Teleport to="body">
|
||||
<div
|
||||
v-if="menuOrder"
|
||||
class="orders-page__menu"
|
||||
role="menu"
|
||||
:style="menuStyle"
|
||||
@click.stop
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
role="menuitem"
|
||||
class="orders-page__menu-item"
|
||||
@click="runMenuAction('detail')"
|
||||
>
|
||||
جزئیات سفارش
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
role="menuitem"
|
||||
class="orders-page__menu-item"
|
||||
@click="runMenuAction('pricing')"
|
||||
>
|
||||
قیمتگذاری
|
||||
</button>
|
||||
<button
|
||||
v-if="menuOrder.settlementMethod === 'check'"
|
||||
type="button"
|
||||
role="menuitem"
|
||||
class="orders-page__menu-item"
|
||||
@click="runMenuAction('checks')"
|
||||
>
|
||||
مدیریت چکها
|
||||
</button>
|
||||
<button
|
||||
v-if="menuOrder.settlementMethod === 'cash'"
|
||||
type="button"
|
||||
role="menuitem"
|
||||
class="orders-page__menu-item"
|
||||
@click="runMenuAction('cash')"
|
||||
>
|
||||
رسید پرداخت
|
||||
</button>
|
||||
</div>
|
||||
</Teleport>
|
||||
|
||||
<BaseModal
|
||||
:open="showDetailModal && !!selectedOrder"
|
||||
title="جزئیات سفارش"
|
||||
@@ -231,12 +350,23 @@ function handleCashReceipt() {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.orders-page__actions-cell {
|
||||
width: 48px;
|
||||
}
|
||||
|
||||
.orders-page__more-btn {
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--color-text-muted);
|
||||
padding: 4px;
|
||||
border-radius: var(--radius-sm);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.orders-page__more-btn:hover,
|
||||
.orders-page__more-btn[aria-expanded='true'] {
|
||||
color: var(--color-text);
|
||||
background: var(--color-border-light);
|
||||
}
|
||||
|
||||
.admin-table tbody tr {
|
||||
@@ -251,3 +381,32 @@ function handleCashReceipt() {
|
||||
margin: 0 0 16px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.orders-page__menu {
|
||||
z-index: 300000;
|
||||
min-width: 160px;
|
||||
padding: 6px;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-surface);
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.25);
|
||||
display: grid;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.orders-page__menu-item {
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--color-text);
|
||||
text-align: start;
|
||||
padding: 8px 10px;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 0.8125rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.orders-page__menu-item:hover {
|
||||
background: var(--color-primary-soft);
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user