fix price

This commit is contained in:
2026-09-13 18:33:38 +03:30
parent 7f8ae8bd74
commit fdc24083cf
2 changed files with 111 additions and 25 deletions
+2 -1
View File
@@ -34,7 +34,8 @@ export function isProductTradingHoursOpen(
auto_close_at?: string | null
} | null,
) {
if (!product) return false
// روت /gold محصول را prop نمی‌گیرد؛ نبود محصول یعنی ساعت محصول اعمال نشود (مثل open_buy)
if (!product) return true
if (isOutsideProductActivationWindow(product)) return false
if (typeof product.trade_open === 'boolean') return product.trade_open
return product.show === true
+109 -24
View File
@@ -27,7 +27,7 @@
class="w-full h-10 flex items-center justify-center font-bold rounded-full transition-all"
:class="isBuy ? 'bg-green-600 text-white shadow-sm' : 'text-green-600'"
@click.stop="setIsBuy(true)"
:style="!isProductBuyOpen(props?.ProductItem) ? 'pointer-events: none; opacity: 0.35;' : ''"
:style="!isProductBuyOpen(tradeProduct) ? 'pointer-events: none; opacity: 0.35;' : ''"
>
خرید
</div>
@@ -35,7 +35,7 @@
class="w-full h-10 flex items-center justify-center font-bold rounded-full transition-all"
:class="!isBuy ? 'bg-red-600 text-white shadow-sm' : 'text-red-600'"
@click.stop="setIsBuy(false)"
:style="!isProductSellOpen(props?.ProductItem) ? 'pointer-events: none; opacity: 0.35;' : ''"
:style="!isProductSellOpen(tradeProduct) ? 'pointer-events: none; opacity: 0.35;' : ''"
>
فروش
</div>
@@ -98,7 +98,7 @@
</div>-->
</div>
<div v-else class="font-bold text-xs">
{{ formatCashWalletBalanceText(usePriceWithoutType(cashWalletBalance), props?.ProductItem?.group_type) }}
{{ formatCashWalletBalanceText(usePriceWithoutType(cashWalletBalance), tradeProduct?.group_type) }}
</div>
</Transition>
<Transition mode="out-in" name="fade">
@@ -241,7 +241,7 @@
<script lang="ts" setup>
import { computed, nextTick, onMounted, ref, watch } from 'vue'
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
import img1 from '@/assets/img/home_ly2.svg'
import { usePrice, usePriceWithoutType } from '@/utils/usePrice'
import BasicInput from '@/components/BasicPacks/BasicInput.vue'
@@ -313,6 +313,12 @@ import {
import { formatCashWalletBalanceText, getWeightUnit } from '@/utils/productDealMethod'
import { formatProductWalletBalanceText, getCashWalletBalance } from '@/utils/productAssets'
import { useWalletAssets } from '@/composables/useWalletAssets'
import {
findProductInCatalog,
getProductsCatalog,
getProductsCatalogRef,
} from '@/composables/useProductsCatalog'
import { subscribeProductListStream } from '@/utils/productStreamBridge'
import { usesQuantityTrade } from '@/utils/productCategories'
import { useSettingsStore } from '@/store/settingsStore'
import { storeToRefs } from 'pinia'
@@ -365,8 +371,54 @@ const contentClass = computed(() => {
const isBuy = ref<boolean>(true)
const inputLock = ref<TradeInputLock>(null)
const catalogProducts = getProductsCatalogRef()
const standaloneProductId = ref<number>()
let unsubscribeProductStream: (() => void) | undefined
/** شناسه محصول آبشده از استریم last / تنظیمات — مثل مسیر خرید محصول */
function resolveMeltedTradeProductId(): number | undefined {
const fromPrice = Number(lastPrice.value?.main_product ?? 0)
if (Number.isFinite(fromPrice) && fromPrice > 0) return fromPrice
const mainProduct = Number(settingsStore.getSettingByKey('main_product') ?? 0)
if (Number.isFinite(mainProduct) && mainProduct > 0) return mainProduct
const raw = String(settingsStore.getSettingByKey('main_wallet_products') ?? '').trim()
if (!raw) return undefined
let parts: unknown[] = []
try {
const decoded = JSON.parse(raw)
parts = Array.isArray(decoded) ? decoded : raw.split(',')
} catch {
parts = raw.split(',')
}
for (const part of parts) {
const id = Number(String(part).trim())
if (Number.isFinite(id) && id > 0) return id
}
return undefined
}
function pickStandaloneMeltedProduct(): Product | undefined {
void catalogProducts.value
const id = standaloneProductId.value ?? resolveMeltedTradeProductId()
if (id) {
const match = findProductInCatalog(id)
?? catalogProducts.value.find((product) => Number(product.id) === Number(id))
if (match) return match
}
return catalogProducts.value.find((product) => product.title === 'آبشده نقدی')
?? catalogProducts.value.find((product) => product.group_type === 'آبشده')
}
const tradeProduct = computed(() => props.ProductItem ?? pickStandaloneMeltedProduct())
const tradeProductLabel = computed(() => {
const title = props.ProductItem?.title?.trim()
const title = tradeProduct.value?.title?.trim()
return title || meltedGoldLabel.value
})
const amountFieldLabel = computed(() => `مقدار ${tradeProductLabel.value}`)
@@ -377,7 +429,7 @@ const productWalletBalance = computed(() =>
const productWalletBalanceText = computed(() =>
formatProductWalletBalanceText(
props.ProductItem,
tradeProduct.value,
assetsSummary.value?.inventory_cards ?? [],
walletStore?.walletInfo?.gold
)
@@ -403,7 +455,7 @@ const sellPagination = ref<TableEngineInput>({
})
const unitPrice = computed(() => {
const product = props.ProductItem
const product = tradeProduct.value
if (usesQuantityTrade(product)) {
return isBuy.value ? getProductBuyPrice(product!) : getProductSellPrice(product!)
}
@@ -498,10 +550,10 @@ const feeFormHint = computed(() =>
}).formHint
)
const minOrderCash = computed(() => productMinOrderCash(props.ProductItem, isBuy.value))
const maxOrderCash = computed(() => productMaxOrderCash(props.ProductItem))
const minOrderCash = computed(() => productMinOrderCash(tradeProduct.value, isBuy.value))
const maxOrderCash = computed(() => productMaxOrderCash(tradeProduct.value))
const cashValidationMessage = computed(() =>
productCashValidationMessage(props.ProductItem, form.value?.goldCash, isBuy.value, usePriceWithoutType)
productCashValidationMessage(tradeProduct.value, form.value?.goldCash, isBuy.value, usePriceWithoutType)
)
const priceUnavailable = computed(() => {
@@ -512,15 +564,15 @@ const priceUnavailable = computed(() => {
const disableTransaction = computed(() =>
loading.value ||
isStoreClosed() ||
!isProductTradingHoursOpen(props.ProductItem) ||
!isProductTradeOpen(props.ProductItem, isBuy.value) ||
!isProductTradingHoursOpen(tradeProduct.value) ||
!isProductTradeOpen(tradeProduct.value, isBuy.value) ||
form.value?.goldValue === undefined ||
form.value?.goldValue === '' ||
form.value?.goldCash === '' ||
form.value?.goldCash === undefined ||
(isBuy.value
? (form.value?.goldCash ?? 0) < minOrderCash.value
: (productMinOrderCash(props.ProductItem, false) > 0 || productMaxOrderCash(props.ProductItem) != null
: (productMinOrderCash(tradeProduct.value, false) > 0 || productMaxOrderCash(tradeProduct.value) != null
? ((form.value?.goldCash ?? 0) < minOrderCash.value || (maxOrderCash.value != null && (form.value?.goldCash ?? 0) > maxOrderCash.value))
: (form.value?.goldValue ?? 0) < 0.001)) ||
(maxOrderCash.value != null && (form.value?.goldCash ?? 0) > maxOrderCash.value) ||
@@ -620,8 +672,25 @@ async function initSellTable() {
// }
onMounted(async () => {
if (!props.ProductItem) {
try {
await getProductsCatalog()
} catch (error) {
console.log(error)
}
unsubscribeProductStream = subscribeProductListStream((products) => {
if (Array.isArray(products) && products.length) {
catalogProducts.value = products
}
})
standaloneProductId.value = resolveMeltedTradeProductId()
const picked = pickStandaloneMeltedProduct()
if (picked?.id) {
standaloneProductId.value = Number(picked.id)
}
}
isBuy.value = resolveProductTradeIsBuy(
props?.ProductItem,
tradeProduct.value,
router.currentRoute.value.query.state?.toString(),
)
await initPrice()
@@ -630,10 +699,26 @@ onMounted(async () => {
await initBuyTable()*/
})
onUnmounted(() => {
unsubscribeProductStream?.()
unsubscribeProductStream = undefined
})
watch(
() => [props?.ProductItem?.id, props?.ProductItem?.open_buy, props?.ProductItem?.open_sell],
() => [tradeProduct.value?.id, tradeProduct.value?.open_buy, tradeProduct.value?.open_sell],
() => {
isBuy.value = resolveProductTradeIsBuy(props?.ProductItem, isBuy.value ? 'buy' : 'sell')
isBuy.value = resolveProductTradeIsBuy(tradeProduct.value, isBuy.value ? 'buy' : 'sell')
},
)
watch(
() => lastPrice.value?.main_product,
(id) => {
if (props.ProductItem) return
const next = Number(id)
if (Number.isFinite(next) && next > 0) {
standaloneProductId.value = next
}
},
)
@@ -713,7 +798,7 @@ function syncFormWithLatestPrice() {
}
watch(
() => [unitPrice.value, isBuy.value, props.ProductItem?.id],
() => [unitPrice.value, isBuy.value, tradeProduct.value?.id],
() => {
syncFormWithLatestPrice()
}
@@ -769,8 +854,8 @@ function useStyle() {
function setIsBuy(val: boolean) {
if (val && !isProductBuyOpen(props?.ProductItem)) return
if (!val && !isProductSellOpen(props?.ProductItem)) return
if (val && !isProductBuyOpen(tradeProduct.value)) return
if (!val && !isProductSellOpen(tradeProduct.value)) return
isBuy.value = val
form.value.goldValue = undefined
form.value.goldCash = undefined
@@ -811,11 +896,11 @@ function okConfirm() {
message.warning(getStoreClosedMessage())
return
}
if (!isProductTradingHoursOpen(props.ProductItem)) {
if (!isProductTradingHoursOpen(tradeProduct.value)) {
message.warning(productHoursClosedMessage())
return
}
if (!isProductTradeOpen(props.ProductItem, isBuy.value)) {
if (!isProductTradeOpen(tradeProduct.value, isBuy.value)) {
message.warning(productTradeClosedMessage(isBuy.value))
return
}
@@ -834,11 +919,11 @@ async function ok() {
message.warning(getStoreClosedMessage())
return
}
if (!isProductTradingHoursOpen(props.ProductItem)) {
if (!isProductTradingHoursOpen(tradeProduct.value)) {
message.warning(productHoursClosedMessage())
return
}
if (!isProductTradeOpen(props.ProductItem, isBuy.value)) {
if (!isProductTradeOpen(tradeProduct.value, isBuy.value)) {
message.warning(productTradeClosedMessage(isBuy.value))
return
}
@@ -853,7 +938,7 @@ async function ok() {
amount: normalizeOrderGoldAmount(orderGoldAmount.value),
// ریال قفل → مبلغ دقیق به سرور می‌رود (کارمزد از طلا). گرم قفل → target_cash خالی، وزن دقیق معامله می‌شود.
target_cash: calcTradeTargetCash(form.value.goldCash, inputLock.value),
abshode_id: props.ProductItem?.id,
abshode_id: tradeProduct.value?.id,
})
const result = await afterCreateOrder(res)
if (result !== 'success') {