feat(convert): show history and let customers edit pending requests
Desktop history sits beside the form. Pending requests can be updated or deleted, and the shared wallet surface drops its extra border and padding. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -62,3 +62,7 @@
|
|||||||
- 2026-11-02 | سه زبانه تبدیل از اول | همان `CopperConvertCalculator.vue` | زبانه ۱.۵ و ۲.۵ دیگر پشت `remainderAmount > 0` نیستند؛ زیر زبانه مقدار، قبل از نتیجه
|
- 2026-11-02 | سه زبانه تبدیل از اول | همان `CopperConvertCalculator.vue` | زبانه ۱.۵ و ۲.۵ دیگر پشت `remainderAmount > 0` نیستند؛ زیر زبانه مقدار، قبل از نتیجه
|
||||||
- 2026-11-02 | زبانه سیم مکمل | `useCopperConvertPage.ts` | بودجه = ارزش مقدار انتخابشده؛ نصف به ۱.۵ و بقیه به ۲.۵؛ جابهجایی یکی دیگری را پر میکند
|
- 2026-11-02 | زبانه سیم مکمل | `useCopperConvertPage.ts` | بودجه = ارزش مقدار انتخابشده؛ نصف به ۱.۵ و بقیه به ۲.۵؛ جابهجایی یکی دیگری را پر میکند
|
||||||
- 2026-11-01 | تاریخچه برداشت کیف پول | `wallet/mobile/MobileWalletView.vue` + `desktop/DesktopWalletView.vue` + `useWalletPageData.ts` | `walletWithdrawItems` از قبل لود میشد؛ زیر راهنمای برداشت و زیر حسابهای دسکتاپ نشان داده میشود
|
- 2026-11-01 | تاریخچه برداشت کیف پول | `wallet/mobile/MobileWalletView.vue` + `desktop/DesktopWalletView.vue` + `useWalletPageData.ts` | `walletWithdrawItems` از قبل لود میشد؛ زیر راهنمای برداشت و زیر حسابهای دسکتاپ نشان داده میشود
|
||||||
|
- 2026-11-03 | تاریخچه تبدیل کاربر | `convert/components/CopperConvertHistory.vue` + `useCopperConvertPage.ts` + سرور `Customer/CopperConvertRequestController@index` | GET `/customer/copper-convert-request` فقط ردیفهای همان مشتری؛ زیر فرم موبایل و دسکتاپ
|
||||||
|
- 2026-11-03 | ویرایش/حذف تبدیل در انتظار | همان تاریخچه + `CopperConvertRequestService::update/remove` | فقط status=pending؛ ویرایش فرم را با همان حلقهها پر میکند و PUT میزند
|
||||||
|
- 2026-11-03 | چیدمان دسکتاپ تبدیل | `convert/desktop/DesktopConvertView.vue` | مثل کیف پول: `dwallet-grid`؛ فرم راست، تاریخچه چپ با `variant=side`
|
||||||
|
- 2026-11-03 | مداد ویرایش تبدیل | `convert/components/CopperConvertHistory.vue` | فقط pending؛ آیکون `vuesax-linear:edit` کنار وضعیت، همان `beginEdit`
|
||||||
|
|||||||
@@ -1,6 +1,18 @@
|
|||||||
import useAxios from '@/utils/useAxios'
|
import useAxios from '@/utils/useAxios'
|
||||||
import type { CopperConvertSource } from '@/utils/copperWireConvert'
|
import type { CopperConvertSource } from '@/utils/copperWireConvert'
|
||||||
|
|
||||||
|
export interface CopperConvertHistoryRow {
|
||||||
|
id: number
|
||||||
|
status: string
|
||||||
|
source: string
|
||||||
|
asset_unit: string
|
||||||
|
consumed_amount: number
|
||||||
|
leftover_amount: number
|
||||||
|
wire15_rings: number
|
||||||
|
wire25_rings: number
|
||||||
|
created_at?: string
|
||||||
|
}
|
||||||
|
|
||||||
export function createCopperConvertRequest(payload: {
|
export function createCopperConvertRequest(payload: {
|
||||||
source: CopperConvertSource
|
source: CopperConvertSource
|
||||||
rings15: number
|
rings15: number
|
||||||
@@ -8,3 +20,22 @@ export function createCopperConvertRequest(payload: {
|
|||||||
}) {
|
}) {
|
||||||
return useAxios.post('/customer/copper-convert-request', payload)
|
return useAxios.post('/customer/copper-convert-request', payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getCopperConvertRequests() {
|
||||||
|
return useAxios.get<CopperConvertHistoryRow[]>('/customer/copper-convert-request')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateCopperConvertRequest(
|
||||||
|
id: number,
|
||||||
|
payload: {
|
||||||
|
source: CopperConvertSource
|
||||||
|
rings15: number
|
||||||
|
rings25: number
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
return useAxios.put(`/customer/copper-convert-request/${id}`, payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteCopperConvertRequest(id: number) {
|
||||||
|
return useAxios.delete(`/customer/copper-convert-request/${id}`)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="copper-convert" dir="rtl">
|
<div class="copper-convert" data-convert-form dir="rtl">
|
||||||
<p class="copper-convert__lead">
|
|
||||||
مقدار کاتد، مفتول یا شمش از دارایی شما خوانده میشود. با زبانه از صفر تا همان موجودی انتخاب کنید. معادل مفتول و حلقه سیم ۱.۵ و ۲.۵ با قیمت روز حساب میشود.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<a-spin :spinning="loading">
|
<a-spin :spinning="loading">
|
||||||
<section class="copper-convert__panel" aria-label="قیمتهای مبنا">
|
<section class="copper-convert__panel" aria-label="قیمتهای مبنا">
|
||||||
|
<p v-if="!result && amountAttempted && !loading" class="copper-convert__warn">
|
||||||
|
مقدار معتبر وارد کنید و از فعال بودن قیمت کاتد/مفتول در سامانه مطمئن شوید.
|
||||||
|
</p>
|
||||||
<div class="copper-convert__panel-head">
|
<div class="copper-convert__panel-head">
|
||||||
<span class="copper-convert__panel-kicker">قیمت لحظهای</span>
|
<span class="copper-convert__panel-kicker">قیمت لحظهای</span>
|
||||||
<span class="copper-convert__panel-title">مبنای محاسبه</span>
|
<span class="copper-convert__panel-title">مبنای محاسبه</span>
|
||||||
@@ -186,23 +186,28 @@
|
|||||||
<p class="copper-convert__asset-hint">
|
<p class="copper-convert__asset-hint">
|
||||||
از مقدار انتخابشده، <span class="copper-convert__measure"><bdi>{{ assetUnit }}</bdi><bdi>{{ formatAssetAmount(leftoverAmount) }}</bdi></span> به حلقهٔ کامل نمیرسد و در دارایی میماند. باقی دارایی که روی زبانه انتخاب نشده هم دستنخورده میماند.
|
از مقدار انتخابشده، <span class="copper-convert__measure"><bdi>{{ assetUnit }}</bdi><bdi>{{ formatAssetAmount(leftoverAmount) }}</bdi></span> به حلقهٔ کامل نمیرسد و در دارایی میماند. باقی دارایی که روی زبانه انتخاب نشده هم دستنخورده میماند.
|
||||||
</p>
|
</p>
|
||||||
<BasicButton
|
<p v-if="editingId" class="copper-convert__asset-hint">
|
||||||
type="primary"
|
در حال ویرایش درخواست در انتظار تایید. ذخیره، همان درخواست را بهروز میکند.
|
||||||
class="!w-full"
|
</p>
|
||||||
:loading="submitting"
|
<div :class="editingId ? 'grid grid-cols-2 gap-2' : ''">
|
||||||
:disabledB="wire15Rings + wire25Rings < 1"
|
<BasicButton
|
||||||
@click="submitRequest"
|
type="primary"
|
||||||
>
|
class="!w-full"
|
||||||
ثبت درخواست تبدیل
|
html-type="button"
|
||||||
</BasicButton>
|
:loading="submitting"
|
||||||
|
:disabledB="wire15Rings + wire25Rings < 1"
|
||||||
|
@click="submitRequest"
|
||||||
|
>
|
||||||
|
{{ editingId ? 'ذخیره ویرایش' : 'ثبت درخواست تبدیل' }}
|
||||||
|
</BasicButton>
|
||||||
|
<BasicButton v-if="editingId" class="!w-full" html-type="button" :disabledB="submitting" @click="cancelEdit">
|
||||||
|
انصراف
|
||||||
|
</BasicButton>
|
||||||
|
</div>
|
||||||
<p class="copper-convert__asset-hint">
|
<p class="copper-convert__asset-hint">
|
||||||
درخواست برای ادمین میرود. بعد از تأیید، همان مقدار دارایی فروخته و حلقههای انتخابشده خریده میشود.
|
درخواست برای ادمین میرود. بعد از تأیید، همان مقدار دارایی فروخته و حلقههای انتخابشده خریده میشود.
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<p v-if="!result && amountAttempted && !loading" class="copper-convert__warn">
|
|
||||||
مقدار معتبر وارد کنید و از فعال بودن قیمت کاتد/مفتول در سامانه مطمئن شوید.
|
|
||||||
</p>
|
|
||||||
</a-spin>
|
</a-spin>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -227,6 +232,8 @@ const {
|
|||||||
leftoverAmount,
|
leftoverAmount,
|
||||||
submitting,
|
submitting,
|
||||||
submitRequest,
|
submitRequest,
|
||||||
|
editingId,
|
||||||
|
cancelEdit,
|
||||||
result,
|
result,
|
||||||
priceRows,
|
priceRows,
|
||||||
} = useCopperConvertPageContext()
|
} = useCopperConvertPageContext()
|
||||||
|
|||||||
@@ -0,0 +1,126 @@
|
|||||||
|
<template>
|
||||||
|
<section
|
||||||
|
:class="side
|
||||||
|
? 'flex min-h-0 min-w-0 flex-1 flex-col'
|
||||||
|
: 'mt-3 flex flex-col gap-2 rounded-xl border border-gray-200 bg-white p-2.5'"
|
||||||
|
aria-label="تاریخچه تبدیل"
|
||||||
|
>
|
||||||
|
<div :class="side ? 'dwallet-side__header' : ''">
|
||||||
|
<div>
|
||||||
|
<h2 :class="side ? 'dwallet-side__title' : 'm-0 text-sm font-bold text-slate-900'">تاریخچه تبدیل</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div :class="side ? 'dwallet-side__scroll' : ''">
|
||||||
|
<a-spin :spinning="historyLoading && !history.length">
|
||||||
|
<div v-if="historyLoading && !history.length" class="min-h-16" />
|
||||||
|
<p
|
||||||
|
v-else-if="!history.length"
|
||||||
|
class="m-0 rounded-lg bg-slate-50 px-3 py-4 text-center text-xs text-slate-500"
|
||||||
|
>
|
||||||
|
درخواست تبدیلی ثبت نشده است.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul v-else class="m-0 flex list-none flex-col gap-2 p-0">
|
||||||
|
<li
|
||||||
|
v-for="item in history"
|
||||||
|
:key="item.id"
|
||||||
|
class="rounded-lg border p-2.5"
|
||||||
|
:class="editingId === item.id ? 'border-amber-400' : 'border-gray-200'"
|
||||||
|
>
|
||||||
|
<div class="flex flex-wrap items-center justify-between gap-2">
|
||||||
|
<span class="text-xs text-slate-500">{{ item.created_at ? toJalali(item.created_at) : '—' }}</span>
|
||||||
|
<span class="inline-flex items-center gap-1 text-xs text-slate-600">
|
||||||
|
<button
|
||||||
|
v-if="item.status === 'pending'"
|
||||||
|
type="button"
|
||||||
|
class="inline-flex h-7 w-7 items-center justify-center rounded-md border border-gray-200 bg-white"
|
||||||
|
aria-label="ویرایش درخواست"
|
||||||
|
title="ویرایش"
|
||||||
|
@click="beginEdit(item)"
|
||||||
|
>
|
||||||
|
<BasicIcon icon="vuesax-linear:edit" size="15" />
|
||||||
|
</button>
|
||||||
|
وضعیت
|
||||||
|
<a-tag class="!m-0" :color="statusColor(item.status)">{{ statusLabel(item.status) }}</a-tag>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-2 grid grid-cols-2 gap-x-3 gap-y-1 text-xs text-slate-600">
|
||||||
|
<span>دارایی: <b class="text-slate-900">{{ sourceLabel(item.source) }}</b></span>
|
||||||
|
<span>
|
||||||
|
مقدار:
|
||||||
|
<b class="text-slate-900">
|
||||||
|
<bdi>{{ item.asset_unit }}</bdi>
|
||||||
|
<bdi> {{ formatAmount(item.consumed_amount) }}</bdi>
|
||||||
|
</b>
|
||||||
|
</span>
|
||||||
|
<span>حلقه ۱.۵: <b class="text-slate-900">{{ item.wire15_rings }}</b></span>
|
||||||
|
<span>حلقه ۲.۵: <b class="text-slate-900">{{ item.wire25_rings }}</b></span>
|
||||||
|
<span v-if="Number(item.leftover_amount) > 0" class="col-span-2">
|
||||||
|
مانده در دارایی:
|
||||||
|
<b class="text-slate-900">
|
||||||
|
<bdi>{{ item.asset_unit }}</bdi>
|
||||||
|
<bdi> {{ formatAmount(item.leftover_amount) }}</bdi>
|
||||||
|
</b>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="item.status === 'pending'" class="mt-2 flex">
|
||||||
|
<a-button
|
||||||
|
size="small"
|
||||||
|
danger
|
||||||
|
:loading="deletingId === item.id"
|
||||||
|
@click="deleteRequest(item.id)"
|
||||||
|
>
|
||||||
|
حذف
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</a-spin>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
|
||||||
|
import { toJalali } from '@/utils/useDateTime'
|
||||||
|
import { useCopperConvertPageContext } from '../composables/useCopperConvertPage'
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<{ variant?: 'stack' | 'side' }>(), {
|
||||||
|
variant: 'stack',
|
||||||
|
})
|
||||||
|
|
||||||
|
const side = computed(() => props.variant === 'side')
|
||||||
|
|
||||||
|
const { history, historyLoading, editingId, beginEdit, deleteRequest, deletingId } =
|
||||||
|
useCopperConvertPageContext()
|
||||||
|
|
||||||
|
function sourceLabel(source: string) {
|
||||||
|
if (source === 'katod') return 'کاتد'
|
||||||
|
if (source === 'maftoul') return 'مفتول'
|
||||||
|
if (source === 'shamsh') return 'شمش'
|
||||||
|
return source
|
||||||
|
}
|
||||||
|
|
||||||
|
function statusLabel(status: string) {
|
||||||
|
if (status === 'pending') return 'در انتظار تایید'
|
||||||
|
if (status === 'approved') return 'تایید شد'
|
||||||
|
if (status === 'rejected') return 'رد شد'
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
|
||||||
|
function statusColor(status: string) {
|
||||||
|
if (status === 'approved') return 'green'
|
||||||
|
if (status === 'rejected') return 'red'
|
||||||
|
return 'orange'
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatAmount(value: number) {
|
||||||
|
const amount = Number(value)
|
||||||
|
if (!Number.isFinite(amount)) return '—'
|
||||||
|
return amount.toLocaleString('en-US', { maximumFractionDigits: 3 })
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
computed,
|
computed,
|
||||||
inject,
|
inject,
|
||||||
|
nextTick,
|
||||||
onMounted,
|
onMounted,
|
||||||
onUnmounted,
|
onUnmounted,
|
||||||
provide,
|
provide,
|
||||||
@@ -10,9 +11,15 @@ import {
|
|||||||
type InjectionKey,
|
type InjectionKey,
|
||||||
type Ref,
|
type Ref,
|
||||||
} from 'vue'
|
} from 'vue'
|
||||||
import { createCopperConvertRequest } from '@/api/copperConvert'
|
import {
|
||||||
|
createCopperConvertRequest,
|
||||||
|
deleteCopperConvertRequest,
|
||||||
|
getCopperConvertRequests,
|
||||||
|
updateCopperConvertRequest,
|
||||||
|
type CopperConvertHistoryRow,
|
||||||
|
} from '@/api/copperConvert'
|
||||||
import { getProducts } from '@/api/products'
|
import { getProducts } from '@/api/products'
|
||||||
import { message } from 'ant-design-vue'
|
import { message, Modal } from 'ant-design-vue'
|
||||||
import { useWalletAssets } from '@/composables/useWalletAssets'
|
import { useWalletAssets } from '@/composables/useWalletAssets'
|
||||||
import type { Product } from '@/models/products'
|
import type { Product } from '@/models/products'
|
||||||
import type { WalletInventoryCard } from '@/models/walletAssets'
|
import type { WalletInventoryCard } from '@/models/walletAssets'
|
||||||
@@ -47,6 +54,13 @@ export interface CopperConvertPageContext {
|
|||||||
leftoverAmount: ComputedRef<number>
|
leftoverAmount: ComputedRef<number>
|
||||||
submitting: Ref<boolean>
|
submitting: Ref<boolean>
|
||||||
submitRequest: () => Promise<void>
|
submitRequest: () => Promise<void>
|
||||||
|
editingId: Ref<number | null>
|
||||||
|
beginEdit: (row: CopperConvertHistoryRow) => Promise<void>
|
||||||
|
cancelEdit: () => void
|
||||||
|
deleteRequest: (id: number) => void
|
||||||
|
deletingId: Ref<number>
|
||||||
|
history: Ref<CopperConvertHistoryRow[]>
|
||||||
|
historyLoading: Ref<boolean>
|
||||||
prices: ComputedRef<CopperConvertPrices>
|
prices: ComputedRef<CopperConvertPrices>
|
||||||
result: ComputedRef<CopperConvertResult | null>
|
result: ComputedRef<CopperConvertResult | null>
|
||||||
priceRows: ComputedRef<{ label: string; value: string; missing?: boolean }[]>
|
priceRows: ComputedRef<{ label: string; value: string; missing?: boolean }[]>
|
||||||
@@ -142,6 +156,10 @@ export function useCopperConvertPage() {
|
|||||||
const wire25Rings = ref(0)
|
const wire25Rings = ref(0)
|
||||||
const submitting = ref(false)
|
const submitting = ref(false)
|
||||||
const catalog = ref<Product[]>([])
|
const catalog = ref<Product[]>([])
|
||||||
|
const history = ref<CopperConvertHistoryRow[]>([])
|
||||||
|
const historyLoading = ref(true)
|
||||||
|
const editingId = ref<number | null>(null)
|
||||||
|
const deletingId = ref(0)
|
||||||
const { assetsSummary, loadWalletAssets } = useWalletAssets()
|
const { assetsSummary, loadWalletAssets } = useWalletAssets()
|
||||||
|
|
||||||
const assetCards = computed(() => {
|
const assetCards = computed(() => {
|
||||||
@@ -310,24 +328,97 @@ export function useCopperConvertPage() {
|
|||||||
writeRings(rings15For(next25), next25)
|
writeRings(rings15For(next25), next25)
|
||||||
}, { flush: 'sync' })
|
}, { flush: 'sync' })
|
||||||
|
|
||||||
|
function hasPendingRequest() {
|
||||||
|
return history.value.some((row) => row.status === 'pending')
|
||||||
|
}
|
||||||
|
|
||||||
|
async function beginEdit(row: CopperConvertHistoryRow) {
|
||||||
|
if (row.status !== 'pending' || submitting.value) return
|
||||||
|
editingId.value = row.id
|
||||||
|
if (row.source === 'katod' || row.source === 'maftoul' || row.source === 'shamsh') {
|
||||||
|
source.value = row.source
|
||||||
|
}
|
||||||
|
await nextTick()
|
||||||
|
const rings15 = Math.max(0, Math.min(maxRings15.value, Math.floor(Number(row.wire15_rings) || 0)))
|
||||||
|
const rings25 = Math.min(Math.floor(Number(row.wire25_rings) || 0), rings25For(rings15))
|
||||||
|
writeRings(rings15, Math.max(0, rings25))
|
||||||
|
const forms = document.querySelectorAll('[data-convert-form]')
|
||||||
|
const visible = [...forms].find((node) => node instanceof HTMLElement && node.getClientRects().length > 0)
|
||||||
|
visible?.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelEdit() {
|
||||||
|
if (submitting.value) return
|
||||||
|
editingId.value = null
|
||||||
|
applyDefaultSplit()
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteRequest(id: number) {
|
||||||
|
if (deletingId.value || submitting.value) return
|
||||||
|
const row = history.value.find((item) => item.id === id)
|
||||||
|
if (!row || row.status !== 'pending') return
|
||||||
|
Modal.confirm({
|
||||||
|
title: 'حذف درخواست',
|
||||||
|
content: 'این درخواست در انتظار تایید حذف شود؟',
|
||||||
|
okText: 'حذف',
|
||||||
|
cancelText: 'انصراف',
|
||||||
|
okType: 'danger',
|
||||||
|
async onOk() {
|
||||||
|
deletingId.value = id
|
||||||
|
try {
|
||||||
|
await deleteCopperConvertRequest(id)
|
||||||
|
if (editingId.value === id) editingId.value = null
|
||||||
|
message.success('درخواست تبدیل حذف شد')
|
||||||
|
await loadHistory()
|
||||||
|
} finally {
|
||||||
|
deletingId.value = 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
async function submitRequest() {
|
async function submitRequest() {
|
||||||
if (submitting.value) return
|
if (submitting.value) return
|
||||||
if (wire15Rings.value + wire25Rings.value < 1) return
|
if (wire15Rings.value + wire25Rings.value < 1) return
|
||||||
|
if (!editingId.value && hasPendingRequest()) {
|
||||||
|
message.warning('یک درخواست در انتظار تایید دارید. همان را ویرایش یا حذف کنید.')
|
||||||
|
return
|
||||||
|
}
|
||||||
submitting.value = true
|
submitting.value = true
|
||||||
try {
|
try {
|
||||||
await createCopperConvertRequest({
|
const payload = {
|
||||||
source: source.value,
|
source: source.value,
|
||||||
rings15: wire15Rings.value,
|
rings15: wire15Rings.value,
|
||||||
rings25: wire25Rings.value,
|
rings25: wire25Rings.value,
|
||||||
})
|
}
|
||||||
|
if (editingId.value) {
|
||||||
|
await updateCopperConvertRequest(editingId.value, payload)
|
||||||
|
editingId.value = null
|
||||||
|
message.success('درخواست تبدیل بهروز شد')
|
||||||
|
} else {
|
||||||
|
await createCopperConvertRequest(payload)
|
||||||
|
message.success('درخواست تبدیل برای تأیید ادمین ثبت شد')
|
||||||
|
}
|
||||||
wire15Rings.value = 0
|
wire15Rings.value = 0
|
||||||
wire25Rings.value = 0
|
wire25Rings.value = 0
|
||||||
message.success('درخواست تبدیل برای تأیید ادمین ثبت شد')
|
void loadHistory()
|
||||||
} finally {
|
} finally {
|
||||||
submitting.value = false
|
submitting.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function loadHistory() {
|
||||||
|
historyLoading.value = true
|
||||||
|
try {
|
||||||
|
const rows = await getCopperConvertRequests()
|
||||||
|
history.value = Array.isArray(rows) ? rows : []
|
||||||
|
} catch {
|
||||||
|
// خطای شبکه را اینترسپتور نشان میدهد؛ لیست قبلی میماند
|
||||||
|
} finally {
|
||||||
|
historyLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const priceRows = computed(() => {
|
const priceRows = computed(() => {
|
||||||
const p = prices.value
|
const p = prices.value
|
||||||
const row = (label: string, amount: number) => ({
|
const row = (label: string, amount: number) => ({
|
||||||
@@ -352,6 +443,7 @@ export function useCopperConvertPage() {
|
|||||||
getLastPrices(),
|
getLastPrices(),
|
||||||
getProducts(),
|
getProducts(),
|
||||||
loadWalletAssets(),
|
loadWalletAssets(),
|
||||||
|
loadHistory(),
|
||||||
])
|
])
|
||||||
catalog.value = Array.isArray(list) ? list : []
|
catalog.value = Array.isArray(list) ? list : []
|
||||||
} catch {
|
} catch {
|
||||||
@@ -385,6 +477,13 @@ export function useCopperConvertPage() {
|
|||||||
leftoverAmount,
|
leftoverAmount,
|
||||||
submitting,
|
submitting,
|
||||||
submitRequest,
|
submitRequest,
|
||||||
|
editingId,
|
||||||
|
beginEdit,
|
||||||
|
cancelEdit,
|
||||||
|
deleteRequest,
|
||||||
|
deletingId,
|
||||||
|
history,
|
||||||
|
historyLoading,
|
||||||
prices,
|
prices,
|
||||||
result,
|
result,
|
||||||
priceRows,
|
priceRows,
|
||||||
|
|||||||
@@ -1,77 +1,43 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="dconvert">
|
<div class="dwallet" dir="rtl">
|
||||||
<div class="dconvert__container">
|
<div class="dwallet__container">
|
||||||
<header class="dconvert-topbar">
|
<DesktopPageTopbar
|
||||||
<button type="button" class="dconvert-topbar__back" @click="router.back()">
|
eyebrow="محصول"
|
||||||
<BasicIcon icon="vuesax-linear:arrow-right-1" size="18" />
|
title="تبدیل محصول"
|
||||||
<span>بازگشت</span>
|
subtitle="تبدیل کاتد، مفتول یا شمش به حلقه سیم"
|
||||||
</button>
|
show-back
|
||||||
<div class="dconvert-topbar__meta">
|
/>
|
||||||
<p class="dconvert-topbar__eyebrow">محصول</p>
|
|
||||||
<h1 class="dconvert-topbar__title">تبدیل محصول</h1>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<section class="dconvert-surface">
|
<div class="dwallet-grid dconvert-grid">
|
||||||
<CopperConvertCalculator />
|
<section class="dwallet-surface dwallet-form">
|
||||||
</section>
|
<CopperConvertCalculator />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="dwallet-surface dwallet-side dconvert-side">
|
||||||
|
<CopperConvertHistory variant="side" />
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
|
import DesktopPageTopbar from '@/layouts/components/DesktopPageTopbar.vue'
|
||||||
import router from '@/router'
|
|
||||||
import CopperConvertCalculator from '../components/CopperConvertCalculator.vue'
|
import CopperConvertCalculator from '../components/CopperConvertCalculator.vue'
|
||||||
|
import CopperConvertHistory from '../components/CopperConvertHistory.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
.dconvert {
|
.dconvert-grid {
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dconvert-side {
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
padding: 1.25rem 1.5rem 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dconvert__container {
|
:deep(.dwallet-side__scroll) {
|
||||||
max-width: 720px;
|
max-height: none;
|
||||||
margin: 0 auto;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dconvert-topbar {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.75rem;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dconvert-topbar__back {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.25rem;
|
|
||||||
border: none;
|
|
||||||
background: transparent;
|
|
||||||
color: var(--colorPrimary, #2563eb);
|
|
||||||
font-family: inherit;
|
|
||||||
font-weight: 700;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dconvert-topbar__eyebrow {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
color: #64748b;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dconvert-topbar__title {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 1.25rem;
|
|
||||||
font-weight: 800;
|
|
||||||
color: #0f172a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dconvert-surface {
|
|
||||||
border: 1px solid #e5e7eb;
|
|
||||||
border-radius: 0.875rem;
|
|
||||||
background: #fff;
|
|
||||||
padding: 1.25rem;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<MobilePageHeader title="تبدیل محصول" show-back />
|
<MobilePageHeader title="تبدیل محصول" show-back />
|
||||||
<div class="px-5 mpage-content pb-2">
|
<div class="px-5 mpage-content pb-[calc(var(--app-bottom-chrome-height)+1rem)]">
|
||||||
<div class="border-gray-200 border-1 rounded-xl p-4 relative bg-white">
|
<div class="border-gray-200 border-1 rounded-xl p-4 relative bg-white">
|
||||||
<CopperConvertCalculator />
|
<CopperConvertCalculator />
|
||||||
</div>
|
</div>
|
||||||
|
<CopperConvertHistory />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -12,4 +13,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import MobilePageHeader from '@/layouts/components/MobilePageHeader.vue'
|
import MobilePageHeader from '@/layouts/components/MobilePageHeader.vue'
|
||||||
import CopperConvertCalculator from '../components/CopperConvertCalculator.vue'
|
import CopperConvertCalculator from '../components/CopperConvertCalculator.vue'
|
||||||
|
import CopperConvertHistory from '../components/CopperConvertHistory.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -120,13 +120,11 @@
|
|||||||
|
|
||||||
.dwallet-surface {
|
.dwallet-surface {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border: 1px solid rgba(15, 23, 42, 0.06);
|
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
|
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dwallet-form {
|
.dwallet-form {
|
||||||
padding: 24px;
|
|
||||||
|
|
||||||
&__title {
|
&__title {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
|||||||
Reference in New Issue
Block a user