diff --git a/components.d.ts b/components.d.ts index e3a5b98..62272de 100644 --- a/components.d.ts +++ b/components.d.ts @@ -12,7 +12,6 @@ declare module 'vue' { ADivider: typeof import('ant-design-vue/es')['Divider'] AForm: typeof import('ant-design-vue/es')['Form'] AFormItem: typeof import('ant-design-vue/es')['FormItem'] - AFormItemRest: typeof import('ant-design-vue/es')['FormItemRest'] AInput: typeof import('ant-design-vue/es')['Input'] AInputPassword: typeof import('ant-design-vue/es')['InputPassword'] AModal: typeof import('ant-design-vue/es')['Modal'] diff --git a/src/api/card.ts b/src/api/card.ts new file mode 100644 index 0000000..b5490d8 --- /dev/null +++ b/src/api/card.ts @@ -0,0 +1,11 @@ +import useAxios from "@/utils/useAxios"; +import type {PInventory, Product} from "@/models/products"; +import type {PWallet, Wallet} from '@/models/wallet' +import type {Order} from "@/models/order"; +import {useUserStore} from "@/store/userStore"; + +const userStore = useUserStore() + +export const checkoutCart = async (orders: Order[]): Promise => { + return await useAxios.post(`/customer/cart/checkout`, orders) +} \ No newline at end of file diff --git a/src/api/order.ts b/src/api/order.ts index 4117e51..ced8c0b 100644 --- a/src/api/order.ts +++ b/src/api/order.ts @@ -11,6 +11,7 @@ export const createOrder = async (obj: Order): Promise => { return await useAxios.post(`/customer${!userStore.isSingle ? '' : '/single'}/order`, obj) } + export const getOrders = async (tbl: Ref>): Promise> => { return await useAxios.post>(`/customer${!userStore.isSingle ? '' : '/single'}/order/table`, tbl) } diff --git a/src/api/products.ts b/src/api/products.ts index f9f376d..dc6b98c 100644 --- a/src/api/products.ts +++ b/src/api/products.ts @@ -6,6 +6,9 @@ import type { PWallet, Wallet } from '@/models/wallet' export const getProducts = async (): Promise => { return await useAxios.get(`/customer/product/list`) } +export const getProductById = async ( id:number|null): Promise => { + return await useAxios.get(`/customer/product/list/${id}`) +} export const getProductInventory = async (): Promise => { return await useAxios.post(`/customer/wallet/product/table`) } diff --git a/src/assets/img/venousImg.svg b/src/assets/img/venousImg.svg index ec74913..8d56783 100644 --- a/src/assets/img/venousImg.svg +++ b/src/assets/img/venousImg.svg @@ -1,3 +1,3 @@ - - + + diff --git a/src/assets/img/venousImg1.svg b/src/assets/img/venousImg1.svg new file mode 100644 index 0000000..ec74913 --- /dev/null +++ b/src/assets/img/venousImg1.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/BasicPacks/BasicActionSheet.vue b/src/components/BasicPacks/BasicActionSheet.vue index 440b49c..1495272 100644 --- a/src/components/BasicPacks/BasicActionSheet.vue +++ b/src/components/BasicPacks/BasicActionSheet.vue @@ -16,7 +16,7 @@ -->
@@ -60,6 +60,8 @@ interface Props { showCancel?: boolean showOk?: boolean okText?: string + show?: boolean + placement?: string } const props = withDefaults(defineProps(), { @@ -68,6 +70,8 @@ const props = withDefaults(defineProps(), { showCancel: true, showOk: true, okText: 'ثبت', + show: false, + placement: 'bottom' }) diff --git a/src/main.ts b/src/main.ts index b4421cf..86847a1 100644 --- a/src/main.ts +++ b/src/main.ts @@ -62,7 +62,7 @@ export function setEnableAuthenticate() { function goToLogin(to: RouteLocationNormalized, next: NavigationGuardNext) { - if (to.name === 'buyProduct') { + if (to.name === 'buyProduct'||to.name === 'productDetail'||to.name==='about'||to.name==='cart') { return next() // Allow access to buyProduct without login } if (to.name !== 'login') { @@ -139,7 +139,7 @@ router.beforeEach(async (to, from, next) => { } } else { // If no token, handle redirect to login or allow access to login, 404, or buyProduct - if (['login', '404', 'buyProduct'].includes(to.name as string)) { + if (['login', '404', 'buyProduct','productDetail','about','cart'].includes(to.name as string)) { return next() // Allow access to login, 404, or buyProduct } goToLogin(to, next) // Redirect to login if no token @@ -156,7 +156,7 @@ router.beforeEach(async (to, from, next) => { return next() // Proceed to the intended route } else { // If no token, ensure proper redirection to login or 404 - if (['login', '404', 'buyProduct'].includes(to.name as string)) { + if (['login', '404', 'buyProduct','productDetail','about','cart'].includes(to.name as string)) { return next() // Allow access to login, 404, or buyProduct } return next({ name: 'login' }) diff --git a/src/models/cart.ts b/src/models/cart.ts new file mode 100644 index 0000000..5d6c0fd --- /dev/null +++ b/src/models/cart.ts @@ -0,0 +1,16 @@ +import type { Product } from './products' + +export interface CartItem { + id: string + product: Product + quantity: number + method: 'cash' | 'gold' + gateway: boolean + goldVazn?: number + goldCash: number + price: number + totalPrice: number + addedAt: number // timestamp +} + + diff --git a/src/models/products.ts b/src/models/products.ts index f1d01bd..a39b04c 100644 --- a/src/models/products.ts +++ b/src/models/products.ts @@ -25,6 +25,7 @@ export interface Product { sellPrice?:number | undefined description: string wallet: Wallet + similar?:Product[] } export interface PInventory { diff --git a/src/router/index.ts b/src/router/index.ts index 2757781..2570c13 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -35,6 +35,11 @@ const router = createRouter({ name: 'buyProduct', component: () => import('@/views/base/products/index.vue') }, + { + path: 'product/:id', + name: 'productDetail', + component: () => import('@/views/base/products/product/[id].vue') + }, { path: 'transactions', name: 'transactions', @@ -45,6 +50,11 @@ const router = createRouter({ name: 'wallet', component: () => import('../views/base/wallet/index.vue') }, + { + path: 'cart', + name: 'cart', + component: () => import('@/views/base/cart/index.vue') + }, { path: 'user', name: 'user', diff --git a/src/store/cartStore.ts b/src/store/cartStore.ts new file mode 100644 index 0000000..1197394 --- /dev/null +++ b/src/store/cartStore.ts @@ -0,0 +1,109 @@ +import { computed, ref } from 'vue' +import { defineStore } from 'pinia' +import type { CartItem } from '@/models/cart' +import type { Product } from '@/models/products' + +const CART_STORAGE_KEY = 'ghatre_cart' + +export const useCartStore = defineStore('cart', () => { + const cartItems = ref([]) + + // Load cart from localStorage on initialization + function loadCartFromStorage() { + try { + const stored = localStorage.getItem(CART_STORAGE_KEY) + if (stored) { + cartItems.value = JSON.parse(stored) + } + } catch (e) { + console.error('Error loading cart from storage:', e) + cartItems.value = [] + } + } + + // Save cart to localStorage + function saveCartToStorage() { + try { + localStorage.setItem(CART_STORAGE_KEY, JSON.stringify(cartItems.value)) + } catch (e) { + console.error('Error saving cart to storage:', e) + } + } + + // Initialize cart from storage + loadCartFromStorage() + + // Add item to cart + function addToCart( + product: Product, + quantity: number, + method: 'cash' | 'gold', + gateway: boolean, + goldVazn?: number, + goldCash: number = 0 + ) { + const itemId = `${Date.now()}_${product.id}` + const price = (product.price ?? 0) + (product.cash_fee_buy ?? 0) + const totalPrice = price * quantity + + const cartItem: CartItem = { + id: itemId, + product: { ...product }, + quantity, + method, + gateway, + goldVazn, + goldCash, + price, + totalPrice, + addedAt: Date.now() + } + + cartItems.value.push(cartItem) + saveCartToStorage() + } + + // Remove item from cart + function removeFromCart(itemId: string) { + const index = cartItems.value.findIndex(item => item.id === itemId) + if (index !== -1) { + cartItems.value.splice(index, 1) + saveCartToStorage() + } + } + + // Clear cart + function clearCart() { + cartItems.value = [] + saveCartToStorage() + } + + // Get cart items + function getCartItems(): CartItem[] { + return cartItems.value + } + + // Computed: Total price + const totalPrice = computed(() => { + return cartItems.value.reduce((sum, item) => sum + item.totalPrice, 0) + }) + + // Computed: Item count + const itemCount = computed(() => { + return cartItems.value.length + }) + + return { + cartItems, + addToCart, + removeFromCart, + clearCart, + getCartItems, + totalPrice, + itemCount, + loadCartFromStorage, + saveCartToStorage + } +}) + + diff --git a/src/utils/index.ts b/src/utils/index.ts index fa564dd..6fa8592 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -108,6 +108,10 @@ function isIphone() { return /iPhone/.test(userAgent) } +function isToken() { + const token = getToken() + return !!token +} export { getToken, @@ -118,5 +122,6 @@ export { isIphone, removeFingerPrint, getIsActiveFingerprint, - setFingerPrint + setFingerPrint, + isToken } diff --git a/src/utils/inputRules.ts b/src/utils/inputRules.ts index d03fbef..90abd41 100644 --- a/src/utils/inputRules.ts +++ b/src/utils/inputRules.ts @@ -31,6 +31,7 @@ export function inputLenRules(len: number, title?: string) { import { computed, ref } from 'vue' import type { Rule } from 'ant-design-vue/es/form' import { useWalletStore } from '@/store/walletStore' +import {isToken} from "@/utils/index"; export const mobileValidation = (title?: string) => { return { len: 11, message: `شماره همراه باید 11 رقم باشد` } @@ -147,7 +148,7 @@ const checkNumber = async (_rule: Rule, value: number) => { } if (isBuyP) { // Skip wallet balance check if gateway is enabled - if (!gatewayP) { + if (!gatewayP && isToken()) { if (!walletCash?.walletInfo?.cash && methodP == 'cash') { return Promise.reject('موجودی کیف پول ناکافی است') } @@ -162,6 +163,7 @@ const checkNumber = async (_rule: Rule, value: number) => { return Promise.resolve() } else { if (value > NumP) + return Promise.reject('موجودی محصول ناکافی است') else return Promise.resolve() diff --git a/src/views/base/cart/index.vue b/src/views/base/cart/index.vue new file mode 100644 index 0000000..a56f39e --- /dev/null +++ b/src/views/base/cart/index.vue @@ -0,0 +1,403 @@ + + + + + diff --git a/src/views/base/index.vue b/src/views/base/index.vue index e2081ad..4d3752e 100644 --- a/src/views/base/index.vue +++ b/src/views/base/index.vue @@ -22,49 +22,49 @@
حساب کاربری
-
هشدار قیمت
-
انتقال قطره
-
دریافت طلای فیزیکی
-
خرید محصول
-
لیست دریافتی ها
-
درباره ما
-
@@ -98,7 +98,7 @@
-
+
@@ -128,9 +128,13 @@
کیف پول
-
- -
دریافت فیزیکی
+ + + + +
+ +
سبد خرید
@@ -138,16 +142,58 @@
+
+
+
+ +
فروشگاه
+
+
+ +
درباره ما
+
+
+
+
+ +
+ +
+
+ +
+ +
سبد خرید
+
+
+ +
ورود
+
+
+
+
+
+
+
+
diff --git a/src/views/base/products/components/DealProduct.vue b/src/views/base/products/components/DealProduct.vue index 1116d54..1779a8d 100644 --- a/src/views/base/products/components/DealProduct.vue +++ b/src/views/base/products/components/DealProduct.vue @@ -9,15 +9,15 @@ :style="!props?.ProductItem?.open_buy ? 'pointer-events: none; opacity: 0.5;' : ''"> خرید {{ props?.ProductItem?.title }} -
- فروش {{ props?.ProductItem?.title }} -
+ + + + + +
+ class="absolute bg-white h-8 w-full rounded-lg animate right">
@@ -104,16 +104,16 @@
- -
-
-
استفاده از درگاه:
-
- -
-
-
-
+ + + + + + + + + +
-
{{ !isBuy ? 'فروش' : 'خرید' }}
+
{{ !isBuy ? 'فروش' : 'افزودن به سبد خرید' }}
+
+
+ خرید {{ props?.ProductItem?.title }} +
+
+ فروش {{ props?.ProductItem?.title }} +
+ +
+
+ +
+ +
+ +
+
{{ !isBuy ? 'قیمت فروش' : 'قیمت خرید' }}
+
+
+ {{ usePriceWithoutType(isBuy ? ((props?.ProductItem?.price ?? 0) + (props?.ProductItem?.cash_fee_buy ?? 0)) : ((props?.ProductItem?.price ?? 0) - (props?.ProductItem?.cash_fee_sell ?? 0))) }} +
+ ریال +
+
+
+
+ +
+
+ + حداقل مبلغ اولیه برای ثبت سفارش {{ usePriceWithoutType(limitedPrice.value as number ?? 0) }} ریال می باشد. +
+ + +
+ +
+ + + + + + + + + + + +
+
قیمت کل
+
{{ usePrice(form.goldCash ?? 0) }}
+
+ +
+
وزن کل
+
{{ form.goldVazn ?? 0 }} گرم
+
+ + + +
+
{{ `موجودی کیف طلا: ${walletStore?.walletInfo?.gold ?? 0} گرم` }}
+
+
+ {{ `موجودی کیف پول: ${usePriceWithoutType(walletStore?.walletInfo?.cash ?? 0)} ریال` }} +
+
{{ `موجودی محصول: ${getInventoryById(props?.ProductItem?.id)}` }} +
+
+ +
+ + ارزش کل وارد شده بر حسب گرم طلا محاسبه خواهد شد و قیمت وارد شده معادل {{ usePrice(finalPrice) }} می + باشد! +
+
+
+
+ +
+
+
استفاده از درگاه:
+
+ +
+
+
+
+ + + +
+ +
{{ !isBuy ? 'فروش' : 'خرید' }}
+
+
+ + +
+
+
+ +
+ + +
برای ثبت سفارش، لطفا اطلاعات خود را تکمیل کنید + یا در صورت تکمیل اطلاعات، منتظر تایید اطلاعات خود بمانید. +
+
+
+ + +
+
+
+
تعداد:
+
+
{{ form.goldValue }}
+
عدد
+
عدد {{ props?.ProductItem?.title }}
+
+
+
+
قیمت:
+
+
+ {{ usePriceWithoutType(isBuy ? props?.ProductItem.price ?? 0 : props?.ProductItem?.sellPrice ?? 0) }} +
+
ریال
+
+
+
+
کارمزد:
+
+
{{ + buyFee + }} +
+ +
{{ + sellFee + }} +
+ +
ریال
+
+
+
+
{{ isBuy ? 'قیمت قابل پرداخت:' : 'مبلغ دریافتی:' }}
+
+
{{ usePriceWithoutType(payablePrice.toFixed(0)) }}
+
ریال
+
+
+ +
+ ثبت نهایی +
+ + + + + diff --git a/src/views/base/products/components/ProductItem.vue b/src/views/base/products/components/ProductItem.vue index 40ba135..8e22730 100644 --- a/src/views/base/products/components/ProductItem.vue +++ b/src/views/base/products/components/ProductItem.vue @@ -1,63 +1,156 @@ -