-
-
+
+
+
{{ getAttribute(row.attributeId)?.slug || '' }}
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
-
-
- ابتدا ویژگی reusable را انتخاب کنید.
+
+
+ {{ dataTypeLabelMap[getAttribute(row.attributeId)!.dataType] }}
+ {{ getAttribute(row.attributeId)?.unit }}
+ فیلترپذیر
+ قابل نمایش
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ابتدا ویژگی reusable را انتخاب کنید.
+
@@ -113,6 +146,9 @@
diff --git a/src/components/admin/products/ProductForm.vue b/src/components/admin/products/ProductForm.vue
index c99d33b..2fa6aec 100644
--- a/src/components/admin/products/ProductForm.vue
+++ b/src/components/admin/products/ProductForm.vue
@@ -537,7 +537,7 @@ const validateForm = () => {
if (!form.sku.trim()) return 'SKU الزامی است'
if (!form.slug.trim()) return 'اسلاگ الزامی است'
if (!form.technicalCode.trim()) return 'کد فنی الزامی است'
- if (!form.brandId && !form.brand.trim()) return 'برند الزامی است'
+ if (!form.brandId && !(form.brand || '').trim()) return 'برند الزامی است'
if (form.basePriceUSD < 0) return 'قیمت پایه نمی تواند منفی باشد'
if (form.salePriceUSD !== null && form.salePriceUSD !== undefined && form.salePriceUSD !== '' && Number(form.salePriceUSD) < 0) {
return 'قیمت فروش نمی تواند منفی باشد'
diff --git a/src/components/layout/Sidebar.vue b/src/components/layout/Sidebar.vue
index c0627e9..5b01fc3 100644
--- a/src/components/layout/Sidebar.vue
+++ b/src/components/layout/Sidebar.vue
@@ -249,6 +249,7 @@ const menuSections: MenuSection[] = [
children: [
{ label: 'تنظیمات عمومی', to: '/admin/settings' },
{ label: 'درگاه های پرداخت', to: '/admin/settings/payment-gateways' },
+ { label: 'فیش های بانکی ارسالی', to: '/admin/payments/bank-slips' },
{ label: 'روش های ارسال', to: '/admin/settings/shipping-methods' },
{ label: 'تنظیمات سئو', to: '/admin/settings/seo' },
],
diff --git a/src/config/api.ts b/src/config/api.ts
new file mode 100644
index 0000000..9cec43d
--- /dev/null
+++ b/src/config/api.ts
@@ -0,0 +1,14 @@
+const DEFAULT_API_ORIGIN = 'https://parsshop-back.mugit.ir'
+const DEFAULT_API_PREFIX = '/api'
+
+const normalizeBaseUrl = (value: string) => {
+ const trimmed = value.trim()
+ if (!trimmed) return `${DEFAULT_API_ORIGIN}${DEFAULT_API_PREFIX}`
+
+ const withProtocol = /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`
+ return withProtocol.replace(/\/+$/, '')
+}
+
+const envBaseUrl = import.meta.env.VITE_API_BASE_URL as string | undefined
+
+export const API_BASE_URL = normalizeBaseUrl(envBaseUrl || `${DEFAULT_API_ORIGIN}${DEFAULT_API_PREFIX}`)
diff --git a/src/router/index.ts b/src/router/index.ts
index 40f22b2..3660917 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -168,7 +168,24 @@ const router = createRouter({
createAdminPlaceholderRoute('support/product-questions', 'AdminSupportProductQuestions', 'سوالات محصولات', 'مدیریت پرسش و پاسخ های ثبت شده برای کالاها'),
createAdminPlaceholderRoute('users/roles', 'AdminUsersRoles', 'نقش ها و دسترسی ها', 'مدیریت نقش های سازمانی و سطح دسترسی کاربران پنل'),
createAdminPlaceholderRoute('users/activity-logs', 'AdminUsersActivityLogs', 'لاگ فعالیت ها', 'مشاهده فعالیت های مدیران و اپراتورهای پنل'),
- createAdminPlaceholderRoute('settings/payment-gateways', 'AdminSettingsPaymentGateways', 'درگاه های پرداخت', 'تنظیم و مدیریت درگاه های پرداخت فعال'),
+ {
+ path: 'settings/payment-gateways',
+ name: 'AdminSettingsPaymentGateways',
+ component: () => import('@/views/admin/AdminSettingsPaymentGateways.vue'),
+ meta: {
+ title: 'درگاه های پرداخت',
+ description: 'تنظیم و مدیریت درگاه های پرداخت فعال',
+ },
+ },
+ {
+ path: 'payments/bank-slips',
+ name: 'AdminPaymentBankSlips',
+ component: () => import('@/views/admin/AdminPaymentBankSlips.vue'),
+ meta: {
+ title: 'فیش های بانکی ارسالی',
+ description: 'بررسی و تایید یا رد فیش های بانکی ثبت شده',
+ },
+ },
createAdminPlaceholderRoute('settings/shipping-methods', 'AdminSettingsShippingMethods', 'روش های ارسال', 'مدیریت روش های ارسال، پیک و پست'),
createAdminPlaceholderRoute('settings/seo', 'AdminSettingsSeo', 'تنظیمات سئو', 'مدیریت تنظیمات فنی و محتوایی سئو فروشگاه'),
{
diff --git a/src/services/admin-api.ts b/src/services/admin-api.ts
index 76c50c1..8937fdb 100644
--- a/src/services/admin-api.ts
+++ b/src/services/admin-api.ts
@@ -82,14 +82,14 @@ export const buildProductFormData = (payload: ProductFormPayload): FormData => {
formData.append('sku', payload.sku)
formData.append('title', payload.title)
formData.append('slug', payload.slug)
- formData.append('summary', payload.summary ?? normalizedMeta.shortDescription)
- formData.append('description', payload.description ?? normalizedMeta.description)
formData.append('meta', JSON.stringify(normalizedMeta))
formData.append('technicalCode', payload.technicalCode)
if (payload.brandId !== undefined) {
formData.append('brandId', payload.brandId)
}
- formData.append('brand', payload.brand)
+ if (payload.brand) {
+ formData.append('brand', payload.brand)
+ }
formData.append('basePriceUSD', String(payload.basePriceUSD))
formData.append('stock', String(payload.stock))
formData.append('featured', String(payload.featured))
@@ -97,14 +97,15 @@ export const buildProductFormData = (payload: ProductFormPayload): FormData => {
formData.append('status', payload.status)
formData.append('attributes', JSON.stringify(payload.attributes || []))
formData.append('tags', JSON.stringify(payload.tags || []))
+ formData.append('categoryIds', JSON.stringify(payload.categoryIds || []))
formData.append('existingGalleryUrls', JSON.stringify(payload.existingGalleryUrls || []))
if (payload.salePriceUSD !== undefined && payload.salePriceUSD !== null) {
formData.append('salePriceUSD', String(payload.salePriceUSD))
}
- if (payload.categoryId) {
- formData.append('categoryId', payload.categoryId)
+ if (payload.primaryCategoryId) {
+ formData.append('primaryCategoryId', payload.primaryCategoryId)
}
if (payload.existingMainImageUrl !== undefined) {
diff --git a/src/services/api.ts b/src/services/api.ts
index 1fa3dcc..6204c36 100644
--- a/src/services/api.ts
+++ b/src/services/api.ts
@@ -1,4 +1,5 @@
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
+import { API_BASE_URL } from '@/config/api'
import type {
ApiResponse,
AuthResponse,
@@ -17,7 +18,7 @@ class ApiService {
constructor() {
this.api = axios.create({
- baseURL: '/api', // استفاده از proxy
+ baseURL: API_BASE_URL,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
diff --git a/src/services/payment-service.ts b/src/services/payment-service.ts
new file mode 100644
index 0000000..74ef715
--- /dev/null
+++ b/src/services/payment-service.ts
@@ -0,0 +1,30 @@
+import apiService from '@/services/api'
+import type {
+ BankSlipOrdersResponse,
+ PaymentMethod,
+ PaymentMethodCode,
+ ReviewBankSlipPayload,
+ ReviewBankSlipResponse,
+ UpdatePaymentMethodPayload,
+} from '@/types/payment'
+
+class PaymentService {
+ async getPaymentMethods(): Promise
{
+ return apiService.get('/admin/payments/methods')
+ }
+
+ async updatePaymentMethod(code: PaymentMethodCode, payload: UpdatePaymentMethodPayload): Promise {
+ return apiService.patch(`/admin/payments/methods/${code}`, payload)
+ }
+
+ async getBankSlipOrders(): Promise {
+ return apiService.get('/admin/payments/bank-slip/orders')
+ }
+
+ async reviewBankSlipOrder(orderId: string, payload: ReviewBankSlipPayload): Promise {
+ return apiService.patch(`/admin/payments/bank-slip/orders/${orderId}/review`, payload)
+ }
+}
+
+export const paymentService = new PaymentService()
+export default paymentService
diff --git a/src/services/settings-service.ts b/src/services/settings-service.ts
new file mode 100644
index 0000000..fa7dbe2
--- /dev/null
+++ b/src/services/settings-service.ts
@@ -0,0 +1,15 @@
+import apiService from '@/services/api'
+import type { PricingSettings, UpdatePricingSettingsPayload } from '@/types/settings'
+
+class SettingsService {
+ async getPricingSettings(): Promise {
+ return apiService.get('/admin/settings/pricing')
+ }
+
+ async updatePricingSettings(payload: UpdatePricingSettingsPayload): Promise {
+ return apiService.patch('/admin/settings/pricing', payload)
+ }
+}
+
+export const settingsService = new SettingsService()
+export default settingsService
diff --git a/src/types/admin-product-create.ts b/src/types/admin-product-create.ts
index a4d4eed..4dc72e5 100644
--- a/src/types/admin-product-create.ts
+++ b/src/types/admin-product-create.ts
@@ -1,6 +1,7 @@
export interface ProductAttributeFormRow {
id: string
attributeId: string
+ displayOrder: number
valueText: string
valueNumber: string
valueBoolean: boolean
diff --git a/src/types/payment.ts b/src/types/payment.ts
new file mode 100644
index 0000000..11cbc9b
--- /dev/null
+++ b/src/types/payment.ts
@@ -0,0 +1,100 @@
+export type PaymentMethodCode =
+ | 'zarinpal'
+ | 'saman'
+ | 'mellat'
+ | 'pasargad'
+ | 'bank_slip'
+ | 'cash_on_delivery'
+
+export type PaymentMethodType = 'online' | 'bank_slip' | 'cash_on_delivery'
+
+export interface PaymentMethod {
+ id: string
+ code: PaymentMethodCode
+ type: PaymentMethodType
+ title: string
+ isEnabled: boolean
+ isSandboxEnabled: boolean
+ displayOrder: number
+ description: string | null
+ instructions: string | null
+ callbackUrl: string | null
+ zarinpalMerchantId: string | null
+ samanTerminalId: string | null
+ mellatTerminalId: string | null
+ mellatUsername: string | null
+ mellatPassword: string | null
+ pasargadMerchantCode: string | null
+ pasargadTerminalCode: string | null
+ pasargadCertificatePem: string | null
+ bankName: string | null
+ accountHolderName: string | null
+ accountNumber: string | null
+ cardNumber: string | null
+ shebaNumber: string | null
+ createdAt: string
+ updatedAt: string
+}
+
+export interface UpdatePaymentMethodPayload {
+ isEnabled?: boolean
+ isSandboxEnabled?: boolean
+ displayOrder?: number
+ title?: string
+ description?: string | null
+ instructions?: string | null
+ callbackUrl?: string | null
+ zarinpalMerchantId?: string | null
+ samanTerminalId?: string | null
+ mellatTerminalId?: string | null
+ mellatUsername?: string | null
+ mellatPassword?: string | null
+ pasargadMerchantCode?: string | null
+ pasargadTerminalCode?: string | null
+ pasargadCertificatePem?: string | null
+ bankName?: string | null
+ accountHolderName?: string | null
+ accountNumber?: string | null
+ cardNumber?: string | null
+ shebaNumber?: string | null
+}
+
+export interface BankSlipOrderUser {
+ id: string
+ fullName: string | null
+ phone: string | null
+ username: string | null
+}
+
+export interface BankSlipOrder {
+ id: string
+ orderNumber: string
+ status: string
+ paymentStatus: string
+ paymentMethod: string
+ paymentGateway: string | null
+ totalAmount: number
+ currency: string
+ bankSlipTrackingNumber: string | null
+ bankSlipImageUrl: string | null
+ bankSlipSubmittedAt: string | null
+ paymentVerifiedAt: string | null
+ paymentReviewedAt: string | null
+ paymentMetadata: Record | null
+ updatedAt: string
+ user: BankSlipOrderUser | null
+}
+
+export interface BankSlipOrdersResponse {
+ items: BankSlipOrder[]
+}
+
+export interface ReviewBankSlipPayload {
+ approved: boolean
+ adminNote: string
+}
+
+export interface ReviewBankSlipResponse {
+ message: string
+ order: BankSlipOrder
+}
diff --git a/src/types/product.ts b/src/types/product.ts
index 7bafd27..dfaf8cf 100644
--- a/src/types/product.ts
+++ b/src/types/product.ts
@@ -21,6 +21,7 @@ export interface ProductAttributeOption {
export interface ProductAttributeAssignment {
attributeId?: string
+ displayOrder?: number
name?: string
slug?: string
dataType?: ProductAttributeDataType
@@ -202,12 +203,12 @@ export interface ProductFormPayload {
sku: string
title: string
slug: string
- summary: string
- description: string
+ summary?: string
+ description?: string
meta?: ProductMeta
technicalCode: string
brandId?: string
- brand: string
+ brand?: string
basePriceUSD: number
salePriceUSD?: number | null | ''
stock: number
@@ -215,6 +216,8 @@ export interface ProductFormPayload {
type: ProductType
status: ProductStatus
categoryId?: string
+ primaryCategoryId?: string
+ categoryIds?: string[]
attributes: ProductAttributes | ProductAttributeAssignment[]
tags: string[]
existingMainImageUrl?: string
diff --git a/src/types/settings.ts b/src/types/settings.ts
new file mode 100644
index 0000000..5950689
--- /dev/null
+++ b/src/types/settings.ts
@@ -0,0 +1,14 @@
+export type CurrencyDisplay = 'IRR' | 'TOMAN'
+
+export interface PricingSettings {
+ id: string
+ usdToIrrRate: number
+ defaultCurrencyDisplay: CurrencyDisplay
+ createdAt: string
+ updatedAt: string
+}
+
+export interface UpdatePricingSettingsPayload {
+ usdToIrrRate: number
+ defaultCurrencyDisplay: CurrencyDisplay
+}
diff --git a/src/views/admin/AdminPaymentBankSlips.vue b/src/views/admin/AdminPaymentBankSlips.vue
new file mode 100644
index 0000000..6b74e02
--- /dev/null
+++ b/src/views/admin/AdminPaymentBankSlips.vue
@@ -0,0 +1,160 @@
+
+
+
+
+
فیش های بانکی ارسالی
+
فیشهای ثبتشده را بررسی کن، تصویر و شماره پیگیری را ببین و نتیجه را ثبت کن.
+
+
+
+
+
+ {{ errorMessage }}
+
+
+
+
+
+
+
+ موردی برای بررسی وجود ندارد.
+
+
+
+
+
+
+
+
{{ order.orderNumber }}
+
{{ order.paymentStatus }} / {{ order.status }}
+
+
منتظر بررسی
+
+
+
+
+
+
![bank slip]()
+
تصویر ندارد
+
+
+
+
+
+
مبلغ
+
{{ formatMoney(order.totalAmount, order.currency) }}
+
+
+
شماره پیگیری
+
{{ order.bankSlipTrackingNumber || '-' }}
+
+
+
+
+
کاربر
+
+
نام: {{ order.user?.fullName || '-' }}
+
موبایل: {{ order.user?.phone || '-' }}
+
نام کاربری: {{ order.user?.username || '-' }}
+
+
+
+
+
یادداشت مشتری
+
{{ getCustomerNote(order) || 'یادداشتی ثبت نشده است.' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/admin/AdminProductEdit.vue b/src/views/admin/AdminProductEdit.vue
index 6d37dc7..afe2848 100644
--- a/src/views/admin/AdminProductEdit.vue
+++ b/src/views/admin/AdminProductEdit.vue
@@ -1,10 +1,10 @@
-
+
diff --git a/src/views/admin/AdminSettingsPaymentGateways.vue b/src/views/admin/AdminSettingsPaymentGateways.vue
new file mode 100644
index 0000000..1d35b66
--- /dev/null
+++ b/src/views/admin/AdminSettingsPaymentGateways.vue
@@ -0,0 +1,378 @@
+
+
+
+
+
درگاه های پرداخت
+
درگاههای بانکی را بهصورت کارتهای مستقل مدیریت کنید. هر کارت با کلیک باز میشود و فقط فیلدهای خودش را نشان میدهد.
+
+
+
+
+
+
+
کل درگاهها
+
{{ paymentMethods.length }}
+
+
+
فعال
+
{{ enabledCount }}
+
+
+
حالت تست
+
{{ sandboxEnabledCount }}
+
+
+
مرتبسازی
+
{{ sortedMethods.length ? sortedMethods[0].displayOrder : 0 }}+
+
+
+
+
+ {{ pageErrorMessage }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vite.config.ts b/vite.config.ts
index ad360b5..31b53d9 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -11,13 +11,7 @@ export default defineConfig({
}
},
server: {
- port: 5173,
- proxy: {
- '/api': {
- target: 'http://localhost:3000',
- changeOrigin: true,
- secure: false
- }
- }
+ host: '0.0.0.0',
+ port: 5173
}
-})
\ No newline at end of file
+})