add banners

This commit is contained in:
2026-02-21 20:10:59 +03:30
parent 984d9edde2
commit 8124e05a0c
12 changed files with 142 additions and 46 deletions
+20
View File
@@ -22,6 +22,7 @@
"odometer": "^0.4.8",
"pinia": "^2.1.4",
"sass": "^1.69.4",
"swiper": "^12.1.2",
"unocss": "^0.55.1",
"unplugin-vue-components": "^0.25.1",
"vant": "^4.7.2",
@@ -8155,6 +8156,25 @@
"node": ">= 0.8.0"
}
},
"node_modules/swiper": {
"version": "12.1.2",
"resolved": "https://mirror-npm.runflare.com/swiper/-/swiper-12.1.2.tgz",
"integrity": "sha512-4gILrI3vXZqoZh71I1PALqukCFgk+gpOwe1tOvz5uE9kHtl2gTDzmYflYCwWvR4LOvCrJi6UEEU+gnuW5BtkgQ==",
"funding": [
{
"type": "patreon",
"url": "https://www.patreon.com/swiperjs"
},
{
"type": "open_collective",
"url": "http://opencollective.com/swiper"
}
],
"license": "MIT",
"engines": {
"node": ">= 4.7.0"
}
},
"node_modules/synckit": {
"version": "0.8.5",
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz",
+1
View File
@@ -26,6 +26,7 @@
"odometer": "^0.4.8",
"pinia": "^2.1.4",
"sass": "^1.69.4",
"swiper": "^12.1.2",
"unocss": "^0.55.1",
"unplugin-vue-components": "^0.25.1",
"vant": "^4.7.2",
Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 89 KiB

+1 -1
View File
@@ -7,7 +7,7 @@
<img v-if="props.item.group_type=='مس'" :src="mesImg">
<img v-if="props.item.group_type=='نقره'" :src="noghrehImg">
<!-- <img :src="props.item.img" alt="image">-->
<img v-if="props.item.group_type=='آبشده'|| props.item.group_type== null" :src="defaultImg">
<img v-if="props.item.group_type=='طلا'|| props.item.group_type=='آبشده'|| props.item.group_type== null" :src="defaultImg">
<div class="mt-3">{{props.item?.title?? '-'}}</div>
<div class="mt-1 flex gap-1"> <div class="font-bold"> {{props.item?.wallet?.gold?? '-'}} </div> {{props.item?.unit}} </div>
<!-- {{props.item?.wallet[0]?.cash}}-->
+1 -1
View File
@@ -20,7 +20,7 @@
<img v-if="info?.group_type=='سکه'" :src="sekkeImg">
<img v-if="info?.group_type=='مس'" :src="mesImg">
<img v-if="info?.group_type=='نقره'" :src="noghrehImg">
<img v-if="info?.group_type=='آبشده'|| info?.group_type== null" :src="defaultImg">
<img v-if="info?.group_type=='طلا' || info?.group_type=='آبشده' || info?.group_type== null" :src="defaultImg">
</div>
</a-spin>
<div class="mt-2 font-bold flex text-xs items-center text-gray-500">
@@ -7,7 +7,7 @@
<BasicIcon v-else-if="props.ProductItem.group_type=='سکه'" stroke-width="1" size="50" color="#0A1B37" class="rotate-310" icon="vuesax-linear:coin"/>
<BasicIcon v-else-if="props.ProductItem.group_type=='شمش'" stroke-width="1" size="50" color="#0A1B37" class="rotate-310" icon="vuesax-linear:slider-vertical"/>
<BasicIcon v-else-if="props.ProductItem.group_type=='مکعب'" stroke-width="1" size="50" color="#0A1B37" class="rotate-310" icon="vuesax-linear:keyboard"/>
<BasicIcon v-else-if="props.ProductItem.group_type=='آبشده'" stroke-width="1" size="50" color="#0A1B37" class="rotate-310" icon="vuesax-linear:slider-vertical"/>
<BasicIcon v-else-if="['آبشده','طلا'].includes(props.ProductItem.group_type)" stroke-width="1" size="50" color="#0A1B37" class="rotate-310" icon="vuesax-linear:slider-vertical"/>
<BasicIcon v-else stroke-width="1" size="50" color="#0A1B37" class="rotate-310" icon="vuesax-linear:slider-vertical"/>
</div>
<div class="flex justify-between px-2 items-center ">
@@ -16,7 +16,7 @@
<BasicIcon v-else-if="props.ProductItem.group_type=='شمش'" stroke-width="1" size="26" color="#0A1B37" class="rotate-310" icon="vuesax-linear:slider-vertical"/>
<BasicIcon v-else-if="props.ProductItem.group_type=='مکعب'" stroke-width="1" size="26" color="#0A1B37" class="rotate-310" icon="vuesax-linear:keyboard"/>
<BasicIcon v-else stroke-width="1" size="26" color="#0A1B37" class="rotate-310" icon="vuesax-linear:slider-vertical"/> <div class="flex justify-between gap-2 items-center">
<span v-if="props.ProductItem.group_type=='آبشده'" class="font-bold text-secondary">{{userWallet.walletInfo?.gold??0}}</span>
<span v-if="['آبشده','طلا'].includes(props.ProductItem.group_type)" class="font-bold text-secondary">{{userWallet.walletInfo?.gold??0}}</span>
<span v-else class="font-bold text-secondary">{{props?.ProductItem.wallet?.gold??0}}</span>
<span class="text-gray-400">{{props?.ProductItem?.unit}}</span>
</div>
+109 -39
View File
@@ -23,8 +23,8 @@
style="touch-action: none" />
</div>-->
<!-- <div class="bg-gradient-to-l mt-3 p-1 shadow-lg from-primary rounded-lg p-px to-transparent ">-->
<div class="mt-3 p-1 shadow-lg from-primary rounded-lg p-px to-transparent">
<div class=" p-2 py-6 rounded-lg bg-tertiary">
<div class="mt-3 p-1 shadow-lg from-primary rounded-lg p-px to-transparent">
<div class="p-2 py-3 rounded-lg bg-tertiary">
<div class="flex justify-between ">
<!-- <div
class="text-center w-full flex-flex-col items-center home_appear_fade_in_up"
@@ -102,7 +102,7 @@
<!-- <ADivider></ADivider>-->
<div>
<!-- <div @click="router.push({ name: 'fund' })">-->
<div class="bg-primary rounded-lg p-2 py-6 mt-2">
<div class="bg-primary rounded-lg p-2 py-3 mt-2">
<div class="flex justify-center text-tertiary w-full z-20">
محصولات:
<div class="text-l mr-2 font-bold">{{ userStore?.userInfo?.wallet?.count_product ?? 0}}عدد</div>
@@ -187,12 +187,11 @@
</div>-->
</div>
<div
class="w-full relative mt-5 border-gray-200 border-1 rounded-lg h-[88px] flex flex-col justify-center home_appear_fade_in"
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="w-full items-center flex flex-col">
<div class="text-gray-600">قیمت خرید از {{ useConfigStore().config.name_fa }}</div>
<div v-if="price" class="flex gap-1 mt-3 text-xs items-center">
<div class="w-full items-center flex justify-center">
<div class="text-gray-600 text-[12px] ml-2">قیمت خرید از {{ useConfigStore().config.name_fa }}</div>
<div v-if="price" class="flex gap-1 text-xs items-center">
<!-- <div id="odometer" class="font-bold text-2xl odometer animated fadeIn">
&lt;!&ndash; {{ usePriceWithoutType(useCounterAnim(price.price_last) ?? 0) }}&ndash;&gt;
</div>-->
@@ -208,7 +207,7 @@
</div>
<Vue3Odometer
:value="goldVal"
class="o-text !text-2xl font-bold"
class="o-text !text-xl font-bold"
style="font-family: iran-sans; direction: ltr"
v-if="key % 2 === 0"
/>
@@ -216,9 +215,35 @@
</div>
</div>
</div>
<div class="flex mt-5 gap-1 bg-primary text-tertiary cursor-pointer font-bold justify-center rounded-lg py-5"
<!-- <div class="flex mt-5 gap-1 bg-primary text-tertiary cursor-pointer font-bold justify-center rounded-lg py-5"
@click="router.push({ name: 'buyProduct' })">
<div>خرید و فروش</div>
</div>-->
<div
v-if="config.config.name === 'garoosi' && banners?.length > 0"
class="w-full relative mt-5 flex flex-col justify-center home_appear_fade_in overflow-hidden rounded-lg"
>
<div class="w-full aspect-[2.8/1] min-h-[10rem] max-h-[14rem]">
<swiper
:modules="bannerSwiperModules"
:slides-per-view="1"
:space-between="6"
:autoplay="{ delay: 4000, disableOnInteraction: false }"
:pagination="{ clickable: true }"
class="banner-swiper h-full w-full rounded-lg"
>
<swiper-slide v-for="(banner, index) in banners" :key="index" class="!h-full">
<img
v-if="banner.show"
class="banner-img w-full h-full object-cover rounded-lg block"
:src="banner.icon"
:alt="banner.caption || ''"
@click="handleBannerClick(banner)"
:style="banner.link ? 'cursor: pointer;' : ''"
/>
</swiper-slide>
</swiper>
</div>
</div>
@@ -439,25 +464,20 @@
<script lang="tsx" setup>
import img from '@/assets/img/home_ly1.svg'
import img1 from '@/assets/img/home_ly2.svg'
import Icon from '@/components/BasicPacks/BasicIcon.vue'
import { usePrice, usePriceWithoutType, useTowDecimal } from '../../../utils/usePrice'
import {computed, createVNode, onMounted, onUnmounted, ref, watch, watchEffect} from 'vue'
import {computed, onMounted, onUnmounted, ref, watch} from 'vue'
import router from '@/router'
import * as echarts from 'echarts'
import { useIntersectionObserver, useWindowScroll } from '@vueuse/core'
import { loading, removeToken } from '@/utils'
import { useUserStore } from '@/store/userStore'
import { useWalletStore } from '@/store/walletStore'
import DialogVant from '@/components/DialogVant.vue'
import type { Price } from '@/models/home'
import { getAllPrices, getChart, getLastPrice, getPrice } from '@/api/home'
// import odometer from 'odometer/odometer'
import { getAllPrices, getChart } from '@/api/home'
import Vue3Odometer from 'vue3-odometer'
import 'odometer/themes/odometer-theme-default.css'
import HomeSkeleton from '@/views/base/home/components/HomeSkeleton.vue'
import { toJalaliNotime } from '@/utils/useDateTime'
import {
getLastPrices,
getUpPrice,
@@ -470,34 +490,27 @@ import {
import { DotLottieVue } from '@lottiefiles/dotlottie-vue'
import BasicActionSheet from '@/components/BasicPacks/BasicActionSheet.vue'
import BasicInfoItem from '@/components/BasicPacks/BasicInfoItem.vue'
import MySlider from '@/views/base/wallet/components/mySlider/MySlider.vue'
import type { Card, PWallet, Wallet } from '@/models/wallet'
import { getActivatedCards } from '@/api/wallet'
import banner1 from '@/assets/img/banner/banner1.png'
import banner2 from '@/assets/img/banner/banner2.png'
import image from '@/assets/img/image.svg'
import BasicButton from '@/components/BasicPacks/BasicButton.vue'
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
import ProductInventory from '@/views/base/home/components/ProductInventory.vue'
import type { PInventory, Product } from '@/models/products'
import { getProductInventory } from '@/api/products'
import type { PInventory } from '@/models/products'
import type { Fund } from '@/models/fund'
import type { TableOutput } from '@/models/table'
import { useFundUserStore } from '@/store/fundStore'
import {Modal} from "ant-design-vue";
import {ExclamationCircleOutlined} from "@ant-design/icons-vue";
import {message} from "ant-design-vue/es";
import {useConfigStore} from "@/store/configStore";
import { Swiper, SwiperSlide } from 'swiper/vue'
import { Autoplay, Pagination } from 'swiper/modules'
import 'swiper/swiper-bundle.css'
import 'swiper/css/pagination'
import { getConstByKey } from '@/api/const'
const bannerSwiperModules = [Autoplay, Pagination]
const nameProject = useConfigStore().config.name
const userStore = useUserStore()
const walletStore = useWalletStore()
const chartLoading = ref<boolean>(false)
const wholeBalanceVisible = ref<boolean>(false)
const { x, y } = useWindowScroll()
const series = ref<Chart[]>([{ data: [] }])
const key = ref<number>(0)
const cardItems = [banner1, banner2]
const main = ref(null)
const logoutLoading = ref(false)
const productInventorys = ref<PInventory[] | undefined>()
@@ -520,16 +533,53 @@ async function initTable() {
}
}
const banners = ref<Array<{ icon: string; show: boolean; caption: string | null; link: string | null }>>([])
async function initBanners() {
try {
const response = await getConstByKey('header_banner')
if (response == null) return
let list: Array<{ icon: string; show: boolean; caption: string | null; link: string | null }> = []
// پاسخ مستقیم به‌صورت رشتهٔ JSON
if (typeof response === 'string') {
list = JSON.parse(response)
}
// پاسخ شیء با فیلد value (یک Const تکی)
else if (typeof response === 'object' && !Array.isArray(response) && 'value' in response) {
const val = (response as { value: string | unknown }).value
list = typeof val === 'string' ? JSON.parse(val) : (val as typeof list)
}
// پاسخ آرایهٔ Const با فیلد value
else if (Array.isArray(response) && response.length > 0) {
const first = response[0] as { value?: string | unknown; icon?: string }
if (first.value !== undefined && first.value !== null) {
list = typeof first.value === 'string' ? JSON.parse(first.value) : (first.value as typeof list)
} else if (first.icon !== undefined) {
list = response as typeof list
}
}
banners.value = Array.isArray(list) ? list.filter((banner: any) => banner.show === true) : []
} catch (e) {
console.log('Error loading banners:', e)
}
}
function handleBannerClick(banner: { icon: string; show: boolean; caption: string | null; link: string | null }) {
if (!banner.link) return
if (banner.link.startsWith('http://') || banner.link.startsWith('https://')) {
window.open(banner.link, '_blank')
} else {
router.push(banner.link)
}
}
onUnmounted(() => {
stopPriceInterval()
})
/*
onMounted(() => {
animateNum(2)
})
*/
const goldVal = ref<number | undefined>()
function openWholeBalanceVisible() {
@@ -834,6 +884,7 @@ onMounted(async () => {
// await initChart()
await initPrice()
await fetchPrices()
await initBanners()
update()
} catch (e) {
console.log(e)
@@ -844,7 +895,6 @@ onMounted(async () => {
onUnmounted(() => {
stopPriceInterval()
// console.log('okhome')
})
@@ -912,6 +962,26 @@ apexchart {
padding: 0 !important;
margin: 0 !important;
}
/* بنر اسلایدر — ظاهر و نقطه‌های pagination مشابه نمونه */
.banner-swiper {
--swiper-pagination-bottom: 0.75rem;
.swiper-slide {
display: flex;
align-items: center;
justify-content: center;
}
.swiper-pagination-bullet {
width: 0.5rem;
height: 0.5rem;
background: rgba(0, 0, 0, 0.35);
opacity: 1;
transition: background 0.2s;
}
.swiper-pagination-bullet-active {
background: #fff;
}
}
</style>
<script setup lang="ts">
@@ -8,7 +8,7 @@
<img v-if="props.ProductItem.group_type=='مس'" :src="mesImg">
<img v-if="props.ProductItem.group_type=='نقره'" :src="noghrehImg">
<img v-if="props.ProductItem.group_type=='سکه پارسیان'" :src="parsianImg">
<img v-if="props.ProductItem.group_type=='آبشده'|| props.ProductItem.group_type== null" :src="defaultImg">
<img v-if="props.ProductItem.group_type=='طلا'|| props.ProductItem.group_type=='آبشده'|| props.ProductItem.group_type== null" :src="defaultImg">
</div>
<div class="m-1 flex flex-col gap-1 justify-center">
<div class="text-base font-500">{{ props.ProductItem.title }}</div>
+2 -2
View File
@@ -27,7 +27,7 @@
<BasicActionSheet placement="bottom" v-model:show="showFilter">
<div class="">
<indexGold v-if="['آبشده'].includes(selectedProduct?.group_type)"
<indexGold v-if="['آبشده', 'طلا'].includes(selectedProduct?.group_type)"
:ProductItem="selectedProduct"
>
</indexGold>
@@ -81,7 +81,7 @@ import DealProduct from '@/views/base/products/components/DealProduct.vue'
import { cloneDeep } from 'lodash'
import img from "@/assets/img/user_ly1.svg";
import BasicModal from "@/components/BasicPacks/BasicModal.vue";
const activekey = ref('آبشده')
const activekey = ref('طلا' ||'آبشده')
const showFilter = ref()
const selectedProduct = ref<Product>()
const closeTime = ref<string>('')
+5
View File
@@ -4626,6 +4626,11 @@ svg.select.js@^3.0.1:
dependencies:
svg.js "^2.6.5"
swiper@^12.1.2:
version "12.1.2"
resolved "https://mirror-npm.runflare.com/swiper/-/swiper-12.1.2.tgz"
integrity sha512-4gILrI3vXZqoZh71I1PALqukCFgk+gpOwe1tOvz5uE9kHtl2gTDzmYflYCwWvR4LOvCrJi6UEEU+gnuW5BtkgQ==
synckit@^0.8.5:
version "0.8.5"
resolved "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz"