store 6
This commit is contained in:
Vendored
-74
@@ -1,74 +0,0 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// Generated by unplugin-vue-components
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
export {}
|
||||
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
ABadge: typeof import('ant-design-vue/es')['Badge']
|
||||
AButton: typeof import('ant-design-vue/es')['Button']
|
||||
AConfigProvider: typeof import('ant-design-vue/es')['ConfigProvider']
|
||||
ADivider: typeof import('ant-design-vue/es')['Divider']
|
||||
AForm: typeof import('ant-design-vue/es')['Form']
|
||||
AFormItem: typeof import('ant-design-vue/es')['FormItem']
|
||||
AInput: typeof import('ant-design-vue/es')['Input']
|
||||
AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
|
||||
AModal: typeof import('ant-design-vue/es')['Modal']
|
||||
ARadio: typeof import('ant-design-vue/es')['Radio']
|
||||
ARadioButton: typeof import('ant-design-vue/es')['RadioButton']
|
||||
ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup']
|
||||
ASelect: typeof import('ant-design-vue/es')['Select']
|
||||
ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
|
||||
ASlider: typeof import('ant-design-vue/es')['Slider']
|
||||
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']
|
||||
BaseDatePicker: typeof import('./src/components/BasicPacks/BaseDatePicker/src/BaseDatePicker.vue')['default']
|
||||
Basic: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/basic.vue')['default']
|
||||
BasicActionSheet: typeof import('./src/components/BasicPacks/BasicActionSheet.vue')['default']
|
||||
BasicButton: typeof import('./src/components/BasicPacks/BasicButton.vue')['default']
|
||||
BasicDraggableFileUpload: typeof import('./src/components/BasicPacks/BasicUploads/BasicDraggableFileUpload.vue')['default']
|
||||
BasicIcon: typeof import('./src/components/BasicPacks/BasicIcon.vue')['default']
|
||||
BasicInfoItem: typeof import('./src/components/BasicPacks/BasicInfoItem.vue')['default']
|
||||
BasicInput: typeof import('./src/components/BasicPacks/BasicInput.vue')['default']
|
||||
BasicModal: typeof import('./src/components/BasicPacks/BasicModal.vue')['default']
|
||||
BasicPriceInput: typeof import('./src/components/BasicPacks/BasicPriceInput/src/BasicPriceInput.vue')['default']
|
||||
BasicSelect: typeof import('./src/components/BasicPacks/BasicSelect.vue')['default']
|
||||
BasicTags: typeof import('./src/components/BasicPacks/BasicTags/index.vue')['default']
|
||||
BasicTextArea: typeof import('./src/components/BasicPacks/BasicTextArea.vue')['default']
|
||||
BasicToggleTwoItem: typeof import('./src/components/BasicPacks/BasicToggleTwoItem.vue')['default']
|
||||
Bg_main: typeof import('./src/components/bg_main.vue')['default']
|
||||
Bordered: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/bordered.vue')['default']
|
||||
DateRender: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/date-render.vue')['default']
|
||||
Demo: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/index.vue')['default']
|
||||
DialogVant: typeof import('./src/components/DialogVant.vue')['default']
|
||||
DialogVantOneButton: typeof import('./src/components/DialogVantOneButton.vue')['default']
|
||||
Disabled: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/disabled.vue')['default']
|
||||
DisabledDate: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/disabled-date.vue')['default']
|
||||
Empty: typeof import('./src/components/empty.vue')['default']
|
||||
ExtraFooter: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/extra-footer.vue')['default']
|
||||
Format: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/format.vue')['default']
|
||||
GoldLoading: typeof import('./src/components/GoldLoading.vue')['default']
|
||||
ListAnim: typeof import('./src/components/ListAnim.vue')['default']
|
||||
Mode: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/mode.vue')['default']
|
||||
PresettedRanges: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/presetted-ranges.vue')['default']
|
||||
RangePicker: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/range-picker.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
SelectInRange: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/select-in-range.vue')['default']
|
||||
Size: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/size.vue')['default']
|
||||
StartEnd: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/start-end.vue')['default']
|
||||
Suffix: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/suffix.vue')['default']
|
||||
Switchable: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/switchable.vue')['default']
|
||||
Text: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/text.vue')['default']
|
||||
Time: typeof import('./src/components/BasicPacks/BaseDatePicker/src/datePick/demo/time.vue')['default']
|
||||
TransactionSkeleton: typeof import('./src/components/TransactionSkeleton.vue')['default']
|
||||
VanActionSheet: typeof import('vant/es')['ActionSheet']
|
||||
VanDialog: typeof import('vant/es')['Dialog']
|
||||
VanField: typeof import('vant/es')['Field']
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import useAxios from '@/utils/useAxios'
|
||||
|
||||
export interface StoreCategory {
|
||||
id: number
|
||||
name: string
|
||||
sort_order?: number
|
||||
}
|
||||
|
||||
export const getStoreCategories = async (): Promise<StoreCategory[]> =>
|
||||
await useAxios.get<StoreCategory[]>('/customer/categories')
|
||||
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<img
|
||||
:src="currentSrc"
|
||||
:class="[imgClass, dimmed && 'product-image--dimmed']"
|
||||
:alt="alt"
|
||||
@error="handleError"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import {
|
||||
getProductFallbackImage,
|
||||
resolveProductImageSrc,
|
||||
type ProductImageSource
|
||||
} from '@/utils/productImage'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
product?: ProductImageSource
|
||||
imgClass?: string
|
||||
alt?: string
|
||||
dimmed?: boolean
|
||||
}>(),
|
||||
{
|
||||
imgClass: 'max-h-full max-w-full object-contain',
|
||||
alt: '',
|
||||
dimmed: false
|
||||
}
|
||||
)
|
||||
|
||||
const currentSrc = ref(resolveProductImageSrc(props.product))
|
||||
const usingFallback = ref(false)
|
||||
|
||||
watch(
|
||||
() => props.product,
|
||||
(product) => {
|
||||
usingFallback.value = false
|
||||
currentSrc.value = resolveProductImageSrc(product)
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
function handleError() {
|
||||
if (usingFallback.value) return
|
||||
usingFallback.value = true
|
||||
currentSrc.value = getProductFallbackImage(props.product)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.product-image--dimmed {
|
||||
opacity: 0.45;
|
||||
filter: grayscale(1);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,51 @@
|
||||
import defaultImg from '@/assets/img/defaultAbshodeh.svg'
|
||||
import goyImg from '@/assets/img/goyImg.svg'
|
||||
import mokaabImg from '@/assets/img/mokaabImg.svg'
|
||||
import sekkeImg from '@/assets/img/sekkeImg.svg'
|
||||
import parsianImg from '@/assets/img/parsianImg.svg'
|
||||
import venusImg from '@/assets/img/venousImg.svg'
|
||||
|
||||
export type ProductImageSource = {
|
||||
file?: { src?: string | null } | null
|
||||
group_type?: string | null
|
||||
} | null | undefined
|
||||
|
||||
function isUsableImageSrc(src?: string | null): boolean {
|
||||
if (!src) return false
|
||||
const value = src.trim()
|
||||
return value !== '' && value !== 'null' && value !== 'undefined'
|
||||
}
|
||||
|
||||
export function getProductFallbackImage(product?: ProductImageSource): string {
|
||||
const type = (product?.group_type ?? '').trim()
|
||||
if (!type) return defaultImg
|
||||
|
||||
const exactMap: Record<string, string> = {
|
||||
گوی: goyImg,
|
||||
مکعب: mokaabImg,
|
||||
سکه: sekkeImg,
|
||||
'شمش ونوس': venusImg,
|
||||
'سکه پارسیان': parsianImg,
|
||||
شمش: defaultImg,
|
||||
آبشده: defaultImg,
|
||||
}
|
||||
|
||||
if (exactMap[type]) return exactMap[type]
|
||||
|
||||
if (type.includes('پارسیان')) return parsianImg
|
||||
if (type.includes('ونوس')) return venusImg
|
||||
if (type.includes('گوی')) return goyImg
|
||||
if (type.includes('مکعب')) return mokaabImg
|
||||
if (type.includes('سکه')) return sekkeImg
|
||||
if (type.includes('شمش')) return defaultImg
|
||||
|
||||
return defaultImg
|
||||
}
|
||||
|
||||
export function resolveProductImageSrc(product?: ProductImageSource): string {
|
||||
const remoteSrc = product?.file?.src
|
||||
if (isUsableImageSrc(remoteSrc)) {
|
||||
return remoteSrc!.trim()
|
||||
}
|
||||
return getProductFallbackImage(product)
|
||||
}
|
||||
@@ -39,10 +39,9 @@
|
||||
<div class="flex gap-4">
|
||||
<!-- Product Image -->
|
||||
<div class="bg-gray-100 rounded-lg flex justify-center items-center p-1 min-w-[80px] h-[80px]">
|
||||
<img
|
||||
:src="getProductImage(item.product)"
|
||||
class="max-w-full max-h-full object-contain"
|
||||
alt=""
|
||||
<ProductImage
|
||||
:product="item.product"
|
||||
img-class="max-w-full max-h-full object-contain"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -151,16 +150,10 @@ import {useFundUserStore} from '@/store/fundStore'
|
||||
import type {Price} from '@/models/home'
|
||||
import type {Order} from '@/models/order'
|
||||
|
||||
// Image imports
|
||||
import defaultImg from '@/assets/img/defaultAbshodeh.svg'
|
||||
import goyImg from '@/assets/img/goyImg.svg'
|
||||
import mokaabImg from '@/assets/img/mokaabImg.svg'
|
||||
import sekkeImg from '@/assets/img/sekkeImg.svg'
|
||||
import parsianImg from '@/assets/img/parsianImg.svg'
|
||||
import venusImg from '@/assets/img/venousImg.svg'
|
||||
import {loading, getToken} from '@/utils'
|
||||
import GoldLoading from '@/components/GoldLoading.vue'
|
||||
import TransactionSkeleton from '@/components/TransactionSkeleton.vue'
|
||||
import ProductImage from '@/components/ProductImage.vue'
|
||||
import {registerCodeDiscount} from "@/api/giftCard";
|
||||
|
||||
const cartStore = useCartStore()
|
||||
@@ -208,30 +201,6 @@ async function submitDiscount() {
|
||||
}
|
||||
}
|
||||
|
||||
function getProductImage(product: any) {
|
||||
// Check if product has file field from server with src property
|
||||
if (product?.file?.src) {
|
||||
return product.file.src
|
||||
}
|
||||
|
||||
// Fallback to local images based on group_type if file is empty or doesn't exist
|
||||
const type = product?.group_type
|
||||
switch (type) {
|
||||
case 'گوی':
|
||||
return goyImg
|
||||
case 'مکعب':
|
||||
return mokaabImg
|
||||
case 'سکه':
|
||||
return sekkeImg
|
||||
case 'شمش ونوس':
|
||||
return venusImg
|
||||
case 'سکه پارسیان':
|
||||
return parsianImg
|
||||
default:
|
||||
return defaultImg
|
||||
}
|
||||
}
|
||||
|
||||
function getProductAmount(amount?: number) {
|
||||
if (!amount) return ''
|
||||
if (amount < 1) {
|
||||
|
||||
@@ -64,23 +64,27 @@
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="flex bg-white border border-gray-200 rounded-xl p-3 transition-all duration-300
|
||||
hover:shadow-lg hover:-translate-y-0.5 cursor-pointer"
|
||||
:class="!props.ProductItem.show && 'opacity-50 grayscale'"
|
||||
class="product-card relative flex cursor-pointer rounded-xl border border-gray-200 bg-white p-3 transition-all duration-300 hover:-translate-y-0.5 hover:shadow-lg"
|
||||
:class="!props.ProductItem.show && 'opacity-50 grayscale'"
|
||||
>
|
||||
<!-- Image -->
|
||||
<div class="bg-gray-100 rounded-lg w-24 h-24 flex items-center justify-center shrink-0">
|
||||
<img
|
||||
:src="productImage"
|
||||
class="max-h-30 object-contain"
|
||||
alt=""
|
||||
<span
|
||||
v-if="isOutOfStock"
|
||||
class="product-card__badge absolute top-2 right-2 z-10 rounded-md bg-gray-700/90 px-2 py-0.5 text-xs font-medium text-white shadow-sm"
|
||||
>
|
||||
ناموجود
|
||||
</span>
|
||||
|
||||
<div class="flex h-24 w-24 shrink-0 items-center justify-center rounded-lg bg-gray-100">
|
||||
<ProductImage
|
||||
:product="props.ProductItem"
|
||||
:dimmed="isOutOfStock"
|
||||
img-class="max-h-20 object-contain"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="mr-4 flex flex-col justify-between flex-1">
|
||||
<div class="mr-4 flex flex-1 flex-col justify-between">
|
||||
<div>
|
||||
<div class="text-sm font-semibold line-clamp-2 mb-1">
|
||||
<div class="mb-1 line-clamp-2 text-sm font-semibold">
|
||||
{{ props.ProductItem.title }}
|
||||
</div>
|
||||
|
||||
@@ -89,17 +93,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between mt-2">
|
||||
<div class="!text-primary font-bold text-sm">
|
||||
{{ usePrice((props.ProductItem.price ?? 0) + (props.ProductItem.cash_fee_buy ?? 0)) }}
|
||||
</div>
|
||||
|
||||
<span
|
||||
v-if="!props.ProductItem?.inventory || props.ProductItem?.inventory==0"
|
||||
class="text-xs bg-gray-300 text-gray-600 px-2 py-0.5 rounded-md"
|
||||
>
|
||||
ناموجود
|
||||
</span>
|
||||
<div class="mt-2 !text-primary text-sm font-bold">
|
||||
{{ usePrice((props.ProductItem.price ?? 0) + (props.ProductItem.cash_fee_buy ?? 0)) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -109,13 +104,7 @@
|
||||
import { computed } from 'vue'
|
||||
import type { Product } from '@/models/products'
|
||||
import { usePrice } from '@/utils/usePrice'
|
||||
|
||||
import defaultImg from '@/assets/img/defaultAbshodeh.svg'
|
||||
import goyImg from '@/assets/img/goyImg.svg'
|
||||
import mokaabImg from '@/assets/img/mokaabImg.svg'
|
||||
import sekkeImg from '@/assets/img/sekkeImg.svg'
|
||||
import parsianImg from '@/assets/img/parsianImg.svg'
|
||||
import venusImg from '@/assets/img/venousImg.svg'
|
||||
import ProductImage from '@/components/ProductImage.vue'
|
||||
|
||||
interface Props {
|
||||
ProductItem: Product
|
||||
@@ -123,6 +112,10 @@ interface Props {
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const isOutOfStock = computed(
|
||||
() => !props.ProductItem?.inventory || props.ProductItem.inventory === 0
|
||||
)
|
||||
|
||||
const amountProduct = computed(() => {
|
||||
if (!props.ProductItem.amount) return ''
|
||||
if (props.ProductItem.amount < 1) {
|
||||
@@ -131,28 +124,6 @@ const amountProduct = computed(() => {
|
||||
return `${props.ProductItem.amount} گرم`
|
||||
})
|
||||
|
||||
const productImage = computed(() => {
|
||||
// Check if product has file field from server with src property
|
||||
if (props.ProductItem?.file?.src) {
|
||||
return props.ProductItem.file.src
|
||||
}
|
||||
|
||||
// Fallback to local images based on group_type if file is empty or doesn't exist
|
||||
switch (props.ProductItem.group_type) {
|
||||
case 'گوی':
|
||||
return goyImg
|
||||
case 'مکعب':
|
||||
return mokaabImg
|
||||
case 'سکه':
|
||||
return sekkeImg
|
||||
case 'شمش ونوس':
|
||||
return venusImg
|
||||
case 'سکه پارسیان':
|
||||
return parsianImg
|
||||
default:
|
||||
return defaultImg
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -1,40 +1,33 @@
|
||||
<template>
|
||||
<div
|
||||
class="bg-white border border-gray-200 rounded-xl p-3 transition-all duration-300
|
||||
hover:shadow-lg hover:-translate-y-1 cursor-pointer"
|
||||
:class="!props.ProductItem.show && 'opacity-50 grayscale'"
|
||||
class="product-card relative flex h-full flex-col rounded-xl border border-gray-200 bg-white p-3 transition-all duration-300 hover:-translate-y-1 hover:shadow-lg cursor-pointer"
|
||||
:class="!props.ProductItem.show && 'opacity-50 grayscale'"
|
||||
>
|
||||
<!-- Image -->
|
||||
<div class="bg-gray-100 rounded-lg h-32 flex items-center justify-center mb-3">
|
||||
<img
|
||||
:src="productImage"
|
||||
class="max-h-35 object-contain"
|
||||
alt=""
|
||||
<span
|
||||
v-if="isOutOfStock"
|
||||
class="product-card__badge absolute top-2 right-2 z-10 rounded-md bg-gray-700/90 px-2 py-0.5 text-xs font-medium text-white shadow-sm"
|
||||
>
|
||||
ناموجود
|
||||
</span>
|
||||
|
||||
<div class="mb-3 flex h-32 items-center justify-center rounded-lg bg-gray-100">
|
||||
<ProductImage
|
||||
:product="props.ProductItem"
|
||||
:dimmed="isOutOfStock"
|
||||
img-class="max-h-28 w-full object-contain"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Title -->
|
||||
<div class="text-sm font-semibold line-clamp-2 mb-2 min-h-[40px]">
|
||||
<div class="mb-2 min-h-[40px] line-clamp-2 text-sm font-semibold">
|
||||
{{ props.ProductItem.title }}
|
||||
</div>
|
||||
|
||||
<!-- Amount -->
|
||||
<div class="text-xs text-gray-500 mb-2">
|
||||
<div class="mb-2 text-xs text-gray-500">
|
||||
{{ amountProduct }}
|
||||
</div>
|
||||
|
||||
<!-- Price & Status -->
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="!text-primary font-bold text-sm">
|
||||
{{ usePrice((props.ProductItem.price ?? 0) + (props.ProductItem.cash_fee_buy ?? 0)) }}
|
||||
</div>
|
||||
|
||||
<span
|
||||
v-if="!props.ProductItem?.inventory || props.ProductItem?.inventory==0"
|
||||
class="text-xs bg-gray-300 text-gray-600 px-2 py-0.5 rounded-md"
|
||||
>
|
||||
ناموجود
|
||||
</span>
|
||||
<div class="mt-auto !text-primary text-sm font-bold">
|
||||
{{ usePrice((props.ProductItem.price ?? 0) + (props.ProductItem.cash_fee_buy ?? 0)) }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -43,13 +36,7 @@
|
||||
import { computed } from 'vue'
|
||||
import type { Product } from '@/models/products'
|
||||
import { usePrice } from '@/utils/usePrice'
|
||||
|
||||
import defaultImg from '@/assets/img/defaultAbshodeh.svg'
|
||||
import goyImg from '@/assets/img/goyImg.svg'
|
||||
import mokaabImg from '@/assets/img/mokaabImg.svg'
|
||||
import sekkeImg from '@/assets/img/sekkeImg.svg'
|
||||
import parsianImg from '@/assets/img/parsianImg.svg'
|
||||
import venusImg from '@/assets/img/venousImg.svg'
|
||||
import ProductImage from '@/components/ProductImage.vue'
|
||||
|
||||
interface Props {
|
||||
ProductItem: Product
|
||||
@@ -57,6 +44,10 @@ interface Props {
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const isOutOfStock = computed(
|
||||
() => !props.ProductItem?.inventory || props.ProductItem.inventory === 0
|
||||
)
|
||||
|
||||
const amountProduct = computed(() => {
|
||||
if (!props.ProductItem.amount) return ''
|
||||
if (props.ProductItem.amount < 1) {
|
||||
@@ -64,29 +55,6 @@ const amountProduct = computed(() => {
|
||||
}
|
||||
return `${props.ProductItem.amount} گرم`
|
||||
})
|
||||
|
||||
const productImage = computed(() => {
|
||||
// Check if product has file field from server with src property
|
||||
if (props.ProductItem?.file?.src) {
|
||||
return props.ProductItem.file.src
|
||||
}
|
||||
|
||||
// Fallback to local images based on group_type if file is empty or doesn't exist
|
||||
switch (props.ProductItem.group_type) {
|
||||
case 'گوی':
|
||||
return goyImg
|
||||
case 'مکعب':
|
||||
return mokaabImg
|
||||
case 'سکه':
|
||||
return sekkeImg
|
||||
case 'شمش ونوس':
|
||||
return venusImg
|
||||
case 'سکه پارسیان':
|
||||
return parsianImg
|
||||
default:
|
||||
return defaultImg
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -17,11 +17,25 @@
|
||||
<BasicIcon v-else icon="vuesax-linear:element-3"></BasicIcon>
|
||||
</div>
|
||||
|
||||
<div class="mx-5 pb-30 pt-2 grid grid-cols-2 gap-2" style="color: #1b2b07">
|
||||
<div class="mt-3 gap-2 " :class="!viewVertical?'col-span-2':''" v-for="item in items" :key="item.id">
|
||||
<ProductItemVertical v-if="viewVertical" :ProductItem="item" @click="clickProduct(item)"></ProductItemVertical>
|
||||
<ProductItem v-else class="col-span-2" :ProductItem="item" @click="clickProduct(item)"></ProductItem>
|
||||
</div>
|
||||
<div class="mx-5 pb-30 pt-2 grid grid-cols-2 gap-3 items-stretch">
|
||||
<template v-if="viewVertical">
|
||||
<ProductItemVertical
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
class="h-full"
|
||||
:ProductItem="item"
|
||||
@click="clickProduct(item)"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<ProductItem
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
class="col-span-2 h-full"
|
||||
:ProductItem="item"
|
||||
@click="clickProduct(item)"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -44,6 +58,7 @@ import { onMounted, onUnmounted, ref, watch } from 'vue'
|
||||
import router from '@/router'
|
||||
import ProductItem from '@/views/base/products/components/ProductItem.vue'
|
||||
import { getProducts } from '@/api/products'
|
||||
import { getStoreCategories } from '@/api/categories'
|
||||
import {
|
||||
getLastPrices,
|
||||
lastPrice,
|
||||
@@ -66,7 +81,7 @@ import ProductItemVertical from "@/views/base/products/components/ProductItemVer
|
||||
const activekey = ref('همه')
|
||||
const showFilter = ref()
|
||||
const selectedProduct = ref<Product>()
|
||||
const viewVertical=ref<boolean>('true')
|
||||
const viewVertical = ref(true)
|
||||
|
||||
function clickProduct(item: Product) {
|
||||
if(item?.show){
|
||||
@@ -81,7 +96,27 @@ function changeTab() {
|
||||
val.group_type == activekey.value
|
||||
)
|
||||
} else items.value = firstItems.value
|
||||
}
|
||||
|
||||
function sortProductsByInventory(products: Product[] | undefined) {
|
||||
if (!products?.length) return products ?? []
|
||||
|
||||
return [...products].sort((a, b) => {
|
||||
const aInStock = Number(a.inventory ?? 0) > 0 ? 0 : 1
|
||||
const bInStock = Number(b.inventory ?? 0) > 0 ? 0 : 1
|
||||
if (aInStock !== bInStock) return aInStock - bInStock
|
||||
return Number(b.inventory ?? 0) - Number(a.inventory ?? 0)
|
||||
})
|
||||
}
|
||||
|
||||
async function loadCategoryTabs() {
|
||||
try {
|
||||
const categories = await getStoreCategories()
|
||||
typeItems.value = categories.map((item) => item.name)
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
typeItems.value = []
|
||||
}
|
||||
}
|
||||
|
||||
const firstItems = ref<Product[]>()
|
||||
@@ -91,13 +126,17 @@ const typeItems = ref([])
|
||||
onMounted(async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
items.value = await getProducts()
|
||||
items.value?.forEach((val) => {
|
||||
if (!typeItems.value.includes(val.group_type)) {
|
||||
typeItems.value.push(val.group_type)
|
||||
}
|
||||
})
|
||||
firstItems.value = items.value
|
||||
const products = sortProductsByInventory(await getProducts())
|
||||
items.value = products
|
||||
firstItems.value = products
|
||||
await loadCategoryTabs()
|
||||
if (!typeItems.value.length) {
|
||||
products.forEach((val) => {
|
||||
if (val.group_type && !typeItems.value.includes(val.group_type)) {
|
||||
typeItems.value.push(val.group_type)
|
||||
}
|
||||
})
|
||||
}
|
||||
await initPrice()
|
||||
// await setIntervalGetLastPrice()
|
||||
// console.log('run product')
|
||||
|
||||
@@ -6,8 +6,12 @@
|
||||
<div class="!h-full overflow-y-scroll">
|
||||
<div class="mx-5 pb-30 pt-5" style="color: #1b2b07">
|
||||
<!-- Product Image -->
|
||||
<div class="bg-gray-100 rounded-lg w-full flex justify-center mb-4">
|
||||
<img :src="getProductImage(product)" class="max-w-full h-auto"/>
|
||||
<div class="bg-gray-100 rounded-lg w-full flex justify-center mb-4">
|
||||
<ProductImage
|
||||
:product="product"
|
||||
:dimmed="isOutOfStock"
|
||||
img-class="max-w-full h-auto"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Product Title -->
|
||||
@@ -68,9 +72,10 @@
|
||||
>
|
||||
<!-- Image -->
|
||||
<div class="bg-gray-100 rounded-md flex justify-center p-4 mb-2">
|
||||
<img
|
||||
:src="getProductImage(item)"
|
||||
class="h-20 object-contain"
|
||||
<ProductImage
|
||||
:product="item"
|
||||
:dimmed="!item.inventory || item.inventory === 0"
|
||||
img-class="h-20 object-contain"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -129,14 +134,7 @@ import {
|
||||
} from '@/utils/price'
|
||||
import {Swiper, SwiperSlide} from 'swiper/vue'
|
||||
import 'swiper/swiper-bundle.css'
|
||||
|
||||
// Image imports
|
||||
import defaultImg from '@/assets/img/defaultAbshodeh.svg'
|
||||
import goyImg from '@/assets/img/goyImg.svg'
|
||||
import mokaabImg from '@/assets/img/mokaabImg.svg'
|
||||
import sekkeImg from '@/assets/img/sekkeImg.svg'
|
||||
import parsianImg from '@/assets/img/parsianImg.svg'
|
||||
import venusImg from '@/assets/img/venousImg.svg'
|
||||
import ProductImage from '@/components/ProductImage.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
@@ -147,6 +145,11 @@ const productId = computed(() => {
|
||||
const id = route.params.id
|
||||
return typeof id === 'string' ? parseInt(id) : id
|
||||
})
|
||||
|
||||
const isOutOfStock = computed(
|
||||
() => !product.value?.inventory || product.value.inventory === 0
|
||||
)
|
||||
|
||||
watch(
|
||||
() => productId.value,
|
||||
(newId, oldId) => {
|
||||
@@ -250,30 +253,6 @@ function goToProduct(id: number) {
|
||||
router.push({name: 'productDetail', params: {id}})
|
||||
}
|
||||
|
||||
function getProductImage(product: any) {
|
||||
// Check if product has file field from server with src property
|
||||
if (product?.file?.src) {
|
||||
return product.file.src
|
||||
}
|
||||
|
||||
// Fallback to local images based on group_type if file is empty or doesn't exist
|
||||
const type = product?.group_type
|
||||
switch (type) {
|
||||
case 'گوی':
|
||||
return goyImg
|
||||
case 'مکعب':
|
||||
return mokaabImg
|
||||
case 'سکه':
|
||||
return sekkeImg
|
||||
case 'شمش ونوس':
|
||||
return venusImg
|
||||
case 'سکه پارسیان':
|
||||
return parsianImg
|
||||
default:
|
||||
return defaultImg
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
+1
-1
@@ -89,7 +89,7 @@ export default defineConfig({
|
||||
}
|
||||
},
|
||||
server: {
|
||||
port: 3100,
|
||||
port: 3200,
|
||||
},
|
||||
// base: `/single`,
|
||||
// base: `/customer`,
|
||||
|
||||
Reference in New Issue
Block a user