feat(customers): add Digipay installment admin section

Wire routes, settings groups, page access, and customer UI for installment management.
This commit is contained in:
2026-10-21 03:38:59 -04:00
parent e5b3d0c931
commit 88271dba2a
7 changed files with 129 additions and 1 deletions
@@ -15,6 +15,12 @@ export type SettingGroupDef = {
/** گروه‌های صفحه تنظیمات عادی (فقط can_edit=1) */
export const EDITABLE_SETTING_GROUPS: SettingGroupDef[] = [
{
id: 'digipay_installment',
title: 'خرید اقساطی دیجی‌پی',
icon: 'vuesax-linear:calendar',
match: (k) => k === 'enable_digipay_installment' || k.startsWith('digipay_')
},
{
id: 'general',
title: 'عمومی و برند',
@@ -0,0 +1,84 @@
<template>
<BasicShowContext title="خرید اقساطی" :help="digipayInstallmentPageIntro">
<div class="flex flex-col gap-2 p-2.5">
<div class="text-sm font-bold">ثبت اعتبار بعد از دیجی‌پی</div>
<p class="text-xs opacity-70">
کاربر اول با لینک تنظیمات به دیجی‌پی می‌رود. بعد از احراز، مانده اعتبارش را اینجا بنویسید تا در اپ بتواند مس بخرد.
</p>
<a-form layout="vertical" class="sm:grid sm:grid-cols-2 sm:gap-2" @finish="save">
<a-form-item label="موبایل مشتری" name="mobile" :rules="[{ required: true, message: 'موبایل را وارد کنید' }]">
<a-input v-model:value="form.mobile" placeholder="09..." />
</a-form-item>
<a-form-item label="مانده ۴ قسط (تومان)" name="plan4">
<a-input-number v-model:value="form.plan4_toman" class="w-full" :min="0" />
</a-form-item>
<a-form-item label="مانده ۱۲ قسط (تومان)" name="plan12">
<a-input-number v-model:value="form.plan12_toman" class="w-full" :min="0" />
</a-form-item>
<a-form-item label=" " class="flex items-end">
<a-button type="primary" html-type="submit" :loading="saving">ثبت اعتبار</a-button>
</a-form-item>
</a-form>
<div class="mt-2 text-sm font-bold">خریدهای اخیر</div>
<a-table
size="small"
row-key="id"
:loading="loading"
:data-source="rows"
:pagination="false"
:columns="columns"
/>
</div>
</BasicShowContext>
</template>
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'
import { message } from 'ant-design-vue'
import useAxios from '@core/utils/useAxios'
import { digipayInstallmentPageIntro } from '@/utils/adminHelpCopy'
const form = reactive({ mobile: '', plan4_toman: 0, plan12_toman: 0 })
const saving = ref(false)
const loading = ref(false)
const rows = ref<Record<string, unknown>[]>([])
const columns = [
{ title: 'مشتری', dataIndex: 'name' },
{ title: 'موبایل', dataIndex: 'mobile' },
{ title: 'طرح', dataIndex: 'plan' },
{ title: 'مبلغ مس (تومان)', dataIndex: 'amount_toman' },
{ title: 'کارمزد (تومان)', dataIndex: 'fee_toman' },
{ title: 'مقدار', dataIndex: 'grams' }
]
async function load() {
loading.value = true
try {
const res = await useAxios.post('/admin/digipay_installment/orders')
rows.value = res?.data?.data || res?.data || []
} finally {
loading.value = false
}
}
async function save() {
saving.value = true
try {
await useAxios.post('/admin/digipay_installment/credit', {
mobile: form.mobile,
plan4_toman: form.plan4_toman || 0,
plan12_toman: form.plan12_toman || 0
})
message.success('اعتبار ثبت شد')
await load()
} catch (e: any) {
message.error(e?.response?.data?.message || 'ثبت اعتبار انجام نشد')
} finally {
saving.value = false
}
}
onMounted(load)
</script>
+2 -1
View File
@@ -21,7 +21,7 @@ import { computed } from 'vue'
import BasicMenu from '@/components/BasicMenu/index.vue'
import { useRouterStore } from '@core/stores/router'
import { get } from 'lodash'
import { customerSpecialPricingEnabledSetting, goldCreditEnabledSetting, initWalletSettingsFromApi } from '@/utils/config'
import { customerSpecialPricingEnabledSetting, digipayInstallmentEnabledSetting, goldCreditEnabledSetting, initWalletSettingsFromApi } from '@/utils/config'
initWalletSettingsFromApi()
@@ -36,6 +36,7 @@ const menu = computed(() => {
.filter((child) => {
if (child.name === 'customerMain_specialPrice') return customerSpecialPricingEnabledSetting.value
if (child.name === 'customerMain_goldCredit') return goldCreditEnabledSetting.value
if (child.name === 'customerMain_digipayInstallment') return digipayInstallmentEnabledSetting.value
return true
})
.sort((a, b) => (a.meta?.order ?? 99) - (b.meta?.order ?? 99))
+10
View File
@@ -81,6 +81,16 @@ const Customers: RouteRecordRaw = {
order: 4
}
},
{
name: 'customerMain_digipayInstallment',
path: 'digipay-installment',
component: () => import('@/pages/menuPages/customers/digipayInstallment/index.vue'),
meta: {
icon: 'vuesax-linear:calendar',
title: 'خرید اقساطی',
order: 6
}
},
{
name: 'customerMain_goldCredit',
path: 'gold-credit',
+3
View File
@@ -183,6 +183,9 @@ export const customersArchivePageIntro =
export const customersOnlinePageIntro =
'مشتریانی که الان اپ را باز دارند. با بستن اپ از لیست حذف می‌شوند. برای جزئیات روی کارت بزنید.'
export const digipayInstallmentPageIntro =
'بعد از احراز کاربر در دیجی‌پی، مانده اعتبار ۴ قسط و ۱۲ قسط را با موبایل مشتری اینجا بنویسید. خرید از اپ همان مانده را کم می‌کند و مس را به کیف می‌نشاند.'
export const goldCreditPageIntro =
'اعتبار طلایی مشتریان: درخواست‌های جدید و اعتبارهای فعال. از تب‌ها بین درخواست و فعال جابه‌جا شوید.'
+8
View File
@@ -7,6 +7,7 @@ import {
customerSpecialPricingEnabledSetting,
getConfigApp,
giftCardEnabledSetting,
digipayInstallmentEnabledSetting,
goldCreditEnabledSetting,
hasLandingSetting,
isStoreApp,
@@ -64,6 +65,12 @@ export const ADMIN_PAGE_TREE: AccessNode[] = [
title: 'اعتبار',
route: 'customerMain_goldCredit',
enabled: () => goldCreditEnabledSetting.value
},
{
key: 'customerMain_digipayInstallment',
title: 'خرید اقساطی',
route: 'customerMain_digipayInstallment',
enabled: () => digipayInstallmentEnabledSetting.value
}
]
},
@@ -240,6 +247,7 @@ export const PAGE_TABLE_ACTIONS: Record<string, PageTableAction[]> = {
{ key: 'toggle', label: 'فعال/غیرفعال' },
{ key: 'delete', label: 'حذف' }
],
customerMain_digipayInstallment: [{ key: 'edit', label: 'ثبت اعتبار' }],
customerMain_goldCredit: [
{ key: 'detail', label: 'جزئیات' },
{ key: 'edit', label: 'ویرایش' },
+16
View File
@@ -80,6 +80,9 @@ export const customerSpecialPricingEnabledSetting = ref(false)
/** reactive — زیرمنوی اعتبار طلایی در مشتریان */
export const goldCreditEnabledSetting = ref(false)
/** reactive — زیرمنوی خرید اقساطی دیجی‌پی */
export const digipayInstallmentEnabledSetting = ref(false)
/** reactive — منو و زنگوله اعلان داخل اپ */
export const appNotificationEnabledSetting = ref(true)
@@ -185,6 +188,10 @@ export function setCustomerSpecialPricingEnabledFromSettings(value: boolean): vo
customerSpecialPricingEnabledSetting.value = value
}
export function setDigipayInstallmentEnabledFromSettings(value: boolean): void {
digipayInstallmentEnabledSetting.value = value
}
export function setGoldCreditEnabledFromSettings(value: boolean): void {
goldCreditEnabledSetting.value = value
}
@@ -314,6 +321,13 @@ export function applyCustomerSpecialPricingFromRows(
if (row) setCustomerSpecialPricingEnabledFromSettings(isSwitchOn(row.value))
}
export function applyDigipayInstallmentEnabledFromRows(
rows: Array<{ key?: string; value?: string | null }>
) {
const row = rows.find((item) => item.key === 'enable_digipay_installment')
if (row) setDigipayInstallmentEnabledFromSettings(isSwitchOn(row.value))
}
export function applyGoldCreditEnabledFromRows(
rows: Array<{ key?: string; value?: string | null }>
) {
@@ -340,6 +354,7 @@ export function applySuperAdminAccessFlagsFromItems(
applyGroupSmsFromRows(rows)
applyCustomerSpecialPricingFromRows(rows)
applyGoldCreditEnabledFromRows(rows)
applyDigipayInstallmentEnabledFromRows(rows)
applyConditionalOrderEnabledFromRows(rows)
applyAutomationEnabledFromRows(rows)
applyProductFundsEnabledFromRows(rows)
@@ -442,6 +457,7 @@ async function bootstrapSettingsFromApi(): Promise<void> {
applyGroupSmsFromRows(list)
applyCustomerSpecialPricingFromRows(list)
applyGoldCreditEnabledFromRows(list)
applyDigipayInstallmentEnabledFromRows(list)
applyConditionalOrderEnabledFromRows(list)
applyAutomationEnabledFromRows(list)
applyProductFundsEnabledFromRows(list)