Split desktop and mobile layouts with shared page data so APIs fetch once per context.
Add layout shells, desktop views, module-level page caches, and UI fixes for auth, home, wallet, transactions, address, and related pages. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
import { inject, onMounted, provide, ref, type InjectionKey, type Ref } from 'vue'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { type FormInstance, message } from 'ant-design-vue'
|
||||
import { createAddress, getCity, updateAddress } from '@/api/address'
|
||||
import type { Address } from '@/models/address'
|
||||
import { useDesktopBreakpoint } from '@/layouts/composables/useAppLayout'
|
||||
import { loading, loadingMore } from '@/utils'
|
||||
import { isBazretalaProject } from '@/utils/config'
|
||||
import {
|
||||
addressItems,
|
||||
addressProvinceList,
|
||||
initAddressPageData,
|
||||
refreshAddressItems,
|
||||
} from './useAddressPageData'
|
||||
|
||||
export interface AddressPageContext {
|
||||
provinceList: typeof addressProvinceList
|
||||
cityList: Ref<any[] | undefined>
|
||||
items: typeof addressItems
|
||||
addAddressVisible: Ref<boolean>
|
||||
editId: Ref<number | undefined>
|
||||
address: Ref<Address>
|
||||
formRef: Ref<FormInstance | undefined>
|
||||
loading: typeof loading
|
||||
loadingMore: typeof loadingMore
|
||||
isBazretalaProject: typeof isBazretalaProject
|
||||
resetFields: () => void
|
||||
openAdd: () => void
|
||||
openEdit: (val: Address) => void
|
||||
handleOk: (e?: MouseEvent) => void
|
||||
initItems: () => Promise<void>
|
||||
changeProvince: (obj: { id: number }) => Promise<void>
|
||||
}
|
||||
|
||||
export const ADDRESS_PAGE_CONTEXT_KEY: InjectionKey<AddressPageContext> =
|
||||
Symbol('addressPageContext')
|
||||
|
||||
const emptyAddress = (): Address => ({
|
||||
city: '',
|
||||
province: '',
|
||||
phone: '',
|
||||
address: '',
|
||||
zip: '',
|
||||
default_address: false,
|
||||
})
|
||||
|
||||
export function useAddressPage() {
|
||||
const isDesktop = useDesktopBreakpoint()
|
||||
const cityList = ref<any[]>()
|
||||
const addAddressVisible = ref(false)
|
||||
const editId = ref<number | undefined>(undefined)
|
||||
const formRef = ref<FormInstance>()
|
||||
const address = ref<Address>(emptyAddress())
|
||||
|
||||
function resetFields() {
|
||||
editId.value = undefined
|
||||
address.value = emptyAddress()
|
||||
cityList.value = undefined
|
||||
}
|
||||
|
||||
function openAdd() {
|
||||
resetFields()
|
||||
editId.value = undefined
|
||||
addAddressVisible.value = !isDesktop.value
|
||||
}
|
||||
|
||||
async function openEdit(val: Address) {
|
||||
address.value = cloneDeep(val)
|
||||
editId.value = address.value.id
|
||||
if (address.value.province) {
|
||||
const province = addressProvinceList.value.find((item) => item.title === address.value.province)
|
||||
if (province?.id) {
|
||||
cityList.value = await getCity(province.id)
|
||||
}
|
||||
}
|
||||
addAddressVisible.value = true
|
||||
}
|
||||
|
||||
const handleOk = (_e?: MouseEvent) => {
|
||||
formRef.value?.validate().then(async () => {
|
||||
if (editId.value) {
|
||||
await updateAddress(editId.value, address.value)
|
||||
} else {
|
||||
await createAddress(address.value)
|
||||
}
|
||||
await initItems()
|
||||
addAddressVisible.value = false
|
||||
message.success('آدرس با موفقیت ثبت شد')
|
||||
resetFields()
|
||||
}).catch((error) => {
|
||||
console.log('error', error)
|
||||
})
|
||||
}
|
||||
|
||||
async function initItems() {
|
||||
await refreshAddressItems()
|
||||
}
|
||||
|
||||
const changeProvince = async (obj: { id: number }) => {
|
||||
try {
|
||||
loading.value = true
|
||||
cityList.value = undefined
|
||||
address.value.city = undefined
|
||||
cityList.value = await getCity(obj.id)
|
||||
} catch (e) {
|
||||
message.error('خطا در دریافت شهرها')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await initAddressPageData()
|
||||
})
|
||||
|
||||
const context: AddressPageContext = {
|
||||
provinceList: addressProvinceList,
|
||||
cityList,
|
||||
items: addressItems,
|
||||
addAddressVisible,
|
||||
editId,
|
||||
address,
|
||||
formRef,
|
||||
loading,
|
||||
loadingMore,
|
||||
isBazretalaProject,
|
||||
resetFields,
|
||||
openAdd,
|
||||
openEdit,
|
||||
handleOk,
|
||||
initItems,
|
||||
changeProvince,
|
||||
}
|
||||
|
||||
provide(ADDRESS_PAGE_CONTEXT_KEY, context)
|
||||
return context
|
||||
}
|
||||
|
||||
export function useAddressPageContext() {
|
||||
const context = inject(ADDRESS_PAGE_CONTEXT_KEY)
|
||||
if (!context) {
|
||||
throw new Error('useAddressPageContext must be used within a page that calls useAddressPage()')
|
||||
}
|
||||
return context
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import { ref } from 'vue'
|
||||
import { getProvince, tableAddress } from '@/api/address'
|
||||
import type { Address } from '@/models/address'
|
||||
import { loading, loadingMore } from '@/utils'
|
||||
import { runOnceAsync, resetOnceAsyncByPrefix } from '@/composables/useOnceAsync'
|
||||
|
||||
/**
|
||||
* Shared address page datasets — mobile and desktop views read this module state only.
|
||||
*/
|
||||
let initPromise: Promise<void> | null = null
|
||||
const initialized = ref(false)
|
||||
|
||||
export const addressItems = ref<Address[]>([])
|
||||
export const addressProvinceList = ref<any[]>([])
|
||||
|
||||
export function isAddressPageDataInitialized() {
|
||||
return initialized.value
|
||||
}
|
||||
|
||||
export async function refreshAddressItems() {
|
||||
try {
|
||||
loading.value = true
|
||||
const data = await tableAddress({ page: 0, limit: 150 })
|
||||
addressItems.value = data.rows
|
||||
} catch (error) {
|
||||
console.error('Address list refresh error:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
loadingMore.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function loadAddressItems() {
|
||||
const data = await tableAddress({ page: 0, limit: 150 })
|
||||
addressItems.value = data.rows
|
||||
}
|
||||
|
||||
async function loadProvinces() {
|
||||
addressProvinceList.value = await getProvince()
|
||||
}
|
||||
|
||||
export async function initAddressPageData(force = false) {
|
||||
if (!force && initialized.value) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!force && initPromise) {
|
||||
return initPromise
|
||||
}
|
||||
|
||||
initPromise = (async () => {
|
||||
try {
|
||||
if (force) {
|
||||
resetAddressPageData()
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
|
||||
await Promise.all([
|
||||
runOnceAsync('address:items', () => loadAddressItems(), force),
|
||||
runOnceAsync('address:provinces', () => loadProvinces(), force),
|
||||
])
|
||||
|
||||
initialized.value = true
|
||||
} catch (error) {
|
||||
console.error('Address page initialization error:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
initPromise = null
|
||||
}
|
||||
})()
|
||||
|
||||
return initPromise
|
||||
}
|
||||
|
||||
export function resetAddressPageData() {
|
||||
initialized.value = false
|
||||
initPromise = null
|
||||
resetOnceAsyncByPrefix('address:')
|
||||
addressItems.value = []
|
||||
addressProvinceList.value = []
|
||||
}
|
||||
Reference in New Issue
Block a user