Merge remote-tracking branch 'origin/master'

This commit is contained in:
M_Kececi
2026-03-16 00:42:35 +03:00
parent 19e67ad9f5
commit e46363a758
4 changed files with 380 additions and 140 deletions

View File

@@ -15,17 +15,30 @@
v-model="filters[def.key]"
:options="filteredOptionLists[def.key] || []"
:label="def.label"
:multiple="isMultiFilter(def.key)"
:use-chips="isMultiFilter(def.key)"
filled
dense
clearable
use-input
use-selected
input-debounce="250"
:disable="isFilterDisabled(def.key)"
:loading="loadingFilterOptions"
@update:model-value="onFilterValueChange(def.key)"
@filter="(val, update) => filterOptions(def.key, val, update)"
@keyup.enter="fetchStockByAttributes"
/>
>
<template v-if="isMultiFilter(def.key)" #before-options>
<q-item clickable dense v-ripple @click.stop="selectAllFilterOptions(def.key)">
<q-item-section>Tumunu Sec</q-item-section>
</q-item>
<q-item clickable dense v-ripple @click.stop="clearAllFilterOptions(def.key)">
<q-item-section>Tumunu Temizle</q-item-section>
</q-item>
<q-separator />
</template>
</q-select>
</div>
<div class="col-auto">
@@ -389,21 +402,16 @@ const filterDefs = [
{ key: 'drop', label: 'Drop' },
{ key: 'beden', label: 'Beden' }
]
const singleSelectFilterKeys = new Set(['kategori', 'urun_ana_grubu'])
const multiSelectFilterKeys = new Set(
filterDefs.map((def) => def.key).filter((key) => !singleSelectFilterKeys.has(key))
)
const filterValueSeparator = '\u001f'
const loadingFilterOptions = ref(false)
const loadingStock = ref(false)
const errorMessage = ref('')
const filters = ref({
kategori: '',
urun_ana_grubu: '',
urun_alt_grubu: '',
renk: '',
renk2: '',
urun_icerigi: '',
fit: '',
drop: '',
beden: ''
})
const filters = ref(createEmptyFilters())
const optionLists = ref({})
const filteredOptionLists = ref({})
const filterOptionsCache = ref({})
@@ -442,8 +450,8 @@ const activeSchema = ref(storeSchemaByKey.tak)
const activeGrpKey = ref('tak')
const openState = ref({})
const canQuery = computed(() =>
String(filters.value?.kategori || '').trim() !== '' &&
String(filters.value?.urun_ana_grubu || '').trim() !== ''
normalizeFilterScalar(filters.value?.kategori) !== '' &&
normalizeFilterScalar(filters.value?.urun_ana_grubu) !== ''
)
const sizeLabels = computed(() => activeSchema.value?.values || [])
@@ -654,6 +662,8 @@ function resolveProductImageUrl(item) {
function extractImageOrder(fileName, fallbackIndex) {
const name = String(fileName || '').trim()
const mg = name.match(/gallery[-_\s]?(\d+)/i)
if (mg) return Number(mg[1] || 999999)
const m = name.match(/\((\d+)\)(?=\.[a-z0-9]+$)/i)
if (m) return Number(m[1] || 999999)
const m2 = name.match(/[-_ ](\d+)(?=\.[a-z0-9]+$)/i)
@@ -759,8 +769,9 @@ async function ensureProductImage(code, color, secondColor = '', dim1Id = '', di
}
}
const rawList = Array.isArray(productImageListByCode.value[listKey]) ? productImageListByCode.value[listKey] : []
const primaryItem = rawList[0] || null
const secondaryItem = rawList.length > 1 ? rawList[rawList.length - 1] : null
const sortedList = sortImagesForDisplay(rawList)
const primaryItem = sortedList[0] || null
const secondaryItem = sortedList.length > 1 ? sortedList[sortedList.length - 1] : null
const primaryResolved = resolveProductImageUrl(primaryItem)
let preferredCardUrl = primaryItem ? await resolveProductImageUrlForCarousel(primaryItem) : ''
@@ -992,14 +1003,51 @@ const level1Groups = computed(() => {
.sort(sortByTotalQtyDesc)
})
function normalizeText(v) {
return String(v || '').trim()
function createEmptyFilters() {
return {
kategori: '',
urun_ana_grubu: '',
urun_alt_grubu: [],
renk: [],
renk2: [],
urun_icerigi: [],
fit: [],
drop: [],
beden: []
}
}
function buildFilterParams() {
function isMultiFilter(key) {
return multiSelectFilterKeys.has(key)
}
function normalizeFilterScalar(v) {
return String(v ?? '').trim()
}
function normalizeFilterList(v) {
const src = Array.isArray(v) ? v : (normalizeFilterScalar(v) ? [v] : [])
const out = []
const seen = new Set()
for (const item of src) {
const val = normalizeFilterScalar(item)
if (!val || seen.has(val)) continue
seen.add(val)
out.push(val)
}
return out
}
function buildFilterParams(excludeKey = '') {
const out = {}
for (const def of filterDefs) {
const val = normalizeText(filters.value?.[def.key])
if (excludeKey && def.key === excludeKey) continue
if (isMultiFilter(def.key)) {
const vals = normalizeFilterList(filters.value?.[def.key])
if (vals.length) out[def.key] = vals.join(filterValueSeparator)
continue
}
const val = normalizeFilterScalar(filters.value?.[def.key])
if (val) out[def.key] = val
}
return out
@@ -1013,7 +1061,7 @@ function buildFilterCacheKey(params) {
function isFilterDisabled(key) {
if (key === 'kategori') return false
if (key === 'urun_ana_grubu') {
return normalizeText(filters.value.kategori) === ''
return normalizeFilterScalar(filters.value.kategori) === ''
}
return !canQuery.value
}
@@ -1021,33 +1069,34 @@ function isFilterDisabled(key) {
function onFilterValueChange(changedKey) {
if (changedKey === 'kategori') {
filters.value.urun_ana_grubu = ''
filters.value.urun_alt_grubu = ''
filters.value.renk = ''
filters.value.renk2 = ''
filters.value.urun_icerigi = ''
filters.value.fit = ''
filters.value.drop = ''
filters.value.beden = ''
filters.value.urun_alt_grubu = []
filters.value.renk = []
filters.value.renk2 = []
filters.value.urun_icerigi = []
filters.value.fit = []
filters.value.drop = []
filters.value.beden = []
} else if (changedKey === 'urun_ana_grubu') {
filters.value.urun_alt_grubu = ''
filters.value.renk = ''
filters.value.renk2 = ''
filters.value.urun_icerigi = ''
filters.value.fit = ''
filters.value.drop = ''
filters.value.beden = ''
filters.value.urun_alt_grubu = []
filters.value.renk = []
filters.value.renk2 = []
filters.value.urun_icerigi = []
filters.value.fit = []
filters.value.drop = []
filters.value.beden = []
} else if (changedKey === 'renk') {
filters.value.renk2 = ''
filters.value.beden = ''
filters.value.renk2 = []
filters.value.beden = []
} else if (changedKey === 'renk2') {
filters.value.beden = ''
filters.value.beden = []
}
if (filterOptionsDebounceTimer) {
clearTimeout(filterOptionsDebounceTimer)
}
const excludeKey = isMultiFilter(changedKey) ? changedKey : ''
filterOptionsDebounceTimer = setTimeout(() => {
void loadFilterOptions()
void loadFilterOptions(false, excludeKey)
}, FILTER_OPTIONS_DEBOUNCE_MS)
}
@@ -1069,8 +1118,21 @@ function filterOptions(field, val, update) {
})
}
async function loadFilterOptions(force = false) {
const params = buildFilterParams()
function selectAllFilterOptions(key) {
if (!isMultiFilter(key) || isFilterDisabled(key)) return
const list = Array.isArray(optionLists.value?.[key]) ? optionLists.value[key] : []
filters.value[key] = [...list]
onFilterValueChange(key)
}
function clearAllFilterOptions(key) {
if (!isMultiFilter(key) || isFilterDisabled(key)) return
filters.value[key] = []
onFilterValueChange(key)
}
async function loadFilterOptions(force = false, excludeKey = '') {
const params = buildFilterParams(excludeKey)
const cacheKey = buildFilterCacheKey(params)
const now = Date.now()
if (!force) {
@@ -1102,9 +1164,14 @@ async function loadFilterOptions(force = false) {
next[def.key] = list
nextFiltered[def.key] = [...list]
const selected = normalizeText(filters.value?.[def.key])
if (selected && !list.includes(selected)) {
filters.value[def.key] = ''
if (isMultiFilter(def.key)) {
const selectedList = normalizeFilterList(filters.value?.[def.key])
filters.value[def.key] = selectedList.filter((v) => list.includes(v))
} else {
const selected = normalizeFilterScalar(filters.value?.[def.key])
if (selected && !list.includes(selected)) {
filters.value[def.key] = ''
}
}
}
@@ -1359,17 +1426,7 @@ function onFullscreenSlideChange() {
}
function resetForm() {
filters.value = {
kategori: '',
urun_ana_grubu: '',
urun_alt_grubu: '',
renk: '',
renk2: '',
urun_icerigi: '',
fit: '',
drop: '',
beden: ''
}
filters.value = createEmptyFilters()
rawRows.value = []
errorMessage.value = ''
openState.value = {}