kashkool ver 1

This commit is contained in:
2026-06-13 14:06:59 +03:30
parent 7050d041c4
commit 213ef1a1b7
2 changed files with 107 additions and 39 deletions
@@ -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>