fix moshtari code
This commit is contained in:
+38
-30
@@ -6,6 +6,8 @@ import {
|
|||||||
LEGACY_SLIDER_BANNERS_CONST_KEY,
|
LEGACY_SLIDER_BANNERS_CONST_KEY,
|
||||||
LEGACY_THEME_BANNERS_CONST_KEY,
|
LEGACY_THEME_BANNERS_CONST_KEY,
|
||||||
emptyAppBanners,
|
emptyAppBanners,
|
||||||
|
isThemeBannersEmpty,
|
||||||
|
mergeAppBannersFromLegacy,
|
||||||
parseAppBanners,
|
parseAppBanners,
|
||||||
serializeAppBanners,
|
serializeAppBanners,
|
||||||
type AppBannersValue,
|
type AppBannersValue,
|
||||||
@@ -51,6 +53,8 @@ export type AppBannersConstState = {
|
|||||||
/** فقط وقتی رکورد واقعاً با key=app_banners است؛ وگرنه null تا create شود */
|
/** فقط وقتی رکورد واقعاً با key=app_banners است؛ وگرنه null تا create شود */
|
||||||
id: number | null
|
id: number | null
|
||||||
data: AppBannersValue
|
data: AppBannersValue
|
||||||
|
/** از settings_profile_banners پر شده — بهتر است یکبار ذخیره شود */
|
||||||
|
healedFromLegacy?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadLegacySliderInto(merged: AppBannersValue): Promise<void> {
|
async function loadLegacySliderInto(merged: AppBannersValue): Promise<void> {
|
||||||
@@ -67,9 +71,22 @@ async function loadLegacySliderInto(merged: AppBannersValue): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function loadLegacyThemeBanners(): Promise<AppBannersValue> {
|
||||||
|
try {
|
||||||
|
const themeRes = await useAxios.get<Const | Const[]>(
|
||||||
|
`/api/const/key/${LEGACY_THEME_BANNERS_CONST_KEY}`
|
||||||
|
)
|
||||||
|
const themeRow = pickConstRow(themeRes)
|
||||||
|
if (!themeRow) return emptyAppBanners()
|
||||||
|
return parseAppBanners(themeRow.value)
|
||||||
|
} catch {
|
||||||
|
return emptyAppBanners()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* یک API واحد برای همهٔ بنرها.
|
* یک API واحد برای همهٔ بنرها.
|
||||||
* اگر `app_banners` نبود یا اسلایدر خالی بود، از کلیدهای قدیمی migrate میکند (فقط خواندن).
|
* اگر `app_banners` نبود یا اسلاتهای تم خالی بود، از settings_profile_banners پر میکند.
|
||||||
*/
|
*/
|
||||||
export async function loadAppBannersConst(): Promise<AppBannersConstState> {
|
export async function loadAppBannersConst(): Promise<AppBannersConstState> {
|
||||||
let appRow: Const | null = null
|
let appRow: Const | null = null
|
||||||
@@ -81,44 +98,36 @@ export async function loadAppBannersConst(): Promise<AppBannersConstState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (appRow) {
|
if (appRow) {
|
||||||
const data = parseAppBanners(appRow.value)
|
let data = parseAppBanners(appRow.value)
|
||||||
// رکورد جدید خالی: اسلایدر را از header_banner پر کن
|
let healedFromLegacy = false
|
||||||
if (!data.slider.length) {
|
|
||||||
await loadLegacySliderInto(data)
|
// رکورد پوچ app_banners روی URLهای واقعی legacy سایه نیندازد
|
||||||
|
if (isThemeBannersEmpty(data) || !data.slider.length) {
|
||||||
|
const before = serializeAppBanners(data)
|
||||||
|
if (isThemeBannersEmpty(data)) {
|
||||||
|
const legacyTheme = await loadLegacyThemeBanners()
|
||||||
|
data = mergeAppBannersFromLegacy(data, legacyTheme)
|
||||||
|
}
|
||||||
|
if (!data.slider.length) {
|
||||||
|
await loadLegacySliderInto(data)
|
||||||
|
}
|
||||||
|
healedFromLegacy = serializeAppBanners(data) !== before
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: appRow.id ?? appRow.const_id ?? null,
|
id: appRow.id ?? appRow.const_id ?? null,
|
||||||
data,
|
data,
|
||||||
|
healedFromLegacy,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const merged = emptyAppBanners()
|
const merged = emptyAppBanners()
|
||||||
|
const legacyTheme = await loadLegacyThemeBanners()
|
||||||
try {
|
Object.assign(merged, mergeAppBannersFromLegacy(merged, legacyTheme))
|
||||||
const themeRes = await useAxios.get<Const | Const[]>(
|
|
||||||
`/api/const/key/${LEGACY_THEME_BANNERS_CONST_KEY}`
|
|
||||||
)
|
|
||||||
const themeRow = pickConstRow(themeRes)
|
|
||||||
if (themeRow) {
|
|
||||||
const themes = parseAppBanners(themeRow.value)
|
|
||||||
Object.assign(merged, {
|
|
||||||
homeAsset: themes.homeAsset,
|
|
||||||
piggyCreate: themes.piggyCreate,
|
|
||||||
productsHero: themes.productsHero?.length ? themes.productsHero : merged.productsHero,
|
|
||||||
referral: themes.referral,
|
|
||||||
share: themes.share,
|
|
||||||
invite: themes.invite,
|
|
||||||
slider: themes.slider?.length ? themes.slider : merged.slider,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
await loadLegacySliderInto(merged)
|
await loadLegacySliderInto(merged)
|
||||||
|
|
||||||
// id=null → اولین ذخیره، رکورد جدید با key=app_banners میسازد
|
const hasLegacy = !isThemeBannersEmpty(merged) || merged.slider.length > 0
|
||||||
return { id: null, data: merged }
|
return { id: null, data: merged, healedFromLegacy: hasLegacy }
|
||||||
}
|
}
|
||||||
|
|
||||||
async function syncLegacySliderConst(slides: IBenefits[]): Promise<void> {
|
async function syncLegacySliderConst(slides: IBenefits[]): Promise<void> {
|
||||||
@@ -177,7 +186,6 @@ export async function saveAppBannersConst(
|
|||||||
nextId = created?.id ?? created?.const_id ?? null
|
nextId = created?.id ?? created?.const_id ?? null
|
||||||
}
|
}
|
||||||
|
|
||||||
// اپ مشتری هنوز header_banner میخواند — اسلایدر را همگام نگه دار
|
|
||||||
try {
|
try {
|
||||||
await syncLegacySliderConst(data.slider ?? [])
|
await syncLegacySliderConst(data.slider ?? [])
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ async function load(force = false) {
|
|||||||
constId.value = res.id
|
constId.value = res.id
|
||||||
applyLoadedData(res.data)
|
applyLoadedData(res.data)
|
||||||
loaded.value = true
|
loaded.value = true
|
||||||
|
// عمداً auto-save نمیکنیم: URLهای legacy ممکن است ۴۰۴ باشند و app_banners را خراب کنند
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (rev !== localRevision) {
|
if (rev !== localRevision) {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -74,6 +74,43 @@ function pairHasImage(pair: ThemeImagePair): boolean {
|
|||||||
return Boolean(pair.light || pair.dark)
|
return Boolean(pair.light || pair.dark)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** همهٔ اسلاتهای تم خالیاند (رکورد app_banners پوچ که روی legacy سایه انداخته) */
|
||||||
|
export function isThemeBannersEmpty(data: AppBannersValue): boolean {
|
||||||
|
return (
|
||||||
|
!pairHasImage(data.homeAsset) &&
|
||||||
|
!pairHasImage(data.piggyCreate) &&
|
||||||
|
!(data.productsHero ?? []).some(pairHasImage) &&
|
||||||
|
!pairHasImage(data.referral) &&
|
||||||
|
!pairHasImage(data.share) &&
|
||||||
|
!pairHasImage(data.invite)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergePair(primary: ThemeImagePair, legacy: ThemeImagePair): ThemeImagePair {
|
||||||
|
return {
|
||||||
|
light: primary.light || legacy.light,
|
||||||
|
dark: primary.dark || legacy.dark,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** اسلات خالی primary را از legacy پر میکند؛ اسلایدر/محصولات اگر آرایه خالی باشد از legacy میآید */
|
||||||
|
export function mergeAppBannersFromLegacy(
|
||||||
|
primary: AppBannersValue,
|
||||||
|
legacy: AppBannersValue,
|
||||||
|
): AppBannersValue {
|
||||||
|
return {
|
||||||
|
slider: primary.slider.length ? primary.slider : legacy.slider,
|
||||||
|
homeAsset: mergePair(primary.homeAsset, legacy.homeAsset),
|
||||||
|
piggyCreate: mergePair(primary.piggyCreate, legacy.piggyCreate),
|
||||||
|
productsHero: (primary.productsHero ?? []).some(pairHasImage)
|
||||||
|
? primary.productsHero
|
||||||
|
: legacy.productsHero ?? [],
|
||||||
|
referral: mergePair(primary.referral, legacy.referral),
|
||||||
|
share: mergePair(primary.share, legacy.share),
|
||||||
|
invite: mergePair(primary.invite, legacy.invite),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** آرایهٔ جدید یا سه اسلات قدیمی productsHero1/2/3 */
|
/** آرایهٔ جدید یا سه اسلات قدیمی productsHero1/2/3 */
|
||||||
export function normalizeProductsHero(parsed: Record<string, unknown>): ThemeImagePair[] {
|
export function normalizeProductsHero(parsed: Record<string, unknown>): ThemeImagePair[] {
|
||||||
if (Array.isArray(parsed.productsHero)) {
|
if (Array.isArray(parsed.productsHero)) {
|
||||||
|
|||||||
@@ -239,7 +239,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { message, Modal } from 'ant-design-vue'
|
import { message, Modal } from 'ant-design-vue'
|
||||||
import { onMounted, ref, computed } from 'vue'
|
import { onMounted, ref, computed, watch } from 'vue'
|
||||||
import {
|
import {
|
||||||
changeCustomerActivation,
|
changeCustomerActivation,
|
||||||
changeCustomerArchive,
|
changeCustomerArchive,
|
||||||
@@ -323,10 +323,15 @@ function rebuildColumns(metals = catalogMetals.value) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch(accountingProviderSetting, () => {
|
||||||
|
rebuildColumns()
|
||||||
|
})
|
||||||
|
|
||||||
const tblProps = ref<any>({
|
const tblProps = ref<any>({
|
||||||
columns: buildCustomerColumns([], { accountingProvider: accountingProviderSetting.value }),
|
columns: buildCustomerColumns([], { accountingProvider: accountingProviderSetting.value }),
|
||||||
api: async (params: TableInput) => {
|
api: async (params: TableInput) => {
|
||||||
await initAccountingSettingsFromApi()
|
await initAccountingSettingsFromApi()
|
||||||
|
rebuildColumns(catalogMetals.value)
|
||||||
const data = await getCustomerTable(params)
|
const data = await getCustomerTable(params)
|
||||||
const metals = data?.catalogMetals ?? []
|
const metals = data?.catalogMetals ?? []
|
||||||
rebuildColumns(metals)
|
rebuildColumns(metals)
|
||||||
@@ -341,8 +346,11 @@ const fetch = async () => {
|
|||||||
defineExpose({ fetch })
|
defineExpose({ fetch })
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
void initAccountingSettingsFromApi()
|
void (async () => {
|
||||||
void initWalletSettingsFromApi()
|
await initAccountingSettingsFromApi()
|
||||||
|
rebuildColumns()
|
||||||
|
await initWalletSettingsFromApi()
|
||||||
|
})()
|
||||||
})
|
})
|
||||||
|
|
||||||
function openDirectSms(user: Customer) {
|
function openDirectSms(user: Customer) {
|
||||||
|
|||||||
@@ -15,12 +15,16 @@ export type CustomerColumnOptions = {
|
|||||||
|
|
||||||
function accountingCodeColumn(provider: AccountingProvider | undefined): BaseTableColumn | null {
|
function accountingCodeColumn(provider: AccountingProvider | undefined): BaseTableColumn | null {
|
||||||
if (provider === 'pargar' || provider === 'tahesab' || provider === 'zarin') {
|
if (provider === 'pargar' || provider === 'tahesab' || provider === 'zarin') {
|
||||||
|
const field = provider === 'pargar' ? 'pargar_account_id' : 'moshtari_code'
|
||||||
return {
|
return {
|
||||||
title: 'کد حسابداری',
|
title: 'کد حسابداری',
|
||||||
dataIndex: provider === 'pargar' ? 'pargar_account_id' : 'moshtari_code',
|
dataIndex: field,
|
||||||
key: 'accounting_code',
|
key: 'accounting_code',
|
||||||
|
filterField: field,
|
||||||
|
sortField: field,
|
||||||
type: 'Text',
|
type: 'Text',
|
||||||
filterable: false,
|
filterable: true,
|
||||||
|
sortable: true,
|
||||||
slot: 'accountingCode'
|
slot: 'accountingCode'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,6 +78,8 @@ export function buildCustomerColumns(
|
|||||||
dataIndex: 'wallet.cash',
|
dataIndex: 'wallet.cash',
|
||||||
key: 'wallet.cash',
|
key: 'wallet.cash',
|
||||||
type: 'Number',
|
type: 'Number',
|
||||||
|
filterable: false,
|
||||||
|
sortable: false,
|
||||||
customRender(data) {
|
customRender(data) {
|
||||||
return usePrice(getCashBalance(data.record?.wallet, { walletCash: data.record?.wallet_cash }), true, '')
|
return usePrice(getCashBalance(data.record?.wallet, { walletCash: data.record?.wallet_cash }), true, '')
|
||||||
}
|
}
|
||||||
@@ -116,18 +122,18 @@ export function buildCustomerColumns(
|
|||||||
key: 'last_seen_at',
|
key: 'last_seen_at',
|
||||||
type: 'DateTime',
|
type: 'DateTime',
|
||||||
width: 130,
|
width: 130,
|
||||||
filterable: false,
|
filterable: true,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
customRender: ({ record }) => <CustomerPresenceCell lastSeenAt={record.last_seen_at} />
|
customRender: ({ record }) => <CustomerPresenceCell lastSeenAt={record.last_seen_at} />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'وضعیت',
|
title: 'وضعیت حساب',
|
||||||
dataIndex: 'active',
|
dataIndex: 'active',
|
||||||
key: 'status',
|
key: 'status',
|
||||||
type: 'Boolean',
|
type: 'Boolean',
|
||||||
width: 132,
|
width: 132,
|
||||||
filterable: false,
|
filterable: true,
|
||||||
sortable: false,
|
sortable: true,
|
||||||
customRender: ({ record }) => {
|
customRender: ({ record }) => {
|
||||||
const userOk = Boolean(record.active)
|
const userOk = Boolean(record.active)
|
||||||
const tradeOk = Boolean(record.trade_active)
|
const tradeOk = Boolean(record.trade_active)
|
||||||
@@ -151,6 +157,15 @@ export function buildCustomerColumns(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'وضعیت سفارش',
|
||||||
|
dataIndex: 'trade_active',
|
||||||
|
key: 'trade_active',
|
||||||
|
type: 'Boolean',
|
||||||
|
filterable: true,
|
||||||
|
sortable: true,
|
||||||
|
show: false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'عملیات',
|
title: 'عملیات',
|
||||||
dataIndex: 'action',
|
dataIndex: 'action',
|
||||||
|
|||||||
Reference in New Issue
Block a user