Merge remote-tracking branch 'origin/modular' into modular
This commit is contained in:
Vendored
+5
@@ -14,12 +14,16 @@ declare module 'vue' {
|
|||||||
AInput: typeof import('ant-design-vue/es')['Input']
|
AInput: typeof import('ant-design-vue/es')['Input']
|
||||||
AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
|
AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
|
||||||
AModal: typeof import('ant-design-vue/es')['Modal']
|
AModal: typeof import('ant-design-vue/es')['Modal']
|
||||||
|
ARadio: typeof import('ant-design-vue/es')['Radio']
|
||||||
ARadioButton: typeof import('ant-design-vue/es')['RadioButton']
|
ARadioButton: typeof import('ant-design-vue/es')['RadioButton']
|
||||||
ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup']
|
ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup']
|
||||||
ASelect: typeof import('ant-design-vue/es')['Select']
|
ASelect: typeof import('ant-design-vue/es')['Select']
|
||||||
ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
|
ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
|
||||||
ASlider: typeof import('ant-design-vue/es')['Slider']
|
ASlider: typeof import('ant-design-vue/es')['Slider']
|
||||||
ASpin: typeof import('ant-design-vue/es')['Spin']
|
ASpin: typeof import('ant-design-vue/es')['Spin']
|
||||||
|
ASwitch: typeof import('ant-design-vue/es')['Switch']
|
||||||
|
ATag: typeof import('ant-design-vue/es')['Tag']
|
||||||
|
ATextarea: typeof import('ant-design-vue/es')['Textarea']
|
||||||
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
|
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
|
||||||
BaseDatePicker: typeof import('./src/components/BasicPacks/BaseDatePicker/src/BaseDatePicker.vue')['default']
|
BaseDatePicker: typeof import('./src/components/BasicPacks/BaseDatePicker/src/BaseDatePicker.vue')['default']
|
||||||
Basic: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/basic.vue')['default']
|
Basic: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/basic.vue')['default']
|
||||||
@@ -63,5 +67,6 @@ declare module 'vue' {
|
|||||||
TransactionSkeleton: typeof import('./src/components/TransactionSkeleton.vue')['default']
|
TransactionSkeleton: typeof import('./src/components/TransactionSkeleton.vue')['default']
|
||||||
VanActionSheet: typeof import('vant/es')['ActionSheet']
|
VanActionSheet: typeof import('vant/es')['ActionSheet']
|
||||||
VanDialog: typeof import('vant/es')['Dialog']
|
VanDialog: typeof import('vant/es')['Dialog']
|
||||||
|
VanField: typeof import('vant/es')['Field']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="text-center w-full flex-flex-col items-center home_appear_fade_in_up"
|
||||||
|
style="text-align: -webkit-center"
|
||||||
|
@click="$emit('click')"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="rounded-full w-[50px] h-[50px] bg-primary flex items-center justify-center"
|
||||||
|
style="border: solid 1px #0A1B37"
|
||||||
|
>
|
||||||
|
<Icon class="text-tertiary" :icon="action.icon" />
|
||||||
|
</div>
|
||||||
|
<div class="text-primary">{{ action.label }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import Icon from '@/components/BasicPacks/BasicIcon.vue'
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
action: {
|
||||||
|
name: string
|
||||||
|
icon: string
|
||||||
|
label: string
|
||||||
|
handler: () => void
|
||||||
|
}
|
||||||
|
}>()
|
||||||
|
|
||||||
|
defineEmits<{
|
||||||
|
click: []
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="y > 50 ? 'bg-white shadow opacity-95' : ''"
|
||||||
|
class="px-4 py-2 top-0 z-5 wrap-fix background-color-transition"
|
||||||
|
>
|
||||||
|
<div class="flex justify-between my-2">
|
||||||
|
<div class="flex items-center gap-2" @click.stop="router.push({ name: 'user' })">
|
||||||
|
<div
|
||||||
|
class="z-100000 rounded-full border-1"
|
||||||
|
:class="y > 50 ? 'text-primary border-primary' : 'text-primary border-primary'"
|
||||||
|
>
|
||||||
|
<Icon icon="vuesax-linear:user" size="20" />
|
||||||
|
</div>
|
||||||
|
<div :class="y > 50 ? 'text-primary' : 'text-primary'" class="color-transition font-bold">
|
||||||
|
{{ userStore.userInfo?.name }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Icon
|
||||||
|
:color="y > 50 ? '#0A1B37' : '#0A1B37'"
|
||||||
|
class="color-transition"
|
||||||
|
icon="vuesax-linear:logout"
|
||||||
|
@click="$emit('logout')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useWindowScroll } from '@vueuse/core'
|
||||||
|
import { useUserStore } from '@/store/userStore'
|
||||||
|
import router from '@/router'
|
||||||
|
import Icon from '@/components/BasicPacks/BasicIcon.vue'
|
||||||
|
|
||||||
|
const { y } = useWindowScroll()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
defineEmits<{
|
||||||
|
logout: []
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex gap-1">
|
||||||
|
<div>{{ label }}</div>
|
||||||
|
<div class="font-bold">{{ value }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
defineProps<{
|
||||||
|
label: string
|
||||||
|
value: string
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<template>
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
<div class="flex mt-2 justify-center text-tertiary w-full z-20">
|
||||||
|
دارایی طلایی:
|
||||||
|
<div class="text-l mr-2 font-bold">{{ (userStore?.userInfo?.wallet?.count_gold ?? 0).toFixed(3) }} گرم</div>
|
||||||
|
<BasicIcon @click="$emit('openGoldModal')" icon="vuesax-linear:info-circle" size="18" color="tertiary" />
|
||||||
|
</div>
|
||||||
|
<div class="flex mt-2 justify-center text-tertiary w-full z-20">
|
||||||
|
دارایی ریالی:
|
||||||
|
<div class="text-l mr-2 font-bold">{{ usePriceWithoutType(userStore?.userInfo?.wallet?.count_cash ?? 0) }} ریال</div>
|
||||||
|
<BasicIcon @click="$emit('openRialModal')" icon="vuesax-linear:info-circle" size="18" color="tertiary" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useUserStore } from '@/store/userStore'
|
||||||
|
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
|
||||||
|
import {usePriceWithoutType} from "../../../../utils/usePrice";
|
||||||
|
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
defineEmits<{
|
||||||
|
openGoldModal: []
|
||||||
|
openRialModal: []
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
<template>
|
||||||
|
<BasicActionSheet v-model:show="visible">
|
||||||
|
<div class="pb-20">
|
||||||
|
<div>
|
||||||
|
<div class="w-full text-center font-bold">موجودی ها</div>
|
||||||
|
<div class="h-30vh overflow-y-auto">
|
||||||
|
<div class="mt-5">
|
||||||
|
<BasicInfoItem
|
||||||
|
class="flex justify-between"
|
||||||
|
title="مجموعه کل دارایی طلایی"
|
||||||
|
is-line
|
||||||
|
:info="`در این قسمت مجموع کل وزن طلای آبشده (${(walletInfo?.gold ?? 0)?.toFixed(3)}) + ارزش دارایی ها (${(walletInfo?.treasure_sum ?? 0)?.toFixed(3)} ) را مشاهده میفرمایید.`"
|
||||||
|
infoHead="مجموع کل دارایی طلایی"
|
||||||
|
:value="parseFloat((walletInfo?.gold_sum ?? 0).toFixed(2)) + ' گرم'"
|
||||||
|
/>
|
||||||
|
<a-divider class="!my-1" />
|
||||||
|
<BasicInfoItem
|
||||||
|
class="flex justify-between"
|
||||||
|
title="دارایی کیف پول ریالی"
|
||||||
|
is-line
|
||||||
|
:value="usePrice(walletInfo?.cash ?? 0)"
|
||||||
|
/>
|
||||||
|
<a-divider class="!my-1" />
|
||||||
|
<BasicInfoItem
|
||||||
|
class="flex justify-between"
|
||||||
|
title="دارایی کیف پول طلایی"
|
||||||
|
is-line
|
||||||
|
:value="(walletInfo?.count_gold_wallet ?? 0) + ' گرم'"
|
||||||
|
/>
|
||||||
|
<a-divider v-if="nameProject === 'goldsun'" class="!my-1" />
|
||||||
|
<BasicInfoItem
|
||||||
|
v-if="nameProject === 'goldsun'"
|
||||||
|
class="flex justify-between"
|
||||||
|
title="دارایی کیف پول نقره"
|
||||||
|
is-line
|
||||||
|
:value="(walletInfo?.count_silver ?? 0) + ' گرم'"
|
||||||
|
/>
|
||||||
|
<a-divider v-if="nameProject === 'goldsun'" class="!my-1" />
|
||||||
|
<BasicInfoItem
|
||||||
|
v-if="nameProject === 'goldsun'"
|
||||||
|
class="flex justify-between"
|
||||||
|
title="دارایی کیف پول مس"
|
||||||
|
is-line
|
||||||
|
:value="(walletInfo?.count_copper ?? 0) + ' گرم'"
|
||||||
|
/>
|
||||||
|
<a-divider class="!my-1" />
|
||||||
|
<BasicInfoItem
|
||||||
|
class="flex justify-between"
|
||||||
|
title="معادل ریالی مجموع دارایی ها"
|
||||||
|
is-line
|
||||||
|
:value="usePrice(walletInfo?.count_cash_total ?? 0)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</BasicActionSheet>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import BasicActionSheet from '@/components/BasicPacks/BasicActionSheet.vue'
|
||||||
|
import BasicInfoItem from '@/components/BasicPacks/BasicInfoItem.vue'
|
||||||
|
import { useConfigStore } from '@/store/configStore'
|
||||||
|
import {usePrice} from "@/utils/usePrice";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
show: boolean
|
||||||
|
walletInfo: any
|
||||||
|
goldVal: number
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
'update:show': [value: boolean]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const config = useConfigStore()
|
||||||
|
const nameProject = config.config.name
|
||||||
|
|
||||||
|
const visible = computed({
|
||||||
|
get: () => props.show,
|
||||||
|
set: (val) => emit('update:show', val)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
<template>
|
||||||
|
<div class="w-full relative mt-2 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 || ''"
|
||||||
|
:style="(banner.link || banner.typeClick === 'share') ? 'cursor: pointer;' : ''"
|
||||||
|
@click="$emit('bannerClick', banner)"
|
||||||
|
/>
|
||||||
|
</swiper-slide>
|
||||||
|
</swiper>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Swiper, SwiperSlide } from 'swiper/vue'
|
||||||
|
import { Autoplay, Pagination } from 'swiper/modules'
|
||||||
|
import 'swiper/swiper-bundle.css'
|
||||||
|
import 'swiper/css/pagination'
|
||||||
|
|
||||||
|
const bannerSwiperModules = [Autoplay, Pagination]
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
banners: any[]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
defineEmits<{
|
||||||
|
bannerClick: [banner: any]
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.banner-swiper {
|
||||||
|
--swiper-pagination-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.banner-swiper .swiper-pagination-bullet {
|
||||||
|
width: 0.5rem;
|
||||||
|
height: 0.5rem;
|
||||||
|
background: rgba(0, 0, 0, 0.35);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.banner-swiper .swiper-pagination-bullet-active {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
<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="w-full items-center flex justify-center">
|
||||||
|
<div class="text-gray-600 text-[12px] ml-2">قیمت خرید از {{ config.config.name_fa }}</div>
|
||||||
|
<div v-if="price" class="flex gap-1 text-xs items-center">
|
||||||
|
<div
|
||||||
|
v-if="percentPriceChanged() !== 0"
|
||||||
|
class="flex flex-row-reverse rounded-lg justify-between items-center gap-2 px-3"
|
||||||
|
:class="percentPriceChanged() >= 0 ? 'bg-green-100 text-green-600' : 'bg-red-100 text-red-600'"
|
||||||
|
>
|
||||||
|
<div dir="ltr">{{ useTowDecimal(percentPriceChanged()) }}%</div>
|
||||||
|
<div>
|
||||||
|
<Icon
|
||||||
|
:size="15"
|
||||||
|
:icon="percentPriceChanged() < 0 ? 'vuesax-linear:received' : 'vuesax-linear:send'"
|
||||||
|
:color="percentPriceChanged() < 0 ? 'red-600' : 'green-600'"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Vue3Odometer
|
||||||
|
:value="displayGoldVal"
|
||||||
|
class="o-text !text-xl font-bold"
|
||||||
|
style="font-family: iran-sans; direction: ltr"
|
||||||
|
/>
|
||||||
|
ریال
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import Icon from '@/components/BasicPacks/BasicIcon.vue'
|
||||||
|
import { useConfigStore } from '@/store/configStore'
|
||||||
|
import {lastPrice, percentPriceChanged, price} from '@/utils/price'
|
||||||
|
import Vue3Odometer from 'vue3-odometer'
|
||||||
|
import 'odometer/themes/odometer-theme-default.css'
|
||||||
|
import {useTowDecimal} from "../../../../utils/usePrice";
|
||||||
|
import {computed} from "vue";
|
||||||
|
|
||||||
|
|
||||||
|
const config = useConfigStore()
|
||||||
|
|
||||||
|
// محاسبه درصد تغییرات به صورت reactive
|
||||||
|
const percentChange = computed(() => {
|
||||||
|
if (! lastPrice?.value?.price_last) return 0
|
||||||
|
return percentPriceChanged()
|
||||||
|
})
|
||||||
|
|
||||||
|
// بررسی بسته بودن بازار
|
||||||
|
const isMarketClosed = computed(() => {
|
||||||
|
return lastPrice?.value?.goldir === 0 && lastPrice?.value?.price_last === 0
|
||||||
|
})
|
||||||
|
|
||||||
|
// مقدار نمایشی برای ادومتر (با مدیریت خطا)
|
||||||
|
const displayGoldVal = computed(() => {
|
||||||
|
if (isMarketClosed.value) return 0
|
||||||
|
return lastPrice?.value?.price_last|| 0
|
||||||
|
})
|
||||||
|
|
||||||
|
// انیمیشن fade-in
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
<template>
|
||||||
|
<DialogVant v-model:show="visible" :closeOnClickOverlay="false" :timeOut="-1">
|
||||||
|
<template v-slot:title>
|
||||||
|
<div class="flex flex-col items-center">
|
||||||
|
<DotLottieVue
|
||||||
|
class="w-11rem h-11rem"
|
||||||
|
autoplay
|
||||||
|
src="https://lottie.host/129cf3a1-df84-4758-a856-58c133ae6013/R5RqclXA8G.json"
|
||||||
|
/>
|
||||||
|
<div class="p-5 text-sm">آیا از خروج خود اطمینان دارید؟</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:button>
|
||||||
|
<div class="text-center text-red flex items-center justify-center" @click="$emit('confirm')">
|
||||||
|
<a-spin :spinning="false" size="small">خروج</a-spin>
|
||||||
|
</div>
|
||||||
|
<div class="text-center" @click="visible = false">انصراف</div>
|
||||||
|
</template>
|
||||||
|
</DialogVant>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import DialogVant from '@/components/DialogVant.vue'
|
||||||
|
import { DotLottieVue } from '@lottiefiles/dotlottie-vue'
|
||||||
|
import {computed} from "vue";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
show: boolean
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
'update:show': [value: boolean]
|
||||||
|
confirm: []
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const visible = computed({
|
||||||
|
get: () => props.show,
|
||||||
|
set: (val) => emit('update:show', val)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-if="message"
|
||||||
|
class="bg-red-50 text-red-600 text-center font-500 py-4 px-2 mt-2 rounded-lg"
|
||||||
|
v-html="message"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
defineProps<{
|
||||||
|
message: string
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
<template>
|
||||||
|
<div class="bg-gray-50 w-full flex justify-evenly rounded-lg py-4 mt-4 h-[15rem]">
|
||||||
|
<div class="w-full text-center pt-2">
|
||||||
|
<a-radio-group v-model:value="selectedValue" button-style="solid">
|
||||||
|
<a-radio-button value="DAILY" @click="handleUpdate('DAILY')">24 ساعت اخیر</a-radio-button>
|
||||||
|
<a-radio-button value="WEEKLY" @click="handleUpdate('WEEKLY')">هفته گذشته</a-radio-button>
|
||||||
|
<a-radio-button value="MONTHLY" @click="handleUpdate('MONTHLY')">ماه گذشته</a-radio-button>
|
||||||
|
</a-radio-group>
|
||||||
|
<div id="chart-timeline">
|
||||||
|
<apexchart
|
||||||
|
type="area"
|
||||||
|
height="200"
|
||||||
|
:options="chartOptions"
|
||||||
|
:series="props.priceChartSeries"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed } from 'vue'
|
||||||
|
import { useConfigStore } from '@/store/configStore'
|
||||||
|
import {usePrice} from "@/utils/usePrice";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
priceChartSeries: any[]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
updateData: [timeline: string]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const config = useConfigStore()
|
||||||
|
const selectedValue = ref('DAILY')
|
||||||
|
|
||||||
|
const toPersianNumber = (num: number) => {
|
||||||
|
return num.toString().replace(/\d/g, (d) => '۰۱۲۳۴۵۶۷۸۹'[d])
|
||||||
|
}
|
||||||
|
|
||||||
|
const chartOptions = computed(() => ({
|
||||||
|
chart: {
|
||||||
|
id: 'نوسانات قیمت',
|
||||||
|
type: 'area',
|
||||||
|
height: 200,
|
||||||
|
toolbar: { show: false },
|
||||||
|
zoom: { autoScaleYaxis: true }
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
padding: { top: 0, right: 0, bottom: 0, left: 0 },
|
||||||
|
yaxis: { lines: { show: false } }
|
||||||
|
},
|
||||||
|
stroke: { width: 2 },
|
||||||
|
dataLabels: {
|
||||||
|
enabled: false,
|
||||||
|
// formatter: (val) => toPersianNumber(val),
|
||||||
|
style: {
|
||||||
|
fontFamily: '\'IranSans\', sans-serif'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yaxis: {
|
||||||
|
tickAmount: 8,
|
||||||
|
show: false,
|
||||||
|
labels: {
|
||||||
|
formatter: (val: number) => toPersianNumber(usePrice(val)),
|
||||||
|
style: { fontSize: '13px', fontFamily: "'IranSans', sans-serif" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xaxis: {
|
||||||
|
show: false,
|
||||||
|
type: 'datetime',
|
||||||
|
axisTicks: { show: false },
|
||||||
|
tooltip: { enabled: false },
|
||||||
|
labels: { show: false }
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
x: {
|
||||||
|
formatter: (value: number) => {
|
||||||
|
const date = new Date(value)
|
||||||
|
return `${date.toLocaleDateString('fa-IR')} - ${date.toLocaleTimeString('fa-IR', {
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit'
|
||||||
|
})}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
formatter: (val: number) => toPersianNumber(usePrice(val)),
|
||||||
|
style: { fontFamily: "'IranSans', sans-serif" }
|
||||||
|
},
|
||||||
|
colors: [config.config.color],
|
||||||
|
fill: {
|
||||||
|
type: 'gradient',
|
||||||
|
gradient: {
|
||||||
|
shadeIntensity: 1,
|
||||||
|
opacityFrom: 0.7,
|
||||||
|
opacityTo: 0.9,
|
||||||
|
stops: [0, 100]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
const handleUpdate = (timeline: string) => {
|
||||||
|
emit('updateData', timeline)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<template>
|
||||||
|
<div class="w-full flex justify-evenly rounded-lg py-2 home_appear_fade_in mt-6 pb-45">
|
||||||
|
<div class="text-center w-full">
|
||||||
|
<div>بالاترین قیمت امروز</div>
|
||||||
|
<div v-if="price" class="flex flex-col items-center mt-3">
|
||||||
|
<div class="text-green-600 flex gap-1 text-xs items-center">
|
||||||
|
<div class="text-base">{{ usePriceWithoutType(price?.buy ?? 0) }}</div>
|
||||||
|
ریال
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a-divider class="bg-gray-200 !h-auto !my-2" type="vertical" />
|
||||||
|
<div class="text-center w-full">
|
||||||
|
<div>پایین ترین قیمت امروز</div>
|
||||||
|
<div class="flex w-full mt-3 justify-evenly">
|
||||||
|
<div v-if="price" class="flex flex-col items-center">
|
||||||
|
<div class="text-red-600 flex gap-1 text-xs items-center">
|
||||||
|
<div class="text-base">{{ usePriceWithoutType(price?.sell ?? 0) }}</div>
|
||||||
|
ریال
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import {usePriceWithoutType} from "../../../../utils/usePrice";
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
price: any
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
<template>
|
||||||
|
<div class="bg-primary rounded-lg p-2 py-1 mt-2">
|
||||||
|
<div class="p-2">
|
||||||
|
<div
|
||||||
|
v-if="userStore.userInfo?.referral_id"
|
||||||
|
class="text-tertiary font-bold w-full text-center flex justify-between gap-2 items-center"
|
||||||
|
>
|
||||||
|
کد معرف:
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<div class="mr-2">{{ userStore.userInfo?.referral_id }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="w-full flex gap-2 mt-1">
|
||||||
|
<BasicButton
|
||||||
|
icon="vuesax-linear:share"
|
||||||
|
size="small"
|
||||||
|
class="flex-1 !bg-primary !border-tertiary !text-tertiary"
|
||||||
|
icon-size="18"
|
||||||
|
@click.stop="shareCode"
|
||||||
|
>
|
||||||
|
دعوت از دوستان
|
||||||
|
</BasicButton>
|
||||||
|
<BasicButton
|
||||||
|
icon="vuesax-linear:copy"
|
||||||
|
size="small"
|
||||||
|
class="flex-1 !bg-primary !border-tertiary !text-tertiary"
|
||||||
|
icon-size="18"
|
||||||
|
@click.stop="copyCode"
|
||||||
|
>
|
||||||
|
رونوشت
|
||||||
|
</BasicButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useUserStore } from '@/store/userStore'
|
||||||
|
import BasicButton from '@/components/BasicPacks/BasicButton.vue'
|
||||||
|
import { message } from 'ant-design-vue'
|
||||||
|
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
const shareCode = () => {
|
||||||
|
const text = `⭐️الان طلا بخر؛ آخر ماه سودشو ببر!
|
||||||
|
با خرید طلای آبشده در مقدارهای کم ولی مستمر، سود بیشتری ببر!
|
||||||
|
|
||||||
|
این دعوت نامه از طرف "${userStore.userInfo?.name}" عزیز برای شما ارسال شده تا شما هم از بازار طلا جا نمونید.
|
||||||
|
روی لینک زیر کلیک کنید و ثبت نام خود را تکمیل کنید.
|
||||||
|
|
||||||
|
${window.location.hostname}/login?id=${userStore.userInfo?.referral_id}`
|
||||||
|
|
||||||
|
navigator.share({ title: 'طلا', text })
|
||||||
|
}
|
||||||
|
|
||||||
|
const copyCode = () => {
|
||||||
|
const code = userStore.userInfo?.referral_id
|
||||||
|
if (code) {
|
||||||
|
navigator.clipboard.writeText(code)
|
||||||
|
message.info('کد رونوشت شد')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
<template>
|
||||||
|
<BasicModal v-model:visible="visible" :custom-footer="true">
|
||||||
|
<div class="p-2">
|
||||||
|
<div
|
||||||
|
v-if="referralId"
|
||||||
|
class="text-primary font-bold w-full text-center flex justify-between gap-2 items-center"
|
||||||
|
>
|
||||||
|
کد معرف:
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<div class="mr-2">{{ referralId }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="w-full flex gap-2 mt-1">
|
||||||
|
<BasicButton
|
||||||
|
icon="vuesax-linear:share"
|
||||||
|
size="small"
|
||||||
|
class="flex-1"
|
||||||
|
icon-size="18"
|
||||||
|
@click.stop="shareCode"
|
||||||
|
>
|
||||||
|
اشتراک گذاری
|
||||||
|
</BasicButton>
|
||||||
|
<BasicButton
|
||||||
|
icon="vuesax-linear:copy"
|
||||||
|
size="small"
|
||||||
|
class="flex-1"
|
||||||
|
icon-size="18"
|
||||||
|
@click.stop="copyCode"
|
||||||
|
>
|
||||||
|
رونوشت
|
||||||
|
</BasicButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</BasicModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import BasicModal from '@/components/BasicPacks/BasicModal.vue'
|
||||||
|
import BasicButton from '@/components/BasicPacks/BasicButton.vue'
|
||||||
|
import { message } from 'ant-design-vue'
|
||||||
|
import { useUserStore } from '@/store/userStore'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
show: boolean
|
||||||
|
referralId: string
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
'update:show': [value: boolean]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
const visible = computed({
|
||||||
|
get: () => props.show,
|
||||||
|
set: (val) => emit('update:show', val)
|
||||||
|
})
|
||||||
|
|
||||||
|
const shareCode = () => {
|
||||||
|
const text = `⭐️الان طلا بخر؛ آخر ماه سودشو ببر!
|
||||||
|
با خرید طلای آبشده در مقدارهای کم ولی مستمر، سود بیشتری ببر!
|
||||||
|
|
||||||
|
این دعوت نامه از طرف "${userStore.userInfo?.name}" عزیز برای شما ارسال شده تا شما هم از بازار طلا جا نمونید.
|
||||||
|
روی لینک زیر کلیک کنید و ثبت نام خود را تکمیل کنید.
|
||||||
|
|
||||||
|
${window.location.hostname}/login?id=${props.referralId}`
|
||||||
|
|
||||||
|
navigator.share({ title: 'طلا', text })
|
||||||
|
}
|
||||||
|
|
||||||
|
const copyCode = () => {
|
||||||
|
if (props.referralId) {
|
||||||
|
navigator.clipboard.writeText(props.referralId)
|
||||||
|
message.info('کد رونوشت شد')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { ref } from 'vue'
|
||||||
|
import { getConstByKey } from '@/api/const'
|
||||||
|
|
||||||
|
export function useBanners() {
|
||||||
|
const banners = ref([])
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
const loadBanners = async () => {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
const response = await getConstByKey('header_banner')
|
||||||
|
if (!response) return
|
||||||
|
|
||||||
|
let list = []
|
||||||
|
if (typeof response === 'string') {
|
||||||
|
list = JSON.parse(response)
|
||||||
|
} else if (typeof response === 'object' && !Array.isArray(response) && 'value' in response) {
|
||||||
|
list = typeof response.value === 'string' ? JSON.parse(response.value) : response.value
|
||||||
|
} else if (Array.isArray(response) && response.length > 0) {
|
||||||
|
const first = response[0]
|
||||||
|
if (first.value) {
|
||||||
|
list = typeof first.value === 'string' ? JSON.parse(first.value) : first.value
|
||||||
|
} else if (first.icon) {
|
||||||
|
list = response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
banners.value = Array.isArray(list) ? list.filter(b => b.show === true) : []
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading banners:', error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { banners, loading, loadBanners }
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { ref } from 'vue'
|
||||||
|
import { getAllPrices } from '@/api/home'
|
||||||
|
|
||||||
|
export function usePriceChart() {
|
||||||
|
const priceChartSeries = ref([{ name: 'قیمت', data: [] }])
|
||||||
|
const selection = ref('DAILY')
|
||||||
|
const chartLoading = ref(false)
|
||||||
|
|
||||||
|
const toPersianNumber = (num: number) => {
|
||||||
|
return num.toString().replace(/\d/g, (d) => '۰۱۲۳۴۵۶۷۸۹'[d])
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchPrices = async () => {
|
||||||
|
try {
|
||||||
|
chartLoading.value = true
|
||||||
|
const response = await getAllPrices()
|
||||||
|
const data = response.data?.prices
|
||||||
|
const selectedData = data[selection.value] || []
|
||||||
|
priceChartSeries.value[0].data = selectedData.map((item: any) => [item.timestamp, item.value - 1])
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching prices:', error)
|
||||||
|
} finally {
|
||||||
|
chartLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateChartData = (timeline: string) => {
|
||||||
|
selection.value = timeline
|
||||||
|
fetchPrices()
|
||||||
|
}
|
||||||
|
|
||||||
|
return { priceChartSeries, chartLoading, updateChartData, fetchPrices }
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { ref } from 'vue'
|
||||||
|
import { useFundUserStore } from '@/store/fundStore'
|
||||||
|
|
||||||
|
export function useUserFunds() {
|
||||||
|
const funds = ref([])
|
||||||
|
const loading = ref(false)
|
||||||
|
const fundStore = useFundUserStore()
|
||||||
|
|
||||||
|
const loadFunds = async () => {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
const result = await fundStore.getMyFund()
|
||||||
|
funds.value = result?.rows || []
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading funds:', error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { funds, loading, loadFunds }
|
||||||
|
}
|
||||||
+232
-1082
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user