fix kashkool
This commit is contained in:
@@ -1,158 +0,0 @@
|
||||
import {
|
||||
buildSupportMessengerUrl,
|
||||
loadSupportMessengerFromSettings,
|
||||
supportMessengerSettings,
|
||||
} from '@/utils/supportMessenger'
|
||||
|
||||
const STORAGE_KEY = 'ghatreh_whatsapp_button_position'
|
||||
const DRAG_THRESHOLD = 6
|
||||
const WIDTH = 60
|
||||
const HEIGHT = 60
|
||||
const DEFAULT_BOTTOM_OFFSET = 90
|
||||
const DEFAULT_RIGHT_OFFSET = 20
|
||||
|
||||
type FabPosition = { x: number; y: number }
|
||||
|
||||
function getViewportWidth() {
|
||||
return Math.min(window.innerWidth, 426)
|
||||
}
|
||||
|
||||
function getHorizontalOffset() {
|
||||
const vw = window.innerWidth
|
||||
const contentWidth = getViewportWidth()
|
||||
return Math.max(0, (vw - contentWidth) / 2)
|
||||
}
|
||||
|
||||
function getDefaultPosition(): FabPosition {
|
||||
const vh = window.innerHeight
|
||||
const offsetX = getHorizontalOffset()
|
||||
const contentWidth = getViewportWidth()
|
||||
|
||||
return {
|
||||
x: offsetX + contentWidth - WIDTH - DEFAULT_RIGHT_OFFSET,
|
||||
y: vh - DEFAULT_BOTTOM_OFFSET - HEIGHT,
|
||||
}
|
||||
}
|
||||
|
||||
function loadPosition(): FabPosition | null {
|
||||
try {
|
||||
const raw = localStorage.getItem(STORAGE_KEY)
|
||||
if (!raw) return null
|
||||
const parsed = JSON.parse(raw) as FabPosition
|
||||
if (typeof parsed?.x === 'number' && typeof parsed?.y === 'number') {
|
||||
return parsed
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function savePosition(pos: FabPosition) {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(pos))
|
||||
}
|
||||
|
||||
function clampPosition(pos: FabPosition): FabPosition {
|
||||
const vw = window.innerWidth
|
||||
const vh = window.innerHeight
|
||||
|
||||
return {
|
||||
x: Math.max(0, Math.min(vw - WIDTH, pos.x)),
|
||||
y: Math.max(0, Math.min(vh - HEIGHT, pos.y)),
|
||||
}
|
||||
}
|
||||
|
||||
function applyPosition(button: HTMLElement, pos: FabPosition) {
|
||||
button.style.position = 'fixed'
|
||||
button.style.bottom = 'auto'
|
||||
button.style.right = 'auto'
|
||||
button.style.left = `${pos.x}px`
|
||||
button.style.top = `${pos.y}px`
|
||||
button.style.touchAction = 'none'
|
||||
}
|
||||
|
||||
function updateWhatsappHref(button: HTMLAnchorElement) {
|
||||
const link = buildSupportMessengerUrl(
|
||||
supportMessengerSettings.value.messenger,
|
||||
supportMessengerSettings.value.messengerId
|
||||
)
|
||||
|
||||
button.href = link || '#'
|
||||
button.style.display = 'flex'
|
||||
}
|
||||
|
||||
export function initWhatsappButtonDrag() {
|
||||
const button = document.getElementById('whatsapp-button') as HTMLAnchorElement | null
|
||||
if (!button) return
|
||||
|
||||
let pos = clampPosition(loadPosition() ?? getDefaultPosition())
|
||||
applyPosition(button, pos)
|
||||
|
||||
let pointerId: number | null = null
|
||||
let startX = 0
|
||||
let startY = 0
|
||||
let originX = 0
|
||||
let originY = 0
|
||||
let hasMoved = false
|
||||
let dragging = false
|
||||
|
||||
function onResize() {
|
||||
pos = clampPosition(pos)
|
||||
applyPosition(button!, pos)
|
||||
savePosition(pos)
|
||||
}
|
||||
|
||||
function onPointerDown(event: PointerEvent) {
|
||||
dragging = true
|
||||
hasMoved = false
|
||||
pointerId = event.pointerId
|
||||
startX = event.clientX
|
||||
startY = event.clientY
|
||||
originX = pos.x
|
||||
originY = pos.y
|
||||
button!.classList.add('is-dragging')
|
||||
button!.setPointerCapture(event.pointerId)
|
||||
event.preventDefault()
|
||||
}
|
||||
|
||||
function onPointerMove(event: PointerEvent) {
|
||||
if (!dragging || pointerId !== event.pointerId) return
|
||||
|
||||
const dx = event.clientX - startX
|
||||
const dy = event.clientY - startY
|
||||
|
||||
if (Math.abs(dx) > DRAG_THRESHOLD || Math.abs(dy) > DRAG_THRESHOLD) {
|
||||
hasMoved = true
|
||||
}
|
||||
|
||||
pos = clampPosition({ x: originX + dx, y: originY + dy })
|
||||
applyPosition(button!, pos)
|
||||
}
|
||||
|
||||
function finishPointer(event: PointerEvent) {
|
||||
if (!dragging || pointerId !== event.pointerId) return
|
||||
|
||||
dragging = false
|
||||
pointerId = null
|
||||
button!.classList.remove('is-dragging')
|
||||
savePosition(pos)
|
||||
|
||||
const clicked = !hasMoved
|
||||
hasMoved = false
|
||||
|
||||
if (clicked && button!.href && button!.href !== '#') {
|
||||
window.open(button!.href, '_blank', 'noopener,noreferrer')
|
||||
}
|
||||
}
|
||||
|
||||
button.addEventListener('pointerdown', onPointerDown)
|
||||
button.addEventListener('pointermove', onPointerMove)
|
||||
button.addEventListener('pointerup', finishPointer)
|
||||
button.addEventListener('pointercancel', finishPointer)
|
||||
button.addEventListener('click', (event) => event.preventDefault())
|
||||
window.addEventListener('resize', onResize)
|
||||
|
||||
void loadSupportMessengerFromSettings().then(() => {
|
||||
updateWhatsappHref(button)
|
||||
})
|
||||
}
|
||||
+25
-6
@@ -20,14 +20,28 @@ export const olderPrice = ref<number | undefined>(0)
|
||||
let lastPriceSourceAt = 0
|
||||
let consecutivePriceFetchFailures = 0
|
||||
|
||||
/** buy/sell نمایش و معامله — همان buy/sell/price_last از /api/price/last */
|
||||
export function getTradeBuyPrice(p?: Price | null) {
|
||||
return p?.buy ?? p?.price_last ?? p?.up?.buy ?? 0
|
||||
}
|
||||
|
||||
export function getTradeSellPrice(p?: Price | null) {
|
||||
return p?.sell ?? p?.up?.sell ?? 0
|
||||
}
|
||||
|
||||
/** up جدا نگه داشته میشود؛ buy/sell/price_last از lastPrice سرور دستنخورده میماند */
|
||||
export function normalizePriceResponse(data: Price): Price {
|
||||
return data
|
||||
}
|
||||
|
||||
function hasValidPricePayload(data?: Price | null): boolean {
|
||||
if (!data) {
|
||||
return false
|
||||
}
|
||||
|
||||
return (
|
||||
Number(data.buy ?? data.up?.buy ?? 0) > 0
|
||||
|| Number(data.sell ?? data.up?.sell ?? 0) > 0
|
||||
getTradeBuyPrice(data) > 0
|
||||
|| getTradeSellPrice(data) > 0
|
||||
|| Number(data.price_last ?? data.price ?? 0) > 0
|
||||
)
|
||||
}
|
||||
@@ -147,13 +161,18 @@ export async function getLastPrices(): Promise<boolean> {
|
||||
}
|
||||
|
||||
export function applyLastPricePayload(dataLast: Price) {
|
||||
if (lastPrice.value?.price_last != dataLast.price_last) {
|
||||
const normalized = normalizePriceResponse(dataLast)
|
||||
if (lastPrice.value?.price_last != normalized.price_last) {
|
||||
olderPrice.value = lastPrice.value?.price_last
|
||||
}
|
||||
lastPrice.value = dataLast
|
||||
price.value = dataLast
|
||||
lastPrice.value = normalized
|
||||
price.value = {
|
||||
...normalized,
|
||||
buy: getTradeBuyPrice(normalized),
|
||||
sell: getTradeSellPrice(normalized),
|
||||
}
|
||||
|
||||
if (hasValidPricePayload(dataLast)) {
|
||||
if (hasValidPricePayload(normalized)) {
|
||||
markPriceSourceReceived()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,16 +189,14 @@ async function runFallbackPoll() {
|
||||
const { getLastPrices, hasRecentPriceFromSource, markPriceSourceFetchFailed } = await import(
|
||||
'@/utils/price'
|
||||
)
|
||||
const { getProducts } = await import('@/api/products')
|
||||
const { applyProductsListFromStream: applyList } = await import('@/utils/productStreamBridge')
|
||||
const [lastResult, products] = await Promise.allSettled([
|
||||
getLastPrices(),
|
||||
getProducts(),
|
||||
])
|
||||
const { refreshProductsPageData } = await import(
|
||||
'@/views/base/products/composables/useProductsPageData'
|
||||
)
|
||||
|
||||
if (products.status === 'fulfilled' && Array.isArray(products.value) && products.value.length) {
|
||||
applyList(products.value)
|
||||
}
|
||||
const [lastResult] = await Promise.allSettled([
|
||||
getLastPrices(),
|
||||
refreshProductsPageData(),
|
||||
])
|
||||
|
||||
const priceOk =
|
||||
lastResult.status === 'fulfilled'
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import type { Product } from '@/models/products'
|
||||
import type { WalletInventoryCard, WalletMetalTotal } from '@/models/walletAssets'
|
||||
import { resolveProductImageSrc } from '@/utils/productImage'
|
||||
|
||||
export function fundPageInventoryCards<T extends { quantity?: number }>(cards: T[]): T[] {
|
||||
return cards.filter((card) => Number(card.quantity ?? 0) > 0)
|
||||
}
|
||||
|
||||
export function resolveSummaryMetalWeight(
|
||||
metal: { total_weight: number },
|
||||
_cards?: Array<{ metal_id?: number; quantity?: number }>
|
||||
): number {
|
||||
return Number(metal.total_weight ?? 0)
|
||||
}
|
||||
|
||||
export function enrichInventoryCardsWithCatalog<T extends WalletInventoryCard>(
|
||||
cards: T[],
|
||||
catalog: Product[]
|
||||
): T[] {
|
||||
return cards.map((card) => {
|
||||
const fromCatalog = catalog.find((product) => product.id === card.product_id)
|
||||
if (!fromCatalog) {
|
||||
return card
|
||||
}
|
||||
|
||||
return {
|
||||
...card,
|
||||
file: card.file ?? fromCatalog.file ?? null,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function getProductDisplayImage(product?: {
|
||||
group_type?: string | null
|
||||
title?: string | null
|
||||
file?: { src?: string | null } | null
|
||||
img?: string | null
|
||||
} | null): string {
|
||||
return resolveProductImageSrc(product)
|
||||
}
|
||||
|
||||
export function formatMetalTotal(
|
||||
metal: WalletMetalTotal,
|
||||
inventoryCards: WalletInventoryCard[]
|
||||
): string {
|
||||
return `${resolveSummaryMetalWeight(metal, inventoryCards)}${metal.unit}`
|
||||
}
|
||||
|
||||
export function resolveAssetsRialValue(
|
||||
summary: {
|
||||
total_rial_value?: number
|
||||
assets_rial_value?: number
|
||||
cash_wallet?: number
|
||||
} | null | undefined,
|
||||
liveGoldPrice = 0
|
||||
): number {
|
||||
const total = Number(summary?.total_rial_value ?? 0)
|
||||
if (total > 0) {
|
||||
return total
|
||||
}
|
||||
|
||||
const cash = Number(summary?.cash_wallet ?? 0)
|
||||
const assetsRial = Number(summary?.assets_rial_value ?? 0)
|
||||
if (assetsRial > 0) {
|
||||
return cash + assetsRial
|
||||
}
|
||||
|
||||
const metalWeight = (summary as { metal_totals?: Array<{ total_weight?: number }> } | null)
|
||||
?.metal_totals?.[0]?.total_weight
|
||||
return cash + Number(metalWeight ?? 0) * liveGoldPrice
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
import type { Product } from '@/models/products'
|
||||
import { getTradeBuyPrice, getTradeSellPrice, lastPrice } from '@/utils/price'
|
||||
|
||||
/**
|
||||
* قیمتگیری ماژولار محصول در حالت فروشگاهی (is_store=true).
|
||||
*
|
||||
* منبع حقیقت قیمت و وضعیت نمایش، سرور است: `/customer/product/list` و رویداد SSE
|
||||
* `list` هر دو قیمت هر محصول (`price`/`price_profit`) و `show` را محاسبهشده میفرستند.
|
||||
* این ماژول فقط همان قیمت سرور را میخواند و فقط برای آبشده وقتی قیمت سرور صفر است
|
||||
* به قیمت زندهٔ مرجع (`lastPrice`) fallback میکند — هیچجا قیمت سرور بازنویسی نمیشود.
|
||||
*/
|
||||
|
||||
const MELTED_GROUP = 'آبشده'
|
||||
|
||||
export function isMeltedProduct(product?: Product | null): boolean {
|
||||
return product?.group_type === MELTED_GROUP
|
||||
}
|
||||
|
||||
/** قیمت پایهٔ خرید (بدون کارمزد) — قیمت سرور، با fallback زندهٔ آبشده وقتی سرور صفر است */
|
||||
export function getProductBaseBuyPrice(product?: Product | null): number {
|
||||
if (!product) {
|
||||
return 0
|
||||
}
|
||||
|
||||
const serverPrice = Number(product.price ?? 0)
|
||||
if (serverPrice > 0) {
|
||||
return serverPrice
|
||||
}
|
||||
|
||||
if (isMeltedProduct(product)) {
|
||||
const liveBuy = getTradeBuyPrice(lastPrice.value)
|
||||
if (liveBuy > 0) {
|
||||
const amount = Number(product.amount ?? 0)
|
||||
return amount > 0 ? liveBuy * amount : liveBuy
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
/** قیمت خرید نمایشی محصول (همان مقداری که در لیست و صفحهٔ محصول نشان داده میشود) */
|
||||
export function getProductBuyPrice(product?: Product | null): number {
|
||||
const base = getProductBaseBuyPrice(product)
|
||||
if (base <= 0) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return base + Number(product?.cash_fee_buy ?? 0)
|
||||
}
|
||||
|
||||
/** قیمت پایهٔ فروش (بدون کارمزد) — قیمت فروش سرور، با fallback زندهٔ آبشده */
|
||||
export function getProductBaseSellPrice(product?: Product | null): number {
|
||||
if (!product) {
|
||||
return 0
|
||||
}
|
||||
|
||||
const serverSell = Number(product.sellPrice ?? 0)
|
||||
if (serverSell > 0) {
|
||||
return serverSell
|
||||
}
|
||||
|
||||
if (isMeltedProduct(product)) {
|
||||
const liveSell = getTradeSellPrice(lastPrice.value)
|
||||
if (liveSell > 0) {
|
||||
const amount = Number(product.amount ?? 0)
|
||||
return amount > 0 ? liveSell * amount : liveSell
|
||||
}
|
||||
}
|
||||
|
||||
return getProductBaseBuyPrice(product)
|
||||
}
|
||||
|
||||
/** قیمت فروش نمایشی محصول */
|
||||
export function getProductSellPrice(product?: Product | null): number {
|
||||
const base = getProductBaseSellPrice(product)
|
||||
if (base <= 0) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return base - Number(product?.cash_fee_sell ?? 0)
|
||||
}
|
||||
|
||||
/** محصول وقتی قیمت پایهٔ معتبری ندارد، غیرفعال (غیرقابل معامله) است */
|
||||
export function isProductPriceInactive(product?: Product | null): boolean {
|
||||
return getProductBaseBuyPrice(product) <= 0
|
||||
}
|
||||
|
||||
/**
|
||||
* نمایش محصول در فروشگاه — اولویت با `show` سرور.
|
||||
* فقط برای آبشدهای که قیمت سرورش هنوز نرسیده، با قیمت زنده تکمیل میکنیم.
|
||||
*/
|
||||
export function isProductVisible(product?: Product | null): boolean {
|
||||
if (!product || product.active === false) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (product.show) {
|
||||
return true
|
||||
}
|
||||
|
||||
// سازگاری: آبشدهای که سرور هنوز قیمت نداده ولی قیمت زنده موجود است
|
||||
return isMeltedProduct(product) && getProductBaseBuyPrice(product) > 0
|
||||
}
|
||||
@@ -3,10 +3,15 @@ import type { Product } from '@/models/products'
|
||||
type ProductListHandler = (products: Product[]) => void
|
||||
|
||||
const handlers = new Set<ProductListHandler>()
|
||||
let lastProductsList: Product[] | null = null
|
||||
|
||||
export function subscribeProductListStream(handler: ProductListHandler) {
|
||||
handlers.add(handler)
|
||||
|
||||
if (lastProductsList?.length) {
|
||||
handler(lastProductsList)
|
||||
}
|
||||
|
||||
return () => {
|
||||
handlers.delete(handler)
|
||||
}
|
||||
@@ -17,5 +22,10 @@ export function applyProductsListFromStream(products: Product[]) {
|
||||
return
|
||||
}
|
||||
|
||||
lastProductsList = products
|
||||
handlers.forEach((handler) => handler(products))
|
||||
}
|
||||
|
||||
export function getLastStreamProductsList(): Product[] | null {
|
||||
return lastProductsList
|
||||
}
|
||||
|
||||
+46
-1
@@ -1,6 +1,6 @@
|
||||
import { ref } from 'vue'
|
||||
import { useSettingsStore } from '@/store/settingsStore'
|
||||
import { lastPrice } from '@/utils/price'
|
||||
import { getTradeBuyPrice, getTradeSellPrice, lastPrice } from '@/utils/price'
|
||||
|
||||
export interface StoreTradingHours {
|
||||
start: string
|
||||
@@ -162,3 +162,48 @@ export function getStoreClosedMessage(): string {
|
||||
|
||||
return `فروشگاه در حال حاضر بسته است. ساعت کاری از ${open} تا ${close} میباشد.`
|
||||
}
|
||||
|
||||
export type PriceDisplayState = 'open' | 'closed' | 'no_quote'
|
||||
|
||||
export const NO_QUOTE_MESSAGE = 'مظنه در دسترس نیست'
|
||||
|
||||
/**
|
||||
* وضعیت نمایش قیمت در صفحه اصلی:
|
||||
* - closed → فروشگاه بسته است (خارج از ساعت کاری close_price یا بازه محصول)
|
||||
* - no_quote → مظنه در دسترس نیست (قیمت زنده نداریم و show_last_price خاموش است)
|
||||
* - open → نمایش عادی قیمت
|
||||
*
|
||||
* اولویت با status ارسالی از سرور است؛ اگر نبود به منطق سمت کلاینت برمیگردیم.
|
||||
*/
|
||||
export function getPriceDisplayState(): PriceDisplayState {
|
||||
void productTradingHoursVersion.value
|
||||
const p = lastPrice.value
|
||||
const status = p?.status
|
||||
|
||||
if (status === 'closed') {
|
||||
return 'closed'
|
||||
}
|
||||
if (status === 'no_quote') {
|
||||
return getTradeBuyPrice(p) > 0 ? 'open' : 'no_quote'
|
||||
}
|
||||
if (status === 'open') {
|
||||
return getTradeBuyPrice(p) > 0 || getTradeSellPrice(p) > 0 ? 'open' : 'no_quote'
|
||||
}
|
||||
|
||||
// سرور status نفرستاده (سازگاری با نسخه قدیمی) → استنتاج سمت کلاینت
|
||||
if (isStoreClosed()) {
|
||||
return 'closed'
|
||||
}
|
||||
|
||||
return getTradeBuyPrice(p) > 0 || getTradeSellPrice(p) > 0 ? 'open' : 'no_quote'
|
||||
}
|
||||
|
||||
export function getPriceDisplayMessage(state: PriceDisplayState = getPriceDisplayState()): string {
|
||||
if (state === 'closed') {
|
||||
return getStoreClosedMessage()
|
||||
}
|
||||
if (state === 'no_quote') {
|
||||
return NO_QUOTE_MESSAGE
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
+75
-43
@@ -55,61 +55,93 @@ _axios.interceptors.response.use(
|
||||
return response.data
|
||||
},
|
||||
async function (error) {
|
||||
if (error.response.data && error.response.data.message) {
|
||||
const response = error.response
|
||||
const config = error.config as MyAxiosRequestConfig | undefined
|
||||
|
||||
checkStatus(error.response.data.message, error.response.status, error.config)
|
||||
}
|
||||
|
||||
//console.log('this is error on interceptors.response', error.config.meta)
|
||||
/* if (error.response && (error.response.status == 401 || error.response.status == 400)) {
|
||||
//debugger
|
||||
removeToken()
|
||||
router.push({ name: 'login' })
|
||||
}*/
|
||||
if (error.response && error.response.status == 401) {
|
||||
///removeToken()
|
||||
console.log('error')
|
||||
if (response?.status === 401) {
|
||||
removeToken()
|
||||
// Don't redirect to login if user is on buyProduct page
|
||||
if (router.currentRoute.value.name !== 'buyProduct') {
|
||||
await router.push({name: 'login'})
|
||||
await router.push({name: 'login'})
|
||||
}
|
||||
} else if (error.response && error.response.status == 403) {
|
||||
message.error('شما دسترسی لازم برای انجام این عملیات را ندارید')
|
||||
} else if (error.response && error.response.status == 422) {
|
||||
// message.error(error.response.data?.message)
|
||||
} else if (error.response) {
|
||||
message.error('خطا در بارگذاری اطلاعات')
|
||||
return Promise.reject(error)
|
||||
}
|
||||
// Any status codes that falls outside the range of 2xx cause this function to trigger
|
||||
// Do something with response error
|
||||
|
||||
//const { response, code, message, config } = error || {}
|
||||
//const errorMessageMode = config?.requestOptions?.errorMessageMode || 'none'
|
||||
//const msg: string = response?.data?.error?.message ?? ''
|
||||
//checkStatus(error?.response?.status, msg)
|
||||
if (!shouldShowApiError(config)) {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
|
||||
const apiMessage = extractApiErrorMessage(response?.data)
|
||||
|
||||
if (response?.status === 403) {
|
||||
showApiError(apiMessage || 'شما دسترسی لازم برای انجام این عملیات را ندارید', config)
|
||||
return Promise.reject(error)
|
||||
}
|
||||
|
||||
if (response?.status === 422) {
|
||||
if (apiMessage) {
|
||||
showApiError(apiMessage, config)
|
||||
}
|
||||
return Promise.reject(error)
|
||||
}
|
||||
|
||||
if (response) {
|
||||
showApiError(apiMessage || DEFAULT_LOAD_ERROR, config)
|
||||
return Promise.reject(error)
|
||||
}
|
||||
|
||||
showApiError(DEFAULT_LOAD_ERROR, config)
|
||||
return Promise.reject(error)
|
||||
})
|
||||
|
||||
function checkStatus(msg: string, code: number, config: any) {
|
||||
//const { code, data, message } = data
|
||||
const errorMessage = get(config, 'meta.errorMessage', 'message')
|
||||
/*if (config.meta) {
|
||||
if (config.meta.message) {
|
||||
errorMessage = config.meta.message
|
||||
}
|
||||
}*/
|
||||
// console.log('this is config', config)
|
||||
if (errorMessage == 'none') return
|
||||
switch (errorMessage) {
|
||||
case 'message':
|
||||
message.error(msg)
|
||||
break
|
||||
default:
|
||||
message.error(msg)
|
||||
const DEFAULT_LOAD_ERROR = 'خطا در بارگذاری اطلاعات'
|
||||
|
||||
function extractApiErrorMessage(data: unknown): string {
|
||||
if (!data || typeof data !== 'object') {
|
||||
return ''
|
||||
}
|
||||
|
||||
const record = data as Record<string, unknown>
|
||||
const rawMessage = record.message
|
||||
|
||||
if (typeof rawMessage === 'string' && rawMessage.trim()) {
|
||||
return rawMessage.trim()
|
||||
}
|
||||
|
||||
if (typeof rawMessage === 'number') {
|
||||
return String(rawMessage)
|
||||
}
|
||||
|
||||
const rawError = record.error
|
||||
if (typeof rawError === 'string' && rawError.trim()) {
|
||||
return rawError.trim()
|
||||
}
|
||||
|
||||
const errors = record.errors
|
||||
if (errors && typeof errors === 'object') {
|
||||
for (const value of Object.values(errors as Record<string, unknown>)) {
|
||||
if (Array.isArray(value) && typeof value[0] === 'string' && value[0].trim()) {
|
||||
return value[0].trim()
|
||||
}
|
||||
if (typeof value === 'string' && value.trim()) {
|
||||
return value.trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
function shouldShowApiError(config: MyAxiosRequestConfig | undefined): boolean {
|
||||
return get(config, 'meta.errorMessage', 'message') !== 'none'
|
||||
}
|
||||
|
||||
function showApiError(msg: string, config: MyAxiosRequestConfig | undefined) {
|
||||
if (!shouldShowApiError(config)) {
|
||||
return
|
||||
}
|
||||
|
||||
message.error(msg)
|
||||
}
|
||||
|
||||
const myAxios = {
|
||||
//AxiosResponse<T>
|
||||
|
||||
Reference in New Issue
Block a user