fix ذشزنلقخعدی

This commit is contained in:
2026-06-23 16:02:41 +03:30
parent 29c58c4202
commit ac28eda09f
29 changed files with 624 additions and 167 deletions
@@ -17,23 +17,23 @@
>
<BasicIcon icon="vuesax-linear:info-circle" size="18" :color="iconColor" />
</button>
</div>
<div
v-if="openRowId === row.id"
class="boursecu-assets__popup"
@click.stop
>
<div
v-if="openRowId === row.id"
class="boursecu-assets__popup"
@click.stop
v-for="line in row.popupLines"
:key="line.label"
class="boursecu-assets__popup-line"
:class="{
'boursecu-assets__popup-line--profit': line.tone === 'profit',
'boursecu-assets__popup-line--loss': line.tone === 'loss',
}"
>
<div
v-for="line in row.popupLines"
:key="line.label"
class="boursecu-assets__popup-line"
:class="{
'boursecu-assets__popup-line--profit': line.tone === 'profit',
'boursecu-assets__popup-line--loss': line.tone === 'loss',
}"
>
<span class="boursecu-assets__popup-label">{{ line.label }}</span>
<span class="boursecu-assets__popup-value">{{ line.value }}</span>
</div>
<span class="boursecu-assets__popup-label">{{ line.label }}</span>
<span class="boursecu-assets__popup-value">{{ line.value }}</span>
</div>
</div>
</div>
@@ -86,6 +86,7 @@ onClickOutside(rootRef, () => {
}
.boursecu-assets__row {
position: relative;
display: flex;
align-items: center;
justify-content: center;
@@ -122,9 +123,12 @@ onClickOutside(rootRef, () => {
.boursecu-assets__popup {
position: absolute;
top: calc(100% + 8px);
right: 0;
left: 50%;
right: auto;
transform: translateX(-50%);
z-index: 40;
min-width: min(18rem, 78vw);
width: min(18rem, calc(100vw - 2rem));
max-width: calc(100vw - 2rem);
padding: 10px 12px;
border-radius: 10px;
background: #fff;
@@ -177,9 +181,11 @@ onClickOutside(rootRef, () => {
}
.boursecu-assets__popup {
right: auto;
left: 0;
min-width: 20rem;
right: auto;
transform: none;
width: min(20rem, calc(100% - 1rem));
max-width: 20rem;
}
}
</style>
+145 -28
View File
@@ -1,29 +1,47 @@
<template>
<div class="w-full relative mt-2 border-gray-200 border-1 rounded-lg py-3 flex flex-col justify-center home_appear_fade_in">
<div class="text-gray-600 text-[12px] ml-2 w-full text-center">قیمت خرید از {{ config.config.name_fa }}</div>
<div class="w-full items-center flex justify-center">
<div v-if="price" class="flex gap-1 text-xs items-center">
<div
v-if="percentChange !== 0"
class="price-change-badge"
:class="percentChange > 0 ? 'price-change-badge--up' : 'price-change-badge--down'"
>
<Icon
:key="displayGoldVal"
:size="15"
:icon="percentChange > 0 ? 'vuesax-linear:send' : 'vuesax-linear:received'"
:color="percentChange > 0 ? 'green-600' : 'red-600'"
/>
<span class="price-change-badge__value">{{ formatSignedPercentChange(percentChange) }}</span>
<div
class="w-full relative mt-2 rounded-lg py-3 flex flex-col justify-center home_appear_fade_in transition-colors duration-300"
:class="containerClass"
>
<template v-if="isBoursecu && showStoreClosed">
<div class="boursecu-closed-box">
<div class="boursecu-closed-box__icon-wrap">
<Icon icon="vuesax-linear:lock" size="22" color="#ffffff" />
</div>
<Vue3Odometer
:value="displayGoldVal"
class="o-text !text-xl font-bold"
style="font-family: iran-sans; direction: ltr"
/>
ریال
<div class="boursecu-closed-box__title">فروشگاه بسته است</div>
<div class="boursecu-closed-box__hint">{{ storeClosedHint }}</div>
</div>
</div>
</template>
<template v-else>
<div class="price-label" :class="isBoursecu ? 'price-label--boursecu' : 'text-gray-600 text-[12px] ml-2 w-full text-center'">
{{ priceLabel }}
</div>
<div class="w-full items-center flex justify-center">
<div v-if="price" class="flex gap-1 text-xs items-center">
<div
v-if="percentChange !== 0"
class="price-change-badge"
:class="percentChange > 0 ? 'price-change-badge--up' : 'price-change-badge--down'"
>
<Icon
:key="displayGoldVal"
:size="15"
:icon="percentChange > 0 ? 'vuesax-linear:send' : 'vuesax-linear:received'"
:color="percentChange > 0 ? 'green-600' : 'red-600'"
/>
<span class="price-change-badge__value">{{ formatSignedPercentChange(percentChange) }}</span>
</div>
<Vue3Odometer
:value="displayGoldVal"
class="o-text !text-xl font-bold"
:class="isBoursecu ? 'price-odometer--boursecu' : ''"
style="font-family: iran-sans; direction: ltr"
/>
<span :class="isBoursecu ? 'price-unit--boursecu' : ''">ریال</span>
</div>
</div>
</template>
</div>
</template>
@@ -31,29 +49,128 @@
import { computed } from 'vue'
import Icon from '@/components/BasicPacks/BasicIcon.vue'
import { useConfigStore } from '@/store/configStore'
import { useUserStore } from '@/store/userStore'
import { isBoursecuProject } from '@/utils/config'
import { getTradeBuyPrice, lastPrice, percentPriceChanged, price } from '@/utils/price'
import { getStoreClosedMessage, isStoreClosed } from '@/utils/storeHours'
import Vue3Odometer from 'vue3-odometer'
import 'odometer/themes/odometer-theme-default.css'
import { formatSignedPercentChange } from '@/utils/usePrice'
const config = useConfigStore()
const userStore = useUserStore()
const isBoursecu = isBoursecuProject()
const priceLabel = computed(() =>
isBoursecu ? 'قیمت لحظه‌ای کاتد مس' : `قیمت خرید از ${config.config.name_fa}`
)
const showStoreClosed = computed(() => {
if (isBoursecu) {
return isStoreClosed()
}
return lastPrice?.value?.goldir === 0 && getTradeBuyPrice(lastPrice.value) === 0
})
const storeClosedHint = computed(() => {
const custom = userStore.getValueByKey('closed_message')
if (typeof custom === 'string' && custom.trim()) {
return custom.replace(/<[^>]*>/g, '').trim()
}
return getStoreClosedMessage()
})
const containerClass = computed(() => {
if (!isBoursecu) {
return 'border-gray-200 border-1'
}
if (showStoreClosed.value) {
return 'boursecu-price-box boursecu-price-box--closed'
}
return 'boursecu-price-box boursecu-price-box--open'
})
const percentChange = computed(() => {
if (!getTradeBuyPrice(lastPrice.value)) return 0
return percentPriceChanged()
})
const isMarketClosed = computed(() => {
return lastPrice?.value?.goldir === 0 && getTradeBuyPrice(lastPrice.value) === 0
})
const displayGoldVal = computed(() => {
if (isMarketClosed.value) return 0
if (showStoreClosed.value) return 0
return getTradeBuyPrice(lastPrice.value)
})
void price
</script>
<style scoped>
.boursecu-price-box {
border: none;
overflow: hidden;
}
.boursecu-price-box--open {
background: linear-gradient(135deg, #c98a4a 0%, #a86528 100%);
box-shadow: 0 4px 14px rgba(168, 101, 40, 0.28);
}
.boursecu-price-box--closed {
background: linear-gradient(135deg, #4a5568 0%, #2d3748 100%);
box-shadow: 0 4px 14px rgba(45, 55, 72, 0.22);
min-height: 88px;
}
.price-label--boursecu {
color: rgba(255, 255, 255, 0.92);
font-size: 12px;
font-weight: 600;
width: 100%;
text-align: center;
}
.price-odometer--boursecu {
color: #ffffff !important;
}
.price-unit--boursecu {
color: rgba(255, 255, 255, 0.85);
font-weight: 500;
}
.boursecu-closed-box {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 6px;
padding: 8px 12px;
text-align: center;
}
.boursecu-closed-box__icon-wrap {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.12);
margin-bottom: 2px;
}
.boursecu-closed-box__title {
color: #ffffff;
font-size: 15px;
font-weight: 700;
}
.boursecu-closed-box__hint {
color: rgba(255, 255, 255, 0.78);
font-size: 11px;
line-height: 1.6;
max-width: 280px;
}
.price-change-badge {
display: inline-flex;
align-items: center;
+14 -6
View File
@@ -25,6 +25,14 @@
>
مفتول مس
</button>
<button
type="button"
class="chart-metal-segment__item"
:class="{ 'chart-metal-segment__item--active': selectedMetal === 'shamsh' }"
@click="emit('update:selectedMetal', 'shamsh')"
>
شمش مس
</button>
</div>
<a-radio-group v-model:value="selectedValue" button-style="solid" class="chart-timeline-group">
<a-radio-button value="DAILY" @click="handleUpdate('DAILY')">24 ساعت اخیر</a-radio-button>
@@ -50,7 +58,7 @@
import { computed, nextTick, onMounted, ref, watch } from 'vue'
import { useConfigStore } from '@/store/configStore'
import { usePrice } from '@/utils/usePrice'
import { getProjectFontFamily } from '@/utils/config'
import { getProjectFontFamily, getProjectPrimaryColor } from '@/utils/config'
import {
computeKatodMesVisualYBounds,
prepareKatodMesChartPoints,
@@ -100,7 +108,7 @@ const emit = defineEmits<{
const config = useConfigStore()
const settingsStore = useSettingsStore()
const { maftolChartRatio } = usePriceChart()
const { selectedMetalChartRatio } = usePriceChart()
const chartContainerRef = ref<HTMLElement | null>(null)
const chartMounted = ref(false)
const selectedValue = ref(props.chartSelection)
@@ -126,7 +134,7 @@ const katodDayRange = computed(() => {
price: price.value,
metal: props.selectedMetal,
isBoursecuLayout: true,
maftolRatio: maftolChartRatio.value,
metalRatio: selectedMetalChartRatio.value,
})
})
const displayChartSeries = computed(() => {
@@ -287,7 +295,7 @@ const chartOptions = computed(() => {
}),
style: { fontFamily },
},
colors: [config.config.color],
colors: [getProjectPrimaryColor() ?? config.config?.color ?? '#2178b5'],
fill: {
type: 'gradient',
gradient: {
@@ -324,8 +332,8 @@ const handleUpdate = (timeline: string) => {
.chart-metal-segment__item {
appearance: none;
cursor: pointer;
min-width: 6.5rem;
padding: 0.4rem 0.75rem;
min-width: 5.5rem;
padding: 0.4rem 0.6rem;
border: none;
border-radius: 8px;
background: transparent;
@@ -46,7 +46,7 @@ withDefaults(
}
)
const { selectedChartMetal, maftolChartRatio } = usePriceChart()
const { selectedChartMetal, selectedMetalChartRatio } = usePriceChart()
const isBoursecuLayout = computed(() => getHomeLayoutId() === 'boursecu')
const rangePrices = computed(() =>
@@ -54,7 +54,7 @@ const rangePrices = computed(() =>
price: price.value,
metal: selectedChartMetal.value,
isBoursecuLayout: isBoursecuLayout.value,
maftolRatio: maftolChartRatio.value,
metalRatio: selectedMetalChartRatio.value,
})
)
@@ -4,10 +4,10 @@ import type { Product } from '@/models/products'
import { getProductsCatalog, getProductsCatalogRef } from '@/composables/useProductsCatalog'
import { getHomeLayoutId } from '../layouts/registry'
import { getProductBuyPrice, resolvePriceOffsetRatio } from '@/utils/productPrice'
import { isKatodMesGroup, isMaftolMesGroup } from '@/utils/productDealMethod'
import { isKatodMesGroup, isMaftolMesGroup, isShamshMesGroup } from '@/utils/productDealMethod'
import { getTradeBuyPrice, getTradeSellPrice, lastPrice } from '@/utils/price'
export type ChartMetalType = 'katod' | 'maftol'
export type ChartMetalType = 'katod' | 'maftol' | 'shamsh'
type ChartPoint = [number, number]
@@ -25,6 +25,7 @@ function resolveChartProducts(products: Product[]) {
return {
katod: findKatodProduct(products),
maftol: findMaftolProduct(products),
shamsh: findShamshProduct(products),
}
}
@@ -34,17 +35,38 @@ const maftolChartRatio = computed(() => {
return 0
}
// lastPrice را track کن تا بعد از polling نسبت به‌روز شود
void lastPrice.value?.price_last
const { katod, maftol } = resolveChartProducts(catalogProducts.value)
return deriveMaftolChartRatio(maftol, katod)
return deriveMetalChartRatio(maftol, katod)
})
/** نسبت شمش مس به کاتد */
const shamshChartRatio = computed(() => {
if (getHomeLayoutId() !== 'boursecu') {
return 0
}
void lastPrice.value?.price_last
const { katod, shamsh } = resolveChartProducts(catalogProducts.value)
return deriveMetalChartRatio(shamsh, katod)
})
const selectedMetalChartRatio = computed(() => {
if (selectedChartMetal.value === 'maftol') {
return maftolChartRatio.value
}
if (selectedChartMetal.value === 'shamsh') {
return shamshChartRatio.value
}
return 0
})
let chartFetchPromise: Promise<void> | null = null
let chartInitialized = false
let maftolOffsetPromise: Promise<void> | null = null
let maftolOffsetLoaded = false
let metalOffsetPromise: Promise<void> | null = null
let metalOffsetLoaded = false
function findKatodProduct(products: Product[]) {
return (
@@ -60,6 +82,12 @@ function findMaftolProduct(products: Product[]) {
)
}
function findShamshProduct(products: Product[]) {
return products.find((product) =>
isShamshMesGroup(product.group_type, product.title)
)
}
function applyChartRatio(basePrice: number, ratio: number) {
if (!ratio) {
return basePrice
@@ -68,12 +96,12 @@ function applyChartRatio(basePrice: number, ratio: number) {
return Math.round(basePrice + basePrice * ratio)
}
/** بالاترین/پایین‌ترین امروز — برای boursecu با نسبت مفتول نسبت به کاتد */
/** بالاترین/پایین‌ترین امروز — برای boursecu با نسبت مفتول/شمش نسبت به کاتد */
export function resolveMetalDayRangePrices(options: {
price: { up?: { buy?: number; sell?: number }; buy?: number; sell?: number; price_last?: number } | null | undefined
metal: ChartMetalType
isBoursecuLayout: boolean
maftolRatio: number
metalRatio: number
}) {
const baseHigh = options.price?.up?.buy ?? getTradeBuyPrice(options.price as Parameters<typeof getTradeBuyPrice>[0])
const baseLow = options.price?.up?.sell ?? getTradeSellPrice(options.price as Parameters<typeof getTradeSellPrice>[0])
@@ -83,37 +111,36 @@ export function resolveMetalDayRangePrices(options: {
}
return {
high: applyChartRatio(baseHigh, options.maftolRatio),
low: applyChartRatio(baseLow, options.maftolRatio),
high: applyChartRatio(baseHigh, options.metalRatio),
low: applyChartRatio(baseLow, options.metalRatio),
}
}
function deriveMaftolChartRatio(maftolProduct?: Product, katodProduct?: Product) {
const maftolOffset = resolvePriceOffsetRatio(maftolProduct?.price_offset_percent)
if (maftolOffset) {
return maftolOffset
function deriveMetalChartRatio(metalProduct?: Product, katodProduct?: Product) {
const metalOffset = resolvePriceOffsetRatio(metalProduct?.price_offset_percent)
if (metalOffset) {
return metalOffset
}
if (!maftolProduct) {
if (!metalProduct) {
return 0
}
const liveBuy = getTradeBuyPrice(lastPrice.value)
const katodLive = katodProduct ? getProductBuyPrice(katodProduct) : liveBuy
const maftolLive = getProductBuyPrice(maftolProduct)
const metalLive = getProductBuyPrice(metalProduct)
// وقتی فروشگاه بسته است قیمت لحظه‌ای صفر می‌شود — از قیمت ثابت کاتالوگ استفاده کن
const katodStatic = Number(katodProduct?.price ?? 0) + Number(katodProduct?.price_profit ?? 0)
const maftolStatic = Number(maftolProduct.price ?? 0) + Number(maftolProduct.price_profit ?? 0)
const metalStatic = Number(metalProduct.price ?? 0) + Number(metalProduct.price_profit ?? 0)
const referencePrice = katodLive || liveBuy || katodStatic
const maftolPrice = maftolLive || maftolStatic
const derivedMetalPrice = metalLive || metalStatic
if (!referencePrice || !maftolPrice || referencePrice === maftolPrice) {
if (!referencePrice || !derivedMetalPrice || referencePrice === derivedMetalPrice) {
return 0
}
return (maftolPrice - referencePrice) / referencePrice
return (derivedMetalPrice - referencePrice) / referencePrice
}
function normalizeChartTimestamp(timestamp: number) {
@@ -147,18 +174,19 @@ function buildChartPoints(items: Array<{ timestamp: number; value: number }>): C
}
function syncChartSeries() {
const ratio = selectedMetalChartRatio.value
const data =
getHomeLayoutId() === 'boursecu' && selectedChartMetal.value === 'maftol'
getHomeLayoutId() === 'boursecu' && selectedChartMetal.value !== 'katod'
? rawChartPoints.value.map(
([timestamp, value]) =>
[timestamp, applyChartRatio(value, maftolChartRatio.value)] as ChartPoint
[timestamp, applyChartRatio(value, ratio)] as ChartPoint
)
: rawChartPoints.value
priceChartSeries.value[0].data = data
}
watch([selectedChartMetal, maftolChartRatio], () => {
watch([selectedChartMetal, maftolChartRatio, shamshChartRatio], () => {
syncChartSeries()
})
@@ -192,34 +220,34 @@ async function fetchPrices(force = false) {
return chartFetchPromise
}
async function loadMaftolOffset(force = false) {
async function loadMetalChartOffsets(force = false) {
if (getHomeLayoutId() !== 'boursecu') {
syncChartSeries()
return
}
if (!force && maftolOffsetLoaded && catalogProducts.value.length > 0) {
if (!force && metalOffsetLoaded && catalogProducts.value.length > 0) {
return
}
if (!force && maftolOffsetPromise) {
return maftolOffsetPromise
if (!force && metalOffsetPromise) {
return metalOffsetPromise
}
maftolOffsetPromise = (async () => {
metalOffsetPromise = (async () => {
try {
await getProductsCatalog(force)
syncChartSeries()
maftolOffsetLoaded = true
metalOffsetLoaded = true
} catch (error) {
console.error('Error loading maftol chart ratio:', error)
console.error('Error loading boursecu chart metal ratios:', error)
syncChartSeries()
} finally {
maftolOffsetPromise = null
metalOffsetPromise = null
}
})()
return maftolOffsetPromise
return metalOffsetPromise
}
function updateChartData(timeline: string) {
@@ -230,8 +258,8 @@ function updateChartData(timeline: string) {
function setChartMetal(metal: ChartMetalType) {
selectedChartMetal.value = metal
if (metal === 'maftol' && getHomeLayoutId() === 'boursecu' && !catalogProducts.value.length) {
void loadMaftolOffset(true)
if (metal !== 'katod' && getHomeLayoutId() === 'boursecu' && !catalogProducts.value.length) {
void loadMetalChartOffsets(true)
}
}
@@ -242,9 +270,12 @@ export function usePriceChart() {
chartLoading,
selectedChartMetal,
maftolChartRatio,
shamshChartRatio,
selectedMetalChartRatio,
updateChartData,
fetchPrices,
loadMaftolOffset,
loadMaftolOffset: loadMetalChartOffsets,
loadMetalChartOffsets,
setChartMetal,
}
}
@@ -254,7 +285,7 @@ export function fetchHomeChartPrices(force = false) {
}
export function loadHomeMaftolOffset(force = false) {
return loadMaftolOffset(force)
return loadMetalChartOffsets(force)
}
export function resetHomePriceChart() {
@@ -264,6 +295,6 @@ export function resetHomePriceChart() {
selectedChartMetal.value = 'katod'
chartInitialized = false
chartFetchPromise = null
maftolOffsetLoaded = false
maftolOffsetPromise = null
metalOffsetLoaded = false
metalOffsetPromise = null
}
@@ -2,11 +2,13 @@
import { computed, inject } from 'vue'
import { useUserStore } from '@/store/userStore'
import { usePriceWithoutType } from '@/utils/usePrice'
import { isBoursecuProject } from '@/utils/config'
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
import { HOME_PAGE_CONTEXT_KEY } from '../../composables/useHomePage'
const ctx = inject(HOME_PAGE_CONTEXT_KEY)!
const userStore = useUserStore()
const isBoursecu = isBoursecuProject()
const showLimitedPrice = computed(
() =>
@@ -28,7 +30,7 @@ const limitedPriceText = computed(() =>
</span>
</div>
<div
v-if="ctx.showClosedMessage.value"
v-if="!isBoursecu && ctx.showClosedMessage.value"
class="dhome-alert dhome-alert--info"
v-html="ctx.closedMessage.value"
/>
@@ -1,26 +1,47 @@
<template>
<div class="dhome-surface dhome-hero-price">
<span class="dhome-label">قیمت لحظه‌ای</span>
<p class="dhome-hero-price__brand">{{ config.config?.name_fa }}</p>
<div class="dhome-hero-price__value-row">
<span
v-if="percentChange !== 0"
class="dhome-hero-price__delta"
:class="percentChange > 0 ? 'dhome-hero-price__delta--up' : 'dhome-hero-price__delta--down'"
>
<BasicIcon
:icon="percentChange > 0 ? 'vuesax-linear:arrow-up-2' : 'vuesax-linear:arrow-down-2'"
size="14"
/>
<span class="dhome-hero-price__delta-value">{{ formatSignedPercentChange(percentChange) }}</span>
<div
class="dhome-surface dhome-hero-price"
:class="{
'dhome-hero-price--boursecu-open': isBoursecu && !showStoreClosed,
'dhome-hero-price--boursecu-closed': isBoursecu && showStoreClosed,
}"
>
<template v-if="isBoursecu && showStoreClosed">
<div class="dhome-hero-price__closed">
<div class="dhome-hero-price__closed-icon">
<BasicIcon icon="vuesax-linear:lock" size="22" color="#ffffff" />
</div>
<p class="dhome-hero-price__closed-title">فروشگاه بسته است</p>
<p class="dhome-hero-price__closed-hint">{{ storeClosedHint }}</p>
</div>
</template>
<template v-else>
<span class="dhome-label" :class="{ 'dhome-label--boursecu': isBoursecu }">
{{ isBoursecu ? 'قیمت لحظه‌ای کاتد مس' : 'قیمت لحظه‌ای' }}
</span>
<Vue3Odometer
:value="displayGoldVal"
class="dhome-hero-price__odometer o-text"
style="font-family: iran-sans"
/>
<span class="dhome-hero-price__unit">ریال</span>
</div>
<p v-if="!isBoursecu" class="dhome-hero-price__brand">{{ config.config?.name_fa }}</p>
<div class="dhome-hero-price__value-row">
<span
v-if="percentChange !== 0"
class="dhome-hero-price__delta"
:class="percentChange > 0 ? 'dhome-hero-price__delta--up' : 'dhome-hero-price__delta--down'"
>
<BasicIcon
:icon="percentChange > 0 ? 'vuesax-linear:arrow-up-2' : 'vuesax-linear:arrow-down-2'"
size="14"
/>
<span class="dhome-hero-price__delta-value">{{ formatSignedPercentChange(percentChange) }}</span>
</span>
<Vue3Odometer
:value="displayGoldVal"
class="dhome-hero-price__odometer o-text"
:class="{ 'dhome-hero-price__odometer--boursecu': isBoursecu }"
style="font-family: iran-sans"
/>
<span class="dhome-hero-price__unit" :class="{ 'dhome-hero-price__unit--boursecu': isBoursecu }">ریال</span>
</div>
</template>
</div>
</template>
@@ -30,17 +51,33 @@ import Vue3Odometer from 'vue3-odometer'
import 'odometer/themes/odometer-theme-default.css'
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
import { useConfigStore } from '@/store/configStore'
import { useUserStore } from '@/store/userStore'
import { isBoursecuProject } from '@/utils/config'
import { getTradeBuyPrice, lastPrice, percentPriceChanged } from '@/utils/price'
import { getStoreClosedMessage, isStoreClosed } from '@/utils/storeHours'
import { formatSignedPercentChange } from '@/utils/usePrice'
const config = useConfigStore()
const userStore = useUserStore()
const isBoursecu = isBoursecuProject()
const isMarketClosed = computed(
() => lastPrice?.value?.goldir === 0 && getTradeBuyPrice(lastPrice.value) === 0,
)
const showStoreClosed = computed(() => {
if (isBoursecu) {
return isStoreClosed()
}
return lastPrice?.value?.goldir === 0 && getTradeBuyPrice(lastPrice.value) === 0
})
const storeClosedHint = computed(() => {
const custom = userStore.getValueByKey('closed_message')
if (typeof custom === 'string' && custom.trim()) {
return custom.replace(/<[^>]*>/g, '').trim()
}
return getStoreClosedMessage()
})
const displayGoldVal = computed(() => {
if (isMarketClosed.value) return 0
if (showStoreClosed.value) return 0
return getTradeBuyPrice(lastPrice.value)
})
@@ -49,3 +86,61 @@ const percentChange = computed(() => {
return percentPriceChanged()
})
</script>
<style scoped lang="less">
.dhome-hero-price--boursecu-open {
background: linear-gradient(135deg, #c98a4a 0%, #a86528 100%);
border-color: transparent;
box-shadow: 0 4px 14px rgba(168, 101, 40, 0.28);
}
.dhome-hero-price--boursecu-closed {
background: linear-gradient(135deg, #4a5568 0%, #2d3748 100%);
border-color: transparent;
box-shadow: 0 4px 14px rgba(45, 55, 72, 0.22);
}
.dhome-label--boursecu {
color: rgba(255, 255, 255, 0.92);
}
.dhome-hero-price__odometer--boursecu {
color: #ffffff !important;
}
.dhome-hero-price__unit--boursecu {
color: rgba(255, 255, 255, 0.85);
}
.dhome-hero-price__closed {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 6px;
}
.dhome-hero-price__closed-icon {
display: flex;
align-items: center;
justify-content: center;
width: 44px;
height: 44px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.12);
margin-bottom: 4px;
}
.dhome-hero-price__closed-title {
margin: 0;
font-size: 18px;
font-weight: 700;
color: #ffffff;
}
.dhome-hero-price__closed-hint {
margin: 0;
font-size: 12px;
line-height: 1.7;
color: rgba(255, 255, 255, 0.78);
}
</style>
@@ -5,7 +5,7 @@ import { usePriceWithoutType } from '@/utils/usePrice'
import { getHomeLayoutId } from '../../layouts/registry'
import { resolveMetalDayRangePrices, usePriceChart } from '../../composables/usePriceChart'
const { selectedChartMetal, maftolChartRatio } = usePriceChart()
const { selectedChartMetal, selectedMetalChartRatio } = usePriceChart()
const isBoursecuLayout = computed(() => getHomeLayoutId() === 'boursecu')
const rangePrices = computed(() =>
@@ -13,7 +13,7 @@ const rangePrices = computed(() =>
price: price.value,
metal: selectedChartMetal.value,
isBoursecuLayout: isBoursecuLayout.value,
maftolRatio: maftolChartRatio.value,
metalRatio: selectedMetalChartRatio.value,
})
)
@@ -1,5 +1,6 @@
<script setup lang="ts">
import { inject } from 'vue'
import { isBoursecuProject } from '@/utils/config'
import MarketClosedMessage from '../../components/MarketClosedMessage.vue'
import { HOME_PAGE_CONTEXT_KEY } from '../../composables/useHomePage'
@@ -8,7 +9,7 @@ const ctx = inject(HOME_PAGE_CONTEXT_KEY)!
<template>
<MarketClosedMessage
v-if="ctx.showClosedMessage.value"
v-if="!isBoursecuProject() && ctx.showClosedMessage.value"
:message="ctx.closedMessage.value as string"
/>
</template>