Merge remote-tracking branch 'origin/master'

This commit is contained in:
M_Kececi
2026-06-19 15:44:12 +03:00
parent da9d7c2fd5
commit 1054a15547
13 changed files with 828 additions and 62 deletions

View File

@@ -80,7 +80,7 @@
<q-item-section>Tumunu Temizle</q-item-section>
</q-item>
<q-separator />
<q-item v-for="option in priceOptions" :key="option.value" clickable @click="togglePriceOption(option.value)">
<q-item v-for="option in allowedPriceOptions" :key="option.value" clickable @click="togglePriceOption(option.value)">
<q-item-section avatar>
<q-checkbox
dense
@@ -165,7 +165,7 @@
>
<div
class="top-x-scroll-inner"
:style="{ width: `${tableMinWidth}px` }"
:style="{ width: `${tableScrollWidth}px` }"
/>
</div>
<q-table
@@ -589,18 +589,6 @@
<div class="field-row"><span class="k">Kampanya</span><span class="v">{{ productCardData.campaignLabel || '-' }}</span></div>
<div class="field-row"><span class="k">Stok</span><span class="v">{{ formatStock(productCardData.stockQty || 0) }}</span></div>
<div class="product-card-section">
<div class="product-card-section-title">Fiyat Bilgileri</div>
<div v-if="productCardPriceRows.length" class="price-info-grid">
<div v-for="item in productCardPriceRows" :key="item.key" class="price-info-row">
<span class="price-label">{{ item.label }}</span>
<span class="price-value">{{ item.price || '-' }}</span>
<span class="price-campaign">{{ item.campaignPrice || '-' }}</span>
</div>
</div>
<div v-else class="product-card-empty-text">Secili fiyat kolonu yok.</div>
</div>
<div class="product-card-section">
<div class="product-card-section-title">Beden Stoklari</div>
<q-inner-loading :showing="productCardStockLoading">
@@ -615,6 +603,25 @@
<div v-else-if="!productCardStockLoading" class="product-card-empty-text">Beden stogu bulunamadi.</div>
</div>
</div>
<div class="product-card-price-panel">
<div class="product-card-section product-card-price-section">
<div class="product-card-section-title">Fiyat Bilgileri</div>
<div class="price-info-header">
<span>Fiyat</span>
<span>Liste</span>
<span>Kampanyali</span>
</div>
<div v-if="productCardPriceRows.length" class="price-info-grid">
<div v-for="item in productCardPriceRows" :key="item.key" :class="['price-info-row', `price-info-row-${item.currency}`, { 'has-campaign-price': item.hasCampaignPrice }]">
<span class="price-label">{{ item.label }}</span>
<span class="price-value">{{ item.price || '-' }}</span>
<span class="price-campaign">{{ item.campaignPrice || '-' }}</span>
</div>
</div>
<div v-else class="product-card-empty-text">Secili fiyat kolonu yok.</div>
</div>
</div>
</div>
</q-card-section>
</q-card>
@@ -664,10 +671,18 @@ import api from 'src/services/api'
const PAGE_LIMIT = 250
const GUIDANCE_MSG = 'Liste icin filtre secin.'
const priceOptions = ['USD', 'EUR', 'TRY'].flatMap((cur) => [1, 2, 3, 4, 5, 6].map((lv) => ({
const allPriceOptions = ['USD', 'EUR', 'TRY'].flatMap((cur) => [1, 2, 3, 4, 5, 6].map((lv) => ({
label: `${cur} ${lv}`,
value: `${cur.toLowerCase()}${lv}`
})))
const allowedPriceGroupValues = ref([])
const priceGroupRestricted = ref(false)
const allowedPriceOptions = computed(() => {
if (!priceGroupRestricted.value) return allPriceOptions
const allowed = new Set(allowedPriceGroupValues.value || [])
return allPriceOptions.filter((x) => allowed.has(x.value))
})
const priceOptions = allPriceOptions
const campaignPairs = priceOptions.map((x) => ({ base: x.value, derived: `${x.value}Campaign` }))
const priceColumnNames = campaignPairs.flatMap((p) => [p.base, p.derived])
@@ -721,8 +736,10 @@ const productCardPriceRows = computed(() => {
.map((option) => ({
key: option.value,
label: option.label,
currency: String(option.value || '').slice(0, 3).toLowerCase(),
price: formatPrice(row?.[option.value]),
campaignPrice: formatPrice(row?.[`${option.value}Campaign`])
campaignPrice: formatPrice(row?.[`${option.value}Campaign`]),
hasCampaignPrice: Number(row?.[`${option.value}Campaign`] || 0) > 0
}))
})
const selectedProductCodeSet = computed(() => new Set(selectedProductCodes.value || []))
@@ -956,6 +973,20 @@ async function fetchServerFilterOptions (field, q = '') {
}
}
async function fetchMyPriceGroups () {
try {
const res = await api.get('/order/price-list/my-price-groups')
priceGroupRestricted.value = !!res?.data?.restricted
allowedPriceGroupValues.value = Array.isArray(res?.data?.price_groups) ? res.data.price_groups : []
normalizeSelectedPriceOptions()
} catch (err) {
console.warn('[order-price-list][ui] price-groups lookup failed', err?.response?.data || err?.message || err)
priceGroupRestricted.value = false
allowedPriceGroupValues.value = []
normalizeSelectedPriceOptions()
}
}
function onTopFilterSearchUrunIlkGrubu (val, update) {
update(() => {
filterSearch.value.urunIlkGrubu = toText(val)
@@ -1306,20 +1337,33 @@ function onPageChange (page) {
}
function togglePriceOption (value) {
if (!allowedPriceOptions.value.some((x) => x.value === value)) return
const set = new Set(selectedPriceOptions.value || [])
if (set.has(value)) set.delete(value)
else set.add(value)
selectedPriceOptions.value = priceOptions.map((x) => x.value).filter((x) => set.has(x))
selectedPriceOptions.value = allowedPriceOptions.value.map((x) => x.value).filter((x) => set.has(x))
}
function selectAllPrices () {
selectedPriceOptions.value = priceOptions.map((x) => x.value)
selectedPriceOptions.value = allowedPriceOptions.value.map((x) => x.value)
}
function clearAllPrices () {
selectedPriceOptions.value = []
}
function normalizeSelectedPriceOptions () {
const allowedValues = allowedPriceOptions.value.map((x) => x.value)
const allowed = new Set(allowedValues)
const current = (selectedPriceOptions.value || []).filter((x) => allowed.has(x))
if (current.length > 0 || allowedValues.length === 0) {
selectedPriceOptions.value = current
return
}
const preferred = ['usd5', 'try5'].filter((x) => allowed.has(x))
selectedPriceOptions.value = preferred.length ? preferred : allowedValues.slice(0, 2)
}
function col (name, label, field, width, extra = {}) {
return {
name,
@@ -1402,6 +1446,7 @@ const filteredRows = computed(() => {
return list
})
const tableMinWidth = computed(() => visibleColumns.value.reduce((sum, c) => sum + extractWidth(c.style), 0))
const tableScrollWidth = computed(() => tableMinWidth.value + stickyScrollComp.value + 48)
const tableStyle = computed(() => ({
width: `${tableMinWidth.value}px`,
minWidth: `${tableMinWidth.value}px`,
@@ -1609,7 +1654,12 @@ watch([tableMinWidth, rows], async () => {
bindTableScrollSync()
})
watch(allowedPriceOptions, () => {
normalizeSelectedPriceOptions()
})
onMounted(() => {
void fetchMyPriceGroups()
void fetchServerFilterOptions('urunIlkGrubu', '')
void fetchServerFilterOptions('urunAnaGrubu', '')
void fetchServerFilterOptions('productCode', '')
@@ -1885,23 +1935,23 @@ onMounted(() => {
.pricing-table :deep(th.usd-col),
.pricing-table :deep(td.usd-col) {
background: #ecf9f0;
color: #178a3e;
font-weight: 700;
background: #fff;
color: #16803a;
font-weight: 800;
}
.pricing-table :deep(th.eur-col),
.pricing-table :deep(td.eur-col) {
background: #fdeeee;
color: #c62828;
font-weight: 700;
background: #fff;
color: #b91c1c;
font-weight: 800;
}
.pricing-table :deep(th.try-col),
.pricing-table :deep(td.try-col) {
background: #edf4ff;
color: #1e63c6;
font-weight: 700;
background: #fff;
color: #185abc;
font-weight: 800;
}
.pricing-table :deep(th.usd-col),
@@ -1910,14 +1960,30 @@ onMounted(() => {
.pricing-table :deep(td.usd-col),
.pricing-table :deep(td.eur-col),
.pricing-table :deep(td.try-col) {
font-size: 10px;
font-size: 12px;
}
.pricing-table :deep(td.campaign-price-col),
.pricing-table :deep(th.campaign-price-col) {
background: #fff3f1;
color: #c62828;
font-weight: 800;
.pricing-table :deep(th.usd-col.campaign-price-col),
.pricing-table :deep(td.usd-col.campaign-price-col) {
background: #dff6e7;
color: #0f6b2f;
font-weight: 900;
letter-spacing: 0;
}
.pricing-table :deep(th.eur-col.campaign-price-col),
.pricing-table :deep(td.eur-col.campaign-price-col) {
background: #fde2e2;
color: #a61717;
font-weight: 900;
letter-spacing: 0;
}
.pricing-table :deep(th.try-col.campaign-price-col),
.pricing-table :deep(td.try-col.campaign-price-col) {
background: #e2edff;
color: #174ea6;
font-weight: 900;
letter-spacing: 0;
}
@@ -1989,7 +2055,6 @@ onMounted(() => {
}
.campaign-price-text {
color: #c62828;
font-weight: 900;
}
@@ -2100,7 +2165,7 @@ onMounted(() => {
.product-card-dialog {
--pc-media-h: calc(100vh - 180px);
--pc-media-w: min(74vw, 1220px);
--pc-media-w: min(28vw, 440px);
background: #f9f8f5;
height: 100vh;
display: flex;
@@ -2115,10 +2180,10 @@ onMounted(() => {
.product-card-content {
display: grid;
grid-template-columns: minmax(360px, 420px) minmax(760px, 1fr);
grid-template-columns: minmax(360px, 420px) minmax(360px, 440px) minmax(320px, 420px);
gap: 14px;
align-items: stretch;
justify-content: start;
justify-content: center;
height: 100%;
}
@@ -2126,6 +2191,7 @@ onMounted(() => {
grid-column: 2;
grid-row: 1;
height: var(--pc-media-h);
min-width: 0;
display: flex;
flex-direction: column;
align-items: stretch;
@@ -2133,7 +2199,7 @@ onMounted(() => {
}
.product-card-carousel {
width: var(--pc-media-w);
width: 100%;
height: 100%;
max-width: 100%;
background: linear-gradient(180deg, #f6f1e6 0%, #efe6d3 100%);
@@ -2146,7 +2212,7 @@ onMounted(() => {
}
.dialog-image-stage {
width: var(--pc-media-w);
width: 100%;
max-width: 100%;
height: 100%;
overflow: hidden;
@@ -2158,7 +2224,7 @@ onMounted(() => {
}
.dialog-image-empty {
width: var(--pc-media-w);
width: 100%;
max-width: 100%;
height: var(--pc-media-h);
border: 1px dashed #c5b28d;
@@ -2180,6 +2246,14 @@ onMounted(() => {
overflow: auto;
}
.product-card-price-panel {
grid-column: 3;
grid-row: 1;
min-width: 0;
height: var(--pc-media-h);
overflow: hidden;
}
.field-row {
display: grid;
grid-template-columns: 150px 1fr;
@@ -2220,6 +2294,13 @@ onMounted(() => {
padding: 10px;
}
.product-card-price-section {
height: 100%;
margin-top: 0;
overflow: auto;
background: linear-gradient(180deg, #fffdf8 0%, #fff7ec 100%);
}
.product-card-section-title {
font-size: 13px;
font-weight: 800;
@@ -2230,20 +2311,36 @@ onMounted(() => {
.price-info-grid {
display: grid;
grid-template-columns: 1fr;
gap: 4px;
gap: 6px;
}
.price-info-header {
display: grid;
grid-template-columns: 74px 1fr 1fr;
gap: 8px;
margin-bottom: 6px;
padding: 0 8px;
color: #6b5a33;
font-size: 11px;
font-weight: 800;
text-align: right;
}
.price-info-header span:first-child {
text-align: left;
}
.price-info-row {
display: grid;
grid-template-columns: 70px 1fr 1fr;
gap: 6px;
grid-template-columns: 74px 1fr 1fr;
gap: 8px;
align-items: center;
min-height: 26px;
padding: 4px 6px;
min-height: 34px;
padding: 6px 8px;
border: 1px solid #f0e5d2;
border-radius: 6px;
background: #fff;
font-size: 12px;
font-size: 13px;
}
.price-label {
@@ -2253,13 +2350,49 @@ onMounted(() => {
.price-value,
.price-campaign {
min-height: 26px;
padding: 5px 7px;
border-radius: 5px;
text-align: right;
font-variant-numeric: tabular-nums;
font-weight: 800;
background: #fff;
}
.price-campaign {
color: #b13a2b;
font-weight: 700;
color: #8a8a8a;
background: #f4f4f4;
font-weight: 800;
}
.price-info-row-usd .price-value {
color: #16803a;
}
.price-info-row-eur .price-value {
color: #b91c1c;
}
.price-info-row-try .price-value {
color: #185abc;
}
.price-info-row-usd.has-campaign-price .price-campaign {
background: #dff6e7;
color: #0f6b2f;
font-weight: 900;
}
.price-info-row-eur.has-campaign-price .price-campaign {
background: #fde2e2;
color: #a61717;
font-weight: 900;
}
.price-info-row-try.has-campaign-price .price-campaign {
background: #e2edff;
color: #174ea6;
font-weight: 900;
}
.size-stock-grid {
@@ -2342,9 +2475,12 @@ onMounted(() => {
}
.product-card-images,
.product-card-fields {
.product-card-fields,
.product-card-price-panel {
grid-column: 1;
grid-row: auto;
height: auto;
min-height: 320px;
}
}
</style>