Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user