Merge remote-tracking branch 'origin/modular' into modular

# Conflicts:
#	src/views/base/home/composables/usePriceChart.ts
This commit is contained in:
2026-06-15 11:33:26 +03:30
14 changed files with 369 additions and 58 deletions
+10
View File
@@ -239,6 +239,7 @@ import {useConfigStore} from "@/store/configStore";
import { calcFeeInGrams } from '@/utils/serviceFee'
import { formatCashWalletBalanceText, formatGoldWalletBalanceText } from '@/utils/productDealMethod'
import { getCashWalletBalance } from '@/utils/productAssets'
import { isStoreClosed, getStoreClosedMessage } from '@/utils/storeHours'
const infoVisible = ref(false)
const walletStore = useWalletStore()
@@ -325,6 +326,7 @@ const equivalentPrice = computed(() => usePriceWithoutType(feeInRial.value.toFix
const disableTransaction = computed(() =>
loading.value ||
isStoreClosed() ||
form.value?.goldValue === undefined ||
form.value?.goldValue === '' ||
form.value?.goldCash === '' ||
@@ -640,10 +642,18 @@ async function initPrice() {
}
function okConfirm() {
if (isStoreClosed()) {
message.warning(getStoreClosedMessage())
return
}
confirmAction.value = true
}
async function ok() {
if (isStoreClosed()) {
message.warning(getStoreClosedMessage())
return
}
try {
loading.value = true
infoVisible.value = true
+10
View File
@@ -236,6 +236,7 @@ import type { Product } from '@/models/products'
import { useFundUserStore } from '@/store/fundStore'
import {useConfigStore} from "@/store/configStore";
import { calcFeeInGrams, loadServiceFeeFromSettings } from '@/utils/serviceFee'
import { isStoreClosed, getStoreClosedMessage } from '@/utils/storeHours'
import { formatCashWalletBalanceText, getWeightUnit } from '@/utils/productDealMethod'
import { formatProductWalletBalanceText, getCashWalletBalance, getProductInventoryQuantity } from '@/utils/productAssets'
import { useWalletAssets } from '@/composables/useWalletAssets'
@@ -344,6 +345,7 @@ const priceUnavailable = computed(() => {
const disableTransaction = computed(() =>
loading.value ||
isStoreClosed() ||
form.value?.goldValue === undefined ||
form.value?.goldValue === '' ||
form.value?.goldCash === '' ||
@@ -633,6 +635,10 @@ async function initPrice() {
}
function okConfirm() {
if (isStoreClosed()) {
message.warning(getStoreClosedMessage())
return
}
if (cashValidationMessage.value) {
message.warning(cashValidationMessage.value)
return
@@ -641,6 +647,10 @@ function okConfirm() {
}
async function ok() {
if (isStoreClosed()) {
message.warning(getStoreClosedMessage())
return
}
try {
loading.value = true
infoVisible.value = true
+7 -4
View File
@@ -38,7 +38,7 @@
<AssetsSection v-else @openGoldModal="openGoldModal" @openRialModal="openRialModal"/>
<!-- پیام بسته بودن بازار -->
<MarketClosedMessage v-if="showClosedMessage" :message="closedMessage"/>
<MarketClosedMessage v-if="showClosedMessage" :message="marketClosedText"/>
<!-- قیمت فعلی -->
<CurrentPrice/>
@@ -89,6 +89,7 @@ import {useConfigStore} from '@/store/configStore'
import {useFundUserStore} from '@/store/fundStore'
import {usePriceWithoutType} from '../../../utils/usePrice'
import {getLastPrices, lastPrice, price, startLastPricePolling, stopLastPricePolling} from '@/utils/price'
import { getStoreClosedMessage, isStoreClosed } from '@/utils/storeHours'
import {getConstByKey} from '@/api/const'
import {message, Modal} from 'ant-design-vue'
@@ -126,9 +127,11 @@ const { assetsSummary, loadWalletAssets } = useWalletAssets()
// Config
const isGaroosi = computed(() => configStore.config.name === 'garoosi')
const closedMessage = computed(() => userStore.getValueByKey('closed_message'))
const showClosedMessage = computed(() =>
closedMessage.value &&
lastPrice?.value?.price_last === 0)
const marketClosedText = computed(() => {
if (!isStoreClosed()) return ''
return closedMessage.value || getStoreClosedMessage()
})
const showClosedMessage = computed(() => !!marketClosedText.value)
// State
@@ -297,6 +297,7 @@ import {
} from '@/utils/productOrderLimit'
import { useWalletAssets } from '@/composables/useWalletAssets'
import { formatProductWalletBalanceText, getCashWalletBalance, getProductInventoryQuantity } from '@/utils/productAssets'
import { isStoreClosed, getStoreClosedMessage } from '@/utils/storeHours'
const typeDeal = computed(() => getProductDealMethodOptions(props?.ProductItem?.group_type, isBuy.value))
@@ -438,6 +439,7 @@ const cashValidationMessage = computed(() =>
const disableTransaction = computed(() =>
loading.value ||
isStoreClosed() ||
form.value?.goldValue === undefined ||
form.value?.goldValue === '' ||
form.value?.goldCash === '' ||
@@ -795,11 +797,19 @@ async function initPrice() {
// })
function okConfirm() {
if (isStoreClosed()) {
message.warning(getStoreClosedMessage())
return
}
confirmAction.value = true
refInput.value?.validateFields()
}
async function ok() {
if (isStoreClosed()) {
message.warning(getStoreClosedMessage())
return
}
try {
loading.value = true
infoVisible.value = true
+59 -15
View File
@@ -13,6 +13,15 @@
</ARadioGroup>
</div>
<div
v-if="storeIsClosed"
class="mx-5 mb-3 rounded-lg bg-red-50 px-3 py-4 text-center text-red-600"
>
<div class="font-bold">مشتریان گرامی</div>
<p class="mt-2 leading-relaxed">{{ storeClosedMessage }}</p>
<p class="mt-2">از همراهی و اعتماد شما سپاسگزاریم.🤍</p>
</div>
<div class="mx-5 pb-30 pt-2" style="color: #1b2b07">
<div class="mt-3" v-for="item in items" :key="item.id">
<ProductItem
@@ -70,8 +79,7 @@
<div class="text-center">
<div class="font-bold text-red-600">مشتریان گرامی</div>
<div class="mt-2 leading-relaxed">
<p>فروشگاه در حال حاضر بسته است.</p>
<p>ساعت کاری فروشگاه از {{ openTime }} تا {{ closeTime }} می‌باشد.</p>
<p>{{ storeClosedMessage }}</p>
<p>از همراهی و اعتماد شما سپاسگزاریم.🤍</p>
</div>
</div>
@@ -103,7 +111,15 @@ import { isMetalCommodityGroup, isMeltedTradeGroup } from '@/utils/productCatego
import { isBoursecuDealProductGroup } from '@/utils/productDealMethod'
import { loadServiceFeeFromSettings } from '@/utils/serviceFee'
import { loadProductCategoryTabNames } from '@/utils/categoryTabs'
import { getLastPrices, startLastPricePolling, stopLastPricePolling } from '@/utils/price'
import { getLastPrices, lastPrice, startLastPricePolling, stopLastPricePolling } from '@/utils/price'
import {
formatTradingTimeDisplay,
getEffectiveTradingHours,
getStoreClosedMessage,
isStoreClosed,
setProductTradingHours
} from '@/utils/storeHours'
import { useSettingsStore } from '@/store/settingsStore'
const activekey = ref('طلا' ||'آبشده')
const showFilter = ref(false)
@@ -113,21 +129,29 @@ const closeTime = ref<string>('')
const openTime = ref<string>('')
const isAfterCloseTime = ref<boolean>(false)
const isProductInactive = ref<boolean>(false)
const storeIsClosed = computed(() => isStoreClosed())
const storeClosedMessage = computed(() => getStoreClosedMessage())
watch([openTime, closeTime], ([open, close]) => {
if (!open || !close) return
const now = new Date()
const currentSeconds = now.getHours() * 3600 + now.getMinutes() * 60 + now.getSeconds()
const [oh, om, os] = open.split(':').map(Number)
const [ch, cm, cs] = close.split(':').map(Number)
const openSeconds = oh * 3600 + om * 60 + os
const closeSeconds = ch * 3600 + cm * 60 + cs
isAfterCloseTime.value = currentSeconds < openSeconds || currentSeconds > closeSeconds
watch([openTime, closeTime], () => {
syncStoreClosedState()
})
function syncStoreClosedState() {
if (isStoreClosed()) {
showFilter.value = false
isAfterCloseTime.value = true
}
}
function clickProduct(item?: Product) {
if (!item?.show) return
if (isStoreClosed()) {
showFilter.value = false
isAfterCloseTime.value = true
return
}
if (isProductPriceInactive(item)) {
showFilter.value = false
isProductInactive.value = true
@@ -144,6 +168,11 @@ function onProductOrdered() {
}
watch(showFilter, (open) => {
if (open && isStoreClosed()) {
showFilter.value = false
isAfterCloseTime.value = true
return
}
if (!open) {
orderSheetKey.value += 1
}
@@ -161,6 +190,7 @@ const firstItems = ref<Product[]>()
const items = ref<Product[]>()
const typeItems = ref<string[]>([])
let productListInterval: ReturnType<typeof setInterval> | undefined
let storeHoursInterval: ReturnType<typeof setInterval> | undefined
async function loadCategoryTabs() {
try {
@@ -212,22 +242,30 @@ async function refreshProducts() {
}
}
watch(() => lastPrice.value?.closed, syncStoreClosedState)
onMounted(async () => {
try {
loading.value = true
await useSettingsStore().fetchSettings()
await getLastPrices()
startLastPricePolling()
items.value = await getProducts()
if (items.value?.length) {
closeTime.value = items.value[0].auto_close_at
openTime.value = items.value[0].auto_open_at
setProductTradingHours(items.value[0].auto_open_at, items.value[0].auto_close_at)
}
const hours = getEffectiveTradingHours()
openTime.value = formatTradingTimeDisplay(hours.start)
closeTime.value = formatTradingTimeDisplay(hours.end)
syncStoreClosedState()
await loadCategoryTabs()
firstItems.value = items.value
await loadServiceFeeFromSettings()
productListInterval = setInterval(refreshProducts, 10000)
storeHoursInterval = setInterval(syncStoreClosedState, 30000)
} catch (e) {
console.log(e)
@@ -239,7 +277,9 @@ onMounted(async () => {
}
if (router.currentRoute.value.query.state) {
if (router.currentRoute.value.query.product) {
if (isStoreClosed()) {
isAfterCloseTime.value = true
} else if (router.currentRoute.value.query.product) {
clickProduct(items.value?.find(x => x.title === 'سکه'))
} else {
clickProduct(items.value?.find(x => x.title === 'آبشده نقدی'))
@@ -253,6 +293,10 @@ onUnmounted(() => {
clearInterval(productListInterval)
productListInterval = undefined
}
if (storeHoursInterval) {
clearInterval(storeHoursInterval)
storeHoursInterval = undefined
}
})
// watch(lastPrice, async () => {
// // console.log(items);
@@ -12,8 +12,8 @@
<div>{{ toJalaliOutOfUTC(item.expire_at) }}</div>
</div>
<div class="flex justify-between mt-2">
<div :class="item.gold >= 0? 'text-green-600':'text-red-600'"
class="text-gray-500 text-xs number-ltr">{{ item.gold }}
<div :class="getTransactionGoldAmount(item) >= 0? 'text-green-600':'text-red-600'"
class="text-gray-500 text-xs number-ltr">{{ formatGoldAmount(getTransactionGoldAmount(item)) }}
<div>{{item.unit}}</div>
</div>
<div class="text-gray-500 flex gap-1 text-xs items-center number-ltr">
@@ -29,7 +29,7 @@
<div class="text-gray-500">قیمت معامله:</div>
<div class="text-gray-500 flex gap-1 text-xs items-center">
<!-- <div class="text-base">{{ typeof parseInt((Math.abs(item.cash) / Math.abs(item.gold)).toFixed(0)) }}</div>-->
<div class="text-base">{{ usePriceWithoutType(parseInt((Math.abs(item.cash) / Math.abs(item.gold)).toFixed(0))) }}</div>
<div class="text-base">{{ usePriceWithoutType(getTransactionTradePrice(item)) }}</div>
ریال
</div>
</div>
@@ -62,16 +62,16 @@
</div>
<div class="flex justify-between mb-3">
<div class="text-primary">مقدار {{ item.wallet?.product ? item.wallet.product.title : getMeltedGoldTradeLabel() }} مبادله شده:</div>
<div :class="item.gold >= 0? 'text-green-600' : 'text-red-600'" class="flex flex-col items-end text-xs number-ltr">
<div :class="getTransactionGoldAmount(item) >= 0? 'text-green-600' : 'text-red-600'" class="flex flex-col items-end text-xs number-ltr">
<div class="flex items-center">
<div class="text-base ml-1">{{ item.gold }}</div>
<div class="text-base ml-1">{{ formatGoldAmount(getTransactionGoldAmount(item)) }}</div>
{{ item.unit }}
</div>
<div
v-if="item.wallet?.product && getProductTotalWeightLabel(Math.abs(item.gold ?? 0), item.wallet.product)"
v-if="item.wallet?.product && getProductTotalWeightLabel(Math.abs(getTransactionGoldAmount(item)), item.wallet.product)"
class="text-gray-500"
>
{{ getProductTotalWeightLabel(Math.abs(item.gold ?? 0), item.wallet.product) }}
{{ getProductTotalWeightLabel(Math.abs(getTransactionGoldAmount(item)), item.wallet.product) }}
</div>
</div>
</div>
@@ -146,7 +146,7 @@
<script lang="ts" setup>
import Icon from '@/components/BasicPacks/BasicIcon.vue'
import { usePriceWithoutType } from '../../../../utils/usePrice'
import { formatGoldAmount, getTransactionGoldAmount, getTransactionTradePrice, usePriceWithoutType } from '../../../../utils/usePrice'
import BasicActionSheet from '@/components/BasicPacks/BasicActionSheet.vue'
import { computed, ref } from 'vue'
import BasicButton from '@/components/BasicPacks/BasicButton.vue'
+2 -2
View File
@@ -40,7 +40,7 @@
<div v-if="isGold" class="flex w-full justify-between mt-2">
<div class="text-gray-500">قیمت معامله:</div>
<div class="text-gray-500 flex gap-1 text-xs items-center">
<div class="text-base">{{ usePriceWithoutType((Math.abs(item.cash) / Math.abs(item.gold)).toFixed(0)) }}</div>
<div class="text-base">{{ usePriceWithoutType(getTransactionTradePrice(item)) }}</div>
ریال
</div>
</div>
@@ -138,7 +138,7 @@
<script lang="ts" setup>
import Icon from "@/components/BasicPacks/BasicIcon.vue";
import {usePriceWithoutType} from "../../../../utils/usePrice";
import {getTransactionTradePrice, usePriceWithoutType} from "../../../../utils/usePrice";
import BasicActionSheet from "@/components/BasicPacks/BasicActionSheet.vue";
import {computed, ref} from "vue";
import BasicButton from "@/components/BasicPacks/BasicButton.vue";
+1 -4
View File
@@ -418,7 +418,6 @@ import { useConfigStore } from '@/store/configStore'
import { isBazretalaProject } from '@/utils/config'
import {
buildSupportMessengerUrl,
DEFAULT_SUPPORT_MESSENGER_TEXT,
loadSupportMessengerFromSettings,
supportMessengerSettings,
} from '@/utils/supportMessenger'
@@ -437,9 +436,7 @@ const withdrawInfModal = ref<boolean>(false)
const config = useConfigStore()
const supportMessengerText = computed(() =>
supportMessengerSettings.value.messengerText || DEFAULT_SUPPORT_MESSENGER_TEXT
)
const supportMessengerText = computed(() => supportMessengerSettings.value.messengerText ?? '')
const supportMessengerLink = computed(() =>
buildSupportMessengerUrl(