Merge remote-tracking branch 'origin/master'

This commit is contained in:
M_Kececi
2026-06-18 23:57:25 +03:00
parent b59889bbdb
commit 81d1af61be

View File

@@ -363,8 +363,7 @@
:class="[props.col.classes, { 'sticky-col': isStickyCol(props.col.name), 'sticky-boundary': isStickyBoundary(props.col.name) }]"
:style="getBodyCellStyle(props.col)"
>
<q-badge v-if="props.row.campaignLabel" color="primary" outline :label="props.row.campaignLabel" />
<span v-else class="text-grey-6">-</span>
<q-badge v-if="props.row.campaignLabel" color="primary" outline :label="props.row.campaignLabel" class="campaign-badge" />
</q-td>
</template>
@@ -505,7 +504,7 @@ const topUrunAnaGrubu = ref(null)
const selectedProductCodes = ref([])
const selectedCampaignLabels = ref([])
const campaignFilterSearch = ref('')
const selectedPriceOptions = ref(priceOptions.map((x) => x.value))
const selectedPriceOptions = ref(['usd5', 'try5'])
const leftDetailsExpanded = ref(true)
const rows = ref([])
@@ -815,7 +814,7 @@ async function reloadData ({ page = 1 } = {}) {
}
rows.value = buildRows(products, variants)
error.value = ''
void loadImagesForRows(rows.value.slice(0, 120))
void loadImagesForRows(rows.value)
await nextTick()
} catch (err) {
rows.value = []
@@ -836,10 +835,18 @@ async function loadImagesForRows (list) {
seen.add(key)
targets.push({ row, key })
}
await Promise.all(targets.map(async ({ row, key }) => {
const concurrency = 12
let cursor = 0
let loaded = 0
const workers = Array.from({ length: Math.min(concurrency, targets.length) }, async () => {
for (;;) {
const target = targets[cursor]
cursor += 1
if (!target) return
const { row, key } = target
if (imageCache.has(key)) {
row.imageUrl = imageCache.get(key)
return
continue
}
try {
const res = await api.get('/product-images', {
@@ -858,7 +865,11 @@ async function loadImagesForRows (list) {
} catch {
imageCache.set(key, '')
}
}))
loaded += 1
if (loaded % 12 === 0) rows.value = [...rows.value]
}
})
await Promise.all(workers)
rows.value = [...rows.value]
}
@@ -978,26 +989,29 @@ function col (name, label, field, width, extra = {}) {
const allColumns = [
col('image', '', 'imageUrl', 108, { align: 'center', classes: 'image-col sticky-col' }),
col('brandGroupSelection', 'MARKA GRUBU', 'brandGroupSelection', 86, { classes: 'ps-col sticky-col' }),
col('marka', 'MARKA', 'marka', 62, { sortable: true, classes: 'ps-col sticky-col' }),
col('marka', 'MARKA', 'marka', 72, { sortable: true, classes: 'ps-col sticky-col' }),
col('productCode', 'URUN KODU', 'productCode', 112, { sortable: true, classes: 'ps-col product-code-col sticky-col' }),
col('variantCodes', 'VARYANT', 'variantCodes', 112, { classes: 'ps-col variant-col sticky-col' }),
col('variantStocks', 'STOK', 'stockQty', 62, { align: 'right', sortable: true, classes: 'ps-col variant-stock-col sticky-col' }),
col('variantCodes', 'VARYANT', 'variantCodes', 128, { classes: 'ps-col variant-col sticky-col' }),
col('variantStocks', 'STOK', 'stockQty', 72, { align: 'right', sortable: true, classes: 'ps-col variant-stock-col sticky-col' }),
col('campaignLabel', 'KAMPANYA', 'campaignLabel', 150, { classes: 'ps-col campaign-col sticky-col' }),
col('campaignRate', 'IND %', 'campaignRate', 58, { align: 'right', classes: 'ps-col campaign-rate-col sticky-col' }),
col('askiliYan', 'ASKILI YAN', 'askiliYan', 58, { sortable: true, classes: 'ps-col' }),
col('kategori', 'KATEGORI', 'kategori', 58, { sortable: true, classes: 'ps-col' }),
col('urunIlkGrubu', 'URUN ILK GRUBU', 'urunIlkGrubu', 70, { sortable: true, classes: 'ps-col' }),
col('urunAnaGrubu', 'URUN ANA GRUBU', 'urunAnaGrubu', 74, { sortable: true, classes: 'ps-col' }),
col('urunAltGrubu', 'URUN ALT GRUBU', 'urunAltGrubu', 74, { sortable: true, classes: 'ps-col' }),
col('icerik', 'ICERIK', 'icerik', 66, { sortable: true, classes: 'ps-col' }),
col('karisim', 'KARISIM', 'karisim', 66, { sortable: true, classes: 'ps-col' }),
col('campaignRate', 'IND %', 'campaignRate', 64, { align: 'right', classes: 'ps-col campaign-rate-col sticky-col' }),
col('askiliYan', 'ASKILI YAN', 'askiliYan', 72, { sortable: true, classes: 'ps-col' }),
col('kategori', 'KATEGORI', 'kategori', 82, { sortable: true, classes: 'ps-col' }),
col('urunIlkGrubu', 'URUN ILK GRUBU', 'urunIlkGrubu', 88, { sortable: true, classes: 'ps-col' }),
col('urunAnaGrubu', 'URUN ANA GRUBU', 'urunAnaGrubu', 96, { sortable: true, classes: 'ps-col' }),
col('urunAltGrubu', 'URUN ALT GRUBU', 'urunAltGrubu', 96, { sortable: true, classes: 'ps-col' }),
col('icerik', 'ICERIK', 'icerik', 112, { sortable: true, classes: 'ps-col' }),
col('karisim', 'KARISIM', 'karisim', 96, { sortable: true, classes: 'ps-col' }),
...campaignPairs.flatMap((p) => [
col(p.base, p.base.toUpperCase().replace(/([A-Z]+)(\d)/, '$1 $2'), p.base, 78, { align: 'right', classes: `${p.base.slice(0, 3)}-col` }),
col(p.derived, `${p.base.toUpperCase().replace(/([A-Z]+)(\d)/, '$1 $2')} KMP`, p.derived, 88, { align: 'right', classes: `${p.base.slice(0, 3)}-col campaign-price-col` })
])
]
const hideableLeftDetailColumnNames = new Set([
const compactHiddenColumnNames = new Set([
'variantStocks',
'campaignLabel',
'campaignRate',
'askiliYan',
'kategori',
'urunIlkGrubu',
@@ -1010,7 +1024,7 @@ const hideableLeftDetailColumnNames = new Set([
const visibleColumns = computed(() => allColumns.filter((c) => {
if (/^(usd|eur|try)[1-6]$/.test(c.name)) return selectedPriceSet.value.has(c.name)
if (/^(usd|eur|try)[1-6]Campaign$/.test(c.name)) return selectedPriceSet.value.has(c.name.replace(/Campaign$/, ''))
if (!leftDetailsExpanded.value && hideableLeftDetailColumnNames.has(c.name)) return false
if (!leftDetailsExpanded.value && compactHiddenColumnNames.has(c.name)) return false
return true
}))
@@ -1027,9 +1041,30 @@ const tableStyle = computed(() => ({
}))
const stickyColumnNames = computed(() => {
const visible = new Set(visibleColumns.value.map((x) => x.name))
return ['image', 'brandGroupSelection', 'marka', 'productCode', 'variantCodes', 'variantStocks', 'campaignLabel', 'campaignRate'].filter((x) => visible.has(x))
const expanded = [
'image',
'brandGroupSelection',
'marka',
'productCode',
'variantCodes',
'variantStocks',
'campaignLabel',
'campaignRate',
'askiliYan',
'kategori',
'urunIlkGrubu',
'urunAnaGrubu',
'urunAltGrubu',
'icerik',
'karisim'
]
const compact = ['image', 'brandGroupSelection', 'marka', 'productCode', 'variantCodes']
return (leftDetailsExpanded.value ? expanded : compact).filter((x) => visible.has(x))
})
const stickyBoundaryColumnName = computed(() => {
const list = stickyColumnNames.value
return list.length ? list[list.length - 1] : ''
})
const stickyBoundaryColumnName = 'campaignRate'
const stickyColumnNameSet = computed(() => new Set(stickyColumnNames.value))
const stickyLeftMap = computed(() => {
const map = {}
@@ -1043,8 +1078,9 @@ const stickyLeftMap = computed(() => {
return map
})
const stickyScrollComp = computed(() => {
const boundaryCol = allColumns.find((x) => x.name === stickyBoundaryColumnName)
return ((stickyLeftMap.value[stickyBoundaryColumnName] || 0) + extractWidth(boundaryCol?.style)) * 1.2
const boundaryName = stickyBoundaryColumnName.value
const boundaryCol = allColumns.find((x) => x.name === boundaryName)
return ((stickyLeftMap.value[boundaryName] || 0) + extractWidth(boundaryCol?.style)) * 1.2
})
function isStickyCol (name) {
@@ -1052,7 +1088,7 @@ function isStickyCol (name) {
}
function isStickyBoundary (name) {
return name === stickyBoundaryColumnName
return name === stickyBoundaryColumnName.value
}
function getHeaderCellStyle (col) {
@@ -1081,12 +1117,11 @@ function exportCell (row, col) {
}
function exportVisibleExcel () {
const cols = visibleColumns.value
const cols = visibleColumns.value.filter((c) => c.name !== 'image')
const body = filteredRows.value.map((row) => `<tr>${cols.map((c) => {
if (c.name === 'image' && row.imageUrl) return `<td><img src="${row.imageUrl}" width="100" height="100"></td>`
return `<td>${escapeHtml(exportCell(row, c))}</td>`
}).join('')}</tr>`).join('')
const html = `<!doctype html><html><head><meta charset="utf-8"></head><body><table border="1"><thead><tr>${cols.map((c) => `<th>${escapeHtml(c.label || 'Gorsel')}</th>`).join('')}</tr></thead><tbody>${body}</tbody></table></body></html>`
const html = `<!doctype html><html><head><meta charset="utf-8"></head><body><table border="1"><thead><tr>${cols.map((c) => `<th>${escapeHtml(c.label)}</th>`).join('')}</tr></thead><tbody>${body}</tbody></table></body></html>`
const blob = new Blob([html], { type: 'application/vnd.ms-excel;charset=utf-8' })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
@@ -1184,7 +1219,7 @@ onMounted(() => {
display: flex;
flex-direction: column;
--pricing-row-height: 108px;
--pricing-header-height: 72px;
--pricing-header-height: 88px;
--pricing-table-height: calc(100vh - 156px);
}
@@ -1315,6 +1350,13 @@ onMounted(() => {
border-bottom: 1px solid rgba(0, 0, 0, 0.08) !important;
}
.pricing-table :deep(td) {
overflow: hidden !important;
text-overflow: ellipsis;
white-space: nowrap;
vertical-align: middle !important;
}
.pricing-table :deep(th),
.pricing-table :deep(.q-table thead tr),
.pricing-table :deep(.q-table thead tr.header-row-fixed),
@@ -1328,9 +1370,10 @@ onMounted(() => {
.pricing-table :deep(th) {
padding-top: 0;
padding-bottom: 0;
white-space: nowrap;
word-break: normal;
text-overflow: ellipsis;
white-space: normal;
word-break: break-word;
overflow-wrap: anywhere;
overflow: hidden;
text-align: center;
font-size: 10px;
font-weight: 800;
@@ -1411,15 +1454,22 @@ onMounted(() => {
width: 100%;
overflow: hidden;
text-align: center;
text-overflow: ellipsis;
white-space: normal;
word-break: break-word;
overflow-wrap: anywhere;
font-weight: 800;
line-height: 1.15;
line-height: 1.12;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-line-clamp: 4;
-webkit-box-orient: vertical;
}
.campaign-badge {
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
}
.header-filter-btn {
width: 20px;
height: 20px;