kashkool ver 1
This commit is contained in:
@@ -1,14 +1,14 @@
|
||||
<template>
|
||||
<BasicActionSheet
|
||||
v-model:visible="visible"
|
||||
v-model:visible="visibleProxy"
|
||||
title="جزئیات همگامسازی قیمت"
|
||||
modal-width="52rem"
|
||||
:showButton="false"
|
||||
:showCancel="true"
|
||||
:showOk="false"
|
||||
cancelText="بستن"
|
||||
@cancel="visible = false"
|
||||
@close="visible = false"
|
||||
@cancel="close"
|
||||
@close="close"
|
||||
>
|
||||
<a-spin :spinning="loading">
|
||||
<div v-if="log" class="flex flex-col gap-4">
|
||||
@@ -37,14 +37,29 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { TableColumnType } from 'ant-design-vue'
|
||||
import { ref, watch } from 'vue'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { getPriceSyncLogDetail } from '@/api/price'
|
||||
import type { PriceSyncLog } from '@/model/priceSyncLog'
|
||||
import BasicActionSheet from '@/components/BasicActionSheet.vue'
|
||||
import { usePrice } from '@core/utils/usePrice'
|
||||
|
||||
const visible = defineModel<boolean>('visible', { default: false })
|
||||
const props = defineProps<{ logId?: number | null }>()
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
logId?: number | null
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', value: boolean): void
|
||||
}>()
|
||||
|
||||
const visibleProxy = computed({
|
||||
get: () => props.visible,
|
||||
set: (value: boolean) => emit('update:visible', value)
|
||||
})
|
||||
|
||||
function close() {
|
||||
emit('update:visible', false)
|
||||
}
|
||||
|
||||
const loading = ref(false)
|
||||
const log = ref<PriceSyncLog | null>(null)
|
||||
@@ -102,7 +117,7 @@ async function loadDetail(id: number) {
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [visible.value, props.logId] as const,
|
||||
() => [props.visible, props.logId] as const,
|
||||
([open, id]) => {
|
||||
if (open && id) {
|
||||
void loadDetail(id)
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
class="!w-16"
|
||||
type="number"
|
||||
v-model:value="record.sort_order"
|
||||
@blur="() => saveSortOrder(record)"
|
||||
@onchange="() => scheduleSortOrder(record)"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
@@ -53,7 +53,8 @@
|
||||
<script setup lang="ts">
|
||||
import { message } from 'ant-design-vue'
|
||||
import type { TableColumnType } from 'ant-design-vue'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { debounce } from 'lodash'
|
||||
import { onMounted, onUnmounted, ref } from 'vue'
|
||||
import { getCategories, syncCategoriesFromProducts, updateCategory } from '@/api/category'
|
||||
import type { Category } from '@/model/category'
|
||||
|
||||
@@ -61,6 +62,12 @@ const loading = ref(false)
|
||||
const syncing = ref(false)
|
||||
const items = ref<Category[]>([])
|
||||
|
||||
type CategoryField = 'active' | 'show_in_filter' | 'show_in_select' | 'sort_order'
|
||||
|
||||
const pendingPatches = new Map<number, Partial<Record<CategoryField, number>>>()
|
||||
const debouncedFlush = new Map<number, ReturnType<typeof debounce>>()
|
||||
let syncInFlight: Promise<void> | null = null
|
||||
|
||||
const columns: TableColumnType<Category>[] = [
|
||||
{ title: 'نام دسته', dataIndex: 'name', key: 'name' },
|
||||
{
|
||||
@@ -106,50 +113,90 @@ async function fetchList() {
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleField(record: Category, field: 'active' | 'show_in_filter' | 'show_in_select', checked: boolean) {
|
||||
const prev = record[field]
|
||||
record[field] = checked ? 1 : 0
|
||||
function scheduleSave(record: Category, patch: Partial<Record<CategoryField, number>>) {
|
||||
const id = record.id
|
||||
const current = pendingPatches.get(id) ?? {}
|
||||
pendingPatches.set(id, { ...current, ...patch })
|
||||
|
||||
if (!debouncedFlush.has(id)) {
|
||||
debouncedFlush.set(
|
||||
id,
|
||||
debounce(() => flushSave(id), 500)
|
||||
)
|
||||
}
|
||||
debouncedFlush.get(id)!()
|
||||
}
|
||||
|
||||
async function flushSave(id: number) {
|
||||
const patch = pendingPatches.get(id)
|
||||
if (!patch || Object.keys(patch).length === 0) return
|
||||
pendingPatches.delete(id)
|
||||
|
||||
const record = items.value.find((item) => item.id === id)
|
||||
if (!record) return
|
||||
|
||||
const snapshot = {
|
||||
active: record.active,
|
||||
show_in_filter: record.show_in_filter,
|
||||
show_in_select: record.show_in_select,
|
||||
sort_order: record.sort_order
|
||||
}
|
||||
|
||||
try {
|
||||
await updateCategory(record.id, { [field]: checked ? 1 : 0 })
|
||||
await updateCategory(id, patch)
|
||||
message.success('ذخیره شد')
|
||||
} catch (e) {
|
||||
record[field] = prev
|
||||
Object.assign(record, snapshot)
|
||||
console.error(e)
|
||||
message.error('خطا در ذخیره')
|
||||
}
|
||||
}
|
||||
|
||||
async function saveSortOrder(record: Category) {
|
||||
function toggleField(
|
||||
record: Category,
|
||||
field: 'active' | 'show_in_filter' | 'show_in_select',
|
||||
checked: boolean
|
||||
) {
|
||||
if (asBool(record[field]) === checked) return
|
||||
|
||||
const next = checked ? 1 : 0
|
||||
record[field] = next
|
||||
scheduleSave(record, { [field]: next })
|
||||
}
|
||||
|
||||
function scheduleSortOrder(record: Category) {
|
||||
const order = Number(record.sort_order)
|
||||
if (Number.isNaN(order)) return
|
||||
|
||||
try {
|
||||
await updateCategory(record.id, { sort_order: order })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
message.error('خطا در ذخیره ترتیب')
|
||||
await fetchList()
|
||||
}
|
||||
scheduleSave(record, { sort_order: order })
|
||||
}
|
||||
|
||||
async function syncList(showMessage = false) {
|
||||
syncing.value = true
|
||||
try {
|
||||
const res = await syncCategoriesFromProducts()
|
||||
items.value = res.items ?? (await getCategories())
|
||||
if (showMessage) {
|
||||
message.success(res.message || 'همگامسازی انجام شد')
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
if (showMessage) {
|
||||
message.error('خطا در همگامسازی')
|
||||
} else {
|
||||
await fetchList()
|
||||
}
|
||||
} finally {
|
||||
syncing.value = false
|
||||
if (syncInFlight) {
|
||||
return syncInFlight
|
||||
}
|
||||
|
||||
syncing.value = true
|
||||
syncInFlight = (async () => {
|
||||
try {
|
||||
const res = await syncCategoriesFromProducts()
|
||||
items.value = res.items ?? (await getCategories())
|
||||
if (showMessage) {
|
||||
message.success(res.message || 'همگامسازی انجام شد')
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
if (showMessage) {
|
||||
message.error('خطا در همگامسازی')
|
||||
} else {
|
||||
await fetchList()
|
||||
}
|
||||
} finally {
|
||||
syncing.value = false
|
||||
syncInFlight = null
|
||||
}
|
||||
})()
|
||||
|
||||
return syncInFlight
|
||||
}
|
||||
|
||||
async function handleSync() {
|
||||
@@ -159,4 +206,10 @@ async function handleSync() {
|
||||
onMounted(() => {
|
||||
void syncList()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
debouncedFlush.forEach((fn) => fn.cancel())
|
||||
debouncedFlush.clear()
|
||||
pendingPatches.clear()
|
||||
})
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user